agent: Support scdaemon operation using KEYGRIP.
authorNIIBE Yutaka <gniibe@fsij.org>
Wed, 15 May 2019 06:53:35 +0000 (15:53 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Wed, 15 May 2019 08:13:32 +0000 (17:13 +0900)
* agent/agent.h (struct card_key_info_s): New.
(divert_pksign, divert_pkdecrypt): New API.
* agent/call-scd.c (card_keyinfo_cb): New.
(agent_card_free_keyinfo, agent_card_keyinfo): New.
* agent/divert-scd.c (ask_for_card): Having GRIP argument,
ask scdaemon with agent_card_keyinfo.
(divert_pksign, divert_pkdecrypt): Ditto.
* agent/pkdecrypt.c (agent_pkdecrypt): Supply GRIP.
* agent/pksign.c (agent_pksign_do): Ditto.

--

We are going to relax the requirment for SERIALNO of card.  It's OK,
when a card doesn't have recorded SERIALNO.  If a card has a key
with GRIP, it can be used.

GnuPG-bug-id: 2291, 4301
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
agent/agent.h
agent/call-scd.c
agent/divert-scd.c
agent/pkdecrypt.c
agent/pksign.c

index b7eacf4..77672bd 100644 (file)
@@ -361,6 +361,15 @@ typedef int (*lookup_ttl_t)(const char *hexgrip);
 #endif
 
 
+/* Information from scdaemon for card keys.  */
+struct card_key_info_s
+{
+  struct card_key_info_s *next;
+  char keygrip[40];
+  char *serialno;
+  char *idstr;
+};
+
 /*-- gpg-agent.c --*/
 void agent_exit (int rc)
                 GPGRT_ATTR_NORETURN; /* Also implemented in other tools */
@@ -544,10 +553,12 @@ void agent_reload_trustlist (void);
 
 /*-- divert-scd.c --*/
 int divert_pksign (ctrl_t ctrl, const char *desc_text,
+                   const unsigned char *grip,
                    const unsigned char *digest, size_t digestlen, int algo,
                    const unsigned char *shadow_info, unsigned char **r_sig,
                    size_t *r_siglen);
 int divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
+                      const unsigned char *grip,
                       const unsigned char *cipher,
                       const unsigned char *shadow_info,
                       char **r_buf, size_t *r_len, int *r_padding);
@@ -604,6 +615,9 @@ int agent_card_scd (ctrl_t ctrl, const char *cmdline,
                     int (*getpin_cb)(void *, const char *,
                                      const char *, char*, size_t),
                     void *getpin_cb_arg, void *assuan_context);
+void agent_card_free_keyinfo (struct card_key_info_s *l);
+gpg_error_t agent_card_keyinfo (ctrl_t ctrl, const char *keygrip,
+                                struct card_key_info_s **result);
 
 
 /*-- learncard.c --*/
index b52c6c8..5b53b02 100644 (file)
@@ -329,13 +329,13 @@ start_scd (ctrl_t ctrl)
     {
       ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local);
       if (!ctrl->scd_local)
-       {
-         err = gpg_error_from_syserror ();
-         rc = npth_mutex_unlock (&start_scd_lock);
-         if (rc)
-           log_error ("failed to release the start_scd lock: %s\n", strerror (rc));
-         return err;
-       }
+        {
+          err = gpg_error_from_syserror ();
+          rc = npth_mutex_unlock (&start_scd_lock);
+          if (rc)
+            log_error ("failed to release the start_scd lock: %s\n", strerror (rc));
+          return err;
+        }
       ctrl->scd_local->next_local = scd_local_list;
       scd_local_list = ctrl->scd_local;
     }
@@ -1281,6 +1281,156 @@ agent_card_cardlist (ctrl_t ctrl, strlist_t *result)
 }
 
 
