diff options
Diffstat (limited to 'security/selinux/selinuxfs.c')
| -rw-r--r-- | security/selinux/selinuxfs.c | 710 |
1 files changed, 300 insertions, 410 deletions
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 0a6894cdc54d..896acad1f5f7 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -35,6 +35,7 @@ /* selinuxfs pseudo filesystem for exporting the security policy API. Based on the proc code and the fs/nfsd/nfsctl.c code. */ +#include "initcalls.h" #include "flask.h" #include "avc.h" #include "avc_ss.h" @@ -75,9 +76,7 @@ struct selinux_fs_info { struct dentry *class_dir; unsigned long last_class_ino; bool policy_opened; - struct dentry *policycap_dir; unsigned long last_ino; - struct selinux_state *state; struct super_block *sb; }; @@ -90,7 +89,6 @@ static int selinux_fs_info_create(struct super_block *sb) return -ENOMEM; fsi->last_ino = SEL_INO_NEXT - 1; - fsi->state = &selinux_state; fsi->sb = sb; sb->s_fs_info = fsi; return 0; @@ -99,7 +97,7 @@ static int selinux_fs_info_create(struct super_block *sb) static void selinux_fs_info_free(struct super_block *sb) { struct selinux_fs_info *fsi = sb->s_fs_info; - int i; + unsigned int i; if (fsi) { for (i = 0; i < fsi->bool_num; i++) @@ -119,18 +117,16 @@ static void selinux_fs_info_free(struct super_block *sb) #define BOOL_DIR_NAME "booleans" #define CLASS_DIR_NAME "class" -#define POLICYCAP_DIR_NAME "policy_capabilities" #define TMPBUFLEN 12 static ssize_t sel_read_enforce(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; char tmpbuf[TMPBUFLEN]; ssize_t length; length = scnprintf(tmpbuf, TMPBUFLEN, "%d", - enforcing_enabled(fsi->state)); + enforcing_enabled()); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } @@ -139,11 +135,10 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *page = NULL; ssize_t length; - int old_value, new_value; + int scan_value; + bool old_value, new_value; if (count >= PAGE_SIZE) return -ENOMEM; @@ -157,15 +152,14 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, return PTR_ERR(page); length = -EINVAL; - if (sscanf(page, "%d", &new_value) != 1) + if (sscanf(page, "%d", &scan_value) != 1) goto out; - new_value = !!new_value; + new_value = !!scan_value; - old_value = enforcing_enabled(state); + old_value = enforcing_enabled(); if (new_value != old_value) { - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETENFORCE, NULL); if (length) @@ -176,15 +170,15 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, new_value, old_value, from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); - enforcing_set(state, new_value); + enforcing_set(new_value); if (new_value) - avc_ss_reset(state->avc, 0); + avc_ss_reset(0); selnl_notify_setenforce(new_value); - selinux_status_update_setenforce(state, new_value); + selinux_status_update_setenforce(new_value); if (!new_value) call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL); - selinux_ima_measure_state(state); + selinux_ima_measure_state(); } length = count; out: @@ -204,14 +198,12 @@ static const struct file_operations sel_enforce_ops = { static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char tmpbuf[TMPBUFLEN]; ssize_t length; ino_t ino = file_inode(filp)->i_ino; int handle_unknown = (ino == SEL_REJECT_UNKNOWN) ? - security_get_reject_unknown(state) : - !security_get_allow_unknown(state); + security_get_reject_unknown() : + !security_get_allow_unknown(); length = scnprintf(tmpbuf, TMPBUFLEN, "%d", handle_unknown); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); @@ -224,8 +216,7 @@ static const struct file_operations sel_handle_unknown_ops = { static int sel_open_handle_status(struct inode *inode, struct file *filp) { - struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; - struct page *status = selinux_kernel_status_page(fsi->state); + struct page *status = selinux_kernel_status_page(); if (!status) return -ENOMEM; @@ -262,7 +253,7 @@ static int sel_mmap_handle_status(struct file *filp, if (vma->vm_flags & VM_WRITE) return -EPERM; /* disallow mprotect() turns it into writable */ - vma->vm_flags &= ~VM_MAYWRITE; + vm_flags_clear(vma, VM_MAYWRITE); return remap_pfn_range(vma, vma->vm_start, page_to_pfn(status), @@ -276,25 +267,13 @@ static const struct file_operations sel_handle_status_ops = { .llseek = generic_file_llseek, }; -#ifdef CONFIG_SECURITY_SELINUX_DISABLE static ssize_t sel_write_disable(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; char *page; ssize_t length; int new_value; - int enforcing; - - /* NOTE: we are now officially considering runtime disable as - * deprecated, and using it will become increasingly painful - * (e.g. sleeping/blocking) as we progress through future - * kernel releases until eventually it is removed - */ - pr_err("SELinux: Runtime disable is deprecated, use selinux=0 on the kernel cmdline.\n"); - pr_err("SELinux: https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable\n"); - ssleep(15); if (count >= PAGE_SIZE) return -ENOMEM; @@ -307,31 +286,21 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, if (IS_ERR(page)) return PTR_ERR(page); - length = -EINVAL; - if (sscanf(page, "%d", &new_value) != 1) + if (sscanf(page, "%d", &new_value) != 1) { + length = -EINVAL; goto out; + } + length = count; if (new_value) { - enforcing = enforcing_enabled(fsi->state); - length = selinux_disable(fsi->state); - if (length) - goto out; - audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, - "enforcing=%d old_enforcing=%d auid=%u ses=%u" - " enabled=0 old-enabled=1 lsm=selinux res=1", - enforcing, enforcing, - from_kuid(&init_user_ns, audit_get_loginuid(current)), - audit_get_sessionid(current)); + pr_err("SELinux: https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable\n"); + pr_err("SELinux: Runtime disable is not supported, use selinux=0 on the kernel cmdline.\n"); } - length = count; out: kfree(page); return length; } -#else -#define sel_write_disable NULL -#endif static const struct file_operations sel_disable_ops = { .write = sel_write_disable, @@ -366,21 +335,17 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name, unsigned long *ino); /* declaration for sel_make_policy_nodes */ -static struct dentry *sel_make_disconnected_dir(struct super_block *sb, +static struct dentry *sel_make_swapover_dir(struct super_block *sb, unsigned long *ino); -/* declaration for sel_make_policy_nodes */ -static void sel_remove_entries(struct dentry *de); - static ssize_t sel_read_mls(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; char tmpbuf[TMPBUFLEN]; ssize_t length; length = scnprintf(tmpbuf, TMPBUFLEN, "%d", - security_mls_enabled(fsi->state)); + security_mls_enabled()); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } @@ -397,16 +362,14 @@ struct policy_load_memory { static int sel_open_policy(struct inode *inode, struct file *filp) { struct selinux_fs_info *fsi = inode->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; struct policy_load_memory *plm = NULL; int rc; BUG_ON(filp->private_data); - mutex_lock(&fsi->state->policy_mutex); + mutex_lock(&selinux_state.policy_mutex); - rc = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + rc = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); if (rc) goto err; @@ -420,7 +383,7 @@ static int sel_open_policy(struct inode *inode, struct file *filp) if (!plm) goto err; - rc = security_read_policy(state, &plm->data, &plm->len); + rc = security_read_policy(&plm->data, &plm->len); if (rc) goto err; @@ -434,11 +397,11 @@ static int sel_open_policy(struct inode *inode, struct file *filp) filp->private_data = plm; - mutex_unlock(&fsi->state->policy_mutex); + mutex_unlock(&selinux_state.policy_mutex); return 0; err: - mutex_unlock(&fsi->state->policy_mutex); + mutex_unlock(&selinux_state.policy_mutex); if (plm) vfree(plm->data); @@ -467,8 +430,7 @@ static ssize_t sel_read_policy(struct file *filp, char __user *buf, struct policy_load_memory *plm = filp->private_data; int ret; - ret = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + ret = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); if (ret) return ret; @@ -506,13 +468,13 @@ static int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma) { if (vma->vm_flags & VM_SHARED) { /* do not allow mprotect to make mapping writable */ - vma->vm_flags &= ~VM_MAYWRITE; + vm_flags_clear(vma, VM_MAYWRITE); if (vma->vm_flags & VM_WRITE) return -EACCES; } - vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP); vma->vm_ops = &sel_mmap_policy_ops; return 0; @@ -542,13 +504,14 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi, struct selinux_policy *newpolicy) { int ret = 0; - struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir, *old_dentry; - unsigned int tmp_bool_num, old_bool_num; - char **tmp_bool_names, **old_bool_names; - int *tmp_bool_values, *old_bool_values; + struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir; + struct renamedata rd = {}; + unsigned int bool_num = 0; + char **bool_names = NULL; + int *bool_values = NULL; unsigned long tmp_ino = fsi->last_ino; /* Don't increment last_ino in this function */ - tmp_parent = sel_make_disconnected_dir(fsi->sb, &tmp_ino); + tmp_parent = sel_make_swapover_dir(fsi->sb, &tmp_ino); if (IS_ERR(tmp_parent)) return PTR_ERR(tmp_parent); @@ -566,8 +529,8 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi, goto out; } - ret = sel_make_bools(newpolicy, tmp_bool_dir, &tmp_bool_num, - &tmp_bool_names, &tmp_bool_values); + ret = sel_make_bools(newpolicy, tmp_bool_dir, &bool_num, + &bool_names, &bool_values); if (ret) goto out; @@ -576,38 +539,40 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi, if (ret) goto out; - /* booleans */ - old_dentry = fsi->bool_dir; - lock_rename(tmp_bool_dir, old_dentry); - d_exchange(tmp_bool_dir, fsi->bool_dir); + rd.old_parent = tmp_parent; + rd.new_parent = fsi->sb->s_root; - old_bool_num = fsi->bool_num; - old_bool_names = fsi->bool_pending_names; - old_bool_values = fsi->bool_pending_values; + /* booleans */ + ret = start_renaming_two_dentries(&rd, tmp_bool_dir, fsi->bool_dir); + if (ret) + goto out; - fsi->bool_num = tmp_bool_num; - fsi->bool_pending_names = tmp_bool_names; - fsi->bool_pending_values = tmp_bool_values; + d_exchange(tmp_bool_dir, fsi->bool_dir); - sel_remove_old_bool_data(old_bool_num, old_bool_names, old_bool_values); + swap(fsi->bool_num, bool_num); + swap(fsi->bool_pending_names, bool_names); + swap(fsi->bool_pending_values, bool_values); fsi->bool_dir = tmp_bool_dir; - unlock_rename(tmp_bool_dir, old_dentry); + end_renaming(&rd); /* classes */ - old_dentry = fsi->class_dir; - lock_rename(tmp_class_dir, old_dentry); + ret = start_renaming_two_dentries(&rd, tmp_class_dir, fsi->class_dir); + if (ret) + goto out; + d_exchange(tmp_class_dir, fsi->class_dir); fsi->class_dir = tmp_class_dir; - unlock_rename(tmp_class_dir, old_dentry); + + end_renaming(&rd); out: + sel_remove_old_bool_data(bool_num, bool_names, bool_values); /* Since the other temporary dirs are children of tmp_parent * this will handle all the cleanup in the case of a failure before * the swapover */ - sel_remove_entries(tmp_parent); - dput(tmp_parent); /* d_genocide() only handles the children */ + simple_recursive_removal(tmp_parent, NULL); return ret; } @@ -616,56 +581,57 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_fs_info *fsi; struct selinux_load_state load_state; ssize_t length; void *data = NULL; - mutex_lock(&fsi->state->policy_mutex); + /* no partial writes */ + if (*ppos) + return -EINVAL; + /* no empty policies */ + if (!count) + return -EINVAL; + + mutex_lock(&selinux_state.policy_mutex); - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL); if (length) goto out; - /* No partial writes. */ - length = -EINVAL; - if (*ppos != 0) - goto out; - - length = -ENOMEM; data = vmalloc(count); - if (!data) + if (!data) { + length = -ENOMEM; goto out; - - length = -EFAULT; - if (copy_from_user(data, buf, count) != 0) + } + if (copy_from_user(data, buf, count) != 0) { + length = -EFAULT; goto out; + } - length = security_load_policy(fsi->state, data, count, &load_state); + length = security_load_policy(data, count, &load_state); if (length) { pr_warn_ratelimited("SELinux: failed to load policy\n"); goto out; } - + fsi = file_inode(file)->i_sb->s_fs_info; length = sel_make_policy_nodes(fsi, load_state.policy); if (length) { pr_warn_ratelimited("SELinux: failed to initialize selinuxfs\n"); - selinux_policy_cancel(fsi->state, &load_state); + selinux_policy_cancel(&load_state); goto out; } - selinux_policy_commit(fsi->state, &load_state); - + selinux_policy_commit(&load_state); length = count; - audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, "auid=%u ses=%u lsm=selinux res=1", from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); + out: - mutex_unlock(&fsi->state->policy_mutex); + mutex_unlock(&selinux_state.policy_mutex); vfree(data); return length; } @@ -677,23 +643,20 @@ static const struct file_operations sel_load_ops = { static ssize_t sel_write_context(struct file *file, char *buf, size_t size) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *canon = NULL; u32 sid, len; ssize_t length; - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, NULL); if (length) goto out; - length = security_context_to_sid(state, buf, size, &sid, GFP_KERNEL); + length = security_context_to_sid(buf, size, &sid, GFP_KERNEL); if (length) goto out; - length = security_sid_to_context(state, sid, &canon, &len); + length = security_sid_to_context(sid, &canon, &len); if (length) goto out; @@ -714,25 +677,22 @@ out: static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; char tmpbuf[TMPBUFLEN]; ssize_t length; length = scnprintf(tmpbuf, TMPBUFLEN, "%u", - checkreqprot_get(fsi->state)); + checkreqprot_get()); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; char *page; ssize_t length; unsigned int new_value; - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, NULL); if (length) @@ -749,24 +709,21 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, if (IS_ERR(page)) return PTR_ERR(page); - length = -EINVAL; - if (sscanf(page, "%u", &new_value) != 1) + if (sscanf(page, "%u", &new_value) != 1) { + length = -EINVAL; goto out; + } + length = count; if (new_value) { char comm[sizeof(current->comm)]; - memcpy(comm, current->comm, sizeof(comm)); - pr_err("SELinux: %s (%d) set checkreqprot to 1. This is deprecated and will be rejected in a future kernel release.\n", + strscpy(comm, current->comm); + pr_err("SELinux: %s (%d) set checkreqprot to 1. This is no longer supported.\n", comm, current->pid); } - checkreqprot_set(fsi->state, (new_value ? 1 : 0)); - if (new_value) - ssleep(15); - length = count; - - selinux_ima_measure_state(fsi->state); + selinux_ima_measure_state(); out: kfree(page); @@ -782,16 +739,13 @@ static ssize_t sel_write_validatetrans(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *oldcon = NULL, *newcon = NULL, *taskcon = NULL; char *req = NULL; u32 osid, nsid, tsid; u16 tclass; int rc; - rc = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + rc = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__VALIDATE_TRANS, NULL); if (rc) goto out; @@ -829,19 +783,19 @@ static ssize_t sel_write_validatetrans(struct file *file, if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4) goto out; - rc = security_context_str_to_sid(state, oldcon, &osid, GFP_KERNEL); + rc = security_context_str_to_sid(oldcon, &osid, GFP_KERNEL); if (rc) goto out; - rc = security_context_str_to_sid(state, newcon, &nsid, GFP_KERNEL); + rc = security_context_str_to_sid(newcon, &nsid, GFP_KERNEL); if (rc) goto out; - rc = security_context_str_to_sid(state, taskcon, &tsid, GFP_KERNEL); + rc = security_context_str_to_sid(taskcon, &tsid, GFP_KERNEL); if (rc) goto out; - rc = security_validate_transition_user(state, osid, nsid, tsid, tclass); + rc = security_validate_transition_user(osid, nsid, tsid, tclass); if (!rc) rc = count; out: @@ -911,16 +865,13 @@ static const struct file_operations transaction_ops = { static ssize_t sel_write_access(struct file *file, char *buf, size_t size) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *scon = NULL, *tcon = NULL; u32 ssid, tsid; u16 tclass; struct av_decision avd; ssize_t length; - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_AV, NULL); if (length) goto out; @@ -939,15 +890,15 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) goto out; - length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); + length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); + length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); if (length) goto out; - security_compute_av_user(state, ssid, tsid, tclass, &avd); + security_compute_av_user(ssid, tsid, tclass, &avd); length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%x %x %x %x %u %x", @@ -962,8 +913,6 @@ out: static ssize_t sel_write_create(struct file *file, char *buf, size_t size) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *scon = NULL, *tcon = NULL; char *namebuf = NULL, *objname = NULL; u32 ssid, tsid, newsid; @@ -973,8 +922,7 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) u32 len; int nargs; - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, NULL); if (length) @@ -1005,7 +953,7 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) * either whitespace or multibyte characters, they shall be * encoded based on the percentage-encoding rule. * If not encoded, the sscanf logic picks up only left-half - * of the supplied name; splitted by a whitespace unexpectedly. + * of the supplied name; split by a whitespace unexpectedly. */ char *r, *w; int c1, c2; @@ -1030,20 +978,20 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) objname = namebuf; } - length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); + length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); + length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); if (length) goto out; - length = security_transition_sid_user(state, ssid, tsid, tclass, + length = security_transition_sid_user(ssid, tsid, tclass, objname, &newsid); if (length) goto out; - length = security_sid_to_context(state, newsid, &newcon, &len); + length = security_sid_to_context(newsid, &newcon, &len); if (length) goto out; @@ -1066,8 +1014,6 @@ out: static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *scon = NULL, *tcon = NULL; u32 ssid, tsid, newsid; u16 tclass; @@ -1075,8 +1021,7 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) char *newcon = NULL; u32 len; - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, NULL); if (length) @@ -1096,19 +1041,19 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) goto out; - length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); + length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); + length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); if (length) goto out; - length = security_change_sid(state, ssid, tsid, tclass, &newsid); + length = security_change_sid(ssid, tsid, tclass, &newsid); if (length) goto out; - length = security_sid_to_context(state, newsid, &newcon, &len); + length = security_sid_to_context(newsid, &newcon, &len); if (length) goto out; @@ -1127,17 +1072,19 @@ out: static ssize_t sel_write_user(struct file *file, char *buf, size_t size) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *con = NULL, *user = NULL, *ptr; u32 sid, *sids = NULL; ssize_t length; char *newcon; - int i, rc; - u32 len, nsids; + int rc; + u32 i, len, nsids; + + pr_warn_ratelimited("SELinux: %s (%d) wrote to /sys/fs/selinux/user!" + " This will not be supported in the future; please update your" + " userspace.\n", current->comm, current->pid); + ssleep(5); - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_USER, NULL); if (length) @@ -1157,18 +1104,18 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s", con, user) != 2) goto out; - length = security_context_str_to_sid(state, con, &sid, GFP_KERNEL); + length = security_context_str_to_sid(con, &sid, GFP_KERNEL); if (length) goto out; - length = security_get_user_sids(state, sid, user, &sids, &nsids); + length = security_get_user_sids(sid, user, &sids, &nsids); if (length) goto out; length = sprintf(buf, "%u", nsids) + 1; ptr = buf + length; for (i = 0; i < nsids; i++) { - rc = security_sid_to_context(state, sids[i], &newcon, &len); + rc = security_sid_to_context(sids[i], &newcon, &len); if (rc) { length = rc; goto out; @@ -1192,8 +1139,6 @@ out: static ssize_t sel_write_member(struct file *file, char *buf, size_t size) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *scon = NULL, *tcon = NULL; u32 ssid, tsid, newsid; u16 tclass; @@ -1201,8 +1146,7 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size) char *newcon = NULL; u32 len; - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, NULL); if (length) @@ -1222,19 +1166,19 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) goto out; - length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); + length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); + length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); if (length) goto out; - length = security_member_sid(state, ssid, tsid, tclass, &newsid); + length = security_member_sid(ssid, tsid, tclass, &newsid); if (length) goto out; - length = security_sid_to_context(state, newsid, &newcon, &len); + length = security_sid_to_context(newsid, &newcon, &len); if (length) goto out; @@ -1254,56 +1198,68 @@ out: return length; } -static struct inode *sel_make_inode(struct super_block *sb, int mode) +static struct inode *sel_make_inode(struct super_block *sb, umode_t mode) { struct inode *ret = new_inode(sb); if (ret) { ret->i_mode = mode; - ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret); + simple_inode_init_ts(ret); } return ret; } +static struct dentry *sel_attach(struct dentry *parent, const char *name, + struct inode *inode) +{ + struct dentry *dentry = d_alloc_name(parent, name); + if (unlikely(!dentry)) { + iput(inode); + return ERR_PTR(-ENOMEM); + } + d_make_persistent(dentry, inode); + dput(dentry); + return dentry; +} + +static int sel_attach_file(struct dentry *parent, const char *name, + struct inode *inode) +{ + struct dentry *dentry = sel_attach(parent, name, inode); + return PTR_ERR_OR_ZERO(dentry); +} + static ssize_t sel_read_bool(struct file *filep, char __user *buf, size_t count, loff_t *ppos) { struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info; - char *page = NULL; + char buffer[4]; ssize_t length; ssize_t ret; int cur_enforcing; unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK; const char *name = filep->f_path.dentry->d_name.name; - mutex_lock(&fsi->state->policy_mutex); + mutex_lock(&selinux_state.policy_mutex); ret = -EINVAL; if (index >= fsi->bool_num || strcmp(name, fsi->bool_pending_names[index])) goto out_unlock; - ret = -ENOMEM; - page = (char *)get_zeroed_page(GFP_KERNEL); - if (!page) - goto out_unlock; - - cur_enforcing = security_get_bool_value(fsi->state, index); + cur_enforcing = security_get_bool_value(index); if (cur_enforcing < 0) { ret = cur_enforcing; goto out_unlock; } - length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, - fsi->bool_pending_values[index]); - mutex_unlock(&fsi->state->policy_mutex); - ret = simple_read_from_buffer(buf, count, ppos, page, length); -out_free: - free_page((unsigned long)page); - return ret; + length = scnprintf(buffer, sizeof(buffer), "%d %d", !!cur_enforcing, + !!fsi->bool_pending_values[index]); + mutex_unlock(&selinux_state.policy_mutex); + return simple_read_from_buffer(buf, count, ppos, buffer, length); out_unlock: - mutex_unlock(&fsi->state->policy_mutex); - goto out_free; + mutex_unlock(&selinux_state.policy_mutex); + return ret; } static ssize_t sel_write_bool(struct file *filep, const char __user *buf, @@ -1327,10 +1283,9 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, if (IS_ERR(page)) return PTR_ERR(page); - mutex_lock(&fsi->state->policy_mutex); + mutex_lock(&selinux_state.policy_mutex); - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETBOOL, NULL); if (length) @@ -1352,7 +1307,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, length = count; out: - mutex_unlock(&fsi->state->policy_mutex); + mutex_unlock(&selinux_state.policy_mutex); kfree(page); return length; } @@ -1383,10 +1338,9 @@ static ssize_t sel_commit_bools_write(struct file *filep, if (IS_ERR(page)) return PTR_ERR(page); - mutex_lock(&fsi->state->policy_mutex); + mutex_lock(&selinux_state.policy_mutex); - length = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETBOOL, NULL); if (length) @@ -1398,14 +1352,14 @@ static ssize_t sel_commit_bools_write(struct file *filep, length = 0; if (new_value && fsi->bool_pending_values) - length = security_set_bools(fsi->state, fsi->bool_num, + length = security_set_bools(fsi->bool_num, fsi->bool_pending_values); if (!length) length = count; out: - mutex_unlock(&fsi->state->policy_mutex); + mutex_unlock(&selinux_state.policy_mutex); kfree(page); return length; } @@ -1415,54 +1369,41 @@ static const struct file_operations sel_commit_bools_ops = { .llseek = generic_file_llseek, }; -static void sel_remove_entries(struct dentry *de) -{ - d_genocide(de); - shrink_dcache_parent(de); -} - static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir, unsigned int *bool_num, char ***bool_pending_names, int **bool_pending_values) { int ret; - ssize_t len; - struct dentry *dentry = NULL; - struct inode *inode = NULL; - struct inode_security_struct *isec; - char **names = NULL, *page; + char **names, *page; u32 i, num; - int *values = NULL; - u32 sid; - ret = -ENOMEM; page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) - goto out; + return -ENOMEM; - ret = security_get_bools(newpolicy, &num, &names, &values); + ret = security_get_bools(newpolicy, &num, &names, bool_pending_values); if (ret) goto out; - for (i = 0; i < num; i++) { - ret = -ENOMEM; - dentry = d_alloc_name(bool_dir, names[i]); - if (!dentry) - goto out; + *bool_num = num; + *bool_pending_names = names; - ret = -ENOMEM; - inode = sel_make_inode(bool_dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR); - if (!inode) { - dput(dentry); - goto out; - } + for (i = 0; !ret && i < num; i++) { + struct inode *inode; + struct inode_security_struct *isec; + ssize_t len; + u32 sid; - ret = -ENAMETOOLONG; len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]); if (len >= PAGE_SIZE) { - dput(dentry); - iput(inode); - goto out; + ret = -ENAMETOOLONG; + break; + } + + inode = sel_make_inode(bool_dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR); + if (!inode) { + ret = -ENOMEM; + break; } isec = selinux_inode(inode); @@ -1478,38 +1419,22 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_ isec->initialized = LABEL_INITIALIZED; inode->i_fop = &sel_bool_ops; inode->i_ino = i|SEL_BOOL_INO_OFFSET; - d_add(dentry, inode); - } - *bool_num = num; - *bool_pending_names = names; - *bool_pending_values = values; - free_page((unsigned long)page); - return 0; + ret = sel_attach_file(bool_dir, names[i], inode); + } out: free_page((unsigned long)page); - - if (names) { - for (i = 0; i < num; i++) - kfree(names[i]); - kfree(names); - } - kfree(values); - sel_remove_entries(bool_dir); - return ret; } static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char tmpbuf[TMPBUFLEN]; ssize_t length; length = scnprintf(tmpbuf, TMPBUFLEN, "%u", - avc_get_cache_threshold(state->avc)); + avc_get_cache_threshold()); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } @@ -1518,14 +1443,11 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *page; ssize_t ret; unsigned int new_value; - ret = avc_has_perm(&selinux_state, - current_sid(), SECINITSID_SECURITY, + ret = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETSECPARAM, NULL); if (ret) @@ -1546,7 +1468,7 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file, if (sscanf(page, "%u", &new_value) != 1) goto out; - avc_set_cache_threshold(state->avc, new_value); + avc_set_cache_threshold(new_value); ret = count; out: @@ -1557,8 +1479,6 @@ out: static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *page; ssize_t length; @@ -1566,7 +1486,7 @@ static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, if (!page) return -ENOMEM; - length = avc_get_hash_stats(state->avc, page); + length = avc_get_hash_stats(page); if (length >= 0) length = simple_read_from_buffer(buf, count, ppos, page, length); free_page((unsigned long)page); @@ -1577,8 +1497,6 @@ static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, static ssize_t sel_read_sidtab_hash_stats(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; - struct selinux_state *state = fsi->state; char *page; ssize_t length; @@ -1586,7 +1504,7 @@ static ssize_t sel_read_sidtab_hash_stats(struct file *filp, char __user *buf, if (!page) return -ENOMEM; - length = security_sidtab_hash_stats(state, page); + length = security_sidtab_hash_stats(page); if (length >= 0) length = simple_read_from_buffer(buf, count, ppos, page, length); @@ -1614,7 +1532,7 @@ static const struct file_operations sel_avc_hash_stats_ops = { #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx) { - int cpu; + loff_t cpu; for (cpu = *idx; cpu < nr_cpu_ids; ++cpu) { if (!cpu_possible(cpu)) @@ -1686,7 +1604,8 @@ static int sel_make_avc_files(struct dentry *dir) { struct super_block *sb = dir->d_sb; struct selinux_fs_info *fsi = sb->s_fs_info; - int i; + unsigned int i; + int err = 0; static const struct tree_descr files[] = { { "cache_threshold", &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR }, @@ -1696,69 +1615,57 @@ static int sel_make_avc_files(struct dentry *dir) #endif }; - for (i = 0; i < ARRAY_SIZE(files); i++) { + for (i = 0; !err && i < ARRAY_SIZE(files); i++) { struct inode *inode; - struct dentry *dentry; - - dentry = d_alloc_name(dir, files[i].name); - if (!dentry) - return -ENOMEM; inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); - if (!inode) { - dput(dentry); + if (!inode) return -ENOMEM; - } inode->i_fop = files[i].ops; inode->i_ino = ++fsi->last_ino; - d_add(dentry, inode); + + err = sel_attach_file(dir, files[i].name, inode); } - return 0; + return err; } static int sel_make_ss_files(struct dentry *dir) { struct super_block *sb = dir->d_sb; struct selinux_fs_info *fsi = sb->s_fs_info; - int i; - static struct tree_descr files[] = { + unsigned int i; + int err = 0; + static const struct tree_descr files[] = { { "sidtab_hash_stats", &sel_sidtab_hash_stats_ops, S_IRUGO }, }; - for (i = 0; i < ARRAY_SIZE(files); i++) { + for (i = 0; !err && i < ARRAY_SIZE(files); i++) { struct inode *inode; - struct dentry *dentry; - - dentry = d_alloc_name(dir, files[i].name); - if (!dentry) - return -ENOMEM; inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); - if (!inode) { - dput(dentry); + if (!inode) return -ENOMEM; - } inode->i_fop = files[i].ops; inode->i_ino = ++fsi->last_ino; - d_add(dentry, inode); + + err = sel_attach_file(dir, files[i].name, inode); } - return 0; + return err; } static ssize_t sel_read_initcon(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; char *con; u32 sid, len; ssize_t ret; sid = file_inode(file)->i_ino&SEL_INO_MASK; - ret = security_sid_to_context(fsi->state, sid, &con, &len); + ret = security_sid_to_context(sid, &con, &len); if (ret) return ret; @@ -1774,31 +1681,26 @@ static const struct file_operations sel_initcon_ops = { static int sel_make_initcon_files(struct dentry *dir) { - int i; + unsigned int i; + int err = 0; - for (i = 1; i <= SECINITSID_NUM; i++) { - struct inode *inode; - struct dentry *dentry; + for (i = 1; !err && i <= SECINITSID_NUM; i++) { const char *s = security_get_initial_sid_context(i); + struct inode *inode; if (!s) continue; - dentry = d_alloc_name(dir, s); - if (!dentry) - return -ENOMEM; inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); - if (!inode) { - dput(dentry); + if (!inode) return -ENOMEM; - } inode->i_fop = &sel_initcon_ops; inode->i_ino = i|SEL_INITCON_INO_OFFSET; - d_add(dentry, inode); + err = sel_attach_file(dir, s, inode); } - return 0; + return err; } static inline unsigned long sel_class_to_ino(u16 class) @@ -1852,13 +1754,12 @@ static const struct file_operations sel_perm_ops = { static ssize_t sel_read_policycap(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; int value; char tmpbuf[TMPBUFLEN]; ssize_t length; unsigned long i_ino = file_inode(file)->i_ino; - value = security_policycap_supported(fsi->state, i_ino & SEL_INO_MASK); + value = security_policycap_supported(i_ino & SEL_INO_MASK); length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); @@ -1873,36 +1774,29 @@ static int sel_make_perm_files(struct selinux_policy *newpolicy, char *objclass, int classvalue, struct dentry *dir) { - int i, rc, nperms; + u32 i, nperms; + int rc; char **perms; rc = security_get_permissions(newpolicy, objclass, &perms, &nperms); if (rc) return rc; - for (i = 0; i < nperms; i++) { + for (i = 0; !rc && i < nperms; i++) { struct inode *inode; - struct dentry *dentry; - - rc = -ENOMEM; - dentry = d_alloc_name(dir, perms[i]); - if (!dentry) - goto out; - rc = -ENOMEM; inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); if (!inode) { - dput(dentry); - goto out; + rc = -ENOMEM; + break; } inode->i_fop = &sel_perm_ops; /* i+1 since perm values are 1-indexed */ inode->i_ino = sel_perm_to_ino(classvalue, i + 1); - d_add(dentry, inode); + + rc = sel_attach_file(dir, perms[i], inode); } - rc = 0; -out: for (i = 0; i < nperms; i++) kfree(perms[i]); kfree(perms); @@ -1917,20 +1811,18 @@ static int sel_make_class_dir_entries(struct selinux_policy *newpolicy, struct selinux_fs_info *fsi = sb->s_fs_info; struct dentry *dentry = NULL; struct inode *inode = NULL; - - dentry = d_alloc_name(dir, "index"); - if (!dentry) - return -ENOMEM; + int err; inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); - if (!inode) { - dput(dentry); + if (!inode) return -ENOMEM; - } inode->i_fop = &sel_class_ops; inode->i_ino = sel_class_to_ino(index); - d_add(dentry, inode); + + err = sel_attach_file(dir, "index", inode); + if (err) + return err; dentry = sel_make_dir(dir, "perms", &fsi->last_class_ino); if (IS_ERR(dentry)) @@ -1943,8 +1835,8 @@ static int sel_make_classes(struct selinux_policy *newpolicy, struct dentry *class_dir, unsigned long *last_class_ino) { - - int rc, nclasses, i; + u32 i, nclasses; + int rc; char **classes; rc = security_get_classes(newpolicy, &classes, &nclasses); @@ -1978,77 +1870,88 @@ out: return rc; } -static int sel_make_policycap(struct selinux_fs_info *fsi) +static int sel_make_policycap(struct dentry *dir) { + struct super_block *sb = dir->d_sb; unsigned int iter; - struct dentry *dentry = NULL; struct inode *inode = NULL; + int err = 0; + + for (iter = 0; !err && iter <= POLICYDB_CAP_MAX; iter++) { + const char *name; - for (iter = 0; iter <= POLICYDB_CAP_MAX; iter++) { if (iter < ARRAY_SIZE(selinux_policycap_names)) - dentry = d_alloc_name(fsi->policycap_dir, - selinux_policycap_names[iter]); + name = selinux_policycap_names[iter]; else - dentry = d_alloc_name(fsi->policycap_dir, "unknown"); - - if (dentry == NULL) - return -ENOMEM; + name = "unknown"; - inode = sel_make_inode(fsi->sb, S_IFREG | 0444); - if (inode == NULL) { - dput(dentry); + inode = sel_make_inode(sb, S_IFREG | 0444); + if (!inode) return -ENOMEM; - } inode->i_fop = &sel_policycap_ops; inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET; - d_add(dentry, inode); + err = sel_attach_file(dir, name, inode); } - return 0; + return err; } static struct dentry *sel_make_dir(struct dentry *dir, const char *name, unsigned long *ino) { - struct dentry *dentry = d_alloc_name(dir, name); struct inode *inode; - if (!dentry) - return ERR_PTR(-ENOMEM); - inode = sel_make_inode(dir->d_sb, S_IFDIR | S_IRUGO | S_IXUGO); - if (!inode) { - dput(dentry); + if (!inode) return ERR_PTR(-ENOMEM); - } inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; inode->i_ino = ++(*ino); /* directory inodes start off with i_nlink == 2 (for "." entry) */ inc_nlink(inode); - d_add(dentry, inode); /* bump link count on parent directory, too */ inc_nlink(d_inode(dir)); - return dentry; + return sel_attach(dir, name, inode); } -static struct dentry *sel_make_disconnected_dir(struct super_block *sb, +static int reject_all(struct mnt_idmap *idmap, struct inode *inode, int mask) +{ + return -EPERM; // no access for anyone, root or no root. +} + +static const struct inode_operations swapover_dir_inode_operations = { + .lookup = simple_lookup, + .permission = reject_all, +}; + +static struct dentry *sel_make_swapover_dir(struct super_block *sb, unsigned long *ino) { - struct inode *inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); + struct dentry *dentry = d_alloc_name(sb->s_root, ".swapover"); + struct inode *inode; - if (!inode) + if (!dentry) return ERR_PTR(-ENOMEM); - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; + inode = sel_make_inode(sb, S_IFDIR); + if (!inode) { + dput(dentry); + return ERR_PTR(-ENOMEM); + } + + inode->i_op = &swapover_dir_inode_operations; inode->i_ino = ++(*ino); /* directory inodes start off with i_nlink == 2 (for "." entry) */ inc_nlink(inode); - return d_obtain_alias(inode); + inode_lock(sb->s_root->d_inode); + d_make_persistent(dentry, inode); + inc_nlink(sb->s_root->d_inode); + inode_unlock(sb->s_root->d_inode); + dput(dentry); + return dentry; // borrowed } #define NULL_FILE_NAME "null" @@ -2081,7 +1984,7 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc) [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO}, [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops, S_IWUGO}, - /* last one */ {""} + /* last one */ {"", NULL, 0} }; ret = selinux_fs_info_create(sb); @@ -2101,16 +2004,9 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc) } ret = -ENOMEM; - dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME); - if (!dentry) - goto err; - - ret = -ENOMEM; inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO); - if (!inode) { - dput(dentry); + if (!inode) goto err; - } inode->i_ino = ++fsi->last_ino; isec = selinux_inode(inode); @@ -2119,7 +2015,9 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc) isec->initialized = LABEL_INITIALIZED; init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); - d_add(dentry, inode); + ret = sel_attach_file(sb->s_root, NULL_FILE_NAME, inode); + if (ret) + goto err; dentry = sel_make_dir(sb->s_root, "avc", &fsi->last_ino); if (IS_ERR(dentry)) { @@ -2158,15 +2056,13 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc) goto err; } - fsi->policycap_dir = sel_make_dir(sb->s_root, POLICYCAP_DIR_NAME, - &fsi->last_ino); - if (IS_ERR(fsi->policycap_dir)) { - ret = PTR_ERR(fsi->policycap_dir); - fsi->policycap_dir = NULL; + dentry = sel_make_dir(sb->s_root, "policy_capabilities", &fsi->last_ino); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto err; } - ret = sel_make_policycap(fsi); + ret = sel_make_policycap(dentry); if (ret) { pr_err("SELinux: failed to load policy capabilities\n"); goto err; @@ -2177,8 +2073,6 @@ err: pr_err("SELinux: %s: failed while creating inodes\n", __func__); - selinux_fs_info_free(sb); - return ret; } @@ -2200,7 +2094,7 @@ static int sel_init_fs_context(struct fs_context *fc) static void sel_kill_sb(struct super_block *sb) { selinux_fs_info_free(sb); - kill_litter_super(sb); + kill_anon_super(sb); } static struct file_system_type sel_fs_type = { @@ -2209,10 +2103,9 @@ static struct file_system_type sel_fs_type = { .kill_sb = sel_kill_sb, }; -static struct vfsmount *selinuxfs_mount __ro_after_init; struct path selinux_null __ro_after_init; -static int __init init_sel_fs(void) +int __init init_sel_fs(void) { struct qstr null_name = QSTR_INIT(NULL_FILE_NAME, sizeof(NULL_FILE_NAME)-1); @@ -2231,31 +2124,28 @@ static int __init init_sel_fs(void) return err; } - selinux_null.mnt = selinuxfs_mount = kern_mount(&sel_fs_type); - if (IS_ERR(selinuxfs_mount)) { + selinux_null.mnt = kern_mount(&sel_fs_type); + if (IS_ERR(selinux_null.mnt)) { pr_err("selinuxfs: could not mount!\n"); - err = PTR_ERR(selinuxfs_mount); - selinuxfs_mount = NULL; + err = PTR_ERR(selinux_null.mnt); + selinux_null.mnt = NULL; + return err; } - selinux_null.dentry = d_hash_and_lookup(selinux_null.mnt->mnt_root, - &null_name); + + selinux_null.dentry = try_lookup_noperm(&null_name, + selinux_null.mnt->mnt_root); if (IS_ERR(selinux_null.dentry)) { pr_err("selinuxfs: could not lookup null!\n"); err = PTR_ERR(selinux_null.dentry); selinux_null.dentry = NULL; + return err; } - return err; -} - -__initcall(init_sel_fs); + /* + * Try to pre-allocate the status page, so the sequence number of the + * initial policy load can be stored. + */ + (void) selinux_kernel_status_page(); -#ifdef CONFIG_SECURITY_SELINUX_DISABLE -void exit_sel_fs(void) -{ - sysfs_remove_mount_point(fs_kobj, "selinux"); - dput(selinux_null.dentry); - kern_unmount(selinuxfs_mount); - unregister_filesystem(&sel_fs_type); + return err; } -#endif |
