diff -purN -X dontdiff linux-2.6.5-rc2-mm2.o/net/ipv6/exthdrs.c linux-2.6.5-rc2-mm2.w/net/ipv6/exthdrs.c --- linux-2.6.5-rc2-mm2.o/net/ipv6/exthdrs.c 2004-03-11 09:13:23.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/net/ipv6/exthdrs.c 2004-03-24 23:26:12.000000000 -0500 @@ -633,105 +633,3 @@ ipv6_dup_options(struct sock *sk, struct } return opt2; } - - -/* - * find out if nexthdr is a well-known extension header or a protocol - */ - -int ipv6_ext_hdr(u8 nexthdr) -{ - /* - * find out if nexthdr is an extension header or a protocol - */ - return ( (nexthdr == NEXTHDR_HOP) || - (nexthdr == NEXTHDR_ROUTING) || - (nexthdr == NEXTHDR_FRAGMENT) || - (nexthdr == NEXTHDR_AUTH) || - (nexthdr == NEXTHDR_NONE) || - (nexthdr == NEXTHDR_DEST) ); -} - -/* - * Skip any extension headers. This is used by the ICMP module. - * - * Note that strictly speaking this conflicts with RFC 2460 4.0: - * ...The contents and semantics of each extension header determine whether - * or not to proceed to the next header. Therefore, extension headers must - * be processed strictly in the order they appear in the packet; a - * receiver must not, for example, scan through a packet looking for a - * particular kind of extension header and process that header prior to - * processing all preceding ones. - * - * We do exactly this. This is a protocol bug. We can't decide after a - * seeing an unknown discard-with-error flavour TLV option if it's a - * ICMP error message or not (errors should never be send in reply to - * ICMP error messages). - * - * But I see no other way to do this. This might need to be reexamined - * when Linux implements ESP (and maybe AUTH) headers. - * --AK - * - * This function parses (probably truncated) exthdr set "hdr" - * of length "len". "nexthdrp" initially points to some place, - * where type of the first header can be found. - * - * It skips all well-known exthdrs, and returns pointer to the start - * of unparsable area i.e. the first header with unknown type. - * If it is not NULL *nexthdr is updated by type/protocol of this header. - * - * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL. - * - it may return pointer pointing beyond end of packet, - * if the last recognized header is truncated in the middle. - * - if packet is truncated, so that all parsed headers are skipped, - * it returns NULL. - * - First fragment header is skipped, not-first ones - * are considered as unparsable. - * - ESP is unparsable for now and considered like - * normal payload protocol. - * - Note also special handling of AUTH header. Thanks to IPsec wizards. - * - * --ANK (980726) - */ - -int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, int len) -{ - u8 nexthdr = *nexthdrp; - - while (ipv6_ext_hdr(nexthdr)) { - struct ipv6_opt_hdr hdr; - int hdrlen; - - if (len < (int)sizeof(struct ipv6_opt_hdr)) - return -1; - if (nexthdr == NEXTHDR_NONE) - return -1; - if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) - BUG(); - if (nexthdr == NEXTHDR_FRAGMENT) { - unsigned short frag_off; - if (skb_copy_bits(skb, - start+offsetof(struct frag_hdr, - frag_off), - &frag_off, - sizeof(frag_off))) { - return -1; - } - - if (ntohs(frag_off) & ~0x7) - break; - hdrlen = 8; - } else if (nexthdr == NEXTHDR_AUTH) - hdrlen = (hdr.hdrlen+2)<<2; - else - hdrlen = ipv6_optlen(&hdr); - - nexthdr = hdr.nexthdr; - len -= hdrlen; - start += hdrlen; - } - - *nexthdrp = nexthdr; - return start; -} - diff -purN -X dontdiff linux-2.6.5-rc2-mm2.o/net/ipv6/ipv6_syms.c linux-2.6.5-rc2-mm2.w/net/ipv6/ipv6_syms.c --- linux-2.6.5-rc2-mm2.o/net/ipv6/ipv6_syms.c 2004-03-11 09:13:23.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/net/ipv6/ipv6_syms.c 2004-03-24 23:28:00.000000000 -0500 @@ -41,9 +41,7 @@ EXPORT_SYMBOL(xfrm6_rcv); #endif EXPORT_SYMBOL(rt6_lookup); EXPORT_SYMBOL(fl6_sock_lookup); -EXPORT_SYMBOL(ipv6_ext_hdr); EXPORT_SYMBOL(ip6_append_data); EXPORT_SYMBOL(ip6_flush_pending_frames); EXPORT_SYMBOL(ip6_push_pending_frames); EXPORT_SYMBOL(ipv6_push_nfrag_opts); -EXPORT_SYMBOL(ipv6_skip_exthdr); diff -purN -X dontdiff linux-2.6.5-rc2-mm2.o/net/ipv6/libipv6.c linux-2.6.5-rc2-mm2.w/net/ipv6/libipv6.c --- linux-2.6.5-rc2-mm2.o/net/ipv6/libipv6.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/net/ipv6/libipv6.c 2004-03-24 23:20:54.000000000 -0500 @@ -0,0 +1,108 @@ +/* + * IPv6 library code, needed by static components when full IPv6 support is + * not configured or static. + */ +#include + +/* + * find out if nexthdr is a well-known extension header or a protocol + */ + +int ipv6_ext_hdr(u8 nexthdr) +{ + /* + * find out if nexthdr is an extension header or a protocol + */ + return ( (nexthdr == NEXTHDR_HOP) || + (nexthdr == NEXTHDR_ROUTING) || + (nexthdr == NEXTHDR_FRAGMENT) || + (nexthdr == NEXTHDR_AUTH) || + (nexthdr == NEXTHDR_NONE) || + (nexthdr == NEXTHDR_DEST) ); +} + +/* + * Skip any extension headers. This is used by the ICMP module. + * + * Note that strictly speaking this conflicts with RFC 2460 4.0: + * ...The contents and semantics of each extension header determine whether + * or not to proceed to the next header. Therefore, extension headers must + * be processed strictly in the order they appear in the packet; a + * receiver must not, for example, scan through a packet looking for a + * particular kind of extension header and process that header prior to + * processing all preceding ones. + * + * We do exactly this. This is a protocol bug. We can't decide after a + * seeing an unknown discard-with-error flavour TLV option if it's a + * ICMP error message or not (errors should never be send in reply to + * ICMP error messages). + * + * But I see no other way to do this. This might need to be reexamined + * when Linux implements ESP (and maybe AUTH) headers. + * --AK + * + * This function parses (probably truncated) exthdr set "hdr" + * of length "len". "nexthdrp" initially points to some place, + * where type of the first header can be found. + * + * It skips all well-known exthdrs, and returns pointer to the start + * of unparsable area i.e. the first header with unknown type. + * If it is not NULL *nexthdr is updated by type/protocol of this header. + * + * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL. + * - it may return pointer pointing beyond end of packet, + * if the last recognized header is truncated in the middle. + * - if packet is truncated, so that all parsed headers are skipped, + * it returns NULL. + * - First fragment header is skipped, not-first ones + * are considered as unparsable. + * - ESP is unparsable for now and considered like + * normal payload protocol. + * - Note also special handling of AUTH header. Thanks to IPsec wizards. + * + * --ANK (980726) + */ + +int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, int len) +{ + u8 nexthdr = *nexthdrp; + + while (ipv6_ext_hdr(nexthdr)) { + struct ipv6_opt_hdr hdr; + int hdrlen; + + if (len < (int)sizeof(struct ipv6_opt_hdr)) + return -1; + if (nexthdr == NEXTHDR_NONE) + return -1; + if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) + BUG(); + if (nexthdr == NEXTHDR_FRAGMENT) { + unsigned short frag_off; + if (skb_copy_bits(skb, + start+offsetof(struct frag_hdr, + frag_off), + &frag_off, + sizeof(frag_off))) { + return -1; + } + + if (ntohs(frag_off) & ~0x7) + break; + hdrlen = 8; + } else if (nexthdr == NEXTHDR_AUTH) + hdrlen = (hdr.hdrlen+2)<<2; + else + hdrlen = ipv6_optlen(&hdr); + + nexthdr = hdr.nexthdr; + len -= hdrlen; + start += hdrlen; + } + + *nexthdrp = nexthdr; + return start; +} + +EXPORT_SYMBOL(ipv6_ext_hdr); +EXPORT_SYMBOL(ipv6_skip_exthdr); diff -purN -X dontdiff linux-2.6.5-rc2-mm2.o/net/ipv6/Makefile linux-2.6.5-rc2-mm2.w/net/ipv6/Makefile --- linux-2.6.5-rc2-mm2.o/net/ipv6/Makefile 2003-09-27 20:50:39.000000000 -0400 +++ linux-2.6.5-rc2-mm2.w/net/ipv6/Makefile 2004-03-24 23:25:36.000000000 -0500 @@ -19,3 +19,4 @@ obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o +obj-$(CONFIG_INET) += libipv6.o diff -purN -X dontdiff linux-2.6.5-rc2-mm2.o/net/Makefile linux-2.6.5-rc2-mm2.w/net/Makefile --- linux-2.6.5-rc2-mm2.o/net/Makefile 2004-03-24 23:06:30.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/net/Makefile 2004-03-24 23:21:39.000000000 -0500 @@ -16,7 +16,7 @@ obj-$(CONFIG_LLC) += llc/ obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ obj-$(CONFIG_INET) += ipv4/ xfrm/ obj-$(CONFIG_UNIX) += unix/ -obj-$(CONFIG_IPV6) += ipv6/ +obj-$(CONFIG_INET) += ipv6/ obj-$(CONFIG_PACKET) += packet/ obj-$(CONFIG_NET_KEY) += key/ obj-$(CONFIG_NET_SCHED) += sched/ diff -purN -X dontdiff linux-2.6.5-rc2-mm2.o/security/selinux/avc.c linux-2.6.5-rc2-mm2.w/security/selinux/avc.c --- linux-2.6.5-rc2-mm2.o/security/selinux/avc.c 2004-03-24 23:06:30.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/security/selinux/avc.c 2004-03-24 23:31:16.000000000 -0500 @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "avc.h" #include "avc_ss.h" #ifdef CONFIG_AUDIT @@ -423,6 +425,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. @@ -594,13 +607,13 @@ 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; int len = 0; char *p = NULL; 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"); @@ -608,6 +621,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) { @@ -631,10 +657,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", diff -purN -X dontdiff linux-2.6.5-rc2-mm2.o/security/selinux/hooks.c linux-2.6.5-rc2-mm2.w/security/selinux/hooks.c --- linux-2.6.5-rc2-mm2.o/security/selinux/hooks.c 2004-03-24 23:06:30.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/security/selinux/hooks.c 2004-03-25 14:56:19.000000000 -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,35 +2648,32 @@ 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; - struct iphdr *iph; + int offset, ihlen, ret; + 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; - - iph = skb->nh.iph; - ihlen = iph->ihl * 4; - if (ihlen < sizeof(struct iphdr)) + + ihlen = iph.ihl * 4; + if (ihlen < sizeof(iph)) goto out; - 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) { + switch (iph.protocol) { case IPPROTO_TCP: { - int offset; struct tcphdr tcph; - if (ntohs(iph->frag_off) & IP_OFFSET) + if (ntohs(iph.frag_off) & IP_OFFSET) break; - - if (dlen < sizeof(tcph)) - break; - offset = skb->nh.raw - skb->data + ihlen; + offset += ihlen; if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0) break; @@ -2684,16 +2683,12 @@ static void selinux_parse_skb_ipv4(struc } case IPPROTO_UDP: { - int offset; struct udphdr udph; - if (ntohs(iph->frag_off) & IP_OFFSET) + if (ntohs(iph.frag_off) & IP_OFFSET) break; - if (dlen < sizeof(udph)) - break; - - offset = skb->nh.raw - skb->data + ihlen; + offset += ihlen; if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0) break; @@ -2706,7 +2701,96 @@ static void selinux_parse_skb_ipv4(struc break; } out: - return; + 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; + + if (skb_copy_bits(skb, offset, &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 /* 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 */ @@ -2769,6 +2853,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 +2861,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 +2919,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,21 +3071,26 @@ 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; + /* 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; /* TCP control messages don't always have a socket. */ @@ -3025,7 +3135,13 @@ 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; + + 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, netif_perm, &nsec->avcr, &ad); @@ -3034,12 +3150,13 @@ 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; err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad); + if (err) + goto out; if (recv_perm) { u32 port_sid; @@ -3054,7 +3171,6 @@ static int selinux_socket_sock_rcv_skb(s err = avc_has_perm(isec->sid, port_sid, isec->sclass, recv_perm, NULL, &ad); } - out: return err; } @@ -3108,18 +3224,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 +3285,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 +3304,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 +3334,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) || 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_SECURITY_NETWORK */ @@ -4022,14 +4169,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) || 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) { int err = 0; @@ -4039,10 +4198,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) || defined(CONFIG_IPV6_MODULE) + + 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 /* IPV6 */ out: return err; } diff -purN -X dontdiff linux-2.6.5-rc2-mm2.o/security/selinux/include/avc.h linux-2.6.5-rc2-mm2.w/security/selinux/include/avc.h --- linux-2.6.5-rc2-mm2.o/security/selinux/include/avc.h 2004-03-24 23:06:30.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/security/selinux/include/avc.h 2004-03-24 23:31:16.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 -purN -X dontdiff linux-2.6.5-rc2-mm2.o/security/selinux/include/security.h linux-2.6.5-rc2-mm2.w/security/selinux/include/security.h --- linux-2.6.5-rc2-mm2.o/security/selinux/include/security.h 2004-03-21 09:36:02.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/security/selinux/include/security.h 2004-03-24 23:31:16.000000000 -0500 @@ -15,8 +15,15 @@ #define SECCLASS_NULL 0x0000 /* no class */ #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 extern int selinux_enabled; diff -purN -X dontdiff linux-2.6.5-rc2-mm2.o/security/selinux/selinuxfs.c linux-2.6.5-rc2-mm2.w/security/selinux/selinuxfs.c --- linux-2.6.5-rc2-mm2.o/security/selinux/selinuxfs.c 2004-03-21 09:36:02.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/security/selinux/selinuxfs.c 2004-03-24 23:31:16.000000000 -0500 @@ -164,7 +164,7 @@ static ssize_t sel_read_policyvers(struc return -ENOMEM; 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) { free_page((unsigned long)page); return length; diff -purN -X dontdiff linux-2.6.5-rc2-mm2.o/security/selinux/ss/policydb.c linux-2.6.5-rc2-mm2.w/security/selinux/ss/policydb.c --- linux-2.6.5-rc2-mm2.o/security/selinux/ss/policydb.c 2004-03-21 09:36:02.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/security/selinux/ss/policydb.c 2004-03-25 15:59:34.539709336 -0500 @@ -48,6 +48,45 @@ static unsigned int symtab_sizes[SYM_NUM 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. */ @@ -1086,9 +1125,10 @@ int policydb_read(struct policydb *p, vo struct role_trans *tr, *ltr; struct ocontext *l, *c, *newc; 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; char *policydb_str; + struct policydb_compat_info *info; config = 0; mls_set_config(config); @@ -1151,12 +1191,15 @@ int policydb_read(struct policydb *p, vo for (i = 0; i < 4; i++) buf[i] = le32_to_cpu(buf[i]); - policy_ver = buf[0]; - if (policy_ver != POLICYDB_VERSION && policy_ver != POLICYDB_VERSION_COMPAT) { - printk(KERN_ERR "security: policydb version %d does not match " - "my version %d\n", buf[0], POLICYDB_VERSION); - goto bad; + r_policyvers = buf[0]; + if (r_policyvers < POLICYDB_VERSION_MIN || + r_policyvers > POLICYDB_VERSION_MAX) { + printk(KERN_ERR "security: policydb version %d does not match " + "my version range %d-%d\n", + buf[0], POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX); + goto bad; } + if (buf[1] != config) { printk(KERN_ERR "security: policydb configuration (%s) does " "not match my configuration (%s)\n", @@ -1164,30 +1207,27 @@ int policydb_read(struct policydb *p, vo mls_config(config)); goto bad; } + - if (policy_ver == POLICYDB_VERSION_COMPAT) { - if (buf[2] != (SYM_NUM - 1) || buf[3] != OCON_NUM) { - printk(KERN_ERR "security: policydb table sizes (%d,%d) do " - "not match mine (%d,%d)\n", - buf[2], buf[3], SYM_NUM, OCON_NUM); - goto bad; - } - num_syms = SYM_NUM - 1; - } else { - if (buf[2] != SYM_NUM || buf[3] != OCON_NUM) { - printk(KERN_ERR "security: policydb table sizes (%d,%d) do " - "not match mine (%d,%d)\n", - buf[2], buf[3], SYM_NUM, OCON_NUM); - goto bad; - } - num_syms = SYM_NUM; + info = policydb_lookup_compat(r_policyvers); + if (!info) { + printk(KERN_ERR "security: unable to find policy compat info " + "for version %d\n", r_policyvers); + goto bad; + } + + if (buf[2] != info->sym_num || buf[3] != info->ocon_num) { + printk(KERN_ERR "security: policydb table sizes (%d,%d) do " + "not match mine (%d,%d)\n", buf[2], buf[3], + info->sym_num, info->ocon_num); + goto bad; } rc = mls_read_nlevels(p, fp); if (rc) goto bad; - for (i = 0; i < num_syms; i++) { + for (i = 0; i < info->sym_num; i++) { buf = next_entry(fp, sizeof(u32)*2); if (!buf) { rc = -EINVAL; @@ -1208,7 +1248,7 @@ int policydb_read(struct policydb *p, vo if (rc) goto bad; - if (policy_ver == POLICYDB_VERSION) { + if (r_policyvers >= POLICYDB_VERSION_BOOL) { rc = cond_read_list(p, fp); if (rc) goto bad; @@ -1281,7 +1321,7 @@ int policydb_read(struct policydb *p, vo if (rc) goto bad; - for (i = 0; i < OCON_NUM; i++) { + for (i = 0; i < info->ocon_num; i++) { buf = next_entry(fp, sizeof(u32)); if (!buf) { rc = -EINVAL; @@ -1379,6 +1419,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 -purN -X dontdiff linux-2.6.5-rc2-mm2.o/security/selinux/ss/policydb.h linux-2.6.5-rc2-mm2.w/security/selinux/ss/policydb.h --- linux-2.6.5-rc2-mm2.o/security/selinux/ss/policydb.h 2004-03-21 09:36:02.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/security/selinux/ss/policydb.h 2004-03-24 23:31:16.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 -purN -X dontdiff linux-2.6.5-rc2-mm2.o/security/selinux/ss/services.c linux-2.6.5-rc2-mm2.w/security/selinux/ss/services.c --- linux-2.6.5-rc2-mm2.o/security/selinux/ss/services.c 2004-03-24 23:06:30.000000000 -0500 +++ linux-2.6.5-rc2-mm2.w/security/selinux/ss/services.c 2004-03-24 23:31:16.000000000 -0500 @@ -1190,6 +1190,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). @@ -1204,22 +1216,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) {