diff options
Diffstat (limited to '')
-rw-r--r-- | agent/call-scd.c | 661 |
1 files changed, 0 insertions, 661 deletions
diff --git a/agent/call-scd.c b/agent/call-scd.c deleted file mode 100644 index 14487f1e3..000000000 --- a/agent/call-scd.c +++ /dev/null @@ -1,661 +0,0 @@ -/* call-scd.c - fork of the scdaemon to do SC operations - * 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 - */ - -/* Fixme: For now we have serialized all access to the scdaemon which - make sense becuase the scdaemon can't handle concurrent connections - right now. We should however keep a list of connections and lock - just that connection - it migth make sense to implemtn parts of - this in Assuan.*/ - -#include <config.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <assert.h> -#include <unistd.h> -#include <sys/stat.h> -#ifdef USE_GNU_PTH -# include <pth.h> -#endif - -#include "agent.h" -#include <assuan.h> - -#ifdef _POSIX_OPEN_MAX -#define MAX_OPEN_FDS _POSIX_OPEN_MAX -#else -#define MAX_OPEN_FDS 20 -#endif - -static ASSUAN_CONTEXT scd_ctx = NULL; -#ifdef USE_GNU_PTH -static pth_mutex_t scd_lock = PTH_MUTEX_INIT; -#endif - -/* callback parameter for learn card */ -struct learn_parm_s { - void (*kpinfo_cb)(void*, const char *); - void *kpinfo_cb_arg; - void (*certinfo_cb)(void*, const char *); - void *certinfo_cb_arg; - void (*sinfo_cb)(void*, const char *, size_t, const char *); - void *sinfo_cb_arg; -}; - -struct inq_needpin_s { - ASSUAN_CONTEXT ctx; - int (*getpin_cb)(void *, const char *, char*, size_t); - void *getpin_cb_arg; -}; - -struct membuf { - size_t len; - size_t size; - char *buf; - int out_of_core; -}; - - - -/* A simple implementation of a dynamic buffer. Use init_membuf() to - create a buffer, put_membuf to append bytes and get_membuf to - release and return the buffer. Allocation errors are detected but - only returned at the final get_membuf(), this helps not to clutter - the code with out of core checks. */ - -static void -init_membuf (struct membuf *mb, int initiallen) -{ - mb->len = 0; - mb->size = initiallen; - mb->out_of_core = 0; - mb->buf = xtrymalloc (initiallen); - if (!mb->buf) - mb->out_of_core = 1; -} - -static void -put_membuf (struct membuf *mb, const void *buf, size_t len) -{ - if (mb->out_of_core) - return; - - if (mb->len + len >= mb->size) - { - char *p; - - mb->size += len + 1024; - p = xtryrealloc (mb->buf, mb->size); - if (!p) - { - mb->out_of_core = 1; - return; - } - mb->buf = p; - } - memcpy (mb->buf + mb->len, buf, len); - mb->len += len; -} - -static void * -get_membuf (struct membuf *mb, size_t *len) -{ - char *p; - - if (mb->out_of_core) - { - xfree (mb->buf); - mb->buf = NULL; - return NULL; - } - - p = mb->buf; - *len = mb->len; - mb->buf = NULL; - mb->out_of_core = 1; /* don't allow a reuse */ - return p; -} - - - - -static int -unlock_scd (int rc) -{ -#ifdef USE_GNU_PTH - if (!pth_mutex_release (&scd_lock)) - { - log_error ("failed to release the SCD lock\n"); - if (!rc) - rc = gpg_error (GPG_ERR_INTERNAL); - } -#endif - return rc; -} - -/* Fork off the SCdaemon if this has not already been done */ -static int -start_scd (void) -{ - int rc; - const char *pgmname; - ASSUAN_CONTEXT ctx; - const char *argv[3]; - int no_close_list[3]; - int i; - -#ifdef USE_GNU_PTH - if (!pth_mutex_acquire (&scd_lock, 0, NULL)) - { - log_error ("failed to acquire the SCD lock\n"); - return gpg_error (GPG_ERR_INTERNAL); - } -#endif - - if (scd_ctx) - return 0; /* No need to serialize things because the agent is - expected to tun as a single-thread (or may be in - future using libpth) */ - - if (opt.verbose) - log_info ("no running SCdaemon - starting it\n"); - - if (fflush (NULL)) - { - gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); - log_error ("error flushing pending output: %s\n", strerror (errno)); - return unlock_scd (tmperr); - } - - if (!opt.scdaemon_program || !*opt.scdaemon_program) - opt.scdaemon_program = GNUPG_DEFAULT_SCDAEMON; - if ( !(pgmname = strrchr (opt.scdaemon_program, '/'))) - pgmname = opt.scdaemon_program; - else - pgmname++; - - argv[0] = pgmname; - argv[1] = "--server"; - argv[2] = NULL; - - i=0; - if (!opt.running_detached) - { - if (log_get_fd () != -1) - no_close_list[i++] = log_get_fd (); - no_close_list[i++] = fileno (stderr); - } - no_close_list[i] = -1; - - /* connect to the pinentry and perform initial handshaking */ - rc = assuan_pipe_connect (&ctx, opt.scdaemon_program, (char**)argv, - no_close_list); - if (rc) - { - log_error ("can't connect to the SCdaemon: %s\n", - assuan_strerror (rc)); - return unlock_scd (gpg_error (GPG_ERR_NO_SCDAEMON)); - } - scd_ctx = ctx; - - if (DBG_ASSUAN) - log_debug ("connection to SCdaemon established\n"); - return 0; -} - - - -static AssuanError -learn_status_cb (void *opaque, const char *line) -{ - struct learn_parm_s *parm = opaque; - const char *keyword = line; - int keywordlen; - - for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) - ; - while (spacep (line)) - line++; - if (keywordlen == 8 && !memcmp (keyword, "CERTINFO", keywordlen)) - { - parm->certinfo_cb (parm->certinfo_cb_arg, line); - } - else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) - { - parm->kpinfo_cb (parm->kpinfo_cb_arg, line); - } - else if (keywordlen && *line) - { - parm->sinfo_cb (parm->sinfo_cb_arg, keyword, keywordlen, line); - } - - return 0; -} - -/* Perform the learn command and return a list of all private keys - stored on the card. */ -int -agent_card_learn (void (*kpinfo_cb)(void*, const char *), - void *kpinfo_cb_arg, - void (*certinfo_cb)(void*, const char *), - void *certinfo_cb_arg, - void (*sinfo_cb)(void*, const char *, size_t, const char *), - void *sinfo_cb_arg) -{ - int rc; - struct learn_parm_s parm; - - rc = start_scd (); - if (rc) - return rc; - - memset (&parm, 0, sizeof parm); - parm.kpinfo_cb = kpinfo_cb; - parm.kpinfo_cb_arg = kpinfo_cb_arg; - parm.certinfo_cb = certinfo_cb; - parm.certinfo_cb_arg = certinfo_cb_arg; - parm.sinfo_cb = sinfo_cb; - parm.sinfo_cb_arg = sinfo_cb_arg; - rc = assuan_transact (scd_ctx, "LEARN --force", - NULL, NULL, NULL, NULL, - learn_status_cb, &parm); - if (rc) - return unlock_scd (map_assuan_err (rc)); - - return unlock_scd (0); -} - - - -static AssuanError -get_serialno_cb (void *opaque, const char *line) -{ - char **serialno = opaque; - const char *keyword = line; - const char *s; - int keywordlen, n; - - for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) - ; - while (spacep (line)) - line++; - - if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) - { - if (*serialno) - return ASSUAN_Unexpected_Status; - for (n=0,s=line; hexdigitp (s); s++, n++) - ; - if (!n || (n&1)|| !(spacep (s) || !*s) ) - return ASSUAN_Invalid_Status; - *serialno = xtrymalloc (n+1); - if (!*serialno) - return ASSUAN_Out_Of_Core; - memcpy (*serialno, line, n); - (*serialno)[n] = 0; - } - - return 0; -} - -/* Return the serial number of the card or an appropriate error. The - serial number is returned as a hexstring. */ -int -agent_card_serialno (char **r_serialno) -{ - int rc; - char *serialno = NULL; - - rc = start_scd (); - if (rc) - return rc; - - /* Hmm, do we really need this reset - scddaemon should do this or - we can do this if we for some reason figure out that the - operation might have failed due to a missing RESET. Hmmm, I feel - this is really SCdaemon's duty */ -/* rc = assuan_transact (scd_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); */ -/* if (rc) */ -/* return unlock_scd (map_assuan_err (rc)); */ - - rc = assuan_transact (scd_ctx, "SERIALNO", - NULL, NULL, NULL, NULL, - get_serialno_cb, &serialno); - if (rc) - { - xfree (serialno); - return unlock_scd (map_assuan_err (rc)); - } - *r_serialno = serialno; - return unlock_scd (0); -} - - -static AssuanError -membuf_data_cb (void *opaque, const void *buffer, size_t length) -{ - struct membuf *data = opaque; - - if (buffer) - put_membuf (data, buffer, length); - return 0; -} - -/* Handle the NEEDPIN inquiry. */ -static AssuanError -inq_needpin (void *opaque, const char *line) -{ - struct inq_needpin_s *parm = opaque; - char *pin; - size_t pinlen; - int rc; - - if (!(!strncmp (line, "NEEDPIN", 7) && (line[7] == ' ' || !line[7]))) - { - log_error ("unsupported inquiry `%s'\n", line); - return ASSUAN_Inquire_Unknown; - } - line += 7; - - pinlen = 90; - pin = gcry_malloc_secure (pinlen); - if (!pin) - return ASSUAN_Out_Of_Core; - - rc = parm->getpin_cb (parm->getpin_cb_arg, line, pin, pinlen); - if (rc) - rc = ASSUAN_Canceled; - if (!rc) - rc = assuan_send_data (parm->ctx, pin, pinlen); - xfree (pin); - - return rc; -} - - - -/* Create a signature using the current card */ -int -agent_card_pksign (const char *keyid, - int (*getpin_cb)(void *, const char *, char*, size_t), - void *getpin_cb_arg, - const unsigned char *indata, size_t indatalen, - char **r_buf, size_t *r_buflen) -{ - int rc, i; - char *p, line[ASSUAN_LINELENGTH]; - struct membuf data; - struct inq_needpin_s inqparm; - size_t len; - unsigned char *sigbuf; - size_t sigbuflen; - - *r_buf = NULL; - rc = start_scd (); - if (rc) - return rc; - - if (indatalen*2 + 50 > DIM(line)) - return unlock_scd (gpg_error (GPG_ERR_GENERAL)); - - sprintf (line, "SETDATA "); - p = line + strlen (line); - for (i=0; i < indatalen ; i++, p += 2 ) - sprintf (p, "%02X", indata[i]); - rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); - if (rc) - return unlock_scd (map_assuan_err (rc)); - - init_membuf (&data, 1024); - inqparm.ctx = scd_ctx; - inqparm.getpin_cb = getpin_cb; - inqparm.getpin_cb_arg = getpin_cb_arg; - snprintf (line, DIM(line)-1, "PKSIGN %s", keyid); - line[DIM(line)-1] = 0; - rc = assuan_transact (scd_ctx, line, - membuf_data_cb, &data, - inq_needpin, &inqparm, - NULL, NULL); - if (rc) - { - xfree (get_membuf (&data, &len)); - return unlock_scd (map_assuan_err (rc)); - } - sigbuf = get_membuf (&data, &sigbuflen); - - /* create an S-expression from it which is formatted like this: - "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" */ - *r_buflen = 21 + 11 + sigbuflen + 4; - *r_buf = xtrymalloc (*r_buflen); - if (!*r_buf) - { - gpg_error_t tmperr = out_of_core (); - xfree (*r_buf); - return unlock_scd (tmperr); - } - p = stpcpy (*r_buf, "(7:sig-val(3:rsa(1:s" ); - sprintf (p, "%u:", (unsigned int)sigbuflen); - p += strlen (p); - memcpy (p, sigbuf, sigbuflen); - p += sigbuflen; - strcpy (p, ")))"); - xfree (sigbuf); - - assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)); - return unlock_scd (0); -} - -/* Decipher INDATA using the current card. Note that the returned value is */ -int -agent_card_pkdecrypt (const char *keyid, - int (*getpin_cb)(void *, const char *, char*, size_t), - void *getpin_cb_arg, - const unsigned char *indata, size_t indatalen, - char **r_buf, size_t *r_buflen) -{ - int rc, i; - char *p, line[ASSUAN_LINELENGTH]; - struct membuf data; - struct inq_needpin_s inqparm; - size_t len; - - *r_buf = NULL; - rc = start_scd (); - if (rc) - return rc; - - /* FIXME: use secure memory where appropriate */ - if (indatalen*2 + 50 > DIM(line)) - return unlock_scd (gpg_error (GPG_ERR_GENERAL)); - - sprintf (line, "SETDATA "); - p = line + strlen (line); - for (i=0; i < indatalen ; i++, p += 2 ) - sprintf (p, "%02X", indata[i]); - rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); - if (rc) - return unlock_scd (map_assuan_err (rc)); - - init_membuf (&data, 1024); - inqparm.ctx = scd_ctx; - inqparm.getpin_cb = getpin_cb; - inqparm.getpin_cb_arg = getpin_cb_arg; - snprintf (line, DIM(line)-1, "PKDECRYPT %s", keyid); - line[DIM(line)-1] = 0; - rc = assuan_transact (scd_ctx, line, - membuf_data_cb, &data, - inq_needpin, &inqparm, - NULL, NULL); - if (rc) - { - xfree (get_membuf (&data, &len)); - return unlock_scd (map_assuan_err (rc)); - } - *r_buf = get_membuf (&data, r_buflen); - if (!*r_buf) - return unlock_scd (gpg_error (GPG_ERR_ENOMEM)); - - return unlock_scd (0); -} - - - -/* Read a certificate with ID into R_BUF and R_BUFLEN. */ -int -agent_card_readcert (const char *id, char **r_buf, size_t *r_buflen) -{ - int rc; - char line[ASSUAN_LINELENGTH]; - struct membuf data; - size_t len; - - *r_buf = NULL; - rc = start_scd (); - if (rc) - return rc; - - init_membuf (&data, 1024); - snprintf (line, DIM(line)-1, "READCERT %s", id); - line[DIM(line)-1] = 0; - rc = assuan_transact (scd_ctx, line, - membuf_data_cb, &data, - NULL, NULL, - NULL, NULL); - if (rc) - { - xfree (get_membuf (&data, &len)); - return unlock_scd (map_assuan_err (rc)); - } - *r_buf = get_membuf (&data, r_buflen); - if (!*r_buf) - return unlock_scd (gpg_error (GPG_ERR_ENOMEM)); - - return unlock_scd (0); -} - - - -/* Read a key with ID and return it in an allocate buffer pointed to - by r_BUF as a valid S-expression. */ -int -agent_card_readkey (const char *id, unsigned char **r_buf) -{ - int rc; - char line[ASSUAN_LINELENGTH]; - struct membuf data; - size_t len, buflen; - - *r_buf = NULL; - rc = start_scd (); - if (rc) - return rc; - - init_membuf (&data, 1024); - snprintf (line, DIM(line)-1, "READKEY %s", id); - line[DIM(line)-1] = 0; - rc = assuan_transact (scd_ctx, line, - membuf_data_cb, &data, - NULL, NULL, - NULL, NULL); - if (rc) - { - xfree (get_membuf (&data, &len)); - return unlock_scd (map_assuan_err (rc)); - } - *r_buf = get_membuf (&data, &buflen); - if (!*r_buf) - return unlock_scd (gpg_error (GPG_ERR_ENOMEM)); - - if (!gcry_sexp_canon_len (*r_buf, buflen, NULL, NULL)) - { - xfree (*r_buf); *r_buf = NULL; - return unlock_scd (gpg_error (GPG_ERR_INV_VALUE)); - } - - return unlock_scd (0); -} - - - - -static AssuanError -pass_status_thru (void *opaque, const char *line) -{ - ASSUAN_CONTEXT ctx = opaque; - char keyword[200]; - int i; - - for (i=0; *line && !spacep (line) && i < DIM(keyword)-1; line++, i++) - keyword[i] = *line; - keyword[i] = 0; - /* truncate any remaining keyword stuff. */ - for (; *line && !spacep (line); line++) - ; - while (spacep (line)) - line++; - - assuan_write_status (ctx, keyword, line); - return 0; -} - -static AssuanError -pass_data_thru (void *opaque, const void *buffer, size_t length) -{ - ASSUAN_CONTEXT ctx = opaque; - - assuan_send_data (ctx, buffer, length); - return 0; -} - - -/* Send the line CMDLINE with command for the SCDdaemon to it and send - all status messages back. This command is used as a general quoting - mechanism to pass everything verbatim to SCDAEMOPN. The PIN - inquirey is handled inside gpg-agent. */ -int -agent_card_scd (const char *cmdline, - int (*getpin_cb)(void *, const char *, char*, size_t), - void *getpin_cb_arg, void *assuan_context) -{ - int rc; - struct inq_needpin_s inqparm; - - rc = start_scd (); - if (rc) - return rc; - - inqparm.ctx = scd_ctx; - inqparm.getpin_cb = getpin_cb; - inqparm.getpin_cb_arg = getpin_cb_arg; - rc = assuan_transact (scd_ctx, cmdline, - pass_data_thru, assuan_context, - inq_needpin, &inqparm, - pass_status_thru, assuan_context); - if (rc) - { - return unlock_scd (map_assuan_err (rc)); - } - - return unlock_scd (0); -} - - |