Pull bugfix into test branch
[powerpc.git] / security / selinux / hooks.c
index 956137b..65fb5e8 100644 (file)
@@ -58,6 +58,7 @@
 #include <linux/netlink.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
+#include <linux/dccp.h>
 #include <linux/quota.h>
 #include <linux/un.h>          /* for Unix socket types */
 #include <net/af_unix.h>       /* for Unix socket types */
@@ -123,7 +124,7 @@ static struct security_operations *secondary_ops = NULL;
 static LIST_HEAD(superblock_security_head);
 static DEFINE_SPINLOCK(sb_security_lock);
 
-static kmem_cache_t *sel_inode_cache;
+static struct kmem_cache *sel_inode_cache;
 
 /* Return security context for a given sid or just the context 
    length if the buffer is null or length is 0 */
@@ -180,7 +181,7 @@ static int inode_alloc_security(struct inode *inode)
        struct task_security_struct *tsec = current->security;
        struct inode_security_struct *isec;
 
-       isec = kmem_cache_alloc(sel_inode_cache, SLAB_KERNEL);
+       isec = kmem_cache_alloc(sel_inode_cache, GFP_KERNEL);
        if (!isec)
                return -ENOMEM;
 
@@ -751,6 +752,8 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
                                return SECCLASS_UDP_SOCKET;
                        else
                                return SECCLASS_RAWIP_SOCKET;
+               case SOCK_DCCP:
+                       return SECCLASS_DCCP_SOCKET;
                default:
                        return SECCLASS_RAWIP_SOCKET;
                }
