GNUPG-1-9-BRANCH gnupg (7 files)

cvs user wk cvs at cvs.gnupg.org
Thu Feb 24 22:29:20 CET 2005


    Date: Thursday, February 24, 2005 @ 22:40:48
  Author: wk
    Path: /cvs/gnupg/gnupg
     Tag: GNUPG-1-9-BRANCH

Modified: agent/ChangeLog agent/agent.h agent/call-scd.c
          agent/command-ssh.c scd/ChangeLog scd/app.c scd/command.c

* call-scd.c (unescape_status_string): New. Actual a copy of
../g10/call-agent.c
(card_getattr_cb, agent_card_getattr): New.

* command-ssh.c (card_key_available): New.
(ssh_handler_request_identities): First see whether a card key is
available.

* app.c (app_getattr): Return APPTYPE or SERIALNO type even if the
application does dot support the getattr call.

* app.c (select_application): Return an error code and the
application context in an new arg.
* command.c (open_card): Adjusted for that.  Don't use the
fallback if no card is present.  Return an error if the card has
been removed without a reset.
(do_reset, cmd_serialno): Clear that error flag.
(TEST_CARD_REMOVAL): New. Use it with all command handlers.
(scd_update_reader_status_file): Set the error flag on all changes.


---------------------+
 agent/ChangeLog     |    8 +
 agent/agent.h       |    1 
 agent/call-scd.c    |  133 ++++++++++++++++++++++++++++--
 agent/command-ssh.c |  218 +++++++++++++++++++++++++++++++++-----------------
 scd/ChangeLog       |    4 
 scd/app.c           |   29 ++++++
 scd/command.c       |   24 +++--
 7 files changed, 328 insertions(+), 89 deletions(-)


Index: gnupg/agent/ChangeLog
diff -u gnupg/agent/ChangeLog:1.59.2.69 gnupg/agent/ChangeLog:1.59.2.70
--- gnupg/agent/ChangeLog:1.59.2.69	Thu Feb 24 18:36:11 2005
+++ gnupg/agent/ChangeLog	Thu Feb 24 22:40:48 2005
@@ -1,5 +1,13 @@
 2005-02-24  Werner Koch  <wk at g10code.com>
 
+	* call-scd.c (unescape_status_string): New. Actual a copy of
+	../g10/call-agent.c
+	(card_getattr_cb, agent_card_getattr): New.
+
+	* command-ssh.c (card_key_available): New.
+	(ssh_handler_request_identities): First see whether a card key is
+	available.
+
 	* gpg-agent.c (handle_connections): Need to check for events if
 	select returns with -1.
 
Index: gnupg/agent/agent.h
diff -u gnupg/agent/agent.h:1.32.2.15 gnupg/agent/agent.h:1.32.2.16
--- gnupg/agent/agent.h:1.32.2.15	Wed Feb 23 22:06:32 2005
+++ gnupg/agent/agent.h	Thu Feb 24 22:40:48 2005
@@ -259,6 +259,7 @@
 int agent_card_readcert (ctrl_t ctrl,
                          const char *id, char **r_buf, size_t *r_buflen);
 int agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf);
+gpg_error_t agent_card_getattr (ctrl_t ctrl, const char *name, char **result);
 int agent_card_scd (ctrl_t ctrl, const char *cmdline,
                     int (*getpin_cb)(void *, const char *, char*, size_t),
                     void *getpin_cb_arg, void *assuan_context);
Index: gnupg/agent/call-scd.c
diff -u gnupg/agent/call-scd.c:1.13.2.9 gnupg/agent/call-scd.c:1.13.2.10
--- gnupg/agent/call-scd.c:1.13.2.9	Tue Feb 22 19:08:28 2005
+++ gnupg/agent/call-scd.c	Thu Feb 24 22:40:48 2005
@@ -1,5 +1,5 @@
 /* call-scd.c - fork of the scdaemon to do SC operations
- *	Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ *	Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -66,7 +66,7 @@
 static int active_connection_fd = -1;
 static int active_connection = 0;
 
-/* callback parameter for learn card */
+/* Callback parameter for learn card */
 struct learn_parm_s {
   void (*kpinfo_cb)(void*, const char *);
   void *kpinfo_cb_arg;
@@ -266,6 +266,41 @@
 }
 
 
