aboutsummaryrefslogtreecommitdiffstats
path: root/scd/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'scd/command.c')
-rw-r--r--scd/command.c1034
1 files changed, 1034 insertions, 0 deletions
diff --git a/scd/command.c b/scd/command.c
new file mode 100644
index 000000000..c53af84f9
--- /dev/null
+++ b/scd/command.c
@@ -0,0 +1,1034 @@
+/* command.c - SCdaemon command handler
+ * Copyright (C) 2001, 2002, 2003 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 <ctype.h>
+#include <unistd.h>
+#include <ksba.h>
+
+#include <assuan.h>
+
+#include "scdaemon.h"
+#include "app-common.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 */
+struct server_local_s {
+ ASSUAN_CONTEXT assuan_ctx;
+};
+
+
+/* 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)));
+}
+
+
+
+
+/* Note, that this reset_notify is also used for cleanup purposes. */
+static void
+reset_notify (ASSUAN_CONTEXT ctx)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ if (ctrl->card_ctx)
+ {
+ card_close (ctrl->card_ctx);
+ ctrl->card_ctx = NULL;
+ xfree (ctrl->in_data.value);
+ ctrl->in_data.value = NULL;
+ }
+ if (ctrl->app_ctx)
+ {
+ /* FIXME: close the application. */
+ xfree (ctrl->app_ctx);
+ ctrl->app_ctx = NULL;
+ }
+}
+
+
+static int
+option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
+{
+ return 0;
+}
+
+
+/* If the card has not yet been opened, do it. Note that this
+ function returns an Assuan error, so don't map the error a second
+ time */
+static AssuanError
+open_card (CTRL ctrl)
+{
+ if (ctrl->app_ctx)
+ return 0; /* Already initialized for one specific application. */
+ if (ctrl->card_ctx)
+ return 0; /* Already initialized using a card context. */
+
+ ctrl->app_ctx = select_application ();
+ if (!ctrl->app_ctx)
+ { /* No application found - fall back to old mode. */
+ int rc = card_open (&ctrl->card_ctx);
+ if (rc)
+ return map_to_assuan_status (rc);
+ }
+ return 0;
+}
+
+
+/* Do the percent and plus/space unescaping in place and return tghe
+ length of the valid buffer. */
+static size_t
+percent_plus_unescape (unsigned char *string)
+{
+ unsigned char *p = string;
+ size_t n = 0;
+
+ while (*string)
+ {
+ if (*string == '%' && string[1] && string[2])
+ {
+ string++;
+ *p++ = xtoi_2 (string);
+ n++;
+ string+= 2;
+ }
+ else if (*string == '+')
+ {
+ *p++ = ' ';
+ n++;
+ string++;
+ }
+ else
+ {
+ *p++ = *string++;
+ n++;
+ }
+ }
+
+ return n;
+}
+
+
+
+/* SERIALNO
+
+ Return the serial number of the card using a status reponse. This
+ functon should be used to check for the presence of a card.
+
+ This function is special in that it can be used to reset the card.
+ Most other functions will return an error when a card change has
+ been detected and the use of this function is therefore required.
+
+ Background: We want to keep the client clear of handling card
+ changes between operations; i.e. the client can assume that all
+ operations are done on the same card unless he calls this function.
+ */
+static int
+cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc = 0;
+ char *serial_and_stamp;
+ char *serial;
+ time_t stamp;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (ctrl->app_ctx)
+ rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
+ else
+ rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
+ if (rc)
+ return map_to_assuan_status (rc);
+ rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
+ xfree (serial);
+ if (rc < 0)
+ return ASSUAN_Out_Of_Core;
+ rc = 0;
+ assuan_write_status (ctx, "SERIALNO", serial_and_stamp);
+ free (serial_and_stamp);
+ return 0;
+}
+
+
+
+
+/* LEARN [--force]
+
+ Learn all useful information of the currently inserted card. When
+ used without the force options, the command might do 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. The response of this command is a list of status
+ lines formatted as this:
+
+ S APPTYPE <apptype>
+
+ This returns the type of the application, currently the strings:
+
+ P15 = PKCS-15 structure used
+ DINSIG = DIN SIG
+ OPENPGP = OpenPGP card
+
+ are implemented. These strings are aliases for the AID
+
+ 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. In addition to the keypair info, information
+ about all certificates stored on the card is also returned:
+
+ S CERTINFO <certtype> <hexstring_with_id>
+
+ Where CERTTYPE 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
+
+ For certain cards, more information will be returned:
+
+ S KEY-FPR <no> <hexstring>
+
+ For OpenPGP cards this returns the stored fingerprints of the
+ keys. This can be used check whether a key is available on the
+ card. NO may be 1, 2 or 3.
+
+ S CA-FPR <no> <hexstring>
+
+ Similar to above, these are the fingerprints of keys assumed to be
+ ultimately trusted.
+
+ S DISP-NAME <name_of_card_holder>
+
+ The name of the card holder as stored on the card; percent
+ aescaping takes place, spaces are encoded as '+'
+
+ S PUBKEY-URL <url>
+
+ The URL to be used for locating the entire public key.
+
+*/
+static int
+cmd_learn (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc = 0;
+ int idx;
+
+ if ((rc = open_card (ctrl)))
+ return 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 */
+ {
+ char *serial_and_stamp;
+ char *serial;
+ time_t stamp;
+
+ if (ctrl->app_ctx)
+ rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
+ else
+ rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
+ if (rc)
+ return map_to_assuan_status (rc);
+ rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
+ xfree (serial);
+ if (rc < 0)
+ return ASSUAN_Out_Of_Core;
+ rc = 0;
+ assuan_write_status (ctx, "SERIALNO", serial_and_stamp);
+
+ if (!has_option (line, "--force"))
+ {
+ char *command;
+
+ rc = asprintf (&command, "KNOWNCARDP %s", serial_and_stamp);
+ if (rc < 0)
+ {
+ free (serial_and_stamp);
+ 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));
+ free (serial_and_stamp);
+ return rc;
+ }
+ /* not canceled, so we have to proceeed */
+ }
+ free (serial_and_stamp);
+ }
+
+ /* Return information about the certificates. */
+ if (ctrl->app_ctx)
+ rc = -1; /* This information is not yet available for applications. */
+ 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 = 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. */
+ if (ctrl->app_ctx)
+ rc = -1; /* This information is not yet available for applications. */
+ for (idx=0; !rc; idx++)
+ {
+ unsigned char keygrip[20];
+ char *keyid;
+ int no_cert = 0;
+
+ rc = card_enum_keypairs (ctrl->card_ctx, idx, keygrip, &keyid);
+ if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT && keyid)
+ {
+ /* this does happen with an incomplete personalized
+ card; i.e. during the time we have stored the key on the
+ card but not stored the certificate; probably becuase it
+ has not yet been received back from the CA. Note that we
+ must release KEYID in this case. */
+ rc = 0;
+ no_cert = 1;
+ }
+ if (!rc)
+ {
+ char *buf, *p;
+
+ buf = p = xtrymalloc (40 + 1 + strlen (keyid) + 1);
+ if (!buf)
+ rc = out_of_core ();
+ else
+ {
+ int i;
+
+ if (no_cert)
+ *p++ = 'X';
+ else
+ {
+ for (i=0; i < 20; i++, p += 2)
+ sprintf (p, "%02X", keygrip[i]);
+ }
+ *p++ = ' ';
+ strcpy (p, keyid);
+ assuan_write_status (ctx, "KEYPAIRINFO", buf);
+ xfree (buf);
+ }
+ }
+ xfree (keyid);
+ }
+ if (rc == -1)
+ rc = 0;
+
+ if (!rc && ctrl->app_ctx)
+ rc = app_write_learn_status (ctrl->app_ctx, ctrl);
+
+
+ return map_to_assuan_status (rc);
+}
+
+
+
+/* READCERT <hexified_certid>
+
+ */
+static int
+cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *cert;
+ size_t ncert;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
+ if (rc)
+ {
+ log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
+ }
+ if (!rc)
+ {
+ rc = assuan_send_data (ctx, cert, ncert);
+ xfree (cert);
+ if (rc)
+ return rc;
+ }
+
+ return map_to_assuan_status (rc);
+}
+
+
+/* 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;
+
+ if (ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
+ if (rc)
+ {
+ log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
+ goto leave;
+ }
+
+ kc = ksba_cert_new ();
+ if (!kc)
+ {
+ rc = out_of_core ();
+ xfree (cert);
+ 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 = gpg_error (GPG_ERR_NO_PUBKEY);
+ 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);
+}
+
+
+
+
+/* SETDATA <hexstring>
+
+ The client should use this command to tell us the data he want to
+ sign. */
+static int
+cmd_setdata (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int n;
+ char *p;
+ unsigned char *buf;
+
+ /* parse the hexstring */
+ for (p=line,n=0; hexdigitp (p); p++, n++)
+ ;
+ if (*p)
+ return set_error (Parameter_Error, "invalid hexstring");
+ if (!n)
+ return set_error (Parameter_Error, "no data given");
+ if ((n&1))
+ return set_error (Parameter_Error, "odd number of digits");
+ n /= 2;
+ buf = xtrymalloc (n);
+ if (!buf)
+ return ASSUAN_Out_Of_Core;
+
+ ctrl->in_data.value = buf;
+ ctrl->in_data.valuelen = n;
+ for (p=line, n=0; n < ctrl->in_data.valuelen; p += 2, n++)
+ buf[n] = xtoi_2 (p);
+ return 0;
+}
+
+
+
+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 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 gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+ *retstr = value;
+ return 0;
+}
+
+
+/* PKSIGN <hexified_id>
+
+ */
+static int
+cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *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;
+
+ if (ctrl->app_ctx)
+ rc = app_sign (ctrl->app_ctx,
+ keyidstr, GCRY_MD_SHA1,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ else
+ 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", gpg_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);
+}
+
+/* PKAUTH <hexified_id>
+
+ */
+static int
+cmd_pkauth (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *outdata;
+ size_t outdatalen;
+ char *keyidstr;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ /* 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 = app_auth (ctrl->app_ctx,
+ keyidstr,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ free (keyidstr);
+ if (rc)
+ {
+ log_error ("app_auth_sign failed: %s\n", gpg_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;
+ unsigned char *outdata;
+ size_t outdatalen;
+ char *keyidstr;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ keyidstr = strdup (line);
+ if (!keyidstr)
+ return ASSUAN_Out_Of_Core;
+ if (ctrl->app_ctx)
+ rc = app_decipher (ctrl->app_ctx,
+ keyidstr,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ else
+ 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", gpg_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);
+}
+
+
+/* SETATTR <name> <value>
+
+ This command is used to store data on a a smartcard. The allowed
+ names and values are depend on the currently selected smartcard
+ application. NAME and VALUE must be percent and '+' escaped.
+
+ However, the curent implementation assumes that Name is not escaped;
+ this works as long as noone uses arbitrary escaping.
+
+ A PIN will be requested for most NAMEs. See the corresponding
+ setattr function of the actually used application (app-*.c) for
+ details. */
+static int
+cmd_setattr (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *keyword;
+ int keywordlen;
+ size_t nbytes;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ keyword = line;
+ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ if (*line)
+ *line++ = 0;
+ while (spacep (line))
+ line++;
+ nbytes = percent_plus_unescape (line);
+
+ rc = app_setattr (ctrl->app_ctx, keyword, pin_cb, ctx, line, nbytes);
+
+ return map_to_assuan_status (rc);
+}
+
+/* GENKEY [--force] <no>
+
+ Generate a key on-card identified by NO, which is application
+ specific. Return values are application specific. For OpenPGP
+ cards 2 status lines are returned:
+
+ S KEY-FPR <hexstring>
+ S KEY-CREATED-AT <seconds_since_epoch>
+ S KEY-DATA [p|n] <hexdata>
+
+
+ --force is required to overwriet an already existing key. The
+ KEY-CREATED-AT is required for further processing because it is
+ part of the hashed key material for the fingerprint.
+
+ The public part of the key can also later be retrieved using the
+ READKEY command.
+
+ */
+static int
+cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *keyno;
+ int force = has_option (line, "--force");
+
+ /* Skip over options. */
+ while ( *line == '-' && line[1] == '-' )
+ {
+ while (!spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ }
+ if (!*line)
+ return set_error (Parameter_Error, "no key number given");
+ keyno = line;
+ while (!spacep (line))
+ line++;
+ *line = 0;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0, pin_cb, ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+/* RANDOM <nbytes>
+
+ Get NBYTES of random from the card and send them back as data.
+*/
+static int
+cmd_random (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ size_t nbytes;
+ unsigned char *buffer;
+
+ if (!*line)
+ return set_error (Parameter_Error, "number of requested bytes missing");
+ nbytes = strtoul (line, NULL, 0);
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ buffer = xtrymalloc (nbytes);
+ if (!buffer)
+ return ASSUAN_Out_Of_Core;
+
+ rc = app_get_challenge (ctrl->app_ctx, nbytes, buffer);
+ if (!rc)
+ {
+ rc = assuan_send_data (ctx, buffer, nbytes);
+ xfree (buffer);
+ return rc; /* that is already an assuan error code */
+ }
+ xfree (buffer);
+
+ return map_to_assuan_status (rc);
+}
+
+
+/* PASSWD [--reset] <chvno>
+
+ Change the PIN or reset thye retry counter of the card holder
+ verfication vector CHVNO. */
+static int
+cmd_passwd (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *chvnostr;
+ int reset_mode = has_option (line, "--reset");
+
+ /* Skip over options. */
+ while (*line == '-' && line[1] == '-')
+ {
+ while (!spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ }
+ if (!*line)
+ return set_error (Parameter_Error, "no CHV number given");
+ chvnostr = line;
+ while (!spacep (line))
+ line++;
+ *line = 0;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ rc = app_change_pin (ctrl->app_ctx, ctrl, chvnostr, reset_mode, pin_cb, ctx
+);
+ if (rc)
+ log_error ("command passwd failed: %s\n", gpg_strerror (rc));
+ return map_to_assuan_status (rc);
+}
+
+
+
+
+/* Tell the assuan library about our commands */
+static int
+register_commands (ASSUAN_CONTEXT ctx)
+{
+ static struct {
+ const char *name;
+ int (*handler)(ASSUAN_CONTEXT, char *line);
+ } table[] = {
+ { "SERIALNO", cmd_serialno },
+ { "LEARN", cmd_learn },
+ { "READCERT", cmd_readcert },
+ { "READKEY", cmd_readkey },
+ { "SETDATA", cmd_setdata },
+ { "PKSIGN", cmd_pksign },
+ { "PKAUTH", cmd_pkauth },
+ { "PKDECRYPT", cmd_pkdecrypt },
+ { "INPUT", NULL },
+ { "OUTPUT", NULL },
+ { "SETATTR", cmd_setattr },
+ { "GENKEY", cmd_genkey },
+ { "RANDOM", cmd_random },
+ { "PASSWD", cmd_passwd },
+ { NULL }
+ };
+ int i, rc;
+
+ for (i=0; table[i].name; i++)
+ {
+ rc = assuan_register_command (ctx, table[i].name, table[i].handler);
+ if (rc)
+ return rc;
+ }
+ assuan_set_hello_line (ctx, "GNU Privacy Guard's Smartcard server ready");
+
+ assuan_register_reset_notify (ctx, reset_notify);
+ assuan_register_option_handler (ctx, option_handler);
+ return 0;
+}
+
+
+/* Startup the server. If LISTEN_FD is given as -1, this is simple
+ piper server, otherwise it is a regular server */
+void
+scd_command_handler (int listen_fd)
+{
+ int rc;
+ ASSUAN_CONTEXT ctx;
+ struct server_control_s ctrl;
+
+ memset (&ctrl, 0, sizeof ctrl);
+ scd_init_default_ctrl (&ctrl);
+
+ if (listen_fd == -1)
+ {
+ int filedes[2];
+
+ filedes[0] = 0;
+ filedes[1] = 1;
+ rc = assuan_init_pipe_server (&ctx, filedes);
+ }
+ else
+ {
+ rc = assuan_init_socket_server (&ctx, listen_fd);
+ }
+ if (rc)
+ {
+ log_error ("failed to initialize the server: %s\n",
+ assuan_strerror(rc));
+ scd_exit (2);
+ }
+ rc = register_commands (ctx);
+ if (rc)
+ {
+ log_error ("failed to register commands with Assuan: %s\n",
+ assuan_strerror(rc));
+ scd_exit (2);
+ }
+ assuan_set_pointer (ctx, &ctrl);
+ ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
+ ctrl.server_local->assuan_ctx = ctx;
+
+ if (DBG_ASSUAN)
+ assuan_set_log_stream (ctx, log_get_stream ());
+
+ for (;;)
+ {
+ rc = assuan_accept (ctx);
+ if (rc == -1)
+ {
+ break;
+ }
+ else if (rc)
+ {
+ log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
+ break;
+ }
+
+ rc = assuan_process (ctx);
+ if (rc)
+ {
+ log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
+ continue;
+ }
+ }
+ reset_notify (ctx); /* used for cleanup */
+
+ assuan_deinit_server (ctx);
+}
+
+
+/* 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;
+ ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
+
+ va_start (arg_ptr, keyword);
+
+ p = buf;
+ n = 0;
+ 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;
+ assuan_write_status (ctx, keyword, buf);
+
+ va_end (arg_ptr);
+}
+