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: > > -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