+
+/* Return a new malloced string by unescaping the string S.  Escaping
+   is percent escaping and '+'/space mapping.  A binary Nul will
+   silently be replaced by a 0xFF.  Function returns NULL to indicate
+   an out of memory status. */
+static char *
+unescape_status_string (const unsigned char *s)
+{
+  char *buffer, *d;
+
+  buffer = d = xtrymalloc (strlen (s)+1);
+  if (!buffer)
+    return NULL;
+  while (*s)
+    {
+      if (*s == '%' && s[1] && s[2])
+        { 
+          s++;
+          *d = xtoi_2 (s);
+          if (!*d)
+            *d = '\xff';
+          d++;
+          s += 2;
+        }
+      else if (*s == '+')
+        {
+          *d++ = ' ';
+          s++;
+        }
+      else
+        *d++ = *s++;
+    }
+  *d = 0; 
+  return buffer;
+}
 
 
 
@@ -375,14 +410,6 @@
   if (rc)
     return rc;
 
-  /* Hmm, do we really need this reset - scddaemon should do this or
-     we can do this if we for some reason figure out that the
-     operation might have failed due to a missing RESET.  Hmmm, I feel
-     this is really SCdaemon's duty */
-/*    rc = assuan_transact (scd_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); */
-/*    if (rc) */
-/*      return unlock_scd (map_assuan_err (rc)); */
-
   rc = assuan_transact (scd_ctx, "SERIALNO",
                         NULL, NULL, NULL, NULL,
                         get_serialno_cb, &serialno);
@@ -395,6 +422,8 @@
   return unlock_scd (0);
 }
 
+
+
 
 static AssuanError
 membuf_data_cb (void *opaque, const void *buffer, size_t length)
@@ -644,6 +673,90 @@
 }
 
 
+
+/* Type used with the card_getattr_cb.  */
+struct card_getattr_parm_s {
+  const char *keyword;  /* Keyword to look for.  */
+  size_t keywordlen;    /* strlen of KEYWORD.  */
+  char *data;           /* Malloced and unescaped data.  */
+  int error;            /* ERRNO value or 0 on success. */
+};
+
+/* Callback function for agent_card_getattr.  */
+static assuan_error_t
+card_getattr_cb (void *opaque, const char *line)
+{
+  struct card_getattr_parm_s *parm = opaque;
+  const char *keyword = line;
+  int keywordlen;
+
+  if (parm->data)
+    return 0; /* We want only the first occurrence.  */
+
+  for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+    ;
+  while (spacep (line))
+    line++;
+
+  if (keywordlen == parm->keywordlen
+      && !memcmp (keyword, parm->keyword, keywordlen))
+    {
+      parm->data = unescape_status_string (line);
+      if (!parm->data)
+        parm->error = errno;
+    }
+  
+  return 0;
+}
+
+
+/* Call the agent to retrieve a single line data object. On success
+   the object is malloced and stored at RESULT; it is guaranteed that
+   NULL is never stored in this case.  On error an error code is
+   returned and NULL stored at RESULT. */
+gpg_error_t
+agent_card_getattr (ctrl_t ctrl, const char *name, char **result)
+{
+  int err;
+  struct card_getattr_parm_s parm;
+  char line[ASSUAN_LINELENGTH];
+
+  *result = NULL;
+
+  if (!*name)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  memset (&parm, 0, sizeof parm);
+  parm.keyword = name;
+  parm.keywordlen = strlen (name);
+
+  /* We assume that NAME does not need escaping. */
+  if (8 + strlen (name) > DIM(line)-1)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+  stpcpy (stpcpy (line, "GETATTR "), name); 
+
+  err = start_scd (ctrl);
+  if (err)
+    return err;
+
+  err = map_assuan_err (assuan_transact (scd_ctx, line,
+                                         NULL, NULL, NULL, NULL,
+                                         card_getattr_cb, &parm));
+  if (!err && parm.error)
+    err = gpg_error_from_errno (parm.error);
+  
+  if (!err && !parm.data)
+    err = gpg_error (GPG_ERR_NO_DATA);
+  
+  if (!err)
+    *result = parm.data;
+  else
+    xfree (parm.data);
+
+  return unlock_scd (err);
+}
+
+
 
 
 static AssuanError
