From 71c11c20f41d660d468de642b33cdc330ff682c7 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 21 Feb 2023 12:14:31 +0100 Subject: gpg: Prepare to accept shorter OIDs for ed25519 and cv25519. * common/openpgp-oid.c (oidtable): Add them. (oid_ed25519_v5, oid_cv25519_v5): New. (openpgp_oidbuf_is_ed25519): Take new OID in account. (openpgp_oidbuf_is_cv25519): Ditto. -- ed25519 is used in GnuPG and other implementations since 2015 and thus we can't simply switch to the shorter OIDs. However, we have not widely used them with v5 keys (only ed448 forced the use of v5) and thus it might be possible to use the new OIDs with v5 keys. Note that Libgcrypt supports the new OIDs even in 1.8. --- common/openpgp-oid.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index f0460b068..510e09f4a 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -48,6 +48,8 @@ static struct { { "Curve25519", "1.3.6.1.4.1.3029.1.5.1", 255, "cv25519", PUBKEY_ALGO_ECDH }, { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519", PUBKEY_ALGO_EDDSA }, + { "Curve25519", "1.3.101.110", 255, "cv25519", PUBKEY_ALGO_ECDH }, + { "Ed25519", "1.3.101.112", 255, "ed25519", PUBKEY_ALGO_EDDSA }, { "X448", "1.3.101.111", 448, "cv448", PUBKEY_ALGO_ECDH }, { "Ed448", "1.3.101.113", 456, "ed448", PUBKEY_ALGO_EDDSA }, @@ -65,13 +67,17 @@ static struct { }; -/* The OID for Curve Ed25519 in OpenPGP format. */ +/* The OID for Curve Ed25519 in OpenPGP format. The shorter v5 + * variant may only be used with v5 keys. */ static const char oid_ed25519[] = { 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 }; +static const char oid_ed25519_v5[] = { 0x03, 0x2b, 0x65, 0x70 }; -/* The OID for Curve25519 in OpenPGP format. */ +/* The OID for Curve25519 in OpenPGP format. The shorter v5 + * variant may only be used with v5 keys. */ static const char oid_cv25519[] = { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 }; +static const char oid_cv25519_v5[] = { 0x03, 0x2b, 0x65, 0x6e }; /* The OID for X448 in OpenPGP format. */ /* @@ -321,8 +327,12 @@ openpgp_oid_to_str (gcry_mpi_t a) int openpgp_oidbuf_is_ed25519 (const void *buf, size_t len) { - return (buf && len == DIM (oid_ed25519) - && !memcmp (buf, oid_ed25519, DIM (oid_ed25519))); + if (!buf) + return 0; + return ((len == DIM (oid_ed25519) + && !memcmp (buf, oid_ed25519, DIM (oid_ed25519))) + || (len == DIM (oid_ed25519_v5) + && !memcmp (buf, oid_ed25519_v5, DIM (oid_ed25519_v5)))); } @@ -345,8 +355,12 @@ openpgp_oid_is_ed25519 (gcry_mpi_t a) int openpgp_oidbuf_is_cv25519 (const void *buf, size_t len) { - return (buf && len == DIM (oid_cv25519) - && !memcmp (buf, oid_cv25519, DIM (oid_cv25519))); + if (!buf) + return 0; + return ((len == DIM (oid_cv25519) + && !memcmp (buf, oid_cv25519, DIM (oid_cv25519))) + || (len == DIM (oid_cv25519_v5) + && !memcmp (buf, oid_cv25519_v5, DIM (oid_cv25519_v5)))); } -- cgit v1.2.3 From 23b4c6e7c2f715145695e8ec3141d248876d1e25 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 23 Feb 2023 10:20:33 +0100 Subject: dirmngr: New debug flag "keeptmp". * dirmngr/dirmngr.c (debug_flags): Add "keeptmp". (set_debug): Don't set in with "guru". * dirmngr/dirmngr.h (DBG_KEEPTMP_VALUE): New. -- Note that flag is not yet used. --- dirmngr/dirmngr.c | 3 ++- dirmngr/dirmngr.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index cbc693bd8..650770dab 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -324,6 +324,7 @@ static struct debug_flags_s debug_flags [] = { DBG_NETWORK_VALUE, "network" }, { DBG_LOOKUP_VALUE , "lookup" }, { DBG_EXTPROG_VALUE, "extprog" }, + { DBG_KEEPTMP_VALUE, "keeptmp" }, { 77, NULL } /* 77 := Do not exit on "help" or "?". */ }; @@ -534,7 +535,7 @@ set_debug (void) select the highest debug value and would then clutter their disk with debug files which may reveal confidential data. */ if (numok) - opt.debug &= ~(DBG_HASHING_VALUE); + opt.debug &= ~(DBG_HASHING_VALUE|DBG_KEEPTMP_VALUE); } else { diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index e7591b998..f65bcd119 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -166,6 +166,7 @@ struct #define DBG_NETWORK_VALUE 2048 /* debug network I/O. */ #define DBG_LOOKUP_VALUE 8192 /* debug lookup details */ #define DBG_EXTPROG_VALUE 16384 /* debug external program calls */ +#define DBG_KEEPTMP_VALUE 32768 /* keep some temporary files */ #define DBG_X509 (opt.debug & DBG_X509_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) @@ -177,6 +178,7 @@ struct #define DBG_NETWORK (opt.debug & DBG_NETWORK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) #define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE) +#define DBG_KEEPTMP (opt.debug & DBG_KEEPTMP_VALUE) /* A simple list of certificate references. FIXME: Better use certlist_t also for references (Store NULL at .cert) */ -- cgit v1.2.3 From 9de180c6d22253d26b4a75c568490a4932c8b96b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 23 Feb 2023 10:23:56 +0100 Subject: doc: Minor comment fixes. -- --- common/recsel.c | 3 ++- scd/app-p15.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/common/recsel.c b/common/recsel.c index 60da4d3c9..ea0858c84 100644 --- a/common/recsel.c +++ b/common/recsel.c @@ -174,6 +174,7 @@ find_next_lc (char *string) * -c The string match in this part is done case-sensitive. * -t Do not trim leading and trailing spaces from VALUE. * Note that a space after is here required. + * -r RFU: String match uses a regular expression * * For example four calls to recsel_parse_expr() with these values for * EXPR @@ -192,7 +193,7 @@ find_next_lc (char *string) * * The caller must pass the address of a selector variable to this * function and initialize the value of the function to NULL before - * the first call. recset_release needs to be called to free the + * the first call. recsel_release needs to be called to free the * selector. */ gpg_error_t diff --git a/scd/app-p15.c b/scd/app-p15.c index bfd693466..29241af6b 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -326,7 +326,7 @@ struct prkdf_object_s char *serial_number; /* KDF/KEK parameter for OpenPGP's ECDH. First byte is zero if not - * available. .*/ + * available. */ unsigned char ecdh_kdf[4]; /* Length and allocated buffer with the Id of this object. */ -- cgit v1.2.3 From 1952a0e5e41c5f27cac81ff876eba5373f4cfc5f Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Tue, 23 Oct 2018 12:46:38 +0200 Subject: sm: Fix dirmngr loadcrl for intermediate certs * sm/call-dirmngr.c (run_command_inq_cb): Support ISTRUSTED. (inq_certificate): Distinguish unsupported inquiry error. -- When loading a CRL through "gpgsm --call-dirmngr loadcrl foo" dirmngr can ask gpgsm back if a certificate used ISTRUSTED, which previously resulted in an error. (cherry picked from commit 6b36c16f77722d17f4f317c788701cbc1e9552b2) That commit was from the 2.2 branch and we forgot to forward port it. --- sm/call-dirmngr.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index cc958ccf8..da3839349 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -362,7 +362,7 @@ inq_certificate (void *opaque, const char *line) } else { - log_error ("unsupported inquiry '%s'\n", line); + log_error ("unsupported certificate inquiry '%s'\n", line); return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } @@ -1035,9 +1035,33 @@ run_command_inq_cb (void *opaque, const char *line) line = s; log_info ("dirmngr: %s\n", line); } + else if ((s = has_leading_keyword (line, "ISTRUSTED"))) + { + /* The server is asking us whether the certificate is a trusted + root certificate. */ + char fpr[41]; + struct rootca_flags_s rootca_flags; + int n; + + line = s; + + for (s=line,n=0; hexdigitp (s); s++, n++) + ; + if (*s || n != 40) + return gpg_error (GPG_ERR_ASS_PARAMETER); + for (s=line, n=0; n < 40; s++, n++) + fpr[n] = (*s >= 'a')? (*s & 0xdf): *s; + fpr[n] = 0; + + if (!gpgsm_agent_istrusted (parm->ctrl, NULL, fpr, &rootca_flags)) + rc = assuan_send_data (parm->ctx, "1", 1); + else + rc = 0; + return rc; + } else { - log_error ("unsupported inquiry '%s'\n", line); + log_error ("unsupported command inquiry '%s'\n", line); rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } -- cgit v1.2.3 From 5d96aab27dcf1b1c826c483e39d7265b89736b53 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Sun, 26 Feb 2023 19:11:27 +0100 Subject: gpgsm: Improve cert lookup callback from dirmngr. * sm/gpgsm.h (FIND_CERT_ALLOW_AMBIG): New. (FIND_CERT_WITH_EPHEM): New. * sm/certlist.c (gpgsm_find_cert): Replace arg allow_ambiguous by a generic flags arg. Implement the new flag FIND_CERT_WITH_EPHEM. * sm/call-dirmngr.c (inq_certificate): Return also ephemeral marked certs. -- The dirmngr may need to get a certificate from gpgsm's store in the course of verifying a CRL. In some cases the certificate is still marked as epehemeral - this needs to be returned as well. This _may_ also fix GnuPG-bug-id: 4436 --- sm/call-dirmngr.c | 7 ++++--- sm/certlist.c | 6 +++++- sm/gpgsm.h | 5 ++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index da3839349..8e2761b1e 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -386,8 +386,8 @@ inq_certificate (void *opaque, const char *line) int err; ksba_cert_t cert; - - err = gpgsm_find_cert (parm->ctrl, line, ski, &cert, 1); + err = gpgsm_find_cert (parm->ctrl, line, ski, &cert, + FIND_CERT_ALLOW_AMBIG|FIND_CERT_WITH_EPHEM); if (err) { log_error ("certificate not found: %s\n", gpg_strerror (err)); @@ -1014,7 +1014,8 @@ run_command_inq_cb (void *opaque, const char *line) if (!*line) return gpg_error (GPG_ERR_ASS_PARAMETER); - err = gpgsm_find_cert (parm->ctrl, line, NULL, &cert, 1); + err = gpgsm_find_cert (parm->ctrl, line, NULL, &cert, + FIND_CERT_ALLOW_AMBIG); if (err) { log_error ("certificate not found: %s\n", gpg_strerror (err)); diff --git a/sm/certlist.c b/sm/certlist.c index b5f9f7874..fdf31a198 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -508,11 +508,12 @@ gpgsm_release_certlist (certlist_t list) int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert, - int allow_ambiguous) + unsigned int flags) { int rc; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; + int allow_ambiguous = (flags & FIND_CERT_ALLOW_AMBIG); *r_cert = NULL; rc = classify_user_id (name, &desc, 0); @@ -523,6 +524,9 @@ gpgsm_find_cert (ctrl_t ctrl, rc = gpg_error (GPG_ERR_ENOMEM); else { + if ((flags & FIND_CERT_WITH_EPHEM)) + keydb_set_ephemeral (kh, 1); + nextone: rc = keydb_search (ctrl, kh, &desc, 1); if (!rc) diff --git a/sm/gpgsm.h b/sm/gpgsm.h index ced2d679f..e09dd75e9 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -404,8 +404,11 @@ int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert, int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, certlist_t *listaddr, int is_encrypt_to); void gpgsm_release_certlist (certlist_t list); + +#define FIND_CERT_ALLOW_AMBIG 1 +#define FIND_CERT_WITH_EPHEM 2 int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, - ksba_cert_t *r_cert, int allow_ambiguous); + ksba_cert_t *r_cert, unsigned int flags); /*-- keylist.c --*/ gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names, -- cgit v1.2.3 From 523b3e1773f51195b5ac1a07ed0c630d1f9d462e Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 28 Feb 2023 14:41:04 +0100 Subject: gpgconf: Print some standard envvars with -X * tools/gpgconf.c (show_configs): Add a list of envvars and print them. -- Note that for simplicity we to not distinguish between Windows and Linux here. --- tools/gpgconf.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/gpgconf.c b/tools/gpgconf.c index 4afc4e9fd..6dcdc9f3c 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -1509,6 +1509,11 @@ show_configs (estream_t outfp) static const char *names[] = { "common.conf", "gpg-agent.conf", "scdaemon.conf", "dirmngr.conf", "gpg.conf", "gpgsm.conf" }; + static const char *envvars[] = { "PATH", + "http_proxy", "HTTP_PROXY", + "https_proxy", "HTTPS_PROXY", + "LD_LIBRARY_PATH", "LD_PRELOAD", + "LD_AUDIT", "LD_ORIGIN_PATH" }; gpg_error_t err; int idx; char *fname; @@ -1539,6 +1544,11 @@ show_configs (estream_t outfp) list_dirs (outfp, NULL, 1); es_fprintf (outfp, "\n"); + for (idx=0; idx < DIM(envvars); idx++) + if ((s = getenv (envvars[idx]))) + es_fprintf (outfp, "%s=%s\n", envvars[idx], s); + es_fprintf (outfp, "\n"); + fname = make_filename (gnupg_sysconfdir (), "gpgconf.conf", NULL); if (!gnupg_access (fname, F_OK)) { -- cgit v1.2.3 From 1aaadede76ccd17a6636b26ec75954d6b709f3fa Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 1 Mar 2023 16:49:40 +0100 Subject: agent: Show "no secret key" instead of "card removed". * agent/findkey.c (agent_key_from_file): Check the error of read_key_file again. * agent/pkdecrypt.c (agent_pkdecrypt): Restore error if no card was found. Also remove useless condition. -- The first patch fixes a likely merge error. The second is about the actual return code: If we have no smardcard but simply try to decrypt with the current smartcard we should return the originla error code. GnuPG-bug-id: 5170 Fixes-commit: eda3c688fc2e85c7cd63029cb9caf06552d203b4 --- agent/findkey.c | 9 +++++++++ agent/pkdecrypt.c | 15 +++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/agent/findkey.c b/agent/findkey.c index d3a3b335c..060cb786d 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -1186,6 +1186,15 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, return gpg_error (GPG_ERR_NO_SECKEY); err = read_key_file (grip? grip : ctrl->keygrip, &s_skey, &keymeta); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_ENOENT) + err = gpg_error (GPG_ERR_NO_SECKEY); + else + log_error ("findkey: error reading key file: %s\n", + gpg_strerror (err)); + return err; + } /* For use with the protection functions we also need the key as an canonical encoded S-expression in a buffer. Create this buffer diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 82818f863..c26f21d35 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -74,8 +74,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, no_shadow_info = 1; else if (err) { - if (gpg_err_code (err) != GPG_ERR_NO_SECKEY) - log_error ("failed to read the secret key\n"); + log_error ("failed to read the secret key\n"); goto leave; } @@ -88,7 +87,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, goto leave; } - if (agent_is_tpm2_key (s_skey)) + if (s_skey && agent_is_tpm2_key (s_skey)) err = divert_tpm2_pkdecrypt (ctrl, ciphertext, shadow_info, &buf, &len, r_padding); else @@ -96,7 +95,15 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, &buf, &len, r_padding); if (err) { - log_error ("smartcard decryption failed: %s\n", gpg_strerror (err)); + /* We restore the original error (ie. no seckey) is no card + * has been found and we have no shadow key. This avoids a + * surprising "card removed" error code. */ + if ((gpg_err_code (err) == GPG_ERR_CARD_REMOVED + || gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) + && no_shadow_info) + err = gpg_error (GPG_ERR_NO_SECKEY); + else + log_error ("smartcard decryption failed: %s\n", gpg_strerror (err)); goto leave; } -- cgit v1.2.3 From 3a18378a92af63f5bccbe78efa546acb04e8a0f8 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 1 Mar 2023 17:22:20 +0100 Subject: gpg: Allow adding of Additional Decryption Subkeys. * g10/free-packet.c (copy_public_key): Factor some code out to ... (copy_public_key_basics): new. * g10/build-packet.c (build_sig_subpkt_from_sig): New arg signhints. * g10/packet.h (PUBKEY_USAGE_RENC): Fix value. (SIGNHINT_KEYSIG, SIGNHINT_SELFSIG): Moved from sign.c. (SIGNHINT_ADSK): New. (PKT_public_key): Change pubkey_usage from byte to u16. (PKT_user_id): Cosmetic fix: change help_key_usage from int to u16. * g10/getkey.c (parse_key_usage): Make public. * g10/misc.c (openpgp_pk_algo_usage): Take PUBKEY_USAGE_RENC in account. * g10/sign.c (update_keysig_packet): Set SIGNHINT_ADSK. (make_keysig_packet): Ditto. (do_sign): No time warp check in ADSK mode. * g10/sig-check.c (check_signature_metadata_validity): Ditto. * g10/keygen.c (struct opaque_data_usage_and_pk): Remove. (write_keybinding): Do not use the removed struct. (do_add_key_flags): Support PUBKEY_USAGE_RENC and others. (keygen_add_key_flags_and_expire): Rewrite and make public. * g10/keyedit.c (enum cmdids): Add cmdADDADSK. (keyedit_menu): Add command "addadsk". (menu_addadsk): New. -- This makes use of a new encryption flag: The "restricted encryption key" (2nd,0x04) does not take part in any automatic selection of encryption keys. It is only found on a subkey signature (type 0x18), one that refers to the key the flag applies to. Followup patches will add encryption support and a --quick command. GnuPG-bug-id: 6395 --- doc/DETAILS | 15 ++--- doc/gpg.texi | 9 +++ g10/build-packet.c | 12 ++-- g10/free-packet.c | 30 +++++++--- g10/getkey.c | 7 ++- g10/keydb.h | 3 + g10/keyedit.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++- g10/keygen.c | 53 ++++++++--------- g10/main.h | 1 + g10/misc.c | 8 +-- g10/packet.h | 18 ++++-- g10/sig-check.c | 3 +- g10/sign.c | 30 ++++++---- 13 files changed, 280 insertions(+), 78 deletions(-) diff --git a/doc/DETAILS b/doc/DETAILS index e01b74ac1..0d86e4750 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1724,17 +1724,10 @@ Description of some debug flags: ** gnupg.org notations - - adsk@gnupg.org :: Additional decryption subkey. This notation - gives a list of keys an implementation SHOULD - also encrypt to. The data consists of an array - of eight-octet numbers holding the Key ID of an - encryption subkey. This notation is only valid - on an encryption subkey (i.e. with first octet - of the key flags 0x04 or 0x08). Subkeys not on - the same keyblock MUST NOT be considered. For - interoperability this notation SHOULD NOT be - marked as criticial. Due to its nature it MUST - NOT be marked as human readable. + - rem@gnupg.org :: Used by Kleopatra to implement the tag feature. + These tags are used to mark keys for easier + searching and grouping. + ** Simplified revocation certificates Revocation certificates consist only of the signature packet; diff --git a/doc/gpg.texi b/doc/gpg.texi index 45cd97241..01b91abec 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -1067,6 +1067,15 @@ signing. "sensitive". If a designated revoker is marked as sensitive, it will not be exported by default (see export-options). + @item addadsk + @opindex keyedit:addadsk + Add an Additional Decryption Subkey. The user is asked to enter the + fingerprint of another encryption subkey. Note that the exact + fingerprint of another key's encryption subkey needs to be entered. + This is because commonly the primary key has no encryption + capability. Use the option @option{--with-subkey-fingerprint} with + a list command to display the subkey fingerprints. + @item passwd @opindex keyedit:passwd Change the passphrase of the secret key. 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/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 093ce61b0..3e94875b2 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -1809,12 +1809,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 +2417,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; diff --git a/g10/keydb.h b/g10/keydb.h index 28b61d4a1..edbae5c3c 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -468,6 +468,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); diff --git a/g10/keyedit.c b/g10/keyedit.c index 83c20b846..6c45fd640 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,7 @@ 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); 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 +1244,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 +1309,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 additional decryption subkeys")}, { "delsig", cmdDELSIG, 0, N_("delete signatures from the selected user IDs")}, { "expire", cmdEXPIRE, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK, @@ -2000,6 +2003,15 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, } break; + case cmdADDADSK: + if (menu_addadsk (ctrl, keyblock)) + { + redisplay = 1; + modified = 1; + merge_keys_and_selfsig (ctrl, keyblock); + } + break; + case cmdREVUID: { int n1; @@ -4643,6 +4655,159 @@ fail: } +/* + * Ask for a new additional decryption subkey and add it to the key + * block. Returns true if the keybloxk was changed and false + * otherwise. + */ +static int +menu_addadsk (ctrl_t ctrl, kbnode_t pub_keyblock) +{ + 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); + 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); + 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 && gpg_err_code (err) == GPG_ERR_UNUSABLE_PUBKEY) + log_info (_("Did you specify the fingerprint of a subkey?\n")); + 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) + log_info (_("Did you specify the fingerprint of a subkey?\n")); + 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); + 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/keygen.c b/g10/keygen.c index 2e07015c6..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; - - /* The spec says that all primary keys MUST be able to certify. */ - if(sig->sig_class!=0x18) - buf[0] |= 0x01; + byte buf[2] = { 0, 0 }; - 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; + /* The spec says that all primary keys MUST be able to certify. */ + if ( sig->sig_class != 0x18 ) + buf[0] |= 0x01; - 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) { diff --git a/g10/main.h b/g10/main.h index 62d2651be..f66f3ef0c 100644 --- a/g10/main.h +++ b/g10/main.h @@ -315,6 +315,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); diff --git a/g10/misc.c b/g10/misc.c index 05d232f8f..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; 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/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); -- cgit v1.2.3 From ef5a48dd5178f61fd0ab1801d980102b2fe4d464 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 1 Mar 2023 18:56:29 +0100 Subject: gpg: Actually encrypt to ADSKs. * g10/getkey.c (get_pubkey_fromfile): Add optional arg r_keyblock. * g10/pkclist.c (find_and_check_key): Also encrypt to RENC subkeys. -- GnuPG-bug-id: 6395 --- g10/getkey.c | 14 +++++++++++--- g10/keydb.h | 3 ++- g10/pkclist.c | 36 ++++++++++++++++++++++++++---------- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/g10/getkey.c b/g10/getkey.c index 3e94875b2..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; } diff --git a/g10/keydb.h b/g10/keydb.h index edbae5c3c..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, 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; } -- cgit v1.2.3 From 6bfb4a8d1202056efe4760c26292c6beaec7bcbb Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 3 Mar 2023 08:50:08 +0100 Subject: doc: Typo fixes and new notes in DETAILS -- --- dirmngr/ldap.c | 2 +- doc/DETAILS | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c index f9f6d5f1a..b80012d03 100644 --- a/dirmngr/ldap.c +++ b/dirmngr/ldap.c @@ -525,7 +525,7 @@ make_one_filter (const char *pattern, char **r_result) { /* We need just the BaseDN. This assumes that the Subject * is correcly stored in the DT. This is however not always - * the case and the actual DN is different ffrom the + * the case and the actual DN is different from the * subject. In this case we won't find anything. */ if (extfilt_need_escape (pattern) && !(pattern = pattern_buffer = extfilt_escape (pattern))) diff --git a/doc/DETAILS b/doc/DETAILS index 0d86e4750..10307bae0 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1706,15 +1706,21 @@ Description of some debug flags: - RFC-5915 :: ECC Private Key Structure - RFC-5958 :: Asymmetric Key Packages - RFC-6337 :: ECC in OpenPGP + - RFC-7748 :: Elliptic Curves for Security (X25519 and X448) + - RFC-8410 :: Algorithm Identifiers for Ed25519, Ed448, X25519, and X448 - RFC-7292 :: PKCS #12: Personal Information Exchange Syntax v1.1 - RFC-8351 :: The PKCS #8 EncryptedPrivateKeyInfo Media Type - RFC-8550 :: S/MIME Version 4.0 Certificate Handling - RFC-8551 :: S/MIME Version 4.0 Message Specification - RFC-2634 :: Enhanced Security Services for S/MIME - RFC-5035 :: Enhanced Security Services (ESS) Update + - RFC-7253 :: The OCB Authenticated-Encryption Algorithm - draft-koch-openpgp-2015-rfc4880bis :: Updates to RFC-4880 + - T6390 :: Notes on use of X25519 in GnuPG (https://dev.gnupg.org/T6390) + + ** v3 fingerprints For packet version 3 we calculate the keyids this way: - RSA :: Low 64 bits of n -- cgit v1.2.3 From 4e391d95e0711646af649167c7de018cb6367097 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Wed, 8 Mar 2023 11:33:18 +0900 Subject: scd: Fix checking memory allocation. * scd/app-openpgp.c (read_public_key): Fix the memory. -- Signed-off-by: NIIBE Yutaka --- scd/app-openpgp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index e445b2409..f5d0b5111 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1863,7 +1863,7 @@ read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno, len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0); keybuf = xtrymalloc (len); - if (!data) + if (!keybuf) { err = gpg_error_from_syserror (); gcry_sexp_release (s_pkey); -- cgit v1.2.3 From 2a13f7f9dc75265ece649e30fecd3dc694b1240e Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 8 Mar 2023 10:57:25 +0100 Subject: gpgsm: Strip trailing zeroes from detached signatures. * common/ksba-io-support.c: Include tlv.h (struct reader_cb_parm_s): Add new fields. (starts_with_sequence): New. (simple_reader_cb): Handle stripping. * common/ksba-io-support.h (GNUPG_KSBA_IO_STRIP): New. (gnupg_ksba_create_reader): Handle the new flag. * sm/verify.c (gpgsm_verify): Use the new flag for detached signatures. -- Note that this works only if --assume-binary is given. The use case for the feature is PDF signature checking where the PDF specs require that the detached signature is padded with zeroes. --- common/ksba-io-support.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++- common/ksba-io-support.h | 1 + common/tlv.c | 3 +- doc/gpgsm.texi | 6 ++- sm/verify.c | 10 ++++- 5 files changed, 120 insertions(+), 7 deletions(-) diff --git a/common/ksba-io-support.c b/common/ksba-io-support.c index 2832a4f3d..a279b67ad 100644 --- a/common/ksba-io-support.c +++ b/common/ksba-io-support.c @@ -40,6 +40,7 @@ #include "util.h" #include "i18n.h" +#include "tlv.h" #include "ksba-io-support.h" @@ -65,6 +66,12 @@ struct reader_cb_parm_s int autodetect; /* Try to detect the input encoding. */ int assume_pem; /* Assume input encoding is PEM. */ int assume_base64; /* Assume input is base64 encoded. */ + int strip_zeroes; /* Expect a SEQUENCE followed by zero padding. */ + /* 1 = check state; 2 = reading; 3 = checking */ + /* for zeroes. */ + int use_maxread; /* If true read not more than MAXREAD. */ + unsigned int maxread; /* # of bytes left to read. */ + off_t nzeroes; /* Number of padding zeroes red. */ int identified; int is_pem; @@ -390,6 +397,55 @@ base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) } +/* Read up to 10 bytes to test whether the data consist of a sequence; + * if that is true, set the limited flag and record the length of the + * entire sequence in PARM. Unget everything then. Return true if we + * have a sequence with a fixed length. */ +static int +starts_with_sequence (struct reader_cb_parm_s *parm) +{ + gpg_error_t err; + unsigned char peekbuf[10]; + int npeeked, c; + int found = 0; + const unsigned char *p; + size_t n, objlen, hdrlen; + int class, tag, constructed, ndef; + + for (npeeked=0; npeeked < sizeof peekbuf; npeeked++) + { + c = es_getc (parm->fp); + if (c == EOF) + goto leave; + peekbuf[npeeked] = c; + } + /* Enough to check for a sequence. */ + + p = peekbuf; + n = npeeked; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + { + log_debug ("%s: error parsing data: %s\n", __func__, gpg_strerror (err)); + goto leave; + } + + if (class == CLASS_UNIVERSAL && constructed && tag == TAG_SEQUENCE && !ndef) + { + /* We need to add 1 due to the way we implement the limit. */ + parm->maxread = objlen + hdrlen + 1; + if (!(parm->maxread < objlen + hdrlen) && parm->maxread) + parm->use_maxread = 1; + found = 1; + } + + leave: + while (npeeked) + es_ungetc (peekbuf[--npeeked], parm->fp); + return found; +} + static int simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) @@ -402,9 +458,55 @@ simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) if (!buffer) return -1; /* not supported */ + restart: + if (parm->strip_zeroes) + { + if (parm->strip_zeroes == 1) + { + if (starts_with_sequence (parm)) + parm->strip_zeroes = 2; /* Found fixed length sequence. */ + else + parm->strip_zeroes = 0; /* Disable zero padding check. */ + } + else if (parm->strip_zeroes == 3) + { + /* Limit reached - check that only zeroes follow. */ + while (!(c = es_getc (parm->fp))) + parm->nzeroes++; + if (c == EOF) + { /* only zeroes found. Reset zero padding engine and + * return EOF. */ + parm->strip_zeroes = 0; + parm->eof_seen = 1; + return -1; + } + /* Not only zeroes. Reset engine and continue. */ + parm->strip_zeroes = 0; + } + } + for (n=0; n < count; n++) { - c = es_getc (parm->fp); + if (parm->use_maxread && !--parm->maxread) + { + parm->use_maxread = 0; + if (parm->strip_zeroes) + { + parm->strip_zeroes = 3; + parm->nzeroes = 0; + if (n) + goto leave; /* Return what we already got. */ + goto restart; /* Immediately check for trailing zeroes. */ + } + } + + if (parm->nzeroes) + { + parm->nzeroes--; + c = 0; + } + else + c = es_getc (parm->fp); if (c == EOF) { parm->eof_seen = 1; @@ -417,6 +519,7 @@ simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) *(byte *)buffer++ = c; } + leave: *nread = n; return 0; } @@ -575,6 +678,7 @@ base64_finish_write (struct writer_cb_parm_s *parm) * GNUPG_KSBA_IO_MULTIPEM - The reader expects that the caller uses * ksba_reader_clear after EOF until no more * objects were found. + * GNUPG_KSBA_IO_STRIP - Strip zero padding from some CMS objects. * * Note that the PEM flag has a higher priority than the BASE64 flag * which in turn has a gight priority than the AUTODETECT flag. @@ -592,6 +696,7 @@ gnupg_ksba_create_reader (gnupg_ksba_io_t *ctx, if (!*ctx) return out_of_core (); (*ctx)->u.rparm.allow_multi_pem = !!(flags & GNUPG_KSBA_IO_MULTIPEM); + (*ctx)->u.rparm.strip_zeroes = !!(flags & GNUPG_KSBA_IO_STRIP); rc = ksba_reader_new (&r); if (rc) diff --git a/common/ksba-io-support.h b/common/ksba-io-support.h index e33e0ed74..02e541b16 100644 --- a/common/ksba-io-support.h +++ b/common/ksba-io-support.h @@ -36,6 +36,7 @@ #define GNUPG_KSBA_IO_BASE64 2 /* Plain Base64 format. */ #define GNUPG_KSBA_IO_AUTODETECT 4 /* Try to autodetect the format. */ #define GNUPG_KSBA_IO_MULTIPEM 8 /* Allow more than one PEM chunk. */ +#define GNUPG_KSBA_IO_STRIP 16 /* Strip off zero padding. */ /* Context object. */ diff --git a/common/tlv.c b/common/tlv.c index 9618d04cb..4cc1dc7cf 100644 --- a/common/tlv.c +++ b/common/tlv.c @@ -156,8 +156,7 @@ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, int *r_class, int *r_tag, int *r_constructed, int *r_ndef, - size_t *r_length, size_t *r_nhdr) -{ + size_t *r_length, size_t *r_nhdr){ int c; unsigned long tag; const unsigned char *buf = *buffer; diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index a328ea5f0..42090a93f 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -492,8 +492,10 @@ This usually means that Dirmngr is employed to search for the certificate. Note that this option makes a "web bug" like behavior possible. LDAP server operators can see which keys you request, so by sending you a message signed by a brand new key (which you naturally -will not have on your local keybox), the operator can tell both your IP -address and the time when you verified the signature. +will not have on your local keybox), the operator can tell both your +IP address and the time when you verified the signature. Note that if +CRL checking is not disabled issuer certificates are retrieved in any +case using the caIssuers authorityInfoAccess method. @anchor{gpgsm-option --validation-model} diff --git a/sm/verify.c b/sm/verify.c index 9f1216f83..a07d1c9c7 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -105,12 +105,17 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) int signer; const char *algoid; int algo; - int is_detached; + int is_detached, maybe_detached; estream_t in_fp = NULL; char *p; audit_set_type (ctrl->audit, AUDIT_TYPE_VERIFY); + /* Although we detect detached signatures during the parsing phase, + * we need to know it earlier and thus accept the caller idea of + * what to verify. */ + maybe_detached = (data_fd != -1); + kh = keydb_new (ctrl); if (!kh) { @@ -131,7 +136,8 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) - | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)), + | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0) + | (maybe_detached? GNUPG_KSBA_IO_STRIP : 0)), in_fp, &reader); if (rc) { -- cgit v1.2.3 From 2d088176b4bdfc38f2a5bb853e14207a85a15f73 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 8 Mar 2023 14:36:11 +0100 Subject: dirmngr: Minor code cleanup in the CRL cache. * dirmngr/crlcache.c (INVCRL_TOO_OLD): New. (INVCRL_UNKNOWN_EXTN, INVCRL_GENERAL): New. (open_dir, crl_cache_insert): Use the new constants. (list_one_crl_entry): Make diagnostics robust for new INVCRL codes. --- dirmngr/crlcache.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c index befc6b94b..5d793494e 100644 --- a/dirmngr/crlcache.c +++ b/dirmngr/crlcache.c @@ -125,6 +125,13 @@ # define O_BINARY 0 #endif + +/* Reason flags for an invalid CRL. */ +#define INVCRL_TOO_OLD 1 +#define INVCRL_UNKNOWN_EXTN 2 +#define INVCRL_GENERAL 127 + + static const char oidstr_crlNumber[] = "2.5.29.20"; /* static const char oidstr_issuingDistributionPoint[] = "2.5.29.28"; */ static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35"; @@ -569,8 +576,8 @@ open_dir (crl_cache_t *r_cache) if (*line == 'i') { entry->invalid = atoi (line+1); - if (entry->invalid < 1) - entry->invalid = 1; + if (!entry->invalid) + entry->invalid = INVCRL_GENERAL; } else if (*line == 'u') entry->user_trust_req = 1; @@ -2338,7 +2345,7 @@ crl_cache_insert (ctrl_t ctrl, const char *url, ksba_reader_t reader) nextupdate); if (!err2) err2 = gpg_error (GPG_ERR_CRL_TOO_OLD); - invalidate_crl |= 1; + invalidate_crl |= INVCRL_TOO_OLD; } } @@ -2353,7 +2360,7 @@ crl_cache_insert (ctrl_t ctrl, const char *url, ksba_reader_t reader) log_error (_("unknown critical CRL extension %s\n"), oid); if (!err2) err2 = gpg_error (GPG_ERR_INV_CRL); - invalidate_crl |= 2; + invalidate_crl |= INVCRL_UNKNOWN_EXTN; } if (gpg_err_code (err) == GPG_ERR_EOF || gpg_err_code (err) == GPG_ERR_NO_DATA ) @@ -2492,6 +2499,7 @@ list_one_crl_entry (crl_cache_t cache, crl_cache_entry_t e, estream_t fp) int rc; int warn = 0; const unsigned char *s; + unsigned int invalid; es_fputs ("--------------------------------------------------------\n", fp ); es_fprintf (fp, _("Begin CRL dump (retrieved via %s)\n"), e->url ); @@ -2516,13 +2524,20 @@ list_one_crl_entry (crl_cache_t cache, crl_cache_entry_t e, estream_t fp) !e->user_trust_req? "[system]" : e->check_trust_anchor? e->check_trust_anchor:"[missing]"); - if ((e->invalid & 1)) - es_fprintf (fp, _(" ERROR: The CRL will not be used " - "because it was still too old after an update!\n")); - if ((e->invalid & 2)) - es_fprintf (fp, _(" ERROR: The CRL will not be used " + invalid = e->invalid; + if ((invalid & INVCRL_TOO_OLD)) + { + invalid &= ~INVCRL_TOO_OLD; + es_fprintf (fp, _(" ERROR: The CRL will not be used " + "because it was still too old after an update!\n")); + } + if ((invalid & INVCRL_UNKNOWN_EXTN)) + { + invalid &= ~INVCRL_UNKNOWN_EXTN; + es_fprintf (fp, _(" ERROR: The CRL will not be used " "due to an unknown critical extension!\n")); - if ((e->invalid & ~3)) + } + if (invalid) /* INVCRL_GENERAL or some other bits are set. */ es_fprintf (fp, _(" ERROR: The CRL will not be used\n")); cdb = lock_db_file (cache, e); -- cgit v1.2.3 From d2d1db88608349bbe00da3adabddc167f6852f9e Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 8 Mar 2023 15:10:52 +0100 Subject: gpg,gpgsm: New option --log-time * g10/gpg.c (oLogTime): New. (opts): Add "log-time". (opt_log_time): New var. (main): Implement. * sm/gpgsm.c (oLogTime): New. (opts): Add "log-time". (opt_log_time): New var. (main): Implement. --- doc/gpg.texi | 4 ++++ doc/gpgsm.texi | 4 ++++ g10/gpg.c | 9 +++++++++ sm/gpgsm.c | 10 ++++++++++ 4 files changed, 27 insertions(+) diff --git a/doc/gpg.texi b/doc/gpg.texi index 01b91abec..a6ab4d57d 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -3199,6 +3199,10 @@ Write log output to file descriptor @var{n} and not to STDERR. Same as @option{--logger-fd}, except the logger data is written to file @var{file}. Use @file{socket://} to log to s socket. +@item --log-time +@opindex log-time +Prefix all log output with a timestamp even if no log file is used. + @item --attribute-fd @var{n} @opindex attribute-fd Write attribute subpackets to the file descriptor @var{n}. This is most diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index 42090a93f..a117009bd 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -408,6 +408,10 @@ Do not print a warning when the so called "secure memory" cannot be used. When running in server mode, append all logging output to @var{file}. Use @file{socket://} to log to socket. +@item --log-time +@opindex log-time +Prefix all log output with a timestamp even if no log file is used. + @end table diff --git a/g10/gpg.c b/g10/gpg.c index 9ec956ac3..632fbb90f 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -337,6 +337,7 @@ enum cmd_and_opt_values oEncryptToDefaultKey, oLoggerFD, oLoggerFile, + oLogTime, oUtf8Strings, oNoUtf8Strings, oDisableCipherAlgo, @@ -600,6 +601,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", "@"), @@ -1041,6 +1043,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 { @@ -2864,6 +2867,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; @@ -3829,6 +3835,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 ()); diff --git a/sm/gpgsm.c b/sm/gpgsm.c index f8b3856c2..8ca398360 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -114,6 +114,7 @@ enum cmd_and_opt_values { oNoLogFile, oAuditLog, oHtmlAuditLog, + oLogTime, oEnableSpecialFilenames, @@ -288,6 +289,7 @@ static gpgrt_opt_t opts[] = { N_("|FILE|write server mode logs to FILE")), ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"), ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"), + ARGPARSE_s_n (oLogTime, "log-time", "@"), ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"), @@ -499,6 +501,9 @@ static int maybe_setuid = 1; static const char *debug_level; static unsigned int debug_value; +/* Helper for --log-time; */ +static int opt_log_time; + /* Default value for include-certs. We need an extra macro for gpgconf-list because the variable will be changed by the command line option. @@ -1247,6 +1252,7 @@ main ( int argc, char **argv) case oLogFile: logfile = pargs.r.ret_str; break; case oNoLogFile: logfile = NULL; break; + case oLogTime: opt_log_time = 1; break; case oAuditLog: auditlog = pargs.r.ret_str; break; case oHtmlAuditLog: htmlauditlog = pargs.r.ret_str; break; @@ -1579,6 +1585,10 @@ main ( int argc, char **argv) log_set_file (logfile); log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | 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 (gnupg_faked_time_p ()) { -- cgit v1.2.3 From 65288fc52f0c60e99f32d6d1981ade08a9ec860b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 8 Mar 2023 16:09:56 +0100 Subject: keyboxd: Allow import of v0 certificates. * kbx/backend-support.c (be_is_x509_blob): Loose detection. -- Here is a sample v0 cert: -----BEGIN CERTIFICATE----- MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQsw CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0Ns YXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH MjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y aXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAe Fw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEX MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGlj IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMx KGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s eTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0B AQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjM HiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjw DqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cC AwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9ji nb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnIn jBJ7xUS0rg== -----END CERTIFICATE----- --- kbx/backend-support.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kbx/backend-support.c b/kbx/backend-support.c index 7ab63985c..4d3738064 100644 --- a/kbx/backend-support.c +++ b/kbx/backend-support.c @@ -207,6 +207,7 @@ be_is_x509_blob (const unsigned char *blob, size_t bloblen) * SEQUENCE SEQUENCE [0] INTEGER INTEGER * (tbs) (version) (s/n) * + * Note that v0 certificates don't have an explict version number. */ p = blob; @@ -226,7 +227,11 @@ be_is_x509_blob (const unsigned char *blob, size_t bloblen) if (parse_ber_header (&p, &n, &class, &tag, &cons, &ndef, &objlen, &hdrlen)) return 0; /* Not a proper BER object. */ if (!(class == CLASS_CONTEXT && tag == 0 && cons)) - return 0; /* No context tag. */ + { + if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER && !cons) + return 1; /* Might be a X.509 v0 cert with implict version. */ + return 0; /* No context tag. */ + } if (parse_ber_header (&p, &n, &class, &tag, &cons, &ndef, &objlen, &hdrlen)) return 0; /* Not a proper BER object. */ -- cgit v1.2.3 From b52a0e244ae18aec4b9c93f90432a551fac95a40 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 9 Mar 2023 18:28:39 +0100 Subject: dirmngr: Distinguish between "no crl" and "crl not trusted". * dirmngr/crlcache.h (CRL_CACHE_NOTTRUSTED): New. * dirmngr/crlcache.c (cache_isvalid): Set this status. (crl_cache_cert_isvalid): Map it to GPG_ERR_NOT_TRUSTED. (crl_cache_reload_crl): Move diagnostic to ... * dirmngr/crlfetch.c (crl_fetch): here. * dirmngr/server.c (cmd_isvalid): Map it to GPG_ERR_NOT_TRUSTED. * dirmngr/validate.c (check_revocations): Handle new status. Improve diagnostics. * common/status.c (get_inv_recpsgnr_code): Map INV_CRL_OBJ. * common/audit.c (proc_type_verify): Ditto. -- This avoids repeated loading of CRLs in case of untrusted root certificates. --- common/audit.c | 1 + common/status.c | 3 ++- dirmngr/crlcache.c | 13 +++++++------ dirmngr/crlcache.h | 1 + dirmngr/crlfetch.c | 3 +++ dirmngr/server.c | 7 +++++-- dirmngr/validate.c | 16 +++++++++++++--- sm/call-dirmngr.c | 1 + 8 files changed, 33 insertions(+), 12 deletions(-) diff --git a/common/audit.c b/common/audit.c index 803523c94..ae0d45216 100644 --- a/common/audit.c +++ b/common/audit.c @@ -1109,6 +1109,7 @@ proc_type_verify (audit_ctx_t ctx) case GPG_ERR_CERT_REVOKED: ok = "bad"; break; case GPG_ERR_NOT_ENABLED: ok = "disabled"; break; case GPG_ERR_NO_CRL_KNOWN: + case GPG_ERR_INV_CRL_OBJ: ok = _("no CRL found for certificate"); break; case GPG_ERR_CRL_TOO_OLD: diff --git a/common/status.c b/common/status.c index b752c12c6..b7dc1de39 100644 --- a/common/status.c +++ b/common/status.c @@ -158,7 +158,8 @@ get_inv_recpsgnr_code (gpg_error_t err) case GPG_ERR_WRONG_KEY_USAGE: errstr = "3"; break; case GPG_ERR_CERT_REVOKED: errstr = "4"; break; case GPG_ERR_CERT_EXPIRED: errstr = "5"; break; - case GPG_ERR_NO_CRL_KNOWN: errstr = "6"; break; + case GPG_ERR_NO_CRL_KNOWN: + case GPG_ERR_INV_CRL_OBJ: errstr = "6"; break; case GPG_ERR_CRL_TOO_OLD: errstr = "7"; break; case GPG_ERR_NO_POLICY_MATCH: errstr = "8"; break; diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c index 5d793494e..9f0b910f3 100644 --- a/dirmngr/crlcache.c +++ b/dirmngr/crlcache.c @@ -164,7 +164,7 @@ struct crl_cache_entry_s unsigned int cdb_use_count; /* Current use count. */ unsigned int cdb_lru_count; /* Used for LRU purposes. */ int dbfile_checked; /* Set to true if the dbfile_hash value has - been checked one. */ + been checked once. */ }; @@ -1402,7 +1402,7 @@ cache_isvalid (ctrl_t ctrl, const char *issuer_hash, { if (opt.verbose) log_info ("no system trust and client does not trust either\n"); - retval = CRL_CACHE_CANTUSE; + retval = CRL_CACHE_NOTTRUSTED; } else { @@ -1522,8 +1522,11 @@ crl_cache_cert_isvalid (ctrl_t ctrl, ksba_cert_t cert, case CRL_CACHE_DONTKNOW: err = gpg_error (GPG_ERR_NO_CRL_KNOWN); break; + case CRL_CACHE_NOTTRUSTED: + err = gpg_error (GPG_ERR_NOT_TRUSTED); + break; case CRL_CACHE_CANTUSE: - err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + err = gpg_error (GPG_ERR_INV_CRL_OBJ); break; default: log_fatal ("cache_isvalid returned invalid status code %d\n", result); @@ -2104,7 +2107,7 @@ crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl, } } while (stopreason != KSBA_SR_READY); - assert (!err); + log_assert (!err); failure: @@ -2729,8 +2732,6 @@ crl_cache_reload_crl (ctrl_t ctrl, ksba_cert_t cert) any_dist_point = 1; - if (opt.verbose) - log_info ("fetching CRL from '%s'\n", distpoint_uri); crl_close_reader (reader); err = crl_fetch (ctrl, distpoint_uri, &reader); if (err) diff --git a/dirmngr/crlcache.h b/dirmngr/crlcache.h index 0e60def8f..7db8f01cc 100644 --- a/dirmngr/crlcache.h +++ b/dirmngr/crlcache.h @@ -27,6 +27,7 @@ typedef enum CRL_CACHE_VALID = 0, CRL_CACHE_INVALID, CRL_CACHE_DONTKNOW, + CRL_CACHE_NOTTRUSTED, CRL_CACHE_CANTUSE } crl_cache_result_t; diff --git a/dirmngr/crlfetch.c b/dirmngr/crlfetch.c index a591a2b5a..5b6b648e2 100644 --- a/dirmngr/crlfetch.c +++ b/dirmngr/crlfetch.c @@ -175,6 +175,9 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) if (!url) return gpg_error (GPG_ERR_INV_ARG); + if (opt.verbose) + log_info ("fetching CRL from '%s'\n", url); + err = http_parse_uri (&uri, url, 0); http_release_parsed_uri (uri); if (!err) /* Yes, our HTTP code groks that. */ diff --git a/dirmngr/server.c b/dirmngr/server.c index fba2233d4..da7e707f9 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -1360,8 +1360,11 @@ cmd_isvalid (assuan_context_t ctx, char *line) goto again; } break; + case CRL_CACHE_NOTTRUSTED: + err = gpg_error (GPG_ERR_NOT_TRUSTED); + break; case CRL_CACHE_CANTUSE: - err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + err = gpg_error (GPG_ERR_INV_CRL_OBJ); break; default: log_fatal ("crl_cache_isvalid returned invalid code\n"); @@ -1469,7 +1472,7 @@ cmd_checkcrl (assuan_context_t ctx, char *line) goto leave; } - assert (cert); + log_assert (cert); err = crl_cache_cert_isvalid (ctrl, cert, ctrl->force_crl_refresh); if (gpg_err_code (err) == GPG_ERR_NO_CRL_KNOWN) diff --git a/dirmngr/validate.c b/dirmngr/validate.c index 399cca3a4..02db3c270 100644 --- a/dirmngr/validate.c +++ b/dirmngr/validate.c @@ -255,6 +255,7 @@ check_revocations (ctrl_t ctrl, chain_item_t chain) int any_revoked = 0; int any_no_crl = 0; int any_crl_too_old = 0; + int any_not_trusted = 0; chain_item_t ci; log_assert (ctrl->check_revocations_nest_level >= 0); @@ -266,7 +267,8 @@ check_revocations (ctrl_t ctrl, chain_item_t chain) return gpg_error(GPG_ERR_BAD_CERT_CHAIN); } ctrl->check_revocations_nest_level++; - + if (opt.verbose) + log_info ("[%d] start checking CRLs\n", ctrl->check_revocations_nest_level); for (ci=chain; ci; ci = ci->next) { @@ -293,17 +295,19 @@ check_revocations (ctrl_t ctrl, chain_item_t chain) if (!err) err = crl_cache_cert_isvalid (ctrl, ci->cert, 0); } + if (opt.verbose) + log_info ("[%d] result of checking this CRL: %s\n", + ctrl->check_revocations_nest_level, gpg_strerror (err)); switch (gpg_err_code (err)) { case 0: err = 0; break; case GPG_ERR_CERT_REVOKED: any_revoked = 1; err = 0; break; case GPG_ERR_NO_CRL_KNOWN: any_no_crl = 1; err = 0; break; + case GPG_ERR_NOT_TRUSTED: any_not_trusted = 1; err = 0; break; case GPG_ERR_CRL_TOO_OLD: any_crl_too_old = 1; err = 0; break; default: break; } } - ctrl->check_revocations_nest_level--; - if (err) ; @@ -311,10 +315,16 @@ check_revocations (ctrl_t ctrl, chain_item_t chain) err = gpg_error (GPG_ERR_CERT_REVOKED); else if (any_no_crl) err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + else if (any_not_trusted) + err = gpg_error (GPG_ERR_NOT_TRUSTED); else if (any_crl_too_old) err = gpg_error (GPG_ERR_CRL_TOO_OLD); else err = 0; + if (opt.verbose) + log_info ("[%d] result of checking all CRLs: %s\n", + ctrl->check_revocations_nest_level, gpg_strerror (err)); + ctrl->check_revocations_nest_level--; return err; } diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index 8e2761b1e..86beeedc1 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -521,6 +521,7 @@ isvalid_status_cb (void *opaque, const char *line) GPG_ERR_CERTIFICATE_REVOKED GPG_ERR_NO_CRL_KNOWN + GPG_ERR_INV_CRL_OBJ GPG_ERR_CRL_TOO_OLD Values for USE_OCSP: -- cgit v1.2.3 From be77a7ab8a8bdbde6f40a233a1a60f7924a21d4a Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Sun, 5 Mar 2023 17:14:07 -0800 Subject: agent: Try to SETREPEATOK if the pinentry supports it. * agent/call-pinentry.c (agent_get_passphrase): Do SETREPEATOK. (agent_askpin): Ditto. Signed-off-by: Ben Kibbey --- agent/call-pinentry.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c index c6c52be74..656d5f623 100644 --- a/agent/call-pinentry.c +++ b/agent/call-pinentry.c @@ -1543,6 +1543,17 @@ agent_askpin (ctrl_t ctrl, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) pininfo->with_repeat = 0; /* Pinentry does not support it. */ + + if (pininfo->with_repeat) + { + snprintf (line, DIM(line), "SETREPEATOK %s", + L_("Passphrases match.")); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + rc = 0; /* Pinentry does not support it. */ + } + } pininfo->repeat_okay = 0; pininfo->status = 0; @@ -1802,6 +1813,16 @@ agent_get_passphrase (ctrl_t ctrl, if (rc) pininfo->with_repeat = 0; /* Pinentry does not support it. */ + if (pininfo->with_repeat) + { + snprintf (line, DIM(line), "SETREPEATOK %s", + L_("Passphrases match.")); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + rc = 0; /* Pinentry does not support it. */ + } + (void)setup_genpin (ctrl); rc = setup_enforced_constraints (ctrl); -- cgit v1.2.3 From 56ca164684b69bcb20eb98a1adc70531c8991576 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 10 Mar 2023 10:52:43 +0100 Subject: dirmngr: Add command "GETINFO stats". * dirmngr/server.c (cmd_getinfo): New sub-command "stats". (dirmngr_status_helpf): Allow for a CTRL of NULL. * dirmngr/certcache.c (cert_cache_print_stats): Add arg ctrl and use dirmngr_status_helpf. Adjust all callers. * dirmngr/domaininfo.c (domaininfo_print_stats): Ditto. * sm/certchain.c (ask_marktrusted): Flush stdout before printing the fingerprint. --- dirmngr/certcache.c | 27 +++++++++++++++------------ dirmngr/certcache.h | 2 +- dirmngr/dirmngr.c | 5 +++-- dirmngr/dirmngr.h | 2 +- dirmngr/domaininfo.c | 13 +++++++------ dirmngr/server.c | 36 ++++++++++++++++++++++++++---------- dirmngr/workqueue.c | 6 +++--- sm/certchain.c | 3 +++ 8 files changed, 59 insertions(+), 35 deletions(-) diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c index 1e73d6f85..6b194f31c 100644 --- a/dirmngr/certcache.c +++ b/dirmngr/certcache.c @@ -768,7 +768,7 @@ cert_cache_init (strlist_t hkp_cacerts) initialization_done = 1; release_cache_lock (); - cert_cache_print_stats (); + cert_cache_print_stats (NULL); } /* Deinitialize the certificate cache. With FULL set to true even the @@ -811,7 +811,7 @@ cert_cache_deinit (int full) /* Print some statistics to the log file. */ void -cert_cache_print_stats (void) +cert_cache_print_stats (ctrl_t ctrl) { cert_item_t ci; int idx; @@ -848,16 +848,19 @@ cert_cache_print_stats (void) release_cache_lock (); - log_info (_("permanently loaded certificates: %u\n"), - n_permanent); - log_info (_(" runtime cached certificates: %u\n"), - n_nonperm); - log_info (_(" trusted certificates: %u (%u,%u,%u,%u)\n"), - n_trusted, - n_trustclass_system, - n_trustclass_config, - n_trustclass_hkp, - n_trustclass_hkpspool); + dirmngr_status_helpf (ctrl, + _("permanently loaded certificates: %u\n"), + n_permanent); + dirmngr_status_helpf (ctrl, + _(" runtime cached certificates: %u\n"), + n_nonperm); + dirmngr_status_helpf (ctrl, + _(" trusted certificates: %u (%u,%u,%u,%u)\n"), + n_trusted, + n_trustclass_system, + n_trustclass_config, + n_trustclass_hkp, + n_trustclass_hkpspool); } diff --git a/dirmngr/certcache.h b/dirmngr/certcache.h index 8d645836d..3a773636f 100644 --- a/dirmngr/certcache.h +++ b/dirmngr/certcache.h @@ -37,7 +37,7 @@ void cert_cache_init (strlist_t hkp_cacerts); void cert_cache_deinit (int full); /* Print some statistics to the log file. */ -void cert_cache_print_stats (void); +void cert_cache_print_stats (ctrl_t ctrl); /* Return true if any cert of a class in MASK is permanently loaded. */ int cert_cache_any_in_class (unsigned int mask); diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index 650770dab..66b7878e5 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -2031,8 +2031,9 @@ handle_signal (int signo) break; case SIGUSR1: - cert_cache_print_stats (); - domaininfo_print_stats (); + /* See also cmd_getinfo:"stats". */ + cert_cache_print_stats (NULL); + domaininfo_print_stats (NULL); break; case SIGUSR2: diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index f65bcd119..bd4660422 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -266,7 +266,7 @@ gpg_error_t dirmngr_load_swdb (ctrl_t ctrl, int force); /*-- domaininfo.c --*/ -void domaininfo_print_stats (void); +void domaininfo_print_stats (ctrl_t ctrl); int domaininfo_is_wkd_not_supported (const char *domain); void domaininfo_set_no_name (const char *domain); void domaininfo_set_wkd_supported (const char *domain); diff --git a/dirmngr/domaininfo.c b/dirmngr/domaininfo.c index b41aef366..b6043be53 100644 --- a/dirmngr/domaininfo.c +++ b/dirmngr/domaininfo.c @@ -81,7 +81,7 @@ hash_domain (const char *domain) void -domaininfo_print_stats (void) +domaininfo_print_stats (ctrl_t ctrl) { int bidx; domaininfo_t di; @@ -112,11 +112,12 @@ domaininfo_print_stats (void) if (minlen == -1 || len < minlen) minlen = len; } - log_info ("domaininfo: items=%d chainlen=%d..%d nn=%d nf=%d ns=%d s=%d\n", - count, - minlen > 0? minlen : 0, - maxlen, - no_name, wkd_not_found, wkd_not_supported, wkd_supported); + dirmngr_status_helpf + (ctrl, "domaininfo: items=%d chainlen=%d..%d nn=%d nf=%d ns=%d s=%d\n", + count, + minlen > 0? minlen : 0, + maxlen, + no_name, wkd_not_found, wkd_not_supported, wkd_supported); } diff --git a/dirmngr/server.c b/dirmngr/server.c index da7e707f9..cd71592a4 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -2788,13 +2788,14 @@ static const char hlp_getinfo[] = "Multi purpose command to return certain information. \n" "Supported values of WHAT are:\n" "\n" - "version - Return the version of the program.\n" - "pid - Return the process id of the server.\n" + "version - Return the version of the program\n" + "pid - Return the process id of the server\n" "tor - Return OK if running in Tor mode\n" "dnsinfo - Return info about the DNS resolver\n" - "socket_name - Return the name of the socket.\n" - "session_id - Return the current session_id.\n" + "socket_name - Return the name of the socket\n" + "session_id - Return the current session_id\n" "workqueue - Inspect the work queue\n" + "stats - Print stats\n" "getenv NAME - Return value of envvar NAME\n"; static gpg_error_t cmd_getinfo (assuan_context_t ctx, char *line) @@ -2863,6 +2864,12 @@ cmd_getinfo (assuan_context_t ctx, char *line) workqueue_dump_queue (ctrl); err = 0; } + else if (!strcmp (line, "stats")) + { + cert_cache_print_stats (ctrl); + domaininfo_print_stats (ctrl); + err = 0; + } else if (!strncmp (line, "getenv", 6) && (line[6] == ' ' || line[6] == '\t' || !line[6])) { @@ -3221,7 +3228,8 @@ dirmngr_status_help (ctrl_t ctrl, const char *text) /* Print a help status line using a printf like format. The function - * splits text at LFs. */ + * splits text at LFs. With CTRL beeing NULL, the function behaves + * like log_info. */ gpg_error_t dirmngr_status_helpf (ctrl_t ctrl, const char *format, ...) { @@ -3230,12 +3238,20 @@ dirmngr_status_helpf (ctrl_t ctrl, const char *format, ...) char *buf; va_start (arg_ptr, format); - buf = es_vbsprintf (format, arg_ptr); - err = buf? 0 : gpg_error_from_syserror (); + if (ctrl) + { + buf = es_vbsprintf (format, arg_ptr); + err = buf? 0 : gpg_error_from_syserror (); + if (!err) + err = dirmngr_status_help (ctrl, buf); + es_free (buf); + } + else + { + log_logv (GPGRT_LOGLVL_INFO, format, arg_ptr); + err = 0; + } va_end (arg_ptr); - if (!err) - err = dirmngr_status_help (ctrl, buf); - es_free (buf); return err; } diff --git a/dirmngr/workqueue.c b/dirmngr/workqueue.c index 2974f5d08..dcac48024 100644 --- a/dirmngr/workqueue.c +++ b/dirmngr/workqueue.c @@ -59,7 +59,7 @@ workqueue_dump_queue (ctrl_t ctrl) wqitem_t item; unsigned int count; - /* Temporarily detach the entiere workqueue so that other threads don't + /* Temporarily detach the entire workqueue so that other threads don't * get into our way. */ saved_workqueue = workqueue; workqueue = NULL; @@ -74,8 +74,8 @@ workqueue_dump_queue (ctrl_t ctrl) item->func? item->func (NULL, NULL): "nop", item->args, strlen (item->args) > 100? "[...]":""); - /* Restore then workqueue. Actually we append the saved queue do a - * possibly updated workqueue. */ + /* Restore the workqueue. Actually we append the saved queue to + * handle a possibly updated workqueue. */ if (!(item=workqueue)) workqueue = saved_workqueue; else diff --git a/sm/certchain.c b/sm/certchain.c index cbb6e1127..7b782190b 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -122,6 +122,7 @@ do_list (int is_error, int listmode, estream_t fp, const char *format, ...) } else { + es_fflush (es_stdout); log_logv (is_error? GPGRT_LOGLVL_ERROR: GPGRT_LOGLVL_INFO, format, arg_ptr); log_printf ("\n"); @@ -1480,6 +1481,7 @@ ask_marktrusted (ctrl_t ctrl, ksba_cert_t cert, int listmode) int success = 0; fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1); + es_fflush (es_stdout); log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); xfree (fpr); @@ -2277,6 +2279,7 @@ gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert) { if (!opt.quiet) { + es_fflush (es_stdout); log_info ("issuer certificate (#/"); gpgsm_dump_string (issuer); log_printf (") not found\n"); -- cgit v1.2.3 From 6d792ae2eb46b3c411d36a87f0d08fbfc1b65cc9 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 13 Mar 2023 08:49:49 +0100 Subject: agent: Make --disable-extended-key-format a dummy option. * agent/agent.h (opt): Remove enable_extended_key_format. * agent/gpg-agent.c (enum cmd_and_opt_values): Turn oDisableExtendedKeyFormat and oEnableExtendedKeyFormat into dummy options. * agent/protect.c (do_encryption): Remove arg use_ocb and corresponding code. (agent_protect): Ditto. Change all callers. * agent/findkey.c (agent_write_private_key): Simplify due to the removal of disable-extended-key-format. (write_extended_private_key): Fold into agent_write_private_key. -- This change is related to GnuPG-bug-id: 6386 but should have no visible effect except for the removal of option --disable-extended-key-format. --- agent/agent.h | 9 +- agent/command-ssh.c | 2 +- agent/command.c | 8 +- agent/cvt-openpgp.c | 2 +- agent/findkey.c | 245 +++++++++++++++++++++++---------------------------- agent/genkey.c | 2 +- agent/gpg-agent.c | 16 +--- agent/protect-tool.c | 6 +- agent/protect.c | 136 +++++++--------------------- agent/t-protect.c | 2 +- doc/gpg-agent.texi | 14 ++- 11 files changed, 157 insertions(+), 285 deletions(-) diff --git a/agent/agent.h b/agent/agent.h index 303f92e50..4e7452eee 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -141,13 +141,6 @@ struct passphrase change. */ int enable_passphrase_history; - /* If set the extended key format is used for new keys. Note that - * this may have the value 2 in which case - * --disable-extended-key-format won't have any effect and thus - * effectivley locking it. This is required to support existing - * profiles which lock the use of --enable-extended-key-format. */ - int enable_extended_key_format; - int running_detached; /* We are running detached from the tty. */ /* If this global option is true, the passphrase cache is ignored @@ -566,7 +559,7 @@ unsigned char get_standard_s2k_count_rfc4880 (void); unsigned long get_standard_s2k_time (void); int agent_protect (const unsigned char *plainkey, const char *passphrase, unsigned char **result, size_t *resultlen, - unsigned long s2k_count, int use_ocb); + unsigned long s2k_count); gpg_error_t agent_unprotect (ctrl_t ctrl, const unsigned char *protectedkey, const char *passphrase, gnupg_isotime_t protected_at, diff --git a/agent/command-ssh.c b/agent/command-ssh.c index 7621e7c2f..b41177be6 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -3142,7 +3142,7 @@ ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase, buffer_new, buffer_new_n); if (*passphrase) - err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0, -1); + err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0); else { /* The key derivation function does not support zero length diff --git a/agent/command.c b/agent/command.c index c113caba7..2e996d096 100644 --- a/agent/command.c +++ b/agent/command.c @@ -1218,12 +1218,6 @@ cmd_keyattr (assuan_context_t ctx, char *line) if (ctrl->restricted) return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); - if (!opt.enable_extended_key_format) - { - err = gpg_error (GPG_ERR_NOT_SUPPORTED); - goto leave; - } - opt_delete = has_option (line, "--delete"); line = skip_options (line); @@ -2910,7 +2904,7 @@ cmd_import_key (assuan_context_t ctx, char *line) if (passphrase) { err = agent_protect (key, passphrase, &finalkey, &finalkeylen, - ctrl->s2k_count, -1); + ctrl->s2k_count); if (!err) err = agent_write_private_key (grip, finalkey, finalkeylen, force, NULL, NULL, opt_timestamp); diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index d170fdedc..9bb815ff8 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -1146,7 +1146,7 @@ convert_from_openpgp_native (ctrl_t ctrl, if (!agent_protect (*r_key, passphrase, &protectedkey, &protectedkeylen, - ctrl->s2k_count, -1)) + ctrl->s2k_count)) agent_write_private_key (grip, protectedkey, protectedkeylen, 1, NULL, NULL, 0); xfree (protectedkey); diff --git a/agent/findkey.c b/agent/findkey.c index 060cb786d..098d5224f 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -2,6 +2,7 @@ * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, * 2010, 2011 Free Software Foundation, Inc. * Copyright (C) 2014, 2019 Werner Koch + * Copyright (C) 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -79,19 +80,114 @@ linefeed_to_percent0A (const char *string) } -/* Note: Ownership of FNAME and FP are moved to this function. */ -static gpg_error_t -write_extended_private_key (char *fname, estream_t fp, int update, int newkey, - const void *buf, size_t len, - const char *serialno, const char *keyref, - time_t timestamp) +/* Write the S-expression formatted key (BUFFER,LENGTH) to our key + * storage. With FORCE passed as true an existing key with the given + * GRIP will get overwritten. If SERIALNO and KEYREF are given a + * Token line is added to the key if the extended format is used. If + * TIMESTAMP is not zero and the key doies not yet exists it will be + * recorded as creation date. */ +int +agent_write_private_key (const unsigned char *grip, + const void *buffer, size_t length, int force, + const char *serialno, const char *keyref, + time_t timestamp) { gpg_error_t err; + char *fname; + estream_t fp; + char hexgrip[40+4+1]; + int update, newkey; nvc_t pk = NULL; gcry_sexp_t key = NULL; int remove = 0; char *token = NULL; + bin2hex (grip, 20, hexgrip); + strcpy (hexgrip+40, ".key"); + + fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, + hexgrip, NULL); + + /* FIXME: Write to a temp file first so that write failures during + key updates won't lead to a key loss. */ + + if (!force && !gnupg_access (fname, F_OK)) + { + log_error ("secret key file '%s' already exists\n", fname); + xfree (fname); + return gpg_error (GPG_ERR_EEXIST); + } + + fp = es_fopen (fname, force? "rb+,mode=-rw" : "wbx,mode=-rw"); + if (!fp) + { + gpg_error_t tmperr = gpg_error_from_syserror (); + + if (force && gpg_err_code (tmperr) == GPG_ERR_ENOENT) + { + fp = es_fopen (fname, "wbx,mode=-rw"); + if (!fp) + tmperr = gpg_error_from_syserror (); + } + if (!fp) + { + log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr)); + xfree (fname); + return tmperr; + } + update = 0; + newkey = 1; + } + else if (force) + { + gpg_error_t rc; + char first; + + /* See if an existing key is in extended format. */ + if (es_fread (&first, 1, 1, fp) != 1) + { + rc = gpg_error_from_syserror (); + log_error ("error reading first byte from '%s': %s\n", + fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + rc = es_fseek (fp, 0, SEEK_SET); + if (rc) + { + log_error ("error seeking in '%s': %s\n", fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + if (first == '(') + { + /* Key is still in the old format - force it into extended + * format. We do not request an update here because an + * existing key is not yet in extended key format and no + * extended infos are yet available. */ + update = 0; + newkey = 0; + } + else + { + /* Key is already in the extended format. */ + update = 1; + newkey = 0; + } + } + else + { + /* The key file did not exist: we assume this is a new key and + * write the Created: entry. */ + update = 0; + newkey = 1; + } + + if (update) { int line; @@ -115,10 +211,11 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey, } es_clearerr (fp); - err = gcry_sexp_sscan (&key, NULL, buf, len); + /* Turn (BUFFER,LENGTH) into a gcrypt s-expression and set it into + * our name value container. */ + err = gcry_sexp_sscan (&key, NULL, buffer, length); if (err) goto leave; - err = nvc_set_private_key (pk, key); if (err) goto leave; @@ -153,7 +250,7 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey, } } - /* If a timestamp has been supplied and the key is new write a + /* If a timestamp has been supplied and the key is new, write a * creation timestamp. (We douple check that there is no Created * item yet.)*/ if (timestamp && newkey && !nvc_lookup (pk, "Created:")) @@ -166,7 +263,7 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey, goto leave; } - + /* Back to start and write. */ err = es_fseek (fp, 0, SEEK_SET); if (err) goto leave; @@ -212,133 +309,6 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey, return err; } -/* Write an S-expression formatted key to our key storage. With FORCE - * passed as true an existing key with the given GRIP will get - * overwritten. If SERIALNO and KEYREF are given a Token line is - * added to the key if the extended format is used. If TIMESTAMP is - * not zero and the key doies not yet exists it will be recorded as - * creation date. */ -int -agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, int force, - const char *serialno, const char *keyref, - time_t timestamp) -{ - char *fname; - estream_t fp; - char hexgrip[40+4+1]; - - bin2hex (grip, 20, hexgrip); - strcpy (hexgrip+40, ".key"); - - fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, - hexgrip, NULL); - - /* FIXME: Write to a temp file first so that write failures during - key updates won't lead to a key loss. */ - - if (!force && !gnupg_access (fname, F_OK)) - { - log_error ("secret key file '%s' already exists\n", fname); - xfree (fname); - return gpg_error (GPG_ERR_EEXIST); - } - - fp = es_fopen (fname, force? "rb+,mode=-rw" : "wbx,mode=-rw"); - if (!fp) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - - if (force && gpg_err_code (tmperr) == GPG_ERR_ENOENT) - { - fp = es_fopen (fname, "wbx,mode=-rw"); - if (!fp) - tmperr = gpg_error_from_syserror (); - } - if (!fp) - { - log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr)); - xfree (fname); - return tmperr; - } - } - else if (force) - { - gpg_error_t rc; - char first; - - /* See if an existing key is in extended format. */ - if (es_fread (&first, 1, 1, fp) != 1) - { - rc = gpg_error_from_syserror (); - log_error ("error reading first byte from '%s': %s\n", - fname, strerror (errno)); - xfree (fname); - es_fclose (fp); - return rc; - } - - rc = es_fseek (fp, 0, SEEK_SET); - if (rc) - { - log_error ("error seeking in '%s': %s\n", fname, strerror (errno)); - xfree (fname); - es_fclose (fp); - return rc; - } - - if (first != '(') - { - /* Key is already in the extended format. */ - return write_extended_private_key (fname, fp, 1, 0, buffer, length, - serialno, keyref, timestamp); - } - if (first == '(' && opt.enable_extended_key_format) - { - /* Key is in the old format - but we want the extended format. */ - return write_extended_private_key (fname, fp, 0, 0, buffer, length, - serialno, keyref, timestamp); - } - } - - if (opt.enable_extended_key_format) - return write_extended_private_key (fname, fp, 0, 1, buffer, length, - serialno, keyref, timestamp); - - if (es_fwrite (buffer, length, 1, fp) != 1) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - log_error ("error writing '%s': %s\n", fname, gpg_strerror (tmperr)); - es_fclose (fp); - gnupg_remove (fname); - xfree (fname); - return tmperr; - } - - /* When force is given, the file might have to be truncated. */ - if (force && ftruncate (es_fileno (fp), es_ftello (fp))) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - log_error ("error truncating '%s': %s\n", fname, gpg_strerror (tmperr)); - es_fclose (fp); - gnupg_remove (fname); - xfree (fname); - return tmperr; - } - - if (es_fclose (fp)) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - log_error ("error closing '%s': %s\n", fname, gpg_strerror (tmperr)); - gnupg_remove (fname); - xfree (fname); - return tmperr; - } - bump_key_eventcounter (); - xfree (fname); - return 0; -} - gpg_error_t agent_update_private_key (const unsigned char *grip, nvc_t pk) @@ -393,6 +363,7 @@ agent_update_private_key (const unsigned char *grip, nvc_t pk) return err; } + /* Callback function to try the unprotection from the passphrase query code. */ static gpg_error_t diff --git a/agent/genkey.c b/agent/genkey.c index eb6791dca..7660443ca 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -57,7 +57,7 @@ store_key (gcry_sexp_t private, const char *passphrase, int force, { unsigned char *p; - rc = agent_protect (buf, passphrase, &p, &len, s2k_count, -1); + rc = agent_protect (buf, passphrase, &p, &len, s2k_count); if (rc) { xfree (buf); diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 381999cea..1db422737 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -116,8 +116,6 @@ enum cmd_and_opt_values oCheckSymPassphrasePattern, oMaxPassphraseDays, oEnablePassphraseHistory, - oDisableExtendedKeyFormat, - oEnableExtendedKeyFormat, oStealSocket, oUseStandardSocket, oNoUseStandardSocket, @@ -238,8 +236,6 @@ static gpgrt_opt_t opts[] = { /* */ "@" #endif ), - ARGPARSE_s_n (oDisableExtendedKeyFormat, "disable-extended-key-format", "@"), - ARGPARSE_s_n (oEnableExtendedKeyFormat, "enable-extended-key-format", "@"), ARGPARSE_s_i (oListenBacklog, "listen-backlog", "@"), ARGPARSE_op_u (oAutoExpandSecmem, "auto-expand-secmem", "@"), ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), @@ -315,7 +311,8 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oNoUseStandardSocket, "no-use-standard-socket", "@"), /* Dummy options. */ - + ARGPARSE_s_n (oNoop, "disable-extended-key-format", "@"), + ARGPARSE_s_n (oNoop, "enable-extended-key-format", "@"), ARGPARSE_end () /* End of list */ }; @@ -885,7 +882,6 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) opt.check_sym_passphrase_pattern = NULL; opt.max_passphrase_days = MAX_PASSPHRASE_DAYS; opt.enable_passphrase_history = 0; - opt.enable_extended_key_format = 1; opt.ignore_cache_for_signing = 0; opt.allow_mark_trusted = 1; opt.sys_trustlist_name = NULL; @@ -974,14 +970,6 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) opt.enable_passphrase_history = 1; break; - case oEnableExtendedKeyFormat: - opt.enable_extended_key_format = 2; - break; - case oDisableExtendedKeyFormat: - if (opt.enable_extended_key_format != 2) - opt.enable_extended_key_format = 0; - break; - case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break; case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break; diff --git a/agent/protect-tool.c b/agent/protect-tool.c index bb17033a8..87cf36814 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -97,7 +97,6 @@ static const char *opt_passphrase; static char *opt_prompt; static int opt_status_msg; static const char *opt_agent_program; -static int opt_debug_use_ocb; static char *get_passphrase (int promptno); static void release_passphrase (char *pw); @@ -343,8 +342,7 @@ read_and_protect (const char *fname) return; pw = get_passphrase (1); - rc = agent_protect (key, pw, &result, &resultlen, 0, - opt_debug_use_ocb? 1 : -1); + rc = agent_protect (key, pw, &result, &resultlen, 0); release_passphrase (pw); xfree (key); if (rc) @@ -610,7 +608,7 @@ main (int argc, char **argv ) case oHaveCert: opt_have_cert = 1; break; case oPrompt: opt_prompt = pargs.r.ret_str; break; case oStatusMsg: opt_status_msg = 1; break; - case oDebugUseOCB: opt_debug_use_ocb = 1; break; + case oDebugUseOCB: /* dummy */; break; default: pargs.err = ARGPARSE_PRINT_ERROR; break; } diff --git a/agent/protect.c b/agent/protect.c index 1084ee208..7197cf7e6 100644 --- a/agent/protect.c +++ b/agent/protect.c @@ -379,12 +379,11 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, const char *passphrase, const char *timestamp_exp, size_t timestamp_exp_len, unsigned char **result, size_t *resultlen, - unsigned long s2k_count, int use_ocb) + unsigned long s2k_count) { gcry_cipher_hd_t hd; const char *modestr; - unsigned char hashvalue[20]; - int blklen, enclen, outlen; + int enclen, outlen; unsigned char *iv = NULL; unsigned int ivsize; /* Size of the buffer allocated for IV. */ const unsigned char *s2ksalt; /* Points into IV. */ @@ -398,44 +397,26 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, *resultlen = 0; *result = NULL; - modestr = (use_ocb? "openpgp-s2k3-ocb-aes" - /* */: "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"); + modestr = "openpgp-s2k3-ocb-aes"; rc = gcry_cipher_open (&hd, PROT_CIPHER, - use_ocb? GCRY_CIPHER_MODE_OCB : - GCRY_CIPHER_MODE_CBC, + GCRY_CIPHER_MODE_OCB, GCRY_CIPHER_SECURE); if (rc) return rc; /* We need to work on a copy of the data because this makes it * easier to add the trailer and the padding and more important we - * have to prefix the text with 2 parenthesis. In CBC mode we - * have to allocate enough space for: - * - * (()(4:hash4:sha120:)) + padding - * - * we always append a full block of random bytes as padding but - * encrypt only what is needed for a full blocksize. In OCB mode we + * have to prefix the text with 2 parenthesis. Due to OCB mode we * have to allocate enough space for just: * * (()) */ - blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER); - if (use_ocb) - { - /* (( )) */ - outlen = 2 + protlen + 2 ; - enclen = outlen + 16 /* taglen */; - outbuf = gcry_malloc_secure (enclen); - } - else - { - /* (( )( 4:hash 4:sha1 20: )) */ - outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen; - enclen = outlen/blklen * blklen; - outbuf = gcry_malloc_secure (outlen); - } + + /* (( )) */ + outlen = 2 + protlen + 2 ; + enclen = outlen + 16 /* taglen */; + outbuf = gcry_malloc_secure (enclen); if (!outbuf) { rc = out_of_core (); @@ -445,10 +426,10 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, /* Allocate a buffer for the nonce and the salt. */ if (!rc) { - /* Allocate random bytes to be used as IV, padding and s2k salt - * or in OCB mode for a nonce and the s2k salt. The IV/nonce is - * set later because for OCB we need to set the key first. */ - ivsize = (use_ocb? 12 : (blklen*2)) + 8; + /* Allocate random bytes to be used as nonce and s2k salt. The + * nonce is set later because for OCB we need to set the key + * first. */ + ivsize = 12 + 8; iv = xtrymalloc (ivsize); if (!iv) rc = gpg_error_from_syserror (); @@ -484,40 +465,17 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, goto leave; /* Set the IV/nonce. */ - rc = gcry_cipher_setiv (hd, iv, use_ocb? 12 : blklen); + rc = gcry_cipher_setiv (hd, iv, 12); if (rc) goto leave; - if (use_ocb) - { - /* In OCB Mode we use only the public key parameters as AAD. */ - rc = gcry_cipher_authenticate (hd, hashbegin, protbegin - hashbegin); - if (!rc) - rc = gcry_cipher_authenticate (hd, timestamp_exp, timestamp_exp_len); - if (!rc) - rc = gcry_cipher_authenticate - (hd, protbegin+protlen, hashlen - (protbegin+protlen - hashbegin)); - } - else - { - /* Hash the entire expression for CBC mode. Because - * TIMESTAMP_EXP won't get protected, we can't simply hash a - * continuous buffer but need to call md_write several times. */ - gcry_md_hd_t md; - - rc = gcry_md_open (&md, GCRY_MD_SHA1, 0 ); - if (!rc) - { - gcry_md_write (md, hashbegin, protbegin - hashbegin); - gcry_md_write (md, protbegin, protlen); - gcry_md_write (md, timestamp_exp, timestamp_exp_len); - gcry_md_write (md, protbegin+protlen, - hashlen - (protbegin+protlen - hashbegin)); - memcpy (hashvalue, gcry_md_read (md, GCRY_MD_SHA1), 20); - gcry_md_close (md); - } - } - + /* In OCB Mode we use only the public key parameters as AAD. */ + rc = gcry_cipher_authenticate (hd, hashbegin, protbegin - hashbegin); + if (!rc) + rc = gcry_cipher_authenticate (hd, timestamp_exp, timestamp_exp_len); + if (!rc) + rc = gcry_cipher_authenticate + (hd, protbegin+protlen, hashlen - (protbegin+protlen - hashbegin)); /* Encrypt. */ if (!rc) @@ -527,36 +485,15 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, *p++ = '('; memcpy (p, protbegin, protlen); p += protlen; - if (use_ocb) - { - *p++ = ')'; - *p++ = ')'; - } - else - { - memcpy (p, ")(4:hash4:sha120:", 17); - p += 17; - memcpy (p, hashvalue, 20); - p += 20; - *p++ = ')'; - *p++ = ')'; - memcpy (p, iv+blklen, blklen); /* Add padding. */ - p += blklen; - } + *p++ = ')'; + *p++ = ')'; log_assert ( p - outbuf == outlen); - if (use_ocb) - { - gcry_cipher_final (hd); - rc = gcry_cipher_encrypt (hd, outbuf, outlen, NULL, 0); - if (!rc) - { - log_assert (outlen + 16 == enclen); - rc = gcry_cipher_gettag (hd, outbuf + outlen, 16); - } - } - else + gcry_cipher_final (hd); + rc = gcry_cipher_encrypt (hd, outbuf, outlen, NULL, 0); + if (!rc) { - rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0); + log_assert (outlen + 16 == enclen); + rc = gcry_cipher_gettag (hd, outbuf + outlen, 16); } } @@ -584,7 +521,7 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, (int)strlen (modestr), modestr, &saltpos, (unsigned int)strlen (countbuf), countbuf, - use_ocb? 12 : blklen, &ivpos, use_ocb? 12 : blklen, "", + 12, &ivpos, 12, "", enclen, &encpos, enclen, ""); if (!p) { @@ -598,7 +535,7 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, *resultlen = strlen (p); *result = (unsigned char*)p; memcpy (p+saltpos, s2ksalt, 8); - memcpy (p+ivpos, iv, use_ocb? 12 : blklen); + memcpy (p+ivpos, iv, 12); memcpy (p+encpos, outbuf, enclen); xfree (iv); xfree (outbuf); @@ -614,13 +551,11 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, /* Protect the key encoded in canonical format in PLAINKEY. We assume - a valid S-Exp here. With USE_UCB set to -1 the default scheme is - used (ie. either CBC or OCB), set to 0 the old CBC mode is used, - and set to 1 OCB is used. */ + * a valid S-Exp here. */ int agent_protect (const unsigned char *plainkey, const char *passphrase, unsigned char **result, size_t *resultlen, - unsigned long s2k_count, int use_ocb) + unsigned long s2k_count) { int rc; const char *parmlist; @@ -637,9 +572,6 @@ agent_protect (const unsigned char *plainkey, const char *passphrase, unsigned char *p; int have_curve = 0; - if (use_ocb == -1) - use_ocb = !!opt.enable_extended_key_format; - /* Create an S-expression with the protected-at timestamp. */ memcpy (timestamp_exp, "(12:protected-at15:", 19); gnupg_get_isotime (timestamp_exp+19); @@ -743,7 +675,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase, rc = do_encryption (hash_begin, hash_end - hash_begin + 1, prot_begin, prot_end - prot_begin + 1, passphrase, timestamp_exp, sizeof (timestamp_exp), - &protected, &protectedlen, s2k_count, use_ocb); + &protected, &protectedlen, s2k_count); if (rc) return rc; diff --git a/agent/t-protect.c b/agent/t-protect.c index 88b552585..e6edbffba 100644 --- a/agent/t-protect.c +++ b/agent/t-protect.c @@ -196,7 +196,7 @@ test_agent_protect (void) { ret = agent_protect ((const unsigned char*)specs[i].key, specs[i].passphrase, - &specs[i].result, &specs[i].resultlen, 0, -1); + &specs[i].result, &specs[i].resultlen, 0); if (gpg_err_code (ret) != specs[i].ret_expected) { printf ("agent_protect(%d) returned '%i/%s'; expected '%i/%s'\n", diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index 921522d53..1a03de010 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -615,15 +615,11 @@ remote machine. @itemx --disable-extended-key-format @opindex enable-extended-key-format @opindex disable-extended-key-format -Since version 2.3 keys are created in the extended private key format. -Changing the passphrase of a key will also convert the key to that new -format. This new key format is supported since GnuPG version 2.1.12 -and thus there should be no need to disable it. The disable option -allows to revert to the old behavior for new keys; be aware that keys -are never migrated back to the old format. However if the enable -option has been used the disable option won't have an effect. The -advantage of the extended private key format is that it is text based -and can carry additional meta data. +These options are obsolete and have no effect. The extended key format +is used for years now and has been supported since 2.1.12. Existing +keys in the old format are migrated to the new format as soon as they +are touched. + @anchor{option --enable-ssh-support} @item --enable-ssh-support -- cgit v1.2.3 From 2e065b4bd2d392a389652511264b5cbe19f90ba6 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 14 Mar 2023 16:16:40 +0100 Subject: scd,openpgp: Switch key attributes between RSA and ECC in writekey. * common/sexputil.c (get_rsa_pk_from_canon_sexp): Also allow private keys. (pubkey_algo_string): Ditto. * scd/app-openpgp.c (do_writekey): Switch key attributes -- The scd WRITEKEY command for OpenPGP cards missed proper support to aautomagically switch key attributes based on the new key. We had this only in GENKEY. GnuPG-bug-id: 6378 --- common/sexputil.c | 5 ++++- scd/app-openpgp.c | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/common/sexputil.c b/common/sexputil.c index b7ddea8fc..29fe508b6 100644 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -536,7 +536,8 @@ get_rsa_pk_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; - if (!tok || toklen != 10 || memcmp ("public-key", tok, toklen)) + if (!tok || !((toklen == 10 && !memcmp ("public-key", tok, toklen)) + || (toklen == 11 && !memcmp ("private-key", tok, toklen)))) return gpg_error (GPG_ERR_BAD_PUBKEY); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; @@ -1074,6 +1075,8 @@ pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid) *r_algoid = 0; l1 = gcry_sexp_find_token (s_pkey, "public-key", 0); + if (!l1) + l1 = gcry_sexp_find_token (s_pkey, "private-key", 0); if (!l1) return xtrystrdup ("E_no_key"); { diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index f5d0b5111..d3f460106 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -4824,6 +4824,7 @@ do_writekey (app_t app, ctrl_t ctrl, const unsigned char *buf, *tok; size_t buflen, toklen; int depth; + char *algostr = NULL; (void)ctrl; @@ -4866,17 +4867,41 @@ do_writekey (app_t app, ctrl_t ctrl, goto leave; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; - if (tok && toklen == 3 && memcmp ("rsa", tok, toklen) == 0) - err = rsa_writekey (app, ctrl, pincb, pincb_arg, keyno, buf, buflen, depth); - else if (tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0) - err = ecc_writekey (app, ctrl, pincb, pincb_arg, keyno, buf, buflen, depth); + + if (tok && toklen == 3 && (!memcmp ("rsa", tok, toklen) + || !memcmp ("ecc", tok, toklen))) + { + gcry_sexp_t stmp; + if (!gcry_sexp_new (&stmp, keydata, keydatalen, 0)) + algostr = pubkey_algo_string (stmp, NULL); + else + algostr = NULL; + gcry_sexp_release (stmp); + if (app->app_local->keyattr[keyno].keyalgo && algostr + && strcmp (app->app_local->keyattr[keyno].keyalgo, algostr)) + { + log_info ("openpgp: changing key attribute from %s to %s\n", + app->app_local->keyattr[keyno].keyalgo, algostr); + err = change_keyattr_from_string (app, ctrl, pincb, pincb_arg, + keyid, algostr, NULL, 0); + if (err) + return err; + } + + if (*tok == 'r') + err = rsa_writekey (app, ctrl, pincb, pincb_arg, keyno, + buf,buflen,depth); + else + err = ecc_writekey (app, ctrl, pincb, pincb_arg, keyno, + buf, buflen, depth); + } else { err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); - goto leave; } leave: + xfree (algostr); return err; } -- cgit v1.2.3 From 5118beeec18f731fe3c0084b181eff9531181be6 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 15 Mar 2023 09:36:36 +0100 Subject: gpg: Delete secret key after "keytocard". * g10/card-util.c (card_store_subkey): Add arg processed_keys. * g10/keyedit.c (keyedit_menu): Delete secret key. -- This used to work using the gpg-agent: learn we called at "save" time. However, the recent change inhibited the creation of a shadow key by learn if a regular key still exists. Now we do an explicit delete key at save time. This syncs the behaviour with the description of the man page. GnuPG-bug-id: 6378 --- g10/card-util.c | 17 +++++++++++------ g10/keyedit.c | 38 ++++++++++++++++++++++++++++++++++++-- g10/main.h | 2 +- 3 files changed, 48 insertions(+), 9 deletions(-) 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/keyedit.c b/g10/keyedit.c index 6c45fd640..d21064a21 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1424,6 +1424,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) ; @@ -1500,6 +1502,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); @@ -1872,10 +1882,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; } } } @@ -1952,7 +1964,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; @@ -2262,6 +2274,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); @@ -2291,6 +2324,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); diff --git a/g10/main.h b/g10/main.h index f66f3ef0c..dbaa0c6f3 100644 --- a/g10/main.h +++ b/g10/main.h @@ -516,7 +516,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 --*/ -- cgit v1.2.3 From 56b65f33d261ae906244edb1170bc1cd4a39d7f3 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 15 Mar 2023 11:18:29 +0100 Subject: gpgtar: Print a result status with skiupped files. * tools/gpgtar.h (struct tarinfo_s): Add new fields. * tools/gpgtar-extract.c (check_suspicious_name): Add arg info. (extract_regular): Count files. (gpgtar_extract): Print stats. --- doc/DETAILS | 11 +++++++++ tools/gpgtar-extract.c | 60 ++++++++++++++++++++++++++++++++++++++++++-------- tools/gpgtar.h | 8 ++++++- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/doc/DETAILS b/doc/DETAILS index 10307bae0..4c1e9b67c 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1183,6 +1183,17 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB: send to the client instead of this status line. Such an inquiry may be used to sync with Pinentry +*** GPGTAR_EXTRACT + This status line is emitted after gpgtar has extracted files. + + - tot :: Total number of files extracted and stored + - skp :: Total number of files skipped during extraction + - bad :: Number of files skipped due to a bad file name + - sus :: Number of files skipped due to a suspicious file name + - sym :: Number of symlinks not restored + - hrd :: Number of hard links not restored + - oth :: Number of files not extracted due to other reasons. + ** Obsolete status codes *** SIGEXPIRED Removed on 2011-02-04. This is deprecated in favor of KEYEXPIRED. diff --git a/tools/gpgtar-extract.c b/tools/gpgtar-extract.c index e690dde7a..936d03e3e 100644 --- a/tools/gpgtar-extract.c +++ b/tools/gpgtar-extract.c @@ -37,7 +37,7 @@ #include "gpgtar.h" static gpg_error_t -check_suspicious_name (const char *name) +check_suspicious_name (const char *name, tarinfo_t info) { size_t n; @@ -47,6 +47,7 @@ check_suspicious_name (const char *name) { log_error ("filename '%s' contains a backslash - " "can't extract on this system\n", name); + info->skipped_badname++; return gpg_error (GPG_ERR_INV_NAME); } #endif /*HAVE_DOSISH_SYSTEM*/ @@ -59,6 +60,7 @@ check_suspicious_name (const char *name) { log_error ("filename '%s' has suspicious parts - not extracting\n", name); + info->skipped_suspicious++; return gpg_error (GPG_ERR_INV_NAME); } @@ -83,7 +85,7 @@ extract_regular (estream_t stream, const char *dirname, if (sl->flags == 1) fname = sl->d; - err = check_suspicious_name (fname); + err = check_suspicious_name (fname, info); if (err) goto leave; @@ -131,8 +133,12 @@ extract_regular (estream_t stream, const char *dirname, /* Fixme: Set permissions etc. */ leave: - if (!err && opt.verbose) - log_info ("extracted '%s'\n", fname); + if (!err) + { + if (opt.verbose) + log_info ("extracted '%s'\n", fname); + info->nextracted++; + } es_fclose (outfp); if (err && fname && outfp) { @@ -146,7 +152,8 @@ extract_regular (estream_t stream, const char *dirname, static gpg_error_t -extract_directory (const char *dirname, tar_header_t hdr, strlist_t exthdr) +extract_directory (const char *dirname, tarinfo_t info, + tar_header_t hdr, strlist_t exthdr) { gpg_error_t err; const char *name; @@ -158,7 +165,7 @@ extract_directory (const char *dirname, tar_header_t hdr, strlist_t exthdr) if (sl->flags == 1) name = sl->d; - err = check_suspicious_name (name); + err = check_suspicious_name (name, info); if (err) goto leave; @@ -230,13 +237,19 @@ extract (estream_t stream, const char *dirname, tarinfo_t info, if (hdr->typeflag == TF_REGULAR || hdr->typeflag == TF_UNKNOWN) err = extract_regular (stream, dirname, info, hdr, exthdr); else if (hdr->typeflag == TF_DIRECTORY) - err = extract_directory (dirname, hdr, exthdr); + err = extract_directory (dirname, info, hdr, exthdr); else { char record[RECORDSIZE]; log_info ("unsupported file type %d for '%s' - skipped\n", (int)hdr->typeflag, hdr->name); + if (hdr->typeflag == TF_SYMLINK) + info->skipped_symlinks++; + else if (hdr->typeflag == TF_HARDLINK) + info->skipped_hardlinks++; + else + info->skipped_other++; for (err = 0, n=0; !err && n < hdr->nrecords; n++) { err = read_record (stream, record); @@ -328,7 +341,7 @@ gpgtar_extract (const char *filename, int decrypt) tarinfo_t tarinfo = &tarinfo_buffer; pid_t pid = (pid_t)(-1); char *logfilename = NULL; - + unsigned long long notextracted; memset (&tarinfo_buffer, 0, sizeof tarinfo_buffer); @@ -478,8 +491,37 @@ gpgtar_extract (const char *filename, int decrypt) } } - leave: + notextracted = tarinfo->skipped_badname; + notextracted += tarinfo->skipped_suspicious; + notextracted += tarinfo->skipped_symlinks; + notextracted += tarinfo->skipped_hardlinks; + notextracted += tarinfo->skipped_other; + if (opt.status_stream) + es_fprintf (opt.status_stream, "[GNUPG:] GPGTAR_EXTRACT" + " %llu %llu %lu %lu %lu %lu %lu\n", + tarinfo->nextracted, + notextracted, + tarinfo->skipped_badname, + tarinfo->skipped_suspicious, + tarinfo->skipped_symlinks, + tarinfo->skipped_hardlinks, + tarinfo->skipped_other); + if (notextracted && !opt.quiet) + { + log_info ("Number of files not extracted: %llu\n", notextracted); + if (tarinfo->skipped_badname) + log_info (" invalid name: %lu\n", tarinfo->skipped_badname); + if (tarinfo->skipped_suspicious) + log_info (" suspicious name: %lu\n", tarinfo->skipped_suspicious); + if (tarinfo->skipped_symlinks) + log_info (" symlink: %lu\n", tarinfo->skipped_symlinks); + if (tarinfo->skipped_hardlinks) + log_info (" hardlink: %lu\n", tarinfo->skipped_hardlinks); + if (tarinfo->skipped_other) + log_info (" other reason: %lu\n", tarinfo->skipped_other); + } + free_strlist (extheader); xfree (header); xfree (dirname); diff --git a/tools/gpgtar.h b/tools/gpgtar.h index 303db0045..9177fcfcb 100644 --- a/tools/gpgtar.h +++ b/tools/gpgtar.h @@ -54,8 +54,14 @@ struct /* An info structure to avoid global variables. */ struct tarinfo_s { - unsigned long long nblocks; /* Count of processed blocks. */ + unsigned long long nblocks; /* Count of processed blocks. */ unsigned long long headerblock; /* Number of current header block. */ + unsigned long long nextracted; /* Number of extracted files. */ + unsigned long skipped_badname; + unsigned long skipped_suspicious; + unsigned long skipped_symlinks; + unsigned long skipped_hardlinks; + unsigned long skipped_other; }; typedef struct tarinfo_s *tarinfo_t; -- cgit v1.2.3 From e5066f2d1c26124359da64fdc46360090910864c Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 15 Mar 2023 12:05:45 +0100 Subject: gpgtar: Do not allow the use of stdout for --status-fd * tools/gpgtar.c (main): Don't allow logging via the Registry. Forbid using stdout for status-fd in crypt mode. -- Without that check a status output would be mixed up with the input to the internal call of gpg. Using the Registry key to enable logging is very annoying. --- tools/gpgtar.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/gpgtar.c b/tools/gpgtar.c index cfd760499..64e5306b2 100644 --- a/tools/gpgtar.c +++ b/tools/gpgtar.c @@ -459,7 +459,7 @@ main (int argc, char **argv) gnupg_reopen_std (GPGTAR_NAME); gpgrt_set_strusage (my_strusage); - log_set_prefix (GPGTAR_NAME, GPGRT_LOG_WITH_PREFIX); + log_set_prefix (GPGTAR_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_NO_REGISTRY); /* Make sure that our subsystems are ready. */ i18n_init(); @@ -501,7 +501,11 @@ main (int argc, char **argv) log_fatal ("status-fd is invalid: %s\n", strerror (errno)); if (fd == 1) - opt.status_stream = es_stdout; + { + opt.status_stream = es_stdout; + if (!skip_crypto) + log_fatal ("using stdout for the status-fd is not possible\n"); + } else if (fd == 2) opt.status_stream = es_stderr; else -- cgit v1.2.3 From e4ac3e7dec92acce32398f571959c7a33534f0c4 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 16 Mar 2023 09:46:05 +0100 Subject: gpgsm: New option --no-pretty-dn * sm/gpgsm.c (oNoPrettyDN): New. (opts): Add --no-pretty-dn. (main): Implement. * sm/gpgsm.h (opt): Add no_pretty_dn. * sm/certdump.c (gpgsm_es_print_name): Act upon. --- doc/gpgsm.texi | 9 +++++++++ sm/certdump.c | 9 ++++++++- sm/gpgsm.c | 7 ++++++- sm/gpgsm.h | 2 ++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index a117009bd..364345741 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -629,6 +629,15 @@ always listed in @option{--with-colons} mode. Include info about the presence of a secret key in public key listings done with @code{--with-colons}. +@item --no-pretty-dn +@opindex no-pretty-dn +By default gpgsm prints distinguished names (DNs) like the Issuer or +Subject in a more readable format (e.g. using a well defined order of +the parts). However, this format can't be used as input strings. +This option reverts printing to standard RFC-2253 format and thus +avoids the need to use --dump-cert or --with-colons to get the +``real'' name. + @end table @c ******************************************* diff --git a/sm/certdump.c b/sm/certdump.c index 3ad0edbe3..03bfd4106 100644 --- a/sm/certdump.c +++ b/sm/certdump.c @@ -728,7 +728,14 @@ gpgsm_es_print_name2 (estream_t fp, const char *name, int translate) void gpgsm_es_print_name (estream_t fp, const char *name) { - gpgsm_es_print_name2 (fp, name, 1); + if (opt.no_pretty_dn) + { + if (!name) + name = "[error]"; + es_write_sanitized (fp, name, strlen (name), NULL, NULL); + } + else + gpgsm_es_print_name2 (fp, name, 1); } diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 8ca398360..aeb6ad7a9 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -170,6 +170,7 @@ enum cmd_and_opt_values { oWithKeyScreening, oAnswerYes, oAnswerNo, + oNoPrettyDN, oKeyring, oDefaultKey, oDefRecipient, @@ -385,7 +386,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"), ARGPARSE_s_n (oWithSecret, "with-secret", "@"), ARGPARSE_s_n (oWithKeyScreening,"with-key-screening", "@"), - + ARGPARSE_s_n (oNoPrettyDN, "no-pretty-dn", "@"), ARGPARSE_header ("Security", N_("Options controlling the security")), @@ -1318,6 +1319,10 @@ main ( int argc, char **argv) opt.with_key_screening = 1; break; + case oNoPrettyDN: + opt.no_pretty_dn = 1; + break; + case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break; case oChUid: break; /* Command line only (see above). */ case oAgentProgram: opt.agent_program = pargs.r.ret_str; break; diff --git a/sm/gpgsm.h b/sm/gpgsm.h index e09dd75e9..6149b8491 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -85,6 +85,8 @@ struct int with_key_screening; /* Option --with-key-screening active. */ + int no_pretty_dn; /* Option --no-pretty-dn */ + int pinentry_mode; int request_origin; -- cgit v1.2.3 From f5347fbc25aee7adce6244112aae639b0ff00ccd Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 16 Mar 2023 14:52:28 +0100 Subject: dirmngr: Add framework to implement a fake CRL feature. * dirmngr/fakecrl.c: New. * dirmngr/dirmngr.h (opt): Add fake_crl. * dirmngr/dirmngr.c (enum cmd_and_opt_values): Add oFakeCRL. (opts): Add "fake-crl" (parse_rereadable_options): Set opt.fake_crl. * dirmngr/server.c (cmd_isvalid): Take care of fakce CRLs. --- dirmngr/Makefile.am | 1 + dirmngr/crlcache.h | 8 +++++++ dirmngr/dirmngr.c | 10 ++++++++- dirmngr/dirmngr.h | 1 + dirmngr/fakecrl.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++ dirmngr/server.c | 6 ++++- 6 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 dirmngr/fakecrl.c diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am index 1c8065dbb..feee2f5c8 100644 --- a/dirmngr/Makefile.am +++ b/dirmngr/Makefile.am @@ -58,6 +58,7 @@ endif noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \ + fakecrl.c \ certcache.c certcache.h \ domaininfo.c \ workqueue.c \ diff --git a/dirmngr/crlcache.h b/dirmngr/crlcache.h index 7db8f01cc..375943462 100644 --- a/dirmngr/crlcache.h +++ b/dirmngr/crlcache.h @@ -45,6 +45,7 @@ crl_sig_result_t; struct crl_cache_entry_s; typedef struct crl_cache_entry_s *crl_cache_entry_t; +/*-- crlcache.c --*/ void crl_cache_init (void); void crl_cache_deinit (void); @@ -68,4 +69,11 @@ gpg_error_t crl_cache_load (ctrl_t ctrl, const char *filename); gpg_error_t crl_cache_reload_crl (ctrl_t ctrl, ksba_cert_t cert); +/*-- fakecrl.c --*/ +crl_cache_result_t fakecrl_isvalid (ctrl_t ctrl, + const char *issuer_hash, + const char *cert_id); + + + #endif /* CRLCACHE_H */ diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index 66b7878e5..3c0818af9 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -158,6 +158,7 @@ enum cmd_and_opt_values { oConnectTimeout, oConnectQuickTimeout, oListenBacklog, + oFakeCRL, aTest }; @@ -274,7 +275,7 @@ static gpgrt_opt_t opts[] = { " points to serverlist")), ARGPARSE_s_i (oLDAPTimeout, "ldaptimeout", N_("|N|set LDAP timeout to N seconds")), - + ARGPARSE_s_s (oFakeCRL, "fake-crl", "@"), ARGPARSE_header ("OCSP", N_("Configuration for OCSP")), @@ -709,6 +710,8 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) opt.ldaptimeout = DEFAULT_LDAP_TIMEOUT; ldapserver_list_needs_reset = 1; opt.debug_cache_expired_certs = 0; + xfree (opt.fake_crl); + opt.fake_crl = NULL; return 1; } @@ -871,6 +874,11 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) opt.debug_cache_expired_certs = 0; break; + case oFakeCRL: + xfree (opt.fake_crl); + opt.fake_crl = *pargs->r.ret_str? xstrdup (pargs->r.ret_str) : NULL; + break; + default: return 0; /* Not handled. */ } diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index bd4660422..bcb364e8d 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -104,6 +104,7 @@ struct int force; /* Force loading outdated CRLs. */ + char *fake_crl; /* Name of a file with faked CRL entries. */ unsigned int connect_timeout; /* Timeout for connect. */ unsigned int connect_quick_timeout; /* Shorter timeout for connect. */ diff --git a/dirmngr/fakecrl.c b/dirmngr/fakecrl.c new file mode 100644 index 000000000..43b68a57a --- /dev/null +++ b/dirmngr/fakecrl.c @@ -0,0 +1,63 @@ +/* fakecrl.c - Debug code to test revocations. + * Copyright (C) 2023 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/* + * For regression testing it is useful to have a way to claim that + * certain certificates are revoked. We achieve this with the + * --fake-crl option which takes a file name as argument. The format + * of the file is: empty lines and lines starting with a hash sign are + * ignored. A line with the issuer DN in brackets starts entries for + * this issuer. All following lines up to the next line with a + * bracket list revoked certificates. For each revoked certificate + * the hexadecimal encoded serial number is listed, followed by the + * revocation date in ISO 14 byte notation, optionally followed by a + * reason keyword. Example: + *--------------------- + * # Sample Fake CRL + * [CN=Bayern-Softtoken-Issuing-CA-2019,OU=IT-DLZ,O=Freistaat Bayern,C=DE] + * 7FD62B1A9EA5BBC84971183080717004 20221125T074346 + * 11223344556677 20230101T000000 key_compromise + * 0000000000000042 20221206T121200 certificate_hold + * + * [CN=CA IVBB Deutsche Telekom AG 18,OU=Bund,O=PKI-1-Verwaltung,C=DE] + * 735D1B97389F 20230210T083947 + *--------------------- + */ +#include + +#include +#include + +#include "dirmngr.h" +#include "crlcache.h" + + + +/* Returns 0 if the given certificate is not listed in the faked CRL + * or no fake CRL is configured. It is expected that the caller then + * consults the real CRL. */ +gpg_error_t +fakecrl_isvalid (ctrl_t ctrl, const char *issuer_hash, const char *cert_id) +{ + (void)ctrl; + (void)issuer_hash; + (void)cert_id; + return 0; +} diff --git a/dirmngr/server.c b/dirmngr/server.c index cd71592a4..cd0839aad 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -1339,6 +1339,10 @@ cmd_isvalid (assuan_context_t ctx, char *line) } else if (only_ocsp) err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + else if (opt.fake_crl && (err = fakecrl_isvalid (ctrl, issuerhash, serialno))) + { + /* We already got the error code. */ + } else { switch (crl_cache_isvalid (ctrl, @@ -1377,7 +1381,7 @@ cmd_isvalid (assuan_context_t ctx, char *line) /* If the line contains a SHA-1 fingerprint as the first argument, - return the FPR vuffer on success. The function checks that the + return the FPR buffer on success. The function checks that the fingerprint consists of valid characters and prints and error message if it does not and returns NULL. Fingerprints are considered optional and thus no explicit error is returned. NULL is -- cgit v1.2.3