selinux: Fix packet forwarding checks on postrouting
[linux-2.6.git] / security / selinux / hooks.c
index 6f637d2..8ffed9f 100644 (file)
 #include <linux/swap.h>
 #include <linux/spinlock.h>
 #include <linux/syscalls.h>
+#include <linux/dcache.h>
 #include <linux/file.h>
 #include <linux/fdtable.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
-#include <linux/proc_fs.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
 #include <linux/tty.h>
@@ -70,7 +70,6 @@
 #include <net/ipv6.h>
 #include <linux/hugetlb.h>
 #include <linux/personality.h>
-#include <linux/sysctl.h>
 #include <linux/audit.h>
 #include <linux/string.h>
 #include <linux/selinux.h>
@@ -1120,39 +1119,35 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
 }
 
 #ifdef CONFIG_PROC_FS
-static int selinux_proc_get_sid(struct proc_dir_entry *de,
+static int selinux_proc_get_sid(struct dentry *dentry,
                                u16 tclass,
                                u32 *sid)
 {
-       int buflen, rc;
-       char *buffer, *path, *end;
+       int rc;
+       char *buffer, *path;
 
        buffer = (char *)__get_free_page(GFP_KERNEL);
        if (!buffer)
                return -ENOMEM;
 
-       buflen = PAGE_SIZE;
-       end = buffer+buflen;
-       *--end = '\0';
-       buflen--;
-       path = end-1;
-       *path = '/';
-       while (de && de != de->parent) {
-               buflen -= de->namelen + 1;
-               if (buflen < 0)
-                       break;
-               end -= de->namelen;
-               memcpy(end, de->name, de->namelen);
-               *--end = '/';
-               path = end;
-               de = de->parent;
+       path = dentry_path_raw(dentry, buffer, PAGE_SIZE);
+       if (IS_ERR(path))
+               rc = PTR_ERR(path);
+       else {
+               /* each process gets a /proc/PID/ entry. Strip off the
+                * PID part to get a valid selinux labeling.
+                * e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */
+               while (path[1] >= '0' && path[1] <= '9') {
+                       path[1] = '/';
+                       path++;
+               }
+               rc = security_genfs_sid("proc", path, tclass, sid);
        }
-       rc = security_genfs_sid("proc", path, tclass, sid);
        free_page((unsigned long)buffer);
        return rc;
 }
 #else
-static int selinux_proc_get_sid(struct proc_dir_entry *de,
+static int selinux_proc_get_sid(struct dentry *dentry,
                                u16 tclass,
                                u32 *sid)
 {
@@ -1300,10 +1295,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
 
                /* Try to obtain a transition SID. */
                isec->sclass = inode_mode_to_security_class(inode->i_mode);
-               rc = security_transition_sid(isec->task_sid,
-                                            sbsec->sid,
-                                            isec->sclass,
-                                            &sid);
+               rc = security_transition_sid(isec->task_sid, sbsec->sid,
+                                            isec->sclass, NULL, &sid);
                if (rc)
                        goto out_unlock;
                isec->sid = sid;
@@ -1316,10 +1309,9 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                isec->sid = sbsec->sid;
 
                if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) {
-                       struct proc_inode *proci = PROC_I(inode);
-                       if (proci->pde) {
+                       if (opt_dentry) {
                                isec->sclass = inode_mode_to_security_class(inode->i_mode);
-                               rc = selinux_proc_get_sid(proci->pde,
+                               rc = selinux_proc_get_sid(opt_dentry,
                                                          isec->sclass,
                                                          &sid);
                                if (rc)
@@ -1578,7 +1570,7 @@ static int may_create(struct inode *dir,
                return rc;
 
        if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
-               rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
+               rc = security_transition_sid(sid, dsec->sid, tclass, NULL, &newsid);
                if (rc)
                        return rc;
        }
@@ -1862,82 +1854,6 @@ static int selinux_capable(struct task_struct *tsk, const struct cred *cred,
        return task_has_capability(tsk, cred, cap, audit);
 }
 
-static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
-{
-       int buflen, rc;
-       char *buffer, *path, *end;
-
-       rc = -ENOMEM;
-       buffer = (char *)__get_free_page(GFP_KERNEL);
-       if (!buffer)
-               goto out;
-
-       buflen = PAGE_SIZE;
-       end = buffer+buflen;
-       *--end = '\0';
-       buflen--;
-       path = end-1;
-       *path = '/';
-       while (table) {
-               const char *name = table->procname;
-               size_t namelen = strlen(name);
-               buflen -= namelen + 1;
-               if (buflen < 0)
-                       goto out_free;
-               end -= namelen;
-               memcpy(end, name, namelen);
-               *--end = '/';
-               path = end;
-               table = table->parent;
-       }
-       buflen -= 4;
-       if (buflen < 0)
-               goto out_free;
-       end -= 4;
-       memcpy(end, "/sys", 4);
-       path = end;
-       rc = security_genfs_sid("proc", path, tclass, sid);
-out_free:
-       free_page((unsigned long)buffer);
-out:
-       return rc;
-}
-
-static int selinux_sysctl(ctl_table *table, int op)
-{
-       int error = 0;
-       u32 av;
-       u32 tsid, sid;
-       int rc;
-
-       sid = current_sid();
-
-       rc = selinux_sysctl_get_sid(table, (op == 0001) ?
-                                   SECCLASS_DIR : SECCLASS_FILE, &tsid);
-       if (rc) {
-               /* Default to the well-defined sysctl SID. */
-               tsid = SECINITSID_SYSCTL;
-       }
-
-       /* The op values are "defined" in sysctl.c, thereby creating
-        * a bad coupling between this module and sysctl.c */
-       if (op == 001) {
-               error = avc_has_perm(sid, tsid,
-                                    SECCLASS_DIR, DIR__SEARCH, NULL);
-       } else {
-               av = 0;
-               if (op & 004)
-                       av |= FILE__READ;
-               if (op & 002)
-                       av |= FILE__WRITE;
-               if (av)
-                       error = avc_has_perm(sid, tsid,
-                                            SECCLASS_FILE, av, NULL);
-       }
-
-       return error;
-}
-
 static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
 {
        const struct cred *cred = current_cred();
@@ -2060,7 +1976,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
        } else {
                /* Check for a default transition on this program. */
                rc = security_transition_sid(old_tsec->sid, isec->sid,
-                                            SECCLASS_PROCESS, &new_tsec->sid);
+                                            SECCLASS_PROCESS, NULL,
+                                            &new_tsec->sid);
                if (rc)
                        return rc;
        }
@@ -2509,8 +2426,8 @@ static void selinux_inode_free_security(struct inode *inode)
 }
 
 static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
-                                      char **name, void **value,
-                                      size_t *len)
+                                      const struct qstr *qstr, char **name,
+                                      void **value, size_t *len)
 {
        const struct task_security_struct *tsec = current_security();
        struct inode_security_struct *dsec;
@@ -2525,10 +2442,13 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
        sid = tsec->sid;
        newsid = tsec->create_sid;
 
-       if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
+       if ((sbsec->flags & SE_SBINITIALIZED) &&
+           (sbsec->behavior == SECURITY_FS_USE_MNTPOINT))
+               newsid = sbsec->mntpoint_sid;
+       else if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
                rc = security_transition_sid(sid, dsec->sid,
                                             inode_mode_to_security_class(inode->i_mode),
-                                            &newsid);
+                                            qstr, &newsid);
                if (rc) {
                        printk(KERN_WARNING "%s:  "
                               "security_transition_sid failed, rc=%d (dev=%s "
@@ -3995,7 +3915,6 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
 {
        int err = 0;
        struct sk_security_struct *sksec = sk->sk_security;
-       u32 peer_sid;
        u32 sk_sid = sksec->sid;
        struct common_audit_data ad;
        char *addrp;
@@ -4014,20 +3933,10 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
                        return err;
        }
 
-       if (selinux_policycap_netpeer) {
-               err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
-               if (err)
-                       return err;
-               err = avc_has_perm(sk_sid, peer_sid,
-                                  SECCLASS_PEER, PEER__RECV, &ad);
-               if (err)
-                       selinux_netlbl_err(skb, err, 0);
-       } else {
-               err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);
-               if (err)
-                       return err;
-               err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
-       }
+       err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);
+       if (err)
+               return err;
+       err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
 
        return err;
 }
@@ -4522,9 +4431,8 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
                                 SECCLASS_PACKET, PACKET__SEND, &ad))
                        return NF_DROP_ERR(-ECONNREFUSED);
 
