diff options
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/avc.c | 25 | ||||
-rw-r--r-- | security/selinux/hooks.c | 292 | ||||
-rw-r--r-- | security/selinux/include/classmap.h | 16 | ||||
-rw-r--r-- | security/selinux/netnode.c | 2 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 16 | ||||
-rw-r--r-- | security/selinux/ss/avtab.c | 39 | ||||
-rw-r--r-- | security/selinux/ss/conditional.c | 65 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 658 | ||||
-rw-r--r-- | security/selinux/ss/policydb.h | 4 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 7 | ||||
-rw-r--r-- | security/selinux/ss/symtab.c | 2 |
11 files changed, 598 insertions, 528 deletions
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 7f1a304712a9..9da6420e2056 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -288,7 +288,6 @@ static struct avc_node *avc_alloc_node(void) if (!node) goto out; - INIT_RCU_HEAD(&node->rhead); INIT_HLIST_NODE(&node->list); avc_cache_stats_incr(allocations); @@ -489,9 +488,29 @@ void avc_audit(u32 ssid, u32 tsid, struct common_audit_data stack_data; u32 denied, audited; denied = requested & ~avd->allowed; - if (denied) + if (denied) { audited = denied & avd->auditdeny; - else if (result) + /* + * a->selinux_audit_data.auditdeny is TRICKY! Setting a bit in + * this field means that ANY denials should NOT be audited if + * the policy contains an explicit dontaudit rule for that + * permission. Take notice that this is unrelated to the + * actual permissions that were denied. As an example lets + * assume: + * + * denied == READ + * avd.auditdeny & ACCESS == 0 (not set means explicit rule) + * selinux_audit_data.auditdeny & ACCESS == 1 + * + * We will NOT audit the denial even though the denied + * permission was READ and the auditdeny checks were for + * ACCESS + */ + if (a && + a->selinux_audit_data.auditdeny && + !(a->selinux_audit_data.auditdeny & avd->auditdeny)) + audited = 0; + } else if (result) audited = denied = requested; else audited = requested & avd->auditallow; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5c9f25ba1c95..9b40f4c0ac70 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -87,9 +87,6 @@ #include "netlabel.h" #include "audit.h" -#define XATTR_SELINUX_SUFFIX "selinux" -#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX - #define NUM_SEL_MNT_OPTS 5 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); @@ -188,7 +185,7 @@ static inline u32 task_sid(const struct task_struct *task) */ static inline u32 current_sid(void) { - const struct task_security_struct *tsec = current_cred()->security; + const struct task_security_struct *tsec = current_security(); return tsec->sid; } @@ -279,32 +276,6 @@ static void superblock_free_security(struct super_block *sb) kfree(sbsec); } -static int sk_alloc_security(struct sock *sk, int family, gfp_t priority) -{ - struct sk_security_struct *sksec; - - sksec = kzalloc(sizeof(*sksec), priority); - if (!sksec) - return -ENOMEM; - - sksec->peer_sid = SECINITSID_UNLABELED; - sksec->sid = SECINITSID_UNLABELED; - sk->sk_security = sksec; - - selinux_netlbl_sk_security_reset(sksec); - - return 0; -} - -static void sk_free_security(struct sock *sk) -{ - struct sk_security_struct *sksec = sk->sk_security; - - sk->sk_security = NULL; - selinux_netlbl_sk_security_free(sksec); - kfree(sksec); -} - /* The security server must be initialized before any labeling or access decisions can be provided. */ extern int ss_initialized; @@ -1584,8 +1555,7 @@ static int may_create(struct inode *dir, struct dentry *dentry, u16 tclass) { - const struct cred *cred = current_cred(); - const struct task_security_struct *tsec = cred->security; + const struct task_security_struct *tsec = current_security(); struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; u32 sid, newsid; @@ -1806,27 +1776,9 @@ static inline u32 open_file_to_av(struct file *file) { u32 av = file_to_av(file); - if (selinux_policycap_openperm) { - mode_t mode = file->f_path.dentry->d_inode->i_mode; - /* - * lnk files and socks do not really have an 'open' - */ - if (S_ISREG(mode)) - av |= FILE__OPEN; - else if (S_ISCHR(mode)) - av |= CHR_FILE__OPEN; - else if (S_ISBLK(mode)) - av |= BLK_FILE__OPEN; - else if (S_ISFIFO(mode)) - av |= FIFO_FILE__OPEN; - else if (S_ISDIR(mode)) - av |= DIR__OPEN; - else if (S_ISSOCK(mode)) - av |= SOCK_FILE__OPEN; - else - printk(KERN_ERR "SELinux: WARNING: inside %s with " - "unknown mode:%o\n", __func__, mode); - } + if (selinux_policycap_openperm) + av |= FILE__OPEN; + return av; } @@ -2183,8 +2135,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) static int selinux_bprm_secureexec(struct linux_binprm *bprm) { - const struct cred *cred = current_cred(); - const struct task_security_struct *tsec = cred->security; + const struct task_security_struct *tsec = current_security(); u32 sid, osid; int atsecure = 0; @@ -2559,8 +2510,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, char **name, void **value, size_t *len) { - const struct cred *cred = current_cred(); - const struct task_security_struct *tsec = cred->security; + const struct task_security_struct *tsec = current_security(); struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; u32 sid, newsid, clen; @@ -2676,14 +2626,26 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na static int selinux_inode_permission(struct inode *inode, int mask) { const struct cred *cred = current_cred(); + struct common_audit_data ad; + u32 perms; + bool from_access; - if (!mask) { - /* No permission to check. Existence test. */ + from_access = mask & MAY_ACCESS; + mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); + + /* No permission to check. Existence test. */ + if (!mask) return 0; - } - return inode_has_perm(cred, inode, - file_mask_to_av(inode->i_mode, mask), NULL); + COMMON_AUDIT_DATA_INIT(&ad, FS); + ad.u.fs.inode = inode; + + if (from_access) + ad.selinux_audit_data.auditdeny |= FILE__AUDIT_ACCESS; + + perms = file_mask_to_av(inode->i_mode, mask); + + return inode_has_perm(cred, inode, perms, &ad); } static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) @@ -3671,71 +3633,54 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) } /* socket security operations */ -static int socket_has_perm(struct task_struct *task, struct socket *sock, - u32 perms) + +static u32 socket_sockcreate_sid(const struct task_security_struct *tsec) { - struct inode_security_struct *isec; - struct common_audit_data ad; - u32 sid; - int err = 0; + return tsec->sockcreate_sid ? : tsec->sid; +} - isec = SOCK_INODE(sock)->i_security; +static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) +{ + struct sk_security_struct *sksec = sk->sk_security; + struct common_audit_data ad; + u32 tsid = task_sid(task); - if (isec->sid == SECINITSID_KERNEL) - goto out; - sid = task_sid(task); + if (sksec->sid == SECINITSID_KERNEL) + return 0; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.sk = sock->sk; - err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); + ad.u.net.sk = sk; -out: - return err; + return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad); } static int selinux_socket_create(int family, int type, int protocol, int kern) { - const struct cred *cred = current_cred(); - const struct task_security_struct *tsec = cred->security; - u32 sid, newsid; + const struct task_security_struct *tsec = current_security(); + u32 newsid; u16 secclass; - int err = 0; if (kern) - goto out; - - sid = tsec->sid; - newsid = tsec->sockcreate_sid ?: sid; + return 0; + newsid = socket_sockcreate_sid(tsec); secclass = socket_type_to_security_class(family, type, protocol); - err = avc_has_perm(sid, newsid, secclass, SOCKET__CREATE, NULL); - -out: - return err; + return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL); } static int selinux_socket_post_create(struct socket *sock, int family, int type, int protocol, int kern) { - const struct cred *cred = current_cred(); - const struct task_security_struct *tsec = cred->security; - struct inode_security_struct *isec; + const struct task_security_struct *tsec = current_security(); + struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; struct sk_security_struct *sksec; - u32 sid, newsid; int err = 0; - sid = tsec->sid; - newsid = tsec->sockcreate_sid; - - isec = SOCK_INODE(sock)->i_security; - if (kern) isec->sid = SECINITSID_KERNEL; - else if (newsid) - isec->sid = newsid; else - isec->sid = sid; + isec->sid = socket_sockcreate_sid(tsec); isec->sclass = socket_type_to_security_class(family, type, protocol); isec->initialized = 1; @@ -3756,10 +3701,11 @@ static int selinux_socket_post_create(struct socket *sock, int family, static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { + struct sock *sk = sock->sk; u16 family; int err; - err = socket_has_perm(current, sock, SOCKET__BIND); + err = sock_has_perm(current, sk, SOCKET__BIND); if (err) goto out; @@ -3768,19 +3714,16 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in * Multiple address binding for SCTP is not supported yet: we just * check the first address now. */ - family = sock->sk->sk_family; + family = sk->sk_family; if (family == PF_INET || family == PF_INET6) { char *addrp; - struct inode_security_struct *isec; + struct sk_security_struct *sksec = sk->sk_security; struct common_audit_data ad; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; unsigned short snum; - struct sock *sk = sock->sk; u32 sid, node_perm; - isec = SOCK_INODE(sock)->i_security; - if (family == PF_INET) { addr4 = (struct sockaddr_in *)address; snum = ntohs(addr4->sin_port); @@ -3804,15 +3747,15 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in COMMON_AUDIT_DATA_INIT(&ad, NET); ad.u.net.sport = htons(snum); ad.u.net.family = family; - err = avc_has_perm(isec->sid, sid, - isec->sclass, + err = avc_has_perm(sksec->sid, sid, + sksec->sclass, SOCKET__NAME_BIND, &ad); if (err) goto out; } } - switch (isec->sclass) { + switch (sksec->sclass) { case SECCLASS_TCP_SOCKET: node_perm = TCP_SOCKET__NODE_BIND; break; @@ -3843,8 +3786,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in else ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); - err = avc_has_perm(isec->sid, sid, - isec->sclass, node_perm, &ad); + err = avc_has_perm(sksec->sid, sid, + sksec->sclass, node_perm, &ad); if (err) goto out; } @@ -3855,19 +3798,18 @@ out: static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) { struct sock *sk = sock->sk; - struct inode_security_struct *isec; + struct sk_security_struct *sksec = sk->sk_security; int err; - err = socket_has_perm(current, sock, SOCKET__CONNECT); + err = sock_has_perm(current, sk, SOCKET__CONNECT); if (err) return err; /* * If a TCP or DCCP socket, check name_connect permission for the port. */ - isec = SOCK_INODE(sock)->i_security; - if (isec->sclass == SECCLASS_TCP_SOCKET || - isec->sclass == SECCLASS_DCCP_SOCKET) { + if (sksec->sclass == SECCLASS_TCP_SOCKET || + sksec->sclass == SECCLASS_DCCP_SOCKET) { struct common_audit_data ad; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; @@ -3890,13 +3832,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, if (err) goto out; - perm = (isec->sclass == SECCLASS_TCP_SOCKET) ? + perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; COMMON_AUDIT_DATA_INIT(&ad, NET); ad.u.net.dport = htons(snum); ad.u.net.family = sk->sk_family; - err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad); + err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad); if (err) goto out; } @@ -3909,7 +3851,7 @@ out: static int selinux_socket_listen(struct socket *sock, int backlog) { - return socket_has_perm(current, sock, SOCKET__LISTEN); + return sock_has_perm(current, sock->sk, SOCKET__LISTEN); } static int selinux_socket_accept(struct socket *sock, struct socket *newsock) @@ -3918,7 +3860,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) struct inode_security_struct *isec; struct inode_security_struct *newisec; - err = socket_has_perm(current, sock, SOCKET__ACCEPT); + err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); if (err) return err; @@ -3935,30 +3877,30 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { - return socket_has_perm(current, sock, SOCKET__WRITE); + return sock_has_perm(current, sock->sk, SOCKET__WRITE); } static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags) { - return socket_has_perm(current, sock, SOCKET__READ); + return sock_has_perm(current, sock->sk, SOCKET__READ); } static int selinux_socket_getsockname(struct socket *sock) { - return socket_has_perm(current, sock, SOCKET__GETATTR); + return sock_has_perm(current, sock->sk, SOCKET__GETATTR); } static int selinux_socket_getpeername(struct socket *sock) { - return socket_has_perm(current, sock, SOCKET__GETATTR); + return sock_has_perm(current, sock->sk, SOCKET__GETATTR); } static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) { int err; - err = socket_has_perm(current, sock, SOCKET__SETOPT); + err = sock_has_perm(current, sock->sk, SOCKET__SETOPT); if (err) return err; @@ -3968,68 +3910,58 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname static int selinux_socket_getsockopt(struct socket *sock, int level, int optname) { - return socket_has_perm(current, sock, SOCKET__GETOPT); + return sock_has_perm(current, sock->sk, SOCKET__GETOPT); } static int selinux_socket_shutdown(struct socket *sock, int how) { - return socket_has_perm(current, sock, SOCKET__SHUTDOWN); + return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN); } static int selinux_socket_unix_stream_connect(struct socket *sock, struct socket *other, struct sock *newsk) { - struct sk_security_struct *sksec; - struct inode_security_struct *isec; - struct inode_security_struct *other_isec; + struct sk_security_struct *sksec_sock = sock->sk->sk_security; + struct sk_security_struct *sksec_other = other->sk->sk_security; + struct sk_security_struct *sksec_new = newsk->sk_security; struct common_audit_data ad; int err; - isec = SOCK_INODE(sock)->i_security; - other_isec = SOCK_INODE(other)->i_security; - COMMON_AUDIT_DATA_INIT(&ad, NET); ad.u.net.sk = other->sk; - err = avc_has_perm(isec->sid, other_isec->sid, - isec->sclass, + err = avc_has_perm(sksec_sock->sid, sksec_other->sid, + sksec_other->sclass, UNIX_STREAM_SOCKET__CONNECTTO, &ad); if (err) return err; - /* connecting socket */ - sksec = sock->sk->sk_security; - sksec->peer_sid = other_isec->sid; - /* server child socket */ - sksec = newsk->sk_security; - sksec->peer_sid = isec->sid; - err = security_sid_mls_copy(other_isec->sid, sksec->peer_sid, &sksec->sid); + sksec_new->peer_sid = sksec_sock->sid; + err = security_sid_mls_copy(sksec_other->sid, sksec_sock->sid, + &sksec_new->sid); + if (err) + return err; - return err; + /* connecting socket */ + sksec_sock->peer_sid = sksec_new->sid; + + return 0; } static int selinux_socket_unix_may_send(struct socket *sock, struct socket *other) { - struct inode_security_struct *isec; - struct inode_security_struct *other_isec; + struct sk_security_struct *ssec = sock->sk->sk_security; + struct sk_security_struct *osec = other->sk->sk_security; struct common_audit_data ad; - int err; - - isec = SOCK_INODE(sock)->i_security; - other_isec = SOCK_INODE(other)->i_security; COMMON_AUDIT_DATA_INIT(&ad, NET); ad.u.net.sk = other->sk; - err = avc_has_perm(isec->sid, other_isec->sid, - isec->sclass, SOCKET__SENDTO, &ad); - if (err) - return err; - - return 0; + return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, + &ad); } static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, @@ -4168,26 +4100,18 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op int err = 0; char *scontext; u32 scontext_len; - struct sk_security_struct *sksec; - struct inode_security_struct *isec; + struct sk_security_struct *sksec = sock->sk->sk_security; u32 peer_sid = SECSID_NULL; - isec = SOCK_INODE(sock)->i_security; - - if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET || - isec->sclass == SECCLASS_TCP_SOCKET) { - sksec = sock->sk->sk_security; + if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET || + sksec->sclass == SECCLASS_TCP_SOCKET) peer_sid = sksec->peer_sid; - } - if (peer_sid == SECSID_NULL) { - err = -ENOPROTOOPT; - goto out; - } + if (peer_sid == SECSID_NULL) + return -ENOPROTOOPT; err = security_sid_to_context(peer_sid, &scontext, &scontext_len); - if (err) - goto out; + return err; if (scontext_len > len) { err = -ERANGE; @@ -4200,9 +4124,7 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op out_len: if (put_user(scontext_len, optlen)) err = -EFAULT; - kfree(scontext); -out: return err; } @@ -4234,12 +4156,27 @@ out: static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) { - return sk_alloc_security(sk, family, priority); + struct sk_security_struct *sksec; + + sksec = kzalloc(sizeof(*sksec), priority); + if (!sksec) + return -ENOMEM; + + sksec->peer_sid = SECINITSID_UNLABELED; + sksec->sid = SECINITSID_UNLABELED; + selinux_netlbl_sk_security_reset(sksec); + sk->sk_security = sksec; + + return 0; } static void selinux_sk_free_security(struct sock *sk) { - sk_free_security(sk); + struct sk_security_struct *sksec = sk->sk_security; + + sk->sk_security = NULL; + selinux_netlbl_sk_security_free(sksec); + kfree(sksec); } static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) @@ -4399,8 +4336,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) int err = 0; u32 perm; struct nlmsghdr *nlh; - struct socket *sock = sk->sk_socket; - struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; + struct sk_security_struct *sksec = sk->sk_security; if (skb->len < NLMSG_SPACE(0)) { err = -EINVAL; @@ -4408,13 +4344,13 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) } nlh = nlmsg_hdr(skb); - err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); + err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); if (err) { if (err == -EINVAL) { audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, "SELinux: unrecognized netlink message" " type=%hu for sclass=%hu\n", - nlh->nlmsg_type, isec->sclass); + nlh->nlmsg_type, sksec->sclass); if (!selinux_enforcing || security_get_allow_unknown()) err = 0; } @@ -4425,7 +4361,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) goto out; } - err = socket_has_perm(current, sock, perm); + err = sock_has_perm(current, sk, perm); out: return err; } diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 8b32e959bb2e..b4c9eb4bd6f9 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -2,7 +2,8 @@ "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ - "rename", "execute", "swapon", "quotaon", "mounton" + "rename", "execute", "swapon", "quotaon", "mounton", "audit_access", \ + "open", "execmod" #define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \ "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \ @@ -43,22 +44,21 @@ struct security_class_mapping secclass_map[] = { "quotaget", NULL } }, { "file", { COMMON_FILE_PERMS, - "execute_no_trans", "entrypoint", "execmod", "open", NULL } }, + "execute_no_trans", "entrypoint", NULL } }, { "dir", { COMMON_FILE_PERMS, "add_name", "remove_name", - "reparent", "search", "rmdir", "open", NULL } }, + "reparent", "search", "rmdir", NULL } }, { "fd", { "use", NULL } }, { "lnk_file", { COMMON_FILE_PERMS, NULL } }, { "chr_file", - { COMMON_FILE_PERMS, - "execute_no_trans", "entrypoint", "execmod", "open", NULL } }, + { COMMON_FILE_PERMS, NULL } }, { "blk_file", - { COMMON_FILE_PERMS, "open", NULL } }, + { COMMON_FILE_PERMS, NULL } }, { "sock_file", - { COMMON_FILE_PERMS, "open", NULL } }, + { COMMON_FILE_PERMS, NULL } }, { "fifo_file", - { COMMON_FILE_PERMS, "open", NULL } }, + { COMMON_FILE_PERMS, NULL } }, { "socket", { COMMON_SOCK_PERMS, NULL } }, { "tcp_socket", diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index dc92792271f1..65ebfe954f85 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -183,8 +183,6 @@ static void sel_netnode_insert(struct sel_netnode *node) BUG(); } - INIT_RCU_HEAD(&node->rcu); - /* we need to impose a limit on the growth of the hash table so check * this bucket to make sure it is within the specified bounds */ list_add_rcu(&node->list, &sel_netnode_hash[idx].list); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 0293843f7eda..79a1bb635662 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -184,6 +184,7 @@ out: static const struct file_operations sel_enforce_ops = { .read = sel_read_enforce, .write = sel_write_enforce, + .llseek = generic_file_llseek, }; static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, @@ -201,6 +202,7 @@ static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, static const struct file_operations sel_handle_unknown_ops = { .read = sel_read_handle_unknown, + .llseek = generic_file_llseek, }; #ifdef CONFIG_SECURITY_SELINUX_DISABLE @@ -251,6 +253,7 @@ out: static const struct file_operations sel_disable_ops = { .write = sel_write_disable, + .llseek = generic_file_llseek, }; static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, @@ -265,6 +268,7 @@ static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, static const struct file_operations sel_policyvers_ops = { .read = sel_read_policyvers, + .llseek = generic_file_llseek, }; /* declaration for sel_write_load */ @@ -289,6 +293,7 @@ static ssize_t sel_read_mls(struct file *filp, char __user *buf, static const struct file_operations sel_mls_ops = { .read = sel_read_mls, + .llseek = generic_file_llseek, }; static ssize_t sel_write_load(struct file *file, const char __user *buf, @@ -356,6 +361,7 @@ out: static const struct file_operations sel_load_ops = { .write = sel_write_load, + .llseek = generic_file_llseek, }; static ssize_t sel_write_context(struct file *file, char *buf, size_t size) @@ -437,6 +443,7 @@ out: static const struct file_operations sel_checkreqprot_ops = { .read = sel_read_checkreqprot, .write = sel_write_checkreqprot, + .llseek = generic_file_llseek, }; /* @@ -482,6 +489,7 @@ static const struct file_operations transaction_ops = { .write = selinux_transaction_write, .read = simple_transaction_read, .release = simple_transaction_release, + .llseek = generic_file_llseek, }; /* @@ -883,6 +891,7 @@ out: static const struct file_operations sel_bool_ops = { .read = sel_read_bool, .write = sel_write_bool, + .llseek = generic_file_llseek, }; static ssize_t sel_commit_bools_write(struct file *filep, @@ -935,6 +944,7 @@ out: static const struct file_operations sel_commit_bools_ops = { .write = sel_commit_bools_write, + .llseek = generic_file_llseek, }; static void sel_remove_entries(struct dentry *de) @@ -1127,10 +1137,12 @@ out: static const struct file_operations sel_avc_cache_threshold_ops = { .read = sel_read_avc_cache_threshold, .write = sel_write_avc_cache_threshold, + .llseek = generic_file_llseek, }; static const struct file_operations sel_avc_hash_stats_ops = { .read = sel_read_avc_hash_stats, + .llseek = generic_file_llseek, }; #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS @@ -1255,6 +1267,7 @@ static ssize_t sel_read_initcon(struct file *file, char __user *buf, static const struct file_operations sel_initcon_ops = { .read = sel_read_initcon, + .llseek = generic_file_llseek, }; static int sel_make_initcon_files(struct dentry *dir) @@ -1330,6 +1343,7 @@ out: static const struct file_operations sel_class_ops = { .read = sel_read_class, + .llseek = generic_file_llseek, }; static ssize_t sel_read_perm(struct file *file, char __user *buf, @@ -1354,6 +1368,7 @@ out: static const struct file_operations sel_perm_ops = { .read = sel_read_perm, + .llseek = generic_file_llseek, }; static ssize_t sel_read_policycap(struct file *file, char __user *buf, @@ -1372,6 +1387,7 @@ static ssize_t sel_read_policycap(struct file *file, char __user *buf, static const struct file_operations sel_policycap_ops = { .read = sel_read_policycap, + .llseek = generic_file_llseek, }; static int sel_make_perm_files(char *objclass, int classvalue, diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 1215b8e47dba..929480c6c430 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -342,20 +342,20 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, if (vers < POLICYDB_VERSION_AVTAB) { rc = next_entry(buf32, fp, sizeof(u32)); - if (rc < 0) { + if (rc) { printk(KERN_ERR "SELinux: avtab: truncated entry\n"); - return -1; + return rc; } items2 = le32_to_cpu(buf32[0]); if (items2 > ARRAY_SIZE(buf32)) { printk(KERN_ERR "SELinux: avtab: entry overflow\n"); - return -1; + return -EINVAL; } rc = next_entry(buf32, fp, sizeof(u32)*items2); - if (rc < 0) { + if (rc) { printk(KERN_ERR "SELinux: avtab: truncated entry\n"); - return -1; + return rc; } items = 0; @@ -363,19 +363,19 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, key.source_type = (u16)val; if (key.source_type != val) { printk(KERN_ERR "SELinux: avtab: truncated source type\n"); - return -1; + return -EINVAL; } val = le32_to_cpu(buf32[items++]); key.target_type = (u16)val; if (key.target_type != val) { printk(KERN_ERR "SELinux: avtab: truncated target type\n"); - return -1; + return -EINVAL; } val = le32_to_cpu(buf32[items++]); key.target_class = (u16)val; if (key.target_class != val) { printk(KERN_ERR "SELinux: avtab: truncated target class\n"); - return -1; + return -EINVAL; } val = le32_to_cpu(buf32[items++]); @@ -383,12 +383,12 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, if (!(val & (AVTAB_AV | AVTAB_TYPE))) { printk(KERN_ERR "SELinux: avtab: null entry\n"); - return -1; + return -EINVAL; } if ((val & AVTAB_AV) && (val & AVTAB_TYPE)) { printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); - return -1; + return -EINVAL; } for (i = 0; i < ARRAY_SIZE(spec_order); i++) { @@ -403,15 +403,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, if (items != items2) { printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items); - return -1; + return -EINVAL; } return 0; } rc = next_entry(buf16, fp, sizeof(u16)*4); - if (rc < 0) { + if (rc) { printk(KERN_ERR "SELinux: avtab: truncated entry\n"); - return -1; + return rc; } items = 0; @@ -424,7 +424,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, !policydb_type_isvalid(pol, key.target_type) || !policydb_class_isvalid(pol, key.target_class)) { printk(KERN_ERR "SELinux: avtab: invalid type or class\n"); - return -1; + return -EINVAL; } set = 0; @@ -434,19 +434,19 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, } if (!set || set > 1) { printk(KERN_ERR "SELinux: avtab: more than one specifier\n"); - return -1; + return -EINVAL; } rc = next_entry(buf32, fp, sizeof(u32)); - if (rc < 0) { + if (rc) { printk(KERN_ERR "SELinux: avtab: truncated entry\n"); - return -1; + return rc; } datum.data = le32_to_cpu(*buf32); if ((key.specified & AVTAB_TYPE) && !policydb_type_isvalid(pol, datum.data)) { printk(KERN_ERR "SELinux: avtab: invalid type\n"); - return -1; + return -EINVAL; } return insertf(a, &key, &datum, p); } @@ -487,8 +487,7 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol) printk(KERN_ERR "SELinux: avtab: out of memory\n"); else if (rc == -EEXIST) printk(KERN_ERR "SELinux: avtab: duplicate entry\n"); - else - rc = -EINVAL; + goto bad; } } diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 4a4e35cac22b..c91e150c3087 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -117,10 +117,14 @@ int evaluate_cond_node(struct policydb *p, struct cond_node *node) int cond_policydb_init(struct policydb *p) { + int rc; + p->bool_val_to_struct = NULL; p->cond_list = NULL; - if (avtab_init(&p->te_cond_avtab)) - return -1; + + rc = avtab_init(&p->te_cond_avtab); + if (rc) + return rc; return 0; } @@ -219,34 +223,37 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); if (!booldatum) - return -1; + return -ENOMEM; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto err; booldatum->value = le32_to_cpu(buf[0]); booldatum->state = le32_to_cpu(buf[1]); + rc = -EINVAL; if (!bool_isvalid(booldatum)) goto err; len = le32_to_cpu(buf[2]); + rc = -ENOMEM; key = kmalloc(len + 1, GFP_KERNEL); if (!key) goto err; rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto err; key[len] = '\0'; - if (hashtab_insert(h, key, booldatum)) + rc = hashtab_insert(h, key, booldatum); + if (rc) goto err; return 0; err: cond_destroy_bool(key, booldatum, NULL); - return -1; + return rc; } struct cond_insertf_data { @@ -263,7 +270,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum struct cond_av_list *other = data->other, *list, *cur; struct avtab_node *node_ptr; u8 found; - + int rc = -EINVAL; /* * For type rules we have to make certain there aren't any @@ -313,12 +320,15 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); if (!node_ptr) { printk(KERN_ERR "SELinux: could not insert rule.\n"); + rc = -ENOMEM; goto err; } list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); - if (!list) + if (!list) { + rc = -ENOMEM; goto err; + } list->node = node_ptr; if (!data->head) @@ -331,7 +341,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum err: cond_av_list_destroy(data->head); data->head = NULL; - return -1; + return rc; } static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other) @@ -345,8 +355,8 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * len = 0; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; len = le32_to_cpu(buf[0]); if (len == 0) @@ -361,7 +371,6 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * &data); if (rc) return rc; - } *ret_list = data.head; @@ -390,24 +399,25 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) struct cond_expr *expr = NULL, *last = NULL; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; node->cur_state = le32_to_cpu(buf[0]); len = 0; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; /* expr */ len = le32_to_cpu(buf[0]); for (i = 0; i < len; i++) { rc = next_entry(buf, fp, sizeof(u32) * 2); - if (rc < 0) + if (rc) goto err; + rc = -ENOMEM; expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); if (!expr) goto err; @@ -416,6 +426,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) expr->bool = le32_to_cpu(buf[1]); if (!expr_isvalid(p, expr)) { + rc = -EINVAL; kfree(expr); goto err; } @@ -427,14 +438,16 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) last = expr; } - if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) + rc = cond_read_av_list(p, fp, &node->true_list, NULL); + if (rc) goto err; - if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0) + rc = cond_read_av_list(p, fp, &node->false_list, node->true_list); + if (rc) goto err; return 0; err: cond_node_destroy(node); - return -1; + return rc; } int cond_read_list(struct policydb *p, void *fp) @@ -445,8 +458,8 @@ int cond_read_list(struct policydb *p, void *fp) int rc; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) - return -1; + if (rc) + return rc; len = le32_to_cpu(buf[0]); @@ -455,11 +468,13 @@ int cond_read_list(struct policydb *p, void *fp) goto err; for (i = 0; i < len; i++) { + rc = -ENOMEM; node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); if (!node) goto err; - if (cond_read_node(p, node, fp) != 0) + rc = cond_read_node(p, node, fp); + if (rc) goto err; if (i == 0) @@ -472,7 +487,7 @@ int cond_read_list(struct policydb *p, void *fp) err: cond_list_destroy(p->cond_list); p->cond_list = NULL; - return -1; + return rc; } /* Determine whether additional permissions are granted by the conditional diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index c57802a164d5..3a29704be8ce 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -31,6 +31,7 @@ #include <linux/string.h> #include <linux/errno.h> #include <linux/audit.h> +#include <linux/flex_array.h> #include "security.h" #include "policydb.h" @@ -655,6 +656,9 @@ static int range_tr_destroy(void *key, void *datum, void *p) static void ocontext_destroy(struct ocontext *c, int i) { + if (!c) + return; + context_destroy(&c->context[0]); context_destroy(&c->context[1]); if (i == OCON_ISID || i == OCON_FS || @@ -736,11 +740,17 @@ void policydb_destroy(struct policydb *p) hashtab_map(p->range_tr, range_tr_destroy, NULL); hashtab_destroy(p->range_tr); - if (p->type_attr_map) { - for (i = 0; i < p->p_types.nprim; i++) - ebitmap_destroy(&p->type_attr_map[i]); + if (p->type_attr_map_array) { + for (i = 0; i < p->p_types.nprim; i++) { + struct ebitmap *e; + + e = flex_array_get(p->type_attr_map_array, i); + if (!e) + continue; + ebitmap_destroy(e); + } + flex_array_free(p->type_attr_map_array); } - kfree(p->type_attr_map); ebitmap_destroy(&p->policycaps); ebitmap_destroy(&p->permissive_map); @@ -1701,6 +1711,333 @@ u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name) return 1U << (perdatum->value-1); } +static int range_read(struct policydb *p, void *fp) +{ + struct range_trans *rt = NULL; + struct mls_range *r = NULL; + int i, rc; + __le32 buf[2]; + u32 nel; + + if (p->policyvers < POLICYDB_VERSION_MLS) + return 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + nel = le32_to_cpu(buf[0]); + for (i = 0; i < nel; i++) { + rc = -ENOMEM; + rt = kzalloc(sizeof(*rt), GFP_KERNEL); + if (!rt) + goto out; + + rc = next_entry(buf, fp, (sizeof(u32) * 2)); + if (rc) + goto out; + + rt->source_type = le32_to_cpu(buf[0]); + rt->target_type = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_RANGETRANS) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + rt->target_class = le32_to_cpu(buf[0]); + } else + rt->target_class = p->process_class; + + rc = -EINVAL; + if (!policydb_type_isvalid(p, rt->source_type) || + !policydb_type_isvalid(p, rt->target_type) || + !policydb_class_isvalid(p, rt->target_class)) + goto out; + + rc = -ENOMEM; + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) + goto out; + + rc = mls_read_range_helper(r, fp); + if (rc) + goto out; + + rc = -EINVAL; + if (!mls_range_isvalid(p, r)) { + printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); + goto out; + } + + rc = hashtab_insert(p->range_tr, rt, r); + if (rc) + goto out; + + rt = NULL; + r = NULL; + } + rangetr_hash_eval(p->range_tr); + rc = 0; +out: + kfree(rt); + kfree(r); + return rc; +} + +static int genfs_read(struct policydb *p, void *fp) +{ + int i, j, rc; + u32 nel, nel2, len, len2; + __le32 buf[1]; + struct ocontext *l, *c; + struct ocontext *newc = NULL; + struct genfs *genfs_p, *genfs; + struct genfs *newgenfs = NULL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + nel = le32_to_cpu(buf[0]); + + for (i = 0; i < nel; i++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); + if (!newgenfs) + goto out; + + rc = -ENOMEM; + newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL); + if (!newgenfs->fstype) + goto out; + + rc = next_entry(newgenfs->fstype, fp, len); + if (rc) + goto out; + + newgenfs->fstype[len] = 0; + + for (genfs_p = NULL, genfs = p->genfs; genfs; + genfs_p = genfs, genfs = genfs->next) { + rc = -EINVAL; + if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { + printk(KERN_ERR "SELinux: dup genfs fstype %s\n", + newgenfs->fstype); + goto out; + } + if (strcmp(newgenfs->fstype, genfs->fstype) < 0) + break; + } + newgenfs->next = genfs; + if (genfs_p) + genfs_p->next = newgenfs; + else + p->genfs = newgenfs; + genfs = newgenfs; + newgenfs = NULL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + nel2 = le32_to_cpu(buf[0]); + for (j = 0; j < nel2; j++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + newc = kzalloc(sizeof(*newc), GFP_KERNEL); + if (!newc) + goto out; + + rc = -ENOMEM; + newc->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!newc->u.name) + goto out; + + rc = next_entry(newc->u.name, fp, len); + if (rc) + goto out; + newc->u.name[len] = 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + newc->v.sclass = le32_to_cpu(buf[0]); + rc = context_read_and_validate(&newc->context[0], p, fp); + if (rc) + goto out; + + for (l = NULL, c = genfs->head; c; + l = c, c = c->next) { + rc = -EINVAL; + if (!strcmp(newc->u.name, c->u.name) && + (!c->v.sclass || !newc->v.sclass || + newc->v.sclass == c->v.sclass)) { + printk(KERN_ERR "SELinux: dup genfs entry (%s,%s)\n", + genfs->fstype, c->u.name); + goto out; + } + len = strlen(newc->u.name); + len2 = strlen(c->u.name); + if (len > len2) + break; + } + + newc->next = c; + if (l) + l->next = newc; + else + genfs->head = newc; + newc = NULL; + } + } + rc = 0; +out: + if (newgenfs) + kfree(newgenfs->fstype); + kfree(newgenfs); + ocontext_destroy(newc, OCON_FSUSE); + + return rc; +} + +static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, + void *fp) +{ + int i, j, rc; + u32 nel, len; + __le32 buf[3]; + struct ocontext *l, *c; + u32 nodebuf[8]; + + for (i = 0; i < info->ocon_num; i++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + nel = le32_to_cpu(buf[0]); + + l = NULL; + for (j = 0; j < nel; j++) { + rc = -ENOMEM; + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + goto out; + if (l) + l->next = c; + else + p->ocontexts[i] = c; + l = c; + + switch (i) { + case OCON_ISID: + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + c->sid[0] = le32_to_cpu(buf[0]); + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_FS: + case OCON_NETIF: + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + c->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!c->u.name) + goto out; + + rc = next_entry(c->u.name, fp, len); + if (rc) + goto out; + + c->u.name[len] = 0; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + rc = context_read_and_validate(&c->context[1], p, fp); + if (rc) + goto out; + break; + case OCON_PORT: + rc = next_entry(buf, fp, sizeof(u32)*3); + if (rc) + goto out; + c->u.port.protocol = le32_to_cpu(buf[0]); + c->u.port.low_port = le32_to_cpu(buf[1]); + c->u.port.high_port = le32_to_cpu(buf[2]); + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_NODE: + rc = next_entry(nodebuf, fp, sizeof(u32) * 2); + if (rc) + goto out; + c->u.node.addr = nodebuf[0]; /* network order */ + c->u.node.mask = nodebuf[1]; /* network order */ + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_FSUSE: + rc = next_entry(buf, fp, sizeof(u32)*2); + if (rc) + goto out; + + rc = -EINVAL; + c->v.behavior = le32_to_cpu(buf[0]); + if (c->v.behavior > SECURITY_FS_USE_NONE) + goto out; + + rc = -ENOMEM; + len = le32_to_cpu(buf[1]); + c->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!c->u.name) + goto out; + + rc = next_entry(c->u.name, fp, len); + if (rc) + goto out; + c->u.name[len] = 0; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_NODE6: { + int k; + + rc = next_entry(nodebuf, fp, sizeof(u32) * 8); + if (rc) + goto out; + for (k = 0; k < 4; k++) + c->u.node6.addr[k] = nodebuf[k]; + for (k = 0; k < 4; k++) + c->u.node6.mask[k] = nodebuf[k+4]; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + } + } + } + } + rc = 0; +out: + return rc; +} + /* * Read the configuration data from a policy database binary * representation file into a policy database structure. @@ -1709,16 +2046,12 @@ int policydb_read(struct policydb *p, void *fp) { struct role_allow *ra, *lra; struct role_trans *tr, *ltr; - struct ocontext *l, *c, *newc; - struct genfs *genfs_p, *genfs, *newgenfs; int i, j, rc; __le32 buf[4]; - u32 nodebuf[8]; - u32 len, len2, nprim, nel, nel2; + u32 len, nprim, nel; + char *policydb_str; struct policydb_compat_info *info; - struct range_trans *rt; - struct mls_range *r; rc = policydb_init(p); if (rc) @@ -1919,294 +2252,45 @@ int policydb_read(struct policydb *p, void *fp) if (!p->process_trans_perms) goto bad; - for (i = 0; i < info->ocon_num; i++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel = le32_to_cpu(buf[0]); - l = NULL; - for (j = 0; j < nel; j++) { - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) { - rc = -ENOMEM; - goto bad; - } - if (l) - l->next = c; - else - p->ocontexts[i] = c; - l = c; - rc = -EINVAL; - switch (i) { - case OCON_ISID: - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - c->sid[0] = le32_to_cpu(buf[0]); - rc = context_read_and_validate(&c->context[0], p, fp); - if (rc) - goto bad; - break; - case OCON_FS: - case OCON_NETIF: - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); - c->u.name = kmalloc(len + 1, GFP_KERNEL); - if (!c->u.name) { - rc = -ENOMEM; - goto bad; - } - rc = next_entry(c->u.name, fp, len); - if (rc < 0) - goto bad; - c->u.name[len] = 0; - rc = context_read_and_validate(&c->context[0], p, fp); - if (rc) - goto bad; - rc = context_read_and_validate(&c->context[1], p, fp); - if (rc) - goto bad; - break; - case OCON_PORT: - rc = next_entry(buf, fp, sizeof(u32)*3); - if (rc < 0) - goto bad; - c->u.port.protocol = le32_to_cpu(buf[0]); - c->u.port.low_port = le32_to_cpu(buf[1]); - c->u.port.high_port = le32_to_cpu(buf[2]); - rc = context_read_and_validate(&c->context[0], p, fp); - if (rc) - goto bad; - break; - case OCON_NODE: - rc = next_entry(nodebuf, fp, sizeof(u32) * 2); - if (rc < 0) - goto bad; - c->u.node.addr = nodebuf[0]; /* network order */ - c->u.node.mask = nodebuf[1]; /* network order */ - rc = context_read_and_validate(&c->context[0], p, fp); - if (rc) - goto bad; - break; - case OCON_FSUSE: - rc = next_entry(buf, fp, sizeof(u32)*2); - if (rc < 0) - goto bad; - c->v.behavior = le32_to_cpu(buf[0]); - if (c->v.behavior > SECURITY_FS_USE_NONE) - goto bad; - len = le32_to_cpu(buf[1]); - c->u.name = kmalloc(len + 1, GFP_KERNEL); - if (!c->u.name) { - rc = -ENOMEM; - goto bad; - } - rc = next_entry(c->u.name, fp, len); - if (rc < 0) - goto bad; - c->u.name[len] = 0; - rc = context_read_and_validate(&c->context[0], p, fp); - if (rc) - goto bad; - break; - case OCON_NODE6: { - int k; - - rc = next_entry(nodebuf, fp, sizeof(u32) * 8); - if (rc < 0) - goto bad; - for (k = 0; k < 4; k++) - c->u.node6.addr[k] = nodebuf[k]; - for (k = 0; k < 4; k++) - c->u.node6.mask[k] = nodebuf[k+4]; - if (context_read_and_validate(&c->context[0], p, fp)) - goto bad; - break; - } - } - } - } - - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + rc = ocontext_read(p, info, fp); + if (rc) goto bad; - nel = le32_to_cpu(buf[0]); - genfs_p = NULL; - rc = -EINVAL; - for (i = 0; i < nel; i++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); - newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); - if (!newgenfs) { - rc = -ENOMEM; - goto bad; - } - newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL); - if (!newgenfs->fstype) { - rc = -ENOMEM; - kfree(newgenfs); - goto bad; - } - rc = next_entry(newgenfs->fstype, fp, len); - if (rc < 0) { - kfree(newgenfs->fstype); - kfree(newgenfs); - goto bad; - } - newgenfs->fstype[len] = 0; - for (genfs_p = NULL, genfs = p->genfs; genfs; - genfs_p = genfs, genfs = genfs->next) { - if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { - printk(KERN_ERR "SELinux: dup genfs " - "fstype %s\n", newgenfs->fstype); - kfree(newgenfs->fstype); - kfree(newgenfs); - goto bad; - } - if (strcmp(newgenfs->fstype, genfs->fstype) < 0) - break; - } - newgenfs->next = genfs; - if (genfs_p) - genfs_p->next = newgenfs; - else - p->genfs = newgenfs; - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel2 = le32_to_cpu(buf[0]); - for (j = 0; j < nel2; j++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); - - newc = kzalloc(sizeof(*newc), GFP_KERNEL); - if (!newc) { - rc = -ENOMEM; - goto bad; - } - - newc->u.name = kmalloc(len + 1, GFP_KERNEL); - if (!newc->u.name) { - rc = -ENOMEM; - goto bad_newc; - } - rc = next_entry(newc->u.name, fp, len); - if (rc < 0) - goto bad_newc; - newc->u.name[len] = 0; - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad_newc; - newc->v.sclass = le32_to_cpu(buf[0]); - if (context_read_and_validate(&newc->context[0], p, fp)) - goto bad_newc; - for (l = NULL, c = newgenfs->head; c; - l = c, c = c->next) { - if (!strcmp(newc->u.name, c->u.name) && - (!c->v.sclass || !newc->v.sclass || - newc->v.sclass == c->v.sclass)) { - printk(KERN_ERR "SELinux: dup genfs " - "entry (%s,%s)\n", - newgenfs->fstype, c->u.name); - goto bad_newc; - } - len = strlen(newc->u.name); - len2 = strlen(c->u.name); - if (len > len2) - break; - } + rc = genfs_read(p, fp); + if (rc) + goto bad; - newc->next = c; - if (l) - l->next = newc; - else - newgenfs->head = newc; - } - } + rc = range_read(p, fp); + if (rc) + goto bad; - if (p->policyvers >= POLICYDB_VERSION_MLS) { - int new_rangetr = p->policyvers >= POLICYDB_VERSION_RANGETRANS; - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel = le32_to_cpu(buf[0]); - for (i = 0; i < nel; i++) { - rt = kzalloc(sizeof(*rt), GFP_KERNEL); - if (!rt) { - rc = -ENOMEM; - goto bad; - } - rc = next_entry(buf, fp, (sizeof(u32) * 2)); - if (rc < 0) { - kfree(rt); - goto bad; - } - rt->source_type = le32_to_cpu(buf[0]); - rt->target_type = le32_to_cpu(buf[1]); - if (new_rangetr) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) { - kfree(rt); - goto bad; - } - rt->target_class = le32_to_cpu(buf[0]); - } else - rt->target_class = p->process_class; - if (!policydb_type_isvalid(p, rt->source_type) || - !policydb_type_isvalid(p, rt->target_type) || - !policydb_class_isvalid(p, rt->target_class)) { - kfree(rt); - rc = -EINVAL; - goto bad; - } - r = kzalloc(sizeof(*r), GFP_KERNEL); - if (!r) { - kfree(rt); - rc = -ENOMEM; - goto bad; - } - rc = mls_read_range_helper(r, fp); - if (rc) { - kfree(rt); - kfree(r); - goto bad; - } - if (!mls_range_isvalid(p, r)) { - printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); - kfree(rt); - kfree(r); - goto bad; - } - rc = hashtab_insert(p->range_tr, rt, r); - if (rc) { - kfree(rt); - kfree(r); - goto bad; - } - } - rangetr_hash_eval(p->range_tr); - } + rc = -ENOMEM; + p->type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap), + p->p_types.nprim, + GFP_KERNEL | __GFP_ZERO); + if (!p->type_attr_map_array) + goto bad; - p->type_attr_map = kmalloc(p->p_types.nprim * sizeof(struct ebitmap), GFP_KERNEL); - if (!p->type_attr_map) + /* preallocate so we don't have to worry about the put ever failing */ + rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim - 1, + GFP_KERNEL | __GFP_ZERO); + if (rc) goto bad; for (i = 0; i < p->p_types.nprim; i++) { - ebitmap_init(&p->type_attr_map[i]); + struct ebitmap *e = flex_array_get(p->type_attr_map_array, i); + + BUG_ON(!e); + ebitmap_init(e); if (p->policyvers >= POLICYDB_VERSION_AVTAB) { - if (ebitmap_read(&p->type_attr_map[i], fp)) + rc = ebitmap_read(e, fp); + if (rc) goto bad; } /* add the type itself as the degenerate case */ - if (ebitmap_set_bit(&p->type_attr_map[i], i, 1)) - goto bad; + rc = ebitmap_set_bit(e, i, 1); + if (rc) + goto bad; } rc = policydb_bounds_sanity_check(p); @@ -2216,8 +2300,6 @@ int policydb_read(struct policydb *p, void *fp) rc = 0; out: return rc; -bad_newc: - ocontext_destroy(newc, OCON_FSUSE); bad: if (!rc) rc = -EINVAL; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 26d9adf8542b..310e94442cb8 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -24,6 +24,8 @@ #ifndef _SS_POLICYDB_H_ #define _SS_POLICYDB_H_ +#include <linux/flex_array.h> + #include "symtab.h" #include "avtab.h" #include "sidtab.h" @@ -246,7 +248,7 @@ struct policydb { struct hashtab *range_tr; /* type -> attribute reverse mapping */ - struct ebitmap *type_attr_map; + struct flex_array *type_attr_map_array; struct ebitmap policycaps; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 1de60ce90d9a..9ea2feca3cd4 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -50,6 +50,7 @@ #include <linux/audit.h> #include <linux/mutex.h> #include <linux/selinux.h> +#include <linux/flex_array.h> #include <net/netlabel.h> #include "flask.h" @@ -626,8 +627,10 @@ static void context_struct_compute_av(struct context *scontext, */ avkey.target_class = tclass; avkey.specified = AVTAB_AV; - sattr = &policydb.type_attr_map[scontext->type - 1]; - tattr = &policydb.type_attr_map[tcontext->type - 1]; + sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); + BUG_ON(!sattr); + tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); + BUG_ON(!tattr); ebitmap_for_each_positive_bit(sattr, snode, i) { ebitmap_for_each_positive_bit(tattr, tnode, j) { avkey.source_type = i + 1; diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index bcf9f620426e..160326ee99e5 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c @@ -36,7 +36,7 @@ int symtab_init(struct symtab *s, unsigned int size) { s->table = hashtab_create(symhash, symcmp, size); if (!s->table) - return -1; + return -ENOMEM; s->nprim = 0; return 0; } |