From a9c317a95c440a083809346d61cdb78abff71b12 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 6 Mar 2009 17:31:27 +0000 Subject: [PATCH] New gpg-agent command to list key information. Gpgsm does now print the S/N of cards. Consider ephemeral keys during listing an export. --- NEWS | 6 ++ agent/ChangeLog | 15 ++++- agent/agent.h | 5 ++ agent/command.c | 161 +++++++++++++++++++++++++++++++++++++++++++- agent/divert-scd.c | 40 +++-------- agent/findkey.c | 130 +++++++++++++++++++++++++---------- agent/protect.c | 67 +++++++++++++++++- common/ChangeLog | 4 ++ common/sexputil.c | 38 ++++++++++- common/util.h | 4 +- doc/gpgsm.texi | 14 ++-- kbx/keybox-search.c | 2 +- scd/ChangeLog | 7 ++ scd/app-nks.c | 139 +++++++++++++++----------------------- sm/ChangeLog | 13 +++- sm/call-agent.c | 67 +++++++++++++++++- sm/export.c | 22 +++--- sm/fingerprint.c | 4 +- sm/gpgsm.h | 2 + sm/keylist.c | 41 ++++++++++- 20 files changed, 601 insertions(+), 180 deletions(-) diff --git a/NEWS b/NEWS index afeba6861..bfa343bb4 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,12 @@ Noteworthy changes in version 2.0.12 ------------------------------------------------- + * GPGSM now always lists ephemeral certificates if specified by + fingerprint or keygrip. + + * New command "KEYINFO" for GPG_AGENT. GPGSM now also returns + information about smartcards. + Noteworthy changes in version 2.0.11 (2009-03-03) ------------------------------------------------- diff --git a/agent/ChangeLog b/agent/ChangeLog index e016377a1..cae70f3bb 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,16 @@ +2009-03-06 Werner Koch + + * command.c (cmd_keyinfo): New command. + (register_commands): Register it. + (agent_write_status): Make sure not to print LR or CR. + * divert-scd.c (ask_for_card): Factor shadow info parsing out to ... + * protect.c (parse_shadow_info): New. + * findkey.c (agent_key_from_file): Use make_canon_sexp. + (agent_write_private_key, unprotect, read_key_file) + (agent_key_available): Use bin2hex. + (agent_key_info_from_file): New. + (read_key_file): Log no error message for ENOENT. + 2009-03-05 Werner Koch * divert-scd.c (getpin_cb): Support flag 'P'. Change max_digits @@ -2227,7 +2240,7 @@ Fri Aug 18 14:27:14 CEST 2000 Werner Koch Copyright 2001, 2002, 2003, 2004, 2005, - 2007 Free Software Foundation, Inc. + 2007, 2008, 2009 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without diff --git a/agent/agent.h b/agent/agent.h index 0e2cc9f40..fa2c61d06 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -233,6 +233,9 @@ gpg_error_t agent_public_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result); int agent_key_available (const unsigned char *grip); +gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, + int *r_keytype, + unsigned char **r_shadow_info); /*-- call-pinentry.c --*/ void initialize_module_call_pinentry (void); @@ -294,6 +297,8 @@ int agent_shadow_key (const unsigned char *pubkey, unsigned char **result); int agent_get_shadow_info (const unsigned char *shadowkey, unsigned char const **shadow_info); +gpg_error_t parse_shadow_info (const unsigned char *shadow_info, + char **r_hexsn, char **r_idstr); /*-- trustlist.c --*/ diff --git a/agent/command.c b/agent/command.c index 9ebcd091f..ba0f8fc4c 100644 --- a/agent/command.c +++ b/agent/command.c @@ -1,6 +1,6 @@ /* command.c - gpg-agent command handler * Copyright (C) 2001, 2002, 2003, 2004, 2005, - * 2006, 2008 Free Software Foundation, Inc. + * 2006, 2008, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -30,6 +30,9 @@ #include #include #include +#include +#include +#include #include @@ -308,8 +311,21 @@ agent_write_status (ctrl_t ctrl, const char *keyword, ...) *p++ = ' '; n++; } - for ( ; *text && n < DIM (buf)-2; n++) - *p++ = *text++; + for ( ; *text && n < DIM (buf)-3; n++, text++) + { + if (*text == '\n') + { + *p++ = '\\'; + *p++ = 'n'; + } + else if (*text == '\r') + { + *p++ = '\\'; + *p++ = 'r'; + } + else + *p++ = *text; + } } *p = 0; err = assuan_write_status (ctx, keyword, buf); @@ -806,7 +822,145 @@ cmd_readkey (assuan_context_t ctx, char *line) } + +/* KEYINFO [--list] + Return information about the key specified by the KEYGRIP. If the + key is not available GPG_ERR_NOT_FOUND is returned. If the option + --list is given the keygrip is ignored and information about all + available keys are returned. The information is returned as a + status line with this format: + + KEYINFO + + KEYGRIP is the keygrip. + + TYPE is describes the type of the key: + 'D' - Regular key stored on disk, + 'T' - Key is stored on a smartcard (token). + '-' - Unknown type. + + SERIALNO is an ASCII string with the serial number of the + smartcard. If the serial number is not known a single + dash '-' is used instead. + + IDSTR is the IDSTR used to distinguish keys on a smartcard. If it + is not known a dash is used instead. + + More information may be added in the future. +*/ +static gpg_error_t +do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip) +{ + gpg_error_t err; + char hexgrip[40+1]; + int keytype; + unsigned char *shadow_info = NULL; + char *serialno = NULL; + char *idstr = NULL; + const char *keytypestr; + + err = agent_key_info_from_file (ctrl, grip, &keytype, &shadow_info); + if (err) + goto leave; + + /* Reformat the grip so that we use uppercase as good style. */ + bin2hex (grip, 20, hexgrip); + + if (keytype == PRIVATE_KEY_CLEAR + || keytype == PRIVATE_KEY_PROTECTED) + keytypestr = "D"; + else if (keytype == PRIVATE_KEY_SHADOWED) + keytypestr = "T"; + else + keytypestr = "-"; + + if (shadow_info) + { + err = parse_shadow_info (shadow_info, &serialno, &idstr); + if (err) + goto leave; + } + + err = agent_write_status (ctrl, "KEYINFO", + hexgrip, + keytypestr, + serialno? serialno : "-", + idstr? idstr : "-", + NULL); + leave: + xfree (shadow_info); + xfree (serialno); + xfree (idstr); + return err; +} + + +static int +cmd_keyinfo (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int err; + unsigned char grip[20]; + DIR *dir = NULL; + int list_mode; + + list_mode = has_option (line, "--list"); + line = skip_options (line); + + if (list_mode) + { + char *dirname; + struct dirent *dir_entry; + char hexgrip[41]; + + dirname = make_filename_try (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, NULL); + if (!dirname) + { + err = gpg_error_from_syserror (); + goto leave; + } + dir = opendir (dirname); + if (!dir) + { + err = gpg_error_from_syserror (); + xfree (dirname); + goto leave; + } + xfree (dirname); + + while ( (dir_entry = readdir (dir)) ) + { + if (strlen (dir_entry->d_name) != 44 + || strcmp (dir_entry->d_name + 40, ".key")) + continue; + strncpy (hexgrip, dir_entry->d_name, 40); + hexgrip[40] = 0; + + if ( hex2bin (hexgrip, grip, 20) < 0 ) + continue; /* Bad hex string. */ + + err = do_one_keyinfo (ctrl, grip); + if (err) + goto leave; + } + err = 0; + } + else + { + err = parse_keygrip (ctx, line, grip); + if (err) + goto leave; + err = do_one_keyinfo (ctrl, grip); + } + + leave: + if (dir) + closedir (dir); + if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND) + log_error ("command keyinfo failed: %s\n", gpg_strerror (err)); + return err; +} @@ -1574,6 +1728,7 @@ register_commands (assuan_context_t ctx) { "GETEVENTCOUNTER",cmd_geteventcounter }, { "ISTRUSTED", cmd_istrusted }, { "HAVEKEY", cmd_havekey }, + { "KEYINFO", cmd_keyinfo }, { "SIGKEY", cmd_sigkey }, { "SETKEY", cmd_sigkey }, { "SETKEYDESC", cmd_setkeydesc }, diff --git a/agent/divert-scd.c b/agent/divert-scd.c index a583f1a61..fd8c28b66 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -28,16 +28,14 @@ #include #include "agent.h" -#include "sexp-parse.h" #include "i18n.h" +#include "sexp-parse.h" static int ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) { int rc, i; - const unsigned char *s; - size_t n; char *serialno; int no_card = 0; char *desc; @@ -45,39 +43,19 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) int want_sn_displen; *r_kid = NULL; - s = shadow_info; - if (*s != '(') - return gpg_error (GPG_ERR_INV_SEXP); - s++; - n = snext (&s); - if (!n) - return gpg_error (GPG_ERR_INV_SEXP); - want_sn = xtrymalloc (n*2+1); - if (!want_sn) - return out_of_core (); - for (i=0; i < n; i++) - sprintf (want_sn+2*i, "%02X", s[i]); - s += n; + + rc = parse_shadow_info (shadow_info, &want_sn, &want_kid); + if (rc) + return rc; + /* We assume that a 20 byte serial number is a standard one which - seems to have the property to have a zero in the last nibble. We - don't display this '0' because it may confuse the user */ + has the property to have a zero in the last nibble (Due to BCD + representation). We don't display this '0' because it may + confuse the user. */ want_sn_displen = strlen (want_sn); if (want_sn_displen == 20 && want_sn[19] == '0') want_sn_displen--; - n = snext (&s); - if (!n) - return gpg_error (GPG_ERR_INV_SEXP); - want_kid = xtrymalloc (n+1); - if (!want_kid) - { - gpg_error_t tmperr = out_of_core (); - xfree (want_sn); - return tmperr; - } - memcpy (want_kid, s, n); - want_kid[n] = 0; - for (;;) { rc = agent_card_serialno (ctrl, &serialno); diff --git a/agent/findkey.c b/agent/findkey.c index 0bb6afdcb..5bea198dc 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -56,14 +56,12 @@ 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]; int fd; - for (i=0; i < 20; i++) - sprintf (hexgrip+2*i, "%02X", grip[i]); + bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); @@ -307,14 +305,12 @@ unprotect (ctrl_t ctrl, const char *desc_text, { struct pin_entry_info_s *pi; struct try_unprotect_arg_s arg; - int rc, i; + int rc; unsigned char *result; size_t resultlen; char hexgrip[40+1]; - for (i=0; i < 20; i++) - sprintf (hexgrip+2*i, "%02X", grip[i]); - hexgrip[40] = 0; + bin2hex (grip, 20, hexgrip); /* First try to get it from the cache - if there is none or we can't unprotect it, we fall back to ask the user */ @@ -425,7 +421,7 @@ unprotect (ctrl_t ctrl, const char *desc_text, static gpg_error_t read_key_file (const unsigned char *grip, gcry_sexp_t *result) { - int i, rc; + int rc; char *fname; FILE *fp; struct stat st; @@ -436,8 +432,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) *result = NULL; - for (i=0; i < 20; i++) - sprintf (hexgrip+2*i, "%02X", grip[i]); + bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); @@ -445,7 +440,8 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) if (!fp) { rc = gpg_error_from_syserror (); - log_error ("can't open `%s': %s\n", fname, strerror (errno)); + if (gpg_err_code (rc) != GPG_ERR_ENOENT) + log_error ("can't open `%s': %s\n", fname, strerror (errno)); xfree (fname); return rc; } @@ -488,11 +484,11 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) /* Return the secret key as an S-Exp in RESULT after locating it using - the grip. Returns NULL in RESULT if the operation should be - diverted to a token; SHADOW_INFO will point then to an allocated - S-Expression with the shadow_info part from the file. CACHE_MODE - defines now the cache shall be used. DESC_TEXT may be set to - present a custom description for the pinentry. */ + the GRIP. Stores NULL at RESULT if the operation shall be diverted + to a token; in this case an allocated S-expression with the + shadow_info part from the file is stored at SHADOW_INFO. + CACHE_MODE defines now the cache shall be used. DESC_TEXT may be + set to present a custom description for the pinentry. */ gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *desc_text, const unsigned char *grip, unsigned char **shadow_info, @@ -513,20 +509,11 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text, return rc; /* For use with the protection functions we also need the key as an - canonical encoded S-expression in abuffer. Create this buffer + canonical encoded S-expression in a buffer. Create this buffer now. */ - len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0); - assert (len); - buf = xtrymalloc (len); - if (!buf) - { - rc = gpg_error_from_syserror (); - gcry_sexp_release (s_skey); - return rc; - } - len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len); - assert (len); - + rc = make_canon_sexp (s_skey, &buf, &len); + if (rc) + return rc; switch (agent_private_key_type (buf)) { @@ -842,19 +829,94 @@ agent_public_key_from_file (ctrl_t ctrl, int agent_key_available (const unsigned char *grip) { - int i; + int result; char *fname; char hexgrip[40+4+1]; - for (i=0; i < 20; i++) - sprintf (hexgrip+2*i, "%02X", grip[i]); + bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); - i = !access (fname, R_OK)? 0 : -1; + result = !access (fname, R_OK)? 0 : -1; xfree (fname); - return i; + return result; } +/* Return the information about the secret key specified by the binary + keygrip GRIP. If the key is a shadowed one the shadow information + will be stored at the address R_SHADOW_INFO as an allocated + S-expression. */ +gpg_error_t +agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, + int *r_keytype, unsigned char **r_shadow_info) +{ + gpg_error_t err; + unsigned char *buf; + size_t len; + int keytype; + + (void)ctrl; + + if (r_keytype) + *r_keytype = PRIVATE_KEY_UNKNOWN; + if (r_shadow_info) + *r_shadow_info = NULL; + + { + gcry_sexp_t sexp; + + err = read_key_file (grip, &sexp); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_ENOENT) + return gpg_error (GPG_ERR_NOT_FOUND); + else + return err; + } + err = make_canon_sexp (sexp, &buf, &len); + gcry_sexp_release (sexp); + if (err) + return err; + } + + keytype = agent_private_key_type (buf); + switch (keytype) + { + case PRIVATE_KEY_CLEAR: + break; + case PRIVATE_KEY_PROTECTED: + /* If we ever require it we could retrieve the comment fields + from such a key. */ + break; + case PRIVATE_KEY_SHADOWED: + if (r_shadow_info) + { + const unsigned char *s; + size_t n; + + err = agent_get_shadow_info (buf, &s); + if (!err) + { + n = gcry_sexp_canon_len (s, 0, NULL, NULL); + assert (n); + *r_shadow_info = xtrymalloc (n); + if (!*r_shadow_info) + err = gpg_error_from_syserror (); + else + memcpy (*r_shadow_info, s, n); + } + } + break; + default: + err = gpg_error (GPG_ERR_BAD_SECKEY); + break; + } + + if (!err && r_keytype) + *r_keytype = keytype; + + xfree (buf); + return err; +} diff --git a/agent/protect.c b/agent/protect.c index ebb02ac89..8b022ecfb 100644 --- a/agent/protect.c +++ b/agent/protect.c @@ -1,6 +1,6 @@ /* protect.c - Un/Protect a secret key * Copyright (C) 1998, 1999, 2000, 2001, 2002, - * 2003, 2007 Free Software Foundation, Inc. + * 2003, 2007, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -1105,3 +1105,68 @@ agent_get_shadow_info (const unsigned char *shadowkey, return 0; } + +/* Parse the canonical encoded SHADOW_INFO S-expression. On success + the hex encoded serial number is returned as a malloced strings at + R_HEXSN and the Id string as a malloced string at R_IDSTR. On + error an error code is returned and NULL is stored at the result + parameters addresses. If the serial number or the ID string is not + required, NULL may be passed for them. */ +gpg_error_t +parse_shadow_info (const unsigned char *shadow_info, + char **r_hexsn, char **r_idstr) +{ + const unsigned char *s; + size_t n; + + if (r_hexsn) + *r_hexsn = NULL; + if (r_idstr) + *r_idstr = NULL; + + s = shadow_info; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + + if (r_hexsn) + { + *r_hexsn = bin2hex (s, n, NULL); + if (!*r_hexsn) + return gpg_error_from_syserror (); + } + s += n; + + n = snext (&s); + if (!n) + { + if (r_hexsn) + { + xfree (*r_hexsn); + *r_hexsn = NULL; + } + return gpg_error (GPG_ERR_INV_SEXP); + } + + if (r_idstr) + { + *r_idstr = xtrymalloc (n+1); + if (!*r_idstr) + { + if (r_hexsn) + { + xfree (*r_hexsn); + *r_hexsn = NULL; + } + return gpg_error_from_syserror (); + } + memcpy (*r_idstr, s, n); + (*r_idstr)[n] = 0; + } + + return 0; +} + diff --git a/common/ChangeLog b/common/ChangeLog index 61ae679b4..f5ba7d725 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,7 @@ +2009-03-06 Werner Koch + + * sexputil.c (make_canon_sexp): New. + 2009-03-03 Werner Koch * exechelp.c (do_exec): Make sure that /dev/null connected FDs are diff --git a/common/sexputil.c b/common/sexputil.c index 4907a9355..4ff7b4955 100644 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -1,5 +1,5 @@ /* sexputil.c - Utility functions for S-expressions. - * Copyright (C) 2005, 2007 Free Software Foundation, Inc. + * Copyright (C) 2005, 2007, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -34,6 +34,42 @@ #include "util.h" #include "sexp-parse.h" + +/* Helper function to create a a canonical encoded S-expression from a + Libgcrypt S-expression object. The function returns 0 on success + and the malloced canonical S-expression is stored at R_BUFFER and + the allocated length at R_BUFLEN. On error an error code is + returned and (NULL, 0) stored at R_BUFFER and R_BUFLEN. If the + allocated buffer length is not required, NULL by be used for + R_BUFLEN. */ +gpg_error_t +make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen) +{ + size_t len; + unsigned char *buf; + + *r_buffer = NULL; + if (r_buflen) + *r_buflen = 0;; + + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); + if (!len) + return gpg_error (GPG_ERR_BUG); + buf = xtrymalloc (len); + if (!buf) + return gpg_error_from_syserror (); + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len); + if (!len) + return gpg_error (GPG_ERR_BUG); + + *r_buffer = buf; + if (r_buflen) + *r_buflen = len; + + return 0; +} + + /* Return the so called "keygrip" which is the SHA-1 hash of the public key parameters expressed in a way depended on the algorithm. diff --git a/common/util.h b/common/util.h index 66569e27e..d117f86bf 100644 --- a/common/util.h +++ b/common/util.h @@ -1,5 +1,5 @@ /* util.h - Utility functions for GnuPG - * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -183,6 +183,8 @@ gpg_error_t b64dec_finish (struct b64state *state); /*-- sexputil.c */ +gpg_error_t make_canon_sexp (gcry_sexp_t sexp, + unsigned char **r_buffer, size_t *r_buflen); gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, unsigned char *grip); int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b); diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index b4c92bbfa..659d546f2 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -240,10 +240,12 @@ optional @var{pattern}. Those pattern consist of a list of user ids (@pxref{how-to-specify-a-user-id}). When used along with the @option{--armor} option a few informational lines are prepended before each block. There is one limitation: As there is no commonly agreed -upon way to pack more than one certificate into an ASN.1 structure, the -binary export (i.e. without using @option{armor}) works only for the -export of one certificate. Thus it is required to specify a -@var{pattern} which yields exactly one certificate. +upon way to pack more than one certificate into an ASN.1 structure, +the binary export (i.e. without using @option{armor}) works only for +the export of one certificate. Thus it is required to specify a +@var{pattern} which yields exactly one certificate. Ephemeral +certificate are only exported if all @var{pattern} are given as +fingerprints or keygrips. @item --export-secret-key-p12 @var{key-id} @opindex export @@ -601,7 +603,9 @@ forth to @var{epoch} which is the number of seconds elapsed since the year @item --with-ephemeral-keys @opindex with-ephemeral-keys -Include ephemeral flagged keys in the output of key listings. +Include ephemeral flagged keys in the output of key listings. Note +that they are included anyway if the key specification for a listing +is given as fingerprint or keygrip. @item --debug-level @var{level} @opindex debug-level diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c index 927399d48..08b59e649 100644 --- a/kbx/keybox-search.c +++ b/kbx/keybox-search.c @@ -457,7 +457,7 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr) #ifdef KEYBOX_WITH_X509 /* Return true if the key in BLOB matches the 20 bytes keygrip GRIP. - We don't have the keygrips as meta data, thus wen need to parse the + We don't have the keygrips as meta data, thus we need to parse the certificate. Fixme: We might want to return proper error codes instead of failing a search for invalid certificates etc. */ static int diff --git a/scd/ChangeLog b/scd/ChangeLog index b1ec7479e..efc888fd7 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,10 @@ +2009-03-06 Werner Koch + + * app-nks.c (do_learn_status): Factor code out to.. + (do_learn_status_core): .. new. + (do_readcert, do_sign, do_decipher): Switch to SigG if needed. + (verify_pin): Use DESC also for keypad based verify. + 2009-03-05 Werner Koch * app-openpgp.c (verify_a_chv): Remove special case for keypads. diff --git a/scd/app-nks.c b/scd/app-nks.c index 6aa205697..1a520bbf0 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -308,17 +308,20 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) - -static gpg_error_t -do_learn_status (app_t app, ctrl_t ctrl) +static void +do_learn_status_core (app_t app, ctrl_t ctrl, int is_sigg) { gpg_error_t err; char ct_buf[100], id_buf[100]; int i; + const char *tag; - err = switch_application (app, 0); - if (err) - return err; + if (is_sigg) + tag = "SIGG"; + else if (app->app_local->nks_version < 3) + tag = "DF01"; + else + tag = "NKS3"; /* Output information about all useful objects in the NKS application. */ for (i=0; filelist[i].fid; i++) @@ -326,7 +329,7 @@ do_learn_status (app_t app, ctrl_t ctrl) if (filelist[i].nks_ver > app->app_local->nks_version) continue; - if (filelist[i].is_sigg) + if (!!filelist[i].is_sigg != !!is_sigg) continue; if (filelist[i].certtype) @@ -342,8 +345,7 @@ do_learn_status (app_t app, ctrl_t ctrl) read that many bytes. */ snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype); snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X", - app->app_local->nks_version < 3? "DF01":"NKS3", - filelist[i].fid); + tag, filelist[i].fid); send_status_info (ctrl, "CERTINFO", ct_buf, strlen (ct_buf), id_buf, strlen (id_buf), @@ -361,8 +363,7 @@ do_learn_status (app_t app, ctrl_t ctrl) else { snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X", - app->app_local->nks_version < 3? "DF01":"NKS3", - filelist[i].fid); + tag, filelist[i].fid); send_status_info (ctrl, "KEYPAIRINFO", gripstr, 40, id_buf, strlen (id_buf), @@ -371,58 +372,26 @@ do_learn_status (app_t app, ctrl_t ctrl) } } + +} + + +static gpg_error_t +do_learn_status (app_t app, ctrl_t ctrl) +{ + gpg_error_t err; + + err = switch_application (app, 0); + if (err) + return err; + + do_learn_status_core (app, ctrl, 0); + err = switch_application (app, 1); if (err) - return 0; /* Silently ignore if we can't swicth to SigG. */ - - for (i=0; filelist[i].fid; i++) - { - if (filelist[i].nks_ver > app->app_local->nks_version) - continue; - - if (!filelist[i].is_sigg) - continue; - - if (filelist[i].certtype) - { - size_t len; - - len = app_help_read_length_of_cert (app->slot, - filelist[i].fid, NULL); - if (len) - { - /* FIXME: We should store the length in the application's - context so that a following readcert does only need to - read that many bytes. */ - snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype); - snprintf (id_buf, sizeof id_buf, "NKS-SIGG.%04X", - filelist[i].fid); - send_status_info (ctrl, "CERTINFO", - ct_buf, strlen (ct_buf), - id_buf, strlen (id_buf), - NULL, (size_t)0); - } - } - else if (filelist[i].iskeypair) - { - char gripstr[40+1]; - - err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr); - if (err) - log_error ("can't get keygrip from FID 0x%04X: %s\n", - filelist[i].fid, gpg_strerror (err)); - else - { - snprintf (id_buf, sizeof id_buf, "NKS-SIGG.%04X", - filelist[i].fid); - send_status_info (ctrl, "KEYPAIRINFO", - gripstr, 40, - id_buf, strlen (id_buf), - NULL, (size_t)0); - } - } - } + return 0; /* Silently ignore if we can't switch to SigG. */ + do_learn_status_core (app, ctrl, 1); return 0; } @@ -446,20 +415,24 @@ do_readcert (app_t app, const char *certid, int class, tag, constructed, ndef; size_t totobjlen, objlen, hdrlen; int rootca = 0; + int is_sigg = 0; *cert = NULL; *certlen = 0; - err = switch_application (app, 0); - if (err) - return err; - if (!strncmp (certid, "NKS-NKS3.", 9)) ; else if (!strncmp (certid, "NKS-DF01.", 9)) ; + else if (!strncmp (certid, "NKS-SIGG.", 9)) + is_sigg = 1; else return gpg_error (GPG_ERR_INV_ID); + + err = switch_application (app, is_sigg); + if (err) + return err; + certid += 9; if (!hexdigitp (certid) || !hexdigitp (certid+1) || !hexdigitp (certid+2) || !hexdigitp (certid+3) @@ -603,9 +576,7 @@ verify_pin (app_t app, int pwid, const char *desc, if (!opt.disable_keypad && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) ) { - rc = pincb (pincb_arg, - _("||Please enter your PIN at the reader's keypad"), - NULL); + rc = pincb (pincb_arg, desc, NULL); if (rc) { log_info (_("PIN callback returned error: %s\n"), @@ -613,11 +584,8 @@ verify_pin (app_t app, int pwid, const char *desc, return rc; } - /* Although it is possible to use a local PIN, we use the global - PIN for this application. */ - rc = iso7816_verify_kp (app->slot, 0, "", 0, &pininfo); - /* Dismiss the prompt. */ - pincb (pincb_arg, NULL, NULL); + rc = iso7816_verify_kp (app->slot, pwid, "", 0, &pininfo); + pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else { @@ -630,8 +598,6 @@ verify_pin (app_t app, int pwid, const char *desc, return rc; } - /* The following limits are due to TCOS but also defined in the - NKS specs. */ rc = basic_pin_checks (pinvalue, pininfo.minlen, pininfo.maxlen); if (rc) { @@ -675,6 +641,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; int rc, i; + int is_sigg = 0; int fid; unsigned char data[35]; /* Must be large enough for a SHA-1 digest + the largest OID _prefix above. */ @@ -684,19 +651,22 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, if (indatalen != 20 && indatalen != 16 && indatalen != 35) return gpg_error (GPG_ERR_INV_VALUE); - rc = switch_application (app, 0); - if (rc) - return rc; - /* Check that the provided ID is valid. This is not really needed but we do it to enforce correct usage by the caller. */ if (!strncmp (keyidstr, "NKS-NKS3.", 9) ) ; else if (!strncmp (keyidstr, "NKS-DF01.", 9) ) ; + else if (!strncmp (keyidstr, "NKS-SIGG.", 9) ) + is_sigg = 1; else return gpg_error (GPG_ERR_INV_ID); keyidstr += 9; + + rc = switch_application (app, is_sigg); + if (rc) + return rc; + if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) || keyidstr[4]) @@ -743,7 +713,6 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, - /* Decrypt the data in INDATA and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ @@ -759,24 +728,28 @@ do_decipher (app_t app, const char *keyidstr, 0x84, 1, 0x81 /* Select local secret key 1 for decryption. */ }; int rc, i; + int is_sigg = 0; int fid; if (!keyidstr || !*keyidstr || !indatalen) return gpg_error (GPG_ERR_INV_VALUE); - rc = switch_application (app, 0); - if (rc) - return rc; - /* Check that the provided ID is valid. This is not really needed but we do it to to enforce correct usage by the caller. */ if (!strncmp (keyidstr, "NKS-NKS3.", 9) ) ; else if (!strncmp (keyidstr, "NKS-DF01.", 9) ) ; + else if (!strncmp (keyidstr, "NKS-SIGG.", 9) ) + is_sigg = 1; else return gpg_error (GPG_ERR_INV_ID); keyidstr += 9; + + rc = switch_application (app, is_sigg); + if (rc) + return rc; + if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) || keyidstr[4]) diff --git a/sm/ChangeLog b/sm/ChangeLog index 83f4569ca..4d98dc50e 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,14 @@ +2009-03-06 Werner Koch + + * call-agent.c (gpgsm_agent_keyinfo, keyinfo_status_cb): New. + * keylist.c (list_cert_colon): Print card S/N. + + * keylist.c (list_internal_keys): Always list ephemeral keys if + specified by keygrip or fingerprint. + (list_cert_raw): Always show ephemeral flag. + * export.c (gpgsm_export): Export ephemeral keys if specified by + keygrip. + 2009-02-09 Werner Koch * gpgsm.c (main): Change default cipher back to 3DES. @@ -2451,7 +2462,7 @@ h2007-11-22 Werner Koch Copyright 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008 Free Software Foundation, Inc. + 2007, 2008, 2009 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without diff --git a/sm/call-agent.c b/sm/call-agent.c index 0add44aae..a6ac6da36 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -1,6 +1,6 @@ /* call-agent.c - Divert GPGSM operations to the agent * Copyright (C) 2001, 2002, 2003, 2005, 2007, - * 2008 Free Software Foundation, Inc. + * 2008, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -836,3 +836,68 @@ gpgsm_agent_send_nop (ctrl_t ctrl) } + +static int +keyinfo_status_cb (void *opaque, const char *line) +{ + char **serialno = opaque; + const char *s, *s2; + + if (!strncmp (line, "KEYINFO ", 8) && !*serialno) + { + s = strchr (line+8, ' '); + if (s && s[1] == 'T' && s[2] == ' ' && s[3]) + { + s += 3; + s2 = strchr (s, ' '); + if ( s2 > s ) + { + *serialno = xtrymalloc ((s2 - s)+1); + if (*serialno) + { + memcpy (*serialno, s, s2 - s); + (*serialno)[s2 - s] = 0; + } + } + } + } + return 0; +} + +/* Return the serial number for a secret key. If the returned serial + number is NULL, the key is not stored on a smartcard. Caller needs + to free R_SERIALNO. */ +gpg_error_t +gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno) +{ + gpg_error_t err; + char line[ASSUAN_LINELENGTH]; + char *serialno = NULL; + + *r_serialno = NULL; + + err = start_agent (ctrl); + if (err) + return err; + + if (!hexkeygrip || strlen (hexkeygrip) != 40) + return gpg_error (GPG_ERR_INV_VALUE); + + snprintf (line, DIM(line)-1, "KEYINFO %s", hexkeygrip); + line[DIM(line)-1] = 0; + + err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, + keyinfo_status_cb, &serialno); + if (!err && serialno) + { + /* Sanity check for bad characters. */ + if (strpbrk (serialno, ":\n\r")) + err = GPG_ERR_INV_VALUE; + } + if (err) + xfree (serialno); + else + *r_serialno = serialno; + return err; +} + diff --git a/sm/export.c b/sm/export.c index fa2e9de1a..f8e23cec1 100644 --- a/sm/export.c +++ b/sm/export.c @@ -1,5 +1,5 @@ /* export.c - Export certificates and private keys. - * Copyright (C) 2002, 2003, 2004, 2007 Free Software Foundation, Inc. + * Copyright (C) 2002, 2003, 2004, 2007, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -37,11 +37,11 @@ /* A table to store a fingerprint as used in a duplicates table. We - don't need to hash here because a fingerprint is alrady a perfect + don't need to hash here because a fingerprint is already a perfect hash value. This we use the most significant bits to index the table and then use a linked list for the overflow. Possible - enhancement for very large number of certictates: Add a second - level table and then resort to a linked list. */ + enhancement for very large number of certificates: Add a second + level table and then resort to a linked list. */ struct duptable_s { struct duptable_s *next; @@ -192,18 +192,16 @@ gpgsm_export (ctrl_t ctrl, strlist_t names, FILE *fp, estream_t stream) } } - /* If all specifications are done by fingerprint, we switch to - ephemeral mode so that _all_ currently available and matching - certificates are exported. - - fixme: we should in this case keep a list of certificates to - avoid accidential export of duplicate certificates. */ + /* If all specifications are done by fingerprint or keygrip, we + switch to ephemeral mode so that _all_ currently available and + matching certificates are exported. */ if (names && ndesc) { for (i=0; (i < ndesc && (desc[i].mode == KEYDB_SEARCH_MODE_FPR || desc[i].mode == KEYDB_SEARCH_MODE_FPR20 - || desc[i].mode == KEYDB_SEARCH_MODE_FPR16)); i++) + || desc[i].mode == KEYDB_SEARCH_MODE_FPR16 + || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++) ; if (i == ndesc) keydb_set_ephemeral (hd, 1); @@ -228,7 +226,7 @@ gpgsm_export (ctrl_t ctrl, strlist_t names, FILE *fp, estream_t stream) rc = insert_duptable (dtable, fpr, &exists); if (rc) { - log_error ("inserting into duplicates table fauiled: %s\n", + log_error ("inserting into duplicates table failed: %s\n", gpg_strerror (rc)); goto leave; } diff --git a/sm/fingerprint.c b/sm/fingerprint.c index 6581688a8..addf56296 100644 --- a/sm/fingerprint.c +++ b/sm/fingerprint.c @@ -196,8 +196,8 @@ gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array) return array; } -/* Return an allocated buffer with the keygrip of CERT in from of an - hexstring. NULL is returned in case of error */ +/* Return an allocated buffer with the keygrip of CERT encoded as a + hexstring. NULL is returned in case of error. */ char * gpgsm_get_keygrip_hexstring (ksba_cert_t cert) { diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 1460dc0d6..984dda135 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -395,6 +395,8 @@ int gpgsm_agent_learn (ctrl_t ctrl); int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc); gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc); gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl); +gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, + char **r_serialno); /*-- call-dirmngr.c --*/ int gpgsm_dirmngr_isvalid (ctrl_t ctrl, diff --git a/sm/keylist.c b/sm/keylist.c index b0d2f56dd..39b82c29d 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -1,6 +1,6 @@ /* keylist.c - Print certificates in various formats. * Copyright (C) 1998, 1999, 2000, 2001, 2003, - * 2004, 2005, 2008 Free Software Foundation, Inc. + * 2004, 2005, 2008, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -481,7 +481,24 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, es_putc (':', fp); /* Field 12, capabilities: */ print_capabilities (cert, fp); + /* Field 13, not used: */ es_putc (':', fp); + if (have_secret) + { + char *cardsn; + + p = gpgsm_get_keygrip_hexstring (cert); + if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && cardsn) + { + /* Field 14, not used: */ + es_putc (':', fp); + /* Field 15: Token serial number. */ + es_fputs (cardsn, fp); + es_putc (':', fp); + } + xfree (cardsn); + xfree (p); + } es_putc ('\n', fp); /* FPR record */ @@ -989,7 +1006,7 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err)); } - if (opt.with_ephemeral_keys && hd) + if (hd) { unsigned int blobflags; @@ -1275,6 +1292,7 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp, gpg_error_t rc = 0; const char *lastresname, *resname; int have_secret; + int want_ephemeral = opt.with_ephemeral_keys; hd = keydb_new (0); if (!hd) @@ -1319,7 +1337,24 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp, } - if (opt.with_ephemeral_keys) + /* If all specifications are done by fingerprint or keygrip, we + switch to ephemeral mode so that _all_ currently available and + matching certificates are listed. */ + if (!want_ephemeral && names && ndesc) + { + int i; + + for (i=0; (i < ndesc + && (desc[i].mode == KEYDB_SEARCH_MODE_FPR + || desc[i].mode == KEYDB_SEARCH_MODE_FPR20 + || desc[i].mode == KEYDB_SEARCH_MODE_FPR16 + || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++) + ; + if (i == ndesc) + want_ephemeral = 1; + } + + if (want_ephemeral) keydb_set_ephemeral (hd, 1); /* It would be nice to see which of the given users did actually