selinux: Fix packet forwarding checks on postrouting
[linux-2.6.git] / security / selinux / hooks.c
index 5c9f25b..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>
@@ -87,9 +86,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 +184,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 +275,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;
@@ -1149,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)
 {
@@ -1329,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;
@@ -1345,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)
@@ -1584,8 +1547,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;
@@ -1608,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;
        }
@@ -1806,27 +1768,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;
 }
 
@@ -1910,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();
@@ -2021,14 +1889,10 @@ static int selinux_quota_on(struct dentry *dentry)
        return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON);
 }
 
-static int selinux_syslog(int type, bool from_file)
+static int selinux_syslog(int type)
 {
        int rc;
 
-       rc = cap_syslog(type, from_file);
-       if (rc)
-               return rc;
-
        switch (type) {
        case SYSLOG_ACTION_READ_ALL:    /* Read last kernel messages */
        case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */
@@ -2112,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;
        }
@@ -2183,8 +2048,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;
 
@@ -2219,8 +2083,9 @@ static inline void flush_unauthorized_files(const struct cred *cred,
 
        tty = get_current_tty();
        if (tty) {
-               file_list_lock();
+               spin_lock(&tty_files_lock);
                if (!list_empty(&tty->tty_files)) {
+                       struct tty_file_private *file_priv;
                        struct inode *inode;
 
                        /* Revalidate access to controlling tty.
@@ -2228,14 +2093,16 @@ static inline void flush_unauthorized_files(const struct cred *cred,
                           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. */
-                       file = list_first_entry(&tty->tty_files, struct file, f_u.fu_list);
+                       file_priv = list_first_entry(&tty->tty_files,
+                                               struct tty_file_private, list);
+                       file = file_priv->file;
                        inode = file->f_path.dentry->d_inode;
                        if (inode_has_perm(cred, inode,
                                           FILE__READ | FILE__WRITE, NULL)) {
                                drop_tty = 1;
                        }
                }
-               file_list_unlock();
+               spin_unlock(&tty_files_lock);
                tty_kref_put(tty);
        }
        /* Reset controlling tty. */
@@ -2333,12 +2200,15 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
        rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS,
                          PROCESS__RLIMITINH, NULL);
        if (rc) {
+               /* protect against do_prlimit() */
+               task_lock(current);
                for (i = 0; i < RLIM_NLIMITS; i++) {
                        rlim = current->signal->rlim + i;
                        initrlim = init_task.signal->rlim + i;
                        rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
                }
-               update_rlimit_cpu(current->signal->rlim[RLIMIT_CPU].rlim_cur);
+               task_unlock(current);
+               update_rlimit_cpu(current, rlimit(RLIMIT_CPU));
        }
 }
 
@@ -2556,11 +2426,10 @@ 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 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;
@@ -2573,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 "
@@ -2676,14 +2548,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)
@@ -3371,25 +3255,26 @@ static int selinux_task_getioprio(struct task_struct *p)
        return current_has_perm(p, PROCESS__GETSCHED);
 }
 
-static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
+static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
+               struct rlimit *new_rlim)
 {
-       struct rlimit *old_rlim = current->signal->rlim + resource;
+       struct rlimit *old_rlim = p->signal->rlim + resource;
 
        /* Control the ability to change the hard limit (whether
           lowering or raising it), so that the hard limit can
           later be used as a safe reset point for the soft limit
           upon context transitions.  See selinux_bprm_committing_creds. */
        if (old_rlim->rlim_max != new_rlim->rlim_max)
-               return current_has_perm(current, PROCESS__SETRLIMIT);
+               return current_has_perm(p, PROCESS__SETRLIMIT);
 
        return 0;
 }
 
-static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
+static int selinux_task_setscheduler(struct task_struct *p)
 {
        int rc;
 
-       rc = cap_task_setscheduler(p, policy, lp);
+       rc = cap_task_setscheduler(p);
        if (rc)
                return rc;
 
@@ -3671,71 +3556,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 +3624,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 +3637,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 +3670,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 +3709,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 +3721,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 +3755,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 +3774,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 +3783,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 +3800,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 +3833,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,
+static int selinux_socket_unix_stream_connect(struct sock *sock,
+                                             struct sock *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_security;
+       struct sk_security_struct *sksec_other = other->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;
+       ad.u.net.sk = other;
 
-       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,
@@ -4060,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;
@@ -4079,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;
 }
@@ -4168,26 +4012,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 +4036,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 +4068,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)
@@ -4335,6 +4184,27 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
        selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
 }
 
+static int selinux_secmark_relabel_packet(u32 sid)
+{
+       const struct task_security_struct *__tsec;
+       u32 tsid;
+
+       __tsec = current_security();
+       tsid = __tsec->sid;
+
+       return avc_has_perm(tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO, NULL);
+}
+
+static void selinux_secmark_refcount_inc(void)
+{
+       atomic_inc(&selinux_secmark_refcount);
+}
+
+static void selinux_secmark_refcount_dec(void)
+{
+       atomic_dec(&selinux_secmark_refcount);
+}
+
 static void selinux_req_classify_flow(const struct request_sock *req,
                                      struct flowi *fl)
 {
@@ -4399,8 +4269,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 +4277,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 +4294,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;
 }
@@ -4560,11 +4429,10 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
        if (selinux_secmark_enabled())
                if (avc_has_perm(sksec->sid, skb->secmark,
                                 SECCLASS_PACKET, PACKET__SEND, &ad))
-                       return NF_DROP;
+                       return NF_DROP_ERR(-ECONNREFUSED);
 
-       if (selinux_policycap_netpeer)
-               if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
-                       return NF_DROP;
+       if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
+               return NF_DROP_ERR(-ECONNREFUSED);
 
        return NF_ACCEPT;
 }
@@ -4607,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;
-               }
-               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;
@@ -4643,7 +4498,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
        if (secmark_active)
                if (avc_has_perm(peer_sid, skb->secmark,
                                 SECCLASS_PACKET, secmark_perm, &ad))
-                       return NF_DROP;
+                       return NF_DROP_ERR(-ECONNREFUSED);
 
        if (peerlbl_active) {
                u32 if_sid;
@@ -4653,13 +4508,13 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
                        return NF_DROP;
                if (avc_has_perm(peer_sid, if_sid,
                                 SECCLASS_NETIF, NETIF__EGRESS, &ad))
-                       return NF_DROP;
+                       return NF_DROP_ERR(-ECONNREFUSED);
 
                if (sel_netnode_sid(addrp, family, &node_sid))
                        return NF_DROP;
                if (avc_has_perm(peer_sid, node_sid,
                                 SECCLASS_NODE, NODE__SENDTO, &ad))
-                       return NF_DROP;
+                       return NF_DROP_ERR(-ECONNREFUSED);
        }
 
        return NF_ACCEPT;
@@ -4881,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;
        }
@@ -5435,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,
@@ -5590,6 +5444,9 @@ static struct security_operations selinux_ops = {
        .inet_conn_request =            selinux_inet_conn_request,
        .inet_csk_clone =               selinux_inet_csk_clone,
        .inet_conn_established =        selinux_inet_conn_established,
+       .secmark_relabel_packet =       selinux_secmark_relabel_packet,
+       .secmark_refcount_inc =         selinux_secmark_refcount_inc,
+       .secmark_refcount_dec =         selinux_secmark_refcount_dec,
        .req_classify_flow =            selinux_req_classify_flow,
        .tun_dev_create =               selinux_tun_dev_create,
        .tun_dev_post_create =          selinux_tun_dev_post_create,