Bunch of refactoring and minor changes for PIV cards.
authorWerner Koch <wk@gnupg.org>
Thu, 14 Feb 2019 10:49:38 +0000 (11:49 +0100)
committerWerner Koch <wk@gnupg.org>
Thu, 14 Feb 2019 10:50:22 +0000 (11:50 +0100)
* src/agent.h (struct key_info_s): New.
(struct agent_card_info_s): Add cardtype, kinfo and is_piv members.
(agent_card_info_t): New.
* src/cert-object.c: Get rid of the CERT_PARSING documentation macro
and replace the attr_one_ext et al macros by straight code for
easier readability.
* src/agent.c (hexgrip_valid_p): New.
(scute_agent_release_card_info): Free kinfo member.
(scute_find_kinfo): New.
(create_kinfo): New.
(learn_status_cb): Parse APPTYPE and CARDTYPE. Extend KEYPAIRINFO
parsing.
(scute_agent_get_cert): Change int arg 'no' to a string arg 'certref'.
* src/gpgsm.c (scute_gpgsm_get_cert): Likewise.
* src/slots.c (slot_init): Adjust for change
(slots_update_slot): Allow for PIV cards.
(slot_token_application): Return "PIV" or "OpenPGP".
(slot_token_serial): Support PIV cards.
(slot_token_version): Ditto.
* src/support.h (scute_copy_string): Make second arg a const.

Signed-off-by: Werner Koch <wk@gnupg.org>
src/agent.c
src/agent.h
src/cert-object.c
src/gpgsm.c
src/gpgsm.h
src/slots.c
src/slots.h
src/support.h

index 46d56d5..f8bea95 100644 (file)
@@ -416,6 +416,21 @@ unhexify_fpr (const char *hexstr, unsigned char *fpr)
   return 1;
 }
 
+/* Return true if HEXSTR is a valid keygrip.  */
+static unsigned int
+hexgrip_valid_p (const char *hexstr)
+{
+  const char *s;
+  int n;
+
+  for (s=hexstr, n=0; hexdigitp (s); s++, n++)
+    ;
+  if ((*s && *s != ' ') || n != 40)
+    return 0; /* Bad keygrip */
+  else
+    return 1; /* Valid.  */
+}
+
 
 /* Take the serial number from LINE and return it verbatim in a newly
    allocated string.  We make sure that only hex characters are
@@ -448,22 +463,70 @@ scute_agent_release_card_info (struct agent_card_info_s *info)
     return;
 
   free (info->serialno);
+  free (info->cardtype);
   free (info->disp_name);
   free (info->disp_lang);
   free (info->pubkey_url);
   free (info->login_data);
+  while (info->kinfo)
+    {
+      key_info_t ki = info->kinfo->next;
+      free (info->kinfo);
+      info->kinfo = ki;
+    }
 
   memset (info, 0, sizeof (*info));
 }
 
 
+/* Return the key info object for the key KEYREF.  If it is not found
+ * NULL is returned.  */
+key_info_t
+scute_find_kinfo (agent_card_info_t info, const char *keyref)
+{
+  key_info_t kinfo;
+
+  for (kinfo = info->kinfo; kinfo; kinfo = kinfo->next)
+    if (!strcmp (kinfo->keyref, keyref))
+      return kinfo;
+  return NULL;
+}
+
+
+/* Create a new key info object with KEYREF.  All fields but the
+ * keyref are zeroed out.  The created object is appended to the list
+ * at INFO. */
+static key_info_t
+create_kinfo (agent_card_info_t info, const char *keyref)
+{
+  key_info_t kinfo, ki;
+
+  kinfo = calloc (1, sizeof *kinfo + strlen (keyref));
+  if (!kinfo)
+    return NULL;
+  strcpy (kinfo->keyref, keyref);
+
+  if (!info->kinfo)
+    info->kinfo = kinfo;
+  else
+    {
+      for (ki=info->kinfo; ki->next; ki = ki->next)
+        ;
+      ki->next = kinfo;
+    }
+  return kinfo;
+}
+
+
 /* FIXME: We are not returning out of memory errors.  */
 static gpg_error_t
 learn_status_cb (void *opaque, const char *line)
 {
-  struct agent_card_info_s *parm = opaque;
+  agent_card_info_t parm = opaque;
   const char *keyword = line;
   int keywordlen;
+  key_info_t kinfo;
+  const char *keyref;
   int i;
 
   for (keywordlen = 0; *line && !spacep (line); line++, keywordlen++)
@@ -473,10 +536,18 @@ learn_status_cb (void *opaque, const char *line)
 
   if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
     {
-      if (parm->serialno)
-       free (parm->serialno);
+      free (parm->serialno);
       parm->serialno = store_serialno (line);
     }
+  else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen))
+    {
+      parm->is_piv = !strcmp (line, "PIV");
+    }
+  else if (keywordlen == 8 && !memcmp (keyword, "CARDTYPE", keywordlen))
+    {
+      free (parm->cardtype);
+      parm->cardtype = unescape_status_string (line);
+    }
   else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
     {
       if (parm->disp_name)
@@ -572,38 +643,53 @@ learn_status_cb (void *opaque, const char *line)
     }
   else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen))
     {
-      const char *grip = line;
+      /* The format of such a line is:
+       *   KEYPARINFO <hexgrip> <keyref>
+       */
+      const char *hexgrip = line;
+
       while (*line && !spacep (line))
         line++;
+      while (spacep (line))
+        line++;
+      keyref = line;
+
+      if (hexgrip_valid_p (hexgrip))
+        {
+          /* Check whether we already have an item for the keyref.  */
+          kinfo = scute_find_kinfo (parm, keyref);
+          if (!kinfo)  /* New entry.  */
+            {
+              kinfo = create_kinfo (parm, keyref);
+              if (!kinfo)
+                goto no_core;
+            }
+          else /* Existing entry - clear the grip.  */
+            *kinfo->grip = 0;
+
+          strncpy (kinfo->grip, hexgrip, sizeof kinfo->grip);
+          kinfo->grip[sizeof kinfo->grip -1] = 0;
 
-      if (line - grip == 40)
-       {
-         while (spacep (line))
-           line++;
-         if (!memcmp (line, "OPENPGP.", 8))
-           {
-             int no;
-             line += 8;
-
-             no = atoi (line);
-
-             if (no == 1)
-               {
-                 memcpy (parm->grip1, grip, 40);
-                 parm->grip1valid = 1;
-               }
-             else if (no == 2)
-               {
-                 memcpy (parm->grip2, grip, 40);
-                 parm->grip2valid = 1;
-               }
-             else if (no == 3)
-               {
-                 memcpy (parm->grip3, grip, 40);
-                 parm->grip3valid = 1;
-               }
-           }
-       }
+          /* Keep legacy info.  */
+         if (!strcmp (keyref, "OPENPGP.1"))
+            {
+              strncpy (parm->grip1, hexgrip, sizeof parm->grip1);
+              parm->grip1[sizeof parm->grip1 - 1] = 0;
+              parm->grip1valid = 1;
+            }
+          else if (!strcmp (keyref, "OPENPGP.2"))
+            {
+              strncpy (parm->grip2, hexgrip, sizeof parm->grip2);
+              parm->grip2[sizeof parm->grip2 - 1] = 0;
+              parm->grip2valid = 1;
+            }
+          else if (!strcmp (keyref, "OPENPGP.3"))
+            {
+              strncpy (parm->grip3, hexgrip, sizeof parm->grip3);
+              parm->grip3[sizeof parm->grip3 - 1] = 0;
+              parm->grip3valid = 1;
+            }
+        }
     }
   else if (keywordlen == 6 && !memcmp (keyword, "EXTCAP", keywordlen))
     {
@@ -630,6 +716,9 @@ learn_status_cb (void *opaque, const char *line)
         }
     }
   return 0;
