1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
208815b62SDavid Howells /* Validate the trust chain of a PKCS#7 message.
308815b62SDavid Howells  *
408815b62SDavid Howells  * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
508815b62SDavid Howells  * Written by David Howells (dhowells@redhat.com)
608815b62SDavid Howells  */
708815b62SDavid Howells 
808815b62SDavid Howells #define pr_fmt(fmt) "PKCS7: "fmt
908815b62SDavid Howells #include <linux/kernel.h>
1008815b62SDavid Howells #include <linux/export.h>
1108815b62SDavid Howells #include <linux/slab.h>
1208815b62SDavid Howells #include <linux/err.h>
1308815b62SDavid Howells #include <linux/asn1.h>
1408815b62SDavid Howells #include <linux/key.h>
1508815b62SDavid Howells #include <keys/asymmetric-type.h>
16db6c43bdSTadeusz Struk #include <crypto/public_key.h>
1708815b62SDavid Howells #include "pkcs7_parser.h"
1808815b62SDavid Howells 
19d13fc874SAlex Shi /*
2008815b62SDavid Howells  * Check the trust on one PKCS#7 SignedInfo block.
2108815b62SDavid Howells  */
pkcs7_validate_trust_one(struct pkcs7_message * pkcs7,struct pkcs7_signed_info * sinfo,struct key * trust_keyring)2215155b9aSDavid Howells static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
2308815b62SDavid Howells 				    struct pkcs7_signed_info *sinfo,
2408815b62SDavid Howells 				    struct key *trust_keyring)
2508815b62SDavid Howells {
26566a117aSDavid Howells 	struct public_key_signature *sig = sinfo->sig;
2708815b62SDavid Howells 	struct x509_certificate *x509, *last = NULL, *p;
2808815b62SDavid Howells 	struct key *key;
2908815b62SDavid Howells 	int ret;
3008815b62SDavid Howells 
3108815b62SDavid Howells 	kenter(",%u,", sinfo->index);
3208815b62SDavid Howells 
3341559420SDavid Howells 	if (sinfo->unsupported_crypto) {
3441559420SDavid Howells 		kleave(" = -ENOPKG [cached]");
3541559420SDavid Howells 		return -ENOPKG;
3641559420SDavid Howells 	}
3741559420SDavid Howells 
3808815b62SDavid Howells 	for (x509 = sinfo->signer; x509; x509 = x509->signer) {
3908815b62SDavid Howells 		if (x509->seen) {
40bda850cdSDavid Howells 			if (x509->verified)
4108815b62SDavid Howells 				goto verified;
4208815b62SDavid Howells 			kleave(" = -ENOKEY [cached]");
4308815b62SDavid Howells 			return -ENOKEY;
4408815b62SDavid Howells 		}
4508815b62SDavid Howells 		x509->seen = true;
4608815b62SDavid Howells 
4708815b62SDavid Howells 		/* Look to see if this certificate is present in the trusted
4808815b62SDavid Howells 		 * keys.
4908815b62SDavid Howells 		 */
509eb02989SDavid Howells 		key = find_asymmetric_key(trust_keyring,
51*7d30198eSAndrew Zaborowski 					  x509->id, x509->skid, NULL, false);
52757932e6SDavid Howells 		if (!IS_ERR(key)) {
5308815b62SDavid Howells 			/* One of the X.509 certificates in the PKCS#7 message
5408815b62SDavid Howells 			 * is apparently the same as one we already trust.
5508815b62SDavid Howells 			 * Verify that the trusted variant can also validate
5608815b62SDavid Howells 			 * the signature on the descendant.
5708815b62SDavid Howells 			 */
58757932e6SDavid Howells 			pr_devel("sinfo %u: Cert %u as key %x\n",
59757932e6SDavid Howells 				 sinfo->index, x509->index, key_serial(key));
6008815b62SDavid Howells 			goto matched;
61757932e6SDavid Howells 		}
6208815b62SDavid Howells 		if (key == ERR_PTR(-ENOMEM))
6308815b62SDavid Howells 			return -ENOMEM;
6408815b62SDavid Howells 
6508815b62SDavid Howells 		 /* Self-signed certificates form roots of their own, and if we
6608815b62SDavid Howells 		  * don't know them, then we can't accept them.
6708815b62SDavid Howells 		  */
687204eb85SEric Biggers 		if (x509->signer == x509) {
6908815b62SDavid Howells 			kleave(" = -ENOKEY [unknown self-signed]");
7008815b62SDavid Howells 			return -ENOKEY;
7108815b62SDavid Howells 		}
7208815b62SDavid Howells 
7308815b62SDavid Howells 		might_sleep();
7408815b62SDavid Howells 		last = x509;
7577d0910dSDavid Howells 		sig = last->sig;
7608815b62SDavid Howells 	}
7708815b62SDavid Howells 
7808815b62SDavid Howells 	/* No match - see if the root certificate has a signer amongst the
7908815b62SDavid Howells 	 * trusted keys.
8008815b62SDavid Howells 	 */
8177d0910dSDavid Howells 	if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) {
829eb02989SDavid Howells 		key = find_asymmetric_key(trust_keyring,
8377d0910dSDavid Howells 					  last->sig->auth_ids[0],
8477d0910dSDavid Howells 					  last->sig->auth_ids[1],
85*7d30198eSAndrew Zaborowski 					  NULL, false);
86757932e6SDavid Howells 		if (!IS_ERR(key)) {
87757932e6SDavid Howells 			x509 = last;
88757932e6SDavid Howells 			pr_devel("sinfo %u: Root cert %u signer is key %x\n",
89757932e6SDavid Howells 				 sinfo->index, x509->index, key_serial(key));
90757932e6SDavid Howells 			goto matched;
91757932e6SDavid Howells 		}
92757932e6SDavid Howells 		if (PTR_ERR(key) != -ENOKEY)
93757932e6SDavid Howells 			return PTR_ERR(key);
9408815b62SDavid Howells 	}
9508815b62SDavid Howells 
96757932e6SDavid Howells 	/* As a last resort, see if we have a trusted public key that matches
97757932e6SDavid Howells 	 * the signed info directly.
98757932e6SDavid Howells 	 */
999eb02989SDavid Howells 	key = find_asymmetric_key(trust_keyring,
100*7d30198eSAndrew Zaborowski 				  sinfo->sig->auth_ids[0], NULL, NULL, false);
101757932e6SDavid Howells 	if (!IS_ERR(key)) {
102757932e6SDavid Howells 		pr_devel("sinfo %u: Direct signer is key %x\n",
103757932e6SDavid Howells 			 sinfo->index, key_serial(key));
104757932e6SDavid Howells 		x509 = NULL;
1056459ae38SEric Biggers 		sig = sinfo->sig;
106757932e6SDavid Howells 		goto matched;
107757932e6SDavid Howells 	}
108757932e6SDavid Howells 	if (PTR_ERR(key) != -ENOKEY)
109757932e6SDavid Howells 		return PTR_ERR(key);
110757932e6SDavid Howells 
111757932e6SDavid Howells 	kleave(" = -ENOKEY [no backref]");
112757932e6SDavid Howells 	return -ENOKEY;
11308815b62SDavid Howells 
11408815b62SDavid Howells matched:
11508815b62SDavid Howells 	ret = verify_signature(key, sig);
11608815b62SDavid Howells 	key_put(key);
11708815b62SDavid Howells 	if (ret < 0) {
11808815b62SDavid Howells 		if (ret == -ENOMEM)
11908815b62SDavid Howells 			return ret;
12008815b62SDavid Howells 		kleave(" = -EKEYREJECTED [verify %d]", ret);
12108815b62SDavid Howells 		return -EKEYREJECTED;
12208815b62SDavid Howells 	}
12308815b62SDavid Howells 
12408815b62SDavid Howells verified:
125757932e6SDavid Howells 	if (x509) {
12608815b62SDavid Howells 		x509->verified = true;
127bda850cdSDavid Howells 		for (p = sinfo->signer; p != x509; p = p->signer)
12808815b62SDavid Howells 			p->verified = true;
12908815b62SDavid Howells 	}
13008815b62SDavid Howells 	kleave(" = 0");
13108815b62SDavid Howells 	return 0;
13208815b62SDavid Howells }
13308815b62SDavid Howells 
13408815b62SDavid Howells /**
13508815b62SDavid Howells  * pkcs7_validate_trust - Validate PKCS#7 trust chain
13608815b62SDavid Howells  * @pkcs7: The PKCS#7 certificate to validate
13708815b62SDavid Howells  * @trust_keyring: Signing certificates to use as starting points
13808815b62SDavid Howells  *
13908815b62SDavid Howells  * Validate that the certificate chain inside the PKCS#7 message intersects
14008815b62SDavid Howells  * keys we already know and trust.
14108815b62SDavid Howells  *
14208815b62SDavid Howells  * Returns, in order of descending priority:
14308815b62SDavid Howells  *
14408815b62SDavid Howells  *  (*) -EKEYREJECTED if a signature failed to match for which we have a valid
14508815b62SDavid Howells  *	key, or:
14608815b62SDavid Howells  *
14708815b62SDavid Howells  *  (*) 0 if at least one signature chain intersects with the keys in the trust
14808815b62SDavid Howells  *	keyring, or:
14908815b62SDavid Howells  *
15008815b62SDavid Howells  *  (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
15108815b62SDavid Howells  *	chain.
15208815b62SDavid Howells  *
15308815b62SDavid Howells  *  (*) -ENOKEY if we couldn't find a match for any of the signature chains in
15408815b62SDavid Howells  *	the message.
15508815b62SDavid Howells  *
15608815b62SDavid Howells  * May also return -ENOMEM.
15708815b62SDavid Howells  */
pkcs7_validate_trust(struct pkcs7_message * pkcs7,struct key * trust_keyring)15808815b62SDavid Howells int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
159bda850cdSDavid Howells 			 struct key *trust_keyring)
16008815b62SDavid Howells {
16108815b62SDavid Howells 	struct pkcs7_signed_info *sinfo;
16208815b62SDavid Howells 	struct x509_certificate *p;
16341559420SDavid Howells 	int cached_ret = -ENOKEY;
16441559420SDavid Howells 	int ret;
16508815b62SDavid Howells 
16608815b62SDavid Howells 	for (p = pkcs7->certs; p; p = p->next)
16708815b62SDavid Howells 		p->seen = false;
16808815b62SDavid Howells 
16908815b62SDavid Howells 	for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
17008815b62SDavid Howells 		ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
17141559420SDavid Howells 		switch (ret) {
17241559420SDavid Howells 		case -ENOKEY:
17341559420SDavid Howells 			continue;
17441559420SDavid Howells 		case -ENOPKG:
17541559420SDavid Howells 			if (cached_ret == -ENOKEY)
17608815b62SDavid Howells 				cached_ret = -ENOPKG;
17741559420SDavid Howells 			continue;
17841559420SDavid Howells 		case 0:
17941559420SDavid Howells 			cached_ret = 0;
18041559420SDavid Howells 			continue;
18141559420SDavid Howells 		default:
18208815b62SDavid Howells 			return ret;
18308815b62SDavid Howells 		}
18408815b62SDavid Howells 	}
18508815b62SDavid Howells 
18608815b62SDavid Howells 	return cached_ret;
18708815b62SDavid Howells }
18808815b62SDavid Howells EXPORT_SYMBOL_GPL(pkcs7_validate_trust);
189