diff --git a/NEWS b/NEWS index cf096b66e..5eab68ef0 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,184 @@ Noteworthy changes in version 2.3.0 (unreleased) ------------------------------------------------ + Changes also found in 2.2.16: + + * gpg,gpgsm: Fix deadlock on Windows due to a keybox sharing + violation. [#4505] + + * gpg: Allow deletion of subkeys with --delete-key. This finally + makes the bang-suffix work as expected for that command. [#4457] + + * gpg: Replace SHA-1 by SHA-256 in self-signatures when updating + them with --quick-set-expire or --quick-set-primary-uid. [#4508] + + * gpg: Improve the photo image viewer selection. [#4334] + + * gpg: Fix decryption with --use-embedded-filename. [#4500] + + * gpg: Remove hints on using the --keyserver option. [#4512] + + * gpg: Fix export of certain secret keys with comments. [#4490] + + * gpg: Reject too long user-ids in --quick-gen-key. [#4532] + + * gpg: Fix a double free in the best key selection code. [#4462] + + * gpg: Fix the key generation dialog for switching back from EdDSA + to ECDSA. + + * gpg: Use AES-192 with SHA-384 to comply with RFC-6637. + + * gpg: Use only the addrspec from the Signer's UID subpacket to + mitigate a problem with another implementation. + + * gpg: Skip invalid packets during a keyring listing and sync + diagnostics with the output. + + * gpgsm: Avoid confusing diagnostic when signing with the default + key. [#4535] + + * agent: Do not delete any secret key in --dry-run mode. + + * agent: Fix failures on 64 bit big-endian boxes related to URIs in + a keyfile. [#4501] + + * agent: Stop scdaemon after a reload with disable-scdaemon newly + configured. [#4326] + + * dirmngr: Improve caching algorithm for WKD domains. + + * dirmngr: Support other hash algorithms than SHA-1 for OCSP. [#3966] + + * gpgconf: Make --homedir work for --launch. [#4496] + + * gpgconf: Before --launch check for a valid config file. [#4497] + + * wkd: Do not import more than 5 keys from one WKD address. + + * wkd: Accept keys which are stored in armored format in the + directory. + + * The installer for Windows now comes with signed binaries. + + Release-info: https://dev.gnupg.org/T4509 + See-also: gnupg-announce/2019q2/000438.html + + Changes also found in 2.2.15: + + * sm: Fix --logger-fd and --status-fd on Windows for non-standard + file descriptors. + + * sm: Allow decryption even if expired keys are configured. [#4431] + + * agent: Change command KEYINFO to print ssh fingerprints with other + hash algos. + + * dirmngr: Fix build problems on Solaris due to the use of reserved + symbol names. [#4420] + + * wkd: New commands --print-wkd-hash and --print-wkd-url for + gpg-wks-client. + + Release-info: https://dev.gnupg.org/T4434 + See-also: gnupg-announce/2019q1/000436.html + + Changes also found in 2.2.14: + + * gpg: Allow import of PGP desktop exported secret keys. Also avoid + importing secret keys if the secret keyblock is not valid. [#4392] + + * gpg: Make invalid primary key algo obvious in key listings. + + * sm: Do not mark a certificate in a key listing as de-vs compliant + if its use for a signature will not be possible. + + * sm: Fix certificate creation with key on card. + + * sm: Create rsa3072 bit certificates by default. + + * sm: Print Yubikey attestation extensions with --dump-cert. + + * agent: Fix cancellation handling for scdaemon. + + * agent: Support --mode=ssh option for CLEAR_PASSPHRASE. [#4340] + + * scd: Fix flushing of the CA-FPR DOs in app-openpgp. + + * scd: Avoid a conflict error with the "undefined" app. + + * dirmngr: Add CSRF protection exception for protonmail. + + * dirmngr: Fix build problems with gcc 9 in libdns. + + * gpgconf: New option --show-socket for use with --launch. + + * gpgtar: Make option -C work for archive creation. + + Release-info: https://dev.gnupg.org/T4412 + See-also: gnupg-announce/2019q1/000435.html + + Changes also found in 2.2.13: + + * gpg: Implement key lookup via keygrip (using the & prefix). + + * gpg: Allow generating Ed25519 key from existing key. + + * gpg: Emit an ERROR status line if no key was found with -k. + + * gpg: Stop early when trying to create a primary Elgamal key. [#4329] + + * gpgsm: Print the card's key algorithms along with their keygrips + in interactive key generation. + + * agent: Clear bogus pinentry cache in the error case. [#4348] + + * scd: Support "acknowledge button" feature. + + * scd: Fix for USB INTERRUPT transfer. [#4308] + + * wks: Do no use compression for the the encrypted challenge and + response. + + Release-info: https://dev.gnupg.org/T4290 + See-also: gnupg-announce/2019q1/000434.html + + Changes also found in 2.2.12: + + * tools: New commands --install-key and --remove-key for + gpg-wks-client. This allows to prepare a Web Key Directory on a + local file system for later upload to a web server. + + * gpg: New --list-option "show-only-fpr-mbox". This makes the use + of the new gpg-wks-client --install-key command easier on Windows. + + * gpg: Improve processing speed when --skip-verify is used. + + * gpg: Fix a bug where a LF was accidentally written to the console. + + * gpg: --card-status now shows whether a card has the new KDF + feature enabled. + + * agent: New runtime option --s2k-calibration=MSEC. New configure + option --with-agent-s2k-calibration=MSEC. [#3399] + + * dirmngr: Try another keyserver from the pool on receiving a 502, + 503, or 504 error. [#4175] + + * dirmngr: Avoid possible CSRF attacks via http redirects. A HTTP + query will not anymore follow a 3xx redirect unless the Location + header gives the same host. If the host is different only the + host and port is taken from the Location header and the original + path and query parts are kept. + + * dirmngr: New command FLUSHCRL to flush all CRLS from disk and + memory. [#3967] + + * New simplified Chinese translation (zh_CN). + + Release-info: https://dev.gnupg.org/T4289 + See-also: gnupg-announce/2018q4/000433.html + Changes also found in 2.2.11: * gpgsm: Fix CRL loading when intermediate certicates are not yet @@ -388,6 +566,11 @@ Noteworthy changes in version 2.3.0 (unreleased) Version 2.2.9 (2018-07-12) Version 2.2.10 (2018-08-30) Version 2.2.11 (2018-11-06) + Version 2.2.12 (2018-12-14) + Version 2.2.13 (2019-02-12) + Version 2.2.14 (2019-03-19) + Version 2.2.15 (2019-03-26) + Version 2.2.16 (2019-05-28) Noteworthy changes in version 2.2.0 (2017-08-28) diff --git a/agent/agent.h b/agent/agent.h index 0f804cd8b..84e5e782b 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -361,6 +361,15 @@ typedef int (*lookup_ttl_t)(const char *hexgrip); #endif +/* Information from scdaemon for card keys. */ +struct card_key_info_s +{ + struct card_key_info_s *next; + char keygrip[40]; + char *serialno; + char *idstr; +}; + /*-- gpg-agent.c --*/ void agent_exit (int rc) GPGRT_ATTR_NORETURN; /* Also implemented in other tools */ @@ -389,8 +398,11 @@ void bump_key_eventcounter (void); void bump_card_eventcounter (void); void start_command_handler (ctrl_t, gnupg_fd_t, gnupg_fd_t); gpg_error_t pinentry_loopback (ctrl_t, const char *keyword, - unsigned char **buffer, size_t *size, - size_t max_length); + unsigned char **buffer, size_t *size, + size_t max_length); +gpg_error_t pinentry_loopback_confirm (ctrl_t ctrl, const char *desc, + int ask_confirmation, + const char *ok, const char *notok); #ifdef HAVE_W32_SYSTEM int serve_mmapped_ssh_request (ctrl_t ctrl, @@ -414,7 +426,8 @@ void start_command_handler_ssh (ctrl_t, gnupg_fd_t); gpg_error_t agent_modify_description (const char *in, const char *comment, const gcry_sexp_t key, char **result); int agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, int force); + const void *buffer, size_t length, int force, + const char *serialno, const char *keyref); gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, @@ -543,10 +556,12 @@ void agent_reload_trustlist (void); /*-- divert-scd.c --*/ int divert_pksign (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip, const unsigned char *digest, size_t digestlen, int algo, const unsigned char *shadow_info, unsigned char **r_sig, size_t *r_siglen); int divert_pkdecrypt (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip, const unsigned char *cipher, const unsigned char *shadow_info, char **r_buf, size_t *r_len, int *r_padding); @@ -603,6 +618,10 @@ int agent_card_scd (ctrl_t ctrl, const char *cmdline, int (*getpin_cb)(void *, const char *, const char *, char*, size_t), void *getpin_cb_arg, void *assuan_context); +void agent_card_free_keyinfo (struct card_key_info_s *l); +gpg_error_t agent_card_keyinfo (ctrl_t ctrl, const char *keygrip, + struct card_key_info_s **result); +void agent_card_killscd (void); /*-- learncard.c --*/ diff --git a/agent/cache.c b/agent/cache.c index 799d595ab..4a3e5a547 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include "agent.h" diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c index 34dde3744..a895a8b8f 100644 --- a/agent/call-pinentry.c +++ b/agent/call-pinentry.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #ifndef HAVE_W32_SYSTEM @@ -424,7 +423,17 @@ start_pinentry (ctrl_t ctrl) opt.no_grab? "OPTION no-grab":"OPTION grab", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return unlock_pinentry (ctrl, rc); + { + if (gpg_err_code (rc) == GPG_ERR_NOT_SUPPORTED + || gpg_err_code (rc) == GPG_ERR_UNKNOWN_OPTION) + { + if (opt.verbose) + log_info ("Option no-grab/grab is ignored by pinentry.\n"); + /* Keep going even if the feature is not supported. */ + } + else + return unlock_pinentry (ctrl, rc); + } value = session_env_getenv (ctrl->session_env, "GPG_TTY"); if (value) @@ -439,7 +448,7 @@ start_pinentry (ctrl_t ctrl) return unlock_pinentry (ctrl, rc); } value = session_env_getenv (ctrl->session_env, "TERM"); - if (value) + if (value && *value) { char *optstr; if (asprintf (&optstr, "OPTION ttytype=%s", value) < 0 ) @@ -949,15 +958,14 @@ build_cmd_setdesc (char *line, size_t linelen, const char *desc) static void * watch_sock (void *arg) { - gnupg_fd_t *p = (gnupg_fd_t *)arg; pid_t pid = assuan_get_pid (entry_ctx); while (1) { int err; - gnupg_fd_t sock = *p; fd_set fdset; struct timeval timeout = { 0, 500000 }; + gnupg_fd_t sock = *(gnupg_fd_t *)arg; if (sock == GNUPG_INVALID_FD) return NULL; @@ -995,18 +1003,11 @@ watch_sock (void *arg) } -/* Ask pinentry to get a pin by "GETPIN" command, spawning a thread - detecting the socket's EOF. - */ static gpg_error_t -do_getpin (ctrl_t ctrl, struct entry_parm_s *parm) +watch_sock_start (gnupg_fd_t *sock_p, npth_t *thread_p) { npth_attr_t tattr; - gpg_error_t rc; int err; - npth_t thread; - int saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL); - gnupg_fd_t sock_watched = ctrl->thread_startup.fd; err = npth_attr_init (&tattr); if (err) @@ -1016,7 +1017,7 @@ do_getpin (ctrl_t ctrl, struct entry_parm_s *parm) } npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); - err = npth_create (&thread, &tattr, watch_sock, (void *)&sock_watched); + err = npth_create (thread_p, &tattr, watch_sock, sock_p); npth_attr_destroy (&tattr); if (err) { @@ -1024,6 +1025,36 @@ do_getpin (ctrl_t ctrl, struct entry_parm_s *parm) return gpg_error_from_errno (err); } + return 0; +} + +static void +watch_sock_end (gnupg_fd_t *sock_p, npth_t *thread_p) +{ + int err; + + *sock_p = GNUPG_INVALID_FD; + err = npth_join (*thread_p, NULL); + if (err) + log_error ("watch_sock_end: error joining thread: %s\n", strerror (err)); +} + + +/* Ask pinentry to get a pin by "GETPIN" command, spawning a thread + detecting the socket's EOF. + */ +static gpg_error_t +do_getpin (ctrl_t ctrl, struct entry_parm_s *parm) +{ + gpg_error_t rc; + int saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL); + gnupg_fd_t sock_watched = ctrl->thread_startup.fd; + npth_t thread; + + rc = watch_sock_start (&sock_watched, &thread); + if (rc) + return rc; + assuan_begin_confidential (entry_ctx); rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, parm, inq_quality, entry_ctx, @@ -1040,10 +1071,7 @@ do_getpin (ctrl_t ctrl, struct entry_parm_s *parm) && gpg_err_code (rc) == GPG_ERR_CANCELED) rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED); - sock_watched = GNUPG_INVALID_FD; - err = npth_join (thread, NULL); - if (err) - log_error ("do_getpin: error joining thread: %s\n", strerror (err)); + watch_sock_end (&sock_watched, &thread); return rc; } @@ -1392,6 +1420,9 @@ agent_get_confirmation (ctrl_t ctrl, if (ctrl->pinentry_mode == PINENTRY_MODE_CANCEL) return gpg_error (GPG_ERR_CANCELED); + if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) + return pinentry_loopback_confirm (ctrl, desc, 1, ok, notok); + return gpg_error (GPG_ERR_NO_PIN_ENTRY); } @@ -1445,70 +1476,38 @@ agent_get_confirmation (ctrl_t ctrl, return unlock_pinentry (ctrl, rc); } - rc = assuan_transact (entry_ctx, "CONFIRM", - NULL, NULL, NULL, NULL, NULL, NULL); - if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) - rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + { + gnupg_fd_t sock_watched = ctrl->thread_startup.fd; + npth_t thread; - return unlock_pinentry (ctrl, rc); + rc = watch_sock_start (&sock_watched, &thread); + if (rc) + return rc; + + rc = assuan_transact (entry_ctx, "CONFIRM", + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + + watch_sock_end (&sock_watched, &thread); + + return unlock_pinentry (ctrl, rc); + } } -/* Pop up the PINentry, display the text DESC and a button with the - text OK_BTN (which may be NULL to use the default of "OK") and wait - for the user to hit this button. The return value is not - relevant. */ -int -agent_show_message (ctrl_t ctrl, const char *desc, const char *ok_btn) -{ - int rc; - char line[ASSUAN_LINELENGTH]; - - if (ctrl->pinentry_mode != PINENTRY_MODE_ASK) - return gpg_error (GPG_ERR_CANCELED); - - rc = start_pinentry (ctrl); - if (rc) - return rc; - - if (desc) - build_cmd_setdesc (line, DIM(line), desc); - else - snprintf (line, DIM(line), "RESET"); - rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); - /* Most pinentries out in the wild return the old Assuan error code - for canceled which gets translated to an assuan Cancel error and - not to the code for a user cancel. Fix this here. */ - if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) - rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); - - if (rc) - return unlock_pinentry (ctrl, rc); - - if (ok_btn) - { - snprintf (line, DIM(line), "SETOK %s", ok_btn); - rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, - NULL, NULL, NULL); - if (rc) - return unlock_pinentry (ctrl, rc); - } - - rc = assuan_transact (entry_ctx, "CONFIRM --one-button", NULL, NULL, NULL, - NULL, NULL, NULL); - if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) - rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); - - return unlock_pinentry (ctrl, rc); -} - - /* The thread running the popup message. */ static void * popup_message_thread (void *arg) { - (void)arg; + gpg_error_t rc; + gnupg_fd_t sock_watched = *(gnupg_fd_t *)arg; + npth_t thread; + + rc = watch_sock_start (&sock_watched, &thread); + if (rc) + return NULL; /* We use the --one-button hack instead of the MESSAGE command to allow the use of old Pinentries. Those old Pinentries will then @@ -1516,6 +1515,7 @@ popup_message_thread (void *arg) annoyance. */ assuan_transact (entry_ctx, "CONFIRM --one-button", NULL, NULL, NULL, NULL, NULL, NULL); + watch_sock_end (&sock_watched, &thread); popup_finished = 1; return NULL; } @@ -1536,7 +1536,15 @@ agent_popup_message_start (ctrl_t ctrl, const char *desc, const char *ok_btn) int err; if (ctrl->pinentry_mode != PINENTRY_MODE_ASK) - return gpg_error (GPG_ERR_CANCELED); + { + if (ctrl->pinentry_mode == PINENTRY_MODE_CANCEL) + return gpg_error (GPG_ERR_CANCELED); + + if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) + return pinentry_loopback_confirm (ctrl, desc, 0, ok_btn, NULL); + + return gpg_error (GPG_ERR_NO_PIN_ENTRY); + } rc = start_pinentry (ctrl); if (rc) @@ -1564,7 +1572,8 @@ agent_popup_message_start (ctrl_t ctrl, const char *desc, const char *ok_btn) npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); popup_finished = 0; - err = npth_create (&popup_tid, &tattr, popup_message_thread, NULL); + err = npth_create (&popup_tid, &tattr, popup_message_thread, + &ctrl->thread_startup.fd); npth_attr_destroy (&tattr); if (err) { @@ -1587,6 +1596,9 @@ agent_popup_message_stop (ctrl_t ctrl) (void)ctrl; + if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) + return; + if (!popup_tid || !entry_ctx) { log_debug ("agent_popup_message_stop called with no active popup\n"); diff --git a/agent/call-scd.c b/agent/call-scd.c index b2266225e..a96f5b783 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #ifdef HAVE_SIGNAL_H # include @@ -330,13 +329,13 @@ start_scd (ctrl_t ctrl) { ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local); if (!ctrl->scd_local) - { - err = gpg_error_from_syserror (); - rc = npth_mutex_unlock (&start_scd_lock); - if (rc) - log_error ("failed to release the start_scd lock: %s\n", strerror (rc)); - return err; - } + { + err = gpg_error_from_syserror (); + rc = npth_mutex_unlock (&start_scd_lock); + if (rc) + log_error ("failed to release the start_scd lock: %s\n", strerror (rc)); + return err; + } ctrl->scd_local->next_local = scd_local_list; scd_local_list = ctrl->scd_local; } @@ -1282,6 +1281,156 @@ agent_card_cardlist (ctrl_t ctrl, strlist_t *result) } +struct card_keyinfo_parm_s { + int error; + struct card_key_info_s *list; +}; + +/* Callback function for agent_card_keylist. */ +static gpg_error_t +card_keyinfo_cb (void *opaque, const char *line) +{ + struct card_keyinfo_parm_s *parm = opaque; + const char *keyword = line; + int keywordlen; + + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + while (spacep (line)) + line++; + + if (keywordlen == 7 && !memcmp (keyword, "KEYINFO", keywordlen)) + { + const char *s; + int n; + struct card_key_info_s *keyinfo; + struct card_key_info_s **l_p = &parm->list; + + while ((*l_p)) + l_p = &(*l_p)->next; + + keyinfo = xtrycalloc (1, sizeof *keyinfo); + if (!keyinfo) + { + alloc_error: + if (!parm->error) + parm->error = gpg_error_from_syserror (); + return 0; + } + + for (n=0,s=line; hexdigitp (s); s++, n++) + ; + + if (n != 40) + { + parm_error: + if (!parm->error) + parm->error = gpg_error (GPG_ERR_ASS_PARAMETER); + return 0; + } + + memcpy (keyinfo->keygrip, line, 40); + + line = s; + + if (!*line) + goto parm_error; + + while (spacep (line)) + line++; + + if (*line++ != 'T') + goto parm_error; + + if (!*line) + goto parm_error; + + while (spacep (line)) + line++; + + for (n=0,s=line; hexdigitp (s); s++, n++) + ; + + if (!n) + goto parm_error; + + keyinfo->serialno = xtrymalloc (n+1); + if (!keyinfo->serialno) + goto alloc_error; + + memcpy (keyinfo->serialno, line, n); + keyinfo->serialno[n] = 0; + + line = s; + + if (!*line) + goto parm_error; + + while (spacep (line)) + line++; + + if (!*line) + goto parm_error; + + keyinfo->idstr = xtrystrdup (line); + if (!keyinfo->idstr) + goto alloc_error; + + *l_p = keyinfo; + } + + return 0; +} + + +void +agent_card_free_keyinfo (struct card_key_info_s *l) +{ + struct card_key_info_s *l_next; + + for (; l; l = l_next) + { + l_next = l->next; + free (l->serialno); + free (l->idstr); + free (l); + } +} + +/* Call the scdaemon to check if a key of KEYGRIP is available, or + retrieve list of available keys on cards. On success the allocated + structure is stored at RESULT. On error an error code is returned + and NULL is stored at RESULT. */ +gpg_error_t +agent_card_keyinfo (ctrl_t ctrl, const char *keygrip, + struct card_key_info_s **result) +{ + int err; + struct card_keyinfo_parm_s parm; + char line[ASSUAN_LINELENGTH]; + + *result = NULL; + + memset (&parm, 0, sizeof parm); + snprintf (line, sizeof line, "KEYINFO %s", keygrip ? keygrip : "--list"); + + err = start_scd (ctrl); + if (err) + return err; + + err = assuan_transact (ctrl->scd_local->ctx, line, + NULL, NULL, NULL, NULL, + card_keyinfo_cb, &parm); + if (!err && parm.error) + err = parm.error; + + if (!err) + *result = parm.list; + else + agent_card_free_keyinfo (parm.list); + + return unlock_scd (ctrl, err); +} static gpg_error_t pass_status_thru (void *opaque, const char *line) @@ -1366,3 +1515,12 @@ agent_card_scd (ctrl_t ctrl, const char *cmdline, return unlock_scd (ctrl, 0); } + +void +agent_card_killscd (void) +{ + if (primary_scd_ctx == NULL) + return; + assuan_transact (primary_scd_ctx, "KILLSCD", + NULL, NULL, NULL, NULL, NULL, NULL); +} diff --git a/agent/command-ssh.c b/agent/command-ssh.c index ebd28ab5a..0849a06fc 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -41,7 +41,6 @@ #include #include #include -#include #ifndef HAVE_W32_SYSTEM #include #include @@ -1030,7 +1029,7 @@ search_control_file (ssh_control_file_t cf, const char *hexgrip, { gpg_error_t err; - assert (strlen (hexgrip) == 40 ); + log_assert (strlen (hexgrip) == 40 ); if (r_disabled) *r_disabled = 0; @@ -2646,7 +2645,7 @@ ssh_handler_request_identities (ctrl_t ctrl, continue; /* Should not happen. */ if (cf->item.disabled) continue; - assert (strlen (cf->item.hexgrip) == 40); + log_assert (strlen (cf->item.hexgrip) == 40); hex2bin (cf->item.hexgrip, grip, sizeof (grip)); err = agent_public_key_from_file (ctrl, grip, &key_public); @@ -3008,8 +3007,8 @@ ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase, goto out; } - gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n); - /* FIXME: guarantee? */ + buffer_new_n = gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, + buffer_new, buffer_new_n); if (*passphrase) err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0, -1); @@ -3142,7 +3141,7 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec, goto out; /* Store this key to our key storage. */ - err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0); + err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0, NULL, NULL); if (err) goto out; diff --git a/agent/command.c b/agent/command.c index 5e2b6df2b..b59532ce5 100644 --- a/agent/command.c +++ b/agent/command.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -1075,7 +1074,7 @@ cmd_readkey (assuan_context_t ctx, char *line) static const char hlp_keyinfo[] = - "KEYINFO [--[ssh-]list] [--data] [--ssh-fpr] [--with-ssh] \n" + "KEYINFO [--[ssh-]list] [--data] [--ssh-fpr[=algo]] [--with-ssh] \n" "\n" "Return information about the key specified by the KEYGRIP. If the\n" "key is not available GPG_ERR_NOT_FOUND is returned. If the option\n" @@ -1111,7 +1110,9 @@ static const char hlp_keyinfo[] = " '-' - Unknown protection.\n" "\n" "FPR returns the formatted ssh-style fingerprint of the key. It is only\n" - " printed if the option --ssh-fpr has been used. It defaults to '-'.\n" + " printed if the option --ssh-fpr has been used. If ALGO is not given\n" + " to that option the default ssh fingerprint algo is used. Without the\n" + " option a '-' is printed.\n" "\n" "TTL is the TTL in seconds for that key or '-' if n/a.\n" "\n" @@ -1119,13 +1120,14 @@ static const char hlp_keyinfo[] = " 'D' - The key has been disabled,\n" " 'S' - The key is listed in sshcontrol (requires --with-ssh),\n" " 'c' - Use of the key needs to be confirmed,\n" + " 'A' - The key is available on card,\n" " '-' - No flags given.\n" "\n" "More information may be added in the future."; static gpg_error_t do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx, int data, int with_ssh_fpr, int in_ssh, - int ttl, int disabled, int confirm) + int ttl, int disabled, int confirm, int on_card) { gpg_error_t err; char hexgrip[40+1]; @@ -1166,6 +1168,8 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx, strcat (flagsbuf, "S"); if (confirm) strcat (flagsbuf, "c"); + if (on_card) + strcat (flagsbuf, "A"); if (!*flagsbuf) strcpy (flagsbuf, "-"); @@ -1198,7 +1202,7 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx, if (!agent_raw_key_from_file (ctrl, grip, &key)) { - ssh_get_fingerprint_string (key, GCRY_MD_MD5, &fpr); + ssh_get_fingerprint_string (key, with_ssh_fpr, &fpr); gcry_sexp_release (key); } } @@ -1256,8 +1260,8 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx, } -/* Entry int for the command KEYINFO. This function handles the - command option processing. For details see hlp_keyinfo above. */ +/* Entry into the command KEYINFO. This function handles the + * command option processing. For details see hlp_keyinfo above. */ static gpg_error_t cmd_keyinfo (assuan_context_t ctx, char *line) { @@ -1270,6 +1274,9 @@ cmd_keyinfo (assuan_context_t ctx, char *line) ssh_control_file_t cf = NULL; char hexgrip[41]; int disabled, ttl, confirm, is_ssh; + struct card_key_info_s *keyinfo_on_cards; + struct card_key_info_s *l; + int on_card; if (ctrl->restricted) return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); @@ -1279,13 +1286,29 @@ cmd_keyinfo (assuan_context_t ctx, char *line) else list_mode = has_option (line, "--list"); opt_data = has_option (line, "--data"); - opt_ssh_fpr = has_option (line, "--ssh-fpr"); + + if (has_option_name (line, "--ssh-fpr")) + { + if (has_option (line, "--ssh-fpr=md5")) + opt_ssh_fpr = GCRY_MD_MD5; + else if (has_option (line, "--ssh-fpr=sha1")) + opt_ssh_fpr = GCRY_MD_SHA1; + else if (has_option (line, "--ssh-fpr=sha256")) + opt_ssh_fpr = GCRY_MD_SHA256; + else + opt_ssh_fpr = opt.ssh_fingerprint_digest; + } + else + opt_ssh_fpr = 0; + opt_with_ssh = has_option (line, "--with-ssh"); line = skip_options (line); if (opt_with_ssh || list_mode == 2) cf = ssh_open_control_file (); + agent_card_keyinfo (ctrl, NULL, &keyinfo_on_cards); + if (list_mode == 2) { if (cf) @@ -1295,8 +1318,14 @@ cmd_keyinfo (assuan_context_t ctx, char *line) { if (hex2bin (hexgrip, grip, 20) < 0 ) continue; /* Bad hex string. */ + + on_card = 0; + for (l = keyinfo_on_cards; l; l = l->next) + if (!memcmp (l->keygrip, hexgrip, 40)) + on_card = 1; + err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr, 1, - ttl, disabled, confirm); + ttl, disabled, confirm, on_card); if (err) goto leave; } @@ -1346,8 +1375,13 @@ cmd_keyinfo (assuan_context_t ctx, char *line) goto leave; } + on_card = 0; + for (l = keyinfo_on_cards; l; l = l->next) + if (!memcmp (l->keygrip, hexgrip, 40)) + on_card = 1; + err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr, is_ssh, - ttl, disabled, confirm); + ttl, disabled, confirm, on_card); if (err) goto leave; } @@ -1369,11 +1403,17 @@ cmd_keyinfo (assuan_context_t ctx, char *line) goto leave; } + on_card = 0; + for (l = keyinfo_on_cards; l; l = l->next) + if (!memcmp (l->keygrip, line, 40)) + on_card = 1; + err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr, is_ssh, - ttl, disabled, confirm); + ttl, disabled, confirm, on_card); } leave: + agent_card_free_keyinfo (keyinfo_on_cards); ssh_close_control_file (cf); if (dir) closedir (dir); @@ -2196,7 +2236,7 @@ cmd_import_key (assuan_context_t ctx, char *line) goto leave; /* Invalid canonical encoded S-expression. */ if (passphrase) { - assert (!opt_unattended); + log_assert (!opt_unattended); if (!cache_nonce) { char buf[12]; @@ -2239,10 +2279,11 @@ cmd_import_key (assuan_context_t ctx, char *line) err = agent_protect (key, passphrase, &finalkey, &finalkeylen, ctrl->s2k_count, -1); if (!err) - err = agent_write_private_key (grip, finalkey, finalkeylen, force); + err = agent_write_private_key (grip, finalkey, finalkeylen, force, + NULL, NULL); } else - err = agent_write_private_key (grip, key, realkeylen, force); + err = agent_write_private_key (grip, key, realkeylen, force, NULL, NULL); leave: gcry_sexp_release (openpgp_sexp); @@ -3069,7 +3110,7 @@ cmd_getinfo (assuan_context_t ctx, char *line) { cmdopt = line; if (!command_has_option (cmd, cmdopt)) - rc = gpg_error (GPG_ERR_GENERAL); + rc = gpg_error (GPG_ERR_FALSE); } } } @@ -3083,7 +3124,7 @@ cmd_getinfo (assuan_context_t ctx, char *line) } else if (!strcmp (line, "restricted")) { - rc = ctrl->restricted? 0 : gpg_error (GPG_ERR_GENERAL); + rc = ctrl->restricted? 0 : gpg_error (GPG_ERR_FALSE); } else if (ctrl->restricted) { @@ -3117,7 +3158,7 @@ cmd_getinfo (assuan_context_t ctx, char *line) } else if (!strcmp (line, "scd_running")) { - rc = agent_scd_check_running ()? 0 : gpg_error (GPG_ERR_GENERAL); + rc = agent_scd_check_running ()? 0 : gpg_error (GPG_ERR_FALSE); } else if (!strcmp (line, "std_env_names")) { @@ -3639,3 +3680,26 @@ pinentry_loopback(ctrl_t ctrl, const char *keyword, assuan_end_confidential (ctx); return rc; } + +/* Helper for the pinentry loopback mode to ask confirmation + or just to show message. */ +gpg_error_t +pinentry_loopback_confirm (ctrl_t ctrl, const char *desc, + int ask_confirmation, + const char *ok, const char *notok) +{ + gpg_error_t err = 0; + assuan_context_t ctx = ctrl->server_local->assuan_ctx; + + if (desc) + err = print_assuan_status (ctx, "SETDESC", "%s", desc); + if (!err && ok) + err = print_assuan_status (ctx, "SETOK", "%s", ok); + if (!err && notok) + err = print_assuan_status (ctx, "SETNOTOK", "%s", notok); + + if (!err) + err = assuan_inquire (ctx, ask_confirmation ? "CONFIRM 1" : "CONFIRM 0", + NULL, NULL, 0); + return err; +} diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 06cd1c840..003402956 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -22,7 +22,6 @@ #include #include #include -#include #include "agent.h" #include "../common/i18n.h" @@ -571,7 +570,7 @@ do_unprotect (const char *passphrase, } skey[i] = NULL; skeylen = i; - assert (skeylen <= skeysize); + log_assert (skeylen <= skeysize); /* Note: at this point NDATA should be 2 for a simple checksum or 20 for the sha1 digest. */ @@ -1067,7 +1066,8 @@ convert_from_openpgp_native (ctrl_t ctrl, if (!agent_protect (*r_key, passphrase, &protectedkey, &protectedkeylen, ctrl->s2k_count, -1)) - agent_write_private_key (grip, protectedkey, protectedkeylen, 1); + agent_write_private_key (grip, protectedkey, protectedkeylen, 1, + NULL, NULL); xfree (protectedkey); } else @@ -1076,7 +1076,7 @@ convert_from_openpgp_native (ctrl_t ctrl, agent_write_private_key (grip, *r_key, gcry_sexp_canon_len (*r_key, 0, NULL,NULL), - 1); + 1, NULL, NULL); } } @@ -1104,8 +1104,8 @@ apply_protection (gcry_mpi_t *array, int npkey, int nskey, int ndata; unsigned char *p, *data; - assert (npkey < nskey); - assert (nskey < DIM (bufarr)); + log_assert (npkey < nskey); + log_assert (nskey < DIM (bufarr)); /* Collect only the secret key parameters into BUFARR et al and compute the required size of the data buffer. */ @@ -1142,7 +1142,7 @@ apply_protection (gcry_mpi_t *array, int npkey, int nskey, xfree (bufarr[i]); bufarr[i] = NULL; } - assert (p == data + ndata - 20); + log_assert (p == data + ndata - 20); /* Append a hash of the secret key parameters. */ gcry_md_hash_buffer (GCRY_MD_SHA1, p, data, ndata - 20); diff --git a/agent/divert-scd.c b/agent/divert-scd.c index e89c74a19..cfa2347c7 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -32,28 +32,50 @@ #include "../common/sexp-parse.h" -static int -ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) +static gpg_error_t +ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, + const unsigned char *grip, char **r_kid) { - int rc, i; + int i; char *serialno; int no_card = 0; char *desc; char *want_sn, *want_kid, *want_sn_disp; int len; + struct card_key_info_s *keyinfo; + gpg_error_t err; + char hexgrip[41]; *r_kid = NULL; - rc = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL); - if (rc) - return rc; + /* Scan device(s), and check if key for GRIP is available. */ + err = agent_card_serialno (ctrl, &serialno, NULL); + if (!err) + { + xfree (serialno); + bin2hex (grip, 20, hexgrip); + err = agent_card_keyinfo (ctrl, hexgrip, &keyinfo); + if (!err) + { + /* Key for GRIP found, use it directly. */ + agent_card_free_keyinfo (keyinfo); + if ((*r_kid = xtrystrdup (hexgrip))) + return 0; + else + return gpg_error_from_syserror (); + } + } + + err = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL); + if (err) + return err; want_sn_disp = xtrystrdup (want_sn); if (!want_sn_disp) { - rc = gpg_error_from_syserror (); + err = gpg_error_from_syserror (); xfree (want_sn); xfree (want_kid); - return rc; + return err; } len = strlen (want_sn_disp); @@ -76,8 +98,8 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) for (;;) { - rc = agent_card_serialno (ctrl, &serialno, want_sn); - if (!rc) + err = agent_card_serialno (ctrl, &serialno, want_sn); + if (!err) { log_debug ("detected card with S/N %s\n", serialno); i = strcmp (serialno, want_sn); @@ -91,24 +113,24 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) return 0; /* yes, we have the correct card */ } } - else if (gpg_err_code (rc) == GPG_ERR_ENODEV) + else if (gpg_err_code (err) == GPG_ERR_ENODEV) { log_debug ("no device present\n"); - rc = 0; + err = 0; no_card = 1; } - else if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT) + else if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) { log_debug ("no card present\n"); - rc = 0; + err = 0; no_card = 2; } else { - log_error ("error accessing card: %s\n", gpg_strerror (rc)); + log_error ("error accessing card: %s\n", gpg_strerror (err)); } - if (!rc) + if (!err) { if (asprintf (&desc, "%s:%%0A%%0A" @@ -119,24 +141,24 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) "insert the one with serial number"), want_sn_disp) < 0) { - rc = out_of_core (); + err = out_of_core (); } else { - rc = agent_get_confirmation (ctrl, desc, NULL, NULL, 0); + err = agent_get_confirmation (ctrl, desc, NULL, NULL, 0); if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK && - gpg_err_code (rc) == GPG_ERR_NO_PIN_ENTRY) - rc = gpg_error (GPG_ERR_CARD_NOT_PRESENT); + gpg_err_code (err) == GPG_ERR_NO_PIN_ENTRY) + err = gpg_error (GPG_ERR_CARD_NOT_PRESENT); xfree (desc); } } - if (rc) + if (err) { xfree (want_sn_disp); xfree (want_sn); xfree (want_kid); - return rc; + return err; } } } @@ -434,7 +456,7 @@ getpin_cb (void *opaque, const char *desc_text, const char *info, * * FIXME: Explain the other args. */ int -divert_pksign (ctrl_t ctrl, const char *desc_text, +divert_pksign (ctrl_t ctrl, const char *desc_text, const unsigned char *grip, const unsigned char *digest, size_t digestlen, int algo, const unsigned char *shadow_info, unsigned char **r_sig, size_t *r_siglen) @@ -446,7 +468,7 @@ divert_pksign (ctrl_t ctrl, const char *desc_text, (void)desc_text; - rc = ask_for_card (ctrl, shadow_info, &kid); + rc = ask_for_card (ctrl, shadow_info, grip, &kid); if (rc) return rc; @@ -490,6 +512,7 @@ divert_pksign (ctrl_t ctrl, const char *desc_text, R_PADDING with -1 for not known. */ int divert_pkdecrypt (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip, const unsigned char *cipher, const unsigned char *shadow_info, char **r_buf, size_t *r_len, int *r_padding) @@ -581,7 +604,7 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text, ciphertext = s; ciphertextlen = n; - rc = ask_for_card (ctrl, shadow_info, &kid); + rc = ask_for_card (ctrl, shadow_info, grip, &kid); if (rc) return rc; diff --git a/agent/findkey.c b/agent/findkey.c index 89a18fa9e..370050d8b 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -1,7 +1,7 @@ /* findkey.c - Locate the secret key * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, * 2010, 2011 Free Software Foundation, Inc. - * Copyright (C) 2014 Werner Koch + * Copyright (C) 2014, 2019 Werner Koch * * This file is part of GnuPG. * @@ -26,10 +26,8 @@ #include #include #include -#include #include #include -#include #include /* (we use pth_sleep) */ #include "agent.h" @@ -52,15 +50,47 @@ struct try_unprotect_arg_s }; +/* Repalce all linefeeds in STRING by "%0A" and return a new malloced + * string. May return NULL on memory error. */ +static char * +linefeed_to_percent0A (const char *string) +{ + const char *s; + size_t n; + char *buf, *p; + + for (n=0, s=string; *s; s++) + if (*s == '\n') + n += 3; + else + n++; + p = buf = xtrymalloc (n+1); + if (!buf) + return NULL; + for (s=string; *s; s++) + if (*s == '\n') + { + memcpy (p, "%0A", 3); + p += 3; + } + else + *p++ = *s; + *p = 0; + return buf; +} + + /* Note: Ownership of FNAME and FP are moved to this function. */ static gpg_error_t write_extended_private_key (char *fname, estream_t fp, int update, - const void *buf, size_t len) + const void *buf, size_t len, + const char *serialno, const char *keyref) { gpg_error_t err; nvc_t pk = NULL; gcry_sexp_t key = NULL; int remove = 0; + char *token = NULL; if (update) { @@ -93,6 +123,37 @@ write_extended_private_key (char *fname, estream_t fp, int update, if (err) goto leave; + /* If requested write a Token line. */ + if (serialno && keyref) + { + nve_t item; + const char *s; + + token = strconcat (serialno, " ", keyref, NULL); + if (!token) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* fixme: the strcmp should compare only the first two strings. */ + for (item = nvc_lookup (pk, "Token:"); + item; + item = nve_next_value (item, "Token:")) + if ((s = nve_value (item)) && !strcmp (s, token)) + break; + if (!item) + { + /* No token or no token with that value exists. Add a new + * one so that keys which have been stored on several cards + * are well supported. */ + err = nvc_add (pk, "Token:", token); + if (err) + goto leave; + } + } + + err = es_fseek (fp, 0, SEEK_SET); if (err) goto leave; @@ -132,15 +193,18 @@ write_extended_private_key (char *fname, estream_t fp, int update, xfree (fname); gcry_sexp_release (key); nvc_release (pk); + xfree (token); return err; } /* Write an S-expression formatted key to our key storage. With FORCE - passed as true an existing key with the given GRIP will get - overwritten. */ + * passed as true an existing key with the given GRIP will get + * overwritten. If SERIALNO and KEYREF are give an a Token line is added to + * th key if the extended format ist used. */ int agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, int force) + const void *buffer, size_t length, int force, + const char *serialno, const char *keyref) { char *fname; estream_t fp; @@ -208,17 +272,20 @@ agent_write_private_key (const unsigned char *grip, if (first != '(') { /* Key is already in the extended format. */ - return write_extended_private_key (fname, fp, 1, buffer, length); + return write_extended_private_key (fname, fp, 1, buffer, length, + serialno, keyref); } if (first == '(' && opt.enable_extended_key_format) { /* Key is in the old format - but we want the extended format. */ - return write_extended_private_key (fname, fp, 0, buffer, length); + return write_extended_private_key (fname, fp, 0, buffer, length, + serialno, keyref); } } if (opt.enable_extended_key_format) - return write_extended_private_key (fname, fp, 0, buffer, length); + return write_extended_private_key (fname, fp, 0, buffer, length, + serialno, keyref); if (es_fwrite (buffer, length, 1, fp) != 1) { @@ -267,7 +334,7 @@ try_unprotect_cb (struct pin_entry_info_s *pi) gnupg_isotime_t now, protected_at, tmptime; char *desc = NULL; - assert (!arg->unprotected_key); + log_assert (!arg->unprotected_key); arg->change_required = 0; err = agent_unprotect (ctrl, arg->protected_key, pi->pin, protected_at, @@ -332,6 +399,33 @@ try_unprotect_cb (struct pin_entry_info_s *pi) } +/* Return true if the STRING has an %C or %c expando. */ +static int +has_comment_expando (const char *string) +{ + const char *s; + int percent = 0; + + if (!string) + return 0; + + for (s = string; *s; s++) + { + if (percent) + { + if (*s == 'c' || *s == 'C') + return 1; + percent = 0; + } + else if (*s == '%') + percent = 1; + } + return 0; +} + + + + /* Modify a Key description, replacing certain special format characters. List of currently supported replacements: @@ -644,7 +738,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, } else { - assert (arg.unprotected_key); + log_assert (arg.unprotected_key); if (arg.change_required) { /* The callback told as that the user should change their @@ -652,7 +746,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, size_t canlen, erroff; gcry_sexp_t s_skey; - assert (arg.unprotected_key); + log_assert (arg.unprotected_key); canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL); rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)arg.unprotected_key, canlen); @@ -695,10 +789,13 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, /* Read the key identified by GRIP from the private key directory and - return it as an gcrypt S-expression object in RESULT. On failure - returns an error code and stores NULL at RESULT. */ + * return it as an gcrypt S-expression object in RESULT. If R_KEYMETA + * is not NULl and the extended key format is used, the meta data + * items are stored there. However the "Key:" item is removed from + * it. On failure returns an error code and stores NULL at RESULT and + * R_KEYMETA. */ static gpg_error_t -read_key_file (const unsigned char *grip, gcry_sexp_t *result) +read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta) { gpg_error_t err; char *fname; @@ -711,6 +808,8 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) char first; *result = NULL; + if (r_keymeta) + *r_keymeta = NULL; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); @@ -749,7 +848,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) if (first != '(') { /* Key is in extended format. */ - nvc_t pk; + nvc_t pk = NULL; int line; err = nvc_parse_private_key (&pk, &line, fp); @@ -761,12 +860,17 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) else { err = nvc_get_private_key (pk, result); - nvc_release (pk); if (err) log_error ("error getting private key from '%s': %s\n", fname, gpg_strerror (err)); + else + nvc_delete_named (pk, "Key:"); } + if (!err && r_keymeta) + *r_keymeta = pk; + else + nvc_release (pk); xfree (fname); return err; } @@ -866,6 +970,8 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, unsigned char *buf; size_t len, buflen, erroff; gcry_sexp_t s_skey; + nvc_t keymeta = NULL; + char *desc_text_buffer = NULL; /* Used in case we extend DESC_TEXT. */ *result = NULL; if (shadow_info) @@ -873,7 +979,7 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, if (r_passphrase) *r_passphrase = NULL; - err = read_key_file (grip, &s_skey); + err = read_key_file (grip, &s_skey, &keymeta); if (err) { if (gpg_err_code (err) == GPG_ERR_ENOENT) @@ -886,7 +992,11 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, now. */ err = make_canon_sexp (s_skey, &buf, &len); if (err) - return err; + { + nvc_release (keymeta); + xfree (desc_text_buffer); + return err; + } switch (agent_private_key_type (buf)) { @@ -911,25 +1021,43 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, case PRIVATE_KEY_PROTECTED: { char *desc_text_final; - char *comment = NULL; + char *comment_buffer = NULL; + const char *comment = NULL; /* Note, that we will take the comment as a C string for - display purposes; i.e. all stuff beyond a Nul character is - ignored. */ - { - gcry_sexp_t comment_sexp; + * display purposes; i.e. all stuff beyond a Nul character is + * ignored. If a "Label" entry is available in the meta data + * this is used instead of the s-ecpression comment. */ + if (keymeta && (comment = nvc_get_string (keymeta, "Label:"))) + { + if (strchr (comment, '\n') + && (comment_buffer = linefeed_to_percent0A (comment))) + comment = comment_buffer; + /* In case DESC_TEXT has no escape pattern for a comment + * we append one. */ + if (desc_text && !has_comment_expando (desc_text)) + { + desc_text_buffer = strconcat (desc_text, "%0A%C", NULL); + if (desc_text_buffer) + desc_text = desc_text_buffer; + } + } + else + { + gcry_sexp_t comment_sexp; - comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); - if (comment_sexp) - comment = gcry_sexp_nth_string (comment_sexp, 1); - gcry_sexp_release (comment_sexp); - } + comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); + if (comment_sexp) + comment_buffer = gcry_sexp_nth_string (comment_sexp, 1); + gcry_sexp_release (comment_sexp); + comment = comment_buffer; + } desc_text_final = NULL; if (desc_text) err = agent_modify_description (desc_text, comment, s_skey, &desc_text_final); - gcry_free (comment); + gcry_free (comment_buffer); if (!err) { @@ -984,6 +1112,8 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, xfree (*r_passphrase); *r_passphrase = NULL; } + nvc_release (keymeta); + xfree (desc_text_buffer); return err; } @@ -1000,10 +1130,14 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, xfree (*r_passphrase); *r_passphrase = NULL; } + nvc_release (keymeta); + xfree (desc_text_buffer); return err; } *result = s_skey; + nvc_release (keymeta); + xfree (desc_text_buffer); return 0; } @@ -1203,7 +1337,7 @@ agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, *result = NULL; - err = read_key_file (grip, &s_skey); + err = read_key_file (grip, &s_skey, NULL); if (!err) *result = s_skey; return err; @@ -1230,6 +1364,7 @@ agent_public_key_from_file (ctrl_t ctrl, gcry_sexp_t uri_sexp, comment_sexp; const char *uri, *comment; size_t uri_length, comment_length; + int uri_intlen, comment_intlen; char *format, *p; void *args[2+7+2+2+1]; /* Size is 2 + max. # of elements + 2 for uri + 2 for comment + end-of-list. */ @@ -1241,7 +1376,7 @@ agent_public_key_from_file (ctrl_t ctrl, *result = NULL; - err = read_key_file (grip, &s_skey); + err = read_key_file (grip, &s_skey, NULL); if (err) return err; @@ -1278,7 +1413,7 @@ agent_public_key_from_file (ctrl_t ctrl, such a task. After all that is what we do in protect.c. Need to find common patterns and write a straightformward API to use them. */ - assert (sizeof (size_t) <= sizeof (void*)); + log_assert (sizeof (size_t) <= sizeof (void*)); format = xtrymalloc (15+4+7*npkey+10+15+1+1); if (!format) @@ -1303,27 +1438,29 @@ agent_public_key_from_file (ctrl_t ctrl, *p++ = '('; *p++ = *s++; p = stpcpy (p, " %m)"); - assert (argidx < DIM (args)); + log_assert (argidx < DIM (args)); args[argidx++] = &array[idx]; } *p++ = ')'; if (uri) { p = stpcpy (p, "(uri %b)"); - assert (argidx+1 < DIM (args)); - args[argidx++] = (void *)&uri_length; + log_assert (argidx+1 < DIM (args)); + uri_intlen = (int)uri_length; + args[argidx++] = (void *)&uri_intlen; args[argidx++] = (void *)&uri; } if (comment) { p = stpcpy (p, "(comment %b)"); - assert (argidx+1 < DIM (args)); - args[argidx++] = (void *)&comment_length; + log_assert (argidx+1 < DIM (args)); + comment_intlen = (int)comment_length; + args[argidx++] = (void *)&comment_intlen; args[argidx++] = (void*)&comment; } *p++ = ')'; *p = 0; - assert (argidx < DIM (args)); + log_assert (argidx < DIM (args)); args[argidx] = NULL; err = gcry_sexp_build_array (&list, NULL, format, args); @@ -1386,7 +1523,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, { gcry_sexp_t sexp; - err = read_key_file (grip, &sexp); + err = read_key_file (grip, &sexp, NULL); if (err) { if (gpg_err_code (err) == GPG_ERR_ENOENT) @@ -1420,7 +1557,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, if (!err) { n = gcry_sexp_canon_len (s, 0, NULL, NULL); - assert (n); + log_assert (n); *r_shadow_info = xtrymalloc (n); if (!*r_shadow_info) err = gpg_error_from_syserror (); @@ -1470,7 +1607,7 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text, char *default_desc = NULL; int key_type; - err = read_key_file (grip, &s_skey); + err = read_key_file (grip, &s_skey, NULL); if (gpg_err_code (err) == GPG_ERR_ENOENT) err = gpg_error (GPG_ERR_NO_SECKEY); if (err) @@ -1580,6 +1717,13 @@ agent_write_shadow_key (const unsigned char *grip, unsigned char *shdkey; size_t len; + /* Just in case some caller did not parse the stuff correctly, skip + * leading spaces. */ + while (spacep (serialno)) + serialno++; + while (spacep (keyid)) + keyid++; + shadow_info = make_shadow_info (serialno, keyid); if (!shadow_info) return gpg_error_from_syserror (); @@ -1593,7 +1737,7 @@ agent_write_shadow_key (const unsigned char *grip, } len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL); - err = agent_write_private_key (grip, shdkey, len, force); + err = agent_write_private_key (grip, shdkey, len, force, serialno, keyid); xfree (shdkey); if (err) log_error ("error writing key: %s\n", gpg_strerror (err)); diff --git a/agent/genkey.c b/agent/genkey.c index d5c80d0aa..0d2038016 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "agent.h" #include "../common/i18n.h" @@ -47,12 +46,12 @@ store_key (gcry_sexp_t private, const char *passphrase, int force, } len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, NULL, 0); - assert (len); + log_assert (len); buf = gcry_malloc_secure (len); if (!buf) return out_of_core (); len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len); - assert (len); + log_assert (len); if (passphrase) { @@ -68,7 +67,7 @@ store_key (gcry_sexp_t private, const char *passphrase, int force, buf = p; } - rc = agent_write_private_key (grip, buf, len, force); + rc = agent_write_private_key (grip, buf, len, force, NULL, NULL); xfree (buf); return rc; } @@ -127,7 +126,7 @@ check_passphrase_pattern (ctrl_t ctrl, const char *pw) argv[i++] = "--", argv[i++] = opt.check_passphrase_pattern, argv[i] = NULL; - assert (i < sizeof argv); + log_assert (i < sizeof argv); if (gnupg_spawn_process_fd (pgmname, argv, fileno (infp), -1, -1, &pid)) result = 1; /* Execute error - assume password should no be used. */ @@ -149,27 +148,10 @@ check_passphrase_pattern (ctrl_t ctrl, const char *pw) static int -take_this_one_anyway2 (ctrl_t ctrl, const char *desc, const char *anyway_btn) +take_this_one_anyway (ctrl_t ctrl, const char *desc, const char *anyway_btn) { - gpg_error_t err; - - if (opt.enforce_passphrase_constraints) - { - err = agent_show_message (ctrl, desc, L_("Enter new passphrase")); - if (!err) - err = gpg_error (GPG_ERR_CANCELED); - } - else - err = agent_get_confirmation (ctrl, desc, - anyway_btn, L_("Enter new passphrase"), 0); - return err; -} - - -static int -take_this_one_anyway (ctrl_t ctrl, const char *desc) -{ - return take_this_one_anyway2 (ctrl, desc, L_("Take this one anyway")); + return agent_get_confirmation (ctrl, desc, + anyway_btn, L_("Enter new passphrase"), 0); } @@ -212,8 +194,8 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw, if (opt.enforce_passphrase_constraints) *failed_constraint = xstrdup (desc); else - err = take_this_one_anyway2 (ctrl, desc, - L_("Yes, protection is not needed")); + err = take_this_one_anyway (ctrl, desc, + L_("Yes, protection is not needed")); } goto leave; @@ -311,7 +293,7 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw, *failed_constraint = msg; else { - err = take_this_one_anyway (ctrl, msg); + err = take_this_one_anyway (ctrl, msg, L_("Take this one anyway")); xfree (msg); } } @@ -557,7 +539,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, if (DBG_CRYPTO) log_debug ("returning public key\n"); len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0); - assert (len); + log_assert (len); buf = xtrymalloc (len); if (!buf) { @@ -567,7 +549,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, return tmperr; } len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len); - assert (len); + log_assert (len); put_membuf (outbuf, buf, len); gcry_sexp_release (s_public); xfree (buf); diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index d9e2bbf25..57d5a459c 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -1952,7 +1951,7 @@ agent_set_progress_cb (void (*cb)(ctrl_t ctrl, const char *what, static void agent_init_default_ctrl (ctrl_t ctrl) { - assert (ctrl->session_env); + log_assert (ctrl->session_env); /* Note we ignore malloc errors because we can't do much about it and the request will fail anyway shortly after this @@ -2442,6 +2441,9 @@ agent_sighup_action (void) "pinentry" binary that one can be used in case the "pinentry-basic" fallback was in use. */ gnupg_module_name_flush_some (); + + if (opt.disable_scdaemon) + agent_card_killscd (); } diff --git a/agent/keyformat.txt b/agent/keyformat.txt index c7426db9d..e2ca05c84 100644 --- a/agent/keyformat.txt +++ b/agent/keyformat.txt @@ -18,7 +18,8 @@ hexadecimal representation of the keygrip[2] and suffixed with ".key". * Extended Private Key Format -GnuPG 2.3+ will use a new format to store private keys that is both +** Overview +GnuPG 2.3+ uses a new format to store private keys that is both more flexible and easier to read and edit by human beings. The new format stores name,value-pairs using the common mail and http header convention. Example (here indented with two spaces): @@ -28,6 +29,8 @@ convention. Example (here indented with two spaces): Use-for-ssh: yes OpenSSH-cert: long base64 encoded string wrapped so that this key file can be easily edited with a standard editor. + Token: D2760001240102000005000011730000 OPENPGP.1 + Token: FF020001008A77C1 PIV.9C Key: (shadowed-private-key (rsa (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900 @@ -52,33 +55,66 @@ Keys in the extended format can be recognized by looking at the first byte of the file. If it starts with a '(' it is a naked S-expression, otherwise it is a key in extended format. -** Names - +*** Names A name must start with a letter and end with a colon. Valid characters are all ASCII letters, numbers and the hyphen. Comparison of names is done case insensitively. Names may be used several times -to represent an array of values. - -The name "Key:" is special in that it may occur only once and the -associated value holds the actual S-expression with the cryptographic -key. The S-expression is formatted using the 'Advanced Format' -(GCRYSEXP_FMT_ADVANCED) that avoids non-printable characters so that -the file can be easily inspected and edited. See section 'Private Key -Format' below for details. - -** Values +to represent an array of values. Note that the name "Key" is special +in that it is madandory must occur only once. +*** Values Values are UTF-8 encoded strings. Values can be wrapped at any point, and continued in the next line indicated by leading whitespace. A continuation line with one leading space does not introduce a blank so that the lines can be effectively concatenated. A blank line as part of a continuation line encodes a newline. -** Comments - +*** Comments Lines containing only whitespace, and lines starting with whitespace followed by '#' are considered to be comments and are ignored. +** Well defined names + +*** Description +This is a human readable string describing the key. + +*** Key +The name "Key" is special in that it is mandatory and must occur only +once. The associated value holds the actual S-expression with the +cryptographic key. The S-expression is formatted using the 'Advanced +Format' (GCRYSEXP_FMT_ADVANCED) that avoids non-printable characters +so that the file can be easily inspected and edited. See section +'Private Key Format' below for details. + +*** Label +This is a short human readable description for the key which can be +used by the software to describe the key in a user interface. For +example as part of the description in a prompt for a PIN or +passphrase. It is often used instead of a comment element as present +in the S-expression of the "Key" item. + +*** OpenSSH-cert +This takes a base64 encoded string wrapped so that this +key file can be easily edited with a standard editor. Several of such +items can be used. + +*** Token +If such an item exists it overrides the info given by the "shadow" +parameter in the S-expression. Using this item makes it possible to +describe a key which is stored on several tokens and also makes it +easy to update this info using a standard editor. The syntax is the +same as with the "shadow" parameter: + +- Serialnumber of the token +- Key reference from the token in full format (e.g. "OpenPGP.2") +- An optional fixed length of the PIN. + +*** Use-for-ssh +If given and the value is "yes" or "1" the key is allowed for use by +gpg-agent's ssh-agent implementation. This is thus the same as +putting the keygrip into the 'sshcontrol' file. Only one such item +should exist. + * Private Key Format ** Unprotected Private Key Format diff --git a/agent/learncard.c b/agent/learncard.c index f3219ed8f..f40f5ac4d 100644 --- a/agent/learncard.c +++ b/agent/learncard.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 06a8e0b6f..ec23daf83 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -86,8 +85,8 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, goto leave; } - rc = divert_pkdecrypt (ctrl, desc_text, ciphertext, shadow_info, - &buf, &len, r_padding); + rc = divert_pkdecrypt (ctrl, desc_text, ctrl->keygrip, ciphertext, + shadow_info, &buf, &len, r_padding); if (rc) { log_error ("smartcard decryption failed: %s\n", gpg_strerror (rc)); @@ -119,10 +118,10 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, gcry_sexp_dump (s_plain); } len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0); - assert (len); + log_assert (len); buf = xmalloc (len); len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, buf, len); - assert (len); + log_assert (len); if (*buf == '(') put_membuf (outbuf, buf, len); else diff --git a/agent/pksign.c b/agent/pksign.c index 828e63f58..4a43b09de 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -24,8 +24,6 @@ #include #include #include -#include -#include #include #include "agent.h" @@ -46,16 +44,21 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash, int i; s = gcry_md_algo_name (algo); - if (s && strlen (s) < 16) + if (!s || strlen (s) >= 16) + { + hash = NULL; + rc = gpg_error (GPG_ERR_DIGEST_ALGO); + } + else { - for (i=0; i < strlen (s); i++) - tmp[i] = tolower (s[i]); + for (i=0; s[i]; i++) + tmp[i] = ascii_tolower (s[i]); tmp[i] = '\0'; - } - rc = gcry_sexp_build (&hash, NULL, - "(data (flags pkcs1) (hash %s %b))", - tmp, (int)mdlen, md); + rc = gcry_sexp_build (&hash, NULL, + "(data (flags pkcs1) (hash %s %b))", + tmp, (int)mdlen, md); + } } else { @@ -250,13 +253,13 @@ do_encode_raw_pkcs1 (const byte *md, size_t mdlen, unsigned int nbits, frame[n++] = 0; frame[n++] = 1; /* Block type. */ i = nframe - mdlen - 3 ; - assert (i >= 8); /* At least 8 bytes of padding. */ + log_assert (i >= 8); /* At least 8 bytes of padding. */ memset (frame+n, 0xff, i ); n += i; frame[n++] = 0; memcpy (frame+n, md, mdlen ); n += mdlen; - assert (n == nframe); + log_assert (n == nframe); /* Create the S-expression. */ rc = gcry_sexp_build (&hash, NULL, @@ -354,6 +357,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, agent_modify_description (desc_text, NULL, s_skey, &desc2); err = divert_pksign (ctrl, desc2? desc2 : desc_text, + ctrl->keygrip, data, datalen, ctrl->digest.algo, shadow_info, &buf, &len); diff --git a/agent/preset-passphrase.c b/agent/preset-passphrase.c index 7a9ea1b44..e22e9d58d 100644 --- a/agent/preset-passphrase.c +++ b/agent/preset-passphrase.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #ifdef HAVE_LOCALE_H diff --git a/agent/protect-tool.c b/agent/protect-tool.c index ec7b47695..059a9bdbd 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #ifdef HAVE_LOCALE_H @@ -198,10 +197,10 @@ make_canonical (const char *fname, const char *buf, size_t buflen) return NULL; } len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); - assert (len); + log_assert (len); result = xmalloc (len); len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, result, len); - assert (len); + log_assert (len); gcry_sexp_release (sexp); return result; } @@ -222,10 +221,10 @@ make_advanced (const unsigned char *buf, size_t buflen) return NULL; } len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); - assert (len); + log_assert (len); result = xmalloc (len); len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len); - assert (len); + log_assert (len); gcry_sexp_release (sexp); return result; } @@ -433,7 +432,7 @@ read_and_shadow (const char *fname) return; } resultlen = gcry_sexp_canon_len (result, 0, NULL,NULL); - assert (resultlen); + log_assert (resultlen); if (opt_armor) { @@ -469,7 +468,7 @@ show_shadow_info (const char *fname) return; } infolen = gcry_sexp_canon_len (info, 0, NULL,NULL); - assert (infolen); + log_assert (infolen); if (opt_armor) { @@ -496,7 +495,7 @@ show_file (const char *fname) return; keylen = gcry_sexp_canon_len (key, 0, NULL,NULL); - assert (keylen); + log_assert (keylen); if (opt_canonical) { @@ -723,7 +722,7 @@ get_passphrase (int promptno) gpg_strerror (err)); agent_exit (0); } - assert (pw); + log_assert (pw); return pw; } @@ -799,12 +798,15 @@ agent_askpin (ctrl_t ctrl, * to stdout. */ int agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, int force) + const void *buffer, size_t length, int force, + const char *serialno, const char *keyref) { char hexgrip[40+4+1]; char *p; (void)force; + (void)serialno; + (void)keyref; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); diff --git a/agent/protect.c b/agent/protect.c index 61fb8f45d..e3bbf3ed5 100644 --- a/agent/protect.c +++ b/agent/protect.c @@ -528,7 +528,7 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, memcpy (p, iv+blklen, blklen); /* Add padding. */ p += blklen; } - assert ( p - outbuf == outlen); + log_assert ( p - outbuf == outlen); if (use_ocb) { gcry_cipher_final (hd); @@ -718,11 +718,11 @@ agent_protect (const unsigned char *plainkey, const char *passphrase, hash_end = s; s++; /* Skip to the end of the S-expression. */ - assert (depth == 1); + log_assert (depth == 1); rc = sskip (&s, &depth); if (rc) return rc; - assert (!depth); + log_assert (!depth); real_end = s-1; rc = do_encryption (hash_begin, hash_end - hash_begin + 1, @@ -760,7 +760,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase, memcpy (p, prot_end+1, real_end - prot_end); p += real_end - prot_end; - assert ( p - *result == *resultlen); + log_assert ( p - *result == *resultlen); xfree (protected); return 0; @@ -999,7 +999,7 @@ merge_lists (const unsigned char *protectedkey, /* Skip over the protected list element in the original list. */ s = protectedkey + replacepos; - assert (*s == '('); + log_assert (*s == '('); s++; i = 1; rc = sskip (&s, &i); @@ -1026,7 +1026,7 @@ merge_lists (const unsigned char *protectedkey, rc = sskip (&s, &i); if (rc) goto failure; - assert (s[-1] == ')'); + log_assert (s[-1] == ')'); endpos = s; /* one behind the end of the list */ /* Append the rest. */ @@ -1571,7 +1571,7 @@ agent_shadow_key (const unsigned char *pubkey, point = s; /* insert right before the point */ depth--; s++; - assert (depth == 1); + log_assert (depth == 1); /* Calculate required length by taking in account: the "shadowed-" prefix, the "shadowed", "t1-v1" as well as some parenthesis */ @@ -1667,7 +1667,8 @@ agent_get_shadow_info (const unsigned char *shadowkey, 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. */ + required, NULL may be passed for them. Note that R_PINLEN is + currently not used by any caller. */ gpg_error_t parse_shadow_info (const unsigned char *shadow_info, char **r_hexsn, char **r_idstr, int *r_pinlen) diff --git a/agent/trans.c b/agent/trans.c index ff1a34e68..9d090ff86 100644 --- a/agent/trans.c +++ b/agent/trans.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/agent/trustlist.c b/agent/trustlist.c index af177b2e2..d91e92e07 100644 --- a/agent/trustlist.c +++ b/agent/trustlist.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -550,7 +549,7 @@ insert_colons (const char *string) } } *p = 0; - assert (strlen (buffer) <= nnew); + log_assert (strlen (buffer) <= nnew); return buffer; } diff --git a/build-aux/speedo/w32/inst.nsi b/build-aux/speedo/w32/inst.nsi index fb452d513..f130a4eda 100644 --- a/build-aux/speedo/w32/inst.nsi +++ b/build-aux/speedo/w32/inst.nsi @@ -624,6 +624,7 @@ Section "GnuPG" SEC_gnupg File "bin/gpgsm.exe" File "bin/gpgconf.exe" File "bin/gpg-connect-agent.exe" + File "bin/gpg-card.exe" File "bin/gpgtar.exe" File "libexec/dirmngr_ldap.exe" File "libexec/gpg-preset-passphrase.exe" @@ -1313,6 +1314,7 @@ Section "-un.gnupg" Delete "$INSTDIR\bin\gpgconf.exe" Delete "$INSTDIR\bin\gpg-connect-agent.exe" Delete "$INSTDIR\bin\gpgtar.exe" + Delete "$INSTDIR\bin\gpg-card.exe" Delete "$INSTDIR\bin\dirmngr_ldap.exe" Delete "$INSTDIR\bin\gpg-preset-passphrase.exe" Delete "$INSTDIR\bin\gpg-wks-client.exe" diff --git a/common/Makefile.am b/common/Makefile.am index b6a6605f1..9e0f10917 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -149,13 +149,13 @@ if MAINTAINER_MODE audit-events.h: Makefile.am mkstrtable.awk exaudit.awk audit.h $(AWK) -f $(srcdir)/exaudit.awk $(srcdir)/audit.h \ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ - -v namespace=eventstr_ > $(srcdir)/audit-events.h + -v pkg_namespace=eventstr_ > $(srcdir)/audit-events.h # Create the status-codes.h include file from status.h status-codes.h: Makefile.am mkstrtable.awk exstatus.awk status.h $(AWK) -f $(srcdir)/exstatus.awk $(srcdir)/status.h \ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ - -v namespace=statusstr_ > $(srcdir)/status-codes.h + -v pkg_namespace=statusstr_ > $(srcdir)/status-codes.h endif # diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c index 86b1d6869..ea158a33f 100644 --- a/common/exechelp-w32.c +++ b/common/exechelp-w32.c @@ -856,6 +856,7 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[], STARTUPINFO si; int cr_flags; char *cmdline; + BOOL in_job = FALSE; /* We don't use ENVP. */ @@ -884,6 +885,50 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[], | GetPriorityClass (GetCurrentProcess ()) | CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS); + + /* Check if we were spawned as part of a Job. + * In a job we need to add CREATE_BREAKAWAY_FROM_JOB + * to the cr_flags, otherwise our child processes + * are killed when we terminate. */ + if (!IsProcessInJob (GetCurrentProcess(), NULL, &in_job)) + { + log_error ("IsProcessInJob() failed: %s\n", w32_strerror (-1)); + in_job = FALSE; + } + + if (in_job) + { + /* Only try to break away from job if it is allowed, otherwise + * CreateProcess() would fail with an "Access is denied" error. */ + JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; + if (!QueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, + &info, sizeof info, NULL)) + { + log_error ("QueryInformationJobObject() failed: %s\n", + w32_strerror (-1)); + } + else if ((info.BasicLimitInformation.LimitFlags & + JOB_OBJECT_LIMIT_BREAKAWAY_OK)) + { + log_debug ("Using CREATE_BREAKAWAY_FROM_JOB flag\n"); + cr_flags |= CREATE_BREAKAWAY_FROM_JOB; + } + else if ((info.BasicLimitInformation.LimitFlags & + JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) + { + /* The child process should automatically detach from the job. */ + log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag; " + "JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK is set\n"); + } + else + { + /* It seems that the child process must remain in the job. + * This is not necessarily an error, although it can cause premature + * termination of the child process when the job is closed. */ + log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag\n"); + } + } + /* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */ /* pgmname, cmdline); */ if (!CreateProcess (pgmname, /* Program to start. */ diff --git a/common/mkstrtable.awk b/common/mkstrtable.awk index b5d4ef07a..60efce8a3 100644 --- a/common/mkstrtable.awk +++ b/common/mkstrtable.awk @@ -76,7 +76,7 @@ # # The variable prefix can be used to prepend a string to each message. # -# The variable namespace can be used to prepend a string to each +# The variable pkg_namespace can be used to prepend a string to each # variable and macro name. BEGIN { @@ -101,7 +101,7 @@ header { print "/* The purpose of this complex string table is to produce"; print " optimal code with a minimum of relocations. */"; print ""; - print "static const char " namespace "msgstr[] = "; + print "static const char " pkg_namespace "msgstr[] = "; header = 0; } else @@ -109,7 +109,7 @@ header { } !header { - sub (/\#.+/, ""); + sub (/#.+/, ""); sub (/[ ]+$/, ""); # Strip trailing space and tab characters. if (/^$/) @@ -149,14 +149,14 @@ END { else print " gettext_noop (\"" prefix last_msgstr "\");"; print ""; - print "static const int " namespace "msgidx[] ="; + print "static const int " pkg_namespace "msgidx[] ="; print " {"; for (i = 0; i < coded_msgs; i++) print " " pos[i] ","; print " " pos[coded_msgs]; print " };"; print ""; - print "#define " namespace "msgidxof(code) (0 ? -1 \\"; + print "#define " pkg_namespace "msgidxof(code) (0 ? -1 \\"; # Gather the ranges. skip = code[0]; diff --git a/common/name-value.c b/common/name-value.c index 5094acd03..989a5b111 100644 --- a/common/name-value.c +++ b/common/name-value.c @@ -514,6 +514,21 @@ nvc_delete (nvc_t pk, nve_t entry) nve_release (entry, pk->private_key_mode); } + +/* Delete the entries with NAME from PK. */ +void +nvc_delete_named (nvc_t pk, const char *name) +{ + nve_t e; + + if (!valid_name (name)) + return; + + while ((e = nvc_lookup (pk, name))) + nvc_delete (pk, e); +} + + /* Lookup and iteration. */ @@ -563,6 +578,25 @@ nve_next_value (nve_t entry, const char *name) return NULL; } + +/* Return the string for the first entry in NVC with NAME. If an + * entry with NAME is missing in NVC or its value is the empty string + * NULL is returned. Note that the The returned string is a pointer + * into NVC. */ +const char * +nvc_get_string (nvc_t nvc, const char *name) +{ + nve_t item; + + if (!nvc) + return NULL; + item = nvc_lookup (nvc, name); + if (!item) + return NULL; + return nve_value (item); +} + + /* Private key handling. */ @@ -778,29 +812,56 @@ nvc_parse_private_key (nvc_t *result, int *errlinep, estream_t stream) } +/* Helper fpr nvc_write. */ +static gpg_error_t +write_one_entry (nve_t entry, estream_t stream) +{ + gpg_error_t err; + strlist_t sl; + + if (entry->name) + es_fputs (entry->name, stream); + + err = assert_raw_value (entry); + if (err) + return err; + + for (sl = entry->raw_value; sl; sl = sl->next) + es_fputs (sl->d, stream); + + if (es_ferror (stream)) + return my_error_from_syserror (); + + return 0; +} + + /* Write a representation of PK to STREAM. */ gpg_error_t nvc_write (nvc_t pk, estream_t stream) { - gpg_error_t err; + gpg_error_t err = 0; nve_t entry; - strlist_t s; + nve_t keyentry = NULL; for (entry = pk->first; entry; entry = entry->next) { - if (entry->name) - es_fputs (entry->name, stream); + if (pk->private_key_mode + && entry->name && !ascii_strcasecmp (entry->name, "Key:")) + { + if (!keyentry) + keyentry = entry; + continue; + } - err = assert_raw_value (entry); + err = write_one_entry (entry, stream); if (err) return err; - - for (s = entry->raw_value; s; s = s->next) - es_fputs (s->d, stream); - - if (es_ferror (stream)) - return my_error_from_syserror (); } - return 0; + /* In private key mode we write the Key always last. */ + if (keyentry) + err = write_one_entry (keyentry, stream); + + return err; } diff --git a/common/name-value.h b/common/name-value.h index 5c24b8db1..a6283a649 100644 --- a/common/name-value.h +++ b/common/name-value.h @@ -72,6 +72,9 @@ nve_t nve_next (nve_t entry); /* Get the next entry with the given name. */ nve_t nve_next_value (nve_t entry, const char *name); +/* Return the string for the first entry in NVC with NAME or NULL. */ +const char *nvc_get_string (nvc_t nvc, const char *name); + /* Adding and modifying values. */ @@ -88,6 +91,9 @@ gpg_error_t nvc_set (nvc_t pk, const char *name, const char *value); /* Delete the given entry from PK. */ void nvc_delete (nvc_t pk, nve_t pke); +/* Delete the entries with NAME from PK. */ +void nvc_delete_named (nvc_t pk, const char *name); + /* Private key handling. */ diff --git a/common/openpgp-s2k.c b/common/openpgp-s2k.c index 2b0ba604b..69de76329 100644 --- a/common/openpgp-s2k.c +++ b/common/openpgp-s2k.c @@ -39,7 +39,7 @@ #include "openpgpdefs.h" -/* Pack an s2k iteration count into the form specified in RFC-48800. +/* Pack an s2k iteration count into the form specified in RFC-4880. * If we're in between valid values, round up. */ unsigned char encode_s2k_iterations (int iterations) diff --git a/common/sexputil.c b/common/sexputil.c index d3020e169..f99bc3b18 100644 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -581,9 +581,9 @@ get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen) /* Given the public key S_PKEY, return a new buffer with a descriptive * string for its algorithm. This function may return NULL on memory - * error. */ + * error. If R_ALGOID is not NULL the gcrypt algo id is stored there. */ char * -pubkey_algo_string (gcry_sexp_t s_pkey) +pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid) { const char *prefix; gcry_sexp_t l1; @@ -591,6 +591,9 @@ pubkey_algo_string (gcry_sexp_t s_pkey) int algo; char *result; + if (r_algoid) + *r_algoid = 0; + l1 = gcry_sexp_find_token (s_pkey, "public-key", 0); if (!l1) return xtrystrdup ("E_no_key"); @@ -632,6 +635,8 @@ pubkey_algo_string (gcry_sexp_t s_pkey) else result = xtryasprintf ("X_algo_%d", algo); + if (r_algoid) + *r_algoid = algo; xfree (algoname); return result; } diff --git a/common/t-name-value.c b/common/t-name-value.c index 57f685ffb..13a383ddb 100644 --- a/common/t-name-value.c +++ b/common/t-name-value.c @@ -292,6 +292,7 @@ run_modification_tests (void) { gpg_error_t err; nvc_t pk; + nve_t e; gcry_sexp_t key; char *buf; @@ -344,6 +345,30 @@ run_modification_tests (void) assert (strcmp (buf, "") == 0); xfree (buf); + /* Test whether we can delete an entry by name. */ + err = nvc_add (pk, "Key:", "(3:foo)"); + assert (!err); + e = nvc_lookup (pk, "Key:"); + assert (e); + nvc_delete_named (pk, "Kez:"); /* Delete an inexistant name. */ + e = nvc_lookup (pk, "Key:"); + assert (e); + nvc_delete_named (pk, "Key:"); + e = nvc_lookup (pk, "Key:"); + assert (!e); + + /* Ditto but now whether it deletes all entries with that name. We + * don't use "Key" because that name is special in private key mode. */ + err = nvc_add (pk, "AKey:", "A-value"); + assert (!err); + err = nvc_add (pk, "AKey:", "B-value"); + assert (!err); + e = nvc_lookup (pk, "AKey:"); + assert (e); + nvc_delete_named (pk, "AKey:"); + e = nvc_lookup (pk, "AKey:"); + assert (!e); + nvc_set (pk, "Foo:", "A really long value spanning across multiple lines" " that has to be wrapped at a convenient space."); buf = nvc_to_string (pk); diff --git a/common/userids.c b/common/userids.c index 181b48866..55bd85546 100644 --- a/common/userids.c +++ b/common/userids.c @@ -380,8 +380,10 @@ classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack) } else if (!hexprefix) { - /* The fingerprint in an X.509 listing is often delimited by - colons, so we try to single this case out. */ + /* The fingerprint of an X.509 listing is often delimited by + * colons, so we try to single this case out. Note that the + * OpenPGP bang suffix is not supported here. */ + desc->exact = 0; mode = 0; hexlength = strspn (s, ":0123456789abcdefABCDEF"); if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength))) @@ -454,7 +456,6 @@ classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack) } if (!mode) /* Default to substring search. */ { - desc->exact = 0; desc->u.name = s; mode = KEYDB_SEARCH_MODE_SUBSTR; } diff --git a/common/util.h b/common/util.h index 8895137ec..bd6cd1ff5 100644 --- a/common/util.h +++ b/common/util.h @@ -192,7 +192,7 @@ gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata, int get_pk_algo_from_key (gcry_sexp_t key); int get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen); -char *pubkey_algo_string (gcry_sexp_t s_pkey); +char *pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid); /*-- convert.c --*/ int hex2bin (const char *string, void *buffer, size_t length); diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c index adb005ec8..5486997b6 100644 --- a/dirmngr/certcache.c +++ b/dirmngr/certcache.c @@ -1471,6 +1471,9 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) { ksba_cert_ref (ci->cert); release_cache_lock (); + if (DBG_LOOKUP) + log_debug ("%s: certificate found in the cache" + " via ocsp_certs\n", __func__); return ci->cert; /* We use this certificate. */ } release_cache_lock (); @@ -1478,7 +1481,7 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) log_debug ("find_cert_bysubject: certificate not in ocsp_certs\n"); } - /* No check whether the certificate is cached. */ + /* Now check whether the certificate is cached. */ for (seq=0; (cert = get_cert_bysubject (subject_dn, seq)); seq++) { if (!keyid) @@ -1487,6 +1490,9 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) && !cmp_simple_canon_sexp (keyid, subj)) { xfree (subj); + if (DBG_LOOKUP) + log_debug ("%s: certificate found in the cache" + " via subject DN\n", __func__); break; /* Found matching cert. */ } xfree (subj); @@ -1495,6 +1501,34 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) if (cert) return cert; /* Done. */ + /* If we do not have a subject DN but have a keyid, try to locate it + * by keyid. */ + if (!subject_dn && keyid) + { + int i; + cert_item_t ci; + ksba_sexp_t ski; + + acquire_cache_read_lock (); + for (i=0; i < 256; i++) + for (ci=cert_cache[i]; ci; ci = ci->next) + if (ci->cert && !ksba_cert_get_subj_key_id (ci->cert, NULL, &ski)) + { + if (!cmp_simple_canon_sexp (keyid, ski)) + { + ksba_free (ski); + ksba_cert_ref (ci->cert); + release_cache_lock (); + if (DBG_LOOKUP) + log_debug ("%s: certificate found in the cache" + " via ski\n", __func__); + return ci->cert; + } + ksba_free (ski); + } + release_cache_lock (); + } + if (DBG_LOOKUP) log_debug ("find_cert_bysubject: certificate not in cache\n"); diff --git a/dirmngr/dns.c b/dirmngr/dns.c index fa5e5283d..142e8d2c1 100644 --- a/dirmngr/dns.c +++ b/dirmngr/dns.c @@ -2217,8 +2217,8 @@ static void dns_p_dump3(struct dns_packet *P, struct dns_rr_i *I, FILE *fp) { void dns_p_dump(struct dns_packet *P, FILE *fp) { - struct dns_rr_i _I = { 0 }; - dns_p_dump3(P, &_I, fp); + struct dns_rr_i I_instance = { 0 }; + dns_p_dump3(P, &I_instance, fp); } /* dns_p_dump() */ @@ -5275,8 +5275,8 @@ error: struct dns_packet *dns_hosts_query(struct dns_hosts *hosts, struct dns_packet *Q, int *error_) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; - struct dns_packet *P = dns_p_init(&_P.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; + struct dns_packet *P = dns_p_init(&P_instance.p, 512); struct dns_packet *A = 0; struct dns_rr rr; struct dns_hosts_entry *ent; @@ -6837,7 +6837,7 @@ unsigned dns_hints_grep(struct sockaddr **sa, socklen_t *sa_len, unsigned lim, s struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q, int *error_) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; struct dns_packet *A, *P; struct dns_rr rr; char zone[DNS_D_MAXNAME + 1]; @@ -6846,11 +6846,11 @@ struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q struct sockaddr *sa; socklen_t slen; int error; - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; - _I.section = DNS_S_QUESTION; + I_instance.section = DNS_S_QUESTION; - if (!dns_rr_grep(&rr, 1, &_I, Q, &error)) + if (!dns_rr_grep(&rr, 1, &I_instance, Q, &error)) goto error; if (!(zlen = dns_d_expand(zone, sizeof zone, rr.dn.p, Q, &error))) @@ -6858,7 +6858,7 @@ struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q else if (zlen >= sizeof zone) goto toolong; - P = dns_p_init(&_P.p, 512); + P = dns_p_init(&P_instance.p, 512); dns_header(P)->qr = 1; if ((error = dns_rr_copy(P, &rr, Q))) @@ -8463,8 +8463,8 @@ error: static struct dns_packet *dns_res_glue(struct dns_resolver *R, struct dns_packet *Q) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; - struct dns_packet *P = dns_p_init(&_P.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; + struct dns_packet *P = dns_p_init(&P_instance.p, 512); char qname[DNS_D_MAXNAME + 1]; size_t qlen; enum dns_type qtype; @@ -8537,20 +8537,20 @@ static int dns_res_nameserv_cmp(struct dns_rr *a, struct dns_rr *b, struct dns_r int cmp, error; if (!(error = dns_ns_parse(&ns, a, P))) { - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; - _I.section = (DNS_S_ALL & ~DNS_S_QD); - _I.name = ns.host; - _I.type = DNS_T_A; - glued[0] = !!dns_rr_grep(&x, 1, &_I, P, &error); + I_instance.section = (DNS_S_ALL & ~DNS_S_QD); + I_instance.name = ns.host; + I_instance.type = DNS_T_A; + glued[0] = !!dns_rr_grep(&x, 1, &I_instance, P, &error); } if (!(error = dns_ns_parse(&ns, b, P))) { - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; - _I.section = (DNS_S_ALL & ~DNS_S_QD); - _I.name = ns.host; - _I.type = DNS_T_A; - glued[1] = !!dns_rr_grep(&y, 1, &_I, P, &error); + I_instance.section = (DNS_S_ALL & ~DNS_S_QD); + I_instance.name = ns.host; + I_instance.type = DNS_T_A; + glued[1] = !!dns_rr_grep(&y, 1, &I_instance, P, &error); } if ((cmp = glued[1] - glued[0])) { return cmp; @@ -9916,13 +9916,13 @@ exec: return dns_ai_setent(ent, &any, rr.type, ai); case DNS_AI_S_SUBMIT_G: { - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; - _I.section = DNS_S_QD; - _I.name = ai->g.name; - _I.type = ai->g.type; + I_instance.section = DNS_S_QD; + I_instance.name = ai->g.name; + I_instance.type = ai->g.type; /* skip if already queried */ - if (dns_rr_grep(&rr, 1, &_I, ai->glue, &error)) + if (dns_rr_grep(&rr, 1, &I_instance, ai->glue, &error)) dns_ai_goto(DNS_AI_S_FOREACH_I); /* skip if we recursed (CNAME chains should have been handled in the resolver) */ if (++ai->g_depth > 1) @@ -10598,7 +10598,7 @@ static struct dns_trace *trace(const char *mode) { static void print_packet(struct dns_packet *P, FILE *fp) { - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; I.sort = MAIN.sort; dns_p_dump3(P, &I, fp); @@ -10608,10 +10608,10 @@ static void print_packet(struct dns_packet *P, FILE *fp) { static int parse_packet(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _Q = { 0 }; - struct dns_packet *P = dns_p_init(&_P.p, 512); - struct dns_packet *Q = dns_p_init(&_Q.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } Q_instance = { 0 }; + struct dns_packet *P = dns_p_init(&P_instance.p, 512); + struct dns_packet *Q = dns_p_init(&Q_instance.p, 512); enum dns_section section; struct dns_rr rr; int error; @@ -10655,7 +10655,7 @@ static int parse_packet(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { const char *dn = "ns8.yahoo.com"; char *_name = dns_d_init(_p, sizeof _p, dn, strlen (dn), DNS_D_ANCHOR); struct dns_rr rrset[32]; - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; struct dns_rr_i *rri = &I; unsigned rrcount = dns_rr_grep(rrset, lengthof(rrset), rri, Q, &error); @@ -10818,8 +10818,8 @@ static int show_hosts(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { static int query_hosts(int argc, char *argv[]) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _Q = { 0 }; - struct dns_packet *Q = dns_p_init(&_Q.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } Q_instance = { 0 }; + struct dns_packet *Q = dns_p_init(&Q_instance.p, 512); struct dns_packet *A; char qname[DNS_D_MAXNAME + 1]; size_t qlen; @@ -10937,8 +10937,8 @@ static int dump_random(int argc, char *argv[]) { static int send_query(int argc, char *argv[]) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _Q = { 0 }; - struct dns_packet *A, *Q = dns_p_init(&_Q.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } Q_instance = { 0 }; + struct dns_packet *A, *Q = dns_p_init(&Q_instance.p, 512); char host[INET6_ADDRSTRLEN + 1]; struct sockaddr_storage ss; struct dns_socket *so; @@ -11033,10 +11033,10 @@ static int show_hints(int argc, char *argv[]) { if (0 == strcmp(how, "plain")) { dns_hints_dump(hints, stdout); } else { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; struct dns_packet *query, *answer; - query = dns_p_init(&_P.p, 512); + query = dns_p_init(&P_instance.p, 512); if ((error = dns_p_push(query, DNS_S_QUESTION, who, strlen(who), DNS_T_A, DNS_C_IN, 0, 0))) panic("%s: %s", who, dns_strerror(error)); @@ -11199,8 +11199,8 @@ static int echo_port(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { panic("127.0.0.1:5353: %s", dns_strerror(errno)); for (;;) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; - struct dns_packet *pkt = dns_p_init(&_P.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; + struct dns_packet *pkt = dns_p_init(&P_instance.p, 512); struct sockaddr_storage ss; socklen_t slen = sizeof ss; ssize_t count; diff --git a/dirmngr/domaininfo.c b/dirmngr/domaininfo.c index f6263b06d..b41aef366 100644 --- a/dirmngr/domaininfo.c +++ b/dirmngr/domaininfo.c @@ -47,6 +47,7 @@ struct domaininfo_s unsigned int wkd_not_found:1; /* A WKD query failed. */ unsigned int wkd_supported:1; /* One WKD entry was found. */ unsigned int wkd_not_supported:1; /* Definitely does not support WKD. */ + unsigned int keepmark:1; /* Private to insert_or_update(). */ char name[1]; }; typedef struct domaininfo_s *domaininfo_t; @@ -143,7 +144,10 @@ insert_or_update (const char *domain, { domaininfo_t di; domaininfo_t di_new; - domaininfo_t di_cut; + domaininfo_t drop = NULL; + domaininfo_t drop_extra = NULL; + int nkept = 0; + int ndropped = 0; u32 hash; int count; @@ -162,7 +166,6 @@ insert_or_update (const char *domain, /* Need to do another lookup because the malloc is a system call and * thus the hash array may have been changed by another thread. */ - di_cut = NULL; for (count=0, di = domainbuckets[hash]; di; di = di->next, count++) if (!strcmp (di->name, domain)) { @@ -172,16 +175,89 @@ insert_or_update (const char *domain, } /* Before we insert we need to check whether the chain gets too long. */ - di_cut = NULL; if (count >= MAX_DOMAINBUCKET_LEN) { - for (count=0, di = domainbuckets[hash]; di; di = di->next, count++) - if (count >= MAX_DOMAINBUCKET_LEN/2) - { - di_cut = di->next; - di->next = NULL; - break; - } + domaininfo_t bucket; + domaininfo_t *array; + int narray, idx; + domaininfo_t keep = NULL; + + /* Unlink from the global list before doing a syscall. */ + bucket = domainbuckets[hash]; + domainbuckets[hash] = NULL; + + array = xtrycalloc (count, sizeof *array); + if (!array) + { + /* That's bad; give up the entire bucket. */ + log_error ("domaininfo: error allocating helper array: %s\n", + gpg_strerror (gpg_err_code_from_syserror ())); + drop_extra = bucket; + goto leave; + } + narray = 0; + + /* Move all items into an array for easier processing. */ + for (di = bucket; di; di = di->next) + array[narray++] = di; + log_assert (narray == count); + + /* Mark all item in the array which are flagged to support wkd + * but not more than half of the maximum. This way we will at + * the end drop half of the items. */ + count = 0; + for (idx=0; idx < narray; idx++) + { + di = array[idx]; + di->keepmark = 0; /* Clear flag here on the first pass. */ + if (di->wkd_supported && count < MAX_DOMAINBUCKET_LEN/2) + { + di->keepmark = 1; + count++; + } + } + /* Now mark those which are marked as not found. */ + /* FIXME: we should use an LRU algorithm here. */ + for (idx=0; idx < narray; idx++) + { + di = array[idx]; + if (!di->keepmark + && di->wkd_not_supported && count < MAX_DOMAINBUCKET_LEN/2) + { + di->keepmark = 1; + count++; + } + } + + /* Build a bucket list and a second list for later freeing the + * items (we can't do it directly because a free is a system + * call and we want to avoid locks in this module. Note that + * the kept items will be reversed order which does not matter. */ + for (idx=0; idx < narray; idx++) + { + di = array[idx]; + if (di->keepmark) + { + di->next = keep; + keep = di; + nkept++; + } + else + { + di->next = drop; + drop = di; + ndropped++; + } + } + + /* In case another thread added new stuff to the domain list we + * simply drop them instead all. It would also be possible to + * append them to our list but then we can't guarantee that a + * bucket list is almost all of the time limited to + * MAX_DOMAINBUCKET_LEN. Not sure whether this is really a + * sensible strategy. */ + drop_extra = domainbuckets[hash]; + domainbuckets[hash] = keep; } /* Insert */ @@ -190,17 +266,28 @@ insert_or_update (const char *domain, di->next = domainbuckets[hash]; domainbuckets[hash] = di; - /* Remove the rest of the cutted chain. */ - while (di_cut) + if (opt.verbose && (nkept || ndropped)) + log_info ("domaininfo: bucket=%lu kept=%d purged=%d\n", + (unsigned long)hash, nkept, ndropped); + + leave: + /* Remove the dropped items. */ + while (drop) { - di = di_cut->next; - xfree (di_cut); - di_cut = di; + di = drop->next; + xfree (drop); + drop = di; + } + while (drop_extra) + { + di = drop_extra->next; + xfree (drop_extra); + drop_extra = di; } } -/* Helper for domaininfo_set_no_name. */ +/* Helper for domaininfo_set_no_name. May not do any syscalls. */ static void set_no_name_cb (domaininfo_t di, int insert_mode) { @@ -224,7 +311,7 @@ domaininfo_set_no_name (const char *domain) } -/* Helper for domaininfo_set_wkd_supported. */ +/* Helper for domaininfo_set_wkd_supported. May not do any syscalls. */ static void set_wkd_supported_cb (domaininfo_t di, int insert_mode) { @@ -245,7 +332,7 @@ domaininfo_set_wkd_supported (const char *domain) } -/* Helper for domaininfo_set_wkd_not_supported. */ +/* Helper for domaininfo_set_wkd_not_supported. May not do any syscalls. */ static void set_wkd_not_supported_cb (domaininfo_t di, int insert_mode) { @@ -265,7 +352,7 @@ domaininfo_set_wkd_not_supported (const char *domain) -/* Helper for domaininfo_set_wkd_not_found. */ +/* Helper for domaininfo_set_wkd_not_found. May not do any syscalls. */ static void set_wkd_not_found_cb (domaininfo_t di, int insert_mode) { diff --git a/dirmngr/http.c b/dirmngr/http.c index d6856fe05..81b7ba897 100644 --- a/dirmngr/http.c +++ b/dirmngr/http.c @@ -3536,8 +3536,13 @@ same_host_p (parsed_uri_t a, parsed_uri_t b) { "protonmail.com", "api.protonmail.com" }, { NULL, "api.protonmail.ch" }, { "protonmail.ch", "api.protonmail.com" }, - { NULL, "api.protonmail.ch" } + { NULL, "api.protonmail.ch" }, + { "pm.me", "api.protonmail.ch" } }; + static const char *subdomains[] = + { + "openpgpkey." + }; int i; const char *from; @@ -3559,6 +3564,22 @@ same_host_p (parsed_uri_t a, parsed_uri_t b) return 1; } + /* Also consider hosts the same if they differ only in a subdomain; + * in both direction. This allows to have redirection between the + * WKD advanced and direct lookup methods. */ + for (i=0; i < DIM (subdomains); i++) + { + const char *subdom = subdomains[i]; + size_t subdomlen = strlen (subdom); + + if (!ascii_strncasecmp (a->host, subdom, subdomlen) + && !ascii_strcasecmp (a->host + subdomlen, b->host)) + return 1; + if (!ascii_strncasecmp (b->host, subdom, subdomlen) + && !ascii_strcasecmp (b->host + subdomlen, a->host)) + return 1; + } + return 0; } diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c index 4d660b87e..f8814ecd0 100644 --- a/dirmngr/ks-engine-hkp.c +++ b/dirmngr/ks-engine-hkp.c @@ -68,6 +68,10 @@ /* Number of retries done for a dead host etc. */ #define SEND_REQUEST_RETRIES 3 +/* Number of retries done in case of transient errors. */ +#define SEND_REQUEST_EXTRA_RETRIES 5 + + enum ks_protocol { KS_PROTOCOL_HKP, KS_PROTOCOL_HKPS, KS_PROTOCOL_MAX }; /* Objects used to maintain information about hosts. */ @@ -1217,6 +1221,7 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, /* FIXME: I am not sure whey we allow a downgrade for hkp requests. * Needs at least an explanation here.. */ + once_more: err = http_session_new (&session, httphost, ((ctrl->http_no_crl? HTTP_FLAG_NO_CRL : 0) | HTTP_FLAG_TRUST_DEF), @@ -1226,7 +1231,6 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, http_session_set_log_cb (session, cert_log_cb); http_session_set_timeout (session, ctrl->timeout); - once_more: err = http_open (ctrl, &http, post_cb? HTTP_REQ_POST : HTTP_REQ_GET, request, @@ -1306,6 +1310,8 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, request = request_buffer; http_close (http, 0); http = NULL; + http_session_release (session); + session = NULL; } goto once_more; @@ -1313,6 +1319,10 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); goto leave; + case 413: /* Payload too large */ + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + default: log_error (_("error accessing '%s': http status %u\n"), request, http_get_status_code (http)); @@ -1349,10 +1359,12 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, with REQUEST. The function returns true if the caller shall try again. TRIES_LEFT points to a variable to track the number of retries; this function decrements it and won't return true if it is - down to zero. */ + down to zero. EXTRA_TRIES_LEFT does the same but only for + transient http status codes. */ static int handle_send_request_error (ctrl_t ctrl, gpg_error_t err, const char *request, - unsigned int http_status, unsigned int *tries_left) + unsigned int http_status, unsigned int *tries_left, + unsigned int *extra_tries_left) { int retry = 0; @@ -1408,9 +1420,12 @@ handle_send_request_error (ctrl_t ctrl, gpg_error_t err, const char *request, case 503: /* Service Unavailable */ case 504: /* Gateway Timeout */ - log_info ("selecting a different host due to a %u (%s)", - http_status, http_status2string (http_status)); - retry = 1; + if (*extra_tries_left) + { + log_info ("selecting a different host due to a %u (%s)", + http_status, http_status2string (http_status)); + retry = 2; + } break; } } @@ -1420,8 +1435,16 @@ handle_send_request_error (ctrl_t ctrl, gpg_error_t err, const char *request, break; } - if (*tries_left) - --*tries_left; + if (retry == 2) + { + if (*extra_tries_left) + --*extra_tries_left; + } + else + { + if (*tries_left) + --*tries_left; + } return retry; } @@ -1446,6 +1469,7 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, char *httphost = NULL; unsigned int http_status; unsigned int tries = SEND_REQUEST_RETRIES; + unsigned int extra_tries = SEND_REQUEST_EXTRA_RETRIES; *r_fp = NULL; @@ -1521,7 +1545,8 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, /* Send the request. */ err = send_request (ctrl, request, hostport, httphost, httpflags, NULL, NULL, &fp, &http_status); - if (handle_send_request_error (ctrl, err, request, http_status, &tries)) + if (handle_send_request_error (ctrl, err, request, http_status, + &tries, &extra_tries)) { reselect = 1; goto again; @@ -1591,6 +1616,7 @@ ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp) unsigned int httpflags; unsigned int http_status; unsigned int tries = SEND_REQUEST_RETRIES; + unsigned int extra_tries = SEND_REQUEST_EXTRA_RETRIES; *r_fp = NULL; @@ -1664,7 +1690,8 @@ ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp) /* Send the request. */ err = send_request (ctrl, request, hostport, httphost, httpflags, NULL, NULL, &fp, &http_status); - if (handle_send_request_error (ctrl, err, request, http_status, &tries)) + if (handle_send_request_error (ctrl, err, request, http_status, + &tries, &extra_tries)) { reselect = 1; goto again; @@ -1740,6 +1767,7 @@ ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen) unsigned int httpflags; unsigned int http_status; unsigned int tries = SEND_REQUEST_RETRIES; + unsigned int extra_tries = SEND_REQUEST_EXTRA_RETRIES; parm.datastring = NULL; @@ -1778,7 +1806,8 @@ ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen) /* Send the request. */ err = send_request (ctrl, request, hostport, httphost, 0, put_post_cb, &parm, &fp, &http_status); - if (handle_send_request_error (ctrl, err, request, http_status, &tries)) + if (handle_send_request_error (ctrl, err, request, http_status, + &tries, &extra_tries)) { reselect = 1; goto again; diff --git a/dirmngr/ks-engine-http.c b/dirmngr/ks-engine-http.c index 0f3e2db4a..a84a3a1ea 100644 --- a/dirmngr/ks-engine-http.c +++ b/dirmngr/ks-engine-http.c @@ -174,6 +174,10 @@ ks_http_fetch (ctrl_t ctrl, const char *url, unsigned int flags, } goto once_more; + case 413: /* Payload too large */ + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + default: log_error (_("error accessing '%s': http status %u\n"), url, http_get_status_code (http)); diff --git a/dirmngr/ocsp.c b/dirmngr/ocsp.c index 79c252d87..e19779c59 100644 --- a/dirmngr/ocsp.c +++ b/dirmngr/ocsp.c @@ -116,10 +116,15 @@ read_response (estream_t fp, unsigned char **r_buffer, size_t *r_buflen) /* Construct an OCSP request, send it to the configured OCSP responder and parse the response. On success the OCSP context may be used to - further process the response. */ + further process the response. The signature value and the + production date are returned at R_SIGVAL and R_PRODUCED_AT; they + may be NULL or an empty string if not available. A new hash + context is returned at R_MD. */ static gpg_error_t -do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, - const char *url, ksba_cert_t cert, ksba_cert_t issuer_cert) +do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, + const char *url, ksba_cert_t cert, ksba_cert_t issuer_cert, + ksba_sexp_t *r_sigval, ksba_isotime_t r_produced_at, + gcry_md_hd_t *r_md) { gpg_error_t err; unsigned char *request, *response; @@ -132,6 +137,10 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, (void)ctrl; + *r_sigval = NULL; + *r_produced_at = 0; + *r_md = NULL; + if (dirmngr_use_tor ()) { /* For now we do not allow OCSP via Tor due to possible privacy @@ -238,6 +247,10 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, } break; + case 413: /* Payload too large */ + err = gpg_error (GPG_ERR_TOO_LARGE); + break; + default: log_error (_("error accessing '%s': http status %u\n"), url, http_get_status_code (http)); @@ -259,6 +272,7 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, xfree (free_this); return err; } + /* log_printhex (response, responselen, "ocsp response"); */ err = ksba_ocsp_parse_response (ocsp, response, responselen, &response_status); @@ -286,11 +300,34 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, } if (response_status == KSBA_OCSP_RSPSTATUS_SUCCESS) { + int hash_algo; + if (opt.verbose) log_info (_("OCSP responder at '%s' status: %s\n"), url, t); + /* Get the signature value now because we can all this fucntion + * only once. */ + *r_sigval = ksba_ocsp_get_sig_val (ocsp, r_produced_at); + + hash_algo = hash_algo_from_sigval (*r_sigval); + if (!hash_algo) + { + if (opt.verbose) + log_info ("ocsp: using SHA-256 as fallback hash algo.\n"); + hash_algo = GCRY_MD_SHA256; + } + err = gcry_md_open (r_md, hash_algo, 0); + if (err) + { + log_error (_("failed to establish a hashing context for OCSP: %s\n"), + gpg_strerror (err)); + goto leave; + } + if (DBG_HASHING) + gcry_md_debug (*r_md, "ocsp"); + err = ksba_ocsp_hash_response (ocsp, response, responselen, - HASH_FNC, md); + HASH_FNC, *r_md); if (err) log_error (_("hashing the OCSP response for '%s' failed: %s\n"), url, gpg_strerror (err)); @@ -301,8 +338,17 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, err = gpg_error (GPG_ERR_GENERAL); } + leave: xfree (response); xfree (free_this); + if (err) + { + xfree (*r_sigval); + *r_sigval = NULL; + *r_produced_at = 0; + gcry_md_close (*r_md); + *r_md = NULL; + } return err; } @@ -387,7 +433,7 @@ check_signature_core (ctrl_t ctrl, ksba_cert_t cert, gcry_sexp_t s_sig, /* We simply ignore all errors. */ gcry_sexp_release (s_pkey); - return -1; + return err; } @@ -406,18 +452,27 @@ check_signature (ctrl_t ctrl, int algo, cert_idx; gcry_sexp_t s_hash; ksba_cert_t cert; + const char *s; /* Create a suitable S-expression with the hash value of our response. */ gcry_md_final (md); algo = gcry_md_get_algo (md); - if (algo != GCRY_MD_SHA1 ) + s = gcry_md_algo_name (algo); + if (algo && s && strlen (s) < 16) { - log_error (_("only SHA-1 is supported for OCSP responses\n")); - return gpg_error (GPG_ERR_DIGEST_ALGO); + char hashalgostr[16+1]; + int i; + + for (i=0; s[i]; i++) + hashalgostr[i] = ascii_tolower (s[i]); + hashalgostr[i] = 0; + err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", + hashalgostr, + (int)gcry_md_get_algo_dlen (algo), + gcry_md_read (md, algo)); } - err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash sha1 %b))", - gcry_md_get_algo_dlen (algo), - gcry_md_read (md, algo)); + else + err = gpg_error (GPG_ERR_DIGEST_ALGO); if (err) { log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); @@ -461,6 +516,7 @@ check_signature (ctrl_t ctrl, { cert_ref_t cref; + /* dump_cert ("from ocsp response", cert); */ cref = xtrymalloc (sizeof *cref); if (!cref) log_error (_("allocating list item failed: %s\n"), @@ -496,8 +552,6 @@ check_signature (ctrl_t ctrl, } log_printf ("not found\n"); } - ksba_free (name); - ksba_free (keyid); if (cert) { @@ -506,10 +560,24 @@ check_signature (ctrl_t ctrl, ksba_cert_release (cert); if (!err) { + ksba_free (name); + ksba_free (keyid); gcry_sexp_release (s_hash); return 0; /* Successfully verified the signature. */ } + log_error ("responder certificate "); + if (name) + log_printf ("'/%s' ", name); + if (keyid) + { + log_printf ("{"); + dump_serial (keyid); + log_printf ("} "); + } + log_printf ("did not verify: %s\n", gpg_strerror (err)); } + ksba_free (name); + ksba_free (keyid); } gcry_sexp_release (s_hash); @@ -584,8 +652,6 @@ ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr, goto leave; } - - /* Figure out the OCSP responder to use. 1. Try to get the reponder from the certificate. We do only take http and https style URIs into account. @@ -642,14 +708,8 @@ ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr, } /* Ask the OCSP responder. */ - err = gcry_md_open (&md, GCRY_MD_SHA1, 0); - if (err) - { - log_error (_("failed to establish a hashing context for OCSP: %s\n"), - gpg_strerror (err)); - goto leave; - } - err = do_ocsp_request (ctrl, ocsp, md, url, cert, issuer_cert); + err = do_ocsp_request (ctrl, ocsp, url, cert, issuer_cert, + &sigval, produced_at, &md); if (err) goto leave; @@ -681,8 +741,7 @@ ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr, } /* We got a useful answer, check that the answer has a valid signature. */ - sigval = ksba_ocsp_get_sig_val (ocsp, produced_at); - if (!sigval || !*produced_at) + if (!sigval || !*produced_at || !md) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; diff --git a/doc/DETAILS b/doc/DETAILS index 74a63ef00..3046523da 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1572,6 +1572,7 @@ Description of some debug flags: | ecc/* | 11 | ECC (set your own capabilities) | | ecc/e | 12 | ECC (encrypt only) | | keygrip | 13 | Existing key | + | cardkey | 14 | Existing key from card | If one of the "foo/*" names are used a "keygen.flags" prompt needs to be answered as well. Instead of toggling the predefined flags, diff --git a/doc/Makefile.am b/doc/Makefile.am index 0720dd366..0c44217d0 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -95,7 +95,7 @@ myman_pages = gpgsm.1 gpg-agent.1 dirmngr.8 scdaemon.1 \ watchgnupg.1 gpgconf.1 addgnupghome.8 gpg-preset-passphrase.1 \ gpg-connect-agent.1 gpgparsemail.1 symcryptrun.1 gpgtar.1 \ applygnupgdefaults.8 gpg-wks-client.1 gpg-wks-server.1 \ - dirmngr-client.1 gpg-card.1 + dirmngr-client.1 gpg-card.1 gpg-check-pattern.1 if USE_GPG2_HACK myman_pages += gpg2.1 gpgv2.1 else @@ -121,6 +121,7 @@ mkdefsinc: mkdefsinc.c Makefile ../config.h $(CC_FOR_BUILD) -I. -I.. -I$(srcdir) $(AM_CPPFLAGS) \ -o $@ $(srcdir)/mkdefsinc.c +if MAINTAINER_MODE .svg.eps: convert `test -f '$<' || echo '$(srcdir)/'`$< $@ @@ -141,6 +142,7 @@ mkdefsinc: mkdefsinc.c Makefile ../config.h .fig.pdf: fig2dev -L pdf `test -f '$<' || echo '$(srcdir)/'`$< $@ +endif yat2m-stamp: $(myman_sources) defs.inc diff --git a/doc/dirmngr.texi b/doc/dirmngr.texi index f5910a884..eb49ad96c 100644 --- a/doc/dirmngr.texi +++ b/doc/dirmngr.texi @@ -251,7 +251,7 @@ The option @option{--use-tor} switches Dirmngr and thus GnuPG into ``Tor mode'' to route all network access via Tor (an anonymity network). Certain other features are disabled in this mode. The effect of @option{--use-tor} cannot be overridden by any other command -or even be reloading gpg-agent. The use of @option{--no-use-tor} +or even by reloading dirmngr. The use of @option{--no-use-tor} disables the use of Tor. The default is to use Tor if it is available on startup or after reloading dirmngr. @@ -1178,5 +1178,3 @@ as a binary blob. @c used for this. The first one starts a search and the second one is @c used to retrieve certificate after certificate. @c - - diff --git a/doc/gpg-card.texi b/doc/gpg-card.texi index aa49f81e7..fcc1792f1 100644 --- a/doc/gpg-card.texi +++ b/doc/gpg-card.texi @@ -8,9 +8,9 @@ @node Smart Card Tool @chapter Smart Card Tool -GnuPG comes with tool to administrate smart cards and USB tokens. This -tool is an extension of the @option{--edit-key} command available with -@command{gpg}. +GnuPG comes with a tool to administrate smart cards and USB tokens. +This tool is an enhanced version of the @option{--edit-key} command +available with @command{gpg}. @menu * gpg-card:: Administrate smart cards. @@ -135,7 +135,7 @@ featuring the PIV application (requires Yubikey-5). We assume that the credentials have not yet been changed and thus are: @table @asis @item Authentication key -This is a 24 byte key described by the hex string +This is a 24 byte key described by the hex string @* @code{010203040506070801020304050607080102030405060708}. @item PIV Application PIN This is the string @code{123456}. @@ -164,11 +164,13 @@ Version ..........: 2.1 [...] @end example -It can be seen by the ``Application type'' line that GnuPG selected the -OpenPGP application of the Yubikey. This is because GnuPG assigns the -highest priority to the OpenPGP application. To use the PIV -application of the Yubikey, the OpenPGP application needs to be -disabled: +It can be seen by the ``Application type'' line that GnuPG selected +the OpenPGP application of the Yubikey. This is because GnuPG assigns +the highest priority to the OpenPGP application. To use the PIV +application of the Yubikey several methods can be used: + +With a Yubikey 5 or later the OpenPGP application on the Yubikey can +be disabled: @example gpg/card> yubikey disable all opgp @@ -186,8 +188,32 @@ gpg/card> reset The @code{reset} is required so that the GnuPG system rereads the card. Note that disabled applications keep all their data and can at -any time be re-enabled (see @emph{help yubikey}). Now a @emph{list} -command shows this: +any time be re-enabled (use @kbd{help yubikey}). + +Another option, which works for all Yubikey versions, is to disable +the support for OpenPGP cards in scdaemon. This is done by adding the +line + +@smallexample +disable-application openpgp +@end smallexample + +to @file{~/.gnupg/scdaemon.conf} and by restarting scdaemon, either by +killing the process or by using @kbd{gpgconf --kill scdaemon}. Finally +the default order in which card applications are tried by scdaemon can +be changed. For example to prefer PIV over OpenPGP it is sufficient +to add + +@smallexample +application-priority piv +@end smallexample + +to @file{~/.gnupg/scdaemon.conf} and to restart @command{scdaemon}. +This has an effect only on tokens which support both, PIV and OpenPGP, +but does not hamper the use of OpenPGP only tokens. + +With one of these methods employed the @code{list} command of +@command{gpg-card} shows this: @example gpg/card> list @@ -210,7 +236,12 @@ Key management ...: [none] keyref .....: PIV.9D @end example -Note that the ``Displayed s/sn'' is printed on the token and also +In case several tokens are plugged into the computer, gpg-card will +show only one. To show another token the number of the token (0, 1, +2, ...) can be given as an argument to the @code{list} command. The +command @kbd{list --cards} prints a list of all inserted tokens. + +Note that the ``Displayed s/n'' is printed on the token and also shown in Pinentry prompts asking for the PIN. The four standard key slots are always shown, if other key slots are initialized they are shown as well. The @emph{PIV authentication} key (internal reference @@ -231,11 +262,11 @@ which needs to be provided only once so that decryption operations can then be done until the card is reset or removed from the reader or USB port. -We now generate tree of the four keys. Note that GnuPG does currently -not use the the @emph{Card authentication} key but because it is -mandatory by the specs we create it anyway. Key generation requires -that we authenticate to the card. This can be done either on the -command line (which would reveal the key): +We now generate three of the four keys. Note that GnuPG does +currently not use the the @emph{Card authentication} key; however, +that key is mandatory by the PIV standard and thus we create it too. +Key generation requires that we authenticate to the card. This can be +done either on the command line (which would reveal the key): @example gpg/card> auth 010203040506070801020304050607080102030405060708 @@ -360,7 +391,7 @@ gpgsm: total number processed: 1 gpgsm: imported: 1 @end example -Note the last steps which imported the created certificate. If you +Note the last step which imported the created certificate. If you you instead created a certificate signing request (CSR) instead of a self-signed certificate and sent this off to a CA you would do the same import step with the certificate received from the CA. Take note @@ -507,7 +538,111 @@ As usual use ssh-add with the uppercase @samp{-L} to list the public ssh key. To use the certificates with Thunderbird or Mozilla, please consult the Scute manual for details. +If you want to use the same PIV keys also for OpenPGP (for example on +a Yubikey to avoid switching between OpenPGP and PIV), this is also +possible: +@example +$ gpgsm --learn +$ gpg --full-gen-key +Please select what kind of key you want: + (1) RSA and RSA (default) + (2) DSA and Elgamal + (3) DSA (sign only) + (4) RSA (sign only) + (14) Existing key from card +Your selection? 14 +Serial number of the card: FF020001008A77C1 +Available keys: + (1) 213D1825FDE0F8240CB4E4229F01AF90AC658C2E PIV.9A nistp384 (auth) + (2) 7A53E6CFFE7220A0E646B4632EE29E5A7104499C PIV.9E nistp256 (auth) + (3) 32A6C6FAFCB8421878608AAB452D5470DD3223ED PIV.9C rsa2048 (cert,sign) + (4) 34798AAFE0A7565088101CC4AE31C5C8C74461CB PIV.9D rsa2048 (encr) +Your selection? 3 +Please specify how long the key should be valid. + 0 = key does not expire + = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years +Key is valid for? (0) +Key does not expire at all +Is this correct? (y/N) y + +GnuPG needs to construct a user ID to identify your key. + +Real name: +Email address: otto@@example.net +Comment: +You selected this USER-ID: + "otto@@example.net" + +Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o +gpg: key C3AFA9ED971BB365 marked as ultimately trusted +gpg: revocation certificate stored as '[...]D971BB365.rev' +public and secret key created and signed. + +Note that this key cannot be used for encryption. You may want to use +the command "--edit-key" to generate a subkey for this purpose. +pub rsa2048 2019-04-04 [SC] + 7F899AE2FB73159DD68A1B20C3AFA9ED971BB365 +uid otto@@example.net +@end example + +Note that you will be asked two times to enter the PIN of your PIV +card. If you run @command{gpg} in @option{--expert} mode you will +also ge given the option to change the usage flags of the key. The next +typescript shows how to add the encryption subkey: + +@example +$ gpg --edit-key 7F899AE2FB73159DD68A1B20C3AFA9ED971BB365 +Secret key is available. + +sec rsa2048/C3AFA9ED971BB365 + created: 2019-04-04 expires: never usage: SC + card-no: FF020001008A77C1 + trust: ultimate validity: ultimate +[ultimate] (1). otto@@example.net +gpg> addkey +Secret parts of primary key are stored on-card. +Please select what kind of key you want: + (3) DSA (sign only) + (4) RSA (sign only) + (5) Elgamal (encrypt only) + (6) RSA (encrypt only) + (14) Existing key from card +Your selection? 14 +Serial number of the card: FF020001008A77C1 +Available keys: + (1) 213D1825FDE0F8240CB4E4229F01AF90AC658C2E PIV.9A nistp384 (auth) + (2) 7A53E6CFFE7220A0E646B4632EE29E5A7104499C PIV.9E nistp256 (auth) + (3) 32A6C6FAFCB8421878608AAB452D5470DD3223ED PIV.9C rsa2048 (cert,sign) + (4) 34798AAFE0A7565088101CC4AE31C5C8C74461CB PIV.9D rsa2048 (encr) +Your selection? 4 +Please specify how long the key should be valid. + 0 = key does not expire + = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years +Key is valid for? (0) +Key does not expire at all +Is this correct? (y/N) y +Really create? (y/N) y + +sec rsa2048/C3AFA9ED971BB365 + created: 2019-04-04 expires: never usage: SC + card-no: FF020001008A77C1 + trust: ultimate validity: ultimate +ssb rsa2048/7067860A98FCE6E1 + created: 2019-04-04 expires: never usage: E + card-no: FF020001008A77C1 +[ultimate] (1). otto@@example.net + +gpg> save +@end example + +Now you can use your PIV card also with @command{gpg}. @c @mansect examples diff --git a/doc/gpg.texi b/doc/gpg.texi index e6829b911..80c7f48f5 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -346,12 +346,17 @@ numbers 1-9 or "T" for 10 and above to indicate trust signature levels @item --locate-keys +@itemx --locate-external-keys @opindex locate-keys +@opindex locate-external-keys Locate the keys given as arguments. This command basically uses the -same algorithm as used when locating keys for encryption or signing and -may thus be used to see what keys @command{@gpgname} might use. In -particular external methods as defined by @option{--auto-key-locate} may -be used to locate a key. Only public keys are listed. +same algorithm as used when locating keys for encryption or signing +and may thus be used to see what keys @command{@gpgname} might use. +In particular external methods as defined by +@option{--auto-key-locate} may be used to locate a key. Only public +keys are listed. The variant @option{--locate-external-keys} does not +consider a locally existing key and can thus be used to force the +refresh of a key via the defined external methods. @item --show-keys @opindex show-keys @@ -404,7 +409,10 @@ functionality is also available as the subcommand "passwd" with the @opindex delete-keys Remove key from the public keyring. In batch mode either @option{--yes} is required or the key must be specified by fingerprint. This is a -safeguard against accidental deletion of multiple keys. +safeguard against accidental deletion of multiple keys. If the +exclamation mark syntax is used with the fingerprint of a subkey only +that subkey is deleted; if the exclamation mark is used with the +fingerprint of the primary key the entire public key is deleted. @item --delete-secret-keys @var{name} @opindex delete-secret-keys @@ -413,7 +421,10 @@ specified by fingerprint. The option @option{--yes} can be used to advice gpg-agent not to request a confirmation. This extra pre-caution is done because @command{@gpgname} can't be sure that the secret key (as controlled by gpg-agent) is only used for the given -OpenPGP public key. +OpenPGP public key. If the exclamation mark syntax is used with the +fingerprint of a subkey only the secret part of that subkey is +deleted; if the exclamation mark is used with the fingerprint of the +primary key only the secret part of the primary key is deleted. @item --delete-secret-and-public-key @var{name} @@ -434,9 +445,8 @@ file given with option @option{--output}. Use together with @item --send-keys @var{keyIDs} @opindex send-keys Similar to @option{--export} but sends the keys to a keyserver. -Fingerprints may be used instead of key IDs. Option -@option{--keyserver} must be used to give the name of this -keyserver. Don't send your complete keyring to a keyserver --- select +Fingerprints may be used instead of key IDs. +Don't send your complete keyring to a keyserver --- select only those keys which are new or changed by you. If no @var{keyIDs} are given, @command{@gpgname} does nothing. @@ -491,27 +501,25 @@ signatures, user-IDs and subkeys. @opindex receive-keys @itemx --recv-keys @var{keyIDs} @opindex recv-keys -Import the keys with the given @var{keyIDs} from a keyserver. Option -@option{--keyserver} must be used to give the name of this keyserver. +Import the keys with the given @var{keyIDs} from a keyserver. @item --refresh-keys @opindex refresh-keys Request updates from a keyserver for keys that already exist on the local keyring. This is useful for updating a key with the latest signatures, user IDs, etc. Calling this with no arguments will refresh -the entire keyring. Option @option{--keyserver} must be used to give the -name of the keyserver for all keys that do not have preferred keyservers -set (see @option{--keyserver-options honor-keyserver-url}). +the entire keyring. @item --search-keys @var{names} @opindex search-keys -Search the keyserver for the given @var{names}. Multiple names given here will -be joined together to create the search string for the keyserver. -Option @option{--keyserver} must be used to give the name of this -keyserver. Keyservers that support different search methods allow using -the syntax specified in "How to specify a user ID" below. Note that -different keyserver types support different search methods. Currently -only LDAP supports them all. +Search the keyserver for the given @var{names}. Multiple names given +here will be joined together to create the search string for the +keyserver. Note that keyservers search for @var{names} in a different +and simpler way than gpg does. The best choice is to use a mail +address. Due to data privacy reasons keyservers may even not even +allow searching by user id or mail address and thus may only return +results when being used with the @option{--recv-key} command to +search by key fingerprint or keyid. @item --fetch-keys @var{URIs} @opindex fetch-keys @@ -1330,8 +1338,8 @@ give the opposite meaning. The options are: @item show-only-fpr-mbox @opindex list-options:show-only-fpr-mbox - For each valid user-id which also has a valid mail address print - only the fingerprint and the mail address. + For each user-id which has a valid mail address print + only the fingerprint followed by the mail address. @end table @item --verify-options @var{parameters} @@ -1429,19 +1437,24 @@ viewed (e.g. "f"), "%V" for the calculated validity as a string (e.g. and "%%" for an actual percent sign. If neither %i or %I are present, then the photo will be supplied to the viewer on standard input. -The default viewer is "xloadimage -fork -quiet -title 'KeyID 0x%k' -STDIN". Note that if your image viewer program is not secure, then -executing it from GnuPG does not make it secure. +On Unix the default viewer is +@code{xloadimage -fork -quiet -title 'KeyID 0x%k' STDIN} +with a fallback to +@code{display -title 'KeyID 0x%k' %i} +and finally to +@code{xdg-open %i}. +On Windows +@code{!ShellExecute 400 %i} is used; here the command is a meta +command to use that API call followed by a wait time in milliseconds +which is used to give the viewer time to read the temporary image file +before gpg deletes it again. Note that if your image viewer program +is not secure, then executing it from gpg does not make it secure. @item --exec-path @var{string} @opindex exec-path @efindex PATH -Sets a list of directories to search for photo viewers and keyserver -helpers. If not provided, keyserver helpers use the compiled-in -default directory, and photo viewers use the @code{PATH} environment -variable. -Note, that on W32 system this value is ignored when searching for -keyserver helpers. +Sets a list of directories to search for photo viewers If not provided +photo viewers use the @code{PATH} environment variable. @item --keyring @var{file} @opindex keyring @@ -1766,12 +1779,11 @@ list. The default is "local,wkd". PGP Universal method of checking @samp{ldap://keys.(thedomain)}. @item keyserver - Locate a key using whatever keyserver is defined using the - @option{--keyserver} option. + Locate a key using a keyserver. @item keyserver-URL - In addition, a keyserver URL as used in the @option{--keyserver} option - may be used here to query that particular keyserver. + In addition, a keyserver URL as used in the @command{dirmngr} + configuration may be used here to query that particular keyserver. @item local Locate the key using the local keyrings. This mechanism allows the user to @@ -1802,10 +1814,26 @@ These options enable or disable the automatic retrieving of keys from a keyserver when verifying signatures made by keys that are not on the local keyring. The default is @option{--no-auto-key-retrieve}. -If the method "wkd" is included in the list of methods given to -@option{auto-key-locate}, the signer's user ID is part of the -signature, and the option @option{--disable-signer-uid} is not used, -the "wkd" method may also be used to retrieve a key. +The order of methods tried to lookup the key is: + +1. If a preferred keyserver is specified in the signature and the +option @option{honor-keyserver-url} is active (which is not the +default), that keyserver is tried. Note that the creator of the +signature uses the option @option{--sig-keyserver-url} to specify the +preferred keyserver for data signatures. + +2. If the signature has the Signer's UID set (e.g. using +@option{--sender} while creating the signature) a Web Key Directory +(WKD) lookup is done. This is the default configuration but can be +disabled by removing WKD from the auto-key-locate list or by using the +option @option{--disable-signer-uid}. + +3. If the option @option{honor-pka-record} is active, the legacy PKA +method is used. + +4. If any keyserver is configured and the Issuer Fingerprint is part +of the signature (since GnuPG 2.1.16), the configured keyservers are +tried. Note that this option makes a "web bug" like behavior possible. Keyserver or Web Key Directory operators can see which keys you @@ -1905,6 +1933,11 @@ are available for all keyserver types, some common options are: @end table +The default list of options is: "self-sigs-only, import-clean, +repair-keys, repair-pks-subkey-bug, export-attributes, +honor-pka-record". + + @item --completes-needed @var{n} @opindex compliant-needed Number of completely trusted users to introduce a new @@ -2334,7 +2367,16 @@ opposite meaning. The options are: can be used to update only the subkeys or other non-user id related information. - @item repair-keys. After import, fix various problems with the + @item self-sigs-only + Accept only self-signatures while importing a key. All other + key-signatures are skipped at an early import stage. This option + can be used with @code{keyserver-options} to mitigate attempts to + flood a key with bogus signatures from a keyserver. The drawback is + that all other valid key-signatures, as required by the Web of Trust + are also not imported. + + @item repair-keys + After import, fix various problems with the keys. For example, this reorders signatures, and strips duplicate signatures. Defaults to yes. @@ -2628,11 +2670,11 @@ legacy non-MDC message is exceptionally required, the option @item --disable-signer-uid @opindex disable-signer-uid -By default the user ID of the signing key is embedded in the data -signature. As of now this is only done if the signing key has been -specified with @option{local-user} using a mail address. This -information can be helpful for verifier to locate the key; see -option @option{--auto-key-retrieve}. +By default the user ID of the signing key is embedded in the data signature. +As of now this is only done if the signing key has been specified with +@option{local-user} using a mail address, or with @option{sender}. This +information can be helpful for verifier to locate the key; see option +@option{--auto-key-retrieve}. @item --personal-cipher-preferences @var{string} @opindex personal-cipher-preferences @@ -3021,7 +3063,8 @@ to display the message. This option overrides @option{--set-filename}. @itemx --no-use-embedded-filename @opindex use-embedded-filename Try to create a file with a name as embedded in the data. This can be -a dangerous option as it enables overwriting files. Defaults to no. +a dangerous option as it enables overwriting files. Defaults to no. +Note that the option @option{--output} overrides this option. @item --cipher-algo @var{name} @opindex cipher-algo @@ -3080,10 +3123,14 @@ the same thing. @opindex cert-digest-algo Use @var{name} as the message digest algorithm used when signing a key. Running the program with the command @option{--version} yields a -list of supported algorithms. Be aware that if you choose an algorithm -that GnuPG supports but other OpenPGP implementations do not, then some -users will not be able to use the key signatures you make, or quite -possibly your entire key. +list of supported algorithms. Be aware that if you choose an +algorithm that GnuPG supports but other OpenPGP implementations do +not, then some users will not be able to use the key signatures you +make, or quite possibly your entire key. Note also that a public key +algorithm must be compatible with the specified digest algorithm; thus +selecting an arbitrary digest algorithm may result in error messages +from lower crypto layers or lead to security flaws. + @item --disable-cipher-algo @var{name} @opindex disable-cipher-algo @@ -3288,7 +3335,8 @@ secret keyrings. @item --no-keyring @opindex no-keyring -Do not add use any keyrings even if specified as options. +Do not use any keyring at all. This overrides the default and all +options which specify keyrings. @item --skip-verify @opindex skip-verify diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index 1736ff111..75ccdc3ba 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -349,7 +349,8 @@ verbose commands to @command{gpgsm}, such as @samp{-vv}. @item --policy-file @var{filename} @opindex policy-file -Change the default name of the policy file to @var{filename}. +Change the default name of the policy file to @var{filename}. The +default name is @file{policies.txt}. @item --agent-program @var{file} @opindex agent-program diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index 81af28105..21c3fd826 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -288,17 +288,9 @@ To get a list of available CCID readers you may use this command: @item --card-timeout @var{n} @opindex card-timeout -If @var{n} is not 0 and no client is actively using the card, the card -will be powered down after @var{n} seconds. Powering down the card -avoids a potential risk of damaging a card when used with certain -cheap readers. This also allows applications that are not aware of -Scdaemon to access the card. The disadvantage of using a card timeout -is that accessing the card takes longer and that the user needs to -enter the PIN again after the next power up. - -Note that with the current version of Scdaemon the card is powered -down immediately at the next timer tick for any value of @var{n} other -than 0. +This option is deprecated. In GnuPG 2.0, it used to be used for +DISCONNECT command to control timing issue. Since DISCONNECT command +works synchronously, it has no effect. @item --enable-pinpad-varlen @opindex enable-pinpad-varlen @@ -332,6 +324,21 @@ This option disables the use of the card application named @var{name}. This is mainly useful for debugging or if a application with lower priority should be used by default. +@item --application-priority @var{namelist} +@opindex application-priority +This option allows to change the order in which applications of a card +a tried if no specific application was requested. @var{namelist} is a +space or comma delimited list of application names. Unknown names are +simply skipped. Applications not mentioned in the list are put in the +former order at the end of the new priority list. + +To get the list of current active applications, use +@cartouche +@smallexample + gpg-connect-agent 'scd getinfo app_list' /bye +@end smallexample +@end cartouche + @end table All the long options may also be given in the configuration file after @@ -767,4 +774,3 @@ length up to N bytes. If N is not given a default value is used @command{gpg2}(1) @end ifset @include see-also-note.texi - diff --git a/doc/tools.texi b/doc/tools.texi index 119f698d6..460030038 100644 --- a/doc/tools.texi +++ b/doc/tools.texi @@ -21,6 +21,7 @@ GnuPG comes with a couple of smaller tools: * gpgparsemail:: Parse a mail message into an annotated format * symcryptrun:: Call a simple symmetric encryption tool. * gpgtar:: Encrypt or sign files into an archive. +* gpg-check-pattern:: Check a passphrase on stdin against the patternfile. @end menu @c @@ -352,11 +353,12 @@ may use this command to ensure that they are started. Using "all" for @item --kill [@var{component}] @opindex kill -Kill the given component. Components which support killing are -@command{gpg-agent} and @command{scdaemon}. Components which don't -support reloading are ignored. Using "all" for @var{component} kills -all components running as daemons. Note that as of now reload and -kill have the same effect for @command{scdaemon}. +Kill the given component that runs as a daemon, including +@command{gpg-agent}, @command{dirmngr}, and @command{scdaemon}. A +@command{component} which does not run as a daemon will be ignored. +Using "all" for @var{component} kills all components running as +daemons. Note that as of now reload and kill have the same effect for +@command{scdaemon}. @item --create-socketdir @opindex create-socketdir @@ -392,6 +394,8 @@ extends numerical field values by human-readable descriptions. @opindex quiet Try to be as quiet as possible. +@include opt-homedir.texi + @item -n @itemx --dry-run Do not actually change anything. This is currently only implemented @@ -2107,3 +2111,50 @@ gpgtar --list-archive test1 @command{tar}(1), @end ifset @include see-also-note.texi + +@c +@c GPG-CHECK-PATTERN +@c +@manpage gpg-check-pattern.1 +@node gpg-check-pattern +@section Check a passphrase on stdin against the patternfile +@ifset manverb +.B gpg-check-pattern +\- Check a passphrase on stdin against the patternfile +@end ifset + +@mansect synopsis +@ifset manverb +.B gpg\-check\-pattern +.RI [ options ] +.I patternfile +@end ifset + +@mansect description +@command{gpg-check-pattern} checks a passphrase given on stdin against +a specified pattern file. + +@mansect options +@noindent + +@table @gnupgtabopt + +@item --verbose +@opindex verbose +Enable extra informational output. + +@item --check +@opindex check +Run only a syntax check on the patternfile. + +@item --null +@opindex null +Input is expected to be null delimited. + +@end table + +@mansect see also +@ifset isman +@command{gpg}(1), +@end ifset +@include see-also-note.texi diff --git a/doc/wks.texi b/doc/wks.texi index f132b3186..9f1fff2a8 100644 --- a/doc/wks.texi +++ b/doc/wks.texi @@ -101,6 +101,14 @@ fingerprint and the mailbox separated by a space. The command @option{--remove-key} removes a key from that directory, its only argument is a user-id. +The command @option{--print-wkd-hash} prints the WKD user-id identifiers +and the corresponding mailboxes from the user-ids given on the command +line or via stdin (one user-id per line). + +The command @option{--print-wkd-url} prints the URLs used to fetch the +key for the given user-ids from WKD. The meanwhile preferred format +with sub-domains is used here. + @command{gpg-wks-client} is not commonly invoked directly and thus it is not installed in the bin directory. Here is an example how it can be invoked manually to check for a Web Key Directory entry for diff --git a/doc/yat2m.c b/doc/yat2m.c index be0ef17fd..2d6f54ea2 100644 --- a/doc/yat2m.c +++ b/doc/yat2m.c @@ -724,7 +724,8 @@ proc_texi_cmd (FILE *fp, const char *command, const char *rest, size_t len, { "url", 0, "\\fB", "\\fR" }, { "sc", 0, "\\fB", "\\fR" }, { "var", 0, "\\fI", "\\fR" }, - { "samp", 0, "\\(aq", "\\(aq" }, + { "samp", 0, "\\(oq", "\\(cq" }, + { "kbd", 0, "\\(oq", "\\(cq" }, { "file", 0, "\\(oq\\fI","\\fR\\(cq" }, { "env", 0, "\\(oq\\fI","\\fR\\(cq" }, { "acronym", 0 }, diff --git a/g10/Makefile.am b/g10/Makefile.am index 3b4464364..884b4749b 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -121,6 +121,7 @@ common_source = \ sig-check.c \ keylist.c \ pkglue.c pkglue.h \ + objcache.c objcache.h \ ecdh.c gpg_sources = server.c \ diff --git a/g10/armor.c b/g10/armor.c index 972766503..eb2d28bca 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -1394,10 +1394,10 @@ armor_filter( void *opaque, int control, } /* write the comment strings */ - for(s=comment->d;comment;comment=comment->next,s=comment->d) + for(;comment;comment=comment->next) { iobuf_writestr(a, "Comment: " ); - for( ; *s; s++ ) + for( s=comment->d; *s; s++ ) { if( *s == '\n' ) iobuf_writestr(a, "\\n" ); diff --git a/g10/build-packet.c b/g10/build-packet.c index 07fccb099..2a95df694 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -447,15 +447,21 @@ do_user_id( IOBUF out, int ctb, PKT_user_id *uid ) * Without forcing HDRLEN to 2 in this case an indeterminate length * packet would be written which is not allowed. Note that we are * always called with a CTB indicating an old packet header format, - * so that forcing a 2 octet header works. */ + * so that forcing a 2 octet header works. We also check for the + * maximum allowed packet size by the parser using an arbitrary + * extra 10 bytes for header data. */ if (uid->attrib_data) { + if (uid->attrib_len > MAX_ATTR_PACKET_LENGTH - 10) + return gpg_error (GPG_ERR_TOO_LARGE); hdrlen = uid->attrib_len? 0 : 2; write_header2 (out, ctb, uid->attrib_len, hdrlen); rc = iobuf_write( out, uid->attrib_data, uid->attrib_len ); } else { + if (uid->len > MAX_UID_PACKET_LENGTH - 10) + return gpg_error (GPG_ERR_TOO_LARGE); hdrlen = uid->len? 0 : 2; write_header2 (out, ctb, uid->len, hdrlen); rc = iobuf_write( out, uid->name, uid->len ); diff --git a/g10/call-agent.c b/g10/call-agent.c index 83777534e..19deb73d7 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -41,6 +41,7 @@ #include "../common/status.h" #include "../common/shareddefs.h" #include "../common/host2net.h" +#include "../common/ttyio.h" #define CONTROL_D ('D' - 'A' + 1) @@ -48,6 +49,13 @@ static assuan_context_t agent_ctx = NULL; static int did_early_card_test; +struct confirm_parm_s +{ + char *desc; + char *ok; + char *notok; +}; + struct default_inq_parm_s { ctrl_t ctrl; @@ -57,6 +65,7 @@ struct default_inq_parm_s u32 *mainkeyid; int pubkey_algo; } keyinfo; + struct confirm_parm_s *confirm; }; struct cipher_parm_s @@ -136,6 +145,7 @@ default_inq_cb (void *opaque, const char *line) { gpg_error_t err = 0; struct default_inq_parm_s *parm = opaque; + const char *s; if (has_leading_keyword (line, "PINENTRY_LAUNCHED")) { @@ -151,7 +161,7 @@ default_inq_cb (void *opaque, const char *line) { if (have_static_passphrase ()) { - const char *s = get_static_passphrase (); + s = get_static_passphrase (); err = assuan_send_data (parm->ctx, s, strlen (s)); } else @@ -176,6 +186,27 @@ default_inq_cb (void *opaque, const char *line) xfree (pw); } } + else if ((s = has_leading_keyword (line, "CONFIRM")) + && opt.pinentry_mode == PINENTRY_MODE_LOOPBACK + && parm->confirm) + { + int ask = atoi (s); + int yes; + + if (ask) + { + yes = cpr_get_answer_is_yes (NULL, parm->confirm->desc); + if (yes) + err = assuan_send_data (parm->ctx, NULL, 0); + else + err = gpg_error (GPG_ERR_NOT_CONFIRMED); + } + else + { + tty_printf ("%s", parm->confirm->desc); + err = assuan_send_data (parm->ctx, NULL, 0); + } + } else log_debug ("ignoring gpg-agent inquiry '%s'\n", line); @@ -336,7 +367,7 @@ start_agent (ctrl_t ctrl, int flag_for_card) if (!(flag_for_card & FLAG_FOR_CARD_SUPPRESS_ERRORS)) rc = warn_version_mismatch (agent_ctx, SCDAEMON_NAME, 2); if (!rc) - rc = assuan_transact (agent_ctx, "SCD SERIALNO openpgp", + rc = assuan_transact (agent_ctx, "SCD SERIALNO", NULL, NULL, NULL, NULL, learn_status_cb, &info); if (rc && !(flag_for_card & FLAG_FOR_CARD_SUPPRESS_ERRORS)) @@ -352,7 +383,7 @@ start_agent (ctrl_t ctrl, int flag_for_card) break; default: write_status_text (STATUS_CARDCTRL, "4"); - log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + log_info ("selecting card failed: %s\n", gpg_strerror (rc)); break; } } @@ -726,7 +757,15 @@ learn_status_cb (void *opaque, const char *line) return 0; } -/* Call the scdaemon to learn about a smartcard */ + +/* Call the scdaemon to learn about a smartcard. Note that in + * contradiction to the function's name, gpg-agent's LEARN command is + * used and not the low-level "SCD LEARN". + * Used by: + * card-util.c + * keyedit_menu + * card_store_key_with_backup (Woth force to remove secret key data) + */ int agent_scd_learn (struct agent_card_info_s *info, int force) { @@ -759,10 +798,109 @@ agent_scd_learn (struct agent_card_info_s *info, int force) } + +/* Callback for the agent_scd_keypairinfo function. */ +static gpg_error_t +scd_keypairinfo_status_cb (void *opaque, const char *line) +{ + strlist_t *listaddr = opaque; + const char *keyword = line; + int keywordlen; + strlist_t sl; + char *p; + + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + while (spacep (line)) + line++; + + if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) + { + sl = append_to_strlist (listaddr, line); + p = sl->d; + /* Make sure that we only have two tokens so that future + * extensions of the format won't change the format expected by + * the caller. */ + while (*p && !spacep (p)) + p++; + if (*p) + { + while (spacep (p)) + p++; + while (*p && !spacep (p)) + p++; + if (*p) + { + *p++ = 0; + while (spacep (p)) + p++; + while (*p && !spacep (p)) + { + switch (*p++) + { + case 'c': sl->flags |= GCRY_PK_USAGE_CERT; break; + case 's': sl->flags |= GCRY_PK_USAGE_SIGN; break; + case 'e': sl->flags |= GCRY_PK_USAGE_ENCR; break; + case 'a': sl->flags |= GCRY_PK_USAGE_AUTH; break; + } + } + } + } + } + + return 0; +} + + +/* Read the keypairinfo lines of the current card directly from + * scdaemon. The list is returned as a string made up of the keygrip, + * a space and the keyref. The flags of the string carry the usage + * bits. If KEYREF is not NULL, only a single string is returned + * which matches the given keyref. */ +gpg_error_t +agent_scd_keypairinfo (ctrl_t ctrl, const char *keyref, strlist_t *r_list) +{ + gpg_error_t err; + strlist_t list = NULL; + struct default_inq_parm_s inq_parm; + char line[ASSUAN_LINELENGTH]; + + *r_list = NULL; + err= start_agent (ctrl, 1); + if (err) + return err; + memset (&inq_parm, 0, sizeof inq_parm); + inq_parm.ctx = agent_ctx; + + if (keyref) + snprintf (line, DIM(line), "SCD READKEY --info-only %s", keyref); + else + snprintf (line, DIM(line), "SCD LEARN --keypairinfo"); + + err = assuan_transact (agent_ctx, line, + NULL, NULL, + default_inq_cb, &inq_parm, + scd_keypairinfo_status_cb, &list); + if (!err && !list) + err = gpg_error (GPG_ERR_NO_DATA); + if (err) + { + free_strlist (list); + return err; + } + *r_list = list; + return 0; +} + + + /* Send an APDU to the current card. On success the status word is - stored at R_SW. With HEXAPDU being NULL only a RESET command is - send to scd. With HEXAPDU being the string "undefined" the command - "SERIALNO undefined" is send to scd. */ + * stored at R_SW. With HEXAPDU being NULL only a RESET command is + * send to scd. With HEXAPDU being the string "undefined" the command + * "SERIALNO undefined" is send to scd. + * Used by: + * card-util.c + */ gpg_error_t agent_scd_apdu (const char *hexapdu, unsigned int *r_sw) { @@ -816,6 +954,10 @@ agent_scd_apdu (const char *hexapdu, unsigned int *r_sw) } +/* Used by: + * card_store_subkey + * card_store_key_with_backup + */ int agent_keytocard (const char *hexgrip, int keyno, int force, const char *serialno, const char *timestamp) @@ -843,10 +985,100 @@ agent_keytocard (const char *hexgrip, int keyno, int force, } + +/* Object used with the agent_scd_getattr_one. */ +struct getattr_one_parm_s { + const char *keyword; /* Keyword to look for. */ + char *data; /* Malloced and unescaped data. */ + gpg_error_t err; /* Error code or 0 on success. */ +}; + + +/* Callback for agent_scd_getattr_one. */ +static gpg_error_t +getattr_one_status_cb (void *opaque, const char *line) +{ + struct getattr_one_parm_s *parm = opaque; + const char *s; + + if (parm->data) + return 0; /* We want only the first occurrence. */ + + if ((s=has_leading_keyword (line, parm->keyword))) + { + parm->data = percent_plus_unescape (s, 0xff); + if (!parm->data) + parm->err = gpg_error_from_syserror (); + } + + return 0; +} + + +/* Simplified version of agent_scd_getattr. This function returns + * only the first occurance of the attribute NAME and stores it at + * R_VALUE. A nul in the result is silennly replaced by 0xff. On + * error NULL is stored at R_VALUE. */ +gpg_error_t +agent_scd_getattr_one (const char *name, char **r_value) +{ + gpg_error_t err; + char line[ASSUAN_LINELENGTH]; + struct default_inq_parm_s inqparm; + struct getattr_one_parm_s parm; + + *r_value = NULL; + + if (!*name) + return gpg_error (GPG_ERR_INV_VALUE); + + memset (&inqparm, 0, sizeof inqparm); + inqparm.ctx = agent_ctx; + + memset (&parm, 0, sizeof parm); + parm.keyword = name; + + /* We assume that NAME does not need escaping. */ + if (12 + strlen (name) > DIM(line)-1) + return gpg_error (GPG_ERR_TOO_LARGE); + stpcpy (stpcpy (line, "SCD GETATTR "), name); + + err = start_agent (NULL, 1); + if (err) + return err; + + err = assuan_transact (agent_ctx, line, + NULL, NULL, + default_inq_cb, &inqparm, + getattr_one_status_cb, &parm); + if (!err && parm.err) + err = parm.err; + else if (!err && !parm.data) + err = gpg_error (GPG_ERR_NO_DATA); + + if (!err) + *r_value = parm.data; + else + xfree (parm.data); + + return err; +} + + /* Call the agent to retrieve a data object. This function returns - the data in the same structure as used by the learn command. It is - allowed to update such a structure using this command. */ + * the data in the same structure as used by the learn command. It is + * allowed to update such a structure using this command. + * + * Used by: + * build_sk_list + * enum_secret_keys + * get_signature_count + * card-util.c + * generate_keypair (KEY-ATTR) + * card_store_key_with_backup (SERIALNO) + * generate_card_subkeypair (KEY-ATTR) + */ int agent_scd_getattr (const char *name, struct agent_card_info_s *info) { @@ -875,24 +1107,23 @@ agent_scd_getattr (const char *name, struct agent_card_info_s *info) return rc; } + -/* Send an setattr command to the SCdaemon. SERIALNO is not actually - used here but required by gpg 1.4's implementation of this code in - cardglue.c. */ -int -agent_scd_setattr (const char *name, - const unsigned char *value, size_t valuelen, - const char *serialno) +/* Send an setattr command to the SCdaemon. + * Used by: + * card-util.c + */ +gpg_error_t +agent_scd_setattr (const char *name, const void *value_arg, size_t valuelen) { - int rc; + gpg_error_t err; + const unsigned char *value = value_arg; char line[ASSUAN_LINELENGTH]; char *p; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); - (void)serialno; - if (!*name || !valuelen) return gpg_error (GPG_ERR_INV_VALUE); @@ -918,16 +1149,16 @@ agent_scd_setattr (const char *name, } *p = 0; - rc = start_agent (NULL, 1); - if (!rc) + err = start_agent (NULL, 1); + if (!err) { parm.ctx = agent_ctx; - rc = assuan_transact (agent_ctx, line, NULL, NULL, + err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, NULL, NULL); } - status_sc_op_failure (rc); - return rc; + status_sc_op_failure (err); + return err; } @@ -953,7 +1184,10 @@ inq_writecert_parms (void *opaque, const char *line) } -/* Send a WRITECERT command to the SCdaemon. */ +/* Send a WRITECERT command to the SCdaemon. + * Used by: + * card-util.c + */ int agent_scd_writecert (const char *certidstr, const unsigned char *certdata, size_t certdatalen) @@ -984,60 +1218,6 @@ agent_scd_writecert (const char *certidstr, } - -/* Handle a KEYDATA inquiry. Note, we only send the data, - assuan_transact takes care of flushing and writing the end */ -static gpg_error_t -inq_writekey_parms (void *opaque, const char *line) -{ - int rc; - struct writekey_parm_s *parm = opaque; - - if (has_leading_keyword (line, "KEYDATA")) - { - rc = assuan_send_data (parm->dflt->ctx, parm->keydata, parm->keydatalen); - } - else - rc = default_inq_cb (parm->dflt, line); - - return rc; -} - - -/* Send a WRITEKEY command to the SCdaemon. */ -int -agent_scd_writekey (int keyno, const char *serialno, - const unsigned char *keydata, size_t keydatalen) -{ - int rc; - char line[ASSUAN_LINELENGTH]; - struct writekey_parm_s parms; - struct default_inq_parm_s dfltparm; - - memset (&dfltparm, 0, sizeof dfltparm); - - (void)serialno; - - rc = start_agent (NULL, 1); - if (rc) - return rc; - - memset (&parms, 0, sizeof parms); - - snprintf (line, DIM(line), "SCD WRITEKEY --force OPENPGP.%d", keyno); - dfltparm.ctx = agent_ctx; - parms.dflt = &dfltparm; - parms.keydata = keydata; - parms.keydatalen = keydatalen; - - rc = assuan_transact (agent_ctx, line, NULL, NULL, - inq_writekey_parms, &parms, NULL, NULL); - - status_sc_op_failure (rc); - return rc; -} - - /* Status callback for the SCD GENKEY command. */ static gpg_error_t @@ -1065,10 +1245,13 @@ scd_genkey_cb (void *opaque, const char *line) } /* Send a GENKEY command to the SCdaemon. If *CREATETIME is not 0, - the value will be passed to SCDAEMON with --timestamp option so that - the key is created with this. Otherwise, timestamp was generated by - SCDEAMON. On success, creation time is stored back to - CREATETIME. */ + * the value will be passed to SCDAEMON with --timestamp option so that + * the key is created with this. Otherwise, timestamp was generated by + * SCDEAMON. On success, creation time is stored back to + * CREATETIME. + * Used by: + * gen_card_key + */ int agent_scd_genkey (int keyno, int force, u32 *createtime) { @@ -1101,9 +1284,17 @@ agent_scd_genkey (int keyno, int force, u32 *createtime) status_sc_op_failure (rc); return rc; } + + /* Return the serial number of the card or an appropriate error. The - serial number is returned as a hexstring. */ + * serial number is returned as a hexstring. With DEMAND the active + * card is switched to the card with that serialno. + * Used by: + * card-util.c + * build_sk_list + * enum_secret_keys + */ int agent_scd_serialno (char **r_serialno, const char *demand) { @@ -1111,7 +1302,7 @@ agent_scd_serialno (char **r_serialno, const char *demand) char *serialno = NULL; char line[ASSUAN_LINELENGTH]; - err = start_agent (NULL, 1 | FLAG_FOR_CARD_SUPPRESS_ERRORS); + err = start_agent (NULL, (1 | FLAG_FOR_CARD_SUPPRESS_ERRORS)); if (err) return err; @@ -1132,8 +1323,13 @@ agent_scd_serialno (char **r_serialno, const char *demand) *r_serialno = serialno; return 0; } + + -/* Send a READCERT command to the SCdaemon. */ +/* Send a READCERT command to the SCdaemon. + * Used by: + * card-util.c + */ int agent_scd_readcert (const char *certidstr, void **r_buf, size_t *r_buflen) @@ -1171,6 +1367,51 @@ agent_scd_readcert (const char *certidstr, return 0; } + + +/* This is a variant of agent_readkey which sends a READKEY command + * directly Scdaemon. On success a new s-expression is stored at + * R_RESULT. */ +gpg_error_t +agent_scd_readkey (const char *keyrefstr, gcry_sexp_t *r_result) +{ + gpg_error_t err; + char line[ASSUAN_LINELENGTH]; + membuf_t data; + unsigned char *buf; + size_t len, buflen; + struct default_inq_parm_s dfltparm; + + memset (&dfltparm, 0, sizeof dfltparm); + dfltparm.ctx = agent_ctx; + + *r_result = NULL; + err = start_agent (NULL, 1); + if (err) + return err; + + init_membuf (&data, 1024); + snprintf (line, DIM(line), "SCD READKEY %s", keyrefstr); + err = assuan_transact (agent_ctx, line, + put_membuf_cb, &data, + default_inq_cb, &dfltparm, + NULL, NULL); + if (err) + { + xfree (get_membuf (&data, &len)); + return err; + } + buf = get_membuf (&data, &buflen); + if (!buf) + return gpg_error_from_syserror (); + + err = gcry_sexp_new (r_result, buf, buflen, 0); + xfree (buf); + + return err; +} + + struct card_cardlist_parm_s { int error; @@ -1208,7 +1449,12 @@ card_cardlist_cb (void *opaque, const char *line) return 0; } -/* Return cardlist. */ + +/* Return a list of currently available cards. + * Used by: + * card-util.c + * skclist.c + */ int agent_scd_cardlist (strlist_t *result) { @@ -1237,16 +1483,20 @@ agent_scd_cardlist (strlist_t *result) return 0; } + + /* Change the PIN of an OpenPGP card or reset the retry counter. - CHVNO 1: Change the PIN - 2: For v1 cards: Same as 1. - For v2 cards: Reset the PIN using the Reset Code. - 3: Change the admin PIN - 101: Set a new PIN and reset the retry counter - 102: For v1 cars: Same as 101. - For v2 cards: Set a new Reset Code. - SERIALNO is not used. + * CHVNO 1: Change the PIN + * 2: For v1 cards: Same as 1. + * For v2 cards: Reset the PIN using the Reset Code. + * 3: Change the admin PIN + * 101: Set a new PIN and reset the retry counter + * 102: For v1 cars: Same as 101. + * For v2 cards: Set a new Reset Code. + * SERIALNO is not used. + * Used by: + * card-util.c */ int agent_scd_change_pin (int chvno, const char *serialno) @@ -1280,8 +1530,11 @@ agent_scd_change_pin (int chvno, const char *serialno) /* Perform a CHECKPIN operation. SERIALNO should be the serial - number of the card - optionally followed by the fingerprint; - however the fingerprint is ignored here. */ + * number of the card - optionally followed by the fingerprint; + * however the fingerprint is ignored here. + * Used by: + * card-util.c + */ int agent_scd_checkpin (const char *serialno) { @@ -1306,15 +1559,6 @@ agent_scd_checkpin (const char *serialno) } -/* Dummy function, only used by the gpg 1.4 implementation. */ -void -agent_clear_pin_cache (const char *sn) -{ - (void)sn; -} - - - /* Note: All strings shall be UTF-8. On success the caller needs to free the string stored at R_PASSPHRASE. On error NULL will be @@ -2299,6 +2543,31 @@ agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, } +/* Status callback for handling confirmation. */ +static gpg_error_t +confirm_status_cb (void *opaque, const char *line) +{ + struct confirm_parm_s *parm = opaque; + const char *s; + + if ((s = has_leading_keyword (line, "SETDESC"))) + { + xfree (parm->desc); + parm->desc = unescape_status_string (s); + } + else if ((s = has_leading_keyword (line, "SETOK"))) + { + xfree (parm->ok); + parm->ok = unescape_status_string (s); + } + else if ((s = has_leading_keyword (line, "SETNOTOK"))) + { + xfree (parm->notok); + parm->notok = unescape_status_string (s); + } + + return 0; +} /* Ask the agent to delete the key identified by HEXKEYGRIP. If DESC is not NULL, display DESC instead of the default description @@ -2311,9 +2580,12 @@ agent_delete_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, gpg_error_t err; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; + struct confirm_parm_s confirm_parm; + memset (&confirm_parm, 0, sizeof confirm_parm); memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; + dfltparm.confirm = &confirm_parm; err = start_agent (ctrl, 0); if (err) @@ -2335,7 +2607,10 @@ agent_delete_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, force? " --force":"", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, - NULL, NULL); + confirm_status_cb, &confirm_parm); + xfree (confirm_parm.desc); + xfree (confirm_parm.ok); + xfree (confirm_parm.notok); return err; } diff --git a/g10/call-agent.h b/g10/call-agent.h index 8619a34f8..c4d0a9de1 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -84,6 +84,10 @@ void agent_release_card_info (struct agent_card_info_s *info); /* Return card info. */ int agent_scd_learn (struct agent_card_info_s *info, int force); +/* Get the keypariinfo directly from scdaemon. */ +gpg_error_t agent_scd_keypairinfo (ctrl_t ctrl, const char *keyref, + strlist_t *r_list); + /* Return list of cards. */ int agent_scd_cardlist (strlist_t *result); @@ -93,6 +97,9 @@ int agent_scd_serialno (char **r_serialno, const char *demand); /* Send an APDU to the card. */ gpg_error_t agent_scd_apdu (const char *hexapdu, unsigned int *r_sw); +/* Get attribute NAME from the card and store at R_VALUE. */ +gpg_error_t agent_scd_getattr_one (const char *name, char **r_value); + /* Update INFO with the attribute NAME. */ int agent_scd_getattr (const char *name, struct agent_card_info_s *info); @@ -101,35 +108,29 @@ int agent_keytocard (const char *hexgrip, int keyno, int force, const char *serialno, const char *timestamp); /* Send a SETATTR command to the SCdaemon. */ -int agent_scd_setattr (const char *name, - const unsigned char *value, size_t valuelen, - const char *serialno); +gpg_error_t agent_scd_setattr (const char *name, + const void *value, size_t valuelen); /* Send a WRITECERT command to the SCdaemon. */ int agent_scd_writecert (const char *certidstr, const unsigned char *certdata, size_t certdatalen); -/* Send a WRITEKEY command to the SCdaemon. */ -int agent_scd_writekey (int keyno, const char *serialno, - const unsigned char *keydata, size_t keydatalen); - /* Send a GENKEY command to the SCdaemon. */ int agent_scd_genkey (int keyno, int force, u32 *createtime); -/* Send a READKEY command to the SCdaemon. */ +/* Send a READCERT command to the SCdaemon. */ int agent_scd_readcert (const char *certidstr, void **r_buf, size_t *r_buflen); +/* Send a READKEY command to the SCdaemon. */ +gpg_error_t agent_scd_readkey (const char *keyrefstr, gcry_sexp_t *r_result); + /* Change the PIN of an OpenPGP card or reset the retry counter. */ int agent_scd_change_pin (int chvno, const char *serialno); /* Send the CHECKPIN command to the SCdaemon. */ int agent_scd_checkpin (const char *serialno); -/* Dummy function, only implemented by gpg 1.4. */ -void agent_clear_pin_cache (const char *sn); - - /* Send the GET_PASSPHRASE command to the agent. */ gpg_error_t agent_get_passphrase (const char *cache_id, const char *err_msg, diff --git a/g10/card-util.c b/g10/card-util.c index 08844bae3..1b9461e0a 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -91,8 +91,6 @@ change_pin (int unblock_v2, int allow_admin) log_info (_("OpenPGP card no. %s detected\n"), info.serialno? info.serialno : "[none]"); - agent_clear_pin_cache (info.serialno); - if (opt.batch) { agent_release_card_info (&info); @@ -421,36 +419,43 @@ current_card_status (ctrl_t ctrl, estream_t fp, if (!info.serialno || strncmp (info.serialno, "D27600012401", 12) || strlen (info.serialno) != 32 ) { + const char *name1, *name2; if (info.apptype && !strcmp (info.apptype, "NKS")) { - if (opt.with_colons) - es_fputs ("netkey-card:\n", fp); - log_info ("this is a NetKey card\n"); + name1 = "netkey"; + name2 = "NetKey"; } else if (info.apptype && !strcmp (info.apptype, "DINSIG")) { - if (opt.with_colons) - es_fputs ("dinsig-card:\n", fp); - log_info ("this is a DINSIG compliant card\n"); + name1 = "dinsig"; + name2 = "DINSIG"; } else if (info.apptype && !strcmp (info.apptype, "P15")) { - if (opt.with_colons) - es_fputs ("pkcs15-card:\n", fp); - log_info ("this is a PKCS#15 compliant card\n"); + name1 = "pkcs15"; + name2 = "PKCS#15"; } else if (info.apptype && !strcmp (info.apptype, "GELDKARTE")) { - if (opt.with_colons) - es_fputs ("geldkarte-card:\n", fp); - log_info ("this is a Geldkarte compliant card\n"); + name1 = "geldkarte"; + name2 = "Geldkarte"; + } + else if (info.apptype && !strcmp (info.apptype, "PIV")) + { + name1 = "piv"; + name2 = "PIV"; } else { - if (opt.with_colons) - es_fputs ("unknown:\n", fp); + name1 = "unknown"; + name2 = "Unknown"; } - log_info ("not an OpenPGP card\n"); + + if (opt.with_colons) + es_fprintf (fp, "%s-card:\n", name1); + else + tty_fprintf (fp, "Application type .: %s\n", name2); + agent_release_card_info (&info); xfree (pk); return; @@ -465,6 +470,8 @@ current_card_status (ctrl_t ctrl, estream_t fp, if (opt.with_colons) es_fputs ("openpgp-card:\n", fp); + else + tty_fprintf (fp, "Application type .: %s\n", "OpenPGP"); if (opt.with_colons) @@ -673,9 +680,8 @@ current_card_status (ctrl_t ctrl, estream_t fp, if ( thefpr && !fpr_is_ff (thefpr, thefprlen) && !get_pubkey_byfprint (ctrl, pk, &keyblock, thefpr, thefprlen)) { - print_pubkey_info (ctrl, fp, pk); - if (keyblock) - print_card_key_info (fp, keyblock); + print_key_info (ctrl, fp, 0, pk, 0); + print_card_key_info (fp, keyblock); } else tty_fprintf (fp, "[none]\n"); @@ -697,6 +703,7 @@ card_status (ctrl_t ctrl, estream_t fp, const char *serialno) strlist_t card_list, sl; char *serialno0, *serialno1; int all_cards = 0; + int any_card = 0; if (serialno == NULL) { @@ -724,6 +731,10 @@ card_status (ctrl_t ctrl, estream_t fp, const char *serialno) if (!all_cards && strcmp (serialno, sl->d)) continue; + if (any_card && !opt.with_colons) + tty_fprintf (fp, "\n"); + any_card = 1; + err = agent_scd_serialno (&serialno1, sl->d); if (err) { @@ -816,7 +827,7 @@ change_name (void) return -1; } - rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname), NULL ); + rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname)); if (rc) log_error ("error setting Name: %s\n", gpg_strerror (rc)); @@ -837,7 +848,7 @@ change_url (void) trim_spaces (url); cpr_kill_prompt (); - rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url), NULL ); + rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url)); if (rc) log_error ("error setting URL: %s\n", gpg_strerror (rc)); xfree (url); @@ -995,7 +1006,7 @@ change_login (const char *args) n = strlen (data); } - rc = agent_scd_setattr ("LOGIN-DATA", data, n, NULL ); + rc = agent_scd_setattr ("LOGIN-DATA", data, n); if (rc) log_error ("error setting login data: %s\n", gpg_strerror (rc)); xfree (data); @@ -1033,7 +1044,7 @@ change_private_do (const char *args, int nr) n = strlen (data); } - rc = agent_scd_setattr (do_name, data, n, NULL ); + rc = agent_scd_setattr (do_name, data, n); if (rc) log_error ("error setting private DO: %s\n", gpg_strerror (rc)); xfree (data); @@ -1132,7 +1143,7 @@ change_lang (void) return -1; } - rc = agent_scd_setattr ("DISP-LANG", data, strlen (data), NULL ); + rc = agent_scd_setattr ("DISP-LANG", data, strlen (data)); if (rc) log_error ("error setting lang: %s\n", gpg_strerror (rc)); xfree (data); @@ -1168,7 +1179,7 @@ change_sex (void) return -1; } - rc = agent_scd_setattr ("DISP-SEX", str, 1, NULL ); + rc = agent_scd_setattr ("DISP-SEX", str, 1); if (rc) log_error ("error setting salutation: %s\n", gpg_strerror (rc)); xfree (data); @@ -1216,7 +1227,7 @@ change_cafpr (int fprno) rc = agent_scd_setattr (fprno==1?"CA-FPR-1": fprno==2?"CA-FPR-2": - fprno==3?"CA-FPR-3":"x", fpr, fprlen, NULL ); + fprno==3?"CA-FPR-3":"x", fpr, fprlen); if (rc) log_error ("error setting cafpr: %s\n", gpg_strerror (rc)); write_sc_op_status (rc); @@ -1242,7 +1253,7 @@ toggle_forcesig (void) newstate = !info.chv1_cached; agent_release_card_info (&info); - rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL); + rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1); if (rc) log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc)); write_sc_op_status (rc); @@ -1285,14 +1296,12 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1) { int rc = 0; - agent_clear_pin_cache (info->serialno); - *forced_chv1 = !info->chv1_cached; if (*forced_chv1) { /* Switch off the forced mode so that during key generation we don't get bothered with PIN queries for each self-signature. */ - rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno); + rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1); if (rc) { log_error ("error clearing forced signature PIN flag: %s\n", @@ -1323,7 +1332,7 @@ restore_forced_chv1 (int *forced_chv1) if (*forced_chv1) { /* Switch back to forced state. */ - rc = agent_scd_setattr ("CHV-STATUS-1", "", 1, NULL); + rc = agent_scd_setattr ("CHV-STATUS-1", "", 1); if (rc) { log_error ("error setting forced signature PIN flag: %s\n", @@ -1570,7 +1579,7 @@ do_change_keyattr (int keyno, const struct key_attr *key_attr) return gpg_error (GPG_ERR_PUBKEY_ALGO); } - err = agent_scd_setattr ("KEY-ATTR", args, strlen (args), NULL); + err = agent_scd_setattr ("KEY-ATTR", args, strlen (args)); if (err) log_error (_("error changing key attribute for key %d: %s\n"), keyno+1, gpg_strerror (err)); @@ -2116,8 +2125,7 @@ kdf_setup (const char *args) goto leave_error; err = agent_scd_setattr ("KDF", kdf_data, - single ? KDF_DATA_LENGTH_MIN : KDF_DATA_LENGTH_MAX, - NULL); + single ? KDF_DATA_LENGTH_MIN : KDF_DATA_LENGTH_MAX); if (err) goto leave_error; @@ -2169,7 +2177,7 @@ uif (int arg_number, const char *arg_rest) data[1] = 0x20; - err = agent_scd_setattr (name, data, 2, NULL); + err = agent_scd_setattr (name, data, 2); if (err) log_error (_("error for setup UIF: %s\n"), gpg_strerror (err)); } diff --git a/g10/cpr.c b/g10/cpr.c index 3d39d6bda..d502e8b52 100644 --- a/g10/cpr.c +++ b/g10/cpr.c @@ -62,7 +62,7 @@ progress_cb (void *ctx, const char *what, int printchar, /* Return true if the status message NO may currently be issued. We - need this to avoid syncronisation problem while auto retrieving a + need this to avoid synchronization problem while auto retrieving a key. There it may happen that a status NODATA is issued for a non available key and the user may falsely interpret this has a missing signature. */ diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c index 4d9dc86d9..c73d5fb45 100644 --- a/g10/decrypt-data.c +++ b/g10/decrypt-data.c @@ -471,6 +471,7 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) { char *filename = NULL; estream_t fp; + rc = get_output_file ("", 0, ed->buf, &filename, &fp); if (! rc) { @@ -492,8 +493,7 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) filename, gpg_strerror (rc)); iobuf_close (output); - if (afx) - release_armor_context (afx); + release_armor_context (afx); } xfree (filename); } diff --git a/g10/decrypt.c b/g10/decrypt.c index 4d6734d40..9589aff58 100644 --- a/g10/decrypt.c +++ b/g10/decrypt.c @@ -48,7 +48,6 @@ decrypt_message (ctrl_t ctrl, const char *filename) armor_filter_context_t *afx = NULL; progress_filter_context_t *pfx; int rc; - int no_out = 0; pfx = new_progress_context (); @@ -82,11 +81,13 @@ decrypt_message (ctrl_t ctrl, const char *filename) if (!opt.outfile) { - no_out = 1; opt.outfile = "-"; + opt.flags.dummy_outfile = 1; } + else + opt.flags.dummy_outfile = 0; rc = proc_encryption_packets (ctrl, NULL, fp ); - if (no_out) + if (opt.flags.dummy_outfile) opt.outfile = NULL; iobuf_close (fp); diff --git a/g10/delkey.c b/g10/delkey.c index cc5673846..b5ab47434 100644 --- a/g10/delkey.c +++ b/g10/delkey.c @@ -1,7 +1,7 @@ /* delkey.c - delete keys * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, * 2005, 2006 Free Software Foundation, Inc. - * Copyright (C) 2014 Werner Koch + * Copyright (C) 2014, 2019 Werner Koch * * This file is part of GnuPG. * @@ -53,13 +53,15 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, gpg_error_t err; kbnode_t keyblock = NULL; kbnode_t node, kbctx; + kbnode_t targetnode; KEYDB_HANDLE hd; PKT_public_key *pk = NULL; u32 keyid[2]; int okay=0; int yes; KEYDB_SEARCH_DESC desc; - int exactmatch; + int exactmatch; /* True if key was found by fingerprint. */ + int thiskeyonly; /* 0 = false, 1 = is primary key, 2 = is a subkey. */ *r_sec_avail = 0; @@ -70,6 +72,7 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, /* Search the userid. */ err = classify_user_id (username, &desc, 1); exactmatch = (desc.mode == KEYDB_SEARCH_MODE_FPR); + thiskeyonly = desc.exact; if (!err) err = keydb_search (hd, &desc, 1, NULL); if (err) @@ -95,7 +98,35 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, err = gpg_error (GPG_ERR_GENERAL); goto leave; } - pk = node->pkt->pkt.public_key; + + /* If an operation only on a subkey is requested, find that subkey + * now. */ + if (thiskeyonly) + { + kbnode_t tmpnode; + + for (kbctx=NULL; (tmpnode = walk_kbnode (keyblock, &kbctx, 0)); ) + { + if (!(tmpnode->pkt->pkttype == PKT_PUBLIC_KEY + || tmpnode->pkt->pkttype == PKT_PUBLIC_SUBKEY)) + continue; + if (exact_subkey_match_p (&desc, tmpnode)) + break; + } + if (!tmpnode) + { + log_error ("Oops; requested subkey not found anymore!\n"); + err = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + /* Set NODE to this specific subkey or primary key. */ + thiskeyonly = node == tmpnode? 1 : 2; + targetnode = tmpnode; + } + else + targetnode = node; + + pk = targetnode->pkt->pkt.public_key; keyid_from_pk (pk, keyid); if (!secret && !force) @@ -135,11 +166,33 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, } else { - if (secret) - print_seckey_info (ctrl, pk); - else - print_pubkey_info (ctrl, NULL, pk ); - tty_printf( "\n" ); + print_key_info (ctrl, NULL, 0, pk, secret); + tty_printf ("\n"); + if (thiskeyonly == 1 && !secret) + { + /* We need to delete the entire public key despite the use + * of the thiskeyonly request. */ + tty_printf (_("Note: The public primary key and all its subkeys" + " will be deleted.\n")); + } + else if (thiskeyonly == 2 && !secret) + { + tty_printf (_("Note: Only the shown public subkey" + " will be deleted.\n")); + } + if (thiskeyonly == 1 && secret) + { + tty_printf (_("Note: Only the secret part of the shown primary" + " key will be deleted.\n")); + } + else if (thiskeyonly == 2 && secret) + { + tty_printf (_("Note: Only the secret part of the shown subkey" + " will be deleted.\n")); + } + + if (thiskeyonly) + tty_printf ("\n"); yes = cpr_get_answer_is_yes (secret? "delete_key.secret.okay": "delete_key.okay", @@ -176,6 +229,9 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) continue; + if (thiskeyonly && targetnode != node) + continue; + if (agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) continue; /* No secret key for that public (sub)key. */ @@ -188,7 +244,7 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, * pre-caution is that since 2.1 the secret key may also * be used for other protocols and thus deleting it from * the gpg would also delete the key for other tools. */ - if (!err) + if (!err && !opt.dry_run) err = agent_delete_key (NULL, hexgrip, prompt, opt.answer_yes); xfree (prompt); @@ -217,6 +273,35 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, if (firsterr) goto leave; } + else if (thiskeyonly == 2) + { + int selected = 0; + + /* Delete the specified public subkey. */ + for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) + { + if (thiskeyonly && targetnode != node) + continue; + + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + selected = targetnode == node; + if (selected) + delete_kbnode (node); + } + else if (selected && node->pkt->pkttype == PKT_SIGNATURE) + delete_kbnode (node); + else + selected = 0; + } + commit_kbnode (&keyblock); + err = keydb_update_keyblock (ctrl, hd, keyblock); + if (err) + { + log_error (_("update failed: %s\n"), gpg_strerror (err)); + goto leave; + } + } else { err = keydb_delete_keyblock (hd); @@ -232,7 +317,8 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, revalidation_mark(). This makes sense - only deleting keys that have ownertrust set should trigger this. */ - if (!secret && pk && clear_ownertrusts (ctrl, pk)) + if (!secret && pk && !opt.dry_run && thiskeyonly != 2 + && clear_ownertrusts (ctrl, pk)) { if (opt.verbose) log_info (_("ownertrust information cleared\n")); @@ -245,7 +331,8 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, return err; } -/**************** + +/* * Delete a public or secret key from a keyring. */ gpg_error_t diff --git a/g10/exec.c b/g10/exec.c index 74a83970e..3e5dc278b 100644 --- a/g10/exec.c +++ b/g10/exec.c @@ -77,37 +77,99 @@ set_exec_path(const char *path) { return GPG_ERR_GENERAL; } static int w32_system(const char *command) { -#ifdef HAVE_W32CE_SYSTEM -#warning Change this code to use common/exechelp.c -#else - PROCESS_INFORMATION pi; - STARTUPINFO si; - char *string; + if (!strncmp (command, "!ShellExecute ", 14)) + { + SHELLEXECUTEINFOW see; + wchar_t *wname; + int waitms; - /* We must use a copy of the command as CreateProcess modifies this - argument. */ - string=xstrdup(command); + command = command + 14; + while (spacep (command)) + command++; + waitms = atoi (command); + if (waitms < 0) + waitms = 0; + else if (waitms > 60*1000) + waitms = 60000; + while (*command && !spacep (command)) + command++; + while (spacep (command)) + command++; - memset(&pi,0,sizeof(pi)); - memset(&si,0,sizeof(si)); - si.cb=sizeof(si); + wname = utf8_to_wchar (command); + if (!wname) + return -1; - if(!CreateProcess(NULL,string,NULL,NULL,FALSE, - DETACHED_PROCESS, - NULL,NULL,&si,&pi)) - return -1; + memset (&see, 0, sizeof see); + see.cbSize = sizeof see; + see.fMask = (SEE_MASK_NOCLOSEPROCESS + | SEE_MASK_NOASYNC + | SEE_MASK_FLAG_NO_UI + | SEE_MASK_NO_CONSOLE); + see.lpVerb = L"open"; + see.lpFile = (LPCWSTR)wname; + see.nShow = SW_SHOW; - /* Wait for the child to exit */ - WaitForSingleObject(pi.hProcess,INFINITE); + if (DBG_EXTPROG) + log_debug ("running ShellExecuteEx(open,'%s')\n", command); + if (!ShellExecuteExW (&see)) + { + if (DBG_EXTPROG) + log_debug ("ShellExecuteEx failed: rc=%d\n", (int)GetLastError ()); + xfree (wname); + return -1; + } + if (DBG_EXTPROG) + log_debug ("ShellExecuteEx succeeded (hProcess=%p,hInstApp=%d)\n", + see.hProcess, (int)see.hInstApp); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - xfree(string); + if (!see.hProcess) + { + gnupg_usleep (waitms*1000); + if (DBG_EXTPROG) + log_debug ("ShellExecuteEx ready (wait=%dms)\n", waitms); + } + else + { + WaitForSingleObject (see.hProcess, INFINITE); + if (DBG_EXTPROG) + log_debug ("ShellExecuteEx ready\n"); + } + CloseHandle (see.hProcess); + + xfree (wname); + } + else + { + char *string; + PROCESS_INFORMATION pi; + STARTUPINFO si; + + /* We must use a copy of the command as CreateProcess modifies + * this argument. */ + string = xstrdup (command); + + memset (&pi, 0, sizeof(pi)); + memset (&si, 0, sizeof(si)); + si.cb = sizeof (si); + + if (!CreateProcess (NULL, string, NULL, NULL, FALSE, + DETACHED_PROCESS, + NULL, NULL, &si, &pi)) + return -1; + + /* Wait for the child to exit */ + WaitForSingleObject (pi.hProcess, INFINITE); + + CloseHandle (pi.hProcess); + CloseHandle (pi.hThread); + xfree (string); + } return 0; -#endif } -#endif +#endif /*_W32*/ + /* Replaces current $PATH */ int @@ -508,7 +570,7 @@ exec_read(struct exec_info *info) if(info->flags.use_temp_files) { if(DBG_EXTPROG) - log_debug("system() command is %s\n",info->command); + log_debug ("running command: %s\n",info->command); #if defined (_WIN32) info->progreturn=w32_system(info->command); diff --git a/g10/export.c b/g10/export.c index 4f6c9137e..e8bf14cf5 100644 --- a/g10/export.c +++ b/g10/export.c @@ -436,8 +436,8 @@ new_subkey_list_item (KBNODE node) (keyID or fingerprint) and does match the one at NODE. It is assumed that the packet at NODE is either a public or secret subkey. */ -static int -exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, KBNODE node) +int +exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, kbnode_t node) { u32 kid[2]; byte fpr[MAX_FINGERPRINT_LEN]; @@ -596,7 +596,10 @@ cleartext_secret_key_to_openpgp (gcry_sexp_t s_key, PKT_public_key *pk) top_list = gcry_sexp_find_token (s_key, "private-key", 0); if (!top_list) goto bad_seckey; - if (gcry_sexp_length(top_list) != 2) + + /* ignore all S-expression after the first sublist -- we assume that + they are comments or otherwise irrelevant to OpenPGP */ + if (gcry_sexp_length(top_list) < 2) goto bad_seckey; key = gcry_sexp_nth (top_list, 1); if (!key) @@ -2171,10 +2174,10 @@ export_ssh_key (ctrl_t ctrl, const char *userid) { getkey_ctx_t getkeyctx; - err = get_pubkey_byname (ctrl, &getkeyctx, NULL, userid, &keyblock, + err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + &getkeyctx, NULL, userid, &keyblock, NULL, - 0 /* Only usable keys or given exact. */, - 1 /* No AKL lookup. */); + 0 /* Only usable keys or given exact. */); if (!err) { err = getkey_next (ctrl, getkeyctx, NULL, NULL); diff --git a/g10/getkey.c b/g10/getkey.c index 9dae879d2..57617a0a9 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -36,6 +36,7 @@ #include "../common/i18n.h" #include "keyserver-internal.h" #include "call-agent.h" +#include "objcache.h" #include "../common/host2net.h" #include "../common/mbox-util.h" #include "../common/status.h" @@ -112,6 +113,7 @@ static struct typedef struct keyid_list { struct keyid_list *next; + byte fprlen; char fpr[MAX_FINGERPRINT_LEN]; u32 keyid[2]; } *keyid_list_t; @@ -132,15 +134,6 @@ static int pk_cache_disabled; #if MAX_UID_CACHE_ENTRIES < 5 #error we really need the userid cache #endif -typedef struct user_id_db -{ - struct user_id_db *next; - keyid_list_t keyids; - int len; - char name[1]; -} *user_id_db_t; -static user_id_db_t user_id_db; -static int uid_cache_entries; /* Number of entries in uid cache. */ static void merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock); static int lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret, @@ -262,112 +255,6 @@ user_id_not_found_utf8 (void) -/* Return the user ID from the given keyblock. - * We use the primary uid flag which has been set by the merge_selfsigs - * function. The returned value is only valid as long as the given - * keyblock is not changed. */ -static const char * -get_primary_uid (KBNODE keyblock, size_t * uidlen) -{ - KBNODE k; - const char *s; - - for (k = keyblock; k; k = k->next) - { - if (k->pkt->pkttype == PKT_USER_ID - && !k->pkt->pkt.user_id->attrib_data - && k->pkt->pkt.user_id->flags.primary) - { - *uidlen = k->pkt->pkt.user_id->len; - return k->pkt->pkt.user_id->name; - } - } - s = user_id_not_found_utf8 (); - *uidlen = strlen (s); - return s; -} - - -static void -release_keyid_list (keyid_list_t k) -{ - while (k) - { - keyid_list_t k2 = k->next; - xfree (k); - k = k2; - } -} - -/**************** - * Store the association of keyid and userid - * Feed only public keys to this function. - */ -static void -cache_user_id (KBNODE keyblock) -{ - user_id_db_t r; - const char *uid; - size_t uidlen; - keyid_list_t keyids = NULL; - KBNODE k; - - for (k = keyblock; k; k = k->next) - { - if (k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) - { - keyid_list_t a = xmalloc_clear (sizeof *a); - /* Hmmm: For a long list of keyids it might be an advantage - * to append the keys. */ - fingerprint_from_pk (k->pkt->pkt.public_key, a->fpr, NULL); - keyid_from_pk (k->pkt->pkt.public_key, a->keyid); - /* First check for duplicates. */ - for (r = user_id_db; r; r = r->next) - { - keyid_list_t b; - - for (b = r->keyids; b; b = b->next) - { - if (!memcmp (b->fpr, a->fpr, MAX_FINGERPRINT_LEN)) - { - if (DBG_CACHE) - log_debug ("cache_user_id: already in cache\n"); - release_keyid_list (keyids); - xfree (a); - return; - } - } - } - /* Now put it into the cache. */ - a->next = keyids; - keyids = a; - } - } - if (!keyids) - BUG (); /* No key no fun. */ - - - uid = get_primary_uid (keyblock, &uidlen); - - if (uid_cache_entries >= MAX_UID_CACHE_ENTRIES) - { - /* fixme: use another algorithm to free some cache slots */ - r = user_id_db; - user_id_db = r->next; - release_keyid_list (r->keyids); - xfree (r); - uid_cache_entries--; - } - r = xmalloc (sizeof *r + uidlen - 1); - r->keyids = keyids; - r->len = uidlen; - memcpy (r->name, uid, r->len); - r->next = user_id_db; - user_id_db = r; - uid_cache_entries++; -} - /* Disable and drop the public key cache (which is filled by cache_public_key and get_pubkey). Note: there is currently no way @@ -948,11 +835,21 @@ key_byname (ctrl_t ctrl, GETKEY_CTX *retctx, strlist_t namelist, /* Find a public key identified by NAME. * - * If name appears to be a valid RFC822 mailbox (i.e., email - * address) and auto key lookup is enabled (no_akl == 0), then the - * specified auto key lookup methods (--auto-key-lookup) are used to - * import the key into the local keyring. Otherwise, just the local - * keyring is consulted. + * If name appears to be a valid RFC822 mailbox (i.e., email address) + * and auto key lookup is enabled (mode != GET_PUBKEY_NO_AKL), then + * the specified auto key lookup methods (--auto-key-lookup) are used + * to import the key into the local keyring. Otherwise, just the + * local keyring is consulted. + * + * MODE can be one of: + * GET_PUBKEY_NORMAL - The standard mode + * GET_PUBKEY_NO_AKL - The auto key locate functionality is + * disabled and only the local key ring is + * considered. Note: the local key ring is + * consulted even if local is not in the + * auto-key-locate option list! + * GET_PUBKEY_NO_LOCAL - Only the auto key locate functionaly is + * used and no local search is done. * * If RETCTX is not NULL, then the constructed context is returned in * *RETCTX so that getpubkey_next can be used to get subsequent @@ -988,18 +885,14 @@ key_byname (ctrl_t ctrl, GETKEY_CTX *retctx, strlist_t namelist, * documentation for skip_unusable for an exact definition) are * skipped unless they are looked up by key id or by fingerprint. * - * If NO_AKL is set, then the auto key locate functionality is - * disabled and only the local key ring is considered. Note: the - * local key ring is consulted even if local is not in the - * --auto-key-locate option list! - * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY or GPG_ERR_NO_SECKEY * (if want_secret is set) is returned if the key is not found. */ int -get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk, +get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, + GETKEY_CTX * retctx, PKT_public_key * pk, const char *name, KBNODE * ret_keyblock, - KEYDB_HANDLE * ret_kdbhd, int include_unusable, int no_akl) + KEYDB_HANDLE * ret_kdbhd, int include_unusable) { int rc; strlist_t namelist = NULL; @@ -1035,7 +928,9 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk, * Note: we only save the search context in RETCTX if the local * method is the first method tried (either explicitly or * implicitly). */ - if (!no_akl) + if (mode == GET_PUBKEY_NO_LOCAL) + nodefault = 1; /* Auto-key-locate but ignore "local". */ + else if (mode != GET_PUBKEY_NO_AKL) { /* auto-key-locate is enabled. */ @@ -1064,7 +959,13 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk, anylocalfirst = 1; } - if (nodefault && is_mbox) + if (mode == GET_PUBKEY_NO_LOCAL) + { + /* Force using the AKL. If IS_MBOX is not set this is the final + * error code. */ + rc = GPG_ERR_NO_PUBKEY; + } + else if (nodefault && is_mbox) { /* Either "nodefault" or "local" (explicitly) appeared in the * auto key locate list and NAME appears to be an email address. @@ -1085,7 +986,9 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk, /* If the requested name resembles a valid mailbox and automatic retrieval has been enabled, we try to import the key. */ - if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && !no_akl && is_mbox) + if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY + && mode != GET_PUBKEY_NO_AKL + && is_mbox) { /* NAME wasn't present in the local keyring (or we didn't try * the local keyring). Since the auto key locate feature is @@ -1104,22 +1007,30 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk, { case AKL_NODEFAULT: /* This is a dummy mechanism. */ - mechanism_string = "None"; + mechanism_string = ""; rc = GPG_ERR_NO_PUBKEY; break; case AKL_LOCAL: - mechanism_string = "Local"; - did_akl_local = 1; - if (retctx) - { - getkey_end (ctrl, *retctx); - *retctx = NULL; - } - add_to_strlist (&namelist, name); - rc = key_byname (ctrl, anylocalfirst ? retctx : NULL, - namelist, pk, 0, - include_unusable, ret_keyblock, ret_kdbhd); + if (mode == GET_PUBKEY_NO_LOCAL) + { + mechanism_string = ""; + rc = GPG_ERR_NO_PUBKEY; + } + else + { + mechanism_string = "Local"; + did_akl_local = 1; + if (retctx) + { + getkey_end (ctrl, *retctx); + *retctx = NULL; + } + add_to_strlist (&namelist, name); + rc = key_byname (ctrl, anylocalfirst ? retctx : NULL, + namelist, pk, 0, + include_unusable, ret_keyblock, ret_kdbhd); + } break; case AKL_CERT: @@ -1246,15 +1157,14 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk, name, mechanism_string); break; } - if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY - || opt.verbose || no_fingerprint) + if ((gpg_err_code (rc) != GPG_ERR_NO_PUBKEY + || opt.verbose || no_fingerprint) && *mechanism_string) log_info (_("error retrieving '%s' via %s: %s\n"), name, mechanism_string, no_fingerprint ? _("No fingerprint") : gpg_strerror (rc)); } } - if (rc && retctx) { getkey_end (ctrl, *retctx); @@ -1315,7 +1225,7 @@ subkey_is_ok (const PKT_public_key *sub) /* Return true if KEYBLOCK has only expired encryption subkyes. Note * that the function returns false if the key has no encryption - * subkeys at all or the subkecys are revoked. */ + * subkeys at all or the subkeys are revoked. */ static int only_expired_enc_subkeys (kbnode_t keyblock) { @@ -1407,7 +1317,8 @@ pubkey_cmp (ctrl_t ctrl, const char *name, struct pubkey_cmp_cookie *old, * resembles a mail address, the results are ranked and only the best * result is returned. */ gpg_error_t -get_best_pubkey_byname (ctrl_t ctrl, GETKEY_CTX *retctx, PKT_public_key *pk, +get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, + GETKEY_CTX *retctx, PKT_public_key *pk, const char *name, KBNODE *ret_keyblock, int include_unusable) { @@ -1430,8 +1341,9 @@ get_best_pubkey_byname (ctrl_t ctrl, GETKEY_CTX *retctx, PKT_public_key *pk, getkey_end (ctrl, ctx); ctx = NULL; } - err = get_pubkey_byname (ctrl, &ctx, pk, name, ret_keyblock, - NULL, include_unusable, 0); + err = get_pubkey_byname (ctrl, mode, + &ctx, pk, name, ret_keyblock, + NULL, include_unusable); if (err) { getkey_end (ctrl, ctx); @@ -1493,15 +1405,14 @@ get_best_pubkey_byname (ctrl_t ctrl, GETKEY_CTX *retctx, PKT_public_key *pk, /* Old key is better. */ release_public_key_parts (&new.key); free_user_id (new.uid); - new.uid = NULL; } else { /* A tie. Keep the old key. */ release_public_key_parts (&new.key); free_user_id (new.uid); - new.uid = NULL; } + new.uid = NULL; } getkey_end (ctrl, ctx); ctx = NULL; @@ -1637,7 +1548,7 @@ get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname) * * FPRINT is a byte array whose contents is the fingerprint to use as * the search term. FPRINT_LEN specifies the length of the - * fingerprint (in bytes). Currently, only 16 and 20-byte + * fingerprint (in bytes). Currently, only 16, 20, and 32-byte * fingerprints are supported. * * FIXME: We should replace this with the _byname function. This can @@ -3291,7 +3202,11 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) memcpy (&pk->revoked, &rinfo, sizeof (rinfo)); } if (main_pk->has_expired) - pk->has_expired = main_pk->has_expired; + { + pk->has_expired = main_pk->has_expired; + if (!pk->expiredate || pk->expiredate > main_pk->expiredate) + pk->expiredate = main_pk->expiredate; + } } } return; @@ -3623,7 +3538,7 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, xfree (tempkeystr); } - cache_user_id (keyblock); + cache_put_keyblock (keyblock); return latest_key ? latest_key : keyblock; /* Found. */ } @@ -3836,67 +3751,40 @@ get_seckey_default_or_card (ctrl_t ctrl, PKT_public_key *pk, * this string must be freed by xfree. If R_NOUID is not NULL it is * set to true if a user id was not found; otherwise to false. */ static char * -get_user_id_string (ctrl_t ctrl, u32 * keyid, int mode, size_t *r_len, - int *r_nouid) +get_user_id_string (ctrl_t ctrl, u32 * keyid, int mode) { - user_id_db_t r; - keyid_list_t a; - int pass = 0; + char *name; + unsigned int namelen; char *p; - if (r_nouid) - *r_nouid = 0; + log_assert (mode != 2); - /* Try it two times; second pass reads from the database. */ - do + name = cache_get_uid_bykid (keyid, &namelen); + if (!name) { - for (r = user_id_db; r; r = r->next) - { - for (a = r->keyids; a; a = a->next) - { - if (a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1]) - { - if (mode == 2) - { - /* An empty string as user id is possible. Make - sure that the malloc allocates one byte and - does not bail out. */ - p = xmalloc (r->len? r->len : 1); - memcpy (p, r->name, r->len); - if (r_len) - *r_len = r->len; - } - else - { - if (mode) - p = xasprintf ("%08lX%08lX %.*s", - (ulong) keyid[0], (ulong) keyid[1], - r->len, r->name); - else - p = xasprintf ("%s %.*s", keystr (keyid), - r->len, r->name); - if (r_len) - *r_len = strlen (p); - } - - return p; - } - } - } + /* Get it so that the cache will be filled. */ + if (!get_pubkey (ctrl, NULL, keyid)) + name = cache_get_uid_bykid (keyid, &namelen); } - while (++pass < 2 && !get_pubkey (ctrl, NULL, keyid)); - if (mode == 2) - p = xstrdup (user_id_not_found_utf8 ()); - else if (mode) - p = xasprintf ("%08lX%08lX [?]", (ulong) keyid[0], (ulong) keyid[1]); + if (name) + { + if (mode) + p = xasprintf ("%08lX%08lX %.*s", + (ulong) keyid[0], (ulong) keyid[1], namelen, name); + else + p = xasprintf ("%s %.*s", keystr (keyid), namelen, name); + + xfree (name); + } else - p = xasprintf ("%s [?]", keystr (keyid)); + { + if (mode) + p = xasprintf ("%08lX%08lX [?]", (ulong) keyid[0], (ulong) keyid[1]); + else + p = xasprintf ("%s [?]", keystr (keyid)); + } - if (r_nouid) - *r_nouid = 1; - if (r_len) - *r_len = strlen (p); return p; } @@ -3904,7 +3792,7 @@ get_user_id_string (ctrl_t ctrl, u32 * keyid, int mode, size_t *r_len, char * get_user_id_string_native (ctrl_t ctrl, u32 * keyid) { - char *p = get_user_id_string (ctrl, keyid, 0, NULL, NULL); + char *p = get_user_id_string (ctrl, keyid, 0); char *p2 = utf8_to_native (p, strlen (p), 0); xfree (p); return p2; @@ -3914,7 +3802,7 @@ get_user_id_string_native (ctrl_t ctrl, u32 * keyid) char * get_long_user_id_string (ctrl_t ctrl, u32 * keyid) { - return get_user_id_string (ctrl, keyid, 1, NULL, NULL); + return get_user_id_string (ctrl, keyid, 1); } @@ -3922,7 +3810,31 @@ get_long_user_id_string (ctrl_t ctrl, u32 * keyid) char * get_user_id (ctrl_t ctrl, u32 *keyid, size_t *rn, int *r_nouid) { - return get_user_id_string (ctrl, keyid, 2, rn, r_nouid); + char *name; + unsigned int namelen; + + if (r_nouid) + *r_nouid = 0; + + name = cache_get_uid_bykid (keyid, &namelen); + if (!name) + { + /* Get it so that the cache will be filled. */ + if (!get_pubkey (ctrl, NULL, keyid)) + name = cache_get_uid_bykid (keyid, &namelen); + } + + if (!name) + { + name = xstrdup (user_id_not_found_utf8 ()); + namelen = strlen (name); + if (r_nouid) + *r_nouid = 1; + } + + if (rn && name) + *rn = namelen; + return name; } @@ -3943,49 +3855,36 @@ get_user_id_native (ctrl_t ctrl, u32 *keyid) returned string, which must be freed using xfree, may not be NUL terminated. To determine the length of the string, you must use *RN. */ -char * -get_user_id_byfpr (ctrl_t ctrl, const byte *fpr, size_t *rn) +static char * +get_user_id_byfpr (ctrl_t ctrl, const byte *fpr, size_t fprlen, size_t *rn) { - user_id_db_t r; - char *p; - int pass = 0; + char *name; - /* Try it two times; second pass reads from the database. */ - do + name = cache_get_uid_byfpr (fpr, fprlen, rn); + if (!name) { - for (r = user_id_db; r; r = r->next) - { - keyid_list_t a; - for (a = r->keyids; a; a = a->next) - { - if (!memcmp (a->fpr, fpr, MAX_FINGERPRINT_LEN)) - { - /* An empty string as user id is possible. Make - sure that the malloc allocates one byte and does - not bail out. */ - p = xmalloc (r->len? r->len : 1); - memcpy (p, r->name, r->len); - *rn = r->len; - return p; - } - } - } + /* Get it so that the cache will be filled. */ + if (!get_pubkey_byfprint (ctrl, NULL, NULL, fpr, fprlen)) + name = cache_get_uid_byfpr (fpr, fprlen, rn); } - while (++pass < 2 - && !get_pubkey_byfprint (ctrl, NULL, NULL, fpr, MAX_FINGERPRINT_LEN)); - p = xstrdup (user_id_not_found_utf8 ()); - *rn = strlen (p); - return p; + + if (!name) + { + name = xstrdup (user_id_not_found_utf8 ()); + *rn = strlen (name); + } + + return name; } /* Like get_user_id_byfpr, but convert the string to the native encoding. The returned string needs to be freed. Unlike get_user_id_byfpr, the returned string is NUL terminated. */ char * -get_user_id_byfpr_native (ctrl_t ctrl, const byte *fpr) +get_user_id_byfpr_native (ctrl_t ctrl, const byte *fpr, size_t fprlen) { size_t rn; - char *p = get_user_id_byfpr (ctrl, fpr, &rn); + char *p = get_user_id_byfpr (ctrl, fpr, fprlen, &rn); char *p2 = utf8_to_native (p, rn, 0); xfree (p); return p2; diff --git a/g10/gpg.c b/g10/gpg.c index 1ab7b0497..0bbe72394 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -59,6 +59,7 @@ #include "../common/asshelp.h" #include "call-dirmngr.h" #include "tofu.h" +#include "objcache.h" #include "../common/init.h" #include "../common/mbox-util.h" #include "../common/shareddefs.h" @@ -148,6 +149,7 @@ enum cmd_and_opt_values aSendKeys, aRecvKeys, aLocateKeys, + aLocateExtKeys, aSearchKeys, aRefreshKeys, aFetchKeys, @@ -502,6 +504,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_c (aRefreshKeys, "refresh-keys", N_("update all keys from a keyserver")), ARGPARSE_c (aLocateKeys, "locate-keys", "@"), + ARGPARSE_c (aLocateExtKeys, "locate-external-keys", "@"), ARGPARSE_c (aFetchKeys, "fetch-keys" , "@" ), ARGPARSE_c (aShowKeys, "show-keys" , "@" ), ARGPARSE_c (aExportSecret, "export-secret-keys" , "@" ), @@ -2421,7 +2424,9 @@ main (int argc, char **argv) opt.import_options = IMPORT_REPAIR_KEYS; opt.export_options = EXPORT_ATTRIBUTES; opt.keyserver_options.import_options = (IMPORT_REPAIR_KEYS - | IMPORT_REPAIR_PKS_SUBKEY_BUG); + | IMPORT_REPAIR_PKS_SUBKEY_BUG + | IMPORT_SELF_SIGS_ONLY + | IMPORT_CLEAN); opt.keyserver_options.export_options = EXPORT_ATTRIBUTES; opt.keyserver_options.options = KEYSERVER_HONOR_PKA_RECORD; opt.verify_options = (LIST_SHOW_UID_VALIDITY @@ -2611,6 +2616,7 @@ main (int argc, char **argv) #endif /* ENABLE_CARD_SUPPORT*/ case aListKeys: case aLocateKeys: + case aLocateExtKeys: case aListSigs: case aExportSecret: case aExportSecretSub: @@ -4511,7 +4517,7 @@ main (int argc, char **argv) sl = NULL; for( ; argc; argc--, argv++ ) add_to_strlist2( &sl, *argv, utf8_strings ); - public_key_list (ctrl, sl, 0); + public_key_list (ctrl, sl, 0, 0); free_strlist(sl); break; case aListSecretKeys: @@ -4522,10 +4528,11 @@ main (int argc, char **argv) free_strlist(sl); break; case aLocateKeys: + case aLocateExtKeys: sl = NULL; for (; argc; argc--, argv++) add_to_strlist2( &sl, *argv, utf8_strings ); - public_key_list (ctrl, sl, 1); + public_key_list (ctrl, sl, 1, cmd == aLocateExtKeys); free_strlist (sl); break; @@ -5223,12 +5230,14 @@ g10_exit( int rc ) { keydb_dump_stats (); sig_check_dump_stats (); + objcache_dump_stats (); gcry_control (GCRYCTL_DUMP_MEMORY_STATS); gcry_control (GCRYCTL_DUMP_RANDOM_STATS); } if (opt.debug) gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); + gnupg_block_all_signals (); emergency_cleanup (); rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c index e882fa8e3..7b7e1dc9a 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -1200,7 +1200,8 @@ sig_revocation_key (const char *option, int argc, char *argv[], void *cookie) option, argv[0]); pk.req_usage = PUBKEY_USAGE_SIG; - err = get_pubkey_byname (NULL, NULL, &pk, argv[1], NULL, NULL, 1, 1); + err = get_pubkey_byname (NULL, GET_PUBKEY_NO_AKL, + NULL, &pk, argv[1], NULL, NULL, 1); if (err) log_fatal ("looking up key %s: %s\n", argv[1], gpg_strerror (err)); @@ -1799,12 +1800,19 @@ signature (const char *option, int argc, char *argv[], void *cookie) keyid_copy (si.issuer_pk->keyid, pk_keyid (pripk)); } + /* The reuse of core gpg stuff by this tool is questionable when it + * requires adding extra code to the actual gpg code. It does not + * make sense to pass an extra parameter and in particular not given + * that gpg already has opt.cert_digest_algo to override it. */ + if (si.digest_algo) + log_info ("note: digest algo can't be passed to make_keysig_packet\n"); + /* Changing the issuer's key id is fragile. Check to make sure make_keysig_packet didn't recompute the keyid. */ keyid_copy (keyid, si.issuer_pk->keyid); err = make_keysig_packet (global_ctrl, &sig, si.pk, si.uid, si.sk, si.issuer_pk, - si.class, si.digest_algo, + si.class, si.timestamp, si.expiration, mksubpkt_callback, &si, NULL); log_assert (keyid_cmp (keyid, si.issuer_pk->keyid) == 0); @@ -2450,7 +2458,8 @@ pk_esk (const char *option, int argc, char *argv[], void *cookie) memset (&pk, 0, sizeof (pk)); pk.req_usage = PUBKEY_USAGE_ENC; - err = get_pubkey_byname (NULL, NULL, &pk, pi.keyid, NULL, NULL, 1, 1); + err = get_pubkey_byname (NULL, GET_PUBKEY_NO_AKL, + NULL, &pk, pi.keyid, NULL, NULL, 1); if (err) log_fatal ("%s: looking up key %s: %s\n", option, pi.keyid, gpg_strerror (err)); diff --git a/g10/import.c b/g10/import.c index c2a1dd033..c32dbf059 100644 --- a/g10/import.c +++ b/g10/import.c @@ -102,7 +102,7 @@ static int import (ctrl_t ctrl, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url); -static int read_block (IOBUF a, int with_meta, +static int read_block (IOBUF a, unsigned int options, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys); static void revocation_present (ctrl_t ctrl, kbnode_t keyblock); static gpg_error_t import_one (ctrl_t ctrl, @@ -129,6 +129,7 @@ static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, unsigned int options); static int any_uid_left (kbnode_t keyblock); static int remove_all_uids (kbnode_t *keyblock); +static void remove_all_non_self_sigs (kbnode_t *keyblock, u32 *keyid); static int merge_blocks (ctrl_t ctrl, unsigned int options, kbnode_t keyblock_orig, kbnode_t keyblock, u32 *keyid, @@ -190,7 +191,10 @@ parse_import_options(char *str,unsigned int *options,int noisy) N_("remove as much as possible from key after import")}, {"import-drop-uids", IMPORT_DROP_UIDS, NULL, - N_("Do not import user id or attribute packets")}, + N_("do not import user id or attribute packets")}, + + {"self-sigs-only", IMPORT_SELF_SIGS_ONLY, NULL, + N_("ignore key-signatures which are not self-signatures")}, {"import-export", IMPORT_EXPORT, NULL, N_("run import filters and export key immediately")}, @@ -589,8 +593,7 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats, release_armor_context (afx); } - while (!(rc = read_block (inp, !!(options & IMPORT_RESTORE), - &pending_pkt, &keyblock, &v3keys))) + while (!(rc = read_block (inp, options, &pending_pkt, &keyblock, &v3keys))) { stats->v3keys += v3keys; if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) @@ -669,6 +672,18 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats, if (!(++stats->count % 100) && !opt.quiet) log_info (_("%lu keys processed so far\n"), stats->count ); + + if (origin == KEYORG_WKD && stats->count >= 5) + { + /* We limit the number of keys _received_ from the WKD to 5. + * In fact there should be only one key but some sites want + * to store a few expired keys there also. gpg's key + * selection will later figure out which key to use. Note + * that for WKD we always return the fingerprint of the + * first imported key. */ + log_info ("import from WKD stopped after %d keys\n", 5); + break; + } } stats->v3keys += v3keys; if (rc == -1) @@ -677,6 +692,13 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats, log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (rc)); release_kbnode (secattic); + + /* When read_block loop was stopped by error, we have PENDING_PKT left. */ + if (pending_pkt) + { + free_packet (pending_pkt, NULL); + xfree (pending_pkt); + } return rc; } @@ -826,31 +848,39 @@ valid_keyblock_packet (int pkttype) } -/**************** - * Read the next keyblock from stream A. - * Meta data (ring trust packets) are only considered of WITH_META is set. - * PENDING_PKT should be initialized to NULL and not changed by the caller. - * Return: 0 = okay, -1 no more blocks or another errorcode. - * The int at R_V3KEY counts the number of unsupported v3 - * keyblocks. +/* Read the next keyblock from stream A. Meta data (ring trust + * packets) are only considered if OPTIONS has the IMPORT_RESTORE flag + * set. PENDING_PKT should be initialized to NULL and not changed by + * the caller. + * + * Returns 0 for okay, -1 no more blocks, or any other errorcode. The + * integer at R_V3KEY counts the number of unsupported v3 keyblocks. */ static int -read_block( IOBUF a, int with_meta, +read_block( IOBUF a, unsigned int options, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys) { int rc; struct parse_packet_ctx_s parsectx; PACKET *pkt; kbnode_t root = NULL; + kbnode_t lastnode = NULL; int in_cert, in_v3key, skip_sigs; + u32 keyid[2]; + int got_keyid = 0; + unsigned int dropped_nonselfsigs = 0; *r_v3keys = 0; if (*pending_pkt) { - root = new_kbnode( *pending_pkt ); + root = lastnode = new_kbnode( *pending_pkt ); *pending_pkt = NULL; + log_assert (root->pkt->pkttype == PKT_PUBLIC_KEY + || root->pkt->pkttype == PKT_SECRET_KEY); in_cert = 1; + keyid_from_pk (root->pkt->pkt.public_key, keyid); + got_keyid = 1; } else in_cert = 0; @@ -858,7 +888,7 @@ read_block( IOBUF a, int with_meta, pkt = xmalloc (sizeof *pkt); init_packet (pkt); init_parse_packet (&parsectx, a); - if (!with_meta) + if (!(options & IMPORT_RESTORE)) parsectx.skip_meta = 1; in_v3key = 0; skip_sigs = 0; @@ -965,24 +995,59 @@ read_block( IOBUF a, int with_meta, init_packet(pkt); break; + case PKT_SIGNATURE: + if (!in_cert) + goto x_default; + if (!(options & IMPORT_SELF_SIGS_ONLY)) + goto x_default; + log_assert (got_keyid); + if (pkt->pkt.signature->keyid[0] == keyid[0] + && pkt->pkt.signature->keyid[1] == keyid[1]) + { /* This is likely a self-signature. We import this one. + * Eventually we should use the ISSUER_FPR to compare + * self-signatures, but that will work only for v5 keys + * which are currently not even deployed. + * Note that we do not do any crypto verify here because + * that would defeat this very mitigation of DoS by + * importing a key with a huge amount of faked + * key-signatures. A verification will be done later in + * the processing anyway. Here we want a cheap an early + * way to drop non-self-signatures. */ + goto x_default; + } + /* Skip this signature. */ + dropped_nonselfsigs++; + free_packet (pkt, &parsectx); + init_packet(pkt); + break; + case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: - if (in_cert ) /* Store this packet. */ + if (!got_keyid) + { + keyid_from_pk (pkt->pkt.public_key, keyid); + got_keyid = 1; + } + if (in_cert) /* Store this packet. */ { *pending_pkt = pkt; pkt = NULL; goto ready; } in_cert = 1; - /* fall through */ + goto x_default; + default: x_default: if (in_cert && valid_keyblock_packet (pkt->pkttype)) { if (!root ) - root = new_kbnode (pkt); + root = lastnode = new_kbnode (pkt); else - add_kbnode (root, new_kbnode (pkt)); + { + lastnode->next = new_kbnode (pkt); + lastnode = lastnode->next; + } pkt = xmalloc (sizeof *pkt); } else @@ -1003,6 +1068,10 @@ read_block( IOBUF a, int with_meta, free_packet (pkt, &parsectx); deinit_parse_packet (&parsectx); xfree( pkt ); + if (!rc && dropped_nonselfsigs && opt.verbose) + log_info ("key %s: number of dropped non-self-signatures: %u\n", + keystr (keyid), dropped_nonselfsigs); + return rc; } @@ -1729,12 +1798,12 @@ update_key_origin (kbnode_t keyblock, u32 curtime, int origin, const char *url) * has valid parts. */ static gpg_error_t -import_one (ctrl_t ctrl, - kbnode_t keyblock, struct import_stats_s *stats, - unsigned char **fpr, size_t *fpr_len, unsigned int options, - int from_sk, int silent, - import_screener_t screener, void *screener_arg, - int origin, const char *url, int *r_valid) +import_one_real (ctrl_t ctrl, + kbnode_t keyblock, struct import_stats_s *stats, + unsigned char **fpr, size_t *fpr_len, unsigned int options, + int from_sk, int silent, + import_screener_t screener, void *screener_arg, + int origin, const char *url, int *r_valid) { gpg_error_t err = 0; PKT_public_key *pk; @@ -1817,6 +1886,13 @@ import_one (ctrl_t ctrl, return 0; } + /* Remove all non-self-sigs if requested. Noe that this is a NOP if + * that option has been globally set but we may also be called + * latter with the already parsed keyblock and a locally changed + * option. This is why we need to remove them here as well. */ + if ((options & IMPORT_SELF_SIGS_ONLY)) + remove_all_non_self_sigs (&keyblock, keyid); + /* Remove or collapse the user ids. */ if ((options & IMPORT_DROP_UIDS)) remove_all_uids (&keyblock); @@ -2026,22 +2102,25 @@ import_one (ctrl_t ctrl, hd = NULL; /* We are ready. */ - if (!opt.quiet && !silent) + if (!err && !opt.quiet && !silent) { - char *p = get_user_id_byfpr_native (ctrl, fpr2); + char *p = get_user_id_byfpr_native (ctrl, fpr2, fpr2len); log_info (_("key %s: public key \"%s\" imported\n"), keystr(keyid), p); xfree(p); } - if (is_status_enabled()) + if (!err && is_status_enabled()) { char *us = get_long_user_id_string (ctrl, keyid); write_status_text( STATUS_IMPORTED, us ); xfree(us); print_import_ok (pk, 1); } - stats->imported++; - new_key = 1; + if (!err) + { + stats->imported++; + new_key = 1; + } } else /* Key already exists - merge. */ { @@ -2111,10 +2190,12 @@ import_one (ctrl_t ctrl, keydb_release (hd); hd = NULL; - /* We are ready. */ - if (!opt.quiet && !silent) + /* We are ready. Print and update stats if we got no error. + * An error here comes from writing the keyblock and thus + * very likely means that no update happened. */ + if (!err && !opt.quiet && !silent) { - char *p = get_user_id_byfpr_native (ctrl, fpr2); + char *p = get_user_id_byfpr_native (ctrl, fpr2, fpr2len); if (n_uids == 1 ) log_info( _("key %s: \"%s\" 1 new user ID\n"), keystr(keyid),p); @@ -2148,14 +2229,17 @@ import_one (ctrl_t ctrl, xfree(p); } - stats->n_uids +=n_uids; - stats->n_sigs +=n_sigs; - stats->n_subk +=n_subk; - stats->n_sigs_cleaned +=n_sigs_cleaned; - stats->n_uids_cleaned +=n_uids_cleaned; + if (!err) + { + stats->n_uids +=n_uids; + stats->n_sigs +=n_sigs; + stats->n_subk +=n_subk; + stats->n_sigs_cleaned +=n_sigs_cleaned; + stats->n_uids_cleaned +=n_uids_cleaned; - if (is_status_enabled () && !silent) - print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); + if (is_status_enabled () && !silent) + print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); + } } else { @@ -2175,7 +2259,7 @@ import_one (ctrl_t ctrl, if (!opt.quiet && !silent) { - char *p = get_user_id_byfpr_native (ctrl, fpr2); + char *p = get_user_id_byfpr_native (ctrl, fpr2, fpr2len); log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p); xfree(p); } @@ -2203,14 +2287,19 @@ import_one (ctrl_t ctrl, fingerprint of the key in all cases. */ if (fpr) { - xfree (*fpr); /* Note that we need to compare against 0 here because COUNT gets only incremented after returning from this function. */ if (!stats->count) - *fpr = fingerprint_from_pk (pk, NULL, fpr_len); - else - *fpr = NULL; + { + xfree (*fpr); + *fpr = fingerprint_from_pk (pk, NULL, fpr_len); + } + else if (origin != KEYORG_WKD) + { + xfree (*fpr); + *fpr = NULL; + } } } @@ -2237,6 +2326,41 @@ import_one (ctrl_t ctrl, } +/* Wrapper around import_one_real to retry the import in some cases. */ +static gpg_error_t +import_one (ctrl_t ctrl, + kbnode_t keyblock, struct import_stats_s *stats, + unsigned char **fpr, size_t *fpr_len, unsigned int options, + int from_sk, int silent, + import_screener_t screener, void *screener_arg, + int origin, const char *url, int *r_valid) +{ + gpg_error_t err; + + err = import_one_real (ctrl, keyblock, stats, fpr, fpr_len, options, + from_sk, silent, screener, screener_arg, + origin, url, r_valid); + if (gpg_err_code (err) == GPG_ERR_TOO_LARGE + && gpg_err_source (err) == GPG_ERR_SOURCE_KEYBOX + && ((options & (IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN)) + != (IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN))) + { + /* We hit the maximum image length. Ask the wrapper to do + * everything again but this time with some extra options. */ + u32 keyid[2]; + + keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); + log_info ("key %s: keyblock too large, retrying with self-sigs-only\n", + keystr (keyid)); + options |= IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN; + err = import_one_real (ctrl, keyblock, stats, fpr, fpr_len, options, + from_sk, silent, screener, screener_arg, + origin, url, r_valid); + } + return err; +} + + /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The * function prints diagnostics and returns an error code. If BATCH is * true the secret keys are stored by gpg-agent in the transfer format @@ -2544,6 +2668,7 @@ sec_to_pub_keyblock (kbnode_t sec_keyblock) kbnode_t pub_keyblock = NULL; kbnode_t ctx = NULL; kbnode_t secnode, pubnode; + kbnode_t lastnode = NULL; unsigned int tag = 0; /* Set a tag to all nodes. */ @@ -2583,9 +2708,12 @@ sec_to_pub_keyblock (kbnode_t sec_keyblock) pubnode->tag = secnode->tag; if (!pub_keyblock) - pub_keyblock = pubnode; + pub_keyblock = lastnode = pubnode; else - add_kbnode (pub_keyblock, pubnode); + { + lastnode->next = pubnode; + lastnode = pubnode; + } } return pub_keyblock; @@ -2915,7 +3043,7 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock, /* The secret keyblock may not have nodes which are deleted in * the public keyblock. Otherwise we would import just the * secret key without having the public key. That would be - * surprising and clutters out private-keys-v1.d. */ + * surprising and clutters our private-keys-v1.d. */ err = resync_sec_with_pub_keyblock (&keyblock, pub_keyblock, &attic); if (err) goto leave; @@ -3764,6 +3892,38 @@ remove_all_uids (kbnode_t *keyblock) } +/* Delete all non-self-sigs from KEYBLOCK. + * Returns: True if the keyblock has changed. */ +static void +remove_all_non_self_sigs (kbnode_t *keyblock, u32 *keyid) +{ + kbnode_t node; + unsigned int dropped = 0; + + for (node = *keyblock; node; node = node->next) + { + if (is_deleted_kbnode (node)) + continue; + + if (node->pkt->pkttype != PKT_SIGNATURE) + continue; + + if (node->pkt->pkt.signature->keyid[0] == keyid[0] + && node->pkt->pkt.signature->keyid[1] == keyid[1]) + continue; + delete_kbnode (node); + dropped++; + } + + if (dropped) + commit_kbnode (keyblock); + + if (dropped && opt.verbose) + log_info ("key %s: number of dropped non-self-signatures: %u\n", + keystr (keyid), dropped); +} + + /* * It may happen that the imported keyblock has duplicated user IDs. * We check this here and collapse those user IDs together with their diff --git a/g10/keydb.c b/g10/keydb.c index 8c067e1df..a7691bbe2 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -1,6 +1,6 @@ /* keydb.c - key database dispatcher * Copyright (C) 2001-2013 Free Software Foundation, Inc. - * Coyrright (C) 2001-2015 Werner Koch + * Copyright (C) 2001-2015 Werner Koch * * This file is part of GnuPG. * @@ -1076,7 +1076,7 @@ lock_all (KEYDB_HANDLE hd) rc = keyring_lock (hd->active[i].u.kr, 1); break; case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_lock (hd->active[i].u.kb, 1); + rc = keybox_lock (hd->active[i].u.kb, 1, -1); break; } } @@ -1094,7 +1094,7 @@ lock_all (KEYDB_HANDLE hd) keyring_lock (hd->active[i].u.kr, 0); break; case KEYDB_RESOURCE_TYPE_KEYBOX: - keybox_lock (hd->active[i].u.kb, 0); + keybox_lock (hd->active[i].u.kb, 0, 0); break; } } @@ -1127,7 +1127,7 @@ unlock_all (KEYDB_HANDLE hd) keyring_lock (hd->active[i].u.kr, 0); break; case KEYDB_RESOURCE_TYPE_KEYBOX: - keybox_lock (hd->active[i].u.kb, 0); + keybox_lock (hd->active[i].u.kb, 0, 0); break; } } @@ -1242,8 +1242,15 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no, } if (err) { + es_fflush (es_stdout); log_error ("parse_keyblock_image: read error: %s\n", gpg_strerror (err)); + if (gpg_err_code (err) == GPG_ERR_INV_PACKET) + { + free_packet (pkt, &parsectx); + init_packet (pkt); + continue; + } err = gpg_error (GPG_ERR_INV_KEYRING); break; } diff --git a/g10/keydb.h b/g10/keydb.h index 7cdfe9bbf..6ad8dce4c 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -345,16 +345,25 @@ typedef struct pubkey_s *pubkey_t; /* Free a list of public keys. */ void pubkeys_free (pubkey_t keys); + +/* Mode flags for get_pubkey_byname. */ +enum get_pubkey_modes + { + GET_PUBKEY_NORMAL = 0, + GET_PUBKEY_NO_AKL = 1, + GET_PUBKEY_NO_LOCAL = 2 + }; + /* Find a public key identified by NAME. */ -int get_pubkey_byname (ctrl_t ctrl, +int get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, GETKEY_CTX *retctx, PKT_public_key *pk, const char *name, KBNODE *ret_keyblock, KEYDB_HANDLE *ret_kdbhd, - int include_unusable, int no_akl ); + int include_unusable); /* Likewise, but only return the best match if NAME resembles a mail * address. */ -gpg_error_t get_best_pubkey_byname (ctrl_t ctrl, +gpg_error_t get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, GETKEY_CTX *retctx, PKT_public_key *pk, const char *name, KBNODE *ret_keyblock, int include_unusable); @@ -436,8 +445,7 @@ char *get_user_id_string_native (ctrl_t ctrl, u32 *keyid); char *get_long_user_id_string (ctrl_t ctrl, u32 *keyid); char *get_user_id (ctrl_t ctrl, u32 *keyid, size_t *rn, int *r_nouid); char *get_user_id_native (ctrl_t ctrl, u32 *keyid); -char *get_user_id_byfpr (ctrl_t ctrl, const byte *fpr, size_t *rn); -char *get_user_id_byfpr_native (ctrl_t ctrl, const byte *fpr); +char *get_user_id_byfpr_native (ctrl_t ctrl, const byte *fpr, size_t fprlen); void release_akl(void); int parse_auto_key_locate(const char *options); diff --git a/g10/keyedit.c b/g10/keyedit.c index c28a565b1..1bf5de9b2 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1012,7 +1012,8 @@ sign_uids (ctrl_t ctrl, estream_t fp, node->pkt->pkt.user_id, NULL, pk, - 0x13, 0, 0, 0, + 0x13, + 0, 0, keygen_add_std_prefs, primary_pk, NULL); else @@ -1020,7 +1021,7 @@ sign_uids (ctrl_t ctrl, estream_t fp, node->pkt->pkt.user_id, NULL, pk, - class, 0, + class, timestamp, duration, sign_mk_attrib, &attrib, NULL); @@ -1437,7 +1438,8 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, #endif /* Get the public key */ - err = get_pubkey_byname (ctrl, NULL, NULL, username, &keyblock, &kdbhd, 1, 1); + err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, NULL, username, &keyblock, &kdbhd, 1); if (err) { log_error (_("key \"%s\" not found: %s\n"), username, gpg_strerror (err)); @@ -2570,7 +2572,8 @@ find_by_primary_fpr (ctrl_t ctrl, const char *fpr, err = gpg_error (GPG_ERR_INV_NAME); goto leave; } - err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1); + err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, NULL, fpr, &keyblock, &kdbhd, 1); if (err) { log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err)); @@ -3991,7 +3994,7 @@ menu_adduid (ctrl_t ctrl, kbnode_t pub_keyblock, return 0; } - err = make_keysig_packet (ctrl, &sig, pk, uid, NULL, pk, 0x13, 0, 0, 0, + err = make_keysig_packet (ctrl, &sig, pk, uid, NULL, pk, 0x13, 0, 0, keygen_add_std_prefs, pk, NULL); if (err) { @@ -4289,7 +4292,8 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive) primary keys only, but some casual testing shows that PGP and GnuPG both can handle a designated revocation from a subkey. */ revoker_pk->req_usage = PUBKEY_USAGE_CERT; - rc = get_pubkey_byname (ctrl, NULL, revoker_pk, answer, NULL, NULL, 1, 1); + rc = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, revoker_pk, answer, NULL, NULL, 1); if (rc) { log_error (_("key \"%s\" not found: %s\n"), answer, @@ -4355,7 +4359,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive) continue; } - print_pubkey_info (ctrl, NULL, revoker_pk); + print_key_info (ctrl, NULL, 0, revoker_pk, 0); print_fingerprint (ctrl, NULL, revoker_pk, 2); tty_printf ("\n"); @@ -4374,7 +4378,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive) break; } - rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk, 0x1F, 0, 0, 0, + rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk, 0x1F, 0, 0, keygen_add_revkey, &revkey, NULL); if (rc) { @@ -5898,7 +5902,7 @@ reloop: /* (must use this, because we are modifying the list) */ } rc = make_keysig_packet (ctrl, &sig, primary_pk, unode->pkt->pkt.user_id, - NULL, signerkey, 0x30, 0, 0, 0, + NULL, signerkey, 0x30, 0, 0, sign_mk_attrib, &attrib, NULL); free_public_key (signerkey); if (rc) @@ -5977,11 +5981,11 @@ core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node, memset (&attrib, 0, sizeof attrib); /* should not need to cast away const here; but revocation_reason_build_cb needs to take a non-const - void* in order to meet the function signtuare for the + void* in order to meet the function signutare for the mksubpkt argument to make_keysig_packet */ attrib.reason = (struct revocation_reason_info *)reason; - rc = make_keysig_packet (ctrl, &sig, pk, uid, NULL, pk, 0x30, 0, + rc = make_keysig_packet (ctrl, &sig, pk, uid, NULL, pk, 0x30, timestamp, 0, sign_mk_attrib, &attrib, NULL); if (rc) @@ -6111,7 +6115,7 @@ menu_revkey (ctrl_t ctrl, kbnode_t pub_keyblock) return 0; rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk, - 0x20, 0, 0, 0, + 0x20, 0, 0, revocation_reason_build_cb, reason, NULL); if (rc) { @@ -6173,7 +6177,7 @@ menu_revsubkey (ctrl_t ctrl, kbnode_t pub_keyblock) node->flag &= ~NODFLG_SELKEY; rc = make_keysig_packet (ctrl, &sig, mainpk, NULL, subpk, mainpk, - 0x28, 0, 0, 0, sign_mk_attrib, &attrib, + 0x28, 0, 0, sign_mk_attrib, &attrib, NULL); if (rc) { diff --git a/g10/keygen.c b/g10/keygen.c index 64fefd231..d9037d29d 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -227,18 +227,22 @@ print_status_key_not_created (const char *handle) -static void -write_uid( KBNODE root, const char *s ) +static gpg_error_t +write_uid (kbnode_t root, const char *s) { - PACKET *pkt = xmalloc_clear(sizeof *pkt ); - size_t n = strlen(s); + PACKET *pkt = xmalloc_clear (sizeof *pkt); + size_t n = strlen (s); - pkt->pkttype = PKT_USER_ID; - pkt->pkt.user_id = xmalloc_clear (sizeof *pkt->pkt.user_id + n); - pkt->pkt.user_id->len = n; - pkt->pkt.user_id->ref = 1; - strcpy(pkt->pkt.user_id->name, s); - add_kbnode( root, new_kbnode( pkt ) ); + if (n > MAX_UID_PACKET_LENGTH - 10) + return gpg_error (GPG_ERR_INV_USER_ID); + + pkt->pkttype = PKT_USER_ID; + pkt->pkt.user_id = xmalloc_clear (sizeof *pkt->pkt.user_id + n); + pkt->pkt.user_id->len = n; + pkt->pkt.user_id->ref = 1; + strcpy (pkt->pkt.user_id->name, s); + add_kbnode (root, new_kbnode (pkt)); + return 0; } static void @@ -1022,7 +1026,7 @@ make_backsig (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pk, cache_public_key (sub_pk); err = make_keysig_packet (ctrl, &backsig, pk, NULL, sub_pk, sub_psk, 0x19, - 0, timestamp, 0, NULL, NULL, cache_nonce); + timestamp, 0, NULL, NULL, cache_nonce); if (err) log_error ("make_keysig_packet failed for backsig: %s\n", gpg_strerror (err)); @@ -1130,7 +1134,7 @@ write_direct_sig (ctrl_t ctrl, kbnode_t root, PKT_public_key *psk, /* Make the signature. */ err = make_keysig_packet (ctrl, &sig, pk, NULL,NULL, psk, 0x1F, - 0, timestamp, 0, + timestamp, 0, keygen_add_revkey, revkey, cache_nonce); if (err) { @@ -1185,7 +1189,7 @@ write_selfsigs (ctrl_t ctrl, kbnode_t root, PKT_public_key *psk, /* Make the signature. */ err = make_keysig_packet (ctrl, &sig, pk, uid, NULL, psk, 0x13, - 0, timestamp, 0, + timestamp, 0, keygen_add_std_prefs, pk, cache_nonce); if (err) { @@ -1245,7 +1249,7 @@ write_keybinding (ctrl_t ctrl, kbnode_t root, oduap.usage = use; oduap.pk = sub_pk; err = make_keysig_packet (ctrl, &sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, - 0, timestamp, 0, + timestamp, 0, keygen_add_key_flags_and_expire, &oduap, cache_nonce); if (err) @@ -1881,24 +1885,26 @@ print_key_flags(int flags) /* Ask for the key flags and return them. CURRENT gives the current - * usage which should normally be given as 0. */ + * usage which should normally be given as 0. MASK gives the allowed + * flags. */ unsigned int -ask_key_flags (int algo, int subkey, unsigned int current) +ask_key_flags_with_mask (int algo, int subkey, unsigned int current, + unsigned int mask) { /* TRANSLATORS: Please use only plain ASCII characters for the - translation. If this is not possible use single digits. The - string needs to 8 bytes long. Here is a description of the - functions: - - s = Toggle signing capability - e = Toggle encryption capability - a = Toggle authentication capability - q = Finish - */ + * translation. If this is not possible use single digits. The + * string needs to 8 bytes long. Here is a description of the + * functions: + * + * s = Toggle signing capability + * e = Toggle encryption capability + * a = Toggle authentication capability + * q = Finish + */ const char *togglers = _("SsEeAaQq"); char *answer = NULL; const char *s; - unsigned int possible = openpgp_pk_algo_usage(algo); + unsigned int possible; if ( strlen(togglers) != 8 ) { @@ -1907,22 +1913,26 @@ ask_key_flags (int algo, int subkey, unsigned int current) togglers = "11223300"; } - /* Only primary keys may certify. */ - if(subkey) - possible&=~PUBKEY_USAGE_CERT; + /* Mask the possible usage flags. This is for example used for a + * card based key. */ + possible = (openpgp_pk_algo_usage (algo) & mask); - /* Preload the current set with the possible set, minus - authentication if CURRENT has been given as 0. If CURRENT has - been has non-zero we mask with all possible usages. */ + /* However, only primary keys may certify. */ + if (subkey) + possible &= ~PUBKEY_USAGE_CERT; + + /* Preload the current set with the possible set, without + * authentication if CURRENT is 0. If CURRENT is non-zero we mask + * with all possible usages. */ if (current) current &= possible; else current = (possible&~PUBKEY_USAGE_AUTH); - for(;;) + for (;;) { tty_printf("\n"); - tty_printf(_("Possible actions for a %s key: "), + tty_printf(_("Possible actions for this %s key: "), (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA) ? "ECDSA/EdDSA" : openpgp_pk_algo_name (algo)); @@ -2009,6 +2019,13 @@ ask_key_flags (int algo, int subkey, unsigned int current) } +unsigned int +ask_key_flags (int algo, int subkey, unsigned int current) +{ + return ask_key_flags_with_mask (algo, subkey, current, ~0); +} + + /* Check whether we have a key for the key with HEXGRIP. Returns 0 if there is no such key or the OpenPGP algo number for the key. */ static int @@ -2047,10 +2064,12 @@ static int ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, char **r_keygrip) { + gpg_error_t err; char *keygrip = NULL; char *answer = NULL; int algo; int dummy_algo; + char *p; if (!r_subkey_algo) r_subkey_algo = &dummy_algo; @@ -2101,6 +2120,8 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, if (opt.expert && r_keygrip) tty_printf (_(" (%d) Existing key\n"), 13 ); + if (r_keygrip) + tty_printf (_(" (%d) Existing key from card\n"), 14 ); for (;;) { @@ -2221,9 +2242,130 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, *r_usage = ask_key_flags (algo, addmode, 0); break; } + else if ((algo == 14 || !strcmp (answer, "cardkey")) && r_keygrip) + { + char *serialno; + strlist_t keypairlist, sl; + int count, selection; + + err = agent_scd_serialno (&serialno, NULL); + if (err) + { + tty_printf (_("error reading the card: %s\n"), + gpg_strerror (err)); + goto ask_again; + } + tty_printf (_("Serial number of the card: %s\n"), serialno); + xfree (serialno); + + err = agent_scd_keypairinfo (ctrl, NULL, &keypairlist); + if (err) + { + tty_printf (_("error reading the card: %s\n"), + gpg_strerror (err)); + goto ask_again; + } + + do + { + tty_printf (_("Available keys:\n")); + for (count=1,sl=keypairlist; sl; sl = sl->next, count++) + { + gcry_sexp_t s_pkey; + char *algostr = NULL; + enum gcry_pk_algos algoid = 0; + const char *keyref; + int any = 0; + + keyref = strchr (sl->d, ' '); + if (keyref) + { + keyref++; + if (!agent_scd_readkey (keyref, &s_pkey)) + { + algostr = pubkey_algo_string (s_pkey, &algoid); + gcry_sexp_release (s_pkey); + } + } + /* We use the flags also encode the algo for use + * below. We need to tweak the algo in case + * GCRY_PK_ECC is returned becuase pubkey_algo_string + * is not aware of the OpenPGP algo mapping. + * FIXME: This is an ugly hack. */ + sl->flags &= 0xff; + if (algoid == GCRY_PK_ECC + && algostr && !strncmp (algostr, "nistp", 5) + && !(sl->flags & GCRY_PK_USAGE_ENCR)) + sl->flags |= (PUBKEY_ALGO_ECDSA << 8); + else + sl->flags |= (map_pk_gcry_to_openpgp (algoid) << 8); + + tty_printf (" (%d) %s %s", count, sl->d, algostr); + if ((sl->flags & GCRY_PK_USAGE_CERT)) + { + tty_printf ("%scert", any?",":" ("); + any = 1; + } + if ((sl->flags & GCRY_PK_USAGE_SIGN)) + { + tty_printf ("%ssign", any?",":" ("); + any = 1; + } + if ((sl->flags & GCRY_PK_USAGE_AUTH)) + { + tty_printf ("%sauth", any?",":" ("); + any = 1; + } + if ((sl->flags & GCRY_PK_USAGE_ENCR)) + { + tty_printf ("%sencr", any?",":" ("); + any = 1; + } + tty_printf ("%s\n", any?")":""); + xfree (algostr); + } + + xfree (answer); + answer = cpr_get ("keygen.cardkey", _("Your selection? ")); + cpr_kill_prompt (); + trim_spaces (answer); + selection = atoi (answer); + } + while (!(selection > 0 && selection < count)); + + for (count=1,sl=keypairlist; sl; sl = sl->next, count++) + if (count == selection) + break; + if (!sl) + { + /* Just in case COUNT is zero (no keys). */ + free_strlist (keypairlist); + goto ask_again; + } + + xfree (keygrip); + keygrip = xstrdup (sl->d); + if ((p = strchr (keygrip, ' '))) + *p = 0; + algo = (sl->flags >>8); + if (opt.expert) + *r_usage = ask_key_flags_with_mask (algo, addmode, + (sl->flags & 0xff), + (sl->flags & 0xff)); + else + { + *r_usage = (sl->flags & 0xff); + if (addmode) + *r_usage &= ~GCRY_PK_USAGE_CERT; + } + free_strlist (keypairlist); + break; + } else tty_printf (_("Invalid selection.\n")); + ask_again: + ; } xfree(answer); @@ -2507,14 +2649,25 @@ ask_curve (int *algo, int *subkey_algo, const char *current) else { /* If the user selected a signing algorithm and Curve25519 - we need to set the algo to EdDSA and update the curve name. */ - if ((*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA) - && curves[idx].eddsa_curve) + we need to set the algo to EdDSA and update the curve name. + If switching away from EdDSA, we need to set the algo back + to ECDSA. */ + if (*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA) { - if (subkey_algo && *subkey_algo == PUBKEY_ALGO_ECDSA) - *subkey_algo = PUBKEY_ALGO_EDDSA; - *algo = PUBKEY_ALGO_EDDSA; - result = curves[idx].eddsa_curve; + if (curves[idx].eddsa_curve) + { + if (subkey_algo && *subkey_algo == PUBKEY_ALGO_ECDSA) + *subkey_algo = PUBKEY_ALGO_EDDSA; + *algo = PUBKEY_ALGO_EDDSA; + result = curves[idx].eddsa_curve; + } + else + { + if (subkey_algo && *subkey_algo == PUBKEY_ALGO_EDDSA) + *subkey_algo = PUBKEY_ALGO_ECDSA; + *algo = PUBKEY_ALGO_ECDSA; + result = curves[idx].name; + } } else result = curves[idx].name; @@ -4943,10 +5096,11 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, if (!err && (s = get_parameter_value (para, pUSERID))) { - write_uid (pub_root, s ); - err = write_selfsigs (ctrl, pub_root, pri_psk, - get_parameter_uint (para, pKEYUSAGE), timestamp, - cache_nonce); + err = write_uid (pub_root, s ); + if (!err) + err = write_selfsigs (ctrl, pub_root, pri_psk, + get_parameter_uint (para, pKEYUSAGE), timestamp, + cache_nonce); } /* Write the auth key to the card before the encryption key. This diff --git a/g10/keyid.c b/g10/keyid.c index aa77b47e2..7605cb386 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -68,7 +68,7 @@ pubkey_letter( int algo ) } /* Return a string describing the public key algorithm and the - keysize. For elliptic curves the functions prints the name of the + keysize. For elliptic curves the function prints the name of the curve because the keysize is a property of the curve. The string is copied to the supplied buffer up a length of BUFSIZE-1. Examples for the output are: @@ -253,20 +253,6 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) } -static gcry_md_hd_t -do_fingerprint_md( PKT_public_key *pk ) -{ - gcry_md_hd_t md; - - if (gcry_md_open (&md, pk->version == 5 ? GCRY_MD_SHA256 : GCRY_MD_SHA1, 0)) - BUG (); - hash_public_key (md,pk); - gcry_md_final (md); - - return md; -} - - /* fixme: Check whether we can replace this function or if not describe why we need it. */ u32 @@ -520,6 +506,37 @@ keystr_from_desc(KEYDB_SEARCH_DESC *desc) } +/* Compute the fingerprint and keyid and store it in PK. */ +static void +compute_fingerprint (PKT_public_key *pk) +{ + const byte *dp; + gcry_md_hd_t md; + size_t len; + + if (gcry_md_open (&md, pk->version == 5 ? GCRY_MD_SHA256 : GCRY_MD_SHA1, 0)) + BUG (); + hash_public_key (md, pk); + gcry_md_final (md); + dp = gcry_md_read (md, 0); + len = gcry_md_get_algo_dlen (gcry_md_get_algo (md)); + log_assert (len <= MAX_FINGERPRINT_LEN); + memcpy (pk->fpr, dp, len); + pk->fprlen = len; + if (pk->version == 5) + { + pk->keyid[0] = buf32_to_u32 (dp); + pk->keyid[1] = buf32_to_u32 (dp+4); + } + else + { + pk->keyid[0] = buf32_to_u32 (dp+12); + pk->keyid[1] = buf32_to_u32 (dp+16); + } + gcry_md_close( md); +} + + /* * Get the keyid from the public key PK and store it at KEYID unless * this is NULL. Returns the 32 bit short keyid. @@ -532,37 +549,11 @@ keyid_from_pk (PKT_public_key *pk, u32 *keyid) if (!keyid) keyid = dummy_keyid; - if( pk->keyid[0] || pk->keyid[1] ) - { - keyid[0] = pk->keyid[0]; - keyid[1] = pk->keyid[1]; - } - else - { - const byte *dp; - gcry_md_hd_t md; + if (!pk->fprlen) + compute_fingerprint (pk); - md = do_fingerprint_md(pk); - if(md) - { - dp = gcry_md_read ( md, 0 ); - if (pk->version == 5) - { - keyid[0] = buf32_to_u32 (dp); - keyid[1] = buf32_to_u32 (dp+4); - } - else - { - keyid[0] = buf32_to_u32 (dp+12); - keyid[1] = buf32_to_u32 (dp+16); - } - gcry_md_close (md); - pk->keyid[0] = keyid[0]; - pk->keyid[1] = keyid[1]; - } - else - pk->keyid[0] = pk->keyid[1] = keyid[0]= keyid[1] = 0xFFFFFFFF; - } + keyid[0] = pk->keyid[0]; + keyid[1] = pk->keyid[1]; return keyid[1]; /*FIXME:shortkeyid ist different for v5*/ } @@ -805,6 +796,7 @@ colon_expirestr_from_sig (PKT_signature *sig) } + /* * Return a byte array with the fingerprint for the given PK/SK * The length of the array is returned in ret_len. Caller must free @@ -813,31 +805,15 @@ colon_expirestr_from_sig (PKT_signature *sig) byte * fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len) { - const byte *dp; - size_t len; - gcry_md_hd_t md; + if (!pk->fprlen) + compute_fingerprint (pk); - md = do_fingerprint_md (pk); - dp = gcry_md_read (md, 0); - len = gcry_md_get_algo_dlen (gcry_md_get_algo (md)); - log_assert (len <= MAX_FINGERPRINT_LEN); if (!array) - array = xmalloc ( len ); - memcpy (array, dp, len ); - if (pk->version == 5) - { - pk->keyid[0] = buf32_to_u32 (dp); - pk->keyid[1] = buf32_to_u32 (dp+4); - } - else - { - pk->keyid[0] = buf32_to_u32 (dp+12); - pk->keyid[1] = buf32_to_u32 (dp+16); - } - gcry_md_close( md); + array = xmalloc (pk->fprlen); + memcpy (array, pk->fpr, pk->fprlen); if (ret_len) - *ret_len = len; + *ret_len = pk->fprlen; return array; } @@ -852,19 +828,19 @@ fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len) char * hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen) { - unsigned char fpr[MAX_FINGERPRINT_LEN]; - size_t len; + if (!pk->fprlen) + compute_fingerprint (pk); - fingerprint_from_pk (pk, fpr, &len); if (!buffer) { - buffer = xtrymalloc (2 * len + 1); + buffer = xtrymalloc (2 * pk->fprlen + 1); if (!buffer) return NULL; } - else if (buflen < 2*len+1) + else if (buflen < 2 * pk->fprlen + 1) log_fatal ("%s: buffer too short (%zu)\n", __func__, buflen); - bin2hex (fpr, len, buffer); + + bin2hex (pk->fpr, pk->fprlen, buffer); return buffer; } diff --git a/g10/keylist.c b/g10/keylist.c index 8d5b2e0b9..1274775d2 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -51,7 +51,7 @@ static void list_all (ctrl_t, int, int); static void list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret); -static void locate_one (ctrl_t ctrl, strlist_t names); +static void locate_one (ctrl_t ctrl, strlist_t names, int no_local); static void print_card_serialno (const char *serialno); struct keylist_context @@ -83,10 +83,11 @@ keylist_context_release (struct keylist_context *listctx) /* List the keys. If list is NULL, all available keys are listed. - With LOCATE_MODE set the locate algorithm is used to find a - key. */ + * With LOCATE_MODE set the locate algorithm is used to find a key; if + * in addition NO_LOCAL is set the locate does not look into the local + * keyring. */ void -public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode) +public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode, int no_local) { #ifndef NO_TRUST_MODELS if (opt.with_colons) @@ -140,7 +141,7 @@ public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode) #endif if (locate_mode) - locate_one (ctrl, list); + locate_one (ctrl, list, no_local); else if (!list) list_all (ctrl, 0, opt.with_secret); else @@ -165,43 +166,15 @@ secret_key_list (ctrl_t ctrl, strlist_t list) list_one (ctrl, list, 1, 0); } -char * -format_seckey_info (ctrl_t ctrl, PKT_public_key *pk) -{ - u32 keyid[2]; - char *p; - char pkstrbuf[PUBKEY_STRING_SIZE]; - char *info; - - keyid_from_pk (pk, keyid); - p = get_user_id_native (ctrl, keyid); - - info = xtryasprintf ("sec %s/%s %s %s", - pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), - keystr (keyid), datestr_from_pk (pk), p); - - xfree (p); - - return info; -} - -void -print_seckey_info (ctrl_t ctrl, PKT_public_key *pk) -{ - char *p = format_seckey_info (ctrl, pk); - tty_printf ("\n%s\n", p); - xfree (p); -} - -/* Print information about the public key. With FP passed as NULL, - the tty output interface is used, otherwise output is directed to - the given stream. */ -void -print_pubkey_info (ctrl_t ctrl, estream_t fp, PKT_public_key *pk) + +/* Helper for print_key_info and print_key_info_log. */ +static char * +format_key_info (ctrl_t ctrl, PKT_public_key *pk, int secret) { u32 keyid[2]; char *p; char pkstrbuf[PUBKEY_STRING_SIZE]; + char *result; keyid_from_pk (pk, keyid); @@ -212,13 +185,59 @@ print_pubkey_info (ctrl_t ctrl, estream_t fp, PKT_public_key *pk) else p = get_user_id_native (ctrl, keyid); - if (!fp) - tty_printf ("\n"); - tty_fprintf (fp, "%s %s/%s %s %s\n", - pk->flags.primary? "pub":"sub", - pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), - keystr (keyid), datestr_from_pk (pk), p); + result = xtryasprintf ("%s %s/%s %s %s", + secret? (pk->flags.primary? "sec":"ssb") + /* */ : (pk->flags.primary? "pub":"sub"), + pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), + keystr (keyid), datestr_from_pk (pk), p); xfree (p); + return result; +} + + +/* Print basic information about a public or secret key. With FP + * passed as NULL, the tty output interface is used, otherwise output + * is directed to the given stream. INDENT gives the requested + * indentation; if that is a negative value indentation is suppressed + * for the first line. SECRET tells that the PK has a secret part. + * FIXME: This is similar in use to print_key_line and thus both + * functions should eventually be united. + */ +void +print_key_info (ctrl_t ctrl, estream_t fp, + int indent, PKT_public_key *pk, int secret) +{ + int indentabs = indent >= 0? indent : -indent; + char *info; + + /* Note: Negative values for INDENT are not yet needed. */ + + info = format_key_info (ctrl, pk, secret); + + if (!fp && indent >= 0) + tty_printf ("\n"); /* (Backward compatibility to old code) */ + tty_fprintf (fp, "%*s%s\n", indentabs, "", + info? info : "[Ooops - out of core]"); + + xfree (info); +} + + +/* Same as print_key_info put print using the log functions at + * LOGLEVEL. */ +void +print_key_info_log (ctrl_t ctrl, int loglevel, + int indent, PKT_public_key *pk, int secret) +{ + int indentabs = indent >= 0? indent : -indent; + char *info; + + info = format_key_info (ctrl, pk, secret); + + log_log (loglevel, "%*s%s\n", indentabs, "", + info? info : "[Ooops - out of core]"); + + xfree (info); } @@ -640,7 +659,7 @@ list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret) static void -locate_one (ctrl_t ctrl, strlist_t names) +locate_one (ctrl_t ctrl, strlist_t names, int no_local) { int rc = 0; strlist_t sl; @@ -654,7 +673,10 @@ locate_one (ctrl_t ctrl, strlist_t names) for (sl = names; sl; sl = sl->next) { - rc = get_best_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, 1); + rc = get_best_pubkey_byname (ctrl, + no_local? GET_PUBKEY_NO_LOCAL + /* */: GET_PUBKEY_NORMAL, + &ctx, NULL, sl->d, &keyblock, 1); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY) diff --git a/g10/keyring.c b/g10/keyring.c index 21791a6ac..5fa499759 100644 --- a/g10/keyring.c +++ b/g10/keyring.c @@ -473,11 +473,14 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) } in_cert = 1; - node = lastnode = new_kbnode (pkt); + node = new_kbnode (pkt); if (!keyblock) - keyblock = node; + keyblock = lastnode = node; else - add_kbnode (keyblock, node); + { + lastnode->next = node; + lastnode = node; + } switch (pkt->pkttype) { case PKT_PUBLIC_KEY: diff --git a/g10/keyserver.c b/g10/keyserver.c index 66900f7a9..b07afb128 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -333,7 +333,7 @@ parse_keyserver_uri (const char *string,int require_scheme) { /* Three slashes means network path with a default host name. This is a hack because it does not crok all possible - combiantions. We should better repalce all code bythe parser + combinations. We should better replace all code by the parser from http.c. */ keyserver->path = xstrdup (uri+2); } @@ -1519,9 +1519,7 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens) log_info (_("key not found on keyserver\n")); } - if (gpg_err_code (err) == GPG_ERR_NO_KEYSERVER) - log_error (_("no keyserver known (use option --keyserver)\n")); - else if (gpg_err_code (err) == GPG_ERR_NO_DATA) + if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = gpg_error (GPG_ERR_NOT_FOUND); else if (err) log_error ("error searching keyserver: %s\n", gpg_strerror (err)); @@ -2051,8 +2049,9 @@ keyserver_import_wkd (ctrl_t ctrl, const char *name, int quick, int armor_status = opt.no_armor; import_filter_t save_filt; - /* Keys returned via WKD are in binary format. */ - opt.no_armor = 1; + /* Keys returned via WKD are in binary format. However, we + * relax that requirement and allow also for armored data. */ + opt.no_armor = 0; save_filt = save_and_clear_import_filter (); if (!save_filt) err = gpg_error_from_syserror (); diff --git a/g10/main.h b/g10/main.h index 34a932b16..f45b03909 100644 --- a/g10/main.h +++ b/g10/main.h @@ -414,6 +414,8 @@ void export_print_stats (export_stats_t stats); int parse_export_options(char *str,unsigned int *options,int noisy); gpg_error_t parse_and_set_export_filter (const char *string); +int exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, kbnode_t node); + int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options, @@ -456,7 +458,8 @@ struct revocation_reason_info * get_default_uid_revocation_reason(void); void release_revocation_reason_info( struct revocation_reason_info *reason ); /*-- keylist.c --*/ -void public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode ); +void public_key_list (ctrl_t ctrl, strlist_t list, + int locate_mode, int no_local); void secret_key_list (ctrl_t ctrl, strlist_t list ); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); @@ -470,9 +473,10 @@ void show_keyserver_url(PKT_signature *sig,int indent,int mode); void show_notation(PKT_signature *sig,int indent,int mode,int which); void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk); void set_attrib_fd(int fd); -char *format_seckey_info (ctrl_t ctrl, PKT_public_key *pk); -void print_seckey_info (ctrl_t ctrl, PKT_public_key *pk); -void print_pubkey_info (ctrl_t ctrl, estream_t fp, PKT_public_key *pk); +void print_key_info (ctrl_t ctrl, estream_t fp, int indent, + PKT_public_key *pk, int secret); +void print_key_info_log (ctrl_t ctrl, int loglevel, int indent, + PKT_public_key *pk, int secret); void print_card_key_info (estream_t fp, KBNODE keyblock); void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret); diff --git a/g10/mainproc.c b/g10/mainproc.c index 7acf67b1e..8a9005c21 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -506,19 +506,18 @@ print_pkenc_list (ctrl_t ctrl, struct pubkey_enc_list *list) for (; list; list = list->next) { PKT_public_key *pk; - const char *algstr; + char pkstrbuf[PUBKEY_STRING_SIZE]; + char *p; - algstr = openpgp_pk_algo_name (list->pubkey_algo); pk = xmalloc_clear (sizeof *pk); - if (!algstr) - algstr = "[?]"; pk->pubkey_algo = list->pubkey_algo; if (!get_pubkey (ctrl, pk, list->keyid)) { - char *p; - log_info (_("encrypted with %u-bit %s key, ID %s, created %s\n"), - nbits_from_pk (pk), algstr, keystr_from_pk(pk), + pubkey_string (pk, pkstrbuf, sizeof pkstrbuf); + + log_info (_("encrypted with %s key, ID %s, created %s\n"), + pkstrbuf, keystr_from_pk (pk), strtimestamp (pk->timestamp)); p = get_user_id_native (ctrl, list->keyid); log_printf (_(" \"%s\"\n"), p); @@ -526,7 +525,8 @@ print_pkenc_list (ctrl_t ctrl, struct pubkey_enc_list *list) } else log_info (_("encrypted with %s key, ID %s\n"), - algstr, keystr(list->keyid)); + openpgp_pk_algo_name (list->pubkey_algo), + keystr(list->keyid)); free_public_key (pk); } @@ -574,7 +574,7 @@ proc_encrypted (CTX c, PACKET *pkt) write_status_error ("pkdecrypt_failed", result); } } - else + else if (c->pkenc_list) { c->dek = xmalloc_secure_clear (sizeof *c->dek); result = get_session_key (c->ctrl, c->pkenc_list, c->dek); @@ -583,7 +583,7 @@ proc_encrypted (CTX c, PACKET *pkt) struct pubkey_enc_list *list; for (list = c->pkenc_list; list; list = list->next) - if (list->result == GPG_ERR_NO_SECKEY) + if (list->result != -1) { char buf[20]; snprintf (buf, sizeof buf, "%08lX%08lX", @@ -668,7 +668,13 @@ proc_encrypted (CTX c, PACKET *pkt) } } else if (!c->dek) - result = GPG_ERR_NO_SECKEY; + { + if (c->symkeys && !c->pkenc_list) + result = gpg_error (GPG_ERR_BAD_KEY); + + if (!result) + result = gpg_error (GPG_ERR_NO_SECKEY); + } /* Compute compliance with CO_DE_VS. */ if (!result && is_status_enabled () @@ -780,7 +786,7 @@ proc_encrypted (CTX c, PACKET *pkt) if ((gpg_err_code (result) == GPG_ERR_BAD_KEY || gpg_err_code (result) == GPG_ERR_CHECKSUM || gpg_err_code (result) == GPG_ERR_CIPHER_ALGO) - && *c->dek->s2k_cacheid != '\0') + && c->dek && *c->dek->s2k_cacheid != '\0') { if (opt.debug) log_debug ("cleared passphrase cached with ID: %s\n", @@ -1840,7 +1846,6 @@ check_sig_and_print (CTX c, kbnode_t node) int is_revkey = 0; char *issuer_fpr = NULL; PKT_public_key *pk = NULL; /* The public key for the signature or NULL. */ - int tried_ks_by_fpr; const void *extrahash = NULL; size_t extrahashlen = 0; @@ -1999,12 +2004,17 @@ check_sig_and_print (CTX c, kbnode_t node) rc = do_check_sig (c, node, extrahash, extrahashlen, NULL, &is_expkey, &is_revkey, &pk); - /* If the key isn't found, check for a preferred keyserver. */ - if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && sig->flags.pref_ks) + /* If the key isn't found, check for a preferred keyserver. Note + * that this is only done if honor-keyserver-url has been set. We + * test for this in the loop so that we can show info about the + * preferred keyservers. */ + if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY + && sig->flags.pref_ks) { const byte *p; int seq = 0; size_t n; + int any_pref_ks = 0; while ((p=enum_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS,&n,&seq,NULL))) { @@ -2015,9 +2025,10 @@ check_sig_and_print (CTX c, kbnode_t node) log_info(_("Key available at: ") ); print_utf8_buffer (log_get_stream(), p, n); log_printf ("\n"); + any_pref_ks = 1; - if (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE - && opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL) + if ((opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) + && (opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL)) { struct keyserver_spec *spec; @@ -2026,6 +2037,10 @@ check_sig_and_print (CTX c, kbnode_t node) { int res; + if (DBG_LOOKUP) + log_debug ("trying auto-key-retrieve method %s\n", + "Pref-KS"); + free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; @@ -2034,6 +2049,9 @@ check_sig_and_print (CTX c, kbnode_t node) if (!res) rc = do_check_sig (c, node, extrahash, extrahashlen, NULL, &is_expkey, &is_revkey, &pk); + else if (DBG_LOOKUP) + log_debug ("lookup via %s failed: %s\n", "Pref-KS", + gpg_strerror (res)); free_keyserver_spec (spec); if (!rc) @@ -2041,10 +2059,44 @@ check_sig_and_print (CTX c, kbnode_t node) } } } + + if (any_pref_ks + && (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) + && !(opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL)) + log_info (_("Note: Use '%s' to make use of this info\n"), + "--keyserver-option honor-keyserver-url"); + } + + /* If the above methods didn't work, our next try is to retrieve the + * key from the WKD. This requires that WKD is in the AKL and the + * Signer's UID is in the signature. */ + if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY + && (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE) + && !opt.flags.disable_signer_uid + && akl_has_wkd_method () + && sig->signers_uid) + { + int res; + + if (DBG_LOOKUP) + log_debug ("trying auto-key-retrieve method %s\n", "WKD"); + free_public_key (pk); + pk = NULL; + glo_ctrl.in_auto_key_retrieve++; + res = keyserver_import_wkd (c->ctrl, sig->signers_uid, 1, NULL, NULL); + glo_ctrl.in_auto_key_retrieve--; + /* Fixme: If the fingerprint is embedded in the signature, + * compare it to the fingerprint of the returned key. */ + if (!res) + rc = do_check_sig (c, node, extrahash, extrahashlen, + NULL, &is_expkey, &is_revkey, &pk); + else if (DBG_LOOKUP) + log_debug ("lookup via %s failed: %s\n", "WKD", gpg_strerror (res)); } /* If the avove methods didn't work, our next try is to use the URI - * from a DNS PKA record. */ + * from a DNS PKA record. This is a legacy method which will + * eventually be removed. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE) && (opt.keyserver_options.options & KEYSERVER_HONOR_PKA_RECORD)) @@ -2061,6 +2113,9 @@ check_sig_and_print (CTX c, kbnode_t node) spec = parse_keyserver_uri (uri, 1); if (spec) { + if (DBG_LOOKUP) + log_debug ("trying auto-key-retrieve method %s\n", "PKA"); + free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; @@ -2070,16 +2125,16 @@ check_sig_and_print (CTX c, kbnode_t node) if (!res) rc = do_check_sig (c, node, extrahash, extrahashlen, NULL, &is_expkey, &is_revkey, &pk); + else if (DBG_LOOKUP) + log_debug ("lookup via %s failed: %s\n", "PKA", + gpg_strerror (res)); } } } /* If the above methods didn't work, our next try is to locate * the key via its fingerprint from a keyserver. This requires - * that the signers fingerprint is encoded in the signature. We - * favor this over the WKD method (to be tried next), because an - * arbitrary keyserver is less subject to web bug like monitoring. */ - tried_ks_by_fpr = 0; + * that the signers fingerprint is encoded in the signature. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) && keyserver_any_configured (c->ctrl)) @@ -2091,60 +2146,23 @@ check_sig_and_print (CTX c, kbnode_t node) p = issuer_fpr_raw (sig, &n); if (p) { + if (DBG_LOOKUP) + log_debug ("trying auto-key-retrieve method %s\n", "KS"); + /* v4 or v5 packet with a SHA-1/256 fingerprint. */ free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; res = keyserver_import_fprint (c->ctrl, p, n, opt.keyserver, 1); - tried_ks_by_fpr = 1; glo_ctrl.in_auto_key_retrieve--; if (!res) rc = do_check_sig (c, node, extrahash, extrahashlen, NULL, &is_expkey, &is_revkey, &pk); + else if (DBG_LOOKUP) + log_debug ("lookup via %s failed: %s\n", "KS", gpg_strerror (res)); } } - /* If the above methods didn't work, our next try is to retrieve the - * key from the WKD. */ - if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY - && (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE) - && !opt.flags.disable_signer_uid - && akl_has_wkd_method () - && sig->signers_uid) - { - int res; - - free_public_key (pk); - pk = NULL; - glo_ctrl.in_auto_key_retrieve++; - res = keyserver_import_wkd (c->ctrl, sig->signers_uid, 1, NULL, NULL); - glo_ctrl.in_auto_key_retrieve--; - /* Fixme: If the fingerprint is embedded in the signature, - * compare it to the fingerprint of the returned key. */ - if (!res) - rc = do_check_sig (c, node, extrahash, extrahashlen, - NULL, &is_expkey, &is_revkey, &pk); - } - - /* If the above methods did't work, our next try is to use a - * keyserver. */ - if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY - && (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) - && !tried_ks_by_fpr - && keyserver_any_configured (c->ctrl)) - { - int res; - - free_public_key (pk); - pk = NULL; - glo_ctrl.in_auto_key_retrieve++; - res = keyserver_import_keyid (c->ctrl, sig->keyid, opt.keyserver, 1); - glo_ctrl.in_auto_key_retrieve--; - if (!res) - rc = do_check_sig (c, node, extrahash, extrahashlen, - NULL, &is_expkey, &is_revkey, &pk); - } - if (!rc || gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE) { kbnode_t un, keyblock; diff --git a/g10/objcache.c b/g10/objcache.c new file mode 100644 index 000000000..adb0717d7 --- /dev/null +++ b/g10/objcache.c @@ -0,0 +1,689 @@ +/* objcache.c - Caching functions for keys and user ids. + * Copyright (C) 2019 g10 Code GmbH + * + * 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 3 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, see . + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include + +#include "gpg.h" +#include "../common/util.h" +#include "packet.h" +#include "keydb.h" +#include "options.h" +#include "objcache.h" + +/* Note that max value for uid_items is actually a the threshold when + * we start to look for ietms which can be removed. */ +#define NO_OF_UID_ITEM_BUCKETS 107 +#define MAX_UID_ITEMS_PER_BUCKET 20 + +#define NO_OF_KEY_ITEM_BUCKETS 383 +#define MAX_KEY_ITEMS_PER_BUCKET 20 + + +/* An object to store a user id. This describes an item in the linked + * lists of a bucket in hash table. The reference count will + * eventually be used to remove items from the table. */ +typedef struct uid_item_s +{ + struct uid_item_s *next; + unsigned int refcount; /* The reference count for this item. */ + unsigned int namelen; /* The length of the UID sans the nul. */ + char name[1]; +} *uid_item_t; + +static uid_item_t *uid_table; /* Hash table for with user ids. */ +static size_t uid_table_size; /* Number of allocated buckets. */ +static unsigned int uid_table_max; /* Max. # of items in a bucket. */ +static unsigned int uid_table_added; /* # of items added. */ +static unsigned int uid_table_dropped;/* # of items dropped. */ + + +/* An object to store properties of a key. Note that this can be used + * for a primary or a subkey. The key is linked to a user if that + * exists. */ +typedef struct key_item_s +{ + struct key_item_s *next; + unsigned int usecount; + byte fprlen; + char fpr[MAX_FINGERPRINT_LEN]; + u32 keyid[2]; + uid_item_t ui; /* NULL of a ref'ed user id item. */ +} *key_item_t; + +static key_item_t *key_table; /* Hash table with the keys. */ +static size_t key_table_size; /* Number of allocated buckents. */ +static unsigned int key_table_max; /* Max. # of items in a bucket. */ +static unsigned int key_table_added; /* # of items added. */ +static unsigned int key_table_dropped;/* # of items dropped. */ +static key_item_t key_item_attic; /* List of freed items. */ + + + +/* Dump stats. */ +void +objcache_dump_stats (void) +{ + unsigned int idx; + int len, minlen, maxlen; + unsigned int count, attic, empty; + key_item_t ki; + uid_item_t ui; + + count = empty = 0; + minlen = -1; + maxlen = 0; + for (idx = 0; idx < key_table_size; idx++) + { + len = 0; + for (ki = key_table[idx]; ki; ki = ki->next) + { + count++; + len++; + /* log_debug ("key bucket %u: kid=%08lX used=%u ui=%p\n", */ + /* idx, (ulong)ki->keyid[0], ki->usecount, ki->ui); */ + } + if (len > maxlen) + maxlen = len; + + if (!len) + empty++; + else if (minlen == -1 || len < minlen) + minlen = len; + } + for (attic=0, ki = key_item_attic; ki; ki = ki->next) + attic++; + log_info ("objcache: keys=%u/%u/%u chains=%u,%d..%d buckets=%zu/%u" + " attic=%u\n", + count, key_table_added, key_table_dropped, + empty, minlen > 0? minlen : 0, maxlen, + key_table_size, key_table_max, attic); + + count = empty = 0; + minlen = -1; + maxlen = 0; + for (idx = 0; idx < uid_table_size; idx++) + { + len = 0; + for (ui = uid_table[idx]; ui; ui = ui->next) + { + count++; + len++; + /* log_debug ("uid bucket %u: %p ref=%u l=%u (%.20s)\n", */ + /* idx, ui, ui->refcount, ui->namelen, ui->name); */ + } + if (len > maxlen) + maxlen = len; + + if (!len) + empty++; + else if (minlen == -1 || len < minlen) + minlen = len; + } + log_info ("objcache: uids=%u/%u/%u chains=%u,%d..%d buckets=%zu/%u\n", + count, uid_table_added, uid_table_dropped, + empty, minlen > 0? minlen : 0, maxlen, + uid_table_size, uid_table_max); +} + + + +/* The hash function we use for the uid_table. Must not call a system + * function. */ +static inline unsigned int +uid_table_hasher (const char *name, unsigned namelen) +{ + const unsigned char *s = (const unsigned char*)name; + unsigned int hashval = 0; + unsigned int carry; + + for (; namelen; namelen--, s++) + { + hashval = (hashval << 4) + *s; + if ((carry = (hashval & 0xf0000000))) + { + hashval ^= (carry >> 24); + hashval ^= carry; + } + } + + return hashval % uid_table_size; +} + + +/* Run time allocation of the uid table. This allows us to eventually + * add an option to gpg to control the size. */ +static void +uid_table_init (void) +{ + if (uid_table) + return; + uid_table_size = NO_OF_UID_ITEM_BUCKETS; + uid_table_max = MAX_UID_ITEMS_PER_BUCKET; + uid_table = xcalloc (uid_table_size, sizeof *uid_table); +} + + +static uid_item_t +uid_item_ref (uid_item_t ui) +{ + if (ui) + ui->refcount++; + return ui; +} + +static void +uid_item_unref (uid_item_t uid) +{ + if (!uid) + return; + if (!uid->refcount) + log_fatal ("too many unrefs for uid_item\n"); + + uid->refcount--; + /* We do not release the item here because that would require that + * we locate the head of the list which has this item. This will + * take too long and thus the item is removed when we need to purge + * some items for the list during uid_item_put. */ +} + + +/* Put (NAME,NAMELEN) into the UID_TABLE and return the item. The + * reference count for that item is incremented. NULL is return on an + * allocation error. The caller should release the returned item + * using uid_item_unref. */ +static uid_item_t +uid_table_put (const char *name, unsigned int namelen) +{ + unsigned int hash; + uid_item_t ui; + unsigned int count; + + if (!uid_table) + uid_table_init (); + + hash = uid_table_hasher (name, namelen); + for (ui = uid_table[hash], count = 0; ui; ui = ui->next, count++) + if (ui->namelen == namelen && !memcmp (ui->name, name, namelen)) + return uid_item_ref (ui); /* Found. */ + + /* If the bucket is full remove all unrefed items. */ + if (count >= uid_table_max) + { + uid_item_t ui_next, ui_prev, list_head, drop_head; + + /* No syscalls from here .. */ + list_head = uid_table[hash]; + drop_head = NULL; + while (list_head && !list_head->refcount) + { + ui = list_head; + list_head = ui->next; + ui->next = drop_head; + drop_head = ui; + } + if ((ui_prev = list_head)) + for (ui = ui_prev->next; ui; ui = ui_next) + { + ui_next = ui->next; + if (!ui->refcount) + { + ui->next = drop_head; + drop_head = ui; + ui_prev->next = ui_next; + } + else + ui_prev = ui; + } + uid_table[hash] = list_head; + /* ... to here */ + + for (ui = drop_head; ui; ui = ui_next) + { + ui_next = ui->next; + xfree (ui); + uid_table_dropped++; + } + } + + count = uid_table_added + uid_table_dropped; + ui = xtrycalloc (1, sizeof *ui + namelen); + if (!ui) + return NULL; /* Out of core. */ + if (count != uid_table_added + uid_table_dropped) + { + /* During the malloc another thread added an item. Thus we need + * to check again. */ + uid_item_t ui_new = ui; + for (ui = uid_table[hash]; ui; ui = ui->next) + if (ui->namelen == namelen && !memcmp (ui->name, name, namelen)) + { + /* Found. */ + xfree (ui_new); + return uid_item_ref (ui); + } + ui = ui_new; + } + + memcpy (ui->name, name, namelen); + ui->name[namelen] = 0; /* Extra Nul so we can use it as a string. */ + ui->namelen = namelen; + ui->refcount = 1; + ui->next = uid_table[hash]; + uid_table[hash] = ui; + uid_table_added++; + return ui; +} + + + +/* The hash function we use for the key_table. Must not call a system + * function. */ +static inline unsigned int +key_table_hasher (u32 *keyid) +{ + /* A fingerprint could be used directly as a hash value. However, + * we use the keyid here because it is used in encrypted packets and + * older signatures to identify a key. Since v4 keys the keyid is + * anyway a part of the fingerprint so it quickly extracted from a + * fingerprint. Note that v3 keys are not supported by gpg. */ + return keyid[0] % key_table_size; +} + + +/* Run time allocation of the key table. This allows us to eventually + * add an option to gpg to control the size. */ +static void +key_table_init (void) +{ + if (key_table) + return; + key_table_size = NO_OF_KEY_ITEM_BUCKETS; + key_table_max = MAX_KEY_ITEMS_PER_BUCKET; + key_table = xcalloc (key_table_size, sizeof *key_table); +} + + +static void +key_item_free (key_item_t ki) +{ + if (!ki) + return; + uid_item_unref (ki->ui); + ki->ui = NULL; + ki->next = key_item_attic; + key_item_attic = ki; +} + + +/* Get a key item from PK or if that is NULL from KEYID. The + * reference count for that item is incremented. NULL is return if it + * was not found. */ +static key_item_t +key_table_get (PKT_public_key *pk, u32 *keyid) +{ + unsigned int hash; + key_item_t ki, ki2; + + if (!key_table) + key_table_init (); + + if (pk) + { + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fprlen; + u32 tmpkeyid[2]; + + fingerprint_from_pk (pk, fpr, &fprlen); + keyid_from_pk (pk, tmpkeyid); + hash = key_table_hasher (tmpkeyid); + for (ki = key_table[hash]; ki; ki = ki->next) + if (ki->fprlen == fprlen && !memcmp (ki->fpr, fpr, fprlen)) + return ki; /* Found */ + } + else if (keyid) + { + hash = key_table_hasher (keyid); + for (ki = key_table[hash]; ki; ki = ki->next) + if (ki->keyid[0] == keyid[0] && ki->keyid[1] == keyid[1]) + { + /* Found. We need to check for dups. */ + for (ki2 = ki->next; ki2; ki2 = ki2->next) + if (ki2->keyid[0] == keyid[0] && ki2->keyid[1] == keyid[1]) + return NULL; /* Duplicated keyid - retrun NULL. */ + + /* This is the only one - return it. */ + return ki; + } + } + return NULL; +} + + +/* Helper for the qsort in key_table_put. */ +static int +compare_key_items (const void *arg_a, const void *arg_b) +{ + const key_item_t a = *(const key_item_t *)arg_a; + const key_item_t b = *(const key_item_t *)arg_b; + + /* Reverse sort on the usecount. */ + if (a->usecount > b->usecount) + return -1; + else if (a->usecount == b->usecount) + return 0; + else + return 1; +} + + +/* Put PK into the KEY_TABLE and return a key item. The reference + * count for that item is incremented. If UI is given it is put into + * the entry. NULL is return on an allocation error. */ +static key_item_t +key_table_put (PKT_public_key *pk, uid_item_t ui) +{ + unsigned int hash; + key_item_t ki; + u32 keyid[2]; + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fprlen; + unsigned int count, n; + + if (!key_table) + key_table_init (); + + fingerprint_from_pk (pk, fpr, &fprlen); + keyid_from_pk (pk, keyid); + hash = key_table_hasher (keyid); + for (ki = key_table[hash], count=0; ki; ki = ki->next, count++) + if (ki->fprlen == fprlen && !memcmp (ki->fpr, fpr, fprlen)) + return ki; /* Found */ + + /* If the bucket is full remove a couple of items. */ + if (count >= key_table_max) + { + key_item_t list_head, *list_tailp, ki_next; + key_item_t *array; + int narray, idx; + + /* Unlink from the global list so that other threads don't + * disturb us. If another thread adds or removes something only + * one will be the winner. Bad luck for the drooped cache items + * but after all it is just a cache. */ + list_head = key_table[hash]; + key_table[hash] = NULL; + + /* Put all items into an array for sorting. */ + array = xtrycalloc (count, sizeof *array); + if (!array) + { + /* That's bad; give up all items of the bucket. */ + log_info ("Note: malloc failed while purging from the key_tabe: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + goto leave_drop; + } + narray = 0; + for (ki = list_head; ki; ki = ki_next) + { + ki_next = ki->next; + array[narray++] = ki; + ki->next = NULL; + } + log_assert (narray == count); + + /* Sort the array and put half of it onto a new list. */ + qsort (array, narray, sizeof *array, compare_key_items); + list_head = NULL; + list_tailp = &list_head; + for (idx=0; idx < narray/2; idx++) + { + *list_tailp = array[idx]; + list_tailp = &array[idx]->next; + } + + /* Put the new list into the bucket. */ + ki = key_table[hash]; + key_table[hash] = list_head; + list_head = ki; + + /* Free the remaining items and the array. */ + for (; idx < narray; idx++) + { + key_item_free (array[idx]); + key_table_dropped++; + } + xfree (array); + + leave_drop: + /* Free any items added in the meantime by other threads. This + * is also used in case of a malloc problem (which won't update + * the counters, though). */ + for ( ; list_head; list_head = ki_next) + { + ki_next = list_head->next; + key_item_free (list_head); + } + } + + /* Add an item to the bucket. We allocate a whole block of items + * for cache performace reasons. */ + if (!key_item_attic) + { + key_item_t kiblock; + int kiblocksize = 256; + + kiblock = xtrymalloc (kiblocksize * sizeof *kiblock); + if (!kiblock) + return NULL; /* Out of core. */ + for (n = 0; n < kiblocksize; n++) + { + ki = kiblock + n; + ki->next = key_item_attic; + key_item_attic = ki; + } + + /* During the malloc another thread may have changed the bucket. + * Thus we need to check again. */ + for (ki = key_table[hash]; ki; ki = ki->next) + if (ki->fprlen == fprlen && !memcmp (ki->fpr, fpr, fprlen)) + return ki; /* Found */ + } + + /* We now know that there is an item in the attic. */ + ki = key_item_attic; + key_item_attic = ki->next; + ki->next = NULL; + + memcpy (ki->fpr, fpr, fprlen); + ki->fprlen = fprlen; + ki->keyid[0] = keyid[0]; + ki->keyid[1] = keyid[1]; + ki->ui = uid_item_ref (ui); + ki->usecount = 0; + ki->next = key_table[hash]; + key_table[hash] = ki; + key_table_added++; + return ki; +} + + + +/* Return the user ID from the given keyblock. We use the primary uid + * flag which should have already been set. The returned value is + * only valid as long as the given keyblock is not changed. */ +static const char * +primary_uid_from_keyblock (kbnode_t keyblock, size_t *uidlen) +{ + kbnode_t k; + + for (k = keyblock; k; k = k->next) + { + if (k->pkt->pkttype == PKT_USER_ID + && !k->pkt->pkt.user_id->attrib_data + && k->pkt->pkt.user_id->flags.primary) + { + *uidlen = k->pkt->pkt.user_id->len; + return k->pkt->pkt.user_id->name; + } + } + return NULL; +} + + +/* Store the associations of keyid/fingerprint and userid. Only + * public keys should be fed to this function. */ +void +cache_put_keyblock (kbnode_t keyblock) +{ + uid_item_t ui = NULL; + kbnode_t k; + + restart: + for (k = keyblock; k; k = k->next) + { + if (k->pkt->pkttype == PKT_PUBLIC_KEY + || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + if (!ui) + { + /* Initially we just test for an entry to avoid the need + * to create a user id item for a put. Only if we miss + * key in the cache we create a user id and restart. */ + if (!key_table_get (k->pkt->pkt.public_key, NULL)) + { + const char *uid; + size_t uidlen; + + uid = primary_uid_from_keyblock (keyblock, &uidlen); + if (uid) + { + ui = uid_table_put (uid, uidlen); + if (!ui) + { + log_info ("Note: failed to cache a user id: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + goto leave; + } + goto restart; + } + } + } + else /* With a UID we use the update cache mode. */ + { + if (!key_table_put (k->pkt->pkt.public_key, ui)) + { + log_info ("Note: failed to cache a key: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + goto leave; + } + } + } + } + + leave: + uid_item_unref (ui); +} + + +/* Return the user id string for KEYID. If a user id is not found (or + * on malloc error) NULL is returned. If R_LENGTH is not NULL the + * length of the user id is stored there; this does not included the + * always appended nul. Note that a user id may include an internal + * nul which can be detected by the caller by comparing to the + * returned length. */ +char * +cache_get_uid_bykid (u32 *keyid, unsigned int *r_length) +{ + key_item_t ki; + char *p; + + if (r_length) + *r_length = 0; + + ki = key_table_get (NULL, keyid); + if (!ki) + return NULL; /* Not found or duplicate keyid. */ + + if (!ki->ui) + p = NULL; /* No user id known for key. */ + else + { + p = xtrymalloc (ki->ui->namelen + 1); + if (p) + { + memcpy (p, ki->ui->name, ki->ui->namelen + 1); + if (r_length) + *r_length = ki->ui->namelen; + ki->usecount++; + } + } + + return p; +} + + +/* Return the user id string for FPR with FPRLEN. If a user id is not + * found (or on malloc error) NULL is returned. If R_LENGTH is not + * NULL the length of the user id is stored there; this does not + * included the always appended nul. Note that a user id may include + * an internal nul which can be detected by the caller by comparing to + * the returned length. */ +char * +cache_get_uid_byfpr (const byte *fpr, size_t fprlen, size_t *r_length) +{ + char *p; + unsigned int hash; + u32 keyid[2]; + key_item_t ki; + + if (r_length) + *r_length = 0; + + if (!key_table) + return NULL; + + keyid_from_fingerprint (NULL, fpr, fprlen, keyid); + hash = key_table_hasher (keyid); + for (ki = key_table[hash]; ki; ki = ki->next) + if (ki->fprlen == fprlen && !memcmp (ki->fpr, fpr, fprlen)) + break; /* Found */ + + if (!ki) + return NULL; /* Not found. */ + + if (!ki->ui) + p = NULL; /* No user id known for key. */ + else + { + p = xtrymalloc (ki->ui->namelen + 1); + if (p) + { + memcpy (p, ki->ui->name, ki->ui->namelen + 1); + if (r_length) + *r_length = ki->ui->namelen; + ki->usecount++; + } + } + + return p; +} diff --git a/g10/objcache.h b/g10/objcache.h new file mode 100644 index 000000000..edf129525 --- /dev/null +++ b/g10/objcache.h @@ -0,0 +1,29 @@ +/* objcache.h - Caching functions for keys and user ids. + * Copyright (C) 2019 g10 Code GmbH + * + * 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 3 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, see . + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef GNUPG_G10_OBJCACHE_H +#define GNUPG_G10_OBJCACHE_H + +void objcache_dump_stats (void); +void cache_put_keyblock (kbnode_t keyblock); +char *cache_get_uid_bykid (u32 *keyid, unsigned int *r_length); +char *cache_get_uid_byfpr (const byte *fpr, size_t fprlen, size_t *r_length); + +#endif /*GNUPG_G10_OBJCACHE_H*/ diff --git a/g10/options.h b/g10/options.h index 8adf09f08..234929b15 100644 --- a/g10/options.h +++ b/g10/options.h @@ -249,6 +249,8 @@ struct unsigned int disable_signer_uid:1; /* Flag to enable experimental features from RFC4880bis. */ unsigned int rfc4880bis:1; + /* Hack: --output is not given but OUTFILE was temporary set to "-". */ + unsigned int dummy_outfile:1; } flags; /* Linked list of ways to find a key if the key isn't on the local @@ -361,6 +363,7 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define IMPORT_REPAIR_KEYS (1<<11) #define IMPORT_DRY_RUN (1<<12) #define IMPORT_DROP_UIDS (1<<13) +#define IMPORT_SELF_SIGS_ONLY (1<<14) #define EXPORT_LOCAL_SIGS (1<<0) #define EXPORT_ATTRIBUTES (1<<1) diff --git a/g10/packet.h b/g10/packet.h index 41dd1a95a..479f25044 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -33,6 +33,11 @@ #define DEBUG_PARSE_PACKET 1 +/* Maximum length of packets to avoid excessive memory allocation. */ +#define MAX_KEY_PACKET_LENGTH (256 * 1024) +#define MAX_UID_PACKET_LENGTH ( 2 * 1024) +#define MAX_COMMENT_PACKET_LENGTH ( 64 * 1024) +#define MAX_ATTR_PACKET_LENGTH ( 16 * 1024*1024) /* Constants to allocate static MPI arrays. */ #define PUBKEY_MAX_NPKEY OPENPGP_MAX_NPKEY @@ -394,6 +399,7 @@ typedef struct byte pubkey_algo; byte pubkey_usage; /* for now only used to pass it to getkey() */ byte req_usage; /* hack to pass a request to getkey() */ + byte fprlen; /* 0 or length of FPR. */ u32 has_expired; /* set to the expiration date if expired */ /* keyid of the primary key. Never access this value directly. Instead, use pk_main_keyid(). */ @@ -401,6 +407,8 @@ typedef struct /* keyid of this key. Never access this value directly! Instead, use pk_keyid(). */ u32 keyid[2]; + /* Fingerprint of the key. Only valid if FPRLEN is not 0. */ + byte fpr[MAX_FINGERPRINT_LEN]; prefitem_t *prefs; /* list of preferences (may be NULL) */ struct { @@ -928,7 +936,7 @@ int ask_for_detached_datafile( gcry_md_hd_t md, gcry_md_hd_t md2, int make_keysig_packet (ctrl_t ctrl, PKT_signature **ret_sig, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *subpk, - PKT_public_key *pksk, int sigclass, int digest_algo, + PKT_public_key *pksk, int sigclass, u32 timestamp, u32 duration, int (*mksubpkt)(PKT_signature *, void *), void *opaque, diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 5b4b1c900..ab82d475a 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -35,14 +35,9 @@ #include "main.h" #include "../common/i18n.h" #include "../common/host2net.h" +#include "../common/mbox-util.h" -/* Maximum length of packets to avoid excessive memory allocation. */ -#define MAX_KEY_PACKET_LENGTH (256 * 1024) -#define MAX_UID_PACKET_LENGTH ( 2 * 1024) -#define MAX_COMMENT_PACKET_LENGTH ( 64 * 1024) -#define MAX_ATTR_PACKET_LENGTH ( 16 * 1024*1024) - static int mpi_print_mode; static int list_mode; static estream_t listfp; @@ -2118,12 +2113,20 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIGNERS_UID, &len); if (p && len) { + char *mbox; + sig->signers_uid = try_make_printable_string (p, len, 0); if (!sig->signers_uid) { rc = gpg_error_from_syserror (); goto leave; } + mbox = mailbox_from_userid (sig->signers_uid, 0); + if (mbox) + { + xfree (sig->signers_uid); + sig->signers_uid = mbox; + } } p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_NOTATION, NULL); diff --git a/g10/photoid.c b/g10/photoid.c index bcea64fbf..f9720d329 100644 --- a/g10/photoid.c +++ b/g10/photoid.c @@ -262,7 +262,8 @@ char *image_type_to_string(byte type,int style) } #if !defined(FIXED_PHOTO_VIEWER) && !defined(DISABLE_PHOTO_VIEWER) -static const char *get_default_photo_command(void) +static const char * +get_default_photo_command(void) { #if defined(_WIN32) OSVERSIONINFO osvi; @@ -274,14 +275,21 @@ static const char *get_default_photo_command(void) if(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS) return "start /w %i"; else - return "cmd /c start /w %i"; + return "!ShellExecute 400 %i"; #elif defined(__APPLE__) /* OS X. This really needs more than just __APPLE__. */ return "open %I"; #elif defined(__riscos__) return "Filer_Run %I"; #else - return "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin"; + if (!path_access ("xloadimage", X_OK)) + return "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin"; + else if (!path_access ("display",X_OK)) + return "display -title 'KeyID 0x%k' %i"; + else if (getuid () && !path_access ("xdg-open", X_OK)) + return "xdg-open %i"; + else + return "/bin/true"; #endif } #endif @@ -312,6 +320,8 @@ show_photos (ctrl_t ctrl, const struct user_attribute *attrs, int count, if (pk) keyid_from_pk (pk, kid); + es_fflush (es_stdout); + for(i=0;ipk = xmalloc_clear (sizeof *r->pk); r->pk->req_usage = PUBKEY_USAGE_ENC; - rc = get_pubkey_byname (ctrl, NULL, r->pk, default_key, - NULL, NULL, 0, 1); + rc = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, r->pk, default_key, NULL, NULL, 0); if (rc) { xfree (r->pk); @@ -1041,8 +1042,8 @@ build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list) /* We explicitly allow encrypt-to to an disabled key; thus we pass 1 for the second last argument and 1 as the last argument to disable AKL. */ - if ( (rc = get_pubkey_byname (ctrl, - NULL, pk, rov->d, NULL, NULL, 1, 1)) ) + if ((rc = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, pk, rov->d, NULL, NULL, 1))) { free_public_key ( pk ); pk = NULL; log_error (_("%s: skipped: %s\n"), rov->d, gpg_strerror (rc) ); @@ -1179,7 +1180,8 @@ build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list) free_public_key (pk); pk = xmalloc_clear( sizeof *pk ); pk->req_usage = PUBKEY_USAGE_ENC; - rc = get_pubkey_byname (ctrl, NULL, pk, answer, NULL, NULL, 0, 0 ); + rc = get_pubkey_byname (ctrl, GET_PUBKEY_NORMAL, + NULL, pk, answer, NULL, NULL, 0); if (rc) tty_printf(_("No such user ID.\n")); else if ( !(rc=openpgp_pk_test_algo2 (pk->pubkey_algo, @@ -1257,7 +1259,8 @@ build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list) /* The default recipient is allowed to be disabled; thus pass 1 as second last argument. We also don't want an AKL. */ - rc = get_pubkey_byname (ctrl, NULL, pk, def_rec, NULL, NULL, 1, 1); + rc = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, pk, def_rec, NULL, NULL, 1); if (rc) log_error(_("unknown default recipient \"%s\"\n"), def_rec ); else if ( !(rc=openpgp_pk_test_algo2(pk->pubkey_algo, diff --git a/g10/plaintext.c b/g10/plaintext.c index c5d1ddb7f..f9e0a4296 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -70,7 +70,8 @@ get_output_file (const byte *embedded_name, int embedded_namelen, goto leave; } } - else if (opt.outfile) + else if (opt.outfile + && !(opt.flags.use_embedded_filename && opt.flags.dummy_outfile)) { fname = xtrystrdup (opt.outfile); if (!fname) diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 055c39b8f..fb1b17143 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -75,25 +75,21 @@ gpg_error_t get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek) { PKT_public_key *sk = NULL; - int rc; + gpg_error_t err; void *enum_context = NULL; u32 keyid[2]; int search_for_secret_keys = 1; + struct pubkey_enc_list *k; if (DBG_CLOCK) log_clock ("get_session_key enter"); while (search_for_secret_keys) { - struct pubkey_enc_list *k; - sk = xmalloc_clear (sizeof *sk); - rc = enum_secret_keys (ctrl, &enum_context, sk); - if (rc) - { - rc = GPG_ERR_NO_SECKEY; - break; - } + err = enum_secret_keys (ctrl, &enum_context, sk); + if (err) + break; if (!(sk->pubkey_usage & PUBKEY_USAGE_ENC)) continue; @@ -132,8 +128,6 @@ get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek) if (openpgp_pk_test_algo2 (k->pubkey_algo, PUBKEY_USAGE_ENC)) continue; - k->result = GPG_ERR_NO_SECKEY; - if (sk->pubkey_algo != k->pubkey_algo) continue; @@ -154,16 +148,16 @@ get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek) else continue; - rc = get_it (ctrl, k, dek, sk, keyid); - if (!rc) + err = get_it (ctrl, k, dek, sk, keyid); + k->result = err; + if (!err) { - k->result = 0; if (!opt.quiet && !k->keyid[0] && !k->keyid[1]) log_info (_("okay, we are the anonymous recipient.\n")); search_for_secret_keys = 0; break; } - else if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) + else if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) { search_for_secret_keys = 0; break; /* Don't try any more secret keys. */ @@ -172,9 +166,19 @@ get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek) } enum_secret_keys (ctrl, &enum_context, NULL); /* free context */ + if (gpg_err_code (err) == GPG_ERR_EOF) + { + err = gpg_error (GPG_ERR_NO_SECKEY); + + /* Return the last specific error, if any. */ + for (k = list; k; k = k->next) + if (k->result != -1) + err = k->result; + } + if (DBG_CLOCK) log_clock ("get_session_key leave"); - return rc; + return err; } @@ -319,6 +323,16 @@ get_it (ctrl_t ctrl, err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } + + /* FIXME: Actually the leading zero is required but due to + * the way we encode the output in libgcrypt as an MPI we + * are not able to encode that leading zero. However, when + * using a Smartcard we are doing it the right way and + * therefore we have to skip the zero. This should be fixed + * in gpg-agent of course. */ + if (!frame[n]) + n++; + if (frame[n] == 1 && frame[nframe - 1] == 2) { log_info (_("old encoding of the DEK is not supported\n")); diff --git a/g10/revoke.c b/g10/revoke.c index e8ce3544c..0a93c31f9 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -305,11 +305,11 @@ gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr) any = 1; - print_pubkey_info (ctrl, NULL, pk); + print_key_info (ctrl, NULL, 0, pk, 0); tty_printf ("\n"); tty_printf (_("To be revoked by:\n")); - print_seckey_info (ctrl, pk2); + print_key_info (ctrl, NULL, 0, pk2, 1); if(pk->revkey[i].class&0x40) tty_printf(_("(This is a sensitive revocation key)\n")); @@ -343,7 +343,7 @@ gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr) push_armor_filter (afx, out); /* create it */ - rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk2, 0x20, 0, + rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk2, 0x20, 0, 0, revocation_reason_build_cb, reason, NULL); @@ -474,7 +474,7 @@ create_revocation (ctrl_t ctrl, afx->hdrlines = "Comment: This is a revocation certificate\n"; push_armor_filter (afx, out); - rc = make_keysig_packet (ctrl, &sig, psk, NULL, NULL, psk, 0x20, 0, + rc = make_keysig_packet (ctrl, &sig, psk, NULL, NULL, psk, 0x20, 0, 0, revocation_reason_build_cb, reason, cache_nonce); if (rc) @@ -669,30 +669,26 @@ gen_revoke (ctrl_t ctrl, const char *uname) rc = keydb_search (kdbhd, &desc, 1, NULL); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) - /* Not ambiguous. */ { + /* Not ambiguous. */ } else if (rc == 0) - /* Ambiguous. */ { - char *info; - + /* Ambiguous. */ /* TRANSLATORS: The %s prints a key specification which for example has been given at the command line. Several lines lines with secret key infos are printed after this message. */ log_error (_("'%s' matches multiple secret keys:\n"), uname); - info = format_seckey_info (ctrl, keyblock->pkt->pkt.public_key); - log_error (" %s\n", info); - xfree (info); + print_key_info_log (ctrl, GPGRT_LOGLVL_ERROR, 2, + keyblock->pkt->pkt.public_key, 1); release_kbnode (keyblock); rc = keydb_get_keyblock (kdbhd, &keyblock); while (! rc) { - info = format_seckey_info (ctrl, keyblock->pkt->pkt.public_key); - log_info (" %s\n", info); - xfree (info); + print_key_info_log (ctrl, GPGRT_LOGLVL_INFO, 2, + keyblock->pkt->pkt.public_key, 1); release_kbnode (keyblock); keyblock = NULL; @@ -726,7 +722,7 @@ gen_revoke (ctrl_t ctrl, const char *uname) } keyid_from_pk (psk, keyid ); - print_seckey_info (ctrl, psk); + print_key_info (ctrl, NULL, 0, psk, 1); tty_printf("\n"); if (!cpr_get_answer_is_yes ("gen_revoke.okay", diff --git a/g10/sig-check.c b/g10/sig-check.c index e7f97de65..4c172d692 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -1076,7 +1076,7 @@ check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer, * signature packet's data structure. * * TODO: add r_revoked here as well. It has the same problems as - * r_expiredate and r_expired and the cache. */ + * r_expiredate and r_expired and the cache [nw]. Which problems [wk]? */ int check_key_signature2 (ctrl_t ctrl, kbnode_t root, kbnode_t node, PKT_public_key *check_pk, diff --git a/g10/sign.c b/g10/sign.c index 176940bff..d71580639 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1593,7 +1593,7 @@ make_keysig_packet (ctrl_t ctrl, PKT_signature **ret_sig, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *subpk, PKT_public_key *pksk, - int sigclass, int digest_algo, + int sigclass, u32 timestamp, u32 duration, int (*mksubpkt)(PKT_signature *, void *), void *opaque, const char *cache_nonce) @@ -1601,6 +1601,7 @@ make_keysig_packet (ctrl_t ctrl, PKT_signature *sig; int rc = 0; int sigversion; + int digest_algo; gcry_md_hd_t md; log_assert ((sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F @@ -1612,31 +1613,22 @@ make_keysig_packet (ctrl_t ctrl, else sigversion = 4; - if (!digest_algo) + /* Select the digest algo to use. */ + if (opt.cert_digest_algo) /* Forceful override by the user. */ + digest_algo = opt.cert_digest_algo; + else if (pksk->pubkey_algo == PUBKEY_ALGO_DSA) /* Meet DSA requirements. */ + digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8); + else if (pksk->pubkey_algo == PUBKEY_ALGO_ECDSA /* Meet ECDSA requirements. */ + || pksk->pubkey_algo == PUBKEY_ALGO_EDDSA) { - /* Basically, this means use SHA1 always unless the user - * specified something (use whatever they said), or it's DSA - * (use the best match). They still can't pick an inappropriate - * hash for DSA or the signature will fail. Note that this - * still allows the caller of make_keysig_packet to override the - * user setting if it must. */ - - if (opt.cert_digest_algo) - digest_algo = opt.cert_digest_algo; - else if (pksk->pubkey_algo == PUBKEY_ALGO_DSA) - digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8); - else if (pksk->pubkey_algo == PUBKEY_ALGO_ECDSA - || pksk->pubkey_algo == PUBKEY_ALGO_EDDSA) - { - if (openpgp_oid_is_ed25519 (pksk->pkey[0])) - digest_algo = DIGEST_ALGO_SHA256; - else - digest_algo = match_dsa_hash - (ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8); - } + if (openpgp_oid_is_ed25519 (pksk->pkey[0])) + digest_algo = DIGEST_ALGO_SHA256; else - digest_algo = DEFAULT_DIGEST_ALGO; + digest_algo = match_dsa_hash + (ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8); } + else /* Use the default. */ + digest_algo = DEFAULT_DIGEST_ALGO; if (gcry_md_open (&md, digest_algo, 0)) BUG (); @@ -1722,8 +1714,19 @@ update_keysig_packet (ctrl_t ctrl, || (orig_sig->sig_class == 0x18 && !subpk)) return GPG_ERR_GENERAL; + /* Either use the override digest algo or in the normal case the + * original digest algorithm. However, iff the original digest + * algorithms is SHA-1 and we are in gnupg or de-vs compliance mode + * we switch to SHA-256 (done by the macro). */ if (opt.cert_digest_algo) digest_algo = opt.cert_digest_algo; + else if (pksk->pubkey_algo == PUBKEY_ALGO_DSA + || pksk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pksk->pubkey_algo == PUBKEY_ALGO_EDDSA) + digest_algo = orig_sig->digest_algo; + else if (orig_sig->digest_algo == DIGEST_ALGO_SHA1 + || orig_sig->digest_algo == DIGEST_ALGO_RMD160) + digest_algo = DEFAULT_DIGEST_ALGO; else digest_algo = orig_sig->digest_algo; diff --git a/g10/skclist.c b/g10/skclist.c index c9c41d0d9..c13566e2b 100644 --- a/g10/skclist.c +++ b/g10/skclist.c @@ -340,6 +340,10 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) SK_LIST results; } *c = *context; +#if MAX_FINGERPRINT_LEN < KEYGRIP_LEN +# error buffer too short for this configuration +#endif + if (!c) { /* Make a new context. */ @@ -423,23 +427,58 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) if (opt.verbose) log_info (_("error getting serial number of card: %s\n"), gpg_strerror (err)); + c->sl = c->sl->next; continue; } xfree (serialno); c->info.fpr2len = 0; err = agent_scd_getattr ("KEY-FPR", &c->info); + if (!err) + { + if (c->info.fpr2len) + { + c->fpr2[0] = '0'; + c->fpr2[1] = 'x'; + bin2hex (c->info.fpr2, sizeof c->info.fpr2, + c->fpr2 + 2); + name = c->fpr2; + } + } + else if (gpg_err_code (err) == GPG_ERR_INV_NAME) + { + /* KEY-FPR not supported by the card - get + * the key using the keygrip. */ + char *keyref; + strlist_t kplist; + const char *s; + int i; + + err = agent_scd_getattr_one ("$ENCRKEYID", &keyref); + if (!err) + { + err = agent_scd_keypairinfo (ctrl, keyref, + &kplist); + if (!err) + { + c->fpr2[0] = '&'; + for (i=1, s=kplist->d; + (*s && *s != ' ' + && i < sizeof c->fpr2 - 3); + s++, i++) + c->fpr2[i] = *s; + c->fpr2[i] = 0; + name = c->fpr2; + free_strlist (kplist); + } + xfree (keyref); + } + } + if (err) - log_error ("error retrieving key fingerprint from card: %s\n", + log_error ("error retrieving key from card: %s\n", gpg_strerror (err)); - if (c->info.fpr2len) - { - c->fpr2[0] = '0'; - c->fpr2[1] = 'x'; - bin2hex (c->info.fpr2, sizeof c->info.fpr2,c->fpr2+2); - name = c->fpr2; - } c->sl = c->sl->next; } else diff --git a/g10/tofu.c b/g10/tofu.c index 44f354512..e78da15c1 100644 --- a/g10/tofu.c +++ b/g10/tofu.c @@ -534,7 +534,7 @@ check_utks (sqlite3 *db) NULL, NULL, &err); if (rc) { - log_error (_("error creating 'ultimately_trusted_keys' TOFU table: %s\n"), + log_error ("error creating 'ultimately_trusted_keys' TOFU table: %s\n", err); sqlite3_free (err); goto out; @@ -840,7 +840,7 @@ initdb (sqlite3 *db) NULL, NULL, &err); if (rc) { - log_error (_("error creating 'encryptions' TOFU table: %s\n"), + log_error ("error creating 'encryptions' TOFU table: %s\n", err); sqlite3_free (err); } @@ -870,7 +870,7 @@ initdb (sqlite3 *db) * safely ignore. */ rc = 0; else - log_error (_("adding column effective_policy to bindings DB: %s\n"), + log_error ("adding column effective_policy to bindings DB: %s\n", err); sqlite3_free (err); } @@ -2146,8 +2146,7 @@ build_conflict_set (ctrl_t ctrl, tofu_dbs_t dbs, rc = keydb_search_reset (hd); if (rc) { - log_error (_("resetting keydb: %s\n"), - gpg_strerror (rc)); + log_error ("resetting keydb failed: %s\n", gpg_strerror (rc)); continue; } @@ -2614,8 +2613,8 @@ get_policy (ctrl_t ctrl, tofu_dbs_t dbs, PKT_public_key *pk, if (record_binding (dbs, fingerprint, email, user_id, policy == TOFU_POLICY_NONE ? TOFU_POLICY_AUTO : policy, effective_policy, conflict, 1, 0, now) != 0) - log_error (_("error setting TOFU binding's policy" - " to %s\n"), tofu_policy_str (policy)); + log_error ("error setting TOFU binding's policy" + " to %s\n", tofu_policy_str (policy)); } /* If the caller wants the set of conflicts, return it. */ @@ -3152,14 +3151,10 @@ show_statistics (tofu_dbs_t dbs, es_fprintf (fp, _("%s: Verified 0 signatures."), email); else { - /* TRANSLATORS: The final %s is replaced by a string like - "7~months". */ + /* Note: Translation not possible with that wording. */ char *ago_str = time_ago_str (now - signature_first_seen); es_fprintf - (fp, - ngettext("%s: Verified %ld~signature in the past %s.", - "%s: Verified %ld~signatures in the past %s.", - signature_count), + (fp, "%s: Verified %ld~signatures in the past %s.", email, signature_count, ago_str); xfree (ago_str); } @@ -3172,12 +3167,9 @@ show_statistics (tofu_dbs_t dbs, { char *ago_str = time_ago_str (now - encryption_first_done); - /* TRANSLATORS: The final %s is replaced by a string like - "7~months". */ - es_fprintf (fp, - ngettext("Encrypted %ld~message in the past %s.", - "Encrypted %ld~messages in the past %s.", - encryption_count), + /* Note: Translation not possible with this kind of + * composition. */ + es_fprintf (fp, "Encrypted %ld~messages in the past %s.", encryption_count, ago_str); xfree (ago_str); } @@ -3944,7 +3936,7 @@ tofu_set_policy (ctrl_t ctrl, kbnode_t kb, enum tofu_policy policy) policy, TOFU_POLICY_NONE, NULL, 0, 1, now); if (err) { - log_error (_("error setting policy for key %s, user id \"%s\": %s"), + log_error ("error setting policy for key %s, user id \"%s\": %s", fingerprint, email, gpg_strerror (err)); xfree (email); break; diff --git a/g13/server.c b/g13/server.c index defde6c02..780295214 100644 --- a/g13/server.c +++ b/g13/server.c @@ -530,7 +530,7 @@ cmd_getinfo (assuan_context_t ctx, char *line) { cmdopt = line; if (!command_has_option (cmd, cmdopt)) - err = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_FALSE); } } } diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c index 0bcd4a323..8ac67afbe 100644 --- a/kbx/keybox-blob.c +++ b/kbx/keybox-blob.c @@ -134,7 +134,7 @@ Note that this value matches TRUST_FLAG_REVOKED - u16 RFU - u32 Recheck_after - - u32 Latest timestamp in the keyblock (useful for KS syncronsiation?) + - u32 Latest timestamp in the keyblock (useful for KS synchronization?) - u32 Blob created at - u32 [NRES] Size of reserved space (not including this field) - bN Reserved space of size NRES for future use. @@ -144,7 +144,7 @@ - bN Space for the keyblock or certificate. - bN RFU. This is the remaining space after keyblock and before the checksum. It is not covered by the checksum. - - b20 SHA-1 checksum (useful for KS syncronisation?) + - b20 SHA-1 checksum (useful for KS synchronization?) Note, that KBX versions before GnuPG 2.1 used an MD5 checksum. However it was only created but never checked. Thus we do not expect problems if we switch to SHA-1. If diff --git a/kbx/keybox-init.c b/kbx/keybox-init.c index 6a83f7162..2223f4d15 100644 --- a/kbx/keybox-init.c +++ b/kbx/keybox-init.c @@ -261,10 +261,12 @@ _keybox_close_file (KEYBOX_HANDLE hd) /* - * Lock the keybox at handle HD, or unlock if YES is false. + * Lock the keybox at handle HD, or unlock if YES is false. TIMEOUT + * is the value used for dotlock_take. In general -1 should be used + * when taking a lock; use 0 when releasing a lock. */ gpg_error_t -keybox_lock (KEYBOX_HANDLE hd, int yes) +keybox_lock (KEYBOX_HANDLE hd, int yes, long timeout) { gpg_error_t err = 0; KB_NAME kb = hd->kb; @@ -289,23 +291,22 @@ keybox_lock (KEYBOX_HANDLE hd, int yes) if (!kb->is_locked) { #ifdef HAVE_W32_SYSTEM - /* Under Windows we need to close the file before we try - * to lock it. This is because another process might have - * taken the lock and is using keybox_file_rename to - * rename the base file. How if our dotlock_take below is - * waiting for the lock but we have the base file still - * open, keybox_file_rename will never succeed as we are - * in a deadlock. */ - if (hd->fp) - { - fclose (hd->fp); - hd->fp = NULL; - } + /* Under Windows we need to close the file before we try + * to lock it. This is because another process might have + * taken the lock and is using keybox_file_rename to + * rename the base file. Now if our dotlock_take below is + * waiting for the lock but we have the base file still + * open, keybox_file_rename will never succeed as we are + * in a deadlock. */ + _keybox_close_file (hd); #endif /*HAVE_W32_SYSTEM*/ - if (dotlock_take (kb->lockhd, -1)) + if (dotlock_take (kb->lockhd, timeout)) { err = gpg_error_from_syserror (); - log_info ("can't lock '%s'\n", kb->fname ); + if (!timeout && gpg_err_code (err) == GPG_ERR_EACCES) + ; /* No diagnostic if we only tried to lock. */ + else + log_info ("can't lock '%s'\n", kb->fname ); } else kb->is_locked = 1; diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c index 101e1b5ea..77469a24c 100644 --- a/kbx/keybox-search.c +++ b/kbx/keybox-search.c @@ -873,16 +873,21 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc, KEYBOXBLOB blob = NULL; struct sn_array_s *sn_array = NULL; int pk_no, uid_no; + off_t lastfoundoff; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); - /* clear last found result */ + /* Clear last found result but reord the offset of the last found + * blob which we may need later. */ if (hd->found.blob) { + lastfoundoff = _keybox_get_blob_fileoffset (hd->found.blob); _keybox_release_blob (hd->found.blob); hd->found.blob = NULL; } + else + lastfoundoff = 0; if (hd->error) return hd->error; /* still in error state */ @@ -901,6 +906,7 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc, case KEYDB_SEARCH_MODE_FIRST: /* always restart the search in this mode */ keybox_search_reset (hd); + lastfoundoff = 0; break; default: break; @@ -925,6 +931,32 @@ keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc, xfree (sn_array); return rc; } + /* log_debug ("%s: re-opened file\n", __func__); */ + if (ndesc && desc[0].mode != KEYDB_SEARCH_MODE_FIRST && lastfoundoff) + { + /* Search mode is not first and the last search operation + * returned a blob which also was not the first one. We now + * need to skip over that blob and hope that the file has + * not changed. */ + if (fseeko (hd->fp, lastfoundoff, SEEK_SET)) + { + rc = gpg_error_from_syserror (); + log_debug ("%s: seeking to last found offset failed: %s\n", + __func__, gpg_strerror (rc)); + xfree (sn_array); + return gpg_error (GPG_ERR_NOTHING_FOUND); + } + /* log_debug ("%s: re-opened file and sought to last offset\n", */ + /* __func__); */ + rc = _keybox_read_blob (NULL, hd->fp, NULL); + if (rc) + { + log_debug ("%s: skipping last found blob failed: %s\n", + __func__, gpg_strerror (rc)); + xfree (sn_array); + return gpg_error (GPG_ERR_NOTHING_FOUND); + } + } } /* Kludge: We need to convert an SN given as hexstring to its binary diff --git a/kbx/keybox-update.c b/kbx/keybox-update.c index 580330f52..e09fefc41 100644 --- a/kbx/keybox-update.c +++ b/kbx/keybox-update.c @@ -423,7 +423,7 @@ keybox_update_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen) if (off == (off_t)-1) return gpg_error (GPG_ERR_GENERAL); - /* Close this the file so that we do no mess up the position for a + /* Close the file so that we do no mess up the position for a next search. */ _keybox_close_file (hd); diff --git a/kbx/keybox.h b/kbx/keybox.h index 665b05fc0..4d941571e 100644 --- a/kbx/keybox.h +++ b/kbx/keybox.h @@ -76,7 +76,7 @@ void keybox_pop_found_state (KEYBOX_HANDLE hd); const char *keybox_get_resource_name (KEYBOX_HANDLE hd); int keybox_set_ephemeral (KEYBOX_HANDLE hd, int yes); -gpg_error_t keybox_lock (KEYBOX_HANDLE hd, int yes); +gpg_error_t keybox_lock (KEYBOX_HANDLE hd, int yes, long timeout); /*-- keybox-file.c --*/ /* Fixme: This function does not belong here: Provide a better diff --git a/m4/iconv.m4 b/m4/iconv.m4 index 66bc76f48..a285e9daa 100644 --- a/m4/iconv.m4 +++ b/m4/iconv.m4 @@ -1,5 +1,6 @@ -# iconv.m4 serial AM6 (gettext-0.17) -dnl Copyright (C) 2000-2002, 2007 Free Software Foundation, Inc. +# iconv.m4 serial 21 +dnl Copyright (C) 2000-2002, 2007-2014, 2016-2019 Free Software Foundation, +dnl Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. @@ -30,61 +31,118 @@ AC_DEFUN([AM_ICONV_LINK], dnl Add $INCICONV to CPPFLAGS before performing the following checks, dnl because if the user has installed libiconv and not disabled its use dnl via --without-libiconv-prefix, he wants to use it. The first - dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed. + dnl AC_LINK_IFELSE will then fail, the second AC_LINK_IFELSE will succeed. am_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) - AC_CACHE_CHECK([for iconv], am_cv_func_iconv, [ + AC_CACHE_CHECK([for iconv], [am_cv_func_iconv], [ am_cv_func_iconv="no, consider installing GNU libiconv" am_cv_lib_iconv=no - AC_TRY_LINK([#include -#include ], - [iconv_t cd = iconv_open("",""); - iconv(cd,NULL,NULL,NULL,NULL); - iconv_close(cd);], - am_cv_func_iconv=yes) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include + ]], + [[iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);]])], + [am_cv_func_iconv=yes]) if test "$am_cv_func_iconv" != yes; then am_save_LIBS="$LIBS" LIBS="$LIBS $LIBICONV" - AC_TRY_LINK([#include -#include ], - [iconv_t cd = iconv_open("",""); - iconv(cd,NULL,NULL,NULL,NULL); - iconv_close(cd);], - am_cv_lib_iconv=yes - am_cv_func_iconv=yes) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include + ]], + [[iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);]])], + [am_cv_lib_iconv=yes] + [am_cv_func_iconv=yes]) LIBS="$am_save_LIBS" fi ]) if test "$am_cv_func_iconv" = yes; then - AC_CACHE_CHECK([for working iconv], am_cv_func_iconv_works, [ - dnl This tests against bugs in AIX 5.1 and HP-UX 11.11. + AC_CACHE_CHECK([for working iconv], [am_cv_func_iconv_works], [ + dnl This tests against bugs in AIX 5.1, AIX 6.1..7.1, HP-UX 11.11, + dnl Solaris 10. am_save_LIBS="$LIBS" if test $am_cv_lib_iconv = yes; then LIBS="$LIBS $LIBICONV" fi - AC_TRY_RUN([ + am_cv_func_iconv_works=no + for ac_iconv_const in '' 'const'; do + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[ #include #include -int main () -{ + +#ifndef ICONV_CONST +# define ICONV_CONST $ac_iconv_const +#endif + ]], + [[int result = 0; /* Test against AIX 5.1 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); if (cd_utf8_to_88591 != (iconv_t)(-1)) { - static const char input[] = "\342\202\254"; /* EURO SIGN */ + static ICONV_CONST char input[] = "\342\202\254"; /* EURO SIGN */ char buf[10]; - const char *inptr = input; + ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_utf8_to_88591, - (char **) &inptr, &inbytesleft, + &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) - return 1; + result |= 1; + iconv_close (cd_utf8_to_88591); + } + } + /* Test against Solaris 10 bug: Failures are not distinguishable from + successful returns. */ + { + iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646"); + if (cd_ascii_to_88591 != (iconv_t)(-1)) + { + static ICONV_CONST char input[] = "\263"; + char buf[10]; + ICONV_CONST char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_ascii_to_88591, + &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res == 0) + result |= 2; + iconv_close (cd_ascii_to_88591); + } + } + /* Test against AIX 6.1..7.1 bug: Buffer overrun. */ + { + iconv_t cd_88591_to_utf8 = iconv_open ("UTF-8", "ISO-8859-1"); + if (cd_88591_to_utf8 != (iconv_t)(-1)) + { + static ICONV_CONST char input[] = "\304"; + static char buf[2] = { (char)0xDE, (char)0xAD }; + ICONV_CONST char *inptr = input; + size_t inbytesleft = 1; + char *outptr = buf; + size_t outbytesleft = 1; + size_t res = iconv (cd_88591_to_utf8, + &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res != (size_t)(-1) || outptr - buf > 1 || buf[1] != (char)0xAD) + result |= 4; + iconv_close (cd_88591_to_utf8); } } #if 0 /* This bug could be worked around by the caller. */ @@ -93,37 +151,53 @@ int main () iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); if (cd_88591_to_utf8 != (iconv_t)(-1)) { - static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; + static ICONV_CONST char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; char buf[50]; - const char *inptr = input; + ICONV_CONST char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_88591_to_utf8, - (char **) &inptr, &inbytesleft, + &inptr, &inbytesleft, &outptr, &outbytesleft); if ((int)res > 0) - return 1; + result |= 8; + iconv_close (cd_88591_to_utf8); } } #endif /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is provided. */ - if (/* Try standardized names. */ - iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) - /* Try IRIX, OSF/1 names. */ - && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) - /* Try AIX names. */ - && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) - /* Try HP-UX names. */ - && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) - return 1; - return 0; -}], [am_cv_func_iconv_works=yes], [am_cv_func_iconv_works=no], - [case "$host_os" in - aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; - *) am_cv_func_iconv_works="guessing yes" ;; - esac]) + { + /* Try standardized names. */ + iconv_t cd1 = iconv_open ("UTF-8", "EUC-JP"); + /* Try IRIX, OSF/1 names. */ + iconv_t cd2 = iconv_open ("UTF-8", "eucJP"); + /* Try AIX names. */ + iconv_t cd3 = iconv_open ("UTF-8", "IBM-eucJP"); + /* Try HP-UX names. */ + iconv_t cd4 = iconv_open ("utf8", "eucJP"); + if (cd1 == (iconv_t)(-1) && cd2 == (iconv_t)(-1) + && cd3 == (iconv_t)(-1) && cd4 == (iconv_t)(-1)) + result |= 16; + if (cd1 != (iconv_t)(-1)) + iconv_close (cd1); + if (cd2 != (iconv_t)(-1)) + iconv_close (cd2); + if (cd3 != (iconv_t)(-1)) + iconv_close (cd3); + if (cd4 != (iconv_t)(-1)) + iconv_close (cd4); + } + return result; +]])], + [am_cv_func_iconv_works=yes], , + [case "$host_os" in + aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; + *) am_cv_func_iconv_works="guessing yes" ;; + esac]) + test "$am_cv_func_iconv_works" = no || break + done LIBS="$am_save_LIBS" ]) case "$am_cv_func_iconv_works" in @@ -134,7 +208,7 @@ int main () am_func_iconv=no am_cv_lib_iconv=no fi if test "$am_func_iconv" = yes; then - AC_DEFINE(HAVE_ICONV, 1, + AC_DEFINE([HAVE_ICONV], [1], [Define if you have the iconv() function and it works.]) fi if test "$am_cv_lib_iconv" = yes; then @@ -147,34 +221,68 @@ int main () LIBICONV= LTLIBICONV= fi - AC_SUBST(LIBICONV) - AC_SUBST(LTLIBICONV) + AC_SUBST([LIBICONV]) + AC_SUBST([LTLIBICONV]) ]) -AC_DEFUN([AM_ICONV], +dnl Define AM_ICONV using AC_DEFUN_ONCE for Autoconf >= 2.64, in order to +dnl avoid warnings like +dnl "warning: AC_REQUIRE: `AM_ICONV' was expanded before it was required". +dnl This is tricky because of the way 'aclocal' is implemented: +dnl - It requires defining an auxiliary macro whose name ends in AC_DEFUN. +dnl Otherwise aclocal's initial scan pass would miss the macro definition. +dnl - It requires a line break inside the AC_DEFUN_ONCE and AC_DEFUN expansions. +dnl Otherwise aclocal would emit many "Use of uninitialized value $1" +dnl warnings. +m4_define([gl_iconv_AC_DEFUN], + m4_version_prereq([2.64], + [[AC_DEFUN_ONCE( + [$1], [$2])]], + [m4_ifdef([gl_00GNULIB], + [[AC_DEFUN_ONCE( + [$1], [$2])]], + [[AC_DEFUN( + [$1], [$2])]])])) +gl_iconv_AC_DEFUN([AM_ICONV], [ AM_ICONV_LINK if test "$am_cv_func_iconv" = yes; then AC_MSG_CHECKING([for iconv declaration]) - AC_CACHE_VAL(am_cv_proto_iconv, [ - AC_TRY_COMPILE([ + AC_CACHE_VAL([am_cv_proto_iconv], [ + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[ #include #include extern #ifdef __cplusplus "C" #endif -#if defined(__STDC__) || defined(__cplusplus) +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); #else size_t iconv(); #endif -], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const") + ]], + [[]])], + [am_cv_proto_iconv_arg1=""], + [am_cv_proto_iconv_arg1="const"]) am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` - AC_MSG_RESULT([$]{ac_t:- - }[$]am_cv_proto_iconv) - AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1, - [Define as const if the declaration of iconv() needs const.]) + AC_MSG_RESULT([ + $am_cv_proto_iconv]) + else + dnl When compiling GNU libiconv on a system that does not have iconv yet, + dnl pick the POSIX compliant declaration without 'const'. + am_cv_proto_iconv_arg1="" fi + AC_DEFINE_UNQUOTED([ICONV_CONST], [$am_cv_proto_iconv_arg1], + [Define as const if the declaration of iconv() needs const.]) + dnl Also substitute ICONV_CONST in the gnulib generated . + m4_ifdef([gl_ICONV_H_DEFAULTS], + [AC_REQUIRE([gl_ICONV_H_DEFAULTS]) + if test -n "$am_cv_proto_iconv_arg1"; then + ICONV_CONST="const" + fi + ]) ]) diff --git a/po/ja.po b/po/ja.po index d2b4c7e8c..ccca194bb 100644 --- a/po/ja.po +++ b/po/ja.po @@ -4,13 +4,13 @@ # IIDA Yosiaki , 1999, 2000, 2002, 2003, 2004. # Yoshihiro Kajiki , 1999. # Takashi P.KATOH, 2002. -# NIIBE Yutaka , 2013, 2014, 2015, 2016, 2017, 2018. +# NIIBE Yutaka , 2013, 2014, 2015, 2016, 2017, 2018, 2019. # msgid "" msgstr "" -"Project-Id-Version: gnupg 2.2.6\n" +"Project-Id-Version: gnupg 2.2.16\n" "Report-Msgid-Bugs-To: translations@gnupg.org\n" -"PO-Revision-Date: 2018-04-12 10:51+0900\n" +"PO-Revision-Date: 2019-06-27 08:29+0900\n" "Last-Translator: NIIBE Yutaka \n" "Language-Team: none\n" "Language: ja\n" @@ -23,12 +23,12 @@ msgstr "" msgid "failed to acquire the pinentry lock: %s\n" msgstr "pinentryのロックの獲得に失敗しました: %s\n" -#. TRANSLATORS: These are labels for buttons etc used in -#. Pinentries. An underscore indicates that the next letter -#. should be used as an accelerator. Double the underscore for -#. a literal one. The actual to be translated text starts after -#. the second vertical bar. Note that gpg-agent has been set to -#. utf-8 so that the strings are in the expected encoding. +#. TRANSLATORS: These are labels for buttons etc as used in +#. * Pinentries. In your translation copy the text before the +#. * second vertical bar verbatim; translate only the following +#. * text. An underscore indicates that the next letter should be +#. * used as an accelerator. Double the underscore to have +#. * pinentry display a literal underscore. msgid "|pinentry-label|_OK" msgstr "|pinentry-label|_OK" @@ -203,9 +203,11 @@ msgstr "PUK" msgid "Reset Code" msgstr "リセット・コード" -#, c-format -msgid "%s%%0A%%0AUse the reader's pinpad for input." -msgstr "%s%%0A%%0Aリーダーのピンパッドを入力に使ってください。" +msgid "Push ACK button on card/token." +msgstr "カード/トークンのACKボタンを押してください。" + +msgid "Use the reader's pinpad for input." +msgstr "リーダーのピンパッドを入力に使ってください。" msgid "Repeat this Reset Code" msgstr "このリセット・コードをもう一度入力してください" @@ -240,9 +242,6 @@ msgstr "一時ファイルの書き込みエラー: %s\n" msgid "Enter new passphrase" msgstr "新しいパスフレーズを入力してください" -msgid "Take this one anyway" -msgstr "それでもこれを使います" - #, c-format msgid "" "You have not entered a passphrase!%0AAn empty passphrase is not allowed." @@ -280,6 +279,9 @@ msgstr "" msgid "Warning: You have entered an insecure passphrase." msgstr "警告: 安全とは言えないパスフレーズが入力されました。" +msgid "Take this one anyway" +msgstr "それでもこれを使います" + #, c-format msgid "Please enter the passphrase to%0Aprotect your new key" msgstr "新しい鍵を保護するために、%0Aパスフレーズを入力してください。" @@ -577,7 +579,7 @@ msgid "error reading '%s', line %d: %s\n" msgstr "'%s'の読み込みエラー(行 %d): %s\n" msgid "error reading list of trusted root certificates\n" -msgstr "信用されたルート証明書のリストの読み込みエラ−\n" +msgstr "信用されたルート証明書のリストの読み込みエラー\n" #. TRANSLATORS: This prompt is shown by the Pinentry #. and has one special property: A "%%0A" is used by @@ -674,7 +676,7 @@ msgid "checking created signature failed: %s\n" msgstr "作成された署名の検査に失敗しました: %s\n" msgid "secret key parts are not available\n" -msgstr "秘密部分が得られません\n" +msgstr "秘密鍵部分が利用できません\n" #, c-format msgid "public key algorithm %d (%s) is not supported\n" @@ -729,7 +731,7 @@ msgid "can't connect to '%s': %s\n" msgstr "'%s'へ接続できません: %s\n" msgid "problem setting the gpg-agent options\n" -msgstr "gpg-agentオプションの設定の問題\n" +msgstr "gpg-agentのオプション設定の問題\n" #, c-format msgid "can't disable core dumps: %s\n" @@ -813,30 +815,30 @@ msgid "unknown debug flag '%s' ignored\n" msgstr "未知のdebugフラグ'%s'は無視されました\n" #, c-format -msgid "no running gpg-agent - starting '%s'\n" -msgstr "gpg-agentが実行されていません - '%s'を開始します\n" +msgid "waiting for the dirmngr to come up ... (%ds)\n" +msgstr "dirmngrの起動のため、%d秒待ちます\n" #, c-format msgid "waiting for the agent to come up ... (%ds)\n" msgstr "agentの起動のため、%d秒待ちます\n" -msgid "connection to agent established\n" +msgid "connection to the dirmngr established\n" +msgstr "dirmngrへの接続が確立しました\n" + +msgid "connection to the agent established\n" msgstr "エージェントへの接続が確立しました。\n" -msgid "connection to agent is in restricted mode\n" +#, c-format +msgid "no running gpg-agent - starting '%s'\n" +msgstr "gpg-agentが実行されていません - '%s'を開始します\n" + +msgid "connection to the agent is in restricted mode\n" msgstr "エージェントへの接続は制限モードです。\n" #, c-format -msgid "no running Dirmngr - starting '%s'\n" +msgid "no running dirmngr - starting '%s'\n" msgstr "dirmngrが動いていません - 開始します'%s'\n" -#, c-format -msgid "waiting for the dirmngr to come up ... (%ds)\n" -msgstr "dirmngrの起動のため、%d秒待ちます\n" - -msgid "connection to the dirmngr established\n" -msgstr "dirmngrへの接続が確立しました\n" - #. TRANSLATORS: Copy the prefix between the vertical bars #. verbatim. It will not be printed. msgid "|audit-log-result|Good" @@ -1128,7 +1130,7 @@ msgid "CRC error; %06lX - %06lX\n" msgstr "CRCエラー。%06lX - %06lX\n" msgid "premature eof (in trailer)\n" -msgstr "ファイル末尾が早すぎます (後尾部の中にあります)\n" +msgstr "ファイル終端が早すぎます (後尾部の中にあります)\n" msgid "error in trailer line\n" msgstr "後尾の行にエラーがあります\n" @@ -1205,6 +1207,10 @@ msgstr "注意: \"%s\"コマンドを使って再起動してください。\n" msgid "%s is not compliant with %s mode\n" msgstr "%sは%sモードに準拠しません\n" +#, c-format +msgid "problem with the agent: %s\n" +msgstr "エージェントに問題: %s\n" + #, c-format msgid "OpenPGP card not available: %s\n" msgstr "OpenPGPカードが利用できません: %s\n" @@ -1228,14 +1234,11 @@ msgstr "あなたの選択は? " msgid "[not set]" msgstr "[未設定]" -msgid "male" -msgstr "男" +msgid "Mr." +msgstr "Mr." -msgid "female" -msgstr "女" - -msgid "unspecified" -msgstr "無指定" +msgid "Mrs." +msgstr "Mrs" msgid "not forced" msgstr "強制なし" @@ -1288,8 +1291,8 @@ msgstr "エラー: 優先指定の文字列の長さが無効です。\n" msgid "Error: invalid characters in preference string.\n" msgstr "エラー: 優先指定の文字列に無効な文字があります。\n" -msgid "Sex ((M)ale, (F)emale or space): " -msgstr "性別 ((M)男、(F)女、または空白): " +msgid "Salutation (M = Mr., F = Mrs., or space): " +msgstr "敬称 (M = Mr., F = Mrs, あるいは空白): " msgid "Error: invalid response.\n" msgstr "エラー: 無効な応答。\n" @@ -1315,13 +1318,15 @@ msgid "Replace existing key? (y/N) " msgstr "既存の鍵を置き換えしますか? (y/N) " msgid "" -"Note: There is no guarantee that the card supports the requested size.\n" -" If the key generation does not succeed, please check the\n" -" documentation of your card to see what sizes are allowed.\n" +"Note: There is no guarantee that the card supports the requested\n" +" key type or size. If the key generation does not succeed,\n" +" please check the documentation of your card to see which\n" +" key types and sizes are supported.\n" msgstr "" -"注意: カードが要求された鍵長をサポートしているという保証はありません。\n" -" 鍵生成が成功しない場合、あなたのカードに関する技術文書を確認し、\n" -" 利用できる鍵長について確認ください。\n" +"注意: カードが要求された鍵のタイプもしくは鍵長をサポートしている保証は\n" +" ありません。鍵生成が成功しない場合、あなたのカードに関する技術文書を\n" +" 確認し、どの鍵のタイプと鍵長がサポートされているかについて確認して\n" +" ください。\n" #, c-format msgid "What keysize do you want? (%u) " @@ -1432,6 +1437,10 @@ msgstr "工場出荷リセットを行いますか? (本当なら \"yes\" と入 msgid "error for setup KDF: %s\n" msgstr "KDF設定のエラー: %s\n" +#, c-format +msgid "error for setup UIF: %s\n" +msgstr "UIF設定のエラー: %s\n" + msgid "quit this menu" msgstr "このメニューを終了" @@ -1459,8 +1468,8 @@ msgstr "ログイン名の変更" msgid "change the language preferences" msgstr "言語の優先指定の変更" -msgid "change card holder's sex" -msgstr "カード所有者の性別の変更" +msgid "change card holder's salutation" +msgstr "カード所有者の敬称の変更" msgid "change a CA fingerprint" msgstr "CAフィンガープリントの変更" @@ -1489,6 +1498,9 @@ msgstr "PIN認証のKDFを設定する" msgid "change the key attribute" msgstr "鍵の属性の変更" +msgid "change the User Interaction Flag" +msgstr "ユーザ・インタラクション・フラグの変更" + msgid "gpg/card> " msgstr "gpg/card> " @@ -1527,7 +1539,19 @@ msgid "(unless you specify the key by fingerprint)\n" msgstr "(フィンガー・プリントで鍵を指定してない限り)\n" msgid "can't do this in batch mode without \"--yes\"\n" -msgstr "\"--yes\"なしでバッチ・モードではできません\n" +msgstr "これは\"--yes\"なしでバッチ・モードではできません\n" + +msgid "Note: The public primary key and all its subkeys will be deleted.\n" +msgstr "注意: 主鍵とすべての副鍵の公開鍵が削除されます。\n" + +msgid "Note: Only the shown public subkey will be deleted.\n" +msgstr "注意: 表示されている副鍵の公開鍵だけが削除されます。\n" + +msgid "Note: Only the secret part of the shown primary key will be deleted.\n" +msgstr "注意: 表示されている主鍵の秘密鍵だけが削除されます。\n" + +msgid "Note: Only the secret part of the shown subkey will be deleted.\n" +msgstr "注意: 表示されている副鍵の秘密鍵だけが削除されます。\n" msgid "Delete this key from the keyring? (y/N) " msgstr "この鍵を鍵リングから削除しますか? (y/N) " @@ -1545,6 +1569,10 @@ msgstr "鍵" msgid "subkey" msgstr "副鍵: " +#, c-format +msgid "update failed: %s\n" +msgstr "更新に失敗しました: %s\n" + #, c-format msgid "deleting keyblock failed: %s\n" msgstr "鍵ブロックの削除に失敗しました: %s\n" @@ -1567,8 +1595,8 @@ msgid "can't use a symmetric ESK packet due to the S2K mode\n" msgstr "S2Kモードのため、共通鍵ESKパケットを使えません\n" #, c-format -msgid "using cipher %s\n" -msgstr "暗号方式 %s を使います\n" +msgid "using cipher %s.%s\n" +msgstr "暗号方式 %s.%s を使います\n" #, c-format msgid "'%s' already compressed\n" @@ -1608,16 +1636,16 @@ msgid "forcing symmetric cipher %s (%d) violates recipient preferences\n" msgstr "共通鍵暗号方式 %s (%d) の強制が、受取人の優先指定をそむきます\n" #, c-format -msgid "%s/%s encrypted for: \"%s\"\n" -msgstr "%s/%s暗号化 受信者:\"%s\"\n" +msgid "%s/%s.%s encrypted for: \"%s\"\n" +msgstr "%s/%s.%s 暗号化 受信者:\"%s\"\n" #, c-format msgid "option '%s' may not be used in %s mode\n" msgstr "オプション'%s'を%sモードで使うことはできません\n" #, c-format -msgid "%s encrypted data\n" -msgstr "%s暗号化済みデータ\n" +msgid "%s.%s encrypted data\n" +msgstr "%s.%s 暗号化データ\n" #, c-format msgid "encrypted with unknown algorithm %d\n" @@ -1688,6 +1716,9 @@ msgstr "エクスポートの際、利用できない部分を除去する" msgid "remove as much as possible from key during export" msgstr "エクスポートの際、できるだけ除去する" +msgid "Do not export user id or attribute packets" +msgstr "ユーザIDもしくは属性パケットをエクスポートしない" + msgid "use the GnuPG key backup format" msgstr "GnuPGの鍵のバックアップフォーマットを使います" @@ -1719,22 +1750,6 @@ msgstr "'%s'の作成エラー: %s\n" msgid "[User ID not found]" msgstr "[ユーザIDが見つかりません]" -#, c-format -msgid "(check argument of option '%s')\n" -msgstr "(オプション'%s'の引数を確認ください)\n" - -#, c-format -msgid "Warning: '%s' should be a long key ID or a fingerprint\n" -msgstr "警告: '%s'は長い鍵IDかフィンガープリントであるべきです。\n" - -#, c-format -msgid "error looking up: %s\n" -msgstr "検索のエラー: %s\n" - -#, c-format -msgid "Warning: %s appears in the keyring %d times\n" -msgstr "警告: %sは鍵リングに%d回出現します\n" - #, c-format msgid "automatically retrieved '%s' via %s\n" msgstr "'%s'を %s から自動取得\n" @@ -1746,10 +1761,18 @@ msgstr "'%s'を %s から取得する際のエラー: %s\n" msgid "No fingerprint" msgstr "フィンガープリントがありません" +#, c-format +msgid "checking for a fresh copy of an expired key via %s\n" +msgstr "%s から失効した鍵の新しいコピーを確認します。\n" + #, c-format msgid "secret key \"%s\" not found: %s\n" msgstr "秘密鍵\"%s\"が見つかりません: %s\n" +#, c-format +msgid "(check argument of option '%s')\n" +msgstr "(オプション'%s'の引数を確認ください)\n" + #, c-format msgid "Warning: not using '%s' as default key: %s\n" msgstr "警告: デフォルトの秘密鍵として '%s' を用いません: %s\n" @@ -2225,7 +2248,10 @@ msgid "will not run with insecure memory due to %s\n" msgstr "%s のため、セキュアでないメモリで実行しません\n" msgid "selected cipher algorithm is invalid\n" -msgstr "選択された暗号アルゴリズムは、無効です\n" +msgstr "選択された暗号アルゴリズムは無効です\n" + +msgid "selected AEAD algorithm is invalid\n" +msgstr "選択された AEAD アルゴリズムは無効です\n" msgid "selected compression algorithm is invalid\n" msgstr "選択された圧縮アルゴリズムは、無効です\n" @@ -2260,16 +2286,27 @@ msgstr "無効なデフォルトの優先指定\n" msgid "invalid personal cipher preferences\n" msgstr "無効な個人用暗号方式の優先指定\n" +msgid "invalid personal AEAD preferences\n" +msgstr "無効な個人用 AEAD 方式の優先指定\n" + msgid "invalid personal digest preferences\n" msgstr "無効な個人用ダイジェストの優先指定\n" msgid "invalid personal compress preferences\n" msgstr "無効な個人用圧縮の優先指定\n" +#, c-format +msgid "chunk size invalid - using %d\n" +msgstr "無効なチャンク長です - %dにします\n" + #, c-format msgid "%s does not yet work with %s\n" msgstr "%sは%sではまだ機能しません\n" +#, c-format +msgid "AEAD algorithm '%s' may not be used in %s mode\n" +msgstr "AEAD アルゴリズム'%s'を%sモードで使うことはできません\n" + #, c-format msgid "digest algorithm '%s' may not be used in %s mode\n" msgstr "ダイジェスト・アルゴリズム'%s'を%sモードで使うことはできません\n" @@ -2417,6 +2454,9 @@ msgstr "インポート後、利用できない部分を鍵から除去します msgid "remove as much as possible from key after import" msgstr "インポート後、できるだけ除去します" +msgid "Do not import user id or attribute packets" +msgstr "ユーザIDもしくは属性パケットをインポートしない" + msgid "run import filters and export key immediately" msgstr "インポート・フィルタを実行し鍵をすぐにエクスポートします" @@ -2510,6 +2550,10 @@ msgstr "" msgid " \"%s\": preference for cipher algorithm %s\n" msgstr " \"%s\": 暗号アルゴリズムの優先指定 %s\n" +#, c-format +msgid " \"%s\": preference for AEAD algorithm %s\n" +msgstr " \"%s\": AEADアルゴリズムの優先指定 %s\n" + #, c-format msgid " \"%s\": preference for digest algorithm %s\n" msgstr " \"%s\": ダイジェスト・アルゴリズムの優先指定 %s\n" @@ -2634,6 +2678,18 @@ msgstr "鍵 %s: 秘密鍵はもうあります\n" msgid "key %s: error sending to agent: %s\n" msgstr "鍵 %s: エージェントへの送信エラー: %s\n" +#. TRANSLATORS: For a smartcard, each private key on host has a +#. * reference (stub) to a smartcard and actual private key data +#. * is stored on the card. A single smartcard can have up to +#. * three private key data. Importing private key stub is always +#. * skipped in 2.1, and it returns GPG_ERR_NOT_PROCESSED. +#. * Instead, user should be suggested to run 'gpg --card-status', +#. * then, references to a card will be automatically created +#. * again. +#, c-format +msgid "To migrate '%s', with each smartcard, run: %s\n" +msgstr "'%s'の移行には、スマードカードそれぞれで、以下を実行してください: %s\n" + #, c-format msgid "secret key %s: %s\n" msgstr "秘密鍵 %s: %s\n" @@ -2645,19 +2701,26 @@ msgstr "秘密鍵のインポートは禁止です\n" msgid "key %s: secret key with invalid cipher %d - skipped\n" msgstr "鍵%s: 無効な暗号方式%dの秘密鍵です - スキップします\n" -#. TRANSLATORS: For smartcard, each private key on -#. host has a reference (stub) to a smartcard and -#. actual private key data is stored on the card. A -#. single smartcard can have up to three private key -#. data. Importing private key stub is always -#. skipped in 2.1, and it returns -#. GPG_ERR_NOT_PROCESSED. Instead, user should be -#. suggested to run 'gpg --card-status', then, -#. references to a card will be automatically -#. created again. -#, c-format -msgid "To migrate '%s', with each smartcard, run: %s\n" -msgstr "'%s'の移行には、スマードカードそれぞれで、以下を実行してください: %s\n" +msgid "No reason specified" +msgstr "理由は指定されていません" + +msgid "Key is superseded" +msgstr "鍵がとりかわっています" + +msgid "Key has been compromised" +msgstr "鍵(の信頼性)が損なわれています" + +msgid "Key is no longer used" +msgstr "鍵はもはや使われていません" + +msgid "User ID is no longer valid" +msgstr "ユーザIDがもはや有効でありません" + +msgid "reason for revocation: " +msgstr "失効理由: " + +msgid "revocation comment: " +msgstr "失効のコメント: " #, c-format msgid "key %s: no public key - can't apply revocation certificate\n" @@ -3227,10 +3290,6 @@ msgstr "変更を保存しますか? (y/N) " msgid "Quit without saving? (y/N) " msgstr "保存せずに終了しますか? (y/N) " -#, c-format -msgid "update failed: %s\n" -msgstr "更新に失敗しました: %s\n" - msgid "Key not changed so no update needed.\n" msgstr "鍵は無変更なので更新は不要です。\n" @@ -3269,12 +3328,15 @@ msgstr "'%s'は、有効な有効期限ではありません\n" #, c-format msgid "\"%s\" is not a proper fingerprint\n" -msgstr "\"%s\"はフ正しいィンガープリントではありません\n" +msgstr "\"%s\"は正しいフィンガープリントではありません\n" #, c-format msgid "subkey \"%s\" not found\n" msgstr "副鍵\"%s\"が見つかりません\n" +msgid "AEAD: " +msgstr "AEAD: " + msgid "Digest: " msgstr "ダイジェスト: " @@ -3607,6 +3669,9 @@ msgstr "ダイジェストの優先指定が多すぎます\n" msgid "too many compression preferences\n" msgstr "圧縮の優先指定が多すぎます\n" +msgid "too many AEAD preferences\n" +msgstr "AEAD方式の優先指定が多すぎます\n" + #, c-format msgid "invalid item '%s' in preference string\n" msgstr "優先指定の文字列に無効な項目'%s'があります\n" @@ -3647,21 +3712,21 @@ msgid "Authenticate" msgstr "Authenticate" #. TRANSLATORS: Please use only plain ASCII characters for the -#. translation. If this is not possible use single digits. The -#. string needs to 8 bytes long. Here is a description of the -#. functions: -#. -#. s = Toggle signing capability -#. e = Toggle encryption capability -#. a = Toggle authentication capability -#. q = Finish +#. * translation. If this is not possible use single digits. The +#. * string needs to 8 bytes long. Here is a description of the +#. * functions: +#. * +#. * s = Toggle signing capability +#. * e = Toggle encryption capability +#. * a = Toggle authentication capability +#. * q = Finish #. msgid "SsEeAaQq" msgstr "SsEeAaQq" #, c-format -msgid "Possible actions for a %s key: " -msgstr "鍵%sに認められた操作: " +msgid "Possible actions for this %s key: " +msgstr "この鍵%sにありうる操作: " msgid "Current allowed actions: " msgstr "現在の認められた操作: " @@ -3734,6 +3799,10 @@ msgstr " (%d) ECC (暗号化のみ)\n" msgid " (%d) Existing key\n" msgstr " (%d) 既存の鍵\n" +#, c-format +msgid " (%d) Existing key from card\n" +msgstr " (%d) カードに存在する鍵\n" + msgid "Enter the keygrip: " msgstr "keygripを入力: " @@ -3743,6 +3812,17 @@ msgstr "有効なkeygrip (40桁の16進数字)ではありません\n" msgid "No key with this keygrip\n" msgstr "このkeygripの鍵はありません\n" +#, c-format +msgid "error reading the card: %s\n" +msgstr "カードの読み込みエラー: %s\n" + +#, c-format +msgid "Serial number of the card: %s\n" +msgstr "カードのシリアル番号: %s\n" + +msgid "Available keys:\n" +msgstr "利用可能な鍵:\n" + #, c-format msgid "rounded to %u bits\n" msgstr "%uビットに切り上げます\n" @@ -4164,9 +4244,6 @@ msgstr "鍵\"%s\"が鍵サーバに見つかりません\n" msgid "key not found on keyserver\n" msgstr "鍵が鍵サーバに見つかりません\n" -msgid "no keyserver known (use option --keyserver)\n" -msgstr "既知の鍵サーバがありません (オプション--keyserverを使いましょう)\n" - #, c-format msgid "requesting key %s from %s server %s\n" msgstr "鍵%sを%sからサーバ%sに要求\n" @@ -4196,11 +4273,15 @@ msgstr "*警告*: URI %s からデータを取れません: %s\n" #, c-format msgid "weird size for an encrypted session key (%d)\n" -msgstr "変な長さの暗号化済みセッション鍵 (%d)\n" +msgstr "変な長さの暗号化セッション鍵 (%d)\n" #, c-format -msgid "%s encrypted session key\n" -msgstr "%s 暗号化済みセッション鍵\n" +msgid "%s.%s encrypted session key\n" +msgstr "%s.%s 暗号化セッション鍵\n" + +#, c-format +msgid "encrypted with unknown algorithm %d.%s\n" +msgstr "不明のアルゴリズム%d.%sによる暗号化\n" #, c-format msgid "passphrase generated with unknown digest algorithm %d\n" @@ -4210,12 +4291,9 @@ msgstr "不明のダイジェスト・アルゴリズムで生成されたパス msgid "public key is %s\n" msgstr "公開鍵は%sです\n" -msgid "public key encrypted data: good DEK\n" -msgstr "公開鍵による暗号化済みデータ: 正しいDEKです\n" - #, c-format -msgid "encrypted with %u-bit %s key, ID %s, created %s\n" -msgstr "%u-ビット%s鍵, ID %s, 日付%sに暗号化されました\n" +msgid "encrypted with %s key, ID %s, created %s\n" +msgstr "%s鍵, ID %s, 作成日付%s により暗号化されました\n" #, c-format msgid " \"%s\"\n" @@ -4225,9 +4303,8 @@ msgstr " \"%s\"\n" msgid "encrypted with %s key, ID %s\n" msgstr "%s鍵, ID %sで暗号化されました\n" -#, c-format -msgid "public key decryption failed: %s\n" -msgstr "公開鍵の復号に失敗しました: %s\n" +msgid "WARNING: multiple plaintexts seen\n" +msgstr "*警告*: 複数のプレインテクストが見られます\n" #, c-format msgid "encrypted with %lu passphrases\n" @@ -4236,9 +4313,16 @@ msgstr "%lu 個のパスフレーズで暗号化\n" msgid "encrypted with 1 passphrase\n" msgstr "1 個のパスフレーズで暗号化\n" +#, c-format +msgid "public key decryption failed: %s\n" +msgstr "公開鍵の復号に失敗しました: %s\n" + +msgid "public key encrypted data: good DEK\n" +msgstr "公開鍵による暗号化データ: 正しいDEKです\n" + #, c-format msgid "assuming %s encrypted data\n" -msgstr "%s暗号化済みデータを仮定\n" +msgstr "%s暗号化データを仮定\n" #, c-format msgid "IDEA cipher unavailable, optimistically attempting to use %s instead\n" @@ -4247,6 +4331,22 @@ msgstr "IDEA暗号方式は利用不能なので、楽天的ですが%sで代用 msgid "WARNING: message was not integrity protected\n" msgstr "*警告*: メッセージの完全性は保護されていません\n" +msgid "" +"Hint: If this message was created before the year 2003 it is\n" +"likely that this message is legitimate. This is because back\n" +"then integrity protection was not widely used.\n" +msgstr "" +"ヒント: もし、このメッセージが2003年以前に作成されたのであれば、\n" +"このメッセージはおそらく正当でしょう。当時、整合性の保護機能は\n" +"広く使われてはいなかったためです。\n" + +#, c-format +msgid "Use the option '%s' to decrypt anyway.\n" +msgstr "それでも復号するにはオプション '%s' を使います。\n" + +msgid "decryption forced to fail!\n" +msgstr "復号は強制的に失敗とされました!\n" + msgid "decryption okay\n" msgstr "復号に成功\n" @@ -4264,9 +4364,6 @@ msgstr "注意: 送信者は\"極秘とする\"ように求めています\n" msgid "original file name='%.*s'\n" msgstr "元のファイル名='%.*s'\n" -msgid "WARNING: multiple plaintexts seen\n" -msgstr "*警告*: 複数のプレインテクストが見られます\n" - msgid "standalone revocation - use \"gpg --import\" to apply\n" msgstr "スタンドアロン失効 - \"gpg --import\"を使って適用してください\n" @@ -4493,14 +4590,13 @@ msgstr "公開鍵のアルゴリズム%dは、取り扱えません\n" msgid "WARNING: potentially insecure symmetrically encrypted session key\n" msgstr "*警告*: 潜在的にセキュアでない共通鍵暗号化セッション鍵です\n" +msgid "Unknown critical signature notation: " +msgstr "不明なクリティカルな署名注釈: " + #, c-format msgid "subpacket of type %d has critical bit set\n" msgstr "型%dの下位パケットにクリティカル・ビットを発見\n" -#, c-format -msgid "problem with the agent: %s\n" -msgstr "エージェントに問題: %s\n" - msgid "Enter passphrase\n" msgstr "パスフレーズを入力\n" @@ -4581,27 +4677,6 @@ msgstr "この写真は正しいですか (y/N/q)? " msgid "unable to display photo ID!\n" msgstr "フォトIDが表示不能!\n" -msgid "No reason specified" -msgstr "理由は指定されていません" - -msgid "Key is superseded" -msgstr "鍵がとりかわっています" - -msgid "Key has been compromised" -msgstr "鍵(の信頼性)が損なわれています" - -msgid "Key is no longer used" -msgstr "鍵はもはや使われていません" - -msgid "User ID is no longer valid" -msgstr "ユーザIDがもはや有効でありません" - -msgid "reason for revocation: " -msgstr "失効理由: " - -msgid "revocation comment: " -msgstr "失効のコメント: " - #. TRANSLATORS: These are the allowed answers in lower and #. uppercase. Below you will find the matching strings which #. should be translated accordingly and the letter changed to @@ -5080,6 +5155,10 @@ msgstr "注意: 鍵 %s は失効済みです\n" msgid "bad key signature from key %s: %s (0x%02x, 0x%x)\n" msgstr "鍵%sによる不正な鍵への署名: %s (0x%02x, 0x%x)\n" +#, c-format +msgid "bad data signature from key %s: %s (0x%02x, 0x%x)\n" +msgstr "鍵%sによる不正なデータへの署名: %s (0x%02x, 0x%x)\n" + #, c-format msgid "assuming bad signature from key %s due to an unknown critical bit\n" msgstr "不明のクリティカル・ビットのため、鍵%sによる署名を不正とみなします\n" @@ -5122,8 +5201,8 @@ msgid "signing:" msgstr "署名:" #, c-format -msgid "%s encryption will be used\n" -msgstr "%s暗号化を使用します\n" +msgid "%s.%s encryption will be used\n" +msgstr "%s.%s 暗号化を使用します\n" msgid "key is not flagged as insecure - can't use it with the faked RNG!\n" msgstr "" @@ -5306,9 +5385,6 @@ msgid "unsupported TOFU database version: %s\n" msgstr "サポートされていないTOFUデータベースバージョン: %s\n" #, c-format -msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n" -msgstr "'ultimately_trusted_keys' TOFUテーブル作成エラー: %s\n" - msgid "TOFU DB error" msgstr "TOFU DBエラー" @@ -5324,14 +5400,6 @@ msgstr "TOFUデータベースのバージョン判定エラー: %s\n" msgid "error initializing TOFU database: %s\n" msgstr "TOFUデータベースの初期化エラー: %s\n" -#, c-format -msgid "error creating 'encryptions' TOFU table: %s\n" -msgstr "'encryptions' TOFUデータベースの作成エラー: %s\n" - -#, c-format -msgid "adding column effective_policy to bindings DB: %s\n" -msgstr "バインディングDBにカラムeffective_policyを追加: %s\n" - #, c-format msgid "error opening TOFU database '%s': %s\n" msgstr "TOFUデータベースのオープンでエラー '%s': %s\n" @@ -5470,14 +5538,6 @@ msgstr "不明をデフォルトとします。\n" msgid "TOFU db corruption detected.\n" msgstr "TOFU dbが壊れていることが検出されました。\n" -#, c-format -msgid "resetting keydb: %s\n" -msgstr "keydbをリセット: %s\n" - -#, c-format -msgid "error setting TOFU binding's policy to %s\n" -msgstr "TOFUバインディングのポリシーを %s に設定エラー\n" - #, c-format msgid "error changing TOFU policy: %s\n" msgstr "TOFUポリシーの作成エラー: %s\n" @@ -5525,19 +5585,9 @@ msgstr "%s: 0個の署名を検証、0個のメッセージを暗号化しまし msgid "%s: Verified 0 signatures." msgstr "%s: 0個の署名を検証しました。" -#, c-format -msgid "%s: Verified %ld~signature in the past %s." -msgid_plural "%s: Verified %ld~signatures in the past %s." -msgstr[0] "%s: 署名を%ld個検証しました(これまで %s に)。" - msgid "Encrypted 0 messages." msgstr "0 個のメッセージを暗号化しました。" -#, c-format -msgid "Encrypted %ld~message in the past %s." -msgid_plural "Encrypted %ld~messages in the past %s." -msgstr[0] "メッセージを%ld個暗号化しました(これまで %s に)。" - #, c-format msgid "(policy: %s)" msgstr "(ポリシー: %s)" @@ -5585,10 +5635,6 @@ msgid "WARNING: Encrypting to %s, which has no non-revoked user ids\n" msgstr "" "*警告*: %s に暗号化します。失効していないユーザIDが一つもないものです\n" -#, c-format -msgid "error setting policy for key %s, user id \"%s\": %s" -msgstr "鍵%s, ユーザID \"%s\"のポリシーの設定エラー: %s" - #, c-format msgid "'%s' is not a valid long keyID\n" msgstr "'%s'は、有効な大型鍵IDでありません\n" @@ -5751,6 +5797,13 @@ msgstr "入力の%u行目が長すぎるか、LFがないようです\n" msgid "can't open fd %d: %s\n" msgstr "fd %dが開けません: %s\n" +msgid "WARNING: encrypting without integrity protection is dangerous\n" +msgstr "*警告*: 完全性保護なしでの暗号化は危険です\n" + +#, c-format +msgid "Hint: Do not use option %s\n" +msgstr "ヒント: オプション %s を使わない\n" + msgid "set debugging flags" msgstr "デバッグ・フラグを設定" @@ -6030,6 +6083,9 @@ msgstr "リーダのピンパッドを使わない" msgid "deny the use of admin card commands" msgstr "管理カード・コマンドの使用を拒否" +msgid "|LIST|Change the application priority to LIST" +msgstr "|LIST|アプリケーションの優先順位をLISTに変更します" + msgid "use variable length input for pinpad" msgstr "ピンパッドの可変長入力を使う" @@ -6298,6 +6354,9 @@ msgstr "証明書は暗号化のために使えません\n" msgid "certificate is not usable for signing\n" msgstr "証明書は署名のために使えません\n" +msgid "looking for another certificate\n" +msgstr "別の証明書を探索する\n" + #, c-format msgid "line %d: invalid algorithm\n" msgstr "行 %d: 無効なアルゴリズムです\n" @@ -6385,17 +6444,6 @@ msgstr " (%d) 既存の鍵\n" msgid " (%d) Existing key from card\n" msgstr " (%d) カードに存在する鍵\n" -#, c-format -msgid "error reading the card: %s\n" -msgstr "カードの読み込みエラー: %s\n" - -#, c-format -msgid "Serial number of the card: %s\n" -msgstr "カードのシリアル番号: %s\n" - -msgid "Available keys:\n" -msgstr "利用可能な鍵:\n" - #, c-format msgid "Possible actions for a %s key:\n" msgstr "%s鍵に可能な操作:\n" @@ -7641,6 +7689,17 @@ msgstr "'%s' は無効なLDAP URLです\n" msgid "error accessing '%s': http status %u\n" msgstr "'%s'へアクセスのエラー: httpステイタス %u\n" +#, c-format +msgid "URL '%s' redirected to '%s' (%u)\n" +msgstr "URL'%s' は '%s' (%u) へリダイレクトされました\n" + +msgid "too many redirections\n" +msgstr "リダイレクトが多すぎます\n" + +#, c-format +msgid "redirection changed to '%s'\n" +msgstr "リダイレクトが'%s'に変更されました\n" + #, c-format msgid "error allocating memory: %s\n" msgstr "メモリの確保のエラー: %s\n" @@ -7736,13 +7795,6 @@ msgstr "'%s'の接続エラー: %s\n" msgid "error reading HTTP response for '%s': %s\n" msgstr "'%s'のHTTP応答の読み込みエラー: %s\n" -#, c-format -msgid "URL '%s' redirected to '%s' (%u)\n" -msgstr "URL'%s' は '%s' (%u) へリダイレクトされました\n" - -msgid "too many redirections\n" -msgstr "リダイレクトが多すぎます\n" - #, c-format msgid "error parsing OCSP response for '%s': %s\n" msgstr "'%s'に対するOCSP応答構文解析エラー: %s\n" @@ -7751,6 +7803,10 @@ msgstr "'%s'に対するOCSP応答構文解析エラー: %s\n" msgid "OCSP responder at '%s' status: %s\n" msgstr "OSCP応答が '%s' でステイタス: %s\n" +#, c-format +msgid "failed to establish a hashing context for OCSP: %s\n" +msgstr "OCSPのハッシュ・コンテクストを確立するのに失敗しました: %s\n" + #, c-format msgid "hashing the OCSP response for '%s' failed: %s\n" msgstr "'%s'に対するOCSP応答のハッシングに失敗しました: %s\n" @@ -7758,9 +7814,6 @@ msgstr "'%s'に対するOCSP応答のハッシングに失敗しました: %s\n" msgid "not signed by a default OCSP signer's certificate" msgstr "デフォルトOCSP署名者の証明で署名されていません" -msgid "only SHA-1 is supported for OCSP responses\n" -msgstr "SHA-1だけがOCSPレスポンスとしてサポートされています\n" - #, c-format msgid "allocating list item failed: %s\n" msgstr "リスト項目の確保に失敗しました: %s\n" @@ -7804,10 +7857,6 @@ msgstr "デフォルトOCSP応答'%s'を使います\n" msgid "using OCSP responder '%s'\n" msgstr "OCSP応答'%s'を使います\n" -#, c-format -msgid "failed to establish a hashing context for OCSP: %s\n" -msgstr "OCSPのハッシュ・コンテクストを確立するのに失敗しました: %s\n" - #, c-format msgid "error getting OCSP status for target certificate: %s\n" msgstr "対象の証明書のOCSPステイタスの取得エラー: %s\n" @@ -8106,6 +8155,14 @@ msgstr "パスフレーズ入力" msgid "Component not suitable for launching" msgstr "コンポーネントが起動するために適切ではありません" +#, c-format +msgid "Configuration file of component %s is broken\n" +msgstr "コンポーネント%sのコンフィグレーション・ファイルが壊れています\n" + +#, c-format +msgid "Note: Use the command \"%s%s\" to get details.\n" +msgstr "注意: \"%s%s\"コマンドを使って詳細を得てください。\n" + #, c-format msgid "External verification of component %s failed" msgstr "コンポーネント%sの外部の検証が失敗しました" @@ -8332,6 +8389,84 @@ msgstr "" "形式: gpg-check-pattern [オプション] パターンファイル\n" "パターンファイルに対して標準入力のパスフレーズを確認する\n" +#, c-format +msgid "%s card no. %s detected\n" +msgstr "%s カード番号 %sを検出しました\n" + +msgid "authenticate to the card" +msgstr "カードに対して認証します" + +msgid "send a reset to the card daemon" +msgstr "リセットをカードデーモンに送ります" + +msgid "change a private data object" +msgstr "プライベート・データオブジェクトを変更します" + +msgid "read a certificate from a data object" +msgstr "証明書をデータオブジェクトから読み出します" + +msgid "store a certificate to a data object" +msgstr "証明書をデータオブジェクトに保管します" + +msgid "store a private key to a data object" +msgstr "プライベート鍵をデータオブジェクトに保管します" + +msgid "Yubikey management commands" +msgstr "Yubikey管理コマンド" + +#~ msgid "no keyserver known (use option --keyserver)\n" +#~ msgstr "既知の鍵サーバがありません (オプション--keyserverを使いましょう)\n" + +#~ msgid "error creating 'ultimately_trusted_keys' TOFU table: %s\n" +#~ msgstr "'ultimately_trusted_keys' TOFUテーブル作成エラー: %s\n" + +#~ msgid "error creating 'encryptions' TOFU table: %s\n" +#~ msgstr "'encryptions' TOFUデータベースの作成エラー: %s\n" + +#~ msgid "adding column effective_policy to bindings DB: %s\n" +#~ msgstr "バインディングDBにカラムeffective_policyを追加: %s\n" + +#~ msgid "resetting keydb: %s\n" +#~ msgstr "keydbをリセット: %s\n" + +#~ msgid "error setting TOFU binding's policy to %s\n" +#~ msgstr "TOFUバインディングのポリシーを %s に設定エラー\n" + +#~ msgid "%s: Verified %ld~signature in the past %s." +#~ msgid_plural "%s: Verified %ld~signatures in the past %s." +#~ msgstr[0] "%s: 署名を%ld個検証しました(これまで %s に)。" + +#~ msgid "Encrypted %ld~message in the past %s." +#~ msgid_plural "Encrypted %ld~messages in the past %s." +#~ msgstr[0] "メッセージを%ld個暗号化しました(これまで %s に)。" + +#~ msgid "error setting policy for key %s, user id \"%s\": %s" +#~ msgstr "鍵%s, ユーザID \"%s\"のポリシーの設定エラー: %s" + +#~ msgid "only SHA-1 is supported for OCSP responses\n" +#~ msgstr "SHA-1だけがOCSPレスポンスとしてサポートされています\n" + +#~ msgid "male" +#~ msgstr "男" + +#~ msgid "female" +#~ msgstr "女" + +#~ msgid "unspecified" +#~ msgstr "無指定" + +#~ msgid "Sex ((M)ale, (F)emale or space): " +#~ msgstr "性別 ((M)男、(F)女、または空白): " + +#~ msgid "Warning: '%s' should be a long key ID or a fingerprint\n" +#~ msgstr "警告: '%s'は長い鍵IDかフィンガープリントであるべきです。\n" + +#~ msgid "error looking up: %s\n" +#~ msgstr "検索のエラー: %s\n" + +#~ msgid "Warning: %s appears in the keyring %d times\n" +#~ msgstr "警告: %sは鍵リングに%d回出現します\n" + #~ msgid "using \"http\" instead of \"https\"\n" #~ msgstr "\"http\" を \"https\" の代わりに使います\n" diff --git a/scd/apdu.c b/scd/apdu.c index 816938ac5..2df113c5e 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -42,23 +42,11 @@ #include "rapdu.h" #endif /*USE_G10CODE_RAPDU*/ -#if defined(GNUPG_SCD_MAIN_HEADER) -#include GNUPG_SCD_MAIN_HEADER -#elif GNUPG_MAJOR_VERSION == 1 -/* This is used with GnuPG version < 1.9. The code has been source - copied from the current GnuPG >= 1.9 and is maintained over - there. */ -#include "../common/options.h" -#include "errors.h" -#include "memory.h" -#include "../common/util.h" -#include "../common/i18n.h" -#include "dynload.h" -#include "cardglue.h" -#else /* GNUPG_MAJOR_VERSION != 1 */ -#include "scdaemon.h" -#include "../common/exechelp.h" -#endif /* GNUPG_MAJOR_VERSION != 1 */ +#if defined(GNUPG_MAJOR_VERSION) +# include "scdaemon.h" +# include "../common/exechelp.h" +#endif /*GNUPG_MAJOR_VERSION*/ + #include "../common/host2net.h" #include "iso7816.h" @@ -266,8 +254,13 @@ static npth_mutex_t reader_table_lock; struct pcsc_io_request_s { +#if defined(_WIN32) || defined(__CYGWIN__) + pcsc_dword_t protocol; + pcsc_dword_t pci_len; +#else unsigned long protocol; unsigned long pci_len; +#endif }; typedef struct pcsc_io_request_s *pcsc_io_request_t; diff --git a/scd/app-common.h b/scd/app-common.h index 3df896228..5866c9b32 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -36,35 +36,95 @@ /* Flags used with app_writekey. */ #define APP_WRITEKEY_FLAG_FORCE 1 /* Force overwriting existing key. */ +/* Flags used with app_readkey. */ +#define APP_READKEY_FLAG_INFO 1 /* Send also a KEYPAIRINFO line. */ + /* Bit flags set by the decipher function into R_INFO. */ #define APP_DECIPHER_INFO_NOPAD 1 /* Padding has been removed. */ +/* List of supported card types. Generic is the usual ISO7817-4 + * compliant card. More specific card or token versions can be given + * here. Use strcardtype() to map them to a string. */ +typedef enum + { + CARDTYPE_GENERIC = 0, + CARDTYPE_YUBIKEY + + } cardtype_t; + +/* List of supported card applications. The source code for each + * application can usually be found in an app-NAME.c file. Use + * strapptype() to map them to a string. */ +typedef enum + { + APPTYPE_NONE = 0, + APPTYPE_UNDEFINED, + APPTYPE_OPENPGP, + APPTYPE_PIV, + APPTYPE_NKS, + APPTYPE_P15, + APPTYPE_GELDKARTE, + APPTYPE_DINSIG, + APPTYPE_SC_HSM + } apptype_t; + + +/* Forward declarations. */ +struct card_ctx_s; +struct app_ctx_s; struct app_local_s; /* Defined by all app-*.c. */ -struct app_ctx_s { - struct app_ctx_s *next; + +typedef struct card_ctx_s *card_t; +typedef struct app_ctx_s *app_t; + +/* The object describing a card. */ +struct card_ctx_s { + card_t next; npth_mutex_t lock; - /* Number of connections currently using this application context. - If this is not 0 the application has been initialized and the - function pointers may be used. Note that for unsupported - operations the particular function pointer is set to NULL */ + /* Number of connections currently using this application context. */ unsigned int ref_count; /* Used reader slot. */ int slot; + cardtype_t cardtype; /* The token's type. */ + unsigned int cardversion;/* Firmware version of the token or 0. */ + + unsigned int card_status; + + /* The serial number is associated with the card and not with a + * specific app. If a card uses different serial numbers for its + * applications, our code picks the serial number of a specific + * application and uses that. */ unsigned char *serialno; /* Serialnumber in raw form, allocated. */ size_t serialnolen; /* Length in octets of serialnumber. */ - const char *cardtype; /* NULL or string with the token's type. */ - const char *apptype; - unsigned int cardversion;/* Firmware version of the token or 0. */ - unsigned int appversion; /* Version of the application or 0. */ - unsigned int card_status; + + /* A linked list of applications used on this card. The app at the + * head of the list is the currently active app; To work with the + * other apps, switching to that app might be needed. Switching will + * put the active app at the head of the list. */ + app_t app; + + /* Various flags. */ unsigned int reset_requested:1; unsigned int periodical_check_needed:1; +}; + + +/* The object describing a card's applications. A card may have + * several applications and it is usuallay required to explicity + * switch between applications. */ +struct app_ctx_s { + app_t next; + + card_t card; /* Link back to the card. */ + + apptype_t apptype; /* The type of the application. */ + unsigned int appversion; /* Version of the application or 0. */ unsigned int did_chv1:1; unsigned int force_chv1:1; /* True if the card does not cache CHV1. */ unsigned int did_chv2:1; @@ -72,11 +132,13 @@ struct app_ctx_s { struct app_local_s *app_local; /* Local to the application. */ struct { void (*deinit) (app_t app); + gpg_error_t (*reselect) (app_t app, ctrl_t ctrl); gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags); gpg_error_t (*readcert) (app_t app, const char *certid, unsigned char **cert, size_t *certlen); - gpg_error_t (*readkey) (app_t app, const char *certid, - unsigned char **pk, size_t *pklen); + gpg_error_t (*readkey) (app_t app, ctrl_t ctrl, + const char *certid, unsigned int flags, + unsigned char **pk, size_t *pklen); gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name); gpg_error_t (*setattr) (app_t app, const char *name, gpg_error_t (*pincb)(void*, const char *, char **), @@ -121,11 +183,35 @@ struct app_ctx_s { gpg_error_t (*check_pin) (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); + gpg_error_t (*with_keygrip) (app_t app, ctrl_t ctrl, int action, + const char *keygrip_str); } fnc; }; + +/* Action values for app_do_with_keygrip. */ +enum + { + KEYGRIP_ACTION_SEND_DATA, + KEYGRIP_ACTION_WRITE_STATUS, + KEYGRIP_ACTION_LOOKUP + }; + + +/* Helper to get the slot from an APP object. */ +static inline int +app_get_slot (app_t app) +{ + if (app && app->card) + return app->card->slot; + return -1; +} + + /*-- app-help.c --*/ unsigned int app_help_count_bits (const unsigned char *a, size_t len); +gpg_error_t app_help_get_keygrip_string_pk (const void *pk, size_t pklen, + char *hexkeygrip); gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip); gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen, unsigned char **r_pk, size_t *r_pklen); @@ -133,70 +219,85 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); /*-- app.c --*/ -void app_send_card_list (ctrl_t ctrl); +const char *strcardtype (cardtype_t t); +const char *strapptype (apptype_t t); + +void app_update_priority_list (const char *arg); +gpg_error_t app_send_card_list (ctrl_t ctrl); +char *card_get_serialno (card_t card); char *app_get_serialno (app_t app); void app_dump_state (void); void application_notify_card_reset (int slot); -gpg_error_t check_application_conflict (const char *name, app_t app); -gpg_error_t app_reset (app_t app, ctrl_t ctrl, int send_reset); -gpg_error_t select_application (ctrl_t ctrl, const char *name, app_t *r_app, +gpg_error_t check_application_conflict (card_t card, const char *name, + const unsigned char *serialno_bin, + size_t serialno_bin_len); +gpg_error_t card_reset (card_t card, ctrl_t ctrl, int send_reset); +gpg_error_t select_application (ctrl_t ctrl, const char *name, card_t *r_app, int scan, const unsigned char *serialno_bin, size_t serialno_bin_len); +gpg_error_t select_additional_application (ctrl_t ctrl, const char *name); char *get_supported_applications (void); -void release_application (app_t app, int locked_already); -gpg_error_t app_munge_serialno (app_t app); -gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl, + +card_t card_ref (card_t card); +void card_unref (card_t card); +void card_unref_locked (card_t card); + +gpg_error_t app_munge_serialno (card_t card); +gpg_error_t app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags); -gpg_error_t app_readcert (app_t app, ctrl_t ctrl, const char *certid, +gpg_error_t app_readcert (card_t card, ctrl_t ctrl, const char *certid, unsigned char **cert, size_t *certlen); -gpg_error_t app_readkey (app_t app, ctrl_t ctrl, - const char *keyid, unsigned char **pk, size_t *pklen); -gpg_error_t app_getattr (app_t app, ctrl_t ctrl, const char *name); -gpg_error_t app_setattr (app_t app, ctrl_t ctrl, const char *name, - gpg_error_t (*pincb)(void*, const char *, char **), - void *pincb_arg, - const unsigned char *value, size_t valuelen); -gpg_error_t app_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, - gpg_error_t (*pincb)(void*, const char *, char **), - void *pincb_arg, - const void *indata, size_t indatalen, - unsigned char **outdata, size_t *outdatalen ); -gpg_error_t app_auth (app_t app, ctrl_t ctrl, const char *keyidstr, +gpg_error_t app_readkey (card_t card, ctrl_t ctrl, + const char *keyid, unsigned int flags, + unsigned char **pk, size_t *pklen); +gpg_error_t app_getattr (card_t card, ctrl_t ctrl, const char *name); +gpg_error_t app_setattr (card_t card, ctrl_t ctrl, const char *name, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *value, size_t valuelen); +gpg_error_t app_sign (card_t card, ctrl_t ctrl, + const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); -gpg_error_t app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, +gpg_error_t app_auth (card_t card, ctrl_t ctrl, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen); +gpg_error_t app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen, unsigned int *r_info); -gpg_error_t app_writecert (app_t app, ctrl_t ctrl, +gpg_error_t app_writecert (card_t card, ctrl_t ctrl, const char *certidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen); -gpg_error_t app_writekey (app_t app, ctrl_t ctrl, +gpg_error_t app_writekey (card_t card, ctrl_t ctrl, const char *keyidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen); -gpg_error_t app_genkey (app_t app, ctrl_t ctrl, +gpg_error_t app_genkey (card_t card, ctrl_t ctrl, const char *keynostr, const char *keytype, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); -gpg_error_t app_get_challenge (app_t app, ctrl_t ctrl, size_t nbytes, +gpg_error_t app_get_challenge (card_t card, ctrl_t ctrl, size_t nbytes, unsigned char *buffer); -gpg_error_t app_change_pin (app_t app, ctrl_t ctrl, +gpg_error_t app_change_pin (card_t card, ctrl_t ctrl, const char *chvnostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); -gpg_error_t app_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr, - gpg_error_t (*pincb)(void*, const char *, char **), - void *pincb_arg); +gpg_error_t app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg); +card_t app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str); /*-- app-openpgp.c --*/ diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index 983bed6e1..9993b68ff 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -81,7 +81,6 @@ #include "../common/i18n.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" @@ -101,7 +100,7 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) /* Return the certificate of the card holder. */ fid = 0xC000; - len = app_help_read_length_of_cert (app->slot, fid, &certoff); + len = app_help_read_length_of_cert (app_get_slot (app), fid, &certoff); if (!len) return 0; /* Card has not been personalized. */ @@ -114,7 +113,8 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) /* Now we need to read the certificate, so that we can get the public key out of it. */ - err = iso7816_read_binary (app->slot, certoff, len-certoff, &der, &derlen); + err = iso7816_read_binary (app_get_slot (app), certoff, len-certoff, + &der, &derlen); if (err) { log_info ("error reading entire certificate from FID 0x%04X: %s\n", @@ -193,14 +193,14 @@ do_readcert (app_t app, const char *certid, /* Read the entire file. fixme: This could be optimized by first reading the header to figure out how long the certificate actually is. */ - err = iso7816_select_file (app->slot, fid, 0); + err = iso7816_select_file (app_get_slot (app), fid, 0); if (err) { log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); return err; } - err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); + err = iso7816_read_binary (app_get_slot (app), 0, 0, &buffer, &buflen); if (err) { log_error ("error reading certificate from FID 0x%04X: %s\n", @@ -293,7 +293,7 @@ verify_pin (app_t app, pininfo.maxlen = 8; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { rc = pincb (pincb_arg, _("||Please enter your PIN at the reader's pinpad"), @@ -304,7 +304,7 @@ verify_pin (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x81, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), 0x81, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); } @@ -345,7 +345,8 @@ verify_pin (app_t app, return gpg_error (GPG_ERR_BAD_PIN); } - rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + rc = iso7816_verify (app_get_slot (app), 0x81, + pinvalue, strlen (pinvalue)); if (gpg_err_code (rc) == GPG_ERR_INV_VALUE) { /* We assume that ISO 9564-1 encoding is used and we failed @@ -366,7 +367,8 @@ verify_pin (app_t app, paddedpin[i++] = (((*s - '0') << 4) | 0x0f); while (i < sizeof paddedpin) paddedpin[i++] = 0xff; - rc = iso7816_verify (app->slot, 0x81, paddedpin, sizeof paddedpin); + rc = iso7816_verify (app_get_slot (app), 0x81, + paddedpin, sizeof paddedpin); } xfree (pinvalue); } @@ -482,7 +484,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, rc = verify_pin (app, pincb, pincb_arg); if (!rc) - rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0, + rc = iso7816_compute_ds (app_get_slot (app), 0, data, datalen, 0, outdata, outdatalen); return rc; } @@ -532,7 +534,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, return err; } - err = iso7816_change_reference_data (app->slot, 0x81, + err = iso7816_change_reference_data (app_get_slot (app), 0x81, oldpin, oldpinlen, pinvalue, strlen (pinvalue)); xfree (pinvalue); @@ -547,14 +549,15 @@ gpg_error_t app_select_dinsig (app_t app) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 }; - int slot = app->slot; + int slot = app_get_slot (app); int rc; rc = iso7816_select_application (slot, aid, sizeof aid, 0); if (!rc) { - app->apptype = "DINSIG"; + app->apptype = APPTYPE_DINSIG; + app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.getattr = NULL; diff --git a/scd/app-geldkarte.c b/scd/app-geldkarte.c index 85bcedc4f..141985932 100644 --- a/scd/app-geldkarte.c +++ b/scd/app-geldkarte.c @@ -39,7 +39,6 @@ #include "../common/i18n.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" @@ -277,7 +276,7 @@ app_select_geldkarte (app_t app) static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x25, 0x45, 0x50, 0x02, 0x00 }; gpg_error_t err; - int slot = app->slot; + int slot = app_get_slot (app); unsigned char *result = NULL; size_t resultlen; struct app_local_s *ld; @@ -312,21 +311,22 @@ app_select_geldkarte (app_t app) goto leave; /* Probably not a Geldkarte. */ } - app->apptype = "GELDKARTE"; + app->apptype = APPTYPE_GELDKARTE; app->fnc.deinit = do_deinit; + app->fnc.reselect = NULL; /* If we don't have a serialno yet construct it from the EF_ID. */ - if (!app->serialno) + if (!app->card->serialno) { - app->serialno = xtrymalloc (10); - if (!app->serialno) + app->card->serialno = xtrymalloc (10); + if (!app->card->serialno) { err = gpg_error_from_syserror (); goto leave; } - memcpy (app->serialno, result, 10); - app->serialnolen = 10; - err = app_munge_serialno (app); + memcpy (app->card->serialno, result, 10); + app->card->serialnolen = 10; + err = app_munge_serialno (app->card); if (err) goto leave; } diff --git a/scd/app-help.c b/scd/app-help.c index f0f551c55..e3ad74434 100644 --- a/scd/app-help.c +++ b/scd/app-help.c @@ -24,7 +24,6 @@ #include #include "scdaemon.h" -#include "app-common.h" #include "iso7816.h" #include "../common/tlv.h" @@ -52,26 +51,17 @@ app_help_count_bits (const unsigned char *a, size_t len) } -/* Return the KEYGRIP for the certificate CERT as an hex encoded - string in the user provided buffer HEXKEYGRIP which must be of at - least 41 bytes. */ +/* Return the KEYGRIP for the canonical encoded public key (PK,PKLEN) + * as an hex encoded string in the user provided buffer HEXKEYGRIP + * which must be of at least 41 bytes. */ gpg_error_t -app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip) +app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip) { gpg_error_t err; gcry_sexp_t s_pkey; - ksba_sexp_t p; - size_t n; - unsigned char array[20]; + unsigned char array[KEYGRIP_LEN]; - p = ksba_cert_get_public_key (cert); - if (!p) - return gpg_error (GPG_ERR_BUG); - n = gcry_sexp_canon_len (p, 0, NULL, NULL); - if (!n) - return gpg_error (GPG_ERR_INV_SEXP); - err = gcry_sexp_sscan (&s_pkey, NULL, (char*)p, n); - xfree (p); + err = gcry_sexp_sscan (&s_pkey, NULL, pk, pklen); if (err) return err; /* Can't parse that S-expression. */ if (!gcry_pk_get_keygrip (s_pkey, array)) @@ -81,12 +71,34 @@ app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip) } gcry_sexp_release (s_pkey); - bin2hex (array, 20, hexkeygrip); + bin2hex (array, KEYGRIP_LEN, hexkeygrip); return 0; } +/* Return the KEYGRIP for the certificate CERT as an hex encoded + string in the user provided buffer HEXKEYGRIP which must be of at + least 41 bytes. */ +gpg_error_t +app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip) +{ + gpg_error_t err; + ksba_sexp_t p; + size_t n; + + p = ksba_cert_get_public_key (cert); + if (!p) + return gpg_error (GPG_ERR_BUG); + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + err = app_help_get_keygrip_string_pk ((void*)p, n, hexkeygrip); + ksba_free (p); + return err; +} + + gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen, unsigned char **r_pk, size_t *r_pklen) diff --git a/scd/app-nks.c b/scd/app-nks.c index 40c941616..d12720cf6 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -53,7 +53,6 @@ #include "scdaemon.h" #include "../common/i18n.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" #include "apdu.h" #include "../common/host2net.h" @@ -151,13 +150,15 @@ keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr) int i; int offset[2] = { 0, 0 }; - err = iso7816_select_file (app->slot, fid, 0); + err = iso7816_select_file (app_get_slot (app), fid, 0); if (err) return err; - err = iso7816_read_record (app->slot, 1, 1, 0, &buffer[0], &buflen[0]); + err = iso7816_read_record (app_get_slot (app), 1, 1, 0, + &buffer[0], &buflen[0]); if (err) return err; - err = iso7816_read_record (app->slot, 2, 1, 0, &buffer[1], &buflen[1]); + err = iso7816_read_record (app_get_slot (app), 2, 1, 0, + &buffer[1], &buflen[1]); if (err) { xfree (buffer[0]); @@ -272,7 +273,7 @@ get_chv_status (app_t app, int sigg, int pwid) command[2] = 0x00; command[3] = pwid; - if (apdu_send_direct (app->slot, 0, (unsigned char *)command, + if (apdu_send_direct (app_get_slot (app), 0, (unsigned char *)command, 4, 0, NULL, &result, &resultlen)) rc = -1; /* Error. */ else if (resultlen < 2) @@ -406,7 +407,7 @@ do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg) { size_t len; - len = app_help_read_length_of_cert (app->slot, + len = app_help_read_length_of_cert (app_get_slot (app), filelist[i].fid, NULL); if (len) { @@ -528,14 +529,14 @@ do_readcert (app_t app, const char *certid, /* Read the entire file. fixme: This could be optimized by first reading the header to figure out how long the certificate actually is. */ - err = iso7816_select_file (app->slot, fid, 0); + err = iso7816_select_file (app_get_slot (app), fid, 0); if (err) { log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); return err; } - err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); + err = iso7816_read_binary (app_get_slot (app), 0, 0, &buffer, &buflen); if (err) { log_error ("error reading certificate from FID 0x%04X: %s\n", @@ -618,7 +619,8 @@ do_readcert (app_t app, const char *certid, certificate parsing code in commands.c:cmd_readkey. For internal use PK and PKLEN may be NULL to just check for an existing key. */ static gpg_error_t -do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) +do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, + unsigned char **pk, size_t *pklen) { gpg_error_t err; unsigned char *buffer[2]; @@ -632,13 +634,14 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* Access the KEYD file which is always in the master directory. */ - err = iso7816_select_path (app->slot, path, DIM (path)); + err = iso7816_select_path (app_get_slot (app), path, DIM (path)); if (err) return err; /* Due to the above select we need to re-select our application. */ app->app_local->need_app_select = 1; /* Get the two records. */ - err = iso7816_read_record (app->slot, 5, 1, 0, &buffer[0], &buflen[0]); + err = iso7816_read_record (app_get_slot (app), 5, 1, 0, + &buffer[0], &buflen[0]); if (err) return err; if (all_zero_p (buffer[0], buflen[0])) @@ -646,13 +649,22 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) xfree (buffer[0]); return gpg_error (GPG_ERR_NOT_FOUND); } - err = iso7816_read_record (app->slot, 6, 1, 0, &buffer[1], &buflen[1]); + err = iso7816_read_record (app_get_slot (app), 6, 1, 0, + &buffer[1], &buflen[1]); if (err) { xfree (buffer[0]); return err; } + if ((flags & APP_READKEY_FLAG_INFO)) + { + /* Not yet implemented but we won't get here for any regular + * keyrefs anyway, thus the top layer will provide the + * keypairinfo from the certificate. */ + (void)ctrl; + } + if (pk && pklen) { *pk = make_canon_sexp_from_rsa_pk (buffer[0], buflen[0], @@ -698,7 +710,7 @@ do_writekey (app_t app, ctrl_t ctrl, else return gpg_error (GPG_ERR_INV_ID); - if (!force && !do_readkey (app, keyid, NULL, NULL)) + if (!force && !do_readkey (app, ctrl, keyid, 0, NULL, NULL)) return gpg_error (GPG_ERR_EEXIST); /* Parse the S-expression. */ @@ -751,7 +763,7 @@ do_writekey (app_t app, ctrl_t ctrl, /* mse[10] = 0x82; /\* RSA public exponent of up to 4 bytes. *\/ */ /* mse[12] = rsa_e_len; */ /* memcpy (mse+12, rsa_e, rsa_e_len); */ -/* err = iso7816_manage_security_env (app->slot, 0x81, 0xB6, */ +/* err = iso7816_manage_security_env (app_get_slot (app), 0x81, 0xB6, */ /* mse, sizeof mse); */ leave: @@ -794,7 +806,7 @@ verify_pin (app_t app, int pwid, const char *desc, pininfo.maxlen = 16; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { rc = pincb (pincb_arg, desc, NULL); if (rc) @@ -804,7 +816,7 @@ verify_pin (app_t app, int pwid, const char *desc, return rc; } - rc = iso7816_verify_kp (app->slot, pwid, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), pwid, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else @@ -825,7 +837,8 @@ verify_pin (app_t app, int pwid, const char *desc, return rc; } - rc = iso7816_verify (app->slot, pwid, pinvalue, strlen (pinvalue)); + rc = iso7816_verify (app_get_slot (app), pwid, + pinvalue, strlen (pinvalue)); xfree (pinvalue); } @@ -963,7 +976,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, mse[3] = 0x84; /* Private key reference. */ mse[4] = 1; mse[5] = kid; - rc = iso7816_manage_security_env (app->slot, 0x41, 0xB6, + rc = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, mse, sizeof mse); } /* Verify using PW1.CH. */ @@ -971,7 +984,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, rc = verify_pin (app, 0, NULL, pincb, pincb_arg); /* Compute the signature. */ if (!rc) - rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0, + rc = iso7816_compute_ds (app_get_slot (app), 0, data, datalen, 0, outdata, outdatalen); return rc; } @@ -1038,7 +1051,7 @@ do_decipher (app_t app, const char *keyidstr, mse[3] = 0x84; /* Private key reference. */ mse[4] = 1; mse[5] = kid; - rc = iso7816_manage_security_env (app->slot, 0x41, 0xB8, + rc = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8, mse, sizeof mse); } else @@ -1048,7 +1061,7 @@ do_decipher (app_t app, const char *keyidstr, 0x80, 1, 0x10, /* Select algorithm RSA. */ 0x84, 1, 0x81 /* Select local secret key 1 for decryption. */ }; - rc = iso7816_manage_security_env (app->slot, 0xC1, 0xB8, + rc = iso7816_manage_security_env (app_get_slot (app), 0xC1, 0xB8, mse, sizeof mse); } @@ -1059,7 +1072,8 @@ do_decipher (app_t app, const char *keyidstr, /* Note that we need to use extended length APDUs for TCOS 3 cards. Command chaining does not work. */ if (!rc) - rc = iso7816_decipher (app->slot, app->app_local->nks_version > 2? 1:0, + rc = iso7816_decipher (app_get_slot (app), + app->app_local->nks_version > 2? 1:0, indata, indatalen, 0, 0x81, outdata, outdatalen); return rc; @@ -1251,13 +1265,13 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, } memcpy (data, oldpin, oldpinlen); memcpy (data+oldpinlen, newpin, newpinlen); - err = iso7816_reset_retry_counter_with_rc (app->slot, pwid, + err = iso7816_reset_retry_counter_with_rc (app_get_slot (app), pwid, data, datalen); wipememory (data, datalen); xfree (data); } else - err = iso7816_change_reference_data (app->slot, pwid, + err = iso7816_change_reference_data (app_get_slot (app), pwid, oldpin, oldpinlen, newpin, newpinlen); leave: @@ -1338,9 +1352,11 @@ switch_application (app_t app, int enable_sigg) log_info ("app-nks: switching to %s\n", enable_sigg? "SigG":"NKS"); if (enable_sigg) - err = iso7816_select_application (app->slot, aid_sigg, sizeof aid_sigg, 0); + err = iso7816_select_application (app_get_slot (app), + aid_sigg, sizeof aid_sigg, 0); else - err = iso7816_select_application (app->slot, aid_nks, sizeof aid_nks, 0); + err = iso7816_select_application (app_get_slot (app), + aid_nks, sizeof aid_nks, 0); if (!err && enable_sigg && app->app_local->nks_version >= 3 && !app->app_local->sigg_msig_checked) @@ -1353,9 +1369,10 @@ switch_application (app_t app, int enable_sigg) app->app_local->sigg_msig_checked = 1; app->app_local->sigg_is_msig = 1; - err = iso7816_select_file (app->slot, 0x5349, 0); + err = iso7816_select_file (app_get_slot (app), 0x5349, 0); if (!err) - err = iso7816_read_record (app->slot, 1, 1, 0, &buffer, &buflen); + err = iso7816_read_record (app_get_slot (app), 1, 1, 0, + &buffer, &buflen); if (!err) { tmpl = find_tlv (buffer, buflen, 0x7a, &tmpllen); @@ -1387,13 +1404,13 @@ switch_application (app_t app, int enable_sigg) gpg_error_t app_select_nks (app_t app) { - int slot = app->slot; + int slot = app_get_slot (app); int rc; rc = iso7816_select_application (slot, aid_nks, sizeof aid_nks, 0); if (!rc) { - app->apptype = "NKS"; + app->apptype = APPTYPE_NKS; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) @@ -1407,6 +1424,7 @@ app_select_nks (app_t app) log_info ("Detected NKS version: %d\n", app->app_local->nks_version); app->fnc.deinit = do_deinit; + app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.readkey = do_readkey; diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 1e904b578..767f29d26 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -50,27 +50,20 @@ #include #include -#if GNUPG_MAJOR_VERSION == 1 -/* This is used with GnuPG version < 1.9. The code has been source - copied from the current GnuPG >= 1.9 and is maintained over - there. */ -#include "options.h" -#include "errors.h" -#include "memory.h" -#include "cardglue.h" -#else /* GNUPG_MAJOR_VERSION != 1 */ #include "scdaemon.h" -#endif /* GNUPG_MAJOR_VERSION != 1 */ - #include "../common/util.h" #include "../common/i18n.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" #include "../common/host2net.h" #include "../common/openpgpdefs.h" + +/* The AID of this application. */ +static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; + + /* A table describing the DOs of the card. */ static struct { int tag; @@ -176,6 +169,7 @@ struct app_local_s { is usually only required for cross checks because the length of an S-expression is implicitly available. */ + unsigned char keygrip_str[41]; /* The keygrip, null terminated */ } pk[3]; unsigned char status_indicator; /* The card status indicator. */ @@ -348,7 +342,7 @@ get_cached_data (app_t app, int tag, else exmode = 0; - err = iso7816_get_data (app->slot, exmode, tag, &p, &len); + err = iso7816_get_data (app_get_slot (app), exmode, tag, &p, &len); if (err) return err; if (len) @@ -479,7 +473,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, if (app->appversion > 0x0100 && data_objects[i].get_immediate_in_v11) { exmode = 0; - rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen); + rc = iso7816_get_data (app_get_slot (app), exmode, tag, &buffer, &buflen); if (rc) { *r_rc = rc; @@ -821,7 +815,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr, tag2 = 0xCE + keynumber; flush_cache_item (app, 0xCD); - rc = iso7816_put_data (app->slot, 0, tag, fpr, 20); + rc = iso7816_put_data (app_get_slot (app), 0, tag, fpr, 20); if (rc) log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc)); @@ -834,7 +828,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr, buf[2] = timestamp >> 8; buf[3] = timestamp; - rc = iso7816_put_data (app->slot, 0, tag2, buf, 4); + rc = iso7816_put_data (app_get_slot (app), 0, tag2, buf, 4); if (rc) log_error (_("failed to store the creation date: %s\n"), gpg_strerror (rc)); @@ -987,6 +981,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) { "PRIVATE-DO-3", 0x0103 }, { "PRIVATE-DO-4", 0x0104 }, { "$AUTHKEYID", 0x0000, -3 }, + { "$ENCRKEYID", 0x0000, -6 }, + { "$SIGNKEYID", 0x0000, -7 }, { "$DISPSERIALNO",0x0000, -4 }, { "UIF-1", 0x00D6, 0 }, { "UIF-2", 0x00D7, 0 }, @@ -1071,6 +1067,18 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) send_key_attr (ctrl, app, table[idx].name, i); return 0; } + if (table[idx].special == -6) + { + char const tmp[] = "OPENPGP.2"; + send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); + return 0; + } + if (table[idx].special == -7) + { + char const tmp[] = "OPENPGP.1"; + send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); + return 0; + } relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc); if (relptr) @@ -1214,7 +1222,6 @@ get_remaining_tries (app_t app, int adminpw) the according hex representation to FPR. Caller must have provide a buffer at FPR of least 41 bytes. Returns 0 on success or an error code. */ -#if GNUPG_MAJOR_VERSION > 1 static gpg_error_t retrieve_fpr_from_card (app_t app, int keyno, char *fpr) { @@ -1233,7 +1240,6 @@ retrieve_fpr_from_card (app_t app, int keyno, char *fpr) xfree (relptr); return err; } -#endif /*GNUPG_MAJOR_VERSION > 1*/ /* Retrieve the public key material for the RSA key, whose fingerprint @@ -1242,7 +1248,6 @@ retrieve_fpr_from_card (app_t app, int keyno, char *fpr) public exponent at E and ELEN. Returns zero on success, an error code on failure. Caller must release the allocated buffers at M and E if the function returns success. */ -#if GNUPG_MAJOR_VERSION > 1 static gpg_error_t retrieve_key_material (FILE *fp, const char *hexkeyid, const unsigned char **m, size_t *mlen, @@ -1347,7 +1352,6 @@ retrieve_key_material (FILE *fp, const char *hexkeyid, xfree (line); return err; } -#endif /*GNUPG_MAJOR_VERSION > 1*/ static gpg_error_t @@ -1561,6 +1565,23 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno, } +static gpg_error_t +store_keygrip (app_t app, int keyno) +{ + gpg_error_t err; + unsigned char grip[20]; + + err = keygrip_from_canon_sexp (app->app_local->pk[keyno].key, + app->app_local->pk[keyno].keylen, + grip); + if (err) + return err; + + bin2hex (grip, 20, app->app_local->pk[keyno].keygrip_str); + return 0; +} + + /* Parse tag-length-value data for public key in BUFFER of BUFLEN length. Key of KEYNO in APP is updated with an S-expression of public key. When CTRL is not NULL, fingerprint is computed with @@ -1612,6 +1633,8 @@ read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno, app->app_local->pk[keyno].key = keybuf; /* Decrement for trailing '\0' */ app->app_local->pk[keyno].keylen = len - 1; + + err = store_keygrip (app, keyno); } return err; @@ -1628,7 +1651,6 @@ read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno, consuming to send it just for the fun of it. However, given that we use the same code in gpg 1.4, we can't use the gcry S-expression here but need to open encode it. */ -#if GNUPG_MAJOR_VERSION > 1 static gpg_error_t get_public_key (app_t app, int keyno) { @@ -1673,7 +1695,7 @@ get_public_key (app_t app, int keyno) le_value = 256; /* Use legacy value. */ } - err = iso7816_read_public_key (app->slot, exmode, + err = iso7816_read_public_key (app_get_slot (app), exmode, (keyno == 0? "\xB6" : keyno == 1? "\xB8" : "\xA4"), 2, le_value, &buffer, &buflen); @@ -1713,7 +1735,8 @@ get_public_key (app_t app, int keyno) hexkeyid = fpr + 24; ret = gpgrt_asprintf - (&command, "gpg --list-keys --with-colons --with-key-data '%s'", fpr); + (&command, "%s --list-keys --with-colons --with-key-data '%s'", + gnupg_module_name (GNUPG_MODULE_NAME_GPG), fpr); if (ret < 0) { err = gpg_error_from_syserror (); @@ -1759,17 +1782,17 @@ get_public_key (app_t app, int keyno) app->app_local->pk[keyno].key = (unsigned char*)keybuf; /* Decrement for trailing '\0' */ app->app_local->pk[keyno].keylen = len - 1; + err = store_keygrip (app, keyno); } leave: /* Set a flag to indicate that we tried to read the key. */ - app->app_local->pk[keyno].read_done = 1; + if (!err) + app->app_local->pk[keyno].read_done = 1; xfree (buffer); return err; } -#endif /* GNUPG_MAJOR_VERSION > 1 */ - /* Send the KEYPAIRINFO back. KEY needs to be in the range [1,3]. @@ -1779,11 +1802,6 @@ send_keypair_info (app_t app, ctrl_t ctrl, int key) { int keyno = key - 1; gpg_error_t err = 0; - /* Note that GnuPG 1.x does not need this and it would be too time - consuming to send it just for the fun of it. */ -#if GNUPG_MAJOR_VERSION > 1 - unsigned char grip[20]; - char gripstr[41]; char idbuf[50]; const char *usage; @@ -1795,14 +1813,6 @@ send_keypair_info (app_t app, ctrl_t ctrl, int key) if (!app->app_local->pk[keyno].key) goto leave; /* No such key - ignore. */ - err = keygrip_from_canon_sexp (app->app_local->pk[keyno].key, - app->app_local->pk[keyno].keylen, - grip); - if (err) - goto leave; - - bin2hex (grip, 20, gripstr); - switch (keyno) { case 0: usage = "sc"; break; @@ -1813,14 +1823,12 @@ send_keypair_info (app_t app, ctrl_t ctrl, int key) sprintf (idbuf, "OPENPGP.%d", keyno+1); send_status_info (ctrl, "KEYPAIRINFO", - gripstr, 40, + app->app_local->pk[keyno].keygrip_str, 40, idbuf, strlen (idbuf), usage, strlen (usage), NULL, (size_t)0); leave: -#endif /* GNUPG_MAJOR_VERSION > 1 */ - return err; } @@ -1875,7 +1883,8 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) buffer. On error PK and PKLEN are not changed and an error code is returned. */ static gpg_error_t -do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) +do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, + unsigned char **pk, size_t *pklen) { gpg_error_t err; int keyno; @@ -1898,15 +1907,25 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) if (!buf) return gpg_error (GPG_ERR_NO_PUBKEY); - *pklen = app->app_local->pk[keyno].keylen; - *pk = xtrymalloc (*pklen); - if (!*pk) + if ((flags & APP_READKEY_FLAG_INFO)) { - err = gpg_error_from_syserror (); - *pklen = 0; - return err; + err = send_keypair_info (app, ctrl, keyno+1); + if (err) + return err; + } + + if (pk && pklen) + { + *pklen = app->app_local->pk[keyno].keylen; + *pk = xtrymalloc (*pklen); + if (!*pk) + { + err = gpg_error_from_syserror (); + *pklen = 0; + return err; + } + memcpy (*pk, buf, *pklen); } - memcpy (*pk, buf, *pklen); return 0; } @@ -1919,7 +1938,6 @@ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen) { -#if GNUPG_MAJOR_VERSION > 1 gpg_error_t err; unsigned char *buffer; size_t buflen; @@ -1948,9 +1966,6 @@ do_readcert (app_t app, const char *certid, } xfree (relptr); return err; -#else - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); -#endif } @@ -2150,7 +2165,7 @@ verify_a_chv (app_t app, /* Special case for def_chv2 mechanism. */ if (opt.verbose) log_info (_("using default PIN as %s\n"), "CHV2"); - rc = iso7816_verify (app->slot, 0x82, "123456", 6); + rc = iso7816_verify (app_get_slot (app), 0x82, "123456", 6); if (rc) { /* Verification of CHV2 with the default PIN failed, @@ -2183,7 +2198,7 @@ verify_a_chv (app_t app, } if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) && !check_pinpad_request (app, &pininfo, 0)) { /* The reader supports the verify command through the pinpad. @@ -2199,7 +2214,7 @@ verify_a_chv (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x80+chvno, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), 0x80+chvno, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); @@ -2230,7 +2245,8 @@ verify_a_chv (app_t app, rc = pin2hash_if_kdf (app, chvno, *pinvalue, pinlen); if (!rc) - rc = iso7816_verify (app->slot, 0x80+chvno, *pinvalue, *pinlen); + rc = iso7816_verify (app_get_slot (app), + 0x80 + chvno, *pinvalue, *pinlen); } if (rc) @@ -2270,7 +2286,7 @@ verify_chv2 (app_t app, the card is not configured to require a verification before each CHV1 controlled operation (force_chv1) and if we are not using the pinpad (PINVALUE == NULL). */ - rc = iso7816_verify (app->slot, 0x81, pinvalue, pinlen); + rc = iso7816_verify (app_get_slot (app), 0x81, pinvalue, pinlen); if (gpg_err_code (rc) == GPG_ERR_BAD_PIN) rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED); if (rc) @@ -2337,13 +2353,11 @@ verify_chv3 (app_t app, { int rc = 0; -#if GNUPG_MAJOR_VERSION != 1 if (!opt.allow_admin) { log_info (_("access to admin commands is not configured\n")); return gpg_error (GPG_ERR_EACCES); } -#endif if (!app->did_chv3) { @@ -2360,7 +2374,8 @@ verify_chv3 (app_t app, return rc; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) + && !iso7816_check_pinpad (app_get_slot (app), + ISO7816_VERIFY, &pininfo) && !check_pinpad_request (app, &pininfo, 1)) { /* The reader supports the verify command through the pinpad. */ @@ -2373,7 +2388,7 @@ verify_chv3 (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x83, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), 0x83, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); } @@ -2402,7 +2417,7 @@ verify_chv3 (app_t app, rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen); if (!rc) - rc = iso7816_verify (app->slot, 0x83, pinvalue, pinlen); + rc = iso7816_verify (app_get_slot (app), 0x83, pinvalue, pinlen); xfree (pinvalue); } @@ -2501,7 +2516,8 @@ do_setattr (app_t app, const char *name, exmode = -254; /* Command chaining with max. 254 bytes. */ else exmode = 0; - rc = iso7816_put_data (app->slot, exmode, table[idx].tag, value, valuelen); + rc = iso7816_put_data (app_get_slot (app), + exmode, table[idx].tag, value, valuelen); if (rc) log_error ("failed to set '%s': %s\n", table[idx].name, gpg_strerror (rc)); @@ -2532,7 +2548,6 @@ do_writecert (app_t app, ctrl_t ctrl, const unsigned char *certdata, size_t certdatalen) { (void)ctrl; -#if GNUPG_MAJOR_VERSION > 1 if (strcmp (certidstr, "OPENPGP.3")) return gpg_error (GPG_ERR_INV_ID); if (!certdata || !certdatalen) @@ -2542,9 +2557,6 @@ do_writecert (app_t app, ctrl_t ctrl, if (certdatalen > app->app_local->extcap.max_certlen_3) return gpg_error (GPG_ERR_TOO_LARGE); return do_setattr (app, "CERT-3", pincb, pincb_arg, certdata, certdatalen); -#else - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); -#endif } @@ -2562,7 +2574,7 @@ clear_chv_status (app_t app, int chvno) apdu[2] = 0xff; apdu[3] = 0x80+chvno; - err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL); + err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, NULL, NULL, NULL); if (err) { if (gpg_err_code (err) == GPG_ERR_INV_VALUE) @@ -2573,7 +2585,8 @@ clear_chv_status (app_t app, int chvno) if (chvno == 1) { apdu[3]++; - err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL); + err = iso7816_apdu_direct (app_get_slot (app), + apdu, 4, 0, NULL, NULL, NULL); app->did_chv1 = app->did_chv2 = 0; } else if (chvno == 2) @@ -2684,7 +2697,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, /* Version 2 cards. */ if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_CHANGE_REFERENCE_DATA, &pininfo) && !check_pinpad_request (app, &pininfo, chvno == 3)) use_pinpad = 1; @@ -2827,7 +2840,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, rc = pin2hash_if_kdf (app, 0, buffer+pinlen0, &pinlen); } if (!rc) - rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81, + rc = iso7816_reset_retry_counter_with_rc (app_get_slot (app), 0x81, buffer, pinlen0+pinlen); wipememory (buffer, pinlen0 + pinlen); xfree (buffer); @@ -2844,31 +2857,37 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, { rc = pin2hash_if_kdf (app, 0, pinvalue, &pinlen); if (!rc) - rc = iso7816_put_data (app->slot, 0, 0xD3, pinvalue, pinlen); + rc = iso7816_put_data (app_get_slot (app), + 0, 0xD3, pinvalue, pinlen); } } else if (reset_mode) { rc = pin2hash_if_kdf (app, 1, pinvalue, &pinlen); if (!rc) - rc = iso7816_reset_retry_counter (app->slot, 0x81, pinvalue, pinlen); + rc = iso7816_reset_retry_counter (app_get_slot (app), + 0x81, pinvalue, pinlen); if (!rc && !app->app_local->extcap.is_v2) - rc = iso7816_reset_retry_counter (app->slot, 0x82, pinvalue, pinlen); + rc = iso7816_reset_retry_counter (app_get_slot (app), + 0x82, pinvalue, pinlen); } else if (!app->app_local->extcap.is_v2) { /* Version 1 cards. */ if (chvno == 1 || chvno == 2) { - rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x81, NULL, 0, pinvalue, strlen (pinvalue)); if (!rc) - rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x82, NULL, 0, pinvalue, strlen (pinvalue)); } else /* CHVNO == 3 */ { - rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x80 + chvno, NULL, 0, pinvalue, strlen (pinvalue)); } } @@ -2889,7 +2908,8 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, gpg_strerror (rc)); goto leave; } - rc = iso7816_change_reference_data_kp (app->slot, 0x80 + chvno, 0, + rc = iso7816_change_reference_data_kp (app_get_slot (app), + 0x80 + chvno, 0, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } @@ -2899,7 +2919,8 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, if (!rc) rc = pin2hash_if_kdf (app, chvno, pinvalue, &pinlen); if (!rc) - rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x80 + chvno, oldpinvalue, pinlen0, pinvalue, pinlen); } @@ -2942,7 +2963,7 @@ does_key_exist (app_t app, int keyidx, int generating, int force) assert (keyidx >=0 && keyidx <= 2); - if (iso7816_get_data (app->slot, 0, 0x006E, &buffer, &buflen)) + if (iso7816_get_data (app_get_slot (app), 0, 0x006E, &buffer, &buflen)) { log_error (_("error reading application data\n")); return gpg_error (GPG_ERR_GENERAL); @@ -3256,7 +3277,7 @@ change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen, return err; /* Change the attribute. */ - err = iso7816_put_data (app->slot, 0, 0xC1+keyno, buf, buflen); + err = iso7816_put_data (app_get_slot (app), 0, 0xC1+keyno, buf, buflen); if (err) log_error ("error changing key attribute (key=%d)\n", keyno+1); else @@ -3648,7 +3669,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), exmode = -254; else exmode = 0; - err = iso7816_put_data_odd (app->slot, exmode, 0x3fff, + err = iso7816_put_data_odd (app_get_slot (app), exmode, 0x3fff, template, template_len); } else @@ -3698,7 +3719,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), goto leave; /* Store the key. */ - err = iso7816_put_data (app->slot, 0, + err = iso7816_put_data (app_get_slot (app), 0, (app->appversion > 0x0007? 0xE0:0xE9)+keyno, template, template_len); } @@ -3970,7 +3991,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), exmode = -254; else exmode = 0; - err = iso7816_put_data_odd (app->slot, exmode, 0x3fff, + err = iso7816_put_data_odd (app_get_slot (app), exmode, 0x3fff, template, template_len); xfree (template); } @@ -4138,7 +4159,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype, log_info (_("please wait while key is being generated ...\n")); start_at = time (NULL); - err = iso7816_generate_keypair (app->slot, exmode, 0x80, 0, + err = iso7816_generate_keypair (app_get_slot (app), exmode, 0x80, 0, (keyno == 0? "\xB6" : keyno == 1? "\xB8" : "\xA4"), 2, le_value, &buffer, &buflen); @@ -4269,6 +4290,74 @@ check_against_given_fingerprint (app_t app, const char *fpr, int key) } +/* Check KEYIDSTR, if it's valid. + When KEYNO is 0, it means it's for PIN check. + Otherwise, KEYNO corresponds to the slot (signing, decipher and auth). + KEYIDSTR is either: + (1) Serial number + (2) Serial number "/" fingerprint + (3) keygrip + + When KEYNO is 0 and KEYIDSTR is for a keygrip, the keygrip should + be to be compared is the first one (keygrip for signing). + */ +static int +check_keyidstr (app_t app, const char *keyidstr, int keyno) +{ + int rc; + const char *s; + int n; + const char *fpr = NULL; + unsigned char tmp_sn[20]; /* Actually 16 bytes but also for the fpr. */ + + if (strlen (keyidstr) < 32) + return gpg_error (GPG_ERR_INV_ID); + else + { + for (s=keyidstr, n=0; hexdigitp (s); s++, n++) + ; + + /* Check if it's a keygrip */ + if (n == 40) + { + const unsigned char *keygrip_str; + + keygrip_str = app->app_local->pk[keyno?keyno-1:0].keygrip_str; + if (!strncmp (keygrip_str, keyidstr, 40)) + return 0; + else + return gpg_error (GPG_ERR_INV_ID); + } + + if (n != 32 || strncmp (keyidstr, "D27600012401", 12)) + return gpg_error (GPG_ERR_INV_ID); + else if (!*s) + ; /* no fingerprint given: we allow this for now. */ + else if (*s == '/') + fpr = s + 1; + 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->card->serialnolen != 16) + return gpg_error (GPG_ERR_INV_CARD); + if (memcmp (app->card->serialno, tmp_sn, 16)) + return gpg_error (GPG_ERR_WRONG_CARD); + } + + /* 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, gpg + will detect a bogus signature anyway due to the + verify-after-signing feature. */ + rc = (fpr&&keyno)? check_against_given_fingerprint (app, fpr, keyno) : 0; + + return rc; +} + /* Compute a digital signature on INDATA which is expected to be the raw message digest. For this application the KEYIDSTR consists of @@ -4314,10 +4403,6 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, int rc; unsigned char data[19+64]; size_t datalen; - unsigned char tmp_sn[20]; /* Actually 16 bytes but also for the fpr. */ - const char *s; - int n; - const char *fpr = NULL; unsigned long sigcount; int use_auth = 0; int exmode, le_value; @@ -4362,40 +4447,13 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, ; else if (!strcmp (keyidstr, "OPENPGP.3")) use_auth = 1; - else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) - return gpg_error (GPG_ERR_INV_ID); else { - 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 == '/') - fpr = s + 1; - 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); + rc = check_keyidstr (app, keyidstr, 1); + if (rc) + return rc; } - /* 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, gpg - will detect a bogus signature anyway due to the - verify-after-signing feature. */ - rc = fpr? check_against_given_fingerprint (app, fpr, 1) : 0; - if (rc) - return rc; - /* Concatenate prefix and digest. */ #define X(a,b,d) \ if (hashalgo == GCRY_MD_ ## a && (d) ) \ @@ -4443,7 +4501,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, char *pinvalue; int pinlen; - rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue, &pinlen); + rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, + &pinvalue, &pinlen); if (rc) return rc; @@ -4456,7 +4515,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, pinpad has been used. */ if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2) { - rc = iso7816_verify (app->slot, 0x82, pinvalue, pinlen); + rc = iso7816_verify (app_get_slot (app), 0x82, pinvalue, pinlen); if (gpg_err_code (rc) == GPG_ERR_BAD_PIN) rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED); if (rc) @@ -4484,7 +4543,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, exmode = 0; le_value = 0; } - rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value, + rc = iso7816_compute_ds (app_get_slot (app), exmode, data, datalen, le_value, outdata, outdatalen); if (gpg_err_code (rc) == GPG_ERR_TIMEOUT) clear_chv_status (app, 1); @@ -4512,10 +4571,6 @@ do_auth (app_t app, const char *keyidstr, unsigned char **outdata, size_t *outdatalen ) { int rc; - unsigned char tmp_sn[20]; /* Actually 16 but we use it also for the fpr. */ - const char *s; - int n; - const char *fpr = NULL; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); @@ -4543,40 +4598,13 @@ do_auth (app_t app, const char *keyidstr, /* Check whether an OpenPGP card of any version has been requested. */ if (!strcmp (keyidstr, "OPENPGP.3")) ; - else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) - return gpg_error (GPG_ERR_INV_ID); else { - 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 == '/') - fpr = s + 1; - 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); + rc = check_keyidstr (app, keyidstr, 3); + if (rc) + return rc; } - /* 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, gpg - will detect a bogus signature anyway due to the - verify-after-signing feature. */ - rc = fpr? check_against_given_fingerprint (app, fpr, 3) : 0; - if (rc) - return rc; - rc = verify_chv2 (app, pincb, pincb_arg); if (!rc) { @@ -4594,7 +4622,7 @@ do_auth (app_t app, const char *keyidstr, exmode = 0; le_value = 0; } - rc = iso7816_internal_authenticate (app->slot, exmode, + rc = iso7816_internal_authenticate (app_get_slot (app), exmode, indata, indatalen, le_value, outdata, outdatalen); if (gpg_err_code (rc) == GPG_ERR_TIMEOUT) @@ -4612,11 +4640,8 @@ do_decipher (app_t app, const char *keyidstr, unsigned char **outdata, size_t *outdatalen, unsigned int *r_info) { - int rc; - unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */ - const char *s; int n; - const char *fpr = NULL; + int rc; int exmode, le_value; unsigned char *fixbuf = NULL; int padind = 0; @@ -4628,39 +4653,13 @@ do_decipher (app_t app, const char *keyidstr, /* Check whether an OpenPGP card of any version has been requested. */ if (!strcmp (keyidstr, "OPENPGP.2")) ; - else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) - return gpg_error (GPG_ERR_INV_ID); else { - 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 == '/') - fpr = s + 1; - 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); + rc = check_keyidstr (app, keyidstr, 2); + if (rc) + return rc; } - /* 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, the - decryption won't produce the right plaintext anyway. */ - rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0; - if (rc) - return rc; - rc = verify_chv2 (app, pincb, pincb_arg); if (rc) return rc; @@ -4818,7 +4817,7 @@ do_decipher (app_t app, const char *keyidstr, else exmode = le_value = 0; - rc = iso7816_decipher (app->slot, exmode, + rc = iso7816_decipher (app_get_slot (app), exmode, indata, indatalen, le_value, padind, outdata, outdatalen); xfree (fixbuf); @@ -4876,38 +4875,19 @@ do_check_pin (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { - unsigned char tmp_sn[20]; - const char *s; - int n; int admin_pin = 0; + int rc; 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); + rc = check_keyidstr (app, keyidstr, 0); + if (rc) + return rc; - 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 if (!strcmp (s, "[CHV3]") ) + if ((strlen (keyidstr) >= 32+6 && !strcmp (keyidstr+32, "[CHV3]")) + || (strlen (keyidstr) >= 40+6 && !strcmp (keyidstr+40, "[CHV3]"))) admin_pin = 1; - 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 @@ -4950,6 +4930,65 @@ do_check_pin (app_t app, const char *keyidstr, return verify_chv2 (app, pincb, pincb_arg); } +static gpg_error_t +do_with_keygrip (app_t app, ctrl_t ctrl, int action, const char *keygrip_str) +{ + int i; + + /* Make sure we have load the public keys. */ + for (i = 0; i < 3; i++) + get_public_key (app, i); + + if (action == KEYGRIP_ACTION_LOOKUP) + { + if (keygrip_str == NULL) + return gpg_error (GPG_ERR_NOT_FOUND); + + for (i = 0; i < 3; i++) + if (app->app_local->pk[i].read_done + && !strcmp (keygrip_str, app->app_local->pk[i].keygrip_str)) + return 0; /* Found */ + } + else + { + char idbuf[50]; + char buf[65]; + int data = (action == KEYGRIP_ACTION_SEND_DATA); + + if (DIM (buf) < 2 * app->card->serialnolen + 1) + return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); + + bin2hex (app->card->serialno, app->card->serialnolen, buf); + + if (keygrip_str == NULL) + { + for (i = 0; i < 3; i++) + if (app->app_local->pk[i].read_done) + { + sprintf (idbuf, "OPENPGP.%d", i+1); + send_keyinfo (ctrl, data, + app->app_local->pk[i].keygrip_str,buf, idbuf); + } + /* Return an error so that the dispatcher keeps on looping + * over the other applications. Only for clarity we use a + * different error code than for the not_found case. */ + return gpg_error (GPG_ERR_TRUE); + } + else + { + for (i = 0; i < 3; i++) + if (app->app_local->pk[i].read_done + && !strcmp (keygrip_str, app->app_local->pk[i].keygrip_str)) + { + sprintf (idbuf, "OPENPGP.%d", i+1); + send_keyinfo (ctrl, data, keygrip_str, buf, idbuf); + return 0; + } + } + } + + return gpg_error (GPG_ERR_NOT_FOUND); +} /* Show information about card capabilities. */ static void @@ -5170,13 +5209,36 @@ parse_algorithm_attribute (app_t app, int keyno) xfree (relptr); } + +/* Reselect the application. This is used by cards which support + * on-the-fly switching between applications. */ +static gpg_error_t +do_reselect (app_t app, ctrl_t ctrl) +{ + gpg_error_t err; + + (void)ctrl; + + /* An extra check which should not be necessary because the caller + * should have made sure that a re-select is only called for + * approriate cards. */ + if (app->card->cardtype != CARDTYPE_YUBIKEY) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + /* Note that the card can't cope with P2=0xCO, thus we need to pass + * a special flag value. */ + err = iso7816_select_application (app_get_slot (app), + openpgp_aid, sizeof openpgp_aid, 0x0001); + return err; +} + + /* Select the OpenPGP application on the card in SLOT. This function must be used before any other OpenPGP application functions. */ gpg_error_t app_select_openpgp (app_t app) { - static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; - int slot = app->slot; + int slot = app_get_slot (app); int rc; unsigned char *buffer; size_t buflen; @@ -5184,12 +5246,13 @@ app_select_openpgp (app_t app) /* Note that the card can't cope with P2=0xCO, thus we need to pass a special flag value. */ - rc = iso7816_select_application (slot, aid, sizeof aid, 0x0001); + rc = iso7816_select_application (slot, + openpgp_aid, sizeof openpgp_aid, 0x0001); if (!rc) { unsigned int manufacturer; - app->apptype = "OPENPGP"; + app->apptype = APPTYPE_OPENPGP; app->did_chv1 = 0; app->did_chv2 = 0; @@ -5214,9 +5277,9 @@ app_select_openpgp (app_t app) app->appversion |= buffer[7]; manufacturer = (buffer[8]<<8 | buffer[9]); - xfree (app->serialno); - app->serialno = buffer; - app->serialnolen = buflen; + xfree (app->card->serialno); + app->card->serialno = buffer; + app->card->serialnolen = buflen; buffer = NULL; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) @@ -5319,6 +5382,7 @@ app_select_openpgp (app_t app) dump_all_do (slot); app->fnc.deinit = do_deinit; + app->fnc.reselect = do_reselect; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.readkey = do_readkey; @@ -5332,6 +5396,7 @@ app_select_openpgp (app_t app) app->fnc.decipher = do_decipher; app->fnc.change_pin = do_change_pin; app->fnc.check_pin = do_check_pin; + app->fnc.with_keygrip = do_with_keygrip; } leave: diff --git a/scd/app-p15.c b/scd/app-p15.c index 190292433..ce82b34a9 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -38,7 +38,6 @@ #include "scdaemon.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" #include "apdu.h" /* fixme: we should move the card detection to a separate file */ @@ -443,7 +442,7 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) if (app->app_local->direct_path_selection) { - err = iso7816_select_path (app->slot, path+1, pathlen-1); + err = iso7816_select_path (app_get_slot (app), path+1, pathlen-1); if (err) { log_error ("error selecting path "); @@ -461,7 +460,8 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) supported by the card. */ for (i=0; i < pathlen; i++) { - err = iso7816_select_file (app->slot, path[i], !(i+1 == pathlen)); + err = iso7816_select_file (app_get_slot (app), + path[i], !(i+1 == pathlen)); if (err) { log_error ("error selecting part %d from path ", i); @@ -612,7 +612,8 @@ read_ef_odf (app_t app, unsigned short odf_fid) unsigned short value; size_t offset; - err = select_and_read_binary (app->slot, odf_fid, "ODF", &buffer, &buflen); + err = select_and_read_binary (app_get_slot (app), odf_fid, "ODF", + &buffer, &buflen); if (err) return err; @@ -826,7 +827,8 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result) if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */ - err = select_and_read_binary (app->slot, fid, "PrKDF", &buffer, &buflen); + err = select_and_read_binary (app_get_slot (app), fid, "PrKDF", + &buffer, &buflen); if (err) return err; @@ -1282,7 +1284,8 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result) if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */ - err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen); + err = select_and_read_binary (app_get_slot (app), fid, "CDF", + &buffer, &buflen); if (err) return err; @@ -1555,7 +1558,8 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result) if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No authentication objects. */ - err = select_and_read_binary (app->slot, fid, "AODF", &buffer, &buflen); + err = select_and_read_binary (app_get_slot (app), fid, "AODF", + &buffer, &buflen); if (err) return err; @@ -2216,7 +2220,7 @@ read_ef_tokeninfo (app_t app) int class, tag, constructed, ndef; unsigned long ul; - err = select_and_read_binary (app->slot, 0x5032, "TokenInfo", + err = select_and_read_binary (app_get_slot (app), 0x5032, "TokenInfo", &buffer, &buflen); if (err) return err; @@ -2294,13 +2298,13 @@ read_p15_info (app_t app) { /* If we don't have a serial number yet but the TokenInfo provides one, use that. */ - if (!app->serialno && app->app_local->serialno) + if (!app->card->serialno && app->app_local->serialno) { - app->serialno = app->app_local->serialno; - app->serialnolen = app->app_local->serialnolen; + app->card->serialno = app->app_local->serialno; + app->card->serialnolen = app->app_local->serialnolen; app->app_local->serialno = NULL; app->app_local->serialnolen = 0; - err = app_munge_serialno (app); + err = app_munge_serialno (app->card); if (err) return err; } @@ -2553,7 +2557,8 @@ readcert_by_cdf (app_t app, cdf_object_t cdf, if (err) goto leave; - err = iso7816_read_binary (app->slot, cdf->off, cdf->len, &buffer, &buflen); + err = iso7816_read_binary (app_get_slot (app), cdf->off, cdf->len, + &buffer, &buflen); if (!err && (!buflen || *buffer == 0xff)) err = gpg_error (GPG_ERR_NOT_FOUND); if (err) @@ -2713,7 +2718,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) err = select_ef_by_path (app, path, DIM(path) ); if (!err) - err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); + err = iso7816_read_binary (app_get_slot (app), 0, 0, + &buffer, &buflen); if (err) { log_error ("error accessing EF(ID): %s\n", gpg_strerror (err)); @@ -2758,7 +2764,7 @@ micardo_mse (app_t app, unsigned short fid) unsigned char msebuf[10]; /* Read the KeyD file containing extra information on keys. */ - err = iso7816_select_file (app->slot, 0x0013, 0); + err = iso7816_select_file (app_get_slot (app), 0x0013, 0); if (err) { log_error ("error reading EF_keyD: %s\n", gpg_strerror (err)); @@ -2772,7 +2778,8 @@ micardo_mse (app_t app, unsigned short fid) size_t n, nn; const unsigned char *p, *pp; - err = iso7816_read_record (app->slot, recno, 1, 0, &buffer, &buflen); + err = iso7816_read_record (app_get_slot (app), recno, 1, 0, + &buffer, &buflen); if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) break; /* ready */ if (err) @@ -2811,7 +2818,8 @@ micardo_mse (app_t app, unsigned short fid) /* Restore the security environment to SE_NUM if needed */ if (se_num) { - err = iso7816_manage_security_env (app->slot, 0xf3, se_num, NULL, 0); + err = iso7816_manage_security_env (app_get_slot (app), + 0xf3, se_num, NULL, 0); if (err) { log_error ("restoring SE to %d failed: %s\n", @@ -2826,7 +2834,7 @@ micardo_mse (app_t app, unsigned short fid) msebuf[2] = 0x80; msebuf[3] = (refdata >> 8); msebuf[4] = refdata; - err = iso7816_manage_security_env (app->slot, 0x41, 0xb6, msebuf, 5); + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xb6, msebuf, 5); if (err) { log_error ("setting SE to reference file %04hX failed: %s\n", @@ -2948,7 +2956,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, mse[3] = 0x84; /* Private key reference tag. */ mse[4] = prkdf->key_reference_valid? prkdf->key_reference : 0x82; - err = iso7816_manage_security_env (app->slot, + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, mse, sizeof mse); no_data_padding = 1; @@ -3101,7 +3109,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, else pinvaluelen = strlen (pinvalue); - err = iso7816_verify (app->slot, + err = iso7816_verify (app_get_slot (app), aodf->pin_reference_valid? aodf->pin_reference : 0, pinvalue, pinvaluelen); xfree (pinvalue); @@ -3170,7 +3178,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, mse[1] = 1; mse[2] = prkdf->key_reference; - err = iso7816_manage_security_env (app->slot, + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, mse, sizeof mse); } @@ -3181,11 +3189,14 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, } if (hashalgo == MD_USER_TLS_MD5SHA1) - err = iso7816_compute_ds (app->slot, 0, data, 36, 0, outdata, outdatalen); + err = iso7816_compute_ds (app_get_slot (app), + 0, data, 36, 0, outdata, outdatalen); else if (no_data_padding) - err = iso7816_compute_ds (app->slot, 0, data+15, 20, 0,outdata,outdatalen); + err = iso7816_compute_ds (app_get_slot (app), + 0, data+15, 20, 0,outdata,outdatalen); else - err = iso7816_compute_ds (app->slot, 0, data, 35, 0, outdata, outdatalen); + err = iso7816_compute_ds (app_get_slot (app), + 0, data, 35, 0, outdata, outdatalen); return err; } @@ -3280,7 +3291,7 @@ read_home_df (int slot, int *r_belpic) gpg_error_t app_select_p15 (app_t app) { - int slot = app->slot; + int slot = app_get_slot (app); int rc; unsigned short def_home_df = 0; card_type_t card_type = CARD_TYPE_UNKNOWN; @@ -3298,7 +3309,7 @@ app_select_p15 (app_t app) Using the 2f02 just works. */ unsigned short path[1] = { 0x2f00 }; - rc = iso7816_select_path (app->slot, path, 1); + rc = iso7816_select_path (app_get_slot (app), path, 1); if (!rc) { direct = 1; @@ -3306,7 +3317,7 @@ app_select_p15 (app_t app) if (def_home_df) { path[0] = def_home_df; - rc = iso7816_select_path (app->slot, path, 1); + rc = iso7816_select_path (app_get_slot (app), path, 1); } } } @@ -3330,7 +3341,7 @@ app_select_p15 (app_t app) size_t atrlen; int i; - atr = apdu_get_atr (app->slot, &atrlen); + atr = apdu_get_atr (app_get_slot (app), &atrlen); if (!atr) rc = gpg_error (GPG_ERR_INV_CARD); else @@ -3348,7 +3359,7 @@ app_select_p15 (app_t app) } if (!rc) { - app->apptype = "P15"; + app->apptype = APPTYPE_P15; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) @@ -3381,8 +3392,8 @@ app_select_p15 (app_t app) prototype card right here because we need to access to EF(TokenInfo). We mark such a serial number by the using a prefix of FF0100. */ - if (app->serialnolen == 12 - && !memcmp (app->serialno, "\xD2\x76\0\0\0\0\0\0\0\0\0\0", 12)) + if (app->card->serialnolen == 12 + && !memcmp (app->card->serialno, "\xD2\x76\0\0\0\0\0\0\0\0\0\0", 12)) { /* This is a German card with a silly serial number. Try to get the serial number from the EF(TokenInfo). . */ @@ -3390,20 +3401,21 @@ app_select_p15 (app_t app) /* FIXME: actually get it from EF(TokenInfo). */ - p = xtrymalloc (3 + app->serialnolen); + p = xtrymalloc (3 + app->card->serialnolen); if (!p) rc = gpg_error (gpg_err_code_from_errno (errno)); else { memcpy (p, "\xff\x01", 3); - memcpy (p+3, app->serialno, app->serialnolen); - app->serialnolen += 3; - xfree (app->serialno); - app->serialno = p; + memcpy (p+3, app->card->serialno, app->card->serialnolen); + app->card->serialnolen += 3; + xfree (app->card->serialno); + app->card->serialno = p; } } app->fnc.deinit = do_deinit; + app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.getattr = do_getattr; diff --git a/scd/app-piv.c b/scd/app-piv.c index 5748c70b8..3b94a28e4 100644 --- a/scd/app-piv.c +++ b/scd/app-piv.c @@ -19,6 +19,7 @@ /* Some notes: * - Specs for PIV are at http://dx.doi.org/10.6028/NIST.SP.800-73-4 + * - https://developers.yubico.com/PIV/Introduction/PIV_attestation.html * * - Access control matrix: * | Action | 9B | PIN | PUK | | @@ -60,7 +61,6 @@ #include "../common/util.h" #include "../common/i18n.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" #include "../common/host2net.h" #include "apdu.h" /* We use apdu_send_direct. */ @@ -81,6 +81,10 @@ #define PIV_ALGORITHM_ECC_P384 0x14 +/* The AID for PIV. */ +static char const piv_aid[] = { 0xA0, 0x00, 0x00, 0x03, 0x08, /* RID=NIST */ + 0x00, 0x00, 0x10, 0x00 /* PIX=PIV */ }; + /* A table describing the DOs of a PIV card. */ struct data_object_s @@ -282,7 +286,7 @@ get_cached_data (app_t app, int tag, } } - err = iso7816_get_data_odd (app->slot, 0, tag, &p, &len); + err = iso7816_get_data_odd (app_get_slot (app), 0, tag, &p, &len); if (err) return err; @@ -736,18 +740,18 @@ get_dispserialno (app_t app, int failmode) { char *result; - if (app->serialno && app->serialnolen == 3+1+4 - && !memcmp (app->serialno, "\xff\x02\x00", 3)) + if (app->card && app->card->serialno && app->card->serialnolen == 3+1+4 + && !memcmp (app->card->serialno, "\xff\x02\x00", 3)) { /* This is a 4 byte S/N of a Yubikey which seems to be printed * on the token in decimal. Maybe they will print larger S/N * also in decimal but we can't be sure, thus do it only for * these 32 bit numbers. */ unsigned long sn; - sn = app->serialno[4] * 16777216; - sn += app->serialno[5] * 65536; - sn += app->serialno[6] * 256; - sn += app->serialno[7]; + sn = app->card->serialno[4] * 16777216; + sn += app->card->serialno[5] * 65536; + sn += app->card->serialno[6] * 256; + sn += app->card->serialno[7]; result = xtryasprintf ("yk-%lu", sn); } else if (failmode) @@ -785,7 +789,7 @@ get_chv_status (app_t app, const char *keyrefstr) apdu[1] = ISO7816_VERIFY; apdu[2] = 0x00; apdu[3] = keyref; - if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL)) + if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL)) result = -5; /* No need to verification. */ else if (sw == 0x6a88 || sw == 0x6a80) result = -2; /* No such PIN. */ @@ -811,7 +815,9 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) int special; } table[] = { { "SERIALNO", 0x0000, -1 }, - { "$AUTHKEYID", 0x0000, -2 }, /* Default key for ssh. */ + { "$AUTHKEYID", 0x0000, -2 }, /* Default ssh key. */ + { "$ENCRKEYID", 0x0000, -6 }, /* Default encryption key. */ + { "$SIGNKEYID", 0x0000, -7 }, /* Default signing key. */ { "$DISPSERIALNO",0x0000, -3 }, { "CHV-STATUS", 0x0000, -4 }, { "CHV-USAGE", 0x007E, -5 } @@ -882,6 +888,16 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) xfree (relptr); } } + else if (table[idx].special == -6) + { + char const tmp[] = "PIV.9D"; /* Key Management. */ + send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); + } + else if (table[idx].special == -7) + { + char const tmp[] = "PIV.9C"; /* Digital Signature. */ + send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); + } else { relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err); @@ -925,7 +941,7 @@ auth_adm_key (app_t app, const unsigned char *value, size_t valuelen) tmpl[2] = 0x80; tmpl[3] = 0; /* (Empty witness requests a witness.) */ tmpllen = 4; - err = iso7816_general_authenticate (app->slot, 0, + err = iso7816_general_authenticate (app_get_slot (app), 0, PIV_ALGORITHM_3DES_ECB_0, 0x9B, tmpl, tmpllen, 0, &outdata, &outdatalen); @@ -958,7 +974,7 @@ auth_adm_key (app_t app, const unsigned char *value, size_t valuelen) tmpl[23] = 0; tmpllen = 24; xfree (outdata); - err = iso7816_general_authenticate (app->slot, 0, + err = iso7816_general_authenticate (app_get_slot (app), 0, PIV_ALGORITHM_3DES_ECB_0, 0x9B, tmpl, tmpllen, 0, &outdata, &outdatalen); @@ -1032,7 +1048,8 @@ set_adm_key (app_t app, const unsigned char *value, size_t valuelen) apdu[6] = 0x9b; apdu[7] = 24; memcpy (apdu+8, value, 24); - err = iso7816_apdu_direct (app->slot, apdu, 8+24, 0, &sw, NULL, NULL); + err = iso7816_apdu_direct (app_get_slot (app), apdu, 8+24, 0, + &sw, NULL, NULL); wipememory (apdu+8, 24); if (err) log_error ("piv: setting admin key failed; sw=%04x\n", sw); @@ -1413,7 +1430,7 @@ do_readcert (app_t app, const char *certid, apdu[1] = 0xf9; /* Yubikey: Get attestation cert. */ apdu[2] = xtoi_2 (certid+9); apdu[3] = 0; - err = iso7816_apdu_direct (app->slot, apdu, 4, 1, + err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 1, NULL, &result, &resultlen); if (!err) { @@ -1452,7 +1469,7 @@ do_readcert (app_t app, const char *certid, * returned. */ static gpg_error_t -do_readkey (app_t app, const char *keyrefstr, +do_readkey (app_t app, ctrl_t ctrl, const char *keyrefstr, unsigned int flags, unsigned char **r_pk, size_t *r_pklen) { gpg_error_t err; @@ -1504,9 +1521,35 @@ do_readkey (app_t app, const char *keyrefstr, goto leave; } - *r_pk = pk; - pk = NULL; - *r_pklen = pklen; + if ((flags & APP_READKEY_FLAG_INFO)) + { + char keygripstr[KEYGRIP_LEN*2+1]; + char idbuf[50]; + const char *usage; + + err = app_help_get_keygrip_string_pk (pk, pklen, keygripstr); + if (err) + { + log_error ("app_help_get_keygrip_string_pk failed: %s\n", + gpg_strerror (err)); + goto leave; + } + usage = dobj->usage? dobj->usage : ""; + + snprintf (idbuf, sizeof idbuf, "PIV.%s", dobj->keyref); + send_status_info (ctrl, "KEYPAIRINFO", + keygripstr, strlen (keygripstr), + idbuf, strlen (idbuf), + usage, strlen (usage), + NULL, (size_t)0); + } + + if (r_pk && r_pklen) + { + *r_pk = pk; + pk = NULL; + *r_pklen = pklen; + } leave: gcry_sexp_release (s_pkey); @@ -1841,7 +1884,7 @@ verify_chv (app_t app, int keyref, int force, apdu[1] = ISO7816_VERIFY; apdu[2] = 0x00; apdu[3] = keyref; - if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL)) + if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL)) { if (!force) /* No need to verification. */ return 0; /* All fine. */ @@ -1857,7 +1900,7 @@ verify_chv (app_t app, int keyref, int force, if (err) return err; - err = iso7816_verify (app->slot, keyref, pin, pinlen); + err = iso7816_verify (app_get_slot (app), keyref, pin, pinlen); wipememory (pin, pinlen); xfree (pin); if (err) @@ -1918,7 +1961,8 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, apdu[1] = ISO7816_VERIFY; apdu[2] = 0xff; apdu[3] = keyref; - err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL); + err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, + NULL, NULL, NULL); goto leave; } @@ -1940,7 +1984,7 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, apdu[1] = ISO7816_VERIFY; apdu[2] = 0x00; apdu[3] = keyref; - if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL)) + if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL)) remaining = -1; /* Already verified, thus full number of tries. */ else if ((sw & 0xfff0) == 0x63C0) remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */ @@ -1957,7 +2001,7 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, * old is wrong. This is not possible for the PUK, though. */ if (keyref != 0x81) { - err = iso7816_verify (app->slot, keyref, oldpin, oldpinlen); + err = iso7816_verify (app_get_slot (app), keyref, oldpin, oldpinlen); if (err) { log_error ("CHV %02X verification failed: %s\n", @@ -1982,7 +2026,8 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, } memcpy (buf, oldpin, oldpinlen); memcpy (buf+oldpinlen, newpin, newpinlen); - err = iso7816_reset_retry_counter_with_rc (app->slot, targetkeyref, + err = iso7816_reset_retry_counter_with_rc (app_get_slot (app), + targetkeyref, buf, oldpinlen+newpinlen); xfree (buf); if (err) @@ -1991,7 +2036,7 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, } else { - err = iso7816_change_reference_data (app->slot, keyref, + err = iso7816_change_reference_data (app_get_slot (app), keyref, oldpin, oldpinlen, newpin, newpinlen); if (err) @@ -2230,7 +2275,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, goto leave; /* Note: the -1 requests command chaining. */ - err = iso7816_general_authenticate (app->slot, -1, + err = iso7816_general_authenticate (app_get_slot (app), -1, mechanism, keyref, apdudata, (int)apdudatalen, 0, &outdata, &outdatalen); @@ -2395,15 +2440,34 @@ do_decipher (app_t app, const char *keyidstr, /* Check that the ciphertext has the right length; due to internal * convey mechanism using MPIs leading zero bytes might have been - * lost. Adjust for this. Note that for ECC this actually - * superfluous because the first octet is always '04' to indicate an + * lost. Adjust for this. Unfortunately the ciphertext might have + * also been prefixed with a leading zero to make it a positive + * number; that may be a too long frame and we need to adjust for + * this too. Note that for ECC thoses fixes are not reqquired + * because the first octet is always '04' to indicate an * uncompressed point. */ if (indatalen > framelen) { - err = gpg_error (GPG_ERR_INV_VALUE); - log_error ("piv: input of %zu octets too large for mechanism %d\n", - indatalen, mechanism); - goto leave; + if (mechanism == PIV_ALGORITHM_RSA + && indatalen == framelen + 1 && !*indata) + { + indata_buffer = xtrycalloc (1, framelen); + if (!indata_buffer) + { + err = gpg_error_from_syserror (); + goto leave; + } + memcpy (indata_buffer, indata+1, framelen); + indata = indata_buffer; + indatalen = framelen; + } + else + { + err = gpg_error (GPG_ERR_INV_VALUE); + log_error ("piv: input of %zu octets too large for mechanism %d\n", + indatalen, mechanism); + goto leave; + } } if (indatalen < framelen) { @@ -2434,7 +2498,7 @@ do_decipher (app_t app, const char *keyidstr, goto leave; /* Note: the -1 requests command chaining. */ - err = iso7816_general_authenticate (app->slot, -1, + err = iso7816_general_authenticate (app_get_slot (app), -1, mechanism, keyref, apdudata, (int)apdudatalen, 0, &outdata, &outdatalen); @@ -2635,7 +2699,7 @@ writekey_rsa (app_t app, data_object_t dobj, int keyref, if (err) goto leave; - err = iso7816_send_apdu (app->slot, + err = iso7816_send_apdu (app_get_slot (app), -1, /* Use command chaining. */ 0, /* Class */ 0xfe, /* Ins: Yubikey Import Asym. Key. */ @@ -2657,7 +2721,7 @@ writekey_rsa (app_t app, data_object_t dobj, int keyref, if (err) goto leave; tmpl[0] = PIV_ALGORITHM_RSA; - err = put_data (app->slot, dobj->tag, + err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)apdudatalen, apdudata, (int)0, (size_t)0, NULL); @@ -2787,7 +2851,7 @@ writekey_ecc (app_t app, data_object_t dobj, int keyref, if (err) goto leave; - err = iso7816_send_apdu (app->slot, + err = iso7816_send_apdu (app_get_slot (app), -1, /* Use command chaining. */ 0, /* Class */ 0xfe, /* Ins: Yubikey Import Asym. Key. */ @@ -2808,7 +2872,7 @@ writekey_ecc (app_t app, data_object_t dobj, int keyref, if (err) goto leave; tmpl[0] = mechanism; - err = put_data (app->slot, dobj->tag, + err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)apdudatalen, apdudata, (int)0, (size_t)0, NULL); @@ -2894,7 +2958,7 @@ do_writekey (app_t app, ctrl_t ctrl, /* First clear an existing key. We do this by writing an empty 7f49 * tag. This will return GPG_ERR_NO_PUBKEY on a later read. */ flush_cached_data (app, dobj->tag); - err = put_data (app->slot, dobj->tag, + err = put_data (app_get_slot (app), dobj->tag, (int)0x7f49, (size_t)0, "", (int)0, (size_t)0, NULL); if (err) @@ -3122,7 +3186,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keyrefstr, const char *keytype, tmpl[3] = 1; tmpl[4] = mechanism; tmpllen = 5; - err = iso7816_generate_keypair (app->slot, 0, 0, keyref, + err = iso7816_generate_keypair (app_get_slot (app), 0, 0, keyref, tmpl, tmpllen, 0, &buffer, &buflen); if (err) { @@ -3152,7 +3216,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keyrefstr, const char *keytype, tmpl[0] = mechanism; flush_cached_data (app, dobj->tag); - err = put_data (app->slot, dobj->tag, + err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)keydatalen, keydata, (int)0, (size_t)0, NULL); @@ -3205,7 +3269,7 @@ do_writecert (app_t app, ctrl_t ctrl, * GPG_ERR_NO_PUBKEY). We enforce this because otherwise the only * way to detect whether a key exists is by trying to use that * key. */ - err = do_readkey (app, certrefstr, &orig_pk, &orig_pklen); + err = do_readkey (app, ctrl, certrefstr, 0, &orig_pk, &orig_pklen); if (err) { if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) @@ -3223,7 +3287,7 @@ do_writecert (app_t app, ctrl_t ctrl, goto leave; } - err = put_data (app->slot, dobj->tag, + err = put_data (app_get_slot (app), dobj->tag, (int)0x70, (size_t)certlen, cert,/* Certificate */ (int)0x71, (size_t)1, "", /* No compress */ (int)0xfe, (size_t)0, "", /* Empty LRC. */ @@ -3242,14 +3306,121 @@ do_writecert (app_t app, ctrl_t ctrl, } +/* Process the various keygrip based info requests. */ +static gpg_error_t +do_with_keygrip (app_t app, ctrl_t ctrl, int action, + const char *want_keygripstr) +{ + gpg_error_t err; + char *keygripstr = NULL; + char *serialno = NULL; + char idbuf[20]; + int data = 0; + int i, tag, dummy_got_cert; + + /* First a quick check for valid parameters. */ + switch (action) + { + case KEYGRIP_ACTION_LOOKUP: + if (!want_keygripstr) + { + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + break; + case KEYGRIP_ACTION_SEND_DATA: + data = 1; + break; + case KEYGRIP_ACTION_WRITE_STATUS: + break; + default: + err = gpg_error (GPG_ERR_INV_ARG); + goto leave; + } + + /* Allocate the s/n string if needed. */ + if (action != KEYGRIP_ACTION_LOOKUP) + { + serialno = app_get_serialno (app); + if (!serialno) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + for (i = 0; (tag = data_objects[i].tag); i++) + { + if (!data_objects[i].keypair) + continue; + + xfree (keygripstr); + if (get_keygrip_by_tag (app, tag, &keygripstr, &dummy_got_cert)) + continue; + + if (action == KEYGRIP_ACTION_LOOKUP) + { + if (!strcmp (keygripstr, want_keygripstr)) + { + err = 0; /* Found */ + goto leave; + } + } + else if (!want_keygripstr || !strcmp (keygripstr, want_keygripstr)) + { + snprintf (idbuf, sizeof idbuf, "PIV.%s", data_objects[i].keyref); + send_keyinfo (ctrl, data, keygripstr, serialno, idbuf); + if (want_keygripstr) + { + err = 0; /* Found */ + goto leave; + } + } + } + + /* Return an error so that the dispatcher keeps on looping over the + * other applications. For clarity we use a different error code + * when listing all keys. Note that in lookup mode WANT_KEYGRIPSTR + * is not NULL. */ + if (!want_keygripstr) + err = gpg_error (GPG_ERR_TRUE); + else + err = gpg_error (GPG_ERR_NOT_FOUND); + + leave: + xfree (keygripstr); + xfree (serialno); + return err; +} + + +/* Reselect the application. This is used by cards which support + * on-the-fly switching between applications. */ +static gpg_error_t +do_reselect (app_t app, ctrl_t ctrl) +{ + gpg_error_t err; + + (void)ctrl; + + /* An extra check which should not be necessary because the caller + * should have made sure that a re-select is only called for + * approriate cards. */ + if (!app->app_local->flags.yubikey) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + err = iso7816_select_application (app_get_slot (app), + piv_aid, sizeof piv_aid, 0x0001); + return err; +} + + /* Select the PIV application on the card in SLOT. This function must * be used before any other PIV application functions. */ gpg_error_t app_select_piv (app_t app) { - static char const aid[] = { 0xA0, 0x00, 0x00, 0x03, 0x08, /* RID=NIST */ - 0x00, 0x00, 0x10, 0x00 /* PIX=PIV */ }; - int slot = app->slot; + int slot = app_get_slot (app); gpg_error_t err; unsigned char *apt = NULL; size_t aptlen; @@ -3259,12 +3430,12 @@ app_select_piv (app_t app) /* Note that we select using the AID without the 2 octet version * number. This allows for better reporting of future specs. We * need to use the use-zero-for-P2-flag. */ - err = iso7816_select_application_ext (slot, aid, sizeof aid, 0x0001, + err = iso7816_select_application_ext (slot, piv_aid, sizeof piv_aid, 0x0001, &apt, &aptlen); if (err) goto leave; - app->apptype = "PIV"; + app->apptype = APPTYPE_PIV; app->did_chv1 = 0; app->did_chv2 = 0; app->did_chv3 = 0; @@ -3279,7 +3450,7 @@ app_select_piv (app_t app) } s = find_tlv (apt, aptlen, 0x4F, &n); - if (!s || n != 6 || memcmp (s, aid+5, 4)) + if (!s || n != 6 || memcmp (s, piv_aid+5, 4)) { /* The PIX does not match. */ log_error ("piv: missing or invalid DO 0x4F in APT\n"); @@ -3302,7 +3473,7 @@ app_select_piv (app_t app) goto leave; } s = find_tlv (s, n, 0x4F, &n); - if (!s || n != 5 || memcmp (s, aid, 5)) + if (!s || n != 5 || memcmp (s, piv_aid, 5)) { /* The RID does not match. */ log_error ("piv: missing or invalid DO 0x79.4F in APT\n"); @@ -3317,7 +3488,7 @@ app_select_piv (app_t app) goto leave; } - if (app->cardtype && !strcmp (app->cardtype, "yubikey")) + if (app->card->cardtype == CARDTYPE_YUBIKEY) app->app_local->flags.yubikey = 1; @@ -3327,6 +3498,7 @@ app_select_piv (app_t app) dump_all_do (slot); app->fnc.deinit = do_deinit; + app->fnc.reselect = do_reselect; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.readkey = do_readkey; @@ -3340,6 +3512,7 @@ app_select_piv (app_t app) app->fnc.decipher = do_decipher; app->fnc.change_pin = do_change_chv; app->fnc.check_pin = do_check_chv; + app->fnc.with_keygrip = do_with_keygrip; leave: diff --git a/scd/app-sc-hsm.c b/scd/app-sc-hsm.c index 8094b2463..16d25b581 100644 --- a/scd/app-sc-hsm.c +++ b/scd/app-sc-hsm.c @@ -33,7 +33,6 @@ #include "scdaemon.h" #include "iso7816.h" -#include "app-common.h" #include "../common/tlv.h" #include "apdu.h" @@ -484,7 +483,8 @@ read_ef_prkd (app_t app, unsigned short fid, prkdf_object_t *prkdresult, if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */ - err = select_and_read_binary (app->slot, fid, "PrKDF", &buffer, &buflen, 255); + err = select_and_read_binary (app_get_slot (app), + fid, "PrKDF", &buffer, &buflen, 255); if (err) return err; @@ -832,7 +832,7 @@ read_ef_prkd (app_t app, unsigned short fid, prkdf_object_t *prkdresult, xfree (buffer); buffer = NULL; buflen = 0; - err = select_and_read_binary (app->slot, + err = select_and_read_binary (app_get_slot (app), ((SC_HSM_EE_PREFIX << 8) | (fid & 0xFF)), "CertEF", &buffer, &buflen, 1); if (!err && buffer[0] == 0x30) @@ -953,7 +953,8 @@ read_ef_cd (app_t app, unsigned short fid, cdf_object_t *result) if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */ - err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen, 255); + err = select_and_read_binary (app_get_slot (app), fid, "CDF", + &buffer, &buflen, 255); if (err) return err; @@ -1202,7 +1203,7 @@ read_serialno(app_t app) size_t n, objlen, hdrlen, chrlen; int class, tag, constructed, ndef; - err = select_and_read_binary (app->slot, 0x2F02, "EF.C_DevAut", + err = select_and_read_binary (app_get_slot (app), 0x2F02, "EF.C_DevAut", &buffer, &buflen, 512); if (err) return err; @@ -1229,15 +1230,15 @@ read_serialno(app_t app) } chrlen -= 5; - app->serialno = xtrymalloc (chrlen); - if (!app->serialno) + app->card->serialno = xtrymalloc (chrlen); + if (!app->card->serialno) { err = gpg_error_from_syserror (); goto leave; } - app->serialnolen = chrlen; - memcpy (app->serialno, chr, chrlen); + app->card->serialnolen = chrlen; + memcpy (app->card->serialno, chr, chrlen); leave: xfree (buffer); @@ -1260,7 +1261,7 @@ read_meta (app_t app) if (err) return err; - err = list_ef (app->slot, &eflist, &eflistlen); + err = list_ef (app_get_slot (app), &eflist, &eflistlen); if (err) return err; @@ -1454,7 +1455,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf, return 0; } - err = select_and_read_binary (app->slot, cdf->fid, "CD", + err = select_and_read_binary (app_get_slot (app), cdf->fid, "CD", &buffer, &buflen, 4096); if (err) { @@ -1592,7 +1593,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) } else if (!strcmp (name, "$DISPSERIALNO")) { - send_status_info (ctrl, name, app->serialno, app->serialnolen, NULL, 0); + send_status_info (ctrl, name, + app->card->serialno, app->card->serialnolen, NULL, 0); return 0; } @@ -1693,8 +1695,8 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), char *prompt; int sw; - sw = apdu_send_simple (app->slot, 0, 0x00, ISO7816_VERIFY, 0x00, 0x81, - -1, NULL); + sw = apdu_send_simple (app_get_slot (app), + 0, 0x00, ISO7816_VERIFY, 0x00, 0x81, -1, NULL); if (sw == SW_SUCCESS) return 0; /* PIN already verified */ @@ -1719,7 +1721,7 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), prompt = "||Please enter the PIN"; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { err = pincb (pincb_arg, prompt, NULL); if (err) @@ -1728,7 +1730,7 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), return err; } - err = iso7816_verify_kp (app->slot, 0x81, &pininfo); + err = iso7816_verify_kp (app_get_slot (app), 0x81, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else @@ -1740,7 +1742,8 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), return err; } - err = iso7816_verify (app->slot, 0x81, pinvalue, strlen(pinvalue)); + err = iso7816_verify (app_get_slot (app), + 0x81, pinvalue, strlen(pinvalue)); xfree (pinvalue); } if (err) @@ -1883,7 +1886,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, if (err) return err; - sw = apdu_send_le (app->slot, 1, 0x80, 0x68, prkdf->key_reference, algoid, + sw = apdu_send_le (app_get_slot (app), + 1, 0x80, 0x68, prkdf->key_reference, algoid, cdsblklen, cdsblk, 0, outdata, outdatalen); return iso7816_map_sw (sw); } @@ -2018,7 +2022,8 @@ do_decipher (app_t app, const char *keyidstr, if (err) return err; - sw = apdu_send_le (app->slot, 1, 0x80, 0x62, prkdf->key_reference, 0x21, + sw = apdu_send_le (app_get_slot (app), + 1, 0x80, 0x62, prkdf->key_reference, 0x21, p1blklen, p1blk, 0, &rspdata, &rspdatalen); err = iso7816_map_sw (sw); if (err) @@ -2044,13 +2049,13 @@ do_decipher (app_t app, const char *keyidstr, gpg_error_t app_select_sc_hsm (app_t app) { - int slot = app->slot; + int slot = app_get_slot (app); int rc; rc = iso7816_select_application (slot, sc_hsm_aid, sizeof sc_hsm_aid, 0); if (!rc) { - app->apptype = "SC-HSM"; + app->apptype = APPTYPE_SC_HSM; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) @@ -2064,6 +2069,7 @@ app_select_sc_hsm (app_t app) goto leave; app->fnc.deinit = do_deinit; + app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.getattr = do_getattr; diff --git a/scd/app.c b/scd/app.c index 2ee104d9e..57c4b7743 100644 --- a/scd/app.c +++ b/scd/app.c @@ -26,14 +26,142 @@ #include "scdaemon.h" #include "../common/exechelp.h" -#include "app-common.h" #include "iso7816.h" #include "apdu.h" #include "../common/tlv.h" -static npth_mutex_t app_list_lock; -static app_t app_top; +/* Lock to protect the list of cards and its associated + * applications. */ +static npth_mutex_t card_list_lock; + +/* A list of card contexts. A card is a collection of applications + * (described by app_t) on the same physical token. */ +static card_t card_top; + + +/* The list of application names and their select function. If no + * specific application is selected the first available application on + * a card is selected. */ +struct app_priority_list_s +{ + apptype_t apptype; + char const *name; + gpg_error_t (*select_func)(app_t); +}; + +static struct app_priority_list_s app_priority_list[] = + {{ APPTYPE_OPENPGP , "openpgp", app_select_openpgp }, + { APPTYPE_PIV , "piv", app_select_piv }, + { APPTYPE_NKS , "nks", app_select_nks }, + { APPTYPE_P15 , "p15", app_select_p15 }, + { APPTYPE_GELDKARTE, "geldkarte", app_select_geldkarte }, + { APPTYPE_DINSIG , "dinsig", app_select_dinsig }, + { APPTYPE_SC_HSM , "sc-hsm", app_select_sc_hsm }, + { APPTYPE_NONE , NULL, NULL } + /* APPTYPE_UNDEFINED is special and not listed here. */ + }; + + + + +/* Map a cardtype to a string. Never returns NULL. */ +const char * +strcardtype (cardtype_t t) +{ + switch (t) + { + case CARDTYPE_GENERIC: return "generic"; + case CARDTYPE_YUBIKEY: return "yubikey"; + } + return "?"; +} + + +/* Map an application type to a string. Never returns NULL. */ +const char * +strapptype (apptype_t t) +{ + int i; + + for (i=0; app_priority_list[i].apptype; i++) + if (app_priority_list[i].apptype == t) + return app_priority_list[i].name; + return t == APPTYPE_UNDEFINED? "undefined" : t? "?" : "none"; +} + + +/* Return the apptype for NAME. */ +static apptype_t +apptype_from_name (const char *name) +{ + int i; + + if (!name) + return APPTYPE_NONE; + + for (i=0; app_priority_list[i].apptype; i++) + if (!ascii_strcasecmp (app_priority_list[i].name, name)) + return app_priority_list[i].apptype; + if (!ascii_strcasecmp ("undefined", name)) + return APPTYPE_UNDEFINED; + return APPTYPE_NONE; +} + + +/* Initialization function to change the default app_priority_list. + * LIST is a list of comma or space separated strings with application + * names. Unknown names will only result in warning message. + * Application not mentioned in LIST are used in their original order + * after the given once. */ +void +app_update_priority_list (const char *arg) +{ + struct app_priority_list_s save; + char **names; + int i, j, idx; + + names = strtokenize (arg, ", "); + if (!names) + log_fatal ("strtokenize failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + + idx = 0; + for (i=0; names[i]; i++) + { + ascii_strlwr (names[i]); + for (j=0; j < i; j++) + if (!strcmp (names[j], names[i])) + break; + if (j < i) + { + log_info ("warning: duplicate application '%s' in priority list\n", + names[i]); + continue; + } + + for (j=idx; app_priority_list[j].name; j++) + if (!strcmp (names[i], app_priority_list[j].name)) + break; + if (!app_priority_list[j].name) + { + log_info ("warning: unknown application '%s' in priority list\n", + names[i]); + continue; + } + save = app_priority_list[idx]; + app_priority_list[idx] = app_priority_list[j]; + app_priority_list[j] = save; + idx++; + } + log_assert (idx < DIM (app_priority_list)); + + xfree (names); + for (i=0; app_priority_list[i].name; i++) + log_info ("app priority %d: %s\n", i, app_priority_list[i].name); +} + + static void print_progress_line (void *opaque, const char *what, int pc, int cur, int tot) { @@ -48,59 +176,69 @@ print_progress_line (void *opaque, const char *what, int pc, int cur, int tot) } -/* Lock the reader SLOT. This function shall be used right before - calling any of the actual application functions to serialize access - to the reader. We do this always even if the reader is not - actually used. This allows an actual connection to assume that it - never shares a reader (while performing one command). Returns 0 on - success; only then the unlock_reader function must be called after - returning from the handler. */ +/* Lock the CARD. This function shall be used right before calling + * any of the actual application functions to serialize access to the + * reader. We do this always even if the card is not actually used. + * This allows an actual connection to assume that it never shares a + * card (while performing one command). Returns 0 on success; only + * then the unlock_reader function must be called after returning from + * the handler. Right now we assume a that a reader has just one + * card; this may eventually need refinement. */ static gpg_error_t -lock_app (app_t app, ctrl_t ctrl) +lock_card (card_t card, ctrl_t ctrl) { - if (npth_mutex_lock (&app->lock)) + if (npth_mutex_lock (&card->lock)) { gpg_error_t err = gpg_error_from_syserror (); - log_error ("failed to acquire APP lock for %p: %s\n", - app, gpg_strerror (err)); + log_error ("failed to acquire CARD lock for %p: %s\n", + card, gpg_strerror (err)); return err; } - apdu_set_progress_cb (app->slot, print_progress_line, ctrl); - apdu_set_prompt_cb (app->slot, popup_prompt, ctrl); + apdu_set_progress_cb (card->slot, print_progress_line, ctrl); + apdu_set_prompt_cb (card->slot, popup_prompt, ctrl); return 0; } -/* Release a lock on the reader. See lock_reader(). */ -static void -unlock_app (app_t app) -{ - apdu_set_progress_cb (app->slot, NULL, NULL); - apdu_set_prompt_cb (app->slot, NULL, NULL); - if (npth_mutex_unlock (&app->lock)) +/* Release a lock on a card. See lock_reader(). */ +static void +unlock_card (card_t card) +{ + apdu_set_progress_cb (card->slot, NULL, NULL); + apdu_set_prompt_cb (card->slot, NULL, NULL); + + if (npth_mutex_unlock (&card->lock)) { gpg_error_t err = gpg_error_from_syserror (); - log_error ("failed to release APP lock for %p: %s\n", - app, gpg_strerror (err)); + log_error ("failed to release CARD lock for %p: %s\n", + card, gpg_strerror (err)); } } /* This function may be called to print information pertaining to the - current state of this module to the log. */ + * current state of this module to the log. */ void app_dump_state (void) { + card_t c; app_t a; - npth_mutex_lock (&app_list_lock); - for (a = app_top; a; a = a->next) - log_info ("app_dump_state: app=%p type='%s'\n", a, a->apptype); - npth_mutex_unlock (&app_list_lock); + npth_mutex_lock (&card_list_lock); + for (c = card_top; c; c = c->next) + { + log_info ("app_dump_state: card=%p slot=%d type=%s\n", + c, c->slot, strcardtype (c->cardtype)); + for (a=c->app; a; a = a->next) + log_info ("app_dump_state: app=%p type='%s'\n", + a, strapptype (a->apptype)); + } + npth_mutex_unlock (&card_list_lock); } + /* Check whether the application NAME is allowed. This does not mean we have support for it though. */ static int @@ -115,34 +253,71 @@ is_app_allowed (const char *name) } -static gpg_error_t -check_conflict (app_t app, const char *name) +/* This function is mainly used by the serialno command to check for + * an application conflict which may appear if the serialno command is + * used to request a specific application and the connection has + * already done a select_application. Return values are: + * 0 - No conflict + * GPG_ERR_FALSE - Another application is in use but it is possible + * to switch to the requested application. + * Other code - Switching is not possible. + * + * If SERIALNO_BIN is not NULL a coflict is onl asserted if the + * serialno of the card matches. + */ +gpg_error_t +check_application_conflict (card_t card, const char *name, + const unsigned char *serialno_bin, + size_t serialno_bin_len) { - if (!app || !name || (app->apptype && !ascii_strcasecmp (app->apptype, name))) + app_t app; + + if (!card || !name) + return 0; + if (!card->app) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); /* Should not happen. */ + + if (serialno_bin && card->serialno) + { + if (serialno_bin_len != card->serialnolen + || memcmp (serialno_bin, card->serialno, card->serialnolen)) + return 0; /* The card does not match the requested S/N. */ + } + + /* Check whether the requested NAME matches any already selected + * application. */ + for (app = card->app; app; app = app->next) + if (!ascii_strcasecmp (strapptype (app->apptype), name)) + return 0; + + if (card->app->apptype == APPTYPE_UNDEFINED) return 0; - if (app->apptype && !strcmp (app->apptype, "UNDEFINED")) - return 0; + if (card->cardtype == CARDTYPE_YUBIKEY) + { + if (card->app->apptype == APPTYPE_OPENPGP) + { + /* Current app is OpenPGP. */ + if (!ascii_strcasecmp (name, "piv")) + return gpg_error (GPG_ERR_FALSE); /* Switching allowed. */ + } + else if (card->app->apptype == APPTYPE_PIV) + { + /* Current app is PIV. */ + if (!ascii_strcasecmp (name, "openpgp")) + return gpg_error (GPG_ERR_FALSE); /* Switching allowed. */ + } + } log_info ("application '%s' in use - can't switch\n", - app->apptype? app->apptype : ""); + strapptype (card->app->apptype)); return gpg_error (GPG_ERR_CONFLICT); } -/* This function is used by the serialno command to check for an - application conflict which may appear if the serialno command is - used to request a specific application and the connection has - already done a select_application. */ -gpg_error_t -check_application_conflict (const char *name, app_t app) -{ - return check_conflict (app, name); -} - gpg_error_t -app_reset (app_t app, ctrl_t ctrl, int send_reset) +card_reset (card_t card, ctrl_t ctrl, int send_reset) { gpg_error_t err = 0; @@ -150,21 +325,21 @@ app_reset (app_t app, ctrl_t ctrl, int send_reset) { int sw; - lock_app (app, ctrl); - sw = apdu_reset (app->slot); + lock_card (card, ctrl); + sw = apdu_reset (card->slot); if (sw) err = gpg_error (GPG_ERR_CARD_RESET); - app->reset_requested = 1; - unlock_app (app); + card->reset_requested = 1; + unlock_card (card); scd_kick_the_loop (); gnupg_sleep (1); } else { - ctrl->app_ctx = NULL; - release_application (app, 0); + ctrl->card_ctx = NULL; + card_unref (card); } return err; @@ -175,35 +350,37 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, int periodical_check_needed) { gpg_error_t err = 0; + card_t card = NULL; app_t app = NULL; unsigned char *result = NULL; size_t resultlen; int want_undefined; + int i; - /* Need to allocate a new one. */ - app = xtrycalloc (1, sizeof *app); - if (!app) + /* Need to allocate a new card object */ + card = xtrycalloc (1, sizeof *card); + if (!card) { err = gpg_error_from_syserror (); log_info ("error allocating context: %s\n", gpg_strerror (err)); return err; } - app->slot = slot; - app->card_status = (unsigned int)-1; + card->slot = slot; + card->card_status = (unsigned int)-1; - if (npth_mutex_init (&app->lock, NULL)) + if (npth_mutex_init (&card->lock, NULL)) { err = gpg_error_from_syserror (); log_error ("error initializing mutex: %s\n", gpg_strerror (err)); - xfree (app); + xfree (card); return err; } - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) { - xfree (app); + xfree (card); return err; } @@ -221,9 +398,12 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, * config. */ static char const yk_aid[] = { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 }; /*MGR*/ + static char const otp_aid[] = + { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01 }; /*OTP*/ unsigned char *buf; size_t buflen; - const unsigned char *s0, *s1; + const unsigned char *s0; + unsigned char formfactor; size_t n; if (!iso7816_select_application (slot, yk_aid, sizeof yk_aid, @@ -231,7 +411,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, && !iso7816_apdu_direct (slot, "\x00\x1d\x00\x00\x00", 5, 0, NULL, &buf, &buflen)) { - app->cardtype = "yubikey"; + card->cardtype = CARDTYPE_YUBIKEY; if (opt.verbose) { log_info ("Yubico: config="); @@ -243,29 +423,43 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, if (buflen > 1) { s0 = find_tlv (buf+1, buflen-1, 0x04, &n); /* Form factor */ - if (s0 && n == 1) + formfactor = (s0 && n == 1)? *s0 : 0; + + s0 = find_tlv (buf+1, buflen-1, 0x02, &n); /* Serial */ + if (s0 && n >= 4) { - s1 = find_tlv (buf+1, buflen-1, 0x02, &n); /* Serial */ - if (s1 && n >= 4) + card->serialno = xtrymalloc (3 + 1 + n); + if (card->serialno) { - app->serialno = xtrymalloc (3 + 1 + n); - if (app->serialno) - { - app->serialnolen = 3 + 1 + n; - app->serialno[0] = 0xff; - app->serialno[1] = 0x02; - app->serialno[2] = 0x0; - app->serialno[3] = *s0; - memcpy (app->serialno + 4, s1, n); - /* Note that we do not clear the error - * so that no further serial number - * testing is done. After all we just - * set the serial number. */ - } + card->serialnolen = 3 + 1 + n; + card->serialno[0] = 0xff; + card->serialno[1] = 0x02; + card->serialno[2] = 0x0; + card->serialno[3] = formfactor; + memcpy (card->serialno + 4, s0, n); + /* Note that we do not clear the error + * so that no further serial number + * testing is done. After all we just + * set the serial number. */ } - s1 = find_tlv (buf+1, buflen-1, 0x05, &n); /* version */ - if (s1 && n == 3) - app->cardversion = ((s1[0]<<16)|(s1[1]<<8)|s1[2]); + } + + s0 = find_tlv (buf+1, buflen-1, 0x05, &n); /* version */ + if (s0 && n == 3) + card->cardversion = ((s0[0]<<16)|(s0[1]<<8)|s0[2]); + else if (!s0) + { + /* No version - this is not a Yubikey 5. We now + * switch to the OTP app and take the first + * three bytes of the reponse as version + * number. */ + xfree (buf); + buf = NULL; + if (!iso7816_select_application_ext (slot, + otp_aid, sizeof otp_aid, + 1, &buf, &buflen) + && buflen > 3) + card->cardversion = ((buf[0]<<16)|(buf[1]<<8)|buf[2]); } } xfree (buf); @@ -286,7 +480,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, resultlen -= (p-result); if (p && n > resultlen && n == 0x0d && resultlen+1 == n) { - /* The object it does not fit into the buffer. This is an + /* The object does not fit into the buffer. This is an invalid encoding (or the buffer is too short. However, I have some test cards with such an invalid encoding and therefore I use this ugly workaround to return something @@ -300,9 +494,9 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, /* The GDO file is pretty short, thus we simply reuse it for storing the serial number. */ memmove (result, p, n); - app->serialno = result; - app->serialnolen = n; - err = app_munge_serialno (app); + card->serialno = result; + card->serialnolen = n; + err = app_munge_serialno (card); if (err) goto leave; } @@ -312,12 +506,23 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, } } + /* Allocate a new app object. */ + app = xtrycalloc (1, sizeof *app); + if (!app) + { + err = gpg_error_from_syserror (); + log_info ("error allocating app context: %s\n", gpg_strerror (err)); + goto leave; + } + card->app = app; + app->card = card; + /* Figure out the application to use. */ if (want_undefined) { /* We switch to the "undefined" application only if explicitly requested. */ - app->apptype = "UNDEFINED"; + app->apptype = APPTYPE_UNDEFINED; /* Clear the error so that we don't run through the application * selection chain. */ err = 0; @@ -330,26 +535,18 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, goto leave; /* Set a default error so that we run through the application - * selecion chain. */ + * selection chain. */ err = gpg_error (GPG_ERR_NOT_FOUND); } - if (err && is_app_allowed ("openpgp") - && (!name || !strcmp (name, "openpgp"))) - err = app_select_openpgp (app); - if (err && is_app_allowed ("piv") && (!name || !strcmp (name, "piv"))) - err = app_select_piv (app); - if (err && is_app_allowed ("nks") && (!name || !strcmp (name, "nks"))) - err = app_select_nks (app); - if (err && is_app_allowed ("p15") && (!name || !strcmp (name, "p15"))) - err = app_select_p15 (app); - if (err && is_app_allowed ("geldkarte") - && (!name || !strcmp (name, "geldkarte"))) - err = app_select_geldkarte (app); - if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig"))) - err = app_select_dinsig (app); - if (err && is_app_allowed ("sc-hsm") && (!name || !strcmp (name, "sc-hsm"))) - err = app_select_sc_hsm (app); + /* Find the first available app if NAME is NULL or the matching + * NAME but only if that application is also enabled. */ + for (i=0; err && app_priority_list[i].name; i++) + { + if (is_app_allowed (app_priority_list[i].name) + && (!name || !strcmp (name, app_priority_list[i].name))) + err = app_priority_list[i].select_func (app); + } if (err && name && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE) err = gpg_error (GPG_ERR_NOT_SUPPORTED); @@ -362,44 +559,47 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, else log_info ("no supported card application found: %s\n", gpg_strerror (err)); - unlock_app (app); + unlock_card (card); xfree (app); + xfree (card); return err; } - app->periodical_check_needed = periodical_check_needed; - app->next = app_top; - app_top = app; - unlock_app (app); + card->periodical_check_needed = periodical_check_needed; + card->next = card_top; + card_top = card; + + unlock_card (card); return 0; } + /* If called with NAME as NULL, select the best fitting application - and return a context; otherwise select the application with NAME - and return a context. Returns an error code and stores NULL at - R_APP if no application was found or no card is present. */ + * and return its card context; otherwise select the application with + * NAME and return its card context. Returns an error code and stores + * NULL at R_CARD if no application was found or no card is present. */ gpg_error_t -select_application (ctrl_t ctrl, const char *name, app_t *r_app, +select_application (ctrl_t ctrl, const char *name, card_t *r_card, int scan, const unsigned char *serialno_bin, size_t serialno_bin_len) { gpg_error_t err = 0; - app_t a, a_prev = NULL; + card_t card, card_prev = NULL; - *r_app = NULL; + *r_card = NULL; - npth_mutex_lock (&app_list_lock); + npth_mutex_lock (&card_list_lock); - if (scan || !app_top) + if (scan || !card_top) { struct dev_list *l; - int new_app = 0; + int new_card = 0; /* Scan the devices to find new device(s). */ err = apdu_dev_list_start (opt.reader_port, &l); if (err) { - npth_mutex_unlock (&app_list_lock); + npth_mutex_unlock (&card_list_lock); return err; } @@ -408,7 +608,7 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app, int slot; int periodical_check_needed_this; - slot = apdu_open_reader (l, !app_top); + slot = apdu_open_reader (l, !card_top); if (slot < 0) break; @@ -422,7 +622,7 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app, { err = app_new_register (slot, ctrl, name, periodical_check_needed_this); - new_app++; + new_card++; } if (err) @@ -432,76 +632,144 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app, apdu_dev_list_finish (l); /* If new device(s), kick the scdaemon loop. */ - if (new_app) + if (new_card) scd_kick_the_loop (); } - for (a = app_top; a; a = a->next) + for (card = card_top; card; card = card->next) { - lock_app (a, ctrl); + lock_card (card, ctrl); if (serialno_bin == NULL) break; - if (a->serialnolen == serialno_bin_len - && !memcmp (a->serialno, serialno_bin, a->serialnolen)) + if (card->serialnolen == serialno_bin_len + && !memcmp (card->serialno, serialno_bin, card->serialnolen)) break; - unlock_app (a); - a_prev = a; + unlock_card (card); + card_prev = card; } - if (a) + if (card) { - err = check_conflict (a, name); + err = check_application_conflict (card, name, NULL, 0); if (!err) { - a->ref_count++; - *r_app = a; - if (a_prev) + /* Note: We do not use card_ref as we are already locked. */ + card->ref_count++; + *r_card = card; + if (card_prev) { - a_prev->next = a->next; - a->next = app_top; - app_top = a; + card_prev->next = card->next; + card->next = card_top; + card_top = card; } - } - unlock_app (a); + + ctrl->current_apptype = card->app ? card->app->apptype : 0; + } + unlock_card (card); } else err = gpg_error (GPG_ERR_ENODEV); - npth_mutex_unlock (&app_list_lock); + npth_mutex_unlock (&card_list_lock); return err; } +/* This function needs to be called with the NAME of the new + * application to be selected on CARD. On success the application is + * added to the list of the card's active applications as currently + * active application. On error no new application is allocated. + * Selecting an already selected application has no effect. */ +gpg_error_t +select_additional_application (ctrl_t ctrl, const char *name) +{ + gpg_error_t err = 0; + apptype_t req_apptype; + card_t card; + app_t app = NULL; + int i; + + req_apptype = apptype_from_name (name); + if (!req_apptype) + err = gpg_error (GPG_ERR_NOT_FOUND); + + card = ctrl->card_ctx; + if (!card) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + + err = lock_card (card, ctrl); + if (err) + return err; + + /* Check that the requested app has not yet been put onto the list. */ + for (app = card->app; app; app = app->next) + if (app->apptype == req_apptype) + { + /* We already got this one. Note that in this case we don't + * make it the current one but it doesn't matter because + * maybe_switch_app will do that anyway. */ + err = 0; + app = NULL; + goto leave; + } + app = NULL; + + /* Allocate a new app object. */ + app = xtrycalloc (1, sizeof *app); + if (!app) + { + err = gpg_error_from_syserror (); + log_info ("error allocating app context: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Find the app and run the select. */ + for (i=0; app_priority_list[i].apptype; i++) + { + if (app_priority_list[i].apptype == req_apptype + && is_app_allowed (app_priority_list[i].name)) + err = app_priority_list[i].select_func (app); + } + if (!app_priority_list[i].apptype + || (err && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE)) + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + + if (err) + goto leave; + + /* Add this app. We make it the current one to avoid an extra + * reselect by maybe_switch_app after the select we just did. */ + app->next = card->app; + card->app = app; + ctrl->current_apptype = app->apptype; + log_error ("added app '%s' to the card context\n", strapptype(app->apptype)); + + leave: + unlock_card (card); + xfree (app); + return err; +} + + char * get_supported_applications (void) { - const char *list[] = { - "openpgp", - "piv", - "nks", - "p15", - "geldkarte", - "dinsig", - "sc-hsm", - /* Note: "undefined" is not listed here because it needs special - treatment by the client. */ - NULL - }; int idx; size_t nbytes; char *buffer, *p; + const char *s; - for (nbytes=1, idx=0; list[idx]; idx++) - nbytes += strlen (list[idx]) + 1 + 1; + for (nbytes=1, idx=0; (s=app_priority_list[idx].name); idx++) + nbytes += strlen (s) + 1 + 1; buffer = xtrymalloc (nbytes); if (!buffer) return NULL; - for (p=buffer, idx=0; list[idx]; idx++) - if (is_app_allowed (list[idx])) - p = stpcpy (stpcpy (p, list[idx]), ":\n"); + for (p=buffer, idx=0; (s=app_priority_list[idx].name); idx++) + if (is_app_allowed (s)) + p = stpcpy (stpcpy (p, s), ":\n"); *p = 0; return buffer; @@ -510,62 +778,89 @@ get_supported_applications (void) /* Deallocate the application. */ static void -deallocate_app (app_t app) +deallocate_card (card_t card) { - app_t a, a_prev = NULL; + card_t c, c_prev = NULL; + app_t a, anext; - for (a = app_top; a; a = a->next) - if (a == app) + for (c = card_top; c; c = c->next) + if (c == card) { - if (a_prev == NULL) - app_top = a->next; + if (c_prev == NULL) + card_top = c->next; else - a_prev->next = a->next; + c_prev->next = c->next; break; } else - a_prev = a; + c_prev = c; - if (app->ref_count) - log_error ("trying to release context used yet (%d)\n", app->ref_count); + if (card->ref_count) + log_error ("releasing still used card context (%d)\n", card->ref_count); - if (app->fnc.deinit) + for (a = card->app; a; a = anext) { - app->fnc.deinit (app); - app->fnc.deinit = NULL; + if (a->fnc.deinit) + { + a->fnc.deinit (a); + a->fnc.deinit = NULL; + } + anext = a->next; + xfree (a); } - xfree (app->serialno); + scd_clear_current_app (card); - unlock_app (app); - xfree (app); + xfree (card->serialno); + unlock_card (card); + xfree (card); } -/* Free the resources associated with the application APP. APP is - allowed to be NULL in which case this is a no-op. Note that we are - using reference counting to track the users of the application and - actually deferring the deallocation to allow for a later reuse by - a new connection. */ -void -release_application (app_t app, int locked_already) + +/* Increment the reference counter of CARD. Returns CARD. */ +card_t +card_ref (card_t card) { - if (!app) + lock_card (card, NULL); + ++card->ref_count; + unlock_card (card); + return card; +} + + +/* Decrement the reference counter for CARD. Note that we are using + * reference counting to track the users of the card's application and + * are deferring the actual deallocation to allow for a later reuse by + * a new connection. Using NULL for CARD is a no-op. */ +void +card_unref (card_t card) +{ + if (!card) return; - /* We don't deallocate app here. Instead, we keep it. This is + /* We don't deallocate CARD here. Instead, we keep it. This is useful so that a card does not get reset even if only one session is using the card - this way the PIN cache and other cached data are preserved. */ - if (!locked_already) - lock_app (app, NULL); + lock_card (card, NULL); + card_unref_locked (card); + unlock_card (card); +} - if (!app->ref_count) - log_bug ("trying to release an already released context\n"); - --app->ref_count; - if (!locked_already) - unlock_app (app); +/* This is the same as card_unref but assumes that CARD is already + * locked. */ +void +card_unref_locked (card_t card) +{ + if (!card) + return; + + if (!card->ref_count) + log_bug ("tried to release an already released card context\n"); + + --card->ref_count; } @@ -585,30 +880,30 @@ release_application (app_t app, int locked_already) All other serial number not starting with FF are used as they are. */ gpg_error_t -app_munge_serialno (app_t app) +app_munge_serialno (card_t card) { - if (app->serialnolen && app->serialno[0] == 0xff) + if (card->serialnolen && card->serialno[0] == 0xff) { /* The serial number starts with our special prefix. This requires that we put our default prefix "FF0000" in front. */ - unsigned char *p = xtrymalloc (app->serialnolen + 3); + unsigned char *p = xtrymalloc (card->serialnolen + 3); if (!p) return gpg_error_from_syserror (); memcpy (p, "\xff\0", 3); - memcpy (p+3, app->serialno, app->serialnolen); - app->serialnolen += 3; - xfree (app->serialno); - app->serialno = p; + memcpy (p+3, card->serialno, card->serialnolen); + card->serialnolen += 3; + xfree (card->serialno); + card->serialno = p; } - else if (!app->serialnolen) + else if (!card->serialnolen) { unsigned char *p = xtrymalloc (3); if (!p) return gpg_error_from_syserror (); memcpy (p, "\xff\x7f", 3); - app->serialnolen = 3; - xfree (app->serialno); - app->serialno = p; + card->serialnolen = 3; + xfree (card->serialno); + card->serialno = p; } return 0; } @@ -619,52 +914,129 @@ app_munge_serialno (app_t app) returned as a malloced string (hex encoded) in SERIAL. Caller must free SERIAL unless the function returns an error. */ char * -app_get_serialno (app_t app) +card_get_serialno (card_t card) { char *serial; - if (!app) + if (!card) return NULL; - if (!app->serialnolen) + if (!card->serialnolen) serial = xtrystrdup ("FF7F00"); else - serial = bin2hex (app->serialno, app->serialnolen, NULL); + serial = bin2hex (card->serialno, card->serialnolen, NULL); return serial; } +/* Same as card_get_serialno but takes an APP object. */ +char * +app_get_serialno (app_t app) +{ + if (!app || !app->card) + return NULL; + return card_get_serialno (app->card); +} + + +/* Check that the card has been initialized and whether we need to + * switch to another application on the same card. Switching means + * that the new active app will be moved to the head of the list at + * CARD->app. Thus function must be called with the card lock held. */ +static gpg_error_t +maybe_switch_app (ctrl_t ctrl, card_t card) +{ + gpg_error_t err; + app_t app, app_prev; + + if (!card->ref_count || !card->app) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!ctrl->current_apptype) + { + /* For whatever reasons the current apptype has not been set - + * fix that and use the current app. */ + ctrl->current_apptype = card->app->apptype; + return 0; + } + log_debug ("card=%p want=%s card->app=%s\n", + card, strapptype (ctrl->current_apptype), + strapptype (card->app->apptype)); + app_dump_state (); + if (ctrl->current_apptype == card->app->apptype) + return 0; /* No need to switch. */ + app_prev = card->app; + for (app = app_prev->next; app; app_prev = app, app = app->next) + if (app->apptype == ctrl->current_apptype) + break; + if (!app) + return gpg_error (GPG_ERR_WRONG_CARD); + + if (!app->fnc.reselect) + { + log_error ("oops: reselect function missing for '%s'\n", + strapptype(app->apptype)); + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + } + err = app->fnc.reselect (app, ctrl); + if (err) + { + log_error ("error re-selecting '%s': %s\n", + strapptype(app->apptype), gpg_strerror (err)); + return err; + } + /* Swap APP with the head of the app list. Note that APP is not the + * head of the list. */ + app_prev->next = app->next; + app->next = card->app; + card->app = app; + ctrl->current_apptype = app->apptype; + log_error ("switched to '%s'\n", strapptype(app->apptype)); + + return 0; +} + /* Write out the application specific status lines for the LEARN command. */ gpg_error_t -app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) +app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags) { gpg_error_t err; + app_t app; - if (!app) + if (!card) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->fnc.learn_status) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - /* We do not send CARD and APPTYPE if only keypairinfo is requested. */ - if (!(flags &1)) - { - if (app->cardtype) - send_status_direct (ctrl, "CARDTYPE", app->cardtype); - if (app->cardversion) - send_status_printf (ctrl, "CARDVERSION", "%X", app->cardversion); - if (app->apptype) - send_status_direct (ctrl, "APPTYPE", app->apptype); - if (app->appversion) - send_status_printf (ctrl, "APPVERSION", "%X", app->appversion); - } - - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.learn_status (app, ctrl, flags); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.learn_status) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + { + app = card->app; + + /* We do not send CARD and APPTYPE if only keypairinfo is requested. */ + if (!(flags &1)) + { + if (card->cardtype) + send_status_direct (ctrl, "CARDTYPE", strcardtype (card->cardtype)); + if (card->cardversion) + send_status_printf (ctrl, "CARDVERSION", "%X", card->cardversion); + if (app->apptype) + send_status_direct (ctrl, "APPTYPE", strapptype (app->apptype)); + if (app->appversion) + send_status_printf (ctrl, "APPVERSION", "%X", app->appversion); + /* FIXME: Send info for the other active apps of the card? */ + } + + err = app->fnc.learn_status (app, ctrl, flags); + } + + unlock_card (card); return err; } @@ -674,35 +1046,39 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) buffer put into CERT and the length of the certificate put into CERTLEN. */ gpg_error_t -app_readcert (app_t app, ctrl_t ctrl, const char *certid, +app_readcert (card_t card, ctrl_t ctrl, const char *certid, unsigned char **cert, size_t *certlen) { gpg_error_t err; - if (!app) + if (!card) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.readcert) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.readcert (app, certid, cert, certlen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.readcert) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.readcert (card->app, certid, cert, certlen); + + unlock_card (card); return err; } /* Read the key with ID KEYID. On success a canonical encoded - S-expression with the public key will get stored at PK and its - length (for assertions) at PKLEN; the caller must release that - buffer. On error NULL will be stored at PK and PKLEN and an error - code returned. - - This function might not be supported by all applications. */ + * S-expression with the public key will get stored at PK and its + * length (for assertions) at PKLEN; the caller must release that + * buffer. On error NULL will be stored at PK and PKLEN and an error + * code returned. If the key is not required NULL may be passed for + * PK; this makse send if the APP_READKEY_FLAG_INFO has also been set. + * + * This function might not be supported by all applications. */ gpg_error_t -app_readkey (app_t app, ctrl_t ctrl, const char *keyid, +app_readkey (card_t card, ctrl_t ctrl, const char *keyid, unsigned int flags, unsigned char **pk, size_t *pklen) { gpg_error_t err; @@ -712,93 +1088,102 @@ app_readkey (app_t app, ctrl_t ctrl, const char *keyid, if (pklen) *pklen = 0; - if (!app || !keyid || !pk || !pklen) + if (!card || !keyid) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.readkey) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err= app->fnc.readkey (app, keyid, pk, pklen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.readkey) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.readkey (card->app, ctrl, keyid, flags, pk, pklen); + + unlock_card (card); return err; } /* Perform a GETATTR operation. */ gpg_error_t -app_getattr (app_t app, ctrl_t ctrl, const char *name) +app_getattr (card_t card, ctrl_t ctrl, const char *name) { gpg_error_t err; - if (!app || !name || !*name) + if (!card || !name || !*name) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + err = lock_card (card, ctrl); + if (err) + return err; - if (app->cardtype && name && !strcmp (name, "CARDTYPE")) + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (name && !strcmp (name, "CARDTYPE")) { - send_status_direct (ctrl, "CARDTYPE", app->cardtype); - return 0; + send_status_direct (ctrl, "CARDTYPE", strcardtype (card->cardtype)); } - if (app->apptype && name && !strcmp (name, "APPTYPE")) + else if (name && !strcmp (name, "APPTYPE")) { - send_status_direct (ctrl, "APPTYPE", app->apptype); - return 0; + send_status_direct (ctrl, "APPTYPE", strapptype (card->app->apptype)); } - if (name && !strcmp (name, "SERIALNO")) + else if (name && !strcmp (name, "SERIALNO")) { char *serial; - serial = app_get_serialno (app); + serial = card_get_serialno (card); if (!serial) - return gpg_error (GPG_ERR_INV_VALUE); - - send_status_direct (ctrl, "SERIALNO", serial); - xfree (serial); - return 0; + err = gpg_error (GPG_ERR_INV_VALUE); + else + { + send_status_direct (ctrl, "SERIALNO", serial); + xfree (serial); + } } + else if (!card->app->fnc.getattr) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.getattr (card->app, ctrl, name); - if (!app->fnc.getattr) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); - if (err) - return err; - err = app->fnc.getattr (app, ctrl, name); - unlock_app (app); + unlock_card (card); return err; } + /* Perform a SETATTR operation. */ gpg_error_t -app_setattr (app_t app, ctrl_t ctrl, const char *name, +app_setattr (card_t card, ctrl_t ctrl, const char *name, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen) { gpg_error_t err; - if (!app || !name || !*name || !value) + if (!card || !name || !*name || !value) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.setattr) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.setattr) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.setattr (card->app, name, pincb, pincb_arg, + value, valuelen); + + unlock_card (card); return err; } + /* Create the signature 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. */ gpg_error_t -app_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, +app_sign (card_t card, ctrl_t ctrl, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, @@ -806,31 +1191,35 @@ app_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, { gpg_error_t err; - if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.sign) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.sign (app, keyidstr, hashalgo, - pincb, pincb_arg, - indata, indatalen, - outdata, outdatalen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.sign) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.sign (card->app, keyidstr, hashalgo, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + + unlock_card (card); if (opt.verbose) log_info ("operation sign result: %s\n", gpg_strerror (err)); return err; } + /* Create the signature using the INTERNAL AUTHENTICATE command 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. */ gpg_error_t -app_auth (app_t app, ctrl_t ctrl, const char *keyidstr, +app_auth (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, @@ -838,20 +1227,23 @@ app_auth (app_t app, ctrl_t ctrl, const char *keyidstr, { gpg_error_t err; - if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.auth) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.auth (app, keyidstr, - pincb, pincb_arg, - indata, indatalen, - outdata, outdatalen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.auth) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.auth (card->app, keyidstr, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + + unlock_card (card); if (opt.verbose) log_info ("operation auth result: %s\n", gpg_strerror (err)); return err; @@ -862,7 +1254,7 @@ app_auth (app_t app, ctrl_t ctrl, const char *keyidstr, 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. */ gpg_error_t -app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, +app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, @@ -873,21 +1265,24 @@ app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, *r_info = 0; - if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.decipher) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.decipher (app, keyidstr, - pincb, pincb_arg, - indata, indatalen, - outdata, outdatalen, - r_info); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.decipher) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.decipher (card->app, keyidstr, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen, + r_info); + + unlock_card (card); if (opt.verbose) log_info ("operation decipher result: %s\n", gpg_strerror (err)); return err; @@ -896,26 +1291,29 @@ app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, /* Perform the WRITECERT operation. */ gpg_error_t -app_writecert (app_t app, ctrl_t ctrl, - const char *certidstr, - gpg_error_t (*pincb)(void*, const char *, char **), - void *pincb_arg, - const unsigned char *data, size_t datalen) +app_writecert (card_t card, ctrl_t ctrl, + const char *certidstr, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *data, size_t datalen) { gpg_error_t err; - if (!app || !certidstr || !*certidstr || !pincb) + if (!card || !certidstr || !*certidstr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.writecert) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.writecert (app, ctrl, certidstr, - pincb, pincb_arg, data, datalen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.writecert) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.writecert (card->app, ctrl, certidstr, + pincb, pincb_arg, data, datalen); + + unlock_card (card); if (opt.verbose) log_info ("operation writecert result: %s\n", gpg_strerror (err)); return err; @@ -924,7 +1322,7 @@ app_writecert (app_t app, ctrl_t ctrl, /* Perform the WRITEKEY operation. */ gpg_error_t -app_writekey (app_t app, ctrl_t ctrl, +app_writekey (card_t card, ctrl_t ctrl, const char *keyidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, @@ -932,45 +1330,51 @@ app_writekey (app_t app, ctrl_t ctrl, { gpg_error_t err; - if (!app || !keyidstr || !*keyidstr || !pincb) + if (!card || !keyidstr || !*keyidstr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.writekey) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.writekey (app, ctrl, keyidstr, flags, - pincb, pincb_arg, keydata, keydatalen); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.writekey) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.writekey (card->app, ctrl, keyidstr, flags, + pincb, pincb_arg, keydata, keydatalen); + + unlock_card (card); if (opt.verbose) log_info ("operation writekey result: %s\n", gpg_strerror (err)); return err; } -/* Perform a SETATTR operation. */ +/* Perform a GENKEY operation. */ gpg_error_t -app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, +app_genkey (card_t card, ctrl_t ctrl, const char *keynostr, const char *keytype, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; - if (!app || !keynostr || !*keynostr || !pincb) + if (!card || !keynostr || !*keynostr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.genkey) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.genkey (app, ctrl, keynostr, keytype, flags, - createtime, pincb, pincb_arg); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.genkey) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.genkey (card->app, ctrl, keynostr, keytype, flags, + createtime, pincb, pincb_arg); + + unlock_card (card); if (opt.verbose) log_info ("operation genkey result: %s\n", gpg_strerror (err)); return err; @@ -981,44 +1385,51 @@ app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, directly accesses the card without any application specific wrapper. */ gpg_error_t -app_get_challenge (app_t app, ctrl_t ctrl, size_t nbytes, unsigned char *buffer) +app_get_challenge (card_t card, ctrl_t ctrl, + size_t nbytes, unsigned char *buffer) { gpg_error_t err; - if (!app || !nbytes || !buffer) + if (!card || !nbytes || !buffer) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = iso7816_get_challenge (app->slot, nbytes, buffer); - unlock_app (app); + + if (!card->ref_count) + err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + else + err = iso7816_get_challenge (card->slot, nbytes, buffer); + + unlock_card (card); return err; } - /* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */ gpg_error_t -app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, +app_change_pin (card_t card, ctrl_t ctrl, const char *chvnostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; - if (!app || !chvnostr || !*chvnostr || !pincb) + if (!card || !chvnostr || !*chvnostr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.change_pin) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.change_pin (app, ctrl, chvnostr, flags, pincb, pincb_arg); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.change_pin) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.change_pin (card->app, ctrl, + chvnostr, flags, pincb, pincb_arg); + + unlock_card (card); if (opt.verbose) log_info ("operation change_pin result: %s\n", gpg_strerror (err)); return err; @@ -1029,28 +1440,32 @@ app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, be used to initialize a the PIN cache for long lasting other operations. Its use is highly application dependent. */ gpg_error_t -app_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr, +app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; - if (!app || !keyidstr || !*keyidstr || !pincb) + if (!card || !keyidstr || !*keyidstr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); - if (!app->ref_count) - return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - if (!app->fnc.check_pin) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_app (app, ctrl); + err = lock_card (card, ctrl); if (err) return err; - err = app->fnc.check_pin (app, keyidstr, pincb, pincb_arg); - unlock_app (app); + + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.check_pin) + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + else + err = card->app->fnc.check_pin (card->app, keyidstr, pincb, pincb_arg); + + unlock_card (card); if (opt.verbose) log_info ("operation check_pin result: %s\n", gpg_strerror (err)); return err; } + static void report_change (int slot, int old_status, int cur_status) { @@ -1110,26 +1525,27 @@ report_change (int slot, int old_status, int cur_status) xfree (homestr); } + int scd_update_reader_status_file (void) { - app_t a, app_next; + card_t card, card_next; int periodical_check_needed = 0; - npth_mutex_lock (&app_list_lock); - for (a = app_top; a; a = app_next) + npth_mutex_lock (&card_list_lock); + for (card = card_top; card; card = card_next) { int sw; unsigned int status; - lock_app (a, NULL); - app_next = a->next; + lock_card (card, NULL); + card_next = card->next; - if (a->reset_requested) + if (card->reset_requested) status = 0; else { - sw = apdu_get_status (a->slot, 0, &status); + sw = apdu_get_status (card->slot, 0, &status); if (sw == SW_HOST_NO_READER) { /* Most likely the _reader_ has been unplugged. */ @@ -1138,40 +1554,41 @@ scd_update_reader_status_file (void) else if (sw) { /* Get status failed. Ignore that. */ - if (a->periodical_check_needed) + if (card->periodical_check_needed) periodical_check_needed = 1; - unlock_app (a); + unlock_card (card); continue; } } - if (a->card_status != status) + if (card->card_status != status) { - report_change (a->slot, a->card_status, status); - send_client_notifications (a, status == 0); + report_change (card->slot, card->card_status, status); + send_client_notifications (card, status == 0); if (status == 0) { - log_debug ("Removal of a card: %d\n", a->slot); - apdu_close_reader (a->slot); - deallocate_app (a); + log_debug ("Removal of a card: %d\n", card->slot); + apdu_close_reader (card->slot); + deallocate_card (card); } else { - a->card_status = status; - if (a->periodical_check_needed) + card->card_status = status; + if (card->periodical_check_needed) periodical_check_needed = 1; - unlock_app (a); + unlock_card (card); } } else { - if (a->periodical_check_needed) + if (card->periodical_check_needed) periodical_check_needed = 1; - unlock_app (a); + unlock_card (card); } } - npth_mutex_unlock (&app_list_lock); + + npth_mutex_unlock (&card_list_lock); return periodical_check_needed; } @@ -1185,7 +1602,7 @@ initialize_module_command (void) { gpg_error_t err; - if (npth_mutex_init (&app_list_lock, NULL)) + if (npth_mutex_init (&card_list_lock, NULL)) { err = gpg_error_from_syserror (); log_error ("app: error initializing mutex: %s\n", gpg_strerror (err)); @@ -1195,20 +1612,115 @@ initialize_module_command (void) return apdu_init (); } -void + +/* Sort helper for app_send_card_list. */ +static int +compare_card_list_items (const void *arg_a, const void *arg_b) +{ + const card_t a = *(const card_t *)arg_a; + const card_t b = *(const card_t *)arg_b; + + return a->slot - b->slot; +} + + +/* Send status lines with the serialno of all inserted cards. */ +gpg_error_t app_send_card_list (ctrl_t ctrl) { - app_t a; + gpg_error_t err; + card_t c; char buf[65]; + card_t *cardlist = NULL; + int n, ncardlist; - npth_mutex_lock (&app_list_lock); - for (a = app_top; a; a = a->next) + npth_mutex_lock (&card_list_lock); + for (n=0, c = card_top; c; c = c->next) + n++; + cardlist = xtrycalloc (n, sizeof *cardlist); + if (!cardlist) { - if (DIM (buf) < 2 * a->serialnolen + 1) + err = gpg_error_from_syserror (); + goto leave; + } + for (ncardlist=0, c = card_top; c; c = c->next) + cardlist[ncardlist++] = c; + qsort (cardlist, ncardlist, sizeof *cardlist, compare_card_list_items); + + for (n=0; n < ncardlist; n++) + { + if (DIM (buf) < 2 * cardlist[n]->serialnolen + 1) continue; - bin2hex (a->serialno, a->serialnolen, buf); + bin2hex (cardlist[n]->serialno, cardlist[n]->serialnolen, buf); send_status_direct (ctrl, "SERIALNO", buf); } - npth_mutex_unlock (&app_list_lock); + + err = 0; + + leave: + npth_mutex_unlock (&card_list_lock); + xfree (cardlist); + return err; +} + + +/* Execute an action for each app. ACTION can be one of: + * + * - KEYGRIP_ACTION_SEND_DATA + * + * If KEYGRIP_STR matches a public key of any active application + * send information as LF terminated data lines about the public + * key. The format of these lines is + * T + * If a match was found a pointer to the matching application is + * returned. With the KEYGRIP_STR given as NULL, lines for all + * keys will be send and the return value is NULL. + * + * - KEYGRIP_ACTION_WRITE_STATUS + * + * Same as KEYGRIP_ACTION_SEND_DATA but uses status lines instead + * of data lines. + * + * - KEYGRIP_ACTION_LOOKUP + * + * Returns a pointer to the application matching KEYGRIP_STR but + * does not emit any status or data lines. If no key with that + * keygrip is available or KEYGRIP_STR is NULL, NULL is returned. + */ +card_t +app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str) +{ + gpg_error_t err; + card_t c; + app_t a; + + npth_mutex_lock (&card_list_lock); + + for (c = card_top; c; c = c->next) + for (a = c->app; a; a = a->next) + if (a->fnc.with_keygrip) + { + if (!lock_card (c, ctrl)) + { + err = a->fnc.with_keygrip (a, ctrl, action, keygrip_str); + unlock_card (c); + if (!err) + goto leave_the_loop; + } + } + + leave_the_loop: + + /* FIXME: Add app switching logic. The above code assumes that the + * actions can be performend without switching. This needs to be + * checked. */ + + /* Force switching of the app if the selected one is not the current + * one. Changing the current apptype is sufficient to do this. */ + if (c && c->app && c->app->apptype != a->apptype) + ctrl->current_apptype = a->apptype; + + npth_mutex_unlock (&card_list_lock); + return c; } diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index 69df17355..d762490c8 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -110,26 +110,12 @@ /* CCID command timeout. */ #define CCID_CMD_TIMEOUT (5*1000) -/* OpenPGPcard v2.1 requires huge timeout for key generation. */ -#define CCID_CMD_TIMEOUT_LONGER (60*1000) /* Depending on how this source is used we either define our error - output to go to stderr or to the GnuPG based logging functions. We - use the latter when GNUPG_MAJOR_VERSION or GNUPG_SCD_MAIN_HEADER - are defined. */ -#if defined(GNUPG_MAJOR_VERSION) || defined(GNUPG_SCD_MAIN_HEADER) - -#if defined(GNUPG_SCD_MAIN_HEADER) -# include GNUPG_SCD_MAIN_HEADER -#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */ -# include "options.h" -# include "util.h" -# include "memory.h" -# include "cardglue.h" -# else /* This is the modularized GnuPG 1.9 or later. */ + * output to go to stderr or to the GnuPG based logging functions. We + * use the latter when GNUPG_MAJOR_VERSION is defined. */ +#if defined(GNUPG_MAJOR_VERSION) # include "scdaemon.h" -#endif - # define DEBUGOUT(t) do { if (debug_level) \ log_debug (DRVNAME t); } while (0) @@ -175,7 +161,7 @@ # define DEBUGOUT_LF() do { if (debug_level) \ putc ('\n', stderr); } while (0) -#endif /* This source not used by scdaemon. */ +#endif /* This source is not used by scdaemon. */ #ifndef EAGAIN @@ -960,7 +946,7 @@ get_escaped_usb_string (libusb_device_handle *idev, int idx, rc = libusb_control_transfer (idev, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8), 0, - (char*)buf, sizeof buf, 1000 /* ms timeout */); + buf, sizeof buf, 1000 /* ms timeout */); #ifdef USE_NPTH npth_protect (); #endif @@ -975,7 +961,7 @@ get_escaped_usb_string (libusb_device_handle *idev, int idx, rc = libusb_control_transfer (idev, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) + idx, langid, - (char*)buf, sizeof buf, 1000 /* ms timeout */); + buf, sizeof buf, 1000 /* ms timeout */); #ifdef USE_NPTH npth_protect (); #endif @@ -1490,7 +1476,9 @@ intr_cb (struct libusb_transfer *transfer) { DEBUGOUT ("CCID: card removed\n"); handle->powered_off = 1; +#if defined(GNUPG_MAJOR_VERSION) scd_kick_the_loop (); +#endif } else { @@ -1505,7 +1493,9 @@ intr_cb (struct libusb_transfer *transfer) device_removed: DEBUGOUT ("CCID: device removed\n"); handle->powered_off = 1; +#if defined(GNUPG_MAJOR_VERSION) scd_kick_the_loop (); +#endif } } @@ -1731,31 +1721,37 @@ ccid_require_get_status (ccid_driver_t handle) return 1; } - -static void -do_close_reader (ccid_driver_t handle) +static int +send_power_off (ccid_driver_t handle) { int rc; unsigned char msg[100]; size_t msglen; unsigned char seqno; - if (!handle->powered_off) - { - msg[0] = PC_to_RDR_IccPowerOff; - msg[5] = 0; /* slot */ - msg[6] = seqno = handle->seqno++; - msg[7] = 0; /* RFU */ - msg[8] = 0; /* RFU */ - msg[9] = 0; /* RFU */ - set_msg_len (msg, 0); - msglen = 10; + msg[0] = PC_to_RDR_IccPowerOff; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; /* RFU */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, 0); + msglen = 10; - rc = bulk_out (handle, msg, msglen, 0); - if (!rc) - bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, - seqno, 2000, 0); - } + rc = bulk_out (handle, msg, msglen, 0); + if (!rc) + bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, + seqno, 2000, 0); + return rc; +} + +static void +do_close_reader (ccid_driver_t handle) +{ + int rc; + + if (!handle->powered_off) + send_power_off (handle); if (handle->transfer) { @@ -1907,7 +1903,7 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen, npth_unprotect (); #endif rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_out, - (char*)msg, msglen, &transferred, + msg, msglen, &transferred, 5000 /* ms timeout */); #ifdef USE_NPTH npth_protect (); @@ -1954,7 +1950,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, npth_unprotect (); #endif rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_in, - (char*)buffer, length, &msglen, timeout); + buffer, length, &msglen, timeout); #ifdef USE_NPTH npth_protect (); #endif @@ -2074,7 +2070,9 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, { DEBUGOUT ("CCID: card inactive/removed\n"); handle->powered_off = 1; +#if defined(GNUPG_MAJOR_VERSION) scd_kick_the_loop (); +#endif } } @@ -2088,7 +2086,7 @@ static int abort_cmd (ccid_driver_t handle, int seqno) { int rc; - char dummybuf[8]; + unsigned char dummybuf[8]; unsigned char msg[100]; int msglen; @@ -2139,7 +2137,7 @@ abort_cmd (ccid_driver_t handle, int seqno) npth_unprotect (); #endif rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_out, - (char*)msg, msglen, &transferred, + msg, msglen, &transferred, 5000 /* ms timeout */); #ifdef USE_NPTH npth_protect (); @@ -2157,7 +2155,7 @@ abort_cmd (ccid_driver_t handle, int seqno) npth_unprotect (); #endif rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_in, - (char*)msg, sizeof msg, &msglen, + msg, sizeof msg, &msglen, 5000 /*ms timeout*/); #ifdef USE_NPTH npth_protect (); @@ -2278,7 +2276,7 @@ ccid_poll (ccid_driver_t handle) int i, j; rc = libusb_interrupt_transfer (handle->idev, handle->ep_intr, - (char*)msg, sizeof msg, &msglen, + msg, sizeof msg, &msglen, 0 /* ms timeout */ ); if (rc == LIBUSB_ERROR_TIMEOUT) return 0; @@ -2611,6 +2609,21 @@ ccid_get_atr (ccid_driver_t handle, NULL, 0, NULL)) goto again; } + else if (statusbits == 0 && CCID_COMMAND_FAILED (msg)) + { + /* Card was active already, and something went wrong with + PC_to_RDR_IccPowerOn command. It may be baud-rate mismatch + between the card and the reader. To recover from this state, + send PC_to_RDR_IccPowerOff command to reset the card and try + again. + */ + rc = send_power_off (handle); + if (rc) + return rc; + + statusbits = 1; + goto again; + } else if (CCID_COMMAND_FAILED (msg)) return CCID_DRIVER_ERR_CARD_IO_ERROR; @@ -2977,7 +2990,7 @@ ccid_transceive_apdu_level (ccid_driver_t handle, bit 7 1 bit 6 1 bit 5 clear=request,set=response - bit 4..0 0 = resyncronisation request + bit 4..0 0 = resynchronization request 1 = information field size request 2 = abort request 3 = extension of BWT request @@ -3094,7 +3107,7 @@ ccid_transceive (ccid_driver_t handle, msg[0] = PC_to_RDR_XfrBlock; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; - msg[7] = 4; /* bBWI */ + msg[7] = (wait_more ? wait_more : 1); /* bBWI */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, tpdulen); @@ -3120,7 +3133,7 @@ ccid_transceive (ccid_driver_t handle, msg = recv_buffer; rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, via_escape? RDR_to_PC_Escape : RDR_to_PC_DataBlock, seqno, - wait_more? CCID_CMD_TIMEOUT_LONGER: CCID_CMD_TIMEOUT, 0); + (wait_more ? wait_more : 1) * CCID_CMD_TIMEOUT, 0); if (rc) return rc; @@ -3150,6 +3163,7 @@ ccid_transceive (ccid_driver_t handle, (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)? " [more]":"")); + wait_more = 0; if (!(tpdu[1] & 0x80)) { /* This is an I-block. */ retries = 0; @@ -3295,9 +3309,7 @@ ccid_transceive (ccid_driver_t handle, /* Wait time extension request. */ unsigned char bwi = tpdu[3]; - /* Check if it's unusual value which can't be expressed in ATR. */ - if (bwi > 15) - wait_more = 1; + wait_more = bwi; msg = send_buffer; tpdu = msg + hdrlen; @@ -3726,7 +3738,7 @@ print_result (int rc, const unsigned char *data, size_t length) int main (int argc, char **argv) { - int rc; + gpg_error_t err; ccid_driver_t ccid; int slotstat; unsigned char result[512]; @@ -3735,6 +3747,8 @@ main (int argc, char **argv) int verify_123456 = 0; int did_verify = 0; int no_poll = 0; + int idx_max; + struct ccid_dev_table *ccid_table; if (argc) { @@ -3778,27 +3792,36 @@ main (int argc, char **argv) break; } - rc = ccid_open_reader (&ccid, argc? *argv:NULL, NULL); - if (rc) + err = ccid_dev_scan (&idx_max, &ccid_table); + if (err) return 1; + if (idx_max == 0) + return 1; + + err = ccid_open_reader (argc? *argv:NULL, 0, ccid_table, &ccid, NULL); + if (err) + return 1; + + ccid_dev_scan_finish (ccid_table, idx_max); + if (!no_poll) ccid_poll (ccid); fputs ("getting ATR ...\n", stderr); - rc = ccid_get_atr (ccid, NULL, 0, NULL); - if (rc) + err = ccid_get_atr (ccid, NULL, 0, NULL); + if (err) { - print_error (rc); + print_error (err); return 1; } if (!no_poll) ccid_poll (ccid); fputs ("getting slot status ...\n", stderr); - rc = ccid_slot_status (ccid, &slotstat, 1); - if (rc) + err = ccid_slot_status (ccid, &slotstat, 1); + if (err) { - print_error (rc); + print_error (err); return 1; } @@ -3809,10 +3832,10 @@ main (int argc, char **argv) { static unsigned char apdu[] = { 0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01}; - rc = ccid_transceive (ccid, - apdu, sizeof apdu, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); + err = ccid_transceive (ccid, + apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (err, result, resultlen); } @@ -3822,9 +3845,9 @@ main (int argc, char **argv) fputs ("getting OpenPGP DO 0x65 ....\n", stderr); { static unsigned char apdu[] = { 0, 0xCA, 0, 0x65, 254 }; - rc = ccid_transceive (ccid, apdu, sizeof apdu, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); + err = ccid_transceive (ccid, apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (err, result, resultlen); } if (!no_pinpad) @@ -3834,22 +3857,18 @@ main (int argc, char **argv) if (!no_pinpad) { static unsigned char apdu[] = { 0, 0x20, 0, 0x81 }; + pininfo_t pininfo = { 0, 0, 0 }; - - if (ccid_transceive_secure (ccid, - apdu, sizeof apdu, - 1, 0, 0, 0, + if (ccid_transceive_secure (ccid, apdu, sizeof apdu, &pininfo, NULL, 0, NULL)) fputs ("can't verify using a PIN-Pad reader\n", stderr); else { - fputs ("verifying CHV1 using the PINPad ....\n", stderr); + fputs ("verifying CHV1 using the PINPad ....\n", stderr); - rc = ccid_transceive_secure (ccid, - apdu, sizeof apdu, - 1, 0, 0, 0, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); + err = ccid_transceive_secure (ccid, apdu, sizeof apdu, &pininfo, + result, sizeof result, &resultlen); + print_result (err, result, resultlen); did_verify = 1; } } @@ -3860,20 +3879,20 @@ main (int argc, char **argv) { static unsigned char apdu[] = {0, 0x20, 0, 0x81, 6, '1','2','3','4','5','6'}; - rc = ccid_transceive (ccid, apdu, sizeof apdu, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); + err = ccid_transceive (ccid, apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (err, result, resultlen); } } - if (!rc) + if (!err) { fputs ("getting OpenPGP DO 0x5E ....\n", stderr); { static unsigned char apdu[] = { 0, 0xCA, 0, 0x5E, 254 }; - rc = ccid_transceive (ccid, apdu, sizeof apdu, - result, sizeof result, &resultlen); - print_result (rc, result, resultlen); + err = ccid_transceive (ccid, apdu, sizeof apdu, + result, sizeof result, &resultlen); + print_result (err, result, resultlen); } } @@ -3884,7 +3903,7 @@ main (int argc, char **argv) /* * Local Variables: - * compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c" + * compile-command: "gcc -DTEST -DGPGRT_ENABLE_ES_MACROS -DHAVE_NPTH -DUSE_NPTH -Wall -I/usr/include/libusb-1.0 -I/usr/local/include -lusb-1.0 -g ccid-driver.c -lnpth -lgpg-error" * End: */ #endif /*TEST*/ diff --git a/scd/command.c b/scd/command.c index 0d1a5cd3f..0096ca96d 100644 --- a/scd/command.c +++ b/scd/command.c @@ -33,7 +33,6 @@ #include "scdaemon.h" #include #include -#include "app-common.h" #include "iso7816.h" #include "apdu.h" /* Required for apdu_*_reader (). */ #include "atr.h" @@ -145,10 +144,10 @@ hex_to_buffer (const char *string, size_t *r_length) static void do_reset (ctrl_t ctrl, int send_reset) { - app_t app = ctrl->app_ctx; + card_t card = ctrl->card_ctx; - if (app) - app_reset (app, ctrl, IS_LOCKED (ctrl)? 0: send_reset); + if (card) + card_reset (card, ctrl, IS_LOCKED (ctrl)? 0: send_reset); /* If we hold a lock, unlock now. */ if (locked_session && ctrl->server_local == locked_session) @@ -157,6 +156,8 @@ do_reset (ctrl_t ctrl, int send_reset) log_info ("implicitly unlocking due to RESET\n"); } } + + static gpg_error_t reset_notify (assuan_context_t ctx, char *line) @@ -211,38 +212,50 @@ open_card (ctrl_t ctrl) if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); - if (ctrl->app_ctx) + if (ctrl->card_ctx) return 0; - return select_application (ctrl, NULL, &ctrl->app_ctx, 0, NULL, 0); + return select_application (ctrl, NULL, &ctrl->card_ctx, 0, NULL, 0); } /* Explicitly open a card for a specific use of APPTYPE or SERIALNO. */ static gpg_error_t -open_card_with_request (ctrl_t ctrl, const char *apptype, const char *serialno) +open_card_with_request (ctrl_t ctrl, + const char *apptypestr, const char *serialno) { gpg_error_t err; unsigned char *serialno_bin = NULL; size_t serialno_bin_len = 0; - app_t app = ctrl->app_ctx; - - /* If we are already initialized for one specific application we - need to check that the client didn't requested a specific - application different from the one in use before we continue. */ - if (apptype && ctrl->app_ctx) - return check_application_conflict (apptype, ctrl->app_ctx); - - /* Re-scan USB devices. Release APP, before the scan. */ - ctrl->app_ctx = NULL; - release_application (app, 0); + card_t card = ctrl->card_ctx; if (serialno) serialno_bin = hex_to_buffer (serialno, &serialno_bin_len); - err = select_application (ctrl, apptype, &ctrl->app_ctx, 1, - serialno_bin, serialno_bin_len); - xfree (serialno_bin); + /* If we are already initialized for one specific application we + need to check that the client didn't requested a specific + application different from the one in use before we continue. */ + if (apptypestr && ctrl->card_ctx) + { + err = check_application_conflict (ctrl->card_ctx, apptypestr, + serialno_bin, serialno_bin_len); + if (gpg_err_code (err) == GPG_ERR_FALSE) + { + /* Different application but switching is supported. */ + err = select_additional_application (ctrl, apptypestr); + } + goto leave; + } + /* Re-scan USB devices. Release CARD, before the scan. */ + /* FIXME: Is a card_unref sufficient or do we need to deallocate? */ + ctrl->card_ctx = NULL; + card_unref (card); + + err = select_application (ctrl, apptypestr, &ctrl->card_ctx, 1, + serialno_bin, serialno_bin_len); + + leave: + xfree (serialno_bin); return err; } @@ -313,7 +326,7 @@ cmd_serialno (assuan_context_t ctx, char *line) c->server_local->card_removed = 0; } - serial = app_get_serialno (ctrl->app_ctx); + serial = card_get_serialno (ctrl->card_ctx); if (!serial) return gpg_error (GPG_ERR_INV_VALUE); @@ -411,20 +424,20 @@ cmd_learn (assuan_context_t ctx, char *line) { const char *reader; char *serial; - app_t app = ctrl->app_ctx; + card_t card = ctrl->card_ctx; - if (!app) + if (!card) return gpg_error (GPG_ERR_CARD_NOT_PRESENT); - reader = apdu_get_reader_name (app->slot); + reader = apdu_get_reader_name (card->slot); if (!reader) return out_of_core (); send_status_direct (ctrl, "READER", reader); /* No need to free the string of READER. */ - serial = app_get_serialno (ctrl->app_ctx); + serial = card_get_serialno (ctrl->card_ctx); if (!serial) - return gpg_error (GPG_ERR_INV_VALUE); + return gpg_error (GPG_ERR_INV_VALUE); rc = assuan_write_status (ctx, "SERIALNO", serial); if (rc < 0) @@ -461,7 +474,7 @@ cmd_learn (assuan_context_t ctx, char *line) /* Let the application print out its collection of useful status information. */ if (!rc) - rc = app_write_learn_status (ctrl->app_ctx, ctrl, only_keypairinfo); + rc = app_write_learn_status (ctrl->card_ctx, ctrl, only_keypairinfo); return rc; } @@ -484,7 +497,7 @@ cmd_readcert (assuan_context_t ctx, char *line) return rc; line = xstrdup (line); /* Need a copy of the line. */ - rc = app_readcert (ctrl->app_ctx, ctrl, line, &cert, &ncert); + rc = app_readcert (ctrl->card_ctx, ctrl, line, &cert, &ncert); if (rc) log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); xfree (line); @@ -502,19 +515,20 @@ cmd_readcert (assuan_context_t ctx, char *line) static const char hlp_readkey[] = - "READKEY [--advanced] |\n" + "READKEY [--advanced] [--info[-only]] |\n" "\n" "Return the public key for the given cert or key ID as a standard\n" - "S-expression.\n" - "In --advanced mode it returns the S-expression in advanced format.\n" - "\n" - "Note that this function may even be used on a locked card."; + "S-expression. With --advanced the S-expression is returned in\n" + "advanced format. With --info a KEYPAIRINFO status line is also\n" + "emitted; with --info-only the regular output is suppressed."; static gpg_error_t cmd_readkey (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; int advanced = 0; + int opt_info = 0; + int opt_nokey = 0; unsigned char *cert = NULL; unsigned char *pk = NULL; size_t ncert, pklen; @@ -524,6 +538,10 @@ cmd_readkey (assuan_context_t ctx, char *line) if (has_option (line, "--advanced")) advanced = 1; + if (has_option (line, "--info")) + opt_info = 1; + if (has_option (line, "--info-only")) + opt_info = opt_nokey = 1; line = skip_options (line); line = xstrdup (line); /* Need a copy of the line. */ @@ -531,14 +549,16 @@ cmd_readkey (assuan_context_t ctx, char *line) /* If the application supports the READKEY function we use that. Otherwise we use the old way by extracting it from the certificate. */ - rc = app_readkey (ctrl->app_ctx, ctrl, line, &pk, &pklen); + rc = app_readkey (ctrl->card_ctx, ctrl, line, + opt_info? APP_READKEY_FLAG_INFO : 0, + opt_nokey? NULL : &pk, &pklen); if (!rc) ; /* Okay, got that key. */ else if (gpg_err_code (rc) == GPG_ERR_UNSUPPORTED_OPERATION || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { /* Fall back to certificate reading. */ - rc = app_readcert (ctrl->app_ctx, ctrl, line, &cert, &ncert); + rc = app_readcert (ctrl->card_ctx, ctrl, line, &cert, &ncert); if (rc) { log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); @@ -551,6 +571,26 @@ cmd_readkey (assuan_context_t ctx, char *line) gpg_strerror (rc)); goto leave; } + + if (opt_info) + { + char keygripstr[KEYGRIP_LEN*2+1]; + + rc = app_help_get_keygrip_string_pk (pk, pklen, keygripstr); + if (rc) + { + log_error ("app_help_get_keygrip_string failed: %s\n", + gpg_strerror (rc)); + goto leave; + } + + /* FIXME: Using LINE is not correct because it might be an + * OID and has not been canonicalized (i.e. uppercased). */ + send_status_info (ctrl, "KEYPAIRINFO", + keygripstr, strlen (keygripstr), + line, strlen (line), + NULL, (size_t)0); + } } else { @@ -558,7 +598,9 @@ cmd_readkey (assuan_context_t ctx, char *line) goto leave; } - if (advanced) + if (opt_nokey) + ; + else if (advanced) { gcry_sexp_t s_key; unsigned char *pkadv; @@ -728,6 +770,8 @@ cmd_pksign (assuan_context_t ctx, char *line) size_t outdatalen; char *keyidstr; int hash_algo; + card_t card; + int direct = 0; if (has_option (line, "--hash=rmd160")) hash_algo = GCRY_MD_RMD160; @@ -760,11 +804,30 @@ cmd_pksign (assuan_context_t ctx, char *line) if (!keyidstr) return out_of_core (); - rc = app_sign (ctrl->app_ctx, ctrl, - keyidstr, hash_algo, - pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen); + /* When it's a keygrip, we directly use the card, with no change of + ctrl->card_ctx. */ + if (strlen (keyidstr) == 40) + { + card = app_do_with_keygrip (ctrl, KEYGRIP_ACTION_LOOKUP, keyidstr); + direct = 1; + } + else + card = ctrl->card_ctx; + + if (card) + { + if (direct) + card_ref (card); + rc = app_sign (card, ctrl, + keyidstr, hash_algo, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + if (direct) + card_unref (card); + } + else + rc = gpg_error (GPG_ERR_NO_SECKEY); xfree (keyidstr); if (rc) @@ -793,11 +856,13 @@ cmd_pkauth (assuan_context_t ctx, char *line) unsigned char *outdata; size_t outdatalen; char *keyidstr; + card_t card; + int direct = 0; if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* We have to use a copy of the key ID because the function may use @@ -807,9 +872,29 @@ cmd_pkauth (assuan_context_t ctx, char *line) if (!keyidstr) return out_of_core (); - rc = app_auth (ctrl->app_ctx, ctrl, keyidstr, pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen); + /* When it's a keygrip, we directly use CARD, with no change of + ctrl->card_ctx. */ + if (strlen (keyidstr) == 40) + { + card = app_do_with_keygrip (ctrl, KEYGRIP_ACTION_LOOKUP, keyidstr); + direct = 1; + } + else + card = ctrl->card_ctx; + + if (card) + { + if (direct) + card_ref (card); + rc = app_auth (card, ctrl, keyidstr, pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + if (direct) + card_unref (card); + } + else + rc = gpg_error (GPG_ERR_NO_SECKEY); + xfree (keyidstr); if (rc) { @@ -838,6 +923,8 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) size_t outdatalen; char *keyidstr; unsigned int infoflags; + card_t card; + int direct = 0; if ((rc = open_card (ctrl))) return rc; @@ -845,9 +932,29 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) keyidstr = xtrystrdup (line); if (!keyidstr) return out_of_core (); - rc = app_decipher (ctrl->app_ctx, ctrl, keyidstr, pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen, &infoflags); + + /* When it's a keygrip, we directly use CARD, with no change of + ctrl->card_ctx. */ + if (strlen (keyidstr) == 40) + { + card = app_do_with_keygrip (ctrl, KEYGRIP_ACTION_LOOKUP, keyidstr); + direct = 1; + } + else + card = ctrl->card_ctx; + + if (card) + { + if (direct) + card_ref (card); + rc = app_decipher (card, ctrl, keyidstr, pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen, &infoflags); + if (direct) + card_unref (card); + } + else + rc = gpg_error (GPG_ERR_NO_SECKEY); xfree (keyidstr); if (rc) @@ -905,7 +1012,7 @@ cmd_getattr (assuan_context_t ctx, char *line) /* FIXME: Applications should not return sensitive data if the card is locked. */ - rc = app_getattr (ctrl->app_ctx, ctrl, keyword); + rc = app_getattr (ctrl->card_ctx, ctrl, keyword); return rc; } @@ -967,7 +1074,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line) assuan_end_confidential (ctx); if (!err) { - err = app_setattr (ctrl->app_ctx, ctrl, keyword, pin_cb, ctx, + err = app_setattr (ctrl->card_ctx, ctrl, keyword, pin_cb, ctx, value, nbytes); wipememory (value, nbytes); xfree (value); @@ -977,7 +1084,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line) else { nbytes = percent_plus_unescape_inplace (line, 0); - err = app_setattr (ctrl->app_ctx, ctrl, keyword, pin_cb, ctx, + err = app_setattr (ctrl->card_ctx, ctrl, keyword, pin_cb, ctx, (const unsigned char*)line, nbytes); } @@ -1018,7 +1125,7 @@ cmd_writecert (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); certid = xtrystrdup (certid); @@ -1035,7 +1142,7 @@ cmd_writecert (assuan_context_t ctx, char *line) } /* Write the certificate to the card. */ - rc = app_writecert (ctrl->app_ctx, ctrl, certid, + rc = app_writecert (ctrl->card_ctx, ctrl, certid, pin_cb, ctx, certdata, certdatalen); xfree (certid); xfree (certdata); @@ -1080,7 +1187,7 @@ cmd_writekey (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); keyid = xtrystrdup (keyid); @@ -1098,7 +1205,7 @@ cmd_writekey (assuan_context_t ctx, char *line) } /* Write the key to the card. */ - rc = app_writekey (ctrl->app_ctx, ctrl, keyid, force? 1:0, + rc = app_writekey (ctrl->card_ctx, ctrl, keyid, force? 1:0, pin_cb, ctx, keydata, keydatalen); xfree (keyid); xfree (keydata); @@ -1172,7 +1279,7 @@ cmd_genkey (assuan_context_t ctx, char *line) if ((err = open_card (ctrl))) goto leave; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); keyref = keyref_buffer = xtrystrdup (keyref); @@ -1181,7 +1288,7 @@ cmd_genkey (assuan_context_t ctx, char *line) err = gpg_error_from_syserror (); goto leave; } - err = app_genkey (ctrl->app_ctx, ctrl, keyref, opt_algo, + err = app_genkey (ctrl->card_ctx, ctrl, keyref, opt_algo, force? APP_GENKEY_FLAG_FORCE : 0, timestamp, pin_cb, ctx); @@ -1216,14 +1323,14 @@ cmd_random (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); buffer = xtrymalloc (nbytes); if (!buffer) return out_of_core (); - rc = app_get_challenge (ctrl->app_ctx, ctrl, nbytes, buffer); + rc = app_get_challenge (ctrl->card_ctx, ctrl, nbytes, buffer); if (!rc) { rc = assuan_send_data (ctx, buffer, nbytes); @@ -1277,13 +1384,13 @@ cmd_passwd (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); chvnostr = xtrystrdup (chvnostr); if (!chvnostr) return out_of_core (); - rc = app_change_pin (ctrl->app_ctx, ctrl, chvnostr, flags, pin_cb, ctx); + rc = app_change_pin (ctrl->card_ctx, ctrl, chvnostr, flags, pin_cb, ctx); if (rc) log_error ("command passwd failed: %s\n", gpg_strerror (rc)); xfree (chvnostr); @@ -1334,7 +1441,7 @@ cmd_checkpin (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* We have to use a copy of the key ID because the function may use @@ -1344,7 +1451,7 @@ cmd_checkpin (assuan_context_t ctx, char *line) if (!idstr) return out_of_core (); - rc = app_check_pin (ctrl->app_ctx, ctrl, idstr, pin_cb, ctx); + rc = app_check_pin (ctrl->card_ctx, ctrl, idstr, pin_cb, ctx); xfree (idstr); if (rc) log_error ("app_check_pin failed: %s\n", gpg_strerror (rc)); @@ -1525,7 +1632,7 @@ cmd_getinfo (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - app_send_card_list (ctrl); + rc = app_send_card_list (ctrl); } else rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT"); @@ -1547,14 +1654,14 @@ static gpg_error_t cmd_restart (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - app_t app = ctrl->app_ctx; + card_t card = ctrl->card_ctx; (void)line; - if (app) + if (card) { - ctrl->app_ctx = NULL; - release_application (app, 0); + ctrl->card_ctx = NULL; + card_unref (card); } if (locked_session && ctrl->server_local == locked_session) { @@ -1576,10 +1683,10 @@ cmd_disconnect (assuan_context_t ctx, char *line) (void)line; - if (!ctrl->app_ctx) + if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - apdu_disconnect (ctrl->app_ctx->slot); + apdu_disconnect (ctrl->card_ctx->slot); return 0; } @@ -1608,7 +1715,7 @@ static gpg_error_t cmd_apdu (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - app_t app; + card_t card; int rc; unsigned char *apdu; size_t apdulen; @@ -1638,8 +1745,8 @@ cmd_apdu (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl))) return rc; - app = ctrl->app_ctx; - if (!app) + card = ctrl->card_ctx; + if (!card) return gpg_error (GPG_ERR_CARD_NOT_PRESENT); if (with_atr) @@ -1648,7 +1755,7 @@ cmd_apdu (assuan_context_t ctx, char *line) size_t atrlen; char hexbuf[400]; - atr = apdu_get_atr (app->slot, &atrlen); + atr = apdu_get_atr (card->slot, &atrlen); if (!atr || atrlen > sizeof hexbuf - 2 ) { rc = gpg_error (GPG_ERR_INV_CARD); @@ -1693,7 +1800,7 @@ cmd_apdu (assuan_context_t ctx, char *line) unsigned char *result = NULL; size_t resultlen; - rc = apdu_send_direct (app->slot, exlen, + rc = apdu_send_direct (card->slot, exlen, apdu, apdulen, handle_more, NULL, &result, &resultlen); if (rc) @@ -1728,6 +1835,87 @@ cmd_killscd (assuan_context_t ctx, char *line) } +static const char hlp_keyinfo[] = + "KEYINFO [--list] [--data] \n" + "\n" + "Return information about the key specified by the KEYGRIP. If the\n" + "key is not available GPG_ERR_NOT_FOUND is returned. If the option\n" + "--list is given the keygrip is ignored and information about all\n" + "available keys are returned. Unless --data is given, the\n" + "information is returned as a status line using the format:\n" + "\n" + " KEYINFO T \n" + "\n" + "KEYGRIP is the keygrip.\n" + "\n" + "SERIALNO is an ASCII string with the serial number of the\n" + " smartcard. If the serial number is not known a single\n" + " dash '-' is used instead.\n" + "\n" + "IDSTR is the IDSTR used to distinguish keys on a smartcard. If it\n" + " is not known a dash is used instead.\n" + "\n" + "More information may be added in the future."; +static gpg_error_t +cmd_keyinfo (assuan_context_t ctx, char *line) +{ + int list_mode; + int opt_data; + int action; + char *keygrip_str; + ctrl_t ctrl = assuan_get_pointer (ctx); + card_t card; + + list_mode = has_option (line, "--list"); + opt_data = has_option (line, "--data"); + line = skip_options (line); + + if (list_mode) + keygrip_str = NULL; + else + keygrip_str = line; + + if (opt_data) + action = KEYGRIP_ACTION_SEND_DATA; + else + action = KEYGRIP_ACTION_WRITE_STATUS; + + card = app_do_with_keygrip (ctrl, action, keygrip_str); + + if (!list_mode && !card) + return gpg_error (GPG_ERR_NOT_FOUND); + return 0; +} + + +/* Send a keyinfo string as used by the KEYGRIP_ACTION_SEND_DATA. If + * DATA is true the string is emitted as a data line, else as a status + * line. */ +void +send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str, + const char *serialno, const char *idstr) +{ + char *string; + assuan_context_t ctx = ctrl->server_local->assuan_ctx; + + string = xtryasprintf ("%s T %s %s%s", keygrip_str, + serialno? serialno : "-", + idstr? idstr : "-", + data? "\n" : ""); + + if (!string) + return; + + if (!data) + assuan_write_status (ctx, "KEYINFO", string); + else + assuan_send_data (ctx, string, strlen (string)); + + xfree (string); + return; +} + + /* Tell the assuan library about our commands */ static int @@ -1763,6 +1951,7 @@ register_commands (assuan_context_t ctx) { "DISCONNECT", cmd_disconnect,hlp_disconnect }, { "APDU", cmd_apdu, hlp_apdu }, { "KILLSCD", cmd_killscd, hlp_killscd }, + { "KEYINFO", cmd_keyinfo, hlp_keyinfo }, { NULL } }; int i, rc; @@ -1890,6 +2079,20 @@ scd_command_handler (ctrl_t ctrl, int fd) } +/* Clear the current application info for CARD from all sessions. + * This is used while deallocating a card. */ +void +scd_clear_current_app (card_t card) +{ + struct server_local_s *sl; + + for (sl=session_list; sl; sl = sl->next_session) + { + if (sl->ctrl_backlink->card_ctx == card) + sl->ctrl_backlink->current_apptype = APPTYPE_NONE; + } +} + /* Send a line with status information via assuan and escape all given buffers. The variable elements are pairs of (char *, size_t), terminated with a (NULL, 0). */ @@ -2001,9 +2204,10 @@ popup_prompt (void *opaque, int on) } -/* Helper to send the clients a status change notification. */ +/* Helper to send the clients a status change notification. Note that + * this function assumes that APP is already locked. */ void -send_client_notifications (app_t app, int removal) +send_client_notifications (card_t card, int removal) { struct { pid_t pid; @@ -2018,7 +2222,7 @@ send_client_notifications (app_t app, int removal) struct server_local_s *sl; for (sl=session_list; sl; sl = sl->next_session) - if (sl->ctrl_backlink && sl->ctrl_backlink->app_ctx == app) + if (sl->ctrl_backlink && sl->ctrl_backlink->card_ctx == card) { pid_t pid; #ifdef HAVE_W32_SYSTEM @@ -2029,9 +2233,9 @@ send_client_notifications (app_t app, int removal) if (removal) { - sl->ctrl_backlink->app_ctx = NULL; + sl->ctrl_backlink->card_ctx = NULL; sl->card_removed = 1; - release_application (app, 1); + card_unref_locked (card); } if (!sl->event_signal || !sl->assuan_ctx) diff --git a/scd/iso7816.c b/scd/iso7816.c index d9f3336c7..954aa3d4a 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -23,20 +23,9 @@ #include #include -#if defined(GNUPG_SCD_MAIN_HEADER) -#include GNUPG_SCD_MAIN_HEADER -#elif GNUPG_MAJOR_VERSION == 1 -/* This is used with GnuPG version < 1.9. The code has been source - copied from the current GnuPG >= 1.9 and is maintained over - there. */ -#include "options.h" -#include "errors.h" -#include "memory.h" -#include "../common/util.h" -#include "../common/i18n.h" -#else /* GNUPG_MAJOR_VERSION != 1 */ -#include "scdaemon.h" -#endif /* GNUPG_MAJOR_VERSION != 1 */ +#if defined(GNUPG_MAJOR_VERSION) +# include "scdaemon.h" +#endif /*GNUPG_MAJOR_VERSION*/ #include "iso7816.h" #include "apdu.h" diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 507108db0..e89569e5d 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -46,7 +46,6 @@ #include "../common/i18n.h" #include "../common/sysutils.h" -#include "app-common.h" #include "iso7816.h" #include "apdu.h" #include "ccid-driver.h" @@ -98,6 +97,7 @@ enum cmd_and_opt_values oAllowAdmin, oDenyAdmin, oDisableApplication, + oApplicationPriority, oEnablePinpadVarlen, oListenBacklog }; @@ -154,6 +154,8 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oDenyAdmin, "deny-admin", N_("deny the use of admin card commands")), ARGPARSE_s_s (oDisableApplication, "disable-application", "@"), + ARGPARSE_s_s (oApplicationPriority, "application-priority", + N_("|LIST|Change the application priority to LIST")), ARGPARSE_s_n (oEnablePinpadVarlen, "enable-pinpad-varlen", N_("use variable length input for pinpad")), ARGPARSE_s_s (oHomedir, "homedir", "@"), @@ -436,6 +438,7 @@ main (int argc, char **argv ) struct assuan_malloc_hooks malloc_hooks; int res; npth_t pipecon_handler; + const char *application_priority = NULL; early_system_init (); set_strusage (my_strusage); @@ -616,6 +619,10 @@ main (int argc, char **argv ) add_to_strlist (&opt.disabled_applications, pargs.r.ret_str); break; + case oApplicationPriority: + application_priority = pargs.r.ret_str; + break; + case oEnablePinpadVarlen: opt.enable_pinpad_varlen = 1; break; case oListenBacklog: @@ -720,6 +727,7 @@ main (int argc, char **argv ) es_printf ("disable-pinpad:%lu:\n", GC_OPT_FLAG_NONE ); es_printf ("card-timeout:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, 0); es_printf ("enable-pinpad-varlen:%lu:\n", GC_OPT_FLAG_NONE ); + es_printf ("application-priority:%lu:\n", GC_OPT_FLAG_NONE ); scd_exit (0); } @@ -739,6 +747,9 @@ main (int argc, char **argv ) log_debug ("... okay\n"); } + if (application_priority) + app_update_priority_list (application_priority); + if (pipe_server) { /* This is the simple pipe based server */ diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 73589ade8..b709b1cbc 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -30,6 +30,8 @@ #include #include "../common/util.h" #include "../common/sysutils.h" +#include "app-common.h" + /* To convey some special hash algorithms we use algorithm numbers reserved for application use. */ @@ -84,6 +86,7 @@ struct #define DBG_READER (opt.debug & DBG_READER_VALUE) struct server_local_s; +struct card_ctx_s; struct app_ctx_s; struct server_control_s @@ -101,7 +104,12 @@ struct server_control_s associated. Note that this is shared with the other connections: All connections accessing the same reader are using the same application context. */ - struct app_ctx_s *app_ctx; + struct card_ctx_s *card_ctx; + + /* The currently active application for this context. We need to + * know this for cards which are able to switch on the fly between + * apps. */ + apptype_t current_apptype; /* Helper to store the value we are going to sign */ struct @@ -111,7 +119,6 @@ struct server_control_s } in_data; }; -typedef struct app_ctx_s *app_t; /*-- scdaemon.c --*/ void scd_exit (int rc); @@ -120,14 +127,20 @@ const char *scd_get_socket_name (void); /*-- command.c --*/ gpg_error_t initialize_module_command (void); int scd_command_handler (ctrl_t, int); +void scd_clear_current_app (card_t card); void send_status_info (ctrl_t ctrl, const char *keyword, ...) GPGRT_ATTR_SENTINEL(1); void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args); gpg_error_t send_status_printf (ctrl_t ctrl, const char *keyword, const char *format, ...) GPGRT_ATTR_PRINTF(3,4); +void send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str, + const char *serialno, const char *idstr); void popup_prompt (void *opaque, int on); -void send_client_notifications (app_t app, int removal); + +/* Take care: this function assumes that CARD is locked. */ +void send_client_notifications (card_t card, int removal); + void scd_kick_the_loop (void); int get_active_connection_count (void); diff --git a/sm/call-agent.c b/sm/call-agent.c index 4f2b83f56..b37c2e53d 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -409,19 +409,19 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, { case GCRY_PK_RSA: rc = gcry_sexp_build (&sig, NULL, "(sig-val(rsa(s%b)))", - sigbuflen, sigbuf); + (int)sigbuflen, sigbuf); break; case GCRY_PK_ECC: rc = gcry_sexp_build (&sig, NULL, "(sig-val(ecdsa(r%b)(s%b)))", - sigbuflen/2, sigbuf, - sigbuflen/2, sigbuf + sigbuflen/2); + (int)sigbuflen/2, sigbuf, + (int)sigbuflen/2, sigbuf + sigbuflen/2); break; case GCRY_PK_EDDSA: rc = gcry_sexp_build (&sig, NULL, "(sig-val(eddsa(r%b)(s%b)))", - sigbuflen/2, sigbuf, - sigbuflen/2, sigbuf + sigbuflen/2); + (int)sigbuflen/2, sigbuf, + (int)sigbuflen/2, sigbuf + sigbuflen/2); break; default: @@ -785,9 +785,9 @@ scd_keypairinfo_status_cb (void *opaque, const char *line) { sl = append_to_strlist (listaddr, line); p = sl->d; - /* Make sure that we only have two tokes so that future - extensions of the format won't change the format expected by - the caller. */ + /* Make sure that we only have two tokens so that future + * extensions of the format won't change the format expected by + * the caller. */ while (*p && !spacep (p)) p++; if (*p) @@ -796,7 +796,22 @@ scd_keypairinfo_status_cb (void *opaque, const char *line) p++; while (*p && !spacep (p)) p++; - *p = 0; + if (*p) + { + *p++ = 0; + while (spacep (p)) + p++; + while (*p && !spacep (p)) + { + switch (*p++) + { + case 'c': sl->flags |= GCRY_PK_USAGE_CERT; break; + case 's': sl->flags |= GCRY_PK_USAGE_SIGN; break; + case 'e': sl->flags |= GCRY_PK_USAGE_ENCR; break; + case 'a': sl->flags |= GCRY_PK_USAGE_AUTH; break; + } + } + } } } @@ -806,7 +821,7 @@ scd_keypairinfo_status_cb (void *opaque, const char *line) /* Call the agent to read the keypairinfo lines of the current card. The list is returned as a string made up of the keygrip, a space - and the keyid. */ + and the keyid. The flags of the string carry the usage bits. */ int gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list) { @@ -821,7 +836,7 @@ gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list) inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; - rc = assuan_transact (agent_ctx, "SCD LEARN --force", + rc = assuan_transact (agent_ctx, "SCD LEARN --keypairinfo", NULL, NULL, default_inq_cb, &inq_parm, scd_keypairinfo_status_cb, &list); diff --git a/sm/certlist.c b/sm/certlist.c index 12a492518..b3d113bfd 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -48,7 +48,7 @@ static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9"; debugging). MODE 4 is for certificate signing, MODE for COSP response signing. */ static int -cert_usage_p (ksba_cert_t cert, int mode) +cert_usage_p (ksba_cert_t cert, int mode, int silent) { gpg_error_t err; unsigned int use; @@ -118,7 +118,7 @@ cert_usage_p (ksba_cert_t cert, int mode) if (gpg_err_code (err) == GPG_ERR_NO_DATA) { err = 0; - if (opt.verbose && mode < 2) + if (opt.verbose && mode < 2 && !silent) log_info (_("no key usage specified - assuming all usages\n")); use = ~0; } @@ -139,8 +139,9 @@ cert_usage_p (ksba_cert_t cert, int mode) { if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) return 0; - log_info (_("certificate should not have " - "been used for certification\n")); + if (!silent) + log_info (_("certificate should not have " + "been used for certification\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } @@ -151,8 +152,9 @@ cert_usage_p (ksba_cert_t cert, int mode) || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN |KSBA_KEYUSAGE_CRL_SIGN)))) return 0; - log_info (_("certificate should not have " - "been used for OCSP response signing\n")); + if (!silent) + log_info (_("certificate should not have " + "been used for OCSP response signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } @@ -162,19 +164,22 @@ cert_usage_p (ksba_cert_t cert, int mode) ) return 0; - log_info (mode==3? _("certificate should not have been used for encryption\n"): - mode==2? _("certificate should not have been used for signing\n"): - mode==1? _("certificate is not usable for encryption\n"): - _("certificate is not usable for signing\n")); + if (!silent) + log_info + (mode==3? _("certificate should not have been used for encryption\n"): + mode==2? _("certificate should not have been used for signing\n"): + mode==1? _("certificate is not usable for encryption\n"): + /**/ _("certificate is not usable for signing\n")); + return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } /* Return 0 if the cert is usable for signing */ int -gpgsm_cert_use_sign_p (ksba_cert_t cert) +gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent) { - return cert_usage_p (cert, 0); + return cert_usage_p (cert, 0, silent); } @@ -182,31 +187,31 @@ gpgsm_cert_use_sign_p (ksba_cert_t cert) int gpgsm_cert_use_encrypt_p (ksba_cert_t cert) { - return cert_usage_p (cert, 1); + return cert_usage_p (cert, 1, 0); } int gpgsm_cert_use_verify_p (ksba_cert_t cert) { - return cert_usage_p (cert, 2); + return cert_usage_p (cert, 2, 0); } int gpgsm_cert_use_decrypt_p (ksba_cert_t cert) { - return cert_usage_p (cert, 3); + return cert_usage_p (cert, 3, 0); } int gpgsm_cert_use_cert_p (ksba_cert_t cert) { - return cert_usage_p (cert, 4); + return cert_usage_p (cert, 4, 0); } int gpgsm_cert_use_ocsp_p (ksba_cert_t cert) { - return cert_usage_p (cert, 5); + return cert_usage_p (cert, 5, 0); } @@ -341,25 +346,20 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, first_subject = ksba_cert_get_subject (cert, 0); first_issuer = ksba_cert_get_issuer (cert, 0); } - rc = secret? gpgsm_cert_use_sign_p (cert) + rc = secret? gpgsm_cert_use_sign_p (cert, 0) : gpgsm_cert_use_encrypt_p (cert); if (gpg_err_code (rc) == GPG_ERR_WRONG_KEY_USAGE) { /* There might be another certificate with the correct usage, so we try again */ - if (!wrong_usage) - { /* save the first match */ - wrong_usage = rc; - ksba_cert_release (cert); - cert = NULL; - goto get_next; - } - else if (same_subject_issuer (first_subject, first_issuer, - cert)) + if (!wrong_usage + || same_subject_issuer (first_subject, first_issuer,cert)) { - wrong_usage = rc; + if (!wrong_usage) + wrong_usage = rc; /* save error of the first match */ ksba_cert_release (cert); cert = NULL; + log_info (_("looking for another certificate\n")); goto get_next; } else @@ -403,8 +403,8 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, first_issuer, cert2) && ((gpg_err_code ( - secret? gpgsm_cert_use_sign_p (cert2) - : gpgsm_cert_use_encrypt_p (cert2) + secret? gpgsm_cert_use_sign_p (cert2,0) + : gpgsm_cert_use_encrypt_p (cert2) ) ) == GPG_ERR_WRONG_KEY_USAGE)); if (tmp) diff --git a/sm/certreqgen-ui.c b/sm/certreqgen-ui.c index 70e5739e8..ae9ec35d0 100644 --- a/sm/certreqgen-ui.c +++ b/sm/certreqgen-ui.c @@ -249,6 +249,7 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream) gcry_sexp_t s_pkey; char *algostr = NULL; const char *keyref; + int any = 0; keyref = strchr (sl->d, ' '); if (keyref) @@ -257,12 +258,33 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream) if (!gpgsm_agent_readkey (ctrl, 1, keyref, &pkey)) { if (!gcry_sexp_new (&s_pkey, pkey, 0, 0)) - algostr = pubkey_algo_string (s_pkey); + algostr = pubkey_algo_string (s_pkey, NULL); gcry_sexp_release (s_pkey); } xfree (pkey); } - tty_printf (" (%d) %s %s\n", count, sl->d, algostr); + tty_printf (" (%d) %s %s", count, sl->d, algostr); + if ((sl->flags & GCRY_PK_USAGE_CERT)) + { + tty_printf ("%scert", any?",":" ("); + any = 1; + } + if ((sl->flags & GCRY_PK_USAGE_SIGN)) + { + tty_printf ("%ssign", any?",":" ("); + any = 1; + } + if ((sl->flags & GCRY_PK_USAGE_AUTH)) + { + tty_printf ("%sauth", any?",":" ("); + any = 1; + } + if ((sl->flags & GCRY_PK_USAGE_ENCR)) + { + tty_printf ("%sencr", any?",":" ("); + any = 1; + } + tty_printf ("%s\n", any?")":""); xfree (algostr); } xfree (answer); diff --git a/sm/decrypt.c b/sm/decrypt.c index b0ab63f00..ec9800840 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -397,16 +397,17 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) char *hexkeygrip = NULL; char *desc = NULL; char kidbuf[16+1]; + int tmp_rc; *kidbuf = 0; - rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); - if (rc == -1 && recp) + tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); + if (tmp_rc == -1 && recp) break; /* no more recipients */ audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp); - if (rc) + if (tmp_rc) log_error ("recp %d - error getting info: %s\n", - recp, gpg_strerror (rc)); + recp, gpg_strerror (tmp_rc)); else { ksba_cert_t cert = NULL; @@ -569,7 +570,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp); if (tmp_rc) log_error ("recp %d - error getting info: %s\n", - recp, gpg_strerror (rc)); + recp, gpg_strerror (tmp_rc)); else { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); @@ -583,7 +584,8 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) if (!any_key) { - rc = gpg_error (GPG_ERR_NO_SECKEY); + if (!rc) + rc = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } } diff --git a/sm/delete.c b/sm/delete.c index f359cc595..b370406de 100644 --- a/sm/delete.c +++ b/sm/delete.c @@ -113,7 +113,8 @@ delete_one (ctrl_t ctrl, const char *username) goto leave; } - /* We need to search again to get back to the right position. */ + /* We need to search again to get back to the right position. Neo + * that the lock is kept until the KH is released. */ rc = keydb_lock (kh); if (rc) { @@ -132,7 +133,7 @@ delete_one (ctrl_t ctrl, const char *username) goto leave; } - rc = keydb_delete (kh, duplicates ? 0 : 1); + rc = keydb_delete (kh); if (rc) goto leave; if (opt.verbose) diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 2f9e5bfd2..f5837079d 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -192,6 +192,8 @@ enum cmd_and_opt_values { oNoRandomSeedFile, oNoCommonCertsImport, oIgnoreCertExtension, + oAuthenticode, + oAttribute, oNoAutostart }; @@ -402,6 +404,8 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oNoCommonCertsImport, "no-common-certs-import", "@"), ARGPARSE_s_s (oIgnoreCertExtension, "ignore-cert-extension", "@"), ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"), + ARGPARSE_s_n (oAuthenticode, "authenticode", "@"), + ARGPARSE_s_s (oAttribute, "attribute", "@"), /* Command aliases. */ ARGPARSE_c (aListKeys, "list-key", "@"), @@ -426,6 +430,8 @@ static struct debug_flags_s debug_flags [] = { DBG_MEMSTAT_VALUE, "memstat" }, { DBG_HASHING_VALUE, "hashing" }, { DBG_IPC_VALUE , "ipc" }, + { DBG_CLOCK_VALUE , "clock" }, + { DBG_LOOKUP_VALUE , "lookup" }, { 0, NULL } }; @@ -1283,8 +1289,12 @@ main ( int argc, char **argv) case oDebugNoChainValidation: opt.no_chain_validation = 1; break; case oDebugIgnoreExpiration: opt.ignore_expiration = 1; break; - case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break; - case oLoggerFD: log_set_fd (pargs.r.ret_int ); break; + case oStatusFD: + ctrl.status_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 1); + break; + case oLoggerFD: + log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1)); + break; case oWithMD5Fingerprint: opt.with_md5_fingerprint=1; /*fall through*/ case oWithFingerprint: @@ -1456,6 +1466,12 @@ main ( int argc, char **argv) add_to_strlist (&opt.ignored_cert_extensions, pargs.r.ret_str); break; + case oAuthenticode: opt.authenticode = 1; break; + + case oAttribute: + add_to_strlist (&opt.attributes, pargs.r.ret_str); + break; + case oNoAutostart: opt.autostart = 0; break; case oCompliance: @@ -1738,6 +1754,8 @@ main ( int argc, char **argv) if (!do_not_setup_keys) { + int errcount = log_get_errorcount (0); + for (sl = locusr; sl ; sl = sl->next) { int rc = gpgsm_add_to_certlist (&ctrl, sl->d, 1, &signerlist, 0); @@ -1766,6 +1784,15 @@ main ( int argc, char **argv) if ((sl->flags & 1)) do_add_recipient (&ctrl, sl->d, &recplist, 1, recp_required); } + + /* We do not require a recipient for decryption but because + * recipients and signers are always checked and log_error is + * sometimes used (for failed signing keys or due to a failed + * CRL checking) that would have bumbed up the error counter. + * We clear the counter in the decryption case because there is + * no reason to force decryption to fail. */ + if (cmd == aDecrypt && !errcount) + log_get_errorcount (1); /* clear counter */ } if (log_get_errorcount(0)) diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 7a5e4917d..65fff853a 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -149,6 +149,21 @@ struct strlist_t ignored_cert_extensions; enum gnupg_compliance_mode compliance; + + /* Enable creation of authenticode signatures. */ + int authenticode; + + /* A list of extra attributes put into a signed data object. For a + * signed each attribute each string has the format: + * :s: + * and for an unsigned attribute + * :u: + * The OID is in the usual dotted decimal for. The HEX_OR_FILENAME + * is either a list of hex digits or a filename with the DER encoded + * value. A filename is detected by the presence of a slash in the + * HEX_OR_FILENAME. The actual value needs to be encoded as a SET OF + * attribute values. */ + strlist_t attributes; } opt; /* Debug values and macros. */ @@ -160,6 +175,8 @@ struct #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_IPC_VALUE 1024 /* debug assuan communication */ +#define DBG_CLOCK_VALUE 4096 +#define DBG_LOOKUP_VALUE 8192 /* debug the key lookup */ #define DBG_X509 (opt.debug & DBG_X509_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) @@ -167,6 +184,8 @@ struct #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) +#define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE) +#define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) /* Forward declaration for an object defined in server.c */ struct server_local_s; @@ -318,7 +337,7 @@ int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, int gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert); /*-- certlist.c --*/ -int gpgsm_cert_use_sign_p (ksba_cert_t cert); +int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent); int gpgsm_cert_use_encrypt_p (ksba_cert_t cert); int gpgsm_cert_use_verify_p (ksba_cert_t cert); int gpgsm_cert_use_decrypt_p (ksba_cert_t cert); diff --git a/sm/keydb.c b/sm/keydb.c index f66a5766d..53e3cf887 100644 --- a/sm/keydb.c +++ b/sm/keydb.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -47,7 +46,6 @@ struct resource_item { KEYBOX_HANDLE kr; } u; void *token; - dotlock_t lockhandle; }; static struct resource_item all_resources[MAX_KEYDB_RESOURCES]; @@ -58,7 +56,14 @@ static int any_registered; struct keydb_handle { + + /* If this flag is set the resources is locked. */ int locked; + + /* If this flag is set a lock will only be released by + * keydb_release. */ + int keep_lock; + int found; int saved_found; int current; @@ -346,26 +351,20 @@ keydb_add_resource (ctrl_t ctrl, const char *url, int force, int *auto_created) err = gpg_error (GPG_ERR_RESOURCE_LIMIT); else { + KEYBOX_HANDLE kbxhd; + all_resources[used_resources].type = rt; all_resources[used_resources].u.kr = NULL; /* Not used here */ all_resources[used_resources].token = token; - all_resources[used_resources].lockhandle - = dotlock_create (filename, 0); - if (!all_resources[used_resources].lockhandle) - log_fatal ( _("can't create lock for '%s'\n"), filename); - - /* Do a compress run if needed and the file is not locked. */ - if (!dotlock_take (all_resources[used_resources].lockhandle, 0)) + /* Do a compress run if needed and the keybox is not locked. */ + kbxhd = keybox_new_x509 (token, 0); + if (kbxhd) { - KEYBOX_HANDLE kbxhd = keybox_new_x509 (token, 0); + if (!keybox_lock (kbxhd, 1, 0)) + keybox_compress (kbxhd); - if (kbxhd) - { - keybox_compress (kbxhd); - keybox_release (kbxhd); - } - dotlock_release (all_resources[used_resources].lockhandle); + keybox_release (kbxhd); } used_resources++; @@ -401,11 +400,14 @@ keydb_new (void) KEYDB_HANDLE hd; int i, j; + if (DBG_CLOCK) + log_clock ("%s: enter\n", __func__); + hd = xcalloc (1, sizeof *hd); hd->found = -1; hd->saved_found = -1; - assert (used_resources <= MAX_KEYDB_RESOURCES); + log_assert (used_resources <= MAX_KEYDB_RESOURCES); for (i=j=0; i < used_resources; i++) { switch (all_resources[i].type) @@ -415,7 +417,6 @@ keydb_new (void) case KEYDB_RESOURCE_TYPE_KEYBOX: hd->active[j].type = all_resources[i].type; hd->active[j].token = all_resources[i].token; - hd->active[j].lockhandle = all_resources[i].lockhandle; hd->active[j].u.kr = keybox_new_x509 (all_resources[i].token, 0); if (!hd->active[j].u.kr) { @@ -429,6 +430,8 @@ keydb_new (void) hd->used = j; active_handles++; + if (DBG_CLOCK) + log_clock ("%s: leave (hd=%p)\n", __func__, hd); return hd; } @@ -439,9 +442,14 @@ keydb_release (KEYDB_HANDLE hd) if (!hd) return; - assert (active_handles > 0); + + if (DBG_CLOCK) + log_clock ("%s: enter (hd=%p)\n", __func__, hd); + + log_assert (active_handles > 0); active_handles--; + hd->keep_lock = 0; unlock_all (hd); for (i=0; i < hd->used; i++) { @@ -455,7 +463,9 @@ keydb_release (KEYDB_HANDLE hd) } } - xfree (hd); + xfree (hd); + if (DBG_CLOCK) + log_clock ("%s: leave\n", __func__); } @@ -526,17 +536,26 @@ keydb_set_ephemeral (KEYDB_HANDLE hd, int yes) /* If the keyring has not yet been locked, lock it now. This - operation is required before any update operation; it is optional - for an insert operation. The lock is released with - keydb_released. */ + * operation is required before any update operation; it is optional + * for an insert operation. The lock is kept until a keydb_release so + * that internal unlock_all calls have no effect. */ gpg_error_t keydb_lock (KEYDB_HANDLE hd) { + gpg_error_t err; + if (!hd) return gpg_error (GPG_ERR_INV_HANDLE); - if (hd->locked) - return 0; /* Already locked. */ - return lock_all (hd); + + if (DBG_CLOCK) + log_clock ("%s: enter (hd=%p)\n", __func__, hd); + err = lock_all (hd); + if (!err) + hd->keep_lock = 1; + + if (DBG_CLOCK) + log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); + return err; } @@ -556,8 +575,7 @@ lock_all (KEYDB_HANDLE hd) case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: - if (hd->active[i].lockhandle) - rc = dotlock_take (hd->active[i].lockhandle, -1); + rc = keybox_lock (hd->active[i].u.kr, 1, -1); break; } if (rc) @@ -566,7 +584,7 @@ lock_all (KEYDB_HANDLE hd) if (rc) { - /* revert the already set locks */ + /* Revert the already set locks. */ for (i--; i >= 0; i--) { switch (hd->active[i].type) @@ -574,8 +592,7 @@ lock_all (KEYDB_HANDLE hd) case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: - if (hd->active[i].lockhandle) - dotlock_release (hd->active[i].lockhandle); + keybox_lock (hd->active[i].u.kr, 0, 0); break; } } @@ -583,10 +600,7 @@ lock_all (KEYDB_HANDLE hd) else hd->locked = 1; - /* make_dotlock () does not yet guarantee that errno is set, thus - we can't rely on the error reason and will simply use - EACCES. */ - return rc? gpg_error (GPG_ERR_EACCES) : 0; + return rc; } static void @@ -594,7 +608,7 @@ unlock_all (KEYDB_HANDLE hd) { int i; - if (!hd->locked) + if (!hd->locked || hd->keep_lock) return; for (i=hd->used-1; i >= 0; i--) @@ -604,8 +618,7 @@ unlock_all (KEYDB_HANDLE hd) case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: - if (hd->active[i].lockhandle) - dotlock_release (hd->active[i].lockhandle); + keybox_lock (hd->active[i].u.kr, 0, 0); break; } } @@ -638,6 +651,8 @@ keydb_push_found_state (KEYDB_HANDLE hd) hd->saved_found = hd->found; hd->found = -1; + if (DBG_CLOCK) + log_clock ("%s: done (hd=%p)\n", __func__, hd); } @@ -661,6 +676,8 @@ keydb_pop_found_state (KEYDB_HANDLE hd) keybox_pop_found_state (hd->active[hd->found].u.kr); break; } + if (DBG_CLOCK) + log_clock ("%s: done (hd=%p)\n", __func__, hd); } @@ -678,9 +695,16 @@ keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert) if (!hd) return gpg_error (GPG_ERR_INV_VALUE); - if ( hd->found < 0 || hd->found >= hd->used) - return -1; /* nothing found */ + if (DBG_CLOCK) + log_clock ("%s: enter (hd=%p)\n", __func__, hd); + if ( hd->found < 0 || hd->found >= hd->used) + { + rc = -1; /* nothing found */ + goto leave; + } + + rc = GPG_ERR_BUG; switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: @@ -691,9 +715,13 @@ keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert) break; } + leave: + if (DBG_CLOCK) + log_clock ("%s: leave (rc=%d)\n", __func__, rc); return rc; } + /* Return a flag of the last found object. WHICH is the flag requested; it should be one of the KEYBOX_FLAG_ values. If the operation is successful, the flag value will be stored at the address given by @@ -701,14 +729,21 @@ keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert) gpg_error_t keydb_get_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int *value) { - int err = 0; + gpg_error_t err; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); - if ( hd->found < 0 || hd->found >= hd->used) - return gpg_error (GPG_ERR_NOTHING_FOUND); + if (DBG_CLOCK) + log_clock ("%s: enter (hd=%p)\n", __func__, hd); + if ( hd->found < 0 || hd->found >= hd->used) + { + err = gpg_error (GPG_ERR_NOTHING_FOUND); + goto leave; + } + + err = gpg_error (GPG_ERR_BUG); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: @@ -719,9 +754,13 @@ keydb_get_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int *value) break; } + leave: + if (DBG_CLOCK) + log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } + /* Set a flag of the last found object. WHICH is the flag to be set; it should be one of the KEYBOX_FLAG_ values. If the operation is successful, the flag value will be stored in the keybox. Note, @@ -731,16 +770,25 @@ keydb_get_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int *value) gpg_error_t keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int value) { - int err = 0; + gpg_error_t err = 0; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); + if (DBG_CLOCK) + log_clock ("%s: enter (hd=%p)\n", __func__, hd); + if ( hd->found < 0 || hd->found >= hd->used) - return gpg_error (GPG_ERR_NOTHING_FOUND); + { + err = gpg_error (GPG_ERR_NOTHING_FOUND); + goto leave; + } if (!hd->locked) - return gpg_error (GPG_ERR_NOT_LOCKED); + { + err = gpg_error (GPG_ERR_NOT_LOCKED); + goto leave; + } switch (hd->active[hd->found].type) { @@ -752,16 +800,19 @@ keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int value) break; } + leave: + if (DBG_CLOCK) + log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } /* * Insert a new Certificate into one of the resources. */ -int +gpg_error_t keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert) { - int rc = -1; + gpg_error_t err; int idx; unsigned char digest[20]; @@ -771,104 +822,136 @@ keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert) if (opt.dry_run) return 0; + if (DBG_CLOCK) + log_clock ("%s: enter (hd=%p)\n", __func__, hd); + if ( hd->found >= 0 && hd->found < hd->used) idx = hd->found; else if ( hd->current >= 0 && hd->current < hd->used) idx = hd->current; else - return gpg_error (GPG_ERR_GENERAL); + { + err = gpg_error (GPG_ERR_GENERAL); + goto leave; + } if (!hd->locked) - return gpg_error (GPG_ERR_NOT_LOCKED); + { + err = gpg_error (GPG_ERR_NOT_LOCKED); + goto leave; + } gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/ + err = gpg_error (GPG_ERR_BUG); switch (hd->active[idx].type) { case KEYDB_RESOURCE_TYPE_NONE: - rc = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_GENERAL); break; case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_insert_cert (hd->active[idx].u.kr, cert, digest); + err = keybox_insert_cert (hd->active[idx].u.kr, cert, digest); break; } unlock_all (hd); - return rc; + + leave: + if (DBG_CLOCK) + log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); + return err; } /* Update the current keyblock with KB. */ -int +/* Note: This function is currently not called. */ +gpg_error_t keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert) { - int rc = 0; + gpg_error_t err; unsigned char digest[20]; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if ( hd->found < 0 || hd->found >= hd->used) - return -1; /* nothing found */ + return gpg_error (GPG_ERR_NOT_FOUND); if (opt.dry_run) return 0; - rc = lock_all (hd); - if (rc) - return rc; + if (DBG_CLOCK) + log_clock ("%s: enter (hd=%p)\n", __func__, hd); + + err = lock_all (hd); + if (err) + goto leave; gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/ + err = gpg_error (GPG_ERR_BUG); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: - rc = gpg_error (GPG_ERR_GENERAL); /* oops */ + err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest); + err = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest); break; } unlock_all (hd); - return rc; + leave: + if (DBG_CLOCK) + log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); + return err; } /* * The current keyblock or cert will be deleted. */ -int -keydb_delete (KEYDB_HANDLE hd, int unlock) +gpg_error_t +keydb_delete (KEYDB_HANDLE hd) { - int rc = -1; + gpg_error_t err; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if ( hd->found < 0 || hd->found >= hd->used) - return -1; /* nothing found */ + return gpg_error (GPG_ERR_NOT_FOUND); - if( opt.dry_run ) + if (opt.dry_run) return 0; - if (!hd->locked) - return gpg_error (GPG_ERR_NOT_LOCKED); + if (DBG_CLOCK) + log_clock ("%s: enter (hd=%p)\n", __func__, hd); + if (!hd->locked) + { + err = gpg_error (GPG_ERR_NOT_LOCKED); + goto leave; + } + + err = gpg_error (GPG_ERR_BUG); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: - rc = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_GENERAL); break; case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_delete (hd->active[hd->found].u.kr); + err = keybox_delete (hd->active[hd->found].u.kr); break; } - if (unlock) - unlock_all (hd); - return rc; + unlock_all (hd); + + leave: + if (DBG_CLOCK) + log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); + return err; } @@ -941,29 +1024,92 @@ keydb_rebuild_caches (void) gpg_error_t keydb_search_reset (KEYDB_HANDLE hd) { + gpg_error_t err = 0; int i; - gpg_error_t rc = 0; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); + if (DBG_CLOCK) + log_clock ("%s: enter (hd=%p)\n", __func__, hd); + hd->current = 0; hd->found = -1; /* and reset all resources */ - for (i=0; !rc && i < hd->used; i++) + for (i=0; !err && i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_search_reset (hd->active[i].u.kr); + err = keybox_search_reset (hd->active[i].u.kr); break; } } - return rc; + + if (DBG_CLOCK) + log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); + return err; } + +char * +keydb_search_desc_dump (struct keydb_search_desc *desc) +{ + char *fpr; + char *result; + + switch (desc->mode) + { + case KEYDB_SEARCH_MODE_EXACT: + return xasprintf ("EXACT: '%s'", desc->u.name); + case KEYDB_SEARCH_MODE_SUBSTR: + return xasprintf ("SUBSTR: '%s'", desc->u.name); + case KEYDB_SEARCH_MODE_MAIL: + return xasprintf ("MAIL: '%s'", desc->u.name); + case KEYDB_SEARCH_MODE_MAILSUB: + return xasprintf ("MAILSUB: '%s'", desc->u.name); + case KEYDB_SEARCH_MODE_MAILEND: + return xasprintf ("MAILEND: '%s'", desc->u.name); + case KEYDB_SEARCH_MODE_WORDS: + return xasprintf ("WORDS: '%s'", desc->u.name); + case KEYDB_SEARCH_MODE_SHORT_KID: + return xasprintf ("SHORT_KID: '%08lX'", (ulong)desc->u.kid[1]); + case KEYDB_SEARCH_MODE_LONG_KID: + return xasprintf ("LONG_KID: '%08lX%08lX'", + (ulong)desc->u.kid[0], (ulong)desc->u.kid[1]); + case KEYDB_SEARCH_MODE_FPR: + fpr = bin2hexcolon (desc->u.fpr, desc->fprlen, NULL); + result = xasprintf ("FPR%02d: '%s'", desc->fprlen, fpr); + xfree (fpr); + return result; + case KEYDB_SEARCH_MODE_ISSUER: + return xasprintf ("ISSUER: '%s'", desc->u.name); + case KEYDB_SEARCH_MODE_ISSUER_SN: + return xasprintf ("ISSUER_SN: '%*s'", + (int) (desc->snlen == -1 + ? strlen (desc->sn) : desc->snlen), + desc->sn); + case KEYDB_SEARCH_MODE_SN: + return xasprintf ("SN: '%*s'", + (int) (desc->snlen == -1 + ? strlen (desc->sn) : desc->snlen), + desc->sn); + case KEYDB_SEARCH_MODE_SUBJECT: + return xasprintf ("SUBJECT: '%s'", desc->u.name); + case KEYDB_SEARCH_MODE_KEYGRIP: + return xasprintf ("KEYGRIP: %s", desc->u.grip); + case KEYDB_SEARCH_MODE_FIRST: + return xasprintf ("FIRST"); + case KEYDB_SEARCH_MODE_NEXT: + return xasprintf ("NEXT"); + default: + return xasprintf ("Bad search mode (%d)", desc->mode); + } +} + + /* * Search through all keydb resources, starting at the current position, * for a keyblock which contains one of the keys described in the DESC array. @@ -974,6 +1120,7 @@ keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, { int rc = -1; unsigned long skipped; + int i; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); @@ -985,7 +1132,22 @@ keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, return gpg_error (GPG_ERR_NOT_FOUND); } - while (rc == -1 && hd->current >= 0 && hd->current < hd->used) + if (DBG_CLOCK) + log_clock ("%s: enter (hd=%p)\n", __func__, hd); + + if (DBG_LOOKUP) + { + log_debug ("%s: %zd search description(s):\n", __func__, ndesc); + for (i = 0; i < ndesc; i ++) + { + char *t = keydb_search_desc_dump (&desc[i]); + log_debug ("%s: %d: %s\n", __func__, i, t); + xfree (t); + } + } + + while ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) + && hd->current >= 0 && hd->current < hd->used) { switch (hd->active[hd->current].type) { @@ -998,6 +1160,15 @@ keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, NULL, &skipped); break; } + + if (DBG_LOOKUP) + log_debug ("%s: searched %s (resource %d of %d) => %s\n", + __func__, + hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYBOX + ? "keybox" : "unknown type", + hd->current, hd->used, + rc == -1 ? "EOF" : gpg_strerror (rc)); + if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) { /* EOF -> switch to next resource */ hd->current++; @@ -1006,6 +1177,9 @@ keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, hd->found = hd->current; } + + if (DBG_CLOCK) + log_clock ("%s: leave (rc=%d)\n", __func__, rc); return rc; } diff --git a/sm/keydb.h b/sm/keydb.h index 623462553..1ab94a2c2 100644 --- a/sm/keydb.h +++ b/sm/keydb.h @@ -46,10 +46,10 @@ gpg_error_t keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, void keydb_push_found_state (KEYDB_HANDLE hd); void keydb_pop_found_state (KEYDB_HANDLE hd); int keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert); -int keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert); -int keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert); +gpg_error_t keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert); +gpg_error_t keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert); -int keydb_delete (KEYDB_HANDLE hd, int unlock); +gpg_error_t keydb_delete (KEYDB_HANDLE hd); int keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved); void keydb_rebuild_caches (void); diff --git a/sm/keylist.c b/sm/keylist.c index e242310be..4eecb8720 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -1373,6 +1373,8 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret, else es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err)); } + if (opt.debug) + es_fflush (fp); } diff --git a/sm/server.c b/sm/server.c index 98505e26d..77ec07fc0 100644 --- a/sm/server.c +++ b/sm/server.c @@ -1162,14 +1162,14 @@ cmd_getinfo (assuan_context_t ctx, char *line) { cmdopt = line; if (!command_has_option (cmd, cmdopt)) - rc = gpg_error (GPG_ERR_GENERAL); + rc = gpg_error (GPG_ERR_FALSE); } } } } else if (!strcmp (line, "offline")) { - rc = ctrl->offline? 0 : gpg_error (GPG_ERR_GENERAL); + rc = ctrl->offline? 0 : gpg_error (GPG_ERR_FALSE); } else rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT"); diff --git a/sm/sign.c b/sm/sign.c index 24ecad3d7..341d8cf68 100644 --- a/sm/sign.c +++ b/sm/sign.c @@ -161,7 +161,7 @@ gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert) return rc; } - if (!gpgsm_cert_use_sign_p (cert)) + if (!gpgsm_cert_use_sign_p (cert, 1)) { p = gpgsm_get_keygrip_hexstring (cert); if (p) @@ -302,6 +302,99 @@ add_certificate_list (ctrl_t ctrl, ksba_cms_t cms, ksba_cert_t cert) } +#if KSBA_VERSION_NUMBER >= 0x010400 && 0 /* 1.4.0 */ +static gpg_error_t +add_signed_attribute (ksba_cms_t cms, const char *attrstr) +{ + gpg_error_t err; + char **fields = NULL; + const char *s; + int i; + unsigned char *der = NULL; + size_t derlen; + + fields = strtokenize (attrstr, ":"); + if (!fields) + { + err = gpg_error_from_syserror (); + log_error ("strtokenize failed: %s\n", gpg_strerror (err)); + goto leave; + } + + for (i=0; fields[i]; i++) + ; + if (i != 3) + { + err = gpg_error (GPG_ERR_SYNTAX); + log_error ("invalid attribute specification '%s': %s\n", + attrstr, i < 3 ? "not enough fields":"too many fields"); + goto leave; + } + if (!ascii_strcasecmp (fields[1], "u")) + { + err = 0; + goto leave; /* Skip unsigned attruibutes. */ + } + if (ascii_strcasecmp (fields[1], "s")) + { + err = gpg_error (GPG_ERR_SYNTAX); + log_error ("invalid attribute specification '%s': %s\n", + attrstr, "type is not 's' or 'u'"); + goto leave; + } + /* Check that the OID is valid. */ + err = ksba_oid_from_str (fields[0], &der, &derlen); + if (err) + { + log_error ("invalid attribute specification '%s': %s\n", + attrstr, gpg_strerror (err)); + goto leave; + } + xfree (der); + der = NULL; + + if (strchr (fields[2], '/')) + { + /* FIXME: read from file. */ + } + else /* Directly given in hex. */ + { + for (i=0, s = fields[2]; hexdigitp (s); s++, i++) + ; + if (*s || !i || (i&1)) + { + log_error ("invalid attribute specification '%s': %s\n", + attrstr, "invalid hex encoding of the data"); + err = gpg_error (GPG_ERR_SYNTAX); + goto leave; + } + der = xtrystrdup (fields[2]); + if (!der) + { + err = gpg_error_from_syserror (); + log_error ("malloc failed: %s\n", gpg_strerror (err)); + goto leave; + } + for (s=fields[2], derlen=0; s[0] && s[1]; s += 2) + der[derlen++] = xtoi_2 (s); + } + + /* Store the data in the CMS object for all signers. */ + err = ksba_cms_add_attribute (cms, -1, fields[0], 0, der, derlen); + if (err) + { + log_error ("invalid attribute specification '%s': %s\n", + attrstr, gpg_strerror (err)); + goto leave; + } + + leave: + xfree (der); + xfree (fields); + return err; +} +#endif /*ksba >= 1.4.0 */ + /* Perform a sign operation. @@ -377,10 +470,17 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, goto leave; } - /* We are going to create signed data with data as encap. content */ + /* We are going to create signed data with data as encap. content. + * In authenticode mode we use spcIndirectDataContext instead. */ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_SIGNED_DATA); if (!err) - err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA); + err = ksba_cms_set_content_type + (cms, 1, +#if KSBA_VERSION_NUMBER >= 0x010400 && 0 + opt.authenticode? KSBA_CT_SPC_IND_DATA_CTX : +#endif + KSBA_CT_DATA + ); if (err) { log_debug ("ksba_cms_set_content_type failed: %s\n", @@ -404,7 +504,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, /* Although we don't check for ambiguous specification we will check that the signer's certificate is usable and valid. */ - rc = gpgsm_cert_use_sign_p (cert); + rc = gpgsm_cert_use_sign_p (cert, 0); if (!rc) rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (rc) @@ -513,7 +613,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, /* Gather certificates of signers and store them in the CMS object. */ for (cl=signerlist; cl; cl = cl->next) { - rc = gpgsm_cert_use_sign_p (cl->cert); + rc = gpgsm_cert_use_sign_p (cl->cert, 0); if (rc) goto leave; @@ -643,6 +743,20 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, } } + /* We can add signed attributes only when build against libksba 1.4. */ +#if KSBA_VERSION_NUMBER >= 0x010400 && 0 /* 1.4.0 */ + { + strlist_t sl; + + for (sl = opt.attributes; sl; sl = sl->next) + if ((err = add_signed_attribute (cms, sl->d))) + goto leave; + } +#else + log_info ("Note: option --attribute is ignored by this version\n"); +#endif /*ksba >= 1.4.0 */ + + /* We need to write at least a minimal list of our capabilities to try to convince some MUAs to use 3DES and not the crippled RC2. Our list is: diff --git a/tests/openpgp/samplekeys/README b/tests/openpgp/samplekeys/README index f8a7e9ed7..74635c702 100644 --- a/tests/openpgp/samplekeys/README +++ b/tests/openpgp/samplekeys/README @@ -20,6 +20,8 @@ ed25519-cv25519-sample-1.asc Ed25519+CV25519 sample key (no passphrase) silent-running.asc Collection of sample secret keys (no passphrases) rsa-primary-auth-only.pub.asc rsa2408 primary only, usage: cert,auth rsa-primary-auth-only.sec.asc Ditto but the secret keyblock. +v5-sample-1-pub.asc A version 5 key (ed25519/cert,sign,v5+cv25519/v5) +v5-sample-1-sec.asc Ditto, but the secret keyblock (unprotected). Notes: diff --git a/tests/openpgp/samplekeys/v5-sample-1-pub.asc b/tests/openpgp/samplekeys/v5-sample-1-pub.asc new file mode 100644 index 000000000..9f7147130 --- /dev/null +++ b/tests/openpgp/samplekeys/v5-sample-1-pub.asc @@ -0,0 +1,19 @@ +pub ed25519 2019-03-20 [SC] + 19347BC9872464025F99DF3EC2E0000ED9884892E1F7B3EA4C94009159569B54 +uid emma.goldman@example.net +sub cv25519 2019-03-20 [E] + E4557C2B02FFBF4B04F87401EC336AF7133D0F85BE7FD09BAEFD9CAEB8C93965 + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDcFXJH05BYAAAAtCSsGAQQB2kcPAQEHQFhZlVcVVtwf+21xNQPX+ecMJJBL0MPd +fj75iux+my8QtBhlbW1hLmdvbGRtYW5AZXhhbXBsZS5uZXSIlgUTFggASCIhBRk0 +e8mHJGQCX5nfPsLgAA7ZiEiS4fez6kyUAJFZVptUBQJckfTkAhsDBQsJCAcCAyIC +AQYVCgkICwIEFgIDAQIeBwIXgAAA9cAA/jiR3yMsZMeEQ40u6uzEoXa6UXeV/S3w +wJAXRJy9M8s0AP9vuL/7AyTfFXwwzSjDnYmzS0qAhbLDQ643N+MXGBJ2Bbg8BVyR +9OQSAAAAMgorBgEEAZdVAQUBAQdA+nysrzml2UCweAqtpDuncSPlvrcBWKU0yfU0 +YvYWWAoDAQgHiHoFGBYIACwiIQUZNHvJhyRkAl+Z3z7C4AAO2YhIkuH3s+pMlACR +WVabVAUCXJH05AIbDAAAOSQBAP4BOOIR/sGLNMOfeb5fPs/02QMieoiSjIBnijho +b2U5AQC+RtOHCHx7TcIYl5/Uyoi+FOvPLcNw4hOv2nwUzSSVAw== +=WYfO +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/v5-sample-1-sec.asc b/tests/openpgp/samplekeys/v5-sample-1-sec.asc new file mode 100644 index 000000000..b5463c93f --- /dev/null +++ b/tests/openpgp/samplekeys/v5-sample-1-sec.asc @@ -0,0 +1,21 @@ +sec ed25519 2019-03-20 [SC] + 19347BC9872464025F99DF3EC2E0000ED9884892E1F7B3EA4C94009159569B54 +uid emma.goldman@example.net +ssb cv25519 2019-03-20 [E] + E4557C2B02FFBF4B04F87401EC336AF7133D0F85BE7FD09BAEFD9CAEB8C93965 + +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lGEFXJH05BYAAAAtCSsGAQQB2kcPAQEHQFhZlVcVVtwf+21xNQPX+ecMJJBL0MPd +fj75iux+my8QAAAAAAAiAQCHZ1SnSUmWqxEsoI6facIVZQu6mph3cBFzzTvcm5lA +Ng5ctBhlbW1hLmdvbGRtYW5AZXhhbXBsZS5uZXSIlgUTFggASCIhBRk0e8mHJGQC +X5nfPsLgAA7ZiEiS4fez6kyUAJFZVptUBQJckfTkAhsDBQsJCAcCAyICAQYVCgkI +CwIEFgIDAQIeBwIXgAAA9cAA/jiR3yMsZMeEQ40u6uzEoXa6UXeV/S3wwJAXRJy9 +M8s0AP9vuL/7AyTfFXwwzSjDnYmzS0qAhbLDQ643N+MXGBJ2BZxmBVyR9OQSAAAA +MgorBgEEAZdVAQUBAQdA+nysrzml2UCweAqtpDuncSPlvrcBWKU0yfU0YvYWWAoD +AQgHAAAAAAAiAP9OdAPppjU1WwpqjIItkxr+VPQRT8Zm/Riw7U3F6v3OiBFHiHoF +GBYIACwiIQUZNHvJhyRkAl+Z3z7C4AAO2YhIkuH3s+pMlACRWVabVAUCXJH05AIb +DAAAOSQBAP4BOOIR/sGLNMOfeb5fPs/02QMieoiSjIBnijhob2U5AQC+RtOHCHx7 +TcIYl5/Uyoi+FOvPLcNw4hOv2nwUzSSVAw== +=IiS2 +-----END PGP PRIVATE KEY BLOCK----- diff --git a/tools/card-call-scd.c b/tools/card-call-scd.c index 0a01bf5ca..c2580bf5c 100644 --- a/tools/card-call-scd.c +++ b/tools/card-call-scd.c @@ -310,11 +310,13 @@ static gpg_error_t start_agent (unsigned int flags) { gpg_error_t err; + int started = 0; if (agent_ctx) err = 0; else { + started = 1; err = start_new_gpg_agent (&agent_ctx, GPG_ERR_SOURCE_DEFAULT, opt.agent_program, @@ -347,7 +349,7 @@ start_agent (unsigned int flags) } } - if (!err && !(flags & START_AGENT_NO_STARTUP_CMDS)) + if (started && !err && !(flags & START_AGENT_NO_STARTUP_CMDS)) { /* Request the serial number of the card for an early test. */ struct card_info_s info; @@ -990,7 +992,7 @@ learn_status_cb (void *opaque, const char *line) /* Call the scdaemon to learn about a smartcard. This fills INFO - * wioth data from the card. */ + * with data from the card. */ gpg_error_t scd_learn (card_info_t info) { @@ -1268,7 +1270,7 @@ scd_genkey (const char *keyref, int force, const char *algo, u32 *createtime) /* Return the serial number of the card or an appropriate error. The * serial number is returned as a hexstring. If DEMAND is not NULL - * the reader with the a card of the serilanumber DEMAND is + * the reader with the a card of the serial number DEMAND is * requested. */ gpg_error_t scd_serialno (char **r_serialno, const char *demand) @@ -1295,7 +1297,10 @@ scd_serialno (char **r_serialno, const char *demand) return err; } - *r_serialno = serialno; + if (r_serialno) + *r_serialno = serialno; + else + xfree (serialno); return 0; } diff --git a/tools/card-yubikey.c b/tools/card-yubikey.c index f9d130988..fff669cc0 100644 --- a/tools/card-yubikey.c +++ b/tools/card-yubikey.c @@ -310,7 +310,7 @@ yk_enable_disable (struct ykapps_s *yk, struct iface_s *iface, * stream to output information. This function must only be called on * Yubikeys. */ gpg_error_t -yubikey_commands (estream_t fp, int argc, char *argv[]) +yubikey_commands (card_info_t info, estream_t fp, int argc, char *argv[]) { gpg_error_t err; enum {ykLIST, ykENABLE, ykDISABLE } cmd; @@ -336,6 +336,14 @@ yubikey_commands (estream_t fp, int argc, char *argv[]) goto leave; } + if (info->cardversion < 0x050000 && cmd != ykLIST) + { + log_info ("Sub-command '%s' is only support by Yubikey-5 and later\n", + argv[0]); + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + goto leave; + } + /* Parse interface if needed. */ if (cmd == ykLIST) iface.usb = iface.nfc = 1; diff --git a/tools/ccidmon.c b/tools/ccidmon.c index d61bb3c64..4e99da54d 100644 --- a/tools/ccidmon.c +++ b/tools/ccidmon.c @@ -64,9 +64,10 @@ static int any_error; struct { int is_bi; + char timestamp[20]; char address[50]; int count; - char data[2000]; + char data[16000]; } databuffer; @@ -576,7 +577,10 @@ flush_data (void) return; if (verbose) - printf ("Address: %s\n", databuffer.address); + { + printf ("Timestamp: %s\n", databuffer.timestamp); + printf ("Address..: %s\n", databuffer.address); + } if (databuffer.is_bi) { print_r2p (databuffer.data, databuffer.count); @@ -590,7 +594,8 @@ flush_data (void) } static void -collect_data (char *hexdata, const char *address, unsigned int lineno) +collect_data (char *hexdata, const char *timestamp, + const char *address, unsigned int lineno) { size_t length; int is_bi; @@ -602,6 +607,9 @@ collect_data (char *hexdata, const char *address, unsigned int lineno) if (databuffer.is_bi != is_bi || strcmp (databuffer.address, address)) flush_data (); databuffer.is_bi = is_bi; + if (strlen (timestamp) >= sizeof databuffer.timestamp) + die ("timestamp field too long"); + strcpy (databuffer.timestamp, timestamp); if (strlen (address) >= sizeof databuffer.address) die ("address field too long"); strcpy (databuffer.address, address); @@ -627,7 +635,7 @@ collect_data (char *hexdata, const char *address, unsigned int lineno) if (length >= sizeof (databuffer.data)) { - err ("too much data at line %u - can handle only up to % bytes", + err ("too much data at line %u - can handle only up to %zu bytes", lineno, sizeof (databuffer.data)); break; } @@ -641,43 +649,50 @@ static void parse_line (char *line, unsigned int lineno) { char *p; - char *event_type, *address, *data, *status, *datatag; + char *timestamp, *event_type, *address, *data, *status, *datatag; + + if (*line == '#' || !*line) + return; if (debug) printf ("line[%u] ='%s'\n", lineno, line); p = strtok (line, " "); if (!p) - die ("invalid line %d (no URB)"); - p = strtok (NULL, " "); - if (!p) - die ("invalid line %d (no timestamp)"); + die ("invalid line %d (no URB)", lineno); + timestamp = strtok (NULL, " "); + if (!timestamp) + die ("invalid line %d (no timestamp)", lineno); event_type = strtok (NULL, " "); if (!event_type) - die ("invalid line %d (no event type)"); + die ("invalid line %d (no event type)", lineno); address = strtok (NULL, " "); if (!address) - die ("invalid line %d (no address"); + die ("invalid line %d (no address", lineno); if (usb_bus || usb_dev) { int bus, dev; p = strchr (address, ':'); if (!p) - die ("invalid line %d (invalid address"); + die ("invalid line %d (invalid address", lineno); p++; bus = atoi (p); p = strchr (p, ':'); if (!p) - die ("invalid line %d (invalid address"); + die ("invalid line %d (invalid address", lineno); p++; dev = atoi (p); if ((usb_bus && usb_bus != bus) || (usb_dev && usb_dev != dev)) return; /* We don't want that one. */ } - if (*address != 'B' || (address[1] != 'o' && address[1] != 'i')) - return; /* We only want block in and block out. */ + if (*address == 'B' && (address[1] == 'o' || address[1] == 'i')) + ; /* We want block ind and out. */ + else if (*address == 'C' && (address[1] == 'o' || address[1] == 'i')) + ; /* We want control ind and out. */ + else + return; /* But nothing else. */ status = strtok (NULL, " "); if (!status) return; @@ -692,7 +707,7 @@ parse_line (char *line, unsigned int lineno) if (datatag && *datatag == '=') { data = strtok (NULL, ""); - collect_data (data?data:"", address, lineno); + collect_data (data?data:"", timestamp, address, lineno); } } diff --git a/tools/gpg-card.c b/tools/gpg-card.c index e2d728dab..ddc4d12bf 100644 --- a/tools/gpg-card.c +++ b/tools/gpg-card.c @@ -429,6 +429,24 @@ put_data_to_file (const char *fname, const void *buffer, size_t length) } +/* Return a malloced string with the number opf the menu PROMPT. + * Control-D is mapped to "Q". */ +static char * +get_selection (const char *prompt) +{ + char *answer; + + tty_printf ("\n"); + tty_printf ("%s", prompt); + tty_printf ("\n"); + answer = tty_get (_("Your selection? ")); + tty_kill_prompt (); + if (*answer == CONTROL_D) + strcpy (answer, "q"); + return answer; +} + + /* Simply prints TEXT to the output. Returns 0 as a convenience. * This is a separate fucntion so that it can be extended to run @@ -655,7 +673,7 @@ list_one_kinfo (key_info_t firstkinfo, key_info_t kinfo, if (!scd_readkey (kinfo->keyref, &s_pkey)) { - char *tmp = pubkey_algo_string (s_pkey); + char *tmp = pubkey_algo_string (s_pkey, NULL); tty_fprintf (fp, " algorithm ..: %s\n", tmp); xfree (tmp); gcry_sexp_release (s_pkey); @@ -981,6 +999,81 @@ list_card (card_info_t info) } + +/* The LIST command. This also updates INFO. */ +static gpg_error_t +cmd_list (card_info_t info, char *argstr) +{ + gpg_error_t err; + int opt_cards; + strlist_t cards = NULL; + strlist_t sl; + estream_t fp = opt.interactive? NULL : es_stdout; + int cardno, count; + + + if (!info) + return print_help + ("LIST [--cards] [N]\n\n" + "Show the content of the current card or with N given the N-th card.\n" + "Option --cards lists available cards.", + 0); + + opt_cards = has_leading_option (argstr, "--cards"); + argstr = skip_options (argstr); + + + if (digitp (argstr)) + { + cardno = atoi (argstr); + while (digitp (argstr)) + argstr++; + while (spacep (argstr)) + argstr++; + } + else + cardno = -1; + + + if (opt_cards) + { + err = scd_cardlist (&cards); + if (err) + goto leave; + for (count = 0, sl = cards; sl; sl = sl->next, count++) + tty_fprintf (fp, "%d %s\n", count, sl->d); + } + else + { + if (cardno != -1) + { + err = scd_cardlist (&cards); + if (err) + goto leave; + for (count = 0, sl = cards; sl; sl = sl->next, count++) + if (count == cardno) + break; + if (!sl) + { + err = gpg_error (GPG_ERR_INV_INDEX); + goto leave; + } + err = scd_serialno (NULL, sl->d); + if (err) + goto leave; + } + + err = scd_learn (info); + if (!err) + list_card (info); + } + + leave: + free_strlist (cards); + return err; +} + + /* The VERIFY command. */ static gpg_error_t @@ -2097,21 +2190,23 @@ cmd_generate (card_info_t info, char *argstr) -/* Sub-menu to change a PIN. */ +/* Change a PIN. */ static gpg_error_t cmd_passwd (card_info_t info, char *argstr) { - gpg_error_t err; + gpg_error_t err = 0; char *answer = NULL; - const char *pinref; + const char *pinref = NULL; + int reset_mode = 0; + int menu_used = 0; if (!info) return print_help ("PASSWD [PINREF]\n\n" - "Menu to change or unblock the PINs. Note that the\n" - "presented menu options depend on the type of card\n" - "and whether the admin mode is enabled. For OpenPGP\n" - "and PIV cards defaults for PINREF are available.", + "Change or unblock the PINs. Note that in interactive mode\n" + "and without a PINREF a menu is presented for certain cards;\n" + "in non-interactive and without a PINREF a default value is\n" + "used for these cards.", 0); if (opt.interactive || opt.verbose) @@ -2119,92 +2214,93 @@ cmd_passwd (card_info_t info, char *argstr) app_type_string (info->apptype), info->dispserialno? info->dispserialno : info->serialno); - if (!*argstr && info->apptype == APP_TYPE_OPENPGP) + if (*argstr) + pinref = argstr; + else if (opt.interactive && info->apptype == APP_TYPE_OPENPGP) { - /* For an OpenPGP card we present the well known menu if no - * argument is given. */ - for (;;) + menu_used = 1; + while (!pinref) { - tty_printf ("\n"); - tty_printf ("1 - change PIN\n" - "2 - unblock and set new PIN\n" - "3 - change Admin PIN\n" - "4 - set the Reset Code\n" - "Q - quit\n"); - tty_printf ("\n"); - - err = 0; xfree (answer); - answer = tty_get (_("Your selection? ")); - tty_kill_prompt (); - if (*answer == CONTROL_D) - break; /* Quit. */ + answer = get_selection ("1 - change the PIN\n" + "2 - unblock and set new a PIN\n" + "3 - change the Admin PIN\n" + "4 - set the Reset Code\n" + "Q - quit\n"); if (strlen (answer) != 1) continue; - if (*answer == 'q' || *answer == 'Q') - break; /* Quit. */ - - if (*answer == '1') - { - /* Change PIN (same as the direct thing in non-admin mode). */ - err = scd_change_pin ("OPENPGP.1", 0); - if (err) - log_error ("Error changing the PIN: %s\n", gpg_strerror (err)); - else - log_info ("PIN changed.\n"); - } + else if (*answer == 'q' || *answer == 'Q') + goto leave; + else if (*answer == '1') + pinref = "OPENPGP.1"; else if (*answer == '2') - { - /* Unblock PIN by setting a new PIN. */ - err = scd_change_pin ("OPENPGP.1", 1); - if (err) - log_error ("Error unblocking the PIN: %s\n", gpg_strerror(err)); - else - log_info ("PIN unblocked and new PIN set.\n"); - } + { pinref = "OPENPGP.1"; reset_mode = 1; } else if (*answer == '3') - { - /* Change Admin PIN. */ - err = scd_change_pin ("OPENPGP.3", 0); - if (err) - log_error ("Error changing the PIN: %s\n", gpg_strerror (err)); - else - log_info ("PIN changed.\n"); - } + pinref = "OPENPGP.3"; else if (*answer == '4') - { - /* Set a new Reset Code. */ - err = scd_change_pin ("OPENPGP.2", 1); - if (err) - log_error ("Error setting the Reset Code: %s\n", - gpg_strerror (err)); - else - log_info ("Reset Code set.\n"); - } + { pinref = "OPENPGP.2"; reset_mode = 1; } + } + } + else if (info->apptype == APP_TYPE_OPENPGP) + pinref = "OPENPGP.1"; + else if (opt.interactive && info->apptype == APP_TYPE_PIV) + { + menu_used = 1; + while (!pinref) + { + xfree (answer); + answer = get_selection ("1 - change the PIN\n" + "2 - change the PUK\n" + "3 - change the Global PIN\n" + "Q - quit\n"); + if (strlen (answer) != 1) + ; + else if (*answer == 'q' || *answer == 'Q') + goto leave; + else if (*answer == '1') + pinref = "PIV.80"; + else if (*answer == '2') + pinref = "PIV.81"; + else if (*answer == '3') + pinref = "PIV.00"; + } + } + else if (info->apptype == APP_TYPE_PIV) + pinref = "PIV.80"; + else + { + err = gpg_error (GPG_ERR_MISSING_VALUE); + goto leave; + } - } /*end for loop*/ + err = scd_change_pin (pinref, reset_mode); + if (err) + { + if (!opt.interactive && !menu_used && !opt.verbose) + ; + else if (!ascii_strcasecmp (pinref, "PIV.81")) + log_error ("Error changing the PUK.\n"); + else if (!ascii_strcasecmp (pinref, "OPENPGP.1") && reset_mode) + log_error ("Error unblocking the PIN.\n"); + else if (!ascii_strcasecmp (pinref, "OPENPGP.2") && reset_mode) + log_error ("Error setting the Reset Code.\n"); + else if (!ascii_strcasecmp (pinref, "OPENPGP.3")) + log_error ("Error changing the Admin PIN.\n"); + else + log_error ("Error changing the PIN.\n"); } else { - if (*argstr) - pinref = argstr; - else if (info->apptype == APP_TYPE_PIV) - pinref = "PIV.80"; - else - { - /* Note that we do not have a default value for OpenPGP - * because we want to be mostly compatible to "gpg - * --card-edit" and show a menu in that case (above). */ - err = gpg_error (GPG_ERR_MISSING_VALUE); - goto leave; - } - err = scd_change_pin (pinref, 0); - if (err) - goto leave; - - if (info->apptype == APP_TYPE_PIV - && !ascii_strcasecmp (pinref, "PIV.81")) + if (!opt.interactive && !opt.verbose) + ; + else if (!ascii_strcasecmp (pinref, "PIV.81")) log_info ("PUK changed.\n"); + else if (!ascii_strcasecmp (pinref, "OPENPGP.1") && reset_mode) + log_info ("PIN unblocked and new PIN set.\n"); + else if (!ascii_strcasecmp (pinref, "OPENPGP.2") && reset_mode) + log_info ("Reset Code set.\n"); + else if (!ascii_strcasecmp (pinref, "OPENPGP.3")) + log_info ("Admin PIN changed.\n"); else log_info ("PIN changed.\n"); } @@ -2261,7 +2357,7 @@ cmd_unblock (card_info_t info) } else { - log_info ("Unblocking not yet supported for '%s'\n", + log_info ("Unblocking not supported for '%s'.\n", app_type_string (info->apptype)); err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); } @@ -2457,9 +2553,8 @@ cmd_factoryreset (card_info_t info) if (err) goto leave; - /* Then, connect the card again (answer used as a dummy). */ - xfree (answer); answer = NULL; - err = scd_serialno (&answer, NULL); + /* Then, connect the card again. */ + err = scd_serialno (NULL, NULL); leave: if (err && any_apdu && !is_yubikey) @@ -2988,7 +3083,7 @@ cmd_yubikey (card_info_t info, char *argstr) /* Note that we always do a learn to get a chance to the card back * into a usable state. */ - err = yubikey_commands (fp, nwords, words); + err = yubikey_commands (info, fp, nwords, words); err2 = scd_learn (info); if (err2) log_error ("Error re-reading card: %s\n", gpg_strerror (err)); @@ -3137,20 +3232,6 @@ dispatch_command (card_info_t info, const char *orig_command) } break; - case cmdLIST: - if (!info) - print_help ("LIST\n\n" - "Show content of the card.", 0); - else - { - err = scd_learn (info); - if (err) - log_error ("Error reading card: %s\n", gpg_strerror (err)); - else - list_card (info); - } - break; - case cmdRESET: if (!info) print_help ("RESET\n\n" @@ -3162,6 +3243,7 @@ dispatch_command (card_info_t info, const char *orig_command) } break; + case cmdLIST: err = cmd_list (info, argstr); break; case cmdVERIFY: err = cmd_verify (info, argstr); break; case cmdAUTH: err = cmd_authenticate (info, argstr); break; case cmdNAME: err = cmd_name (info, argstr); break; @@ -3247,14 +3329,11 @@ interactive_loop (void) } else if (redisplay) { - err = scd_learn (info); + err = cmd_list (info, ""); if (err) - { - log_error ("Error reading card: %s\n", gpg_strerror (err)); - } + log_error ("Error reading card: %s\n", gpg_strerror (err)); else { - list_card (info); tty_printf("\n"); redisplay = 0; } @@ -3367,17 +3446,6 @@ interactive_loop (void) } break; - case cmdLIST: - if (!info) - print_help ("LIST\n\n" - "Show content of the card.", 0); - else - { - /* Actual work is done by the redisplay code block. */ - redisplay = 1; - } - break; - case cmdRESET: if (!info) print_help ("RESET\n\n" @@ -3389,6 +3457,7 @@ interactive_loop (void) } break; + case cmdLIST: err = cmd_list (info, argstr); break; case cmdVERIFY: err = cmd_verify (info, argstr); if (!err) diff --git a/tools/gpg-card.h b/tools/gpg-card.h index 099ea5448..35db14d25 100644 --- a/tools/gpg-card.h +++ b/tools/gpg-card.h @@ -224,7 +224,8 @@ gpg_error_t scd_checkpin (const char *serialno); unsigned long agent_get_s2k_count (void); /*-- card-yubikey.c --*/ -gpg_error_t yubikey_commands (estream_t fp, int argc, char *argv[]); +gpg_error_t yubikey_commands (card_info_t info, + estream_t fp, int argc, char *argv[]); #endif /*GNUPG_GPG_CARD_H*/ diff --git a/tools/gpg-pair-tool.c b/tools/gpg-pair-tool.c index 347b29d24..9a781def1 100644 --- a/tools/gpg-pair-tool.c +++ b/tools/gpg-pair-tool.c @@ -976,7 +976,7 @@ read_message (unsigned char **r_msg, size_t *r_msglen, int *r_msgtype, static gpg_error_t display_sas (const unsigned char *hash, size_t hashlen, int wait) { - gpg_error_t err; + gpg_error_t err = 0; unsigned long sas = 0; char sasbuf[12]; diff --git a/tools/gpg-wks-client.c b/tools/gpg-wks-client.c index 0f08737c4..861a1fc61 100644 --- a/tools/gpg-wks-client.c +++ b/tools/gpg-wks-client.c @@ -61,6 +61,8 @@ enum cmd_and_opt_values aRead, aInstallKey, aRemoveKey, + aPrintWKDHash, + aPrintWKDURL, oGpgProgram, oSend, @@ -90,6 +92,10 @@ static ARGPARSE_OPTS opts[] = { "install a key into a directory"), ARGPARSE_c (aRemoveKey, "remove-key", "remove a key from a directory"), + ARGPARSE_c (aPrintWKDHash, "print-wkd-hash", + "Print the WKD identifier for the given user ids"), + ARGPARSE_c (aPrintWKDURL, "print-wkd-url", + "Print the WKD URL for the given user id"), ARGPARSE_group (301, ("@\nOptions:\n ")), @@ -129,6 +135,8 @@ const char *fake_submission_addr; static void wrong_args (const char *text) GPGRT_ATTR_NORETURN; +static gpg_error_t proc_userid_from_stdin (gpg_error_t (*func)(const char *), + const char *text); static gpg_error_t command_supported (char *userid); static gpg_error_t command_check (char *userid); static gpg_error_t command_send (const char *fingerprint, const char *userid); @@ -230,6 +238,8 @@ parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts) case aCheck: case aInstallKey: case aRemoveKey: + case aPrintWKDHash: + case aPrintWKDURL: cmd = pargs->r_opt; break; @@ -246,7 +256,7 @@ parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts) int main (int argc, char **argv) { - gpg_error_t err; + gpg_error_t err, delayed_err; ARGPARSE_ARGS pargs; enum cmd_and_opt_values cmd; @@ -377,6 +387,39 @@ main (int argc, char **argv) err = wks_cmd_remove_key (*argv); break; + case aPrintWKDHash: + case aPrintWKDURL: + if (!argc) + { + if (cmd == aPrintWKDHash) + err = proc_userid_from_stdin (wks_cmd_print_wkd_hash, + "printing WKD hash"); + else + err = proc_userid_from_stdin (wks_cmd_print_wkd_url, + "printing WKD URL"); + } + else + { + for (err = delayed_err = 0; !err && argc; argc--, argv++) + { + if (cmd == aPrintWKDHash) + err = wks_cmd_print_wkd_hash (*argv); + else + err = wks_cmd_print_wkd_url (*argv); + if (gpg_err_code (err) == GPG_ERR_INV_USER_ID) + { + /* Diagnostic already printed. */ + delayed_err = err; + err = 0; + } + else if (err) + log_error ("printing hash failed: %s\n", gpg_strerror (err)); + } + if (!err) + err = delayed_err; + } + break; + default: usage (1); err = 0; @@ -390,10 +433,63 @@ main (int argc, char **argv) wks_write_status (STATUS_FAILURE, "- %u", GPG_ERR_GENERAL); else wks_write_status (STATUS_SUCCESS, NULL); - return log_get_errorcount (0)? 1:0; + return (err || log_get_errorcount (0))? 1:0; } +/* Read user ids from stdin and call FUNC for each user id. TEXT is + * used for error messages. */ +static gpg_error_t +proc_userid_from_stdin (gpg_error_t (*func)(const char *), const char *text) +{ + gpg_error_t err = 0; + gpg_error_t delayed_err = 0; + char line[2048]; + size_t n = 0; + + /* If we are on a terminal disable buffering to get direct response. */ + if (gnupg_isatty (es_fileno (es_stdin)) + && gnupg_isatty (es_fileno (es_stdout))) + { + es_setvbuf (es_stdin, NULL, _IONBF, 0); + es_setvbuf (es_stdout, NULL, _IOLBF, 0); + } + + while (es_fgets (line, sizeof line - 1, es_stdin)) + { + n = strlen (line); + if (!n || line[n-1] != '\n') + { + err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG + : GPG_ERR_INCOMPLETE_LINE); + log_error ("error reading stdin: %s\n", gpg_strerror (err)); + break; + } + trim_spaces (line); + err = func (line); + if (gpg_err_code (err) == GPG_ERR_INV_USER_ID) + { + delayed_err = err; + err = 0; + } + else if (err) + log_error ("%s failed: %s\n", text, gpg_strerror (err)); + } + if (es_ferror (es_stdin)) + { + err = gpg_error_from_syserror (); + log_error ("error reading stdin: %s\n", gpg_strerror (err)); + goto leave; + } + + leave: + if (!err) + err = delayed_err; + return err; +} + + + /* Add the user id UID to the key identified by FINGERPRINT. */ static gpg_error_t diff --git a/tools/gpg-wks-server.c b/tools/gpg-wks-server.c index f83ef6528..2082fb87d 100644 --- a/tools/gpg-wks-server.c +++ b/tools/gpg-wks-server.c @@ -1939,7 +1939,7 @@ command_check_key (const char *userid) char *addrspec = NULL; char *fname = NULL; - err = wks_fname_from_userid (userid, &fname, &addrspec); + err = wks_fname_from_userid (userid, 0, &fname, &addrspec); if (err) goto leave; diff --git a/tools/gpg-wks.h b/tools/gpg-wks.h index e36943090..9acd7c37f 100644 --- a/tools/gpg-wks.h +++ b/tools/gpg-wks.h @@ -98,11 +98,13 @@ gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown); void wks_free_policy (policy_flags_t policy); -gpg_error_t wks_fname_from_userid (const char *userid, +gpg_error_t wks_fname_from_userid (const char *userid, int hash_only, char **r_fname, char **r_addrspec); gpg_error_t wks_compute_hu_fname (char **r_fname, const char *addrspec); gpg_error_t wks_cmd_install_key (const char *fname, const char *userid); gpg_error_t wks_cmd_remove_key (const char *userid); +gpg_error_t wks_cmd_print_wkd_hash (const char *userid); +gpg_error_t wks_cmd_print_wkd_url (const char *userid); /*-- wks-receive.c --*/ diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c index 2ae79d91d..628ca358f 100644 --- a/tools/gpgconf-comp.c +++ b/tools/gpgconf-comp.c @@ -653,6 +653,10 @@ static gc_option_t gc_options_scdaemon[] = { "card-timeout", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, "gnupg", "|N|disconnect the card after N seconds of inactivity", GC_ARG_TYPE_UINT32, GC_BACKEND_SCDAEMON }, + { "application-priority", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, + "gnupg", "|LIST|Change the application priority to LIST", + GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON }, { "Debug", GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, @@ -967,7 +971,7 @@ static gc_option_t gc_options_dirmngr[] = GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, "gnupg", N_("Options controlling the use of Tor") }, { "use-tor", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "route all network traffic via TOR", + "dirmngr", "route all network traffic via Tor", GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, { "Keyserver", @@ -1152,12 +1156,8 @@ gpg_agent_runtime_change (int killflag) pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT); if (!gnupg_default_homedir_p ()) { - abs_homedir = make_absfilename_try (gnupg_homedir (), NULL); - if (!abs_homedir) - err = gpg_error_from_syserror (); - argv[i++] = "--homedir"; - argv[i++] = abs_homedir; + argv[i++] = gnupg_homedir (); } argv[i++] = "--no-autostart"; argv[i++] = killflag? "KILLAGENT" : "RELOADAGENT"; @@ -1195,12 +1195,8 @@ scdaemon_runtime_change (int killflag) pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT); if (!gnupg_default_homedir_p ()) { - abs_homedir = make_absfilename_try (gnupg_homedir (), NULL); - if (!abs_homedir) - err = gpg_error_from_syserror (); - argv[i++] = "--homedir"; - argv[i++] = abs_homedir; + argv[i++] = gnupg_homedir (); } argv[i++] = "-s"; argv[i++] = "--no-autostart"; @@ -1239,12 +1235,8 @@ dirmngr_runtime_change (int killflag) argv[3] = NULL; else { - abs_homedir = make_absfilename_try (gnupg_homedir (), NULL); - if (!abs_homedir) - err = gpg_error_from_syserror (); - argv[3] = "--homedir"; - argv[4] = abs_homedir; + argv[4] = gnupg_homedir (); argv[5] = NULL; } @@ -1266,7 +1258,7 @@ gc_component_launch (int component) { gpg_error_t err; const char *pgmname; - const char *argv[3]; + const char *argv[5]; int i; pid_t pid; @@ -1281,13 +1273,27 @@ gc_component_launch (int component) if (!(component == GC_COMPONENT_GPG_AGENT || component == GC_COMPONENT_DIRMNGR)) { - es_fputs (_("Component not suitable for launching"), es_stderr); - es_putc ('\n', es_stderr); + log_error ("%s\n", _("Component not suitable for launching")); + gpgconf_failure (0); + } + + if (gc_component_check_options (component, NULL, NULL)) + { + log_error (_("Configuration file of component %s is broken\n"), + gc_component[component].name); + if (!opt.quiet) + log_info (_("Note: Use the command \"%s%s\" to get details.\n"), + "gpgconf --check-options ", gc_component[component].name); gpgconf_failure (0); } pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT); i = 0; + if (!gnupg_default_homedir_p ()) + { + argv[i++] = "--homedir"; + argv[i++] = gnupg_homedir (); + } if (component == GC_COMPONENT_DIRMNGR) argv[i++] = "--dirmngr"; argv[i++] = "NOP"; @@ -1337,7 +1343,7 @@ gc_component_kill (int component) } /* Do the restart for the selected backends. */ - for (backend = 0; backend < GC_BACKEND_NR; backend++) + for (backend = GC_BACKEND_NR-1; backend; backend--) { if (runtime[backend] && gc_backend[backend].runtime_change) (*gc_backend[backend].runtime_change) (1); @@ -1688,8 +1694,9 @@ collect_error_output (estream_t fp, const char *tag) } -/* Check the options of a single component. Returns 0 if everything - is OK. */ +/* Check the options of a single component. If CONF_FILE is NULL the + * standard config file is used. If OUT is not NULL the output is + * written to that stream. Returns 0 if everything is OK. */ int gc_component_check_options (int component, estream_t out, const char *conf_file) { diff --git a/tools/gpgconf.c b/tools/gpgconf.c index b67125b89..a0cd97f60 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -900,6 +900,7 @@ main (int argc, char **argv) void gpgconf_failure (gpg_error_t err) { + log_flush (); if (!err) err = gpg_error (GPG_ERR_GENERAL); gpgconf_write_status diff --git a/tools/rfc822parse.c b/tools/rfc822parse.c index f1e95bd34..ac6ecb17c 100644 --- a/tools/rfc822parse.c +++ b/tools/rfc822parse.c @@ -420,7 +420,12 @@ transition_to_body (rfc822parse_t msg) s = rfc822parse_query_parameter (ctx, "boundary", 0); if (s) { - assert (!msg->current_part->boundary); + if (msg->current_part->boundary) + { + errno = ENOENT; + return -1; + } + msg->current_part->boundary = malloc (strlen (s) + 1); if (msg->current_part->boundary) { @@ -437,7 +442,11 @@ transition_to_body (rfc822parse_t msg) return -1; } rc = do_callback (msg, RFC822PARSE_LEVEL_DOWN); - assert (!msg->current_part->down); + if (msg->current_part->down) + { + errno = ENOENT; + return -1; + } msg->current_part->down = part; msg->current_part = part; msg->in_preamble = 1; @@ -458,8 +467,12 @@ transition_to_header (rfc822parse_t msg) { part_t part; - assert (msg->current_part); - assert (!msg->current_part->right); + if (!(msg->current_part + && !msg->current_part->right)) + { + errno = ENOENT; + return -1; + } part = new_part (); if (!part) @@ -476,7 +489,12 @@ insert_header (rfc822parse_t msg, const unsigned char *line, size_t length) { HDR_LINE hdr; - assert (msg->current_part); + if (!msg->current_part) + { + errno = ENOENT; + return -1; + } + if (!length) { msg->in_body = 1; diff --git a/tools/wks-receive.c b/tools/wks-receive.c index e67da628d..e5d2ed4b2 100644 --- a/tools/wks-receive.c +++ b/tools/wks-receive.c @@ -96,7 +96,7 @@ decrypt_data (receive_ctx_t ctx) /* We limit the output to 64 KiB to avoid DoS using compression * tricks. A regular client will anyway only send a minimal key; * that is one w/o key signatures and attribute packets. */ - ccparray_put (&ccp, "--max-output=0xf0000"); /*FIXME: Change s/F/1/ */ + ccparray_put (&ccp, "--max-output=0x10000"); ccparray_put (&ccp, "--batch"); if (opt.verbose) ccparray_put (&ccp, "--verbose"); diff --git a/tools/wks-util.c b/tools/wks-util.c index 1459045ef..1c1ac8c0b 100644 --- a/tools/wks-util.c +++ b/tools/wks-util.c @@ -749,9 +749,12 @@ write_to_file (estream_t src, const char *fname) /* Return the filename and optionally the addrspec for USERID at - * R_FNAME and R_ADDRSPEC. R_ADDRSPEC might also be set on error. */ + * R_FNAME and R_ADDRSPEC. R_ADDRSPEC might also be set on error. If + * HASH_ONLY is set only the has is returned at R_FNAME and no file is + * created. */ gpg_error_t -wks_fname_from_userid (const char *userid, char **r_fname, char **r_addrspec) +wks_fname_from_userid (const char *userid, int hash_only, + char **r_fname, char **r_addrspec) { gpg_error_t err; char *addrspec = NULL; @@ -767,7 +770,7 @@ wks_fname_from_userid (const char *userid, char **r_fname, char **r_addrspec) addrspec = mailbox_from_userid (userid, 0); if (!addrspec) { - if (opt.verbose) + if (opt.verbose || hash_only) log_info ("\"%s\" is not a proper mail address\n", userid); err = gpg_error (GPG_ERR_INV_USER_ID); goto leave; @@ -788,11 +791,20 @@ wks_fname_from_userid (const char *userid, char **r_fname, char **r_addrspec) goto leave; } - *r_fname = make_filename_try (opt.directory, domain, "hu", hash, NULL); - if (!*r_fname) - err = gpg_error_from_syserror (); + if (hash_only) + { + *r_fname = hash; + hash = NULL; + err = 0; + } else - err = 0; + { + *r_fname = make_filename_try (opt.directory, domain, "hu", hash, NULL); + if (!*r_fname) + err = gpg_error_from_syserror (); + else + err = 0; + } leave: if (r_addrspec && addrspec) @@ -1061,7 +1073,7 @@ wks_cmd_remove_key (const char *userid) char *addrspec = NULL; char *fname = NULL; - err = wks_fname_from_userid (userid, &fname, &addrspec); + err = wks_fname_from_userid (userid, 0, &fname, &addrspec); if (err) goto leave; @@ -1089,3 +1101,47 @@ wks_cmd_remove_key (const char *userid) xfree (addrspec); return err; } + + +/* Print the WKD hash for the user id to stdout. */ +gpg_error_t +wks_cmd_print_wkd_hash (const char *userid) +{ + gpg_error_t err; + char *addrspec, *fname; + + err = wks_fname_from_userid (userid, 1, &fname, &addrspec); + if (err) + return err; + + es_printf ("%s %s\n", fname, addrspec); + + xfree (fname); + xfree (addrspec); + return err; +} + + +/* Print the WKD URL for the user id to stdout. */ +gpg_error_t +wks_cmd_print_wkd_url (const char *userid) +{ + gpg_error_t err; + char *addrspec, *fname; + char *domain; + + err = wks_fname_from_userid (userid, 1, &fname, &addrspec); + if (err) + return err; + + domain = strchr (addrspec, '@'); + if (domain) + *domain++ = 0; + + es_printf ("https://openpgpkey.%s/.well-known/openpgpkey/%s/hu/%s?l=%s\n", + domain, domain, fname, addrspec); + + xfree (fname); + xfree (addrspec); + return err; +}