18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Validate the trust chain of a PKCS#7 message.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "PKCS7: "fmt
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/export.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/asn1.h>
148c2ecf20Sopenharmony_ci#include <linux/key.h>
158c2ecf20Sopenharmony_ci#include <keys/asymmetric-type.h>
168c2ecf20Sopenharmony_ci#include <crypto/public_key.h>
178c2ecf20Sopenharmony_ci#include "pkcs7_parser.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/**
208c2ecf20Sopenharmony_ci * Check the trust on one PKCS#7 SignedInfo block.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_cistatic int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
238c2ecf20Sopenharmony_ci				    struct pkcs7_signed_info *sinfo,
248c2ecf20Sopenharmony_ci				    struct key *trust_keyring)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct public_key_signature *sig = sinfo->sig;
278c2ecf20Sopenharmony_ci	struct x509_certificate *x509, *last = NULL, *p;
288c2ecf20Sopenharmony_ci	struct key *key;
298c2ecf20Sopenharmony_ci	int ret;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	kenter(",%u,", sinfo->index);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (sinfo->unsupported_crypto) {
348c2ecf20Sopenharmony_ci		kleave(" = -ENOPKG [cached]");
358c2ecf20Sopenharmony_ci		return -ENOPKG;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	for (x509 = sinfo->signer; x509; x509 = x509->signer) {
398c2ecf20Sopenharmony_ci		if (x509->seen) {
408c2ecf20Sopenharmony_ci			if (x509->verified)
418c2ecf20Sopenharmony_ci				goto verified;
428c2ecf20Sopenharmony_ci			kleave(" = -ENOKEY [cached]");
438c2ecf20Sopenharmony_ci			return -ENOKEY;
448c2ecf20Sopenharmony_ci		}
458c2ecf20Sopenharmony_ci		x509->seen = true;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci		/* Look to see if this certificate is present in the trusted
488c2ecf20Sopenharmony_ci		 * keys.
498c2ecf20Sopenharmony_ci		 */
508c2ecf20Sopenharmony_ci		key = find_asymmetric_key(trust_keyring,
518c2ecf20Sopenharmony_ci					  x509->id, x509->skid, false);
528c2ecf20Sopenharmony_ci		if (!IS_ERR(key)) {
538c2ecf20Sopenharmony_ci			/* One of the X.509 certificates in the PKCS#7 message
548c2ecf20Sopenharmony_ci			 * is apparently the same as one we already trust.
558c2ecf20Sopenharmony_ci			 * Verify that the trusted variant can also validate
568c2ecf20Sopenharmony_ci			 * the signature on the descendant.
578c2ecf20Sopenharmony_ci			 */
588c2ecf20Sopenharmony_ci			pr_devel("sinfo %u: Cert %u as key %x\n",
598c2ecf20Sopenharmony_ci				 sinfo->index, x509->index, key_serial(key));
608c2ecf20Sopenharmony_ci			goto matched;
618c2ecf20Sopenharmony_ci		}
628c2ecf20Sopenharmony_ci		if (key == ERR_PTR(-ENOMEM))
638c2ecf20Sopenharmony_ci			return -ENOMEM;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		 /* Self-signed certificates form roots of their own, and if we
668c2ecf20Sopenharmony_ci		  * don't know them, then we can't accept them.
678c2ecf20Sopenharmony_ci		  */
688c2ecf20Sopenharmony_ci		if (x509->signer == x509) {
698c2ecf20Sopenharmony_ci			kleave(" = -ENOKEY [unknown self-signed]");
708c2ecf20Sopenharmony_ci			return -ENOKEY;
718c2ecf20Sopenharmony_ci		}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		might_sleep();
748c2ecf20Sopenharmony_ci		last = x509;
758c2ecf20Sopenharmony_ci		sig = last->sig;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* No match - see if the root certificate has a signer amongst the
798c2ecf20Sopenharmony_ci	 * trusted keys.
808c2ecf20Sopenharmony_ci	 */
818c2ecf20Sopenharmony_ci	if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) {
828c2ecf20Sopenharmony_ci		key = find_asymmetric_key(trust_keyring,
838c2ecf20Sopenharmony_ci					  last->sig->auth_ids[0],
848c2ecf20Sopenharmony_ci					  last->sig->auth_ids[1],
858c2ecf20Sopenharmony_ci					  false);
868c2ecf20Sopenharmony_ci		if (!IS_ERR(key)) {
878c2ecf20Sopenharmony_ci			x509 = last;
888c2ecf20Sopenharmony_ci			pr_devel("sinfo %u: Root cert %u signer is key %x\n",
898c2ecf20Sopenharmony_ci				 sinfo->index, x509->index, key_serial(key));
908c2ecf20Sopenharmony_ci			goto matched;
918c2ecf20Sopenharmony_ci		}
928c2ecf20Sopenharmony_ci		if (PTR_ERR(key) != -ENOKEY)
938c2ecf20Sopenharmony_ci			return PTR_ERR(key);
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/* As a last resort, see if we have a trusted public key that matches
978c2ecf20Sopenharmony_ci	 * the signed info directly.
988c2ecf20Sopenharmony_ci	 */
998c2ecf20Sopenharmony_ci	key = find_asymmetric_key(trust_keyring,
1008c2ecf20Sopenharmony_ci				  sinfo->sig->auth_ids[0], NULL, false);
1018c2ecf20Sopenharmony_ci	if (!IS_ERR(key)) {
1028c2ecf20Sopenharmony_ci		pr_devel("sinfo %u: Direct signer is key %x\n",
1038c2ecf20Sopenharmony_ci			 sinfo->index, key_serial(key));
1048c2ecf20Sopenharmony_ci		x509 = NULL;
1058c2ecf20Sopenharmony_ci		sig = sinfo->sig;
1068c2ecf20Sopenharmony_ci		goto matched;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	if (PTR_ERR(key) != -ENOKEY)
1098c2ecf20Sopenharmony_ci		return PTR_ERR(key);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	kleave(" = -ENOKEY [no backref]");
1128c2ecf20Sopenharmony_ci	return -ENOKEY;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cimatched:
1158c2ecf20Sopenharmony_ci	ret = verify_signature(key, sig);
1168c2ecf20Sopenharmony_ci	key_put(key);
1178c2ecf20Sopenharmony_ci	if (ret < 0) {
1188c2ecf20Sopenharmony_ci		if (ret == -ENOMEM)
1198c2ecf20Sopenharmony_ci			return ret;
1208c2ecf20Sopenharmony_ci		kleave(" = -EKEYREJECTED [verify %d]", ret);
1218c2ecf20Sopenharmony_ci		return -EKEYREJECTED;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_civerified:
1258c2ecf20Sopenharmony_ci	if (x509) {
1268c2ecf20Sopenharmony_ci		x509->verified = true;
1278c2ecf20Sopenharmony_ci		for (p = sinfo->signer; p != x509; p = p->signer)
1288c2ecf20Sopenharmony_ci			p->verified = true;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci	kleave(" = 0");
1318c2ecf20Sopenharmony_ci	return 0;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/**
1358c2ecf20Sopenharmony_ci * pkcs7_validate_trust - Validate PKCS#7 trust chain
1368c2ecf20Sopenharmony_ci * @pkcs7: The PKCS#7 certificate to validate
1378c2ecf20Sopenharmony_ci * @trust_keyring: Signing certificates to use as starting points
1388c2ecf20Sopenharmony_ci *
1398c2ecf20Sopenharmony_ci * Validate that the certificate chain inside the PKCS#7 message intersects
1408c2ecf20Sopenharmony_ci * keys we already know and trust.
1418c2ecf20Sopenharmony_ci *
1428c2ecf20Sopenharmony_ci * Returns, in order of descending priority:
1438c2ecf20Sopenharmony_ci *
1448c2ecf20Sopenharmony_ci *  (*) -EKEYREJECTED if a signature failed to match for which we have a valid
1458c2ecf20Sopenharmony_ci *	key, or:
1468c2ecf20Sopenharmony_ci *
1478c2ecf20Sopenharmony_ci *  (*) 0 if at least one signature chain intersects with the keys in the trust
1488c2ecf20Sopenharmony_ci *	keyring, or:
1498c2ecf20Sopenharmony_ci *
1508c2ecf20Sopenharmony_ci *  (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
1518c2ecf20Sopenharmony_ci *	chain.
1528c2ecf20Sopenharmony_ci *
1538c2ecf20Sopenharmony_ci *  (*) -ENOKEY if we couldn't find a match for any of the signature chains in
1548c2ecf20Sopenharmony_ci *	the message.
1558c2ecf20Sopenharmony_ci *
1568c2ecf20Sopenharmony_ci * May also return -ENOMEM.
1578c2ecf20Sopenharmony_ci */
1588c2ecf20Sopenharmony_ciint pkcs7_validate_trust(struct pkcs7_message *pkcs7,
1598c2ecf20Sopenharmony_ci			 struct key *trust_keyring)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct pkcs7_signed_info *sinfo;
1628c2ecf20Sopenharmony_ci	struct x509_certificate *p;
1638c2ecf20Sopenharmony_ci	int cached_ret = -ENOKEY;
1648c2ecf20Sopenharmony_ci	int ret;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	for (p = pkcs7->certs; p; p = p->next)
1678c2ecf20Sopenharmony_ci		p->seen = false;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
1708c2ecf20Sopenharmony_ci		ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
1718c2ecf20Sopenharmony_ci		switch (ret) {
1728c2ecf20Sopenharmony_ci		case -ENOKEY:
1738c2ecf20Sopenharmony_ci			continue;
1748c2ecf20Sopenharmony_ci		case -ENOPKG:
1758c2ecf20Sopenharmony_ci			if (cached_ret == -ENOKEY)
1768c2ecf20Sopenharmony_ci				cached_ret = -ENOPKG;
1778c2ecf20Sopenharmony_ci			continue;
1788c2ecf20Sopenharmony_ci		case 0:
1798c2ecf20Sopenharmony_ci			cached_ret = 0;
1808c2ecf20Sopenharmony_ci			continue;
1818c2ecf20Sopenharmony_ci		default:
1828c2ecf20Sopenharmony_ci			return ret;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return cached_ret;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pkcs7_validate_trust);
189