Smack: secmark support for netfilter
authorCasey Schaufler <casey@schaufler-ca.com>
Sat, 13 Dec 2014 01:08:40 +0000 (17:08 -0800)
committerCasey Schaufler <casey@schaufler-ca.com>
Wed, 21 Jan 2015 00:34:25 +0000 (16:34 -0800)
Smack uses CIPSO to label internet packets and thus provide
for access control on delivery of packets. The netfilter facility
was not used to allow for Smack to work properly without netfilter
configuration. Smack does not need netfilter, however there are
cases where it would be handy.

As a side effect, the labeling of local IPv4 packets can be optimized
and the handling of local IPv6 packets is just all out better.

The best part is that the netfilter tools use "contexts" that
are just strings, and they work just as well for Smack as they
do for SELinux.

All of the conditional compilation for IPv6 was implemented
by Rafal Krypa <r.krypa@samsung.com>

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
security/smack/Kconfig
security/smack/Makefile
security/smack/smack.h
security/smack/smack_lsm.c
security/smack/smack_netfilter.c [new file with mode: 0644]

index b065f97894185557c88fe77c554719a3deca955d..271adae81796c2825797b3941f3bf9fd3481a86b 100644 (file)
@@ -28,3 +28,15 @@ config SECURITY_SMACK_BRINGUP
          access rule set once the behavior is well understood.
          This is a superior mechanism to the oft abused
          "permissive" mode of other systems.
+         If you are unsure how to answer this question, answer N.
+
+config SECURITY_SMACK_NETFILTER
+       bool "Packet marking using secmarks for netfilter"
+       depends on SECURITY_SMACK
+       depends on NETWORK_SECMARK
+       depends on NETFILTER
+       default n
+       help
+         This enables security marking of network packets using
+         Smack labels.
+         If you are unsure how to answer this question, answer N.
index 67a63aaec827bedbb692ec61c44782d5c51f6f3e..616cf93b368ed7569e79f6643c6713c4c8627a6b 100644 (file)
@@ -5,3 +5,4 @@
 obj-$(CONFIG_SECURITY_SMACK) := smack.o
 
 smack-y := smack_lsm.o smack_access.o smackfs.o
+smack-$(CONFIG_NETFILTER) += smack_netfilter.o
index b828a379377c9cd1c2dee54bc2ddab5b3bde920a..7629eaeb1fb2813c1f82d65e08712d60fa25334f 100644 (file)
@@ -248,6 +248,7 @@ struct smack_known *smk_find_entry(const char *);
 /*
  * Shared data.
  */
+extern int smack_enabled;
 extern int smack_cipso_direct;
 extern int smack_cipso_mapped;
 extern struct smack_known *smack_net_ambient;
index 1fa72317bbecffe2f0593fa3fe51e93f23165fcf..81b30e32c52674659a787d59e5a4b29552ada839 100644 (file)
 #define SMK_RECEIVING  1
 #define SMK_SENDING    2
 
+#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
 LIST_HEAD(smk_ipv6_port_list);
+#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
 static struct kmem_cache *smack_inode_cache;
+int smack_enabled;
 
 #ifdef CONFIG_SECURITY_SMACK_BRINGUP
 static void smk_bu_mode(int mode, char *s)
@@ -2213,6 +2216,7 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
        return smack_netlabel(sk, sk_lbl);
 }
 