@@ -1117,8 +1120,8 @@ static int file_has_perm(struct task_struct *tsk,
 {
        struct task_security_struct *tsec = tsk->security;
        struct file_security_struct *fsec = file->f_security;
-       struct vfsmount *mnt = file->f_vfsmnt;
-       struct dentry *dentry = file->f_dentry;
+       struct vfsmount *mnt = file->f_path.mnt;
+       struct dentry *dentry = file->f_path.dentry;
        struct inode *inode = dentry->d_inode;
        struct avc_audit_data ad;
        int rc;
@@ -1578,7 +1581,7 @@ static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
 static int selinux_bprm_set_security(struct linux_binprm *bprm)
 {
        struct task_security_struct *tsec;
-       struct inode *inode = bprm->file->f_dentry->d_inode;
+       struct inode *inode = bprm->file->f_path.dentry->d_inode;
        struct inode_security_struct *isec;
        struct bprm_security_struct *bsec;
        u32 newsid;
@@ -1618,10 +1621,10 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
        }
 
        AVC_AUDIT_DATA_INIT(&ad, FS);
-       ad.u.fs.mnt = bprm->file->f_vfsmnt;
-       ad.u.fs.dentry = bprm->file->f_dentry;
+       ad.u.fs.mnt = bprm->file->f_path.mnt;
+       ad.u.fs.dentry = bprm->file->f_path.dentry;
 
-       if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)
+       if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
                newsid = tsec->sid;
 
         if (tsec->sid == newsid) {
@@ -1692,9 +1695,10 @@ static inline void flush_unauthorized_files(struct files_struct * files)
        struct tty_struct *tty;
        struct fdtable *fdt;
        long j = -1;
+       int drop_tty = 0;
 
        mutex_lock(&tty_mutex);
-       tty = current->signal->tty;
+       tty = get_current_tty();
        if (tty) {
                file_list_lock();
                file = list_entry(tty->tty_files.next, typeof(*file), f_u.fu_list);
@@ -1704,15 +1708,17 @@ static inline void flush_unauthorized_files(struct files_struct * files)
                           than using file_has_perm, as this particular open
                           file may belong to another process and we are only
                           interested in the inode-based check here. */
-                       struct inode *inode = file->f_dentry->d_inode;
+                       struct inode *inode = file->f_path.dentry->d_inode;
                        if (inode_has_perm(current, inode,
                                           FILE__READ | FILE__WRITE, NULL)) {
-                               /* Reset controlling tty. */
-                               current->signal->tty = NULL;
-                               current->signal->tty_old_pgrp = 0;
+                               drop_tty = 1;
                        }
                }
                file_list_unlock();
+
+               /* Reset controlling tty. */
+               if (drop_tty)
+                       proc_set_tty(current, NULL);
        }
        mutex_unlock(&tty_mutex);
 
@@ -1728,7 +1734,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
                j++;
                i = j * __NFDBITS;
                fdt = files_fdtable(files);
-               if (i >= fdt->max_fds || i >= fdt->max_fdset)
+               if (i >= fdt->max_fds)
                        break;
                set = fdt->open_fds->fds_bits[j];
                if (!set)
@@ -2414,7 +2420,7 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
 static int selinux_file_permission(struct file *file, int mask)
 {
        int rc;
-       struct inode *inode = file->f_dentry->d_inode;
+       struct inode *inode = file->f_path.dentry->d_inode;
 
        if (!mask) {
                /* No permission to check.  Existence test. */
@@ -2591,7 +2597,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
 
        switch (cmd) {
                case F_SETFL:
-                       if (!file->f_dentry || !file->f_dentry->d_inode) {
+                       if (!file->f_path.dentry || !file->f_path.dentry->d_inode) {
                                err = -EINVAL;
                                break;
                        }
@@ -2617,7 +2623,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
                case F_SETLK64:
                case F_SETLKW64:
 #endif
-                       if (!file->f_dentry || !file->f_dentry->d_inode) {
+                       if (!file->f_path.dentry || !file->f_path.dentry->d_inode) {
                                err = -EINVAL;
                                break;
                        }
@@ -2944,6 +2950,22 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb,
                break;
         }
 
+       case IPPROTO_DCCP: {
+               struct dccp_hdr _dccph, *dh;
+
+               if (ntohs(ih->frag_off) & IP_OFFSET)
+                       break;
+
+               offset += ihlen;
+               dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
+               if (dh == NULL)
+                       break;
+
+               ad->u.net.sport = dh->dccph_sport;
+               ad->u.net.dport = dh->dccph_dport;
+               break;
+        }
+
         default:
                break;
         }
@@ -3004,6 +3026,18 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb,
                break;
        }
 
+       case IPPROTO_DCCP: {
+               struct dccp_hdr _dccph, *dh;
+
+               dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
+               if (dh == NULL)
+                       break;
+
+               ad->u.net.sport = dh->dccph_sport;
+               ad->u.net.dport = dh->dccph_dport;
+               break;
+        }
+
        /* includes fragments */
        default:
                break;
@@ -3109,9 +3143,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
        if (sock->sk) {
                sksec = sock->sk->sk_security;
                sksec->sid = isec->sid;
-               err = selinux_netlbl_socket_post_create(sock,
-                                                       family,
-                                                       isec->sid);
+               err = selinux_netlbl_socket_post_create(sock);
        }
 
        return err;
@@ -3188,7 +3220,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                case SECCLASS_UDP_SOCKET:
                        node_perm = UDP_SOCKET__NODE_BIND;
                        break;
-                       
+
+               case SECCLASS_DCCP_SOCKET:
+                       node_perm = DCCP_SOCKET__NODE_BIND;
+                       break;
+
                default:
                        node_perm = RAWIP_SOCKET__NODE_BIND;
                        break;
@@ -3226,16 +3262,17 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
                return err;
 
        /*
-        * If a TCP socket, check name_connect permission for the port.
+        * 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) {
+       if (isec->sclass == SECCLASS_TCP_SOCKET ||
+           isec->sclass == SECCLASS_DCCP_SOCKET) {
                struct sock *sk = sock->sk;
                struct avc_audit_data ad;
                struct sockaddr_in *addr4 = NULL;
                struct sockaddr_in6 *addr6 = NULL;
                unsigned short snum;
-               u32 sid;
+               u32 sid, perm;
 
                if (sk->sk_family == PF_INET) {
                        addr4 = (struct sockaddr_in *)address;
@@ -3254,11 +3291,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
                if (err)
                        goto out;
 
+               perm = (isec->sclass == SECCLASS_TCP_SOCKET) ?
+                      TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
+
                AVC_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,
-                                  TCP_SOCKET__NAME_CONNECT, &ad);
+               err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad);
                if (err)
                        goto out;
        }
@@ -3446,7 +3485,13 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
                node_perm = NODE__TCP_RECV;
                recv_perm = TCP_SOCKET__RECV_MSG;
                break;
-       
+
+       case SECCLASS_DCCP_SOCKET:
+               netif_perm = NETIF__DCCP_RECV;
+               node_perm = NODE__DCCP_RECV;
+               recv_perm = DCCP_SOCKET__RECV_MSG;
+               break;
+
        default:
                netif_perm = NETIF__RAWIP_RECV;
                node_perm = NODE__RAWIP_RECV;
@@ -3495,7 +3540,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                goto out;
 
        /* Handle mapped IPv4 packets arriving via IPv6 sockets */
-       if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
+       if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
                family = PF_INET;
 
        AVC_AUDIT_DATA_INIT(&ad, NET);
@@ -3532,27 +3577,16 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
        u32 scontext_len;
        struct sk_security_struct *ssec;
        struct inode_security_struct *isec;
-       u32 peer_sid = 0;
+       u32 peer_sid = SECSID_NULL;
 
        isec = SOCK_INODE(sock)->i_security;
 
-       /* if UNIX_STREAM check peer_sid, if TCP check dst for labelled sa */
-       if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET) {
+       if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
+           isec->sclass == SECCLASS_TCP_SOCKET) {
                ssec = sock->sk->sk_security;
                peer_sid = ssec->peer_sid;
        }
-       else if (isec->sclass == SECCLASS_TCP_SOCKET) {
-               peer_sid = selinux_netlbl_socket_getpeersec_stream(sock);
-               if (peer_sid == SECSID_NULL) {
-                       ssec = sock->sk->sk_security;
-                       peer_sid = ssec->peer_sid;
-               }
-               if (peer_sid == SECSID_NULL) {
-                       err = -ENOPROTOOPT;
-                       goto out;
-               }
-       }
-       else {
+       if (peer_sid == SECSID_NULL) {
                err = -ENOPROTOOPT;
                goto out;
        }
@@ -3584,13 +3618,12 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
        u32 peer_secid = SECSID_NULL;
        int err = 0;
 
-       if (sock && (sock->sk->sk_family == PF_UNIX))
+       if (sock && sock->sk->sk_family == PF_UNIX)
                selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
-       else if (skb) {
-               peer_secid = selinux_netlbl_socket_getpeersec_dgram(skb);
-               if (peer_secid == SECSID_NULL)
-                       peer_secid = selinux_socket_getpeer_dgram(skb);
-       }
+       else if (skb)
+               security_skb_extlbl_sid(skb,
+                                       SECINITSID_UNLABELED,
+                                       &peer_secid);
 
        if (peer_secid == SECSID_NULL)
                err = -EINVAL;
@@ -3617,7 +3650,7 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
        newssec->sid = ssec->sid;
        newssec->peer_sid = ssec->peer_sid;
 
-       selinux_netlbl_sk_clone_security(ssec, newssec);
+       selinux_netlbl_sk_security_clone(ssec, newssec);
 }
 
 static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
@@ -3651,17 +3684,10 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
        u32 newsid;
        u32 peersid;
 
-       newsid = selinux_netlbl_inet_conn_request(skb, sksec->sid);
-       if (newsid != SECSID_NULL) {
-               req->secid = newsid;
-               return 0;
-       }
-
-       selinux_skb_xfrm_sid(skb, &peersid);
-
+       security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid);
        if (peersid == SECSID_NULL) {
                req->secid = sksec->sid;
-               req->peer_secid = 0;
+               req->peer_secid = SECSID_NULL;
                return 0;
        }
 
@@ -3686,7 +3712,9 @@ static void selinux_inet_csk_clone(struct sock *newsk,
           So we will wait until sock_graft to do it, by which
           time it will have been created and available. */
 
-       selinux_netlbl_sk_security_init(newsksec, req->rsk_ops->family);
+       /* We don't need to take any sort of lock here as we are the only
+        * thread with access to newsksec */
+       selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family);
 }
 
 static void selinux_inet_conn_established(struct sock *sk,
@@ -3694,7 +3722,7 @@ static void selinux_inet_conn_established(struct sock *sk,
 {
        struct sk_security_struct *sksec = sk->sk_security;
 
-       selinux_skb_xfrm_sid(skb, &sksec->peer_sid);
+       security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid);
 }
 
 static void selinux_req_classify_flow(const struct request_sock *req,
@@ -3777,7 +3805,13 @@ static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *
                node_perm = NODE__TCP_SEND;
                send_perm = TCP_SOCKET__SEND_MSG;
                break;
-       
+
+       case SECCLASS_DCCP_SOCKET:
+               netif_perm = NETIF__DCCP_SEND;
+               node_perm = NODE__DCCP_SEND;
+               send_perm = DCCP_SOCKET__SEND_MSG;
+               break;
+
        default:
                netif_perm = NETIF__RAWIP_SEND;
                node_perm = NODE__RAWIP_SEND;