diff options
author | Paul Moore <paul.moore@hp.com> | 2008-01-29 08:38:23 -0500 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2008-01-29 16:17:25 -0500 |
commit | 220deb966ea51e0dedb6a187c0763120809f3e64 (patch) | |
tree | 7d0e5dd8048907c364b4eeff294991937b466c7e /security/selinux/hooks.c | |
parent | f67f4f315f31e7907779adb3296fb6682e755342 (diff) |
SELinux: Better integration between peer labeling subsystems
Rework the handling of network peer labels so that the different peer labeling
subsystems work better together. This includes moving both subsystems to a
single "peer" object class which involves not only changes to the permission
checks but an improved method of consolidating multiple packet peer labels.
As part of this work the inbound packet permission check code has been heavily
modified to handle both the old and new behavior in as sane a fashion as
possible.
Signed-off-by: Paul Moore <paul.moore@hp.com>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 204 |
1 files changed, 112 insertions, 92 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4bca0af4f2af..bfe9a05db3a2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #include <net/icmp.h> | 50 | #include <net/icmp.h> |
51 | #include <net/ip.h> /* for local_port_range[] */ | 51 | #include <net/ip.h> /* for local_port_range[] */ |
52 | #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ | 52 | #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ |
53 | #include <net/net_namespace.h> | ||
53 | #include <asm/uaccess.h> | 54 | #include <asm/uaccess.h> |
54 | #include <asm/ioctls.h> | 55 | #include <asm/ioctls.h> |
55 | #include <linux/bitops.h> | 56 | #include <linux/bitops.h> |
@@ -3426,36 +3427,39 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, | |||
3426 | } | 3427 | } |
3427 | 3428 | ||
3428 | /** | 3429 | /** |
3429 | * selinux_skb_extlbl_sid - Determine the external label of a packet | 3430 | * selinux_skb_peerlbl_sid - Determine the peer label of a packet |
3430 | * @skb: the packet | 3431 | * @skb: the packet |
3431 | * @family: protocol family | 3432 | * @family: protocol family |
3432 | * @sid: the packet's SID | 3433 | * @sid: the packet's peer label SID |
3433 | * | 3434 | * |
3434 | * Description: | 3435 | * Description: |
3435 | * Check the various different forms of external packet labeling and determine | 3436 | * Check the various different forms of network peer labeling and determine |
3436 | * the external SID for the packet. If only one form of external labeling is | 3437 | * the peer label/SID for the packet; most of the magic actually occurs in |
3437 | * present then it is used, if both labeled IPsec and NetLabel labels are | 3438 | * the security server function security_net_peersid_cmp(). The function |
3438 | * present then the SELinux type information is taken from the labeled IPsec | 3439 | * returns zero if the value in @sid is valid (although it may be SECSID_NULL) |
3439 | * SA and the MLS sensitivity label information is taken from the NetLabel | 3440 | * or -EACCES if @sid is invalid due to inconsistencies with the different |
3440 | * security attributes. This bit of "magic" is done in the call to | 3441 | * peer labels. |
3441 | * selinux_netlbl_skbuff_getsid(). | ||
3442 | * | 3442 | * |
3443 | */ | 3443 | */ |
3444 | static void selinux_skb_extlbl_sid(struct sk_buff *skb, | 3444 | static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) |
3445 | u16 family, | ||
3446 | u32 *sid) | ||
3447 | { | 3445 | { |
3448 | u32 xfrm_sid; | 3446 | u32 xfrm_sid; |
3449 | u32 nlbl_sid; | 3447 | u32 nlbl_sid; |
3448 | u32 nlbl_type; | ||
3450 | 3449 | ||
3451 | selinux_skb_xfrm_sid(skb, &xfrm_sid); | 3450 | selinux_skb_xfrm_sid(skb, &xfrm_sid); |
3452 | if (selinux_netlbl_skbuff_getsid(skb, | 3451 | selinux_netlbl_skbuff_getsid(skb, |
3453 | family, | 3452 | family, |
3454 | (xfrm_sid == SECSID_NULL ? | 3453 | SECINITSID_NETMSG, |
3455 | SECINITSID_NETMSG : xfrm_sid), | 3454 | &nlbl_type, |
3456 | &nlbl_sid) != 0) | 3455 | &nlbl_sid); |
3457 | nlbl_sid = SECSID_NULL; | 3456 | |
3458 | *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid); | 3457 | if (security_net_peersid_resolve(nlbl_sid, nlbl_type, |
3458 | xfrm_sid, | ||
3459 | sid) != 0) | ||
3460 | return -EACCES; | ||
3461 | |||
3462 | return 0; | ||
3459 | } | 3463 | } |
3460 | 3464 | ||
3461 | /* socket security operations */ | 3465 | /* socket security operations */ |
@@ -3521,6 +3525,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, | |||
3521 | if (sock->sk) { | 3525 | if (sock->sk) { |
3522 | sksec = sock->sk->sk_security; | 3526 | sksec = sock->sk->sk_security; |
3523 | sksec->sid = isec->sid; | 3527 | sksec->sid = isec->sid; |
3528 | sksec->sclass = isec->sclass; | ||
3524 | err = selinux_netlbl_socket_post_create(sock); | 3529 | err = selinux_netlbl_socket_post_create(sock); |
3525 | } | 3530 | } |
3526 | 3531 | ||
@@ -3824,104 +3829,114 @@ static int selinux_socket_unix_may_send(struct socket *sock, | |||
3824 | return 0; | 3829 | return 0; |
3825 | } | 3830 | } |
3826 | 3831 | ||
3827 | static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, | 3832 | static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk, |
3828 | struct avc_audit_data *ad, | 3833 | struct sk_buff *skb, |
3829 | u16 family, char *addrp) | 3834 | struct avc_audit_data *ad, |
3835 | u16 family, | ||
3836 | char *addrp) | ||
3830 | { | 3837 | { |
3831 | int err = 0; | 3838 | int err; |
3832 | u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0; | 3839 | struct sk_security_struct *sksec = sk->sk_security; |
3833 | struct socket *sock; | 3840 | u16 sk_class; |
3834 | u16 sock_class = 0; | 3841 | u32 netif_perm, node_perm, recv_perm; |
3835 | u32 sock_sid = 0; | 3842 | u32 port_sid, node_sid, if_sid, sk_sid; |
3836 | |||
3837 | read_lock_bh(&sk->sk_callback_lock); | ||
3838 | sock = sk->sk_socket; | ||
3839 | if (sock) { | ||
3840 | struct inode *inode; | ||
3841 | inode = SOCK_INODE(sock); | ||
3842 | if (inode) { | ||
3843 | struct inode_security_struct *isec; | ||
3844 | isec = inode->i_security; | ||
3845 | sock_sid = isec->sid; | ||
3846 | sock_class = isec->sclass; | ||
3847 | } | ||
3848 | } | ||
3849 | read_unlock_bh(&sk->sk_callback_lock); | ||
3850 | if (!sock_sid) | ||
3851 | goto out; | ||
3852 | |||
3853 | if (!skb->dev) | ||
3854 | goto out; | ||
3855 | 3843 | ||
3856 | err = sel_netif_sid(skb->iif, &if_sid); | 3844 | sk_sid = sksec->sid; |
3857 | if (err) | 3845 | sk_class = sksec->sclass; |
3858 | goto out; | ||
3859 | 3846 | ||
3860 | switch (sock_class) { | 3847 | switch (sk_class) { |
3861 | case SECCLASS_UDP_SOCKET: | 3848 | case SECCLASS_UDP_SOCKET: |
3862 | netif_perm = NETIF__UDP_RECV; | 3849 | netif_perm = NETIF__UDP_RECV; |
3863 | node_perm = NODE__UDP_RECV; | 3850 | node_perm = NODE__UDP_RECV; |
3864 | recv_perm = UDP_SOCKET__RECV_MSG; | 3851 | recv_perm = UDP_SOCKET__RECV_MSG; |
3865 | break; | 3852 | break; |
3866 | |||
3867 | case SECCLASS_TCP_SOCKET: | 3853 | case SECCLASS_TCP_SOCKET: |
3868 | netif_perm = NETIF__TCP_RECV; | 3854 | netif_perm = NETIF__TCP_RECV; |
3869 | node_perm = NODE__TCP_RECV; | 3855 | node_perm = NODE__TCP_RECV; |
3870 | recv_perm = TCP_SOCKET__RECV_MSG; | 3856 | recv_perm = TCP_SOCKET__RECV_MSG; |
3871 | break; | 3857 | break; |
3872 | |||
3873 | case SECCLASS_DCCP_SOCKET: | 3858 | case SECCLASS_DCCP_SOCKET: |
3874 | netif_perm = NETIF__DCCP_RECV; | 3859 | netif_perm = NETIF__DCCP_RECV; |
3875 | node_perm = NODE__DCCP_RECV; | 3860 | node_perm = NODE__DCCP_RECV; |
3876 | recv_perm = DCCP_SOCKET__RECV_MSG; | 3861 | recv_perm = DCCP_SOCKET__RECV_MSG; |
3877 | break; | 3862 | break; |
3878 | |||
3879 | default: | 3863 | default: |
3880 | netif_perm = NETIF__RAWIP_RECV; | 3864 | netif_perm = NETIF__RAWIP_RECV; |
3881 | node_perm = NODE__RAWIP_RECV; | 3865 | node_perm = NODE__RAWIP_RECV; |
3866 | recv_perm = 0; | ||
3882 | break; | 3867 | break; |
3883 | } | 3868 | } |
3884 | 3869 | ||
3885 | err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); | 3870 | err = sel_netif_sid(skb->iif, &if_sid); |
3886 | if (err) | 3871 | if (err) |
3887 | goto out; | 3872 | return err; |
3873 | err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); | ||
3874 | if (err) | ||
3875 | return err; | ||
3888 | 3876 | ||
3889 | err = sel_netnode_sid(addrp, family, &node_sid); | 3877 | err = sel_netnode_sid(addrp, family, &node_sid); |
3890 | if (err) | 3878 | if (err) |
3891 | goto out; | 3879 | return err; |
3892 | 3880 | err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad); | |
3893 | err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, ad); | ||
3894 | if (err) | 3881 | if (err) |
3895 | goto out; | 3882 | return err; |
3896 | 3883 | ||
3897 | if (recv_perm) { | 3884 | if (!recv_perm) |
3898 | u32 port_sid; | 3885 | return 0; |
3886 | err = security_port_sid(sk->sk_family, sk->sk_type, | ||
3887 | sk->sk_protocol, ntohs(ad->u.net.sport), | ||
3888 | &port_sid); | ||
3889 | if (err) | ||
3890 | return err; | ||
3891 | return avc_has_perm(sk_sid, port_sid, sk_class, recv_perm, ad); | ||
3892 | } | ||
3899 | 3893 | ||
3900 | err = security_port_sid(sk->sk_family, sk->sk_type, | 3894 | static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, |
3901 | sk->sk_protocol, ntohs(ad->u.net.sport), | 3895 | struct avc_audit_data *ad, |
3902 | &port_sid); | 3896 | u16 family, char *addrp) |
3903 | if (err) | 3897 | { |
3904 | goto out; | 3898 | int err; |
3899 | struct sk_security_struct *sksec = sk->sk_security; | ||
3900 | u32 peer_sid; | ||
3901 | u32 sk_sid = sksec->sid; | ||
3902 | |||
3903 | if (selinux_compat_net) | ||
3904 | err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad, | ||
3905 | family, addrp); | ||
3906 | else | ||
3907 | err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, | ||
3908 | PACKET__RECV, ad); | ||
3909 | if (err) | ||
3910 | return err; | ||
3905 | 3911 | ||
3906 | err = avc_has_perm(sock_sid, port_sid, | 3912 | if (selinux_policycap_netpeer) { |
3907 | sock_class, recv_perm, ad); | 3913 | err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); |
3914 | if (err) | ||
3915 | return err; | ||
3916 | err = avc_has_perm(sk_sid, peer_sid, | ||
3917 | SECCLASS_PEER, PEER__RECV, ad); | ||
3918 | } else { | ||
3919 | err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad); | ||
3920 | if (err) | ||
3921 | return err; | ||
3922 | err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad); | ||
3908 | } | 3923 | } |
3909 | 3924 | ||
3910 | out: | ||
3911 | return err; | 3925 | return err; |
3912 | } | 3926 | } |
3913 | 3927 | ||
3914 | static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | 3928 | static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) |
3915 | { | 3929 | { |
3916 | u16 family; | 3930 | int err; |
3917 | char *addrp; | ||
3918 | int err = 0; | ||
3919 | struct avc_audit_data ad; | ||
3920 | struct sk_security_struct *sksec = sk->sk_security; | 3931 | struct sk_security_struct *sksec = sk->sk_security; |
3932 | u16 family = sk->sk_family; | ||
3933 | u32 sk_sid = sksec->sid; | ||
3934 | u32 peer_sid; | ||
3935 | struct avc_audit_data ad; | ||
3936 | char *addrp; | ||
3921 | 3937 | ||
3922 | family = sk->sk_family; | ||
3923 | if (family != PF_INET && family != PF_INET6) | 3938 | if (family != PF_INET && family != PF_INET6) |
3924 | goto out; | 3939 | return 0; |
3925 | 3940 | ||
3926 | /* Handle mapped IPv4 packets arriving via IPv6 sockets */ | 3941 | /* Handle mapped IPv4 packets arriving via IPv6 sockets */ |
3927 | if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) | 3942 | if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) |
@@ -3930,26 +3945,27 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
3930 | AVC_AUDIT_DATA_INIT(&ad, NET); | 3945 | AVC_AUDIT_DATA_INIT(&ad, NET); |
3931 | ad.u.net.netif = skb->iif; | 3946 | ad.u.net.netif = skb->iif; |
3932 | ad.u.net.family = family; | 3947 | ad.u.net.family = family; |
3933 | |||
3934 | err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); | 3948 | err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); |
3935 | if (err) | 3949 | if (err) |
3936 | goto out; | 3950 | return err; |
3937 | 3951 | ||
3938 | if (selinux_compat_net) | 3952 | /* If any sort of compatibility mode is enabled then handoff processing |
3939 | err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family, addrp); | 3953 | * to the selinux_sock_rcv_skb_compat() function to deal with the |
3940 | else | 3954 | * special handling. We do this in an attempt to keep this function |
3941 | err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, | 3955 | * as fast and as clean as possible. */ |
3942 | PACKET__RECV, &ad); | 3956 | if (selinux_compat_net || !selinux_policycap_netpeer) |
3943 | if (err) | 3957 | return selinux_sock_rcv_skb_compat(sk, skb, &ad, |
3944 | goto out; | 3958 | family, addrp); |
3945 | 3959 | ||
3946 | err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad); | 3960 | err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, |
3961 | PACKET__RECV, &ad); | ||
3947 | if (err) | 3962 | if (err) |
3948 | goto out; | 3963 | return err; |
3949 | 3964 | ||
3950 | err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad); | 3965 | err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); |
3951 | out: | 3966 | if (err) |
3952 | return err; | 3967 | return err; |
3968 | return avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, PEER__RECV, &ad); | ||
3953 | } | 3969 | } |
3954 | 3970 | ||
3955 | static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval, | 3971 | static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval, |
@@ -4011,7 +4027,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff * | |||
4011 | if (sock && family == PF_UNIX) | 4027 | if (sock && family == PF_UNIX) |
4012 | selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid); | 4028 | selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid); |
4013 | else if (skb) | 4029 | else if (skb) |
4014 | selinux_skb_extlbl_sid(skb, family, &peer_secid); | 4030 | selinux_skb_peerlbl_sid(skb, family, &peer_secid); |
4015 | 4031 | ||
4016 | out: | 4032 | out: |
4017 | *secid = peer_secid; | 4033 | *secid = peer_secid; |
@@ -4037,6 +4053,7 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) | |||
4037 | 4053 | ||
4038 | newssec->sid = ssec->sid; | 4054 | newssec->sid = ssec->sid; |
4039 | newssec->peer_sid = ssec->peer_sid; | 4055 | newssec->peer_sid = ssec->peer_sid; |
4056 | newssec->sclass = ssec->sclass; | ||
4040 | 4057 | ||
4041 | selinux_netlbl_sk_security_clone(ssec, newssec); | 4058 | selinux_netlbl_sk_security_clone(ssec, newssec); |
4042 | } | 4059 | } |
@@ -4060,6 +4077,7 @@ static void selinux_sock_graft(struct sock* sk, struct socket *parent) | |||
4060 | if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || | 4077 | if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || |
4061 | sk->sk_family == PF_UNIX) | 4078 | sk->sk_family == PF_UNIX) |
4062 | isec->sid = sksec->sid; | 4079 | isec->sid = sksec->sid; |
4080 | sksec->sclass = isec->sclass; | ||
4063 | 4081 | ||
4064 | selinux_netlbl_sock_graft(sk, parent); | 4082 | selinux_netlbl_sock_graft(sk, parent); |
4065 | } | 4083 | } |
@@ -4072,7 +4090,9 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
4072 | u32 newsid; | 4090 | u32 newsid; |
4073 | u32 peersid; | 4091 | u32 peersid; |
4074 | 4092 | ||
4075 | selinux_skb_extlbl_sid(skb, sk->sk_family, &peersid); | 4093 | err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid); |
4094 | if (err) | ||
4095 | return err; | ||
4076 | if (peersid == SECSID_NULL) { | 4096 | if (peersid == SECSID_NULL) { |
4077 | req->secid = sksec->sid; | 4097 | req->secid = sksec->sid; |
4078 | req->peer_secid = SECSID_NULL; | 4098 | req->peer_secid = SECSID_NULL; |
@@ -4110,7 +4130,7 @@ static void selinux_inet_conn_established(struct sock *sk, | |||
4110 | { | 4130 | { |
4111 | struct sk_security_struct *sksec = sk->sk_security; | 4131 | struct sk_security_struct *sksec = sk->sk_security; |
4112 | 4132 | ||
4113 | selinux_skb_extlbl_sid(skb, sk->sk_family, &sksec->peer_sid); | 4133 | selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid); |
4114 | } | 4134 | } |
4115 | 4135 | ||
4116 | static void selinux_req_classify_flow(const struct request_sock *req, | 4136 | static void selinux_req_classify_flow(const struct request_sock *req, |