Smack: Set socket labels only once
authorCasey Schaufler <casey@schaufler-ca.com>
Wed, 12 Aug 2020 00:39:42 +0000 (17:39 -0700)
committerCasey Schaufler <cschaufler@localhost.localdomain>
Fri, 11 Sep 2020 22:31:30 +0000 (15:31 -0700)
Refactor the IP send checks so that the netlabel value
is set only when necessary, not on every send. Some functions
get renamed as the changes made the old name misleading.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
security/smack/smack.h
security/smack/smack_lsm.c

index e9e817d..c5d745a 100644 (file)
@@ -100,7 +100,12 @@ struct socket_smack {
        struct smack_known      *smk_out;       /* outbound label */
        struct smack_known      *smk_in;        /* inbound label */
        struct smack_known      *smk_packet;    /* TCP peer label */
+       int                     smk_state;      /* netlabel socket states */
 };
+#define        SMK_NETLBL_UNSET        0
+#define        SMK_NETLBL_UNLABELED    1
+#define        SMK_NETLBL_LABELED      2
+#define        SMK_NETLBL_REQSKB       3
 
 /*
  * Inode smack data
@@ -197,19 +202,6 @@ enum {
 #define SMACK_CIPSO_OPTION     "-CIPSO"
 
 /*
- * How communications on this socket are treated.
- * Usually it's determined by the underlying netlabel code
- * but there are certain cases, including single label hosts
- * and potentially single label interfaces for which the
- * treatment can not be known in advance.
- *
- * The possibility of additional labeling schemes being
- * introduced in the future exists as well.
- */
-#define SMACK_UNLABELED_SOCKET 0
-#define SMACK_CIPSO_SOCKET     1
-
-/*
  * CIPSO defaults.
  */
 #define SMACK_CIPSO_DOI_DEFAULT                3       /* Historical */
index 3402ac4..7a79ddb 100644 (file)
@@ -2383,38 +2383,31 @@ static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
 }
 
 /**
- * smack_netlabel - Set the secattr on a socket
+ * smack_netlbl_add - Set the secattr on a socket
  * @sk: the socket
- * @labeled: socket label scheme
  *
- * Convert the outbound smack value (smk_out) to a
- * secattr and attach it to the socket.
+ * Attach the outbound smack value (smk_out) to the socket.
  *
  * Returns 0 on success or an error code
  */
-static int smack_netlabel(struct sock *sk, int labeled)
+static int smack_netlbl_add(struct sock *sk)
 {
-       struct smack_known *skp;
        struct socket_smack *ssp = sk->sk_security;
-       int rc = 0;
+       struct smack_known *skp = ssp->smk_out;
+       int rc;
 
-       /*
-        * Usually the netlabel code will handle changing the
-        * packet labeling based on the label.
-        * The case of a single label host is different, because
-        * a single label host should never get a labeled packet
-        * even though the label is usually associated with a packet
-        * label.
-        */
        local_bh_disable();
        bh_lock_sock_nested(sk);
 
-       if (ssp->smk_out == smack_net_ambient ||
-           labeled == SMACK_UNLABELED_SOCKET)
-               netlbl_sock_delattr(sk);
-       else {
-               skp = ssp->smk_out;
-               rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel);
+       rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel);
+       switch (rc) {
+       case 0:
+               ssp->smk_state = SMK_NETLBL_LABELED;
+               break;
+       case -EDESTADDRREQ:
+               ssp->smk_state = SMK_NETLBL_REQSKB;
+               rc = 0;
+               break;
        }
 
        bh_unlock_sock(sk);
@@ -2424,7 +2417,31 @@ static int smack_netlabel(struct sock *sk, int labeled)
 }
 
 /**
- * smack_netlbel_send - Set the secattr on a socket and perform access checks
+ * smack_netlbl_delete - Remove the secattr from a socket
+ * @sk: the socket
+ *
+ * Remove the outbound smack value from a socket
+ */
+static void smack_netlbl_delete(struct sock *sk)
+{
+       struct socket_smack *ssp = sk->sk_security;
+
+       /*
+        * Take the label off the socket if one is set.
+        */
+       if (ssp->smk_state != SMK_NETLBL_LABELED)
+               return;
+
+       local_bh_disable();
+       bh_lock_sock_nested(sk);
+       netlbl_sock_delattr(sk);
+       bh_unlock_sock(sk);
+       local_bh_enable();
+       ssp->smk_state = SMK_NETLBL_UNLABELED;
+}
+
+/**
+ * smk_ipv4_check - Perform IPv4 host access checks
  * @sk: the socket
  * @sap: the destination address
  *
@@ -2434,11 +2451,10 @@ static int smack_netlabel(struct sock *sk, int labeled)
  * Returns 0 on success or an error code.
  *
  */
-static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
+static int smk_ipv4_check(struct sock *sk, struct sockaddr_in *sap)
 {
        struct smack_known *skp;
-       int rc;
-       int sk_lbl;
+       int rc = 0;
        struct smack_known *hkp;
        struct socket_smack *ssp = sk->sk_security;
        struct smk_audit_info ad;
@@ -2454,19 +2470,18 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
                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;
                skp = ssp->smk_out;
                rc = smk_access(skp, hkp, MAY_WRITE, &ad);
                rc = smk_bu_note("IPv4 host check", skp, hkp, MAY_WRITE, rc);
-       } else {
-               sk_lbl = SMACK_CIPSO_SOCKET;
-               rc = 0;
+               /*
+                * Clear the socket netlabel if it's set.
+                */
+               if (!rc)
+                       smack_netlbl_delete(sk);
        }
        rcu_read_unlock();
-       if (rc != 0)
-               return rc;
 
