blob: a5ec80250196795b3e98bd159e48f83107bb8c39 [file] [log] [blame]
Andrew Geissler595f6302022-01-24 19:11:47 +00001From 07676ca03ad8afcf1ca95a2353c83fbb1d970b9b Mon Sep 17 00:00:00 2001
2From: Panu Matilainen <pmatilai@redhat.com>
3Date: Thu, 30 Sep 2021 09:59:30 +0300
4Subject: [PATCH 3/3] Validate and require subkey binding signatures on PGP
5 public keys
6
7All subkeys must be followed by a binding signature by the primary key
8as per the OpenPGP RFC, enforce the presence and validity in the parser.
9
10The implementation is as kludgey as they come to work around our
11simple-minded parser structure without touching API, to maximise
12backportability. Store all the raw packets internally as we decode them
13to be able to access previous elements at will, needed to validate ordering
14and access the actual data. Add testcases for manipulated keys whose
15import previously would succeed.
16
17Depends on the two previous commits:
187b399fcb8f52566e6f3b4327197a85facd08db91 and
19236b802a4aa48711823a191d1b7f753c82a89ec5
20
21Fixes CVE-2021-3521.
22
23Upstream-Status: Backport [https://github.com/rpm-software-management/rpm/commit/bd36c5dc9]
24CVE:CVE-2021-3521
25
26Signed-off-by: Changqing Li <changqing.li@windriver.com>
27
28---
29 rpmio/rpmpgp.c | 99 +++++++++++++++++--
30 tests/Makefile.am | 3 +
31 tests/data/keys/CVE-2021-3521-badbind.asc | 25 +++++
32 .../data/keys/CVE-2021-3521-nosubsig-last.asc | 25 +++++
33 tests/data/keys/CVE-2021-3521-nosubsig.asc | 37 +++++++
34 tests/rpmsigdig.at | 28 ++++++
35 6 files changed, 209 insertions(+), 8 deletions(-)
36 create mode 100644 tests/data/keys/CVE-2021-3521-badbind.asc
37 create mode 100644 tests/data/keys/CVE-2021-3521-nosubsig-last.asc
38 create mode 100644 tests/data/keys/CVE-2021-3521-nosubsig.asc
39
40diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c
41index 509e777e6d..371ad4d9b6 100644
42--- a/rpmio/rpmpgp.c
43+++ b/rpmio/rpmpgp.c
44@@ -1061,33 +1061,116 @@ static pgpDigParams pgpDigParamsNew(uint8_t tag)
45 return digp;
46 }
47
48+static int hashKey(DIGEST_CTX hash, const struct pgpPkt *pkt, int exptag)
49+{
50+ int rc = -1;
51+ if (pkt->tag == exptag) {
52+ uint8_t head[] = {
53+ 0x99,
54+ (pkt->blen >> 8),
55+ (pkt->blen ),
56+ };
57+
58+ rpmDigestUpdate(hash, head, 3);
59+ rpmDigestUpdate(hash, pkt->body, pkt->blen);
60+ rc = 0;
61+ }
62+ return rc;
63+}
64+
65+static int pgpVerifySelf(pgpDigParams key, pgpDigParams selfsig,
66+ const struct pgpPkt *all, int i)
67+{
68+ int rc = -1;
69+ DIGEST_CTX hash = NULL;
70+
71+ switch (selfsig->sigtype) {
72+ case PGPSIGTYPE_SUBKEY_BINDING:
73+ hash = rpmDigestInit(selfsig->hash_algo, 0);
74+ if (hash) {
75+ rc = hashKey(hash, &all[0], PGPTAG_PUBLIC_KEY);
76+ if (!rc)
77+ rc = hashKey(hash, &all[i-1], PGPTAG_PUBLIC_SUBKEY);
78+ }
79+ break;
80+ default:
81+ /* ignore types we can't handle */
82+ rc = 0;
83+ break;
84+ }
85+
86+ if (hash && rc == 0)
87+ rc = pgpVerifySignature(key, selfsig, hash);
88+
89+ rpmDigestFinal(hash, NULL, NULL, 0);
90+
91+ return rc;
92+}
93+
94 int pgpPrtParams(const uint8_t * pkts, size_t pktlen, unsigned int pkttype,
95 pgpDigParams * ret)
96 {
97 const uint8_t *p = pkts;
98 const uint8_t *pend = pkts + pktlen;
99 pgpDigParams digp = NULL;
100- struct pgpPkt pkt;
101+ pgpDigParams selfsig = NULL;
102+ int i = 0;
103+ int alloced = 16; /* plenty for normal cases */
104+ struct pgpPkt *all = xmalloc(alloced * sizeof(*all));
105 int rc = -1; /* assume failure */
106+ int expect = 0;
107+ int prevtag = 0;
108
109 while (p < pend) {
110- if (decodePkt(p, (pend - p), &pkt))
111+ struct pgpPkt *pkt = &all[i];
112+ if (decodePkt(p, (pend - p), pkt))
113 break;
114
115 if (digp == NULL) {
116- if (pkttype && pkt.tag != pkttype) {
117+ if (pkttype && pkt->tag != pkttype) {
118 break;
119 } else {
120- digp = pgpDigParamsNew(pkt.tag);
121+ digp = pgpDigParamsNew(pkt->tag);
122 }
123 }
124
125- if (pgpPrtPkt(&pkt, digp))
126+ if (expect) {
127+ if (pkt->tag != expect)
128+ break;
129+ selfsig = pgpDigParamsNew(pkt->tag);
130+ }
131+ if (pgpPrtPkt(pkt, selfsig ? selfsig : digp))
132 break;
133
134- p += (pkt.body - pkt.head) + pkt.blen;
135- if (pkttype == PGPTAG_SIGNATURE)
136- break;
137+ if (selfsig) {
138+ /* subkeys must be followed by binding signature */
139+ if (prevtag == PGPTAG_PUBLIC_SUBKEY) {
140+ if (selfsig->sigtype != PGPSIGTYPE_SUBKEY_BINDING)
141+ break;
142+ }
143+
144+ int xx = pgpVerifySelf(digp, selfsig, all, i);
145+
146+ selfsig = pgpDigParamsFree(selfsig);
147+ if (xx)
148+ break;
149+ expect = 0;
150+ }
151+
152+ if (pkt->tag == PGPTAG_PUBLIC_SUBKEY)
153+ expect = PGPTAG_SIGNATURE;
154+ prevtag = pkt->tag;
155+
156+ i++;
157+ p += (pkt->body - pkt->head) + pkt->blen;
158+ if (pkttype == PGPTAG_SIGNATURE)
159+ break;
160+
161+ if (alloced <= i) {
162+ alloced *= 2;
163+ all = xrealloc(all, alloced * sizeof(*all));
164+ }
165+
166 }
167
168 rc = (digp && (p == pend)) ? 0 : -1;
169diff --git a/tests/Makefile.am b/tests/Makefile.am
170index a41ce10de8..7bb23247f1 100644
171--- a/tests/Makefile.am
172+++ b/tests/Makefile.am
173@@ -107,6 +107,9 @@ EXTRA_DIST += data/SPECS/hello-config-buildid.spec
174 EXTRA_DIST += data/SPECS/hello-cd.spec
175 EXTRA_DIST += data/keys/rpm.org-rsa-2048-test.pub
176 EXTRA_DIST += data/keys/rpm.org-rsa-2048-test.secret
177+EXTRA_DIST += data/keys/CVE-2021-3521-badbind.asc
178+EXTRA_DIST += data/keys/CVE-2022-3521-nosubsig.asc
179+EXTRA_DIST += data/keys/CVE-2022-3521-nosubsig-last.asc
180 EXTRA_DIST += data/macros.testfile
181 EXTRA_DIST += data/macros.debug
182 EXTRA_DIST += data/SOURCES/foo.c
183diff --git a/tests/data/keys/CVE-2021-3521-badbind.asc b/tests/data/keys/CVE-2021-3521-badbind.asc
184new file mode 100644
185index 0000000000..aea00f9d7a
186--- /dev/null
187+++ b/tests/data/keys/CVE-2021-3521-badbind.asc
188@@ -0,0 +1,25 @@
189+-----BEGIN PGP PUBLIC KEY BLOCK-----
190+Version: rpm-4.17.90 (NSS-3)
191+
192+mQENBFjmORgBCAC7TMEk6wnjSs8Dr4yqSScWdU2pjcqrkTxuzdWvowcIUPZI0w/g
193+HkRqGd4apjvY2V15kjL10gk3QhFP3pZ/9p7zh8o8NHX7aGdSGDK7NOq1eFaErPRY
194+91LW9RiZ0lbOjXEzIL0KHxUiTQEmdXJT43DJMFPyW9fkCWg0OltiX618FUdWWfI8
195+eySdLur1utnqBvdEbCUvWK2RX3vQZQdvEBODnNk2pxqTyV0w6VPQ96W++lF/5Aas
196+7rUv3HIyIXxIggc8FRrnH+y9XvvHDonhTIlGnYZN4ubm9i4y3gOkrZlGTrEw7elQ
197+1QeMyG2QQEbze8YjpTm4iLABCBrRfPRaQpwrABEBAAG0IXJwbS5vcmcgUlNBIHRl
198+c3RrZXkgPHJzYUBycG0ub3JnPokBNwQTAQgAIQUCWOY5GAIbAwULCQgHAgYVCAkK
199+CwIEFgIDAQIeAQIXgAAKCRBDRFkeGWTF/MxxCACnjqFL+MmPh9W9JQKT2DcLbBzf
200+Cqo6wcEBoCOcwgRSk8dSikhARoteoa55JRJhuMyeKhhEAogE9HRmCPFdjezFTwgB
201+BDVBpO2dZ023mLXDVCYX3S8pShOgCP6Tn4wqCnYeAdLcGg106N4xcmgtcssJE+Pr
202+XzTZksbZsrTVEmL/Ym+R5w5jBfFnGk7Yw7ndwfQsfNXQb5AZynClFxnX546lcyZX
203+fEx3/e6ezw57WNOUK6WT+8b+EGovPkbetK/rGxNXuWaP6X4A/QUm8O98nCuHYFQq
204++mvNdsCBqGf7mhaRGtpHk/JgCn5rFvArMDqLVrR9hX0LdCSsH7EGE+bR3r7wuQEN
205+BFjmORgBCACk+vDZrIXQuFXEYToZVwb2attzbbJJCqD71vmZTLsW0QxuPKRgbcYY
206+zp4K4lVBnHhFrF8MOUOxJ7kQWIJZMZFt+BDcptCYurbD2H4W2xvnWViiC+LzCMzz
207+iMJT6165uefL4JHTDPxC2fFiM9yrc72LmylJNkM/vepT128J5Qv0gRUaQbHiQuS6
208+Dm/+WRnUfx3i89SV4mnBxb/Ta93GVqoOciWwzWSnwEnWYAvOb95JL4U7c5J5f/+c
209+KnQDHsW7sIiIdscsWzvgf6qs2Ra1Zrt7Fdk4+ZS2f/adagLhDO1C24sXf5XfMk5m
210+L0OGwZSr9m5s17VXxfspgU5ugc8kBJfzABEBAAE=
211+=WCfs
212+-----END PGP PUBLIC KEY BLOCK-----
213+
214diff --git a/tests/data/keys/CVE-2021-3521-nosubsig-last.asc b/tests/data/keys/CVE-2021-3521-nosubsig-last.asc
215new file mode 100644
216index 0000000000..aea00f9d7a
217--- /dev/null
218+++ b/tests/data/keys/CVE-2021-3521-nosubsig-last.asc
219@@ -0,0 +1,25 @@
220+-----BEGIN PGP PUBLIC KEY BLOCK-----
221+Version: rpm-4.17.90 (NSS-3)
222+
223+mQENBFjmORgBCAC7TMEk6wnjSs8Dr4yqSScWdU2pjcqrkTxuzdWvowcIUPZI0w/g
224+HkRqGd4apjvY2V15kjL10gk3QhFP3pZ/9p7zh8o8NHX7aGdSGDK7NOq1eFaErPRY
225+91LW9RiZ0lbOjXEzIL0KHxUiTQEmdXJT43DJMFPyW9fkCWg0OltiX618FUdWWfI8
226+eySdLur1utnqBvdEbCUvWK2RX3vQZQdvEBODnNk2pxqTyV0w6VPQ96W++lF/5Aas
227+7rUv3HIyIXxIggc8FRrnH+y9XvvHDonhTIlGnYZN4ubm9i4y3gOkrZlGTrEw7elQ
228+1QeMyG2QQEbze8YjpTm4iLABCBrRfPRaQpwrABEBAAG0IXJwbS5vcmcgUlNBIHRl
229+c3RrZXkgPHJzYUBycG0ub3JnPokBNwQTAQgAIQUCWOY5GAIbAwULCQgHAgYVCAkK
230+CwIEFgIDAQIeAQIXgAAKCRBDRFkeGWTF/MxxCACnjqFL+MmPh9W9JQKT2DcLbBzf
231+Cqo6wcEBoCOcwgRSk8dSikhARoteoa55JRJhuMyeKhhEAogE9HRmCPFdjezFTwgB
232+BDVBpO2dZ023mLXDVCYX3S8pShOgCP6Tn4wqCnYeAdLcGg106N4xcmgtcssJE+Pr
233+XzTZksbZsrTVEmL/Ym+R5w5jBfFnGk7Yw7ndwfQsfNXQb5AZynClFxnX546lcyZX
234+fEx3/e6ezw57WNOUK6WT+8b+EGovPkbetK/rGxNXuWaP6X4A/QUm8O98nCuHYFQq
235++mvNdsCBqGf7mhaRGtpHk/JgCn5rFvArMDqLVrR9hX0LdCSsH7EGE+bR3r7wuQEN
236+BFjmORgBCACk+vDZrIXQuFXEYToZVwb2attzbbJJCqD71vmZTLsW0QxuPKRgbcYY
237+zp4K4lVBnHhFrF8MOUOxJ7kQWIJZMZFt+BDcptCYurbD2H4W2xvnWViiC+LzCMzz
238+iMJT6165uefL4JHTDPxC2fFiM9yrc72LmylJNkM/vepT128J5Qv0gRUaQbHiQuS6
239+Dm/+WRnUfx3i89SV4mnBxb/Ta93GVqoOciWwzWSnwEnWYAvOb95JL4U7c5J5f/+c
240+KnQDHsW7sIiIdscsWzvgf6qs2Ra1Zrt7Fdk4+ZS2f/adagLhDO1C24sXf5XfMk5m
241+L0OGwZSr9m5s17VXxfspgU5ugc8kBJfzABEBAAE=
242+=WCfs
243+-----END PGP PUBLIC KEY BLOCK-----
244+
245diff --git a/tests/data/keys/CVE-2021-3521-nosubsig.asc b/tests/data/keys/CVE-2021-3521-nosubsig.asc
246new file mode 100644
247index 0000000000..3a2e7417f8
248--- /dev/null
249+++ b/tests/data/keys/CVE-2021-3521-nosubsig.asc
250@@ -0,0 +1,37 @@
251+-----BEGIN PGP PUBLIC KEY BLOCK-----
252+Version: rpm-4.17.90 (NSS-3)
253+
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
284+E4XX4jtDmdZPreZALsiB
285+=rRop
286+-----END PGP PUBLIC KEY BLOCK-----
287+
288diff --git a/tests/rpmsigdig.at b/tests/rpmsigdig.at
289index 8e7c759b8f..e2d30a7f1b 100644
290--- a/tests/rpmsigdig.at
291+++ b/tests/rpmsigdig.at
292@@ -2,6 +2,34 @@
293
294 AT_BANNER([RPM signatures and digests])
295
296+AT_SETUP([rpmkeys --import invalid keys])
297+AT_KEYWORDS([rpmkeys import])
298+RPMDB_INIT
299+
300+AT_CHECK([
301+runroot rpmkeys --import /data/keys/CVE-2021-3521-badbind.asc
302+],
303+[1],
304+[],
305+[error: /data/keys/CVE-2021-3521-badbind.asc: key 1 import failed.]
306+)
307+AT_CHECK([
308+runroot rpmkeys --import /data/keys/CVE-2021-3521-nosubsig.asc
309+],
310+[1],
311+[],
312+[error: /data/keys/CVE-2021-3521-nosubsig.asc: key 1 import failed.]
313+)
314+
315+AT_CHECK([
316+runroot rpmkeys --import /data/keys/CVE-2021-3521-nosubsig-last.asc
317+],
318+[1],
319+[],
320+[error: /data/keys/CVE-2021-3521-nosubsig-last.asc: key 1 import failed.]
321+)
322+AT_CLEANUP
323+
324 # ------------------------------
325 # Test pre-built package verification
326 AT_SETUP([rpmkeys -Kv <unsigned> 1])
327--
3282.17.1
329