📄 selinuxfs.c
字号:
/* Updated: Karl MacMillan <kmacmillan@tresys.com> * * Added conditional policy language extensions * * Copyright (C) 2003 - 2004 Tresys Technology, LLC * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */#include <linux/kernel.h>#include <linux/pagemap.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/fs.h>#include <linux/mutex.h>#include <linux/init.h>#include <linux/string.h>#include <linux/security.h>#include <linux/major.h>#include <linux/seq_file.h>#include <linux/percpu.h>#include <linux/audit.h>#include <asm/uaccess.h>#include <asm/semaphore.h>/* selinuxfs pseudo filesystem for exporting the security policy API. Based on the proc code and the fs/nfsd/nfsctl.c code. */#include "flask.h"#include "avc.h"#include "avc_ss.h"#include "security.h"#include "objsec.h"#include "conditional.h"unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;#ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT#define SELINUX_COMPAT_NET_VALUE 0#else#define SELINUX_COMPAT_NET_VALUE 1#endifint selinux_compat_net = SELINUX_COMPAT_NET_VALUE;static int __init checkreqprot_setup(char *str){ selinux_checkreqprot = simple_strtoul(str,NULL,0) ? 1 : 0; return 1;}__setup("checkreqprot=", checkreqprot_setup);static int __init selinux_compat_net_setup(char *str){ selinux_compat_net = simple_strtoul(str,NULL,0) ? 1 : 0; return 1;}__setup("selinux_compat_net=", selinux_compat_net_setup);static DEFINE_MUTEX(sel_mutex);/* global data for booleans */static struct dentry *bool_dir = NULL;static int bool_num = 0;static char **bool_pending_names;static int *bool_pending_values = NULL;/* global data for classes */static struct dentry *class_dir = NULL;static unsigned long last_class_ino;extern void selnl_notify_setenforce(int val);/* Check whether a task is allowed to use a security operation. */static int task_has_security(struct task_struct *tsk, u32 perms){ struct task_security_struct *tsec; tsec = tsk->security; if (!tsec) return -EACCES; return avc_has_perm(tsec->sid, SECINITSID_SECURITY, SECCLASS_SECURITY, perms, NULL);}enum sel_inos { SEL_ROOT_INO = 2, SEL_LOAD, /* load policy */ SEL_ENFORCE, /* get or set enforcing status */ SEL_CONTEXT, /* validate context */ SEL_ACCESS, /* compute access decision */ SEL_CREATE, /* compute create labeling decision */ SEL_RELABEL, /* compute relabeling decision */ SEL_USER, /* compute reachable user contexts */ SEL_POLICYVERS, /* return policy version for this kernel */ SEL_COMMIT_BOOLS, /* commit new boolean values */ SEL_MLS, /* return if MLS policy is enabled */ SEL_DISABLE, /* disable SELinux until next reboot */ SEL_MEMBER, /* compute polyinstantiation membership decision */ SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */ SEL_COMPAT_NET, /* whether to use old compat network packet controls */ SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */ SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */ SEL_INO_NEXT, /* The next inode number to use */};static unsigned long sel_last_ino = SEL_INO_NEXT - 1;#define SEL_INITCON_INO_OFFSET 0x01000000#define SEL_BOOL_INO_OFFSET 0x02000000#define SEL_CLASS_INO_OFFSET 0x04000000#define SEL_INO_MASK 0x00ffffff#define TMPBUFLEN 12static ssize_t sel_read_enforce(struct file *filp, char __user *buf, size_t count, loff_t *ppos){ char tmpbuf[TMPBUFLEN]; ssize_t length; length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_enforcing); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);}#ifdef CONFIG_SECURITY_SELINUX_DEVELOPstatic ssize_t sel_write_enforce(struct file * file, const char __user * buf, size_t count, loff_t *ppos){ char *page; ssize_t length; int new_value; if (count >= PAGE_SIZE) return -ENOMEM; if (*ppos != 0) { /* No partial writes. */ return -EINVAL; } page = (char*)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; length = -EINVAL; if (sscanf(page, "%d", &new_value) != 1) goto out; if (new_value != selinux_enforcing) { length = task_has_security(current, SECURITY__SETENFORCE); if (length) goto out; audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, "enforcing=%d old_enforcing=%d auid=%u", new_value, selinux_enforcing, audit_get_loginuid(current->audit_context)); selinux_enforcing = new_value; if (selinux_enforcing) avc_ss_reset(0); selnl_notify_setenforce(selinux_enforcing); } length = count;out: free_page((unsigned long) page); return length;}#else#define sel_write_enforce NULL#endifstatic const struct file_operations sel_enforce_ops = { .read = sel_read_enforce, .write = sel_write_enforce,};static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, size_t count, loff_t *ppos){ char tmpbuf[TMPBUFLEN]; ssize_t length; ino_t ino = filp->f_path.dentry->d_inode->i_ino; int handle_unknown = (ino == SEL_REJECT_UNKNOWN) ? 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);}static const struct file_operations sel_handle_unknown_ops = { .read = sel_read_handle_unknown,};#ifdef CONFIG_SECURITY_SELINUX_DISABLEstatic ssize_t sel_write_disable(struct file * file, const char __user * buf, size_t count, loff_t *ppos){ char *page; ssize_t length; int new_value; extern int selinux_disable(void); if (count >= PAGE_SIZE) return -ENOMEM; if (*ppos != 0) { /* No partial writes. */ return -EINVAL; } page = (char*)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; length = -EINVAL; if (sscanf(page, "%d", &new_value) != 1) goto out; if (new_value) { length = selinux_disable(); if (length < 0) goto out; audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, "selinux=0 auid=%u", audit_get_loginuid(current->audit_context)); } length = count;out: free_page((unsigned long) page); return length;}#else#define sel_write_disable NULL#endifstatic const struct file_operations sel_disable_ops = { .write = sel_write_disable,};static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, size_t count, loff_t *ppos){ char tmpbuf[TMPBUFLEN]; ssize_t length; length = scnprintf(tmpbuf, TMPBUFLEN, "%u", POLICYDB_VERSION_MAX); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);}static const struct file_operations sel_policyvers_ops = { .read = sel_read_policyvers,};/* declaration for sel_write_load */static int sel_make_bools(void);static int sel_make_classes(void);/* declaration for sel_make_class_dirs */static int sel_make_dir(struct inode *dir, struct dentry *dentry, unsigned long *ino);static ssize_t sel_read_mls(struct file *filp, char __user *buf, size_t count, loff_t *ppos){ char tmpbuf[TMPBUFLEN]; ssize_t length; length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_mls_enabled); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);}static const struct file_operations sel_mls_ops = { .read = sel_read_mls,};static ssize_t sel_write_load(struct file * file, const char __user * buf, size_t count, loff_t *ppos){ int ret; ssize_t length; void *data = NULL; mutex_lock(&sel_mutex); length = task_has_security(current, SECURITY__LOAD_POLICY); if (length) goto out; if (*ppos != 0) { /* No partial writes. */ length = -EINVAL; goto out; } if ((count > 64 * 1024 * 1024) || (data = vmalloc(count)) == NULL) { length = -ENOMEM; goto out; } length = -EFAULT; if (copy_from_user(data, buf, count) != 0) goto out; length = security_load_policy(data, count); if (length) goto out; ret = sel_make_bools(); if (ret) { length = ret; goto out1; } ret = sel_make_classes(); if (ret) length = ret; else length = count;out1: printk(KERN_INFO "SELinux: policy loaded with handle_unknown=%s\n", (security_get_reject_unknown() ? "reject" : (security_get_allow_unknown() ? "allow" : "deny"))); audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, "policy loaded auid=%u", audit_get_loginuid(current->audit_context));out: mutex_unlock(&sel_mutex); vfree(data); return length;}static const struct file_operations sel_load_ops = { .write = sel_write_load,};static ssize_t sel_write_context(struct file * file, char *buf, size_t size){ char *canon; u32 sid, len; ssize_t length; length = task_has_security(current, SECURITY__CHECK_CONTEXT); if (length) return length; length = security_context_to_sid(buf, size, &sid); if (length < 0) return length; length = security_sid_to_context(sid, &canon, &len); if (length < 0) return length; if (len > SIMPLE_TRANSACTION_LIMIT) { printk(KERN_ERR "%s: context size (%u) exceeds payload " "max\n", __FUNCTION__, len); length = -ERANGE; goto out; } memcpy(buf, canon, len); length = len;out: kfree(canon); return length;}static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, size_t count, loff_t *ppos){ char tmpbuf[TMPBUFLEN]; ssize_t length; length = scnprintf(tmpbuf, TMPBUFLEN, "%u", selinux_checkreqprot); 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){ char *page; ssize_t length; unsigned int new_value; length = task_has_security(current, SECURITY__SETCHECKREQPROT); if (length) return length; if (count >= PAGE_SIZE) return -ENOMEM; if (*ppos != 0) { /* No partial writes. */ return -EINVAL; } page = (char*)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; length = -EINVAL; if (sscanf(page, "%u", &new_value) != 1) goto out; selinux_checkreqprot = new_value ? 1 : 0; length = count;out: free_page((unsigned long) page); return length;}static const struct file_operations sel_checkreqprot_ops = { .read = sel_read_checkreqprot, .write = sel_write_checkreqprot,};static ssize_t sel_read_compat_net(struct file *filp, char __user *buf, size_t count, loff_t *ppos){ char tmpbuf[TMPBUFLEN]; ssize_t length; length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_compat_net); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);}static ssize_t sel_write_compat_net(struct file * file, const char __user * buf, size_t count, loff_t *ppos){ char *page; ssize_t length; int new_value; length = task_has_security(current, SECURITY__LOAD_POLICY); if (length) return length; if (count >= PAGE_SIZE) return -ENOMEM; if (*ppos != 0) { /* No partial writes. */ return -EINVAL; } page = (char*)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; length = -EINVAL; if (sscanf(page, "%d", &new_value) != 1) goto out; selinux_compat_net = new_value ? 1 : 0; length = count;out: free_page((unsigned long) page); return length;}static const struct file_operations sel_compat_net_ops = { .read = sel_read_compat_net, .write = sel_write_compat_net,};/* * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c */static ssize_t sel_write_access(struct file * file, char *buf, size_t size);static ssize_t sel_write_create(struct file * file, char *buf, size_t size);static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size);static ssize_t sel_write_user(struct file * file, char *buf, size_t size);static ssize_t sel_write_member(struct file * file, char *buf, size_t size);static ssize_t (*write_op[])(struct file *, char *, size_t) = { [SEL_ACCESS] = sel_write_access, [SEL_CREATE] = sel_write_create, [SEL_RELABEL] = sel_write_relabel, [SEL_USER] = sel_write_user, [SEL_MEMBER] = sel_write_member, [SEL_CONTEXT] = sel_write_context,};static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos){ ino_t ino = file->f_path.dentry->d_inode->i_ino; char *data; ssize_t rv; if (ino >= ARRAY_SIZE(write_op) || !write_op[ino]) return -EINVAL; data = simple_transaction_get(file, buf, size); if (IS_ERR(data)) return PTR_ERR(data); rv = write_op[ino](file, data, size); if (rv>0) { simple_transaction_set(file, rv); rv = size; } return rv;}static const struct file_operations transaction_ops = { .write = selinux_transaction_write, .read = simple_transaction_read, .release = simple_transaction_release,};/* * payload - write methods * If the method has a response, the response should be put in buf, * and the length returned. Otherwise return 0 or and -error. */static ssize_t sel_write_access(struct file * file, char *buf, size_t size){ char *scon, *tcon; u32 ssid, tsid; u16 tclass; u32 req; struct av_decision avd; ssize_t length; length = task_has_security(current, SECURITY__COMPUTE_AV); if (length) return length; length = -ENOMEM; scon = kzalloc(size+1, GFP_KERNEL); if (!scon) return length; tcon = kzalloc(size+1, GFP_KERNEL); if (!tcon) goto out; length = -EINVAL; if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4) goto out2; length = security_context_to_sid(scon, strlen(scon)+1, &ssid); if (length < 0) goto out2; length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid); if (length < 0) goto out2; length = security_compute_av(ssid, tsid, tclass, req, &avd); if (length < 0) goto out2; length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%x %x %x %x %u", avd.allowed, avd.decided, avd.auditallow, avd.auditdeny, avd.seqno);out2: kfree(tcon);out:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -