diff options
-rw-r--r-- | Documentation/kernel-parameters.txt | 5 | ||||
-rw-r--r-- | crypto/asymmetric_keys/asymmetric_keys.h | 2 | ||||
-rw-r--r-- | crypto/asymmetric_keys/asymmetric_type.c | 51 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_public_key.c | 109 | ||||
-rw-r--r-- | include/keys/system_keyring.h | 10 | ||||
-rw-r--r-- | include/linux/key.h | 1 | ||||
-rw-r--r-- | kernel/system_keyring.c | 1 | ||||
-rw-r--r-- | security/integrity/digsig.c | 28 | ||||
-rw-r--r-- | security/integrity/ima/Kconfig | 10 | ||||
-rw-r--r-- | security/integrity/ima/ima.h | 12 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 10 | ||||
-rw-r--r-- | security/integrity/integrity.h | 5 | ||||
-rw-r--r-- | security/keys/keyctl.c | 6 |
13 files changed, 225 insertions, 25 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 8b2ab548b6e..90c12c59116 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -566,6 +566,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. possible to determine what the correct size should be. This option provides an override for these situations. + ca_keys= [KEYS] This parameter identifies a specific key(s) on + the system trusted keyring to be used for certificate + trust validation. + format: { id:<keyid> | builtin } + ccw_timeout_log [S390] See Documentation/s390/CommonIO for details. diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index 515b6343081..a63c551c655 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -9,6 +9,8 @@ * 2 of the Licence, or (at your option) any later version. */ +int asymmetric_keyid_match(const char *kid, const char *id); + static inline const char *asymmetric_key_id(const struct key *key) { return key->type_data.p[1]; diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 21960a4e74e..eb8cd46961a 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -23,6 +23,35 @@ static LIST_HEAD(asymmetric_key_parsers); static DECLARE_RWSEM(asymmetric_key_parsers_sem); /* + * Match asymmetric key id with partial match + * @id: key id to match in a form "id:<id>" + */ +int asymmetric_keyid_match(const char *kid, const char *id) +{ + size_t idlen, kidlen; + + if (!kid || !id) + return 0; + + /* make it possible to use id as in the request: "id:<id>" */ + if (strncmp(id, "id:", 3) == 0) + id += 3; + + /* Anything after here requires a partial match on the ID string */ + idlen = strlen(id); + kidlen = strlen(kid); + if (idlen > kidlen) + return 0; + + kid += kidlen - idlen; + if (strcasecmp(id, kid) != 0) + return 0; + + return 1; +} +EXPORT_SYMBOL_GPL(asymmetric_keyid_match); + +/* * Match asymmetric keys on (part of) their name * We have some shorthand methods for matching keys. We allow: * @@ -34,9 +63,8 @@ static int asymmetric_key_match(const struct key *key, const void *description) { const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); const char *spec = description; - const char *id, *kid; + const char *id; ptrdiff_t speclen; - size_t idlen, kidlen; if (!subtype || !spec || !*spec) return 0; @@ -55,23 +83,8 @@ static int asymmetric_key_match(const struct key *key, const void *description) speclen = id - spec; id++; - /* Anything after here requires a partial match on the ID string */ - kid = asymmetric_key_id(key); - if (!kid) - return 0; - - idlen = strlen(id); - kidlen = strlen(kid); - if (idlen > kidlen) - return 0; - - kid += kidlen - idlen; - if (strcasecmp(id, kid) != 0) - return 0; - - if (speclen == 2 && - memcmp(spec, "id", 2) == 0) - return 1; + if (speclen == 2 && memcmp(spec, "id", 2) == 0) + return asymmetric_keyid_match(asymmetric_key_id(key), id); if (speclen == subtype->name_len && memcmp(spec, subtype->name, speclen) == 0) diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 3fc8a0634ed..a0f7cd196c9 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -18,11 +18,80 @@ #include <linux/asn1_decoder.h> #include <keys/asymmetric-subtype.h> #include <keys/asymmetric-parser.h> +#include <keys/system_keyring.h> #include <crypto/hash.h> #include "asymmetric_keys.h" #include "public_key.h" #include "x509_parser.h" +static bool use_builtin_keys; +static char *ca_keyid; + +#ifndef MODULE +static int __init ca_keys_setup(char *str) +{ + if (!str) /* default system keyring */ + return 1; + + if (strncmp(str, "id:", 3) == 0) + ca_keyid = str; /* owner key 'id:xxxxxx' */ + else if (strcmp(str, "builtin") == 0) + use_builtin_keys = true; + + return 1; +} +__setup("ca_keys=", ca_keys_setup); +#endif + +/* + * Find a key in the given keyring by issuer and authority. + */ +static struct key *x509_request_asymmetric_key(struct key *keyring, + const char *signer, + size_t signer_len, + const char *authority, + size_t auth_len) +{ + key_ref_t key; + char *id; + + /* Construct an identifier. */ + id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); + if (!id) + return ERR_PTR(-ENOMEM); + + memcpy(id, signer, signer_len); + id[signer_len + 0] = ':'; + id[signer_len + 1] = ' '; + memcpy(id + signer_len + 2, authority, auth_len); + id[signer_len + 2 + auth_len] = 0; + + pr_debug("Look up: \"%s\"\n", id); + + key = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, id); + if (IS_ERR(key)) + pr_debug("Request for module key '%s' err %ld\n", + id, PTR_ERR(key)); + kfree(id); + + if (IS_ERR(key)) { + switch (PTR_ERR(key)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + return ERR_PTR(-ENOKEY); + default: + return ERR_CAST(key); + } + } + + pr_devel("<==%s() = 0 [%x]\n", __func__, + key_serial(key_ref_to_ptr(key))); + return key_ref_to_ptr(key); +} + /* * Set up the signature parameters in an X.509 certificate. This involves * digesting the signed data and extracting the signature. @@ -103,6 +172,40 @@ int x509_check_signature(const struct public_key *pub, EXPORT_SYMBOL_GPL(x509_check_signature); /* + * 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 + * new certificate as being trusted. + * + * Return 0 if the new certificate was successfully validated, 1 if we couldn't + * find a matching parent certificate in the trusted list and an error if there + * is a matching certificate but the signature check fails. + */ +static int x509_validate_trust(struct x509_certificate *cert, + struct key *trust_keyring) +{ + struct key *key; + int ret = 1; + + if (!trust_keyring) + return -EOPNOTSUPP; + + if (ca_keyid && !asymmetric_keyid_match(cert->authority, ca_keyid)) + return -EPERM; + + key = x509_request_asymmetric_key(trust_keyring, + cert->issuer, strlen(cert->issuer), + cert->authority, + strlen(cert->authority)); + if (!IS_ERR(key)) { + if (!use_builtin_keys + || test_bit(KEY_FLAG_BUILTIN, &key->flags)) + ret = x509_check_signature(key->payload.data, cert); + key_put(key); + } + return ret; +} + +/* * Attempt to parse a data blob for a key as an X509 certificate. */ static int x509_key_preparse(struct key_preparsed_payload *prep) @@ -155,9 +258,13 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) /* Check the signature on the key if it appears to be self-signed */ if (!cert->authority || strcmp(cert->fingerprint, cert->authority) == 0) { - ret = x509_check_signature(cert->pub, cert); + ret = x509_check_signature(cert->pub, cert); /* self-signed */ if (ret < 0) goto error_free_cert; + } else if (!prep->trusted) { + ret = x509_validate_trust(cert, get_system_trusted_keyring()); + if (!ret) + prep->trusted = 1; } /* Propose a description */ diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 8dabc399bd1..72665eb8069 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -17,7 +17,15 @@ #include <linux/key.h> extern struct key *system_trusted_keyring; - +static inline struct key *get_system_trusted_keyring(void) +{ + return system_trusted_keyring; +} +#else +static inline struct key *get_system_trusted_keyring(void) +{ + return NULL; +} #endif #endif /* _KEYS_SYSTEM_KEYRING_H */ diff --git a/include/linux/key.h b/include/linux/key.h index 017b0826642..65316f7ae79 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -170,6 +170,7 @@ struct key { #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ #define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ #define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ +#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ /* the key type and key description string * - the desc is used to match a key against search criteria diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c index 52ebc70263f..875f64e8935 100644 --- a/kernel/system_keyring.c +++ b/kernel/system_keyring.c @@ -89,6 +89,7 @@ static __init int load_system_certificate_list(void) pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", PTR_ERR(key)); } else { + set_bit(KEY_FLAG_BUILTIN, &key_ref_to_ptr(key)->flags); pr_notice("Loaded X.509 cert '%s'\n", key_ref_to_ptr(key)->description); key_ref_put(key); diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index b4af4ebc5be..8d4fbff8b87 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -13,7 +13,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/err.h> +#include <linux/sched.h> #include <linux/rbtree.h> +#include <linux/cred.h> #include <linux/key-type.h> #include <linux/digsig.h> @@ -24,7 +26,11 @@ static struct key *keyring[INTEGRITY_KEYRING_MAX]; static const char *keyring_name[INTEGRITY_KEYRING_MAX] = { "_evm", "_module", +#ifndef CONFIG_IMA_TRUSTED_KEYRING "_ima", +#else + ".ima", +#endif }; int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, @@ -56,3 +62,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, return -EOPNOTSUPP; } + +int integrity_init_keyring(const unsigned int id) +{ + const struct cred *cred = current_cred(); + int err = 0; + + keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0), + KGIDT_INIT(0), cred, + ((KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ | + KEY_USR_WRITE | KEY_USR_SEARCH), + KEY_ALLOC_NOT_IN_QUOTA, NULL); + if (!IS_ERR(keyring[id])) + set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags); + else { + err = PTR_ERR(keyring[id]); + pr_info("Can't allocate %s keyring (%d)\n", + keyring_name[id], err); + keyring[id] = NULL; + } + return err; +} diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 81a27971d88..08758fbd496 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -123,3 +123,13 @@ config IMA_APPRAISE For more information on integrity appraisal refer to: <http://linux-ima.sourceforge.net> If unsure, say N. + +config IMA_TRUSTED_KEYRING + bool "Require all keys on the .ima keyring be signed" + depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING + depends on INTEGRITY_ASYMMETRIC_KEYS + select KEYS_DEBUG_PROC_KEYS + default y + help + This option requires that all keys added to the .ima + keyring be signed by a key on the system trusted keyring. diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index f79fa8be203..c42056edfc9 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -249,4 +249,16 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op, return -EINVAL; } #endif /* CONFIG_IMA_LSM_RULES */ + +#ifdef CONFIG_IMA_TRUSTED_KEYRING +static inline int ima_init_keyring(const unsigned int id) +{ + return integrity_init_keyring(id); +} +#else +static inline int ima_init_keyring(const unsigned int id) +{ + return 0; +} +#endif /* CONFIG_IMA_TRUSTED_KEYRING */ #endif diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index f474c608fa1..0d696431209 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -325,8 +325,14 @@ static int __init init_ima(void) hash_setup(CONFIG_IMA_DEFAULT_HASH); error = ima_init(); - if (!error) - ima_initialized = 1; + if (error) + goto out; + + error = ima_init_keyring(INTEGRITY_KEYRING_IMA); + if (error) + goto out; + ima_initialized = 1; +out: return error; } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 33c0a70f6b1..09c440d9aae 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -124,6 +124,7 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode); int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, const char *digest, int digestlen); +int integrity_init_keyring(const unsigned int id); #else static inline int integrity_digsig_verify(const unsigned int id, @@ -133,6 +134,10 @@ static inline int integrity_digsig_verify(const unsigned int id, return -EOPNOTSUPP; } +static inline int integrity_init_keyring(const unsigned int id) +{ + return 0; +} #endif /* CONFIG_INTEGRITY_SIGNATURE */ #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index cd5bd0cef25..8a8c2335729 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -37,8 +37,6 @@ static int key_get_type_from_user(char *type, return ret; if (ret == 0 || ret >= len) return -EINVAL; - if (type[0] == '.') - return -EPERM; type[len - 1] = '\0'; return 0; } @@ -86,6 +84,10 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, if (!*description) { kfree(description); description = NULL; + } else if ((description[0] == '.') && + (strncmp(type, "keyring", 7) == 0)) { + ret = -EPERM; + goto error2; } } |