Merge branch 'master' of git://git.infradead.org/users/eparis/selinux into next
[linux-3.10.git] / security / smack / smack_lsm.c
index 8ef0199..d583c05 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/slab.h>
 #include <linux/mutex.h>
 #include <linux/pipe_fs_i.h>
-#include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
 #include <linux/audit.h>
 #include <linux/magic.h>
 static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp)
 {
        int rc;
-       char in[SMK_LABELLEN];
+       char *buffer;
+       char *result = NULL;
 
        if (ip->i_op->getxattr == NULL)
                return NULL;
 
-       rc = ip->i_op->getxattr(dp, name, in, SMK_LABELLEN);
-       if (rc < 0)
+       buffer = kzalloc(SMK_LONGLABEL, GFP_KERNEL);
+       if (buffer == NULL)
                return NULL;
 
-       return smk_import(in, rc);
+       rc = ip->i_op->getxattr(dp, name, buffer, SMK_LONGLABEL);
+       if (rc > 0)
+               result = smk_import(buffer, rc);
+
+       kfree(buffer);
+
+       return result;
 }
 
 /**
@@ -79,7 +85,7 @@ struct inode_smack *new_inode_smack(char *smack)
 {
        struct inode_smack *isp;
 
-       isp = kzalloc(sizeof(struct inode_smack), GFP_KERNEL);
+       isp = kzalloc(sizeof(struct inode_smack), GFP_NOFS);
        if (isp == NULL)
                return NULL;
 
@@ -556,13 +562,14 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
                                     void **value, size_t *len)
 {
        struct smack_known *skp;
+       struct inode_smack *issp = inode->i_security;
        char *csp = smk_of_current();
        char *isp = smk_of_inode(inode);
        char *dsp = smk_of_inode(dir);
        int may;
 
        if (name) {
-               *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL);
+               *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_NOFS);
                if (*name == NULL)
                        return -ENOMEM;
        }
@@ -577,12 +584,15 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
                 * If the access rule allows transmutation and
                 * the directory requests transmutation then
                 * by all means transmute.
+                * Mark the inode as changed.
                 */
                if (may > 0 && ((may & MAY_TRANSMUTE) != 0) &&
-                   smk_inode_transmutable(dir))
+                   smk_inode_transmutable(dir)) {
                        isp = dsp;
+                       issp->smk_flags |= SMK_INODE_CHANGED;
+               }
 
-               *value = kstrdup(isp, GFP_KERNEL);
+               *value = kstrdup(isp, GFP_NOFS);
                if (*value == NULL)
                        return -ENOMEM;
        }
@@ -821,7 +831,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
                 * check label validity here so import wont fail on
                 * post_setxattr
                 */
-               if (size == 0 || size >= SMK_LABELLEN ||
+               if (size == 0 || size >= SMK_LONGLABEL ||
                    smk_import(value, size) == NULL)
                        rc = -EINVAL;
        } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
