/* We can't use ip_options_get() directly because it makes a call to
* ip_options_get_alloc() which allocates memory with GFP_KERNEL and
- * we can't block here. */
+ * we won't always have CAP_NET_RAW even though we _always_ want to
+ * set the IPOPT_CIPSO option. */
opt_len = (buf_len + 3) & ~3;
opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
if (opt == NULL) {
memcpy(opt->__data, buf, buf_len);
opt->optlen = opt_len;
opt->is_data = 1;
+ opt->cipso = sizeof(struct iphdr);
kfree(buf);
buf = NULL;
- ret_val = ip_options_compile(opt, NULL);
- if (ret_val != 0)
- goto socket_setattr_failure;
sk_inet = inet_sk(sk);
if (sk_inet->is_icsk) {
opt->router_alert = optptr - iph;
break;
case IPOPT_CIPSO:
- if (opt->cipso) {
+ if ((!skb && !capable(CAP_NET_RAW)) || opt->cipso) {
pp_ptr = optptr;
goto error;
}
static int selinux_socket_setsockopt(struct socket *sock,int level,int optname)
{
- return socket_has_perm(current, sock, SOCKET__SETOPT);
+ int err;
+
+ err = socket_has_perm(current, sock, SOCKET__SETOPT);
+ if (err)
+ return err;
+
+ return selinux_netlbl_socket_setsockopt(sock, level, optname);
}
static int selinux_socket_getsockopt(struct socket *sock, int level,
void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec,
struct sk_security_struct *newssec);
int selinux_netlbl_inode_permission(struct inode *inode, int mask);
+int selinux_netlbl_socket_setsockopt(struct socket *sock,
+ int level,
+ int optname);
#else
static inline void selinux_netlbl_cache_invalidate(void)
{
{
return 0;
}
+
+static inline int selinux_netlbl_socket_setsockopt(struct socket *sock,
+ int level,
+ int optname)
+{
+ return 0;
+}
#endif /* CONFIG_NETLABEL */
#endif
return peer_sid;
}
+
+/**
+ * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
+ * @sock: the socket
+ * @level: the socket level or protocol
+ * @optname: the socket option name
+ *
+ * Description:
+ * Check the setsockopt() call and if the user is trying to replace the IP
+ * options on a socket and a NetLabel is in place for the socket deny the
+ * access; otherwise allow the access. Returns zero when the access is
+ * allowed, -EACCES when denied, and other negative values on error.
+ *
+ */
+int selinux_netlbl_socket_setsockopt(struct socket *sock,
+ int level,
+ int optname)
+{
+ int rc = 0;
+ struct inode *inode = SOCK_INODE(sock);
+ struct sk_security_struct *sksec = sock->sk->sk_security;
+ struct inode_security_struct *isec = inode->i_security;
+ struct netlbl_lsm_secattr secattr;
+
+ mutex_lock(&isec->lock);
+ if (level == IPPROTO_IP && optname == IP_OPTIONS &&
+ sksec->nlbl_state == NLBL_LABELED) {
+ netlbl_secattr_init(&secattr);
+ rc = netlbl_socket_getattr(sock, &secattr);
+ if (rc == 0 && (secattr.cache || secattr.mls_lvl_vld))
+ rc = -EACCES;
+ netlbl_secattr_destroy(&secattr);
+ }
+ mutex_unlock(&isec->lock);
+
+ return rc;
+}
#endif /* CONFIG_NETLABEL */