summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-03 08:50:52 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-03 08:50:52 -0700
commit0302e28dee643932ee7b3c112ebccdbb9f8ec32c (patch)
tree405d4cb3f772ef069ed7f291adc4b74a4e73346e /crypto
parent89c9fea3c8034cdb2fd745f551cde0b507fd6893 (diff)
parent8979b02aaf1d6de8d52cc143aa4da961ed32e5a2 (diff)
downloadlinux-0302e28dee643932ee7b3c112ebccdbb9f8ec32c.tar.bz2
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "Highlights: IMA: - provide ">" and "<" operators for fowner/uid/euid rules KEYS: - add a system blacklist keyring - add KEYCTL_RESTRICT_KEYRING, exposes keyring link restriction functionality to userland via keyctl() LSM: - harden LSM API with __ro_after_init - add prlmit security hook, implement for SELinux - revive security_task_alloc hook TPM: - implement contextual TPM command 'spaces'" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (98 commits) tpm: Fix reference count to main device tpm_tis: convert to using locality callbacks tpm: fix handling of the TPM 2.0 event logs tpm_crb: remove a cruft constant keys: select CONFIG_CRYPTO when selecting DH / KDF apparmor: Make path_max parameter readonly apparmor: fix parameters so that the permission test is bypassed at boot apparmor: fix invalid reference to index variable of iterator line 836 apparmor: use SHASH_DESC_ON_STACK security/apparmor/lsm.c: set debug messages apparmor: fix boolreturn.cocci warnings Smack: Use GFP_KERNEL for smk_netlbl_mls(). smack: fix double free in smack_parse_opts_str() KEYS: add SP800-56A KDF support for DH KEYS: Keyring asymmetric key restrict method with chaining KEYS: Restrict asymmetric key linkage using a specific keychain KEYS: Add a lookup_restriction function for the asymmetric key type KEYS: Add KEYCTL_RESTRICT_KEYRING KEYS: Consistent ordering for __key_link_begin and restrict check KEYS: Add an optional lookup_restriction hook to key_type ...
Diffstat (limited to 'crypto')
-rw-r--r--crypto/asymmetric_keys/asymmetric_type.c102
-rw-r--r--crypto/asymmetric_keys/pkcs7_parser.h1
-rw-r--r--crypto/asymmetric_keys/pkcs7_verify.c32
-rw-r--r--crypto/asymmetric_keys/restrict.c161
-rw-r--r--crypto/asymmetric_keys/x509_parser.h1
-rw-r--r--crypto/asymmetric_keys/x509_public_key.c15
6 files changed, 293 insertions, 19 deletions
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index 6600181d5d01..e4b0ed386bc8 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ctype.h>
+#include <keys/system_keyring.h>
#include "asymmetric_keys.h"
MODULE_LICENSE("GPL");
@@ -451,15 +452,100 @@ static void asymmetric_key_destroy(struct key *key)
asymmetric_key_free_kids(kids);
}
+static struct key_restriction *asymmetric_restriction_alloc(
+ key_restrict_link_func_t check,
+ struct key *key)
+{
+ struct key_restriction *keyres =
+ kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
+
+ if (!keyres)
+ return ERR_PTR(-ENOMEM);
+
+ keyres->check = check;
+ keyres->key = key;
+ keyres->keytype = &key_type_asymmetric;
+
+ return keyres;
+}
+
+/*
+ * look up keyring restrict functions for asymmetric keys
+ */
+static struct key_restriction *asymmetric_lookup_restriction(
+ const char *restriction)
+{
+ char *restrict_method;
+ char *parse_buf;
+ char *next;
+ struct key_restriction *ret = ERR_PTR(-EINVAL);
+
+ if (strcmp("builtin_trusted", restriction) == 0)
+ return asymmetric_restriction_alloc(
+ restrict_link_by_builtin_trusted, NULL);
+
+ if (strcmp("builtin_and_secondary_trusted", restriction) == 0)
+ return asymmetric_restriction_alloc(
+ restrict_link_by_builtin_and_secondary_trusted, NULL);
+
+ parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL);
+ if (!parse_buf)
+ return ERR_PTR(-ENOMEM);
+
+ next = parse_buf;
+ restrict_method = strsep(&next, ":");
+
+ if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) {
+ char *key_text;
+ key_serial_t serial;
+ struct key *key;
+ key_restrict_link_func_t link_fn =
+ restrict_link_by_key_or_keyring;
+ bool allow_null_key = false;
+
+ key_text = strsep(&next, ":");
+
+ if (next) {
+ if (strcmp(next, "chain") != 0)
+ goto out;
+
+ link_fn = restrict_link_by_key_or_keyring_chain;
+ allow_null_key = true;
+ }
+
+ if (kstrtos32(key_text, 0, &serial) < 0)
+ goto out;
+
+ if ((serial == 0) && allow_null_key) {
+ key = NULL;
+ } else {
+ key = key_lookup(serial);
+ if (IS_ERR(key)) {
+ ret = ERR_CAST(key);
+ goto out;
+ }
+ }
+
+ ret = asymmetric_restriction_alloc(link_fn, key);
+ if (IS_ERR(ret))
+ key_put(key);
+ }
+
+out:
+ kfree(parse_buf);
+ return ret;
+}
+
struct key_type key_type_asymmetric = {
- .name = "asymmetric",
- .preparse = asymmetric_key_preparse,
- .free_preparse = asymmetric_key_free_preparse,
- .instantiate = generic_key_instantiate,
- .match_preparse = asymmetric_key_match_preparse,
- .match_free = asymmetric_key_match_free,
- .destroy = asymmetric_key_destroy,
- .describe = asymmetric_key_describe,
+ .name = "asymmetric",
+ .preparse = asymmetric_key_preparse,
+ .free_preparse = asymmetric_key_free_preparse,
+ .instantiate = generic_key_instantiate,
+ .match_preparse = asymmetric_key_match_preparse,
+ .match_free = asymmetric_key_match_free,
+ .destroy = asymmetric_key_destroy,
+ .describe = asymmetric_key_describe,
+ .lookup_restriction = asymmetric_lookup_restriction,
};
EXPORT_SYMBOL_GPL(key_type_asymmetric);
diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h
index f4e81074f5e0..ac341e19e530 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.h
+++ b/crypto/asymmetric_keys/pkcs7_parser.h
@@ -23,6 +23,7 @@ struct pkcs7_signed_info {
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
unsigned index;
bool unsupported_crypto; /* T if not usable due to missing crypto */
+ bool blacklisted;
/* Message digest - the digest of the Content Data (or NULL) */
const void *msgdigest;
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index 2ffd69769466..2d93d9eccb4d 100644
--- a/crypto/asymmetric_keys/pkcs7_verify.c
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -190,6 +190,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
x509->subject,
x509->raw_serial_size, x509->raw_serial);
x509->seen = true;
+
+ if (x509->blacklisted) {
+ /* If this cert is blacklisted, then mark everything
+ * that depends on this as blacklisted too.
+ */
+ sinfo->blacklisted = true;
+ for (p = sinfo->signer; p != x509; p = p->signer)
+ p->blacklisted = true;
+ pr_debug("- blacklisted\n");
+ return 0;
+ }
+
if (x509->unsupported_key)
goto unsupported_crypto_in_x509;
@@ -357,17 +369,19 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
*
* (*) -EBADMSG if some part of the message was invalid, or:
*
- * (*) -ENOPKG if none of the signature chains are verifiable because suitable
- * crypto modules couldn't be found, or:
+ * (*) 0 if no signature chains were found to be blacklisted or to contain
+ * unsupported crypto, or:
*
- * (*) 0 if all the signature chains that don't incur -ENOPKG can be verified
- * (note that a signature chain may be of zero length), or:
+ * (*) -EKEYREJECTED if a blacklisted key was encountered, or:
+ *
+ * (*) -ENOPKG if none of the signature chains are verifiable because suitable
+ * crypto modules couldn't be found.
*/
int pkcs7_verify(struct pkcs7_message *pkcs7,
enum key_being_used_for usage)
{
struct pkcs7_signed_info *sinfo;
- int enopkg = -ENOPKG;
+ int actual_ret = -ENOPKG;
int ret;
kenter("");
@@ -412,6 +426,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
ret = pkcs7_verify_one(pkcs7, sinfo);
+ if (sinfo->blacklisted && actual_ret == -ENOPKG)
+ actual_ret = -EKEYREJECTED;
if (ret < 0) {
if (ret == -ENOPKG) {
sinfo->unsupported_crypto = true;
@@ -420,11 +436,11 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
kleave(" = %d", ret);
return ret;
}
- enopkg = 0;
+ actual_ret = 0;
}
- kleave(" = %d", enopkg);
- return enopkg;
+ kleave(" = %d", actual_ret);
+ return actual_ret;
}
EXPORT_SYMBOL_GPL(pkcs7_verify);
diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c
index 19d1afb9890f..86fb68508952 100644
--- a/crypto/asymmetric_keys/restrict.c
+++ b/crypto/asymmetric_keys/restrict.c
@@ -56,9 +56,10 @@ __setup("ca_keys=", ca_keys_setup);
/**
* restrict_link_by_signature - Restrict additions to a ring of public keys
- * @trust_keyring: A ring of keys that can be used to vouch for the new cert.
+ * @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 the new certificate against the ones in the trust keyring. If one of
* those is the signing key and validates the new certificate, then mark the
@@ -69,9 +70,10 @@ __setup("ca_keys=", ca_keys_setup);
* signature check fails or the key is blacklisted and some other error if
* there is a matching certificate but the signature check cannot be performed.
*/
-int restrict_link_by_signature(struct key *trust_keyring,
+int restrict_link_by_signature(struct key *dest_keyring,
const struct key_type *type,
- const union key_payload *payload)
+ const union key_payload *payload,
+ struct key *trust_keyring)
{
const struct public_key_signature *sig;
struct key *key;
@@ -106,3 +108,156 @@ int restrict_link_by_signature(struct key *trust_keyring,
key_put(key);
return ret;
}
+
+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 = NULL;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (!dest_keyring)
+ return -ENOKEY;
+ else if (dest_keyring->type != &key_type_keyring)
+ return -EOPNOTSUPP;
+
+ if (!trusted && !check_dest)
+ return -ENOKEY;
+
+ if (type != &key_type_asymmetric)
+ return -EOPNOTSUPP;
+
+ sig = payload->data[asym_auth];
+ if (!sig->auth_ids[0] && !sig->auth_ids[1])
+ 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);
+ if (IS_ERR(key))
+ key = NULL;
+ } else if (trusted->type == &key_type_asymmetric) {
+ const struct asymmetric_key_ids *signer_ids;
+
+ signer_ids = asymmetric_key_ids(trusted);
+
+ /*
+ * 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;
+
+ 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);
+
+ 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);
+}
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index 05eef1c68881..e373e7483812 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -42,6 +42,7 @@ struct x509_certificate {
bool self_signed; /* T if self-signed (check unsupported_sig too) */
bool unsupported_key; /* T if key uses unsupported crypto */
bool unsupported_sig; /* T if signature uses unsupported crypto */
+ bool blacklisted;
};
/*
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index fb732296cd36..eea71dc9686c 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -84,6 +84,16 @@ int x509_get_sig_params(struct x509_certificate *cert)
goto error_2;
might_sleep();
ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
+ if (ret < 0)
+ goto error_2;
+
+ ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs");
+ if (ret == -EKEYREJECTED) {
+ pr_err("Cert %*phN is blacklisted\n",
+ sig->digest_size, sig->digest);
+ cert->blacklisted = true;
+ ret = 0;
+ }
error_2:
kfree(desc);
@@ -186,6 +196,11 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
cert->sig->pkey_algo, cert->sig->hash_algo);
}
+ /* Don't permit addition of blacklisted keys */
+ ret = -EKEYREJECTED;
+ if (cert->blacklisted)
+ goto error_free_cert;
+
/* Propose a description */
sulen = strlen(cert->subject);
if (cert->raw_skid) {