[MLSXFRM]: Add flow labeling

This labels the flows that could utilize IPSec xfrms at the points the
flows are defined so that IPSec policy and SAs at the right label can
be used.

The following protos are currently not handled, but they should
continue to be able to use single-labeled IPSec like they currently
do.

ipmr
ip_gre
ipip
igmp
sit
sctp
ip6_tunnel (IPv6 over IPv6 tunnel device)
decnet

Signed-off-by: Venkat Yekkirala <vyekkirala@TrustedCS.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Venkat Yekkirala 2006-08-04 23:12:42 -07:00 committed by David S. Miller
parent 4e2ba18eae
commit beb8d13bed
26 changed files with 79 additions and 40 deletions

View File

@ -32,6 +32,7 @@
#include <linux/sched.h>
#include <linux/key.h>
#include <linux/xfrm.h>
#include <net/flow.h>
struct ctl_table;
@ -815,8 +816,8 @@ struct swap_info_struct;
* Deallocate security structure.
* @sk_clone_security:
* Clone/copy security structure.
* @sk_getsid:
* Retrieve the LSM-specific sid for the sock to enable caching of network
* @sk_getsecid:
* Retrieve the LSM-specific secid for the sock to enable caching of network
* authorizations.
*
* Security hooks for XFRM operations.
@ -882,8 +883,9 @@ struct swap_info_struct;
* Return 1 if there is a match.
* @xfrm_decode_session:
* @skb points to skb to decode.
* @fl points to the flow key to set.
* Return 0 if successful decoding.
* @secid points to the flow key secid to set.
* @ckall says if all xfrms used should be checked for same secid.
* Return 0 if ckall is zero or all xfrms used have the same secid.
*
* Security hooks affecting all Key Management operations
*
@ -1353,7 +1355,7 @@ struct security_operations {
int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
void (*sk_free_security) (struct sock *sk);
void (*sk_clone_security) (const struct sock *sk, struct sock *newsk);
unsigned int (*sk_getsid) (struct sock *sk, struct flowi *fl, u8 dir);
void (*sk_getsecid) (struct sock *sk, u32 *secid);
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
@ -1370,7 +1372,7 @@ struct security_operations {
int (*xfrm_state_pol_flow_match)(struct xfrm_state *x,
struct xfrm_policy *xp, struct flowi *fl);
int (*xfrm_flow_state_match)(struct flowi *fl, struct xfrm_state *xfrm);
int (*xfrm_decode_session)(struct sk_buff *skb, struct flowi *fl);
int (*xfrm_decode_session)(struct sk_buff *skb, u32 *secid, int ckall);
#endif /* CONFIG_SECURITY_NETWORK_XFRM */
/* key management security hooks */
@ -2917,9 +2919,9 @@ static inline void security_sk_clone(const struct sock *sk, struct sock *newsk)
return security_ops->sk_clone_security(sk, newsk);
}
static inline unsigned int security_sk_sid(struct sock *sk, struct flowi *fl, u8 dir)
static inline void security_sk_classify_flow(struct sock *sk, struct flowi *fl)
{
return security_ops->sk_getsid(sk, fl, dir);
security_ops->sk_getsecid(sk, &fl->secid);
}
#else /* CONFIG_SECURITY_NETWORK */
static inline int security_unix_stream_connect(struct socket * sock,
@ -3047,9 +3049,8 @@ static inline void security_sk_clone(const struct sock *sk, struct sock *newsk)
{
}
static inline unsigned int security_sk_sid(struct sock *sk, struct flowi *fl, u8 dir)
static inline void security_sk_classify_flow(struct sock *sk, struct flowi *fl)
{
return 0;
}
#endif /* CONFIG_SECURITY_NETWORK */
@ -3114,9 +3115,16 @@ static inline int security_xfrm_flow_state_match(struct flowi *fl, struct xfrm_s
return security_ops->xfrm_flow_state_match(fl, xfrm);
}
static inline int security_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
static inline int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid)
{
return security_ops->xfrm_decode_session(skb, fl);
return security_ops->xfrm_decode_session(skb, secid, 1);
}
static inline void security_skb_classify_flow(struct sk_buff *skb, struct flowi *fl)
{
int rc = security_ops->xfrm_decode_session(skb, &fl->secid, 0);
BUG_ON(rc);
}
#else /* CONFIG_SECURITY_NETWORK_XFRM */
static inline int security_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx)
@ -3176,11 +3184,15 @@ static inline int security_xfrm_flow_state_match(struct flowi *fl,
return 1;
}
static inline int security_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
static inline int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid)
{
return 0;
}
static inline void security_skb_classify_flow(struct sk_buff *skb, struct flowi *fl)
{
}
#endif /* CONFIG_SECURITY_NETWORK_XFRM */
#ifdef CONFIG_KEYS

