diff options
-rw-r--r-- | include/linux/in.h | 1 | ||||
-rw-r--r-- | include/linux/security.h | 25 | ||||
-rw-r--r-- | include/linux/socket.h | 1 | ||||
-rw-r--r-- | net/core/sock.c | 2 | ||||
-rw-r--r-- | net/ipv4/ip_sockglue.c | 31 | ||||
-rw-r--r-- | security/dummy.c | 10 | ||||
-rw-r--r-- | security/selinux/hooks.c | 46 | ||||
-rw-r--r-- | security/selinux/include/xfrm.h | 2 | ||||
-rw-r--r-- | security/selinux/xfrm.c | 68 |
9 files changed, 169 insertions, 17 deletions
diff --git a/include/linux/in.h b/include/linux/in.h index ba355384016a..94f557fa4636 100644 --- a/include/linux/in.h +++ b/include/linux/in.h | |||
@@ -72,6 +72,7 @@ struct in_addr { | |||
72 | #define IP_FREEBIND 15 | 72 | #define IP_FREEBIND 15 |
73 | #define IP_IPSEC_POLICY 16 | 73 | #define IP_IPSEC_POLICY 16 |
74 | #define IP_XFRM_POLICY 17 | 74 | #define IP_XFRM_POLICY 17 |
75 | #define IP_PASSSEC 18 | ||
75 | 76 | ||
76 | /* BSD compatibility */ | 77 | /* BSD compatibility */ |
77 | #define IP_RECVRETOPTS IP_RETOPTS | 78 | #define IP_RECVRETOPTS IP_RETOPTS |
diff --git a/include/linux/security.h b/include/linux/security.h index 7cbef482e13a..b18eb8cfa639 100644 --- a/include/linux/security.h +++ b/include/linux/security.h | |||
@@ -1286,7 +1286,8 @@ struct security_operations { | |||
1286 | int (*socket_setsockopt) (struct socket * sock, int level, int optname); | 1286 | int (*socket_setsockopt) (struct socket * sock, int level, int optname); |
1287 | int (*socket_shutdown) (struct socket * sock, int how); | 1287 | int (*socket_shutdown) (struct socket * sock, int how); |
1288 | int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb); | 1288 | int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb); |
1289 | int (*socket_getpeersec) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len); | 1289 | int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len); |
1290 | int (*socket_getpeersec_dgram) (struct sk_buff *skb, char **secdata, u32 *seclen); | ||
1290 | int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority); | 1291 | int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority); |
1291 | void (*sk_free_security) (struct sock *sk); | 1292 | void (*sk_free_security) (struct sock *sk); |
1292 | unsigned int (*sk_getsid) (struct sock *sk, struct flowi *fl, u8 dir); | 1293 | unsigned int (*sk_getsid) (struct sock *sk, struct flowi *fl, u8 dir); |
@@ -2741,10 +2742,16 @@ static inline int security_sock_rcv_skb (struct sock * sk, | |||
2741 | return security_ops->socket_sock_rcv_skb (sk, skb); | 2742 | return security_ops->socket_sock_rcv_skb (sk, skb); |
2742 | } | 2743 | } |
2743 | 2744 | ||
2744 | static inline int security_socket_getpeersec(struct socket *sock, char __user *optval, | 2745 | static inline int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, |
2745 | int __user *optlen, unsigned len) | 2746 | int __user *optlen, unsigned len) |
2746 | { | 2747 | { |
2747 | return security_ops->socket_getpeersec(sock, optval, optlen, len); | 2748 | return security_ops->socket_getpeersec_stream(sock, optval, optlen, len); |
2749 | } | ||
2750 | |||
2751 | static inline int security_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, | ||
2752 | u32 *seclen) | ||
2753 | { | ||
2754 | return security_ops->socket_getpeersec_dgram(skb, secdata, seclen); | ||
2748 | } | 2755 | } |
2749 | 2756 | ||
2750 | static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority) | 2757 | static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority) |
@@ -2863,8 +2870,14 @@ static inline int security_sock_rcv_skb (struct sock * sk, | |||
2863 | return 0; | 2870 | return 0; |
2864 | } | 2871 | } |
2865 | 2872 | ||
2866 | static inline int security_socket_getpeersec(struct socket *sock, char __user *optval, | 2873 | static inline int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, |
2867 | int __user *optlen, unsigned len) | 2874 | int __user *optlen, unsigned len) |
2875 | { | ||
2876 | return -ENOPROTOOPT; | ||
2877 | } | ||
2878 | |||
2879 | static inline int security_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, | ||
2880 | u32 *seclen) | ||
2868 | { | 2881 | { |
2869 | return -ENOPROTOOPT; | 2882 | return -ENOPROTOOPT; |
2870 | } | 2883 | } |
diff --git a/include/linux/socket.h b/include/linux/socket.h index b02dda4ee83d..9ab2ddd80221 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h | |||
@@ -150,6 +150,7 @@ __KINLINE struct cmsghdr * cmsg_nxthdr (struct msghdr *__msg, struct cmsghdr *__ | |||
150 | 150 | ||
151 | #define SCM_RIGHTS 0x01 /* rw: access rights (array of int) */ | 151 | #define SCM_RIGHTS 0x01 /* rw: access rights (array of int) */ |
152 | #define SCM_CREDENTIALS 0x02 /* rw: struct ucred */ | 152 | #define SCM_CREDENTIALS 0x02 /* rw: struct ucred */ |
153 | #define SCM_SECURITY 0x03 /* rw: security label */ | ||
153 | 154 | ||
154 | struct ucred { | 155 | struct ucred { |
155 | __u32 pid; | 156 | __u32 pid; |
diff --git a/net/core/sock.c b/net/core/sock.c index 6e00811d44bc..5038a5a7bd84 100644 --- a/net/core/sock.c +++ b/net/core/sock.c | |||
@@ -616,7 +616,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname, | |||
616 | break; | 616 | break; |
617 | 617 | ||
618 | case SO_PEERSEC: | 618 | case SO_PEERSEC: |
619 | return security_socket_getpeersec(sock, optval, optlen, len); | 619 | return security_socket_getpeersec_stream(sock, optval, optlen, len); |
620 | 620 | ||
621 | default: | 621 | default: |
622 | return(-ENOPROTOOPT); | 622 | return(-ENOPROTOOPT); |
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 2bf8d782f678..b5c4f61518e8 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #define IP_CMSG_TOS 4 | 50 | #define IP_CMSG_TOS 4 |
51 | #define IP_CMSG_RECVOPTS 8 | 51 | #define IP_CMSG_RECVOPTS 8 |
52 | #define IP_CMSG_RETOPTS 16 | 52 | #define IP_CMSG_RETOPTS 16 |
53 | #define IP_CMSG_PASSSEC 32 | ||
53 | 54 | ||
54 | /* | 55 | /* |
55 | * SOL_IP control messages. | 56 | * SOL_IP control messages. |
@@ -109,6 +110,19 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb) | |||
109 | put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data); | 110 | put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data); |
110 | } | 111 | } |
111 | 112 | ||
113 | static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb) | ||
114 | { | ||
115 | char *secdata; | ||
116 | u32 seclen; | ||
117 | int err; | ||
118 | |||
119 | err = security_socket_getpeersec_dgram(skb, &secdata, &seclen); | ||
120 | if (err) | ||
121 | return; | ||
122 | |||
123 | put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata); | ||
124 | } | ||
125 | |||
112 | 126 | ||
113 | void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) | 127 | void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) |
114 | { | 128 | { |
@@ -138,6 +152,11 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) | |||
138 | 152 | ||
139 | if (flags & 1) | 153 | if (flags & 1) |
140 | ip_cmsg_recv_retopts(msg, skb); | 154 | ip_cmsg_recv_retopts(msg, skb); |
155 | if ((flags>>=1) == 0) | ||
156 | return; | ||
157 | |||
158 | if (flags & 1) | ||
159 | ip_cmsg_recv_security(msg, skb); | ||
141 | } | 160 | } |
142 | 161 | ||
143 | int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc) | 162 | int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc) |
@@ -393,7 +412,8 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, | |||
393 | (1<<IP_RETOPTS) | (1<<IP_TOS) | | 412 | (1<<IP_RETOPTS) | (1<<IP_TOS) | |
394 | (1<<IP_TTL) | (1<<IP_HDRINCL) | | 413 | (1<<IP_TTL) | (1<<IP_HDRINCL) | |
395 | (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | | 414 | (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | |
396 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND))) || | 415 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) | |
416 | (1<<IP_PASSSEC))) || | ||
397 | optname == IP_MULTICAST_TTL || | 417 | optname == IP_MULTICAST_TTL || |
398 | optname == IP_MULTICAST_LOOP) { | 418 | optname == IP_MULTICAST_LOOP) { |
399 | if (optlen >= sizeof(int)) { | 419 | if (optlen >= sizeof(int)) { |
@@ -478,6 +498,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, | |||
478 | else | 498 | else |
479 | inet->cmsg_flags &= ~IP_CMSG_RETOPTS; | 499 | inet->cmsg_flags &= ~IP_CMSG_RETOPTS; |
480 | break; | 500 | break; |
501 | case IP_PASSSEC: | ||
502 | if (val) | ||
503 | inet->cmsg_flags |= IP_CMSG_PASSSEC; | ||
504 | else | ||
505 | inet->cmsg_flags &= ~IP_CMSG_PASSSEC; | ||
506 | break; | ||
481 | case IP_TOS: /* This sets both TOS and Precedence */ | 507 | case IP_TOS: /* This sets both TOS and Precedence */ |
482 | if (sk->sk_type == SOCK_STREAM) { | 508 | if (sk->sk_type == SOCK_STREAM) { |
483 | val &= ~3; | 509 | val &= ~3; |
@@ -932,6 +958,9 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, | |||
932 | case IP_RETOPTS: | 958 | case IP_RETOPTS: |
933 | val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; | 959 | val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; |
934 | break; | 960 | break; |
961 | case IP_PASSSEC: | ||
962 | val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; | ||
963 | break; | ||
935 | case IP_TOS: | 964 | case IP_TOS: |
936 | val = inet->tos; | 965 | val = inet->tos; |
937 | break; | 966 | break; |
diff --git a/security/dummy.c b/security/dummy.c index f1a5bd98bf10..a326d6e0b6d7 100644 --- a/security/dummy.c +++ b/security/dummy.c | |||
@@ -763,8 +763,14 @@ static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb) | |||
763 | return 0; | 763 | return 0; |
764 | } | 764 | } |
765 | 765 | ||
766 | static int dummy_socket_getpeersec(struct socket *sock, char __user *optval, | 766 | static int dummy_socket_getpeersec_stream(struct socket *sock, char __user *optval, |
767 | int __user *optlen, unsigned len) | 767 | int __user *optlen, unsigned len) |
768 | { | ||
769 | return -ENOPROTOOPT; | ||
770 | } | ||
771 | |||
772 | static int dummy_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, | ||
773 | u32 *seclen) | ||
768 | { | 774 | { |
769 | return -ENOPROTOOPT; | 775 | return -ENOPROTOOPT; |
770 | } | 776 | } |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b65c201e9ff5..5b16196f2823 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -3318,24 +3318,38 @@ out: | |||
3318 | return err; | 3318 | return err; |
3319 | } | 3319 | } |
3320 | 3320 | ||
3321 | static int selinux_socket_getpeersec(struct socket *sock, char __user *optval, | 3321 | static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval, |
3322 | int __user *optlen, unsigned len) | 3322 | int __user *optlen, unsigned len) |
3323 | { | 3323 | { |
3324 | int err = 0; | 3324 | int err = 0; |
3325 | char *scontext; | 3325 | char *scontext; |
3326 | u32 scontext_len; | 3326 | u32 scontext_len; |
3327 | struct sk_security_struct *ssec; | 3327 | struct sk_security_struct *ssec; |
3328 | struct inode_security_struct *isec; | 3328 | struct inode_security_struct *isec; |
3329 | u32 peer_sid = 0; | ||
3329 | 3330 | ||
3330 | isec = SOCK_INODE(sock)->i_security; | 3331 | isec = SOCK_INODE(sock)->i_security; |
3331 | if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) { | 3332 | |
3333 | /* if UNIX_STREAM check peer_sid, if TCP check dst for labelled sa */ | ||
3334 | if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET) { | ||
3335 | ssec = sock->sk->sk_security; | ||
3336 | peer_sid = ssec->peer_sid; | ||
3337 | } | ||
3338 | else if (isec->sclass == SECCLASS_TCP_SOCKET) { | ||
3339 | peer_sid = selinux_socket_getpeer_stream(sock->sk); | ||
3340 | |||
3341 | if (peer_sid == SECSID_NULL) { | ||
3342 | err = -ENOPROTOOPT; | ||
3343 | goto out; | ||
3344 | } | ||
3345 | } | ||
3346 | else { | ||
3332 | err = -ENOPROTOOPT; | 3347 | err = -ENOPROTOOPT; |
3333 | goto out; | 3348 | goto out; |
3334 | } | 3349 | } |
3335 | 3350 | ||
3336 | ssec = sock->sk->sk_security; | 3351 | err = security_sid_to_context(peer_sid, &scontext, &scontext_len); |
3337 | 3352 | ||
3338 | err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len); | ||
3339 | if (err) | 3353 | if (err) |
3340 | goto out; | 3354 | goto out; |
3341 | 3355 | ||
@@ -3356,6 +3370,23 @@ out: | |||
3356 | return err; | 3370 | return err; |
3357 | } | 3371 | } |
3358 | 3372 | ||
3373 | static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, u32 *seclen) | ||
3374 | { | ||
3375 | int err = 0; | ||
3376 | u32 peer_sid = selinux_socket_getpeer_dgram(skb); | ||
3377 | |||
3378 | if (peer_sid == SECSID_NULL) | ||
3379 | return -EINVAL; | ||
3380 | |||
3381 | err = security_sid_to_context(peer_sid, secdata, seclen); | ||
3382 | if (err) | ||
3383 | return err; | ||
3384 | |||
3385 | return 0; | ||
3386 | } | ||
3387 | |||
3388 | |||
3389 | |||
3359 | static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) | 3390 | static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) |
3360 | { | 3391 | { |
3361 | return sk_alloc_security(sk, family, priority); | 3392 | return sk_alloc_security(sk, family, priority); |
@@ -4344,7 +4375,8 @@ static struct security_operations selinux_ops = { | |||
4344 | .socket_setsockopt = selinux_socket_setsockopt, | 4375 | .socket_setsockopt = selinux_socket_setsockopt, |
4345 | .socket_shutdown = selinux_socket_shutdown, | 4376 | .socket_shutdown = selinux_socket_shutdown, |
4346 | .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb, | 4377 | .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb, |
4347 | .socket_getpeersec = selinux_socket_getpeersec, | 4378 | .socket_getpeersec_stream = selinux_socket_getpeersec_stream, |
4379 | .socket_getpeersec_dgram = selinux_socket_getpeersec_dgram, | ||
4348 | .sk_alloc_security = selinux_sk_alloc_security, | 4380 | .sk_alloc_security = selinux_sk_alloc_security, |
4349 | .sk_free_security = selinux_sk_free_security, | 4381 | .sk_free_security = selinux_sk_free_security, |
4350 | .sk_getsid = selinux_sk_getsid_security, | 4382 | .sk_getsid = selinux_sk_getsid_security, |
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 8e87996c6dd5..a7f388bff3f2 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h | |||
@@ -39,6 +39,8 @@ static inline u32 selinux_no_sk_sid(struct flowi *fl) | |||
39 | #ifdef CONFIG_SECURITY_NETWORK_XFRM | 39 | #ifdef CONFIG_SECURITY_NETWORK_XFRM |
40 | int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb); | 40 | int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb); |
41 | int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb); | 41 | int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb); |
42 | u32 selinux_socket_getpeer_stream(struct sock *sk); | ||
43 | u32 selinux_socket_getpeer_dgram(struct sk_buff *skb); | ||
42 | #else | 44 | #else |
43 | static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb) | 45 | static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb) |
44 | { | 46 | { |
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index b2af7ca496c1..dfab6c886698 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c | |||
@@ -225,6 +225,74 @@ void selinux_xfrm_state_free(struct xfrm_state *x) | |||
225 | } | 225 | } |
226 | 226 | ||
227 | /* | 227 | /* |
228 | * SELinux internal function to retrieve the context of a connected | ||
229 | * (sk->sk_state == TCP_ESTABLISHED) TCP socket based on its security | ||
230 | * association used to connect to the remote socket. | ||
231 | * | ||
232 | * Retrieve via getsockopt SO_PEERSEC. | ||
233 | */ | ||
234 | u32 selinux_socket_getpeer_stream(struct sock *sk) | ||
235 | { | ||
236 | struct dst_entry *dst, *dst_test; | ||
237 | u32 peer_sid = SECSID_NULL; | ||
238 | |||
239 | if (sk->sk_state != TCP_ESTABLISHED) | ||
240 | goto out; | ||
241 | |||
242 | dst = sk_dst_get(sk); | ||
243 | if (!dst) | ||
244 | goto out; | ||
245 | |||
246 | for (dst_test = dst; dst_test != 0; | ||
247 | dst_test = dst_test->child) { | ||
248 | struct xfrm_state *x = dst_test->xfrm; | ||
249 | |||
250 | if (x && selinux_authorizable_xfrm(x)) { | ||
251 | struct xfrm_sec_ctx *ctx = x->security; | ||
252 | peer_sid = ctx->ctx_sid; | ||
253 | break; | ||
254 | } | ||
255 | } | ||
256 | dst_release(dst); | ||
257 | |||
258 | out: | ||
259 | return peer_sid; | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * SELinux internal function to retrieve the context of a UDP packet | ||
264 | * based on its security association used to connect to the remote socket. | ||
265 | * | ||
266 | * Retrieve via setsockopt IP_PASSSEC and recvmsg with control message | ||
267 | * type SCM_SECURITY. | ||
268 | */ | ||
269 | u32 selinux_socket_getpeer_dgram(struct sk_buff *skb) | ||
270 | { | ||
271 | struct sec_path *sp; | ||
272 | |||
273 | if (skb == NULL) | ||
274 | return SECSID_NULL; | ||
275 | |||
276 | if (skb->sk->sk_protocol != IPPROTO_UDP) | ||
277 | return SECSID_NULL; | ||
278 | |||
279 | sp = skb->sp; | ||
280 | if (sp) { | ||
281 | int i; | ||
282 | |||
283 | for (i = sp->len-1; i >= 0; i--) { | ||
284 | struct xfrm_state *x = sp->x[i].xvec; | ||
285 | if (selinux_authorizable_xfrm(x)) { | ||
286 | struct xfrm_sec_ctx *ctx = x->security; | ||
287 | return ctx->ctx_sid; | ||
288 | } | ||
289 | } | ||
290 | } | ||
291 | |||
292 | return SECSID_NULL; | ||
293 | } | ||
294 | |||
295 | /* | ||
228 | * LSM hook that controls access to unlabelled packets. If | 296 | * LSM hook that controls access to unlabelled packets. If |
229 | * a xfrm_state is authorizable (defined by macro) then it was | 297 | * a xfrm_state is authorizable (defined by macro) then it was |
230 | * already authorized by the IPSec process. If not, then | 298 | * already authorized by the IPSec process. If not, then |