+struct card_keyinfo_parm_s {
+  int error;
+  struct card_key_info_s *list;
+};
+
+/* Callback function for agent_card_keylist.  */
+static gpg_error_t
+card_keyinfo_cb (void *opaque, const char *line)
+{
+  struct card_keyinfo_parm_s *parm = opaque;
+  const char *keyword = line;
+  int keywordlen;
+
+  for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+    ;
+  while (spacep (line))
+    line++;
+
+  if (keywordlen == 7 && !memcmp (keyword, "KEYINFO", keywordlen))
+    {
+      const char *s;
+      int n;
+      struct card_key_info_s *keyinfo;
+      struct card_key_info_s **l_p = &parm->list;
+
+      while ((*l_p))
+        l_p = &(*l_p)->next;
+
+      keyinfo = xtrycalloc (1, sizeof *keyinfo);
+      if (!keyinfo)
+        {
+        alloc_error:
+          if (!parm->error)
+            parm->error = gpg_error_from_syserror ();
+          return 0;
+        }
+
+      for (n=0,s=line; hexdigitp (s); s++, n++)
+        ;
+
+      if (n != 40)
+        {
+        parm_error:
+          if (!parm->error)
+            parm->error = gpg_error (GPG_ERR_ASS_PARAMETER);
+          return 0;
+        }
+
+      memcpy (keyinfo->keygrip, line, 40);
+
+      line = s;
+
+      if (!*line)
+        goto parm_error;
+
+      while (spacep (line))
+        line++;
+
+      if (*line++ != 'T')
+        goto parm_error;
+
+      if (!*line)
+        goto parm_error;
+
+      while (spacep (line))
+        line++;
+
+      for (n=0,s=line; hexdigitp (s); s++, n++)
+        ;
+
+      if (!n)
+        goto parm_error;
+
+      keyinfo->serialno = xtrymalloc (n+1);
+      if (!keyinfo->serialno)
+        goto alloc_error;
+
+      memcpy (keyinfo->serialno, line, n);
+      keyinfo->serialno[n] = 0;
+
+      line = s;
+
+      if (!*line)
+        goto parm_error;
+
+      while (spacep (line))
+        line++;
+
+      if (!*line)
+        goto parm_error;
+
+      keyinfo->idstr = xtrystrdup (line);
+      if (!keyinfo->idstr)
+        goto alloc_error;
+
+      *l_p = keyinfo;
+    }
+
+  return 0;
+}
+
+
+void
+agent_card_free_keyinfo (struct card_key_info_s *l)
+{
+  struct card_key_info_s *l_next;
+
+  for (; l; l = l_next)
+    {
+      l_next = l->next;
+      free (l->serialno);
+      free (l->idstr);
+      free (l);
+    }
+}
+
+/* Call the scdaemon to check if a key of KEYGRIP is available, or
+   retrieve list of available keys on cards. On success the allocated
+   structure is stored at RESULT.  On error an error code is returned
+   and NULL is stored at RESULT.  */
+gpg_error_t
+agent_card_keyinfo (ctrl_t ctrl, const char *keygrip,
+                    struct card_key_info_s **result)
+{
+  int err;
+  struct card_keyinfo_parm_s parm;
+  char line[ASSUAN_LINELENGTH];
+
+  *result = NULL;
+
+  memset (&parm, 0, sizeof parm);
+  snprintf (line, sizeof line, "KEYINFO %s", keygrip ? keygrip : "--list");
+
+  err = start_scd (ctrl);
+  if (err)
+    return err;
+
+  err = assuan_transact (ctrl->scd_local->ctx, line,
+                         NULL, NULL, NULL, NULL,
+                         card_keyinfo_cb, &parm);
+  if (!err && parm.error)
+    err = parm.error;
+
+  if (!err)
+    *result = parm.list;
+  else
+    agent_card_free_keyinfo (parm.list);
+
+  return unlock_scd (ctrl, err);
+}
 \f
 static gpg_error_t
 pass_status_thru (void *opaque, const char *line)
index e89c74a..a6ffba7 100644 (file)
 #include "../common/sexp-parse.h"
 
 
-static int
-ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
+static gpg_error_t
+ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info,
+              const unsigned char *grip, char **r_kid)
 {
-  int rc, i;
+  int i;
   char *serialno;
   int no_card = 0;
   char *desc;
   char *want_sn, *want_kid, *want_sn_disp;
   int len;
+  struct card_key_info_s *keyinfo;
+  gpg_error_t err;
+  char hexgrip[41];
 
   *r_kid = NULL;
 
-  rc = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL);
-  if (rc)
-    return rc;
+  bin2hex (grip, 20, hexgrip);
+  err = agent_card_keyinfo (ctrl, hexgrip, &keyinfo);
+  if (!err)
+    {
+      agent_card_free_keyinfo (keyinfo);
+      if ((*r_kid = xtrystrdup (hexgrip)))
+        return 0;
+      else
+        return gpg_error_from_syserror ();
+    }
+
+  err = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL);
+  if (err)
+    return err;
   want_sn_disp = xtrystrdup (want_sn);
   if (!want_sn_disp)
     {
-      rc = gpg_error_from_syserror ();
+      err = gpg_error_from_syserror ();
       xfree (want_sn);
       xfree (want_kid);
-      return rc;
+      return err;
     }
 
   len = strlen (want_sn_disp);
