diff options
Diffstat (limited to 'g10')
-rw-r--r-- | g10/build-packet.c | 12 | ||||
-rw-r--r-- | g10/card-util.c | 17 | ||||
-rw-r--r-- | g10/free-packet.c | 30 | ||||
-rw-r--r-- | g10/getkey.c | 25 | ||||
-rw-r--r-- | g10/gpg.c | 98 | ||||
-rw-r--r-- | g10/gpgv.c | 2 | ||||
-rw-r--r-- | g10/import.c | 1 | ||||
-rw-r--r-- | g10/key-clean.c | 76 | ||||
-rw-r--r-- | g10/keydb.c | 31 | ||||
-rw-r--r-- | g10/keydb.h | 15 | ||||
-rw-r--r-- | g10/keyedit.c | 288 | ||||
-rw-r--r-- | g10/keyedit.h | 1 | ||||
-rw-r--r-- | g10/keygen.c | 167 | ||||
-rw-r--r-- | g10/keylist.c | 69 | ||||
-rw-r--r-- | g10/main.h | 9 | ||||
-rw-r--r-- | g10/mainproc.c | 5 | ||||
-rw-r--r-- | g10/misc.c | 15 | ||||
-rw-r--r-- | g10/options.h | 8 | ||||
-rw-r--r-- | g10/packet.h | 18 | ||||
-rw-r--r-- | g10/pkclist.c | 36 | ||||
-rw-r--r-- | g10/sig-check.c | 3 | ||||
-rw-r--r-- | g10/sign.c | 30 | ||||
-rw-r--r-- | g10/t-keydb-get-keyblock.c | 9 | ||||
-rw-r--r-- | g10/t-keydb.c | 10 | ||||
-rw-r--r-- | g10/t-stutter.c | 9 | ||||
-rw-r--r-- | g10/trust.c | 2 | ||||
-rw-r--r-- | g10/verify.c | 124 |
27 files changed, 919 insertions, 191 deletions
diff --git a/g10/build-packet.c b/g10/build-packet.c index f33d156b3..192dfaef5 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -1345,19 +1345,23 @@ build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, /* * Put all the required stuff from SIG into subpackets of sig. - * PKSK is the signing key. + * PKSK is the signing key. SIGNHINTS are various flags like + * SIGNHINT_ADSK. * Hmmm, should we delete those subpackets which are in a wrong area? */ void -build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk) +build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk, + unsigned int signhints) { u32 u; byte buf[1+MAX_FINGERPRINT_LEN]; size_t fprlen; /* For v4 keys we need to write the ISSUER subpacket. We do not - * want that for a future v5 format. */ - if (pksk->version < 5) + * want that for a future v5 format. We also don't write it if + * only the new RENC keyflag is set (implementations with support + * for this key flag should understand the ISSUER_FPR). */ + if (pksk->version < 5 && !(signhints & SIGNHINT_ADSK)) { u = sig->keyid[0]; buf[0] = (u >> 24) & 0xff; diff --git a/g10/card-util.c b/g10/card-util.c index 02de241f2..6451b31e7 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -1781,12 +1781,13 @@ card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock) } -/* Store the key at NODE into the smartcard and modify NODE to - carry the serialno stuff instead of the actual secret key - parameters. USE is the usage for that key; 0 means any - usage. */ +/* Store the key at NODE into the smartcard and modify NODE to carry + the serialno stuff instead of the actual secret key parameters. + USE is the usage for that key; 0 means any usage. If + PROCESSED_KEYS is not NULL it is a poiter to an strlist which will + be filled with the keygrips of successfully stored keys. */ int -card_store_subkey (KBNODE node, int use) +card_store_subkey (KBNODE node, int use, strlist_t *processed_keys) { struct agent_card_info_s info; int okay = 0; @@ -1875,7 +1876,11 @@ card_store_subkey (KBNODE node, int use) if (rc) log_error (_("KEYTOCARD failed: %s\n"), gpg_strerror (rc)); else - okay = 1; + { + okay = 1; + if (processed_keys) + add_to_strlist (processed_keys, hexgrip); + } xfree (hexgrip); leave: diff --git a/g10/free-packet.c b/g10/free-packet.c index 243cc7518..b0642043e 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -211,10 +211,10 @@ copy_prefs (const prefitem_t *prefs) /* Copy the public key S to D. If D is NULL allocate a new public key - structure. If S has seckret key infos, only the public stuff is - copied. */ + * structure. Only the basic stuff is copied; not any ancillary + * data. */ PKT_public_key * -copy_public_key (PKT_public_key *d, PKT_public_key *s) +copy_public_key_basics (PKT_public_key *d, PKT_public_key *s) { int n, i; @@ -222,8 +222,8 @@ copy_public_key (PKT_public_key *d, PKT_public_key *s) d = xmalloc (sizeof *d); memcpy (d, s, sizeof *d); d->seckey_info = NULL; - d->user_id = scopy_user_id (s->user_id); - d->prefs = copy_prefs (s->prefs); + d->user_id = NULL; + d->prefs = NULL; n = pubkey_get_npkey (s->pubkey_algo); i = 0; @@ -237,6 +237,24 @@ copy_public_key (PKT_public_key *d, PKT_public_key *s) for (; i < PUBKEY_MAX_NSKEY; i++) d->pkey[i] = NULL; + d->revkey = NULL; + d->serialno = NULL; + d->updateurl = NULL; + + return d; +} + + +/* Copy the public key S to D. If D is NULL allocate a new public key + structure. If S has seckret key infos, only the public stuff is + copied. */ +PKT_public_key * +copy_public_key (PKT_public_key *d, PKT_public_key *s) +{ + d = copy_public_key_basics (d, s); + d->user_id = scopy_user_id (s->user_id); + d->prefs = copy_prefs (s->prefs); + if (!s->revkey && s->numrevkeys) BUG(); if (s->numrevkeys) @@ -244,8 +262,6 @@ copy_public_key (PKT_public_key *d, PKT_public_key *s) d->revkey = xmalloc(sizeof(struct revocation_key)*s->numrevkeys); memcpy(d->revkey,s->revkey,sizeof(struct revocation_key)*s->numrevkeys); } - else - d->revkey = NULL; if (s->serialno) d->serialno = xstrdup (s->serialno); diff --git a/g10/getkey.c b/g10/getkey.c index 6363fea9f..1b37c597d 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -1718,7 +1718,8 @@ get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY is returned if the key - * is not found. + * is not found. If R_KEYBLOCK is not NULL and a key was found the + * keyblock is stored there; otherwiese NULL is stored there. * * The self-signed data has already been merged into the public key * using merge_selfsigs. The caller must release the content of PK by @@ -1726,13 +1727,17 @@ get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, * free_public_key). */ gpg_error_t -get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname) +get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname, + kbnode_t *r_keyblock) { gpg_error_t err; kbnode_t keyblock; kbnode_t found_key; unsigned int infoflags; + if (r_keyblock) + *r_keyblock = NULL; + err = read_key_from_file_or_buffer (ctrl, fname, NULL, 0, &keyblock); if (!err) { @@ -1747,7 +1752,10 @@ get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname) err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); } - release_kbnode (keyblock); + if (!err && r_keyblock) + *r_keyblock = keyblock; + else + release_kbnode (keyblock); return err; } @@ -1809,12 +1817,12 @@ get_pubkey_from_buffer (ctrl_t ctrl, PKT_public_key *pkbuf, * returned public key may be a subkey rather than the primary key. * Note: The self-signed data has already been merged into the public * key using merge_selfsigs. Free *PK by calling - * release_public_key_parts (or, if PK was allocated using xfree, you + * release_public_key_parts (or, if PK was allocated using xmalloc, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * If PK->REQ_USAGE is set, it is used to filter the search results. - * (Thus, if PK is not NULL, PK->REQ_USAGE must be valid!!!) See the + * Thus, if PK is not NULL, PK->REQ_USAGE must be valid! See the * documentation for finish_lookup to understand exactly how this is * used. * @@ -2417,7 +2425,8 @@ merge_keys_and_selfsig (ctrl_t ctrl, kbnode_t keyblock) } -static int +/* This function parses the key flags and returns PUBKEY_USAGE_ flags. */ +unsigned int parse_key_usage (PKT_signature * sig) { int key_usage = 0; @@ -2707,7 +2716,7 @@ merge_selfsigs_main (ctrl_t ctrl, kbnode_t keyblock, int *r_revoked, * and there was no way to change it, so we start with the one * from the key packet. We do not support v3 keys anymore but * we keep the code in case a future key versions introduces a - * hadr expire time again. */ + * hard expire time again. */ key_expire = pk->max_expiredate; key_expire_seen = 1; } @@ -3247,7 +3256,7 @@ buf_to_sig (const byte * buf, size_t len) * has_expired * expired_date * - * On this subkey's most revent valid self-signed packet, the + * On this subkey's most recent valid self-signed packet, the * following field is set: * * flags.chosen_selfsig @@ -130,6 +130,7 @@ enum cmd_and_opt_values aQuickRevSig, aQuickAddUid, aQuickAddKey, + aQuickAddADSK, aQuickRevUid, aQuickSetExpire, aQuickSetPrimaryUid, @@ -337,6 +338,7 @@ enum cmd_and_opt_values oEncryptToDefaultKey, oLoggerFD, oLoggerFile, + oLogTime, oUtf8Strings, oNoUtf8Strings, oDisableCipherAlgo, @@ -443,6 +445,8 @@ enum cmd_and_opt_values oForbidGenKey, oRequireCompliance, oCompatibilityFlags, + oAddDesigRevoker, + oAssertSigner, oNoop }; @@ -484,6 +488,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_c (aQuickAddUid, "quick-adduid", "@"), ARGPARSE_c (aQuickAddKey, "quick-add-key", "@"), ARGPARSE_c (aQuickAddKey, "quick-addkey", "@"), + ARGPARSE_c (aQuickAddADSK, "quick-add-adsk", "@"), ARGPARSE_c (aQuickRevUid, "quick-revoke-uid", N_("quickly revoke a user-id")), ARGPARSE_c (aQuickRevUid, "quick-revuid", "@"), @@ -599,6 +604,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_s (oLoggerFile, "log-file", N_("|FILE|write server mode logs to FILE")), ARGPARSE_s_s (oLoggerFile, "logger-file", "@"), /* 1.4 compatibility. */ + ARGPARSE_s_n (oLogTime, "log-time", "@"), ARGPARSE_s_n (oQuickRandom, "debug-quick-random", "@"), @@ -702,7 +708,8 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_s (oForceOwnertrust, "force-ownertrust", "@"), ARGPARSE_s_n (oNoAutoTrustNewKey, "no-auto-trust-new-key", "@"), #endif - + ARGPARSE_s_s (oAddDesigRevoker, "add-desig-revoker", "@"), + ARGPARSE_s_s (oAssertSigner, "assert-signer", "@"), ARGPARSE_header ("Input", N_("Options controlling the input")), @@ -1026,8 +1033,12 @@ static struct compatibility_flags_s compatibility_flags [] = /* The list of the default AKL methods. */ #define DEFAULT_AKL_LIST "local,wkd" - +/* Can be set to true to force gpg to return with EXIT_FAILURE. */ int g10_errors_seen = 0; +/* If opt.assert_signer_list is used and this variabale is not true + * gpg will be forced to return EXIT_FAILURE. */ +int assert_signer_true = 0; + static int utf8_strings = #ifdef HAVE_W32_SYSTEM @@ -1039,6 +1050,7 @@ static int utf8_strings = static int maybe_setuid = 1; static unsigned int opt_set_iobuf_size; static unsigned int opt_set_iobuf_size_used; +static int opt_log_time; /* Collection of options used only in this module. */ static struct { @@ -2047,6 +2059,8 @@ parse_list_options(char *str) char *subpackets=""; /* something that isn't NULL */ struct parse_options lopts[]= { + {"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL, + NULL}, {"show-photos",LIST_SHOW_PHOTOS,NULL, N_("display photo IDs during key listings")}, {"show-usage",LIST_SHOW_USAGE,NULL, @@ -2069,6 +2083,8 @@ parse_list_options(char *str) N_("show revoked and expired user IDs in key listings")}, {"show-unusable-subkeys",LIST_SHOW_UNUSABLE_SUBKEYS,NULL, N_("show revoked and expired subkeys in key listings")}, + {"show-unusable-sigs",LIST_SHOW_UNUSABLE_SIGS,NULL, + N_("show signatures with invalid algorithms during signature listings")}, {"show-keyring",LIST_SHOW_KEYRING,NULL, N_("show the keyring name in key listings")}, {"show-sig-expire",LIST_SHOW_SIG_EXPIRE,NULL, @@ -2077,20 +2093,25 @@ parse_list_options(char *str) N_("show preferences")}, {"show-pref-verbose", LIST_SHOW_PREF_VERBOSE, NULL, N_("show preferences")}, - {"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL, - NULL}, {"show-only-fpr-mbox",LIST_SHOW_ONLY_FPR_MBOX, NULL, NULL}, {"sort-sigs", LIST_SORT_SIGS, NULL, NULL}, {NULL,0,NULL,NULL} }; + int i; /* C99 allows for non-constant initializers, but we'd like to compile everywhere, so fill in the show-sig-subpackets argument here. Note that if the parse_options array changes, we'll have - to change the subscript here. */ - lopts[13].value=&subpackets; + to change the subscript here. We use a loop here in case the + list above is reordered. */ + for (i=0; lopts[i].name; i++) + if (lopts[i].bit == LIST_SHOW_SIG_SUBPACKETS) + { + lopts[i].value = &subpackets; + break; + } if(parse_options(str,&opt.list_options,lopts,1)) { @@ -2677,6 +2698,7 @@ main (int argc, char **argv) case aQuickKeygen: case aQuickAddUid: case aQuickAddKey: + case aQuickAddADSK: case aQuickRevUid: case aQuickSetExpire: case aQuickSetPrimaryUid: @@ -2853,6 +2875,9 @@ main (int argc, char **argv) case oLoggerFile: logfile = pargs.r.ret_str; break; + case oLogTime: + opt_log_time = 1; + break; case oWithFingerprint: opt.with_fingerprint = 1; @@ -3707,6 +3732,18 @@ main (int argc, char **argv) opt.flags.require_compliance = 1; break; + case oAddDesigRevoker: + if (!strcmp (pargs.r.ret_str, "clear")) + FREE_STRLIST (opt.desig_revokers); + else + append_to_strlist (&opt.desig_revokers, pargs.r.ret_str); + break; + + case oAssertSigner: + add_to_strlist (&opt.assert_signer_list, pargs.r.ret_str); + break; + + case oNoop: break; default: @@ -3811,6 +3848,9 @@ main (int argc, char **argv) | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID )); } + else if (opt_log_time) + log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_NO_REGISTRY + |GPGRT_LOG_WITH_TIME)); if (opt.verbose > 2) log_info ("using character set '%s'\n", get_native_charset ()); @@ -4157,17 +4197,27 @@ main (int argc, char **argv) * need to add the keyrings if we are running under SELinux, this * is so that the rings are added to the list of secured files. * We do not add any keyring if --no-keyring or --use-keyboxd has - * been used. */ + * been used. Note that keydb_add_resource may create a new + * homedir and also tries to write a common.conf to enable the use + * of the keyboxd - in this case a special error code is returned + * and use_keyboxd is then also set. */ if (!opt.use_keyboxd && default_keyring >= 0 && (ALWAYS_ADD_KEYRINGS || (cmd != aDeArmor && cmd != aEnArmor && cmd != aGPGConfTest))) { + gpg_error_t tmperr = 0; + if (!nrings || default_keyring > 0) /* Add default ring. */ - keydb_add_resource ("pubring" EXTSEP_S GPGEXT_GPG, - KEYDB_RESOURCE_FLAG_DEFAULT); - for (sl = nrings; sl; sl = sl->next ) - keydb_add_resource (sl->d, sl->flags); + tmperr = keydb_add_resource ("pubring" EXTSEP_S GPGEXT_GPG, + KEYDB_RESOURCE_FLAG_DEFAULT); + if (gpg_err_code (tmperr) == GPG_ERR_TRUE && opt.use_keyboxd) + ; /* The keyboxd has been enabled. */ + else + { + for (sl = nrings; sl; sl = sl->next ) + keydb_add_resource (sl->d, sl->flags); + } } FREE_STRLIST(nrings); @@ -4275,6 +4325,7 @@ main (int argc, char **argv) case aQuickKeygen: case aQuickAddUid: case aQuickAddKey: + case aQuickAddADSK: case aQuickRevUid: case aQuickSetPrimaryUid: case aQuickUpdatePref: @@ -4742,6 +4793,17 @@ main (int argc, char **argv) } break; + case aQuickAddADSK: + { + if (argc != 2) + wrong_args ("--quick-add-adsk FINGERPRINT ADSK-FINGERPRINT"); + if (mopt.forbid_gen_key) + gen_key_forbidden (); + else + keyedit_quick_addadsk (ctrl, argv[0], argv[1]); + } + break; + case aQuickRevUid: { const char *uid, *uidtorev; @@ -5048,9 +5110,6 @@ main (int argc, char **argv) size_t nn; p = gcry_random_bytes (n, level); -#ifdef HAVE_DOSISH_SYSTEM - setmode ( fileno(stdout), O_BINARY ); -#endif if (hexhack) { for (nn = 0; nn < n; nn++) @@ -5068,6 +5127,7 @@ main (int argc, char **argv) } else { + es_set_binary (es_stdout); es_fwrite( p, n, 1, es_stdout ); } xfree(p); @@ -5398,7 +5458,15 @@ g10_exit( int rc ) gnupg_block_all_signals (); emergency_cleanup (); - rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; + if (rc) + ; + else if (log_get_errorcount(0)) + rc = 2; + else if (g10_errors_seen) + rc = 1; + else if (opt.assert_signer_list && !assert_signer_true) + rc = 1; + exit (rc); } diff --git a/g10/gpgv.c b/g10/gpgv.c index ceded4af9..f2895563e 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -118,7 +118,7 @@ static struct debug_flags_s debug_flags [] = int g10_errors_seen = 0; - +int assert_signer_true = 0; static char * make_libversion (const char *libname, const char *(*getfnc)(const char*)) diff --git a/g10/import.c b/g10/import.c index 9fab46ca6..1ed40a63c 100644 --- a/g10/import.c +++ b/g10/import.c @@ -2656,6 +2656,7 @@ transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, * in case of cv25519. We have only opaque MPIs here. */ if (pk->pubkey_algo == PUBKEY_ALGO_ECDH && !strcmp (curvestr, "1.3.6.1.4.1.3029.1.5.1") + && !gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1) && gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) { const unsigned char *pp; diff --git a/g10/key-clean.c b/g10/key-clean.c index 9320428c8..c8a6efe50 100644 --- a/g10/key-clean.c +++ b/g10/key-clean.c @@ -35,14 +35,19 @@ #include "key-clean.h" +#define NF_USABLE 8 /* Usable signature and not a revocation. */ +#define NF_CONSIDER 9 /* Internal use. */ +#define NF_PROCESSED 10 /* Internal use. */ +#define NF_REVOC 11 /* Usable revocation. */ +#define NF_NOKEY 12 /* Key not available. */ + /* * Mark the signature of the given UID which are used to certify it. * To do this, we first remove all signatures which are not valid and * from the remaining we look for the latest one. If this is not a * certification revocation signature we mark the signature by setting - * node flag bit 8. Revocations are marked with flag 11, and sigs - * from unavailable keys are marked with flag 12. Note that flag bits - * 9 and 10 are used for internal purposes. + * node flag bit NF_USABLE. Revocations are marked with NF_REVOC, and + * sigs from unavailable keys are marked with NF_NOKEY. */ void mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, @@ -57,7 +62,8 @@ mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, { int rc; - node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); + node->flag &= ~(1<<NF_USABLE | 1<<NF_CONSIDER + | 1<<NF_PROCESSED | 1<<NF_REVOC | 1<<NF_NOKEY); if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) @@ -81,19 +87,20 @@ mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, /* we ignore anything that won't verify, but tag the no_pubkey case */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY) - node->flag |= 1<<12; + node->flag |= 1<<NF_NOKEY; continue; } - node->flag |= 1<<9; + node->flag |= 1<<NF_CONSIDER; } /* Reset the remaining flags. */ for (; node; node = node->next) - node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); + node->flag &= ~(1<<NF_USABLE | 1<<NF_CONSIDER + | 1<<NF_PROCESSED | 1<<NF_REVOC | 1<<NF_NOKEY); - /* kbnode flag usage: bit 9 is here set for signatures to consider, - * bit 10 will be set by the loop to keep track of keyIDs already - * processed, bit 8 will be set for the usable signatures, and bit - * 11 will be set for usable revocations. */ + /* kbnode flag usage: bit NF_CONSIDER is here set for signatures to consider, + * bit NF_PROCESSED will be set by the loop to keep track of keyIDs already + * processed, bit NF_USABLE will be set for the usable signatures, and bit + * NF_REVOC will be set for usable revocations. */ /* For each cert figure out the latest valid one. */ for (node=uidnode->next; node; node = node->next) @@ -105,11 +112,11 @@ mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) break; - if ( !(node->flag & (1<<9)) ) + if ( !(node->flag & (1<<NF_CONSIDER)) ) continue; /* not a node to look at */ - if ( (node->flag & (1<<10)) ) + if ( (node->flag & (1<<NF_PROCESSED)) ) continue; /* signature with a keyID already processed */ - node->flag |= (1<<10); /* mark this node as processed */ + node->flag |= (1<<NF_PROCESSED); /* mark this node as processed */ sig = node->pkt->pkt.signature; signode = node; sigdate = sig->timestamp; @@ -121,14 +128,14 @@ mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY || n->pkt->pkttype == PKT_SECRET_SUBKEY) break; - if ( !(n->flag & (1<<9)) ) + if ( !(n->flag & (1<<NF_CONSIDER)) ) continue; - if ( (n->flag & (1<<10)) ) + if ( (n->flag & (1<<NF_PROCESSED)) ) continue; /* shortcut already processed signatures */ sig = n->pkt->pkt.signature; if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1]) continue; - n->flag |= (1<<10); /* mark this node as processed */ + n->flag |= (1<<NF_PROCESSED); /* mark this node as processed */ /* If signode is nonrevocable and unexpired and n isn't, then take signode (skip). It doesn't matter which is @@ -197,13 +204,13 @@ mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, if (expire==0 || expire > curtime ) { - signode->flag |= (1<<8); /* yeah, found a good cert */ + signode->flag |= (1<<NF_USABLE); /* yeah, found a good cert */ if (next_expire && expire && expire < *next_expire) *next_expire = expire; } } else - signode->flag |= (1<<11); + signode->flag |= (1<<NF_REVOC); } } @@ -231,12 +238,13 @@ clean_sigs_from_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, signatures are out, as are any signatures that aren't the last of a series of uid sigs or revocations It breaks down like this: coming out of mark_usable_uid_certs, if a sig is unflagged, it is - not even a candidate. If a sig has flag 9 or 10, that means it - was selected as a candidate and vetted. If a sig has flag 8 it - is a usable signature. If a sig has flag 11 it is a usable - revocation. If a sig has flag 12 it was issued by an unavailable - key. "Usable" here means the most recent valid - signature/revocation in a series from a particular signer. + not even a candidate. If a sig has flag NF_CONSIDER or + NF_PROCESSED, that means it was selected as a candidate and + vetted. If a sig has flag NF_USABLE it is a usable signature. + If a sig has flag NF_REVOC it is a usable revocation. If a sig + has flag NF_NOKEY it was issued by an unavailable key. "Usable" + here means the most recent valid signature/revocation in a series + from a particular signer. Delete everything that isn't a usable uid sig (which might be expired), a usable revocation, or a sig from an unavailable @@ -252,34 +260,34 @@ clean_sigs_from_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, && node->pkt->pkt.signature->keyid[1] == keyid[1]) : 1; /* Keep usable uid sigs ... */ - if ((node->flag & (1<<8)) && keep) + if ((node->flag & (1<<NF_USABLE)) && keep) continue; /* ... and usable revocations... */ - if ((node->flag & (1<<11)) && keep) + if ((node->flag & (1<<NF_REVOC)) && keep) continue; /* ... and sigs from unavailable keys. */ /* disabled for now since more people seem to want sigs from unavailable keys removed altogether. */ /* - if(node->flag & (1<<12)) + if(node->flag & (1<<NF_NOKEY)) continue; */ /* Everything else we delete */ - /* At this point, if 12 is set, the signing key was unavailable. - If 9 or 10 is set, it's superseded. Otherwise, it's - invalid. */ + /* At this point, if NF_NOKEY is set, the signing key was + * unavailable. If NF_CONSIDER or NF_PROCESSED is set, it's + * superseded. Otherwise, it's invalid. */ if (noisy) log_info ("removing signature from key %s on user ID \"%s\": %s\n", keystr (node->pkt->pkt.signature->keyid), uidnode->pkt->pkt.user_id->name, - node->flag&(1<<12)? "key unavailable": - node->flag&(1<<9)? "signature superseded" - /* */ :"invalid signature" ); + node->flag&(1<<NF_NOKEY)? "key unavailable": + node->flag&(1<<NF_CONSIDER)? "signature superseded" + /* */ : "invalid signature" ); delete_kbnode (node); deleted++; diff --git a/g10/keydb.c b/g10/keydb.c index 3938d7e16..d2d085291 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -37,6 +37,7 @@ #include "../kbx/keybox.h" #include "keydb.h" #include "../common/i18n.h" +#include "../common/comopt.h" #include "keydb-private.h" /* For struct keydb_handle_s */ @@ -265,8 +266,24 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create) *last_slash_in_filename = save_slash; goto leave; } + + *last_slash_in_filename = save_slash; + + if (!opt.use_keyboxd + && !parse_comopt (GNUPG_MODULE_NAME_GPG, 0) + && comopt.use_keyboxd) + { + /* The above try_make_homedir created a new default hoemdir + * and also wrote a new common.conf. Thus we now see that + * use-keyboxd has been set. Let's set this option and + * return a dedicated error code. */ + opt.use_keyboxd = comopt.use_keyboxd; + rc = gpg_error (GPG_ERR_TRUE); + goto leave; + } } - *last_slash_in_filename = save_slash; + else + *last_slash_in_filename = save_slash; /* To avoid races with other instances of gpg trying to create or update the keyring (it is removed during an update for a short @@ -555,7 +572,8 @@ keydb_search_desc_dump (struct keydb_search_desc *desc) * If KEYDB_RESOURCE_FLAG_READONLY is set and the resource is a * keyring (not a keybox), then the keyring is marked as read only and * operations just as keyring_insert_keyblock will return - * GPG_ERR_ACCESS. */ + * GPG_ERR_ACCESS. + */ gpg_error_t keydb_add_resource (const char *url, unsigned int flags) { @@ -774,9 +792,12 @@ keydb_add_resource (const char *url, unsigned int flags) leave: if (err) { - log_error (_("keyblock resource '%s': %s\n"), - filename, gpg_strerror (err)); - write_status_error ("add_keyblock_resource", err); + if (gpg_err_code (err) != GPG_ERR_TRUE) + { + log_error (_("keyblock resource '%s': %s\n"), + filename, gpg_strerror (err)); + write_status_error ("add_keyblock_resource", err); + } } else any_registered = 1; diff --git a/g10/keydb.h b/g10/keydb.h index 771bc8e16..9323e3137 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -391,7 +391,8 @@ gpg_error_t get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, /* Get a public key directly from file FNAME. */ gpg_error_t get_pubkey_fromfile (ctrl_t ctrl, - PKT_public_key *pk, const char *fname); + PKT_public_key *pk, const char *fname, + kbnode_t *r_keyblock); /* Get a public key from a buffer. */ gpg_error_t get_pubkey_from_buffer (ctrl_t ctrl, PKT_public_key *pkbuf, @@ -468,6 +469,9 @@ void setup_main_keyids (kbnode_t keyblock); data structures. */ void merge_keys_and_selfsig (ctrl_t ctrl, kbnode_t keyblock); +/* This function parses the key flags and returns PUBKEY_USAGE_ flags. */ +unsigned int parse_key_usage (PKT_signature *sig); + 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); @@ -511,11 +515,18 @@ keyid_cmp (const u32 *a, const u32 *b) return 0; } +/* Return true if both keyids are equal. */ +static int GPGRT_ATTR_UNUSED +keyid_eq (const u32 *a, const u32 *b) +{ + return a[0] == b[0] && a[1] == b[1]; +} + /* Return whether PK is a primary key. */ static int GPGRT_ATTR_UNUSED pk_is_primary (PKT_public_key *pk) { - return keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) == 0; + return keyid_eq (pk_keyid (pk), pk_main_keyid (pk)); } /* Copy the keyid in SRC to DEST and return DEST. */ diff --git a/g10/keyedit.c b/g10/keyedit.c index 83c20b846..a91cc4447 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,7 +1,7 @@ /* keyedit.c - Edit properties of a key * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1998-2017 Werner Koch - * Copyright (C) 2015, 2016, 2022 g10 Code GmbH + * Copyright (C) 2015, 2016, 2022-2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -73,6 +73,8 @@ static int menu_delsig (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_clean (ctrl_t ctrl, kbnode_t keyblock, int self_only); static void menu_delkey (KBNODE pub_keyblock); static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive); +static int menu_addadsk (ctrl_t ctrl, kbnode_t pub_keyblock, + const char *adskfpr); static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, int unattended, u32 newexpiration); static int menu_changeusage (ctrl_t ctrl, kbnode_t keyblock); @@ -1243,7 +1245,7 @@ enum cmdids cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, - cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN, + cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN, cmdADDADSK, #ifndef NO_TRUST_MODELS cmdENABLEKEY, cmdDISABLEKEY, #endif /*!NO_TRUST_MODELS*/ @@ -1308,6 +1310,8 @@ static struct { "delkey", cmdDELKEY, 0, N_("delete selected subkeys")}, { "addrevoker", cmdADDREVOKER, KEYEDIT_NEED_SK, N_("add a revocation key")}, + { "addadsk", cmdADDADSK, KEYEDIT_NEED_SK, + N_("add an additional decryption subkey")}, { "delsig", cmdDELSIG, 0, N_("delete signatures from the selected user IDs")}, { "expire", cmdEXPIRE, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK, @@ -1421,6 +1425,8 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, int sec_shadowing = 0; int run_subkey_warnings = 0; int have_commands = !!commands; + strlist_t delseckey_list = NULL; + int delseckey_list_warn = 0; if (opt.command_fd != -1) ; @@ -1497,6 +1503,14 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, subkey_expire_warning (keyblock); } + if (delseckey_list_warn) + { + delseckey_list_warn = 0; + tty_printf + (_("Note: the local copy of the secret key" + " will only be deleted with \"save\".\n")); + } + do { xfree (answer); @@ -1869,10 +1883,12 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, if (node) { PKT_public_key *xxpk = node->pkt->pkt.public_key; - if (card_store_subkey (node, xxpk ? xxpk->pubkey_usage : 0)) + if (card_store_subkey (node, xxpk ? xxpk->pubkey_usage : 0, + &delseckey_list)) { redisplay = 1; sec_shadowing = 1; + delseckey_list_warn = 1; } } } @@ -1949,7 +1965,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, pkt->pkttype = PKT_PUBLIC_KEY; /* Ask gpg-agent to store the secret key to card. */ - if (card_store_subkey (node, 0)) + if (card_store_subkey (node, 0, NULL)) { redisplay = 1; sec_shadowing = 1; @@ -2000,6 +2016,15 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, } break; + case cmdADDADSK: + if (menu_addadsk (ctrl, keyblock, NULL)) + { + redisplay = 1; + modified = 1; + merge_keys_and_selfsig (ctrl, keyblock); + } + break; + case cmdREVUID: { int n1; @@ -2250,6 +2275,27 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, } } + if (delseckey_list) + { + strlist_t sl; + for (err = 0, sl = delseckey_list; sl; sl = sl->next) + { + if (*sl->d) + { + err = agent_delete_key (ctrl, sl->d, NULL, 1/*force*/); + if (err) + break; + *sl->d = 0; /* Mark deleted. */ + } + } + if (err) + { + log_error (_("deleting copy of secret key failed: %s\n"), + gpg_strerror (err)); + break; /* the "save". */ + } + } + if (sec_shadowing) { err = agent_scd_learn (NULL, 1); @@ -2279,6 +2325,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, } /* End of the main command loop. */ leave: + free_strlist (delseckey_list); release_kbnode (keyblock); keydb_release (kdbhd); xfree (answer); @@ -3201,6 +3248,69 @@ keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr, } +/* Unattended ADSK setup function. + * + * FPR is the fingerprint of our key. ADSKFPR is the fingerprint of + * another subkey which we want to add as ADSK to our key. + */ +void +keyedit_quick_addadsk (ctrl_t ctrl, const char *fpr, const char *adskfpr) +{ + gpg_error_t err; + kbnode_t keyblock; + KEYDB_HANDLE kdbhd; + int modified = 0; + PKT_public_key *pk; + +#ifdef HAVE_W32_SYSTEM + /* See keyedit_menu for why we need this. */ + check_trustdb_stale (ctrl); +#endif + + /* We require a fingerprint because only this uniquely identifies a + * key and may thus be used to select a key for unattended adsk + * adding. */ + if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)) + goto leave; + + if (fix_keyblock (ctrl, &keyblock)) + modified++; + + pk = keyblock->pkt->pkt.public_key; + if (pk->flags.revoked) + { + if (!opt.verbose) + show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); + log_error ("%s%s", _("Key is revoked."), "\n"); + goto leave; + } + + /* Locate and add the ADSK. Note that the called function already + * prints error messages. */ + if (menu_addadsk (ctrl, keyblock, adskfpr)) + modified = 1; + else + log_inc_errorcount (); /* (We use log_info in menu_adsk) */ + + es_fflush (es_stdout); + + /* Store. */ + if (modified) + { + err = keydb_update_keyblock (ctrl, kdbhd, keyblock); + if (err) + { + log_error (_("update failed: %s\n"), gpg_strerror (err)); + goto leave; + } + } + + leave: + release_kbnode (keyblock); + keydb_release (kdbhd); +} + + /* Unattended expiration setting function for the main key. If * SUBKEYFPRS is not NULL and SUBKEYSFPRS[0] is neither NULL, it is * expected to be an array of fingerprints for subkeys to change. It @@ -4643,6 +4753,176 @@ fail: } +/* + * Ask for a new additional decryption subkey and add it to the key + * block. Returns true if the keyblock was changed and false + * otherwise. If ADSKFPR is not NULL, this fucntion has been called + * by quick_addadsk and gives the fingerprint of the to be added key. + */ +static int +menu_addadsk (ctrl_t ctrl, kbnode_t pub_keyblock, const char *adskfpr) +{ + PKT_public_key *pk; + PKT_public_key *sub_pk; + PKT_public_key *main_pk; + PKT_public_key *adsk_pk = NULL; + kbnode_t adsk_keyblock = NULL; + PKT_signature *sig = NULL; + char *answer = NULL; + gpg_error_t err; + KEYDB_SEARCH_DESC desc; + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fprlen; + kbnode_t node, node2; + kbnode_t subkeynode = NULL; + PACKET *pkt; /* (temp. use; will be put into a kbnode_t) */ + + log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY); + main_pk = pub_keyblock->pkt->pkt.public_key; + + for (;;) + { + xfree (answer); + if (adskfpr) + answer = xstrdup (adskfpr); + else + { + answer = cpr_get_utf8 + ("keyedit.addadsk", + _("Enter the fingerprint of the additional decryption subkey: ")); + if (answer[0] == '\0' || answer[0] == CONTROL_D) + { + err = gpg_error (GPG_ERR_CANCELED); + goto leave; + } + } + if (classify_user_id (answer, &desc, 1) + || desc.mode != KEYDB_SEARCH_MODE_FPR) + { + log_info (_("\"%s\" is not a fingerprint\n"), answer); + err = gpg_error (GPG_ERR_INV_USER_ID); + if (adskfpr) + goto leave; + continue; + } + + free_public_key (adsk_pk); + adsk_pk = xcalloc (1, sizeof *adsk_pk); + adsk_pk->req_usage = PUBKEY_USAGE_ENC; + release_kbnode (adsk_keyblock); + adsk_keyblock = NULL; + err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, adsk_pk, answer, &adsk_keyblock, NULL, 1); + if (err) + { + log_info (_("key \"%s\" not found: %s\n"), answer, + gpg_strerror (err)); + if ((!opt.batch || adskfpr) && !opt.quiet + && gpg_err_code (err) == GPG_ERR_UNUSABLE_PUBKEY) + log_info (_("Did you specify the fingerprint of a subkey?\n")); + if (adskfpr) + goto leave; + continue; + } + + for (node = adsk_keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_PUBLIC_KEY + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + pk = node->pkt->pkt.public_key; + fingerprint_from_pk (pk, fpr, &fprlen); + if (fprlen == desc.fprlen + && !memcmp (fpr, desc.u.fpr, fprlen) + && (pk->pubkey_usage & PUBKEY_USAGE_ENC)) + break; + } + } + if (!node) + { + err = gpg_error (GPG_ERR_WRONG_KEY_USAGE); + log_info (_("key \"%s\" not found: %s\n"), answer, + gpg_strerror (err)); + if ((!opt.batch || adskfpr) && !opt.quiet) + log_info (_("Did you specify the fingerprint of a subkey?\n")); + if (adskfpr) + goto leave; + continue; + } + + /* Check that the selected subkey is not yet on our keyblock. */ + for (node2 = pub_keyblock; node2; node2 = node2->next) + { + if (node2->pkt->pkttype == PKT_PUBLIC_KEY + || node2->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + pk = node2->pkt->pkt.public_key; + fingerprint_from_pk (pk, fpr, &fprlen); + if (fprlen == desc.fprlen + && !memcmp (fpr, desc.u.fpr, fprlen)) + break; + } + } + if (node2) + { + log_info (_("key \"%s\" is already on this keyblock\n"), answer); + err = gpg_error (GPG_ERR_DUP_KEY); + if (adskfpr) + goto leave; + continue; + } + + break; + } + + /* Append the subkey. + * Note that we don't use the ADSK_PK directly because this is the + * primary key and in general we use a subkey to which NODE points. + * ADSK_PK has only been used to pass the requested key usage to + * get_pubkey_byname. SUB_PK will point to the actual adsk. */ + log_assert (node->pkt->pkttype == PKT_PUBLIC_KEY + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY); + sub_pk = copy_public_key_basics (NULL, node->pkt->pkt.public_key); + keyid_from_pk (main_pk, sub_pk->main_keyid); /* Fixup main keyid. */ + log_assert ((sub_pk->pubkey_usage & PUBKEY_USAGE_ENC)); + sub_pk->pubkey_usage = PUBKEY_USAGE_RENC; /* 'e' -> 'r' */ + pkt = xcalloc (1, sizeof *pkt); + pkt->pkttype = PKT_PUBLIC_SUBKEY; /* Make sure it is a subkey. */ + pkt->pkt.public_key = sub_pk; + subkeynode = new_kbnode (pkt); + + /* Make the signature. */ + err = make_keysig_packet (ctrl, &sig, main_pk, NULL, sub_pk, main_pk, 0x18, + sub_pk->timestamp, 0, + keygen_add_key_flags_and_expire, sub_pk, NULL); + if (err) + { + write_status_error ("keysig", err); + log_error ("creating key binding failed: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Append the subkey packet and the binding signature. */ + add_kbnode (pub_keyblock, subkeynode); + subkeynode = NULL; + pkt = xcalloc (1, sizeof *pkt); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = sig; + add_kbnode (pub_keyblock, new_kbnode (pkt)); + + leave: + xfree (answer); + free_public_key (adsk_pk); + release_kbnode (adsk_keyblock); + release_kbnode (subkeynode); + if (!err) + return 1; /* The keyblock was modified. */ + else + return 0; /* Not modified. */ + +} + + /* With FORCE_MAINKEY cleared this function handles the interactive * menu option "expire". With UNATTENDED set to 1 this function only * sets the expiration date of the primary key to NEWEXPIRATION and diff --git a/g10/keyedit.h b/g10/keyedit.h index ea4fd253c..3ed0d0fea 100644 --- a/g10/keyedit.h +++ b/g10/keyedit.h @@ -44,6 +44,7 @@ void keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid); void keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr, const char *usagestr, const char *expirestr); +void keyedit_quick_addadsk (ctrl_t ctrl, const char *fpr, const char *adskfpr); void keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev); void keyedit_quick_sign (ctrl_t ctrl, const char *fpr, diff --git a/g10/keygen.c b/g10/keygen.c index 4dcf7a494..c97783124 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -130,12 +130,6 @@ struct output_control_s }; -struct opaque_data_usage_and_pk { - unsigned int usage; - PKT_public_key *pk; -}; - - /* FIXME: These globals vars are ugly. And using MAX_PREFS even for * aeads is useless, given that we don't expects more than a very few * algorithms. */ @@ -256,22 +250,27 @@ write_uid (kbnode_t root, const char *s) static void do_add_key_flags (PKT_signature *sig, unsigned int use) { - byte buf[1]; - - buf[0] = 0; + byte buf[2] = { 0, 0 }; - /* The spec says that all primary keys MUST be able to certify. */ - if(sig->sig_class!=0x18) - buf[0] |= 0x01; + /* The spec says that all primary keys MUST be able to certify. */ + if ( sig->sig_class != 0x18 ) + buf[0] |= 0x01; - if (use & PUBKEY_USAGE_SIG) - buf[0] |= 0x02; - if (use & PUBKEY_USAGE_ENC) - buf[0] |= 0x04 | 0x08; - if (use & PUBKEY_USAGE_AUTH) - buf[0] |= 0x20; - - build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, 1); + if (use & PUBKEY_USAGE_SIG) + buf[0] |= 0x02; + if (use & PUBKEY_USAGE_ENC) + buf[0] |= 0x04 | 0x08; + if (use & PUBKEY_USAGE_AUTH) + buf[0] |= 0x20; + if (use & PUBKEY_USAGE_GROUP) + buf[0] |= 0x80; + + if (use & PUBKEY_USAGE_RENC) + buf[1] |= 0x04; + if (use & PUBKEY_USAGE_TIME) + buf[1] |= 0x08; + + build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, buf[1]? 2:1); } @@ -318,13 +317,11 @@ keygen_add_key_flags (PKT_signature *sig, void *opaque) } -static int +int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) { - struct opaque_data_usage_and_pk *oduap = opaque; - - do_add_key_flags (sig, oduap->usage); - return keygen_add_key_expire (sig, oduap->pk); + keygen_add_key_flags (sig, opaque); + return keygen_add_key_expire (sig, opaque); } @@ -1215,7 +1212,6 @@ write_keybinding (ctrl_t ctrl, kbnode_t root, PKT_signature *sig; KBNODE node; PKT_public_key *pri_pk, *sub_pk; - struct opaque_data_usage_and_pk oduap; if (opt.verbose) log_info(_("writing key binding signature\n")); @@ -1241,11 +1237,10 @@ write_keybinding (ctrl_t ctrl, kbnode_t root, BUG(); /* Make the signature. */ - oduap.usage = use; - oduap.pk = sub_pk; + sub_pk->pubkey_usage = use; err = make_keysig_packet (ctrl, &sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, timestamp, 0, - keygen_add_key_flags_and_expire, &oduap, + keygen_add_key_flags_and_expire, sub_pk, cache_nonce); if (err) { @@ -3792,14 +3787,29 @@ release_parameter_list (struct para_data_s *r) } } +/* Return the N-th parameter of name KEY from PARA. An IDX of 0 + * returns the first and so on. */ static struct para_data_s * -get_parameter( struct para_data_s *para, enum para_name key ) +get_parameter_idx (struct para_data_s *para, enum para_name key, + unsigned int idx) { - struct para_data_s *r; + struct para_data_s *r; - for( r = para; r && r->key != key; r = r->next ) - ; - return r; + for(r = para; r; r = r->next) + if (r->key == key) + { + if (!idx) + return r; + idx--; + } + return NULL; +} + +/* Return the first parameter of name KEY from PARA. */ +static struct para_data_s * +get_parameter (struct para_data_s *para, enum para_name key) +{ + return get_parameter_idx (para, key, 0); } static const char * @@ -3947,6 +3957,69 @@ parse_parameter_usage (const char *fname, } +/* Parse the revocation key specified by NAME, check that the public + * key exists (so that we can get the required public key algorithm), + * and return a parameter wit the revocation key information. On + * error print a diagnostic and return NULL. */ +static struct para_data_s * +prepare_desig_revoker (ctrl_t ctrl, const char *name) +{ + gpg_error_t err; + struct para_data_s *para = NULL; + KEYDB_SEARCH_DESC desc; + int sensitive = 0; + struct revocation_key revkey; + PKT_public_key *revoker_pk = NULL; + size_t fprlen; + + if (!ascii_strncasecmp (name, "sensitive:", 10) && !spacep (name+10)) + { + name += 10; + sensitive = 1; + } + + if (classify_user_id (name, &desc, 1) + || desc.mode != KEYDB_SEARCH_MODE_FPR) + { + log_info (_("\"%s\" is not a fingerprint\n"), name); + err = gpg_error (GPG_ERR_INV_NAME); + goto leave; + } + + revoker_pk = xcalloc (1, sizeof *revoker_pk); + revoker_pk->req_usage = PUBKEY_USAGE_CERT; + err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, revoker_pk, name, NULL, NULL, 1); + if (err) + goto leave; + + fingerprint_from_pk (revoker_pk, revkey.fpr, &fprlen); + if (fprlen != 20 && fprlen != 32) + { + log_info (_("cannot appoint a PGP 2.x style key as a " + "designated revoker\n")); + err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); + goto leave; + } + revkey.fprlen = fprlen; + revkey.class = 0x80; + if (sensitive) + revkey.class |= 0x40; + revkey.algid = revoker_pk->pubkey_algo; + + para = xcalloc (1, sizeof *para); + para->key = pREVOKER; + memcpy (¶->u.revkey, &revkey, sizeof revkey); + + leave: + if (err) + log_error ("invalid revocation key '%s': %s\n", name, gpg_strerror (err)); + free_public_key (revoker_pk); + return para; +} + + +/* Parse a pREVOKER parameter into its dedicated parts. */ static int parse_revocation_key (const char *fname, struct para_data_s *para, enum para_name key) @@ -4030,10 +4103,11 @@ get_parameter_uint( struct para_data_s *para, enum para_name key ) } static struct revocation_key * -get_parameter_revkey( struct para_data_s *para, enum para_name key ) +get_parameter_revkey (struct para_data_s *para, enum para_name key, + unsigned int idx) { - struct para_data_s *r = get_parameter( para, key ); - return r? &r->u.revkey : NULL; + struct para_data_s *r = get_parameter_idx (para, key, idx); + return r? &r->u.revkey : NULL; } static int @@ -4052,6 +4126,7 @@ proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, const char *s1, *s2, *s3; size_t n; char *p; + strlist_t sl; int is_default = 0; int have_user_id = 0; int err, algo; @@ -4197,10 +4272,20 @@ proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, } } - /* Set revoker, if any. */ + /* Set revoker from parameter file, if any. Must be done first so + * that we don't find a parameter set via prepare_desig_revoker. */ if (parse_revocation_key (fname, para, pREVOKER)) return -1; + /* Check and appened revokers from the config file. */ + for (sl = opt.desig_revokers; sl; sl = sl->next) + { + r = prepare_desig_revoker (ctrl, sl->d); + if (!r) + return -1; + append_to_parameter (para, r); + } + /* Make KEYCREATIONDATE from Creation-Date. We ignore this if the * key has been taken from a card and a keycreationtime has already @@ -5330,6 +5415,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, const char *key_from_hexgrip = NULL; int cardkey; unsigned int keygen_flags; + unsigned int idx; if (outctrl->dryrun) { @@ -5464,7 +5550,10 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, keyid_copy (pri_psk->main_keyid, pri_psk->keyid); } - if (!err && (revkey = get_parameter_revkey (para, pREVOKER))) + /* Write all signatures specifying designated revokers. */ + for (idx=0; + !err && (revkey = get_parameter_revkey (para, pREVOKER, idx)); + idx++) err = write_direct_sig (ctrl, pub_root, pri_psk, revkey, signtimestamp, cache_nonce); diff --git a/g10/keylist.c b/g10/keylist.c index 1ced732a4..8b7c597cb 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1216,7 +1216,8 @@ cmp_signodes (const void *av, const void *bv) } -/* Helper for list_keyblock_print. */ +/* Helper for list_keyblock_print. The caller must have set + * NODFLG_MARK_B to indicate self-signatures. */ static void list_signature_print (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, struct keylist_context *listctx) @@ -1247,6 +1248,11 @@ list_signature_print (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, case GPG_ERR_UNUSABLE_PUBKEY: listctx->no_key++; return; + case GPG_ERR_DIGEST_ALGO: + case GPG_ERR_PUBKEY_ALGO: + if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS)) + return; + /* fallthru. */ default: listctx->oth_err++; sigrc = '%'; @@ -1259,6 +1265,15 @@ list_signature_print (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, } else { + if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS) + && (gpg_err_code (openpgp_pk_test_algo (sig->pubkey_algo) + == GPG_ERR_PUBKEY_ALGO) + || gpg_err_code (openpgp_md_test_algo (sig->digest_algo) + == GPG_ERR_DIGEST_ALGO) + || (sig->digest_algo == DIGEST_ALGO_SHA1 + && !(node->flag & NODFLG_MARK_B) /*no selfsig*/ + && !opt.flags.allow_weak_key_signatures))) + return; rc = 0; sigrc = ' '; } @@ -1306,7 +1321,9 @@ list_signature_print (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); else if (sigrc == '?') ; - else if (!opt.fast_list_mode) + else if ((node->flag & NODFLG_MARK_B)) + es_fputs (_("[self-signature]"), es_stdout); + else if (!opt.fast_list_mode ) { size_t n; char *p = get_user_id (ctrl, sig->keyid, &n, NULL); @@ -1585,37 +1602,33 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs) { - if ((opt.list_options & LIST_SORT_SIGS)) - { - kbnode_t n; - unsigned int sigcount = 0; - kbnode_t *sigarray; - unsigned int idx; + kbnode_t n; + unsigned int sigcount = 0; + kbnode_t *sigarray; + unsigned int idx; - for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) - sigcount++; - sigarray = xcalloc (sigcount, sizeof *sigarray); + for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) + sigcount++; + sigarray = xcalloc (sigcount, sizeof *sigarray); - sigcount = 0; - for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) - { - if (!keyid_cmp (mainkid, n->pkt->pkt.signature->keyid)) - n->flag |= NODFLG_MARK_B; /* Is a self-sig. */ - else - n->flag &= ~NODFLG_MARK_B; + sigcount = 0; + for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) + { + if (keyid_eq (mainkid, n->pkt->pkt.signature->keyid)) + n->flag |= NODFLG_MARK_B; /* Is a self-sig. */ + else + n->flag &= ~NODFLG_MARK_B; - sigarray[sigcount++] = node = n; - } - /* Note that NODE is now at the last signature. */ + sigarray[sigcount++] = node = n; + } + /* Note that NODE is now at the last signature. */ - qsort (sigarray, sigcount, sizeof *sigarray, cmp_signodes); + if ((opt.list_options & LIST_SORT_SIGS)) + qsort (sigarray, sigcount, sizeof *sigarray, cmp_signodes); - for (idx=0; idx < sigcount; idx++) - list_signature_print (ctrl, keyblock, sigarray[idx], listctx); - xfree (sigarray); - } - else - list_signature_print (ctrl, keyblock, node, listctx); + for (idx=0; idx < sigcount; idx++) + list_signature_print (ctrl, keyblock, sigarray[idx], listctx); + xfree (sigarray); } } es_putc ('\n', es_stdout); diff --git a/g10/main.h b/g10/main.h index 62d2651be..b29e23e51 100644 --- a/g10/main.h +++ b/g10/main.h @@ -42,7 +42,7 @@ #endif #define DEFAULT_DIGEST_ALGO ((GNUPG)? DIGEST_ALGO_SHA256:DIGEST_ALGO_SHA1) -#define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1 +#define DEFAULT_S2K_DIGEST_ALGO DEFAULT_DIGEST_ALGO #ifdef HAVE_ZIP # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP #else @@ -83,6 +83,7 @@ struct weakhash /*-- gpg.c --*/ extern int g10_errors_seen; +extern int assert_signer_true; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_exit(int rc) __attribute__ ((__noreturn__)); @@ -235,7 +236,7 @@ int cpr_get_answer_okay_cancel (const char *keyword, /*-- helptext.c --*/ void display_online_help( const char *keyword ); -/*-- encode.c --*/ +/*-- encrypt.c --*/ gpg_error_t setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek); aead_algo_t use_aead (pk_list_t pk_list, int algo); int use_mdc (pk_list_t pk_list,int algo); @@ -315,6 +316,7 @@ int keygen_set_std_prefs (const char *string,int personal); PKT_user_id *keygen_get_std_prefs (void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_key_flags (PKT_signature *sig, void *opaque); +int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int keygen_add_keyserver_url(PKT_signature *sig, void *opaque); @@ -491,6 +493,7 @@ void print_file_status( int status, const char *name, int what ); int verify_signatures (ctrl_t ctrl, int nfiles, char **files ); int verify_files (ctrl_t ctrl, int nfiles, char **files ); int gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp); +void check_assert_signer_list (const char *mainpkhex, const char *pkhex); /*-- decrypt.c --*/ int decrypt_message (ctrl_t ctrl, const char *filename ); @@ -515,7 +518,7 @@ void change_pin (int no, int allow_admin); void card_status (ctrl_t ctrl, estream_t fp, const char *serialno); void card_edit (ctrl_t ctrl, strlist_t commands); gpg_error_t card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock); -int card_store_subkey (KBNODE node, int use); +int card_store_subkey (KBNODE node, int use, strlist_t *processed_keys); #endif /*-- migrate.c --*/ diff --git a/g10/mainproc.c b/g10/mainproc.c index 4710386ea..ce0fdaaac 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -2410,7 +2410,7 @@ check_sig_and_print (CTX c, kbnode_t node) } /* For good signatures print the VALIDSIG status line. */ - if (!rc && is_status_enabled () && pk) + if (!rc && (is_status_enabled () || opt.assert_signer_list) && pk) { char pkhex[MAX_FINGERPRINT_LEN*2+1]; char mainpkhex[MAX_FINGERPRINT_LEN*2+1]; @@ -2430,6 +2430,8 @@ check_sig_and_print (CTX c, kbnode_t node) sig->digest_algo, sig->sig_class, mainpkhex); + /* Handle the --assert-signer option. */ + check_assert_signer_list (mainpkhex, pkhex); } /* Print compliance warning for Good signatures. */ @@ -2510,6 +2512,7 @@ check_sig_and_print (CTX c, kbnode_t node) is not a detached signature. */ log_info (_("WARNING: not a detached signature; " "file '%s' was NOT verified!\n"), dfile); + assert_signer_true = 0; } xfree (dfile); } diff --git a/g10/misc.c b/g10/misc.c index dcf001877..2f4b452dd 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -777,21 +777,21 @@ openpgp_pk_algo_usage ( int algo ) switch ( algo ) { case PUBKEY_ALGO_RSA: use = (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG - | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH); + | PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC | PUBKEY_USAGE_AUTH); break; case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_ECDH: - use = PUBKEY_USAGE_ENC; + use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; case PUBKEY_ALGO_RSA_S: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; break; case PUBKEY_ALGO_ELGAMAL: if (RFC2440) - use = PUBKEY_USAGE_ENC; + use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; case PUBKEY_ALGO_ELGAMAL_E: - use = PUBKEY_USAGE_ENC; + use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; case PUBKEY_ALGO_DSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; @@ -1563,9 +1563,10 @@ parse_options(char *str,unsigned int *options, { char *tok; - if (str && !strcmp (str, "help")) + if (str && (!strcmp (str, "help") || !strcmp (str, "full-help"))) { int i,maxlen=0; + int full = *str == 'f'; /* Figure out the longest option name so we can line these up neatly. */ @@ -1577,6 +1578,10 @@ parse_options(char *str,unsigned int *options, if(opts[i].help) es_printf("%s%*s%s\n",opts[i].name, maxlen+2-(int)strlen(opts[i].name),"",_(opts[i].help)); + if (full) + for (i=0; opts[i].name; i++) + if(!opts[i].help) + es_printf("%s\n",opts[i].name); g10_exit(0); } diff --git a/g10/options.h b/g10/options.h index 74a6cdb16..9015e321f 100644 --- a/g10/options.h +++ b/g10/options.h @@ -111,6 +111,9 @@ struct * the option --sender. */ strlist_t sender_list; + /* A list of fingerprints added as designated revokers to new keys. */ + strlist_t desig_revokers; + int def_cert_level; int min_cert_level; int ask_cert_level; @@ -232,6 +235,10 @@ struct value. */ int limit_card_insert_tries; + /* The list of --assert-signer option values. Note: The values are + * modify to be uppercase if they represent a fingerrint */ + strlist_t assert_signer_list; + struct { /* If set, require an 0x19 backsig to be present on signatures @@ -426,6 +433,7 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define LIST_SORT_SIGS (1<<13) #define LIST_SHOW_PREF (1<<14) #define LIST_SHOW_PREF_VERBOSE (1<<15) +#define LIST_SHOW_UNUSABLE_SIGS (1<<16) #define VERIFY_SHOW_PHOTOS (1<<0) #define VERIFY_SHOW_POLICY_URLS (1<<1) diff --git a/g10/packet.h b/g10/packet.h index eeea9b450..39dab96c9 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -56,9 +56,15 @@ | GCRY_PK_USAGE_AUTH | GCRY_PK_USAGE_UNKN) >= 256 # error Please choose another value for PUBKEY_USAGE_NONE #endif -#define PUBKEY_USAGE_RENC 512 /* Restricted encryption. */ -#define PUBKEY_USAGE_TIME 1024 /* Timestamp use. */ #define PUBKEY_USAGE_GROUP 512 /* Group flag. */ +#define PUBKEY_USAGE_RENC 1024 /* Restricted encryption. */ +#define PUBKEY_USAGE_TIME 2048 /* Timestamp use. */ + +/* Bitflags to convey hints on what kind of signature is created. */ +#define SIGNHINT_KEYSIG 1 +#define SIGNHINT_SELFSIG 2 +#define SIGNHINT_ADSK 4 + /* Helper macros. */ #define is_RSA(a) ((a)==PUBKEY_ALGO_RSA || (a)==PUBKEY_ALGO_RSA_E \ @@ -287,7 +293,7 @@ typedef struct /* The length of ATTRIB_DATA. */ unsigned long attrib_len; byte *namehash; - int help_key_usage; + u16 help_key_usage; u32 help_key_expire; int help_full_count; int help_marginal_count; @@ -388,7 +394,7 @@ typedef struct byte selfsigversion; /* highest version of all of the self-sigs */ /* The public key algorithm. (Serialized.) */ byte pubkey_algo; - byte pubkey_usage; /* for now only used to pass it to getkey() */ + u16 pubkey_usage; /* carries the usage info. */ 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 */ @@ -861,7 +867,8 @@ gpg_error_t gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a); u32 calc_packet_length( PACKET *pkt ); void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ); -void build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk); +void build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk, + unsigned int signhints); int delete_sig_subpkt(subpktarea_t *buffer, sigsubpkttype_t type ); void build_attribute_subpkt(PKT_user_id *uid,byte type, const void *buf,u32 buflen, @@ -883,6 +890,7 @@ void free_user_id( PKT_user_id *uid ); void free_comment( PKT_comment *rem ); void free_packet (PACKET *pkt, parse_packet_ctx_t parsectx); prefitem_t *copy_prefs (const prefitem_t *prefs); +PKT_public_key *copy_public_key_basics (PKT_public_key *d, PKT_public_key *s); PKT_public_key *copy_public_key( PKT_public_key *d, PKT_public_key *s ); PKT_signature *copy_signature( PKT_signature *d, PKT_signature *s ); PKT_user_id *scopy_user_id (PKT_user_id *sd ); diff --git a/g10/pkclist.c b/g10/pkclist.c index 459e7595a..2e8932b9c 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -845,7 +845,8 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, { int rc; PKT_public_key *pk; - KBNODE keyblock = NULL; + kbnode_t keyblock = NULL; + kbnode_t node; if (!name || !*name) return gpg_error (GPG_ERR_INV_USER_ID); @@ -856,7 +857,7 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, pk->req_usage = use; if (from_file) - rc = get_pubkey_fromfile (ctrl, pk, name); + rc = get_pubkey_fromfile (ctrl, pk, name, &keyblock); else rc = get_best_pubkey_byname (ctrl, GET_PUBKEY_NORMAL, NULL, pk, name, &keyblock, 0); @@ -895,10 +896,10 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, int trustlevel; trustlevel = get_validity (ctrl, keyblock, pk, pk->user_id, NULL, 1); - release_kbnode (keyblock); if ( (trustlevel & TRUST_FLAG_DISABLED) ) { /* Key has been disabled. */ + release_kbnode (keyblock); send_status_inv_recp (13, name); log_info (_("%s: skipped: public key is disabled\n"), name); free_public_key (pk); @@ -908,6 +909,7 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, if ( !do_we_trust_pre (ctrl, pk, trustlevel) ) { /* We don't trust this key. */ + release_kbnode (keyblock); send_status_inv_recp (10, name); free_public_key (pk); return GPG_ERR_UNUSABLE_PUBKEY; @@ -926,19 +928,33 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, { pk_list_t r; - r = xtrymalloc (sizeof *r); - if (!r) - { - rc = gpg_error_from_syserror (); - free_public_key (pk); - return rc; - } + r = xmalloc (sizeof *r); r->pk = pk; r->next = *pk_list_addr; r->flags = mark_hidden? 1:0; *pk_list_addr = r; } + for (node = keyblock; node; node = node->next) + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + && ((pk=node->pkt->pkt.public_key)->pubkey_usage & PUBKEY_USAGE_RENC) + && pk->flags.valid + && !pk->flags.revoked + && !pk->flags.disabled + && !pk->has_expired + && key_present_in_pk_list (*pk_list_addr, pk)) + { + pk_list_t r; + + r = xmalloc (sizeof *r); + r->pk = copy_public_key (NULL, pk); + r->next = *pk_list_addr; + r->flags = mark_hidden? 1:0; /* FIXME: Use PK_LIST_HIDDEN ? */ + *pk_list_addr = r; + } + + + release_kbnode (keyblock); return 0; } diff --git a/g10/sig-check.c b/g10/sig-check.c index 7a2c934cd..06329f659 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -363,7 +363,8 @@ check_signature_metadata_validity (PKT_public_key *pk, PKT_signature *sig, if (r_revoked) *r_revoked = 0; - if (pk->timestamp > sig->timestamp ) + if (pk->timestamp > sig->timestamp + && !(parse_key_usage (sig) & PUBKEY_USAGE_RENC)) { ulong d = pk->timestamp - sig->timestamp; if ( d < 86400 ) diff --git a/g10/sign.c b/g10/sign.c index a66410ebd..b5e9d422d 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -50,11 +50,6 @@ #endif -/* Bitflags to convey hints on what kind of signayire is created. */ -#define SIGNHINT_KEYSIG 1 -#define SIGNHINT_SELFSIG 2 - - /* Hack */ static int recipient_digest_algo; @@ -416,7 +411,10 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, byte *dp; char *hexgrip; - if (pksk->timestamp > sig->timestamp ) + /* An ADSK key commonly has a creation date older than the primary + * key. For example because the ADSK is used as an archive key for + * a group of users. */ + if (pksk->timestamp > sig->timestamp && !(signhints & SIGNHINT_ADSK)) { ulong d = pksk->timestamp - sig->timestamp; log_info (ngettext("key %s was created %lu second" @@ -964,7 +962,7 @@ write_signature_packets (ctrl_t ctrl, if (gcry_md_copy (&md, hash)) BUG (); - build_sig_subpkt_from_sig (sig, pk); + build_sig_subpkt_from_sig (sig, pk, 0); mk_notation_policy_etc (ctrl, sig, NULL, pk); if (opt.flags.include_key_block && IS_SIG (sig)) err = mk_sig_subpkt_key_block (ctrl, sig, pk); @@ -1758,14 +1756,14 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) * * SIGCLASS is the type of signature to create. * - * DIGEST_ALGO is the digest algorithm. If it is 0 the function - * selects an appropriate one. - * * TIMESTAMP is the timestamp to use for the signature. 0 means "now" * * DURATION is the amount of time (in seconds) until the signature * expires. * + * If CACHED_NONCE is not NULL the agent may use it to avoid + * additional pinnetry popups for the same keyblock. + * * This function creates the following subpackets: issuer, created, * and expire (if duration is not 0). Additional subpackets can be * added using MKSUBPKT, which is called after these subpackets are @@ -1833,6 +1831,8 @@ make_keysig_packet (ctrl_t ctrl, { /* Hash the subkey binding/backsig/revocation. */ hash_public_key (md, subpk); + if ((subpk->pubkey_usage & PUBKEY_USAGE_RENC)) + signhints |= SIGNHINT_ADSK; } else if (sigclass != 0x1F && sigclass != 0x20) { @@ -1852,7 +1852,7 @@ make_keysig_packet (ctrl_t ctrl, sig->expiredate = sig->timestamp + duration; sig->sig_class = sigclass; - build_sig_subpkt_from_sig (sig, pksk); + build_sig_subpkt_from_sig (sig, pksk, signhints); mk_notation_policy_etc (ctrl, sig, pk, pksk); /* Crucial that the call to mksubpkt comes LAST before the calls @@ -1976,6 +1976,12 @@ update_keysig_packet (ctrl_t ctrl, } } + /* Detect an ADSK key binding signature. */ + if ((sig->sig_class == 0x18 + || sig->sig_class == 0x19 || sig->sig_class == 0x28) + && (pk->pubkey_usage & PUBKEY_USAGE_RENC)) + signhints |= SIGNHINT_ADSK; + /* Note that already expired sigs will remain expired (with a * duration of 1) since build-packet.c:build_sig_subpkt_from_sig * detects this case. */ @@ -1984,7 +1990,7 @@ update_keysig_packet (ctrl_t ctrl, * automagically lower any sig expiration dates to correctly * correspond to the differences in the timestamps (i.e. the * duration will shrink). */ - build_sig_subpkt_from_sig (sig, pksk); + build_sig_subpkt_from_sig (sig, pksk, signhints); if (mksubpkt) rc = (*mksubpkt)(sig, opaque); diff --git a/g10/t-keydb-get-keyblock.c b/g10/t-keydb-get-keyblock.c index 90ce6e9a6..e40be9cc1 100644 --- a/g10/t-keydb-get-keyblock.c +++ b/g10/t-keydb-get-keyblock.c @@ -67,3 +67,12 @@ do_test (int argc, char *argv[]) release_kbnode (kb1); xfree (ctrl); } + +int assert_signer_true = 0; + +void +check_assert_signer_list (const char *mainpkhex, const char *pkhex) +{ + (void)mainpkhex; + (void)pkhex; +} diff --git a/g10/t-keydb.c b/g10/t-keydb.c index 4c78dac48..9055d5b94 100644 --- a/g10/t-keydb.c +++ b/g10/t-keydb.c @@ -105,3 +105,13 @@ do_test (int argc, char *argv[]) keydb_release (hd2); xfree (ctrl); } + + +int assert_signer_true = 0; + +void +check_assert_signer_list (const char *mainpkhex, const char *pkhex) +{ + (void)mainpkhex; + (void)pkhex; +} diff --git a/g10/t-stutter.c b/g10/t-stutter.c index 503a92004..7b2ea4b37 100644 --- a/g10/t-stutter.c +++ b/g10/t-stutter.c @@ -611,3 +611,12 @@ do_test (int argc, char *argv[]) xfree (filename); } + +int assert_signer_true = 0; + +void +check_assert_signer_list (const char *mainpkhex, const char *pkhex) +{ + (void)mainpkhex; + (void)pkhex; +} diff --git a/g10/trust.c b/g10/trust.c index 9749bd786..f11dfb759 100644 --- a/g10/trust.c +++ b/g10/trust.c @@ -59,7 +59,7 @@ register_trusted_key (const char *string) /* Some users have conf files with entries like * trusted-key 0x1234567812345678 # foo * That is obviously wrong. Before fixing bug#1206 trailing garbage - * on a key specification if was ignored. We detect the above use case + * on a key specification was ignored. We detect the above use case * here and cut off the junk-looking-like-a comment. */ if (strchr (string, '#')) { diff --git a/g10/verify.c b/g10/verify.c index fc18882b0..e9792939d 100644 --- a/g10/verify.c +++ b/g10/verify.c @@ -1,6 +1,8 @@ /* verify.c - Verify signed data * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, * 2007, 2010 Free Software Foundation, Inc. + * Copyright (C) 2003, 2006-2008, 2010-2011, 2015-2017, + * 2020, 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -16,6 +18,7 @@ * * 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> @@ -281,3 +284,124 @@ gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp) release_armor_context (afx); return rc; } + + +static int +is_fingerprint (const char *string) +{ + int n; + + if (!string || !*string) + return 0; + for (n=0; hexdigitp (string); string++) + n++; + if (!*string && (n == 40 || n == 64)) + return 1; /* v4 or v5 fingerprint. */ + + return 0; +} + + +/* This function shall be called with the main and subkey fingerprint + * iff a signature is fully valid. If the option --assert-signer is + * active it check whether the signing key matches one of the keys + * given by this option and if so, sets a global flag. */ +void +check_assert_signer_list (const char *mainpkhex, const char *pkhex) +{ + gpg_error_t err; + strlist_t item; + const char *fname; + estream_t fp = NULL; + int lnr; + int n, c; + char *p, *pend; + char line[256]; + + if (!opt.assert_signer_list) + return; /* Nothing to do. */ + if (assert_signer_true) + return; /* Already one valid signature seen. */ + + for (item = opt.assert_signer_list; item; item = item->next) + { + if (is_fingerprint (item->d)) + { + ascii_strupr (item->d); + if (!strcmp (item->d, mainpkhex) || !strcmp (item->d, pkhex)) + { + assert_signer_true = 1; + write_status_text (STATUS_ASSERT_SIGNER, item->d); + if (!opt.quiet) + log_info ("signer '%s' matched\n", item->d); + goto leave; + } + } + else /* Assume this is a file - read and compare. */ + { + fname = item->d; + es_fclose (fp); + fp = es_fopen (fname, "r"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error (_("error opening '%s': %s\n"), + fname, gpg_strerror (err)); + continue; + } + + lnr = 0; + err = 0; + while (es_fgets (line, DIM(line)-1, fp)) + { + lnr++; + + n = strlen (line); + if (!n || line[n-1] != '\n') + { + /* Eat until end of line. */ + while ( (c=es_getc (fp)) != EOF && c != '\n') + ; + err = gpg_error (GPG_ERR_INCOMPLETE_LINE); + log_error (_("file '%s', line %d: %s\n"), + fname, lnr, gpg_strerror (err)); + continue; + } + line[--n] = 0; /* Chop the LF. */ + if (n && line[n-1] == '\r') + line[--n] = 0; /* Chop an optional CR. */ + + /* Allow for empty lines and spaces */ + for (p=line; spacep (p); p++) + ; + if (!*p || *p == '#') + continue; + + /* Get the first token and ignore trailing stuff. */ + for (pend = p; *pend && !spacep (pend); pend++) + ; + *pend = 0; + ascii_strupr (p); + + if (!strcmp (p, mainpkhex) || !strcmp (p, pkhex)) + { + assert_signer_true = 1; + write_status_text (STATUS_ASSERT_SIGNER, p); + if (!opt.quiet) + log_info ("signer '%s' matched '%s', line %d\n", + p, fname, lnr); + goto leave; + } + } + if (!err && !es_feof (fp)) + { + err = gpg_error_from_syserror (); + log_error (_("error reading '%s', line %d: %s\n"), + fname, lnr, gpg_strerror (err)); + } + } + } + + leave: + es_fclose (fp); +} |