diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 8b2ab548b6e42ef9657a5bc7f004d04abfe5ad10..90c12c59116830cd67e7fecda515f0ee33a8a103 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: | 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 515b634308121972f4a79bdcff917a49a6ba4ff8..a63c551c65570006ef2281ad2dc3ad6b70622482 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 21960a4e74e8c193e8a69e07e0611c7139aaa82f..eb8cd46961a5aa5a6a7470c6d17008d1745bbdf6 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -22,6 +22,35 @@ MODULE_LICENSE("GPL"); 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:" + */ +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:" */ + 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 3fc8a0634ed7daa5eabd40a25f79e02374e67fbb..a0f7cd196c9b031f1448f1c3b9c2795a56301b48 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -18,11 +18,80 @@ #include #include #include +#include #include #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. @@ -102,6 +171,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. */ @@ -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 8dabc399bd1d156d9ced4d371ba8b8136deea5f4..72665eb8069269f4e1b6726c8cd2d04374405ab8 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -17,7 +17,15 @@ #include 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 017b0826642f04c070137114ee98958737677cd6..65316f7ae79437a8bfedcb0ed2a3338dafab3b7e 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 52ebc70263f4d8e806dc436fd55e6000cf818915..875f64e8935bbf1f27320a42ae467f170b09e3d9 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 b4af4ebc5be284d7f2665a5266be6a5bf267a683..8d4fbff8b87ca80460c9192610c01add6a150aa0 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -13,7 +13,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include +#include #include #include @@ -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 81a27971d884215bc20e71d756f6778893ae1d67..08758fbd496f90c9176a726d3f48b7d91bb3e82a 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: 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 f79fa8be203cd3703ad1a7fedddfe7037f602cdd..c42056edfc97699a7e85d7c1826614ef3686981f 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 f474c608fa1194ebde04e21f2bdabc5be64c5d4a..0d696431209c09e1ac3f1c55145544648dbfa3be 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 33c0a70f6b15a66a7b024aa8fbb534d72c76c898..09c440d9aaee5b3c1624a93c7cebd45f3cf0d1c6 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 cd5bd0cef25df3fce5801e938567e156d47f275f..8a8c23357291471a35a201ebfcde9f08c2e9b893 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; } }