-       return smack_netlabel(sk, sk_lbl);
+       return rc;
 }
 
 /**
@@ -2703,7 +2718,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
        else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
                ssp->smk_out = skp;
                if (sock->sk->sk_family == PF_INET) {
-                       rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
+                       rc = smack_netlbl_add(sock->sk);
                        if (rc != 0)
                                printk(KERN_WARNING
                                        "Smack: \"%s\" netlbl error %d.\n",
@@ -2754,7 +2769,7 @@ static int smack_socket_post_create(struct socket *sock, int family,
        /*
         * Set the outbound netlbl.
         */
-       return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
+       return smack_netlbl_add(sock->sk);
 }
 
 /**
@@ -2845,7 +2860,7 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
        }
        if (sap->sa_family != AF_INET || addrlen < sizeof(struct sockaddr_in))
                return 0;
-       rc = smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap);
+       rc = smk_ipv4_check(sock->sk, (struct sockaddr_in *)sap);
        return rc;
 }
 
@@ -3663,7 +3678,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
                if (msg->msg_namelen < sizeof(struct sockaddr_in) ||
                    sip->sin_family != AF_INET)
                        return -EINVAL;
-               rc = smack_netlabel_send(sock->sk, sip);
+               rc = smk_ipv4_check(sock->sk, sip);
                break;
 #if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6:
@@ -3825,6 +3840,33 @@ static struct smack_known *smack_from_skb(struct sk_buff *skb)
 }
 
 /**
+ * smack_from_netlbl - Smack data from the IP options in an skb
+ * @sk: socket data came in on
+ * @family: address family
+ * @skb: packet
+ *
+ * Returns smack_known of the IP options or NULL if that won't work.
+ */
+static struct smack_known *smack_from_netlbl(struct sock *sk, u16 family,
+                                            struct sk_buff *skb)
+{
+       struct netlbl_lsm_secattr secattr;
+       struct socket_smack *ssp = NULL;
+       struct smack_known *skp = NULL;
+
+       netlbl_secattr_init(&secattr);
+
+       if (sk)
+               ssp = sk->sk_security;
+       if (netlbl_skbuff_getattr(skb, family, &secattr) == 0)
+               skp = smack_from_secattr(&secattr, ssp);
+
+       netlbl_secattr_destroy(&secattr);
+
+       return skp;
+}
+
+/**
  * smack_socket_sock_rcv_skb - Smack packet delivery access check
  * @sk: socket
  * @skb: packet
@@ -3833,7 +3875,6 @@ static struct smack_known *smack_from_skb(struct sk_buff *skb)
  */
 static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
-       struct netlbl_lsm_secattr secattr;
        struct socket_smack *ssp = sk->sk_security;
        struct smack_known *skp = NULL;
        int rc = 0;
@@ -3858,22 +3899,11 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                 * The secmark is assumed to reflect policy better.
                 */
                skp = smack_from_skb(skb);
-               if (skp)
-                       goto access_check;
-               /*
-                * Translate what netlabel gave us.
-                */
-               netlbl_secattr_init(&secattr);
-
-               rc = netlbl_skbuff_getattr(skb, family, &secattr);
-               if (rc == 0)
-                       skp = smack_from_secattr(&secattr, ssp);
-               else
-                       skp = smack_net_ambient;
-
-               netlbl_secattr_destroy(&secattr);
-
-access_check:
+               if (skp == NULL) {
+                       skp = smack_from_netlbl(sk, family, skb);
+                       if (skp == NULL)
+                               skp = smack_net_ambient;
+               }
 
 #ifdef CONFIG_AUDIT
                smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
@@ -3979,12 +4009,11 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
                                         struct sk_buff *skb, u32 *secid)
 
 {
-       struct netlbl_lsm_secattr secattr;
        struct socket_smack *ssp = NULL;
        struct smack_known *skp;
+       struct sock *sk = NULL;
        int family = PF_UNSPEC;
        u32 s = 0;      /* 0 is the invalid secid */
-       int rc;
 
        if (skb != NULL) {
                if (skb->protocol == htons(ETH_P_IP))
@@ -4011,15 +4040,11 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
                /*
                 * Translate what netlabel gave us.
                 */
-               if (sock != NULL && sock->sk != NULL)
-                       ssp = sock->sk->sk_security;
-               netlbl_secattr_init(&secattr);
-               rc = netlbl_skbuff_getattr(skb, family, &secattr);
-               if (rc == 0) {
-                       skp = smack_from_secattr(&secattr, ssp);
+               if (sock != NULL)
+                       sk = sock->sk;
+               skp = smack_from_netlbl(sk, family, skb);
+               if (skp != NULL)
                        s = skp->smk_secid;
-               }
-               netlbl_secattr_destroy(&secattr);
                break;
        case PF_INET6:
 #ifdef SMACK_IPV6_SECMARK_LABELING
@@ -4073,7 +4098,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
        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;
        struct smack_known *hskp;
@@ -4103,18 +4127,11 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
         * The secmark is assumed to reflect policy better.
         */
        skp = smack_from_skb(skb);
-       if (skp)
-               goto access_check;
-
-       netlbl_secattr_init(&secattr);
-       rc = netlbl_skbuff_getattr(skb, family, &secattr);
-       if (rc == 0)
-               skp = smack_from_secattr(&secattr, ssp);
-       else
-               skp = &smack_known_huh;
-       netlbl_secattr_destroy(&secattr);
-
-access_check:
+       if (skp == NULL) {
+               skp = smack_from_netlbl(sk, family, skb);
+               if (skp == NULL)
+                       skp = &smack_known_huh;
+       }
 
 #ifdef CONFIG_AUDIT
        smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);