+#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
 /**
  * smk_ipv6_port_label - Smack port access table management
  * @sock: socket
@@ -2362,6 +2366,7 @@ auditout:
        rc = smk_bu_note("IPv6 port check", skp, object, MAY_WRITE, rc);
        return rc;
 }
+#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
 
 /**
  * smack_inode_setsecurity - set smack xattrs
@@ -2422,8 +2427,10 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
        } else
                return -EOPNOTSUPP;
 
+#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
        if (sock->sk->sk_family == PF_INET6)
                smk_ipv6_port_label(sock, NULL);
+#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
 
        return 0;
 }
@@ -2451,6 +2458,7 @@ static int smack_socket_post_create(struct socket *sock, int family,
        return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
 }
 
+#ifndef CONFIG_SECURITY_SMACK_NETFILTER
 /**
  * smack_socket_bind - record port binding information.
  * @sock: the socket
@@ -2464,11 +2472,14 @@ static int smack_socket_post_create(struct socket *sock, int family,
 static int smack_socket_bind(struct socket *sock, struct sockaddr *address,
                                int addrlen)
 {
+#if IS_ENABLED(CONFIG_IPV6)
        if (sock->sk != NULL && sock->sk->sk_family == PF_INET6)
                smk_ipv6_port_label(sock, address);
+#endif
 
        return 0;
 }
+#endif /* !CONFIG_SECURITY_SMACK_NETFILTER */
 
 /**
  * smack_socket_connect - connect access check
@@ -2497,8 +2508,10 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
        case PF_INET6:
                if (addrlen < sizeof(struct sockaddr_in6))
                        return -EINVAL;
+#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
                rc = smk_ipv6_port_check(sock->sk, (struct sockaddr_in6 *)sap,
                                                SMK_CONNECTING);
+#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
                break;
        }
        return rc;
@@ -3381,7 +3394,9 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
                                int size)
 {
        struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name;
+#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
        struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name;
+#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
        int rc = 0;
 
        /*
@@ -3395,7 +3410,9 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
                rc = smack_netlabel_send(sock->sk, sip);
                break;
        case AF_INET6:
+#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
                rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING);
+#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
                break;
        }
        return rc;
@@ -3486,6 +3503,7 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
        return smack_net_ambient;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
 static int smk_skb_to_addr_ipv6(struct sk_buff *skb, struct sockaddr_in6 *sip)
 {
        u8 nexthdr;
@@ -3532,6 +3550,7 @@ static int smk_skb_to_addr_ipv6(struct sk_buff *skb, struct sockaddr_in6 *sip)
        }
        return proto;
 }
+#endif /* CONFIG_IPV6 */
 
 /**
  * smack_socket_sock_rcv_skb - Smack packet delivery access check
@@ -3544,15 +3563,30 @@ 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;
-       struct sockaddr_in6 sadd;
+       struct smack_known *skp = NULL;
        int rc = 0;
        struct smk_audit_info ad;
 #ifdef CONFIG_AUDIT
        struct lsm_network_audit net;
 #endif
+#if IS_ENABLED(CONFIG_IPV6)
+       struct sockaddr_in6 sadd;
+       int proto;
+#endif /* CONFIG_IPV6 */
+
        switch (sk->sk_family) {
        case PF_INET:
+#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+               /*
+                * If there is a secmark use it rather than the CIPSO label.
+                * If there is no secmark fall back to CIPSO.
+                * The secmark is assumed to reflect policy better.
+                */
+               if (skb && skb->secmark != 0) {
+                       skp = smack_from_secid(skb->secmark);
+                       goto access_check;
+               }
+#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
                /*
                 * Translate what netlabel gave us.
                 */
@@ -3566,6 +3600,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 
                netlbl_secattr_destroy(&secattr);
 
+#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+access_check:
+#endif
 #ifdef CONFIG_AUDIT
                smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
                ad.a.u.net->family = sk->sk_family;
@@ -3584,14 +3621,32 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                if (rc != 0)
                        netlbl_skbuff_err(skb, rc, 0);
                break;
+#if IS_ENABLED(CONFIG_IPV6)
        case PF_INET6:
-               rc = smk_skb_to_addr_ipv6(skb, &sadd);
-               if (rc == IPPROTO_UDP || rc == IPPROTO_TCP)
-                       rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING);
+               proto = smk_skb_to_addr_ipv6(skb, &sadd);
+               if (proto != IPPROTO_UDP && proto != IPPROTO_TCP)
+                       break;
+#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+               if (skb && skb->secmark != 0)
+                       skp = smack_from_secid(skb->secmark);
                else
-                       rc = 0;
+                       skp = smack_net_ambient;
+#ifdef CONFIG_AUDIT
+               smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
+               ad.a.u.net->family = sk->sk_family;
+               ad.a.u.net->netif = skb->skb_iif;
+               ipv6_skb_to_auditdata(skb, &ad.a, NULL);
+#endif /* CONFIG_AUDIT */
+               rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
+               rc = smk_bu_note("IPv6 delivery", skp, ssp->smk_in,
+                                       MAY_WRITE, rc);
+#else /* CONFIG_SECURITY_SMACK_NETFILTER */
+               rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING);
+#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
                break;
+#endif /* CONFIG_IPV6 */
        }
+
        return rc;
 }
 
@@ -3653,16 +3708,25 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
        if (skb != NULL) {
                if (skb->protocol == htons(ETH_P_IP))
                        family = PF_INET;
+#if IS_ENABLED(CONFIG_IPV6)
                else if (skb->protocol == htons(ETH_P_IPV6))
                        family = PF_INET6;
+#endif /* CONFIG_IPV6 */
        }
        if (family == PF_UNSPEC && sock != NULL)
                family = sock->sk->sk_family;
 
