Commit 5e752b7e authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] selinux: add IPv6 support

From: James Morris <jmorris@redhat.com>

The patch below adds explicit IPv6 support to SELinux.

Brief description of changes:

o IPv6 networking is now subject to the same controls as IPv4 (in
  addition to the generic socket permissions which cover all protocols),
  namely: bind to local node address; bind to local port; send & receive
  TCP/UDP and raw IP packets based on local network interface and remote
  node address.

o Packet parsing has been extended to IPv6 packets for logging and
  control, and simplified for IPv4.

o Support for logging of IPv6 addresses has also been added.

o The kernel policy database code has been modified to support IPv6, and
  reworked to provide generic security policy version handling so that
  older policy versions will still work, making upgrading simpler.

Corresponding userspace patches are available at
<http://people.redhat.com/jmorris/selinux/ipv6/>, although current
userspace tools will continue to function normally (but without explicit
IPv6 support).

For more details at the security management level, see
<http://marc.theaimsgroup.com/?l=selinux&m=108068187630948&w=2>

This code has been under testing and review for several weeks.
parent bcf506bd
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include <linux/un.h> #include <linux/un.h>
#include <net/af_unix.h> #include <net/af_unix.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h>
#include <net/ipv6.h>
#include "avc.h" #include "avc.h"
#include "avc_ss.h" #include "avc_ss.h"
#include "class_to_string.h" #include "class_to_string.h"
...@@ -418,6 +420,16 @@ int avc_insert(u32 ssid, u32 tsid, u16 tclass, ...@@ -418,6 +420,16 @@ int avc_insert(u32 ssid, u32 tsid, u16 tclass,
return rc; return rc;
} }
static inline void avc_print_ipv6_addr(struct in6_addr *addr, u16 port,
char *name1, char *name2)
{
if (!ipv6_addr_any(addr))
printk(" %s=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
name1, NIP6(*addr));
if (port)
printk(" %s=%d", name2, ntohs(port));
}
static inline void avc_print_ipv4_addr(u32 addr, u16 port, char *name1, char *name2) static inline void avc_print_ipv4_addr(u32 addr, u16 port, char *name1, char *name2)
{ {
if (addr) if (addr)
...@@ -602,11 +614,11 @@ void avc_audit(u32 ssid, u32 tsid, ...@@ -602,11 +614,11 @@ void avc_audit(u32 ssid, u32 tsid,
if (a->u.net.sk) { if (a->u.net.sk) {
struct sock *sk = a->u.net.sk; struct sock *sk = a->u.net.sk;
struct unix_sock *u; struct unix_sock *u;
struct inet_opt *inet;
switch (sk->sk_family) { switch (sk->sk_family) {
case AF_INET: case AF_INET: {
inet = inet_sk(sk); struct inet_opt *inet = inet_sk(sk);
avc_print_ipv4_addr(inet->rcv_saddr, avc_print_ipv4_addr(inet->rcv_saddr,
inet->sport, inet->sport,
"laddr", "lport"); "laddr", "lport");
...@@ -614,6 +626,19 @@ void avc_audit(u32 ssid, u32 tsid, ...@@ -614,6 +626,19 @@ void avc_audit(u32 ssid, u32 tsid,
inet->dport, inet->dport,
"faddr", "fport"); "faddr", "fport");
break; break;
}
case AF_INET6: {
struct inet_opt *inet = inet_sk(sk);
struct ipv6_pinfo *inet6 = inet6_sk(sk);
avc_print_ipv6_addr(&inet6->rcv_saddr,
inet->sport,
"laddr", "lport");
avc_print_ipv6_addr(&inet6->daddr,
inet->dport,
"faddr", "fport");
break;
}
case AF_UNIX: case AF_UNIX:
u = unix_sk(sk); u = unix_sk(sk);
if (u->dentry) { if (u->dentry) {
...@@ -639,11 +664,24 @@ void avc_audit(u32 ssid, u32 tsid, ...@@ -639,11 +664,24 @@ void avc_audit(u32 ssid, u32 tsid,
} }
} }
avc_print_ipv4_addr(a->u.net.saddr, a->u.net.sport, switch (a->u.net.family) {
case AF_INET:
avc_print_ipv4_addr(a->u.net.v4info.saddr,
a->u.net.sport,
"saddr", "src"); "saddr", "src");
avc_print_ipv4_addr(a->u.net.daddr, a->u.net.dport, avc_print_ipv4_addr(a->u.net.v4info.daddr,
a->u.net.dport,
"daddr", "dest"); "daddr", "dest");
break;
case AF_INET6:
avc_print_ipv6_addr(&a->u.net.v6info.saddr,
a->u.net.sport,
"saddr", "src");
avc_print_ipv6_addr(&a->u.net.v6info.daddr,
a->u.net.dport,
"daddr", "dest");
break;
}
if (a->u.net.netif) if (a->u.net.netif)
printk(" netif=%s", a->u.net.netif); printk(" netif=%s", a->u.net.netif);
break; break;
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/kd.h> #include <linux/kd.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <net/icmp.h> #include <net/icmp.h>
#include <net/ip.h> /* for sysctl_local_port_range[] */ #include <net/ip.h> /* for sysctl_local_port_range[] */
#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
...@@ -59,6 +60,7 @@ ...@@ -59,6 +60,7 @@
#include <net/af_unix.h> /* for Unix socket types */ #include <net/af_unix.h> /* for Unix socket types */
#include <linux/parser.h> #include <linux/parser.h>
#include <linux/nfs_mount.h> #include <linux/nfs_mount.h>
#include <net/ipv6.h>
#include <linux/hugetlb.h> #include <linux/hugetlb.h>
#include "avc.h" #include "avc.h"
...@@ -2647,35 +2649,32 @@ static void selinux_task_to_inode(struct task_struct *p, ...@@ -2647,35 +2649,32 @@ static void selinux_task_to_inode(struct task_struct *p,
#ifdef CONFIG_SECURITY_NETWORK #ifdef CONFIG_SECURITY_NETWORK
static void selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad) /* Returns error only if unable to parse addresses */
static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad)
{ {
int dlen, ihlen; int offset, ihlen, ret;
struct iphdr *iph; struct iphdr iph;
if (skb->len < sizeof(struct iphdr)) offset = skb->nh.raw - skb->data;
ret = skb_copy_bits(skb, offset, &iph, sizeof(iph));
if (ret)
goto out; goto out;
iph = skb->nh.iph; ihlen = iph.ihl * 4;
ihlen = iph->ihl * 4; if (ihlen < sizeof(iph))
if (ihlen < sizeof(struct iphdr))
goto out; goto out;
dlen = skb->len - ihlen; ad->u.net.v4info.saddr = iph.saddr;
ad->u.net.saddr = iph->saddr; ad->u.net.v4info.daddr = iph.daddr;
ad->u.net.daddr = iph->daddr;
switch (iph->protocol) { switch (iph.protocol) {
case IPPROTO_TCP: { case IPPROTO_TCP: {
int offset;
struct tcphdr tcph; struct tcphdr tcph;
if (ntohs(iph->frag_off) & IP_OFFSET) if (ntohs(iph.frag_off) & IP_OFFSET)
break; break;
if (dlen < sizeof(tcph)) offset += ihlen;
break;
offset = skb->nh.raw - skb->data + ihlen;
if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0) if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0)
break; break;
...@@ -2685,16 +2684,66 @@ static void selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *a ...@@ -2685,16 +2684,66 @@ static void selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *a
} }
case IPPROTO_UDP: { case IPPROTO_UDP: {
int offset;
struct udphdr udph; struct udphdr udph;
if (ntohs(iph->frag_off) & IP_OFFSET) if (ntohs(iph.frag_off) & IP_OFFSET)
break; break;
if (dlen < sizeof(udph)) offset += ihlen;
if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0)
break;
ad->u.net.sport = udph.source;
ad->u.net.dport = udph.dest;
break; break;
}
default:
break;
}
out:
return ret;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/* Returns error only if unable to parse addresses */
static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad)
{
u8 nexthdr;
int ret, offset = skb->nh.raw - skb->data;
struct ipv6hdr ipv6h;
offset = skb->nh.raw - skb->data;
ret = skb_copy_bits(skb, offset, &ipv6h, sizeof(ipv6h));
if (ret)
goto out;
ipv6_addr_copy(&ad->u.net.v6info.saddr, &ipv6h.saddr);
ipv6_addr_copy(&ad->u.net.v6info.daddr, &ipv6h.daddr);
nexthdr = ipv6h.nexthdr;
offset += sizeof(ipv6h);
offset = ipv6_skip_exthdr(skb, offset, &nexthdr,
skb->tail - skb->head - offset);
if (offset < 0)
goto out;
switch (nexthdr) {
case IPPROTO_TCP: {
struct tcphdr tcph;
if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0)
break;
ad->u.net.sport = tcph.source;
ad->u.net.dport = tcph.dest;
break;
}
case IPPROTO_UDP: {
struct udphdr udph;
offset = skb->nh.raw - skb->data + ihlen;
if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0) if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0)
break; break;
...@@ -2703,11 +2752,46 @@ static void selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *a ...@@ -2703,11 +2752,46 @@ static void selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *a
break; break;
} }
/* includes fragments */
default: default:
break; break;
} }
out: out:
return; return ret;
}
#endif /* IPV6 */
static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
char **addrp, int *len, int src)
{
int ret = 0;
switch (ad->u.net.family) {
case PF_INET:
ret = selinux_parse_skb_ipv4(skb, ad);
if (ret || !addrp)
break;
*len = 4;
*addrp = (char *)(src ? &ad->u.net.v4info.saddr :
&ad->u.net.v4info.daddr);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case PF_INET6:
ret = selinux_parse_skb_ipv6(skb, ad);
if (ret || !addrp)
break;
*len = 16;
*addrp = (char *)(src ? &ad->u.net.v6info.saddr :
&ad->u.net.v6info.daddr);
break;
#endif /* IPV6 */
default:
break;
}
return ret;
} }
/* socket security operations */ /* socket security operations */
...@@ -2770,6 +2854,7 @@ static void selinux_socket_post_create(struct socket *sock, int family, int type ...@@ -2770,6 +2854,7 @@ static void selinux_socket_post_create(struct socket *sock, int family, int type
static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
{ {
u16 family;
int err; int err;
err = socket_has_perm(current, sock, SOCKET__BIND); err = socket_has_perm(current, sock, SOCKET__BIND);
...@@ -2777,20 +2862,35 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ...@@ -2777,20 +2862,35 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
goto out; goto out;
/* /*
* If PF_INET, check name_bind permission for the port. * If PF_INET or PF_INET6, check name_bind permission for the port.
*/ */
if (sock->sk->sk_family == PF_INET) { family = sock->sk->sk_family;
if (family == PF_INET || family == PF_INET6) {
char *addrp;
struct inode_security_struct *isec; struct inode_security_struct *isec;
struct task_security_struct *tsec; struct task_security_struct *tsec;
struct avc_audit_data ad; struct avc_audit_data ad;
struct sockaddr_in *addr = (struct sockaddr_in *)address; struct sockaddr_in *addr4 = NULL;
unsigned short snum = ntohs(addr->sin_port); struct sockaddr_in6 *addr6 = NULL;
unsigned short snum;
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
u32 sid, node_perm; u32 sid, node_perm, addrlen;
tsec = current->security; tsec = current->security;
isec = SOCK_INODE(sock)->i_security; isec = SOCK_INODE(sock)->i_security;
if (family == PF_INET) {
addr4 = (struct sockaddr_in *)address;
snum = ntohs(addr4->sin_port);
addrlen = sizeof(addr4->sin_addr.s_addr);
addrp = (char *)&addr4->sin_addr.s_addr;
} else {
addr6 = (struct sockaddr_in6 *)address;
snum = ntohs(addr6->sin6_port);
addrlen = sizeof(addr6->sin6_addr.s6_addr);
addrp = (char *)&addr6->sin6_addr.s6_addr;
}
if (snum&&(snum < max(PROT_SOCK,ip_local_port_range_0) || if (snum&&(snum < max(PROT_SOCK,ip_local_port_range_0) ||
snum > ip_local_port_range_1)) { snum > ip_local_port_range_1)) {
err = security_port_sid(sk->sk_family, sk->sk_type, err = security_port_sid(sk->sk_family, sk->sk_type,
...@@ -2820,14 +2920,19 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ...@@ -2820,14 +2920,19 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
break; break;
} }
err = security_node_sid(PF_INET, &addr->sin_addr.s_addr, err = security_node_sid(family, addrp, addrlen, &sid);
sizeof(addr->sin_addr.s_addr), &sid);
if (err) if (err)
goto out; goto out;
AVC_AUDIT_DATA_INIT(&ad,NET); AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sport = htons(snum); ad.u.net.sport = htons(snum);
ad.u.net.saddr = addr->sin_addr.s_addr; ad.u.net.family = family;
if (family == PF_INET)
ad.u.net.v4info.saddr = addr4->sin_addr.s_addr;
else
ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr);
err = avc_has_perm(isec->sid, sid, err = avc_has_perm(isec->sid, sid,
isec->sclass, node_perm, NULL, &ad); isec->sclass, node_perm, NULL, &ad);
if (err) if (err)
...@@ -2967,21 +3072,26 @@ static int selinux_socket_unix_may_send(struct socket *sock, ...@@ -2967,21 +3072,26 @@ static int selinux_socket_unix_may_send(struct socket *sock,
static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{ {
int err = 0; u16 family;
char *addrp;
int len, err = 0;
u32 netif_perm, node_perm, node_sid, recv_perm = 0; u32 netif_perm, node_perm, node_sid, recv_perm = 0;
struct socket *sock; struct socket *sock;
struct inode *inode; struct inode *inode;
struct net_device *dev; struct net_device *dev;
struct iphdr *iph;
struct sel_netif *netif; struct sel_netif *netif;
struct netif_security_struct *nsec; struct netif_security_struct *nsec;
struct inode_security_struct *isec; struct inode_security_struct *isec;
struct avc_audit_data ad; struct avc_audit_data ad;
/* Only IPv4 is supported here at this stage */ family = sk->sk_family;
if (sk->sk_family != PF_INET) if (family != PF_INET && family != PF_INET6)
goto out; goto out;
/* Handle mapped IPv4 packets arriving via IPv6 sockets */
if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
family = PF_INET;
sock = sk->sk_socket; sock = sk->sk_socket;
/* TCP control messages don't always have a socket. */ /* TCP control messages don't always have a socket. */
...@@ -3026,7 +3136,13 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3026,7 +3136,13 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
AVC_AUDIT_DATA_INIT(&ad, NET); AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = dev->name; ad.u.net.netif = dev->name;
selinux_parse_skb_ipv4(skb, &ad); ad.u.net.family = family;
err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
if (err) {
sel_netif_put(netif);
goto out;
}
err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF, err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF,
netif_perm, &nsec->avcr, &ad); netif_perm, &nsec->avcr, &ad);
...@@ -3035,8 +3151,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3035,8 +3151,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
goto out; goto out;
/* Fixme: this lookup is inefficient */ /* Fixme: this lookup is inefficient */
iph = skb->nh.iph; err = security_node_sid(family, addrp, len, &node_sid);
err = security_node_sid(PF_INET, &iph->saddr, sizeof(iph->saddr), &node_sid);
if (err) if (err)
goto out; goto out;
...@@ -3057,7 +3172,6 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3057,7 +3172,6 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
err = avc_has_perm(isec->sid, port_sid, isec->sclass, err = avc_has_perm(isec->sid, port_sid, isec->sclass,
recv_perm, NULL, &ad); recv_perm, NULL, &ad);
} }
out: out:
return err; return err;
} }
...@@ -3111,18 +3225,20 @@ static void selinux_sk_free_security(struct sock *sk) ...@@ -3111,18 +3225,20 @@ static void selinux_sk_free_security(struct sock *sk)
} }
#ifdef CONFIG_NETFILTER #ifdef CONFIG_NETFILTER
static unsigned int selinux_ip_postroute_last(unsigned int hooknum, static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
struct sk_buff **pskb, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *in,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *),
u16 family)
{ {
int err = NF_ACCEPT; char *addrp;
int len, err = NF_ACCEPT;
u32 netif_perm, node_perm, node_sid, send_perm = 0; u32 netif_perm, node_perm, node_sid, send_perm = 0;
struct sock *sk; struct sock *sk;
struct socket *sock; struct socket *sock;
struct inode *inode; struct inode *inode;
struct iphdr *iph;
struct sel_netif *netif; struct sel_netif *netif;
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
struct netif_security_struct *nsec; struct netif_security_struct *nsec;
...@@ -3170,9 +3286,17 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, ...@@ -3170,9 +3286,17 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
break; break;
} }
AVC_AUDIT_DATA_INIT(&ad, NET); AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = dev->name; ad.u.net.netif = dev->name;
selinux_parse_skb_ipv4(skb, &ad); ad.u.net.family = family;
err = selinux_parse_skb(skb, &ad, &addrp,
&len, 0) ? NF_DROP : NF_ACCEPT;
if (err != NF_ACCEPT) {
sel_netif_put(netif);
goto out;
}
err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF, err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF,
netif_perm, &nsec->avcr, &ad) ? NF_DROP : NF_ACCEPT; netif_perm, &nsec->avcr, &ad) ? NF_DROP : NF_ACCEPT;
...@@ -3181,8 +3305,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, ...@@ -3181,8 +3305,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
goto out; goto out;
/* Fixme: this lookup is inefficient */ /* Fixme: this lookup is inefficient */
iph = skb->nh.iph; err = security_node_sid(family, addrp, len,
err = security_node_sid(PF_INET, &iph->daddr, sizeof(iph->daddr),
&node_sid) ? NF_DROP : NF_ACCEPT; &node_sid) ? NF_DROP : NF_ACCEPT;
if (err != NF_ACCEPT) if (err != NF_ACCEPT)
goto out; goto out;
...@@ -3212,6 +3335,28 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, ...@@ -3212,6 +3335,28 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
return err; return err;
} }
static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET);
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET6);
}
#endif /* IPV6 */
#endif /* CONFIG_NETFILTER */ #endif /* CONFIG_NETFILTER */
#endif /* CONFIG_SECURITY_NETWORK */ #endif /* CONFIG_SECURITY_NETWORK */
...@@ -4025,14 +4170,26 @@ security_initcall(selinux_init); ...@@ -4025,14 +4170,26 @@ security_initcall(selinux_init);
#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_NETFILTER) #if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_NETFILTER)
static struct nf_hook_ops selinux_ip_ops[] = { static struct nf_hook_ops selinux_ipv4_op = {
{ .hook = selinux_ip_postroute_last, .hook = selinux_ipv4_postroute_last,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pf = PF_INET, .pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING, .hooknum = NF_IP_POST_ROUTING,
.priority = NF_IP_PRI_SELINUX_LAST, }, .priority = NF_IP_PRI_SELINUX_LAST,
};
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static struct nf_hook_ops selinux_ipv6_op = {
.hook = selinux_ipv6_postroute_last,
.owner = THIS_MODULE,
.pf = PF_INET6,
.hooknum = NF_IP6_POST_ROUTING,
.priority = NF_IP6_PRI_SELINUX_LAST,
}; };
#endif /* IPV6 */
static int __init selinux_nf_ip_init(void) static int __init selinux_nf_ip_init(void)
{ {
int err = 0; int err = 0;
...@@ -4042,10 +4199,17 @@ static int __init selinux_nf_ip_init(void) ...@@ -4042,10 +4199,17 @@ static int __init selinux_nf_ip_init(void)
printk(KERN_INFO "SELinux: Registering netfilter hooks\n"); printk(KERN_INFO "SELinux: Registering netfilter hooks\n");
err = nf_register_hook(&selinux_ip_ops[0]); err = nf_register_hook(&selinux_ipv4_op);
if (err)
panic("SELinux: nf_register_hook for IPv4: error %d\n", err);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
err = nf_register_hook(&selinux_ipv6_op);
if (err) if (err)
panic("SELinux: nf_register_hook 0 error %d\n", err); panic("SELinux: nf_register_hook for IPv6: error %d\n", err);
#endif /* IPV6 */
out: out:
return err; return err;
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/in6.h>
#include <asm/system.h> #include <asm/system.h>
#include "flask.h" #include "flask.h"
#include "av_permissions.h" #include "av_permissions.h"
...@@ -65,16 +66,28 @@ struct avc_audit_data { ...@@ -65,16 +66,28 @@ struct avc_audit_data {
struct { struct {
char *netif; char *netif;
struct sock *sk; struct sock *sk;
u16 family;
u16 dport; u16 dport;
u16 sport; u16 sport;
union {
struct {
u32 daddr; u32 daddr;
u32 saddr; u32 saddr;
} v4;
struct {
struct in6_addr daddr;
struct in6_addr saddr;
} v6;
} fam;
} net; } net;
int cap; int cap;
int ipc_id; int ipc_id;
} u; } u;
}; };
#define v4info fam.v4
#define v6info fam.v6
/* Initialize an AVC audit data structure. */ /* Initialize an AVC audit data structure. */
#define AVC_AUDIT_DATA_INIT(_d,_t) \ #define AVC_AUDIT_DATA_INIT(_d,_t) \
{ memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; } { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; }
......
...@@ -15,8 +15,15 @@ ...@@ -15,8 +15,15 @@
#define SECCLASS_NULL 0x0000 /* no class */ #define SECCLASS_NULL 0x0000 /* no class */
#define SELINUX_MAGIC 0xf97cff8c #define SELINUX_MAGIC 0xf97cff8c
#define POLICYDB_VERSION 16
#define POLICYDB_VERSION_COMPAT 15 /* Identify specific policy version changes */
#define POLICYDB_VERSION_BASE 15
#define POLICYDB_VERSION_BOOL 16
#define POLICYDB_VERSION_IPV6 17
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_IPV6
#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
extern int selinux_enabled; extern int selinux_enabled;
......
...@@ -164,7 +164,7 @@ static ssize_t sel_read_policyvers(struct file *filp, char *buf, ...@@ -164,7 +164,7 @@ static ssize_t sel_read_policyvers(struct file *filp, char *buf,
return -ENOMEM; return -ENOMEM;
memset(page, 0, PAGE_SIZE); memset(page, 0, PAGE_SIZE);
length = scnprintf(page, PAGE_SIZE, "%u", POLICYDB_VERSION); length = scnprintf(page, PAGE_SIZE, "%u", POLICYDB_VERSION_MAX);
if (length < 0) { if (length < 0) {
free_page((unsigned long)page); free_page((unsigned long)page);
return length; return length;
......
...@@ -48,6 +48,45 @@ static unsigned int symtab_sizes[SYM_NUM] = { ...@@ -48,6 +48,45 @@ static unsigned int symtab_sizes[SYM_NUM] = {
16 16
}; };
struct policydb_compat_info {
int version;
int sym_num;
int ocon_num;
};
/* These need to be updated if SYM_NUM or OCON_NUM changes */
static struct policydb_compat_info policydb_compat[] = {
{
.version = POLICYDB_VERSION_BASE,
.sym_num = SYM_NUM - 1,
.ocon_num = OCON_NUM - 1,
},
{
.version = POLICYDB_VERSION_BOOL,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM - 1,
},
{
.version = POLICYDB_VERSION_IPV6,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
},
};
static struct policydb_compat_info *policydb_lookup_compat(int version)
{
int i;
struct policydb_compat_info *info = NULL;
for (i = 0; i < sizeof(policydb_compat)/sizeof(*info); i++) {
if (policydb_compat[i].version == version) {
info = &policydb_compat[i];
break;
}
}
return info;
}
/* /*
* Initialize the role table. * Initialize the role table.
*/ */
...@@ -1086,9 +1125,10 @@ int policydb_read(struct policydb *p, void *fp) ...@@ -1086,9 +1125,10 @@ int policydb_read(struct policydb *p, void *fp)
struct role_trans *tr, *ltr; struct role_trans *tr, *ltr;
struct ocontext *l, *c, *newc; struct ocontext *l, *c, *newc;
struct genfs *genfs_p, *genfs, *newgenfs; struct genfs *genfs_p, *genfs, *newgenfs;
int i, j, rc, policy_ver, num_syms; int i, j, rc, r_policyvers;
u32 *buf, len, len2, config, nprim, nel, nel2; u32 *buf, len, len2, config, nprim, nel, nel2;
char *policydb_str; char *policydb_str;
struct policydb_compat_info *info;
config = 0; config = 0;
mls_set_config(config); mls_set_config(config);
...@@ -1151,12 +1191,15 @@ int policydb_read(struct policydb *p, void *fp) ...@@ -1151,12 +1191,15 @@ int policydb_read(struct policydb *p, void *fp)
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
buf[i] = le32_to_cpu(buf[i]); buf[i] = le32_to_cpu(buf[i]);
policy_ver = buf[0]; r_policyvers = buf[0];
if (policy_ver != POLICYDB_VERSION && policy_ver != POLICYDB_VERSION_COMPAT) { if (r_policyvers < POLICYDB_VERSION_MIN ||
r_policyvers > POLICYDB_VERSION_MAX) {
printk(KERN_ERR "security: policydb version %d does not match " printk(KERN_ERR "security: policydb version %d does not match "
"my version %d\n", buf[0], POLICYDB_VERSION); "my version range %d-%d\n",
buf[0], POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX);
goto bad; goto bad;
} }
if (buf[1] != config) { if (buf[1] != config) {
printk(KERN_ERR "security: policydb configuration (%s) does " printk(KERN_ERR "security: policydb configuration (%s) does "
"not match my configuration (%s)\n", "not match my configuration (%s)\n",
...@@ -1165,29 +1208,26 @@ int policydb_read(struct policydb *p, void *fp) ...@@ -1165,29 +1208,26 @@ int policydb_read(struct policydb *p, void *fp)
goto bad; goto bad;
} }
if (policy_ver == POLICYDB_VERSION_COMPAT) {
if (buf[2] != (SYM_NUM - 1) || buf[3] != OCON_NUM) { info = policydb_lookup_compat(r_policyvers);
printk(KERN_ERR "security: policydb table sizes (%d,%d) do " if (!info) {
"not match mine (%d,%d)\n", printk(KERN_ERR "security: unable to find policy compat info "
buf[2], buf[3], SYM_NUM, OCON_NUM); "for version %d\n", r_policyvers);
goto bad; goto bad;
} }
num_syms = SYM_NUM - 1;
} else { if (buf[2] != info->sym_num || buf[3] != info->ocon_num) {
if (buf[2] != SYM_NUM || buf[3] != OCON_NUM) {
printk(KERN_ERR "security: policydb table sizes (%d,%d) do " printk(KERN_ERR "security: policydb table sizes (%d,%d) do "
"not match mine (%d,%d)\n", "not match mine (%d,%d)\n", buf[2], buf[3],
buf[2], buf[3], SYM_NUM, OCON_NUM); info->sym_num, info->ocon_num);
goto bad; goto bad;
} }
num_syms = SYM_NUM;
}
rc = mls_read_nlevels(p, fp); rc = mls_read_nlevels(p, fp);
if (rc) if (rc)
goto bad; goto bad;
for (i = 0; i < num_syms; i++) { for (i = 0; i < info->sym_num; i++) {
buf = next_entry(fp, sizeof(u32)*2); buf = next_entry(fp, sizeof(u32)*2);
if (!buf) { if (!buf) {
rc = -EINVAL; rc = -EINVAL;
...@@ -1208,7 +1248,7 @@ int policydb_read(struct policydb *p, void *fp) ...@@ -1208,7 +1248,7 @@ int policydb_read(struct policydb *p, void *fp)
if (rc) if (rc)
goto bad; goto bad;
if (policy_ver == POLICYDB_VERSION) { if (r_policyvers >= POLICYDB_VERSION_BOOL) {
rc = cond_read_list(p, fp); rc = cond_read_list(p, fp);
if (rc) if (rc)
goto bad; goto bad;
...@@ -1281,7 +1321,7 @@ int policydb_read(struct policydb *p, void *fp) ...@@ -1281,7 +1321,7 @@ int policydb_read(struct policydb *p, void *fp)
if (rc) if (rc)
goto bad; goto bad;
for (i = 0; i < OCON_NUM; i++) { for (i = 0; i < info->ocon_num; i++) {
buf = next_entry(fp, sizeof(u32)); buf = next_entry(fp, sizeof(u32));
if (!buf) { if (!buf) {
rc = -EINVAL; rc = -EINVAL;
...@@ -1379,6 +1419,20 @@ int policydb_read(struct policydb *p, void *fp) ...@@ -1379,6 +1419,20 @@ int policydb_read(struct policydb *p, void *fp)
if (rc) if (rc)
goto bad; goto bad;
break; break;
case OCON_NODE6: {
int k;
buf = next_entry(fp, sizeof(u32) * 8);
if (!buf)
goto bad;
for (k = 0; k < 4; k++)
c->u.node6.addr[k] = le32_to_cpu(buf[k]);
for (k = 0; k < 4; k++)
c->u.node6.mask[k] = le32_to_cpu(buf[k+4]);
if (context_read_and_validate(&c->context[0], p, fp))
goto bad;
break;
}
} }
} }
} }
......
...@@ -138,6 +138,10 @@ struct ocontext { ...@@ -138,6 +138,10 @@ struct ocontext {
u32 addr; u32 addr;
u32 mask; u32 mask;
} node; /* node information */ } node; /* node information */
struct {
u32 addr[4];
u32 mask[4];
} node6; /* IPv6 node information */
} u; } u;
union { union {
u32 sclass; /* security class for genfs */ u32 sclass; /* security class for genfs */
...@@ -177,7 +181,8 @@ struct genfs { ...@@ -177,7 +181,8 @@ struct genfs {
#define OCON_NETIF 3 /* network interfaces */ #define OCON_NETIF 3 /* network interfaces */
#define OCON_NODE 4 /* nodes */ #define OCON_NODE 4 /* nodes */
#define OCON_FSUSE 5 /* fs_use */ #define OCON_FSUSE 5 /* fs_use */
#define OCON_NUM 6 #define OCON_NODE6 6 /* IPv6 nodes */
#define OCON_NUM 7
/* The policy database */ /* The policy database */
struct policydb { struct policydb {
......
...@@ -1187,6 +1187,18 @@ int security_netif_sid(char *name, ...@@ -1187,6 +1187,18 @@ int security_netif_sid(char *name,
return rc; return rc;
} }
static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask)
{
int i, fail = 0;
for(i = 0; i < 4; i++)
if(addr[i] != (input[i] & mask[i])) {
fail = 1;
break;
}
return !fail;
}
/** /**
* security_node_sid - Obtain the SID for a node (host). * security_node_sid - Obtain the SID for a node (host).
...@@ -1201,15 +1213,19 @@ int security_node_sid(u16 domain, ...@@ -1201,15 +1213,19 @@ int security_node_sid(u16 domain,
u32 *out_sid) u32 *out_sid)
{ {
int rc = 0; int rc = 0;
u32 addr;
struct ocontext *c; struct ocontext *c;
POLICY_RDLOCK; POLICY_RDLOCK;
if (domain != AF_INET || addrlen != sizeof(u32)) { switch (domain) {
*out_sid = SECINITSID_NODE; case AF_INET: {
u32 addr;
if (addrlen != sizeof(u32)) {
rc = -EINVAL;
goto out; goto out;
} }
addr = *((u32 *)addrp); addr = *((u32 *)addrp);
c = policydb.ocontexts[OCON_NODE]; c = policydb.ocontexts[OCON_NODE];
...@@ -1218,6 +1234,27 @@ int security_node_sid(u16 domain, ...@@ -1218,6 +1234,27 @@ int security_node_sid(u16 domain,
break; break;
c = c->next; c = c->next;
} }
break;
}
case AF_INET6:
if (addrlen != sizeof(u64) * 2) {
rc = -EINVAL;
goto out;
}
c = policydb.ocontexts[OCON_NODE6];
while (c) {
if (match_ipv6_addrmask(addrp, c->u.node6.addr,
c->u.node6.mask))
break;
c = c->next;
}
break;
default:
*out_sid = SECINITSID_NODE;
goto out;
}
if (c) { if (c) {
if (!c->sid[0]) { if (!c->sid[0]) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment