Some experimental code - don't use it.
authorWerner Koch <wk@gnupg.org>
Mon, 18 Feb 2002 20:47:17 +0000 (20:47 +0000)
committerWerner Koch <wk@gnupg.org>
Mon, 18 Feb 2002 20:47:17 +0000 (20:47 +0000)
scd/Makefile.am
scd/card.c [new file with mode: 0644]
scd/command.c
scd/scdaemon.h

index 4e4cd65..dca4f3b 100644 (file)
@@ -25,7 +25,7 @@ LDFLAGS = @LDFLAGS@
 
 scdaemon_SOURCES = \
        scdaemon.c scdaemon.h \
-       command.c 
+       command.c card.c
 
 
 scdaemon_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a  \
diff --git a/scd/card.c b/scd/card.c
new file mode 100644 (file)
index 0000000..3702ae3
--- /dev/null
@@ -0,0 +1,221 @@
+/* card.c - SCdaemon card functions
+ *     Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <opensc-pkcs15.h>
+
+#include "scdaemon.h"
+
+
+
+struct card_ctx_s {
+  int reader;   /* used reader */
+  struct sc_context *ctx;
+  struct sc_card *scard;
+  struct sc_pkcs15_card *p15card; /* only if there is a pkcs15 application */
+  
+};
+
+/* Map the SC error codes to the GNUPG ones */
+static int
+map_sc_err (int rc)
+{
+  switch (rc)
+    {
+    case 0: rc = 0; break;
+    case SC_ERROR_CMD_TOO_SHORT:         rc = GNUPG_Card_Error; break;
+    case SC_ERROR_CMD_TOO_LONG:          rc = GNUPG_Card_Error; break;
+    case SC_ERROR_NOT_SUPPORTED:         rc = GNUPG_Not_Supported; break;
+    case SC_ERROR_TRANSMIT_FAILED:       rc = GNUPG_Card_Error; break;
+    case SC_ERROR_FILE_NOT_FOUND:        rc = GNUPG_Card_Error; break;
+    case SC_ERROR_INVALID_ARGUMENTS:     rc = GNUPG_Card_Error; break;
+    case SC_ERROR_PKCS15_APP_NOT_FOUND:  rc = GNUPG_No_PKCS15_App; break;
+    case SC_ERROR_REQUIRED_PARAMETER_NOT_FOUND: rc = GNUPG_Card_Error; break;
+    case SC_ERROR_OUT_OF_MEMORY:         rc = GNUPG_Out_Of_Core; break;
+    case SC_ERROR_NO_READERS_FOUND:      rc = GNUPG_Card_Error; break;
+    case SC_ERROR_OBJECT_NOT_VALID:      rc = GNUPG_Card_Error; break;
+    case SC_ERROR_ILLEGAL_RESPONSE:      rc = GNUPG_Card_Error; break;
+    case SC_ERROR_PIN_CODE_INCORRECT:    rc = GNUPG_Card_Error; break;
+    case SC_ERROR_SECURITY_STATUS_NOT_SATISFIED: rc = GNUPG_Card_Error; break;
+    case SC_ERROR_CONNECTING_TO_RES_MGR: rc = GNUPG_Card_Error; break;
+    case SC_ERROR_INVALID_ASN1_OBJECT:   rc = GNUPG_Card_Error; break;
+    case SC_ERROR_BUFFER_TOO_SMALL:      rc = GNUPG_Card_Error; break;
+    case SC_ERROR_CARD_NOT_PRESENT:      rc = GNUPG_Card_Not_Present; break;
+    case SC_ERROR_RESOURCE_MANAGER:      rc = GNUPG_Card_Error; break;
+    case SC_ERROR_CARD_REMOVED:          rc = GNUPG_Card_Removed; break;
+    case SC_ERROR_INVALID_PIN_LENGTH:    rc = GNUPG_Card_Error; break;
+    case SC_ERROR_UNKNOWN_SMARTCARD:     rc = GNUPG_Card_Error; break;
+    case SC_ERROR_UNKNOWN_REPLY:         rc = GNUPG_Card_Error; break;
+    case SC_ERROR_OBJECT_NOT_FOUND:      rc = GNUPG_Card_Error; break;
+    case SC_ERROR_CARD_RESET:            rc = GNUPG_Card_Reset; break;
+    case SC_ERROR_ASN1_OBJECT_NOT_FOUND: rc = GNUPG_Card_Error; break;
+    case SC_ERROR_ASN1_END_OF_CONTENTS:  rc = GNUPG_Card_Error; break;
+    case SC_ERROR_TOO_MANY_OBJECTS:      rc = GNUPG_Card_Error; break;
+    case SC_ERROR_INVALID_CARD:          rc = GNUPG_Invalid_Card; break;
+    case SC_ERROR_WRONG_LENGTH:          rc = GNUPG_Card_Error; break;
+    case SC_ERROR_RECORD_NOT_FOUND:      rc = GNUPG_Card_Error; break;
+    case SC_ERROR_INTERNAL:              rc = GNUPG_Card_Error; break;
+    default: rc = GNUPG_Card_Error; break;
+    }
+  return rc;
+}
+
+
+/* Create a new context for the card and figures out some basic
+   information of the card.  Detects whether a PKCS_15 application is
+   stored.
+
+   Common errors: GNUPG_Card_Not_Present */
+int
+card_open (CARD *rcard)
+{
+  CARD card;
+  int rc;
+
+  card = xtrycalloc (1, sizeof *card);
+  if (!card)
+    return GNUPG_Out_Of_Core;
+  card->reader = 0;
+  
+  rc = sc_establish_context (&card->ctx);
+  if (rc)
+    {
+      log_error ("failed to establish SC context: %s\n", sc_strerror (rc));
+      rc = map_sc_err (rc);
+      goto leave;
+    }
+  if (card->reader >= card->ctx->reader_count)
+    {
+      log_error ("no card reader available\n");
+      rc = GNUPG_Card_Error;
+    }
+  card->ctx->error_file = log_get_stream ();
+  card->ctx->debug_file = log_get_stream ();
+  if (sc_detect_card (card->ctx, card->reader) != 1)
+    {
+      rc = GNUPG_Card_Not_Present;
+      goto leave;
+    }
+
+  rc = sc_connect_card (card->ctx, card->reader, &card->scard);
+  if (rc)
+    {
+      log_error ("failed to connect card in reader %d: %s\n",
+                 card->reader, sc_strerror (rc));
+      rc = map_sc_err (rc);
+      goto leave;
+    }
+  if (opt.verbose)
+    log_info ("connected to card in reader %d using driver `%s'\n",
+              card->reader, card->scard->driver->name);
+
+  rc = sc_lock (card->scard);
+  if (rc)
+    {
+      log_error ("can't lock card in reader %d: %s\n",
+                 card->reader, sc_strerror (rc));
+      rc = map_sc_err (rc);
+      goto leave;
+    }
+
+  rc = sc_pkcs15_bind (card->scard, &card->p15card);
+  if (rc == SC_ERROR_PKCS15_APP_NOT_FOUND)
+    rc = 0; /* okay */
+  else if (rc)
+    {
+      log_error ("binding of existing PKCS-15 failed in reader %d: %s\n",
+                 card->reader, sc_strerror (rc));
+      rc = map_sc_err (rc);
+      goto leave;
+    }
+    
+ leave:
+  if (rc)
+    card_close (card);
+  else
+    *rcard = card;
+  return rc;
+}
+
+
+/* Close a card and release all resources */
+void
+card_close (CARD card)
+{
+  if (card)
+    {
+      if (card->p15card)
+        {
+          sc_pkcs15_unbind (card->p15card);
+          card->p15card = NULL;
+        }
+      if (card->scard)
+        {
+          sc_unlock (card->scard);
+          sc_disconnect_card (card->scard);
+          card->scard = NULL;
+       }
+      if (card->ctx)
+        {
+          sc_destroy_context (card->ctx);
+          card->ctx = NULL;
+        }
+      xfree (card);
+    }      
+}
+
+/* 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 no update time is available the returned value is 0.  The serial
+   is mandatory for a PKCS_15 application and an error will be
+   returned if this value is not availbale.  For non-PKCS-15 cards a
+   serial number is constructed by other means. Caller must free
+   SERIAL unless the fucntion returns an error. */
+int 
+card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp)
+{
+  char *s;
+
+  if (!card || !serial || !stamp)
+    return GNUPG_Invalid_Value;
+
+  *serial = NULL;
+  *stamp = 0; /* not available */
+  if (!card->p15card)
+    { /* fixme: construct a serial number */
+      /* We should lookup the iso 7812-1 and 8583-3 - argh ISO practice is
+         suppressing innovation - IETF rules! */
+      return GNUPG_No_PKCS15_App;
+    }
+  s = card->p15card->serial_number;
+  if (!s || !hexdigitp (s) )
+    return GNUPG_Invalid_Card; /* the serial number is mandatory */
+  *serial = xstrdup (s);
+  if (!*serial)
+    return GNUPG_Out_Of_Core;
+  return 0;
+}
index b2631bb..10b9006 100644 (file)
@@ -18,9 +18,6 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  */
 
-/* FIXME: we should not use the default assuan buffering but setup
-   some buffering in secure mempory to protect session keys etc. */
-
 #include <config.h>
 #include <errno.h>
 #include <stdio.h>
@@ -40,32 +37,103 @@ struct server_local_s {
 };
 
 
+/* Check whether the option NAME appears in LINE */
+static int
+has_option (const char *line, const char *name)
+{
+  const char *s;
+  int n = strlen (name);
+
+  s = strstr (line, name);
+  return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
+}
+
+
 
 \f
+/* Note, that this reset_notify is also used for cleanup purposes. */
 static void
 reset_notify (ASSUAN_CONTEXT ctx)
 {
-/*    CTRL ctrl = assuan_get_pointer (ctx); */
-
+  CTRL ctrl = assuan_get_pointer (ctx); 
 
+  if (ctrl->card_ctx)
+    {
+      card_close (ctrl->card_ctx);
+      ctrl->card_ctx = NULL;
+    }
 }
 
 
 static int
 option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
 {
-/*    CTRL ctrl = assuan_get_pointer (ctx); */
-
-/*    if (!strcmp (key, "foo")) */
-/*      { */
-/*      } */
-/*    else */
-/*      return ASSUAN_Invalid_Option; */
-
   return 0;
 }
 
 
