diff options
Diffstat (limited to 'agent')
-rw-r--r-- | agent/agent.h | 25 | ||||
-rw-r--r-- | agent/cache.c | 1 | ||||
-rw-r--r-- | agent/call-pinentry.c | 156 | ||||
-rw-r--r-- | agent/call-scd.c | 174 | ||||
-rw-r--r-- | agent/command-ssh.c | 11 | ||||
-rw-r--r-- | agent/command.c | 98 | ||||
-rw-r--r-- | agent/cvt-openpgp.c | 14 | ||||
-rw-r--r-- | agent/divert-scd.c | 73 | ||||
-rw-r--r-- | agent/findkey.c | 232 | ||||
-rw-r--r-- | agent/genkey.c | 42 | ||||
-rw-r--r-- | agent/gpg-agent.c | 6 | ||||
-rw-r--r-- | agent/keyformat.txt | 66 | ||||
-rw-r--r-- | agent/learncard.c | 1 | ||||
-rw-r--r-- | agent/pkdecrypt.c | 9 | ||||
-rw-r--r-- | agent/pksign.c | 26 | ||||
-rw-r--r-- | agent/preset-passphrase.c | 1 | ||||
-rw-r--r-- | agent/protect-tool.c | 22 | ||||
-rw-r--r-- | agent/protect.c | 17 | ||||
-rw-r--r-- | agent/trans.c | 1 | ||||
-rw-r--r-- | agent/trustlist.c | 3 |
20 files changed, 709 insertions, 269 deletions
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 <stdlib.h> #include <string.h> #include <time.h> -#include <assert.h> #include <npth.h> #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 <stdlib.h> #include <string.h> #include <ctype.h> -#include <assert.h> #include <unistd.h> #include <sys/stat.h> #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); - - 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]; + { + gnupg_fd_t sock_watched = ctrl->thread_startup.fd; + npth_t thread; - if (ctrl->pinentry_mode != PINENTRY_MODE_ASK) - return gpg_error (GPG_ERR_CANCELED); + rc = watch_sock_start (&sock_watched, &thread); + if (rc) + return rc; - rc = start_pinentry (ctrl); - 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); - 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); + watch_sock_end (&sock_watched, &thread); - 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 <stdlib.h> #include <string.h> #include <ctype.h> -#include <assert.h> #include <unistd.h> #ifdef HAVE_SIGNAL_H # include <signal.h> @@ -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 <errno.h> #include <sys/types.h> #include <sys/stat.h> -#include <assert.h> #ifndef HAVE_W32_SYSTEM #include <sys/socket.h> #include <sys/un.h> @@ -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 <string.h> #include <ctype.h> #include <unistd.h> -#include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> @@ -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] <keygrip>\n" + "KEYINFO [--[ssh-]list] [--data] [--ssh-fpr[=algo]] [--with-ssh] <keygrip>\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 <stdio.h> #include <stdlib.h> #include <string.h> -#include <assert.h> #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 <string.h> #include <ctype.h> #include <fcntl.h> -#include <assert.h> #include <unistd.h> #include <sys/stat.h> -#include <assert.h> #include <npth.h> /* (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 <stdlib.h> #include <string.h> #include <ctype.h> -#include <assert.h> #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 <stdarg.h> #include <string.h> #include <errno.h> -#include <assert.h> #include <time.h> #include <fcntl.h> #include <sys/stat.h> @@ -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 <stdlib.h> #include <string.h> #include <ctype.h> -#include <assert.h> #include <unistd.h> #include <sys/stat.h> 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 <stdlib.h> #include <string.h> #include <ctype.h> -#include <assert.h> #include <unistd.h> #include <sys/stat.h> @@ -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 <stdlib.h> #include <string.h> #include <ctype.h> -#include <assert.h> -#include <unistd.h> #include <sys/stat.h> #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 <stdarg.h> #include <string.h> #include <errno.h> -#include <assert.h> #include <sys/stat.h> #include <unistd.h> #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 <stdarg.h> #include <string.h> #include <errno.h> -#include <assert.h> #include <sys/stat.h> #include <unistd.h> #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 <stdlib.h> #include <string.h> #include <ctype.h> -#include <assert.h> #include <unistd.h> #include <sys/stat.h> 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 <stdlib.h> #include <string.h> #include <ctype.h> -#include <assert.h> #include <unistd.h> #include <sys/stat.h> #include <npth.h> @@ -550,7 +549,7 @@ insert_colons (const char *string) } } *p = 0; - assert (strlen (buffer) <= nnew); + log_assert (strlen (buffer) <= nnew); return buffer; } |