gpg: Show private DO information in the card status.
[gnupg.git] / g10 / call-agent.c
index f5c943d..a98a177 100644 (file)
@@ -343,6 +343,9 @@ start_agent (ctrl_t ctrl, int for_card)
             case GPG_ERR_NO_SCDAEMON:
               write_status_text (STATUS_CARDCTRL, "6");
               break;
             case GPG_ERR_NO_SCDAEMON:
               write_status_text (STATUS_CARDCTRL, "6");
               break;
+            case GPG_ERR_OBJ_TERM_STATE:
+              write_status_text (STATUS_CARDCTRL, "7");
+              break;
             default:
               write_status_text (STATUS_CARDCTRL, "4");
               log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
             default:
               write_status_text (STATUS_CARDCTRL, "4");
               log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
@@ -468,6 +471,8 @@ get_serialno_cb (void *opaque, const char *line)
 void
 agent_release_card_info (struct agent_card_info_s *info)
 {
 void
 agent_release_card_info (struct agent_card_info_s *info)
 {
+  int i;
+
   if (!info)
     return;
 
   if (!info)
     return;
 
@@ -479,8 +484,14 @@ agent_release_card_info (struct agent_card_info_s *info)
   xfree (info->login_data); info->login_data = NULL;
   info->cafpr1valid = info->cafpr2valid = info->cafpr3valid = 0;
   info->fpr1valid = info->fpr2valid = info->fpr3valid = 0;
   xfree (info->login_data); info->login_data = NULL;
   info->cafpr1valid = info->cafpr2valid = info->cafpr3valid = 0;
   info->fpr1valid = info->fpr2valid = info->fpr3valid = 0;
+  for (i=0; i < DIM(info->private_do); i++)
+    {
+      xfree (info->private_do[i]);
+      info->private_do[i] = NULL;
+    }
 }
 
 }
 
+
 static gpg_error_t
 learn_status_cb (void *opaque, const char *line)
 {
 static gpg_error_t
 learn_status_cb (void *opaque, const char *line)
 {
@@ -586,6 +597,8 @@ learn_status_cb (void *opaque, const char *line)
                     parm->extcap.ki = abool;
                   else if (!strcmp (p, "aac"))
                     parm->extcap.aac = abool;
                     parm->extcap.ki = abool;
                   else if (!strcmp (p, "aac"))
                     parm->extcap.aac = abool;
+                  else if (!strcmp (p, "si"))
+                    parm->status_indicator = strtoul (p2, NULL, 10);
                 }
             }
           xfree (buf);
                 }
             }
           xfree (buf);
@@ -645,6 +658,14 @@ learn_status_cb (void *opaque, const char *line)
           parm->key_attr[keyno].nbits = nbits;
         }
     }
           parm->key_attr[keyno].nbits = nbits;
         }
     }
+  else if (keywordlen == 12 && !memcmp (keyword, "PRIVATE-DO-", 11)
+           && strchr("1234", keyword[11]))
+    {
+      int no = keyword[11] - '1';
+      assert (no >= 0 && no <= 3);
+      xfree (parm->private_do[no]);
+      parm->private_do[no] = unescape_status_string (line);
+    }
 
   return 0;
 }
 
   return 0;
 }
@@ -655,7 +676,11 @@ agent_scd_learn (struct agent_card_info_s *info)
 {
   int rc;
   struct default_inq_parm_s parm;
 {
   int rc;
   struct default_inq_parm_s parm;
+  struct agent_card_info_s dummyinfo;
 
 
+  if (!info)
+    info = &dummyinfo;
+  memset (info, 0, sizeof *info);
   memset (&parm, 0, sizeof parm);
 
   rc = start_agent (NULL, 1);
   memset (&parm, 0, sizeof parm);
 
   rc = start_agent (NULL, 1);
@@ -675,36 +700,72 @@ agent_scd_learn (struct agent_card_info_s *info)
     return rc;
 
   parm.ctx = agent_ctx;
     return rc;
 
   parm.ctx = agent_ctx;
-  memset (info, 0, sizeof *info);
-  rc = assuan_transact (agent_ctx, "SCD LEARN --force",
+  rc = assuan_transact (agent_ctx, "LEARN --sendinfo",
                         dummy_data_cb, NULL, default_inq_cb, &parm,
                         learn_status_cb, info);
   /* Also try to get the key attributes.  */
   if (!rc)
     agent_scd_getattr ("KEY-ATTR", info);
 
                         dummy_data_cb, NULL, default_inq_cb, &parm,
                         learn_status_cb, info);
   /* Also try to get the key attributes.  */
   if (!rc)
     agent_scd_getattr ("KEY-ATTR", info);
 
+  if (info == &dummyinfo)
+    agent_release_card_info (info);
+
   return rc;
 }
 
 
   return rc;
 }
 
 
-/* Call the agent to learn about the current smartcard.  This is
-   currently only used to have the agent create the shadow key.  */
+/* Send an APDU to the current card.  On success the status word is
+   stored at R_SW.  With HEXAPDU being NULL only a RESET command is
+   send to scd.  With HEXAPDU being the string "undefined" the command
+   "SERIALNO undefined" is send to scd. */
 gpg_error_t
 gpg_error_t
-agent_learn (void)
+agent_scd_apdu (const char *hexapdu, unsigned int *r_sw)
 {
   gpg_error_t err;
 {
   gpg_error_t err;
-  struct default_inq_parm_s parm;
 
 
-  memset (&parm, 0, sizeof parm);
-
-  err = start_agent (NULL, 1);
+  /* Start the agent but not with the card flag so that we do not
+     autoselect the openpgp application.  */
+  err = start_agent (NULL, 0);
   if (err)
     return err;
 
   if (err)
     return err;
 
-  parm.ctx = agent_ctx;
-  err = assuan_transact (agent_ctx, "LEARN",
-                         dummy_data_cb, NULL, default_inq_cb, &parm,
-                         NULL, NULL);
+  if (!hexapdu)
+    {
+      err = assuan_transact (agent_ctx, "SCD RESET",
+                             NULL, NULL, NULL, NULL, NULL, NULL);
+
+    }
+  else if (!strcmp (hexapdu, "undefined"))
+    {
+      err = assuan_transact (agent_ctx, "SCD SERIALNO undefined",
+                             NULL, NULL, NULL, NULL, NULL, NULL);
+    }
+  else
+    {
+      char line[ASSUAN_LINELENGTH];
+      membuf_t mb;
+      unsigned char *data;
+      size_t datalen;
+
+      init_membuf (&mb, 256);
+
+      snprintf (line, DIM(line)-1, "SCD APDU %s", hexapdu);
+      err = assuan_transact (agent_ctx, line,
+                             membuf_data_cb, &mb, NULL, NULL, NULL, NULL);
+      if (!err)
+        {
+          data = get_membuf (&mb, &datalen);
+          if (!data)
+            err = gpg_error_from_syserror ();
+          else if (datalen < 2) /* Ooops */
+            err = gpg_error (GPG_ERR_CARD);
+          else
+            {
+              *r_sw = (data[datalen-2] << 8) | data[datalen-1];
+            }
+          xfree (data);
+        }
+    }
 
   return err;
 }
 
   return err;
 }