diff -urpN -X dontdiff linux-2.6.5-rc1-mm2.o/security/selinux/avc.c linux-2.6.5-rc1-mm2.w/security/selinux/avc.c --- linux-2.6.5-rc1-mm2.o/security/selinux/avc.c 2004-03-18 02:15:31.000000000 -0500 +++ linux-2.6.5-rc1-mm2.w/security/selinux/avc.c 2004-03-18 12:24:48.000000000 -0500 @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "avc.h" #include "avc_ss.h" #include "class_to_string.h" @@ -421,6 +423,17 @@ static inline void avc_print_ipv4_addr(s audit_log_format(ab, " %s=%d", name2, ntohs(port)); } +static inline void avc_print_ipv6_addr(struct audit_buffer *ab, + struct in6_addr *addr, + u16 port, char *name1, char *name2) +{ + if (!ipv6_addr_any(addr)) + audit_log_format(ab, " %s=%04x:%04x:%04x:%04x:%04x:%04x:" + "%04x:%04x", name1, NIP6(*addr)); + if (port) + audit_log_format(ab, " %s=%d", name2, ntohs(port)); +} + /* * Copied from net/core/utils.c:net_ratelimit and modified for * use by the AVC audit facility. @@ -591,11 +604,11 @@ void avc_audit(u32 ssid, u32 tsid, if (a->u.net.sk) { struct sock *sk = a->u.net.sk; struct unix_sock *u; - struct inet_opt *inet; switch (sk->sk_family) { - case AF_INET: - inet = inet_sk(sk); + case AF_INET: { + struct inet_opt *inet = inet_sk(sk); + avc_print_ipv4_addr(ab, inet->rcv_saddr, inet->sport, "laddr", "lport"); @@ -603,6 +616,19 @@ void avc_audit(u32 ssid, u32 tsid, inet->dport, "faddr", "fport"); break; + } + case AF_INET6: { + struct inet_opt *inet = inet_sk(sk); + struct ipv6_pinfo *inet6 = inet6_sk(sk); + + avc_print_ipv6_addr(ab, &inet6->rcv_saddr, + inet->sport, + "laddr", "lport"); + avc_print_ipv6_addr(ab, &inet6->daddr, + inet->dport, + "faddr", "fport"); + break; + } case AF_UNIX: u = unix_sk(sk); if (u->dentry) { @@ -622,10 +648,25 @@ void avc_audit(u32 ssid, u32 tsid, } } - avc_print_ipv4_addr(ab, a->u.net.saddr, a->u.net.sport, - "saddr", "src"); - avc_print_ipv4_addr(ab, a->u.net.daddr, a->u.net.dport, - "daddr", "dest"); + switch (a->u.net.family) { + case AF_INET: + avc_print_ipv4_addr(ab, a->u.net.v4info.saddr, + a->u.net.sport, + "saddr", "src"); + avc_print_ipv4_addr(ab, a->u.net.v4info.daddr, + a->u.net.dport, + "daddr", "dest"); + break; + + case AF_INET6: + avc_print_ipv6_addr(ab, &a->u.net.v6info.saddr, + a->u.net.sport, + "saddr", "src"); + avc_print_ipv6_addr(ab, &a->u.net.v6info.daddr, + a->u.net.dport, + "daddr", "dest"); + break; + } if (a->u.net.netif) audit_log_format(ab, " netif=%s", a->u.net.netif); diff -urpN -X dontdiff linux-2.6.5-rc1-mm2.o/security/selinux/hooks.c linux-2.6.5-rc1-mm2.w/security/selinux/hooks.c --- linux-2.6.5-rc1-mm2.o/security/selinux/hooks.c 2004-03-18 02:15:31.000000000 -0500 +++ linux-2.6.5-rc1-mm2.w/security/selinux/hooks.c 2004-03-18 15:37:46.135367312 -0500 @@ -42,6 +42,7 @@ #include #include #include +#include #include #include /* for sysctl_local_port_range[] */ #include /* struct or_callable used in sock_rcv_skb */ @@ -59,6 +60,7 @@ #include /* for Unix socket types */ #include #include +#include #include "avc.h" #include "objsec.h" @@ -2646,22 +2648,24 @@ static void selinux_task_to_inode(struct #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 dlen, ihlen, ret = -EINVAL; struct iphdr *iph; - if (skb->len < sizeof(struct iphdr)) + if (skb->len < sizeof(*iph)) goto out; iph = skb->nh.iph; ihlen = iph->ihl * 4; - if (ihlen < sizeof(struct iphdr)) + if (ihlen < sizeof(*iph)) goto out; + ret = 0; dlen = skb->len - ihlen; - ad->u.net.saddr = iph->saddr; - ad->u.net.daddr = iph->daddr; + ad->u.net.v4info.saddr = iph->saddr; + ad->u.net.v4info.daddr = iph->daddr; switch (iph->protocol) { case IPPROTO_TCP: { @@ -2706,7 +2710,104 @@ static void selinux_parse_skb_ipv4(struc break; } out: - return; + return ret; +} + +#if defined(CONFIG_IPV6) + +/* 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 plen, protoff, ret = 0; + struct ipv6hdr *ipv6h = skb->nh.ipv6h; + + if (skb->len < sizeof(*ipv6h)) { + ret = -EINVAL; + goto out; + } + + ipv6_addr_copy(&ad->u.net.v6info.saddr, &ipv6h->saddr); + ipv6_addr_copy(&ad->u.net.v6info.daddr, &ipv6h->daddr); + plen = ntohs(ipv6h->payload_len); + if (plen != skb->len - sizeof(*ipv6h)) + goto out; + protoff = (u8 *)(skb->nh.ipv6h + 1) - skb->data; + protoff = ipv6_skip_exthdr(skb, protoff, &nexthdr, skb->len - protoff); + + switch (nexthdr) { + case IPPROTO_TCP: { + int len; + struct tcphdr tcph; + + len = skb->len - protoff; + if (len < sizeof(struct tcphdr)) + break; + + if (skb_copy_bits(skb, protoff, &tcph, sizeof(tcph)) < 0) + break; + + ad->u.net.sport = tcph.source; + ad->u.net.dport = tcph.dest; + break; + } + + case IPPROTO_UDP: { + int len; + struct udphdr udph; + + len = skb->len - protoff; + if (len < sizeof(struct udphdr)) + break; + + if (skb_copy_bits(skb, protoff, &udph, sizeof(udph)) < 0) + break; + + ad->u.net.sport = udph.source; + ad->u.net.dport = udph.dest; + break; + } + + /* includes fragments */ + default: + break; + } +out: + return ret; +} + +#endif /* CONFIG_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) + 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 /* CONFIG_IPV6 */ + default: + break; + } + + return ret; } /* socket security operations */ @@ -2769,6 +2870,7 @@ static void selinux_socket_post_create(s static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { + u16 family; int err; err = socket_has_perm(current, sock, SOCKET__BIND); @@ -2776,20 +2878,35 @@ static int selinux_socket_bind(struct so 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 task_security_struct *tsec; struct avc_audit_data ad; - struct sockaddr_in *addr = (struct sockaddr_in *)address; - unsigned short snum = ntohs(addr->sin_port); + struct sockaddr_in *addr4 = NULL; + struct sockaddr_in6 *addr6 = NULL; + unsigned short snum; struct sock *sk = sock->sk; - u32 sid, node_perm; + u32 sid, node_perm, addrlen; tsec = current->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) || snum > ip_local_port_range_1)) { err = security_port_sid(sk->sk_family, sk->sk_type, @@ -2819,14 +2936,19 @@ static int selinux_socket_bind(struct so break; } - err = security_node_sid(PF_INET, &addr->sin_addr.s_addr, - sizeof(addr->sin_addr.s_addr), &sid); + err = security_node_sid(family, addrp, addrlen, &sid); if (err) goto out; AVC_AUDIT_DATA_INIT(&ad,NET); 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, isec->sclass, node_perm, NULL, &ad); if (err) @@ -2966,19 +3088,20 @@ static int selinux_socket_unix_may_send( 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; struct socket *sock; struct inode *inode; struct net_device *dev; - struct iphdr *iph; struct sel_netif *netif; struct netif_security_struct *nsec; struct inode_security_struct *isec; struct avc_audit_data ad; - /* Only IPv4 is supported here at this stage */ - if (sk->sk_family != PF_INET) + family = sk->sk_family; + if (family != PF_INET && family != PF_INET6) goto out; sock = sk->sk_socket; @@ -3025,7 +3148,12 @@ static int selinux_socket_sock_rcv_skb(s AVC_AUDIT_DATA_INIT(&ad, NET); ad.u.net.netif = dev->name; - selinux_parse_skb_ipv4(skb, &ad); + ad.u.net.family = family; + + if (selinux_parse_skb(skb, &ad, &addrp, &len, 1)) { + sel_netif_put(netif); + goto out; + } err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF, netif_perm, &nsec->avcr, &ad); @@ -3034,8 +3162,7 @@ static int selinux_socket_sock_rcv_skb(s goto out; /* Fixme: this lookup is inefficient */ - iph = skb->nh.iph; - err = security_node_sid(PF_INET, &iph->saddr, sizeof(iph->saddr), &node_sid); + err = security_node_sid(family, addrp, len, &node_sid); if (err) goto out; @@ -3108,18 +3235,20 @@ static void selinux_sk_free_security(str } #ifdef CONFIG_NETFILTER + static unsigned int selinux_ip_postroute_last(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, 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; struct sock *sk; struct socket *sock; struct inode *inode; - struct iphdr *iph; struct sel_netif *netif; struct sk_buff *skb = *pskb; struct netif_security_struct *nsec; @@ -3167,9 +3296,17 @@ static unsigned int selinux_ip_postroute break; } + AVC_AUDIT_DATA_INIT(&ad, NET); 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, netif_perm, &nsec->avcr, &ad) ? NF_DROP : NF_ACCEPT; @@ -3178,8 +3315,7 @@ static unsigned int selinux_ip_postroute goto out; /* Fixme: this lookup is inefficient */ - iph = skb->nh.iph; - err = security_node_sid(PF_INET, &iph->daddr, sizeof(iph->daddr), + err = security_node_sid(family, addrp, len, &node_sid) ? NF_DROP : NF_ACCEPT; if (err != NF_ACCEPT) goto out; @@ -3209,6 +3345,28 @@ out: 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) + +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 /* CONFIG_IPV6 */ + #endif /* CONFIG_NETFILTER */ #endif /* CONFIG_SECURITY_NETWORK */ @@ -4022,14 +4180,26 @@ security_initcall(selinux_init); #if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_NETFILTER) -static struct nf_hook_ops selinux_ip_ops[] = { - { .hook = selinux_ip_postroute_last, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, - .priority = NF_IP_PRI_SELINUX_LAST, }, +static struct nf_hook_ops selinux_ipv4_op = { + .hook = selinux_ipv4_postroute_last, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_IP_POST_ROUTING, + .priority = NF_IP_PRI_SELINUX_LAST, +}; + +#if defined(CONFIG_IPV6) + +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 /* CONFIG_IPV6 */ + static int __init selinux_nf_ip_init(void) { int err = 0; @@ -4039,10 +4209,17 @@ static int __init selinux_nf_ip_init(voi 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) + + err = nf_register_hook(&selinux_ipv6_op); if (err) - panic("SELinux: nf_register_hook 0 error %d\n", err); + panic("SELinux: nf_register_hook for IPv6: error %d\n", err); +#endif /* CONFIG_IPV6 */ out: return err; } diff -urpN -X dontdiff linux-2.6.5-rc1-mm2.o/security/selinux/include/avc.h linux-2.6.5-rc1-mm2.w/security/selinux/include/avc.h --- linux-2.6.5-rc1-mm2.o/security/selinux/include/avc.h 2004-03-18 02:15:31.000000000 -0500 +++ linux-2.6.5-rc1-mm2.w/security/selinux/include/avc.h 2004-03-18 11:59:17.000000000 -0500 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "flask.h" #include "av_permissions.h" @@ -65,16 +66,28 @@ struct avc_audit_data { struct { char *netif; struct sock *sk; + u16 family; u16 dport; u16 sport; - u32 daddr; - u32 saddr; + union { + struct { + u32 daddr; + u32 saddr; + } v4; + struct { + struct in6_addr daddr; + struct in6_addr saddr; + } v6; + } fam; } net; int cap; int ipc_id; } u; }; +#define v4info fam.v4 +#define v6info fam.v6 + /* Initialize an AVC audit data structure. */ #define AVC_AUDIT_DATA_INIT(_d,_t) \ { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; } diff -urpN -X dontdiff linux-2.6.5-rc1-mm2.o/security/selinux/include/security.h linux-2.6.5-rc1-mm2.w/security/selinux/include/security.h --- linux-2.6.5-rc1-mm2.o/security/selinux/include/security.h 2004-03-18 02:15:31.000000000 -0500 +++ linux-2.6.5-rc1-mm2.w/security/selinux/include/security.h 2004-03-18 02:28:11.000000000 -0500 @@ -15,7 +15,7 @@ #define SECCLASS_NULL 0x0000 /* no class */ #define SELINUX_MAGIC 0xf97cff8c -#define POLICYDB_VERSION 16 +#define POLICYDB_VERSION 17 #define POLICYDB_VERSION_COMPAT 15 #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM diff -urpN -X dontdiff linux-2.6.5-rc1-mm2.o/security/selinux/ss/policydb.c linux-2.6.5-rc1-mm2.w/security/selinux/ss/policydb.c --- linux-2.6.5-rc1-mm2.o/security/selinux/ss/policydb.c 2004-03-18 02:15:31.000000000 -0500 +++ linux-2.6.5-rc1-mm2.w/security/selinux/ss/policydb.c 2004-03-18 02:28:11.000000000 -0500 @@ -1379,6 +1379,20 @@ int policydb_read(struct policydb *p, vo if (rc) goto bad; 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; + } } } } diff -urpN -X dontdiff linux-2.6.5-rc1-mm2.o/security/selinux/ss/policydb.h linux-2.6.5-rc1-mm2.w/security/selinux/ss/policydb.h --- linux-2.6.5-rc1-mm2.o/security/selinux/ss/policydb.h 2004-03-18 02:15:31.000000000 -0500 +++ linux-2.6.5-rc1-mm2.w/security/selinux/ss/policydb.h 2004-03-18 02:28:11.000000000 -0500 @@ -138,6 +138,10 @@ struct ocontext { u32 addr; u32 mask; } node; /* node information */ + struct { + u32 addr[4]; + u32 mask[4]; + } node6; /* IPv6 node information */ } u; union { u32 sclass; /* security class for genfs */ @@ -177,7 +181,8 @@ struct genfs { #define OCON_NETIF 3 /* network interfaces */ #define OCON_NODE 4 /* nodes */ #define OCON_FSUSE 5 /* fs_use */ -#define OCON_NUM 6 +#define OCON_NODE6 6 /* IPv6 nodes */ +#define OCON_NUM 7 /* The policy database */ struct policydb { diff -urpN -X dontdiff linux-2.6.5-rc1-mm2.o/security/selinux/ss/services.c linux-2.6.5-rc1-mm2.w/security/selinux/ss/services.c --- linux-2.6.5-rc1-mm2.o/security/selinux/ss/services.c 2004-03-18 02:15:31.000000000 -0500 +++ linux-2.6.5-rc1-mm2.w/security/selinux/ss/services.c 2004-03-18 02:28:11.000000000 -0500 @@ -1187,6 +1187,18 @@ out: 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). @@ -1201,22 +1213,47 @@ int security_node_sid(u16 domain, u32 *out_sid) { int rc = 0; - u32 addr; struct ocontext *c; POLICY_RDLOCK; - if (domain != AF_INET || addrlen != sizeof(u32)) { - *out_sid = SECINITSID_NODE; - goto out; + switch (domain) { + case AF_INET: { + u32 addr; + + if (addrlen != sizeof(u32)) { + rc = -EINVAL; + goto out; + } + + addr = *((u32 *)addrp); + + c = policydb.ocontexts[OCON_NODE]; + while (c) { + if (c->u.node.addr == (addr & c->u.node.mask)) + break; + c = c->next; + } + break; } - addr = *((u32 *)addrp); - c = policydb.ocontexts[OCON_NODE]; - while (c) { - if (c->u.node.addr == (addr & c->u.node.mask)) - break; - c = c->next; + 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) {