diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/commoncap.c | 2 | ||||
-rw-r--r-- | security/dummy.c | 8 | ||||
-rw-r--r-- | security/keys/key.c | 2 | ||||
-rw-r--r-- | security/keys/request_key.c | 3 | ||||
-rw-r--r-- | security/security.c | 1 | ||||
-rw-r--r-- | security/selinux/avc.c | 14 | ||||
-rw-r--r-- | security/selinux/hooks.c | 46 | ||||
-rw-r--r-- | security/selinux/include/av_perm_to_string.h | 1 | ||||
-rw-r--r-- | security/selinux/include/av_permissions.h | 1 | ||||
-rw-r--r-- | security/selinux/include/avc.h | 6 | ||||
-rw-r--r-- | security/selinux/include/class_to_string.h | 1 | ||||
-rw-r--r-- | security/selinux/include/flask.h | 1 | ||||
-rw-r--r-- | security/selinux/include/security.h | 4 | ||||
-rw-r--r-- | security/selinux/netlabel.c | 85 | ||||
-rw-r--r-- | security/selinux/nlmsgtab.c | 2 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 269 | ||||
-rw-r--r-- | security/selinux/ss/avtab.c | 2 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 7 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 144 |
19 files changed, 500 insertions, 99 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index 384379ede4fd..338606eb7238 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -148,7 +148,7 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || !cap_issubset (new_permitted, current->cap_permitted)) { - current->mm->dumpable = suid_dumpable; + set_dumpable(current->mm, suid_dumpable); if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { if (!capable(CAP_SETUID)) { diff --git a/security/dummy.c b/security/dummy.c index 8ffd76405b5b..19d813d5e083 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -130,7 +130,7 @@ static void dummy_bprm_free_security (struct linux_binprm *bprm) static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) { if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) { - current->mm->dumpable = suid_dumpable; + set_dumpable(current->mm, suid_dumpable); if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) { bprm->e_uid = current->uid; @@ -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/keys/key.c b/security/keys/key.c index 700400d801dc..01bbc6d9d19b 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1001,7 +1001,7 @@ void __init key_init(void) { /* allocate a slab in which we can store keys */ key_jar = kmem_cache_create("key_jar", sizeof(struct key), - 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); + 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); /* add the special key types */ list_add_tail(&key_type_keyring.link, &key_types_list); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index f573ac189a0a..557500110a13 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -108,7 +108,8 @@ static int call_sbin_request_key(struct key *key, argv[i] = NULL; /* do it */ - ret = call_usermodehelper_keys(argv[0], argv, envp, keyring, 1); + ret = call_usermodehelper_keys(argv[0], argv, envp, keyring, + UMH_WAIT_PROC); error_link: key_put(keyring); diff --git a/security/security.c b/security/security.c index fc8601b2b7ac..27e5863d30f1 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 e4396a89edc6..ecd067384531 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -239,7 +239,7 @@ void __init avc_init(void) atomic_set(&avc_cache.lru_hint, 0); avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), - 0, SLAB_PANIC, NULL, NULL); + 0, SLAB_PANIC, NULL); audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n"); } @@ -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 ad8dd4e8657e..0fac6829c63a 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; @@ -2317,7 +2318,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) return -EOPNOTSUPP; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; AVC_AUDIT_DATA_INIT(&ad,FS); @@ -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); @@ -4907,7 +4913,7 @@ static __init int selinux_init(void) sel_inode_cache = kmem_cache_create("selinux_inode_security", sizeof(struct inode_security_struct), - 0, SLAB_PANIC, NULL, NULL); + 0, SLAB_PANIC, NULL); avc_init(); original_ops = secondary_ops = security_ops; diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index b83e74012a97..049bf69429b6 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 5fee1735bffe..eda89a2ec635 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 6ed10c3d3339..e145f6e13b0b 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 378799068441..e77de0e62ea0 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 35f309f47873..a9c2b20f14b5 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 b94378afea25..83bdd4d2a29e 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 bf8750791dd1..051b14c88e2d 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -36,8 +36,8 @@ #include "security.h" /** - * selinux_netlbl_socket_setsid - Label a socket using the NetLabel mechanism - * @sock: the socket to label + * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism + * @sk: the socket to label * @sid: the SID to use * * Description: @@ -47,17 +47,17 @@ * this function and rcu_read_unlock() after this function returns. * */ -static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) +static int selinux_netlbl_sock_setsid(struct sock *sk, u32 sid) { int rc; - struct sk_security_struct *sksec = sock->sk->sk_security; + struct sk_security_struct *sksec = sk->sk_security; struct netlbl_lsm_secattr secattr; rc = security_netlbl_sid_to_secattr(sid, &secattr); if (rc != 0) return rc; - rc = netlbl_socket_setattr(sock, &secattr); + rc = netlbl_sock_setattr(sk, &secattr); if (rc == 0) { spin_lock_bh(&sksec->nlbl_lock); sksec->nlbl_state = NLBL_LABELED; @@ -155,12 +155,15 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid) int rc; struct netlbl_lsm_secattr secattr; + if (!netlbl_enabled()) { + *sid = SECSID_NULL; + return 0; + } + 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 +201,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); @@ -206,7 +209,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) /* Try to set the NetLabel on the socket to save time later, if we fail * here we will pick up the pieces in later calls to * selinux_netlbl_inode_permission(). */ - selinux_netlbl_socket_setsid(sock, sksec->sid); + selinux_netlbl_sock_setsid(sk, sksec->sid); rcu_read_unlock(); } @@ -223,14 +226,15 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) int selinux_netlbl_socket_post_create(struct socket *sock) { int rc = 0; + struct sock *sk = sock->sk; struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; - struct sk_security_struct *sksec = sock->sk->sk_security; + struct sk_security_struct *sksec = sk->sk_security; sksec->sclass = isec->sclass; rcu_read_lock(); if (sksec->nlbl_state == NLBL_REQUIRE) - rc = selinux_netlbl_socket_setsid(sock, sksec->sid); + rc = selinux_netlbl_sock_setsid(sk, sksec->sid); rcu_read_unlock(); return rc; @@ -251,14 +255,16 @@ int selinux_netlbl_socket_post_create(struct socket *sock) int selinux_netlbl_inode_permission(struct inode *inode, int mask) { int rc; - struct sk_security_struct *sksec; + struct sock *sk; struct socket *sock; + struct sk_security_struct *sksec; if (!S_ISSOCK(inode->i_mode) || ((mask & (MAY_WRITE | MAY_APPEND)) == 0)) return 0; sock = SOCKET_I(inode); - sksec = sock->sk->sk_security; + sk = sock->sk; + sksec = sk->sk_security; rcu_read_lock(); if (sksec->nlbl_state != NLBL_REQUIRE) { @@ -266,9 +272,9 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask) return 0; } local_bh_disable(); - bh_lock_sock_nested(sock->sk); - rc = selinux_netlbl_socket_setsid(sock, sksec->sid); - bh_unlock_sock(sock->sk); + bh_lock_sock_nested(sk); + rc = selinux_netlbl_sock_setsid(sk, sksec->sid); + bh_unlock_sock(sk); local_bh_enable(); rcu_read_unlock(); @@ -292,38 +298,42 @@ 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; + struct netlbl_lsm_secattr secattr; - rc = selinux_netlbl_skbuff_getsid(skb, - SECINITSID_UNLABELED, - &netlbl_sid); + if (!netlbl_enabled()) + return 0; + + 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, + SECINITSID_NETMSG, + &nlbl_sid); + else + nlbl_sid = SECINITSID_UNLABELED; + netlbl_secattr_destroy(&secattr); if (rc != 0) return rc; - if (netlbl_sid == SECSID_NULL) - return 0; - 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; } @@ -345,14 +355,17 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, int optname) { int rc = 0; - struct sk_security_struct *sksec = sock->sk->sk_security; + struct sock *sk = sock->sk; + struct sk_security_struct *sksec = sk->sk_security; struct netlbl_lsm_secattr secattr; rcu_read_lock(); if (level == IPPROTO_IP && optname == IP_OPTIONS && sksec->nlbl_state == NLBL_LABELED) { netlbl_secattr_init(&secattr); - rc = netlbl_socket_getattr(sock, &secattr); + lock_sock(sk); + rc = netlbl_sock_getattr(sk, &secattr); + release_sock(sk); if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) rc = -EACCES; netlbl_secattr_destroy(&secattr); diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index ccfe8755735e..eddc7b420109 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -110,6 +110,8 @@ static struct nlmsg_perm nlmsg_audit_perms[] = { AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, }; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index aca099aa2ed3..c9e92daedee2 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/avtab.c b/security/selinux/ss/avtab.c index 3122908afdc1..85705eb289e0 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -445,7 +445,7 @@ void avtab_cache_init(void) { avtab_node_cachep = kmem_cache_create("avtab_node", sizeof(struct avtab_node), - 0, SLAB_PANIC, NULL, NULL); + 0, SLAB_PANIC, NULL); } void avtab_cache_destroy(void) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 0ac1021734c0..f05f97a2bc3a 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 40660ffd49b6..b5f017f07a75 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; |