Commit ddbf9ef3 authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/pub/scm/linux/kernel/git/chrisw/lsm-2.6

parents 5d54e69c 2c40579b
...@@ -1907,6 +1907,11 @@ extern int register_security (struct security_operations *ops); ...@@ -1907,6 +1907,11 @@ extern int register_security (struct security_operations *ops);
extern int unregister_security (struct security_operations *ops); extern int unregister_security (struct security_operations *ops);
extern int mod_reg_security (const char *name, struct security_operations *ops); extern int mod_reg_security (const char *name, struct security_operations *ops);
extern int mod_unreg_security (const char *name, struct security_operations *ops); extern int mod_unreg_security (const char *name, struct security_operations *ops);
extern struct dentry *securityfs_create_file(const char *name, mode_t mode,
struct dentry *parent, void *data,
struct file_operations *fops);
extern struct dentry *securityfs_create_dir(const char *name, struct dentry *parent);
extern void securityfs_remove(struct dentry *dentry);
#else /* CONFIG_SECURITY */ #else /* CONFIG_SECURITY */
......
...@@ -35,6 +35,7 @@ config KEYS_DEBUG_PROC_KEYS ...@@ -35,6 +35,7 @@ config KEYS_DEBUG_PROC_KEYS
config SECURITY config SECURITY
bool "Enable different security models" bool "Enable different security models"
depends on SYSFS
help help
This allows you to choose different security modules to be This allows you to choose different security modules to be
configured into your kernel. configured into your kernel.
......
...@@ -11,7 +11,7 @@ obj-y += commoncap.o ...@@ -11,7 +11,7 @@ obj-y += commoncap.o
endif endif
# Object file lists # Object file lists
obj-$(CONFIG_SECURITY) += security.o dummy.o obj-$(CONFIG_SECURITY) += security.o dummy.o inode.o
# Must precede capability.o in order to stack properly. # Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
......
/*
* 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");
...@@ -118,69 +118,6 @@ MODULE_PARM_DESC(hideHash, "When set to 0, reading seclvl/passwd from sysfs " ...@@ -118,69 +118,6 @@ MODULE_PARM_DESC(hideHash, "When set to 0, reading seclvl/passwd from sysfs "
} \ } \
} while (0) } while (0)
/**
* kobject stuff
*/
struct subsystem seclvl_subsys;
struct seclvl_obj {
char *name;
struct list_head slot_list;
struct kobject kobj;
};
/**
* There is a seclvl_attribute struct for each file in sysfs.
*
* In our case, we have one of these structs for "passwd" and another
* for "seclvl".
*/
struct seclvl_attribute {
struct attribute attr;
ssize_t(*show) (struct seclvl_obj *, char *);
ssize_t(*store) (struct seclvl_obj *, const char *, size_t);
};
/**
* When this function is called, one of the files in sysfs is being
* written to. attribute->store is a function pointer to whatever the
* struct seclvl_attribute store function pointer points to. It is
* unique for "passwd" and "seclvl".
*/
static ssize_t
seclvl_attr_store(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t len)
{
struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj);
struct seclvl_attribute *attribute =
container_of(attr, struct seclvl_attribute, attr);
return attribute->store ? attribute->store(obj, buf, len) : -EIO;
}
static ssize_t
seclvl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj);
struct seclvl_attribute *attribute =
container_of(attr, struct seclvl_attribute, attr);
return attribute->show ? attribute->show(obj, buf) : -EIO;
}
/**
* Callback function pointers for show and store
*/
static struct sysfs_ops seclvlfs_sysfs_ops = {
.show = seclvl_attr_show,
.store = seclvl_attr_store,
};
static struct kobj_type seclvl_ktype = {
.sysfs_ops = &seclvlfs_sysfs_ops
};
decl_subsys(seclvl, &seclvl_ktype, NULL);
/** /**
* The actual security level. Ranges between -1 and 2 inclusive. * The actual security level. Ranges between -1 and 2 inclusive.
*/ */
...@@ -212,97 +149,44 @@ static int seclvl_sanity(int reqlvl) ...@@ -212,97 +149,44 @@ static int seclvl_sanity(int reqlvl)
return 0; return 0;
} }
/**
* Called whenever the user reads the sysfs handle to this kernel
* object
*/
static ssize_t seclvl_read_file(struct seclvl_obj *obj, char *buff)
{
return snprintf(buff, PAGE_SIZE, "%d\n", seclvl);
}
/** /**
* security level advancement rules: * security level advancement rules:
* Valid levels are -1 through 2, inclusive. * Valid levels are -1 through 2, inclusive.
* From -1, stuck. [ in case compiled into kernel ] * From -1, stuck. [ in case compiled into kernel ]
* From 0 or above, can only increment. * From 0 or above, can only increment.
*/ */
static int do_seclvl_advance(int newlvl) static void do_seclvl_advance(void *data, u64 val)
{ {
if (newlvl <= seclvl) { int ret;
seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl " int newlvl = (int)val;
"[%d]\n", newlvl);
return -EINVAL; ret = seclvl_sanity(newlvl);
} if (ret)
return;
if (newlvl > 2) { if (newlvl > 2) {
seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl " seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl "
"[%d]\n", newlvl); "[%d]\n", newlvl);
return -EINVAL; return;
} }
if (seclvl == -1) { if (seclvl == -1) {
seclvl_printk(1, KERN_WARNING, "Not allowed to advance to " seclvl_printk(1, KERN_WARNING, "Not allowed to advance to "
"seclvl [%d]\n", seclvl); "seclvl [%d]\n", seclvl);
return -EPERM; return;
} }
seclvl = newlvl; seclvl = newlvl; /* would it be more "correct" to set *data? */
return 0; return;
} }
/** static u64 seclvl_int_get(void *data)
* Called whenever the user writes to the sysfs handle to this kernel
* object (seclvl/seclvl). It expects a single-digit number.
*/
static ssize_t
seclvl_write_file(struct seclvl_obj *obj, const char *buff, size_t count)
{ {
unsigned long val; return *(int *)data;
if (count > 2 || (count == 2 && buff[1] != '\n')) {
seclvl_printk(1, KERN_WARNING, "Invalid value passed to "
"seclvl: [%s]\n", buff);
return -EINVAL;
}
val = buff[0] - 48;
if (seclvl_sanity(val)) {
seclvl_printk(1, KERN_WARNING, "Illegal secure level "
"requested: [%d]\n", (int)val);
return -EPERM;
}
if (do_seclvl_advance(val)) {
seclvl_printk(0, KERN_ERR, "Failure advancing security level "
"to %lu\n", val);
}
return count;
} }
/* Generate sysfs_attr_seclvl */ DEFINE_SIMPLE_ATTRIBUTE(seclvl_file_ops, seclvl_int_get, do_seclvl_advance, "%lld\n");
static struct seclvl_attribute sysfs_attr_seclvl =
__ATTR(seclvl, (S_IFREG | S_IRUGO | S_IWUSR), seclvl_read_file,
seclvl_write_file);
static unsigned char hashedPassword[SHA1_DIGEST_SIZE]; static unsigned char hashedPassword[SHA1_DIGEST_SIZE];
/**
* Called whenever the user reads the sysfs passwd handle.
*/
static ssize_t seclvl_read_passwd(struct seclvl_obj *obj, char *buff)
{
/* So just how good *is* your password? :-) */
char tmp[3];
int i = 0;
buff[0] = '\0';
if (hideHash) {
/* Security through obscurity */
return 0;
}
while (i < SHA1_DIGEST_SIZE) {
snprintf(tmp, 3, "%02x", hashedPassword[i]);
strncat(buff, tmp, 2);
i++;
}
strcat(buff, "\n");
return ((SHA1_DIGEST_SIZE * 2) + 1);
}
/** /**
* Converts a block of plaintext of into its SHA1 hashed value. * Converts a block of plaintext of into its SHA1 hashed value.
* *
...@@ -347,12 +231,15 @@ plaintext_to_sha1(unsigned char *hash, const char *plaintext, int len) ...@@ -347,12 +231,15 @@ plaintext_to_sha1(unsigned char *hash, const char *plaintext, int len)
* object. It hashes the password and compares the hashed results. * object. It hashes the password and compares the hashed results.
*/ */
static ssize_t static ssize_t
seclvl_write_passwd(struct seclvl_obj *obj, const char *buff, size_t count) passwd_write_file(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{ {
int i; int i;
unsigned char tmp[SHA1_DIGEST_SIZE]; unsigned char tmp[SHA1_DIGEST_SIZE];
char *page;
int rc; int rc;
int len; int len;
if (!*passwd && !*sha1_passwd) { if (!*passwd && !*sha1_passwd) {
seclvl_printk(0, KERN_ERR, "Attempt to password-unlock the " seclvl_printk(0, KERN_ERR, "Attempt to password-unlock the "
"seclvl module, but neither a plain text " "seclvl module, but neither a plain text "
...@@ -363,13 +250,26 @@ seclvl_write_passwd(struct seclvl_obj *obj, const char *buff, size_t count) ...@@ -363,13 +250,26 @@ seclvl_write_passwd(struct seclvl_obj *obj, const char *buff, size_t count)
"maintainer about this event.\n"); "maintainer about this event.\n");
return -EINVAL; return -EINVAL;
} }
len = strlen(buff);
if (count < 0 || count >= PAGE_SIZE)
return -ENOMEM;
if (*ppos != 0) {
return -EINVAL;
}
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
len = -EFAULT;
if (copy_from_user(page, buf, count))
goto out;
len = strlen(page);
/* ``echo "secret" > seclvl/passwd'' includes a newline */ /* ``echo "secret" > seclvl/passwd'' includes a newline */
if (buff[len - 1] == '\n') { if (page[len - 1] == '\n') {
len--; len--;
} }
/* Hash the password, then compare the hashed values */ /* Hash the password, then compare the hashed values */
if ((rc = plaintext_to_sha1(tmp, buff, len))) { if ((rc = plaintext_to_sha1(tmp, page, len))) {
seclvl_printk(0, KERN_ERR, "Error hashing password: rc = " seclvl_printk(0, KERN_ERR, "Error hashing password: rc = "
"[%d]\n", rc); "[%d]\n", rc);
return rc; return rc;
...@@ -382,13 +282,16 @@ seclvl_write_passwd(struct seclvl_obj *obj, const char *buff, size_t count) ...@@ -382,13 +282,16 @@ seclvl_write_passwd(struct seclvl_obj *obj, const char *buff, size_t count)
seclvl_printk(0, KERN_INFO, seclvl_printk(0, KERN_INFO,
"Password accepted; seclvl reduced to 0.\n"); "Password accepted; seclvl reduced to 0.\n");
seclvl = 0; seclvl = 0;
return count; len = count;
out:
free_page((unsigned long)page);
return len;
} }
/* Generate sysfs_attr_passwd */ static struct file_operations passwd_file_ops = {
static struct seclvl_attribute sysfs_attr_passwd = .write = passwd_write_file,
__ATTR(passwd, (S_IFREG | S_IRUGO | S_IWUSR), seclvl_read_passwd, };
seclvl_write_passwd);
/** /**
* Explicitely disallow ptrace'ing the init process. * Explicitely disallow ptrace'ing the init process.
...@@ -647,22 +550,34 @@ static int processPassword(void) ...@@ -647,22 +550,34 @@ static int processPassword(void)
} }
/** /**
* Sysfs registrations * securityfs registrations
*/ */
static int doSysfsRegistrations(void) struct dentry *dir_ino, *seclvl_ino, *passwd_ino;
static int seclvlfs_register(void)
{ {
int rc = 0; dir_ino = securityfs_create_dir("seclvl", NULL);
if ((rc = subsystem_register(&seclvl_subsys))) { if (!dir_ino)
seclvl_printk(0, KERN_WARNING, return -EFAULT;
"Error [%d] registering seclvl subsystem\n", rc);
return rc; seclvl_ino = securityfs_create_file("seclvl", S_IRUGO | S_IWUSR,
} dir_ino, &seclvl, &seclvl_file_ops);
sysfs_create_file(&seclvl_subsys.kset.kobj, &sysfs_attr_seclvl.attr); if (!seclvl_ino)
goto out_deldir;
if (*passwd || *sha1_passwd) { if (*passwd || *sha1_passwd) {
sysfs_create_file(&seclvl_subsys.kset.kobj, passwd_ino = securityfs_create_file("passwd", S_IRUGO | S_IWUSR,
&sysfs_attr_passwd.attr); dir_ino, NULL, &passwd_file_ops);
if (!passwd_ino)
goto out_delf;
} }
return 0; return 0;
out_deldir:
securityfs_remove(dir_ino);
out_delf:
securityfs_remove(seclvl_ino);
return -EFAULT;
} }
/** /**
...@@ -677,8 +592,6 @@ static int __init seclvl_init(void) ...@@ -677,8 +592,6 @@ static int __init seclvl_init(void)
rc = -EINVAL; rc = -EINVAL;
goto exit; goto exit;
} }
sysfs_attr_seclvl.attr.owner = THIS_MODULE;
sysfs_attr_passwd.attr.owner = THIS_MODULE;
if (initlvl < -1 || initlvl > 2) { if (initlvl < -1 || initlvl > 2) {
seclvl_printk(0, KERN_ERR, "Error: bad initial securelevel " seclvl_printk(0, KERN_ERR, "Error: bad initial securelevel "
"[%d].\n", initlvl); "[%d].\n", initlvl);
...@@ -706,7 +619,7 @@ static int __init seclvl_init(void) ...@@ -706,7 +619,7 @@ static int __init seclvl_init(void)
} /* if primary module registered */ } /* if primary module registered */
secondary = 1; secondary = 1;
} /* if we registered ourselves with the security framework */ } /* if we registered ourselves with the security framework */
if ((rc = doSysfsRegistrations())) { if ((rc = seclvlfs_register())) {
seclvl_printk(0, KERN_ERR, "Error registering with sysfs\n"); seclvl_printk(0, KERN_ERR, "Error registering with sysfs\n");
goto exit; goto exit;
} }
...@@ -724,12 +637,11 @@ static int __init seclvl_init(void) ...@@ -724,12 +637,11 @@ static int __init seclvl_init(void)
*/ */
static void __exit seclvl_exit(void) static void __exit seclvl_exit(void)
{ {
sysfs_remove_file(&seclvl_subsys.kset.kobj, &sysfs_attr_seclvl.attr); securityfs_remove(seclvl_ino);
if (*passwd || *sha1_passwd) { if (*passwd || *sha1_passwd) {
sysfs_remove_file(&seclvl_subsys.kset.kobj, securityfs_remove(passwd_ino);
&sysfs_attr_passwd.attr);
} }
subsystem_unregister(&seclvl_subsys); securityfs_remove(dir_ino);
if (secondary == 1) { if (secondary == 1) {
mod_unreg_security(MY_NAME, &seclvl_ops); mod_unreg_security(MY_NAME, &seclvl_ops);
} else if (unregister_security(&seclvl_ops)) { } else if (unregister_security(&seclvl_ops)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment