summary refs log tree commit diff
path: root/crypto/asymmetric_keys/restrict.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/asymmetric_keys/restrict.c')
-rw-r--r--crypto/asymmetric_keys/restrict.c158
1 files changed, 120 insertions, 38 deletions
diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c
index 183cb642580e..86fb68508952 100644
--- a/crypto/asymmetric_keys/restrict.c
+++ b/crypto/asymmetric_keys/restrict.c
@@ -109,31 +109,20 @@ int restrict_link_by_signature(struct key *dest_keyring,
 	return ret;
 }
 
-/**
- * restrict_link_by_key_or_keyring - Restrict additions to a ring of public
- * keys using the restrict_key information stored in the ring.
- * @dest_keyring: Keyring being linked to.
- * @type: The type of key being added.
- * @payload: The payload of the new key.
- * @trusted: A key or ring of keys that can be used to vouch for the new cert.
- *
- * Check the new certificate only against the key or keys passed in the data
- * parameter. If one of those is the signing key and validates the new
- * certificate, then mark the new certificate as being ok to link.
- *
- * Returns 0 if the new certificate was accepted, -ENOKEY if we
- * couldn't find a matching parent certificate in the trusted list,
- * -EKEYREJECTED if the signature check fails, and some other error if
- * there is a matching certificate but the signature check cannot be
- * performed.
- */
-int restrict_link_by_key_or_keyring(struct key *dest_keyring,
-				    const struct key_type *type,
-				    const union key_payload *payload,
-				    struct key *trusted)
+static bool match_either_id(const struct asymmetric_key_ids *pair,
+			    const struct asymmetric_key_id *single)
+{
+	return (asymmetric_key_id_same(pair->id[0], single) ||
+		asymmetric_key_id_same(pair->id[1], single));
+}
+
+static int key_or_keyring_common(struct key *dest_keyring,
+				 const struct key_type *type,
+				 const union key_payload *payload,
+				 struct key *trusted, bool check_dest)
 {
 	const struct public_key_signature *sig;
-	struct key *key;
+	struct key *key = NULL;
 	int ret;
 
 	pr_devel("==>%s()\n", __func__);
@@ -143,7 +132,7 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring,
 	else if (dest_keyring->type != &key_type_keyring)
 		return -EOPNOTSUPP;
 
-	if (!trusted)
+	if (!trusted && !check_dest)
 		return -ENOKEY;
 
 	if (type != &key_type_asymmetric)
@@ -153,25 +142,64 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring,
 	if (!sig->auth_ids[0] && !sig->auth_ids[1])
 		return -ENOKEY;
 
-	if (trusted->type == &key_type_keyring) {
-		/* See if we have a key that signed this one. */
-		key = find_asymmetric_key(trusted, sig->auth_ids[0],
-					  sig->auth_ids[1], false);
-		if (IS_ERR(key))
-			return -ENOKEY;
-	} else if (trusted->type == &key_type_asymmetric) {
-		const struct asymmetric_key_ids *kids;
+	if (trusted) {
+		if (trusted->type == &key_type_keyring) {
+			/* See if we have a key that signed this one. */
+			key = find_asymmetric_key(trusted, sig->auth_ids[0],
+						  sig->auth_ids[1], false);
+			if (IS_ERR(key))
+				key = NULL;
+		} else if (trusted->type == &key_type_asymmetric) {
+			const struct asymmetric_key_ids *signer_ids;
 
-		kids = asymmetric_key_ids(trusted);
+			signer_ids = asymmetric_key_ids(trusted);
 
-		if (!asymmetric_key_id_same(kids->id[1], sig->auth_ids[0]))
-			return -ENOKEY;
+			/*
+			 * The auth_ids come from the candidate key (the
+			 * one that is being considered for addition to
+			 * dest_keyring) and identify the key that was
+			 * used to sign.
+			 *
+			 * The signer_ids are identifiers for the
+			 * signing key specified for dest_keyring.
+			 *
+			 * The first auth_id is the preferred id, and
+			 * the second is the fallback. If only one
+			 * auth_id is present, it may match against
+			 * either signer_id. If two auth_ids are
+			 * present, the first auth_id must match one
+			 * signer_id and the second auth_id must match
+			 * the second signer_id.
+			 */
+			if (!sig->auth_ids[0] || !sig->auth_ids[1]) {
+				const struct asymmetric_key_id *auth_id;
 
-		key = __key_get(trusted);
-	} else {
-		return -EOPNOTSUPP;
+				auth_id = sig->auth_ids[0] ?: sig->auth_ids[1];
+				if (match_either_id(signer_ids, auth_id))
+					key = __key_get(trusted);
+
+			} else if (asymmetric_key_id_same(signer_ids->id[1],
+							  sig->auth_ids[1]) &&
+				   match_either_id(signer_ids,
+						   sig->auth_ids[0])) {
+				key = __key_get(trusted);
+			}
+		} else {
+			return -EOPNOTSUPP;
+		}
 	}
 
+	if (check_dest && !key) {
+		/* See if the destination has a key that signed this one. */
+		key = find_asymmetric_key(dest_keyring, sig->auth_ids[0],
+					  sig->auth_ids[1], false);
+		if (IS_ERR(key))
+			key = NULL;
+	}
+
+	if (!key)
+		return -ENOKEY;
+
 	ret = key_validate(key);
 	if (ret == 0)
 		ret = verify_signature(key, sig);
@@ -179,3 +207,57 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring,
 	key_put(key);
 	return ret;
 }
+
+/**
+ * restrict_link_by_key_or_keyring - Restrict additions to a ring of public
+ * keys using the restrict_key information stored in the ring.
+ * @dest_keyring: Keyring being linked to.
+ * @type: The type of key being added.
+ * @payload: The payload of the new key.
+ * @trusted: A key or ring of keys that can be used to vouch for the new cert.
+ *
+ * Check the new certificate only against the key or keys passed in the data
+ * parameter. If one of those is the signing key and validates the new
+ * certificate, then mark the new certificate as being ok to link.
+ *
+ * Returns 0 if the new certificate was accepted, -ENOKEY if we
+ * couldn't find a matching parent certificate in the trusted list,
+ * -EKEYREJECTED if the signature check fails, and some other error if
+ * there is a matching certificate but the signature check cannot be
+ * performed.
+ */
+int restrict_link_by_key_or_keyring(struct key *dest_keyring,
+				    const struct key_type *type,
+				    const union key_payload *payload,
+				    struct key *trusted)
+{
+	return key_or_keyring_common(dest_keyring, type, payload, trusted,
+				     false);
+}
+
+/**
+ * restrict_link_by_key_or_keyring_chain - Restrict additions to a ring of
+ * public keys using the restrict_key information stored in the ring.
+ * @dest_keyring: Keyring being linked to.
+ * @type: The type of key being added.
+ * @payload: The payload of the new key.
+ * @trusted: A key or ring of keys that can be used to vouch for the new cert.
+ *
+ * Check the new certificate only against the key or keys passed in the data
+ * parameter. If one of those is the signing key and validates the new
+ * certificate, then mark the new certificate as being ok to link.
+ *
+ * Returns 0 if the new certificate was accepted, -ENOKEY if we
+ * couldn't find a matching parent certificate in the trusted list,
+ * -EKEYREJECTED if the signature check fails, and some other error if
+ * there is a matching certificate but the signature check cannot be
+ * performed.
+ */
+int restrict_link_by_key_or_keyring_chain(struct key *dest_keyring,
+					  const struct key_type *type,
+					  const union key_payload *payload,
+					  struct key *trusted)
+{
+	return key_or_keyring_common(dest_keyring, type, payload, trusted,
+				     true);
+}