diff --git a/ChangeLog b/ChangeLog index ad3acafc2..0fb4257c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2003-10-21 Werner Koch + + * configure.ac (PRINTABLE_OS_NAME): Remove special case for The + Hurd; Robert Millan reported that the uname test is now + sufficient. + 2003-10-16 David Shaw * configure.ac: Include -ldl when card support is used. diff --git a/configure.ac b/configure.ac index 5d24d1529..7050f4dfc 100644 --- a/configure.ac +++ b/configure.ac @@ -584,10 +584,6 @@ case "${target}" in *-linux*) PRINTABLE_OS_NAME="GNU/Linux" ;; -dnl let that after linux to avoid gnu-linux problems - *-gnu*) - PRINTABLE_OS_NAME="GNU/Hurd" - ;; *) PRINTABLE_OS_NAME=`uname -s || echo "Unknown"` ;; diff --git a/g10/ChangeLog b/g10/ChangeLog index 2c506c040..e2e2157e0 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,22 @@ +2003-10-21 Werner Koch + + * passphrase.c (ask_passphrase): Add optional promptid arg. + Changed all callers. + * cardglue.c (pin_cb): Use it here, so the machine interface can + tell whether the Admin PIN is requested. + + * cardglue.c (agent_scd_checkpin): New. + + * misc.c (openpgp_pk_algo_usage): Added AUTH usage. + + * app-openpgp.c (check_against_given_fingerprint): New. Factored + out that code elsewhere. + (do_check_pin): New. + * card-util.c (card_edit): New command "passwd". Add logic to + check the PIN in advance. + (card_status): Add new args to return the serial number. Changed + all callers. + 2003-10-14 David Shaw * import.c (import_one): Show the keyid when giving the Elgamal diff --git a/g10/apdu.c b/g10/apdu.c index 1587bfe5b..2b17ef53b 100644 --- a/g10/apdu.c +++ b/g10/apdu.c @@ -51,6 +51,13 @@ insertion of the card (1 = don't wait). */ +#ifdef _WIN32 +#define DLSTDCALL __stdcall +#else +#define DLSTDCALL +#endif + + /* A structure to collect information pertaining to one reader slot. */ struct reader_table_s { @@ -84,12 +91,12 @@ static struct reader_table_s reader_table[MAX_READER]; /* ct API function pointer. */ -static char (*CT_init) (unsigned short ctn, unsigned short Pn); -static char (*CT_data) (unsigned short ctn, unsigned char *dad, - unsigned char *sad, unsigned short lc, - unsigned char *cmd, unsigned short *lr, - unsigned char *rsp); -static char (*CT_close) (unsigned short ctn); +static char (* DLSTDCALL CT_init) (unsigned short ctn, unsigned short Pn); +static char (* DLSTDCALL CT_data) (unsigned short ctn, unsigned char *dad, + unsigned char *sad, unsigned short lc, + unsigned char *cmd, unsigned short *lr, + unsigned char *rsp); +static char (* DLSTDCALL CT_close) (unsigned short ctn); /* PC/SC constants and function pointer. */ #define PCSC_SCOPE_USER 0 @@ -117,34 +124,38 @@ struct pcsc_io_request_s { typedef struct pcsc_io_request_s *pcsc_io_request_t; -long (*pcsc_establish_context) (unsigned long scope, - const void *reserved1, - const void *reserved2, - unsigned long *r_context); -long (*pcsc_release_context) (unsigned long context); -long (*pcsc_list_readers) (unsigned long context, const char *groups, - char *readers, unsigned long *readerslen); -long (*pcsc_connect) (unsigned long context, - const char *reader, - unsigned long share_mode, - unsigned long preferred_protocols, - unsigned long *r_card, - unsigned long *r_active_protocol); -long (*pcsc_disconnect) (unsigned long card, unsigned long disposition); -long (*pcsc_status) (unsigned long card, - char *reader, unsigned long *readerlen, - unsigned long *r_state, unsigned long *r_protocol, - unsigned char *atr, unsigned long *atrlen); -long (*pcsc_begin_transaction) (unsigned long card); -long (*pcsc_end_transaction) (unsigned long card); -long (*pcsc_transmit) (unsigned long card, - const pcsc_io_request_t send_pci, - const unsigned char *send_buffer, - unsigned long send_len, - pcsc_io_request_t recv_pci, - unsigned char *recv_buffer, - unsigned long *recv_len); -long (*pcsc_set_timeout) (unsigned long context, unsigned long timeout); +long (* DLSTDCALL pcsc_establish_context) (unsigned long scope, + const void *reserved1, + const void *reserved2, + unsigned long *r_context); +long (* DLSTDCALL pcsc_release_context) (unsigned long context); +long (* DLSTDCALL pcsc_list_readers) (unsigned long context, + const char *groups, + char *readers, unsigned long*readerslen); +long (* DLSTDCALL pcsc_connect) (unsigned long context, + const char *reader, + unsigned long share_mode, + unsigned long preferred_protocols, + unsigned long *r_card, + unsigned long *r_active_protocol); +long (* DLSTDCALL pcsc_disconnect) (unsigned long card, + unsigned long disposition); +long (* DLSTDCALL pcsc_status) (unsigned long card, + char *reader, unsigned long *readerlen, + unsigned long *r_state, + unsigned long *r_protocol, + unsigned char *atr, unsigned long *atrlen); +long (* DLSTDCALL pcsc_begin_transaction) (unsigned long card); +long (* DLSTDCALL pcsc_end_transaction) (unsigned long card); +long (* DLSTDCALL pcsc_transmit) (unsigned long card, + const pcsc_io_request_t send_pci, + const unsigned char *send_buffer, + unsigned long send_len, + pcsc_io_request_t recv_pci, + unsigned char *recv_buffer, + unsigned long *recv_len); +long (* DLSTDCALL pcsc_set_timeout) (unsigned long context, + unsigned long timeout); diff --git a/g10/app-common.h b/g10/app-common.h index 33f23127b..de1e02cac 100644 --- a/g10/app-common.h +++ b/g10/app-common.h @@ -65,6 +65,9 @@ struct app_ctx_s { const char *chvnostr, int reset_mode, int (*pincb)(void*, const char *, char **), void *pincb_arg); + int (*check_pin) (APP app, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg); } fnc; @@ -106,6 +109,9 @@ int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer); int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, int (*pincb)(void*, const char *, char **), void *pincb_arg); +int app_check_pin (APP app, const char *keyidstr, + int (*pincb)(void*, const char *, char **), + void *pincb_arg); /*-- app-openpgp.c --*/ diff --git a/g10/app-openpgp.c b/g10/app-openpgp.c index 174d2e974..07abf9bfb 100644 --- a/g10/app-openpgp.c +++ b/g10/app-openpgp.c @@ -912,6 +912,33 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr) } + /* If a fingerprint has been specified check it against the one on + the card. This is allows for a meaningful error message in case + the key on the card has been replaced but the shadow information + known to gpg was not updated. If there is no fingerprint we + assume that this is okay. */ +static int +check_against_given_fingerprint (APP app, const char *fpr, int keyno) +{ + unsigned char tmp[20]; + const char *s; + int n; + + for (s=fpr, n=0; hexdigitp (s); s++, n++) + ; + if (n != 40) + return gpg_error (GPG_ERR_INV_ID); + else if (!*s) + ; /* okay */ + else + return gpg_error (GPG_ERR_INV_ID); + + for (s=fpr, n=0; n < 20; s += 2, n++) + tmp[n] = xtoi_2 (s); + return compare_fingerprint (app, keyno, tmp); +} + + /* Compute a digital signature on INDATA which is expected to be the raw message digest. For this application the KEYIDSTR consists of @@ -976,23 +1003,9 @@ do_sign (APP app, const char *keyidstr, int hashalgo, known to gpg was not updated. If there is no fingerprint, gpg will detect a bogus signature anyway due to the verify-after-signing feature. */ - if (fpr) - { - for (s=fpr, n=0; hexdigitp (s); s++, n++) - ; - if (n != 40) - return gpg_error (GPG_ERR_INV_ID); - else if (!*s) - ; /* okay */ - else - return gpg_error (GPG_ERR_INV_ID); - - for (s=fpr, n=0; n < 20; s += 2, n++) - tmp_sn[n] = xtoi_2 (s); - rc = compare_fingerprint (app, 1, tmp_sn); - if (rc) - return rc; - } + rc = fpr? check_against_given_fingerprint (app, fpr, 1) : 0; + if (rc) + return rc; if (hashalgo == GCRY_MD_SHA1) memcpy (data, sha1_prefix, 15); @@ -1107,23 +1120,9 @@ do_auth (APP app, const char *keyidstr, known to gpg was not updated. If there is no fingerprint, gpg will detect a bogus signature anyway due to the verify-after-signing feature. */ - if (fpr) - { - for (s=fpr, n=0; hexdigitp (s); s++, n++) - ; - if (n != 40) - return gpg_error (GPG_ERR_INV_ID); - else if (!*s) - ; /* okay */ - else - return gpg_error (GPG_ERR_INV_ID); - - for (s=fpr, n=0; n < 20; s += 2, n++) - tmp_sn[n] = xtoi_2 (s); - rc = compare_fingerprint (app, 3, tmp_sn); - if (rc) - return rc; - } + rc = fpr? check_against_given_fingerprint (app, fpr, 3) : 0; + if (rc) + return rc; rc = verify_chv2 (app, pincb, pincb_arg); if (!rc) @@ -1177,23 +1176,9 @@ do_decipher (APP app, const char *keyidstr, the key on the card has been replaced but the shadow information known to gpg was not updated. If there is no fingerprint, the decryption will won't produce the right plaintext anyway. */ - if (fpr) - { - for (s=fpr, n=0; hexdigitp (s); s++, n++) - ; - if (n != 40) - return gpg_error (GPG_ERR_INV_ID); - else if (!*s) - ; /* okay */ - else - return gpg_error (GPG_ERR_INV_ID); - - for (s=fpr, n=0; n < 20; s += 2, n++) - tmp_sn[n] = xtoi_2 (s); - rc = compare_fingerprint (app, 2, tmp_sn); - if (rc) - return rc; - } + rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0; + if (rc) + return rc; rc = verify_chv2 (app, pincb, pincb_arg); if (!rc) @@ -1202,6 +1187,55 @@ do_decipher (APP app, const char *keyidstr, } +/* Perform a simple verify operation for CHV1 and CHV2, so that + further operations won't ask for CHV2 and it is possible to do a + cheap check on the PIN: If there is something wrong with the PIN + entry system, only the regular CHV will get blocked and not the + dangerous CHV3. KEYIDSTR is the usual card's serial number; an + optional fingerprint part will be ignored. */ +static int +do_check_pin (APP app, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg) +{ + unsigned char tmp_sn[20]; + const char *s; + int n; + + if (!keyidstr || !*keyidstr) + return gpg_error (GPG_ERR_INV_VALUE); + + /* Check whether an OpenPGP card of any version has been requested. */ + if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) + return gpg_error (GPG_ERR_INV_ID); + + for (s=keyidstr, n=0; hexdigitp (s); s++, n++) + ; + if (n != 32) + return gpg_error (GPG_ERR_INV_ID); + else if (!*s) + ; /* No fingerprint given: we allow this for now. */ + else if (*s == '/') + ; /* We ignore a fingerprint. */ + else + return gpg_error (GPG_ERR_INV_ID); + + for (s=keyidstr, n=0; n < 16; s += 2, n++) + tmp_sn[n] = xtoi_2 (s); + + if (app->serialnolen != 16) + return gpg_error (GPG_ERR_INV_CARD); + if (memcmp (app->serialno, tmp_sn, 16)) + return gpg_error (GPG_ERR_WRONG_CARD); + /* Yes, there is a race conditions: The user might pull the card + right here and we won't notice that. However this is not a + problem and the check above is merely for a graceful failure + between operations. */ + + return verify_chv2 (app, pincb, pincb_arg); +} + + /* Select the OpenPGP application on the card in SLOT. This function @@ -1262,6 +1296,7 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) app->fnc.auth = do_auth; app->fnc.decipher = do_decipher; app->fnc.change_pin = do_change_pin; + app->fnc.check_pin = do_check_pin; } leave: diff --git a/g10/card-util.c b/g10/card-util.c index 669927707..de445b796 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -255,13 +255,16 @@ fpr_is_zero (const char *fpr) /* Print all available information about the current card. */ void -card_status (FILE *fp) +card_status (FILE *fp, char *serialno, size_t serialnobuflen) { struct agent_card_info_s info; PKT_public_key *pk = xcalloc (1, sizeof *pk); int rc; unsigned int uval; + if (serialno && serialnobuflen) + *serialno = 0; + rc = agent_learn (&info); if (rc) { @@ -289,6 +292,13 @@ card_status (FILE *fp) return; } + if (!serialno) + ; + else if (strlen (serialno)+1 > serialnobuflen) + log_error ("serial number longer than expected\n"); + else + strcpy (serialno, info.serialno); + if (opt.with_colons) fputs ("openpgp-card:\n", fp); @@ -660,29 +670,33 @@ card_edit (STRLIST commands) cmdNOP = 0, cmdQUIT, cmdHELP, cmdLIST, cmdDEBUG, cmdNAME, cmdURL, cmdLOGIN, cmdLANG, cmdSEX, - cmdFORCESIG, cmdGENERATE, + cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdINVCMD }; static struct { const char *name; enum cmdids id; + int requires_pin; const char *desc; } cmds[] = { - { N_("quit") , cmdQUIT , N_("quit this menu") }, - { N_("q") , cmdQUIT , NULL }, - { N_("help") , cmdHELP , N_("show this help") }, - { "?" , cmdHELP , NULL }, - { N_("list") , cmdLIST , N_("list all available data") }, - { N_("l") , cmdLIST , NULL }, - { N_("debug") , cmdDEBUG , NULL }, - { N_("name") , cmdNAME , N_("change card holder's name") }, - { N_("url") , cmdURL , N_("change URL to retrieve key") }, - { N_("login") , cmdLOGIN , N_("change the login name") }, - { N_("lang") , cmdLANG , N_("change the language preferences") }, - { N_("sex") , cmdSEX , N_("change card holder's sex") }, - { N_("forcesig"), cmdFORCESIG, N_("toggle the signature force PIN flag") }, - { N_("generate"), cmdGENERATE, N_("generate new keys") }, + { N_("quit") , cmdQUIT , 0, N_("quit this menu") }, + { N_("q") , cmdQUIT , 0, NULL }, + { N_("help") , cmdHELP , 0, N_("show this help") }, + { "?" , cmdHELP , 0, NULL }, + { N_("list") , cmdLIST , 0, N_("list all available data") }, + { N_("l") , cmdLIST , 0, NULL }, + { N_("debug") , cmdDEBUG , 0, NULL }, + { N_("name") , cmdNAME , 1, N_("change card holder's name") }, + { N_("url") , cmdURL , 1, N_("change URL to retrieve key") }, + { N_("login") , cmdLOGIN , 1, N_("change the login name") }, + { N_("lang") , cmdLANG , 1, N_("change the language preferences") }, + { N_("sex") , cmdSEX , 1, N_("change card holder's sex") }, + { N_("forcesig"), + cmdFORCESIG, 1, N_("toggle the signature force PIN flag") }, + { N_("generate"), + cmdGENERATE, 1, N_("generate new keys") }, + { N_("passwd"), cmdPASSWD, 0, N_("menu to change or unblock the PIN") }, { NULL, cmdINVCMD } }; @@ -690,6 +704,9 @@ card_edit (STRLIST commands) int have_commands = !!commands; int redisplay = 1; char *answer = NULL; + int did_checkpin = 0; + char serialnobuf[50]; + if (opt.command_fd != -1) ; @@ -705,18 +722,19 @@ card_edit (STRLIST commands) const char *arg_string = ""; char *p; int i; - + int requires_pin; + tty_printf("\n"); if (redisplay ) { if (opt.with_colons) { - card_status (stdout); + card_status (stdout, serialnobuf, DIM (serialnobuf)); fflush (stdout); } else { - card_status (NULL); + card_status (NULL, serialnobuf, DIM (serialnobuf)); tty_printf("\n"); } redisplay = 0; @@ -750,6 +768,7 @@ card_edit (STRLIST commands) while( *answer == '#' ); arg_number = 0; /* Yes, here is the init which egcc complains about */ + requires_pin = 0; if (!*answer) cmd = cmdLIST; /* Default to the list command */ else if (*answer == CONTROL_D) @@ -769,7 +788,19 @@ card_edit (STRLIST commands) break; cmd = cmds[i].id; + requires_pin = cmds[i].requires_pin; } + + if (requires_pin && !did_checkpin) + { + int rc = agent_scd_checkpin (serialnobuf); + if (rc) + { + log_error ("error checking the PIN: %s\n", gpg_strerror (rc)); + continue; + } + did_checkpin = 1; + } switch (cmd) { @@ -811,6 +842,11 @@ card_edit (STRLIST commands) generate_card_keys (); break; + case cmdPASSWD: + change_pin (0); + did_checkpin = 0; /* Need to reset it of course. */ + break; + case cmdQUIT: goto leave; diff --git a/g10/cardglue.c b/g10/cardglue.c index 0cbb70336..b258e390f 100644 --- a/g10/cardglue.c +++ b/g10/cardglue.c @@ -508,13 +508,16 @@ pin_cb (void *opaque, const char *info, char **retstr) { char *value; int canceled; + int isadmin = (info && strstr (info, "dmin")); + *retstr = NULL; log_debug ("asking for PIN '%s'\n", info); value = ask_passphrase (info, - info && strstr (info, "dmin")? - _("Enter Admin PIN: ") : _("Enter PIN: "), + isadmin? "passphrase.adminpin.ask" + : "passphrase.pin.ask", + isadmin? _("Enter Admin PIN: ") : _("Enter PIN: "), &canceled); if (!value && canceled) return -1; @@ -645,7 +648,6 @@ agent_scd_pkdecrypt (const char *serialno, const unsigned char *indata, size_t indatalen, unsigned char **r_buf, size_t *r_buflen) { - APP app; *r_buf = NULL; @@ -680,3 +682,18 @@ agent_scd_change_pin (int chvno) pin_cb, NULL); } +/* Perform a CHECKPIN operation. SERIALNO should be the seriial + number of the card - optioanlly followed by the fingerprint; + however the fingerprint is ignored here. */ +int +agent_scd_checkpin (const char *serialnobuf) +{ + APP app; + + app = current_app? current_app : open_card (); + if (!app) + return gpg_error (GPG_ERR_CARD); + + return app->fnc.check_pin (app, serialnobuf, pin_cb, NULL); +} + diff --git a/g10/cardglue.h b/g10/cardglue.h index b0ad8aa61..b2f020589 100644 --- a/g10/cardglue.h +++ b/g10/cardglue.h @@ -158,6 +158,10 @@ int agent_scd_pkdecrypt (const char *serialno, /* Change the PIN of an OpenPGP card or reset the retry counter. */ int agent_scd_change_pin (int chvno); +/* Send a CHECKPIN command. */ +int agent_scd_checkpin (const char *serialnobuf); + + #endif /*ENABLE_CARD_SUPPORT*/ #endif /*GNUPG_G10_CARDGLUE_H*/ diff --git a/g10/g10.c b/g10/g10.c index 60a6aaef4..b63987989 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -2914,7 +2914,7 @@ main( int argc, char **argv ) case aCardStatus: if (argc) wrong_args ("--card-status"); - card_status (stdout); + card_status (stdout, NULL, 0); break; case aCardEdit: diff --git a/g10/keydb.h b/g10/keydb.h index d02e427d8..900d0267c 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -186,8 +186,8 @@ int build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list, int have_static_passphrase(void); void read_passphrase_from_fd( int fd ); void passphrase_clear_cache ( u32 *keyid, int algo ); -char *ask_passphrase (const char *description, const char *prompt, - int *canceled); +char *ask_passphrase (const char *description, const char *promptid, + const char *prompt, int *canceled); DEK *passphrase_to_dek( u32 *keyid, int pubkey_algo, int cipher_algo, STRING2KEY *s2k, int mode, const char *tryagain_text, int *canceled); diff --git a/g10/main.h b/g10/main.h index 6bfa3cd00..b64e33577 100644 --- a/g10/main.h +++ b/g10/main.h @@ -244,7 +244,7 @@ void unblock_all_signals(void); #ifdef ENABLE_CARD_SUPPORT /*-- card-util.c --*/ void change_pin (int no); -void card_status (FILE *fp); +void card_status (FILE *fp, char *serialno, size_t serialnobuflen); void card_edit (STRLIST commands); #endif diff --git a/g10/misc.c b/g10/misc.c index 01a38a4f3..727c49920 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -258,7 +258,7 @@ openpgp_pk_algo_usage ( int algo ) /* they are hardwired in gpg 1.0 */ switch ( algo ) { case PUBKEY_ALGO_RSA: - use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; + use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_RSA_E: use = PUBKEY_USAGE_ENC; @@ -270,10 +270,10 @@ openpgp_pk_algo_usage ( int algo ) use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_DSA: - use = PUBKEY_USAGE_SIG; + use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_ELGAMAL: - use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; + use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH; break; default: break; diff --git a/g10/passphrase.c b/g10/passphrase.c index 10dd3ff81..d3989bbe0 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -1007,7 +1007,9 @@ passphrase_clear_cache ( u32 *keyid, int algo ) * Ask for a passphrase and return that string. */ char * -ask_passphrase (const char *description, const char *prompt, int *canceled) +ask_passphrase (const char *description, + const char *promptid, + const char *prompt, int *canceled) { char *pw = NULL; @@ -1042,7 +1044,7 @@ ask_passphrase (const char *description, const char *prompt, int *canceled) pw = NULL; } else { - pw = cpr_get_hidden("passphrase.ask", + pw = cpr_get_hidden(promptid? promptid : "passphrase.ask", prompt?prompt : _("Enter passphrase: ") ); tty_kill_prompt(); }