diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst index 80d5a5af62a1de0a571ab749319958e7c9912e75..f614dad7de12f90aec85b32955986bd76126a39f 100644 --- a/Documentation/security/keys/trusted-encrypted.rst +++ b/Documentation/security/keys/trusted-encrypted.rst @@ -107,12 +107,13 @@ Encrypted Keys -------------- Encrypted keys do not depend on a trust source, and are faster, as they use AES -for encryption/decryption. New keys are created from kernel-generated random -numbers, and are encrypted/decrypted using a specified ‘master’ key. The -‘master’ key can either be a trusted-key or user-key type. The main disadvantage -of encrypted keys is that if they are not rooted in a trusted key, they are only -as secure as the user key encrypting them. The master user key should therefore -be loaded in as secure a way as possible, preferably early in boot. +for encryption/decryption. New keys are created either from kernel-generated +random numbers or user-provided decrypted data, and are encrypted/decrypted +using a specified ‘master’ key. The ‘master’ key can either be a trusted-key or +user-key type. The main disadvantage of encrypted keys is that if they are not +rooted in a trusted key, they are only as secure as the user key encrypting +them. The master user key should therefore be loaded in as secure a way as +possible, preferably early in boot. Usage @@ -199,6 +200,8 @@ Usage:: keyctl add encrypted name "new [format] key-type:master-key-name keylen" ring + keyctl add encrypted name "new [format] key-type:master-key-name keylen + decrypted-data" ring keyctl add encrypted name "load hex_blob" ring keyctl update keyid "update key-type:master-key-name" @@ -303,6 +306,16 @@ Load an encrypted key "evm" from saved blob:: 82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0 24717c64 5972dcb82ab2dde83376d82b2e3c09ffc +Instantiate an encrypted key "evm" using user-provided decrypted data:: + + $ keyctl add encrypted evm "new default user:kmk 32 `cat evm_decrypted_data.blob`" @u + 794890253 + + $ keyctl print 794890253 + default user:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382d + bbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0247 + 17c64 5972dcb82ab2dde83376d82b2e3c09ffc + Other uses for trusted and encrypted keys, such as for disk and file encryption are anticipated. In particular the new format 'ecryptfs' has been defined in order to use encrypted keys to mount an eCryptfs filesystem. More details diff --git a/MAINTAINERS b/MAINTAINERS index 625ae3ca69b780805d8a4344330cecd22697129a..4386b12f86e8c5615c9f0a2a65e2e080195ce9c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7280,7 +7280,9 @@ Extended Verification Module (EVM) M: Mimi Zohar L: linux-integrity@vger.kernel.org S: Supported +T: git git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git F: security/integrity/evm/ +F: security/integrity/ EXTENSIBLE FIRMWARE INTERFACE (EFI) M: Ard Biesheuvel @@ -9537,6 +9539,7 @@ L: linux-integrity@vger.kernel.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git F: security/integrity/ima/ +F: security/integrity/ INTEL 810/815 FRAMEBUFFER DRIVER M: Antonino Daplas @@ -10675,6 +10678,14 @@ F: include/linux/keyctl.h F: include/uapi/linux/keyctl.h F: security/keys/ +KEYS/KEYRINGS_INTEGRITY +M: Jarkko Sakkinen +M: Mimi Zohar +L: linux-integrity@vger.kernel.org +L: keyrings@vger.kernel.org +S: Supported +F: security/integrity/platform_certs + KFENCE M: Alexander Potapenko M: Marco Elver diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 08f907382c6187b6616b5211aa6fbb360aff24b0..7d87772f0ce681971db412ad606a0e84ab1d7a51 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -86,7 +86,7 @@ static int __init evm_set_fixmode(char *str) else pr_err("invalid \"%s\" mode", str); - return 0; + return 1; } __setup("evm=", evm_set_fixmode); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index a64fb0130b01520f863559eb23ca90a9c1ff8610..c6805af4621187767d87ba1dd9cce908f8f8f38a 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -217,14 +217,11 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, const char *audit_cause = "failed"; struct inode *inode = file_inode(file); const char *filename = file->f_path.dentry->d_name.name; + struct ima_max_digest_data hash; int result = 0; int length; void *tmpbuf; u64 i_version; - struct { - struct ima_digest_data hdr; - char digest[IMA_MAX_DIGEST_SIZE]; - } hash; /* * Always collect the modsig, because IMA might have already collected @@ -238,9 +235,10 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, goto out; /* - * Dectecting file change is based on i_version. On filesystems - * which do not support i_version, support is limited to an initial - * measurement/appraisal/audit. + * Detecting file change is based on i_version. On filesystems + * which do not support i_version, support was originally limited + * to an initial measurement/appraisal/audit, but was modified to + * assume the file changed. */ i_version = inode_query_iversion(inode); hash.hdr.algo = algo; diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 3ad8f7734208b93a8ecf02a7af5b3df248d5decb..cd1683dad3bf016e8d1189bef8616361c1eed80b 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -452,47 +452,61 @@ static const struct file_operations ima_measure_policy_ops = { int __init ima_fs_init(void) { + int ret; + ima_dir = securityfs_create_dir("ima", integrity_dir); if (IS_ERR(ima_dir)) - return -1; + return PTR_ERR(ima_dir); ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima", NULL); - if (IS_ERR(ima_symlink)) + if (IS_ERR(ima_symlink)) { + ret = PTR_ERR(ima_symlink); goto out; + } binary_runtime_measurements = securityfs_create_file("binary_runtime_measurements", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_measurements_ops); - if (IS_ERR(binary_runtime_measurements)) + if (IS_ERR(binary_runtime_measurements)) { + ret = PTR_ERR(binary_runtime_measurements); goto out; + } ascii_runtime_measurements = securityfs_create_file("ascii_runtime_measurements", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_ascii_measurements_ops); - if (IS_ERR(ascii_runtime_measurements)) + if (IS_ERR(ascii_runtime_measurements)) { + ret = PTR_ERR(ascii_runtime_measurements); goto out; + } runtime_measurements_count = securityfs_create_file("runtime_measurements_count", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_measurements_count_ops); - if (IS_ERR(runtime_measurements_count)) + if (IS_ERR(runtime_measurements_count)) { + ret = PTR_ERR(runtime_measurements_count); goto out; + } violations = securityfs_create_file("violations", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_htable_violations_ops); - if (IS_ERR(violations)) + if (IS_ERR(violations)) { + ret = PTR_ERR(violations); goto out; + } ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS, ima_dir, NULL, &ima_measure_policy_ops); - if (IS_ERR(ima_policy)) + if (IS_ERR(ima_policy)) { + ret = PTR_ERR(ima_policy); goto out; + } return 0; out: @@ -503,5 +517,6 @@ int __init ima_fs_init(void) securityfs_remove(binary_runtime_measurements); securityfs_remove(ima_symlink); securityfs_remove(ima_dir); - return -1; + + return ret; } diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index b26fa67476b411d35a576e9754a1f8d450e633b7..63979aefc95f7e018852bc301a740468c75db742 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -47,12 +47,9 @@ static int __init ima_add_boot_aggregate(void) struct integrity_iint_cache tmp_iint, *iint = &tmp_iint; struct ima_event_data event_data = { .iint = iint, .filename = boot_aggregate_name }; + struct ima_max_digest_data hash; int result = -ENOMEM; int violation = 0; - struct { - struct ima_digest_data hdr; - char digest[TPM_MAX_DIGEST_SIZE]; - } hash; memset(iint, 0, sizeof(*iint)); memset(&hash, 0, sizeof(hash)); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 8c6e4514d4944c0ba7c3081733846ffde70d2f63..c6412dec3810b1d757ff50c8d724f0aeec1ac51b 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -263,7 +263,7 @@ static int process_measurement(struct file *file, const struct cred *cred, /* reset appraisal flags if ima_inode_post_setattr was called */ iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED | IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK | - IMA_ACTION_FLAGS); + IMA_NONACTION_FLAGS); /* * Re-evaulate the file if either the xattr has changed or the @@ -764,7 +764,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size, * call to ima_post_load_data(). * * Callers of this LSM hook can not measure, appraise, or audit the - * data provided by userspace. Enforce policy rules requring a file + * data provided by userspace. Enforce policy rules requiring a file * signature (eg. kexec'ed kernel image). * * For permission return 0, otherwise return -EACCES. @@ -874,10 +874,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns, .buf = buf, .buf_len = size}; struct ima_template_desc *template; - struct { - struct ima_digest_data hdr; - char digest[IMA_MAX_DIGEST_SIZE]; - } hash = {}; + struct ima_max_digest_data hash; char digest_hash[IMA_MAX_DIGEST_SIZE]; int digest_hash_len = hash_digest_size[ima_hash_algo]; int violation = 0; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 99d23ac4c35d68a5e45bb74b1d690ab78ae6f827..eea6e92500b8ea03853b119cfab9f5622c6672fb 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -428,7 +428,7 @@ static int ima_lsm_update_rule(struct ima_rule_entry *entry) /* * ima_lsm_copy_rule() shallow copied all references, except for the * LSM references, from entry to nentry so we only want to free the LSM - * references and the entry itself. All other memory refrences will now + * references and the entry itself. All other memory references will now * be owned by nentry. */ ima_lsm_free_rule(entry); @@ -711,7 +711,7 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode, func, mask, func_data)) continue; - action |= entry->flags & IMA_ACTION_FLAGS; + action |= entry->flags & IMA_NONACTION_FLAGS; action |= entry->action & IMA_DO_MASK; if (entry->action & IMA_APPRAISE) { diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index ca017cae73eb3bd02a5656049e87552f113d5450..7155d17a3b75f52aaa42ad23c0323350db110846 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -272,7 +272,7 @@ static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize, * digest formats: * - DATA_FMT_DIGEST: digest * - DATA_FMT_DIGEST_WITH_ALGO: [] + ':' + '\0' + digest, - * where is provided if the hash algoritm is not + * where is provided if the hash algorithm is not * SHA1 or MD5 */ u8 buffer[CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE] = { 0 }; @@ -307,10 +307,7 @@ static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize, int ima_eventdigest_init(struct ima_event_data *event_data, struct ima_field_data *field_data) { - struct { - struct ima_digest_data hdr; - char digest[IMA_MAX_DIGEST_SIZE]; - } hash; + struct ima_max_digest_data hash; u8 *cur_digest = NULL; u32 cur_digestsize = 0; struct inode *inode; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 2e214c76115869ac2f0d576f609b3e12a68075c6..3510e413ea179e4e738649b32220d9c3122b5952 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -30,8 +31,8 @@ #define IMA_HASH 0x00000100 #define IMA_HASHED 0x00000200 -/* iint cache flags */ -#define IMA_ACTION_FLAGS 0xff000000 +/* iint policy rule cache flags */ +#define IMA_NONACTION_FLAGS 0xff000000 #define IMA_DIGSIG_REQUIRED 0x01000000 #define IMA_PERMIT_DIRECTIO 0x02000000 #define IMA_NEW_FILE 0x04000000 @@ -110,6 +111,15 @@ struct ima_digest_data { u8 digest[]; } __packed; +/* + * Instead of wrapping the ima_digest_data struct inside a local structure + * with the maximum hash size, define ima_max_digest_data struct. + */ +struct ima_max_digest_data { + struct ima_digest_data hdr; + u8 digest[HASH_MAX_DIGESTSIZE]; +} __packed; + /* * signature format v2 - for using with asymmetric keys */ diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 969122c7b92f4d46dd0a8499b5b1068eda1bc1e5..0e30b361e1c1e386322e1d1a2b5930b9d10b8ee7 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -98,10 +98,21 @@ config ENCRYPTED_KEYS select CRYPTO_RNG help This option provides support for create/encrypting/decrypting keys - in the kernel. Encrypted keys are kernel generated random numbers, - which are encrypted/decrypted with a 'master' symmetric key. The - 'master' key can be either a trusted-key or user-key type. - Userspace only ever sees/stores encrypted blobs. + in the kernel. Encrypted keys are instantiated using kernel + generated random numbers or provided decrypted data, and are + encrypted/decrypted with a 'master' symmetric key. The 'master' + key can be either a trusted-key or user-key type. Only encrypted + blobs are ever output to Userspace. + + If you are unsure as to whether this is required, answer N. + +config USER_DECRYPTED_DATA + bool "Allow encrypted keys with user decrypted data" + depends on ENCRYPTED_KEYS + help + This option provides support for instantiating encrypted keys using + user-provided decrypted data. The decrypted data must be hex-ascii + encoded. If you are unsure as to whether this is required, answer N. diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 87432b35d7713f94118ad2be5bca285736a8502b..e05cfc2e49aeb1c52f31171310aa14576c5213dd 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -78,6 +78,11 @@ static const match_table_t key_tokens = { {Opt_err, NULL} }; +static bool user_decrypted_data = IS_ENABLED(CONFIG_USER_DECRYPTED_DATA); +module_param(user_decrypted_data, bool, 0); +MODULE_PARM_DESC(user_decrypted_data, + "Allow instantiation of encrypted keys using provided decrypted data"); + static int aes_get_sizes(void) { struct crypto_skcipher *tfm; @@ -158,7 +163,7 @@ static int valid_master_desc(const char *new_desc, const char *orig_desc) * datablob_parse - parse the keyctl data * * datablob format: - * new [] + * new [] [] * load [] * * update @@ -170,7 +175,7 @@ static int valid_master_desc(const char *new_desc, const char *orig_desc) */ static int datablob_parse(char *datablob, const char **format, char **master_desc, char **decrypted_datalen, - char **hex_encoded_iv) + char **hex_encoded_iv, char **decrypted_data) { substring_t args[MAX_OPT_ARGS]; int ret = -EINVAL; @@ -231,6 +236,7 @@ static int datablob_parse(char *datablob, const char **format, "when called from .update method\n", keyword); break; } + *decrypted_data = strsep(&datablob, " \t"); ret = 0; break; case Opt_load: @@ -595,7 +601,8 @@ static int derived_key_decrypt(struct encrypted_key_payload *epayload, static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, const char *format, const char *master_desc, - const char *datalen) + const char *datalen, + const char *decrypted_data) { struct encrypted_key_payload *epayload = NULL; unsigned short datablob_len; @@ -604,6 +611,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, unsigned int encrypted_datalen; unsigned int format_len; long dlen; + int i; int ret; ret = kstrtol(datalen, 10, &dlen); @@ -613,6 +621,24 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, format_len = (!format) ? strlen(key_format_default) : strlen(format); decrypted_datalen = dlen; payload_datalen = decrypted_datalen; + + if (decrypted_data) { + if (!user_decrypted_data) { + pr_err("encrypted key: instantiation of keys using provided decrypted data is disabled since CONFIG_USER_DECRYPTED_DATA is set to false\n"); + return ERR_PTR(-EINVAL); + } + if (strlen(decrypted_data) != decrypted_datalen) { + pr_err("encrypted key: decrypted data provided does not match decrypted data length provided\n"); + return ERR_PTR(-EINVAL); + } + for (i = 0; i < strlen(decrypted_data); i++) { + if (!isxdigit(decrypted_data[i])) { + pr_err("encrypted key: decrypted data provided must contain only hexadecimal characters\n"); + return ERR_PTR(-EINVAL); + } + } + } + if (format) { if (!strcmp(format, key_format_ecryptfs)) { if (dlen != ECRYPTFS_MAX_KEY_BYTES) { @@ -740,13 +766,14 @@ static void __ekey_init(struct encrypted_key_payload *epayload, /* * encrypted_init - initialize an encrypted key * - * For a new key, use a random number for both the iv and data - * itself. For an old key, decrypt the hex encoded data. + * For a new key, use either a random number or user-provided decrypted data in + * case it is provided. A random number is used for the iv in both cases. For + * an old key, decrypt the hex encoded data. */ static int encrypted_init(struct encrypted_key_payload *epayload, const char *key_desc, const char *format, const char *master_desc, const char *datalen, - const char *hex_encoded_iv) + const char *hex_encoded_iv, const char *decrypted_data) { int ret = 0; @@ -760,21 +787,26 @@ static int encrypted_init(struct encrypted_key_payload *epayload, } __ekey_init(epayload, format, master_desc, datalen); - if (!hex_encoded_iv) { - get_random_bytes(epayload->iv, ivsize); - - get_random_bytes(epayload->decrypted_data, - epayload->decrypted_datalen); - } else + if (hex_encoded_iv) { ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv); + } else if (decrypted_data) { + get_random_bytes(epayload->iv, ivsize); + memcpy(epayload->decrypted_data, decrypted_data, + epayload->decrypted_datalen); + } else { + get_random_bytes(epayload->iv, ivsize); + get_random_bytes(epayload->decrypted_data, epayload->decrypted_datalen); + } return ret; } /* * encrypted_instantiate - instantiate an encrypted key * - * Decrypt an existing encrypted datablob or create a new encrypted key - * based on a kernel random number. + * Instantiates the key: + * - by decrypting an existing encrypted datablob, or + * - by creating a new encrypted key based on a kernel random number, or + * - using provided decrypted data. * * On success, return 0. Otherwise return errno. */ @@ -787,6 +819,7 @@ static int encrypted_instantiate(struct key *key, char *master_desc = NULL; char *decrypted_datalen = NULL; char *hex_encoded_iv = NULL; + char *decrypted_data = NULL; size_t datalen = prep->datalen; int ret; @@ -799,18 +832,18 @@ static int encrypted_instantiate(struct key *key, datablob[datalen] = 0; memcpy(datablob, prep->data, datalen); ret = datablob_parse(datablob, &format, &master_desc, - &decrypted_datalen, &hex_encoded_iv); + &decrypted_datalen, &hex_encoded_iv, &decrypted_data); if (ret < 0) goto out; epayload = encrypted_key_alloc(key, format, master_desc, - decrypted_datalen); + decrypted_datalen, decrypted_data); if (IS_ERR(epayload)) { ret = PTR_ERR(epayload); goto out; } ret = encrypted_init(epayload, key->description, format, master_desc, - decrypted_datalen, hex_encoded_iv); + decrypted_datalen, hex_encoded_iv, decrypted_data); if (ret < 0) { kfree_sensitive(epayload); goto out; @@ -860,7 +893,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) buf[datalen] = 0; memcpy(buf, prep->data, datalen); - ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL); + ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL, NULL); if (ret < 0) goto out; @@ -869,7 +902,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) goto out; new_epayload = encrypted_key_alloc(key, epayload->format, - new_master_desc, epayload->datalen); + new_master_desc, epayload->datalen, NULL); if (IS_ERR(new_epayload)) { ret = PTR_ERR(new_epayload); goto out;