-       if (selinux_policycap_netpeer)
-               if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
-                       return NF_DROP_ERR(-ECONNREFUSED);
+       if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
+               return NF_DROP_ERR(-ECONNREFUSED);
 
        return NF_ACCEPT;
 }
@@ -4567,27 +4475,14 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
         * from the sending socket, otherwise use the kernel's sid */
        sk = skb->sk;
        if (sk == NULL) {
-               switch (family) {
-               case PF_INET:
-                       if (IPCB(skb)->flags & IPSKB_FORWARDED)
-                               secmark_perm = PACKET__FORWARD_OUT;
-                       else
-                               secmark_perm = PACKET__SEND;
-                       break;
-               case PF_INET6:
-                       if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
-                               secmark_perm = PACKET__FORWARD_OUT;
-                       else
-                               secmark_perm = PACKET__SEND;
-                       break;
-               default:
-                       return NF_DROP_ERR(-ECONNREFUSED);
-               }
-               if (secmark_perm == PACKET__FORWARD_OUT) {
+               if (skb->skb_iif) {
+                       secmark_perm = PACKET__FORWARD_OUT;
                        if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
                                return NF_DROP;
-               } else
+               } else {
+                       secmark_perm = PACKET__SEND;
                        peer_sid = SECINITSID_KERNEL;
+               }
        } else {
                struct sk_security_struct *sksec = sk->sk_security;
                peer_sid = sksec->sid;
@@ -4841,7 +4736,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
                 * message queue this message will be stored in
                 */
                rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG,
-                                            &msec->sid);
+                                            NULL, &msec->sid);
                if (rc)
                        return rc;
        }
@@ -5395,7 +5290,6 @@ static struct security_operations selinux_ops = {
        .ptrace_traceme =               selinux_ptrace_traceme,
        .capget =                       selinux_capget,
        .capset =                       selinux_capset,
-       .sysctl =                       selinux_sysctl,
        .capable =                      selinux_capable,
        .quotactl =                     selinux_quotactl,
        .quota_on =                     selinux_quota_on,