X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=security%2Fselinux%2Fhooks.c;h=52fa3cfdfd5b86e4328140c2d82698b3971c0dab;hb=9bf4aaab3e101692164d49b7ca357651eb691cb6;hp=c447232c6ae4c7bfe4c9d101b242c54d3128d5d5;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c447232c6..52fa3cfdf 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -63,6 +63,7 @@ #include #include #include +#include #include "avc.h" #include "objsec.h" @@ -71,6 +72,9 @@ #define XATTR_SELINUX_SUFFIX "selinux" #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX +extern int policydb_loaded_version; +extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); + #ifdef CONFIG_SECURITY_SELINUX_DEVELOP int selinux_enforcing = 0; @@ -627,7 +631,7 @@ static inline u16 inode_mode_to_security_class(umode_t mode) return SECCLASS_FILE; } -static inline u16 socket_type_to_security_class(int family, int type) +static inline u16 socket_type_to_security_class(int family, int type, int protocol) { switch (family) { case PF_UNIX: @@ -648,7 +652,28 @@ static inline u16 socket_type_to_security_class(int family, int type) return SECCLASS_RAWIP_SOCKET; } case PF_NETLINK: - return SECCLASS_NETLINK_SOCKET; + switch (protocol) { + case NETLINK_ROUTE: + return SECCLASS_NETLINK_ROUTE_SOCKET; + case NETLINK_FIREWALL: + return SECCLASS_NETLINK_FIREWALL_SOCKET; + case NETLINK_TCPDIAG: + return SECCLASS_NETLINK_TCPDIAG_SOCKET; + case NETLINK_NFLOG: + return SECCLASS_NETLINK_NFLOG_SOCKET; + case NETLINK_XFRM: + return SECCLASS_NETLINK_XFRM_SOCKET; + case NETLINK_SELINUX: + return SECCLASS_NETLINK_SELINUX_SOCKET; + case NETLINK_AUDIT: + return SECCLASS_NETLINK_AUDIT_SOCKET; + case NETLINK_IP6_FW: + return SECCLASS_NETLINK_IP6FW_SOCKET; + case NETLINK_DNRTMSG: + return SECCLASS_NETLINK_DNRT_SOCKET; + default: + return SECCLASS_NETLINK_SOCKET; + } case PF_PACKET: return SECCLASS_PACKET_SOCKET; case PF_KEY: @@ -853,7 +878,8 @@ out: struct socket *sock = SOCKET_I(inode); if (sock->sk) { isec->sclass = socket_type_to_security_class(sock->sk->sk_family, - sock->sk->sk_type); + sock->sk->sk_type, + sock->sk->sk_protocol); } else { isec->sclass = SECCLASS_SOCKET; } @@ -1364,11 +1390,11 @@ static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effect { int error; - error = task_has_perm(current, target, PROCESS__SETCAP); + error = secondary_ops->capset_check(target, effective, inheritable, permitted); if (error) return error; - return secondary_ops->capset_check(target, effective, inheritable, permitted); + return task_has_perm(current, target, PROCESS__SETCAP); } static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective, @@ -1402,6 +1428,10 @@ static int selinux_sysctl(ctl_table *table, int op) u32 tsid; int rc; + rc = secondary_ops->sysctl(table, op); + if (rc) + return rc; + tsec = current->security; rc = selinux_proc_get_sid(table->de, (op == 001) ? @@ -1567,22 +1597,6 @@ static int selinux_vm_enough_memory(long pages) return -ENOMEM; } -static int selinux_netlink_send(struct sk_buff *skb) -{ - if (capable(CAP_NET_ADMIN)) - cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN); - else - NETLINK_CB(skb).eff_cap = 0; - return 0; -} - -static int selinux_netlink_recv(struct sk_buff *skb) -{ - if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) - return -EPERM; - return 0; -} - /* binprm security operations */ static int selinux_bprm_alloc_security(struct linux_binprm *bprm) @@ -1671,6 +1685,9 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm) if (rc) return rc; + /* Clear any possibly unsafe personality bits on exec: */ + current->personality &= ~PER_CLEAR_ON_SETID; + /* Set the security field to the new SID. */ bsec->sid = newsid; } @@ -1681,7 +1698,7 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm) static int selinux_bprm_check_security (struct linux_binprm *bprm) { - return 0; + return secondary_ops->bprm_check_security(bprm); } @@ -1699,12 +1716,7 @@ static int selinux_bprm_secureexec (struct linux_binprm *bprm) PROCESS__NOATSECURE, NULL, NULL); } - /* Note that we must include the legacy uid/gid test below - to retain it, as the new userland will simply use the - value passed by AT_SECURE to decide whether to enable - secure mode. */ - return ( atsecure || current->euid != current->uid || - current->egid != current->gid); + return (atsecure || secondary_ops->bprm_secureexec(bprm)); } static void selinux_bprm_free_security(struct linux_binprm *bprm) @@ -1784,7 +1796,8 @@ static inline void flush_unauthorized_files(struct files_struct * files) spin_lock(&files->file_lock); for (;;) { - unsigned long set, i, fd; + unsigned long set, i; + int fd; j++; i = j * __NFDBITS; @@ -2049,6 +2062,12 @@ static int selinux_mount(char * dev_name, unsigned long flags, void * data) { + int rc; + + rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data); + if (rc) + return rc; + if (flags & MS_REMOUNT) return superblock_has_perm(current, nd->mnt->mnt_sb, FILESYSTEM__REMOUNT, NULL); @@ -2059,6 +2078,12 @@ static int selinux_mount(char * dev_name, static int selinux_umount(struct vfsmount *mnt, int flags) { + int rc; + + rc = secondary_ops->sb_umount(mnt, flags); + if (rc) + return rc; + return superblock_has_perm(current,mnt->mnt_sb, FILESYSTEM__UNMOUNT,NULL); } @@ -2102,6 +2127,11 @@ static void selinux_inode_post_link(struct dentry *old_dentry, struct inode *ino static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry) { + int rc; + + rc = secondary_ops->inode_unlink(dir, dentry); + if (rc) + return rc; return may_link(dir, dentry, MAY_UNLINK); } @@ -2132,6 +2162,12 @@ static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry) static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { + int rc; + + rc = secondary_ops->inode_mknod(dir, dentry, mode, dev); + if (rc) + return rc; + return may_create(dir, dentry, inode_mode_to_security_class(mode)); } @@ -2170,6 +2206,12 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na static int selinux_inode_permission(struct inode *inode, int mask, struct nameidata *nd) { + int rc; + + rc = secondary_ops->inode_permission(inode, mask, nd); + if (rc) + return rc; + if (!mask) { /* No permission to check. Existence test. */ return 0; @@ -2181,6 +2223,12 @@ static int selinux_inode_permission(struct inode *inode, int mask, static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) { + int rc; + + rc = secondary_ops->inode_setattr(dentry, iattr); + if (rc) + return rc; + if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_ATIME_SET | ATTR_MTIME_SET)) return dentry_has_perm(current, NULL, dentry, FILE__SETATTR); @@ -2447,6 +2495,11 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags) { u32 av; + int rc; + + rc = secondary_ops->file_mmap(file, prot, flags); + if (rc) + return rc; if (file) { /* read access is always possible with a mapping */ @@ -2467,6 +2520,12 @@ static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned lon static int selinux_file_mprotect(struct vm_area_struct *vma, unsigned long prot) { + int rc; + + rc = secondary_ops->file_mprotect(vma, prot); + if (rc) + return rc; + return selinux_file_mmap(vma->vm_file, prot, vma->vm_flags); } @@ -2564,6 +2623,12 @@ static int selinux_file_receive(struct file *file) static int selinux_task_create(unsigned long clone_flags) { + int rc; + + rc = secondary_ops->task_create(clone_flags); + if (rc) + return rc; + return task_has_perm(current, current, PROCESS__FORK); } @@ -2639,12 +2704,23 @@ static int selinux_task_setgroups(struct group_info *group_info) static int selinux_task_setnice(struct task_struct *p, int nice) { + int rc; + + rc = secondary_ops->task_setnice(p, nice); + if (rc) + return rc; + return task_has_perm(current,p, PROCESS__SETSCHED); } static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) { struct rlimit *old_rlim = current->rlim + resource; + int rc; + + rc = secondary_ops->task_setrlimit(resource, new_rlim); + if (rc) + return rc; /* Control the ability to change the hard limit (whether lowering or raising it), so that the hard limit can @@ -2679,6 +2755,11 @@ static int selinux_task_getscheduler(struct task_struct *p) static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig) { u32 perm; + int rc; + + rc = secondary_ops->task_kill(p, info, sig); + if (rc) + return rc; if (info && ((unsigned long)info == 1 || (unsigned long)info == 2 || SI_FROMKERNEL(info))) @@ -2918,8 +2999,8 @@ static int selinux_socket_create(int family, int type, tsec = current->security; err = avc_has_perm(tsec->sid, tsec->sid, - socket_type_to_security_class(family, type), - SOCKET__CREATE, NULL, NULL); + socket_type_to_security_class(family, type, + protocol), SOCKET__CREATE, NULL, NULL); out: return err; @@ -2938,7 +3019,7 @@ static void selinux_socket_post_create(struct socket *sock, int family, isec = SOCK_INODE(sock)->i_security; tsec = current->security; - isec->sclass = socket_type_to_security_class(family, type); + isec->sclass = socket_type_to_security_class(family, type, protocol); isec->sid = kern ? SECINITSID_KERNEL : tsec->sid; return; @@ -3120,6 +3201,10 @@ static int selinux_socket_unix_stream_connect(struct socket *sock, struct avc_audit_data ad; int err; + err = secondary_ops->unix_stream_connect(sock, other, newsk); + if (err) + return err; + isec = SOCK_INODE(sock)->i_security; other_isec = SOCK_INODE(other)->i_security; @@ -3174,12 +3259,12 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) char *addrp; int len, err = 0; u32 netif_perm, node_perm, node_sid, recv_perm = 0; + u32 sock_sid = 0; + u16 sock_class = 0; struct socket *sock; - struct inode *inode; struct net_device *dev; struct sel_netif *netif; struct netif_security_struct *nsec; - struct inode_security_struct *isec; struct avc_audit_data ad; family = sk->sk_family; @@ -3190,15 +3275,21 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP)) family = PF_INET; - sock = sk->sk_socket; - - /* TCP control messages don't always have a socket. */ - if (!sock) - goto out; - - inode = SOCK_INODE(sock); - if (!inode) - goto out; + read_lock_bh(&sk->sk_callback_lock); + sock = sk->sk_socket; + if (sock) { + struct inode *inode; + inode = SOCK_INODE(sock); + if (inode) { + struct inode_security_struct *isec; + isec = inode->i_security; + sock_sid = isec->sid; + sock_class = isec->sclass; + } + } + read_unlock_bh(&sk->sk_callback_lock); + if (!sock_sid) + goto out; dev = skb->dev; if (!dev) @@ -3211,9 +3302,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) } nsec = &netif->nsec; - isec = inode->i_security; - switch (isec->sclass) { + switch (sock_class) { case SECCLASS_UDP_SOCKET: netif_perm = NETIF__UDP_RECV; node_perm = NODE__UDP_RECV; @@ -3242,7 +3332,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) goto out; } - err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF, + err = avc_has_perm(sock_sid, nsec->if_sid, SECCLASS_NETIF, netif_perm, &nsec->avcr, &ad); sel_netif_put(netif); if (err) @@ -3253,7 +3343,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (err) goto out; - err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad); + err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad); if (err) goto out; @@ -3267,7 +3357,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (err) goto out; - err = avc_has_perm(isec->sid, port_sid, isec->sclass, + err = avc_has_perm(sock_sid, port_sid, sock_class, recv_perm, NULL, &ad); } out: @@ -3322,6 +3412,33 @@ static void selinux_sk_free_security(struct sock *sk) sk_free_security(sk); } +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; + + if (skb->len < NLMSG_SPACE(0)) { + err = -EINVAL; + goto out; + } + nlh = (struct nlmsghdr *)skb->data; + + err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); + if (err) { + /* Ignore */ + if (err == -ENOENT) + err = 0; + goto out; + } + + err = socket_has_perm(current, sock, perm); +out: + return err; +} + #ifdef CONFIG_NETFILTER static unsigned int selinux_ip_postroute_last(unsigned int hooknum, @@ -3457,8 +3574,37 @@ static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum, #endif /* CONFIG_NETFILTER */ +#else + +static inline int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) +{ + return 0; +} + #endif /* CONFIG_SECURITY_NETWORK */ +static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) +{ + int err = 0; + + if (capable(CAP_NET_ADMIN)) + cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN); + else + NETLINK_CB(skb).eff_cap = 0; + + if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS) + err = selinux_nlmsg_perm(sk, skb); + + return err; +} + +static int selinux_netlink_recv(struct sk_buff *skb) +{ + if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) + return -EPERM; + return 0; +} + static int ipc_alloc_security(struct task_struct *task, struct kern_ipc_perm *perm, u16 sclass) @@ -3784,6 +3930,11 @@ static int selinux_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmflg) { u32 perms; + int rc; + + rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg); + if (rc) + return rc; if (shmflg & SHM_RDONLY) perms = SHM__READ;