* configure.ac: Do not build gpg by default.
[gnupg.git] / scd / app.c
index 6ac1827..e035e9b 100644 (file)
--- a/scd/app.c
+++ b/scd/app.c
@@ -1,5 +1,5 @@
 /* app.c - Application selection.
- *     Copyright (C) 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include "tlv.h"
 
 
+/* Check wether the application NAME is allowed.  This does not mean
+   we have support for it though.  */
+static int
+is_app_allowed (const char *name)
+{
+  strlist_t l;
+
+  for (l=opt.disabled_applications; l; l = l->next)
+    if (!strcmp (l->d, name))
+      return 0; /* no */
+  return 1; /* yes */
+}
+
+
 /* If called with NAME as NULL, select the best fitting application
    and return a context; otherwise select the application with NAME
    and return a context.  SLOT identifies the reader device. Returns
-   NULL if no application was found or no card is present. */
-APP
-select_application (ctrl_t ctrl, int slot, const char *name)
+   an error code and stores NULL at R_APP if no application was found
+   or no card is present. */
+gpg_error_t
+select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
 {
   int rc;
-  APP app;
+  app_t app;
   unsigned char *result = NULL;
   size_t resultlen;
 
+  *r_app = NULL;
   app = xtrycalloc (1, sizeof *app);
   if (!app)
     {
-      rc = out_of_core ();
+      rc = gpg_error_from_errno (errno);
       log_info ("error allocating context: %s\n", gpg_strerror (rc));
-      return NULL;
+      return rc;
     }
   app->slot = slot;
 
@@ -61,38 +77,62 @@ select_application (ctrl_t ctrl, int slot, const char *name)
   if (!rc)
     rc = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
   if (!rc)
-    rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
+     rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
   if (!rc)
     {
       size_t n;
       const unsigned char *p;
 
       p = find_tlv (result, resultlen, 0x5A, &n);
-      if (p && n && n >= (resultlen - (p - result)))
+      if (p)
+        resultlen -= (p-result);
+      if (p && n > resultlen && n == 0x0d && resultlen+1 == n)
+        {
+          /* The object it does not fit into the buffer.  This is an
+             invalid encoding (or the buffer is too short.  However, I
+             have some test cards with such an invalid encoding and
+             therefore I use this ugly workaround to return something
+             I can further experiment with. */
+          log_debug ("enabling BMI testcard workaround\n");
+          n--;
+        }
+
+      if (p && n <= resultlen)
         {
           /* The GDO file is pretty short, thus we simply reuse it for
              storing the serial number. */
           memmove (result, p, n);
           app->serialno = result;
           app->serialnolen = n;
+          rc = app_munge_serialno (app);
+          if (rc)
+            goto leave;
         }
       else
         xfree (result);
       result = NULL;
     }
 
+  /* For certain error codes, there is no need to try more.  */
+  if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT)
+    goto leave;
+  
 
+  /* Figure out the application to use.  */
   rc = gpg_error (GPG_ERR_NOT_FOUND);
 
-  if (!name || !strcmp (name, "openpgp"))
+  if (rc && is_app_allowed ("openpgp") && (!name || !strcmp (name, "openpgp")))
     rc = app_select_openpgp (app);
-  if (rc && (!name || !strcmp (name, "nks")))
+  if (rc && is_app_allowed ("nks") && (!name || !strcmp (name, "nks")))
     rc = app_select_nks (app);
-  if (rc && (!name || !strcmp (name, "dinsig")))
+/*   if (rc && is_app_allowed ("p12") && (!name || !strcmp (name, "p12"))) */
+/*     rc = app_select_p12 (app); */
+  if (rc && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
     rc = app_select_dinsig (app);
   if (rc && name)
     rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
 
+ leave:
   if (rc)
     {
       if (name)
@@ -102,11 +142,12 @@ select_application (ctrl_t ctrl, int slot, const char *name)
         log_info ("no supported card application found: %s\n",
                   gpg_strerror (rc));
       xfree (app);
-      return NULL;
+      return rc;
     }
 
   app->initialized = 1;
-  return app;
+  *r_app = app;
+  return 0;
 }
 
 
