diff options
| author | Greg KH <greg@kroah.com> | 2005-07-07 14:37:53 -0700 | 
|---|---|---|
| committer | Chris Wright <chrisw@osdl.org> | 2005-07-08 18:48:41 -0700 | 
| commit | b67dbf9d4c1987c370fd18fdc4cf9d8aaea604c2 (patch) | |
| tree | 76c8bf2d44a9e8b3fb8df8dedf950bbb78d340ae /security/inode.c | |
| parent | 043d051615aa5da09a7e44f1edbb69798458e067 (diff) | |
| download | olio-linux-3.10-b67dbf9d4c1987c370fd18fdc4cf9d8aaea604c2.tar.xz olio-linux-3.10-b67dbf9d4c1987c370fd18fdc4cf9d8aaea604c2.zip  | |
[PATCH] add securityfs for all LSMs to use
Here's a small patch against 2.6.13-rc2 that adds securityfs, a virtual
fs that all LSMs can use instead of creating their own.  The fs should
be mounted at /sys/kernel/security, and the fs creates that mount point.
This will make the LSB people happy that we aren't creating a new
/my_lsm_fs directory in the root for every different LSM.
It has changed a bit since the last version, thanks to comments from
Mike Waychison.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Chris Wright <chrisw@osdl.org>
Diffstat (limited to 'security/inode.c')
| -rw-r--r-- | security/inode.c | 347 | 
1 files changed, 347 insertions, 0 deletions
diff --git a/security/inode.c b/security/inode.c new file mode 100644 index 00000000000..a5964502ae3 --- /dev/null +++ b/security/inode.c @@ -0,0 +1,347 @@ +/* + *  inode.c - securityfs + * + *  Copyright (C) 2005 Greg Kroah-Hartman <gregkh@suse.de> + * + *	This program is free software; you can redistribute it and/or + *	modify it under the terms of the GNU General Public License version + *	2 as published by the Free Software Foundation. + * + *  Based on fs/debugfs/inode.c which had the following copyright notice: + *    Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com> + *    Copyright (C) 2004 IBM Inc. + */ + +/* #define DEBUG */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/mount.h> +#include <linux/pagemap.h> +#include <linux/init.h> +#include <linux/namei.h> +#include <linux/security.h> + +#define SECURITYFS_MAGIC	0x73636673 + +static struct vfsmount *mount; +static int mount_count; + +/* + * TODO: + *   I think I can get rid of these default_file_ops, but not quite sure... + */ +static ssize_t default_read_file(struct file *file, char __user *buf, +				 size_t count, loff_t *ppos) +{ +	return 0; +} + +static ssize_t default_write_file(struct file *file, const char __user *buf, +				   size_t count, loff_t *ppos) +{ +	return count; +} + +static int default_open(struct inode *inode, struct file *file) +{ +	if (inode->u.generic_ip) +		file->private_data = inode->u.generic_ip; + +	return 0; +} + +static struct file_operations default_file_ops = { +	.read =		default_read_file, +	.write =	default_write_file, +	.open =		default_open, +}; + +static struct inode *get_inode(struct super_block *sb, int mode, dev_t dev) +{ +	struct inode *inode = new_inode(sb); + +	if (inode) { +		inode->i_mode = mode; +		inode->i_uid = 0; +		inode->i_gid = 0; +		inode->i_blksize = PAGE_CACHE_SIZE; +		inode->i_blocks = 0; +		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; +		switch (mode & S_IFMT) { +		default: +			init_special_inode(inode, mode, dev); +			break; +		case S_IFREG: +			inode->i_fop = &default_file_ops; +			break; +		case S_IFDIR: +			inode->i_op = &simple_dir_inode_operations; +			inode->i_fop = &simple_dir_operations; + +			/* directory inodes start off with i_nlink == 2 (for "." entry) */ +			inode->i_nlink++; +			break; +		} +	} +	return inode; +} + +/* SMP-safe */ +static int mknod(struct inode *dir, struct dentry *dentry, +			 int mode, dev_t dev) +{ +	struct inode *inode; +	int error = -EPERM; + +	if (dentry->d_inode) +		return -EEXIST; + +	inode = get_inode(dir->i_sb, mode, dev); +	if (inode) { +		d_instantiate(dentry, inode); +		dget(dentry); +		error = 0; +	} +	return error; +} + +static int mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ +	int res; + +	mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR; +	res = mknod(dir, dentry, mode, 0); +	if (!res) +		dir->i_nlink++; +	return res; +} + +static int create(struct inode *dir, struct dentry *dentry, int mode) +{ +	mode = (mode & S_IALLUGO) | S_IFREG; +	return mknod(dir, dentry, mode, 0); +} + +static inline int positive(struct dentry *dentry) +{ +	return dentry->d_inode && !d_unhashed(dentry); +} + +static int fill_super(struct super_block *sb, void *data, int silent) +{ +	static struct tree_descr files[] = {{""}}; + +	return simple_fill_super(sb, SECURITYFS_MAGIC, files); +} + +static struct super_block *get_sb(struct file_system_type *fs_type, +				        int flags, const char *dev_name, +					void *data) +{ +	return get_sb_single(fs_type, flags, data, fill_super); +} + +static struct file_system_type fs_type = { +	.owner =	THIS_MODULE, +	.name =		"securityfs", +	.get_sb =	get_sb, +	.kill_sb =	kill_litter_super, +}; + +static int create_by_name(const char *name, mode_t mode, +			  struct dentry *parent, +			  struct dentry **dentry) +{ +	int error = 0; + +	*dentry = NULL; + +	/* If the parent is not specified, we create it in the root. +	 * We need the root dentry to do this, which is in the super +	 * block. A pointer to that is in the struct vfsmount that we +	 * have around. +	 */ +	if (!parent ) { +		if (mount && mount->mnt_sb) { +			parent = mount->mnt_sb->s_root; +		} +	} +	if (!parent) { +		pr_debug("securityfs: Ah! can not find a parent!\n"); +		return -EFAULT; +	} + +	down(&parent->d_inode->i_sem); +	*dentry = lookup_one_len(name, parent, strlen(name)); +	if (!IS_ERR(dentry)) { +		if ((mode & S_IFMT) == S_IFDIR) +			error = mkdir(parent->d_inode, *dentry, mode); +		else +			error = create(parent->d_inode, *dentry, mode); +	} else +		error = PTR_ERR(dentry); +	up(&parent->d_inode->i_sem); + +	return error; +} + +/** + * securityfs_create_file - create a file in the securityfs filesystem + * + * @name: a pointer to a string containing the name of the file to create. + * @mode: the permission that the file should have + * @parent: a pointer to the parent dentry for this file.  This should be a + *          directory dentry if set.  If this paramater is NULL, then the + *          file will be created in the root of the securityfs filesystem. + * @data: a pointer to something that the caller will want to get to later + *        on.  The inode.u.generic_ip pointer will point to this value on + *        the open() call. + * @fops: a pointer to a struct file_operations that should be used for + *        this file. + * + * This is the basic "create a file" function for securityfs.  It allows for a + * wide range of flexibility in createing a file, or a directory (if you + * want to create a directory, the securityfs_create_dir() function is + * recommended to be used instead.) + * + * This function will return a pointer to a dentry if it succeeds.  This + * pointer must be passed to the securityfs_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.)  If an error occurs, NULL will be returned. + * + * If securityfs is not enabled in the kernel, the value -ENODEV will be + * returned.  It is not wise to check for this value, but rather, check for + * NULL or !NULL instead as to eliminate the need for #ifdef in the calling + * code. + */ +struct dentry *securityfs_create_file(const char *name, mode_t mode, +				   struct dentry *parent, void *data, +				   struct file_operations *fops) +{ +	struct dentry *dentry = NULL; +	int error; + +	pr_debug("securityfs: creating file '%s'\n",name); + +	error = simple_pin_fs("securityfs", &mount, &mount_count); +	if (error) { +		dentry = ERR_PTR(error); +		goto exit; +	} + +	error = create_by_name(name, mode, parent, &dentry); +	if (error) { +		dentry = ERR_PTR(error); +		simple_release_fs(&mount, &mount_count); +		goto exit; +	} + +	if (dentry->d_inode) { +		if (fops) +			dentry->d_inode->i_fop = fops; +		if (data) +			dentry->d_inode->u.generic_ip = data; +	} +exit: +	return dentry; +} +EXPORT_SYMBOL_GPL(securityfs_create_file); + +/** + * securityfs_create_dir - create a directory in the securityfs filesystem + * + * @name: a pointer to a string containing the name of the directory to + *        create. + * @parent: a pointer to the parent dentry for this file.  This should be a + *          directory dentry if set.  If this paramater is NULL, then the + *          directory will be created in the root of the securityfs filesystem. + * + * This function creates a directory in securityfs with the given name. + * + * This function will return a pointer to a dentry if it succeeds.  This + * pointer must be passed to the securityfs_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.)  If an error occurs, NULL will be returned. + * + * If securityfs is not enabled in the kernel, the value -ENODEV will be + * returned.  It is not wise to check for this value, but rather, check for + * NULL or !NULL instead as to eliminate the need for #ifdef in the calling + * code. + */ +struct dentry *securityfs_create_dir(const char *name, struct dentry *parent) +{ +	return securityfs_create_file(name, +				      S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, +				      parent, NULL, NULL); +} +EXPORT_SYMBOL_GPL(securityfs_create_dir); + +/** + * securityfs_remove - removes a file or directory from the securityfs filesystem + * + * @dentry: a pointer to a the dentry of the file or directory to be + *          removed. + * + * This function removes a file or directory in securityfs that was previously + * created with a call to another securityfs function (like + * securityfs_create_file() or variants thereof.) + * + * This function is required to be called in order for the file to be + * removed, no automatic cleanup of files will happen when a module is + * removed, you are responsible here. + */ +void securityfs_remove(struct dentry *dentry) +{ +	struct dentry *parent; + +	if (!dentry) +		return; + +	parent = dentry->d_parent; +	if (!parent || !parent->d_inode) +		return; + +	down(&parent->d_inode->i_sem); +	if (positive(dentry)) { +		if (dentry->d_inode) { +			if (S_ISDIR(dentry->d_inode->i_mode)) +				simple_rmdir(parent->d_inode, dentry); +			else +				simple_unlink(parent->d_inode, dentry); +			dput(dentry); +		} +	} +	up(&parent->d_inode->i_sem); +	simple_release_fs(&mount, &mount_count); +} +EXPORT_SYMBOL_GPL(securityfs_remove); + +static decl_subsys(security, NULL, NULL); + +static int __init securityfs_init(void) +{ +	int retval; + +	kset_set_kset_s(&security_subsys, kernel_subsys); +	retval = subsystem_register(&security_subsys); +	if (retval) +		return retval; + +	retval = register_filesystem(&fs_type); +	if (retval) +		subsystem_unregister(&security_subsys); +	return retval; +} + +static void __exit securityfs_exit(void) +{ +	simple_release_fs(&mount, &mount_count); +	unregister_filesystem(&fs_type); +	subsystem_unregister(&security_subsys); +} + +core_initcall(securityfs_init); +module_exit(securityfs_exit); +MODULE_LICENSE("GPL"); +  |