summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-12 13:46:48 -0700
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-12 13:46:48 -0700
commitbb50cbbd4beacd5ceda76c32fcb116c67fe8c66c (patch)
treed57fe1a7c4214afbaacbcc5fb62ea6a5e9169b3f
parent702ed6ef375c19d65f2eeeefd3851476f2c4cee4 (diff)
parentd4cf291526a74cc33d33700a35b74395eec812fd (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6: security: unexport mmap_min_addr SELinux: use SECINITSID_NETMSG instead of SECINITSID_UNLABELED for NetLabel security: Protection for exploiting null dereference using mmap SELinux: Use %lu for inode->i_no when printing avc SELinux: allow preemption between transition permission checks selinux: introduce schedule points in policydb_destroy() selinux: add selinuxfs structure for object class discovery selinux: change sel_make_dir() to specify inode counter. selinux: rename sel_remove_bools() for more general usage. selinux: add support for querying object classes and permissions from the running policy
-rw-r--r--Documentation/sysctl/vm.txt15
-rw-r--r--include/linux/security.h17
-rw-r--r--kernel/sysctl.c10
-rw-r--r--mm/mmap.c4
-rw-r--r--mm/mremap.c13
-rw-r--r--mm/nommu.c2
-rw-r--r--security/dummy.c6
-rw-r--r--security/security.c1
-rw-r--r--security/selinux/avc.c12
-rw-r--r--security/selinux/hooks.c42
-rw-r--r--security/selinux/include/av_perm_to_string.h1
-rw-r--r--security/selinux/include/av_permissions.h1
-rw-r--r--security/selinux/include/avc.h6
-rw-r--r--security/selinux/include/class_to_string.h1
-rw-r--r--security/selinux/include/flask.h1
-rw-r--r--security/selinux/include/security.h4
-rw-r--r--security/selinux/netlabel.c34
-rw-r--r--security/selinux/selinuxfs.c269
-rw-r--r--security/selinux/ss/policydb.c7
-rw-r--r--security/selinux/ss/services.c144
20 files changed, 504 insertions, 86 deletions
diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt
index 1d192565e18..8cfca173d4b 100644
--- a/Documentation/sysctl/vm.txt
+++ b/Documentation/sysctl/vm.txt
@@ -31,6 +31,7 @@ Currently, these files are in /proc/sys/vm:
- min_unmapped_ratio
- min_slab_ratio
- panic_on_oom
+- mmap_min_address
==============================================================
@@ -216,3 +217,17 @@ above-mentioned.
The default value is 0.
1 and 2 are for failover of clustering. Please select either
according to your policy of failover.
+
+==============================================================
+
+mmap_min_addr
+
+This file indicates the amount of address space which a user process will
+be restricted from mmaping. Since kernel null dereference bugs could
+accidentally operate based on the information in the first couple of pages
+of memory userspace processes should not be allowed to write to them. By
+default this value is set to 0 and no protections will be enforced by the
+security module. Setting this value to something like 64k will allow the
+vast majority of applications to work correctly and provide defense in depth
+against future potential kernel bugs.
+
diff --git a/include/linux/security.h b/include/linux/security.h
index 9eb9e0fe033..c11dc8aa035 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -71,6 +71,7 @@ struct xfrm_user_sec_ctx;
extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);
extern int cap_netlink_recv(struct sk_buff *skb, int cap);
+extern unsigned long mmap_min_addr;
/*
* Values used in the task_security_ops calls
*/
@@ -1241,8 +1242,9 @@ struct security_operations {
int (*file_ioctl) (struct file * file, unsigned int cmd,
unsigned long arg);
int (*file_mmap) (struct file * file,
- unsigned long reqprot,
- unsigned long prot, unsigned long flags);
+ unsigned long reqprot, unsigned long prot,
+ unsigned long flags, unsigned long addr,
+ unsigned long addr_only);
int (*file_mprotect) (struct vm_area_struct * vma,
unsigned long reqprot,
unsigned long prot);
@@ -1814,9 +1816,12 @@ static inline int security_file_ioctl (struct file *file, unsigned int cmd,
static inline int security_file_mmap (struct file *file, unsigned long reqprot,
unsigned long prot,
- unsigned long flags)
+ unsigned long flags,
+ unsigned long addr,
+ unsigned long addr_only)
{
- return security_ops->file_mmap (file, reqprot, prot, flags);
+ return security_ops->file_mmap (file, reqprot, prot, flags, addr,
+ addr_only);
}
static inline int security_file_mprotect (struct vm_area_struct *vma,
@@ -2489,7 +2494,9 @@ static inline int security_file_ioctl (struct file *file, unsigned int cmd,
static inline int security_file_mmap (struct file *file, unsigned long reqprot,
unsigned long prot,
- unsigned long flags)
+ unsigned long flags,
+ unsigned long addr,
+ unsigned long addr_only)
{
return 0;
}
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 51f5dac42a0..d93e13d93f2 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -949,6 +949,16 @@ static ctl_table vm_table[] = {
.strategy = &sysctl_jiffies,
},
#endif
+#ifdef CONFIG_SECURITY
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "mmap_min_addr",
+ .data = &mmap_min_addr,
+ .maxlen = sizeof(unsigned long),
+ .mode = 0644,
+ .proc_handler = &proc_doulongvec_minmax,
+ },
+#endif
#if defined(CONFIG_X86_32) || \
(defined(CONFIG_SUPERH) && defined(CONFIG_VSYSCALL))
{
diff --git a/mm/mmap.c b/mm/mmap.c
index 906ed402f7c..9f70c8e8c87 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1023,10 +1023,10 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
}
}
- error = security_file_mmap(file, reqprot, prot, flags);
+ error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
if (error)
return error;
-
+
/* Clear old maps */
error = -ENOMEM;
munmap_back:
diff --git a/mm/mremap.c b/mm/mremap.c
index 5d4bd4f95b8..bc7c52efc71 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -291,6 +291,10 @@ unsigned long do_mremap(unsigned long addr,
if ((addr <= new_addr) && (addr+old_len) > new_addr)
goto out;
+ ret = security_file_mmap(0, 0, 0, 0, new_addr, 1);
+ if (ret)
+ goto out;
+
ret = do_munmap(mm, new_addr, new_len);
if (ret)
goto out;
@@ -390,8 +394,13 @@ unsigned long do_mremap(unsigned long addr,
new_addr = get_unmapped_area(vma->vm_file, 0, new_len,
vma->vm_pgoff, map_flags);
- ret = new_addr;
- if (new_addr & ~PAGE_MASK)
+ if (new_addr & ~PAGE_MASK) {
+ ret = new_addr;
+ goto out;
+ }
+
+ ret = security_file_mmap(0, 0, 0, 0, new_addr, 1);
+ if (ret)
goto out;
}
ret = move_vma(vma, addr, old_len, new_len, new_addr);
diff --git a/mm/nommu.c b/mm/nommu.c
index 2b16b00a5b1..989e2e9af5c 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -639,7 +639,7 @@ static int validate_mmap_request(struct file *file,
}
/* allow the security API to have its say */
- ret = security_file_mmap(file, reqprot, prot, flags);
+ ret = security_file_mmap(file, reqprot, prot, flags, addr, 0);
if (ret < 0)
return ret;
diff --git a/security/dummy.c b/security/dummy.c
index 8ffd76405b5..d6a112ce297 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -420,8 +420,12 @@ static int dummy_file_ioctl (struct file *file, unsigned int command,
static int dummy_file_mmap (struct file *file, unsigned long reqprot,
unsigned long prot,
- unsigned long flags)
+ unsigned long flags,
+ unsigned long addr,
+ unsigned long addr_only)
{
+ if (addr < mmap_min_addr)
+ return -EACCES;
return 0;
}
diff --git a/security/security.c b/security/security.c
index fc8601b2b7a..27e5863d30f 100644
--- a/security/security.c
+++ b/security/security.c
@@ -24,6 +24,7 @@ extern struct security_operations dummy_security_ops;
extern void security_fixup_ops(struct security_operations *ops);
struct security_operations *security_ops; /* Initialized to NULL */
+unsigned long mmap_min_addr; /* 0 means no protection */
static inline int verify(struct security_operations *ops)
{
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index e4396a89edc..78c408fd2b0 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -586,7 +586,7 @@ void avc_audit(u32 ssid, u32 tsid,
}
}
if (inode)
- audit_log_format(ab, " dev=%s ino=%ld",
+ audit_log_format(ab, " dev=%s ino=%lu",
inode->i_sb->s_id,
inode->i_ino);
break;
@@ -832,6 +832,7 @@ int avc_ss_reset(u32 seqno)
* @tsid: target security identifier
* @tclass: target security class
* @requested: requested permissions, interpreted based on @tclass
+ * @flags: AVC_STRICT or 0
* @avd: access vector decisions
*
* Check the AVC to determine whether the @requested permissions are granted
@@ -846,8 +847,9 @@ int avc_ss_reset(u32 seqno)
* should be released for the auditing.
*/
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct av_decision *avd)
+ u16 tclass, u32 requested,
+ unsigned flags,
+ struct av_decision *avd)
{
struct avc_node *node;
struct avc_entry entry, *p_ae;
@@ -874,7 +876,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
denied = requested & ~(p_ae->avd.allowed);
if (!requested || denied) {
- if (selinux_enforcing)
+ if (selinux_enforcing || (flags & AVC_STRICT))
rc = -EACCES;
else
if (node)
@@ -909,7 +911,7 @@ int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
struct av_decision avd;
int rc;
- rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd);
+ rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
return rc;
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index ad8dd4e8657..aff8f46c2aa 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1592,9 +1592,10 @@ static int selinux_vm_enough_memory(long pages)
rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
if (rc == 0)
rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY,
- CAP_TO_MASK(CAP_SYS_ADMIN),
- NULL);
+ SECCLASS_CAPABILITY,
+ CAP_TO_MASK(CAP_SYS_ADMIN),
+ 0,
+ NULL);
if (rc == 0)
cap_sys_admin = 1;
@@ -2568,12 +2569,16 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
}
static int selinux_file_mmap(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags)
+ unsigned long prot, unsigned long flags,
+ unsigned long addr, unsigned long addr_only)
{
- int rc;
+ int rc = 0;
+ u32 sid = ((struct task_security_struct*)(current->security))->sid;
- rc = secondary_ops->file_mmap(file, reqprot, prot, flags);
- if (rc)
+ if (addr < mmap_min_addr)
+ rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
+ MEMPROTECT__MMAP_ZERO, NULL);
+ if (rc || addr_only)
return rc;
if (selinux_checkreqprot)
@@ -3124,17 +3129,19 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
/**
* selinux_skb_extlbl_sid - Determine the external label of a packet
* @skb: the packet
- * @base_sid: the SELinux SID to use as a context for MLS only external labels
* @sid: the packet's SID
*
* Description:
* Check the various different forms of external packet labeling and determine
- * the external SID for the packet.
+ * the external SID for the packet. If only one form of external labeling is
+ * present then it is used, if both labeled IPsec and NetLabel labels are
+ * present then the SELinux type information is taken from the labeled IPsec
+ * SA and the MLS sensitivity label information is taken from the NetLabel
+ * security attributes. This bit of "magic" is done in the call to
+ * selinux_netlbl_skbuff_getsid().
*
*/
-static void selinux_skb_extlbl_sid(struct sk_buff *skb,
- u32 base_sid,
- u32 *sid)
+static void selinux_skb_extlbl_sid(struct sk_buff *skb, u32 *sid)
{
u32 xfrm_sid;
u32 nlbl_sid;
@@ -3142,10 +3149,9 @@ static void selinux_skb_extlbl_sid(struct sk_buff *skb,
selinux_skb_xfrm_sid(skb, &xfrm_sid);
if (selinux_netlbl_skbuff_getsid(skb,
(xfrm_sid == SECSID_NULL ?
- base_sid : xfrm_sid),
+ SECINITSID_NETMSG : xfrm_sid),
&nlbl_sid) != 0)
nlbl_sid = SECSID_NULL;
-
*sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid);
}
@@ -3690,7 +3696,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
if (sock && sock->sk->sk_family == PF_UNIX)
selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
else if (skb)
- selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peer_secid);
+ selinux_skb_extlbl_sid(skb, &peer_secid);
if (peer_secid == SECSID_NULL)
err = -EINVAL;
@@ -3751,7 +3757,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
u32 newsid;
u32 peersid;
- selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid);
+ selinux_skb_extlbl_sid(skb, &peersid);
if (peersid == SECSID_NULL) {
req->secid = sksec->sid;
req->peer_secid = SECSID_NULL;
@@ -3789,7 +3795,7 @@ static void selinux_inet_conn_established(struct sock *sk,
{
struct sk_security_struct *sksec = sk->sk_security;
- selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid);
+ selinux_skb_extlbl_sid(skb, &sksec->peer_sid);
}
static void selinux_req_classify_flow(const struct request_sock *req,
@@ -4626,7 +4632,7 @@ static int selinux_setprocattr(struct task_struct *p,
if (p->ptrace & PT_PTRACED) {
error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
SECCLASS_PROCESS,
- PROCESS__PTRACE, &avd);
+ PROCESS__PTRACE, 0, &avd);
if (!error)
tsec->sid = sid;
task_unlock(p);
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
index b83e74012a9..049bf69429b 100644
--- a/security/selinux/include/av_perm_to_string.h
+++ b/security/selinux/include/av_perm_to_string.h
@@ -158,3 +158,4 @@
S_(SECCLASS_KEY, KEY__CREATE, "create")
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind")
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect")
+ S_(SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, "mmap_zero")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index 5fee1735bff..eda89a2ec63 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -823,3 +823,4 @@
#define DCCP_SOCKET__NAME_BIND 0x00200000UL
#define DCCP_SOCKET__NODE_BIND 0x00400000UL
#define DCCP_SOCKET__NAME_CONNECT 0x00800000UL
+#define MEMPROTECT__MMAP_ZERO 0x00000001UL
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 6ed10c3d333..e145f6e13b0 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -102,9 +102,11 @@ void avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd, int result, struct avc_audit_data *auditdata);
+#define AVC_STRICT 1 /* Ignore permissive mode. */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct av_decision *avd);
+ u16 tclass, u32 requested,
+ unsigned flags,
+ struct av_decision *avd);
int avc_has_perm(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h
index 37879906844..e77de0e62ea 100644
--- a/security/selinux/include/class_to_string.h
+++ b/security/selinux/include/class_to_string.h
@@ -63,3 +63,4 @@
S_("key")
S_(NULL)
S_("dccp_socket")
+ S_("memprotect")
diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h
index 35f309f4787..a9c2b20f14b 100644
--- a/security/selinux/include/flask.h
+++ b/security/selinux/include/flask.h
@@ -49,6 +49,7 @@
#define SECCLASS_PACKET 57
#define SECCLASS_KEY 58
#define SECCLASS_DCCP_SOCKET 60
+#define SECCLASS_MEMPROTECT 61
/*
* Security identifier indices for initial entities
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index b94378afea2..83bdd4d2a29 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -41,6 +41,7 @@ extern int selinux_mls_enabled;
int security_load_policy(void * data, size_t len);
+#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
u32 decided;
@@ -87,6 +88,9 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
+int security_get_classes(char ***classes, int *nclasses);
+int security_get_permissions(char *class, char ***perms, int *nperms);
+
#define SECURITY_FS_USE_XATTR 1 /* use xattr */
#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
#define SECURITY_FS_USE_TASK 3 /* use task SIDs, e.g. pipefs/sockfs */
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index e64eca246f1..8192e8bc9f5 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -158,9 +158,7 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, &secattr);
if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
- rc = security_netlbl_secattr_to_sid(&secattr,
- base_sid,
- sid);
+ rc = security_netlbl_secattr_to_sid(&secattr, base_sid, sid);
else
*sid = SECSID_NULL;
netlbl_secattr_destroy(&secattr);
@@ -198,7 +196,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
if (netlbl_sock_getattr(sk, &secattr) == 0 &&
secattr.flags != NETLBL_SECATTR_NONE &&
security_netlbl_secattr_to_sid(&secattr,
- SECINITSID_UNLABELED,
+ SECINITSID_NETMSG,
&nlbl_peer_sid) == 0)
sksec->peer_sid = nlbl_peer_sid;
netlbl_secattr_destroy(&secattr);
@@ -295,38 +293,32 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct avc_audit_data *ad)
{
int rc;
- u32 netlbl_sid;
- u32 recv_perm;
+ u32 nlbl_sid;
+ u32 perm;
- rc = selinux_netlbl_skbuff_getsid(skb,
- SECINITSID_UNLABELED,
- &netlbl_sid);
+ rc = selinux_netlbl_skbuff_getsid(skb, SECINITSID_NETMSG, &nlbl_sid);
if (rc != 0)
return rc;
-
- if (netlbl_sid == SECSID_NULL)
- return 0;
+ if (nlbl_sid == SECSID_NULL)
+ nlbl_sid = SECINITSID_UNLABELED;
switch (sksec->sclass) {
case SECCLASS_UDP_SOCKET:
- recv_perm = UDP_SOCKET__RECVFROM;
+ perm = UDP_SOCKET__RECVFROM;
break;
case SECCLASS_TCP_SOCKET:
- recv_perm = TCP_SOCKET__RECVFROM;
+ perm = TCP_SOCKET__RECVFROM;
break;
default:
- recv_perm = RAWIP_SOCKET__RECVFROM;
+ perm = RAWIP_SOCKET__RECVFROM;
}
- rc = avc_has_perm(sksec->sid,
- netlbl_sid,
- sksec->sclass,
- recv_perm,
- ad);
+ rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
if (rc == 0)
return 0;
- netlbl_skbuff_err(skb, rc);
+ if (nlbl_sid != SECINITSID_UNLABELED)
+ netlbl_skbuff_err(skb, rc);
return rc;
}
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index aca099aa2ed..c9e92daedee 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -67,6 +67,10 @@ static struct dentry *bool_dir = NULL;
static int bool_num = 0;
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. */
@@ -106,6 +110,7 @@ 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 12
@@ -237,6 +242,11 @@ 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);
+
+/* 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)
@@ -287,10 +297,18 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf,
goto out;
ret = sel_make_bools();
+ if (ret) {
+ length = ret;
+ goto out1;
+ }
+
+ ret = sel_make_classes();
if (ret)
length = ret;
else
length = count;
+
+out1:
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
"policy loaded auid=%u",
audit_get_loginuid(current->audit_context));
@@ -940,9 +958,8 @@ static const struct file_operations sel_commit_bools_ops = {
.write = sel_commit_bools_write,
};
-/* delete booleans - partial revoke() from
- * fs/proc/generic.c proc_kill_inodes */
-static void sel_remove_bools(struct dentry *de)
+/* partial revoke() from fs/proc/generic.c proc_kill_inodes */
+static void sel_remove_entries(struct dentry *de)
{
struct list_head *p, *node;
struct super_block *sb = de->d_sb;
@@ -998,7 +1015,7 @@ static int sel_make_bools(void)
kfree(bool_pending_values);
bool_pending_values = NULL;
- sel_remove_bools(dir);
+ sel_remove_entries(dir);
if (!(page = (char*)get_zeroed_page(GFP_KERNEL)))
return -ENOMEM;
@@ -1048,7 +1065,7 @@ out:
return ret;
err:
kfree(values);
- sel_remove_bools(dir);
+ sel_remove_entries(dir);
ret = -ENOMEM;
goto out;
}
@@ -1294,7 +1311,227 @@ out:
return ret;
}
-static int sel_make_dir(struct inode *dir, struct dentry *dentry)
+static inline unsigned int sel_div(unsigned long a, unsigned long b)
+{
+ return a / b - (a % b < 0);
+}
+
+static inline unsigned long sel_class_to_ino(u16 class)
+{
+ return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET;
+}
+
+static inline u16 sel_ino_to_class(unsigned long ino)
+{
+ return sel_div(ino & SEL_INO_MASK, SEL_VEC_MAX + 1);
+}
+
+static inline unsigned long sel_perm_to_ino(u16 class, u32 perm)
+{
+ return (class * (SEL_VEC_MAX + 1) + perm) | SEL_CLASS_INO_OFFSET;
+}
+
+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,
+ size_t count, loff_t *ppos)
+{
+ ssize_t rc, len;
+ char *page;
+ unsigned long ino = file->f_path.dentry->d_inode->i_ino;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_class(ino));
+ rc = simple_read_from_buffer(buf, count, ppos, page, len);
+ free_page((unsigned long)page);
+out:
+ return rc;
+}
+
+static const struct file_operations sel_class_ops = {
+ .read = sel_read_class,
+};
+
+static ssize_t sel_read_perm(struct file * file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t rc, len;
+ char *page;
+ unsigned long ino = file->f_path.dentry->d_inode->i_ino;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ 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:
+ return rc;
+}
+
+static const struct file_operations sel_perm_ops = {
+ .read = sel_read_perm,
+};
+
+static int sel_make_perm_files(char *objclass, int classvalue,
+ struct dentry *dir)
+{
+ int i, rc = 0, nperms;
+ char **perms;
+
+ rc = security_get_permissions(objclass, &perms, &nperms);
+ if (rc)
+ goto out;
+
+ for (i = 0; i < nperms; i++) {
+ struct inode *inode;
+ struct dentry *dentry;
+
+ dentry = d_alloc_name(dir, perms[i]);
+ if (!dentry) {
+ rc = -ENOMEM;
+ goto out1;
+ }
+
+ inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
+ if (!inode) {
+ rc = -ENOMEM;
+ goto out1;
+ }
+ 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);
+ }
+
+out1:
+ for (i = 0; i < nperms; i++)
+ kfree(perms[i]);
+ kfree(perms);
+out:
+ return rc;
+}
+
+static int sel_make_class_dir_entries(char *classname, int index,
+ struct dentry *dir)
+{
+ struct dentry *dentry = NULL;
+ struct inode *inode = NULL;
+ int rc;
+
+ dentry = d_alloc_name(dir, "index");
+ if (!dentry) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
+ if (!inode) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ inode->i_fop = &sel_class_ops;
+ inode->i_ino = sel_class_to_ino(index);
+ d_add(dentry, inode);
+
+ dentry = d_alloc_name(dir, "perms");
+ if (!dentry) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = sel_make_dir(dir->d_inode, dentry, &last_class_ino);
+ if (rc)
+ goto out;
+
+ rc = sel_make_perm_files(classname, index, dentry);
+
+out:
+ return rc;
+}
+
+static void sel_remove_classes(void)
+{
+ struct list_head *class_node;
+
+ list_for_each(class_node, &class_dir->d_subdirs) {
+ struct dentry *class_subdir = list_entry(class_node,
+ struct dentry, d_u.d_child);
+ struct list_head *class_subdir_node;
+
+ list_for_each(class_subdir_node, &class_subdir->d_subdirs) {
+ struct dentry *d = list_entry(class_subdir_node,
+ struct dentry, d_u.d_child);
+
+ if (d->d_inode)
+ if (d->d_inode->i_mode & S_IFDIR)
+ sel_remove_entries(d);
+ }
+
+ sel_remove_entries(class_subdir);
+ }
+
+ sel_remove_entries(class_dir);
+}
+
+static int sel_make_classes(void)
+{
+ int rc = 0, nclasses, i;
+ char **classes;
+
+ /* delete any existing entries */
+ sel_remove_classes();
+
+ rc = security_get_classes(&classes, &nclasses);
+ if (rc < 0)
+ goto out;
+
+ /* +2 since classes are 1-indexed */
+ last_class_ino = sel_class_to_ino(nclasses+2);
+
+ for (i = 0; i < nclasses; i++) {
+ struct dentry *class_name_dir;
+
+ class_name_dir = d_alloc_name(class_dir, classes[i]);
+ if (!class_name_dir) {
+ rc = -ENOMEM;
+ goto out1;
+ }
+
+ rc = sel_make_dir(class_dir->d_inode, class_name_dir,
+ &last_class_ino);
+ if (rc)
+ goto out1;
+
+ /* i+1 since class values are 1-indexed */
+ rc = sel_make_class_dir_entries(classes[i], i+1,
+ class_name_dir);
+ if (rc)
+ goto out1;
+ }
+
+out1:
+ for (i = 0; i < nclasses; i++)
+ kfree(classes[i]);
+ kfree(classes);
+out:
+ return rc;
+}
+
+static int sel_make_dir(struct inode *dir, struct dentry *dentry,
+ unsigned long *ino)
{
int ret = 0;
struct inode *inode;
@@ -1306,7 +1543,7 @@ static int sel_make_dir(struct inode *dir, struct dentry *dentry)
}
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
- inode->i_ino = ++sel_last_ino;
+ inode->i_ino = ++(*ino);
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode);
d_add(dentry, inode);
@@ -1352,7 +1589,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
goto err;
}
- ret = sel_make_dir(root_inode, dentry);
+ ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
if (ret)
goto err;
@@ -1385,7 +1622,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
goto err;
}
- ret = sel_make_dir(root_inode, dentry);
+ ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
if (ret)
goto err;
@@ -1399,7 +1636,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
goto err;
}
- ret = sel_make_dir(root_inode, dentry);
+ ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
if (ret)
goto err;
@@ -1407,6 +1644,18 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
if (ret)
goto err;
+ dentry = d_alloc_name(sb->s_root, "class");
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
+ if (ret)
+ goto err;
+
+ class_dir = dentry;
+
out:
return ret;
err:
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 0ac1021734c..f05f97a2bc3 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -21,6 +21,7 @@
*/
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/errno.h>
@@ -598,6 +599,7 @@ void policydb_destroy(struct policydb *p)
struct range_trans *rt, *lrt = NULL;
for (i = 0; i < SYM_NUM; i++) {
+ cond_resched();
hashtab_map(p->symtab[i].table, destroy_f[i], NULL);
hashtab_destroy(p->symtab[i].table);
}
@@ -612,6 +614,7 @@ void policydb_destroy(struct policydb *p)
avtab_destroy(&p->te_avtab);
for (i = 0; i < OCON_NUM; i++) {
+ cond_resched();
c = p->ocontexts[i];
while (c) {
ctmp = c;
@@ -623,6 +626,7 @@ void policydb_destroy(struct policydb *p)
g = p->genfs;
while (g) {
+ cond_resched();
kfree(g->fstype);
c = g->head;
while (c) {
@@ -639,18 +643,21 @@ void policydb_destroy(struct policydb *p)
cond_policydb_destroy(p);
for (tr = p->role_tr; tr; tr = tr->next) {
+ cond_resched();
kfree(ltr);
ltr = tr;
}
kfree(ltr);
for (ra = p->role_allow; ra; ra = ra -> next) {
+ cond_resched();
kfree(lra);
lra = ra;
}
kfree(lra);
for (rt = p->range_tr; rt; rt = rt -> next) {
+ cond_resched();
if (lrt) {
ebitmap_destroy(&lrt->target_range.level[0].cat);
ebitmap_destroy(&lrt->target_range.level[1].cat);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 40660ffd49b..b5f017f07a7 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1587,19 +1587,18 @@ int security_get_user_sids(u32 fromsid,
u32 *nel)
{
struct context *fromcon, usercon;
- u32 *mysids, *mysids2, sid;
+ u32 *mysids = NULL, *mysids2, sid;
u32 mynel = 0, maxnel = SIDS_NEL;
struct user_datum *user;
struct role_datum *role;
- struct av_decision avd;
struct ebitmap_node *rnode, *tnode;
int rc = 0, i, j;
- if (!ss_initialized) {
- *sids = NULL;
- *nel = 0;
+ *sids = NULL;
+ *nel = 0;
+
+ if (!ss_initialized)
goto out;
- }
POLICY_RDLOCK;
@@ -1635,17 +1634,9 @@ int security_get_user_sids(u32 fromsid,
if (mls_setup_user_range(fromcon, user, &usercon))
continue;
- rc = context_struct_compute_av(fromcon, &usercon,
- SECCLASS_PROCESS,
- PROCESS__TRANSITION,
- &avd);
- if (rc || !(avd.allowed & PROCESS__TRANSITION))
- continue;
rc = sidtab_context_to_sid(&sidtab, &usercon, &sid);
- if (rc) {
- kfree(mysids);
+ if (rc)
goto out_unlock;
- }
if (mynel < maxnel) {
mysids[mynel++] = sid;
} else {
@@ -1653,7 +1644,6 @@ int security_get_user_sids(u32 fromsid,
mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC);
if (!mysids2) {
rc = -ENOMEM;
- kfree(mysids);
goto out_unlock;
}
memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
@@ -1664,11 +1654,32 @@ int security_get_user_sids(u32 fromsid,
}
}
- *sids = mysids;
- *nel = mynel;
-
out_unlock:
POLICY_RDUNLOCK;
+ if (rc || !mynel) {
+ kfree(mysids);
+ goto out;
+ }
+
+ mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
+ if (!mysids2) {
+ rc = -ENOMEM;
+ kfree(mysids);
+ goto out;
+ }
+ for (i = 0, j = 0; i < mynel; i++) {
+ rc = avc_has_perm_noaudit(fromsid, mysids[i],
+ SECCLASS_PROCESS,
+ PROCESS__TRANSITION, AVC_STRICT,
+ NULL);
+ if (!rc)
+ mysids2[j++] = mysids[i];
+ cond_resched();
+ }
+ rc = 0;
+ kfree(mysids);
+ *sids = mysids2;
+ *nel = j;
out:
return rc;
}
@@ -1996,6 +2007,101 @@ out:
return rc;
}
+static int get_classes_callback(void *k, void *d, void *args)
+{
+ struct class_datum *datum = d;
+ char *name = k, **classes = args;
+ int value = datum->value - 1;
+
+ classes[value] = kstrdup(name, GFP_ATOMIC);
+ if (!classes[value])
+ return -ENOMEM;
+
+ return 0;
+}
+
+int security_get_classes(char ***classes, int *nclasses)
+{
+ int rc = -ENOMEM;
+
+ POLICY_RDLOCK;
+
+ *nclasses = policydb.p_classes.nprim;
+ *classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC);
+ if (!*classes)
+ goto out;
+
+ rc = hashtab_map(policydb.p_classes.table, get_classes_callback,
+ *classes);
+ if (rc < 0) {
+ int i;
+ for (i = 0; i < *nclasses; i++)
+ kfree((*classes)[i]);
+ kfree(*classes);
+ }
+
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+}
+
+static int get_permissions_callback(void *k, void *d, void *args)
+{
+ struct perm_datum *datum = d;
+ char *name = k, **perms = args;
+ int value = datum->value - 1;
+
+ perms[value] = kstrdup(name, GFP_ATOMIC);
+ if (!perms[value])
+ return -ENOMEM;
+
+ return 0;
+}
+
+int security_get_permissions(char *class, char ***perms, int *nperms)
+{
+ int rc = -ENOMEM, i;
+ struct class_datum *match;
+
+ POLICY_RDLOCK;
+
+ match = hashtab_search(policydb.p_classes.table, class);
+ if (!match) {
+ printk(KERN_ERR "%s: unrecognized class %s\n",
+ __FUNCTION__, class);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ *nperms = match->permissions.nprim;
+ *perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC);
+ if (!*perms)
+ goto out;
+
+ if (match->comdatum) {
+ rc = hashtab_map(match->comdatum->permissions.table,
+ get_permissions_callback, *perms);
+ if (rc < 0)
+ goto err;
+ }
+
+ rc = hashtab_map(match->permissions.table, get_permissions_callback,
+ *perms);
+ if (rc < 0)
+ goto err;
+
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+
+err:
+ POLICY_RDUNLOCK;
+ for (i = 0; i < *nperms; i++)
+ kfree((*perms)[i]);
+ kfree(*perms);
+ return rc;
+}
+
struct selinux_audit_rule {
u32 au_seqno;
struct context au_ctxt;