Index: gnupg/agent/command-ssh.c
diff -u gnupg/agent/command-ssh.c:1.1.4.11 gnupg/agent/command-ssh.c:1.1.4.12
--- gnupg/agent/command-ssh.c:1.1.4.11	Wed Feb 23 22:06:32 2005
+++ gnupg/agent/command-ssh.c	Thu Feb 24 22:40:48 2005
@@ -1201,7 +1201,7 @@
   return err;
 }
 
-/* Extract the car from SEXP, and create a newly created C-string it,
+/* Extract the car from SEXP, and create a newly created C-string 
    which is to be stored in IDENTIFIER.  */
 static gpg_error_t
 sexp_extract_identifier (gcry_sexp_t sexp, const char **identifier)
@@ -1404,6 +1404,7 @@
 }
 			      
 
+/* Write the public key KEY_PUBLIC to STREAM in SSH key format. */
 static gpg_error_t
 ssh_send_key_public (estream_t stream, gcry_sexp_t key_public)
 {
@@ -1516,7 +1517,78 @@
   return err;
 }
 
-
+
+/* Chec whether a smartcard is available and whether it has a usable
+   key.  Store a copy of that key at R_PK and return 0.  If no key is
+   available store NULL at R_PK and return an error code.  */
+static gpg_error_t
+card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk)
+{
+  gpg_error_t err;
+  char *appname;
+  unsigned char *sbuf;
+  size_t sbuflen;
+  gcry_sexp_t pk;
+
+  *r_pk = NULL;
+
+  /* First see whether a card is available and whether the application
+     is supported.  */
+  err = agent_card_getattr (ctrl, "APPTYPE", &appname);
+  if ( gpg_err_code (err) == GPG_ERR_CARD_REMOVED )
+    {
+      /* Ask for the serial number to reset the card.  */
+      err = agent_card_serialno (ctrl, &appname);
+      if (err)
+        {
+          if (opt.verbose)
+            log_info (_("can't get serial number of card: %s\n"),
+                      gpg_strerror (err));
+          return err;
+        }
+      log_info (_("detected card with S/N: %s\n"), appname);
+      xfree (appname);
+      err = agent_card_getattr (ctrl, "APPTYPE", &appname);
+    }
+  if (err)
+    {
+      log_error (_("error getting application type of card: %s\n"),
+                 gpg_strerror (err));
+      return err;
+    }
+  if (strcmp (appname, "OPENPGP"))
+    {
+      log_info (_("card application `%s' is not supported\n"), appname);
+      xfree (appname);
+      return gpg_error (GPG_ERR_NOT_SUPPORTED);
+    }
+  xfree (appname);
+  appname = NULL;
+
+  /* Read the public key.  */
+  err = agent_card_readkey (ctrl, "OPENPGP.3", &sbuf);
+  if (err)
+    {
+      if (opt.verbose)
+        log_info (_("no suitable card key found: %s\n"), gpg_strerror (err));
+      return err;
+    }
+
+  sbuflen = gcry_sexp_canon_len (sbuf, 0, NULL, NULL);
+  err = gcry_sexp_sscan (&pk, NULL, sbuf, sbuflen);
+  xfree (sbuf);
+  if (err)
+    {
+      log_error ("failed to build S-Exp from received card key: %s\n",
+                 gpg_strerror (err));
+      return err;
+    }
+  
+  *r_pk = pk;
+  return 0;
+}
+
+
 
 /*
   Request handler.  
@@ -1589,91 +1661,95 @@
       goto out;
     }
 
-  /* Iterate over key files.  */
 
-  /* FIXME: make sure that buffer gets deallocated properly.  */
+
+  /* First check whether a key is currently available in the card
+     reader - this should be allowed even without being listed in
+     sshcontrol.txt. */
+
+  if (!card_key_available (ctrl, &key_public))
+    {
+      err = ssh_send_key_public (key_blobs, key_public);
+      gcry_sexp_release (key_public);
+      key_public = NULL;
+      if (err)
+        goto out;
+      
+      key_counter++;
+    }
+
+
+  /* Then look at all the registered an allowed keys. */
+
 
   /* Fixme: We should better iterate over the control file and check
      whether the key file is there.  This is better in resepct to
      performance if tehre are a lot of key sin our key storage. */
-
+  /* FIXME: make sure that buffer gets deallocated properly.  */
   err = open_control_file (&ctrl_fp, 0);
   if (err)
     goto out;
 
