Make it also work for the card.
[gnupg.git] / agent / divert-scd.c
index 455d230..49768fe 100644 (file)
@@ -15,7 +15,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
@@ -34,7 +35,7 @@
 
 
 static int
-ask_for_card (CTRL ctrl, const unsigned char *shadow_info, char **r_kid)
+ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
 {
   int rc, i;
   const unsigned char *s;
@@ -108,13 +109,6 @@ ask_for_card (CTRL ctrl, const unsigned char *shadow_info, char **r_kid)
 
       if (!rc)
         {
-          /* We better reset the SCD now.  This is kludge requred
-             because the scdaemon is currently not always able to
-             detect the presence of a card.  With a fully working
-             scdaemon this would not be required; i.e. the pkcs#15
-             support does not require it becuase OpenSC correclty
-             detects a present card. */
-          agent_reset_scd (ctrl);
           if (asprintf (&desc,
                     "%s:%%0A%%0A"
                     "  \"%.*s\"",
@@ -146,11 +140,16 @@ static int
 encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
                     unsigned char **r_val, size_t *r_len)
 {
-  byte *frame;
-  byte asn[100];
+  unsigned char *frame;
+  unsigned char asn[100];
   size_t asnlen;
 
+  *r_val = NULL;
+  *r_len = 0;
+
   asnlen = DIM(asn);
+  if (!algo || gcry_md_test_algo (algo))
+    return gpg_error (GPG_ERR_DIGEST_ALGO);
   if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
     {
       log_error ("no object identifier for algo %d\n", algo);
@@ -175,35 +174,130 @@ encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
    buf has been allocated by the caller and is of size MAXBUF which
    includes the terminating null.  The function should return an UTF-8
    string with the passphrase, the buffer may optionally be padded
-   with arbitrary characters */
+   with arbitrary characters.
+
+   INFO gets displayed as part of a generic string.  However if the
+   first character of INFO is a vertical bar all up to the next
+   verical bar are considered flags and only everything after the
+   second vertical bar gets displayed as the full prompt.
+
+   Flags:
+
+      'N' = New PIN, this requests a second prompt to repeat the the
+            PIN.  If the PIN is not correctly repeated it starts from
+            all over.
+      'A' = The PIN is an Admin PIN, SO-PIN, PUK or alike.
+
+   Example:
+
+     "|AN|Please enter the new security officer's PIN"
+     
+   The text "Please ..." will get displayed and the flags 'A' and 'N'
+   are considered.
+ */
 static int 
 getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
 {
   struct pin_entry_info_s *pi;
   int rc;
-  char *desc;
-  CTRL ctrl = opaque;
-
-  if (maxbuf < 2)
+  ctrl_t ctrl = opaque;
+  const char *ends, *s;
+  int any_flags = 0;
+  int newpin = 0;
+  const char *again_text = NULL;
+  const char *prompt = "PIN";
+
+  if (buf && maxbuf < 2)
     return gpg_error (GPG_ERR_INV_VALUE);
 
+  /* Parse the flags. */
+  if (info && *info =='|' && (ends=strchr (info+1, '|')))
+    {
+      for (s=info+1; s < ends; s++)
+        {
+          if (*s == 'A')
+            prompt = _("Admin PIN");
+          else if (*s == 'N')
+            newpin = 1;
+        }
+      info = ends+1;
+      any_flags = 1;
+    }
+  else if (info && *info == '|')
+    log_debug ("pin_cb called without proper PIN info hack\n");
+
+  /* If BUF has been passed as NULL, we are in keypad mode: The
+     callback opens the popup and immediatley returns. */
+  if (!buf)
+    {
+      if (maxbuf == 0) /* Close the pinentry. */
+        {
+          agent_popup_message_stop (ctrl);
+          rc = 0;
+        }
+      else if (maxbuf == 1)  /* Open the pinentry. */
+        {
+          rc = agent_popup_message_start (ctrl, info, NULL, NULL);
+        }
+      else
+        rc = gpg_error (GPG_ERR_INV_VALUE);
+      return rc;
+    }
 
   /* FIXME: keep PI and TRIES in OPAQUE.  Frankly this is a whole
      mess because we should call the card's verify function from the
      pinentry check pin CB. */
-  pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
+ again:
+  pi = gcry_calloc_secure (1, sizeof (*pi) + maxbuf + 10);
+  if (!pi)
+    return gpg_error_from_syserror ();
   pi->max_length = maxbuf-1;
   pi->min_digits = 0;  /* we want a real passphrase */
   pi->max_digits = 8;
   pi->max_tries = 3;
 
-  if ( asprintf (&desc, _("Please enter the PIN%s%s%s to unlock the card"), 
-                 info? " (`":"",
-                 info? info:"",
-                 info? "')":"") < 0)
-    desc = NULL;
-  rc = agent_askpin (ctrl, desc?desc:info, pi);
-  free (desc);
+  if (any_flags)
+    {
+      rc = agent_askpin (ctrl, info, prompt, again_text, pi);
+      again_text = NULL;
+      if (!rc && newpin)
+        {
+          struct pin_entry_info_s *pi2;
+          pi2 = gcry_calloc_secure (1, sizeof (*pi) + maxbuf + 10);
+          if (!pi2)
+            {
+              rc = gpg_error_from_syserror ();
+              xfree (pi);
+              return rc;
+            }
+          pi2->max_length = maxbuf-1;
+          pi2->min_digits = 0;
+          pi2->max_digits = 8;
+          pi2->max_tries = 1;
+          rc = agent_askpin (ctrl, _("Repeat this PIN"), prompt, NULL, pi2);
+          if (!rc && strcmp (pi->pin, pi2->pin))
+            {
+              again_text = N_("PIN not correctly repeated; try again");
+              xfree (pi2);
+              xfree (pi);
+              goto again;
+            }
+          xfree (pi2);
+        }
+    }
+  else
+    {
+      char *desc;
+      if ( asprintf (&desc,
+                     _("Please enter the PIN%s%s%s to unlock the card"), 
+                     info? " (`":"",
+                     info? info:"",
+                     info? "')":"") < 0)
+        desc = NULL;
+      rc = agent_askpin (ctrl, desc?desc:info, prompt, NULL, pi);
+      free (desc);
+    }
+
   if (!rc)
     {
       strncpy (buf, pi->pin, maxbuf-1);
@@ -217,33 +311,46 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
 
 
 int
-divert_pksign (CTRL ctrl, 
+divert_pksign (ctrl_t ctrl, 
                const unsigned char *digest, size_t digestlen, int algo,
                const unsigned char *shadow_info, unsigned char **r_sig)
 {
   int rc;
   char *kid;
   size_t siglen;
-  char *sigval;
-  unsigned char *data;
-  size_t ndata;
+  unsigned char *sigval = NULL;
 
   rc = ask_for_card (ctrl, shadow_info, &kid);
   if (rc)
     return rc;
 
-  rc = encode_md_for_card (digest, digestlen, algo, 
-                           &data, &ndata);
-  if (rc)
-    return rc;
+  if (algo == GCRY_MD_USER_TLS_MD5SHA1)
+    {
+      int save = ctrl->use_auth_call;
+      ctrl->use_auth_call = 1;
+      rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl,
+                              digest, digestlen, &sigval, &siglen);
+      ctrl->use_auth_call = save;
+    }
+  else
+    {
+      unsigned char *data;
+      size_t ndata;
+
+      rc = encode_md_for_card (digest, digestlen, algo, &data, &ndata);
+      if (!rc)
+        {
+          rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl,
+                                  data, ndata, &sigval, &siglen);
+          xfree (data);
+        }
+    }
 
-  rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl,
-                          data, ndata, &sigval, &siglen);
   if (!rc)
     *r_sig = sigval;
-  xfree (data);
+
   xfree (kid);
-  
+
   return rc;
 }
 
@@ -252,7 +359,7 @@ divert_pksign (CTRL ctrl,
    key identified by SHADOW_INFO and return the plaintext in an
    allocated buffer in R_BUF.  */
 int  
-divert_pkdecrypt (CTRL ctrl,
+divert_pkdecrypt (ctrl_t ctrl,
                   const unsigned char *cipher,
                   const unsigned char *shadow_info,
                   char **r_buf, size_t *r_len)
@@ -315,7 +422,7 @@ divert_pkdecrypt (CTRL ctrl,
 
 
 int  
-divert_generic_cmd (CTRL ctrl, const char *cmdline, void *assuan_context)
+divert_generic_cmd (ctrl_t ctrl, const char *cmdline, void *assuan_context)
 {
   return agent_card_scd (ctrl, cmdline, getpin_cb, ctrl, assuan_context);
 }