-       if (family == PF_UNIX) {
+       switch (family) {
+       case PF_UNIX:
                ssp = sock->sk->sk_security;
                s = ssp->smk_out->smk_secid;
-       } else if (family == PF_INET || family == PF_INET6) {
+               break;
+       case PF_INET:
+#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+               s = skb->secmark;
+               if (s != 0)
+                       break;
+#endif
                /*
                 * Translate what netlabel gave us.
                 */
@@ -3675,6 +3739,14 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
                        s = skp->smk_secid;
                }
                netlbl_secattr_destroy(&secattr);
+               break;
+#if IS_ENABLED(CONFIG_IPV6)
+       case PF_INET6:
+#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+               s = skb->secmark;
+#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
+               break;
+#endif /* CONFIG_IPV6 */
        }
        *secid = s;
        if (s == 0)
@@ -3730,6 +3802,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
        struct lsm_network_audit net;
 #endif
 
+#if IS_ENABLED(CONFIG_IPV6)
        if (family == PF_INET6) {
                /*
                 * Handle mapped IPv4 packets arriving
@@ -3741,6 +3814,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
                else
                        return 0;
        }
+#endif /* CONFIG_IPV6 */
 
        netlbl_secattr_init(&secattr);
        rc = netlbl_skbuff_getattr(skb, family, &secattr);
@@ -4199,7 +4273,9 @@ struct security_operations smack_ops = {
        .unix_may_send =                smack_unix_may_send,
 
        .socket_post_create =           smack_socket_post_create,
+#ifndef CONFIG_SECURITY_SMACK_NETFILTER
        .socket_bind =                  smack_socket_bind,
+#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
        .socket_connect =               smack_socket_connect,
        .socket_sendmsg =               smack_socket_sendmsg,
        .socket_sock_rcv_skb =          smack_socket_sock_rcv_skb,
@@ -4280,6 +4356,8 @@ static __init int smack_init(void)
        if (!security_module_enable(&smack_ops))
                return 0;
 
+       smack_enabled = 1;
+
        smack_inode_cache = KMEM_CACHE(inode_smack, 0);
        if (!smack_inode_cache)
                return -ENOMEM;
diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c
new file mode 100644 (file)
index 0000000..c952632
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  Simplified MAC Kernel (smack) security module
+ *
+ *  This file contains the Smack netfilter implementation
+ *
+ *  Author:
+ *     Casey Schaufler <casey@schaufler-ca.com>
+ *
+ *  Copyright (C) 2014 Casey Schaufler <casey@schaufler-ca.com>
+ *  Copyright (C) 2014 Intel Corporation.
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License version 2,
+ *     as published by the Free Software Foundation.
+ */
+
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/netdevice.h>
+#include "smack.h"
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+static unsigned int smack_ipv6_output(const struct nf_hook_ops *ops,
+                                       struct sk_buff *skb,
+                                       const struct net_device *in,
+                                       const struct net_device *out,
+                                       int (*okfn)(struct sk_buff *))
+{
+       struct socket_smack *ssp;
+       struct smack_known *skp;
+
+       if (skb && skb->sk && skb->sk->sk_security) {
+               ssp = skb->sk->sk_security;
+               skp = ssp->smk_out;
+               skb->secmark = skp->smk_secid;
+       }
+
+       return NF_ACCEPT;
+}
+#endif /* IPV6 */
+
+static unsigned int smack_ipv4_output(const struct nf_hook_ops *ops,
+                                       struct sk_buff *skb,
+                                       const struct net_device *in,
+                                       const struct net_device *out,
+                                       int (*okfn)(struct sk_buff *))
+{
+       struct socket_smack *ssp;
+       struct smack_known *skp;
+
+       if (skb && skb->sk && skb->sk->sk_security) {
+               ssp = skb->sk->sk_security;
+               skp = ssp->smk_out;
+               skb->secmark = skp->smk_secid;
+       }
+
+       return NF_ACCEPT;
+}
+
+static struct nf_hook_ops smack_nf_ops[] = {
+       {
+               .hook =         smack_ipv4_output,
+               .owner =        THIS_MODULE,
+               .pf =           NFPROTO_IPV4,
+               .hooknum =      NF_INET_LOCAL_OUT,
+               .priority =     NF_IP_PRI_SELINUX_FIRST,
+       },
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       {
+               .hook =         smack_ipv6_output,
+               .owner =        THIS_MODULE,
+               .pf =           NFPROTO_IPV6,
+               .hooknum =      NF_INET_LOCAL_OUT,
+               .priority =     NF_IP6_PRI_SELINUX_FIRST,
+       },
+#endif /* IPV6 */
+};
+
+static int __init smack_nf_ip_init(void)
+{
+       int err;
+
+       if (smack_enabled == 0)
+               return 0;
+
+       printk(KERN_DEBUG "Smack: Registering netfilter hooks\n");
+
+       err = nf_register_hooks(smack_nf_ops, ARRAY_SIZE(smack_nf_ops));
+       if (err)
+               pr_info("Smack: nf_register_hooks: error %d\n", err);
+
+       return 0;
+}
+
+__initcall(smack_nf_ip_init);