[PATCH v3] fscrypt: add support for the encrypted key type
flat view
From: Eric Biggers <hidden>
Date: 2018-01-29 18:19:43
Also in:
keyrings, linux-fscrypt, linux-integrity, lkml
On Thu, Jan 25, 2018 at 04:37:48PM -0800, Eric Biggers wrote:
On Thu, Jan 18, 2018 at 01:13:59PM +0000, Andr? Draszik wrote:quoted
-static int validate_user_key(struct fscrypt_info *crypt_info, +static inline struct key *fscrypt_get_encrypted_key(const char *description) +{ + if (IS_ENABLED(CONFIG_ENCRYPTED_KEYS)) + return request_key(&key_type_encrypted, description, NULL); + return ERR_PTR(-ENOKEY); +} + +static int validate_keyring_key(struct fscrypt_info *crypt_info, struct fscrypt_context *ctx, u8 *raw_key, const char *prefix, int min_keysize) { char *description; struct key *keyring_key; - struct fscrypt_key *master_key; - const struct user_key_payload *ukp; + const u8 *master_key; + u32 master_key_len; int res; description = kasprintf(GFP_NOFS, "%s%*phN", prefix,@@ -83,39 +93,55 @@ static int validate_user_key(struct fscrypt_info *crypt_info, return -ENOMEM; keyring_key = request_key(&key_type_logon, description, NULL); + if (keyring_key == ERR_PTR(-ENOKEY)) + keyring_key = fscrypt_get_encrypted_key(description); kfree(description); if (IS_ERR(keyring_key)) return PTR_ERR(keyring_key); down_read(&keyring_key->sem); - if (keyring_key->type != &key_type_logon) { + if (keyring_key->type == &key_type_logon) { + const struct user_key_payload *ukp; + const struct fscrypt_key *fk; + + ukp = user_key_payload_locked(keyring_key); + if (!ukp) { + /* key was revoked before we acquired its semaphore */ + res = -EKEYREVOKED; + goto out; + } + if (ukp->datalen != sizeof(struct fscrypt_key)) { + res = -EINVAL; + goto out; + } + fk = (struct fscrypt_key *)ukp->data; + master_key = fk->raw; + master_key_len = fk->size; + } else if (keyring_key->type == &key_type_encrypted) { + const struct encrypted_key_payload *ekp; + + ekp = keyring_key->payload.data[0]; + master_key = ekp->decrypted_data; + master_key_len = ekp->decrypted_datalen; + } else { printk_once(KERN_WARNING - "%s: key type must be logon\n", __func__); + "%s: key type must be logon or encrypted\n", + __func__); res = -ENOKEY; goto out; } - ukp = user_key_payload_locked(keyring_key); - if (!ukp) { - /* key was revoked before we acquired its semaphore */ - res = -EKEYREVOKED; - goto out; - } - if (ukp->datalen != sizeof(struct fscrypt_key)) { - res = -EINVAL; - goto out; - } - master_key = (struct fscrypt_key *)ukp->data; BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE); - if (master_key->size < min_keysize || master_key->size > FS_MAX_KEY_SIZE - || master_key->size % AES_BLOCK_SIZE != 0) { + if (master_key_len < min_keysize || master_key_len > FS_MAX_KEY_SIZE + || master_key_len % AES_BLOCK_SIZE != 0) { printk_once(KERN_WARNING - "%s: key size incorrect: %d\n", - __func__, master_key->size); + "%s: key size incorrect: %u\n", + __func__, master_key_len); res = -ENOKEY; goto out; }The code changes here look fine, but unfortunately there is a lock ordering problem exposed by using a key type that implements the key_type ->read() method. The problem is that encrypted_read() can take a page fault during copy_to_user() while holding the key semaphore, which then (with ext4) can trigger the start of a jbd2 transaction. Meanwhile fscrypt_get_encryption_info() can be called from within a jbd2 transaction via fscrypt_inherit_context(), which results in a different locking order. We probably will need to fix this by removing the call to fscrypt_get_encryption_info() from fscrypt_inherit_context().
Actually, a better idea might be to access the key payload under rcu_read_lock() rather than the key semaphore. It looks like that's possible with both the "logon" and "encrypted" key types. Note that derive_key_aes() can sleep, so the part under the rcu_read_lock() would have to just copy the payload to a temporary buffer, and derive_key_aes() would be done after rcu_read_unlock(), using the temporary buffer. But I think you can just reuse the 'raw_key' buffer, so that the encryption operation in derive_key_aes() is done in-place. Eric -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html