From: Venkat Yekkirala <[EMAIL PROTECTED]> This fixes the secid reconciliation code in the following ways:
1. Null-out secmark on an outgoing packet after we are done with all the checks. This has been necessitated by the fact that some packets sent to a multicast address could arrive back on a non-loopback interface but with the secmark intact. This would result in the current flow_out control logic to use it as a security point context when no explicit security points have been defined for the inbound packet. 2. Label udp/raw packets with the label of the socket. 3. Label igmp traffic with the igmp_packet initial context. 4. Limit flow-controlling of loopback traffic to the socket.recv permission check. This means that packet.flow_in/flow_out checks are no longer applicable to loopback traffic. This is because of current implementation constraints. DOCUMENTATION OF SECID RECONCILIATION AND FLOW CONTROL FOR POLICY WRITERS: ON INBOUND: 1. PACKETS ENTERING SYSTEM FROM A NON-LOOPBACK DEVICE: Can a packet "carrying" external domain label x_t "flow_in" thru the security point with the peer domain label p_d_t? NOTE: a. x_t defaults to unlabeled_t, if no external label. b. p_d_t defaults to network_t in the absence of any applicable [conn]secmark rules for the packet. If there are multiple secmark rules applicable to a packet, the context on the LAST rule will apply. NO: Drop packet. YES: If no external label, let packet "carry" p_d_t. 2. INPUT ONLY: Can a socket "recv" a packet from domain p_d_t? NO: Drop packet. YES: If setting up a tcp connection, set peer context to p_d_t. ON OUTBOUND: 1. Let packet "carry" the originating socket domain label. 2. IPSEC Handling: LABELED IPSEC: If packet "polmatch"es to an otherwise applicable and labeled SPD entry, choose a Security Association (SA) with the SAME context as the domain label being carried by packet. NOTE: If no such SA present, call into IKE with context on packet. NON-LABELED (PLAIN/TRADITIONAL) IPSEC: If there's an applicable SPD entry that does NOT have an explicit context associated with it, an applicable SA that does NOT have an explicit context associated with it is chosen. NOTE: If no such SA present, call into IKE, but with NO context. 3. PACKETS DESTINED FOR NON-LOOPBACK DEVICE: a. IPTABLES Processing: As EACH applicable iptables [CONN]SECMARK rule with domain p_d_t is encountered, do the following: Can a packet carrying domain label a_t "flow_out" of the security point with the domain label p_d_t? NO: Drop packet. YES: Replace the domain label a_t on the packet with the security point label p_d_t. b. Before a packet is let out of the system: Can a packet with domain label p_d_t "flow_out" into the network domain network_t? NO: Drop packet. YES: Let packet out. NOTE: Ideally this check should be applicable only to packets that didn't go thru [conn]secmark checks for outbound, but there's currently no way to know this due to implementation constrains. Hence a blanket check for ALL packets leaving the system. FORWARDED TRAFFIC: Forwarded Traffic will undergo the following: 1. Step 1 under ON INBOUND. 2. Steps 2 and 3 under ON OUTBOUND. Signed-off-by: Venkat Yekkirala <[EMAIL PROTECTED]> --- include/linux/security.h | 24 +++++++++------- include/net/ip.h | 13 ++++++++ include/net/request_sock.h | 11 +++++++ net/ipv4/igmp.c | 4 ++ net/ipv4/raw.c | 2 + net/ipv4/udp.c | 2 + net/netfilter/xt_CONNSECMARK.c | 21 ++++++++++---- net/netfilter/xt_SECMARK.c | 16 ++++++++-- security/dummy.c | 8 ++--- security/selinux/hooks.c | 61 +++++++++++++++++++++++++++++++++-------- 10 files changed, 129 insertions(+), 33 deletions(-) Index: net-2.6_secidfinal/include/linux/security.h =================================================================== --- net-2.6_secidfinal.orig/include/linux/security.h +++ net-2.6_secidfinal/include/linux/security.h @@ -67,6 +67,7 @@ struct xfrm_selector; struct xfrm_policy; struct xfrm_state; struct xfrm_user_sec_ctx; +struct net_device; extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb); extern int cap_netlink_recv(struct sk_buff *skb, int cap); @@ -828,8 +829,8 @@ struct request_sock; * Sets the new child socket's sid to the openreq sid. * @inet_conn_established: * Sets the connection's peersid to the secmark on skb. - * @req_classify_flow: - * Sets the flow's sid to the openreq sid. + * @igmp_classify_skb: + * Classifies an skb representing an igmp packet. * @skb_flow_in: * Checks to see if security policy would allow skb into the system * while also reconciling the xfrm secid, cipso, etc, if any, and @@ -1383,9 +1384,10 @@ struct security_operations { struct request_sock *req); void (*inet_csk_clone)(struct sock *newsk, const struct request_sock *req); void (*inet_conn_established)(struct sock *sk, struct sk_buff *skb); - void (*req_classify_flow)(const struct request_sock *req, struct flowi *fl); + void (*igmp_classify_skb)(struct sk_buff *skb); int (*skb_flow_in)(struct sk_buff *skb, unsigned short family); - int (*skb_flow_out)(struct sk_buff *skb, u32 nf_secid); + int (*skb_flow_out)(struct sk_buff *skb, u32 nf_secid, + const struct net_device *out, unsigned short family); #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM @@ -2956,9 +2958,9 @@ static inline void security_sk_classify_ security_ops->sk_getsecid(sk, &fl->secid); } -static inline void security_req_classify_flow(const struct request_sock *req, struct flowi *fl) +static inline void security_igmp_classify_skb(struct sk_buff *skb) { - security_ops->req_classify_flow(req, fl); + security_ops->igmp_classify_skb(skb); } static inline int security_skb_flow_in(struct sk_buff *skb, @@ -2968,9 +2970,10 @@ static inline int security_skb_flow_in(s } static inline int security_skb_flow_out(struct sk_buff *skb, - u32 nf_secid) + u32 nf_secid, const struct net_device *out, + unsigned short family) { - return security_ops->skb_flow_out(skb, nf_secid); + return security_ops->skb_flow_out(skb, nf_secid, out, family); } static inline void security_sock_graft(struct sock* sk, struct socket *parent) @@ -3126,7 +3129,7 @@ static inline void security_sk_classify_ { } -static inline void security_req_classify_flow(const struct request_sock *req, struct flowi *fl) +static inline void security_igmp_classify_skb(struct sk_buff *skb) { } @@ -3137,7 +3140,8 @@ static inline int security_skb_flow_in(s } static inline int security_skb_flow_out(struct sk_buff *skb, - u32 nf_secid) + u32 nf_secid, const struct net_device *out, + unsigned short family) { return -ENOENT; } Index: net-2.6_secidfinal/include/net/ip.h =================================================================== --- net-2.6_secidfinal.orig/include/net/ip.h +++ net-2.6_secidfinal/include/net/ip.h @@ -388,6 +388,14 @@ extern struct ctl_table ipv4_table[]; #ifdef CONFIG_SECURITY_NETWORK +extern struct security_operations *security_ops; + +static inline void security_sk_classify_ipcm(struct sock *sk, + struct ipcm_cookie *ipc) +{ + security_ops->sk_getsecid(sk, &ipc->secid); +} + static inline void security_skb_classify_ipcm(struct sk_buff *skb, struct ipcm_cookie *ipc) { @@ -402,6 +410,11 @@ static inline void security_ipcm_classif #else +static inline void security_sk_classify_ipcm(struct sock *sk, + struct ipcm_cookie *ipc) +{ +} + static inline void security_skb_classify_ipcm(struct sk_buff *skb, struct ipcm_cookie *ipc) { Index: net-2.6_secidfinal/include/net/request_sock.h =================================================================== --- net-2.6_secidfinal.orig/include/net/request_sock.h +++ net-2.6_secidfinal/include/net/request_sock.h @@ -262,6 +262,12 @@ static inline void reqsk_queue_hash_req( #ifdef CONFIG_SECURITY_NETWORK +static inline void security_req_classify_flow(const struct request_sock *req, + struct flowi *fl) +{ + fl->secid = req->secid; +} + static inline void security_req_classify_skb(struct request_sock *req, struct sk_buff *skb) { @@ -270,6 +276,11 @@ static inline void security_req_classify #else +static inline void security_req_classify_flow(const struct request_sock *req, + struct flowi *fl) +{ +} + static inline void security_req_classify_skb(struct request_sock *req, struct sk_buff *skb) { Index: net-2.6_secidfinal/net/ipv4/igmp.c =================================================================== --- net-2.6_secidfinal.orig/net/ipv4/igmp.c +++ net-2.6_secidfinal/net/ipv4/igmp.c @@ -293,6 +293,8 @@ static struct sk_buff *igmpv3_newpack(st if (skb == NULL) return NULL; + security_igmp_classify_skb(skb); + { struct flowi fl = { .oif = dev->ifindex, .nl_u = { .ip4_u = { @@ -658,6 +660,8 @@ static int igmp_send_report(struct in_de return -1; } + security_igmp_classify_skb(skb); + skb->dst = &rt->u.dst; skb_reserve(skb, LL_RESERVED_SPACE(dev)); Index: net-2.6_secidfinal/net/ipv4/raw.c =================================================================== --- net-2.6_secidfinal.orig/net/ipv4/raw.c +++ net-2.6_secidfinal/net/ipv4/raw.c @@ -433,6 +433,8 @@ static int raw_sendmsg(struct kiocb *ioc ipc.opt = NULL; ipc.oif = sk->sk_bound_dev_if; + security_sk_classify_ipcm(sk, &ipc); + if (msg->msg_controllen) { err = ip_cmsg_send(msg, &ipc); if (err) Index: net-2.6_secidfinal/net/ipv4/udp.c =================================================================== --- net-2.6_secidfinal.orig/net/ipv4/udp.c +++ net-2.6_secidfinal/net/ipv4/udp.c @@ -588,6 +588,8 @@ int udp_sendmsg(struct kiocb *iocb, stru if (!ipc.opt) ipc.opt = inet->opt; + security_sk_classify_ipcm(sk, &ipc); + saddr = ipc.addr; ipc.addr = faddr = daddr; Index: net-2.6_secidfinal/net/netfilter/xt_CONNSECMARK.c =================================================================== --- net-2.6_secidfinal.orig/net/netfilter/xt_CONNSECMARK.c +++ net-2.6_secidfinal/net/netfilter/xt_CONNSECMARK.c @@ -68,7 +68,8 @@ static void secmark_save(struct sk_buff * On the outbound, filter based on the current secmark. */ static unsigned int secmark_restore(struct sk_buff *skb, unsigned int hooknum, - const struct net_device *in, unsigned short family) + const struct net_device *in, const struct net_device *out, + unsigned short family) { u32 *psecmark; enum ip_conntrack_info ctinfo; @@ -81,14 +82,24 @@ static unsigned int secmark_restore(stru if (outbound(family, hooknum)) { int err; - err = security_skb_flow_out(skb, *psecmark); + /* + * We can't currently flow-control loopback traffic + * very well since we want to retain the secmark of + * the originating socket and if we do retain it, + * it will cause connsecmark save & restore to use this + * socket label and mess up the semantics of the "peer + * security point". + */ + if (out == &loopback_dev) + goto out; + + err = security_skb_flow_out(skb, *psecmark, out, family); if (!err) return NF_DROP; } else /* * inbound: - * loopback traffic should already be labeled - * and any filtering on outbound should suffice + * No filtering of loopback traffic. */ if (in == &loopback_dev) goto out; @@ -119,7 +130,7 @@ static unsigned int target(struct sk_buf break; case CONNSECMARK_RESTORE: - return secmark_restore(skb, hooknum, in, target->family); + return secmark_restore(skb, hooknum, in, out, target->family); break; default: Index: net-2.6_secidfinal/net/netfilter/xt_SECMARK.c =================================================================== --- net-2.6_secidfinal.orig/net/netfilter/xt_SECMARK.c +++ net-2.6_secidfinal/net/netfilter/xt_SECMARK.c @@ -70,14 +70,24 @@ static unsigned int target(struct sk_buf if (outbound(target->family, hooknum)) { int err; - err = security_skb_flow_out(*pskb, secmark); + /* + * We can't currently flow-control loopback traffic + * very well since we want to retain the secmark of + * the originating socket and if we do retain it, + * it will cause connsecmark save & restore to use this + * socket label and mess up the semantics of the "peer + * security point". + */ + if (out == &loopback_dev) + goto out; + + err = security_skb_flow_out(*pskb, secmark, out, target->family); if (!err) return NF_DROP; } else /* * inbound: - * loopback traffic should already be labeled - * and any filtering on outbound should suffice + * No filtering of loopback traffic. */ if (in == &loopback_dev) goto out; Index: net-2.6_secidfinal/security/dummy.c =================================================================== --- net-2.6_secidfinal.orig/security/dummy.c +++ net-2.6_secidfinal/security/dummy.c @@ -833,8 +833,7 @@ static inline void dummy_inet_conn_estab { } -static inline void dummy_req_classify_flow(const struct request_sock *req, - struct flowi *fl) +static inline void dummy_igmp_classify_skb(struct sk_buff *skb) { } @@ -844,7 +843,8 @@ static inline int dummy_skb_flow_in(stru return -ENOENT; } -static inline int dummy_skb_flow_out(struct sk_buff *skb, u32 nf_secid) +static inline int dummy_skb_flow_out(struct sk_buff *skb, u32 nf_secid, + const struct net_device *out, unsigned short family) { return -ENOENT; } @@ -1124,7 +1124,7 @@ void security_fixup_ops (struct security set_to_dummy_if_null(ops, inet_conn_request); set_to_dummy_if_null(ops, inet_csk_clone); set_to_dummy_if_null(ops, inet_conn_established); - set_to_dummy_if_null(ops, req_classify_flow); + set_to_dummy_if_null(ops, igmp_classify_skb); set_to_dummy_if_null(ops, skb_flow_in); set_to_dummy_if_null(ops, skb_flow_out); #endif /* CONFIG_SECURITY_NETWORK */ Index: net-2.6_secidfinal/security/selinux/hooks.c =================================================================== --- net-2.6_secidfinal.orig/security/selinux/hooks.c +++ net-2.6_secidfinal/security/selinux/hooks.c @@ -3691,35 +3691,42 @@ static void selinux_inet_conn_establishe sksec->peer_sid = skb->secmark; } -static void selinux_req_classify_flow(const struct request_sock *req, - struct flowi *fl) +static void selinux_igmp_classify_skb(struct sk_buff *skb) { - fl->secid = req->secid; + skb->secmark = SECINITSID_IGMP_PACKET; } static int selinux_skb_flow_in(struct sk_buff *skb, unsigned short family) { u32 xfrm_sid; int err; + struct avc_audit_data ad; + char *addrp; + int len; if (selinux_compat_net) return 1; /* - * loopback traffic already labeled and - * flow-controlled on outbound. We may - * need to flow-control on the inbound - * as well if there's ever a use-case for it. + * loopback traffic should already be labeled with + * the originating socket label. */ if (skb->dev == &loopback_dev) return 1; + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]"; + ad.u.net.family = family; + err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); + if (err) + goto out; + err = selinux_xfrm_decode_session(skb, &xfrm_sid, 0); BUG_ON(err); err = avc_has_perm(xfrm_sid, skb->secmark? : SECINITSID_NETMSG, SECCLASS_PACKET, - PACKET__FLOW_IN, NULL); + PACKET__FLOW_IN, &ad); if (err) goto out; @@ -3732,9 +3739,14 @@ out: return err ? 0 : 1; }; -static int selinux_skb_flow_out(struct sk_buff *skb, u32 nf_secid) +static int selinux_skb_flow_out(struct sk_buff *skb, u32 nf_secid, + const struct net_device *out, unsigned short family) { int err; + char *addrp; + int len; + struct net_device *dev = (struct net_device *)out; + struct avc_audit_data ad; if (selinux_compat_net) return 1; @@ -3752,9 +3764,17 @@ static int selinux_skb_flow_out(struct s } } + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = dev->name; + ad.u.net.family = family; + err = selinux_parse_skb(skb, &ad, &addrp, &len, 0); + if (err) + goto out; + err = avc_has_perm(skb->secmark, nf_secid, SECCLASS_PACKET, - PACKET__FLOW_OUT, NULL); + PACKET__FLOW_OUT, &ad); +out: return err ? 0 : 1; } @@ -3915,8 +3935,27 @@ static unsigned int selinux_ip_postroute skb->secmark = sksec->sid; } } + if (out == &loopback_dev) + return NF_ACCEPT; + err = avc_has_perm(skb->secmark, SECINITSID_NETMSG, SECCLASS_PACKET, PACKET__FLOW_OUT, &ad); + + if (skb->secmark) + /* + * Our multicast packets could get copied back + * to us, arriving on a non-loopback device. + * Leaving the secmark intact here will cause it + * to be used as a security point context in + * the flow_in hook above while it's not in fact + * a security point context. + * + * We may be able to retain this marking if + * we can reliably determine that it was a local + * packet although it arrived on a non-loopback + * device, in the flow_in hook above. + */ + skb->secmark = SECSID_NULL; } out: return err ? NF_DROP : NF_ACCEPT; @@ -4824,7 +4863,7 @@ static struct security_operations selinu .inet_conn_request = selinux_inet_conn_request, .inet_csk_clone = selinux_inet_csk_clone, .inet_conn_established = selinux_inet_conn_established, - .req_classify_flow = selinux_req_classify_flow, + .igmp_classify_skb = selinux_igmp_classify_skb, .skb_flow_in = selinux_skb_flow_in, .skb_flow_out = selinux_skb_flow_out, -- paul moore linux security @ hp - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html