+
+ no_core:
+  return gpg_error_from_syserror ();
 }
 
 
@@ -1027,9 +1116,9 @@ get_cert_data_cb (void *opaque, const void *data, size_t data_len)
 }
 
 
-/* Try to get certificate for key numer NO.  */
+/* Try to get certificate for CERTREF.  */
 gpg_error_t
-scute_agent_get_cert (int no, struct cert *cert)
+scute_agent_get_cert (const char *certref, struct cert *cert)
 {
   gpg_error_t err;
   char cmd[150];
@@ -1039,7 +1128,7 @@ scute_agent_get_cert (int no, struct cert *cert)
   cert_s.cert_der_len = 0;
   cert_s.cert_der_size = 0;
 
-  snprintf (cmd, sizeof (cmd), "SCD READCERT OPENPGP.%i", no);
+  snprintf (cmd, sizeof (cmd), "SCD READCERT %s", certref);
   err = assuan_transact (agent_ctx, cmd, get_cert_data_cb, &cert_s,
                         NULL, NULL, NULL, NULL);
   /* Just to be safe... */
index 0d6bb30..3fb2f89 100644 (file)
@@ -2,7 +2,7 @@
    Copyright (C) 2006, 2007 g10 Code GmbH
 
    This file is part of Scute.
+
    Scute is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
 #include "cert.h"
 
 \f
+/* An object to store information pertaining to a keypair as stored on
+ * a card.  This is commonly used as a linked list of all keys known
+ * for a card.  */
+struct key_info_s
+{
+  struct key_info_s *next;
+
+  char grip[41];/* The keygrip as hex encoded string.  */
+
+  unsigned char xflag;   /* Temporary flag to help processing a list. */
+
+  /* The three next items are mostly useful for OpenPGP cards.  */
+  unsigned char fprlen;  /* Use length of the next item.  */
+  unsigned char fpr[32]; /* The binary fingerprint of length FPRLEN.  */
+  unsigned long created; /* The time the key was created.  */
+
+  char keyref[1];        /* String with the keyref (e.g. OPENPGP.1).  */
+};
+typedef struct key_info_s *key_info_t;
+
+
 /* The information structure for a smart card.  */
-struct agent_card_info_s 
+struct agent_card_info_s
 {
   char *serialno;      /* Malloced hex string.  */
+  char *cardtype;       /* Null or mallcoed string with the card type.  */
   char *disp_name;     /* Malloced.  */
   char *disp_lang;     /* Malloced.  */
   int  disp_sex;       /* 0 = unspecified, 1 = male, 2 = female.  */
@@ -52,7 +74,8 @@ struct agent_card_info_s
   char cafpr1[20];
   char cafpr2[20];
   char cafpr3[20];
-  char fpr1valid;
+  key_info_t kinfo;     /* Linked list with all keypair related data.  */
+  char fpr1valid;       /* Duplicated info for the legacy parts of the code. */
   char fpr2valid;
   char fpr3valid;
   char fpr1[20];
@@ -75,7 +98,10 @@ struct agent_card_info_s
   char grip3[41];
   int rng_available;    /* True if the GET CHALLENGE operation
                            is supported. */
+  int is_piv;           /* True if this is a PIV card.  */
 };
+typedef struct agent_card_info_s *agent_card_info_t;
+
 
 \f
 /* Try to connect to the agent via socket.  Handle the server's
@@ -102,6 +128,8 @@ gpg_error_t scute_agent_learn (struct agent_card_info_s *info);
 /* Release the card info structure INFO.  */
 void scute_agent_release_card_info (struct agent_card_info_s *info);
 
+key_info_t scute_find_kinfo (agent_card_info_t info, const char *keyref);
+
 
 /* Sign the data DATA of length LEN with the key GRIP and return the
    signature in SIG_RESULT and SIG_LEN.  */
@@ -113,7 +141,7 @@ gpg_error_t scute_agent_sign (char *grip, unsigned char *data, int len,
 gpg_error_t scute_agent_is_trusted (char *fpr, bool *is_trusted);
 
 /* Try to get certificate for key numer NO.  */
-gpg_error_t scute_agent_get_cert (int no, struct cert *cert);
+gpg_error_t scute_agent_get_cert (const char *certref, struct cert *cert);
 
 /* Get random bytes from the card. */
 gpg_error_t scute_agent_get_random (unsigned char *data, size_t len);
index 4582ae5..6b559a8 100644 (file)
@@ -2,7 +2,7 @@
    Copyright (C) 2006, 2007 g10 Code GmbH
 
    This file is part of Scute.
+
    Scute is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
 #include "debug.h"
 
 
-/* Parsing certificates is problematic, as there is no reliable
-   standard.  However, we must parse at least the subject field (see
-   below).  So, disabling cert parsing does currently not disable it
-   completely.  The purpose of the symbol is to document which parts
-   of the code are mandatory, and which are optional.  */
-#define CERT_PARSING 1
-
-\f
-#if CERT_PARSING || 1
-
 #define atoi_1(p)   (*(p) - '0' )
 #define atoi_2(p)   ((atoi_1(p) * 10) + atoi_1((p)+1))
 #define atoi_4(p)   ((atoi_2(p) * 100) + atoi_2((p)+2))
 
 
+#if 0 /* Currently not used.  */
 static bool
 time_to_ck_date (time_t *atime, CK_DATE *ckdate)
 {
@@ -86,7 +77,7 @@ time_to_ck_date (time_t *atime, CK_DATE *ckdate)
   if (!(broken_time.tm_year >= 0 && broken_time.tm_year <= 8099
        && broken_time.tm_mon >= 0 && broken_time.tm_mon <= 11
        && broken_time.tm_mday >= 1 && broken_time.tm_mday <= 31))
-    { 
+    {
       DEBUG (DBG_INFO, "unrepresentable time %i-%i-%i",
             broken_time.tm_year, broken_time.tm_mon, broken_time.tm_mday);
       return false;
@@ -101,12 +92,12 @@ time_to_ck_date (time_t *atime, CK_DATE *ckdate)
   ckdate->year[1] = LAST_DIGIT (nr);
   nr = nr / 10;
   ckdate->year[0] = LAST_DIGIT (nr);
-  
+
   nr = broken_time.tm_mon + 1;
   ckdate->month[1] = LAST_DIGIT (nr);
   nr = nr / 10;
   ckdate->month[0] = LAST_DIGIT (nr);
-  
+
   nr = broken_time.tm_mday;
   ckdate->day[1] = LAST_DIGIT (nr);
   nr = nr / 10;
@@ -114,7 +105,7 @@ time_to_ck_date (time_t *atime, CK_DATE *ckdate)
 
   return true;
 }
-
+#endif /*0*/
 
 static gpg_error_t
 asn1_get_len (unsigned char **asn1, int *asn1_len, int *rlen)
@@ -241,7 +232,7 @@ asn1_get_element (unsigned char *cert, int cert_len,
   /* We found the subject.  */
   *sub_start = prev_certp;
   *sub_len = certp - prev_certp;
-  
+
   return 0;
 }
 
@@ -380,7 +371,6 @@ asn1_get_public_exp (unsigned char *cert, int cert_len,
 
   return 0;
 }
-#endif
 
 
 static gpg_error_t
@@ -453,8 +443,6 @@ scute_attr_cert (struct cert *cert,
   CK_DATE obj_end_date;
   CK_ULONG obj_java_midp_sec_domain = 0;
 
-#if CERT_PARSING || 1
-  /* See below.  */
   err = asn1_get_subject (cert->cert_der, cert->cert_der_len,
                          &subject_start, &subject_len);
   if (err)
@@ -463,8 +451,7 @@ scute_attr_cert (struct cert *cert,
             gpg_strerror (err));
       return err;
     }
-#endif
-#if CERT_PARSING
+
   err = asn1_get_issuer (cert->cert_der, cert->cert_der_len,
                         &issuer_start, &issuer_len);
   if (err)
@@ -482,7 +469,6 @@ scute_attr_cert (struct cert *cert,
             gpg_strerror (err));
       return err;
     }
-#endif
 
 
 #define NR_ATTR_CERT 20
@@ -494,77 +480,106 @@ scute_attr_cert (struct cert *cert,
       return gpg_error (GPG_ERR_ENOMEM);
     }
 
-#define one_attr_ext(type, val, size)                                  \
-  if (!err)                                                            \
-    err = attr_one (attr, &attr_count, type, val, size)
-
-#define one_attr(type, val) one_attr_ext (type, &val, sizeof (val))
-
-#define empty_attr(type)                                               \
-  if (!err)                                                            \
-    err = attr_empty (attr, &attr_count, type)
-
-  one_attr (CKA_CLASS, obj_class);
-  one_attr (CKA_TOKEN, obj_token);
-  one_attr (CKA_PRIVATE, obj_private);
-  one_attr (CKA_MODIFIABLE, obj_modifiable);
-  one_attr (CKA_LABEL, obj_label);
-  one_attr (CKA_CERTIFICATE_TYPE, obj_cert_type);
-  one_attr (CKA_TRUSTED, obj_trusted);
-  one_attr (CKA_CERTIFICATE_CATEGORY, obj_cert_cat);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_CLASS,
+                    &obj_class, sizeof obj_class);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_TOKEN,
+                    &obj_token, sizeof obj_token);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_PRIVATE,
+                    &obj_private, sizeof obj_private);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_MODIFIABLE,
+                    &obj_modifiable, sizeof obj_modifiable);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_LABEL,
+                    &obj_label, sizeof obj_label);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_CERTIFICATE_TYPE,
+                    &obj_cert_type, sizeof obj_cert_type);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_TRUSTED,
+                    &obj_trusted, sizeof obj_trusted);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_CERTIFICATE_CATEGORY,
+                    &obj_cert_cat, sizeof obj_cert_cat);
 
   /* FIXME: Calculate check_value.  */