+/* LEARN [--force]
+
+   Learn all useful information of the currently inserted card.  When
+   used without the force options, the command might to an INQUIRE
+   like this:
+
+      INQUIRE KNOWNCARDP <hexstring_with_serialNumber> <timestamp>
+
+   The client should just send an "END" if the processing should go on
+   or a "CANCEL" to force the function to terminate with a Cancel
+   error message.
+
+*/
+static int
+cmd_learn (ASSUAN_CONTEXT ctx, char *line)
+{
+  CTRL ctrl = assuan_get_pointer (ctx);
+  int rc = 0;
+
+  /* if this is the first command issued for a new card, open the card and 
+     and create a context */
+  if (!ctrl->card_ctx)
+    {
+      rc = card_open (&ctrl->card_ctx);
+      if (rc)
+        return map_to_assuan_status (rc);
+    }
+
+  /* Unless the force option is used we try a shortcut by identifying
+     the card using a serial number and inquiring the client with
+     that. The client may choose to cancel the operation if he already
+     knows about this card */
+  if (!has_option (line, "--force"))
+    {
+      char *serial;
+      time_t stamp;
+      char *command;
+
+      rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
+      if (rc)
+        return map_to_assuan_status (rc);
+
+      rc = asprintf (&command, "KNOWNCARDP %s %lu",
+                     serial, (unsigned long)stamp);
+      xfree (serial);
+      if (rc < 0)
+        return ASSUAN_Out_Of_Core;
+      rc = 0;
+      rc = assuan_inquire (ctx, command, NULL, NULL, 0); 
+      free (command);  /* (must use standard free here) */
+      if (rc)
+        {
+          if (rc != ASSUAN_Canceled)
+            log_error ("inquire KNOWNCARDP failed: %s\n",
+                       assuan_strerror (rc));
+          return rc; 
+        }
+      /* not canceled, so we have to proceeed */
+    }
+
+  return map_to_assuan_status (rc);
+}
 
 
 
@@ -81,6 +149,7 @@ register_commands (ASSUAN_CONTEXT ctx)
     int cmd_id;
     int (*handler)(ASSUAN_CONTEXT, char *line);
   } table[] = {
+    { "LEARN", 0, cmd_learn },
     { "",     ASSUAN_CMD_INPUT, NULL }, 
     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
     { NULL }
@@ -168,7 +237,7 @@ scd_command_handler (int listen_fd)
           continue;
         }
     }
-
+  reset_notify (ctx); /* used for cleanup */
 
   assuan_deinit_server (ctx);
 }
index 76d00b1..7d43dc1 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef SCDAEMON_H
 #define SCDAEMON_H
 
+#include <time.h>
 #include <gcrypt.h>
 #include "../common/util.h"
 #include "../common/errors.h"
@@ -55,13 +56,15 @@ struct {
 #define DBG_ASSUAN  (opt.debug & DBG_ASSUAN_VALUE)
 
 struct server_local_s;
+struct card_ctx_s;
 
 struct server_control_s {
   struct server_local_s *server_local;
+  struct card_ctx_s *card_ctx;
 
 };
 typedef struct server_control_s *CTRL;
-
+typedef struct card_ctx_s *CARD;
 
 /*-- scdaemon.c --*/
 void scd_exit (int rc);
@@ -70,5 +73,11 @@ void scd_init_default_ctrl (CTRL ctrl);
 /*-- command.c --*/
 void scd_command_handler (int);
 
+/*-- card.c --*/
+int card_open (CARD *rcard);
+void card_close (CARD card);
+int card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp);
+
+
 
 #endif /*SCDAEMON_H*/