Commit d93aca60 authored by James Morris's avatar James Morris

Merge branch 'master' of git://git.infradead.org/users/pcmoore/selinux_fixes into for-linus

parents 54fb723c c0828e50
...@@ -53,6 +53,7 @@ ...@@ -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>
...@@ -3828,7 +3829,7 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) ...@@ -3828,7 +3829,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);
...@@ -3846,6 +3847,30 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) ...@@ -3846,6 +3847,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,
...@@ -4452,7 +4477,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, ...@@ -4452,7 +4477,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 */
...@@ -4462,16 +4487,11 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, ...@@ -4462,16 +4487,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);
} }
...@@ -4731,6 +4751,7 @@ static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, ...@@ -4731,6 +4751,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())
...@@ -4739,8 +4760,27 @@ static unsigned int selinux_ip_output(struct sk_buff *skb, ...@@ -4739,8 +4760,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;
...@@ -4810,27 +4850,36 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, ...@@ -4810,27 +4850,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))
...@@ -4839,7 +4888,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, ...@@ -4839,7 +4888,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, ...@@ -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, ...@@ -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)
{ {
} }
#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_ */ #endif /* _SELINUX_XFRM_H_ */
...@@ -209,19 +209,26 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, ...@@ -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.
*/
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
{ {
u32 sid_session = SECSID_NULL; struct dst_entry *dst = skb_dst(skb);
struct sec_path *sp; struct xfrm_state *x;
if (skb == NULL) if (dst == NULL)
goto out; 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) { if (sp) {
int i; int i;
...@@ -247,6 +254,30 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) ...@@ -247,6 +254,30 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
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, ...@@ -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;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment