Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
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
This commit is contained in:
commit
b5745c5962
|
@ -53,6 +53,7 @@
|
||||||
#include <net/ip.h> /* for local_port_range[] */
|
#include <net/ip.h> /* for local_port_range[] */
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
|
#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/net_namespace.h>
|
||||||
#include <net/netlabel.h>
|
#include <net/netlabel.h>
|
||||||
#include <linux/uaccess.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_sid;
|
||||||
u32 nlbl_type;
|
u32 nlbl_type;
|
||||||
|
|
||||||
err = selinux_skb_xfrm_sid(skb, &xfrm_sid);
|
err = selinux_xfrm_skb_sid(skb, &xfrm_sid);
|
||||||
if (unlikely(err))
|
if (unlikely(err))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
|
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;
|
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 */
|
/* socket security operations */
|
||||||
|
|
||||||
static int socket_sockcreate_sid(const struct task_security_struct *tsec,
|
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;
|
struct sk_security_struct *sksec = sk->sk_security;
|
||||||
int err;
|
int err;
|
||||||
u16 family = sk->sk_family;
|
u16 family = sk->sk_family;
|
||||||
u32 newsid;
|
u32 connsid;
|
||||||
u32 peersid;
|
u32 peersid;
|
||||||
|
|
||||||
/* handle mapped IPv4 packets arriving via IPv6 sockets */
|
/* 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);
|
err = selinux_skb_peerlbl_sid(skb, family, &peersid);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
if (peersid == SECSID_NULL) {
|
err = selinux_conn_sid(sksec->sid, peersid, &connsid);
|
||||||
req->secid = sksec->sid;
|
|
||||||
req->peer_secid = SECSID_NULL;
|
|
||||||
} else {
|
|
||||||
err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
req->secid = newsid;
|
req->secid = connsid;
|
||||||
req->peer_secid = peersid;
|
req->peer_secid = peersid;
|
||||||
}
|
|
||||||
|
|
||||||
return selinux_netlbl_inet_conn_request(req, family);
|
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,
|
static unsigned int selinux_ip_output(struct sk_buff *skb,
|
||||||
u16 family)
|
u16 family)
|
||||||
{
|
{
|
||||||
|
struct sock *sk;
|
||||||
u32 sid;
|
u32 sid;
|
||||||
|
|
||||||
if (!netlbl_enabled())
|
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
|
/* 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
|
* because we want to make sure we apply the necessary labeling
|
||||||
* before IPsec is applied so we can leverage AH protection */
|
* before IPsec is applied so we can leverage AH protection */
|
||||||
if (skb->sk) {
|
sk = skb->sk;
|
||||||
struct sk_security_struct *sksec = skb->sk->sk_security;
|
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;
|
sid = sksec->sid;
|
||||||
} else
|
} else
|
||||||
sid = SECINITSID_KERNEL;
|
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. */
|
* as fast and as clean as possible. */
|
||||||
if (!selinux_policycap_netpeer)
|
if (!selinux_policycap_netpeer)
|
||||||
return selinux_ip_postroute_compat(skb, ifindex, family);
|
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
|
#ifdef CONFIG_XFRM
|
||||||
/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
|
/* 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
|
* packet transformation so allow the packet to pass without any checks
|
||||||
* since we'll have another chance to perform access control checks
|
* since we'll have another chance to perform access control checks
|
||||||
* when the packet is on it's final way out.
|
* when the packet is on it's final way out.
|
||||||
* NOTE: there appear to be some IPv6 multicast cases where skb->dst
|
* NOTE: there appear to be some IPv6 multicast cases where skb->dst
|
||||||
* is NULL, in this case go ahead and apply access control. */
|
* is NULL, in this case go ahead and apply access control.
|
||||||
if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL)
|
* 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;
|
return NF_ACCEPT;
|
||||||
#endif
|
#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) {
|
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) {
|
if (skb->skb_iif) {
|
||||||
secmark_perm = PACKET__FORWARD_OUT;
|
secmark_perm = PACKET__FORWARD_OUT;
|
||||||
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
|
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;
|
secmark_perm = PACKET__SEND;
|
||||||
peer_sid = SECINITSID_KERNEL;
|
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 {
|
} else {
|
||||||
|
/* Locally generated packet, fetch the security label from the
|
||||||
|
* associated socket. */
|
||||||
struct sk_security_struct *sksec = sk->sk_security;
|
struct sk_security_struct *sksec = sk->sk_security;
|
||||||
peer_sid = sksec->sid;
|
peer_sid = sksec->sid;
|
||||||
secmark_perm = PACKET__SEND;
|
secmark_perm = PACKET__SEND;
|
||||||
|
|
|
@ -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,
|
int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
|
||||||
struct common_audit_data *ad, u8 proto);
|
struct common_audit_data *ad, u8 proto);
|
||||||
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
|
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)
|
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)
|
static inline void selinux_xfrm_notify_policyload(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid)
|
||||||
|
{
|
||||||
|
*sid = SECSID_NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline int selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
|
|
||||||
{
|
|
||||||
return selinux_xfrm_decode_session(skb, sid, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _SELINUX_XFRM_H_ */
|
#endif /* _SELINUX_XFRM_H_ */
|
||||||
|
|
|
@ -209,19 +209,26 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
|
||||||
NULL) ? 0 : 1);
|
NULL) ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb)
|
||||||
* LSM hook implementation that checks and/or returns the xfrm sid for the
|
{
|
||||||
* incoming packet.
|
struct dst_entry *dst = skb_dst(skb);
|
||||||
*/
|
struct xfrm_state *x;
|
||||||
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
|
|
||||||
|
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;
|
u32 sid_session = SECSID_NULL;
|
||||||
struct sec_path *sp;
|
struct sec_path *sp = skb->sp;
|
||||||
|
|
||||||
if (skb == NULL)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
sp = skb->sp;
|
|
||||||
if (sp) {
|
if (sp) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -247,6 +254,30 @@ out:
|
||||||
return 0;
|
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.
|
* 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;
|
return rc;
|
||||||
|
|
||||||
ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC);
|
ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC);
|
||||||
if (!ctx)
|
if (!ctx) {
|
||||||
return -ENOMEM;
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
ctx->ctx_doi = XFRM_SC_DOI_LSM;
|
ctx->ctx_doi = XFRM_SC_DOI_LSM;
|
||||||
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
|
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
|
||||||
ctx->ctx_sid = secid;
|
ctx->ctx_sid = secid;
|
||||||
ctx->ctx_len = str_len;
|
ctx->ctx_len = str_len;
|
||||||
memcpy(ctx->ctx_str, ctx_str, str_len);
|
memcpy(ctx->ctx_str, ctx_str, str_len);
|
||||||
kfree(ctx_str);
|
|
||||||
|
|
||||||
x->security = ctx;
|
x->security = ctx;
|
||||||
atomic_inc(&selinux_xfrm_refcount);
|
atomic_inc(&selinux_xfrm_refcount);
|
||||||
return 0;
|
out:
|
||||||
|
kfree(ctx_str);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue