* tdbio.h, tdbio.c (tdbio_read_record, tdbio_write_record): Store trust
[gnupg.git] / scd / command.c
index e172484..aa410a6 100644 (file)
 #include <string.h>
 #include <ctype.h>
 #include <unistd.h>
+#include <ksba.h>
 
 #include "scdaemon.h"
 #include "../assuan/assuan.h"
 
+/* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */
+#define MAXLEN_PIN 100
+
 #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
 
 /* Data used to associate an Assuan context with local server data */
@@ -147,7 +151,17 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
      S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id>
 
    If there is no certificate yet stored on the card a single "X" is
-   returned as the keygrip.
+   returned as the keygrip.  In addition to the keypair info, information
+   about all certificates stored on the card is also returned:
+
+     S CERTINFO <certtype> <hexstring_with_id>
+
+   Where CERTINFO is a number indicating the type of certificate:
+      0   := Unknown
+      100 := Regular X.509 cert
+      101 := Trusted X.509 cert
+      102 := Useful X.509 cert
+
 
 */
 static int
@@ -205,15 +219,41 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
     free (serial_and_stamp);
   }
 
+  /* Return information about the certificates. */
+  for (idx=0; !rc; idx++)
+    {
+      char *certid;
+      int certtype;
+
+      rc = card_enum_certs (ctrl->card_ctx, idx, &certid, &certtype);
+      if (!rc)
+        {
+          char *buf;
+
+          buf = xtrymalloc (40 + 1 + strlen (certid) + 1);
+          if (!buf)
+            rc = GNUPG_Out_Of_Core;
+          else
+            {
+              sprintf (buf, "%d %s", certtype, certid);
+              assuan_write_status (ctx, "CERTINFO", buf);
+              xfree (buf);
+            }
+        }
+      xfree (certid);
+    }
+  if (rc == -1)
+    rc = 0;
+
+
+  /* Return information about the keys. */
   for (idx=0; !rc; idx++)
     {
       unsigned char keygrip[20];
-      unsigned char *keyid;
-      size_t nkeyid;
+      char *keyid;
       int no_cert = 0;
 
-      rc = card_enum_keypairs (ctrl->card_ctx, idx, 
-                               keygrip, &keyid, &nkeyid);
+      rc = card_enum_keypairs (ctrl->card_ctx, idx, keygrip, &keyid);
       if (rc == GNUPG_Missing_Certificate && keyid)
         {
           /* this does happen with an incomplete personalized
@@ -228,7 +268,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
         {
           char *buf, *p;
 
-          buf = p = xtrymalloc (40+1+9+2*nkeyid+1);
+          buf = p = xtrymalloc (40 + 1 + strlen (keyid) + 1);
           if (!buf)
             rc = GNUPG_Out_Of_Core;
           else
@@ -243,11 +283,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
                     sprintf (p, "%02X", keygrip[i]);
                 }
               *p++ = ' ';
-              /* fixme: we need to get the pkcs-15 DF from the card function */
-              p = stpcpy (p, "3F005015.");
-              for (i=0; i < nkeyid; i++, p += 2)
-                sprintf (p, "%02X", keyid[i]);
-              *p = 0;
+              strcpy (p, keyid);
               assuan_write_status (ctx, "KEYPAIRINFO", buf);
               xfree (buf);
             }
@@ -294,6 +330,65 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
 }
 
 