-#warning Really need to fix this fixme.
-  /*
- FIXME:  First check whether a key is currently available in the card reader - this should be allowed even without being listed in sshcontrol.txt.
-  */
-
-  while (1)
+  while ( (dir_entry = readdir (dir)) )
     {
-      dir_entry = readdir (dir);
-      if (dir_entry)
-	{
-	  if ((strlen (dir_entry->d_name) == 44)
-	      && (! strncmp (dir_entry->d_name + 40, ".key", 4)))
-	    {
-              char hexgrip[41];
-              int disabled;
-
-              /* We do only want to return keys listed in our control
-                 file. */
-              strncpy (hexgrip, dir_entry->d_name, 40);
-              hexgrip[40] = 0;
-              if ( strlen (hexgrip) != 40 )
-                continue;
-              if (search_control_file (ctrl_fp, hexgrip, &disabled)
-                  || disabled)
-                continue;
-
-	      strncpy (key_path + key_directory_n + 1, dir_entry->d_name, 40);
-
-	      /* Read file content.  */
-	      err = file_to_buffer (key_path, &buffer, &buffer_n);
-	      if (err)
-		break;
+      if ((strlen (dir_entry->d_name) == 44)
+          && (! strncmp (dir_entry->d_name + 40, ".key", 4)))
+        {
+          char hexgrip[41];
+          int disabled;
+
+          /* We do only want to return keys listed in our control
+             file. */
+          strncpy (hexgrip, dir_entry->d_name, 40);
+          hexgrip[40] = 0;
+          if ( strlen (hexgrip) != 40 )
+            continue;
+          if (search_control_file (ctrl_fp, hexgrip, &disabled)
+              || disabled)
+            continue;
+
+          strncpy (key_path + key_directory_n + 1, dir_entry->d_name, 40);
+
+          /* Read file content.  */
+          err = file_to_buffer (key_path, &buffer, &buffer_n);
+          if (err)
+            goto out;
 	      
-	      err = gcry_sexp_sscan (&key_secret, NULL, buffer, buffer_n);
-	      if (err)
-		break;
-
-	      xfree (buffer);
-	      buffer = NULL;
-
-	      err = sexp_extract_identifier (key_secret, &key_type);
-	      if (err)
-		break;
-
-	      err = ssh_key_type_lookup (NULL, key_type, &spec);
-	      if (err)
-		break;
-
-	      xfree ((void *) key_type);
-	      key_type = NULL;
-
-	      err = key_secret_to_public (&key_public, spec, key_secret);
-	      if (err)
-		break;
+          err = gcry_sexp_sscan (&key_secret, NULL, buffer, buffer_n);
+          if (err)
+            goto out;
+
+          xfree (buffer);
+          buffer = NULL;
+
+          err = sexp_extract_identifier (key_secret, &key_type);
+          if (err)
+            goto out;
+
+          err = ssh_key_type_lookup (NULL, key_type, &spec);
+          if (err)
+            goto out;
+
+          xfree ((void *) key_type);
+          key_type = NULL;
+
+          err = key_secret_to_public (&key_public, spec, key_secret);
+          if (err)
+            goto out;
 
-	      gcry_sexp_release (key_secret);
-	      key_secret = NULL;
+          gcry_sexp_release (key_secret);
+          key_secret = NULL;
 	      
-	      err = ssh_send_key_public (key_blobs, key_public);
-	      if (err)
-		break;
+          err = ssh_send_key_public (key_blobs, key_public);
+          if (err)
+            goto out;
 
-	      gcry_sexp_release (key_public);
-	      key_public = NULL;
+          gcry_sexp_release (key_public);
+          key_public = NULL;
 
-	      key_counter++;
-	    }
-	}
-      else
-	break;
+          key_counter++;
+        }
     }
-  if (err)
-    goto out;
   
   ret = es_fseek (key_blobs, 0, SEEK_SET);
   if (ret)
Index: gnupg/scd/ChangeLog
diff -u gnupg/scd/ChangeLog:1.25.2.68 gnupg/scd/ChangeLog:1.25.2.69
--- gnupg/scd/ChangeLog:1.25.2.68	Thu Feb 24 18:36:11 2005
+++ gnupg/scd/ChangeLog	Thu Feb 24 22:40:48 2005
@@ -1,5 +1,8 @@
 2005-02-24  Werner Koch  <wk at g10code.com>
 
