diff options
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/hooks.c | 60 | ||||
-rw-r--r-- | security/selinux/include/xfrm.h | 12 | ||||
-rw-r--r-- | security/selinux/nlmsgtab.c | 7 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 112 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 9 | ||||
-rw-r--r-- | security/selinux/xfrm.c | 68 |
6 files changed, 190 insertions, 78 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 81b726b1a41..b61b9554bc2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -117,6 +117,8 @@ static struct security_operations *secondary_ops = NULL; static LIST_HEAD(superblock_security_head); static DEFINE_SPINLOCK(sb_security_lock); +static kmem_cache_t *sel_inode_cache; + /* Return security context for a given sid or just the context length if the buffer is null or length is 0 */ static int selinux_getsecurity(u32 sid, void *buffer, size_t size) @@ -172,10 +174,11 @@ static int inode_alloc_security(struct inode *inode) struct task_security_struct *tsec = current->security; struct inode_security_struct *isec; - isec = kzalloc(sizeof(struct inode_security_struct), GFP_KERNEL); + isec = kmem_cache_alloc(sel_inode_cache, SLAB_KERNEL); if (!isec) return -ENOMEM; + memset(isec, 0, sizeof(*isec)); init_MUTEX(&isec->sem); INIT_LIST_HEAD(&isec->list); isec->inode = inode; @@ -198,7 +201,7 @@ static void inode_free_security(struct inode *inode) spin_unlock(&sbsec->isec_lock); inode->i_security = NULL; - kfree(isec); + kmem_cache_free(sel_inode_cache, isec); } static int file_alloc_security(struct file *file) @@ -1955,7 +1958,6 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, struct task_security_struct *tsec; struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; - struct inode_security_struct *isec; u32 newsid, clen; int rc; char *namep = NULL, *context; @@ -1963,7 +1965,6 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, tsec = current->security; dsec = dir->i_security; sbsec = dir->i_sb->s_security; - isec = inode->i_security; if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { newsid = tsec->create_sid; @@ -1983,7 +1984,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, inode_security_set_sid(inode, newsid); - if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) + if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) return -EOPNOTSUPP; if (name) { @@ -3316,24 +3317,38 @@ out: return err; } -static int selinux_socket_getpeersec(struct socket *sock, char __user *optval, - int __user *optlen, unsigned len) +static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval, + int __user *optlen, unsigned len) { int err = 0; char *scontext; u32 scontext_len; struct sk_security_struct *ssec; struct inode_security_struct *isec; + u32 peer_sid = 0; isec = SOCK_INODE(sock)->i_security; - if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) { + + /* if UNIX_STREAM check peer_sid, if TCP check dst for labelled sa */ + if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET) { + ssec = sock->sk->sk_security; + peer_sid = ssec->peer_sid; + } + else if (isec->sclass == SECCLASS_TCP_SOCKET) { + peer_sid = selinux_socket_getpeer_stream(sock->sk); + + if (peer_sid == SECSID_NULL) { + err = -ENOPROTOOPT; + goto out; + } + } + else { err = -ENOPROTOOPT; goto out; } - ssec = sock->sk->sk_security; - - err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len); + err = security_sid_to_context(peer_sid, &scontext, &scontext_len); + if (err) goto out; @@ -3354,6 +3369,23 @@ out: return err; } +static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, u32 *seclen) +{ + int err = 0; + u32 peer_sid = selinux_socket_getpeer_dgram(skb); + + if (peer_sid == SECSID_NULL) + return -EINVAL; + + err = security_sid_to_context(peer_sid, secdata, seclen); + if (err) + return err; + + return 0; +} + + + static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) { return sk_alloc_security(sk, family, priority); @@ -4338,7 +4370,8 @@ static struct security_operations selinux_ops = { .socket_setsockopt = selinux_socket_setsockopt, .socket_shutdown = selinux_socket_shutdown, .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb, - .socket_getpeersec = selinux_socket_getpeersec, + .socket_getpeersec_stream = selinux_socket_getpeersec_stream, + .socket_getpeersec_dgram = selinux_socket_getpeersec_dgram, .sk_alloc_security = selinux_sk_alloc_security, .sk_free_security = selinux_sk_free_security, .sk_getsid = selinux_sk_getsid_security, @@ -4370,6 +4403,9 @@ static __init int selinux_init(void) tsec = current->security; tsec->osid = tsec->sid = SECINITSID_KERNEL; + sel_inode_cache = kmem_cache_create("selinux_inode_security", + sizeof(struct inode_security_struct), + 0, SLAB_PANIC, NULL, NULL); avc_init(); original_ops = secondary_ops = security_ops; diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 8e87996c6dd..c10f1fc4150 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) #ifdef CONFIG_SECURITY_NETWORK_XFRM int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb); int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb); +u32 selinux_socket_getpeer_stream(struct sock *sk); +u32 selinux_socket_getpeer_dgram(struct sk_buff *skb); #else static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb) { @@ -49,6 +51,16 @@ static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb) { return NF_ACCEPT; } + +static inline int selinux_socket_getpeer_stream(struct sock *sk) +{ + return SECSID_NULL; +} + +static inline int selinux_socket_getpeer_dgram(struct sk_buff *skb) +{ + return SECSID_NULL; +} #endif #endif /* _SELINUX_XFRM_H_ */ diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 73158244cf8..b8f4d25cf33 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -88,8 +88,15 @@ static struct nlmsg_perm nlmsg_xfrm_perms[] = { XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, { XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ }, { XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_ACQUIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_EXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, { XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, { XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_POLEXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_FLUSHSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_NEWAE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_GETAE, NETLINK_XFRM_SOCKET__NLMSG_READ }, }; static struct nlmsg_perm nlmsg_audit_perms[] = diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 5eba6664eac..a4efc966f06 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/fs.h> +#include <linux/mutex.h> #include <linux/init.h> #include <linux/string.h> #include <linux/security.h> @@ -45,7 +46,7 @@ static int __init checkreqprot_setup(char *str) __setup("checkreqprot=", checkreqprot_setup); -static DECLARE_MUTEX(sel_sem); +static DEFINE_MUTEX(sel_mutex); /* global data for booleans */ static struct dentry *bool_dir = NULL; @@ -238,7 +239,7 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf, ssize_t length; void *data = NULL; - down(&sel_sem); + mutex_lock(&sel_mutex); length = task_has_security(current, SECURITY__LOAD_POLICY); if (length) @@ -273,7 +274,7 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf, "policy loaded auid=%u", audit_get_loginuid(current->audit_context)); out: - up(&sel_sem); + mutex_unlock(&sel_mutex); vfree(data); return length; } @@ -720,12 +721,11 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf, { char *page = NULL; ssize_t length; - ssize_t end; ssize_t ret; int cur_enforcing; struct inode *inode; - down(&sel_sem); + mutex_lock(&sel_mutex); ret = -EFAULT; @@ -751,26 +751,9 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf, length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, bool_pending_values[inode->i_ino - BOOL_INO_OFFSET]); - if (length < 0) { - ret = length; - goto out; - } - - if (*ppos >= length) { - ret = 0; - goto out; - } - if (count + *ppos > length) - count = length - *ppos; - end = count + *ppos; - if (copy_to_user(buf, (char *) page + *ppos, count)) { - ret = -EFAULT; - goto out; - } - *ppos = end; - ret = count; + ret = simple_read_from_buffer(buf, count, ppos, page, length); out: - up(&sel_sem); + mutex_unlock(&sel_mutex); if (page) free_page((unsigned long)page); return ret; @@ -784,7 +767,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, int new_value; struct inode *inode; - down(&sel_sem); + mutex_lock(&sel_mutex); length = task_has_security(current, SECURITY__SETBOOL); if (length) @@ -823,7 +806,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, length = count; out: - up(&sel_sem); + mutex_unlock(&sel_mutex); if (page) free_page((unsigned long) page); return length; @@ -842,7 +825,7 @@ static ssize_t sel_commit_bools_write(struct file *filep, ssize_t length = -EFAULT; int new_value; - down(&sel_sem); + mutex_lock(&sel_mutex); length = task_has_security(current, SECURITY__SETBOOL); if (length) @@ -880,7 +863,7 @@ static ssize_t sel_commit_bools_write(struct file *filep, length = count; out: - up(&sel_sem); + mutex_unlock(&sel_mutex); if (page) free_page((unsigned long) page); return length; @@ -998,7 +981,7 @@ out: return ret; err: kfree(values); - d_genocide(dir); + sel_remove_bools(dir); ret = -ENOMEM; goto out; } @@ -1179,37 +1162,38 @@ static int sel_make_avc_files(struct dentry *dir) dentry = d_alloc_name(dir, files[i].name); if (!dentry) { ret = -ENOMEM; - goto err; + goto out; } inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); if (!inode) { ret = -ENOMEM; - goto err; + goto out; } inode->i_fop = files[i].ops; d_add(dentry, inode); } out: return ret; -err: - d_genocide(dir); - goto out; } -static int sel_make_dir(struct super_block *sb, struct dentry *dentry) +static int sel_make_dir(struct inode *dir, struct dentry *dentry) { int ret = 0; struct inode *inode; - inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); + inode = sel_make_inode(dir->i_sb, S_IFDIR | S_IRUGO | S_IXUGO); if (!inode) { ret = -ENOMEM; goto out; } inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inode->i_nlink++; d_add(dentry, inode); + /* bump link count on parent directory, too */ + dir->i_nlink++; out: return ret; } @@ -1218,7 +1202,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) { int ret; struct dentry *dentry; - struct inode *inode; + struct inode *inode, *root_inode; struct inode_security_struct *isec; static struct tree_descr selinux_files[] = { @@ -1239,30 +1223,33 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) }; ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); if (ret) - return ret; + goto err; + + root_inode = sb->s_root->d_inode; dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME); - if (!dentry) - return -ENOMEM; + if (!dentry) { + ret = -ENOMEM; + goto err; + } - inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); - if (!inode) - goto out; - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; - d_add(dentry, inode); - bool_dir = dentry; - ret = sel_make_bools(); + ret = sel_make_dir(root_inode, dentry); if (ret) - goto out; + goto err; + + bool_dir = dentry; dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME); - if (!dentry) - return -ENOMEM; + if (!dentry) { + ret = -ENOMEM; + goto err; + } inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO); - if (!inode) - goto out; + if (!inode) { + ret = -ENOMEM; + goto err; + } isec = (struct inode_security_struct*)inode->i_security; isec->sid = SECINITSID_DEVNULL; isec->sclass = SECCLASS_CHR_FILE; @@ -1273,22 +1260,23 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) selinux_null = dentry; dentry = d_alloc_name(sb->s_root, "avc"); - if (!dentry) - return -ENOMEM; + if (!dentry) { + ret = -ENOMEM; + goto err; + } - ret = sel_make_dir(sb, dentry); + ret = sel_make_dir(root_inode, dentry); if (ret) - goto out; + goto err; ret = sel_make_avc_files(dentry); if (ret) - goto out; - - return 0; + goto err; out: - dput(dentry); + return ret; +err: printk(KERN_ERR "%s: failed while creating inodes\n", __FUNCTION__); - return -ENOMEM; + goto out; } static struct super_block *sel_get_sb(struct file_system_type *fs_type, diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index d877cd16a81..61492485de8 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -27,7 +27,8 @@ #include <linux/in.h> #include <linux/sched.h> #include <linux/audit.h> -#include <asm/semaphore.h> +#include <linux/mutex.h> + #include "flask.h" #include "avc.h" #include "avc_ss.h" @@ -48,9 +49,9 @@ static DEFINE_RWLOCK(policy_rwlock); #define POLICY_RDUNLOCK read_unlock(&policy_rwlock) #define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock) -static DECLARE_MUTEX(load_sem); -#define LOAD_LOCK down(&load_sem) -#define LOAD_UNLOCK up(&load_sem) +static DEFINE_MUTEX(load_mutex); +#define LOAD_LOCK mutex_lock(&load_mutex) +#define LOAD_UNLOCK mutex_unlock(&load_mutex) static struct sidtab sidtab; struct policydb policydb; diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index b2af7ca496c..dfab6c88669 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -225,6 +225,74 @@ void selinux_xfrm_state_free(struct xfrm_state *x) } /* + * SELinux internal function to retrieve the context of a connected + * (sk->sk_state == TCP_ESTABLISHED) TCP socket based on its security + * association used to connect to the remote socket. + * + * Retrieve via getsockopt SO_PEERSEC. + */ +u32 selinux_socket_getpeer_stream(struct sock *sk) +{ + struct dst_entry *dst, *dst_test; + u32 peer_sid = SECSID_NULL; + + if (sk->sk_state != TCP_ESTABLISHED) + goto out; + + dst = sk_dst_get(sk); + if (!dst) + goto out; + + for (dst_test = dst; dst_test != 0; + dst_test = dst_test->child) { + struct xfrm_state *x = dst_test->xfrm; + + if (x && selinux_authorizable_xfrm(x)) { + struct xfrm_sec_ctx *ctx = x->security; + peer_sid = ctx->ctx_sid; + break; + } + } + dst_release(dst); + +out: + return peer_sid; +} + +/* + * SELinux internal function to retrieve the context of a UDP packet + * based on its security association used to connect to the remote socket. + * + * Retrieve via setsockopt IP_PASSSEC and recvmsg with control message + * type SCM_SECURITY. + */ +u32 selinux_socket_getpeer_dgram(struct sk_buff *skb) +{ + struct sec_path *sp; + + if (skb == NULL) + return SECSID_NULL; + + if (skb->sk->sk_protocol != IPPROTO_UDP) + return SECSID_NULL; + + sp = skb->sp; + if (sp) { + int i; + + for (i = sp->len-1; i >= 0; i--) { + struct xfrm_state *x = sp->x[i].xvec; + if (selinux_authorizable_xfrm(x)) { + struct xfrm_sec_ctx *ctx = x->security; + return ctx->ctx_sid; + } + } + } + + return SECSID_NULL; +} + +/* * LSM hook that controls access to unlabelled packets. If * a xfrm_state is authorizable (defined by macro) then it was * already authorized by the IPSec process. If not, then |