-  one_attr (CKA_CHECK_VALUE, obj_check_value);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_CHECK_VALUE,
+                    &obj_check_value, sizeof obj_check_value);
 
 #if 0
   if (time_to_ck_date (&cert->timestamp, &obj_start_date))
     {
-      one_attr (CKA_START_DATE, obj_start_date);
+      if (!err)
+        err = attr_one (attr, &attr_count, CKA_START_DATE,
+                        &obj_start_date, sizeof obj_start_date);
     }
 
   if (time_to_ck_date (&cert->expires, &obj_end_date))
     {
-      one_attr (CKA_END_DATE, obj_end_date);
+      if (!err)
+        err = attr_one (attr, &attr_count, CKA_END_DATE,
+                        &obj_end_date, sizeof obj_end_date);
     }
 #else
   /* For now, we disable these fields.  We can parse them from the
      certificate just as the other data.  However, we would like to
      avoid parsing the certificates at all, let's see how much
      functionality we really need in the PKCS#11 token first.  */
-  empty_attr (CKA_START_DATE);
-  empty_attr (CKA_END_DATE);
+  (void)obj_start_date;
+  (void)obj_end_date;
+  if (!err)
+    err = attr_empty (attr, &attr_count, CKA_START_DATE);
+  if (!err)
+    err = attr_empty (attr, &attr_count, CKA_END_DATE);
 #endif
 
   /* Note: This attribute is mandatory.  Without it, Firefox client
      authentication won't work.  */
-#if CERT_PARSING || 1
-  one_attr_ext (CKA_SUBJECT, subject_start, subject_len);
-#endif
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_SUBJECT,
+                    subject_start, subject_len);
 
 #if 0
   /* If we get the info directly from the card, we don't have a
      fingerprint, and parsing the subject key identifier is quite a
      mouth full.  Let's try a different approach for now.  */
-  one_attr_ext (CKA_ID, cert->fpr, 40);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_ID,
+                    cert->fpr, 40);
 #else
   {
     char certptr[40];
     snprintf (certptr, DIM (certptr), "%p", cert);
-    one_attr_ext (CKA_ID, certptr, strlen (certptr));
+    if (!err)
+      err = attr_one (attr, &attr_count, CKA_ID,
+                      certptr, strlen (certptr));
   }
 #endif
 
-#if CERT_PARSING
-  one_attr_ext (CKA_ISSUER, issuer_start, issuer_len);
-  one_attr_ext (CKA_SERIAL_NUMBER, serial_start, serial_len);
-#endif
-  one_attr_ext (CKA_VALUE, cert->cert_der, cert->cert_der_len);
-  
-  empty_attr (CKA_URL);
-  empty_attr (CKA_HASH_OF_SUBJECT_PUBLIC_KEY);
-  empty_attr (CKA_HASH_OF_ISSUER_PUBLIC_KEY);
-
-  one_attr (CKA_JAVA_MIDP_SECURITY_DOMAIN, obj_java_midp_sec_domain);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_ISSUER,
+                    issuer_start, issuer_len);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_SERIAL_NUMBER,
+                    serial_start, serial_len);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_VALUE,
+                    cert->cert_der, cert->cert_der_len);
+
+  if (!err)
+    err = attr_empty (attr, &attr_count, CKA_URL);
+  if (!err)
+    err = attr_empty (attr, &attr_count, CKA_HASH_OF_SUBJECT_PUBLIC_KEY);
+  if (!err)
+    err = attr_empty (attr, &attr_count, CKA_HASH_OF_ISSUER_PUBLIC_KEY);
+
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_JAVA_MIDP_SECURITY_DOMAIN,
+                    &obj_java_midp_sec_domain, sizeof obj_java_midp_sec_domain);
 
   if (err)
     {
@@ -624,7 +639,6 @@ scute_attr_prv (struct cert *cert, CK_ATTRIBUTE_PTR *attrp,
   CK_BBOOL obj_wrap_with_trusted = CK_FALSE;
   CK_BBOOL obj_always_authenticate = CK_FALSE;
 
-#if CERT_PARSING
   err = asn1_get_subject (cert->cert_der, cert->cert_der_len,
                          &subject_start, &subject_len);
   if (err)
@@ -649,7 +663,6 @@ scute_attr_prv (struct cert *cert, CK_ATTRIBUTE_PTR *attrp,
             gpg_strerror (err));
       return err;
     }
-#endif
 
 #define NR_ATTR_PRV 27
   attr = malloc (sizeof (CK_ATTRIBUTE) * NR_ATTR_PRV);
@@ -660,36 +673,39 @@ scute_attr_prv (struct cert *cert, CK_ATTRIBUTE_PTR *attrp,
       return gpg_error (GPG_ERR_ENOMEM);
     }
 
-#undef one_attr_ext
-#define one_attr_ext(type, val, size)                                  \
-  if (!err)                                                            \
-    err = attr_one (attr, &attr_count, type, val, size)
-
-#undef one_attr
-#define one_attr(type, val) one_attr_ext (type, &val, sizeof (val))
-
-#undef empty_attr
-#define empty_attr(type)                                               \
-  if (!err)                                                            \
-    err = attr_empty (attr, &attr_count, type)
-
-  one_attr (CKA_CLASS, obj_class);
-  one_attr (CKA_TOKEN, obj_token);
-  one_attr (CKA_PRIVATE, obj_private);
-  one_attr (CKA_MODIFIABLE, obj_modifiable);
-  one_attr (CKA_LABEL, obj_label);
-
-  one_attr (CKA_KEY_TYPE, obj_key_type);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_CLASS,
+                    &obj_class, sizeof obj_class);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_TOKEN,
+                    &obj_token, sizeof obj_token);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_PRIVATE,
+                    &obj_private, sizeof obj_private);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_MODIFIABLE,
+                    &obj_modifiable, sizeof obj_modifiable);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_LABEL,
+                    &obj_label, sizeof obj_label);
+
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_KEY_TYPE,
+                    &obj_key_type, sizeof obj_key_type);
 #if 0
   /* If we get the info directly from the card, we don't have a
      fingerprint, and parsing the subject key identifier is quite a
      mouth full.  Let's try a different approach for now.  */
