scd: support multiple tokens simultaneously

NIIBE Yutaka gniibe at fsij.org
Fri Jan 13 06:06:43 CET 2017


Hello,

This is an interim report which explains multiple tokens
now works for me for decryption and signing.

The gpg-agent <-> scdaemon protocol is extended to allow SERIALNO
command to have --demand=<SERIALNO> option.

(1) Query to open card readers / token, expecting the serial number of
    the card

    SERIALNO

(2) Ask matched application on a card

    SERIALNO opengpg

(3) Ask specific card/token with <SERIALNO> 

    SERIALNO --demand=<SERIALNO>

Authentication is not yet supported.


diff --git a/agent/agent.h b/agent/agent.h
index 89dc46d..2db5a5c 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -532,7 +532,7 @@ int agent_card_learn (ctrl_t ctrl,
                       void (*sinfo_cb)(void*, const char *,
                                        size_t, const char *),
                       void *sinfo_cb_arg);
-int agent_card_serialno (ctrl_t ctrl, char **r_serialno);
+int agent_card_serialno (ctrl_t ctrl, char **r_serialno, const char
*demand);
 int agent_card_pksign (ctrl_t ctrl,
                        const char *keyid,
                        int (*getpin_cb)(void *, const char *, char*,
size_t),
diff --git a/agent/call-scd.c b/agent/call-scd.c
index ba59c18..15a2ba5 100644
--- a/agent/call-scd.c
+++ b/agent/call-scd.c
@@ -679,16 +679,22 @@ get_serialno_cb (void *opaque, const char *line)
 /* Return the serial number of the card or an appropriate error.  The
    serial number is returned as a hexstring. */
 int
-agent_card_serialno (ctrl_t ctrl, char **r_serialno)
+agent_card_serialno (ctrl_t ctrl, char **r_serialno, const char
*demand)
 {
   int rc;
   char *serialno = NULL;
+  char line[ASSUAN_LINELENGTH];
 
   rc = start_scd (ctrl);
   if (rc)
     return rc;
 
-  rc = assuan_transact (ctrl->scd_local->ctx, "SERIALNO",
+  if (!demand)
+    strcpy (line, "SERIALNO");
+  else
+    snprintf (line, DIM(line), "SERIALNO --demand=%s", demand);
+
+  rc = assuan_transact (ctrl->scd_local->ctx, line,
                         NULL, NULL, NULL, NULL,
                         get_serialno_cb, &serialno);
   if (rc)
diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 95cef41..79cb057 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -2408,7 +2408,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t
*r_pk, char **cardsn)
   if ( gpg_err_code (err) == GPG_ERR_CARD_REMOVED )
     {
       /* Ask for the serial number to reset the card.  */
-      err = agent_card_serialno (ctrl, &serialno);
+      err = agent_card_serialno (ctrl, &serialno, NULL);
       if (err)
         {
           if (opt.verbose)
diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index 7b07008..7331f58 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -58,7 +58,7 @@ ask_for_card (ctrl_t ctrl, const unsigned char
*shadow_info, char **r_kid)
 
   for (;;)
     {
-      rc = agent_card_serialno (ctrl, &serialno);
+      rc = agent_card_serialno (ctrl, &serialno, want_sn);
       if (!rc)
         {
           log_debug ("detected card with S/N %s\n", serialno);
@@ -72,11 +72,17 @@ ask_for_card (ctrl_t ctrl, const unsigned char
*shadow_info, char **r_kid)
               return 0; /* yes, we have the correct card */
             }
         }
+      else if (gpg_err_code (rc) == GPG_ERR_ENODEV)
+        {
+          log_debug ("no device present\n");
+          rc = 0;
+          no_card = 1;
+        }
       else if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT)
         {
           log_debug ("no card present\n");
           rc = 0;
-          no_card = 1;
+          no_card = 2;
         }
       else
         {
diff --git a/agent/learncard.c b/agent/learncard.c
index 57bce7a..cce9c3a 100644
--- a/agent/learncard.c
+++ b/agent/learncard.c
@@ -330,7 +330,7 @@ agent_handle_learn (ctrl_t ctrl, int send, void
*assuan_context, int force)
   cparm.ctrl = ctrl;
 
   /* Check whether a card is present and get the serial number */
-  rc = agent_card_serialno (ctrl, &serialno);
+  rc = agent_card_serialno (ctrl, &serialno, NULL);
   if (rc)
     goto leave;
 
diff --git a/scd/app-common.h b/scd/app-common.h
index 2371818..fb0fe55 100644
--- a/scd/app-common.h
+++ b/scd/app-common.h
@@ -129,7 +129,8 @@ void application_notify_card_reset (int slot);
 gpg_error_t check_application_conflict (const char *name, app_t app);
 gpg_error_t app_reset (app_t app, ctrl_t ctrl, int send_reset);
 gpg_error_t select_application (ctrl_t ctrl, const char *name, app_t
*r_app,
-                                int scan);
+                                int scan, const unsigned char
*serialno_bin,
+                                size_t serialno_bin_len);
 char *get_supported_applications (void);
 void release_application (app_t app);
 gpg_error_t app_munge_serialno (app_t app);
diff --git a/scd/app.c b/scd/app.c
index 6db9e27..06850d8 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -317,10 +317,12 @@ app_new_register (int slot, ctrl_t ctrl, const
char *name)
    and return a context.  Returns an error code and stores NULL at
    R_APP if no application was found or no card is present. */
 gpg_error_t
-select_application (ctrl_t ctrl, const char *name, app_t *r_app, int
scan)
+select_application (ctrl_t ctrl, const char *name, app_t *r_app,
+                    int scan, const unsigned char *serialno_bin,
+                    size_t serialno_bin_len)
 {
   gpg_error_t err = 0;
-  app_t app;
+  app_t a;
 
   *r_app = NULL;
 
@@ -352,33 +354,50 @@ select_application (ctrl_t ctrl, const char
*name, app_t *r_app, int scan)
 
           if (!sw || sw == SW_HOST_ALREADY_CONNECTED)
             err = 0;
+          else if (sw == SW_HOST_NO_CARD)
+            err = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
           else
             err = gpg_error (GPG_ERR_ENODEV);
 
           if (!err)
             err = app_new_register (slot, ctrl, name);
           else
-            apdu_close_reader (slot);
+            {
+              /* We close a reader with no card.  */
+              apdu_close_reader (slot);
+            }
         }
 
       apdu_dev_list_finish (l);
     }
 
-  app = app_top;
-  if (app)
+  npth_mutex_lock (&app_list_lock);
+  for (a = app_top; a; a = a->next)
     {
-      lock_app (app, ctrl);
-      err = check_conflict (app, name);
+      lock_app (a, ctrl);
+      if (serialno_bin == NULL)
+        break;
+      if (a->serialnolen == serialno_bin_len
+          && !memcmp (a->serialno, serialno_bin, a->serialnolen))
+        break;
+      unlock_app (a);
+    }
+
+  if (a)
+    {
+      err = check_conflict (a, name);
       if (!err)
         {
-          app->ref_count++;
-          *r_app = app;
+          a->ref_count++;
+          *r_app = a;
         }
-      unlock_app (app);
+      unlock_app (a);
     }
   else
     err = gpg_error (GPG_ERR_ENODEV);
 
+  npth_mutex_unlock (&app_list_lock);
+
   return err;
 }
 
diff --git a/scd/command.c b/scd/command.c
index ce2be34..7592181 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -193,9 +193,11 @@ option_handler (assuan_context_t ctx, const char
*key, const char *value)
 
 /* If the card has not yet been opened, do it.  */
 static gpg_error_t
-open_card (ctrl_t ctrl, const char *apptype, int scan)
+open_card (ctrl_t ctrl, const char *apptype, const char *demand)
 {
   gpg_error_t err;
+  unsigned char *serialno_bin;
+  size_t serialno_bin_len = 0;
 
   /* If we ever got a card not present error code, return that.  Only
      the SERIALNO command and a reset are able to clear from that
@@ -209,21 +211,31 @@ open_card (ctrl_t ctrl, const char *apptype, int
scan)
   /* If we are already initialized for one specific application we
      need to check that the client didn't requested a specific
      application different from the one in use before we continue. */
-  if (!scan && ctrl->app_ctx)
+  if (!demand && ctrl->app_ctx)
     return check_application_conflict (apptype, ctrl->app_ctx);
 
-  err = select_application (ctrl, apptype, &ctrl->app_ctx, scan);
+  if (demand && *demand)
+    serialno_bin = hex_to_buffer (demand, &serialno_bin_len);
+  else
+    serialno_bin = NULL;
+
+  err = select_application (ctrl, apptype, &ctrl->app_ctx, demand !=
NULL,
+                            serialno_bin, serialno_bin_len);
+  xfree (serialno_bin);
 
   return err;
 }
 
 
 static const char hlp_serialno[] =
-  "SERIALNO [<apptype>]\n"
+  "SERIALNO [--demand=<serialno>] [<apptype>]\n"
   "\n"
   "Return the serial number of the card using a status
response.  This\n"
   "function should be used to check for the presence of a card.\n"
   "\n"
+  "If --demand is given, an application on the card with SERIALNO
is\n"
+  "selected and an error is returned if no such card available.\n"
+  "\n"
   "If APPTYPE is given, an application of that type is selected and
an\n"
   "error is returned if the application is not supported or
available.\n"
   "The default is to auto-select the application using a hardwired\n"
@@ -245,6 +257,20 @@ cmd_serialno (assuan_context_t ctx, char *line)
   int rc = 0;
   char *serial;
   time_t stamp;
+  const char *demand;
+
+  if ((demand = has_option_name (line, "--demand")))
+    {
+      if (*demand != '=')
+        return set_error (GPG_ERR_ASS_PARAMETER, "missing value for
option");
+      line = (char *)++demand;
+      for (; *line && !spacep (line); line++)
+        ;
+      if (*line)
+        *line++ = 0;
+    }
+  else
+    demand = "";
 
   /* Clear the remove flag so that the open_card is able to reread
it.  */
   if (ctrl->server_local->card_removed)
@@ -255,7 +281,7 @@ cmd_serialno (assuan_context_t ctx, char *line)
       ctrl->server_local->card_removed = 0;
     }
 
-  if ((rc = open_card (ctrl, *line? line:NULL, 1)))
+  if ((rc = open_card (ctrl, *line? line:NULL, demand)))
     {
       ctrl->server_local->card_removed = 1;
       return rc;
@@ -270,7 +296,7 @@ cmd_serialno (assuan_context_t ctx, char *line)
         c->server_local->card_removed = 0;
     }
 
-  rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
+  rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);//???
   if (rc)
     return rc;
 
