diff options
Diffstat (limited to 'fs/crypto/hooks.c')
| -rw-r--r-- | fs/crypto/hooks.c | 69 | 
1 files changed, 53 insertions, 16 deletions
diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 56debb1fcf5e..bd525f7573a4 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only  /*   * fs/crypto/hooks.c   * @@ -49,7 +50,8 @@ int fscrypt_file_open(struct inode *inode, struct file *filp)  }  EXPORT_SYMBOL_GPL(fscrypt_file_open); -int __fscrypt_prepare_link(struct inode *inode, struct inode *dir) +int __fscrypt_prepare_link(struct inode *inode, struct inode *dir, +			   struct dentry *dentry)  {  	int err; @@ -57,6 +59,10 @@ int __fscrypt_prepare_link(struct inode *inode, struct inode *dir)  	if (err)  		return err; +	/* ... in case we looked up ciphertext name before key was added */ +	if (dentry->d_flags & DCACHE_ENCRYPTED_NAME) +		return -ENOKEY; +  	if (!fscrypt_has_permitted_context(dir, inode))  		return -EXDEV; @@ -78,6 +84,11 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry,  	if (err)  		return err; +	/* ... in case we looked up ciphertext name(s) before key was added */ +	if ((old_dentry->d_flags | new_dentry->d_flags) & +	    DCACHE_ENCRYPTED_NAME) +		return -ENOKEY; +  	if (old_dir != new_dir) {  		if (IS_ENCRYPTED(new_dir) &&  		    !fscrypt_has_permitted_context(new_dir, @@ -94,21 +105,21 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry,  }  EXPORT_SYMBOL_GPL(__fscrypt_prepare_rename); -int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry) +int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry, +			     struct fscrypt_name *fname)  { -	int err = fscrypt_get_encryption_info(dir); +	int err = fscrypt_setup_filename(dir, &dentry->d_name, 1, fname); -	if (err) +	if (err && err != -ENOENT)  		return err; -	if (fscrypt_has_encryption_key(dir)) { +	if (fname->is_ciphertext_name) {  		spin_lock(&dentry->d_lock); -		dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY; +		dentry->d_flags |= DCACHE_ENCRYPTED_NAME;  		spin_unlock(&dentry->d_lock); +		d_set_d_op(dentry, &fscrypt_d_ops);  	} - -	d_set_d_op(dentry, &fscrypt_d_ops); -	return 0; +	return err;  }  EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup); @@ -179,11 +190,9 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,  	sd->len = cpu_to_le16(ciphertext_len);  	err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len); -	if (err) { -		if (!disk_link->name) -			kfree(sd); -		return err; -	} +	if (err) +		goto err_free_sd; +  	/*  	 * Null-terminating the ciphertext doesn't make sense, but we still  	 * count the null terminator in the length, so we might as well @@ -191,9 +200,20 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,  	 */  	sd->encrypted_path[ciphertext_len] = '\0'; +	/* Cache the plaintext symlink target for later use by get_link() */ +	err = -ENOMEM; +	inode->i_link = kmemdup(target, len + 1, GFP_NOFS); +	if (!inode->i_link) +		goto err_free_sd; +  	if (!disk_link->name)  		disk_link->name = (unsigned char *)sd;  	return 0; + +err_free_sd: +	if (!disk_link->name) +		kfree(sd); +	return err;  }  EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink); @@ -202,7 +222,7 @@ EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink);   * @inode: the symlink inode   * @caddr: the on-disk contents of the symlink   * @max_size: size of @caddr buffer - * @done: if successful, will be set up to free the returned target + * @done: if successful, will be set up to free the returned target if needed   *   * If the symlink's encryption key is available, we decrypt its target.   * Otherwise, we encode its target for presentation. @@ -217,12 +237,18 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,  {  	const struct fscrypt_symlink_data *sd;  	struct fscrypt_str cstr, pstr; +	bool has_key;  	int err;  	/* This is for encrypted symlinks only */  	if (WARN_ON(!IS_ENCRYPTED(inode)))  		return ERR_PTR(-EINVAL); +	/* If the decrypted target is already cached, just return it. */ +	pstr.name = READ_ONCE(inode->i_link); +	if (pstr.name) +		return pstr.name; +  	/*  	 * Try to set up the symlink's encryption key, but we can continue  	 * regardless of whether the key is available or not. @@ -230,6 +256,7 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,  	err = fscrypt_get_encryption_info(inode);  	if (err)  		return ERR_PTR(err); +	has_key = fscrypt_has_encryption_key(inode);  	/*  	 * For historical reasons, encrypted symlink targets are prefixed with @@ -261,7 +288,17 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,  		goto err_kfree;  	pstr.name[pstr.len] = '\0'; -	set_delayed_call(done, kfree_link, pstr.name); + +	/* +	 * Cache decrypted symlink targets in i_link for later use.  Don't cache +	 * symlink targets encoded without the key, since those become outdated +	 * once the key is added.  This pairs with the READ_ONCE() above and in +	 * the VFS path lookup code. +	 */ +	if (!has_key || +	    cmpxchg_release(&inode->i_link, NULL, pstr.name) != NULL) +		set_delayed_call(done, kfree_link, pstr.name); +  	return pstr.name;  err_kfree:  |