From a9cca032a2b7c0c6bcacc6ab4ecd25c95cc75305 Mon Sep 17 00:00:00 2001
From: Demi Marie Obenour <demi@invisiblethingslab.com>
Date: Sun, 27 Mar 2022 12:49:07 -0400
Subject: [PATCH] Ignore subkeys that cannot be used for signing

This ensures that a signature is only accepted if the subkey that made
it is actually allowed to sign.  Test 265 verifies that RPM ignores
subkeys that cannot sign.

A subkey is considered to be capable of signing if, and only if, its
subkey binding signature has a hashed key flags subpacket that contains
the flag 0x02.  RFC4880 requires that the subkey binding signature be
v4, which this requirement enforces implicitly.  RFC4880 also requires
that primary key binding signatures be present and checked.  This is not
yet implemented, but may be implemented later.

Fixes #1911.
---
 rpmio/rpmpgp.c     | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
 tests/rpmsigdig.at |  2 --
 2 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c
index 66837b1..22ac9c8 100644
--- a/rpmio/rpmpgp.c
+++ b/rpmio/rpmpgp.c
@@ -1133,6 +1133,31 @@ static int pgpVerifySelf(pgpDigParams key, pgpDigParams selfsig,
     return rc;
 }
 
+static int parseSubkeySig(const struct pgpPkt *pkt, uint8_t tag,
+			  pgpDigParams *params_p) {
+    pgpDigParams params = *params_p = NULL; /* assume failure */
+
+    if (pkt->tag != PGPTAG_SIGNATURE)
+	goto fail;
+
+    params = pgpDigParamsNew(tag);
+
+    if (pgpPrtSig(tag, pkt->body, pkt->blen, params))
+	goto fail;
+
+    if (params->sigtype != PGPSIGTYPE_SUBKEY_BINDING &&
+	params->sigtype != PGPSIGTYPE_SUBKEY_REVOKE)
+    {
+	goto fail;
+    }
+
+    *params_p = params;
+    return 0;
+fail:
+    pgpDigParamsFree(params);
+    return -1;
+}
+
 int pgpPrtParams(const uint8_t * pkts, size_t pktlen, unsigned int pkttype,
 		 pgpDigParams * ret)
 {
@@ -1238,7 +1263,28 @@ int pgpPrtParamsSubkeys(const uint8_t *pkts, size_t pktlen,
 		pgpDigParamsFree(digps[count]);
 		continue;
 	    }
-	    count++;
+
+	    pgpDigParams subkey_sig = NULL;
+	    if (decodePkt(p, pend - p, &pkt) ||
+	        parseSubkeySig(&pkt, 0, &subkey_sig))
+	    {
+		pgpDigParamsFree(digps[count]);
+		break;
+	    }
+
+	    /* Is the subkey revoked or incapable of signing? */
+	    int ignore = subkey_sig->sigtype != PGPSIGTYPE_SUBKEY_BINDING ||
+			 !((subkey_sig->saved & PGPDIG_SIG_HAS_KEY_FLAGS) &&
+			   (subkey_sig->key_flags & 0x02));
+	    if (ignore) {
+		pgpDigParamsFree(digps[count]);
+	    } else {
+		digps[count]->key_flags = subkey_sig->key_flags;
+		digps[count]->saved |= PGPDIG_SIG_HAS_KEY_FLAGS;
+		count++;
+	    }
+	    p += (pkt.body - pkt.head) + pkt.blen;
+	    pgpDigParamsFree(subkey_sig);
 	}
     }
     rc = (p == pend) ? 0 : -1;
diff --git a/tests/rpmsigdig.at b/tests/rpmsigdig.at
index 5d781d8..ab9b473 100644
--- a/tests/rpmsigdig.at
+++ b/tests/rpmsigdig.at
@@ -247,8 +247,6 @@ UNW2iqnN3BA7guhOv6OMiROF1+I7Q5nWT63mQC7IgQ==
 gpg(rpm.org RSA testkey <rsa@rpm.org>) = 4:4344591e1964c5fc-58e63918
 gpg(1964c5fc) = 4:4344591e1964c5fc-58e63918
 gpg(4344591e1964c5fc) = 4:4344591e1964c5fc-58e63918
-gpg(f00650f8) = 4:185e6146f00650f8-58e63918
-gpg(185e6146f00650f8) = 4:185e6146f00650f8-58e63918
 ],
 [])
 AT_CLEANUP
-- 
1.8.3.1