+/* READKEY <hexified_certid>
+
+   Return the public key for the given cert or key ID as an standard
+   S-Expression.  */
+static int
+cmd_readkey (ASSUAN_CONTEXT ctx, char *line)
+{
+  CTRL ctrl = assuan_get_pointer (ctx);
+  int rc;
+  unsigned char *cert = NULL;
+  size_t ncert, n;
+  KsbaCert kc = NULL;
+  KsbaSexp p;
+
+  if ((rc = open_card (ctrl)))
+    return rc;
+
+  rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
+  if (rc)
+    {
+      log_error ("card_read_cert failed: %s\n", gnupg_strerror (rc));
+      goto leave;
+    }
+      
+  kc = ksba_cert_new ();
+  if (!kc)
+    {
+      xfree (cert);
+      rc = GNUPG_Out_Of_Core;
+      goto leave;
+    }
+  rc = ksba_cert_init_from_mem (kc, cert, ncert);
+  if (rc)
+    {
+      log_error ("failed to parse the certificate: %s\n", ksba_strerror (rc));
+      rc = map_ksba_err (rc);
+      goto leave;
+    }
+
+  p = ksba_cert_get_public_key (kc);
+  if (!p)
+    {
+      rc = GNUPG_No_Public_Key;
+      goto leave;
+    }
+
+  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+  rc = assuan_send_data (ctx, p, n);
+  rc = map_assuan_err (rc);
+  xfree (p);
+
+
+ leave:
+  ksba_cert_release (kc);
+  xfree (cert);
+  return map_to_assuan_status (rc);
+}
+
+
 \f
 
 /* SETDATA <hexstring> 
@@ -329,6 +424,40 @@ cmd_setdata (ASSUAN_CONTEXT ctx, char *line)
 
 
 
+static int 
+pin_cb (void *opaque, const char *info, char **retstr)
+{
+  ASSUAN_CONTEXT ctx = opaque;
+  char *command;
+  int rc;
+  char *value;
+  size_t valuelen;
+
+  *retstr = NULL;
+  log_debug ("asking for PIN '%s'\n", info);
+
+  rc = asprintf (&command, "NEEDPIN %s", info);
+  if (rc < 0)
+    return GNUPG_Out_Of_Core;
+
+  /* FIXME: Write an inquire function which returns the result in
+     secure memory */
+  rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN); 
+  free (command);  
+  if (rc)
+    return map_assuan_err (rc);
+
+  if (!valuelen || value[valuelen-1])
+    {
+      /* We require that the returned value is an UTF-8 string */
+      xfree (value);
+      return GNUPG_Invalid_Response;
+    }
+  *retstr = value;
+  return 0;
+}
+
+
 /* PKSIGN <hexified_id>
 
  */
@@ -337,11 +466,75 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
 {
   CTRL ctrl = assuan_get_pointer (ctx);
   int rc;
+  void *outdata;
+  size_t outdatalen;
+  char *keyidstr;
 
   if ((rc = open_card (ctrl)))
     return rc;
 
+  /* We have to use a copy of the key ID because the function may use
+     the pin_cb which in turn uses the assuan line buffer and thus
+     overwriting the original line with the keyid */
+  keyidstr = strdup (line);
+  if (!keyidstr)
+    return ASSUAN_Out_Of_Core;
+  rc = card_sign (ctrl->card_ctx,
+                  keyidstr, GCRY_MD_SHA1,
+                  pin_cb, ctx,
+                  ctrl->in_data.value, ctrl->in_data.valuelen,
+                  &outdata, &outdatalen);
+  free (keyidstr);
+  if (rc)
+    {
+      log_error ("card_sign failed: %s\n", gnupg_strerror (rc));
+    }
+  else
+    {
+      rc = assuan_send_data (ctx, outdata, outdatalen);
+      xfree (outdata);
+      if (rc)
+        return rc; /* that is already an assuan error code */
+    }
+
+  return map_to_assuan_status (rc);
+}
+
+/* PKDECRYPT <hexified_id>
+
+ */
+static int
+cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
+{
+  CTRL ctrl = assuan_get_pointer (ctx);
+  int rc;
+  void *outdata;
+  size_t outdatalen;
+  char *keyidstr;
+
+  if ((rc = open_card (ctrl)))
+    return rc;
 
+  keyidstr = strdup (line);
+  if (!keyidstr)
+    return ASSUAN_Out_Of_Core;
+  rc = card_decipher (ctrl->card_ctx,
+                      keyidstr, 
+                      pin_cb, ctx,
+                      ctrl->in_data.value, ctrl->in_data.valuelen,
+                      &outdata, &outdatalen);
+  free (keyidstr);
+  if (rc)
+    {
+      log_error ("card_create_signature failed: %s\n", gnupg_strerror (rc));
+    }
+  else
+    {
+      rc = assuan_send_data (ctx, outdata, outdatalen);
+      xfree (outdata);
+      if (rc)
+        return rc; /* that is already an assuan error code */
+    }
 
   return map_to_assuan_status (rc);
 }
@@ -361,8 +554,10 @@ register_commands (ASSUAN_CONTEXT ctx)
     { "SERIALNO", 0, cmd_serialno },
     { "LEARN", 0, cmd_learn },
     { "READCERT", 0, cmd_readcert },
+    { "READKEY", 0,  cmd_readkey },
     { "SETDATA", 0,  cmd_setdata },
     { "PKSIGN", 0,   cmd_pksign },
+    { "PKDECRYPT", 0,cmd_pkdecrypt },
     { "",     ASSUAN_CMD_INPUT, NULL }, 
     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
     { NULL }