aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2025-03-06 16:17:17 +0000
committerWerner Koch <[email protected]>2025-03-06 16:17:17 +0000
commitd3d7713c1799754160260cb350309dd183b397f5 (patch)
tree35f2c0ca3b27c2e25dd97d8180a1e7c64776bf4c
parentdirmngr: Use the same thread init strategy as gpg-agent et al. (diff)
downloadgnupg-d3d7713c1799754160260cb350309dd183b397f5.tar.gz
gnupg-d3d7713c1799754160260cb350309dd183b397f5.zip
gpg: Fix regression for the recent malicious subkey DoS fix.
* g10/packet.h (PUBKEY_USAGE_VERIFY): New. * g10/getkey.c (get_pubkey_for_sig): Pass new flag also to requested usage. (finish_lookup): Introduce a verify_mode. -- Fixes-commit: 48978ccb4e20866472ef18436a32744350a65158 GnuPG-bug-id: 7547
-rw-r--r--g10/getkey.c52
-rw-r--r--g10/packet.h1
2 files changed, 34 insertions, 19 deletions
diff --git a/g10/getkey.c b/g10/getkey.c
index 21f996a5b..e93c0a904 100644
--- a/g10/getkey.c
+++ b/g10/getkey.c
@@ -315,11 +315,12 @@ pk_from_block (PKT_public_key *pk, kbnode_t keyblock, kbnode_t found_key)
/* Specialized version of get_pubkey which retrieves the key based on
- * information in SIG. In contrast to get_pubkey PK is required. IF
+ * information in SIG. In contrast to get_pubkey PK is required. If
* FORCED_PK is not NULL, this public key is used and copied to PK.
* If R_KEYBLOCK is not NULL the entire keyblock is stored there if
* found and FORCED_PK is not used; if not used or on error NULL is
- * stored there. */
+ * stored there. Use this function only to find the key for
+ * verification; it can't be used to select a key for signing. */
gpg_error_t
get_pubkey_for_sig (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig,
PKT_public_key *forced_pk, kbnode_t *r_keyblock)
@@ -339,8 +340,9 @@ get_pubkey_for_sig (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig,
/* Make sure to request only keys cabable of signing. This makes
* sure that a subkey w/o a valid backsig or with bad usage flags
- * will be skipped. */
- pk->req_usage = PUBKEY_USAGE_SIG;
+ * will be skipped. We also request the verification mode so that
+ * expired and reoked keys are returned. */
+ pk->req_usage = (PUBKEY_USAGE_SIG | PUBKEY_USAGE_VERIFY);
/* First try the ISSUER_FPR info. */
fpr = issuer_fpr_raw (sig, &fprlen);
@@ -404,10 +406,10 @@ get_pubkey_bykid (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock,
/* Try to get it from the cache. We don't do this when pk is
* NULL as it does not guarantee that the user IDs are cached.
* The old get_pubkey_function did not check PK->REQ_USAGE when
- * reading form the caceh. This is probably a bug. Note that
+ * reading from the cache. This is probably a bug. Note that
* the cache is not used when the caller asked to return the
* entire keyblock. This is because the cache does not
- * associate the public key wit its primary key. */
+ * associate the public key with its primary key. */
pk_cache_entry_t ce;
for (ce = pk_cache; ce; ce = ce->next)
{
@@ -3723,11 +3725,18 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
PKT_public_key *pk;
int req_prim;
int diag_exactfound = 0;
+ int verify_mode = 0;
u32 curtime = make_timestamp ();
if (r_flags)
*r_flags = 0;
+
+ /* The verify mode is used to change the behaviour so that we can
+ * return an expired or revoked key for signature verification. */
+ verify_mode = ((req_usage & PUBKEY_USAGE_VERIFY)
+ && (req_usage & PUBKEY_USAGE_SIG));
+
#define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC|PUBKEY_USAGE_CERT)
req_usage &= USAGE_MASK;
/* In allow ADSK mode make sure both encryption bits are set. */
@@ -3783,9 +3792,9 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
}
if (DBG_LOOKUP)
- log_debug ("finish_lookup: checking key %08lX (%s)(req_usage=%x)\n",
+ log_debug ("finish_lookup: checking key %08lX (%s)(req_usage=%x%s)\n",
(ulong) keyid_from_pk (keyblock->pkt->pkt.public_key, NULL),
- foundk ? "one" : "all", req_usage);
+ foundk ? "one" : "all", req_usage, verify_mode? ",verify":"");
if (diag_exactfound && DBG_LOOKUP)
log_debug ("\texact search requested and found\n");
@@ -3847,7 +3856,8 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
req_usage, pk->pubkey_usage);
continue;
}
- if (opt.flags.disable_pqc_encryption
+ if (!verify_mode
+ && opt.flags.disable_pqc_encryption
&& pk->pubkey_algo == PUBKEY_ALGO_KYBER)
{
if (DBG_LOOKUP)
@@ -3857,28 +3867,29 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
}
n_subkeys++;
- if (pk->flags.revoked)
+ if (!verify_mode && pk->flags.revoked)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey has been revoked\n");
n_revoked_or_expired++;
continue;
}
- if (pk->has_expired && !opt.ignore_expiration)
+ if (!verify_mode && pk->has_expired && !opt.ignore_expiration)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey has expired\n");
n_revoked_or_expired++;
continue;
}
- if (pk->timestamp > curtime && !opt.ignore_valid_from)
+ if (!verify_mode && pk->timestamp > curtime && !opt.ignore_valid_from)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey not yet valid\n");
continue;
}
- if (opt.flags.require_pqc_encryption
+ if (!verify_mode
+ && opt.flags.require_pqc_encryption
&& (req_usage & PUBKEY_USAGE_XENC_MASK)
&& pk->pubkey_algo != PUBKEY_ALGO_KYBER)
{
@@ -3888,7 +3899,7 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
}
- if (want_secret)
+ if (!verify_mode && want_secret)
{
int secret_key_avail = agent_probe_secret_key (NULL, pk);
@@ -3915,7 +3926,8 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
}
if (DBG_LOOKUP)
- log_debug ("\tsubkey might be fine\n");
+ log_debug ("\tsubkey might be fine%s\n",
+ verify_mode? " for verification":"");
/* In case a key has a timestamp of 0 set, we make sure
that it is used. A better change would be to compare
">=" but that might also change the selected keys and
@@ -3956,17 +3968,18 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
log_debug ("\tprimary key usage does not match: "
"want=%x have=%x\n", req_usage, pk->pubkey_usage);
}
- else if (pk->flags.revoked)
+ else if (!verify_mode && pk->flags.revoked)
{
if (DBG_LOOKUP)
log_debug ("\tprimary key has been revoked\n");
}
- else if (pk->has_expired)
+ else if (!verify_mode && pk->has_expired)
{
if (DBG_LOOKUP)
log_debug ("\tprimary key has expired\n");
}
- else if (opt.flags.require_pqc_encryption
+ else if (!verify_mode
+ && opt.flags.require_pqc_encryption
&& (req_usage & PUBKEY_USAGE_XENC_MASK)
&& pk->pubkey_algo != PUBKEY_ALGO_KYBER)
{
@@ -3976,7 +3989,8 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
else /* Okay. */
{
if (DBG_LOOKUP)
- log_debug ("\tprimary key may be used\n");
+ log_debug ("\tprimary key may be used%s\n",
+ verify_mode? " for verification":"");
latest_key = keyblock;
}
}
diff --git a/g10/packet.h b/g10/packet.h
index 6be7f207c..29e58d2df 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -60,6 +60,7 @@
#define PUBKEY_USAGE_RENC 1024 /* Restricted encryption. */
#define PUBKEY_USAGE_TIME 2048 /* Timestamp use. */
+#define PUBKEY_USAGE_VERIFY 16384 /* Verify only modifier. */
/* The usage bits which can be derived from the algo. */
#define PUBKEY_USAGE_BASIC_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC\