162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Validate the trust chain of a PKCS#7 message.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) "PKCS7: "fmt
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/export.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/asn1.h>
1462306a36Sopenharmony_ci#include <linux/key.h>
1562306a36Sopenharmony_ci#include <keys/asymmetric-type.h>
1662306a36Sopenharmony_ci#include <crypto/public_key.h>
1762306a36Sopenharmony_ci#include "pkcs7_parser.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * Check the trust on one PKCS#7 SignedInfo block.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_cistatic int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
2362306a36Sopenharmony_ci				    struct pkcs7_signed_info *sinfo,
2462306a36Sopenharmony_ci				    struct key *trust_keyring)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct public_key_signature *sig = sinfo->sig;
2762306a36Sopenharmony_ci	struct x509_certificate *x509, *last = NULL, *p;
2862306a36Sopenharmony_ci	struct key *key;
2962306a36Sopenharmony_ci	int ret;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	kenter(",%u,", sinfo->index);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (sinfo->unsupported_crypto) {
3462306a36Sopenharmony_ci		kleave(" = -ENOPKG [cached]");
3562306a36Sopenharmony_ci		return -ENOPKG;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	for (x509 = sinfo->signer; x509; x509 = x509->signer) {
3962306a36Sopenharmony_ci		if (x509->seen) {
4062306a36Sopenharmony_ci			if (x509->verified)
4162306a36Sopenharmony_ci				goto verified;
4262306a36Sopenharmony_ci			kleave(" = -ENOKEY [cached]");
4362306a36Sopenharmony_ci			return -ENOKEY;
4462306a36Sopenharmony_ci		}
4562306a36Sopenharmony_ci		x509->seen = true;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		/* Look to see if this certificate is present in the trusted
4862306a36Sopenharmony_ci		 * keys.
4962306a36Sopenharmony_ci		 */
5062306a36Sopenharmony_ci		key = find_asymmetric_key(trust_keyring,
5162306a36Sopenharmony_ci					  x509->id, x509->skid, NULL, false);
5262306a36Sopenharmony_ci		if (!IS_ERR(key)) {
5362306a36Sopenharmony_ci			/* One of the X.509 certificates in the PKCS#7 message
5462306a36Sopenharmony_ci			 * is apparently the same as one we already trust.
5562306a36Sopenharmony_ci			 * Verify that the trusted variant can also validate
5662306a36Sopenharmony_ci			 * the signature on the descendant.
5762306a36Sopenharmony_ci			 */
5862306a36Sopenharmony_ci			pr_devel("sinfo %u: Cert %u as key %x\n",
5962306a36Sopenharmony_ci				 sinfo->index, x509->index, key_serial(key));
6062306a36Sopenharmony_ci			goto matched;
6162306a36Sopenharmony_ci		}
6262306a36Sopenharmony_ci		if (key == ERR_PTR(-ENOMEM))
6362306a36Sopenharmony_ci			return -ENOMEM;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		 /* Self-signed certificates form roots of their own, and if we
6662306a36Sopenharmony_ci		  * don't know them, then we can't accept them.
6762306a36Sopenharmony_ci		  */
6862306a36Sopenharmony_ci		if (x509->signer == x509) {
6962306a36Sopenharmony_ci			kleave(" = -ENOKEY [unknown self-signed]");
7062306a36Sopenharmony_ci			return -ENOKEY;
7162306a36Sopenharmony_ci		}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		might_sleep();
7462306a36Sopenharmony_ci		last = x509;
7562306a36Sopenharmony_ci		sig = last->sig;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* No match - see if the root certificate has a signer amongst the
7962306a36Sopenharmony_ci	 * trusted keys.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) {
8262306a36Sopenharmony_ci		key = find_asymmetric_key(trust_keyring,
8362306a36Sopenharmony_ci					  last->sig->auth_ids[0],
8462306a36Sopenharmony_ci					  last->sig->auth_ids[1],
8562306a36Sopenharmony_ci					  NULL, false);
8662306a36Sopenharmony_ci		if (!IS_ERR(key)) {
8762306a36Sopenharmony_ci			x509 = last;
8862306a36Sopenharmony_ci			pr_devel("sinfo %u: Root cert %u signer is key %x\n",
8962306a36Sopenharmony_ci				 sinfo->index, x509->index, key_serial(key));
9062306a36Sopenharmony_ci			goto matched;
9162306a36Sopenharmony_ci		}
9262306a36Sopenharmony_ci		if (PTR_ERR(key) != -ENOKEY)
9362306a36Sopenharmony_ci			return PTR_ERR(key);
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* As a last resort, see if we have a trusted public key that matches
9762306a36Sopenharmony_ci	 * the signed info directly.
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	key = find_asymmetric_key(trust_keyring,
10062306a36Sopenharmony_ci				  sinfo->sig->auth_ids[0], NULL, NULL, false);
10162306a36Sopenharmony_ci	if (!IS_ERR(key)) {
10262306a36Sopenharmony_ci		pr_devel("sinfo %u: Direct signer is key %x\n",
10362306a36Sopenharmony_ci			 sinfo->index, key_serial(key));
10462306a36Sopenharmony_ci		x509 = NULL;
10562306a36Sopenharmony_ci		sig = sinfo->sig;
10662306a36Sopenharmony_ci		goto matched;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci	if (PTR_ERR(key) != -ENOKEY)
10962306a36Sopenharmony_ci		return PTR_ERR(key);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	kleave(" = -ENOKEY [no backref]");
11262306a36Sopenharmony_ci	return -ENOKEY;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cimatched:
11562306a36Sopenharmony_ci	ret = verify_signature(key, sig);
11662306a36Sopenharmony_ci	key_put(key);
11762306a36Sopenharmony_ci	if (ret < 0) {
11862306a36Sopenharmony_ci		if (ret == -ENOMEM)
11962306a36Sopenharmony_ci			return ret;
12062306a36Sopenharmony_ci		kleave(" = -EKEYREJECTED [verify %d]", ret);
12162306a36Sopenharmony_ci		return -EKEYREJECTED;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_civerified:
12562306a36Sopenharmony_ci	if (x509) {
12662306a36Sopenharmony_ci		x509->verified = true;
12762306a36Sopenharmony_ci		for (p = sinfo->signer; p != x509; p = p->signer)
12862306a36Sopenharmony_ci			p->verified = true;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci	kleave(" = 0");
13162306a36Sopenharmony_ci	return 0;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/**
13562306a36Sopenharmony_ci * pkcs7_validate_trust - Validate PKCS#7 trust chain
13662306a36Sopenharmony_ci * @pkcs7: The PKCS#7 certificate to validate
13762306a36Sopenharmony_ci * @trust_keyring: Signing certificates to use as starting points
13862306a36Sopenharmony_ci *
13962306a36Sopenharmony_ci * Validate that the certificate chain inside the PKCS#7 message intersects
14062306a36Sopenharmony_ci * keys we already know and trust.
14162306a36Sopenharmony_ci *
14262306a36Sopenharmony_ci * Returns, in order of descending priority:
14362306a36Sopenharmony_ci *
14462306a36Sopenharmony_ci *  (*) -EKEYREJECTED if a signature failed to match for which we have a valid
14562306a36Sopenharmony_ci *	key, or:
14662306a36Sopenharmony_ci *
14762306a36Sopenharmony_ci *  (*) 0 if at least one signature chain intersects with the keys in the trust
14862306a36Sopenharmony_ci *	keyring, or:
14962306a36Sopenharmony_ci *
15062306a36Sopenharmony_ci *  (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
15162306a36Sopenharmony_ci *	chain.
15262306a36Sopenharmony_ci *
15362306a36Sopenharmony_ci *  (*) -ENOKEY if we couldn't find a match for any of the signature chains in
15462306a36Sopenharmony_ci *	the message.
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci * May also return -ENOMEM.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_ciint pkcs7_validate_trust(struct pkcs7_message *pkcs7,
15962306a36Sopenharmony_ci			 struct key *trust_keyring)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct pkcs7_signed_info *sinfo;
16262306a36Sopenharmony_ci	struct x509_certificate *p;
16362306a36Sopenharmony_ci	int cached_ret = -ENOKEY;
16462306a36Sopenharmony_ci	int ret;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	for (p = pkcs7->certs; p; p = p->next)
16762306a36Sopenharmony_ci		p->seen = false;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
17062306a36Sopenharmony_ci		ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
17162306a36Sopenharmony_ci		switch (ret) {
17262306a36Sopenharmony_ci		case -ENOKEY:
17362306a36Sopenharmony_ci			continue;
17462306a36Sopenharmony_ci		case -ENOPKG:
17562306a36Sopenharmony_ci			if (cached_ret == -ENOKEY)
17662306a36Sopenharmony_ci				cached_ret = -ENOPKG;
17762306a36Sopenharmony_ci			continue;
17862306a36Sopenharmony_ci		case 0:
17962306a36Sopenharmony_ci			cached_ret = 0;
18062306a36Sopenharmony_ci			continue;
18162306a36Sopenharmony_ci		default:
18262306a36Sopenharmony_ci			return ret;
18362306a36Sopenharmony_ci		}
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return cached_ret;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pkcs7_validate_trust);
189