ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/crypto/asymmetric_keys/pkcs7_trust.c b/marvell/linux/crypto/asymmetric_keys/pkcs7_trust.c
new file mode 100644
index 0000000..61af3c4
--- /dev/null
+++ b/marvell/linux/crypto/asymmetric_keys/pkcs7_trust.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Validate the trust chain of a PKCS#7 message.
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PKCS7: "fmt
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/asn1.h>
+#include <linux/key.h>
+#include <keys/asymmetric-type.h>
+#include <crypto/public_key.h>
+#include "pkcs7_parser.h"
+
+/**
+ * Check the trust on one PKCS#7 SignedInfo block.
+ */
+static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
+				    struct pkcs7_signed_info *sinfo,
+				    struct key *trust_keyring)
+{
+	struct public_key_signature *sig = sinfo->sig;
+	struct x509_certificate *x509, *last = NULL, *p;
+	struct key *key;
+	int ret;
+
+	kenter(",%u,", sinfo->index);
+
+	if (sinfo->unsupported_crypto) {
+		kleave(" = -ENOPKG [cached]");
+		return -ENOPKG;
+	}
+
+	for (x509 = sinfo->signer; x509; x509 = x509->signer) {
+		if (x509->seen) {
+			if (x509->verified)
+				goto verified;
+			kleave(" = -ENOKEY [cached]");
+			return -ENOKEY;
+		}
+		x509->seen = true;
+
+		/* Look to see if this certificate is present in the trusted
+		 * keys.
+		 */
+		key = find_asymmetric_key(trust_keyring,
+					  x509->id, x509->skid, false);
+		if (!IS_ERR(key)) {
+			/* One of the X.509 certificates in the PKCS#7 message
+			 * is apparently the same as one we already trust.
+			 * Verify that the trusted variant can also validate
+			 * the signature on the descendant.
+			 */
+			pr_devel("sinfo %u: Cert %u as key %x\n",
+				 sinfo->index, x509->index, key_serial(key));
+			goto matched;
+		}
+		if (key == ERR_PTR(-ENOMEM))
+			return -ENOMEM;
+
+		 /* Self-signed certificates form roots of their own, and if we
+		  * don't know them, then we can't accept them.
+		  */
+		if (x509->signer == x509) {
+			kleave(" = -ENOKEY [unknown self-signed]");
+			return -ENOKEY;
+		}
+
+		might_sleep();
+		last = x509;
+		sig = last->sig;
+	}
+
+	/* No match - see if the root certificate has a signer amongst the
+	 * trusted keys.
+	 */
+	if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) {
+		key = find_asymmetric_key(trust_keyring,
+					  last->sig->auth_ids[0],
+					  last->sig->auth_ids[1],
+					  false);
+		if (!IS_ERR(key)) {
+			x509 = last;
+			pr_devel("sinfo %u: Root cert %u signer is key %x\n",
+				 sinfo->index, x509->index, key_serial(key));
+			goto matched;
+		}
+		if (PTR_ERR(key) != -ENOKEY)
+			return PTR_ERR(key);
+	}
+
+	/* As a last resort, see if we have a trusted public key that matches
+	 * the signed info directly.
+	 */
+	key = find_asymmetric_key(trust_keyring,
+				  sinfo->sig->auth_ids[0], NULL, false);
+	if (!IS_ERR(key)) {
+		pr_devel("sinfo %u: Direct signer is key %x\n",
+			 sinfo->index, key_serial(key));
+		x509 = NULL;
+		sig = sinfo->sig;
+		goto matched;
+	}
+	if (PTR_ERR(key) != -ENOKEY)
+		return PTR_ERR(key);
+
+	kleave(" = -ENOKEY [no backref]");
+	return -ENOKEY;
+
+matched:
+	ret = verify_signature(key, sig);
+	key_put(key);
+	if (ret < 0) {
+		if (ret == -ENOMEM)
+			return ret;
+		kleave(" = -EKEYREJECTED [verify %d]", ret);
+		return -EKEYREJECTED;
+	}
+
+verified:
+	if (x509) {
+		x509->verified = true;
+		for (p = sinfo->signer; p != x509; p = p->signer)
+			p->verified = true;
+	}
+	kleave(" = 0");
+	return 0;
+}
+
+/**
+ * pkcs7_validate_trust - Validate PKCS#7 trust chain
+ * @pkcs7: The PKCS#7 certificate to validate
+ * @trust_keyring: Signing certificates to use as starting points
+ *
+ * Validate that the certificate chain inside the PKCS#7 message intersects
+ * keys we already know and trust.
+ *
+ * Returns, in order of descending priority:
+ *
+ *  (*) -EKEYREJECTED if a signature failed to match for which we have a valid
+ *	key, or:
+ *
+ *  (*) 0 if at least one signature chain intersects with the keys in the trust
+ *	keyring, or:
+ *
+ *  (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
+ *	chain.
+ *
+ *  (*) -ENOKEY if we couldn't find a match for any of the signature chains in
+ *	the message.
+ *
+ * May also return -ENOMEM.
+ */
+int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
+			 struct key *trust_keyring)
+{
+	struct pkcs7_signed_info *sinfo;
+	struct x509_certificate *p;
+	int cached_ret = -ENOKEY;
+	int ret;
+
+	for (p = pkcs7->certs; p; p = p->next)
+		p->seen = false;
+
+	for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
+		ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
+		switch (ret) {
+		case -ENOKEY:
+			continue;
+		case -ENOPKG:
+			if (cached_ret == -ENOKEY)
+				cached_ret = -ENOPKG;
+			continue;
+		case 0:
+			cached_ret = 0;
+			continue;
+		default:
+			return ret;
+		}
+	}
+
+	return cached_ret;
+}
+EXPORT_SYMBOL_GPL(pkcs7_validate_trust);