@@ -116,12 +157,51 @@ release_application (app_t app)
   if (!app)
     return;
 
+  if (app->fnc.deinit)
+    {
+      app->fnc.deinit (app);
+      app->fnc.deinit = NULL;
+    }
+
   xfree (app->serialno);
   xfree (app);
 }
 
 
 
+/* The serial number may need some cosmetics.  Do it here.  This
+   function shall only be called once after a new serial number has
+   been put into APP->serialno. 
+
+   Prefixes we use:
+   
+     FF 00 00 = For serial numbers starting with an FF
+     FF 01 00 = Some german p15 cards return an empty serial number so the
+                serial number from the EF(TokeInfo is used instead.
+     
+     All other serial number not starting with FF are used as they are.
+*/
+int
+app_munge_serialno (app_t app)
+{
+  if (app->serialnolen && app->serialno[0] == 0xff)
+    { 
+      /* The serial number starts with our special prefix.  This
+         requires that we put our default prefix "FF0000" in front. */
+      unsigned char *p = xtrymalloc (app->serialnolen + 3);
+      if (!p)
+        return gpg_error (gpg_err_code_from_errno (errno));
+      memcpy (p, "\xff\0", 3);
+      memcpy (p+3, app->serialno, app->serialnolen);
+      app->serialnolen += 3;
+      xfree (app->serialno);
+      app->serialno = p;
+    }
+  return 0;
+}
+
+
+
 /* Retrieve the serial number and the time of the last update of the
    card.  The serial number is returned as a malloced string (hex
    encoded) in SERIAL and the time of update is returned in STAMP.  If
@@ -191,6 +271,32 @@ app_readcert (app_t app, const char *certid,
 }
 
 
+/* Read the key with ID KEYID.  On success a canonical encoded
+   S-expression with the public key will get stored at PK and its
+   length (for assertions) at PKLEN; the caller must release that
+   buffer. On error NULL will be stored at PK and PKLEN and an error
+   code returned.
+
+   This function might not be supported by all applications.  */
+int
+app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
+{
+  if (pk)
+    *pk = NULL;
+  if (pklen)
+    *pklen = 0;
+
+  if (!app || !keyid || !pk || !pklen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (!app->initialized)
+    return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+  if (!app->fnc.readkey)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+  return app->fnc.readkey (app, keyid, pk, pklen);
+}
+
+
 /* Perform a GETATTR operation.  */
 int 
 app_getattr (APP app, CTRL ctrl, const char *name)
@@ -199,6 +305,27 @@ app_getattr (APP app, CTRL ctrl, const char *name)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (!app->initialized)
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+
+  if (app->apptype && name && !strcmp (name, "APPTYPE"))
+    {
+      send_status_info (ctrl, "APPTYPE",
+                        app->apptype, strlen (app->apptype), NULL, 0);
+      return 0;
+    }
+  if (name && !strcmp (name, "SERIALNO"))
+    {
+      char *serial;
+      time_t stamp;
+      int rc;
+      
+      rc = app_get_serial_and_stamp (app, &serial, &stamp);
+      if (rc)
+        return rc;
+      send_status_info (ctrl, "SERIALNO", serial, strlen (serial), NULL, 0);
+      xfree (serial);
+      return 0;
+    }
+
   if (!app->fnc.getattr)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
   return app->fnc.getattr (app, ctrl, name);
@@ -362,7 +489,7 @@ app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
 
 
 /* Perform a VERIFY operation without doing anything lese.  This may
-   be used to initialze a the PION cache for long lasting other
+   be used to initialze a the PIN cache for long lasting other
    operations.  Its use is highly application dependent. */
 int 
 app_check_pin (APP app, const char *keyidstr,