@@ -76,8 +91,8 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
 
   for (;;)
     {
-      rc = agent_card_serialno (ctrl, &serialno, want_sn);
-      if (!rc)
+      err = agent_card_serialno (ctrl, &serialno, want_sn);
+      if (!err)
         {
           log_debug ("detected card with S/N %s\n", serialno);
           i = strcmp (serialno, want_sn);
@@ -91,24 +106,24 @@ 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)
+      else if (gpg_err_code (err) == GPG_ERR_ENODEV)
         {
           log_debug ("no device present\n");
-          rc = 0;
+          err = 0;
           no_card = 1;
         }
-      else if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT)
+      else if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT)
         {
           log_debug ("no card present\n");
-          rc = 0;
+          err = 0;
           no_card = 2;
         }
       else
         {
-          log_error ("error accessing card: %s\n", gpg_strerror (rc));
+          log_error ("error accessing card: %s\n", gpg_strerror (err));
         }
 
-      if (!rc)
+      if (!err)
         {
           if (asprintf (&desc,
                     "%s:%%0A%%0A"
@@ -119,24 +134,24 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
                              "insert the one with serial number"),
                         want_sn_disp) < 0)
             {
-              rc = out_of_core ();
+              err = out_of_core ();
             }
           else
             {
-              rc = agent_get_confirmation (ctrl, desc, NULL, NULL, 0);
+              err = agent_get_confirmation (ctrl, desc, NULL, NULL, 0);
               if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK &&
-                  gpg_err_code (rc) == GPG_ERR_NO_PIN_ENTRY)
-                rc = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
+                  gpg_err_code (err) == GPG_ERR_NO_PIN_ENTRY)
+                err = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
 
               xfree (desc);
             }
         }
-      if (rc)
+      if (err)
         {
           xfree (want_sn_disp);
           xfree (want_sn);
           xfree (want_kid);
-          return rc;
+          return err;
         }
     }
 }
@@ -434,7 +449,7 @@ getpin_cb (void *opaque, const char *desc_text, const char *info,
  *
  * FIXME: Explain the other args.  */
 int
-divert_pksign (ctrl_t ctrl, const char *desc_text,
+divert_pksign (ctrl_t ctrl, const char *desc_text, const unsigned char *grip,
                const unsigned char *digest, size_t digestlen, int algo,
                const unsigned char *shadow_info, unsigned char **r_sig,
                size_t *r_siglen)
@@ -446,7 +461,7 @@ divert_pksign (ctrl_t ctrl, const char *desc_text,
 
   (void)desc_text;
 
-  rc = ask_for_card (ctrl, shadow_info, &kid);
+  rc = ask_for_card (ctrl, shadow_info, grip, &kid);
   if (rc)
     return rc;
 
@@ -490,6 +505,7 @@ divert_pksign (ctrl_t ctrl, const char *desc_text,
    R_PADDING with -1 for not known.  */
 int
 divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
+                  const unsigned char *grip,
                   const unsigned char *cipher,
                   const unsigned char *shadow_info,
                   char **r_buf, size_t *r_len, int *r_padding)
@@ -581,7 +597,7 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
   ciphertext = s;
   ciphertextlen = n;
 
-  rc = ask_for_card (ctrl, shadow_info, &kid);
+  rc = ask_for_card (ctrl, shadow_info, grip, &kid);
   if (rc)
     return rc;
 
index a0ced2f..ec23daf 100644 (file)
@@ -85,8 +85,8 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
           goto leave;
         }
 
-      rc = divert_pkdecrypt (ctrl, desc_text, ciphertext, shadow_info,
-                             &buf, &len, r_padding);
+      rc = divert_pkdecrypt (ctrl, desc_text, ctrl->keygrip, ciphertext,
+                             shadow_info, &buf, &len, r_padding);
       if (rc)
         {
           log_error ("smartcard decryption failed: %s\n", gpg_strerror (rc));
index bc8d733..d9519d1 100644 (file)
@@ -352,6 +352,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
           agent_modify_description (desc_text, NULL, s_skey, &desc2);
 
         err = divert_pksign (ctrl, desc2? desc2 : desc_text,
+                             ctrl->keygrip,
                              data, datalen,
                              ctrl->digest.algo,
                              shadow_info, &buf, &len);