aboutsummaryrefslogtreecommitdiffstats
path: root/g10/cardglue.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/cardglue.c')
-rw-r--r--g10/cardglue.c433
1 files changed, 433 insertions, 0 deletions
diff --git a/g10/cardglue.c b/g10/cardglue.c
index ca47cefd5..8924889dd 100644
--- a/g10/cardglue.c
+++ b/g10/cardglue.c
@@ -30,6 +30,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <stdarg.h>
#include <assert.h>
#include "options.h"
@@ -42,6 +43,17 @@
#include "i18n.h"
#include "cardglue.h"
+#include "apdu.h"
+#include "app-common.h"
+
+struct ctrl_ctx_s {
+ int (*status_cb)(void *opaque, const char *line);
+ void *status_cb_arg;
+};
+
+
+static char *default_reader_port;
+
@@ -70,11 +82,432 @@ serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
}
+/* Send a line with status information via assuan and escape all given
+ buffers. The variable elements are pairs of (char *, size_t),
+ terminated with a (NULL, 0). */
+void
+send_status_info (CTRL ctrl, const char *keyword, ...)
+{
+ va_list arg_ptr;
+ const unsigned char *value;
+ size_t valuelen;
+ char buf[950], *p;
+ size_t n;
+
+ va_start (arg_ptr, keyword);
+
+ p = buf;
+ n = 0;
+ valuelen = strlen (keyword);
+ for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, keyword++)
+ *p++ = *keyword;
+
+ while ( (value = va_arg (arg_ptr, const unsigned char *)) )
+ {
+ valuelen = va_arg (arg_ptr, size_t);
+ if (!valuelen)
+ continue; /* empty buffer */
+ if (n)
+ {
+ *p++ = ' ';
+ n++;
+ }
+ for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++)
+ {
+ if (*value < ' ' || *value == '+')
+ {
+ sprintf (p, "%%%02X", *value);
+ p += 3;
+ }
+ else if (*value == ' ')
+ *p++ = '+';
+ else
+ *p++ = *value;
+ }
+ }
+ *p = 0;
+ ctrl->status_cb (ctrl->status_cb_arg, buf);
+
+ va_end (arg_ptr);
+}
+void gcry_md_hash_buffer (int algo, void *digest,
+ const void *buffer, size_t length)
+{
+ MD_HANDLE h = md_open (algo, 0);
+ if (!h)
+ BUG();
+ md_write (h, (byte *) buffer, length);
+ md_final (h);
+ memcpy (digest, md_read (h, algo), md_digest_length (algo));
+ md_close (h);
+}
+
+
+/* This is a limited version of the one in 1.9 but it should be
+ sufficient here. */
+void
+log_printf (const char *fmt, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, fmt);
+ vfprintf (log_stream (), fmt, arg_ptr);
+ va_end (arg_ptr);
+}
+
+
+
+/* Print a hexdump of BUFFER. With TEXT of NULL print just the raw
+ dump, with TEXT just an empty string, print a trailing linefeed,
+ otherwise print an entire debug line. */
+void
+log_printhex (const char *text, const void *buffer, size_t length)
+{
+ if (text && *text)
+ log_debug ("%s ", text);
+ if (length)
+ {
+ const unsigned char *p = buffer;
+ log_printf ("%02X", *p);
+ for (length--, p++; length--; p++)
+ log_printf (" %02X", *p);
+ }
+ if (text)
+ log_printf ("\n");
+}
+
+
+
+void
+app_set_default_reader_port (const char *portstr)
+{
+ xfree (default_reader_port);
+ default_reader_port = portstr? xstrdup (portstr): NULL;
+}
+
+
+/* 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. Caller must
+ free SERIAL unless the function returns an error. */
+int
+app_get_serial_and_stamp (APP app, char **serial, time_t *stamp)
+{
+ unsigned char *buf, *p;
+ int i;
+
+ if (!app || !serial || !stamp)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ *serial = NULL;
+ *stamp = 0; /* not available */
+
+ buf = xtrymalloc (app->serialnolen * 2 + 1);
+ if (!buf)
+ return gpg_error_from_errno (errno);
+ for (p=buf, i=0; i < app->serialnolen; p +=2, i++)
+ sprintf (p, "%02X", app->serialno[i]);
+ *p = 0;
+ *serial = buf;
+ return 0;
+}
+
+
+
+
+
+
+/* Release the card info structure. */
+void
+agent_release_card_info (struct agent_card_info_s *info)
+{
+ if (!info)
+ return;
+
+ xfree (info->serialno); info->serialno = NULL;
+ xfree (info->disp_name); info->disp_name = NULL;
+ xfree (info->disp_lang); info->disp_lang = NULL;
+ xfree (info->pubkey_url); info->pubkey_url = NULL;
+ xfree (info->login_data); info->login_data = NULL;
+ info->fpr1valid = info->fpr2valid = info->fpr3valid = 0;
+}
+
+
+/* Open the current card and select the openpgp application. Return
+ an APP context handle to be used for further procesing or NULL on
+ error or if no OpenPGP application exists.*/
+static APP
+open_card (void)
+{
+ int slot;
+ int rc;
+ APP app;
+
+ slot = apdu_open_reader (default_reader_port);
+ if (slot == -1)
+ {
+ log_error ("card reader not available\n");
+ return NULL;
+ }
+
+ app = xcalloc (1, sizeof *app);
+ app->slot = slot;
+ rc = app_select_openpgp (app, &app->serialno, &app->serialnolen);
+ if (rc)
+ {
+/* apdu_close_reader (slot); */
+ log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+ xfree (app);
+ return NULL;
+ }
+
+ app->initialized = 1;
+ return app;
+}
+/* Return a new malloced string by unescaping the string S. Escaping
+ is percent escaping and '+'/space mapping. A binary nul will
+ silently be replaced by a 0xFF. Function returns NULL to indicate
+ an out of memory status. */
+static char *
+unescape_status_string (const unsigned char *s)
+{
+ char *buffer, *d;
+
+ buffer = d = xmalloc (strlen (s)+1);
+ while (*s)
+ {
+ if (*s == '%' && s[1] && s[2])
+ {
+ s++;
+ *d = xtoi_2 (s);
+ if (!*d)
+ *d = '\xff';
+ d++;
+ s += 2;
+ }
+ else if (*s == '+')
+ {
+ *d++ = ' ';
+ s++;
+ }
+ else
+ *d++ = *s++;
+ }
+ *d = 0;
+ return buffer;
+}
+
+/* Take a 20 byte hexencoded string and put it into the the provided
+ 20 byte buffer FPR in binary format. */
+static int
+unhexify_fpr (const char *hexstr, unsigned char *fpr)
+{
+ const char *s;
+ int n;
+
+ for (s=hexstr, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (*s || (n != 40))
+ return 0; /* no fingerprint (invalid or wrong length). */
+ n /= 2;
+ for (s=hexstr, n=0; *s; s += 2, n++)
+ fpr[n] = xtoi_2 (s);
+ return 1; /* okay */
+}
+
+/* Take the serial number from LINE and return it verbatim in a newly
+ allocated string. We make sure that only hex characters are
+ returned. */
+static char *
+store_serialno (const char *line)
+{
+ const char *s;
+ char *p;
+
+ for (s=line; hexdigitp (s); s++)
+ ;
+ p = xmalloc (s + 1 - line);
+ memcpy (p, line, s-line);
+ p[s-line] = 0;
+ return p;
+}
+
+
+
+static int
+learn_status_cb (void *opaque, const char *line)
+{
+ struct agent_card_info_s *parm = opaque;
+ const char *keyword = line;
+ int keywordlen;
+ int i;
+
+ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ while (spacep (line))
+ line++;
+
+ if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
+ {
+ parm->serialno = store_serialno (line);
+ }
+ else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
+ {
+ parm->disp_name = unescape_status_string (line);
+ }
+ else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen))
+ {
+ parm->disp_lang = unescape_status_string (line);
+ }
+ else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen))
+ {
+ parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0;
+ }
+ else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen))
+ {
+ parm->pubkey_url = unescape_status_string (line);
+ }
+ else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen))
+ {
+ parm->login_data = unescape_status_string (line);
+ }
+ else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen))
+ {
+ parm->sig_counter = strtoul (line, NULL, 0);
+ }
+ else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen))
+ {
+ char *p, *buf;
+
+ buf = p = unescape_status_string (line);
+ if (buf)
+ {
+ while (spacep (p))
+ p++;
+ parm->chv1_cached = atoi (p);
+ while (!spacep (p))
+ p++;
+ while (spacep (p))
+ p++;
+ for (i=0; *p && i < 3; i++)
+ {
+ parm->chvmaxlen[i] = atoi (p);
+ while (!spacep (p))
+ p++;
+ while (spacep (p))
+ p++;
+ }
+ for (i=0; *p && i < 3; i++)
+ {
+ parm->chvretry[i] = atoi (p);
+ while (!spacep (p))
+ p++;
+ while (spacep (p))
+ p++;
+ }
+ xfree (buf);
+ }
+ }
+ else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
+ {
+ int no = atoi (line);
+ while (!spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ if (no == 1)
+ parm->fpr1valid = unhexify_fpr (line, parm->fpr1);
+ else if (no == 2)
+ parm->fpr2valid = unhexify_fpr (line, parm->fpr2);
+ else if (no == 3)
+ parm->fpr3valid = unhexify_fpr (line, parm->fpr3);
+ }
+
+ return 0;
+}
+
+
+/* Return card info. */
+int
+agent_learn (struct agent_card_info_s *info)
+{
+ APP app;
+ int rc;
+ struct ctrl_ctx_s ctrl;
+ time_t stamp;
+ char *serial;
+
+ app = open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ memset (&ctrl, 0, sizeof ctrl);
+ ctrl.status_cb = learn_status_cb;
+ ctrl.status_cb_arg = info;
+
+ rc = app_get_serial_and_stamp (app, &serial, &stamp);
+ if (!rc)
+ {
+ send_status_info (&ctrl, "SERIALNO", serial, strlen(serial), NULL, 0);
+ xfree (serial);
+ rc = app->fnc.learn_status (app, &ctrl);
+ }
+
+ return rc;
+}
+
+/* Send a SETATTR command to the SCdaemon. */
+int
+agent_scd_setattr (const char *name,
+ const unsigned char *value, size_t valuelen)
+{
+
+ return gpg_error (GPG_ERR_CARD);
+}
+
+/* Send a GENKEY command to the SCdaemon. */
+int
+agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force)
+{
+
+ return gpg_error (GPG_ERR_CARD);
+}
+
+/* Send a PKSIGN command to the SCdaemon. */
+int
+agent_scd_pksign (const char *keyid, int hashalgo,
+ const unsigned char *indata, size_t indatalen,
+ char **r_buf, size_t *r_buflen)
+{
+
+ return gpg_error (GPG_ERR_CARD);
+}
+
+
+/* Send a PKDECRYPT command to the SCdaemon. */
+int
+agent_scd_pkdecrypt (const char *serialno,
+ const unsigned char *indata, size_t indatalen,
+ char **r_buf, size_t *r_buflen)
+{
+
+ return gpg_error (GPG_ERR_CARD);
+}
+
+/* Change the PIN of an OpenPGP card or reset the retry counter. */
+int
+agent_scd_change_pin (int chvno)
+{
+
+ return gpg_error (GPG_ERR_CARD);
+}
+