@@ -1820,65 +1830,6 @@ static char *smack_host_label(struct sockaddr_in *sip)
 }
 
 /**
- * smack_set_catset - convert a capset to netlabel mls categories
- * @catset: the Smack categories
- * @sap: where to put the netlabel categories
- *
- * Allocates and fills attr.mls.cat
- */
-static void smack_set_catset(char *catset, struct netlbl_lsm_secattr *sap)
-{
-       unsigned char *cp;
-       unsigned char m;
-       int cat;
-       int rc;
-       int byte;
-
-       if (!catset)
-               return;
-
-       sap->flags |= NETLBL_SECATTR_MLS_CAT;
-       sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
-       sap->attr.mls.cat->startbit = 0;
-
-       for (cat = 1, cp = catset, byte = 0; byte < SMK_LABELLEN; cp++, byte++)
-               for (m = 0x80; m != 0; m >>= 1, cat++) {
-                       if ((m & *cp) == 0)
-                               continue;
-                       rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat,
-                                                         cat, GFP_ATOMIC);
-               }
-}
-
-/**
- * smack_to_secattr - fill a secattr from a smack value
- * @smack: the smack value
- * @nlsp: where the result goes
- *
- * Casey says that CIPSO is good enough for now.
- * It can be used to effect.
- * It can also be abused to effect when necessary.
- * Apologies to the TSIG group in general and GW in particular.
- */
-static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)
-{
-       struct smack_cipso cipso;
-       int rc;
-
-       nlsp->domain = smack;
-       nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
-
-       rc = smack_to_cipso(smack, &cipso);
-       if (rc == 0) {
-               nlsp->attr.mls.lvl = cipso.smk_level;
-               smack_set_catset(cipso.smk_catset, nlsp);
-       } else {
-               nlsp->attr.mls.lvl = smack_cipso_direct;
-               smack_set_catset(smack, nlsp);
-       }
-}
-
-/**
  * smack_netlabel - Set the secattr on a socket
  * @sk: the socket
  * @labeled: socket label scheme
@@ -1890,8 +1841,8 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)
  */
 static int smack_netlabel(struct sock *sk, int labeled)
 {
+       struct smack_known *skp;
        struct socket_smack *ssp = sk->sk_security;
-       struct netlbl_lsm_secattr secattr;
        int rc = 0;
 
        /*
@@ -1909,10 +1860,8 @@ static int smack_netlabel(struct sock *sk, int labeled)
            labeled == SMACK_UNLABELED_SOCKET)
                netlbl_sock_delattr(sk);
        else {
-               netlbl_secattr_init(&secattr);
-               smack_to_secattr(ssp->smk_out, &secattr);
-               rc = netlbl_sock_setattr(sk, sk->sk_family, &secattr);
-               netlbl_secattr_destroy(&secattr);
+               skp = smk_find_entry(ssp->smk_out);
+               rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel);
        }
 
        bh_unlock_sock(sk);
@@ -1939,18 +1888,19 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
        char *hostsp;
        struct socket_smack *ssp = sk->sk_security;
        struct smk_audit_info ad;
-       struct lsm_network_audit net;
 
        rcu_read_lock();
        hostsp = smack_host_label(sap);
        if (hostsp != NULL) {
-               sk_lbl = SMACK_UNLABELED_SOCKET;
 #ifdef CONFIG_AUDIT
+               struct lsm_network_audit net;
+
                smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
                ad.a.u.net->family = sap->sin_family;
                ad.a.u.net->dport = sap->sin_port;
                ad.a.u.net->v4info.daddr = sap->sin_addr.s_addr;
 #endif
+               sk_lbl = SMACK_UNLABELED_SOCKET;
                rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE, &ad);
        } else {
                sk_lbl = SMACK_CIPSO_SOCKET;
@@ -1984,7 +1934,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
        struct socket *sock;
        int rc = 0;
 
-       if (value == NULL || size > SMK_LABELLEN || size == 0)
+       if (value == NULL || size > SMK_LONGLABEL || size == 0)
                return -EACCES;
 
        sp = smk_import(value, size);
@@ -2551,6 +2501,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
        char *final;
        char trattr[TRANS_TRUE_SIZE];
        int transflag = 0;
+       int rc;
        struct dentry *dp;
 
        if (inode == NULL)
@@ -2669,17 +2620,38 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
                 */
                dp = dget(opt_dentry);
                fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp);
-               if (fetched != NULL) {
+               if (fetched != NULL)
                        final = fetched;
-                       if (S_ISDIR(inode->i_mode)) {
-                               trattr[0] = '\0';
-                               inode->i_op->getxattr(dp,
+
+               /*
+                * Transmuting directory
+                */
+               if (S_ISDIR(inode->i_mode)) {
+                       /*
+                        * If this is a new directory and the label was
+                        * transmuted when the inode was initialized
+                        * set the transmute attribute on the directory
+                        * and mark the inode.
+                        *
+                        * If there is a transmute attribute on the
+                        * directory mark the inode.
+                        */
+                       if (isp->smk_flags & SMK_INODE_CHANGED) {
+                               isp->smk_flags &= ~SMK_INODE_CHANGED;
+                               rc = inode->i_op->setxattr(dp,
                                        XATTR_NAME_SMACKTRANSMUTE,
-                                       trattr, TRANS_TRUE_SIZE);
-                               if (strncmp(trattr, TRANS_TRUE,
-                                           TRANS_TRUE_SIZE) == 0)
-                                       transflag = SMK_INODE_TRANSMUTE;
+                                       TRANS_TRUE, TRANS_TRUE_SIZE,
+                                       0);
+                       } else {
+                               rc = inode->i_op->getxattr(dp,
+                                       XATTR_NAME_SMACKTRANSMUTE, trattr,
+                                       TRANS_TRUE_SIZE);
+                               if (rc >= 0 && strncmp(trattr, TRANS_TRUE,
+                                                      TRANS_TRUE_SIZE) != 0)
+                                       rc = -EINVAL;
                        }
+                       if (rc >= 0)
+                               transflag = SMK_INODE_TRANSMUTE;
                }
                isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
                isp->smk_mmap = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp);
@@ -2758,7 +2730,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
        if (!capable(CAP_MAC_ADMIN))
                return -EPERM;
 
-       if (value == NULL || size == 0 || size >= SMK_LABELLEN)
+       if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
                return -EINVAL;
 
        if (strcmp(name, "current") != 0)
@@ -2809,11 +2781,14 @@ static int smack_unix_stream_connect(struct sock *sock,
        struct socket_smack *osp = other->sk_security;
        struct socket_smack *nsp = newsk->sk_security;
        struct smk_audit_info ad;
-       struct lsm_network_audit net;
        int rc = 0;
 
+#ifdef CONFIG_AUDIT
+       struct lsm_network_audit net;
+
        smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
        smk_ad_setfield_u_net_sk(&ad, other);
+#endif
 
        if (!capable(CAP_MAC_OVERRIDE))
                rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
@@ -2842,11 +2817,14 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
        struct socket_smack *ssp = sock->sk->sk_security;
        struct socket_smack *osp = other->sk->sk_security;
        struct smk_audit_info ad;
-       struct lsm_network_audit net;
        int rc = 0;
 
+#ifdef CONFIG_AUDIT
+       struct lsm_network_audit net;
+
        smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
        smk_ad_setfield_u_net_sk(&ad, other->sk);
+#endif
 
        if (!capable(CAP_MAC_OVERRIDE))
                rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
@@ -2888,10 +2866,9 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
 static char *smack_from_secattr(struct netlbl_lsm_secattr *sap,
                                struct socket_smack *ssp)
 {
-       struct smack_known *skp;
-       char smack[SMK_LABELLEN];
+       struct smack_known *kp;
        char *sp;
-       int pcat;
+       int found = 0;
 
        if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) {
                /*
@@ -2899,59 +2876,27 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap,
                 * If there are flags but no level netlabel isn't
                 * behaving the way we expect it to.
                 *
-                * Get the categories, if any
+                * Look it up in the label table
                 * Without guidance regarding the smack value
                 * for the packet fall back on the network
                 * ambient value.
                 */
-               memset(smack, '\0', SMK_LABELLEN);
-               if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0)
-                       for (pcat = -1;;) {
-                               pcat = netlbl_secattr_catmap_walk(
-                                       sap->attr.mls.cat, pcat + 1);
-                               if (pcat < 0)
-                                       break;
-                               smack_catset_bit(pcat, smack);
-                       }
-               /*
-                * If it is CIPSO using smack direct mapping
-                * we are already done. WeeHee.
-                */
-               if (sap->attr.mls.lvl == smack_cipso_direct) {
-                       /*
-                        * The label sent is usually on the label list.
-                        *
-                        * If it is not we may still want to allow the
-                        * delivery.
-                        *
-                        * If the recipient is accepting all packets
-                        * because it is using the star ("*") label
-                        * for SMACK64IPIN provide the web ("@") label
-                        * so that a directed response will succeed.
-                        * This is not very correct from a MAC point
-                        * of view, but gets around the problem that
-                        * locking prevents adding the newly discovered
-                        * label to the list.
-                        * The case where the recipient is not using
-                        * the star label should obviously fail.
-                        * The easy way to do this is to provide the
-                        * star label as the subject label.
-                        */
-                       skp = smk_find_entry(smack);
-                       if (skp != NULL)
-                               return skp->smk_known;
-                       if (ssp != NULL &&
-                           ssp->smk_in == smack_known_star.smk_known)
-                               return smack_known_web.smk_known;
-                       return smack_known_star.smk_known;
+               rcu_read_lock();
+               list_for_each_entry(kp, &smack_known_list, list) {
+                       if (sap->attr.mls.lvl != kp->smk_netlabel.attr.mls.lvl)
+                               continue;
+                       if (memcmp(sap->attr.mls.cat,
+                               kp->smk_netlabel.attr.mls.cat,
+                               SMK_CIPSOLEN) != 0)
+                               continue;
+                       found = 1;
+                       break;
                }
-               /*
-                * Look it up in the supplied table if it is not
-                * a direct mapping.
-                */
-               sp = smack_from_cipso(sap->attr.mls.lvl, smack);
-               if (sp != NULL)
-                       return sp;
+               rcu_read_unlock();
+
+               if (found)
+                       return kp->smk_known;
+
                if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known)
                        return smack_known_web.smk_known;
                return smack_known_star.smk_known;
@@ -2993,7 +2938,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        char *csp;
        int rc;
        struct smk_audit_info ad;
+#ifdef CONFIG_AUDIT
        struct lsm_network_audit net;
+#endif
        if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
                return 0;
 
@@ -3149,14 +3096,18 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
                                   struct request_sock *req)
 {
        u16 family = sk->sk_family;
+       struct smack_known *skp;
        struct socket_smack *ssp = sk->sk_security;
        struct netlbl_lsm_secattr secattr;
        struct sockaddr_in addr;
        struct iphdr *hdr;
        char *sp;
+       char *hsp;
        int rc;
        struct smk_audit_info ad;
+#ifdef CONFIG_AUDIT
        struct lsm_network_audit net;
+#endif
 
        /* handle mapped IPv4 packets arriving via IPv6 sockets */
        if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
@@ -3198,16 +3149,14 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
        hdr = ip_hdr(skb);
        addr.sin_addr.s_addr = hdr->saddr;
        rcu_read_lock();
-       if (smack_host_label(&addr) == NULL) {
-               rcu_read_unlock();
-               netlbl_secattr_init(&secattr);
-               smack_to_secattr(sp, &secattr);
-               rc = netlbl_req_setattr(req, &secattr);
-               netlbl_secattr_destroy(&secattr);
-       } else {
-               rcu_read_unlock();
+       hsp = smack_host_label(&addr);
+       rcu_read_unlock();
+
+       if (hsp == NULL) {
+               skp = smk_find_entry(sp);
+               rc = netlbl_req_setattr(req, &skp->smk_netlabel);
+       } else
                netlbl_req_delattr(req);
-       }
 
        return rc;
 }
@@ -3389,7 +3338,7 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule,
        char *rule = vrule;
 
        if (!rule) {
-               audit_log(actx, GFP_KERNEL, AUDIT_SELINUX_ERR,
+               audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
                          "Smack: missing rule\n");
                return -ENOENT;
        }
@@ -3629,8 +3578,29 @@ struct security_operations smack_ops = {
 };
 
 
-static __init void init_smack_know_list(void)
+static __init void init_smack_known_list(void)
 {
+       /*
+        * Initialize rule list locks
+        */
+       mutex_init(&smack_known_huh.smk_rules_lock);
+       mutex_init(&smack_known_hat.smk_rules_lock);
+       mutex_init(&smack_known_floor.smk_rules_lock);
+       mutex_init(&smack_known_star.smk_rules_lock);
+       mutex_init(&smack_known_invalid.smk_rules_lock);
+       mutex_init(&smack_known_web.smk_rules_lock);
+       /*
+        * Initialize rule lists
+        */
+       INIT_LIST_HEAD(&smack_known_huh.smk_rules);
+       INIT_LIST_HEAD(&smack_known_hat.smk_rules);
+       INIT_LIST_HEAD(&smack_known_star.smk_rules);
+       INIT_LIST_HEAD(&smack_known_floor.smk_rules);
+       INIT_LIST_HEAD(&smack_known_invalid.smk_rules);
+       INIT_LIST_HEAD(&smack_known_web.smk_rules);
+       /*
+        * Create the known labels list
+        */
        list_add(&smack_known_huh.list, &smack_known_list);
        list_add(&smack_known_hat.list, &smack_known_list);
        list_add(&smack_known_star.list, &smack_known_list);
@@ -3665,16 +3635,8 @@ static __init int smack_init(void)
        cred = (struct cred *) current->cred;
        cred->security = tsp;
 
-       /* initialize the smack_know_list */
-       init_smack_know_list();
-       /*
-        * Initialize locks
-        */
-       spin_lock_init(&smack_known_huh.smk_cipsolock);
-       spin_lock_init(&smack_known_hat.smk_cipsolock);
-       spin_lock_init(&smack_known_star.smk_cipsolock);
-       spin_lock_init(&smack_known_floor.smk_cipsolock);
-       spin_lock_init(&smack_known_invalid.smk_cipsolock);
+       /* initialize the smack_known_list */
+       init_smack_known_list();
 
        /*
         * Register with LSM