Commit 7420ed23 authored by Venkat Yekkirala's avatar Venkat Yekkirala Committed by David S. Miller

[NetLabel]: SELinux support

Add NetLabel support to the SELinux LSM and modify the
socket_post_create() LSM hook to return an error code.  The most
significant part of this patch is the addition of NetLabel hooks into
the following SELinux LSM hooks:

 * selinux_file_permission()
 * selinux_socket_sendmsg()
 * selinux_socket_post_create()
 * selinux_socket_sock_rcv_skb()
 * selinux_socket_getpeersec_stream()
 * selinux_socket_getpeersec_dgram()
 * selinux_sock_graft()
 * selinux_inet_conn_request()

The basic reasoning behind this patch is that outgoing packets are
"NetLabel'd" by labeling their socket and the NetLabel security
attributes are checked via the additional hook in
selinux_socket_sock_rcv_skb().  NetLabel itself is only a labeling
mechanism, similar to filesystem extended attributes, it is up to the
SELinux enforcement mechanism to perform the actual access checks.

In addition to the changes outlined above this patch also includes
some changes to the extended bitmap (ebitmap) and multi-level security
(mls) code to import and export SELinux TE/MLS attributes into and out
of NetLabel.
Signed-off-by: default avatarPaul Moore <paul.moore@hp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 96cb8e33
......@@ -1341,7 +1341,7 @@ struct security_operations {
int (*unix_may_send) (struct socket * sock, struct socket * other);
int (*socket_create) (int family, int type, int protocol, int kern);
void (*socket_post_create) (struct socket * sock, int family,
int (*socket_post_create) (struct socket * sock, int family,
int type, int protocol, int kern);
int (*socket_bind) (struct socket * sock,
struct sockaddr * address, int addrlen);
......@@ -2824,12 +2824,12 @@ static inline int security_socket_create (int family, int type,
return security_ops->socket_create(family, type, protocol, kern);
}
static inline void security_socket_post_create(struct socket * sock,
static inline int security_socket_post_create(struct socket * sock,
int family,
int type,
int protocol, int kern)
{
security_ops->socket_post_create(sock, family, type,
return security_ops->socket_post_create(sock, family, type,
protocol, kern);
}
......@@ -2982,11 +2982,12 @@ static inline int security_socket_create (int family, int type,
return 0;
}
static inline void security_socket_post_create(struct socket * sock,
static inline int security_socket_post_create(struct socket * sock,
int family,
int type,
int protocol, int kern)
{
return 0;
}
static inline int security_socket_bind(struct socket * sock,
......
......@@ -973,11 +973,18 @@ int sock_create_lite(int family, int type, int protocol, struct socket **res)
goto out;
}
security_socket_post_create(sock, family, type, protocol, 1);
sock->type = type;
err = security_socket_post_create(sock, family, type, protocol, 1);
if (err)
goto out_release;
out:
*res = sock;
return err;
out_release:
sock_release(sock);
sock = NULL;
goto out;
}
/* No kernel lock held - perfect */
......@@ -1214,7 +1221,9 @@ static int __sock_create(int family, int type, int protocol, struct socket **res
*/
module_put(net_families[family]->owner);
*res = sock;
security_socket_post_create(sock, family, type, protocol, kern);
err = security_socket_post_create(sock, family, type, protocol, kern);
if (err)
goto out_release;
out:
net_family_read_unlock();
......
......@@ -709,10 +709,10 @@ static int dummy_socket_create (int family, int type,
return 0;
}
static void dummy_socket_post_create (struct socket *sock, int family, int type,
static int dummy_socket_post_create (struct socket *sock, int family, int type,
int protocol, int kern)
{
return;
return 0;
}
static int dummy_socket_bind (struct socket *sock, struct sockaddr *address,
......
......@@ -12,6 +12,8 @@
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* <dgoeddel@trustedcs.com>
* Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
* Paul Moore, <paul.moore@hp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
......@@ -74,6 +76,7 @@
#include "objsec.h"
#include "netif.h"
#include "xfrm.h"
#include "selinux_netlabel.h"
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
......@@ -2395,6 +2398,7 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
static int selinux_file_permission(struct file *file, int mask)
{
int rc;
struct inode *inode = file->f_dentry->d_inode;
if (!mask) {
......@@ -2406,8 +2410,12 @@ static int selinux_file_permission(struct file *file, int mask)
if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
mask |= MAY_APPEND;
return file_has_perm(current, file,
rc = file_has_perm(current, file,
file_mask_to_av(inode->i_mode, mask));
if (rc)
return rc;
return selinux_netlbl_inode_permission(inode, mask);
}
static int selinux_file_alloc_security(struct file *file)
......@@ -3058,9 +3066,10 @@ out:
return err;
}
static void selinux_socket_post_create(struct socket *sock, int family,
static int selinux_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern)
{
int err = 0;
struct inode_security_struct *isec;
struct task_security_struct *tsec;
struct sk_security_struct *sksec;
......@@ -3077,9 +3086,12 @@ static void selinux_socket_post_create(struct socket *sock, int family,
if (sock->sk) {
sksec = sock->sk->sk_security;
sksec->sid = isec->sid;
err = selinux_netlbl_socket_post_create(sock,
family,
isec->sid);
}
return;
return err;
}
/* Range of port numbers used to automatically bind.
......@@ -3260,7 +3272,13 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size)
{
return socket_has_perm(current, sock, SOCKET__WRITE);
int rc;
rc = socket_has_perm(current, sock, SOCKET__WRITE);
if (rc)
return rc;
return selinux_netlbl_inode_permission(SOCK_INODE(sock), MAY_WRITE);
}
static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
......@@ -3468,6 +3486,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (err)
goto out;
err = selinux_netlbl_sock_rcv_skb(sksec, skb, &ad);
if (err)
goto out;
err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
out:
return err;
......@@ -3491,8 +3513,9 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
peer_sid = ssec->peer_sid;
}
else if (isec->sclass == SECCLASS_TCP_SOCKET) {
peer_sid = selinux_netlbl_socket_getpeersec_stream(sock);
if (peer_sid == SECSID_NULL)
peer_sid = selinux_socket_getpeer_stream(sock->sk);
if (peer_sid == SECSID_NULL) {
err = -ENOPROTOOPT;
goto out;
......@@ -3532,8 +3555,11 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
if (sock && (sock->sk->sk_family == PF_UNIX))
selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
else if (skb)
else if (skb) {
peer_secid = selinux_netlbl_socket_getpeersec_dgram(skb);
if (peer_secid == SECSID_NULL)
peer_secid = selinux_socket_getpeer_dgram(skb);
}
if (peer_secid == SECSID_NULL)
err = -EINVAL;
......@@ -3578,6 +3604,8 @@ void selinux_sock_graft(struct sock* sk, struct socket *parent)
struct sk_security_struct *sksec = sk->sk_security;
isec->sid = sksec->sid;
selinux_netlbl_sock_graft(sk, parent);
}
int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
......@@ -3585,9 +3613,15 @@ int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
{
struct sk_security_struct *sksec = sk->sk_security;
int err;
u32 newsid = 0;
u32 newsid;
u32 peersid;
newsid = selinux_netlbl_inet_conn_request(skb, sksec->sid);
if (newsid != SECSID_NULL) {
req->secid = newsid;
return 0;
}
err = selinux_xfrm_decode_session(skb, &peersid, 0);
BUG_ON(err);
......
......@@ -101,6 +101,14 @@ struct sk_security_struct {
struct sock *sk; /* back pointer to sk object */
u32 sid; /* SID of this object */
u32 peer_sid; /* SID of peer */
#ifdef CONFIG_NETLABEL
u16 sclass; /* sock security class */
enum { /* NetLabel state */
NLBL_UNSET = 0,
NLBL_REQUIRE,
NLBL_LABELED,
} nlbl_state;
#endif
};
struct key_security_struct {
......
/*
* SELinux interface to the NetLabel subsystem
*
* Author : Paul Moore <paul.moore@hp.com>
*
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _SELINUX_NETLABEL_H_
#define _SELINUX_NETLABEL_H_
#ifdef CONFIG_NETLABEL
void selinux_netlbl_cache_invalidate(void);
int selinux_netlbl_socket_post_create(struct socket *sock,
int sock_family,
u32 sid);
void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid);
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
struct avc_audit_data *ad);
u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock);
u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb);
int __selinux_netlbl_inode_permission(struct inode *inode, int mask);
/**
* selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled
* @inode: the file descriptor's inode
* @mask: the permission mask
*
* Description:
* Looks at a file's inode and if it is marked as a socket protected by
* NetLabel then verify that the socket has been labeled, if not try to label
* the socket now with the inode's SID. Returns zero on success, negative
* values on failure.
*
*/
static inline int selinux_netlbl_inode_permission(struct inode *inode,
int mask)
{
int rc = 0;
struct inode_security_struct *isec;
struct sk_security_struct *sksec;
if (!S_ISSOCK(inode->i_mode))
return 0;
isec = inode->i_security;
sksec = SOCKET_I(inode)->sk->sk_security;
down(&isec->sem);
if (unlikely(sksec->nlbl_state == NLBL_REQUIRE &&
(mask & (MAY_WRITE | MAY_APPEND))))
rc = __selinux_netlbl_inode_permission(inode, mask);
up(&isec->sem);
return rc;
}
#else
static inline void selinux_netlbl_cache_invalidate(void)
{
return;
}
static inline int selinux_netlbl_socket_post_create(struct socket *sock,
int sock_family,
u32 sid)
{
return 0;
}
static inline void selinux_netlbl_sock_graft(struct sock *sk,
struct socket *sock)
{
return;
}
static inline u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb,
u32 sock_sid)
{
return SECSID_NULL;
}
static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
struct avc_audit_data *ad)
{
return 0;
}
static inline u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock)
{
return SECSID_NULL;
}
static inline u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb)
{
return SECSID_NULL;
}
static inline int selinux_netlbl_inode_permission(struct inode *inode,
int mask)
{
return 0;
}
#endif /* CONFIG_NETLABEL */
#endif
......@@ -3,6 +3,14 @@
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
/*
* Updated: Hewlett-Packard <paul.moore@hp.com>
*
* Added ebitmap_export() and ebitmap_import()
*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
......@@ -59,6 +67,142 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src)
return 0;
}
/**
* ebitmap_export - Export an ebitmap to a unsigned char bitmap string
* @src: the ebitmap to export
* @dst: the resulting bitmap string
* @dst_len: length of dst in bytes
*
* Description:
* Allocate a buffer at least src->highbit bits long and export the extensible
* bitmap into the buffer. The bitmap string will be in little endian format,
* i.e. LSB first. The value returned in dst_len may not the true size of the
* buffer as the length of the buffer is rounded up to a multiple of MAPTYPE.
* The caller must free the buffer when finished. Returns zero on success,
* negative values on failure.
*
*/
int ebitmap_export(const struct ebitmap *src,
unsigned char **dst,
size_t *dst_len)
{
size_t bitmap_len;
unsigned char *bitmap;
struct ebitmap_node *iter_node;
MAPTYPE node_val;
size_t bitmap_byte;
unsigned char bitmask;
bitmap_len = src->highbit / 8;
if (src->highbit % 7)
bitmap_len += 1;
if (bitmap_len == 0)
return -EINVAL;
bitmap = kzalloc((bitmap_len & ~(sizeof(MAPTYPE) - 1)) +
sizeof(MAPTYPE),
GFP_ATOMIC);
if (bitmap == NULL)
return -ENOMEM;
iter_node = src->node;
do {
bitmap_byte = iter_node->startbit / 8;
bitmask = 0x80;
node_val = iter_node->map;
do {
if (bitmask == 0) {
bitmap_byte++;
bitmask = 0x80;
}
if (node_val & (MAPTYPE)0x01)
bitmap[bitmap_byte] |= bitmask;
node_val >>= 1;
bitmask >>= 1;
} while (node_val > 0);
iter_node = iter_node->next;
} while (iter_node);
*dst = bitmap;
*dst_len = bitmap_len;
return 0;
}
/**
* ebitmap_import - Import an unsigned char bitmap string into an ebitmap
* @src: the bitmap string
* @src_len: the bitmap length in bytes
* @dst: the empty ebitmap
*
* Description:
* This function takes a little endian bitmap string in src and imports it into
* the ebitmap pointed to by dst. Returns zero on success, negative values on
* failure.
*
*/
int ebitmap_import(const unsigned char *src,
size_t src_len,
struct ebitmap *dst)
{
size_t src_off = 0;
struct ebitmap_node *node_new;
struct ebitmap_node *node_last = NULL;
size_t iter;
size_t iter_bit;
size_t iter_limit;
unsigned char src_byte;
do {
iter_limit = src_len - src_off;
if (iter_limit >= sizeof(MAPTYPE)) {
if (*(MAPTYPE *)&src[src_off] == 0) {
src_off += sizeof(MAPTYPE);
continue;
}
iter_limit = sizeof(MAPTYPE);
} else {
iter = src_off;
src_byte = 0;
do {
src_byte |= src[iter++];
} while (iter < src_len && src_byte == 0);
if (src_byte == 0)
break;
}
node_new = kzalloc(sizeof(*node_new), GFP_ATOMIC);
if (unlikely(node_new == NULL)) {
ebitmap_destroy(dst);
return -ENOMEM;
}
node_new->startbit = src_off * 8;
iter = 0;
do {
src_byte = src[src_off++];
iter_bit = iter++ * 8;
while (src_byte != 0) {
if (src_byte & 0x80)
node_new->map |= MAPBIT << iter_bit;
iter_bit++;
src_byte <<= 1;
}
} while (iter < iter_limit);
if (node_last != NULL)
node_last->next = node_new;
else
dst->node = node_new;
node_last = node_new;
} while (src_off < src_len);
if (likely(node_last != NULL))
dst->highbit = node_last->startbit + MAPSIZE;
else
ebitmap_init(dst);
return 0;
}
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
{
struct ebitmap_node *n1, *n2;
......
......@@ -69,6 +69,12 @@ static inline int ebitmap_node_get_bit(struct ebitmap_node * n,
int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);
int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src);
int ebitmap_export(const struct ebitmap *src,
unsigned char **dst,
size_t *dst_len);
int ebitmap_import(const unsigned char *src,
size_t src_len,
struct ebitmap *dst);
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2);
int ebitmap_get_bit(struct ebitmap *e, unsigned long bit);
int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);
......
......@@ -10,6 +10,13 @@
*
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
*/
/*
* Updated: Hewlett-Packard <paul.moore@hp.com>
*
* Added support to import/export the MLS label
*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
*/
#include <linux/kernel.h>
#include <linux/slab.h>
......@@ -565,3 +572,152 @@ int mls_compute_sid(struct context *scontext,
return -EINVAL;
}
/**
* mls_export_lvl - Export the MLS sensitivity levels
* @context: the security context
* @low: the low sensitivity level
* @high: the high sensitivity level
*
* Description:
* Given the security context copy the low MLS sensitivity level into lvl_low
* and the high sensitivity level in lvl_high. The MLS levels are only
* exported if the pointers are not NULL, if they are NULL then that level is
* not exported.
*
*/
void mls_export_lvl(const struct context *context, u32 *low, u32 *high)
{
if (!selinux_mls_enabled)
return;
if (low != NULL)
*low = context->range.level[0].sens - 1;
if (high != NULL)
*high = context->range.level[1].sens - 1;
}
/**
* mls_import_lvl - Import the MLS sensitivity levels
* @context: the security context
* @low: the low sensitivity level
* @high: the high sensitivity level
*
* Description:
* Given the security context and the two sensitivty levels, set the MLS levels
* in the context according the two given as parameters. Returns zero on
* success, negative values on failure.
*
*/
void mls_import_lvl(struct context *context, u32 low, u32 high)
{
if (!selinux_mls_enabled)
return;
context->range.level[0].sens = low + 1;
context->range.level[1].sens = high + 1;
}
/**
* mls_export_cat - Export the MLS categories
* @context: the security context
* @low: the low category
* @low_len: length of the cat_low bitmap in bytes
* @high: the high category
* @high_len: length of the cat_high bitmap in bytes
*
* Description:
* Given the security context export the low MLS category bitmap into cat_low
* and the high category bitmap into cat_high. The MLS categories are only
* exported if the pointers are not NULL, if they are NULL then that level is
* not exported. The caller is responsibile for freeing the memory when
* finished. Returns zero on success, negative values on failure.
*
*/
int mls_export_cat(const struct context *context,
unsigned char **low,
size_t *low_len,
unsigned char **high,
size_t *high_len)
{
int rc = -EPERM;
if (!selinux_mls_enabled)
return 0;
if (low != NULL) {
rc = ebitmap_export(&context->range.level[0].cat,
low,
low_len);
if (rc != 0)
goto export_cat_failure;
}
if (high != NULL) {
rc = ebitmap_export(&context->range.level[1].cat,
high,
high_len);
if (rc != 0)
goto export_cat_failure;
}
return 0;
export_cat_failure:
if (low != NULL)
kfree(*low);
if (high != NULL)
kfree(*high);
return rc;
}
/**
* mls_import_cat - Import the MLS categories
* @context: the security context
* @low: the low category
* @low_len: length of the cat_low bitmap in bytes
* @high: the high category
* @high_len: length of the cat_high bitmap in bytes
*
* Description:
* Given the security context and the two category bitmap strings import the
* categories into the security context. The MLS categories are only imported
* if the pointers are not NULL, if they are NULL they are skipped. Returns
* zero on success, negative values on failure.
*
*/
int mls_import_cat(struct context *context,
const unsigned char *low,
size_t low_len,
const unsigned char *high,
size_t high_len)
{
int rc = -EPERM;
if (!selinux_mls_enabled)
return 0;
if (low != NULL) {
rc = ebitmap_import(low,
low_len,
&context->range.level[0].cat);
if (rc != 0)
goto import_cat_failure;
}
if (high != NULL) {
if (high == low)
rc = ebitmap_cpy(&context->range.level[1].cat,
&context->range.level[0].cat);
else
rc = ebitmap_import(high,
high_len,
&context->range.level[1].cat);
if (rc != 0)
goto import_cat_failure;
}
return 0;
import_cat_failure:
ebitmap_destroy(&context->range.level[0].cat);
ebitmap_destroy(&context->range.level[1].cat);
return rc;
}
......@@ -10,6 +10,13 @@
*
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
*/
/*
* Updated: Hewlett-Packard <paul.moore@hp.com>
*
* Added support to import/export the MLS label
*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
*/
#ifndef _SS_MLS_H_
#define _SS_MLS_H_
......@@ -62,5 +69,19 @@ int mls_compute_sid(struct context *scontext,
int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
struct context *usercon);
void mls_export_lvl(const struct context *context, u32 *low, u32 *high);
void mls_import_lvl(struct context *context, u32 low, u32 high);
int mls_export_cat(const struct context *context,
unsigned char **low,
size_t *low_len,
unsigned char **high,
size_t *high_len);
int mls_import_cat(struct context *context,
const unsigned char *low,
size_t low_len,
const unsigned char *high,
size_t high_len);
#endif /* _SS_MLS_H */
This diff is collapsed.
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