Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b5745c59 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull SELinux fixes from James Morris.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
  selinux: process labeled IPsec TCP SYN-ACK packets properly in selinux_ip_postroute()
  selinux: look for IPsec labels on both inbound and outbound packets
  selinux: handle TCP SYN-ACK packets correctly in selinux_ip_postroute()
  selinux: handle TCP SYN-ACK packets correctly in selinux_ip_output()
  selinux: fix possible memory leak
parents 29b1deb2 d93aca60
Loading
Loading
Loading
Loading
+112 −25
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@
#include <net/ip.h>		/* for local_port_range[] */
#include <net/sock.h>
#include <net/tcp.h>		/* struct or_callable used in sock_rcv_skb */
#include <net/inet_connection_sock.h>
#include <net/net_namespace.h>
#include <net/netlabel.h>
#include <linux/uaccess.h>
@@ -3824,7 +3825,7 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
	u32 nlbl_sid;
	u32 nlbl_type;

	err = selinux_skb_xfrm_sid(skb, &xfrm_sid);
	err = selinux_xfrm_skb_sid(skb, &xfrm_sid);
	if (unlikely(err))
		return -EACCES;
	err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
@@ -3842,6 +3843,30 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
	return 0;
}

/**
 * selinux_conn_sid - Determine the child socket label for a connection
 * @sk_sid: the parent socket's SID
 * @skb_sid: the packet's SID
 * @conn_sid: the resulting connection SID
 *
 * If @skb_sid is valid then the user:role:type information from @sk_sid is
 * combined with the MLS information from @skb_sid in order to create
 * @conn_sid.  If @skb_sid is not valid then then @conn_sid is simply a copy
 * of @sk_sid.  Returns zero on success, negative values on failure.
 *
 */
static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid)
{
	int err = 0;

	if (skb_sid != SECSID_NULL)
		err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid);
	else
		*conn_sid = sk_sid;

	return err;
}

/* socket security operations */

static int socket_sockcreate_sid(const struct task_security_struct *tsec,
@@ -4448,7 +4473,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
	struct sk_security_struct *sksec = sk->sk_security;
	int err;
	u16 family = sk->sk_family;
	u32 newsid;
	u32 connsid;
	u32 peersid;

	/* handle mapped IPv4 packets arriving via IPv6 sockets */
@@ -4458,16 +4483,11 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
	err = selinux_skb_peerlbl_sid(skb, family, &peersid);
	if (err)
		return err;
	if (peersid == SECSID_NULL) {
		req->secid = sksec->sid;
		req->peer_secid = SECSID_NULL;
	} else {
		err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
	err = selinux_conn_sid(sksec->sid, peersid, &connsid);
	if (err)
		return err;
		req->secid = newsid;
	req->secid = connsid;
	req->peer_secid = peersid;
	}

	return selinux_netlbl_inet_conn_request(req, family);
}
@@ -4727,6 +4747,7 @@ static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,
static unsigned int selinux_ip_output(struct sk_buff *skb,
				      u16 family)
{
	struct sock *sk;
	u32 sid;

	if (!netlbl_enabled())
@@ -4735,8 +4756,27 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,
	/* we do this in the LOCAL_OUT path and not the POST_ROUTING path
	 * because we want to make sure we apply the necessary labeling
	 * before IPsec is applied so we can leverage AH protection */
	if (skb->sk) {
		struct sk_security_struct *sksec = skb->sk->sk_security;
	sk = skb->sk;
	if (sk) {
		struct sk_security_struct *sksec;

		if (sk->sk_state == TCP_LISTEN)
			/* if the socket is the listening state then this
			 * packet is a SYN-ACK packet which means it needs to
			 * be labeled based on the connection/request_sock and
			 * not the parent socket.  unfortunately, we can't
			 * lookup the request_sock yet as it isn't queued on
			 * the parent socket until after the SYN-ACK is sent.
			 * the "solution" is to simply pass the packet as-is
			 * as any IP option based labeling should be copied
			 * from the initial connection request (in the IP
			 * layer).  it is far from ideal, but until we get a
			 * security label in the packet itself this is the
			 * best we can do. */
			return NF_ACCEPT;

		/* standard practice, label using the parent socket */
		sksec = sk->sk_security;
		sid = sksec->sid;
	} else
		sid = SECINITSID_KERNEL;
@@ -4806,27 +4846,36 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
	 * as fast and as clean as possible. */
	if (!selinux_policycap_netpeer)
		return selinux_ip_postroute_compat(skb, ifindex, family);

	secmark_active = selinux_secmark_enabled();
	peerlbl_active = selinux_peerlbl_enabled();
	if (!secmark_active && !peerlbl_active)
		return NF_ACCEPT;

	sk = skb->sk;

#ifdef CONFIG_XFRM
	/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
	 * packet transformation so allow the packet to pass without any checks
	 * since we'll have another chance to perform access control checks
	 * when the packet is on it's final way out.
	 * NOTE: there appear to be some IPv6 multicast cases where skb->dst
	 *       is NULL, in this case go ahead and apply access control. */
	if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL)
	 *       is NULL, in this case go ahead and apply access control.
	 * NOTE: if this is a local socket (skb->sk != NULL) that is in the
	 *       TCP listening state we cannot wait until the XFRM processing
	 *       is done as we will miss out on the SA label if we do;
	 *       unfortunately, this means more work, but it is only once per
	 *       connection. */
	if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL &&
	    !(sk != NULL && sk->sk_state == TCP_LISTEN))
		return NF_ACCEPT;
