[PATCH] slab: remove kmem_cache_t
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / security / selinux / hooks.c
index 8ab5679a37a30324b9b61a61c159dd4978512447..44e9cd470543320ad736af1db38e2e63b94648ca 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;
                }
@@ -1754,7 +1757,8 @@ static inline void flush_unauthorized_files(struct files_struct * files)
                                                get_file(devnull);
                                        } else {
                                                devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
-                                               if (!devnull) {
+                                               if (IS_ERR(devnull)) {
+                                                       devnull = NULL;
                                                        put_unused_fd(fd);
                                                        fput(file);
                                                        continue;
@@ -2888,7 +2892,8 @@ static void selinux_task_to_inode(struct task_struct *p,
 }
 
 /* Returns error only if unable to parse addresses */
-static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad)
+static int selinux_parse_skb_ipv4(struct sk_buff *skb,
+                       struct avc_audit_data *ad, u8 *proto)
 {
        int offset, ihlen, ret = -EINVAL;
        struct iphdr _iph, *ih;
@@ -2906,6 +2911,9 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad
        ad->u.net.v4info.daddr = ih->daddr;
        ret = 0;
 
+       if (proto)
+               *proto = ih->protocol;
+
        switch (ih->protocol) {
         case IPPROTO_TCP: {
                struct tcphdr _tcph, *th;
@@ -2939,6 +2947,22 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad
                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;
         }
@@ -2949,7 +2973,8 @@ out:
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 
 /* Returns error only if unable to parse addresses */
-static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad)
+static int selinux_parse_skb_ipv6(struct sk_buff *skb,
+                       struct avc_audit_data *ad, u8 *proto)
 {
        u8 nexthdr;
        int ret = -EINVAL, offset;
@@ -2970,6 +2995,9 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad
        if (offset < 0)
                goto out;
 
+       if (proto)
+               *proto = nexthdr;
+
        switch (nexthdr) {
        case IPPROTO_TCP: {
                struct tcphdr _tcph, *th;
@@ -2995,6 +3023,18 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad
                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;
@@ -3006,13 +3046,13 @@ out:
 #endif /* IPV6 */
 
 static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
-                            char **addrp, int *len, int src)
+                            char **addrp, int *len, int src, u8 *proto)
 {
        int ret = 0;
 
        switch (ad->u.net.family) {
        case PF_INET:
-               ret = selinux_parse_skb_ipv4(skb, ad);
+               ret = selinux_parse_skb_ipv4(skb, ad, proto);
                if (ret || !addrp)
                        break;
                *len = 4;
@@ -3022,7 +3062,7 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case PF_INET6:
-               ret = selinux_parse_skb_ipv6(skb, ad);
+               ret = selinux_parse_skb_ipv6(skb, ad, proto);
                if (ret || !addrp)
                        break;
                *len = 16;
@@ -3100,9 +3140,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;
@@ -3179,7 +3217,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;
@@ -3217,16 +3259,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;
@@ -3245,11 +3288,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;
        }
@@ -3437,7 +3482,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;
@@ -3486,14 +3537,14 @@ 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);
        ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]";
        ad.u.net.family = family;
 
-       err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
+       err = selinux_parse_skb(skb, &ad, &addrp, &len, 1, NULL);
        if (err)
                goto out;
 
@@ -3523,25 +3574,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)
-                       peer_sid = selinux_socket_getpeer_stream(sock->sk);
-               if (peer_sid == SECSID_NULL) {
-                       err = -ENOPROTOOPT;
-                       goto out;
-               }
-       }
-       else {
+       if (peer_sid == SECSID_NULL) {
                err = -ENOPROTOOPT;
                goto out;
        }
@@ -3573,13 +3615,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;
@@ -3606,7 +3647,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)
@@ -3640,17 +3681,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;
-       }
-
-       err = selinux_xfrm_decode_session(skb, &peersid, 0);
-       BUG_ON(err);
-
+       security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid);
        if (peersid == SECSID_NULL) {
                req->secid = sksec->sid;
+               req->peer_secid = SECSID_NULL;
                return 0;
        }
 
@@ -3659,6 +3693,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
                return err;
 
        req->secid = newsid;
+       req->peer_secid = peersid;
        return 0;
 }
 
@@ -3668,12 +3703,23 @@ static void selinux_inet_csk_clone(struct sock *newsk,
        struct sk_security_struct *newsksec = newsk->sk_security;
 
        newsksec->sid = req->secid;
+       newsksec->peer_sid = req->peer_secid;
        /* NOTE: Ideally, we should also get the isec->sid for the
           new socket in sync, but we don't have the isec available yet.
           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,
+                               struct sk_buff *skb)
+{
+       struct sk_security_struct *sksec = sk->sk_security;
+
+       security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid);
 }
 
 static void selinux_req_classify_flow(const struct request_sock *req,
@@ -3756,7 +3802,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;
@@ -3807,6 +3859,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
        struct avc_audit_data ad;
        struct net_device *dev = (struct net_device *)out;
        struct sk_security_struct *sksec;
+       u8 proto;
 
        sk = skb->sk;
        if (!sk)
@@ -3818,7 +3871,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
        ad.u.net.netif = dev->name;
        ad.u.net.family = family;
 
-       err = selinux_parse_skb(skb, &ad, &addrp, &len, 0);
+       err = selinux_parse_skb(skb, &ad, &addrp, &len, 0, &proto);
        if (err)
                goto out;
 
@@ -3832,7 +3885,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
        if (err)
                goto out;
 
-       err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad);
+       err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto);
 out:
        return err ? NF_DROP : NF_ACCEPT;
 }
@@ -4738,6 +4791,7 @@ static struct security_operations selinux_ops = {
        .sock_graft =                   selinux_sock_graft,
        .inet_conn_request =            selinux_inet_conn_request,
        .inet_csk_clone =               selinux_inet_csk_clone,
+       .inet_conn_established =        selinux_inet_conn_established,
        .req_classify_flow =            selinux_req_classify_flow,
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -4750,7 +4804,6 @@ static struct security_operations selinux_ops = {
        .xfrm_state_delete_security =   selinux_xfrm_state_delete,
        .xfrm_policy_lookup =           selinux_xfrm_policy_lookup,
        .xfrm_state_pol_flow_match =    selinux_xfrm_state_pol_flow_match,
-       .xfrm_flow_state_match =        selinux_xfrm_flow_state_match,
        .xfrm_decode_session =          selinux_xfrm_decode_session,
 #endif