X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=security%2Fselinux%2Fselinuxfs.c;h=69c9dccc8cf0a116beecd3b093950f5c9a33b791;hb=cdcf43335cbb2380e533441e007e0c4ec42634d5;hp=2fa483f26113c1656d950e11579bda5c7903cf8c;hpb=41f81e88e01eb959f439f8537c58078e4bfc5291;p=linux-2.6-omap-h63xx.git diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 2fa483f2611..69c9dccc8cf 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1,11 +1,16 @@ /* Updated: Karl MacMillan * - * Added conditional policy language extensions + * Added conditional policy language extensions * + * Updated: Hewlett-Packard + * + * Added support for the policy capability bitmap + * + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. * Copyright (C) 2003 - 2004 Tresys Technology, LLC * Copyright (C) 2004 Red Hat, Inc., James Morris * 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 + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ @@ -22,8 +27,7 @@ #include #include #include -#include -#include +#include /* selinuxfs pseudo filesystem for exporting the security policy API. Based on the proc code and the fs/nfsd/nfsctl.c code. */ @@ -35,6 +39,12 @@ #include "objsec.h" #include "conditional.h" +/* Policy capability filenames */ +static char *policycap_names[] = { + "network_peer_controls", + "open_perms" +}; + unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; #ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT @@ -47,14 +57,18 @@ int selinux_compat_net = SELINUX_COMPAT_NET_VALUE; static int __init checkreqprot_setup(char *str) { - selinux_checkreqprot = simple_strtoul(str,NULL,0) ? 1 : 0; + unsigned long checkreqprot; + if (!strict_strtoul(str, 0, &checkreqprot)) + selinux_checkreqprot = checkreqprot ? 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; + unsigned long compat_net; + if (!strict_strtoul(str, 0, &compat_net)) + selinux_compat_net = compat_net ? 1 : 0; return 1; } __setup("selinux_compat_net=", selinux_compat_net_setup); @@ -63,15 +77,18 @@ __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 struct dentry *bool_dir; +static int bool_num; static char **bool_pending_names; -static int *bool_pending_values = NULL; +static int *bool_pending_values; /* global data for classes */ -static struct dentry *class_dir = NULL; +static struct dentry *class_dir; static unsigned long last_class_ino; +/* global data for policy capabilities */ +static struct dentry *policycap_dir; + extern void selnl_notify_setenforce(int val); /* Check whether a task is allowed to use a security operation. */ @@ -111,10 +128,11 @@ enum sel_inos { 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 SEL_INITCON_INO_OFFSET 0x01000000 +#define SEL_BOOL_INO_OFFSET 0x02000000 +#define SEL_CLASS_INO_OFFSET 0x04000000 +#define SEL_POLICYCAP_INO_OFFSET 0x08000000 +#define SEL_INO_MASK 0x00ffffff #define TMPBUFLEN 12 static ssize_t sel_read_enforce(struct file *filp, char __user *buf, @@ -128,7 +146,7 @@ static ssize_t sel_read_enforce(struct file *filp, char __user *buf, } #ifdef CONFIG_SECURITY_SELINUX_DEVELOP -static ssize_t sel_write_enforce(struct file * file, const char __user * buf, +static ssize_t sel_write_enforce(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { @@ -142,7 +160,7 @@ static ssize_t sel_write_enforce(struct file * file, const char __user * buf, /* No partial writes. */ return -EINVAL; } - page = (char*)get_zeroed_page(GFP_KERNEL); + page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; length = -EFAULT; @@ -158,9 +176,10 @@ static ssize_t sel_write_enforce(struct file * file, const char __user * buf, 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)); + "enforcing=%d old_enforcing=%d auid=%u ses=%u", + new_value, selinux_enforcing, + audit_get_loginuid(current), + audit_get_sessionid(current)); selinux_enforcing = new_value; if (selinux_enforcing) avc_ss_reset(0); @@ -198,7 +217,7 @@ static const struct file_operations sel_handle_unknown_ops = { }; #ifdef CONFIG_SECURITY_SELINUX_DISABLE -static ssize_t sel_write_disable(struct file * file, const char __user * buf, +static ssize_t sel_write_disable(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { @@ -213,7 +232,7 @@ static ssize_t sel_write_disable(struct file * file, const char __user * buf, /* No partial writes. */ return -EINVAL; } - page = (char*)get_zeroed_page(GFP_KERNEL); + page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; length = -EFAULT; @@ -229,8 +248,9 @@ static ssize_t sel_write_disable(struct file * file, const char __user * buf, 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)); + "selinux=0 auid=%u ses=%u", + audit_get_loginuid(current), + audit_get_sessionid(current)); } length = count; @@ -247,7 +267,7 @@ static const struct file_operations sel_disable_ops = { }; static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { char tmpbuf[TMPBUFLEN]; ssize_t length; @@ -263,6 +283,7 @@ static const struct file_operations sel_policyvers_ops = { /* declaration for sel_write_load */ static int sel_make_bools(void); static int sel_make_classes(void); +static int sel_make_policycap(void); /* declaration for sel_make_class_dirs */ static int sel_make_dir(struct inode *dir, struct dentry *dentry, @@ -282,7 +303,7 @@ 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, +static ssize_t sel_write_load(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { @@ -323,20 +344,22 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf, } ret = sel_make_classes(); + if (ret) { + length = ret; + goto out1; + } + + ret = sel_make_policycap(); 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)); + "policy loaded auid=%u ses=%u", + audit_get_loginuid(current), + audit_get_sessionid(current)); out: mutex_unlock(&sel_mutex); vfree(data); @@ -347,7 +370,7 @@ 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) +static ssize_t sel_write_context(struct file *file, char *buf, size_t size) { char *canon; u32 sid, len; @@ -366,8 +389,8 @@ static ssize_t sel_write_context(struct file * file, char *buf, size_t size) return length; if (len > SIMPLE_TRANSACTION_LIMIT) { - printk(KERN_ERR "%s: context size (%u) exceeds payload " - "max\n", __FUNCTION__, len); + printk(KERN_ERR "SELinux: %s: context size (%u) exceeds " + "payload max\n", __func__, len); length = -ERANGE; goto out; } @@ -389,7 +412,7 @@ static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } -static ssize_t sel_write_checkreqprot(struct file * file, const char __user * buf, +static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char *page; @@ -406,7 +429,7 @@ static ssize_t sel_write_checkreqprot(struct file * file, const char __user * bu /* No partial writes. */ return -EINVAL; } - page = (char*)get_zeroed_page(GFP_KERNEL); + page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; length = -EFAULT; @@ -438,7 +461,7 @@ static ssize_t sel_read_compat_net(struct file *filp, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } -static ssize_t sel_write_compat_net(struct file * file, const char __user * buf, +static ssize_t sel_write_compat_net(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char *page; @@ -455,7 +478,7 @@ static ssize_t sel_write_compat_net(struct file * file, const char __user * buf, /* No partial writes. */ return -EINVAL; } - page = (char*)get_zeroed_page(GFP_KERNEL); + page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; length = -EFAULT; @@ -480,11 +503,11 @@ static const struct file_operations sel_compat_net_ops = { /* * 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 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, @@ -497,7 +520,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { 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; + ino_t ino = file->f_path.dentry->d_inode->i_ino; char *data; ssize_t rv; @@ -508,8 +531,8 @@ static ssize_t selinux_transaction_write(struct file *file, const char __user *b if (IS_ERR(data)) return PTR_ERR(data); - rv = write_op[ino](file, data, size); - if (rv>0) { + rv = write_op[ino](file, data, size); + if (rv > 0) { simple_transaction_set(file, rv); rv = size; } @@ -528,7 +551,7 @@ static const struct file_operations transaction_ops = { * and the length returned. Otherwise return 0 or and -error. */ -static ssize_t sel_write_access(struct file * file, char *buf, size_t size) +static ssize_t sel_write_access(struct file *file, char *buf, size_t size) { char *scon, *tcon; u32 ssid, tsid; @@ -577,7 +600,7 @@ out: return length; } -static ssize_t sel_write_create(struct file * file, char *buf, size_t size) +static ssize_t sel_write_create(struct file *file, char *buf, size_t size) { char *scon, *tcon; u32 ssid, tsid, newsid; @@ -619,8 +642,8 @@ static ssize_t sel_write_create(struct file * file, char *buf, size_t size) goto out2; if (len > SIMPLE_TRANSACTION_LIMIT) { - printk(KERN_ERR "%s: context size (%u) exceeds payload " - "max\n", __FUNCTION__, len); + printk(KERN_ERR "SELinux: %s: context size (%u) exceeds " + "payload max\n", __func__, len); length = -ERANGE; goto out3; } @@ -636,7 +659,7 @@ out: return length; } -static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size) +static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) { char *scon, *tcon; u32 ssid, tsid, newsid; @@ -693,7 +716,7 @@ out: return length; } -static ssize_t sel_write_user(struct file * file, char *buf, size_t size) +static ssize_t sel_write_user(struct file *file, char *buf, size_t size) { char *con, *user, *ptr; u32 sid, *sids; @@ -754,7 +777,7 @@ out: return length; } -static ssize_t sel_write_member(struct file * file, char *buf, size_t size) +static ssize_t sel_write_member(struct file *file, char *buf, size_t size) { char *scon, *tcon; u32 ssid, tsid, newsid; @@ -796,8 +819,8 @@ static ssize_t sel_write_member(struct file * file, char *buf, size_t size) goto out2; if (len > SIMPLE_TRANSACTION_LIMIT) { - printk(KERN_ERR "%s: context size (%u) exceeds payload " - "max\n", __FUNCTION__, len); + printk(KERN_ERR "SELinux: %s: context size (%u) exceeds " + "payload max\n", __func__, len); length = -ERANGE; goto out3; } @@ -848,7 +871,8 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf, ret = -EINVAL; goto out; } - if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) { + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) { ret = -ENOMEM; goto out; } @@ -899,7 +923,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, length = -EINVAL; goto out; } - page = (char*)get_zeroed_page(GFP_KERNEL); + page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) { length = -ENOMEM; goto out; @@ -927,8 +951,8 @@ out: } static const struct file_operations sel_bool_ops = { - .read = sel_read_bool, - .write = sel_write_bool, + .read = sel_read_bool, + .write = sel_write_bool, }; static ssize_t sel_commit_bools_write(struct file *filep, @@ -953,7 +977,7 @@ static ssize_t sel_commit_bools_write(struct file *filep, /* No partial writes. */ goto out; } - page = (char*)get_zeroed_page(GFP_KERNEL); + page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) { length = -ENOMEM; goto out; @@ -967,9 +991,8 @@ static ssize_t sel_commit_bools_write(struct file *filep, if (sscanf(page, "%d", &new_value) != 1) goto out; - if (new_value && bool_pending_values) { + if (new_value && bool_pending_values) security_set_bools(bool_num, bool_pending_values); - } length = count; @@ -981,7 +1004,7 @@ out: } static const struct file_operations sel_commit_bools_ops = { - .write = sel_commit_bools_write, + .write = sel_commit_bools_write, }; static void sel_remove_entries(struct dentry *de) @@ -1031,7 +1054,8 @@ static int sel_make_bools(void) sel_remove_entries(dir); - if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) return -ENOMEM; ret = security_get_bools(&num, &names, &values); @@ -1058,8 +1082,9 @@ static int sel_make_bools(void) ret = -ENAMETOOLONG; goto err; } - isec = (struct inode_security_struct*)inode->i_security; - if ((ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid))) + isec = (struct inode_security_struct *)inode->i_security; + ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid); + if (ret) goto err; isec->sid = sid; isec->initialized = 1; @@ -1087,7 +1112,7 @@ err: #define NULL_FILE_NAME "null" -struct dentry *selinux_null = NULL; +struct dentry *selinux_null; static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, size_t count, loff_t *ppos) @@ -1099,8 +1124,8 @@ static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } -static ssize_t sel_write_avc_cache_threshold(struct file * file, - const char __user * buf, +static ssize_t sel_write_avc_cache_threshold(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) { @@ -1119,7 +1144,7 @@ static ssize_t sel_write_avc_cache_threshold(struct file * file, goto out; } - page = (char*)get_zeroed_page(GFP_KERNEL); + page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) { ret = -ENOMEM; goto out; @@ -1222,7 +1247,7 @@ static int sel_avc_stats_seq_show(struct seq_file *seq, void *v) static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v) { } -static struct seq_operations sel_avc_cache_stats_seq_ops = { +static const struct seq_operations sel_avc_cache_stats_seq_ops = { .start = sel_avc_stats_seq_start, .next = sel_avc_stats_seq_next, .show = sel_avc_stats_seq_show, @@ -1277,7 +1302,7 @@ out: return ret; } -static ssize_t sel_read_initcon(struct file * file, char __user *buf, +static ssize_t sel_read_initcon(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct inode *inode; @@ -1351,7 +1376,7 @@ static inline u32 sel_ino_to_perm(unsigned long ino) return (ino & SEL_INO_MASK) % (SEL_VEC_MAX + 1); } -static ssize_t sel_read_class(struct file * file, char __user *buf, +static ssize_t sel_read_class(struct file *file, char __user *buf, size_t count, loff_t *ppos) { ssize_t rc, len; @@ -1375,7 +1400,7 @@ static const struct file_operations sel_class_ops = { .read = sel_read_class, }; -static ssize_t sel_read_perm(struct file * file, char __user *buf, +static ssize_t sel_read_perm(struct file *file, char __user *buf, size_t count, loff_t *ppos) { ssize_t rc, len; @@ -1388,7 +1413,7 @@ static ssize_t sel_read_perm(struct file * file, char __user *buf, goto out; } - len = snprintf(page, PAGE_SIZE,"%d", sel_ino_to_perm(ino)); + len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_perm(ino)); rc = simple_read_from_buffer(buf, count, ppos, page, len); free_page((unsigned long)page); out: @@ -1399,6 +1424,24 @@ static const struct file_operations sel_perm_ops = { .read = sel_read_perm, }; +static ssize_t sel_read_policycap(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int value; + char tmpbuf[TMPBUFLEN]; + ssize_t length; + unsigned long i_ino = file->f_path.dentry->d_inode->i_ino; + + 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); +} + +static const struct file_operations sel_policycap_ops = { + .read = sel_read_policycap, +}; + static int sel_make_perm_files(char *objclass, int classvalue, struct dentry *dir) { @@ -1545,6 +1588,36 @@ out: return rc; } +static int sel_make_policycap(void) +{ + unsigned int iter; + struct dentry *dentry = NULL; + struct inode *inode = NULL; + + sel_remove_entries(policycap_dir); + + for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) { + if (iter < ARRAY_SIZE(policycap_names)) + dentry = d_alloc_name(policycap_dir, + policycap_names[iter]); + else + dentry = d_alloc_name(policycap_dir, "unknown"); + + if (dentry == NULL) + return -ENOMEM; + + inode = sel_make_inode(policycap_dir->d_sb, S_IFREG | S_IRUGO); + if (inode == NULL) + return -ENOMEM; + + inode->i_fop = &sel_policycap_ops; + inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET; + d_add(dentry, inode); + } + + return 0; +} + static int sel_make_dir(struct inode *dir, struct dentry *dentry, unsigned long *ino) { @@ -1568,7 +1641,7 @@ out: return ret; } -static int sel_fill_super(struct super_block * sb, void * data, int silent) +static int sel_fill_super(struct super_block *sb, void *data, int silent) { int ret; struct dentry *dentry; @@ -1624,7 +1697,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) goto err; } inode->i_ino = ++sel_last_ino; - isec = (struct inode_security_struct*)inode->i_security; + isec = (struct inode_security_struct *)inode->i_security; isec->sid = SECINITSID_DEVNULL; isec->sclass = SECCLASS_CHR_FILE; isec->initialized = 1; @@ -1673,10 +1746,23 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) class_dir = dentry; + dentry = d_alloc_name(sb->s_root, "policy_capabilities"); + if (!dentry) { + ret = -ENOMEM; + goto err; + } + + ret = sel_make_dir(root_inode, dentry, &sel_last_ino); + if (ret) + goto err; + + policycap_dir = dentry; + out: return ret; err: - printk(KERN_ERR "%s: failed while creating inodes\n", __FUNCTION__); + printk(KERN_ERR "SELinux: %s: failed while creating inodes\n", + __func__); goto out; }