895 lines · 24.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * eCryptfs: Linux filesystem encryption layer * * Copyright (C) 1997-2003 Erez Zadok * Copyright (C) 2001-2003 Stony Brook University * Copyright (C) 2004-2007 International Business Machines Corp. *   Author(s): Michael A. Halcrow <mahalcro@us.ibm.com> *              Michael C. Thompson <mcthomps@us.ibm.com> *              Tyler Hicks <code@tyhicks.com> */ #include <linux/dcache.h>#include <linux/file.h>#include <linux/fips.h>#include <linux/module.h>#include <linux/namei.h>#include <linux/skbuff.h>#include <linux/pagemap.h>#include <linux/key.h>#include <linux/fs_context.h>#include <linux/fs_parser.h>#include <linux/fs_stack.h>#include <linux/sysfs.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/magic.h>#include "ecryptfs_kernel.h" /* * Module parameter that defines the ecryptfs_verbosity level. */int ecryptfs_verbosity = 0; module_param(ecryptfs_verbosity, int, 0);MODULE_PARM_DESC(ecryptfs_verbosity,		 "Initial verbosity level (0 or 1; defaults to "		 "0, which is Quiet)"); /* * Module parameter that defines the number of message buffer elements */unsigned int ecryptfs_message_buf_len = ECRYPTFS_DEFAULT_MSG_CTX_ELEMS; module_param(ecryptfs_message_buf_len, uint, 0);MODULE_PARM_DESC(ecryptfs_message_buf_len,		 "Number of message buffer elements"); /* * Module parameter that defines the maximum guaranteed amount of time to wait * for a response from ecryptfsd.  The actual sleep time will be, more than * likely, a small amount greater than this specified value, but only less if * the message successfully arrives. */signed long ecryptfs_message_wait_timeout = ECRYPTFS_MAX_MSG_CTX_TTL / HZ; module_param(ecryptfs_message_wait_timeout, long, 0);MODULE_PARM_DESC(ecryptfs_message_wait_timeout,		 "Maximum number of seconds that an operation will "		 "sleep while waiting for a message response from "		 "userspace"); /* * Module parameter that is an estimate of the maximum number of users * that will be concurrently using eCryptfs. Set this to the right * value to balance performance and memory use. */unsigned int ecryptfs_number_of_users = ECRYPTFS_DEFAULT_NUM_USERS; module_param(ecryptfs_number_of_users, uint, 0);MODULE_PARM_DESC(ecryptfs_number_of_users, "An estimate of the number of "		 "concurrent users of eCryptfs"); void __ecryptfs_printk(const char *fmt, ...){	va_list args;	va_start(args, fmt);	if (fmt[1] == '7') { /* KERN_DEBUG */		if (ecryptfs_verbosity >= 1)			vprintk(fmt, args);	} else		vprintk(fmt, args);	va_end(args);} /* * ecryptfs_init_lower_file * @ecryptfs_dentry: Fully initialized eCryptfs dentry object, with *                   the lower dentry and the lower mount set * * eCryptfs only ever keeps a single open file for every lower * inode. All I/O operations to the lower inode occur through that * file. When the first eCryptfs dentry that interposes with the first * lower dentry for that inode is created, this function creates the * lower file struct and associates it with the eCryptfs * inode. When all eCryptfs files associated with the inode are released, the * file is closed. * * The lower file will be opened with read/write permissions, if * possible. Otherwise, it is opened read-only. * * This function does nothing if a lower file is already * associated with the eCryptfs inode. * * Returns zero on success; non-zero otherwise */static int ecryptfs_init_lower_file(struct dentry *dentry,				    struct file **lower_file){	const struct cred *cred = current_cred();	struct path path = ecryptfs_lower_path(dentry);	int rc; 	rc = ecryptfs_privileged_open(lower_file, path.dentry, path.mnt, cred);	if (rc) {		printk(KERN_ERR "Error opening lower file "		       "for lower_dentry [0x%p] and lower_mnt [0x%p]; "		       "rc = [%d]\n", path.dentry, path.mnt, rc);		(*lower_file) = NULL;	}	return rc;} int ecryptfs_get_lower_file(struct dentry *dentry, struct inode *inode){	struct ecryptfs_inode_info *inode_info;	int count, rc = 0; 	inode_info = ecryptfs_inode_to_private(inode);	mutex_lock(&inode_info->lower_file_mutex);	count = atomic_inc_return(&inode_info->lower_file_count);	if (WARN_ON_ONCE(count < 1))		rc = -EINVAL;	else if (count == 1) {		rc = ecryptfs_init_lower_file(dentry,					      &inode_info->lower_file);		if (rc)			atomic_set(&inode_info->lower_file_count, 0);	}	mutex_unlock(&inode_info->lower_file_mutex);	return rc;} void ecryptfs_put_lower_file(struct inode *inode){	struct ecryptfs_inode_info *inode_info; 	inode_info = ecryptfs_inode_to_private(inode);	if (atomic_dec_and_mutex_lock(&inode_info->lower_file_count,				      &inode_info->lower_file_mutex)) {		filemap_write_and_wait(inode->i_mapping);		fput(inode_info->lower_file);		inode_info->lower_file = NULL;		mutex_unlock(&inode_info->lower_file_mutex);	}} enum {	Opt_sig, Opt_ecryptfs_sig, Opt_cipher, Opt_ecryptfs_cipher,	Opt_ecryptfs_key_bytes, Opt_passthrough, Opt_xattr_metadata,	Opt_encrypted_view, Opt_fnek_sig, Opt_fn_cipher,	Opt_fn_cipher_key_bytes, Opt_unlink_sigs, Opt_mount_auth_tok_only,	Opt_check_dev_ruid}; static const struct fs_parameter_spec ecryptfs_fs_param_spec[] = {	fsparam_string	("sig",			    Opt_sig),	fsparam_string	("ecryptfs_sig",	    Opt_ecryptfs_sig),	fsparam_string	("cipher",		    Opt_cipher),	fsparam_string	("ecryptfs_cipher",	    Opt_ecryptfs_cipher),	fsparam_u32	("ecryptfs_key_bytes",	    Opt_ecryptfs_key_bytes),	fsparam_flag	("ecryptfs_passthrough",    Opt_passthrough),	fsparam_flag	("ecryptfs_xattr_metadata", Opt_xattr_metadata),	fsparam_flag	("ecryptfs_encrypted_view", Opt_encrypted_view),	fsparam_string	("ecryptfs_fnek_sig",	    Opt_fnek_sig),	fsparam_string	("ecryptfs_fn_cipher",	    Opt_fn_cipher),	fsparam_u32	("ecryptfs_fn_key_bytes",   Opt_fn_cipher_key_bytes),	fsparam_flag	("ecryptfs_unlink_sigs",    Opt_unlink_sigs),	fsparam_flag	("ecryptfs_mount_auth_tok_only", Opt_mount_auth_tok_only),	fsparam_flag	("ecryptfs_check_dev_ruid", Opt_check_dev_ruid),	{}}; static int ecryptfs_init_global_auth_toks(	struct ecryptfs_mount_crypt_stat *mount_crypt_stat){	struct ecryptfs_global_auth_tok *global_auth_tok;	struct ecryptfs_auth_tok *auth_tok;	int rc = 0; 	list_for_each_entry(global_auth_tok,			    &mount_crypt_stat->global_auth_tok_list,			    mount_crypt_stat_list) {		rc = ecryptfs_keyring_auth_tok_for_sig(			&global_auth_tok->global_auth_tok_key, &auth_tok,			global_auth_tok->sig);		if (rc) {			printk(KERN_ERR "Could not find valid key in user "			       "session keyring for sig specified in mount "			       "option: [%s]\n", global_auth_tok->sig);			global_auth_tok->flags |= ECRYPTFS_AUTH_TOK_INVALID;			goto out;		} else {			global_auth_tok->flags &= ~ECRYPTFS_AUTH_TOK_INVALID;			up_write(&(global_auth_tok->global_auth_tok_key)->sem);		}	}out:	return rc;} static void ecryptfs_init_mount_crypt_stat(	struct ecryptfs_mount_crypt_stat *mount_crypt_stat){	memset((void *)mount_crypt_stat, 0,	       sizeof(struct ecryptfs_mount_crypt_stat));	INIT_LIST_HEAD(&mount_crypt_stat->global_auth_tok_list);	mutex_init(&mount_crypt_stat->global_auth_tok_list_mutex);	mount_crypt_stat->flags |= ECRYPTFS_MOUNT_CRYPT_STAT_INITIALIZED;} struct ecryptfs_fs_context {	/* Mount option status trackers */	bool check_ruid;	bool sig_set;	bool cipher_name_set;	bool cipher_key_bytes_set;	bool fn_cipher_name_set;	bool fn_cipher_key_bytes_set;}; /** * ecryptfs_parse_param * @fc: The ecryptfs filesystem context * @param: The mount parameter to parse * * The signature of the key to use must be the description of a key * already in the keyring. Mounting will fail if the key can not be * found. * * Returns zero on success; non-zero on error */static int ecryptfs_parse_param(	struct fs_context *fc,	struct fs_parameter *param){	int rc;	int opt;	struct fs_parse_result result;	struct ecryptfs_fs_context *ctx = fc->fs_private;	struct ecryptfs_sb_info *sbi = fc->s_fs_info;	struct ecryptfs_mount_crypt_stat *mount_crypt_stat =		&sbi->mount_crypt_stat; 	opt = fs_parse(fc, ecryptfs_fs_param_spec, param, &result);	if (opt < 0)		return opt; 	switch (opt) {	case Opt_sig:	case Opt_ecryptfs_sig:		rc = ecryptfs_add_global_auth_tok(mount_crypt_stat,						  param->string, 0);		if (rc) {			printk(KERN_ERR "Error attempting to register "			       "global sig; rc = [%d]\n", rc);			return rc;		}		ctx->sig_set = 1;		break;	case Opt_cipher:	case Opt_ecryptfs_cipher:		strscpy(mount_crypt_stat->global_default_cipher_name,			param->string);		ctx->cipher_name_set = 1;		break;	case Opt_ecryptfs_key_bytes:		mount_crypt_stat->global_default_cipher_key_size =			result.uint_32;		ctx->cipher_key_bytes_set = 1;		break;	case Opt_passthrough:		mount_crypt_stat->flags |=			ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED;		break;	case Opt_xattr_metadata:		mount_crypt_stat->flags |= ECRYPTFS_XATTR_METADATA_ENABLED;		break;	case Opt_encrypted_view:		mount_crypt_stat->flags |= ECRYPTFS_XATTR_METADATA_ENABLED;		mount_crypt_stat->flags |= ECRYPTFS_ENCRYPTED_VIEW_ENABLED;		break;	case Opt_fnek_sig:		strscpy(mount_crypt_stat->global_default_fnek_sig,			param->string);		rc = ecryptfs_add_global_auth_tok(			mount_crypt_stat,			mount_crypt_stat->global_default_fnek_sig,			ECRYPTFS_AUTH_TOK_FNEK);		if (rc) {			printk(KERN_ERR "Error attempting to register "			       "global fnek sig [%s]; rc = [%d]\n",			       mount_crypt_stat->global_default_fnek_sig, rc);			return rc;		}		mount_crypt_stat->flags |=			(ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES			 | ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK);		break;	case Opt_fn_cipher:		strscpy(mount_crypt_stat->global_default_fn_cipher_name,			param->string);		ctx->fn_cipher_name_set = 1;		break;	case Opt_fn_cipher_key_bytes:		mount_crypt_stat->global_default_fn_cipher_key_bytes =			result.uint_32;		ctx->fn_cipher_key_bytes_set = 1;		break;	case Opt_unlink_sigs:		mount_crypt_stat->flags |= ECRYPTFS_UNLINK_SIGS;		break;	case Opt_mount_auth_tok_only:		mount_crypt_stat->flags |= ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY;		break;	case Opt_check_dev_ruid:		ctx->check_ruid = 1;		break;	default:		return -EINVAL;	} 	return 0;} static int ecryptfs_validate_options(struct fs_context *fc){	int rc = 0;	u8 cipher_code;	struct ecryptfs_fs_context *ctx = fc->fs_private;	struct ecryptfs_sb_info *sbi = fc->s_fs_info;	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;  	mount_crypt_stat = &sbi->mount_crypt_stat; 	if (!ctx->sig_set) {		rc = -EINVAL;		ecryptfs_printk(KERN_ERR, "You must supply at least one valid "				"auth tok signature as a mount "				"parameter; see the eCryptfs README\n");		goto out;	}	if (!ctx->cipher_name_set) {		int cipher_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER); 		BUG_ON(cipher_name_len > ECRYPTFS_MAX_CIPHER_NAME_SIZE);		strscpy(mount_crypt_stat->global_default_cipher_name,			ECRYPTFS_DEFAULT_CIPHER);	}	if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)	    && !ctx->fn_cipher_name_set)		strscpy(mount_crypt_stat->global_default_fn_cipher_name,			mount_crypt_stat->global_default_cipher_name);	if (!ctx->cipher_key_bytes_set)		mount_crypt_stat->global_default_cipher_key_size = 0;	if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)	    && !ctx->fn_cipher_key_bytes_set)		mount_crypt_stat->global_default_fn_cipher_key_bytes =			mount_crypt_stat->global_default_cipher_key_size; 	cipher_code = ecryptfs_code_for_cipher_string(		mount_crypt_stat->global_default_cipher_name,		mount_crypt_stat->global_default_cipher_key_size);	if (!cipher_code) {		ecryptfs_printk(KERN_ERR,				"eCryptfs doesn't support cipher: %s\n",				mount_crypt_stat->global_default_cipher_name);		rc = -EINVAL;		goto out;	} 	mutex_lock(&key_tfm_list_mutex);	if (!ecryptfs_tfm_exists(mount_crypt_stat->global_default_cipher_name,				 NULL)) {		rc = ecryptfs_add_new_key_tfm(			NULL, mount_crypt_stat->global_default_cipher_name,			mount_crypt_stat->global_default_cipher_key_size);		if (rc) {			printk(KERN_ERR "Error attempting to initialize "			       "cipher with name = [%s] and key size = [%td]; "			       "rc = [%d]\n",			       mount_crypt_stat->global_default_cipher_name,			       mount_crypt_stat->global_default_cipher_key_size,			       rc);			rc = -EINVAL;			mutex_unlock(&key_tfm_list_mutex);			goto out;		}	}	if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)	    && !ecryptfs_tfm_exists(		    mount_crypt_stat->global_default_fn_cipher_name, NULL)) {		rc = ecryptfs_add_new_key_tfm(			NULL, mount_crypt_stat->global_default_fn_cipher_name,			mount_crypt_stat->global_default_fn_cipher_key_bytes);		if (rc) {			printk(KERN_ERR "Error attempting to initialize "			       "cipher with name = [%s] and key size = [%td]; "			       "rc = [%d]\n",			       mount_crypt_stat->global_default_fn_cipher_name,			       mount_crypt_stat->global_default_fn_cipher_key_bytes,			       rc);			rc = -EINVAL;			mutex_unlock(&key_tfm_list_mutex);			goto out;		}	}	mutex_unlock(&key_tfm_list_mutex);	rc = ecryptfs_init_global_auth_toks(mount_crypt_stat);	if (rc)		printk(KERN_WARNING "One or more global auth toks could not "		       "properly register; rc = [%d]\n", rc);out:	return rc;} struct kmem_cache *ecryptfs_sb_info_cache;static struct file_system_type ecryptfs_fs_type; /* * ecryptfs_get_tree * @fc: The filesystem context */static int ecryptfs_get_tree(struct fs_context *fc){	struct super_block *s;	struct ecryptfs_fs_context *ctx = fc->fs_private;	struct ecryptfs_sb_info *sbi = fc->s_fs_info;	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;	const char *err = "Getting sb failed";	struct inode *inode;	struct path path;	int rc; 	if (!fc->source) {		rc = -EINVAL;		err = "Device name cannot be null";		goto out;	} 	mount_crypt_stat = &sbi->mount_crypt_stat;	rc = ecryptfs_validate_options(fc);	if (rc) {		err = "Error validating options";		goto out;	} 	if (fips_enabled) {		rc = -EINVAL;		err = "eCryptfs support is disabled due to FIPS";		goto out;	} 	s = sget_fc(fc, NULL, set_anon_super_fc);	if (IS_ERR(s)) {		rc = PTR_ERR(s);		goto out;	} 	rc = super_setup_bdi(s);	if (rc)		goto out1; 	ecryptfs_set_superblock_private(s, sbi); 	/* ->kill_sb() will take care of sbi after that point */	sbi = NULL;	s->s_op = &ecryptfs_sops;	s->s_xattr = ecryptfs_xattr_handlers;	set_default_d_op(s, &ecryptfs_dops); 	err = "Reading sb failed";	rc = kern_path(fc->source, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);	if (rc) {		ecryptfs_printk(KERN_WARNING, "kern_path() failed\n");		goto out1;	}	if (path.dentry->d_sb->s_type == &ecryptfs_fs_type) {		rc = -EINVAL;		printk(KERN_ERR "Mount on filesystem of type "			"eCryptfs explicitly disallowed due to "			"known incompatibilities\n");		goto out_free;	} 	if (is_idmapped_mnt(path.mnt)) {		rc = -EINVAL;		printk(KERN_ERR "Mounting on idmapped mounts currently disallowed\n");		goto out_free;

Lines 1–500 of 895.