#endif
	secmark_active = selinux_secmark_enabled();
	peerlbl_active = selinux_peerlbl_enabled();
	if (!secmark_active && !peerlbl_active)
		return NF_ACCEPT;

	/* if the packet is being forwarded then get the peer label from the
	 * packet itself; otherwise check to see if it is from a local
	 * application or the kernel, if from an application get the peer label
	 * from the sending socket, otherwise use the kernel's sid */
	sk = skb->sk;
	if (sk == NULL) {
		/* Without an associated socket the packet is either coming
		 * from the kernel or it is being forwarded; check the packet
		 * to determine which and if the packet is being forwarded
		 * query the packet directly to determine the security label. */
		if (skb->skb_iif) {
			secmark_perm = PACKET__FORWARD_OUT;
			if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
@@ -4835,7 +4884,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
			secmark_perm = PACKET__SEND;
			peer_sid = SECINITSID_KERNEL;
		}
	} else if (sk->sk_state == TCP_LISTEN) {
		/* Locally generated packet but the associated socket is in the
		 * listening state which means this is a SYN-ACK packet.  In
		 * this particular case the correct security label is assigned
		 * to the connection/request_sock but unfortunately we can't
		 * query the request_sock as it isn't queued on the parent
		 * socket until after the SYN-ACK packet is sent; the only
		 * viable choice is to regenerate the label like we do in
		 * selinux_inet_conn_request().  See also selinux_ip_output()
		 * for similar problems. */
		u32 skb_sid;
		struct sk_security_struct *sksec = sk->sk_security;
		if (selinux_skb_peerlbl_sid(skb, family, &skb_sid))
			return NF_DROP;
		/* At this point, if the returned skb peerlbl is SECSID_NULL
		 * and the packet has been through at least one XFRM
		 * transformation then we must be dealing with the "final"
		 * form of labeled IPsec packet; since we've already applied
		 * all of our access controls on this packet we can safely
		 * pass the packet. */
		if (skb_sid == SECSID_NULL) {
			switch (family) {
			case PF_INET:
				if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
					return NF_ACCEPT;
				break;
			case PF_INET6:
				if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED)
					return NF_ACCEPT;
			default:
				return NF_DROP_ERR(-ECONNREFUSED);
			}
		}
		if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid))
			return NF_DROP;
		secmark_perm = PACKET__SEND;
	} else {
		/* Locally generated packet, fetch the security label from the
		 * associated socket. */
		struct sk_security_struct *sksec = sk->sk_security;
		peer_sid = sksec->sid;
		secmark_perm = PACKET__SEND;
+5 −3
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
				struct common_audit_data *ad, u8 proto);
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid);

static inline void selinux_xfrm_notify_policyload(void)
{
@@ -79,11 +80,12 @@ static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid,
static inline void selinux_xfrm_notify_policyload(void)
{
}
#endif

static inline int selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
static inline int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid)
{
	return selinux_xfrm_decode_session(skb, sid, 0);
	*sid = SECSID_NULL;
	return 0;
}
#endif

#endif /* _SELINUX_XFRM_H_ */
+48 −14
Original line number Diff line number Diff line
@@ -209,19 +209,26 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
			    NULL) ? 0 : 1);
}

/*
 * LSM hook implementation that checks and/or returns the xfrm sid for the
 * incoming packet.
 */
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
static u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb)
{
	u32 sid_session = SECSID_NULL;
	struct sec_path *sp;
	struct dst_entry *dst = skb_dst(skb);
	struct xfrm_state *x;

	if (skb == NULL)
		goto out;
	if (dst == NULL)
		return SECSID_NULL;
	x = dst->xfrm;
	if (x == NULL || !selinux_authorizable_xfrm(x))
		return SECSID_NULL;

	return x->security->ctx_sid;
}

static int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb,
					u32 *sid, int ckall)
{
	u32 sid_session = SECSID_NULL;
	struct sec_path *sp = skb->sp;

	sp = skb->sp;
	if (sp) {
		int i;

@@ -247,6 +254,30 @@ out:
	return 0;
}

/*
 * LSM hook implementation that checks and/or returns the xfrm sid for the
 * incoming packet.
 */
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
{
	if (skb == NULL) {
		*sid = SECSID_NULL;
		return 0;
	}
	return selinux_xfrm_skb_sid_ingress(skb, sid, ckall);
}

int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid)
{
	int rc;

	rc = selinux_xfrm_skb_sid_ingress(skb, sid, 0);
	if (rc == 0 && *sid == SECSID_NULL)
		*sid = selinux_xfrm_skb_sid_egress(skb);

	return rc;
}

/*
 * LSM hook implementation that allocs and transfers uctx spec to xfrm_policy.
 */
@@ -327,19 +358,22 @@ int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x,
		return rc;

	ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC);
	if (!ctx)
		return -ENOMEM;
	if (!ctx) {
		rc = -ENOMEM;
		goto out;
	}

	ctx->ctx_doi = XFRM_SC_DOI_LSM;
	ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
	ctx->ctx_sid = secid;
	ctx->ctx_len = str_len;
	memcpy(ctx->ctx_str, ctx_str, str_len);
	kfree(ctx_str);

	x->security = ctx;
	atomic_inc(&selinux_xfrm_refcount);
	return 0;
out:
	kfree(ctx_str);
	return rc;
}

/*