diff options
Diffstat (limited to 'crypto/asymmetric_keys/restrict.c')
| -rw-r--r-- | crypto/asymmetric_keys/restrict.c | 156 |
1 files changed, 127 insertions, 29 deletions
diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 7c93c7728454..86292965f493 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* Instantiate a public key crypto key from an X.509 Certificate * * Copyright (C) 2012, 2016 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. */ #define pr_fmt(fmt) "ASYM: "fmt @@ -21,9 +17,12 @@ static struct asymmetric_key_id *ca_keyid; #ifndef MODULE static struct { - struct asymmetric_key_id id; - unsigned char data[10]; + /* Must be last as it ends in a flexible-array member. */ + TRAILING_OVERLAP(struct asymmetric_key_id, id, data, + unsigned char data[10]; + ); } cakey; +static_assert(offsetof(typeof(cakey), id.data) == offsetof(typeof(cakey), data)); static int __init ca_keys_setup(char *str) { @@ -91,7 +90,7 @@ int restrict_link_by_signature(struct key *dest_keyring, sig = payload->data[asym_auth]; if (!sig) return -ENOPKG; - if (!sig->auth_ids[0] && !sig->auth_ids[1]) + if (!sig->auth_ids[0] && !sig->auth_ids[1] && !sig->auth_ids[2]) return -ENOKEY; if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid)) @@ -100,23 +99,111 @@ int restrict_link_by_signature(struct key *dest_keyring, /* See if we have a key that signed this one. */ key = find_asymmetric_key(trust_keyring, sig->auth_ids[0], sig->auth_ids[1], - false); + sig->auth_ids[2], false); if (IS_ERR(key)) return -ENOKEY; if (use_builtin_keys && !test_bit(KEY_FLAG_BUILTIN, &key->flags)) ret = -ENOKEY; + else if (IS_BUILTIN(CONFIG_SECONDARY_TRUSTED_KEYRING_SIGNED_BY_BUILTIN) && + !strcmp(dest_keyring->description, ".secondary_trusted_keys") && + !test_bit(KEY_FLAG_BUILTIN, &key->flags)) + ret = -ENOKEY; else ret = verify_signature(key, sig); key_put(key); return ret; } -static bool match_either_id(const struct asymmetric_key_ids *pair, +/** + * restrict_link_by_ca - Restrict additions to a ring of CA keys + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @trust_keyring: Unused. + * + * Check if the new certificate is a CA. If it is a CA, then mark the new + * certificate as being ok to link. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if the + * certificate is not a CA. -ENOPKG if the signature uses unsupported + * crypto, or some other error if there is a matching certificate but + * the signature check cannot be performed. + */ +int restrict_link_by_ca(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trust_keyring) +{ + const struct public_key *pkey; + + if (type != &key_type_asymmetric) + return -EOPNOTSUPP; + + pkey = payload->data[asym_crypto]; + if (!pkey) + return -ENOPKG; + if (!test_bit(KEY_EFLAG_CA, &pkey->key_eflags)) + return -ENOKEY; + if (!test_bit(KEY_EFLAG_KEYCERTSIGN, &pkey->key_eflags)) + return -ENOKEY; + if (!IS_ENABLED(CONFIG_INTEGRITY_CA_MACHINE_KEYRING_MAX)) + return 0; + if (test_bit(KEY_EFLAG_DIGITALSIG, &pkey->key_eflags)) + return -ENOKEY; + + return 0; +} + +/** + * restrict_link_by_digsig - Restrict additions to a ring of digsig keys + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @trust_keyring: A ring of keys that can be used to vouch for the new cert. + * + * Check if the new certificate has digitalSignature usage set. If it is, + * then mark the new certificate as being ok to link. Afterwards verify + * the new certificate against the ones in the trust_keyring. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if the + * certificate is not a digsig. -ENOPKG if the signature uses unsupported + * crypto, or some other error if there is a matching certificate but + * the signature check cannot be performed. + */ +int restrict_link_by_digsig(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trust_keyring) +{ + const struct public_key *pkey; + + if (type != &key_type_asymmetric) + return -EOPNOTSUPP; + + pkey = payload->data[asym_crypto]; + + if (!pkey) + return -ENOPKG; + + if (!test_bit(KEY_EFLAG_DIGITALSIG, &pkey->key_eflags)) + return -ENOKEY; + + if (test_bit(KEY_EFLAG_CA, &pkey->key_eflags)) + return -ENOKEY; + + if (test_bit(KEY_EFLAG_KEYCERTSIGN, &pkey->key_eflags)) + return -ENOKEY; + + return restrict_link_by_signature(dest_keyring, type, payload, + trust_keyring); +} + +static bool match_either_id(const struct asymmetric_key_id **pair, const struct asymmetric_key_id *single) { - return (asymmetric_key_id_same(pair->id[0], single) || - asymmetric_key_id_same(pair->id[1], single)); + return (asymmetric_key_id_same(pair[0], single) || + asymmetric_key_id_same(pair[1], single)); } static int key_or_keyring_common(struct key *dest_keyring, @@ -144,20 +231,22 @@ static int key_or_keyring_common(struct key *dest_keyring, sig = payload->data[asym_auth]; if (!sig) return -ENOPKG; - if (!sig->auth_ids[0] && !sig->auth_ids[1]) + if (!sig->auth_ids[0] && !sig->auth_ids[1] && !sig->auth_ids[2]) return -ENOKEY; 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); + sig->auth_ids[1], + sig->auth_ids[2], false); if (IS_ERR(key)) key = NULL; } else if (trusted->type == &key_type_asymmetric) { - const struct asymmetric_key_ids *signer_ids; + const struct asymmetric_key_id **signer_ids; - signer_ids = asymmetric_key_ids(trusted); + signer_ids = (const struct asymmetric_key_id **) + asymmetric_key_ids(trusted)->id; /* * The auth_ids come from the candidate key (the @@ -168,22 +257,29 @@ static int key_or_keyring_common(struct key *dest_keyring, * 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. + * The first auth_id is the preferred id, 2nd and + * 3rd are the fallbacks. If exactly one of + * auth_ids[0] and auth_ids[1] is present, it may + * match either signer_ids[0] or signed_ids[1]. + * If both are present the first one may match + * either signed_id but the second one must match + * the second signer_id. If neither of them is + * available, auth_ids[2] is matched against + * signer_ids[2] as a fallback. */ - if (!sig->auth_ids[0] || !sig->auth_ids[1]) { + if (!sig->auth_ids[0] && !sig->auth_ids[1]) { + if (asymmetric_key_id_same(signer_ids[2], + sig->auth_ids[2])) + key = __key_get(trusted); + + } else if (!sig->auth_ids[0] || !sig->auth_ids[1]) { const struct asymmetric_key_id *auth_id; 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], + } else if (asymmetric_key_id_same(signer_ids[1], sig->auth_ids[1]) && match_either_id(signer_ids, sig->auth_ids[0])) { @@ -197,7 +293,8 @@ static int key_or_keyring_common(struct key *dest_keyring, 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); + sig->auth_ids[1], sig->auth_ids[2], + false); if (IS_ERR(key)) key = NULL; } @@ -248,9 +345,10 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, * @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. + * Check the new certificate against the key or keys passed in the data + * parameter and against the keys already linked to the destination keyring. 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, |