@@ -357,7 +383,7 @@ cmd_learn (assuan_context_t ctx, char *line)
   int rc = 0;
   int only_keypairinfo = has_option (line, "--keypairinfo");
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   /* Unless the force option is used we try a shortcut by identifying
@@ -440,7 +466,7 @@ cmd_readcert (assuan_context_t ctx, char *line)
   unsigned char *cert;
   size_t ncert;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   line = xstrdup (line); /* Need a copy of the line. */
@@ -482,7 +508,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
   unsigned char *pk;
   size_t pklen;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   if (has_option (line, "--advanced"))
@@ -704,7 +730,7 @@ cmd_pksign (assuan_context_t ctx, char *line)
 
   line = skip_options (line);
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   /* We have to use a copy of the key ID because the function may use
@@ -748,7 +774,7 @@ cmd_pkauth (assuan_context_t ctx, char *line)
   size_t outdatalen;
   char *keyidstr;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -793,7 +819,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
   char *keyidstr;
   unsigned int infoflags;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   keyidstr = xtrystrdup (line);
@@ -846,7 +872,7 @@ cmd_getattr (assuan_context_t ctx, char *line)
   int rc;
   const char *keyword;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   keyword = line;
@@ -888,7 +914,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
   size_t nbytes;
   char *line, *linebuf;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   /* We need to use a copy of LINE, because PIN_CB uses the same
@@ -943,7 +969,7 @@ cmd_writecert (assuan_context_t ctx, char *line)
     line++;
   *line = 0;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1005,7 +1031,7 @@ cmd_writekey (assuan_context_t ctx, char *line)
     line++;
   *line = 0;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1092,7 +1118,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
     line++;
   *line = 0;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1130,7 +1156,7 @@ cmd_random (assuan_context_t ctx, char *line)
                       "number of requested bytes missing");
   nbytes = strtoul (line, NULL, 0);
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1183,7 +1209,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
     line++;
   *line = 0;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1240,7 +1266,7 @@ cmd_checkpin (assuan_context_t ctx, char *line)
   int rc;
   char *idstr;
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1531,7 +1557,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
 
   line = skip_options (line);
 
-  if ((rc = open_card (ctrl, NULL, 0)))
+  if ((rc = open_card (ctrl, NULL, NULL)))
     return rc;
 
   app = ctrl->app_ctx;
--


More information about the Gnupg-devel mailing list