-  one_attr_ext (CKA_ID, cert->fpr, 40);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_ID,
+                    &cert->fpr, 40);
 #else
   {
     char certptr[40];
     snprintf (certptr, DIM (certptr), "%p", cert);
-    one_attr_ext (CKA_ID, certptr, strlen (certptr));
+    if (!err)
+      err = attr_one (attr, &attr_count, CKA_ID,
+                      certptr, strlen (certptr));
   }
 #endif
 
@@ -703,47 +719,86 @@ scute_attr_prv (struct cert *cert, CK_ATTRIBUTE_PTR *attrp,
      gpgsm.  */
   if (time_to_ck_date (&cert->timestamp, &obj_start_date))
     {
-      one_attr (CKA_START_DATE, obj_start_date);
+      if (!err)
+        err = attr_one (attr, &attr_count, CKA_START_DATE,
+                        &obj_start_date, sizeof obj_start_date);
     }
 
   if (time_to_ck_date (&cert->expires, &obj_end_date))
     {
-      one_attr (CKA_END_DATE, obj_end_date);
+      if (!err)
+        err = attr_one (attr, &attr_count, CKA_END_DATE,
+                        &obj_end_date, sizeof obj_end_date);
     }
 #else
   /* For now, we disable these fields.  We can parse them from the
      certificate just as the other data.  However, we would like to
      avoid parsing the certificates at all, let's see how much
      functionality we really need in the PKCS#11 token first.  */
-  empty_attr (CKA_START_DATE);
-  empty_attr (CKA_END_DATE);
+  (void)obj_start_date;
+  (void)obj_end_date;
+  if (!err)
+    err = attr_empty (attr, &attr_count, CKA_START_DATE);
+  if (!err)
+    err = attr_empty (attr, &attr_count, CKA_END_DATE);
 #endif
 
-  one_attr (CKA_DERIVE, obj_derive);
-  one_attr (CKA_LOCAL, obj_local);
-  one_attr (CKA_KEY_GEN_MECHANISM, obj_key_gen);
-  one_attr (CKA_ALLOWED_MECHANISMS, obj_mechanisms);
-
-#if CERT_PARSING
-  one_attr_ext (CKA_SUBJECT, subject_start, subject_len);
-#endif
-
-  one_attr (CKA_SENSITIVE, obj_sensitive);
-  one_attr (CKA_DECRYPT, obj_decrypt);
-  one_attr (CKA_SIGN, obj_sign);
-  one_attr (CKA_SIGN_RECOVER, obj_sign_recover);
-  one_attr (CKA_UNWRAP, obj_unwrap);
-  one_attr (CKA_EXTRACTABLE, obj_extractable);
-  one_attr (CKA_ALWAYS_SENSITIVE, obj_always_sensitive);
-  one_attr (CKA_NEVER_EXTRACTABLE, obj_never_extractable);
-  one_attr (CKA_WRAP_WITH_TRUSTED, obj_wrap_with_trusted);
-  empty_attr (CKA_UNWRAP_TEMPLATE);
-  one_attr (CKA_ALWAYS_AUTHENTICATE, obj_always_authenticate);
-
-#if CERT_PARSING
-  one_attr_ext (CKA_MODULUS, modulus_start, modulus_len);
-  one_attr_ext (CKA_PUBLIC_EXPONENT, public_exp_start, public_exp_len);
-#endif
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_DERIVE,
+                    &obj_derive, sizeof obj_derive);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_LOCAL,
+                    &obj_local, sizeof obj_local);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_KEY_GEN_MECHANISM,
+                    &obj_key_gen, sizeof obj_key_gen);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_ALLOWED_MECHANISMS,
+                    &obj_mechanisms, sizeof obj_mechanisms);
+
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_SUBJECT,
+                    subject_start, subject_len);
+
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_SENSITIVE,
+                    &obj_sensitive, sizeof obj_sensitive);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_DECRYPT,
+                    &obj_decrypt, sizeof obj_decrypt);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_SIGN,
+                    &obj_sign, sizeof obj_sign);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_SIGN_RECOVER,
+                    &obj_sign_recover, sizeof obj_sign_recover);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_UNWRAP,
+                    &obj_unwrap, sizeof obj_unwrap);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_EXTRACTABLE,
+                    &obj_extractable, sizeof obj_extractable);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_ALWAYS_SENSITIVE,
+                    &obj_always_sensitive, sizeof obj_always_sensitive);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_NEVER_EXTRACTABLE,
+                    &obj_never_extractable, sizeof obj_never_extractable);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_WRAP_WITH_TRUSTED,
+                    &obj_wrap_with_trusted, sizeof obj_wrap_with_trusted);
+  if (!err)
+    err = attr_empty (attr, &attr_count, CKA_UNWRAP_TEMPLATE);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_ALWAYS_AUTHENTICATE,
+                    &obj_always_authenticate, sizeof obj_always_authenticate);
+
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_MODULUS,
+                    modulus_start, modulus_len);
+  if (!err)
+    err = attr_one (attr, &attr_count, CKA_PUBLIC_EXPONENT,
+                    public_exp_start, public_exp_len);
 
   if (err)
     {
index 2a2906f..57a364f 100644 (file)
@@ -2,7 +2,7 @@
    Copyright (C) 2006, 2008 g10 Code GmbH
 
    This file is part of Scute.
+
    Scute is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
@@ -51,7 +51,7 @@
 \f
 struct search
 {
-  bool found;
+  bool found;    /* Set to true if a private key object was found.  */
   cert_get_cb_t cert_get_cb;
   void *hook;
   bool with_chain;
@@ -95,7 +95,7 @@ search_cb (void *hook, struct cert *cert)
   err = scute_attr_cert (cert, &attrp, &attr_countp);
   if (err)
     return err;
-  
+
   err = (*ctx->cert_get_cb) (ctx->hook, attrp, attr_countp);
   if (err)
     {
@@ -107,12 +107,20 @@ search_cb (void *hook, struct cert *cert)
 }
 
 
-/* Create the attributes required for a new certificate object.
-   Returns allocated attributes for the certificate object in ATTRP
-   and ATTR_COUNTP, and for the private key object in PRV_ATTRP
-   and PRV_ATTR_COUNTP.  */
+/* Create the attributes required for a new certificate object.  If
+ * CERTREF is not NULL it is used to locate the cert directly from the
+ * card; if CERTREF is NULL or a cert was not found on the card, GRIP
+ * is used to find the certificate in the local key store of gpgsm.
+ *
+ * FIXME: This is all pretty questionable because our input data
+ * always comes from the card.
+ *
+ * Returns allocated attributes for the certificate object in ATTRP
+ * and ATTR_COUNTP, and for the private key object in PRV_ATTRP and
+ * PRV_ATTR_COUNTP.  */
 gpg_error_t
-scute_gpgsm_get_cert (char *grip, int no, cert_get_cb_t cert_get_cb, void *hook)
+scute_gpgsm_get_cert (char *grip, const char *certref,
+                      cert_get_cb_t cert_get_cb, void *hook)
 {
   gpg_error_t err;
   struct search search;
@@ -122,14 +130,14 @@ scute_gpgsm_get_cert (char *grip, int no, cert_get_cb_t cert_get_cb, void *hook)
   search.hook = hook;
   search.with_chain = false;
 
-  /* If the key is from the card, we might get the certificate from
-     the card as well.  */
-  if (no >= 0)
+  /* If the cert is requested from the card, we try to get it from
+   * the card as well.  */
+  if (certref)
     {
       struct cert cert;
 
       memset (&cert, '\0', sizeof (cert));
-      err = scute_agent_get_cert (no, &cert);
+      err = scute_agent_get_cert (certref, &cert);
       if (! err)
        {
 #if 0
@@ -145,7 +153,7 @@ scute_gpgsm_get_cert (char *grip, int no, cert_get_cb_t cert_get_cb, void *hook)
          return err;
        }
     }
-  
+
   search.with_chain = true;
   err = scute_gpgsm_search_certs_by_grip (grip, search_cb, &search);
   return err;
index 963fdd4..a68a38e 100644 (file)
@@ -2,7 +2,7 @@
    Copyright (C) 2006 g10 Code GmbH
 
    This file is part of Scute.
+
    Scute is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
@@ -39,7 +39,7 @@
 \f
 /* The callback type invoked for each certificate found in the
    search.  */
-typedef gpg_error_t (*cert_get_cb_t) (void *hook, 
+typedef gpg_error_t (*cert_get_cb_t) (void *hook,
                                      CK_ATTRIBUTE_PTR attrp,
                                      CK_ULONG attr_countp);
 
@@ -47,7 +47,7 @@ typedef gpg_error_t (*cert_get_cb_t) (void *hook,
    Returns allocated attributes for the certificate object in ATTRP
    and ATTR_COUNTP, and for the private key object in PRV_ATTRP
    and PRV_ATTR_COUNTP.  */
-gpg_error_t scute_gpgsm_get_cert (char *grip, int no,
+gpg_error_t scute_gpgsm_get_cert (char *grip, const char *certref,
                                  cert_get_cb_t cert_get_cb, void *hook);
 
 #endif /* GPGSM_H */
index fc69d15..5dfc94a 100644 (file)
@@ -385,7 +385,7 @@ slot_init (slot_iterator_t id)
   gpg_error_t err = 0;
   struct slot *slot = scute_table_data (slots, id);
 
-  err = scute_gpgsm_get_cert (slot->info.grip3, 3, add_object, slot);
+  err = scute_gpgsm_get_cert (slot->info.grip3, "OPENPGP.3", add_object, slot);
   if (err)
     goto init_out;
 
@@ -423,11 +423,15 @@ slots_update_slot (slot_iterator_t id)
      the session, if possible.  */
   err = scute_agent_learn (&slot->info);
 
-  /* First check if this is really an OpenPGP card.  FIXME: Should
-     probably report the error in a better way.  */
-  if (!err && (!slot->info.serialno
-              || strncmp (slot->info.serialno, "D27600012401", 12)
-              || strlen (slot->info.serialno) != 32))
+  /* First check if this is really a PIV or an OpenPGP card.  FIXME:
+   * Should probably report the error in a better way and use a
+   * generic way to identify cards without resorting to special-casing
+   * PIV cards. */
+  if (!err && slot->info.is_piv)
+    ; /* Okay, this is a PIV card.  */
+  else if (!err && (!slot->info.serialno
+                    || strncmp (slot->info.serialno, "D27600012401", 12)
+                    || strlen (slot->info.serialno) != 32))
     {
       DEBUG (DBG_INFO, "token not an OpenPGP card: %s", slot->info.serialno);
       err = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
@@ -540,12 +544,19 @@ slot_token_label (slot_iterator_t id)
 
 
 /* Get the manufacturer of the token.  */
-char *
+const char *
 slot_token_manufacturer (slot_iterator_t id)
 {
   struct slot *slot = scute_table_data (slots, id);
   unsigned int uval;
 
+  if (slot->info.is_piv)
+    {
+      if (slot->info.cardtype && !strcmp (slot->info.cardtype, "yubikey"))
+        return "Yubikey";
+      return "Unknown";
+    }
+
   /* slots_update() makes sure this is valid.  */
   uval = xtoi_2 (slot->info.serialno + 16) * 256
     + xtoi_2 (slot->info.serialno + 18);
@@ -582,13 +593,21 @@ slot_token_manufacturer (slot_iterator_t id)
 }
 
 
-/* Get the manufacturer of the token.  */
-char *
+/* Get the application used on the token.  */
+const char *
 slot_token_application (slot_iterator_t id)
 {
-  (void) id;
+  struct slot *slot = scute_table_data (slots, id);
+
+  if (!slot)
+    return "[ooops]";
+
   /* slots_update() makes sure this is correct.  */
-  return "OpenPGP";
+
+  if (slot->info.is_piv)
+    return "PIV";
+  else
+    return "OpenPGP";
 }
 
 
@@ -600,6 +619,13 @@ slot_token_serial (slot_iterator_t id, char *dst)
   struct slot *slot = scute_table_data (slots, id);
   int i;
 
+  if (slot->info.is_piv)
+    {
+      strncpy (dst, slot->info.serialno, 15);
+      dst[15] = 0;
+      return 16;
+    }
+
   /* slots_update() makes sure serialno is valid.  */
   for (i = 0; i < 8; i++)
     dst[i] = slot->info.serialno[20 + i];
@@ -616,10 +642,20 @@ slot_token_version (slot_iterator_t id, CK_BYTE *hw_major, CK_BYTE *hw_minor,
   struct slot *slot = scute_table_data (slots, id);
 
   /* slots_update() makes sure serialno is valid.  */
-  *hw_major = xtoi_2 (slot->info.serialno + 12);
-  *hw_minor = xtoi_2 (slot->info.serialno + 14);
-  *fw_major = 0;
-  *fw_minor = 0;
+  if (slot->info.is_piv)
+    {
+      *hw_major = 0;
+      *hw_minor = 0;
+      *fw_major = 0;
+      *fw_minor = 0;
+    }
+  else
+    {
+      *hw_major = xtoi_2 (slot->info.serialno + 12);
+      *hw_minor = xtoi_2 (slot->info.serialno + 14);
+      *fw_major = 0;
+      *fw_minor = 0;
+    }
 }
 
 
index 3624c55..ac34734 100644 (file)
@@ -2,7 +2,7 @@
    Copyright (C) 2006 g10 Code GmbH
 
    This file is part of Scute.
+
    Scute is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
@@ -93,10 +93,10 @@ bool slot_token_present (slot_iterator_t slot);
 char *slot_token_label (slot_iterator_t id);
 
 /* Get the manufacturer of the token.  */
-char *slot_token_manufacturer (slot_iterator_t id);
+const char *slot_token_manufacturer (slot_iterator_t id);
 
-/* Get the manufacturer of the token.  */
-char *slot_token_application (slot_iterator_t id);
+/* Get the application of the token.  */
+const char *slot_token_application (slot_iterator_t id);
 
 /* Get the serial number of the token.  Must not write more than 16
    bytes starting from DST.  */
index 38149ad..34f67d2 100644 (file)
@@ -2,7 +2,7 @@
    Copyright (C) 2006 g10 Code GmbH
 
    This file is part of Scute.
+
    Scute is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
@@ -43,7 +43,7 @@
 
 /* Copy a string into its location, with blank character padding.  */
 static inline void
-scute_copy_string (char *dest, char *src, int max_len)
+scute_copy_string (char *dest, const char *src, int max_len)
 {
   int i;
   for (i = 0; (i < max_len) && (*src != '\0'); i++)
@@ -63,7 +63,7 @@ scute_copy_string (char *dest, char *src, int max_len)
 #ifndef HAVE_TTYNAME
 /* Systems without ttyname (W32) will merely return NULL. */
 static inline char *
-ttyname (int fd) 
+ttyname (int fd)
 {
   return NULL;
 }
@@ -87,5 +87,5 @@ const char *get_gpgsm_path (void);
 const char *get_gpg_connect_agent_path (void);
 
 
-    
+
 #endif /* !SUPPORT_H */