View File

@ -32,6 +32,7 @@
#include <linux/route.h>
#include <linux/ip.h>
#include <linux/cache.h>
#include <linux/security.h>
#ifndef __KERNEL__
#warning This file is not supposed to be used outside of kernel.
@ -166,6 +167,7 @@ static inline int ip_route_connect(struct rtable **rp, u32 dst,
ip_rt_put(*rp);
*rp = NULL;
}
security_sk_classify_flow(sk, &fl);
return ip_route_output_flow(rp, &fl, sk, 0);
}
@ -182,6 +184,7 @@ static inline int ip_route_newports(struct rtable **rp, u8 protocol,
fl.proto = protocol;
ip_rt_put(*rp);
*rp = NULL;
security_sk_classify_flow(sk, &fl);
return ip_route_output_flow(rp, &fl, sk, 0);
}
return 0;

View File

@ -678,6 +678,7 @@ static struct dst_entry* dccp_v4_route_skb(struct sock *sk,
}
};
security_skb_classify_flow(skb, &fl);
if (ip_route_output_flow(&rt, &fl, sk, 0)) {
IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
return NULL;

View File

@ -201,6 +201,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
fl.oif = sk->sk_bound_dev_if;
fl.fl_ip_dport = usin->sin6_port;
fl.fl_ip_sport = inet->sport;
security_sk_classify_flow(sk, &fl);
if (np->opt != NULL && np->opt->srcrt != NULL) {
const struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
@ -322,6 +323,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
fl.oif = sk->sk_bound_dev_if;
fl.fl_ip_dport = inet->dport;
fl.fl_ip_sport = inet->sport;
security_sk_classify_flow(sk, &fl);
err = ip6_dst_lookup(sk, &dst, &fl);
if (err) {
@ -422,6 +424,7 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
fl.oif = ireq6->iif;
fl.fl_ip_dport = inet_rsk(req)->rmt_port;
fl.fl_ip_sport = inet_sk(sk)->sport;
security_sk_classify_flow(sk, &fl);
if (dst == NULL) {
opt = np->opt;
@ -566,6 +569,7 @@ static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
fl.oif = inet6_iif(rxskb);
fl.fl_ip_dport = dh->dccph_dport;
fl.fl_ip_sport = dh->dccph_sport;
security_skb_classify_flow(rxskb, &fl);
/* sk = NULL, but it is safe for now. RST socket required. */
if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
@ -622,6 +626,7 @@ static void dccp_v6_reqsk_send_ack(struct sk_buff *rxskb,
fl.oif = inet6_iif(rxskb);
fl.fl_ip_dport = dh->dccph_dport;
fl.fl_ip_sport = dh->dccph_sport;
security_skb_classify_flow(rxskb, &fl);
if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
@ -842,6 +847,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
fl.oif = sk->sk_bound_dev_if;
fl.fl_ip_dport = inet_rsk(req)->rmt_port;
fl.fl_ip_sport = inet_sk(sk)->sport;
security_sk_classify_flow(sk, &fl);
if (ip6_dst_lookup(sk, &dst, &fl))
goto out;

View File

@ -1074,6 +1074,7 @@ int inet_sk_rebuild_header(struct sock *sk)
},
};
security_sk_classify_flow(sk, &fl);
err = ip_route_output_flow(&rt, &fl, sk, 0);
}
if (!err)

View File

@ -406,6 +406,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
.saddr = rt->rt_spec_dst,
.tos = RT_TOS(skb->nh.iph->tos) } },
.proto = IPPROTO_ICMP };
security_skb_classify_flow(skb, &fl);
if (ip_route_output_key(&rt, &fl))
goto out_unlock;
}
@ -560,6 +561,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, u32 info)
}
}
};
security_skb_classify_flow(skb_in, &fl);
if (ip_route_output_key(&rt, &fl))
goto out_unlock;
}

View File

@ -327,6 +327,7 @@ struct dst_entry* inet_csk_route_req(struct sock *sk,
{ .sport = inet_sk(sk)->sport,
.dport = ireq->rmt_port } } };
security_sk_classify_flow(sk, &fl);
if (ip_route_output_flow(&rt, &fl, sk, 0)) {
IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
return NULL;

View File

@ -328,6 +328,7 @@ int ip_queue_xmit(struct sk_buff *skb, int ipfragok)
* keep trying until route appears or the connection times
* itself out.
*/
security_sk_classify_flow(sk, &fl);
if (ip_route_output_flow(&rt, &fl, sk, 0))
goto no_route;
}
@ -1366,6 +1367,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
{ .sport = skb->h.th->dest,
.dport = skb->h.th->source } },
.proto = sk->sk_protocol };
security_skb_classify_flow(skb, &fl);
if (ip_route_output_key(&rt, &fl))
return;
}

View File

@ -90,6 +90,7 @@ static inline struct rtable *route_reverse(struct sk_buff *skb,
fl.proto = IPPROTO_TCP;
fl.fl_ip_sport = tcph->dest;
fl.fl_ip_dport = tcph->source;
security_skb_classify_flow(skb, &fl);
xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0);

View File

@ -484,6 +484,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
if (!inet->hdrincl)
raw_probe_proto_opt(&fl, msg);
security_sk_classify_flow(sk, &fl);
err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT));
}
if (err)

View File

@ -259,6 +259,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
.uli_u = { .ports =
{ .sport = skb->h.th->dest,
.dport = skb->h.th->source } } };
security_sk_classify_flow(sk, &fl);
if (ip_route_output_key(&rt, &fl)) {
reqsk_free(req);
goto out;

View File

@ -603,6 +603,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
.uli_u = { .ports =
{ .sport = inet->sport,
.dport = dport } } };
security_sk_classify_flow(sk, &fl);
err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT));
if (err)
goto out;

View File

@ -637,6 +637,7 @@ int inet6_sk_rebuild_header(struct sock *sk)
fl.oif = sk->sk_bound_dev_if;
fl.fl_ip_dport = inet->dport;
fl.fl_ip_sport = inet->sport;
security_sk_classify_flow(sk, &fl);
if (np->opt && np->opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;

View File

@ -156,6 +156,8 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (!fl.oif && (addr_type&IPV6_ADDR_MULTICAST))
fl.oif = np->mcast_oif;
security_sk_classify_flow(sk, &fl);
if (flowlabel) {
if (flowlabel->opt && flowlabel->opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt;

View File

@ -358,6 +358,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
fl.oif = iif;
fl.fl_icmp_type = type;
fl.fl_icmp_code = code;
security_skb_classify_flow(skb, &fl);
if (icmpv6_xmit_lock())
return;
@ -472,6 +473,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
ipv6_addr_copy(&fl.fl6_src, saddr);
fl.oif = skb->dev->ifindex;
fl.fl_icmp_type = ICMPV6_ECHO_REPLY;
security_skb_classify_flow(skb, &fl);
if (icmpv6_xmit_lock())
return;

View File

@ -157,6 +157,7 @@ int inet6_csk_xmit(struct sk_buff *skb, int ipfragok)
fl.oif = sk->sk_bound_dev_if;
fl.fl_ip_sport = inet->sport;
fl.fl_ip_dport = inet->dport;
security_sk_classify_flow(sk, &fl);
if (np->opt && np->opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;

View File

@ -419,6 +419,7 @@ static inline void ndisc_flow_init(struct flowi *fl, u8 type,
fl->proto = IPPROTO_ICMPV6;
fl->fl_icmp_type = type;
fl->fl_icmp_code = 0;
security_sk_classify_flow(ndisc_socket->sk, fl);
}
static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,

View File

@ -96,6 +96,7 @@ static void send_reset(struct sk_buff *oldskb)
ipv6_addr_copy(&fl.fl6_dst, &oip6h->saddr);
fl.fl_ip_sport = otcph.dest;
fl.fl_ip_dport = otcph.source;
security_skb_classify_flow(oldskb, &fl);
dst = ip6_route_output(NULL, &fl);
if (dst == NULL)
return;

View File

@ -759,6 +759,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst))
fl.oif = np->mcast_oif;
security_sk_classify_flow(sk, &fl);
err = ip6_dst_lookup(sk, &dst, &fl);
if (err)

View File

@ -251,6 +251,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
final_p = &final;
}
security_sk_classify_flow(sk, &fl);
err = ip6_dst_lookup(sk, &dst, &fl);
if (err)
goto failure;
@ -374,6 +376,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
fl.oif = sk->sk_bound_dev_if;
fl.fl_ip_dport = inet->dport;
fl.fl_ip_sport = inet->sport;
security_skb_classify_flow(skb, &fl);
if ((err = ip6_dst_lookup(sk, &dst, &fl))) {
sk->sk_err_soft = -err;
@ -467,6 +470,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
fl.oif = treq->iif;
fl.fl_ip_dport = inet_rsk(req)->rmt_port;
fl.fl_ip_sport = inet_sk(sk)->sport;
security_sk_classify_flow(sk, &fl);
if (dst == NULL) {
opt = np->opt;
@ -625,6 +629,7 @@ static void tcp_v6_send_reset(struct sk_buff *skb)
fl.oif = inet6_iif(skb);
fl.fl_ip_dport = t1->dest;
fl.fl_ip_sport = t1->source;
security_skb_classify_flow(skb, &fl);
/* sk = NULL, but it is safe for now. RST socket required. */
if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
@ -691,6 +696,7 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
fl.oif = inet6_iif(skb);
fl.fl_ip_dport = t1->dest;
fl.fl_ip_sport = t1->source;
security_skb_classify_flow(skb, &fl);
if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
@ -923,6 +929,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
fl.oif = sk->sk_bound_dev_if;
fl.fl_ip_dport = inet_rsk(req)->rmt_port;
fl.fl_ip_sport = inet_sk(sk)->sport;
security_sk_classify_flow(sk, &fl);
if (ip6_dst_lookup(sk, &dst, &fl))
goto out;

View File

@ -782,6 +782,8 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
connected = 0;
}
security_sk_classify_flow(sk, fl);
err = ip6_sk_dst_lookup(sk, &dst, fl);
if (err)
goto out;

View File

@ -863,7 +863,6 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
u16 family;
u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
fl->secid = security_sk_sid(sk, fl, dir);
restart:
genid = atomic_read(&flow_cache_genid);
policy = NULL;
@ -1039,7 +1038,7 @@ xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family
return -EAFNOSUPPORT;
afinfo->decode_session(skb, fl);
err = security_xfrm_decode_session(skb, fl);
err = security_xfrm_decode_session(skb, &fl->secid);
xfrm_policy_put_afinfo(afinfo);
return err;
}

View File

@ -809,9 +809,8 @@ static inline void dummy_sk_clone_security (const struct sock *sk, struct sock *
{
}
static unsigned int dummy_sk_getsid(struct sock *sk, struct flowi *fl, u8 dir)
static inline void dummy_sk_getsecid(struct sock *sk, u32 *secid)
{
return 0;
}
#endif /* CONFIG_SECURITY_NETWORK */
@ -866,7 +865,7 @@ static int dummy_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm
return 1;
}
static int dummy_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
static int dummy_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall)
{
return 0;
}
@ -1083,7 +1082,7 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, sk_alloc_security);
set_to_dummy_if_null(ops, sk_free_security);
set_to_dummy_if_null(ops, sk_clone_security);
set_to_dummy_if_null(ops, sk_getsid);
set_to_dummy_if_null(ops, sk_getsecid);
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
set_to_dummy_if_null(ops, xfrm_policy_alloc_security);

View File

@ -3561,14 +3561,14 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
newssec->peer_sid = ssec->peer_sid;
}
static unsigned int selinux_sk_getsid_security(struct sock *sk, struct flowi *fl, u8 dir)
static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
{
if (!sk)
return selinux_no_sk_sid(fl);
*secid = SECINITSID_ANY_SOCKET;
else {
struct sk_security_struct *sksec = sk->sk_security;
return sksec->sid;
*secid = sksec->sid;
}
}
@ -4622,7 +4622,7 @@ static struct security_operations selinux_ops = {
.sk_alloc_security = selinux_sk_alloc_security,
.sk_free_security = selinux_sk_free_security,
.sk_clone_security = selinux_sk_clone_security,
.sk_getsid = selinux_sk_getsid_security,
.sk_getsecid = selinux_sk_getsecid,
#ifdef CONFIG_SECURITY_NETWORK_XFRM
.xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,

View File

@ -19,7 +19,7 @@ int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir);
int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
struct xfrm_policy *xp, struct flowi *fl);
int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm);
int selinux_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl);
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall);
/*
@ -33,18 +33,6 @@ static inline struct inode_security_struct *get_sock_isec(struct sock *sk)
return SOCK_INODE(sk->sk_socket)->i_security;
}
static inline u32 selinux_no_sk_sid(struct flowi *fl)
{
/* NOTE: no sock occurs on ICMP reply, forwards, ... */
/* icmp_reply: authorize as kernel packet */
if (fl && fl->proto == IPPROTO_ICMP) {
return SECINITSID_KERNEL;
}
return SECINITSID_ANY_SOCKET;
}
#ifdef CONFIG_SECURITY_NETWORK_XFRM
int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
struct avc_audit_data *ad);

View File

@ -158,11 +158,11 @@ int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm)
* LSM hook implementation that determines the sid for the session.
*/
int selinux_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
{
struct sec_path *sp;
fl->secid = SECSID_NULL;
*sid = SECSID_NULL;
if (skb == NULL)
return 0;
@ -177,10 +177,13 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
struct xfrm_sec_ctx *ctx = x->security;
if (!sid_set) {
fl->secid = ctx->ctx_sid;
*sid = ctx->ctx_sid;
sid_set = 1;
if (!ckall)
break;
}
else if (fl->secid != ctx->ctx_sid)
else if (*sid != ctx->ctx_sid)
return -EINVAL;
}
}