diff options
Diffstat (limited to 'g10')
40 files changed, 2366 insertions, 860 deletions
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) @@ -844,9 +986,99 @@ 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) @@ -985,60 +1219,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 scd_genkey_cb (void *opaque, const char *line) @@ -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)); } @@ -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; + + 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++; + + wname = utf8_to_wchar (command); + if (!wname) + 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; + + 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); + + 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); + /* 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); + 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; + 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); + /* Wait for the child to exit */ + WaitForSingleObject (pi.hProcess, INFINITE); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - xfree(string); + 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; @@ -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 (is_status_enabled () && !silent) - print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); + 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))); + } } 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); - - 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 ) ); + PACKET *pkt = xmalloc_clear (sizeof *pkt); + size_t n = strlen (s); + + 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; - - 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; - } + if (!pk->fprlen) + compute_fingerprint (pk); + + 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,60 +166,78 @@ 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) + +/* 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 *info; + char *result; 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); + /* If the pk was chosen by a particular user ID, that is the one to + print. */ + if (pk->user_id) + p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0); + else + p = get_user_id_native (ctrl, keyid); + 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 info; + 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_seckey_info (ctrl_t ctrl, PKT_public_key *pk) +print_key_info (ctrl_t ctrl, estream_t fp, + int indent, PKT_public_key *pk, int secret) { - char *p = format_seckey_info (ctrl, pk); - tty_printf ("\n%s\n", p); - xfree (p); + 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); } -/* 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. */ + +/* Same as print_key_info put print using the log functions at + * LOGLEVEL. */ void -print_pubkey_info (ctrl_t ctrl, estream_t fp, PKT_public_key *pk) +print_key_info_log (ctrl_t ctrl, int loglevel, + int indent, PKT_public_key *pk, int secret) { - u32 keyid[2]; - char *p; - char pkstrbuf[PUBKEY_STRING_SIZE]; + int indentabs = indent >= 0? indent : -indent; + char *info; - keyid_from_pk (pk, keyid); + info = format_key_info (ctrl, pk, secret); - /* If the pk was chosen by a particular user ID, that is the one to - print. */ - if (pk->user_id) - p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0); - else - p = get_user_id_native (ctrl, keyid); + log_log (loglevel, "%*s%s\n", indentabs, "", + info? info : "[Ooops - out of core]"); - 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); - xfree (p); + 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 <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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 <https://www.gnu.org/licenses/>. + * 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;i<count;i++) if(attrs[i].type==ATTRIB_IMAGE && parse_image_header(&attrs[i],&args.imagetype,&len)) diff --git a/g10/pkclist.c b/g10/pkclist.c index 46258bf85..1fd23a3e4 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -475,7 +475,7 @@ do_we_trust_pre (ctrl_t ctrl, PKT_public_key *pk, unsigned int trustlevel ) if( !opt.batch && !rc ) { - print_pubkey_info (ctrl, NULL,pk); + print_key_info (ctrl, NULL, 0, pk, 0); print_fingerprint (ctrl, NULL, pk, 2); tty_printf("\n"); @@ -834,7 +834,8 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, if (from_file) rc = get_pubkey_fromfile (ctrl, pk, name); else - rc = get_best_pubkey_byname (ctrl, NULL, pk, name, &keyblock, 0); + rc = get_best_pubkey_byname (ctrl, GET_PUBKEY_NORMAL, + NULL, pk, name, &keyblock, 0); if (rc) { int code; @@ -975,8 +976,8 @@ build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list) r->pk = 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; |