summaryrefslogtreecommitdiffstats
path: root/scd/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'scd/command.c')
-rw-r--r--scd/command.c651
1 files changed, 0 insertions, 651 deletions
diff --git a/scd/command.c b/scd/command.c
deleted file mode 100644
index aa410a6ee..000000000
--- a/scd/command.c
+++ /dev/null
@@ -1,651 +0,0 @@
-/* command.c - SCdaemon command handler
- * Copyright (C) 2001, 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 <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 */
-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;
- }
-}
-
-
-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->card_ctx)
- {
- int rc = card_open (&ctrl->card_ctx);
- if (rc)
- return map_to_assuan_status (rc);
- }
- return 0;
-}
-
-
-/* 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 doneon the same card unless he call 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;
-
- 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 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 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
-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;
-
- 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. */
- 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];
- char *keyid;
- int no_cert = 0;
-
- rc = card_enum_keypairs (ctrl->card_ctx, idx, keygrip, &keyid);
- if (rc == GNUPG_Missing_Certificate && 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 = GNUPG_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;
-
-
- 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;
-
- rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
- if (rc)
- {
- log_error ("card_read_cert failed: %s\n", gnupg_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;
-
- 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);
-}
-
-
-
-
-/* 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&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 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>
-
- */
-static int
-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);
-}
-
-
-
-
-/* Tell the assuan library about our commands */
-static int
-register_commands (ASSUAN_CONTEXT ctx)
-{
- static struct {
- const char *name;
- int cmd_id;
- int (*handler)(ASSUAN_CONTEXT, char *line);
- } table[] = {
- { "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 }
- };
- int i, j, rc;
-
- for (i=j=0; table[i].name; i++)
- {
- rc = assuan_register_command (ctx,
- table[i].cmd_id? table[i].cmd_id
- : (ASSUAN_CMD_USER + j++),
- 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);
-}