diff options
-rw-r--r-- | net/ipv4/cipso_ipv4.c | 7 | ||||
-rw-r--r-- | net/ipv4/ip_options.c | 2 | ||||
-rw-r--r-- | security/selinux/hooks.c | 8 | ||||
-rw-r--r-- | security/selinux/include/selinux_netlabel.h | 10 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 37 |
5 files changed, 58 insertions, 6 deletions
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index e2077a3aa8c0..6460233407c7 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c | |||
@@ -1307,7 +1307,8 @@ int cipso_v4_socket_setattr(const struct socket *sock, | |||
1307 | 1307 | ||
1308 | /* We can't use ip_options_get() directly because it makes a call to | 1308 | /* We can't use ip_options_get() directly because it makes a call to |
1309 | * ip_options_get_alloc() which allocates memory with GFP_KERNEL and | 1309 | * ip_options_get_alloc() which allocates memory with GFP_KERNEL and |
1310 | * we can't block here. */ | 1310 | * we won't always have CAP_NET_RAW even though we _always_ want to |
1311 | * set the IPOPT_CIPSO option. */ | ||
1311 | opt_len = (buf_len + 3) & ~3; | 1312 | opt_len = (buf_len + 3) & ~3; |
1312 | opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC); | 1313 | opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC); |
1313 | if (opt == NULL) { | 1314 | if (opt == NULL) { |
@@ -1317,11 +1318,9 @@ int cipso_v4_socket_setattr(const struct socket *sock, | |||
1317 | memcpy(opt->__data, buf, buf_len); | 1318 | memcpy(opt->__data, buf, buf_len); |
1318 | opt->optlen = opt_len; | 1319 | opt->optlen = opt_len; |
1319 | opt->is_data = 1; | 1320 | opt->is_data = 1; |
1321 | opt->cipso = sizeof(struct iphdr); | ||
1320 | kfree(buf); | 1322 | kfree(buf); |
1321 | buf = NULL; | 1323 | buf = NULL; |
1322 | ret_val = ip_options_compile(opt, NULL); | ||
1323 | if (ret_val != 0) | ||
1324 | goto socket_setattr_failure; | ||
1325 | 1324 | ||
1326 | sk_inet = inet_sk(sk); | 1325 | sk_inet = inet_sk(sk); |
1327 | if (sk_inet->is_icsk) { | 1326 | if (sk_inet->is_icsk) { |
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 8dabbfc31267..9f02917d6f45 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c | |||
@@ -443,7 +443,7 @@ int ip_options_compile(struct ip_options * opt, struct sk_buff * skb) | |||
443 | opt->router_alert = optptr - iph; | 443 | opt->router_alert = optptr - iph; |
444 | break; | 444 | break; |
445 | case IPOPT_CIPSO: | 445 | case IPOPT_CIPSO: |
446 | if (opt->cipso) { | 446 | if ((!skb && !capable(CAP_NET_RAW)) || opt->cipso) { |
447 | pp_ptr = optptr; | 447 | pp_ptr = optptr; |
448 | goto error; | 448 | goto error; |
449 | } | 449 | } |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e9969a2fc846..8ab5679a37a3 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -3313,7 +3313,13 @@ static int selinux_socket_getpeername(struct socket *sock) | |||
3313 | 3313 | ||
3314 | static int selinux_socket_setsockopt(struct socket *sock,int level,int optname) | 3314 | static int selinux_socket_setsockopt(struct socket *sock,int level,int optname) |
3315 | { | 3315 | { |
3316 | return socket_has_perm(current, sock, SOCKET__SETOPT); | 3316 | int err; |
3317 | |||
3318 | err = socket_has_perm(current, sock, SOCKET__SETOPT); | ||
3319 | if (err) | ||
3320 | return err; | ||
3321 | |||
3322 | return selinux_netlbl_socket_setsockopt(sock, level, optname); | ||
3317 | } | 3323 | } |
3318 | 3324 | ||
3319 | static int selinux_socket_getsockopt(struct socket *sock, int level, | 3325 | static int selinux_socket_getsockopt(struct socket *sock, int level, |
diff --git a/security/selinux/include/selinux_netlabel.h b/security/selinux/include/selinux_netlabel.h index ecab4bddaaf4..9de10cc2cef2 100644 --- a/security/selinux/include/selinux_netlabel.h +++ b/security/selinux/include/selinux_netlabel.h | |||
@@ -53,6 +53,9 @@ void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, | |||
53 | void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec, | 53 | void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec, |
54 | struct sk_security_struct *newssec); | 54 | struct sk_security_struct *newssec); |
55 | int selinux_netlbl_inode_permission(struct inode *inode, int mask); | 55 | int selinux_netlbl_inode_permission(struct inode *inode, int mask); |
56 | int selinux_netlbl_socket_setsockopt(struct socket *sock, | ||
57 | int level, | ||
58 | int optname); | ||
56 | #else | 59 | #else |
57 | static inline void selinux_netlbl_cache_invalidate(void) | 60 | static inline void selinux_netlbl_cache_invalidate(void) |
58 | { | 61 | { |
@@ -114,6 +117,13 @@ static inline int selinux_netlbl_inode_permission(struct inode *inode, | |||
114 | { | 117 | { |
115 | return 0; | 118 | return 0; |
116 | } | 119 | } |
120 | |||
121 | static inline int selinux_netlbl_socket_setsockopt(struct socket *sock, | ||
122 | int level, | ||
123 | int optname) | ||
124 | { | ||
125 | return 0; | ||
126 | } | ||
117 | #endif /* CONFIG_NETLABEL */ | 127 | #endif /* CONFIG_NETLABEL */ |
118 | 128 | ||
119 | #endif | 129 | #endif |
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b1f6fb36c699..bfe122764c98 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
@@ -2682,4 +2682,41 @@ u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb) | |||
2682 | 2682 | ||
2683 | return peer_sid; | 2683 | return peer_sid; |
2684 | } | 2684 | } |
2685 | |||
2686 | /** | ||
2687 | * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel | ||
2688 | * @sock: the socket | ||
2689 | * @level: the socket level or protocol | ||
2690 | * @optname: the socket option name | ||
2691 | * | ||
2692 | * Description: | ||
2693 | * Check the setsockopt() call and if the user is trying to replace the IP | ||
2694 | * options on a socket and a NetLabel is in place for the socket deny the | ||
2695 | * access; otherwise allow the access. Returns zero when the access is | ||
2696 | * allowed, -EACCES when denied, and other negative values on error. | ||
2697 | * | ||
2698 | */ | ||
2699 | int selinux_netlbl_socket_setsockopt(struct socket *sock, | ||
2700 | int level, | ||
2701 | int optname) | ||
2702 | { | ||
2703 | int rc = 0; | ||
2704 | struct inode *inode = SOCK_INODE(sock); | ||
2705 | struct sk_security_struct *sksec = sock->sk->sk_security; | ||
2706 | struct inode_security_struct *isec = inode->i_security; | ||
2707 | struct netlbl_lsm_secattr secattr; | ||
2708 | |||
2709 | mutex_lock(&isec->lock); | ||
2710 | if (level == IPPROTO_IP && optname == IP_OPTIONS && | ||
2711 | sksec->nlbl_state == NLBL_LABELED) { | ||
2712 | netlbl_secattr_init(&secattr); | ||
2713 | rc = netlbl_socket_getattr(sock, &secattr); | ||
2714 | if (rc == 0 && (secattr.cache || secattr.mls_lvl_vld)) | ||
2715 | rc = -EACCES; | ||
2716 | netlbl_secattr_destroy(&secattr); | ||
2717 | } | ||
2718 | mutex_unlock(&isec->lock); | ||
2719 | |||
2720 | return rc; | ||
2721 | } | ||
2685 | #endif /* CONFIG_NETLABEL */ | 2722 | #endif /* CONFIG_NETLABEL */ |