diff options
-rw-r--r-- | fs/ecryptfs/crypto.c | 39 | ||||
-rw-r--r-- | fs/ecryptfs/debug.c | 13 | ||||
-rw-r--r-- | fs/ecryptfs/ecryptfs_kernel.h | 15 | ||||
-rw-r--r-- | fs/ecryptfs/events.c | 146 | ||||
-rw-r--r-- | fs/ecryptfs/keystore.c | 68 | ||||
-rw-r--r-- | fs/ecryptfs/main.c | 19 | ||||
-rw-r--r-- | include/linux/ecryptfs.h | 7 | ||||
-rw-r--r-- | security/pfe/pfk.c | 52 | ||||
-rw-r--r-- | security/pfe/pfk_ice.c | 40 | ||||
-rw-r--r-- | security/pfe/pfk_ice.h | 2 | ||||
-rw-r--r-- | security/pfe/pfk_kc.c | 145 | ||||
-rw-r--r-- | security/pfe/pfk_kc.h | 5 |
12 files changed, 485 insertions, 66 deletions
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 75a68cc247b5..f68e4aa0165e 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -351,9 +351,9 @@ static int crypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, || !(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED)); if (unlikely(ecryptfs_verbosity > 0)) { ecryptfs_printk(KERN_DEBUG, "Key size [%zd]; key:\n", - crypt_stat->key_size); + ecryptfs_get_key_size_to_enc_data(crypt_stat)); ecryptfs_dump_hex(crypt_stat->key, - crypt_stat->key_size); + ecryptfs_get_key_size_to_enc_data(crypt_stat)); } init_completion(&ecr.completion); @@ -372,7 +372,7 @@ static int crypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, /* Consider doing this once, when the file is opened */ if (!(crypt_stat->flags & ECRYPTFS_KEY_SET)) { rc = crypto_ablkcipher_setkey(crypt_stat->tfm, crypt_stat->key, - crypt_stat->key_size); + ecryptfs_get_key_size_to_enc_data(crypt_stat)); if (rc) { ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n", @@ -693,7 +693,7 @@ int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat) "Initializing cipher [%s]; strlen = [%d]; " "key_size_bits = [%zd]\n", crypt_stat->cipher, (int)strlen(crypt_stat->cipher), - crypt_stat->key_size << 3); + ecryptfs_get_key_size_to_enc_data(crypt_stat) << 3); mutex_lock(&crypt_stat->cs_tfm_mutex); if (crypt_stat->tfm) { rc = 0; @@ -775,7 +775,7 @@ int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat) goto out; } rc = ecryptfs_calculate_md5(dst, crypt_stat, crypt_stat->key, - crypt_stat->key_size); + ecryptfs_get_key_size_to_enc_data(crypt_stat)); if (rc) { ecryptfs_printk(KERN_WARNING, "Error attempting to compute " "MD5 while generating root IV\n"); @@ -802,6 +802,33 @@ static void ecryptfs_generate_new_key(struct ecryptfs_crypt_stat *crypt_stat) } } +static int ecryptfs_generate_new_salt(struct ecryptfs_crypt_stat *crypt_stat) +{ + size_t salt_size = 0; + + salt_size = ecryptfs_get_salt_size_for_cipher( + ecryptfs_get_full_cipher(crypt_stat->cipher, + crypt_stat->cipher_mode)); + + if (0 == salt_size) + return 0; + + if (!ecryptfs_check_space_for_salt(crypt_stat->key_size, salt_size)) { + ecryptfs_printk(KERN_WARNING, "not enough space for salt\n"); + crypt_stat->flags |= ECRYPTFS_SECURITY_WARNING; + return -EINVAL; + } + + get_random_bytes(crypt_stat->key + crypt_stat->key_size, salt_size); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Generated new session salt:\n"); + ecryptfs_dump_hex(crypt_stat->key + crypt_stat->key_size, + salt_size); + } + + return 0; +} + /** * ecryptfs_copy_mount_wide_flags_to_inode_flags * @crypt_stat: The inode's cryptographic context @@ -928,6 +955,8 @@ int ecryptfs_new_file_context(struct inode *ecryptfs_inode) crypt_stat->key_size = mount_crypt_stat->global_default_cipher_key_size; ecryptfs_generate_new_key(crypt_stat); + ecryptfs_generate_new_salt(crypt_stat); + rc = ecryptfs_init_crypt_ctx(crypt_stat); if (rc) ecryptfs_printk(KERN_ERR, "Error initializing cryptographic " diff --git a/fs/ecryptfs/debug.c b/fs/ecryptfs/debug.c index 3d2bdf546ec6..1276764f3716 100644 --- a/fs/ecryptfs/debug.c +++ b/fs/ecryptfs/debug.c @@ -119,3 +119,16 @@ void ecryptfs_dump_hex(char *data, int bytes) printk("\n"); } +void ecryptfs_dump_salt_hex(char *data, int key_size, char *cipher) +{ + size_t salt_size = ecryptfs_get_salt_size_for_cipher(cipher); + + if (0 == salt_size) + return; + + if (!ecryptfs_check_space_for_salt(key_size, salt_size)) + return; + + ecryptfs_printk(KERN_DEBUG, "Decrypted session salt key:\n"); + ecryptfs_dump_hex(data + key_size, salt_size); +} diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index c02926a677bc..1c04de22e258 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -623,6 +623,7 @@ int ecryptfs_encrypt_and_encode_filename( const char *name, size_t name_size); struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry); void ecryptfs_dump_hex(char *data, int bytes); +void ecryptfs_dump_salt_hex(char *data, int key_size, char *cipher); int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg, int sg_size); int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat); @@ -777,4 +778,18 @@ void ecryptfs_freepage(struct page *page); struct ecryptfs_events *get_events(void); +size_t ecryptfs_get_salt_size_for_cipher(const char *cipher); + +size_t ecryptfs_get_key_size_to_enc_data( + struct ecryptfs_crypt_stat *crypt_stat); + +size_t ecryptfs_get_key_size_to_store_key( + struct ecryptfs_crypt_stat *crypt_stat); + +size_t ecryptfs_get_key_size_to_restore_key(size_t stored_key_size, + const char *cipher); + +bool ecryptfs_check_space_for_salt(const size_t key_size, + const size_t salt_size); + #endif /* #ifndef ECRYPTFS_KERNEL_H */ diff --git a/fs/ecryptfs/events.c b/fs/ecryptfs/events.c index efce062f5c48..0e970fa70996 100644 --- a/fs/ecryptfs/events.c +++ b/fs/ecryptfs/events.c @@ -73,6 +73,7 @@ int ecryptfs_register_to_events(struct ecryptfs_events *ops) events_ptr->is_cipher_supported_cb = ops->is_cipher_supported_cb; events_ptr->is_hw_crypt_cb = ops->is_hw_crypt_cb; + events_ptr->get_salt_key_size_cb = ops->get_salt_key_size_cb; get_random_bytes(&handle, sizeof(handle)); ret_value = handle; @@ -168,6 +169,29 @@ size_t ecryptfs_get_key_size(void *data) /** * Given ecryptfs data, the function + * returns appropriate salt size. + * + * !!! crypt_stat cipher name and mode must be initialized + */ +size_t ecryptfs_get_salt_size(void *data) +{ + struct ecryptfs_crypt_stat *stat = NULL; + + if (!data) { + ecryptfs_printk(KERN_ERR, + "ecryptfs_get_salt_size: invalid data parameter\n"); + return 0; + } + + stat = (struct ecryptfs_crypt_stat *)data; + return ecryptfs_get_salt_size_for_cipher( + ecryptfs_get_full_cipher(stat->cipher, + stat->cipher_mode)); + +} + +/** + * Given ecryptfs data, the function * returns appropriate cipher. */ const unsigned char *ecryptfs_get_cipher(void *data) @@ -203,6 +227,23 @@ const unsigned char *ecryptfs_get_key(void *data) } /** + * Given ecryptfs data, the function + * returns file encryption salt. + */ +const unsigned char *ecryptfs_get_salt(void *data) +{ + struct ecryptfs_crypt_stat *stat = NULL; + + if (!data) { + ecryptfs_printk(KERN_ERR, + "ecryptfs_get_salt: invalid data parameter\n"); + return NULL; + } + stat = (struct ecryptfs_crypt_stat *)data; + return stat->key + ecryptfs_get_salt_size(data); +} + +/** * Returns ecryptfs events pointer */ inline struct ecryptfs_events *get_events(void) @@ -210,3 +251,108 @@ inline struct ecryptfs_events *get_events(void) return events_ptr; } +/** + * If external crypto module requires salt in addition to key, + * we store it as part of key array (if there is enough space) + * Checks whether a salt key can fit into array allocated for + * regular key + */ +bool ecryptfs_check_space_for_salt(const size_t key_size, + const size_t salt_size) +{ + if ((salt_size + key_size) > ECRYPTFS_MAX_KEY_BYTES) + return false; + + return true; +} + +/* + * If there is salt that is used by external crypto module, it is stored + * in the same array where regular key is. Salt is going to be used by + * external crypto module only, so for all internal crypto operations salt + * should be ignored. + * + * Get key size in cases where it is going to be used for data encryption + * or for all other general purposes + */ +size_t ecryptfs_get_key_size_to_enc_data( + struct ecryptfs_crypt_stat *crypt_stat) +{ + if (!crypt_stat) + return 0; + + return crypt_stat->key_size; +} + +/* + * If there is salt that is used by external crypto module, it is stored + * in the same array where regular key is. Salt is going to be used by + * external crypto module only, but we still need to save and restore it + * (in encrypted form) as part of ecryptfs header along with the regular + * key. + * + * Get key size in cases where it is going to be stored persistently + * + * !!! crypt_stat cipher name and mode must be initialized + */ +size_t ecryptfs_get_key_size_to_store_key( + struct ecryptfs_crypt_stat *crypt_stat) +{ + size_t salt_size = 0; + + if (!crypt_stat) + return 0; + + salt_size = ecryptfs_get_salt_size(crypt_stat); + + if (!ecryptfs_check_space_for_salt(crypt_stat->key_size, salt_size)) { + ecryptfs_printk(KERN_WARNING, + "ecryptfs_get_key_size_to_store_key: not enough space for salt\n"); + return crypt_stat->key_size; + } + + return crypt_stat->key_size + salt_size; +} + +/* + * If there is salt that is used by external crypto module, it is stored + * in the same array where regular key is. Salt is going to be used by + * external crypto module only, but we still need to save and restore it + * (in encrypted form) as part of ecryptfs header along with the regular + * key. + * + * Get key size in cases where it is going to be restored from storage + * + * !!! crypt_stat cipher name and mode must be initialized + */ +size_t ecryptfs_get_key_size_to_restore_key(size_t stored_key_size, + const char *cipher) +{ + size_t salt_size = 0; + + if (!cipher) + return 0; + + salt_size = ecryptfs_get_salt_size_for_cipher(cipher); + + if (salt_size >= stored_key_size) { + ecryptfs_printk(KERN_WARNING, + "ecryptfs_get_key_size_to_restore_key: salt %zu >= stred size %zu\n", + salt_size, stored_key_size); + + return stored_key_size; + } + + return stored_key_size - salt_size; +} + +/** + * Given cipher, the function returns appropriate salt size. + */ +size_t ecryptfs_get_salt_size_for_cipher(const char *cipher) +{ + if (!get_events() || !(get_events()->get_salt_key_size_cb)) + return 0; + + return get_events()->get_salt_key_size_cb(cipher); +} diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 2b61b5719754..70708270c905 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -315,7 +315,8 @@ write_tag_66_packet(char *signature, u8 cipher_code, * | File Encryption Key Size | 1 or 2 bytes | * | File Encryption Key | arbitrary | */ - data_len = (5 + ECRYPTFS_SIG_SIZE_HEX + crypt_stat->key_size); + data_len = (5 + ECRYPTFS_SIG_SIZE_HEX + + ecryptfs_get_key_size_to_store_key(crypt_stat)); *packet = kmalloc(data_len, GFP_KERNEL); message = *packet; if (!message) { @@ -335,8 +336,9 @@ write_tag_66_packet(char *signature, u8 cipher_code, memcpy(&message[i], signature, ECRYPTFS_SIG_SIZE_HEX); i += ECRYPTFS_SIG_SIZE_HEX; /* The encrypted key includes 1 byte cipher code and 2 byte checksum */ - rc = ecryptfs_write_packet_length(&message[i], crypt_stat->key_size + 3, - &packet_size_len); + rc = ecryptfs_write_packet_length(&message[i], + ecryptfs_get_key_size_to_store_key(crypt_stat) + 3, + &packet_size_len); if (rc) { ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet " "header; cannot generate packet length\n"); @@ -344,9 +346,10 @@ write_tag_66_packet(char *signature, u8 cipher_code, } i += packet_size_len; message[i++] = cipher_code; - memcpy(&message[i], crypt_stat->key, crypt_stat->key_size); - i += crypt_stat->key_size; - for (j = 0; j < crypt_stat->key_size; j++) + memcpy(&message[i], crypt_stat->key, + ecryptfs_get_key_size_to_store_key(crypt_stat)); + i += ecryptfs_get_key_size_to_store_key(crypt_stat); + for (j = 0; j < ecryptfs_get_key_size_to_store_key(crypt_stat); j++) checksum += crypt_stat->key[j]; message[i++] = (checksum / 256) % 256; message[i++] = (checksum % 256); @@ -1187,16 +1190,20 @@ decrypt_pki_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok, rc); goto out; } - auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_DECRYPTED_KEY; - memcpy(crypt_stat->key, auth_tok->session_key.decrypted_key, - auth_tok->session_key.decrypted_key_size); - crypt_stat->key_size = auth_tok->session_key.decrypted_key_size; + rc = ecryptfs_cipher_code_to_string(full_cipher, cipher_code); if (rc) { ecryptfs_printk(KERN_ERR, "Cipher code [%d] is invalid\n", cipher_code) - goto out; + goto out; } + + auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_DECRYPTED_KEY; + memcpy(crypt_stat->key, auth_tok->session_key.decrypted_key, + auth_tok->session_key.decrypted_key_size); + crypt_stat->key_size = ecryptfs_get_key_size_to_restore_key( + auth_tok->session_key.decrypted_key_size, full_cipher); + ecryptfs_parse_full_cipher(full_cipher, crypt_stat->cipher, crypt_stat->cipher_mode); @@ -1205,6 +1212,9 @@ decrypt_pki_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok, ecryptfs_printk(KERN_DEBUG, "Decrypted session key:\n"); ecryptfs_dump_hex(crypt_stat->key, crypt_stat->key_size); + + ecryptfs_dump_salt_hex(crypt_stat->key, crypt_stat->key_size, + full_cipher); } out: kfree(msg); @@ -1475,7 +1485,10 @@ parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat, break; default: crypt_stat->key_size = - (*new_auth_tok)->session_key.encrypted_key_size; + ecryptfs_get_key_size_to_restore_key( + (*new_auth_tok)->session_key.encrypted_key_size, + full_cipher); + } rc = ecryptfs_init_crypt_ctx(crypt_stat); if (rc) @@ -1723,7 +1736,7 @@ decrypt_passphrase_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok, mutex_lock(tfm_mutex); rc = crypto_blkcipher_setkey( desc.tfm, auth_tok->token.password.session_key_encryption_key, - crypt_stat->key_size); + auth_tok->token.password.session_key_encryption_key_bytes); if (unlikely(rc < 0)) { mutex_unlock(tfm_mutex); printk(KERN_ERR "Error setting key for crypto context\n"); @@ -1746,6 +1759,9 @@ decrypt_passphrase_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok, crypt_stat->key_size); ecryptfs_dump_hex(crypt_stat->key, crypt_stat->key_size); + ecryptfs_dump_salt_hex(crypt_stat->key, crypt_stat->key_size, + ecryptfs_get_full_cipher(crypt_stat->cipher, + crypt_stat->cipher_mode)); } out: return rc; @@ -1984,12 +2000,13 @@ pki_encrypt_session_key(struct key *auth_tok_key, int rc; rc = write_tag_66_packet(auth_tok->token.private_key.signature, - ecryptfs_code_for_cipher_string( + ecryptfs_code_for_cipher_string( ecryptfs_get_full_cipher( crypt_stat->cipher, crypt_stat->cipher_mode), - crypt_stat->key_size), - crypt_stat, &payload, &payload_len); + ecryptfs_get_key_size_to_enc_data( + crypt_stat)), + crypt_stat, &payload, &payload_len); up_write(&(auth_tok_key->sem)); key_put(auth_tok_key); if (rc) { @@ -2047,7 +2064,7 @@ write_tag_1_packet(char *dest, size_t *remaining_bytes, ecryptfs_from_hex(key_rec->sig, auth_tok->token.private_key.signature, ECRYPTFS_SIG_SIZE); encrypted_session_key_valid = 0; - for (i = 0; i < crypt_stat->key_size; i++) + for (i = 0; i < ecryptfs_get_key_size_to_store_key(crypt_stat); i++) encrypted_session_key_valid |= auth_tok->session_key.encrypted_key[i]; if (encrypted_session_key_valid) { @@ -2233,13 +2250,14 @@ write_tag_3_packet(char *dest, size_t *remaining_bytes, mount_crypt_stat->global_default_cipher_key_size; if (auth_tok->session_key.encrypted_key_size == 0) auth_tok->session_key.encrypted_key_size = - crypt_stat->key_size; + ecryptfs_get_key_size_to_store_key(crypt_stat); if (crypt_stat->key_size == 24 && strcmp("aes", crypt_stat->cipher) == 0) { memset((crypt_stat->key + 24), 0, 8); auth_tok->session_key.encrypted_key_size = 32; } else - auth_tok->session_key.encrypted_key_size = crypt_stat->key_size; + auth_tok->session_key.encrypted_key_size = + ecryptfs_get_key_size_to_store_key(crypt_stat); key_rec->enc_key_size = auth_tok->session_key.encrypted_key_size; encrypted_session_key_valid = 0; @@ -2263,8 +2281,8 @@ write_tag_3_packet(char *dest, size_t *remaining_bytes, auth_tok->token.password. session_key_encryption_key_bytes); memcpy(session_key_encryption_key, - auth_tok->token.password.session_key_encryption_key, - crypt_stat->key_size); + auth_tok->token.password.session_key_encryption_key, + auth_tok->token.password.session_key_encryption_key_bytes); ecryptfs_printk(KERN_DEBUG, "Cached session key encryption key:\n"); if (ecryptfs_verbosity > 0) @@ -2297,7 +2315,7 @@ write_tag_3_packet(char *dest, size_t *remaining_bytes, } mutex_lock(tfm_mutex); rc = crypto_blkcipher_setkey(desc.tfm, session_key_encryption_key, - crypt_stat->key_size); + auth_tok->token.password.session_key_encryption_key_bytes); if (rc < 0) { mutex_unlock(tfm_mutex); ecryptfs_printk(KERN_ERR, "Error setting key for crypto " @@ -2306,7 +2324,11 @@ write_tag_3_packet(char *dest, size_t *remaining_bytes, } rc = 0; ecryptfs_printk(KERN_DEBUG, "Encrypting [%zd] bytes of the key\n", - crypt_stat->key_size); + crypt_stat->key_size); + ecryptfs_printk(KERN_DEBUG, "Encrypting [%zd] bytes of the salt key\n", + ecryptfs_get_salt_size_for_cipher( + ecryptfs_get_full_cipher(crypt_stat->cipher, + crypt_stat->cipher_mode))); rc = crypto_blkcipher_encrypt(&desc, dst_sg, src_sg, (*key_rec).enc_key_size); mutex_unlock(tfm_mutex); diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 7dbccb4b38db..a3bd96a80ade 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -266,6 +266,7 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options, int cipher_key_bytes_set = 0; int fn_cipher_key_bytes; int fn_cipher_key_bytes_set = 0; + size_t salt_size = 0; struct ecryptfs_mount_crypt_stat *mount_crypt_stat = &sbi->mount_crypt_stat; substring_t args[MAX_OPT_ARGS]; @@ -418,8 +419,24 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options, && !fn_cipher_name_set) strcpy(mount_crypt_stat->global_default_fn_cipher_name, mount_crypt_stat->global_default_cipher_name); - if (!cipher_key_bytes_set) + + if (cipher_key_bytes_set) { + + salt_size = ecryptfs_get_salt_size_for_cipher( + ecryptfs_get_full_cipher( + mount_crypt_stat->global_default_cipher_name, + mount_crypt_stat->global_default_cipher_mode)); + + if (!ecryptfs_check_space_for_salt( + mount_crypt_stat->global_default_cipher_key_size, + salt_size)) { + ecryptfs_printk( + KERN_WARNING, + "eCryptfs internal error: no space for salt"); + } + } else mount_crypt_stat->global_default_cipher_key_size = 0; + if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) && !fn_cipher_key_bytes_set) mount_crypt_stat->global_default_fn_cipher_key_bytes = diff --git a/include/linux/ecryptfs.h b/include/linux/ecryptfs.h index 4f3006ae2a42..55433c6c603d 100644 --- a/include/linux/ecryptfs.h +++ b/include/linux/ecryptfs.h @@ -119,7 +119,7 @@ struct ecryptfs_auth_tok { * such as ecryptfs_get_key_size(), ecryptfs_get_cipher() etc. */ struct ecryptfs_events { - bool (*is_cipher_supported_cb)(char *cipher); + bool (*is_cipher_supported_cb)(const char *cipher); void (*open_cb)(struct inode *inode, void *ecrytpfs_data); void (*release_cb)(struct inode *inode); int (*encrypt_cb)(struct page *in_page, struct page *out_page, @@ -127,6 +127,7 @@ struct ecryptfs_events { int (*decrypt_cb)(struct page *in_page, struct page *out_page, struct inode *inode, unsigned long extent_offset); bool (*is_hw_crypt_cb)(void); + size_t (*get_salt_key_size_cb)(const char *cipher); }; @@ -138,6 +139,10 @@ const unsigned char *ecryptfs_get_key(void *ecrytpfs_data); size_t ecryptfs_get_key_size(void *ecrytpfs_data); +const unsigned char *ecryptfs_get_salt(void *ecrytpfs_data); + +size_t ecryptfs_get_salt_size(void *ecrytpfs_data); + const unsigned char *ecryptfs_get_cipher(void *ecrytpfs_data); bool ecryptfs_is_page_in_metadata(void *ecrytpfs_data, pgoff_t offset); diff --git a/security/pfe/pfk.c b/security/pfe/pfk.c index e8d7cb678aed..3c8e2f8fad41 100644 --- a/security/pfe/pfk.c +++ b/security/pfe/pfk.c @@ -65,6 +65,7 @@ static int g_events_handle; /* might be replaced by a table when more than one cipher is supported */ #define PFK_SUPPORTED_CIPHER "aes_xts" #define PFK_SUPPORTED_KEY_SIZE 32 +#define PFK_SUPPORTED_SALT_SIZE 32 static int pfk_inode_alloc_security(struct inode *inode) { @@ -326,12 +327,14 @@ int pfk_load_key(const struct bio *bio, struct ice_crypto_setting *ice_setting) struct inode *inode = NULL; int ret = 0; const unsigned char *key = NULL; + const unsigned char *salt = NULL; const unsigned char *cipher = NULL; void *ecryptfs_data = NULL; u32 key_index = 0; enum ice_cryto_algo_mode algo_mode = 0; enum ice_crpto_key_size key_size_type = 0; size_t key_size = 0; + size_t salt_size = 0; pgoff_t offset; bool is_metadata = false; @@ -386,6 +389,20 @@ int pfk_load_key(const struct bio *bio, struct ice_crypto_setting *ice_setting) goto end; } + salt = ecryptfs_get_salt(ecryptfs_data); + if (!salt) { + pr_err("could not parse salt from ecryptfs\n"); + ret = -EINVAL; + goto end; + } + + salt_size = ecryptfs_get_salt_size(ecryptfs_data); + if (!salt_size) { + pr_err("could not parse salt size from ecryptfs\n"); + ret = -EINVAL; + goto end; + } + cipher = ecryptfs_get_cipher(ecryptfs_data); if (!key) { pr_err("could not parse key from ecryptfs\n"); @@ -401,7 +418,7 @@ int pfk_load_key(const struct bio *bio, struct ice_crypto_setting *ice_setting) if (ret != 0) return ret; - ret = pfk_kc_load_key(key, key_size, &key_index); + ret = pfk_kc_load_key(key, key_size, salt, salt_size, &key_index); if (ret != 0) { pr_err("could not load key into pfk key cache, error %d\n", ret); @@ -503,7 +520,6 @@ bool pfk_allow_merge_bio(struct bio *bio1, struct bio *bio2) goto end; } - /* * at this point both bio's are in the same file which is probably * encrypted, last thing to check is header vs data @@ -587,7 +603,9 @@ static void pfk_open_cb(struct inode *inode, void *ecryptfs_data) static void pfk_release_cb(struct inode *inode) { const unsigned char *key = NULL; - size_t key_size; + const unsigned char *salt = NULL; + size_t key_size = 0; + size_t salt_size = 0; void *data = NULL; if (!pfk_is_ready()) @@ -616,14 +634,26 @@ static void pfk_release_cb(struct inode *inode) return; } - pfk_kc_remove_key(key, key_size); + salt = ecryptfs_get_salt(data); + if (!salt) { + pr_err("could not parse salt from ecryptfs\n"); + return; + } + + salt_size = ecryptfs_get_salt_size(data); + if (!salt_size) { + pr_err("could not parse salt size from ecryptfs\n"); + return; + } + + pfk_kc_remove_key_with_salt(key, key_size, salt, salt_size); mutex_lock(&pfk_lock); pfk_set_ecryptfs_data(inode, NULL); mutex_unlock(&pfk_lock); } -static bool pfk_is_cipher_supported_cb(char *cipher) +static bool pfk_is_cipher_supported_cb(const char *cipher) { if (!pfk_is_ready()) return false; @@ -642,6 +672,17 @@ static bool pfk_is_hw_crypt_cb(void) return true; } +static size_t pfk_get_salt_key_size_cb(const char *cipher) +{ + if (!pfk_is_ready()) + return 0; + + if (!pfk_is_cipher_supported_cb(cipher)) + return 0; + + return PFK_SUPPORTED_SALT_SIZE; +} + static void __exit pfk_exit(void) { @@ -660,6 +701,7 @@ static int __init pfk_init(void) events.release_cb = pfk_release_cb; events.is_cipher_supported_cb = pfk_is_cipher_supported_cb; events.is_hw_crypt_cb = pfk_is_hw_crypt_cb; + events.get_salt_key_size_cb = pfk_get_salt_key_size_cb; g_events_handle = ecryptfs_register_to_events(&events); if (0 == g_events_handle) { diff --git a/security/pfe/pfk_ice.c b/security/pfe/pfk_ice.c index d26dee245cc5..1cb350296159 100644 --- a/security/pfe/pfk_ice.c +++ b/security/pfe/pfk_ice.c @@ -48,46 +48,60 @@ #define TZ_ES_SET_ICE_KEY_PARAM_ID \ - TZ_SYSCALL_CREATE_PARAM_ID_3( \ - TZ_SYSCALL_PARAM_TYPE_VAL, \ - TZ_SYSCALL_PARAM_TYPE_BUF_RW, TZ_SYSCALL_PARAM_TYPE_VAL) + TZ_SYSCALL_CREATE_PARAM_ID_5( \ + TZ_SYSCALL_PARAM_TYPE_VAL, \ + TZ_SYSCALL_PARAM_TYPE_BUF_RW, TZ_SYSCALL_PARAM_TYPE_VAL, \ + TZ_SYSCALL_PARAM_TYPE_BUF_RW, TZ_SYSCALL_PARAM_TYPE_VAL) #define TZ_ES_INVALIDATE_ICE_KEY_PARAM_ID \ TZ_SYSCALL_CREATE_PARAM_ID_1( \ TZ_SYSCALL_PARAM_TYPE_VAL) #define ICE_KEY_SIZE 32 - +#define ICE_SALT_SIZE 32 uint8_t ice_key[ICE_KEY_SIZE]; +uint8_t ice_salt[ICE_KEY_SIZE]; -int qti_pfk_ice_set_key(uint32_t index, uint8_t *key) +int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt) { struct scm_desc desc = {0}; int ret; - char *tzbuf = (char *)ice_key; + char *tzbuf_key = (char *)ice_key; + char *tzbuf_salt = (char *)ice_salt; uint32_t smc_id = 0; - u32 tzbuflen = sizeof(ice_key); + u32 tzbuflen_key = sizeof(ice_key); + u32 tzbuflen_salt = sizeof(ice_salt); if (index < MIN_ICE_KEY_INDEX || index > MAX_ICE_KEY_INDEX) return -EINVAL; - if (!tzbuf) + if (!key || !salt) + return -EINVAL; + + if (!tzbuf_key || !tzbuf_salt) return -ENOMEM; - memset(tzbuf, 0, tzbuflen); - memcpy(ice_key, key, ICE_KEY_SIZE); + memset(tzbuf_key, 0, tzbuflen_key); + memset(tzbuf_salt, 0, tzbuflen_salt); - dmac_flush_range(tzbuf, tzbuf + tzbuflen); + memcpy(ice_key, key, tzbuflen_key); + memcpy(ice_salt, salt, tzbuflen_salt); + + dmac_flush_range(tzbuf_key, tzbuf_key + tzbuflen_key); + dmac_flush_range(tzbuf_salt, tzbuf_salt + tzbuflen_salt); smc_id = TZ_ES_SET_ICE_KEY_ID; pr_debug(" %s , smc_id = 0x%x\n", __func__, smc_id); desc.arginfo = TZ_ES_SET_ICE_KEY_PARAM_ID; desc.args[0] = index; - desc.args[1] = virt_to_phys(tzbuf); - desc.args[2] = tzbuflen; + desc.args[1] = virt_to_phys(tzbuf_key); + desc.args[2] = tzbuflen_key; + desc.args[3] = virt_to_phys(tzbuf_salt); + desc.args[4] = tzbuflen_salt; + ret = scm_call2_atomic(smc_id, &desc); pr_debug(" %s , ret = %d\n", __func__, ret); diff --git a/security/pfe/pfk_ice.h b/security/pfe/pfk_ice.h index b1a5c4c807a3..1d6339a575be 100644 --- a/security/pfe/pfk_ice.h +++ b/security/pfe/pfk_ice.h @@ -26,7 +26,7 @@ int pfk_ice_init(void); int pfk_ice_deinit(void); -int qti_pfk_ice_set_key(uint32_t index, uint8_t *key); +int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt); int qti_pfk_ice_invalidate_key(uint32_t index); diff --git a/security/pfe/pfk_kc.c b/security/pfe/pfk_kc.c index 687663f5a7ac..eff49d0315b5 100644 --- a/security/pfe/pfk_kc.c +++ b/security/pfe/pfk_kc.c @@ -41,15 +41,17 @@ /** the first available index in ice engine */ #define PFK_KC_STARTING_INDEX 2 -/** currently the only supported key size */ +/** currently the only supported key and salt sizes */ #define PFK_KC_KEY_SIZE 32 +#define PFK_KC_SALT_SIZE 32 /** Table size */ /* TODO replace by some constant from ice.h */ #define PFK_KC_TABLE_SIZE ((32) - (PFK_KC_STARTING_INDEX)) -/** The maximum key size */ +/** The maximum key and salt size */ #define PFK_MAX_KEY_SIZE PFK_KC_KEY_SIZE +#define PFK_MAX_SALT_SIZE PFK_KC_SALT_SIZE static DEFINE_SPINLOCK(kc_lock); static bool kc_ready; @@ -57,11 +59,15 @@ static bool kc_ready; struct kc_entry { unsigned char key[PFK_MAX_KEY_SIZE]; size_t key_size; + + unsigned char salt[PFK_MAX_SALT_SIZE]; + size_t salt_size; + u64 time_stamp; u32 key_index; }; -static struct kc_entry kc_table[PFK_KC_TABLE_SIZE] = {{{0} , 0, 0, 0} }; +static struct kc_entry kc_table[PFK_KC_TABLE_SIZE] = {{{0}, 0, {0}, 0, 0, 0} }; /** * pfk_min_time_entry() - update min time and update min entry @@ -96,31 +102,66 @@ static inline bool kc_is_ready(void) } /** - * kc_find_key() - find kc entry + * kc_find_key_at_index() - find kc entry starting at specific index * @key: key to look for * @key_size: the key size + * @salt: salt to look for + * @salt_size: the salt size + * @sarting_index: index to start search with, if entry found, updated with + * index of that entry * * Return entry or NULL in case of error * Should be invoked under lock */ -static struct kc_entry *kc_find_key(const unsigned char *key, size_t key_size) +static struct kc_entry *kc_find_key_at_index(const unsigned char *key, + size_t key_size, const unsigned char *salt, size_t salt_size, + int *starting_index) { struct kc_entry *entry = NULL; int i = 0; - for (i = 0; i < PFK_KC_TABLE_SIZE; i++) { + for (i = *starting_index; i < PFK_KC_TABLE_SIZE; i++) { entry = &(kc_table[i]); + + if (NULL != salt) { + if (entry->salt_size != salt_size) + continue; + + if (0 != memcmp(entry->salt, salt, salt_size)) + continue; + } + if (entry->key_size != key_size) continue; - if (0 == memcmp(entry->key, key, key_size)) + if (0 == memcmp(entry->key, key, key_size)) { + *starting_index = i; return entry; + } } return NULL; } /** + * kc_find_key() - find kc entry + * @key: key to look for + * @key_size: the key size + * @salt: salt to look for + * @salt_size: the salt size + * + * Return entry or NULL in case of error + * Should be invoked under lock + */ +static struct kc_entry *kc_find_key(const unsigned char *key, size_t key_size, + const unsigned char *salt, size_t salt_size) +{ + int index = 0; + + return kc_find_key_at_index(key, key_size, salt, salt_size, &index); +} + +/** * kc_find_oldest_entry() - finds the entry with minimal timestamp * * Returns entry with minimal timestamp. Empty entries have timestamp @@ -183,6 +224,7 @@ static void kc_clear_entry(struct kc_entry *entry, bool clear_qscee) qti_pfk_ice_invalidate_key(entry->key_index); memset(entry->key, 0, entry->key_size); + memset(entry->salt, 0, entry->salt_size); entry->time_stamp = 0; } @@ -194,13 +236,15 @@ static void kc_clear_entry(struct kc_entry *entry, bool clear_qscee) * @entry: entry to replace key in * @key: key * @key_size: key_size + * @salt: salt + * @salt_size: salt_size * * The previous key is securely released and wiped, the new one is loaded * to ICE. * Should be invoked under lock */ static int kc_replace_entry(struct kc_entry *entry, const unsigned char *key, - size_t key_size) + size_t key_size, const unsigned char *salt, size_t salt_size) { int ret = 0; @@ -209,7 +253,11 @@ static int kc_replace_entry(struct kc_entry *entry, const unsigned char *key, memcpy(entry->key, key, key_size); entry->key_size = key_size; - ret = qti_pfk_ice_set_key(entry->key_index, (uint8_t *) key); + memcpy(entry->salt, salt, salt_size); + entry->salt_size = salt_size; + + ret = qti_pfk_ice_set_key(entry->key_index, (uint8_t *) key, + (uint8_t *) salt); if (ret != 0) { ret = -EINVAL; goto err; @@ -265,7 +313,9 @@ int pfk_kc_deinit(void) * pfk_kc_load_key() - retrieve the key from cache or add it if it's not there * return the ICE hw key index * @key: pointer to the key - * @key_size: the size of the key, assumed to be not bigger than + * @key_size: the size of the key + * @salt: pointer to the salt + * @salt_size: the size of the salt * @key_index: the pointer to key_index where the output will be stored * * If key is present in cache, than the key_index will be retrieved from cache. @@ -275,7 +325,8 @@ int pfk_kc_deinit(void) * * Return 0 in case of success, error otherwise */ -int pfk_kc_load_key(const unsigned char *key, size_t key_size, u32 *key_index) +int pfk_kc_load_key(const unsigned char *key, size_t key_size, + const unsigned char *salt, size_t salt_size, u32 *key_index) { int ret = 0; struct kc_entry *entry = NULL; @@ -283,14 +334,17 @@ int pfk_kc_load_key(const unsigned char *key, size_t key_size, u32 *key_index) if (!kc_is_ready()) return -ENODEV; - if (!key || !key_index) + if (!key || !salt || !key_index) return -EPERM; if (key_size != PFK_KC_KEY_SIZE) return -EPERM; + if (salt_size != PFK_KC_SALT_SIZE) + return -EPERM; + spin_lock(&kc_lock); - entry = kc_find_key(key, key_size); + entry = kc_find_key(key, key_size, salt, salt_size); if (!entry) { entry = kc_find_oldest_entry(); if (!entry) { @@ -302,7 +356,7 @@ int pfk_kc_load_key(const unsigned char *key, size_t key_size, u32 *key_index) pr_debug("didn't found key in cache, replacing entry with index %d\n", entry->key_index); - ret = kc_replace_entry(entry, key, key_size); + ret = kc_replace_entry(entry, key, key_size, salt, salt_size); if (ret) { spin_unlock(&kc_lock); return -EINVAL; @@ -322,7 +376,54 @@ int pfk_kc_load_key(const unsigned char *key, size_t key_size, u32 *key_index) /** * pfk_kc_remove_key() - remove the key from cache and from ICE engine * @key: pointer to the key - * @key_size: the size of the key, assumed to be not bigger than + * @key_size: the size of the key + * @salt: pointer to the key + * @salt_size: the size of the key + * + * Return 0 in case of success, error otherwise (also in case of non + * (existing key) + */ +int pfk_kc_remove_key_with_salt(const unsigned char *key, size_t key_size, + const unsigned char *salt, size_t salt_size) +{ + struct kc_entry *entry = NULL; + + if (!kc_is_ready()) + return -ENODEV; + + if (!key) + return -EPERM; + + if (!salt) + return -EPERM; + + if (key_size != PFK_KC_KEY_SIZE) + return -EPERM; + + if (salt_size != PFK_KC_SALT_SIZE) + return -EPERM; + + spin_lock(&kc_lock); + entry = kc_find_key(key, key_size, salt, salt_size); + if (!entry) { + pr_err("key does not exist\n"); + spin_unlock(&kc_lock); + return -EINVAL; + } + + kc_clear_entry(entry, true); + spin_unlock(&kc_lock); + + return 0; +} + +/** + * pfk_kc_remove_key() - remove the key from cache and from ICE engine + * when no salt is available. Will only search key part, if there are several, + * all will be removed + * + * @key: pointer to the key + * @key_size: the size of the key * * Return 0 in case of success, error otherwise (also in case of non * (existing key) @@ -330,6 +431,7 @@ int pfk_kc_load_key(const unsigned char *key, size_t key_size, u32 *key_index) int pfk_kc_remove_key(const unsigned char *key, size_t key_size) { struct kc_entry *entry = NULL; + int index = 0; if (!kc_is_ready()) return -ENODEV; @@ -341,7 +443,8 @@ int pfk_kc_remove_key(const unsigned char *key, size_t key_size) return -EPERM; spin_lock(&kc_lock); - entry = kc_find_key(key, key_size); + + entry = kc_find_key_at_index(key, key_size, NULL, 0, &index); if (!entry) { pr_err("key does not exist\n"); spin_unlock(&kc_lock); @@ -349,6 +452,16 @@ int pfk_kc_remove_key(const unsigned char *key, size_t key_size) } kc_clear_entry(entry, true); + + /* let's clean additional entries with the same key if there are any */ + do { + entry = kc_find_key_at_index(key, key_size, NULL, 0, &index); + if (!entry) + break; + + kc_clear_entry(entry, true); + } while (true); + spin_unlock(&kc_lock); return 0; diff --git a/security/pfe/pfk_kc.h b/security/pfe/pfk_kc.h index 86cc1b43e4f3..07b7827e8ddc 100644 --- a/security/pfe/pfk_kc.h +++ b/security/pfe/pfk_kc.h @@ -17,7 +17,10 @@ int pfk_kc_init(void); int pfk_kc_deinit(void); -int pfk_kc_load_key(const unsigned char *key, size_t key_size, u32 *key_index); +int pfk_kc_load_key(const unsigned char *key, size_t key_size, + const unsigned char *salt, size_t salt_size, u32 *key_index); +int pfk_kc_remove_key_with_salt(const unsigned char *key, size_t key_size, + const unsigned char *salt, size_t salt_size); int pfk_kc_remove_key(const unsigned char *key, size_t key_size); void pfk_kc_clear(void); |