+	* app.c (app_getattr): Return APPTYPE or SERIALNO type even if the
+	application does dot support the getattr call.
+
 	* app-openpgp.c (get_one_do): Never try to get a non cacheable
 	object from the cache.
 	(get_one_do): Add new arg to return an error code.  Changed all
@@ -13,6 +16,7 @@
 	been removed without a reset.
 	(do_reset, cmd_serialno): Clear that error flag.
 	(TEST_CARD_REMOVAL): New. Use it with all command handlers.
+	(scd_update_reader_status_file): Set the error flag on all changes.
 
 	* scdaemon.c (ticker_thread): Termintate if a shutdown is pending.
 
Index: gnupg/scd/app.c
diff -u gnupg/scd/app.c:1.3.2.11 gnupg/scd/app.c:1.3.2.12
--- gnupg/scd/app.c:1.3.2.11	Thu Feb 24 18:36:11 2005
+++ gnupg/scd/app.c	Thu Feb 24 22:40:48 2005
@@ -305,6 +305,35 @@
     return gpg_error (GPG_ERR_INV_VALUE);
   if (!app->initialized)
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+
+  if (app->apptype && name && !strcmp (name, "APPTYPE"))
+    {
+      send_status_info (ctrl, "APPTYPE",
+                        app->apptype, strlen (app->apptype), NULL, 0);
+      return 0;
+    }
+  if (name && !strcmp (name, "SERIALNO"))
+    {
+      char *serial_and_stamp;
+      char *serial;
+      time_t stamp;
+      int rc;
+      
+      rc = app_get_serial_and_stamp (app, &serial, &stamp);
+      if (rc)
+        return rc;
+      rc = asprintf (&serial_and_stamp, "%s %lu",
+                     serial, (unsigned long)stamp);
+      rc = (rc < 0)? gpg_error_from_errno (errno) : 0;
+      xfree (serial);
+      if (rc)
+        return rc;
+      send_status_info (ctrl, "SERIALNO",
+                        serial_and_stamp, strlen (serial_and_stamp), NULL, 0);
+      free (serial_and_stamp);
+      return 0;
+    }
+
   if (!app->fnc.getattr)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
   return app->fnc.getattr (app, ctrl, name);
Index: gnupg/scd/command.c
diff -u gnupg/scd/command.c:1.19.2.17 gnupg/scd/command.c:1.19.2.18
--- gnupg/scd/command.c:1.19.2.17	Thu Feb 24 18:36:11 2005
+++ gnupg/scd/command.c	Thu Feb 24 22:40:48 2005
@@ -239,7 +239,7 @@
    operations are done on the same card unless he calls this function.
  */
 static int
-cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
+cmd_serialno (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc = 0;
@@ -248,7 +248,8 @@
   time_t stamp;
 
   /* Clear the remove flag so that the open_card is able to reread it.  */
-  ctrl->server_local->card_removed = 0;
+  if (ctrl->server_local->card_removed)
+    do_reset (ctrl, 0);
 
   if ((rc = open_card (ctrl, *line? line:NULL)))
     return rc;
@@ -1092,7 +1093,6 @@
 
 
 
-
 
 /* Tell the assuan library about our commands */
 static int
@@ -1299,10 +1299,6 @@
             char templ[50];
             FILE *fp;
 
-            last[slot].any = 1;
-            last[slot].status = status;
-            last[slot].changed = changed;
-
             log_info ("updating status of slot %d to 0x%04X\n", slot, status);
             
             sprintf (templ, "reader_%d.status", slot);
@@ -1318,7 +1314,19 @@
               }
             xfree (fname);
 
-            /* Send a signal to the primary client, if any. */
+            /* Set the card removed flag.  We will set this on any
+               card change because a reset or SERIALNO request must be
+               done in any case.  */
+            if (primary_connection && primary_connection->server_local
+                && last[slot].any )
+              primary_connection->server_local->card_removed = 1;
+
+            last[slot].any = 1;
+            last[slot].status = status;
+            last[slot].changed = changed;
+
+
+            /* Send a signal to the primary client, if any.  */
             if (primary_connection && primary_connection->server_local
                 && primary_connection->server_local->assuan_ctx)
               {




More information about the Gnupg-commits mailing list