diff options
author | Werner Koch <[email protected]> | 2002-03-06 14:16:37 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2002-03-06 14:16:37 +0000 |
commit | 4e637f22851869020929ad541012e23d859fa1a9 (patch) | |
tree | 0d1f66624c4de4f1679c7e14a96e3b387848c4d6 | |
parent | * assuan-client.c (_assuan_read_from_server): Detect END. (diff) | |
download | gnupg-4e637f22851869020929ad541012e23d859fa1a9.tar.gz gnupg-4e637f22851869020929ad541012e23d859fa1a9.zip |
sm/
* gpgsm.c: New command --learn-card
* call-agent.c (learn_cb,gpgsm_agent_learn): New.
* gpgsm.c (main): Print error messages for non-implemented commands.
agent/
* learncard.c: New.
* divert-scd.c (ask_for_card): The serial number is binary so
convert it to hex here.
* findkey.c (agent_write_private_key): New.
* genkey.c (store_key): And use it here.
scd/
* pkdecrypt.c (agent_pkdecrypt): Changed the way the diversion is done.
* divert-scd.c (divert_pkdecrypt): Changed interface and
implemented it.
-rw-r--r-- | agent/ChangeLog | 6 | ||||
-rw-r--r-- | agent/Makefile.am | 1 | ||||
-rw-r--r-- | agent/agent.h | 16 | ||||
-rw-r--r-- | agent/call-scd.c | 102 | ||||
-rw-r--r-- | agent/command.c | 25 | ||||
-rw-r--r-- | agent/divert-scd.c | 11 | ||||
-rw-r--r-- | agent/findkey.c | 58 | ||||
-rw-r--r-- | agent/genkey.c | 59 | ||||
-rw-r--r-- | agent/learncard.c | 263 | ||||
-rw-r--r-- | agent/query.c | 3 | ||||
-rw-r--r-- | scd/command.c | 61 | ||||
-rw-r--r-- | sm/ChangeLog | 5 | ||||
-rw-r--r-- | sm/call-agent.c | 93 | ||||
-rw-r--r-- | sm/gpgsm.c | 28 | ||||
-rw-r--r-- | sm/gpgsm.h | 1 | ||||
-rw-r--r-- | sm/import.c | 2 |
16 files changed, 649 insertions, 85 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog index bc0ca11ef..cbc05b1e4 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,5 +1,11 @@ 2002-03-06 Werner Koch <[email protected]> + * learncard.c: New. + * divert-scd.c (ask_for_card): The serial number is binary so + convert it to hex here. + * findkey.c (agent_write_private_key): New. + * genkey.c (store_key): And use it here. + * pkdecrypt.c (agent_pkdecrypt): Changed the way the diversion is done. * divert-scd.c (divert_pkdecrypt): Changed interface and implemented it. diff --git a/agent/Makefile.am b/agent/Makefile.am index 7ee2fb398..5716519bf 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -38,6 +38,7 @@ gpg_agent_SOURCES = \ trustlist.c \ divert-scd.c \ call-scd.c \ + learncard.c \ sexp-parse.h diff --git a/agent/agent.h b/agent/agent.h index 2d92f94be..2b5a2fa31 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -102,6 +102,8 @@ const char *trans (const char *text); void start_command_handler (int); /*-- findkey.c --*/ +int agent_write_private_key (const unsigned char *grip, + const void *buffer, size_t length, int force); GCRY_SEXP agent_key_from_file (const unsigned char *grip, unsigned char **shadow_info); int agent_key_available (const unsigned char *grip); @@ -152,12 +154,14 @@ int agent_marktrusted (const char *name, const char *fpr, int flag); /*-- divert-scd.c --*/ int divert_pksign (const unsigned char *digest, size_t digestlen, int algo, - const char *shadow_info, unsigned char **r_sig); -int divert_pkdecrypt (const unsigned char *cipher, const char *shadow_info, + const unsigned char *shadow_info, unsigned char **r_sig); +int divert_pkdecrypt (const unsigned char *cipher, + const unsigned char *shadow_info, char **r_buf, size_t *r_len); /*-- call-scd.c --*/ -int agent_card_learn (void); +int agent_card_learn (void (*kpinfo_cb)(void*, const char *), + void *kpinfo_cb_arg); int agent_card_serialno (char **r_serialno); int agent_card_pksign (const char *keyid, int (*getpin_cb)(void *, const char *, char*, size_t), @@ -169,6 +173,12 @@ int agent_card_pkdecrypt (const char *keyid, void *getpin_cb_arg, const unsigned char *indata, size_t indatalen, char **r_buf, size_t *r_buflen); +int agent_card_readcert (const char *id, char **r_buf, size_t *r_buflen); +int agent_card_readkey (const char *id, unsigned char **r_buf); + + +/*-- learncard.c --*/ +int agent_handle_learn (void *assuan_context); #endif /*AGENT_H*/ diff --git a/agent/call-scd.c b/agent/call-scd.c index dd8853f22..040201f25 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -41,9 +41,8 @@ static ASSUAN_CONTEXT scd_ctx = NULL; /* callback parameter for learn card */ struct learn_parm_s { - int lines; - size_t size; - char *buffer; + void (*kpinfo_cb)(void*, const char *); + void *kpinfo_cb_arg; }; struct inq_needpin_s { @@ -176,7 +175,7 @@ start_scd (void) static AssuanError learn_status_cb (void *opaque, const char *line) { - /* struct learn_parm_s *parm = opaque;*/ + struct learn_parm_s *parm = opaque; const char *keyword = line; int keywordlen; @@ -186,7 +185,7 @@ learn_status_cb (void *opaque, const char *line) line++; if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) { - log_debug ("learn_status_cb: keypair `%s'\n", line); + parm->kpinfo_cb (parm->kpinfo_cb_arg, line); } else if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { @@ -201,7 +200,7 @@ learn_status_cb (void *opaque, const char *line) /* Perform the learn command and return a list of all private keys stored on the card. */ int -agent_card_learn (void) +agent_card_learn (void (*kpinfo_cb)(void*, const char *), void *kpinfo_cb_arg) { int rc; struct learn_parm_s parm; @@ -210,12 +209,9 @@ agent_card_learn (void) if (rc) return rc; - rc = assuan_transact (scd_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); - if (rc) - return map_assuan_err (rc); - memset (&parm, 0, sizeof parm); - + parm.kpinfo_cb = kpinfo_cb; + parm.kpinfo_cb_arg = kpinfo_cb_arg; rc = assuan_transact (scd_ctx, "LEARN --force", NULL, NULL, NULL, NULL, learn_status_cb, &parm); @@ -259,7 +255,7 @@ get_serialno_cb (void *opaque, const char *line) } /* Return the serial number of the card or an appropriate error. The - serial number is returned as a hext string. */ + serial number is returned as a hexstring. */ int agent_card_serialno (char **r_serialno) { @@ -296,7 +292,8 @@ membuf_data_cb (void *opaque, const void *buffer, size_t length) { struct membuf *data = opaque; - put_membuf (data, buffer, length); + if (buffer) + put_membuf (data, buffer, length); return 0; } @@ -456,3 +453,82 @@ agent_card_pkdecrypt (const char *keyid, return 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 map_assuan_err (rc); + } + *r_buf = get_membuf (&data, r_buflen); + if (!*r_buf) + return GNUPG_Out_Of_Core; + + return 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 map_assuan_err (rc); + } + *r_buf = get_membuf (&data, &buflen); + if (!*r_buf) + return GNUPG_Out_Of_Core; + + if (!gcry_sexp_canon_len (*r_buf, buflen, NULL, NULL)) + { + xfree (*r_buf); *r_buf = NULL; + return GNUPG_Invalid_Value; + } + + return 0; +} + + + diff --git a/agent/command.c b/agent/command.c index bed9da648..e617d684f 100644 --- a/agent/command.c +++ b/agent/command.c @@ -64,6 +64,21 @@ reset_notify (ASSUAN_CONTEXT ctx) ctrl->digest.valuelen = 0; } + +/* 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))); +} + + + + /* ISTRUSTED <hexstring_with_fingerprint> Return OK when we have an entry with this fingerprint in our @@ -463,18 +478,18 @@ cmd_clear_passphrase (ASSUAN_CONTEXT ctx, char *line) } -/* LEARN +/* LEARN [--send] - Learn something about the currently inserted smartcard - */ + Learn something about the currently inserted smartcard. With + --send the new certificates are send back. */ static int cmd_learn (ASSUAN_CONTEXT ctx, char *line) { int rc; - rc = agent_card_learn (); + rc = agent_handle_learn (has_option (line, "--send")? ctx : NULL); if (rc) - log_error ("agent_learn_card failed: %s\n", gnupg_strerror (rc)); + log_error ("agent_handle_learn failed: %s\n", gnupg_strerror (rc)); return map_to_assuan_status (rc); } diff --git a/agent/divert-scd.c b/agent/divert-scd.c index 33fd5ce26..9b176a6bd 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -52,11 +52,11 @@ ask_for_card (const unsigned char *shadow_info, char **r_kid) n = snext (&s); if (!n) return GNUPG_Invalid_Sexp; - want_sn = xtrymalloc (n+1); + want_sn = xtrymalloc (n*2+1); if (!want_sn) return GNUPG_Out_Of_Core; - memcpy (want_sn, s, n); - want_sn[n] = 0; + for (i=0; i < n; i++) + sprintf (want_sn+2*i, "%02X", s[i]); s += n; n = snext (&s); @@ -229,7 +229,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) int divert_pksign (const unsigned char *digest, size_t digestlen, int algo, - const char *shadow_info, unsigned char **r_sig) + const unsigned char *shadow_info, unsigned char **r_sig) { int rc; char *kid; @@ -262,7 +262,8 @@ divert_pksign (const unsigned char *digest, size_t digestlen, int algo, key identified by SHADOW_INFO and return the plaintext in an allocated buffer in R_BUF. */ int -divert_pkdecrypt (const unsigned char *cipher, const char *shadow_info, +divert_pkdecrypt (const unsigned char *cipher, + const unsigned char *shadow_info, char **r_buf, size_t *r_len) { int rc; diff --git a/agent/findkey.c b/agent/findkey.c index 14ad762d9..662a56f89 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -30,6 +30,64 @@ #include "agent.h" + +int +agent_write_private_key (const unsigned char *grip, + const void *buffer, size_t length, int force) +{ + int i; + char *fname; + FILE *fp; + char hexgrip[40+4+1]; + + for (i=0; i < 20; i++) + sprintf (hexgrip+2*i, "%02X", grip[i]); + strcpy (hexgrip+40, ".key"); + + fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL); + if (force) + fp = fopen (fname, "wb"); + else + { + if (!access (fname, F_OK)) + { + log_error ("secret key file `%s' already exists\n", fname); + xfree (fname); + return seterr (General_Error); + } + fp = fopen (fname, "wbx"); /* FIXME: the x is a GNU extension - let + configure check whether this actually + works */ + } + + if (!fp) + { + log_error ("can't create `%s': %s\n", fname, strerror (errno)); + xfree (fname); + return seterr (File_Create_Error); + } + + if (fwrite (buffer, length, 1, fp) != 1) + { + log_error ("error writing `%s': %s\n", fname, strerror (errno)); + fclose (fp); + remove (fname); + xfree (fname); + return seterr (File_Create_Error); + } + if ( fclose (fp) ) + { + log_error ("error closing `%s': %s\n", fname, strerror (errno)); + remove (fname); + xfree (fname); + return seterr (File_Create_Error); + } + + xfree (fname); + return 0; +} + + static int unprotect (unsigned char **keybuf, const unsigned char *grip) { diff --git a/agent/genkey.c b/agent/genkey.c index 1aca33b83..ae46c4694 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -25,8 +25,6 @@ #include <string.h> #include <ctype.h> #include <assert.h> -#include <unistd.h> -#include <sys/stat.h> #include "agent.h" @@ -34,65 +32,32 @@ static int store_key (GCRY_SEXP private, const char *passphrase) { - int i; - char *fname; - FILE *fp; + int rc; char *buf; size_t len; unsigned char grip[20]; - char hexgrip[40+4+1]; if ( !gcry_pk_get_keygrip (private, grip) ) { log_error ("can't calculate keygrip\n"); return seterr (General_Error); } - for (i=0; i < 20; i++) - sprintf (hexgrip+2*i, "%02X", grip[i]); - strcpy (hexgrip+40, ".key"); - - fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL); - if (!access (fname, F_OK)) - { - log_error ("secret key file `%s' already exists - very strange\n", - fname); - xfree (fname); - return seterr (General_Error); - } - fp = fopen (fname, "wbx"); /* FIXME: the x is a GNU extension - let - configure check whether this actually - works */ - if (!fp) - { - log_error ("can't create `%s': %s\n", fname, strerror (errno)); - xfree (fname); - return seterr (File_Create_Error); - } len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, NULL, 0); assert (len); buf = gcry_malloc_secure (len); if (!buf) - { - fclose (fp); - remove (fname); - xfree (fname); return seterr (Out_Of_Core); - } len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len); assert (len); if (passphrase) { unsigned char *p; - int rc; rc = agent_protect (buf, passphrase, &p, &len); if (rc) { - fclose (fp); - remove (fname); - xfree (fname); xfree (buf); return rc; } @@ -100,27 +65,9 @@ store_key (GCRY_SEXP private, const char *passphrase) buf = p; } - if (fwrite (buf, len, 1, fp) != 1) - { - log_error ("error writing `%s': %s\n", fname, strerror (errno)); - fclose (fp); - remove (fname); - xfree (fname); - xfree (buf); - return seterr (File_Create_Error); - } - if ( fclose (fp) ) - { - log_error ("error closing `%s': %s\n", fname, strerror (errno)); - remove (fname); - xfree (fname); - xfree (buf); - return seterr (File_Create_Error); - } - - xfree (fname); + rc = agent_write_private_key (grip, buf, len, 0); xfree (buf); - return 0; + return rc; } diff --git a/agent/learncard.c b/agent/learncard.c new file mode 100644 index 000000000..4cd1cb848 --- /dev/null +++ b/agent/learncard.c @@ -0,0 +1,263 @@ +/* learncard.c - Handle the LEARN command + * 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 <ctype.h> +#include <assert.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "agent.h" +#include "../assuan/assuan.h" + +struct keypair_info_s { + struct keypair_info_s *next; + int no_cert; + char *id; /* points into grip */ + char hexgrip[1]; +}; +typedef struct keypair_info_s *KEYPAIR_INFO; + +struct kpinfo_cb_parm_s { + int error; + KEYPAIR_INFO info; +}; + + +static void +release_keypair_info (KEYPAIR_INFO info) +{ + while (info) + { + KEYPAIR_INFO tmp = info->next; + xfree (info); + info = tmp; + } +} + + + +/* This callback is used by agent_card_leanr and passed the content of + all KEYPAIRINFO lines. It merely store this data away */ +static void +kpinfo_cb (void *opaque, const char *line) +{ + struct kpinfo_cb_parm_s *parm = opaque; + KEYPAIR_INFO item; + char *p; + + if (parm->error) + return; /* no need to gather data after an error coccured */ + item = xtrycalloc (1, sizeof *item + strlen (line)); + if (!item) + { + parm->error = GNUPG_Out_Of_Core; + return; + } + strcpy (item->hexgrip, line); + for (p = item->hexgrip; hexdigitp (p); p++) + ; + if (p == item->hexgrip && *p == 'X' && spacep (p+1)) + { + item->no_cert = 1; + p++; + } + else if ((p - item->hexgrip) != 40 || !spacep (p)) + { /* not a 20 byte hex keygrip or now followed by a space */ + parm->error = GNUPG_Invalid_Response; + xfree (item); + return; + } + *p++ = 0; + while (spacep (p)) + p++; + item->id = p; + for (; hexdigitp (p) || *p == '.'; p++) + ; + if (!(spacep (p) || !*p)) + { /* invalid ID string */ + parm->error = GNUPG_Invalid_Response; + xfree (item); + return; + } + *p = 0; /* ignore trailing stuff */ + + /* store it */ + item->next = parm->info; + parm->info = item; +} + + +/* Create an S-expression with the shadow info. */ +static unsigned char * +make_shadow_info (const char *serialno, const char *idstring) +{ + const char *s; + unsigned char *info, *p; + char numbuf[21]; + int n; + + for (s=serialno, n=0; *s && s[1]; s += 2) + n++; + + info = p = xtrymalloc (1 + 21 + n + + 21 + strlen (idstring) + 1 + 1); + *p++ = '('; + sprintf (numbuf, "%d:", n); + p = stpcpy (p, numbuf); + for (s=serialno; *s && s[1]; s += 2) + *p++ = xtoi_2 (s); + sprintf (numbuf, "%d:", strlen (idstring)); + p = stpcpy (p, numbuf); + p = stpcpy (p, idstring); + *p++ = ')'; + *p = 0; + return info; +} + + +/* Perform the learn operation. If ASSUAN_CONTEXT is not NULL all new + certificates are send via Assuan */ +int +agent_handle_learn (void *assuan_context) +{ + int rc; + struct kpinfo_cb_parm_s parm; + char *serialno = NULL; + KEYPAIR_INFO item; + unsigned char grip[20]; + char *p; + int i; + + memset (&parm, 0, sizeof parm); + + /* Check whether a card is present and get the serial number */ + rc = agent_card_serialno (&serialno); + if (rc) + goto leave; + + /* now gather all the availabe info */ + rc = agent_card_learn (kpinfo_cb, &parm); + if (!rc && parm.error) + rc = parm.error; + if (rc) + { + log_debug ("agent_card_learn failed: %s\n", gnupg_strerror (rc)); + goto leave; + } + + log_info ("card has S/N: %s\n", serialno); + for (item = parm.info; item; item = item->next) + { + unsigned char *pubkey, *shdkey; + size_t n; + + if (opt.verbose) + log_info (" id: %s (grip=%s)\n", item->id, item->hexgrip); + + if (item->no_cert) + continue; /* no public key yet available */ + + for (p=item->hexgrip, i=0; i < 20; p += 2, i++) + grip[i] = xtoi_2 (p); + + if (!agent_key_available (grip)) + continue; + + /* unknown - store it */ + rc = agent_card_readkey (item->id, &pubkey); + if (rc) + { + log_debug ("agent_card_readkey failed: %s\n", gnupg_strerror (rc)); + goto leave; + } + + { + unsigned char *shadow_info = make_shadow_info (serialno, item->id); + if (!shadow_info) + { + rc = GNUPG_Out_Of_Core; + xfree (pubkey); + goto leave; + } + rc = agent_shadow_key (pubkey, shadow_info, &shdkey); + xfree (shadow_info); + } + xfree (pubkey); + if (rc) + { + log_error ("shadowing the key failed: %s\n", gnupg_strerror (rc)); + goto leave; + } + n = gcry_sexp_canon_len (shdkey, 0, NULL, NULL); + assert (n); + + rc = agent_write_private_key (grip, shdkey, n, 0); + xfree (shdkey); + if (rc) + { + log_error ("error writing key: %s\n", gnupg_strerror (rc)); + goto leave; + } + + if (opt.verbose) + log_info ("stored\n"); + + if (assuan_context) + { + char *derbuf; + size_t derbuflen; + + rc = agent_card_readcert (item->id, &derbuf, &derbuflen); + if (rc) + { + log_error ("error reading certificate: %s\n", + gnupg_strerror (rc)); + goto leave; + } + + rc = assuan_send_data (assuan_context, derbuf, derbuflen); + xfree (derbuf); + if (!rc) + rc = assuan_send_data (assuan_context, NULL, 0); + if (!rc) + rc = assuan_write_line (assuan_context, "END"); + if (rc) + { + log_error ("sending certificate failed: %s\n", + assuan_strerror (rc)); + rc = map_assuan_err (rc); + goto leave; + } + } + } + + + leave: + xfree (serialno); + release_keypair_info (parm.info); + return rc; +} + + diff --git a/agent/query.c b/agent/query.c index 795e214e3..09aada0fd 100644 --- a/agent/query.c +++ b/agent/query.c @@ -110,6 +110,9 @@ getpin_cb (void *opaque, const void *buffer, size_t length) { struct entry_parm_s *parm = opaque; + if (!buffer) + return 0; + /* we expect the pin to fit on one line */ if (parm->lines || length >= parm->size) return ASSUAN_Too_Much_Data; diff --git a/scd/command.c b/scd/command.c index 30191dd95..5186a2fd4 100644 --- a/scd/command.c +++ b/scd/command.c @@ -25,6 +25,7 @@ #include <string.h> #include <ctype.h> #include <unistd.h> +#include <ksba.h> #include "scdaemon.h" #include "../assuan/assuan.h" @@ -297,6 +298,65 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line) } +/* 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> @@ -449,6 +509,7 @@ register_commands (ASSUAN_CONTEXT ctx) { "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 }, diff --git a/sm/ChangeLog b/sm/ChangeLog index e47057eb7..756cb45f8 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,5 +1,10 @@ 2002-03-06 Werner Koch <[email protected]> + * gpgsm.c: New command --learn-card + * call-agent.c (learn_cb,gpgsm_agent_learn): New. + + * gpgsm.c (main): Print error messages for non-implemented commands. + * base64.c (base64_reader_cb): Use case insensitive compare of the Content-Type string to detect plain base-64. diff --git a/sm/call-agent.c b/sm/call-agent.c index 14e2fdf34..1982b228c 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -32,7 +32,7 @@ #include "gpgsm.h" #include "../assuan/assuan.h" #include "i18n.h" - +#include "keydb.h" /* fixme: Move this to import.c */ static ASSUAN_CONTEXT agent_ctx = NULL; static int force_pipe_server = 0; @@ -49,6 +49,11 @@ struct genkey_parm_s { size_t sexplen; }; +struct learn_parm_s { + int error; + ASSUAN_CONTEXT ctx; + struct membuf *data; +}; struct membuf { size_t len; @@ -218,7 +223,8 @@ membuf_data_cb (void *opaque, const void *buffer, size_t length) { struct membuf *data = opaque; - put_membuf (data, buffer, length); + if (buffer) + put_membuf (data, buffer, length); return 0; } @@ -520,3 +526,86 @@ gpgsm_agent_havekey (const char *hexkeygrip) return map_assuan_err (rc); } + +static AssuanError +learn_cb (void *opaque, const void *buffer, size_t length) +{ + struct learn_parm_s *parm = opaque; + size_t len; + char *buf; + KsbaCert cert; + int rc; + + if (parm->error) + return 0; + + if (buffer) + { + put_membuf (parm->data, buffer, length); + return 0; + } + /* END encountered - process what we have */ + buf = get_membuf (parm->data, &len); + if (!buf) + { + parm->error = GNUPG_Out_Of_Core; + return 0; + } + + + /* FIXME: this shoudl go inot import.c */ + cert = ksba_cert_new (); + if (!cert) + { + parm->error = GNUPG_Out_Of_Core; + return 0; + } + rc = ksba_cert_init_from_mem (cert, buf, len); + if (rc) + { + log_error ("failed to parse a certificate: %s\n", ksba_strerror (rc)); + ksba_cert_release (cert); + parm->error = map_ksba_err (rc); + return 0; + } + + rc = gpgsm_basic_cert_check (cert); + if (rc) + log_error ("invalid certificate: %s\n", gnupg_strerror (rc)); + else + { + keydb_store_cert (cert); + log_error ("certificate stored\n"); + } + + ksba_cert_release (cert); + init_membuf (parm->data, 4096); + return 0; +} + +/* Call the agent to learn about a smartcard */ +int +gpgsm_agent_learn () +{ + int rc; + struct learn_parm_s learn_parm; + struct membuf data; + size_t len; + + rc = start_agent (); + if (rc) + return rc; + + init_membuf (&data, 4096); + learn_parm.error = 0; + learn_parm.ctx = agent_ctx; + learn_parm.data = &data; + rc = assuan_transact (agent_ctx, "LEARN --send", + learn_cb, &learn_parm, + NULL, NULL, NULL, NULL); + xfree (get_membuf (&data, &len)); + if (rc) + return map_assuan_err (rc); + return learn_parm.error; +} + diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 3aa35b78c..bc285034a 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -76,6 +76,7 @@ enum cmd_and_opt_values { aExportAll, aCheckKeys, aServer, + aLearnCard, oOptions, oDebug, @@ -217,6 +218,7 @@ static ARGPARSE_OPTS opts[] = { { aSendKeys, "send-keys" , 256, N_("export keys to a key server") }, { aRecvKeys, "recv-keys" , 256, N_("import keys from a key server") }, { aImport, "import", 256 , N_("import/merge keys")}, + { aLearnCard, "learn-card", 256 ,N_("register a smartcard")}, { aServer, "server", 256, N_("run in server mode")}, @@ -709,6 +711,8 @@ main ( int argc, char **argv) case aListKeys: set_cmd (&cmd, aListKeys); break; case aListSecretKeys: set_cmd (&cmd, aListSecretKeys); break; + case aLearnCard: set_cmd (&cmd, aLearnCard); break; + case aDeleteKey: set_cmd (&cmd, aDeleteKey); greeting=1; @@ -727,6 +731,7 @@ main ( int argc, char **argv) case aClearsign: set_cmd (&cmd, aClearsign); break; case aVerify: set_cmd (&cmd, aVerify); break; + /* output encoding selection */ case oArmor: ctrl.create_pem = 1; @@ -1069,6 +1074,7 @@ main ( int argc, char **argv) break; case aSignEncr: /* sign and encrypt the given file */ + log_error ("this command has not yet been implemented\n"); #if 0 if (argc > 1) wrong_args(_("--sign --encrypt [filename]")); @@ -1088,6 +1094,7 @@ main ( int argc, char **argv) break; case aClearsign: /* make a clearsig */ + log_error ("this command has not yet been implemented\n"); #if 0 if (argc > 1) wrong_args (_("--clearsign [filename]")); @@ -1109,6 +1116,7 @@ main ( int argc, char **argv) break; case aVerifyFiles: + log_error ("this command has not yet been implemented\n"); /* if ((rc = verify_files( argc, argv ))) */ /* log_error ("verify files failed: %s\n", gpg_errstr(rc) ); */ break; @@ -1125,6 +1133,7 @@ main ( int argc, char **argv) case aDeleteKey: if (argc != 1) wrong_args(_("--delete-key user-id")); + log_error ("this command has not yet been implemented\n"); /* username = make_username (fname); */ /* if( (rc = delete_key(username)) ) */ /* log_error ("%s: delete key failed: %s\n", username, gpg_errstr(rc) ); */ @@ -1146,6 +1155,7 @@ main ( int argc, char **argv) break; case aKeygen: /* generate a key */ + log_error ("this function is not yet available from the commandline\n"); /* if (opt.batch) */ /* { */ /* if (argc > 1) */ @@ -1169,10 +1179,12 @@ main ( int argc, char **argv) gpgsm_import (&ctrl, open_read (*argv)); } break; + case aExport: case aSendKeys: case aRecvKeys: + log_error ("this command has not yet been implemented\n"); /* sl = NULL; */ /* for ( ; argc; argc--, argv++ ) */ /* add_to_strlist (&sl, *argv); */ @@ -1185,7 +1197,21 @@ main ( int argc, char **argv) /* free_strlist (sl); */ break; - default: + + case aLearnCard: + if (argc) + wrong_args ("--learn-card"); + else + { + int rc = gpgsm_agent_learn (); + if (rc) + log_error ("error learning card: %s\n", gnupg_strerror (rc)); + } + break; + + + default: + log_error ("invalid command\n"); if (argc > 1) wrong_args(_("[filename]")); /* Issue some output for the unix newbie */ diff --git a/sm/gpgsm.h b/sm/gpgsm.h index bb75c56d1..fa5e3a003 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -216,6 +216,7 @@ int gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey); int gpgsm_agent_istrusted (KsbaCert cert); int gpgsm_agent_havekey (const char *hexkeygrip); int gpgsm_agent_marktrusted (KsbaCert cert); +int gpgsm_agent_learn (void); /*-- call-dirmngr.c --*/ int gpgsm_dirmngr_isvalid (KsbaCert cert); diff --git a/sm/import.c b/sm/import.c index 02db65555..80da8392a 100644 --- a/sm/import.c +++ b/sm/import.c @@ -86,3 +86,5 @@ gpgsm_import (CTRL ctrl, int in_fd) } + + |