1183 lines · 32.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * eCryptfs: Linux filesystem encryption layer * * Copyright (C) 1997-2004 Erez Zadok * Copyright (C) 2001-2004 Stony Brook University * Copyright (C) 2004-2007 International Business Machines Corp. *   Author(s): Michael A. Halcrow <mahalcro@us.ibm.com> *              Michael C. Thompsion <mcthomps@us.ibm.com> */ #include <linux/file.h>#include <linux/vmalloc.h>#include <linux/pagemap.h>#include <linux/dcache.h>#include <linux/namei.h>#include <linux/mount.h>#include <linux/fs_stack.h>#include <linux/slab.h>#include <linux/xattr.h>#include <linux/posix_acl.h>#include <linux/posix_acl_xattr.h>#include <linux/fileattr.h>#include <linux/unaligned.h>#include "ecryptfs_kernel.h" static struct dentry *ecryptfs_start_creating_dentry(struct dentry *dentry){	struct dentry *parent = dget_parent(dentry);	struct dentry *ret; 	ret = start_creating_dentry(ecryptfs_dentry_to_lower(parent),				    ecryptfs_dentry_to_lower(dentry));	dput(parent);	return ret;} static struct dentry *ecryptfs_start_removing_dentry(struct dentry *dentry){	struct dentry *parent = dget_parent(dentry);	struct dentry *ret; 	ret = start_removing_dentry(ecryptfs_dentry_to_lower(parent),				    ecryptfs_dentry_to_lower(dentry));	dput(parent);	return ret;} static int ecryptfs_inode_test(struct inode *inode, void *lower_inode){	return ecryptfs_inode_to_lower(inode) == lower_inode;} static int ecryptfs_inode_set(struct inode *inode, void *opaque){	struct inode *lower_inode = opaque; 	ecryptfs_set_inode_lower(inode, lower_inode);	fsstack_copy_attr_all(inode, lower_inode);	/* i_size will be overwritten for encrypted regular files */	fsstack_copy_inode_size(inode, lower_inode);	inode->i_ino = lower_inode->i_ino;	inode->i_mapping->a_ops = &ecryptfs_aops; 	if (S_ISLNK(inode->i_mode))		inode->i_op = &ecryptfs_symlink_iops;	else if (S_ISDIR(inode->i_mode))		inode->i_op = &ecryptfs_dir_iops;	else		inode->i_op = &ecryptfs_main_iops; 	if (S_ISDIR(inode->i_mode))		inode->i_fop = &ecryptfs_dir_fops;	else if (special_file(inode->i_mode))		init_special_inode(inode, inode->i_mode, inode->i_rdev);	else		inode->i_fop = &ecryptfs_main_fops; 	return 0;} static struct inode *__ecryptfs_get_inode(struct inode *lower_inode,					  struct super_block *sb){	struct inode *inode; 	if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb))		return ERR_PTR(-EXDEV); 	/* Reject dealing with casefold directories. */	if (IS_CASEFOLDED(lower_inode)) {		pr_err_ratelimited("%s: Can't handle casefolded directory.\n",				   __func__);		return ERR_PTR(-EREMOTE);	} 	if (!igrab(lower_inode))		return ERR_PTR(-ESTALE);	inode = iget5_locked(sb, (unsigned long)lower_inode,			     ecryptfs_inode_test, ecryptfs_inode_set,			     lower_inode);	if (!inode) {		iput(lower_inode);		return ERR_PTR(-EACCES);	}	if (!(inode_state_read_once(inode) & I_NEW))		iput(lower_inode); 	return inode;} struct inode *ecryptfs_get_inode(struct inode *lower_inode,				 struct super_block *sb){	struct inode *inode = __ecryptfs_get_inode(lower_inode, sb); 	if (!IS_ERR(inode) && (inode_state_read_once(inode) & I_NEW))		unlock_new_inode(inode); 	return inode;} /** * ecryptfs_interpose * @lower_dentry: Existing dentry in the lower filesystem * @dentry: ecryptfs' dentry * @sb: ecryptfs's super_block * * Interposes upper and lower dentries. * * Returns zero on success; non-zero otherwise */static int ecryptfs_interpose(struct dentry *lower_dentry,			      struct dentry *dentry, struct super_block *sb){	struct inode *inode = ecryptfs_get_inode(d_inode(lower_dentry), sb); 	if (IS_ERR(inode))		return PTR_ERR(inode);	d_instantiate(dentry, inode); 	return 0;} static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,			      struct inode *inode){	struct dentry *lower_dentry;	struct inode *lower_dir;	int rc; 	lower_dentry = ecryptfs_start_removing_dentry(dentry);	if (IS_ERR(lower_dentry))		return PTR_ERR(lower_dentry); 	lower_dir = lower_dentry->d_parent->d_inode;	rc = vfs_unlink(&nop_mnt_idmap, lower_dir, lower_dentry, NULL);	if (rc) {		printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);		goto out_unlock;	}	fsstack_copy_attr_times(dir, lower_dir);	set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);	inode_set_ctime_to_ts(inode, inode_get_ctime(dir));out_unlock:	end_removing(lower_dentry);	if (!rc)		d_drop(dentry);	return rc;} /** * ecryptfs_do_create * @directory_inode: inode of the new file's dentry's parent in ecryptfs * @ecryptfs_dentry: New file's dentry in ecryptfs * @mode: The mode of the new file * * Creates the underlying file and the eCryptfs inode which will link to * it. It will also update the eCryptfs directory inode to mimic the * stat of the lower directory inode. * * Returns the new eCryptfs inode on success; an ERR_PTR on error condition */static struct inode *ecryptfs_do_create(struct inode *directory_inode,		   struct dentry *ecryptfs_dentry, umode_t mode){	int rc;	struct dentry *lower_dentry;	struct inode *lower_dir;	struct inode *inode; 	lower_dentry = ecryptfs_start_creating_dentry(ecryptfs_dentry);	if (IS_ERR(lower_dentry))		return ERR_CAST(lower_dentry);	lower_dir = lower_dentry->d_parent->d_inode;	rc = vfs_create(&nop_mnt_idmap, lower_dentry, mode, NULL);	if (rc) {		printk(KERN_ERR "%s: Failure to create dentry in lower fs; "		       "rc = [%d]\n", __func__, rc);		inode = ERR_PTR(rc);		goto out_lock;	}	inode = __ecryptfs_get_inode(d_inode(lower_dentry),				     directory_inode->i_sb);	if (IS_ERR(inode)) {		vfs_unlink(&nop_mnt_idmap, lower_dir, lower_dentry, NULL);		goto out_lock;	}	fsstack_copy_attr_times(directory_inode, lower_dir);	fsstack_copy_inode_size(directory_inode, lower_dir);out_lock:	end_creating(lower_dentry);	return inode;} /* * ecryptfs_initialize_file * * Cause the file to be changed from a basic empty file to an ecryptfs * file with a header and first data page. * * Returns zero on success */int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry,			     struct inode *ecryptfs_inode){	struct ecryptfs_crypt_stat *crypt_stat =		&ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;	int rc = 0; 	if (S_ISDIR(ecryptfs_inode->i_mode)) {		ecryptfs_printk(KERN_DEBUG, "This is a directory\n");		crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);		goto out;	}	ecryptfs_printk(KERN_DEBUG, "Initializing crypto context\n");	rc = ecryptfs_new_file_context(ecryptfs_inode);	if (rc) {		ecryptfs_printk(KERN_ERR, "Error creating new file "				"context; rc = [%d]\n", rc);		goto out;	}	rc = ecryptfs_get_lower_file(ecryptfs_dentry, ecryptfs_inode);	if (rc) {		printk(KERN_ERR "%s: Error attempting to initialize "			"the lower file for the dentry with name "			"[%pd]; rc = [%d]\n", __func__,			ecryptfs_dentry, rc);		goto out;	}	rc = ecryptfs_write_metadata(ecryptfs_dentry, ecryptfs_inode);	if (rc)		printk(KERN_ERR "Error writing headers; rc = [%d]\n", rc);	ecryptfs_put_lower_file(ecryptfs_inode);out:	return rc;} /* * ecryptfs_create * @mode: The mode of the new file. * * Creates a new file. * * Returns zero on success; non-zero on error condition */static intecryptfs_create(struct mnt_idmap *idmap,		struct inode *directory_inode, struct dentry *ecryptfs_dentry,		umode_t mode){	struct inode *ecryptfs_inode;	int rc; 	ecryptfs_inode = ecryptfs_do_create(directory_inode, ecryptfs_dentry,					    mode);	if (IS_ERR(ecryptfs_inode)) {		ecryptfs_printk(KERN_WARNING, "Failed to create file in"				"lower filesystem\n");		rc = PTR_ERR(ecryptfs_inode);		goto out;	}	/* At this point, a file exists on "disk"; we need to make sure	 * that this on disk file is prepared to be an ecryptfs file */	rc = ecryptfs_initialize_file(ecryptfs_dentry, ecryptfs_inode);	if (rc) {		ecryptfs_do_unlink(directory_inode, ecryptfs_dentry,				   ecryptfs_inode);		iget_failed(ecryptfs_inode);		goto out;	}	d_instantiate_new(ecryptfs_dentry, ecryptfs_inode);out:	return rc;} static int ecryptfs_i_size_read(struct dentry *dentry, struct inode *inode){	struct ecryptfs_crypt_stat *crypt_stat;	int rc; 	rc = ecryptfs_get_lower_file(dentry, inode);	if (rc) {		printk(KERN_ERR "%s: Error attempting to initialize "			"the lower file for the dentry with name "			"[%pd]; rc = [%d]\n", __func__,			dentry, rc);		return rc;	} 	crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;	/* TODO: lock for crypt_stat comparison */	if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED))		ecryptfs_set_default_sizes(crypt_stat); 	rc = ecryptfs_read_and_validate_header_region(inode);	ecryptfs_put_lower_file(inode);	if (rc) {		rc = ecryptfs_read_and_validate_xattr_region(dentry, inode);		if (!rc)			crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;	} 	/* Must return 0 to allow non-eCryptfs files to be looked up, too */	return 0;} /* * ecryptfs_lookup_interpose - Dentry interposition for a lookup */static struct dentry *ecryptfs_lookup_interpose(struct dentry *dentry,				     struct dentry *lower_dentry){	struct dentry *lower_parent = ecryptfs_dentry_to_lower(dentry->d_parent);	struct inode *inode, *lower_inode;	int rc = 0; 	fsstack_copy_attr_atime(d_inode(dentry->d_parent),				d_inode(lower_parent));	BUG_ON(!d_count(lower_dentry)); 	ecryptfs_set_dentry_lower(dentry, lower_dentry); 	/*	 * negative dentry can go positive under us here - its parent is not	 * locked.  That's OK and that could happen just as we return from	 * ecryptfs_lookup() anyway.  Just need to be careful and fetch	 * ->d_inode only once - it's not stable here.	 */	lower_inode = READ_ONCE(lower_dentry->d_inode); 	if (!lower_inode) /* We want to add because we couldn't find in lower */		return d_splice_alias(NULL, dentry); 	inode = __ecryptfs_get_inode(lower_inode, dentry->d_sb);	if (IS_ERR(inode)) {		printk(KERN_ERR "%s: Error interposing; rc = [%ld]\n",		       __func__, PTR_ERR(inode));		return ERR_CAST(inode);	}	if (S_ISREG(inode->i_mode)) {		rc = ecryptfs_i_size_read(dentry, inode);		if (rc) {			make_bad_inode(inode);			return ERR_PTR(rc);		}	} 	if (inode_state_read_once(inode) & I_NEW)		unlock_new_inode(inode);	return d_splice_alias(inode, dentry);} /** * ecryptfs_lookup * @ecryptfs_dir_inode: The eCryptfs directory inode * @ecryptfs_dentry: The eCryptfs dentry that we are looking up * @flags: lookup flags * * Find a file on disk. If the file does not exist, then we'll add it to the * dentry cache and continue on to read it from the disk. */static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,				      struct dentry *ecryptfs_dentry,				      unsigned int flags){	char *encrypted_and_encoded_name = NULL;	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;	struct dentry *lower_dir_dentry, *lower_dentry;	struct qstr qname = QSTR_INIT(ecryptfs_dentry->d_name.name,				      ecryptfs_dentry->d_name.len);	struct dentry *res;	int rc = 0; 	lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent); 	mount_crypt_stat = &ecryptfs_superblock_to_private(				ecryptfs_dentry->d_sb)->mount_crypt_stat;	if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) {		size_t len = qname.len;		rc = ecryptfs_encrypt_and_encode_filename(			&encrypted_and_encoded_name, &len,			mount_crypt_stat, qname.name, len);		if (rc) {			printk(KERN_ERR "%s: Error attempting to encrypt and encode "			       "filename; rc = [%d]\n", __func__, rc);			return ERR_PTR(rc);		}		qname.name = encrypted_and_encoded_name;		qname.len = len;	} 	lower_dentry = lookup_noperm_unlocked(&qname, lower_dir_dentry);	if (IS_ERR(lower_dentry)) {		ecryptfs_printk(KERN_DEBUG, "%s: lookup_noperm() returned "				"[%ld] on lower_dentry = [%s]\n", __func__,				PTR_ERR(lower_dentry),				qname.name);		res = ERR_CAST(lower_dentry);	} else {		res = ecryptfs_lookup_interpose(ecryptfs_dentry, lower_dentry);	}	kfree(encrypted_and_encoded_name);	return res;} static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,			 struct dentry *new_dentry){	struct dentry *lower_old_dentry;	struct dentry *lower_new_dentry;	struct inode *lower_dir;	u64 file_size_save;	int rc; 	file_size_save = i_size_read(d_inode(old_dentry));	lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);	lower_new_dentry = ecryptfs_start_creating_dentry(new_dentry);	if (IS_ERR(lower_new_dentry))		return PTR_ERR(lower_new_dentry);	lower_dir = lower_new_dentry->d_parent->d_inode;	rc = vfs_link(lower_old_dentry, &nop_mnt_idmap, lower_dir,		      lower_new_dentry, NULL);	if (rc || d_really_is_negative(lower_new_dentry))		goto out_lock;	rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb);	if (rc)		goto out_lock;	fsstack_copy_attr_times(dir, lower_dir);	fsstack_copy_inode_size(dir, lower_dir);	set_nlink(d_inode(old_dentry),		  ecryptfs_inode_to_lower(d_inode(old_dentry))->i_nlink);	i_size_write(d_inode(new_dentry), file_size_save);out_lock:	end_creating(lower_new_dentry);	return rc;} static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry){	return ecryptfs_do_unlink(dir, dentry, d_inode(dentry));} static int ecryptfs_symlink(struct mnt_idmap *idmap,			    struct inode *dir, struct dentry *dentry,			    const char *symname){	int rc;	struct dentry *lower_dentry;	struct inode *lower_dir;	char *encoded_symname;	size_t encoded_symlen;	struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL; 	lower_dentry = ecryptfs_start_creating_dentry(dentry);	if (IS_ERR(lower_dentry))		return PTR_ERR(lower_dentry);	lower_dir = lower_dentry->d_parent->d_inode; 	mount_crypt_stat = &ecryptfs_superblock_to_private(		dir->i_sb)->mount_crypt_stat;	rc = ecryptfs_encrypt_and_encode_filename(&encoded_symname,						  &encoded_symlen,						  mount_crypt_stat, symname,						  strlen(symname));	if (rc)		goto out_lock;	rc = vfs_symlink(&nop_mnt_idmap, lower_dir, lower_dentry,			 encoded_symname, NULL);	kfree(encoded_symname);	if (rc || d_really_is_negative(lower_dentry))		goto out_lock;	rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);	if (rc)		goto out_lock;	fsstack_copy_attr_times(dir, lower_dir);	fsstack_copy_inode_size(dir, lower_dir);out_lock:	end_creating(lower_dentry);

Lines 1–500 of 1183.