Commit 6b877699 authored by Venkat Yekkirala's avatar Venkat Yekkirala Committed by David S. Miller

SELinux: Return correct context for SO_PEERSEC

Fix SO_PEERSEC for tcp sockets to return the security context of
the peer (as represented by the SA from the peer) as opposed to the
SA used by the local/source socket.
Signed-off-by: default avatarVenkat Yekkirala <vyekkirala@TrustedCS.com>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent c1a856c9
...@@ -826,6 +826,8 @@ struct request_sock; ...@@ -826,6 +826,8 @@ struct request_sock;
* Sets the openreq's sid to socket's sid with MLS portion taken from peer sid. * Sets the openreq's sid to socket's sid with MLS portion taken from peer sid.
* @inet_csk_clone: * @inet_csk_clone:
* Sets the new child socket's sid to the openreq sid. * 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: * @req_classify_flow:
* Sets the flow's sid to the openreq sid. * Sets the flow's sid to the openreq sid.
* *
...@@ -1368,6 +1370,7 @@ struct security_operations { ...@@ -1368,6 +1370,7 @@ struct security_operations {
int (*inet_conn_request)(struct sock *sk, struct sk_buff *skb, int (*inet_conn_request)(struct sock *sk, struct sk_buff *skb,
struct request_sock *req); struct request_sock *req);
void (*inet_csk_clone)(struct sock *newsk, const 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 (*req_classify_flow)(const struct request_sock *req, struct flowi *fl);
#endif /* CONFIG_SECURITY_NETWORK */ #endif /* CONFIG_SECURITY_NETWORK */
...@@ -2961,9 +2964,15 @@ static inline void security_inet_csk_clone(struct sock *newsk, ...@@ -2961,9 +2964,15 @@ static inline void security_inet_csk_clone(struct sock *newsk,
{ {
security_ops->inet_csk_clone(newsk, req); security_ops->inet_csk_clone(newsk, req);
} }
static inline void security_inet_conn_established(struct sock *sk,
struct sk_buff *skb)
{
security_ops->inet_conn_established(sk, skb);
}
#else /* CONFIG_SECURITY_NETWORK */ #else /* CONFIG_SECURITY_NETWORK */
static inline int security_unix_stream_connect(struct socket * sock, static inline int security_unix_stream_connect(struct socket * sock,
struct socket * other, struct socket * other,
struct sock * newsk) struct sock * newsk)
{ {
return 0; return 0;
...@@ -3110,6 +3119,11 @@ static inline void security_inet_csk_clone(struct sock *newsk, ...@@ -3110,6 +3119,11 @@ static inline void security_inet_csk_clone(struct sock *newsk,
const struct request_sock *req) const struct request_sock *req)
{ {
} }
static inline void security_inet_conn_established(struct sock *sk,
struct sk_buff *skb)
{
}
#endif /* CONFIG_SECURITY_NETWORK */ #endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
......
...@@ -54,6 +54,7 @@ struct request_sock { ...@@ -54,6 +54,7 @@ struct request_sock {
struct request_sock_ops *rsk_ops; struct request_sock_ops *rsk_ops;
struct sock *sk; struct sock *sk;
u32 secid; u32 secid;
u32 peer_secid;
}; };
static inline struct request_sock *reqsk_alloc(struct request_sock_ops *ops) static inline struct request_sock *reqsk_alloc(struct request_sock_ops *ops)
......
...@@ -4230,6 +4230,8 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, ...@@ -4230,6 +4230,8 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
mb(); mb();
tcp_set_state(sk, TCP_ESTABLISHED); tcp_set_state(sk, TCP_ESTABLISHED);
security_inet_conn_established(sk, skb);
/* Make sure socket is routed, for correct metrics. */ /* Make sure socket is routed, for correct metrics. */
icsk->icsk_af_ops->rebuild_header(sk); icsk->icsk_af_ops->rebuild_header(sk);
......
...@@ -828,6 +828,11 @@ static inline void dummy_inet_csk_clone(struct sock *newsk, ...@@ -828,6 +828,11 @@ static inline void dummy_inet_csk_clone(struct sock *newsk,
{ {
} }
static inline void dummy_inet_conn_established(struct sock *sk,
struct sk_buff *skb)
{
}
static inline void dummy_req_classify_flow(const struct request_sock *req, static inline void dummy_req_classify_flow(const struct request_sock *req,
struct flowi *fl) struct flowi *fl)
{ {
...@@ -1108,6 +1113,7 @@ void security_fixup_ops (struct security_operations *ops) ...@@ -1108,6 +1113,7 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, sock_graft); set_to_dummy_if_null(ops, sock_graft);
set_to_dummy_if_null(ops, inet_conn_request); 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_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, req_classify_flow);
#endif /* CONFIG_SECURITY_NETWORK */ #endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
......
...@@ -3535,8 +3535,10 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op ...@@ -3535,8 +3535,10 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
} }
else if (isec->sclass == SECCLASS_TCP_SOCKET) { else if (isec->sclass == SECCLASS_TCP_SOCKET) {
peer_sid = selinux_netlbl_socket_getpeersec_stream(sock); peer_sid = selinux_netlbl_socket_getpeersec_stream(sock);
if (peer_sid == SECSID_NULL) if (peer_sid == SECSID_NULL) {
peer_sid = selinux_socket_getpeer_stream(sock->sk); ssec = sock->sk->sk_security;
peer_sid = ssec->peer_sid;
}
if (peer_sid == SECSID_NULL) { if (peer_sid == SECSID_NULL) {
err = -ENOPROTOOPT; err = -ENOPROTOOPT;
goto out; goto out;
...@@ -3647,11 +3649,11 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, ...@@ -3647,11 +3649,11 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
return 0; return 0;
} }
err = selinux_xfrm_decode_session(skb, &peersid, 0); selinux_skb_xfrm_sid(skb, &peersid);
BUG_ON(err);
if (peersid == SECSID_NULL) { if (peersid == SECSID_NULL) {
req->secid = sksec->sid; req->secid = sksec->sid;
req->peer_secid = 0;
return 0; return 0;
} }
...@@ -3660,6 +3662,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, ...@@ -3660,6 +3662,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
return err; return err;
req->secid = newsid; req->secid = newsid;
req->peer_secid = peersid;
return 0; return 0;
} }
...@@ -3669,6 +3672,7 @@ static void selinux_inet_csk_clone(struct sock *newsk, ...@@ -3669,6 +3672,7 @@ static void selinux_inet_csk_clone(struct sock *newsk,
struct sk_security_struct *newsksec = newsk->sk_security; struct sk_security_struct *newsksec = newsk->sk_security;
newsksec->sid = req->secid; newsksec->sid = req->secid;
newsksec->peer_sid = req->peer_secid;
/* NOTE: Ideally, we should also get the isec->sid for the /* NOTE: Ideally, we should also get the isec->sid for the
new socket in sync, but we don't have the isec available yet. new socket in sync, but we don't have the isec available yet.
So we will wait until sock_graft to do it, by which So we will wait until sock_graft to do it, by which
...@@ -3677,6 +3681,14 @@ static void selinux_inet_csk_clone(struct sock *newsk, ...@@ -3677,6 +3681,14 @@ static void selinux_inet_csk_clone(struct sock *newsk,
selinux_netlbl_sk_security_init(newsksec, req->rsk_ops->family); selinux_netlbl_sk_security_init(newsksec, req->rsk_ops->family);
} }
static void selinux_inet_conn_established(struct sock *sk,
struct sk_buff *skb)
{
struct sk_security_struct *sksec = sk->sk_security;
selinux_skb_xfrm_sid(skb, &sksec->peer_sid);
}
static void selinux_req_classify_flow(const struct request_sock *req, static void selinux_req_classify_flow(const struct request_sock *req,
struct flowi *fl) struct flowi *fl)
{ {
...@@ -4739,6 +4751,7 @@ static struct security_operations selinux_ops = { ...@@ -4739,6 +4751,7 @@ static struct security_operations selinux_ops = {
.sock_graft = selinux_sock_graft, .sock_graft = selinux_sock_graft,
.inet_conn_request = selinux_inet_conn_request, .inet_conn_request = selinux_inet_conn_request,
.inet_csk_clone = selinux_inet_csk_clone, .inet_csk_clone = selinux_inet_csk_clone,
.inet_conn_established = selinux_inet_conn_established,
.req_classify_flow = selinux_req_classify_flow, .req_classify_flow = selinux_req_classify_flow,
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
......
...@@ -39,7 +39,6 @@ int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb, ...@@ -39,7 +39,6 @@ int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
struct avc_audit_data *ad); struct avc_audit_data *ad);
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
struct avc_audit_data *ad); struct avc_audit_data *ad);
u32 selinux_socket_getpeer_stream(struct sock *sk);
u32 selinux_socket_getpeer_dgram(struct sk_buff *skb); u32 selinux_socket_getpeer_dgram(struct sk_buff *skb);
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);
#else #else
...@@ -55,11 +54,6 @@ static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, ...@@ -55,11 +54,6 @@ static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
return 0; return 0;
} }
static inline int selinux_socket_getpeer_stream(struct sock *sk)
{
return SECSID_NULL;
}
static inline int selinux_socket_getpeer_dgram(struct sk_buff *skb) static inline int selinux_socket_getpeer_dgram(struct sk_buff *skb)
{ {
return SECSID_NULL; return SECSID_NULL;
...@@ -71,4 +65,10 @@ static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ...@@ -71,4 +65,10 @@ static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int
} }
#endif #endif
static inline void selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
{
int err = selinux_xfrm_decode_session(skb, sid, 0);
BUG_ON(err);
}
#endif /* _SELINUX_XFRM_H_ */ #endif /* _SELINUX_XFRM_H_ */
...@@ -184,7 +184,8 @@ int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, ...@@ -184,7 +184,8 @@ int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm,
} }
/* /*
* LSM hook implementation that determines the sid for the session. * 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) int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
...@@ -402,44 +403,9 @@ void selinux_xfrm_state_free(struct xfrm_state *x) ...@@ -402,44 +403,9 @@ void selinux_xfrm_state_free(struct xfrm_state *x)
kfree(ctx); kfree(ctx);
} }
/*
* SELinux internal function to retrieve the context of a connected
* (sk->sk_state == TCP_ESTABLISHED) TCP socket based on its security
* association used to connect to the remote socket.
*
* Retrieve via getsockopt SO_PEERSEC.
*/
u32 selinux_socket_getpeer_stream(struct sock *sk)
{
struct dst_entry *dst, *dst_test;
u32 peer_sid = SECSID_NULL;
if (sk->sk_state != TCP_ESTABLISHED)
goto out;
dst = sk_dst_get(sk);
if (!dst)
goto out;
for (dst_test = dst; dst_test != 0;
dst_test = dst_test->child) {
struct xfrm_state *x = dst_test->xfrm;
if (x && selinux_authorizable_xfrm(x)) {
struct xfrm_sec_ctx *ctx = x->security;
peer_sid = ctx->ctx_sid;
break;
}
}
dst_release(dst);
out:
return peer_sid;
}
/* /*
* SELinux internal function to retrieve the context of a UDP packet * SELinux internal function to retrieve the context of a UDP packet
* based on its security association used to connect to the remote socket. * based on its security association.
* *
* Retrieve via setsockopt IP_PASSSEC and recvmsg with control message * Retrieve via setsockopt IP_PASSSEC and recvmsg with control message
* type SCM_SECURITY. * type SCM_SECURITY.
......
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