*
* Copyright (C) 2001,2002 Networks Associates Technology, Inc.
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ * <dgoeddel@trustedcs.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,
* as published by the Free Software Foundation.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/security.h>
#include <linux/kd.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
+#include <linux/tty.h>
#include <net/icmp.h>
#include <net/ip.h> /* for sysctl_local_port_range[] */
#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
#include <linux/nfs_mount.h>
#include <net/ipv6.h>
#include <linux/hugetlb.h>
+#include <linux/personality.h>
+#include <linux/sysctl.h>
+#include <linux/audit.h>
+#include <linux/string.h>
+#include <linux/selinux.h>
#include "avc.h"
#include "objsec.h"
#include "netif.h"
+#include "xfrm.h"
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
+extern unsigned int policydb_loaded_version;
+extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
+extern int selinux_compat_net;
+
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
int selinux_enforcing = 0;
#endif
#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
-int selinux_enabled = 1;
+int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
static int __init selinux_enabled_setup(char *str)
{
return 1;
}
__setup("selinux=", selinux_enabled_setup);
+#else
+int selinux_enabled = 1;
#endif
/* Original (dummy) security module. */
/* Lists of inode and superblock security structures initialized
before the policy was loaded. */
static LIST_HEAD(superblock_security_head);
-static spinlock_t sb_security_lock = SPIN_LOCK_UNLOCKED;
+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)
+{
+ char *context;
+ unsigned len;
+ int rc;
+
+ rc = security_sid_to_context(sid, &context, &len);
+ if (rc)
+ return rc;
+
+ if (!buffer || !size)
+ goto getsecurity_exit;
+
+ if (size < len) {
+ len = -ERANGE;
+ goto getsecurity_exit;
+ }
+ memcpy(buffer, context, len);
+
+getsecurity_exit:
+ kfree(context);
+ return len;
+}
/* Allocate and free functions for each kind of security blob. */
{
struct task_security_struct *tsec;
- tsec = kmalloc(sizeof(struct task_security_struct), GFP_KERNEL);
+ tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
if (!tsec)
return -ENOMEM;
- memset(tsec, 0, sizeof(struct task_security_struct));
- tsec->magic = SELINUX_MAGIC;
tsec->task = task;
- tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
+ tsec->osid = tsec->sid = SECINITSID_UNLABELED;
task->security = tsec;
return 0;
static void task_free_security(struct task_struct *task)
{
struct task_security_struct *tsec = task->security;
-
- if (!tsec || tsec->magic != SELINUX_MAGIC)
- return;
-
task->security = NULL;
kfree(tsec);
}
struct task_security_struct *tsec = current->security;
struct inode_security_struct *isec;
- isec = kmalloc(sizeof(struct inode_security_struct), GFP_KERNEL);
+ isec = kmem_cache_alloc(sel_inode_cache, SLAB_KERNEL);
if (!isec)
return -ENOMEM;
- memset(isec, 0, sizeof(struct inode_security_struct));
+ memset(isec, 0, sizeof(*isec));
init_MUTEX(&isec->sem);
INIT_LIST_HEAD(&isec->list);
- isec->magic = SELINUX_MAGIC;
isec->inode = inode;
isec->sid = SECINITSID_UNLABELED;
isec->sclass = SECCLASS_FILE;
- if (tsec && tsec->magic == SELINUX_MAGIC)
- isec->task_sid = tsec->sid;
- else
- isec->task_sid = SECINITSID_UNLABELED;
+ isec->task_sid = tsec->sid;
inode->i_security = isec;
return 0;
struct inode_security_struct *isec = inode->i_security;
struct superblock_security_struct *sbsec = inode->i_sb->s_security;
- if (!isec || isec->magic != SELINUX_MAGIC)
- return;
-
spin_lock(&sbsec->isec_lock);
if (!list_empty(&isec->list))
list_del_init(&isec->list);
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)
struct task_security_struct *tsec = current->security;
struct file_security_struct *fsec;
- fsec = kmalloc(sizeof(struct file_security_struct), GFP_ATOMIC);
+ fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
if (!fsec)
return -ENOMEM;
- memset(fsec, 0, sizeof(struct file_security_struct));
- fsec->magic = SELINUX_MAGIC;
fsec->file = file;
- if (tsec && tsec->magic == SELINUX_MAGIC) {
- fsec->sid = tsec->sid;
- fsec->fown_sid = tsec->sid;
- } else {
- fsec->sid = SECINITSID_UNLABELED;
- fsec->fown_sid = SECINITSID_UNLABELED;
- }
+ fsec->sid = tsec->sid;
+ fsec->fown_sid = tsec->sid;
file->f_security = fsec;
return 0;
static void file_free_security(struct file *file)
{
struct file_security_struct *fsec = file->f_security;
-
- if (!fsec || fsec->magic != SELINUX_MAGIC)
- return;
-
file->f_security = NULL;
kfree(fsec);
}
{
struct superblock_security_struct *sbsec;
- sbsec = kmalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
+ sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
if (!sbsec)
return -ENOMEM;
- memset(sbsec, 0, sizeof(struct superblock_security_struct));
init_MUTEX(&sbsec->sem);
INIT_LIST_HEAD(&sbsec->list);
INIT_LIST_HEAD(&sbsec->isec_head);
spin_lock_init(&sbsec->isec_lock);
- sbsec->magic = SELINUX_MAGIC;
sbsec->sb = sb;
sbsec->sid = SECINITSID_UNLABELED;
sbsec->def_sid = SECINITSID_FILE;
+ sbsec->mntpoint_sid = SECINITSID_UNLABELED;
sb->s_security = sbsec;
return 0;
{
struct superblock_security_struct *sbsec = sb->s_security;
- if (!sbsec || sbsec->magic != SELINUX_MAGIC)
- return;
-
spin_lock(&sb_security_lock);
if (!list_empty(&sbsec->list))
list_del_init(&sbsec->list);
kfree(sbsec);
}
-#ifdef CONFIG_SECURITY_NETWORK
-static int sk_alloc_security(struct sock *sk, int family, int priority)
+static int sk_alloc_security(struct sock *sk, int family, gfp_t priority)
{
struct sk_security_struct *ssec;
if (family != PF_UNIX)
return 0;
- ssec = kmalloc(sizeof(*ssec), priority);
+ ssec = kzalloc(sizeof(*ssec), priority);
if (!ssec)
return -ENOMEM;
- memset(ssec, 0, sizeof(*ssec));
- ssec->magic = SELINUX_MAGIC;
ssec->sk = sk;
ssec->peer_sid = SECINITSID_UNLABELED;
sk->sk_security = ssec;
{
struct sk_security_struct *ssec = sk->sk_security;
- if (sk->sk_family != PF_UNIX || ssec->magic != SELINUX_MAGIC)
+ if (sk->sk_family != PF_UNIX)
return;
sk->sk_security = NULL;
kfree(ssec);
}
-#endif /* CONFIG_SECURITY_NETWORK */
/* The security server must be initialized before
any labeling or access decisions can be provided. */
Opt_context = 1,
Opt_fscontext = 2,
Opt_defcontext = 4,
+ Opt_rootcontext = 8,
};
static match_table_t tokens = {
{Opt_context, "context=%s"},
{Opt_fscontext, "fscontext=%s"},
{Opt_defcontext, "defcontext=%s"},
+ {Opt_rootcontext, "rootcontext=%s"},
};
#define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n"
+static int may_context_mount_sb_relabel(u32 sid,
+ struct superblock_security_struct *sbsec,
+ struct task_security_struct *tsec)
+{
+ int rc;
+
+ rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__RELABELFROM, NULL);
+ if (rc)
+ return rc;
+
+ rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__RELABELTO, NULL);
+ return rc;
+}
+
+static int may_context_mount_inode_relabel(u32 sid,
+ struct superblock_security_struct *sbsec,
+ struct task_security_struct *tsec)
+{
+ int rc;
+ rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__RELABELFROM, NULL);
+ if (rc)
+ return rc;
+
+ rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__ASSOCIATE, NULL);
+ return rc;
+}
+
static int try_context_mount(struct super_block *sb, void *data)
{
char *context = NULL, *defcontext = NULL;
+ char *fscontext = NULL, *rootcontext = NULL;
const char *name;
u32 sid;
int alloc = 0, rc = 0, seen = 0;
switch (token) {
case Opt_context:
- if (seen) {
+ if (seen & (Opt_context|Opt_defcontext)) {
rc = -EINVAL;
printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
goto out_free;
break;
case Opt_fscontext:
- if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
+ if (seen & Opt_fscontext) {
rc = -EINVAL;
- printk(KERN_WARNING "SELinux: "
- "fscontext option is invalid for"
- " this filesystem type\n");
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_free;
+ }
+ fscontext = match_strdup(&args[0]);
+ if (!fscontext) {
+ rc = -ENOMEM;
goto out_free;
}
- if (seen & (Opt_context|Opt_fscontext)) {
+ if (!alloc)
+ alloc = 1;
+ seen |= Opt_fscontext;
+ break;
+
+ case Opt_rootcontext:
+ if (seen & Opt_rootcontext) {
rc = -EINVAL;
printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
goto out_free;
}
- context = match_strdup(&args[0]);
- if (!context) {
+ rootcontext = match_strdup(&args[0]);
+ if (!rootcontext) {
rc = -ENOMEM;
goto out_free;
}
if (!alloc)
alloc = 1;
- seen |= Opt_fscontext;
+ seen |= Opt_rootcontext;
break;
case Opt_defcontext:
if (!seen)
goto out;
+ /* sets the context of the superblock for the fs being mounted. */
+ if (fscontext) {
+ rc = security_context_to_sid(fscontext, strlen(fscontext), &sid);
+ if (rc) {
+ printk(KERN_WARNING "SELinux: security_context_to_sid"
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ fscontext, sb->s_id, name, rc);
+ goto out_free;
+ }
+
+ rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+ if (rc)
+ goto out_free;
+
+ sbsec->sid = sid;
+ }
+
+ /*
+ * Switch to using mount point labeling behavior.
+ * sets the label used on all file below the mountpoint, and will set
+ * the superblock context if not already set.
+ */
if (context) {
rc = security_context_to_sid(context, strlen(context), &sid);
if (rc) {
goto out_free;
}
- rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__RELABELFROM, NULL, NULL);
- if (rc)
+ if (!fscontext) {
+ rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+ if (rc)
+ goto out_free;
+ sbsec->sid = sid;
+ } else {
+ rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
+ if (rc)
+ goto out_free;
+ }
+ sbsec->mntpoint_sid = sid;
+
+ sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+ }
+
+ if (rootcontext) {
+ struct inode *inode = sb->s_root->d_inode;
+ struct inode_security_struct *isec = inode->i_security;
+ rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid);
+ if (rc) {
+ printk(KERN_WARNING "SELinux: security_context_to_sid"
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ rootcontext, sb->s_id, name, rc);
goto out_free;
+ }
- rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__RELABELTO, NULL, NULL);
+ rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
if (rc)
goto out_free;
- sbsec->sid = sid;
-
- if (seen & Opt_context)
- sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+ isec->sid = sid;
+ isec->initialized = 1;
}
if (defcontext) {
if (sid == sbsec->def_sid)
goto out_free;
- rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__RELABELFROM, NULL, NULL);
- if (rc)
- goto out_free;
-
- rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__ASSOCIATE, NULL, NULL);
+ rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
if (rc)
goto out_free;
if (alloc) {
kfree(context);
kfree(defcontext);
+ kfree(fscontext);
+ kfree(rootcontext);
}
out:
return rc;
spin_unlock(&sbsec->isec_lock);
inode = igrab(inode);
if (inode) {
- inode_doinit(inode);
+ if (!IS_PRIVATE (inode))
+ inode_doinit(inode);
iput(inode);
}
spin_lock(&sbsec->isec_lock);
return SECCLASS_FILE;
}
-static inline u16 socket_type_to_security_class(int family, int type)
+static inline int default_protocol_stream(int protocol)
+{
+ return (protocol == IPPROTO_IP || protocol == IPPROTO_TCP);
+}
+
+static inline int default_protocol_dgram(int protocol)
+{
+ return (protocol == IPPROTO_IP || protocol == IPPROTO_UDP);
+}
+
+static inline u16 socket_type_to_security_class(int family, int type, int protocol)
{
switch (family) {
case PF_UNIX:
switch (type) {
case SOCK_STREAM:
+ case SOCK_SEQPACKET:
return SECCLASS_UNIX_STREAM_SOCKET;
case SOCK_DGRAM:
return SECCLASS_UNIX_DGRAM_SOCKET;
}
+ break;
case PF_INET:
case PF_INET6:
switch (type) {
case SOCK_STREAM:
- return SECCLASS_TCP_SOCKET;
+ if (default_protocol_stream(protocol))
+ return SECCLASS_TCP_SOCKET;
+ else
+ return SECCLASS_RAWIP_SOCKET;
case SOCK_DGRAM:
- return SECCLASS_UDP_SOCKET;
- case SOCK_RAW:
+ if (default_protocol_dgram(protocol))
+ return SECCLASS_UDP_SOCKET;
+ else
+ return SECCLASS_RAWIP_SOCKET;
+ default:
return SECCLASS_RAWIP_SOCKET;
}
+ break;
case PF_NETLINK:
- return SECCLASS_NETLINK_SOCKET;
+ switch (protocol) {
+ case NETLINK_ROUTE:
+ return SECCLASS_NETLINK_ROUTE_SOCKET;
+ case NETLINK_FIREWALL:
+ return SECCLASS_NETLINK_FIREWALL_SOCKET;
+ case NETLINK_INET_DIAG:
+ return SECCLASS_NETLINK_TCPDIAG_SOCKET;
+ case NETLINK_NFLOG:
+ return SECCLASS_NETLINK_NFLOG_SOCKET;
+ case NETLINK_XFRM:
+ return SECCLASS_NETLINK_XFRM_SOCKET;
+ case NETLINK_SELINUX:
+ return SECCLASS_NETLINK_SELINUX_SOCKET;
+ case NETLINK_AUDIT:
+ return SECCLASS_NETLINK_AUDIT_SOCKET;
+ case NETLINK_IP6_FW:
+ return SECCLASS_NETLINK_IP6FW_SOCKET;
+ case NETLINK_DNRTMSG:
+ return SECCLASS_NETLINK_DNRT_SOCKET;
+ case NETLINK_KOBJECT_UEVENT:
+ return SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET;
+ default:
+ return SECCLASS_NETLINK_SOCKET;
+ }
case PF_PACKET:
return SECCLASS_PACKET_SOCKET;
case PF_KEY:
return SECCLASS_KEY_SOCKET;
+ case PF_APPLETALK:
+ return SECCLASS_APPLETALK_SOCKET;
}
return SECCLASS_SOCKET;
sid = sbsec->def_sid;
rc = 0;
} else {
- rc = security_context_to_sid(context, rc, &sid);
+ rc = security_context_to_sid_default(context, rc, &sid,
+ sbsec->def_sid);
if (rc) {
printk(KERN_WARNING "%s: context_to_sid(%s) "
"returned %d for dev=%s ino=%ld\n",
__FUNCTION__, context, -rc,
inode->i_sb->s_id, inode->i_ino);
kfree(context);
- goto out;
+ /* Leave with the unlabeled SID */
+ rc = 0;
+ break;
}
}
kfree(context);
goto out;
isec->sid = sid;
break;
+ case SECURITY_FS_USE_MNTPOINT:
+ isec->sid = sbsec->mntpoint_sid;
+ break;
default:
- /* Default to the fs SID. */
+ /* Default to the fs superblock SID. */
isec->sid = sbsec->sid;
if (sbsec->proc) {
isec->initialized = 1;
out:
- if (inode->i_sock) {
- struct socket *sock = SOCKET_I(inode);
- if (sock->sk) {
- isec->sclass = socket_type_to_security_class(sock->sk->sk_family,
- sock->sk->sk_type);
- } else {
- isec->sclass = SECCLASS_SOCKET;
- }
- } else {
+ if (isec->sclass == SECCLASS_FILE)
isec->sclass = inode_mode_to_security_class(inode->i_mode);
- }
if (hold_sem)
up(&isec->sem);
/* Check permission betweeen a pair of tasks, e.g. signal checks,
fork check, ptrace check, etc. */
-int task_has_perm(struct task_struct *tsk1,
- struct task_struct *tsk2,
- u32 perms)
+static int task_has_perm(struct task_struct *tsk1,
+ struct task_struct *tsk2,
+ u32 perms)
{
struct task_security_struct *tsec1, *tsec2;
tsec1 = tsk1->security;
tsec2 = tsk2->security;
return avc_has_perm(tsec1->sid, tsec2->sid,
- SECCLASS_PROCESS, perms, &tsec2->avcr, NULL);
+ SECCLASS_PROCESS, perms, NULL);
}
/* Check whether a task is allowed to use a capability. */
-int task_has_capability(struct task_struct *tsk,
- int cap)
+static int task_has_capability(struct task_struct *tsk,
+ int cap)
{
struct task_security_struct *tsec;
struct avc_audit_data ad;
ad.u.cap = cap;
return avc_has_perm(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY, CAP_TO_MASK(cap), NULL, &ad);
+ SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
}
/* Check whether a task is allowed to use a system operation. */
-int task_has_system(struct task_struct *tsk,
- u32 perms)
+static int task_has_system(struct task_struct *tsk,
+ u32 perms)
{
struct task_security_struct *tsec;
tsec = tsk->security;
return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
- SECCLASS_SYSTEM, perms, NULL, NULL);
+ SECCLASS_SYSTEM, perms, NULL);
}
/* Check whether a task has a particular permission to an inode.
- The 'aeref' parameter is optional and allows other AVC
- entry references to be passed (e.g. the one in the struct file).
The 'adp' parameter is optional and allows other audit
data to be passed (e.g. the dentry). */
-int inode_has_perm(struct task_struct *tsk,
- struct inode *inode,
- u32 perms,
- struct avc_entry_ref *aeref,
- struct avc_audit_data *adp)
+static int inode_has_perm(struct task_struct *tsk,
+ struct inode *inode,
+ u32 perms,
+ struct avc_audit_data *adp)
{
struct task_security_struct *tsec;
struct inode_security_struct *isec;
ad.u.fs.inode = inode;
}
- return avc_has_perm(tsec->sid, isec->sid, isec->sclass,
- perms, aeref ? aeref : &isec->avcr, adp);
+ return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
}
/* Same as inode_has_perm, but pass explicit audit data containing
AVC_AUDIT_DATA_INIT(&ad,FS);
ad.u.fs.mnt = mnt;
ad.u.fs.dentry = dentry;
- return inode_has_perm(tsk, inode, av, NULL, &ad);
+ return inode_has_perm(tsk, inode, av, &ad);
}
/* Check whether a task can use an open file descriptor to
has the same SID as the process. If av is zero, then
access to the file is not checked, e.g. for cases
where only the descriptor is affected like seek. */
-static inline int file_has_perm(struct task_struct *tsk,
+static int file_has_perm(struct task_struct *tsk,
struct file *file,
u32 av)
{
rc = avc_has_perm(tsec->sid, fsec->sid,
SECCLASS_FD,
FD__USE,
- &fsec->avcr, &ad);
+ &ad);
if (rc)
return rc;
}
/* av is zero if only checking access to the descriptor. */
if (av)
- return inode_has_perm(tsk, inode, av, &fsec->inode_avcr, &ad);
+ return inode_has_perm(tsk, inode, av, &ad);
return 0;
}
rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
DIR__ADD_NAME | DIR__SEARCH,
- &dsec->avcr, &ad);
+ &ad);
if (rc)
return rc;
return rc;
}
- rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, NULL, &ad);
+ rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
if (rc)
return rc;
return avc_has_perm(newsid, sbsec->sid,
SECCLASS_FILESYSTEM,
- FILESYSTEM__ASSOCIATE, NULL, &ad);
+ FILESYSTEM__ASSOCIATE, &ad);
+}
+
+/* Check whether a task can create a key. */
+static int may_create_key(u32 ksid,
+ struct task_struct *ctx)
+{
+ struct task_security_struct *tsec;
+
+ tsec = ctx->security;
+
+ return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
}
#define MAY_LINK 0
av = DIR__SEARCH;
av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
- rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
- av, &dsec->avcr, &ad);
+ rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
return 0;
}
- rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
- av, &isec->avcr, &ad);
+ rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
return rc;
}
ad.u.fs.dentry = old_dentry;
rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
- DIR__REMOVE_NAME | DIR__SEARCH,
- &old_dsec->avcr, &ad);
+ DIR__REMOVE_NAME | DIR__SEARCH, &ad);
if (rc)
return rc;
rc = avc_has_perm(tsec->sid, old_isec->sid,
- old_isec->sclass,
- FILE__RENAME,
- &old_isec->avcr, &ad);
+ old_isec->sclass, FILE__RENAME, &ad);
if (rc)
return rc;
if (old_is_dir && new_dir != old_dir) {
rc = avc_has_perm(tsec->sid, old_isec->sid,
- old_isec->sclass,
- DIR__REPARENT,
- &old_isec->avcr, &ad);
+ old_isec->sclass, DIR__REPARENT, &ad);
if (rc)
return rc;
}
av = DIR__ADD_NAME | DIR__SEARCH;
if (new_dentry->d_inode)
av |= DIR__REMOVE_NAME;
- rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR,
- av,&new_dsec->avcr, &ad);
+ rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
if (new_dentry->d_inode) {
new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
rc = avc_has_perm(tsec->sid, new_isec->sid,
new_isec->sclass,
- (new_is_dir ? DIR__RMDIR : FILE__UNLINK),
- &new_isec->avcr, &ad);
+ (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
if (rc)
return rc;
}
}
/* Check whether a task can perform a filesystem operation. */
-int superblock_has_perm(struct task_struct *tsk,
- struct super_block *sb,
- u32 perms,
- struct avc_audit_data *ad)
+static int superblock_has_perm(struct task_struct *tsk,
+ struct super_block *sb,
+ u32 perms,
+ struct avc_audit_data *ad)
{
struct task_security_struct *tsec;
struct superblock_security_struct *sbsec;
tsec = tsk->security;
sbsec = sb->s_security;
return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- perms, NULL, ad);
+ perms, ad);
}
/* Convert a Linux mode and permission mask to an access vector. */
}
/* Set an inode's SID to a specified value. */
-int inode_security_set_sid(struct inode *inode, u32 sid)
+static int inode_security_set_sid(struct inode *inode, u32 sid)
{
struct inode_security_struct *isec = inode->i_security;
+ struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+
+ if (!sbsec->initialized) {
+ /* Defer initialization to selinux_complete_init. */
+ return 0;
+ }
down(&isec->sem);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
return 0;
}
-/* Set the security attributes on a newly created file. */
-static int post_create(struct inode *dir,
- struct dentry *dentry)
-{
-
- struct task_security_struct *tsec;
- struct inode *inode;
- struct inode_security_struct *dsec;
- struct superblock_security_struct *sbsec;
- u32 newsid;
- char *context;
- unsigned int len;
- int rc;
-
- tsec = current->security;
- dsec = dir->i_security;
- sbsec = dir->i_sb->s_security;
-
- inode = dentry->d_inode;
- if (!inode) {
- /* Some file system types (e.g. NFS) may not instantiate
- a dentry for all create operations (e.g. symlink),
- so we have to check to see if the inode is non-NULL. */
- printk(KERN_WARNING "post_create: no inode, dir (dev=%s, "
- "ino=%ld)\n", dir->i_sb->s_id, dir->i_ino);
- return 0;
- }
-
- if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
- newsid = tsec->create_sid;
- } else {
- rc = security_transition_sid(tsec->sid, dsec->sid,
- inode_mode_to_security_class(inode->i_mode),
- &newsid);
- if (rc) {
- printk(KERN_WARNING "post_create: "
- "security_transition_sid failed, rc=%d (dev=%s "
- "ino=%ld)\n",
- -rc, inode->i_sb->s_id, inode->i_ino);
- return rc;
- }
- }
-
- rc = inode_security_set_sid(inode, newsid);
- if (rc) {
- printk(KERN_WARNING "post_create: inode_security_set_sid "
- "failed, rc=%d (dev=%s ino=%ld)\n",
- -rc, inode->i_sb->s_id, inode->i_ino);
- return rc;
- }
-
- if (sbsec->behavior == SECURITY_FS_USE_XATTR &&
- inode->i_op->setxattr) {
- /* Use extended attributes. */
- rc = security_sid_to_context(newsid, &context, &len);
- if (rc) {
- printk(KERN_WARNING "post_create: sid_to_context "
- "failed, rc=%d (dev=%s ino=%ld)\n",
- -rc, inode->i_sb->s_id, inode->i_ino);
- return rc;
- }
- down(&inode->i_sem);
- rc = inode->i_op->setxattr(dentry,
- XATTR_NAME_SELINUX,
- context, len, 0);
- up(&inode->i_sem);
- kfree(context);
- if (rc < 0) {
- printk(KERN_WARNING "post_create: setxattr failed, "
- "rc=%d (dev=%s ino=%ld)\n",
- -rc, inode->i_sb->s_id, inode->i_ino);
- return rc;
- }
- }
-
- return 0;
-}
-
-
/* Hook functions begin here. */
static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
{
- struct task_security_struct *psec = parent->security;
- struct task_security_struct *csec = child->security;
int rc;
rc = secondary_ops->ptrace(parent,child);
if (rc)
return rc;
- rc = task_has_perm(parent, child, PROCESS__PTRACE);
- /* Save the SID of the tracing process for later use in apply_creds. */
- if (!rc)
- csec->ptrace_sid = psec->sid;
- return rc;
+ return task_has_perm(parent, child, PROCESS__PTRACE);
}
static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
{
int error;
- error = task_has_perm(current, target, PROCESS__SETCAP);
+ error = secondary_ops->capset_check(target, effective, inheritable, permitted);
if (error)
return error;
- return secondary_ops->capset_check(target, effective, inheritable, permitted);
+ return task_has_perm(current, target, PROCESS__SETCAP);
}
static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
- int error;
-
- error = task_has_perm(current, target, PROCESS__SETCAP);
- if (error)
- return;
-
- return secondary_ops->capset_set(target, effective, inheritable, permitted);
+ secondary_ops->capset_set(target, effective, inheritable, permitted);
}
static int selinux_capable(struct task_struct *tsk, int cap)
u32 tsid;
int rc;
+ rc = secondary_ops->sysctl(table, op);
+ if (rc)
+ return rc;
+
tsec = current->security;
rc = selinux_proc_get_sid(table->de, (op == 001) ?
* a bad coupling between this module and sysctl.c */
if(op == 001) {
error = avc_has_perm(tsec->sid, tsid,
- SECCLASS_DIR, DIR__SEARCH, NULL, NULL);
+ SECCLASS_DIR, DIR__SEARCH, NULL);
} else {
av = 0;
if (op & 004)
av |= FILE__WRITE;
if (av)
error = avc_has_perm(tsec->sid, tsid,
- SECCLASS_FILE, av, NULL, NULL);
+ SECCLASS_FILE, av, NULL);
}
return error;
return rc;
}
-static int selinux_quota_on(struct file *f)
+static int selinux_quota_on(struct dentry *dentry)
{
- return file_has_perm(current, f, FILE__QUOTAON);
+ return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON);
}
static int selinux_syslog(int type)
switch (type) {
case 3: /* Read last kernel messages */
+ case 10: /* Return size of the log buffer */
rc = task_has_system(current, SYSTEM__SYSLOG_READ);
break;
case 6: /* Disable logging to console */
* mapping. 0 means there is enough memory for the allocation to
* succeed and -ENOMEM implies there is not.
*
- * We currently support three overcommit policies, which are set via the
- * vm.overcommit_memory sysctl. See Documentation/vm/overcommit-accounting
+ * Note that secondary_ops->capable and task_has_perm_noaudit return 0
+ * if the capability is granted, but __vm_enough_memory requires 1 if
+ * the capability is granted.
*
- * Strict overcommit modes added 2002 Feb 26 by Alan Cox.
- * Additional code 2002 Jul 20 by Robert Love.
+ * Do not audit the selinux permission check, as this is applied to all
+ * processes that allocate mappings.
*/
static int selinux_vm_enough_memory(long pages)
{
- unsigned long free, allowed;
- int rc;
+ int rc, cap_sys_admin = 0;
struct task_security_struct *tsec = current->security;
- vm_acct_memory(pages);
-
- /*
- * Sometimes we want to use more memory than we have
- */
- if (sysctl_overcommit_memory == 1)
- return 0;
-
- if (sysctl_overcommit_memory == 0) {
- free = get_page_cache_size();
- free += nr_free_pages();
- free += nr_swap_pages;
-
- /*
- * Any slabs which are created with the
- * SLAB_RECLAIM_ACCOUNT flag claim to have contents
- * which are reclaimable, under pressure. The dentry
- * cache and most inode caches should fall into this
- */
- free += atomic_read(&slab_reclaim_pages);
-
- /*
- * Leave the last 3% for privileged processes.
- * Don't audit the check, as it is applied to all processes
- * that allocate mappings.
- */
- rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
- if (!rc) {
- rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY,
- CAP_TO_MASK(CAP_SYS_ADMIN),
- NULL, NULL);
- }
- if (rc)
- free -= free / 32;
-
- if (free > pages)
- return 0;
- vm_unacct_memory(pages);
- return -ENOMEM;
- }
-
- allowed = (totalram_pages - hugetlb_total_pages())
- * sysctl_overcommit_ratio / 100;
- allowed += total_swap_pages;
-
- if (atomic_read(&vm_committed_space) < allowed)
- return 0;
-
- vm_unacct_memory(pages);
-
- return -ENOMEM;
-}
+ rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
+ if (rc == 0)
+ rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
+ SECCLASS_CAPABILITY,
+ CAP_TO_MASK(CAP_SYS_ADMIN),
+ NULL);
-static int selinux_netlink_send(struct sk_buff *skb)
-{
- if (capable(CAP_NET_ADMIN))
- cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN);
- else
- NETLINK_CB(skb).eff_cap = 0;
- return 0;
-}
+ if (rc == 0)
+ cap_sys_admin = 1;
-static int selinux_netlink_recv(struct sk_buff *skb)
-{
- if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
- return -EPERM;
- return 0;
+ return __vm_enough_memory(pages, cap_sys_admin);
}
/* binprm security operations */
{
struct bprm_security_struct *bsec;
- bsec = kmalloc(sizeof(struct bprm_security_struct), GFP_KERNEL);
+ bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL);
if (!bsec)
return -ENOMEM;
- memset(bsec, 0, sizeof *bsec);
- bsec->magic = SELINUX_MAGIC;
bsec->bprm = bprm;
bsec->sid = SECINITSID_UNLABELED;
bsec->set = 0;
/* Default to the current task SID. */
bsec->sid = tsec->sid;
- /* Reset create SID on execve. */
+ /* Reset fs, key, and sock SIDs on execve. */
tsec->create_sid = 0;
+ tsec->keycreate_sid = 0;
+ tsec->sockcreate_sid = 0;
if (tsec->exec_sid) {
newsid = tsec->exec_sid;
if (tsec->sid == newsid) {
rc = avc_has_perm(tsec->sid, isec->sid,
- SECCLASS_FILE, FILE__EXECUTE_NO_TRANS,
- &isec->avcr, &ad);
+ SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
if (rc)
return rc;
} else {
/* Check permissions for the transition. */
rc = avc_has_perm(tsec->sid, newsid,
- SECCLASS_PROCESS, PROCESS__TRANSITION,
- NULL,
- &ad);
+ SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
if (rc)
return rc;
rc = avc_has_perm(newsid, isec->sid,
- SECCLASS_FILE, FILE__ENTRYPOINT,
- &isec->avcr, &ad);
+ SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
if (rc)
return rc;
+ /* Clear any possibly unsafe personality bits on exec: */
+ current->personality &= ~PER_CLEAR_ON_SETID;
+
/* Set the security field to the new SID. */
bsec->sid = newsid;
}
static int selinux_bprm_check_security (struct linux_binprm *bprm)
{
- return 0;
+ return secondary_ops->bprm_check_security(bprm);
}
the two SIDs, i.e. ahp returns 0. */
atsecure = avc_has_perm(tsec->osid, tsec->sid,
SECCLASS_PROCESS,
- PROCESS__NOATSECURE, NULL, NULL);
+ PROCESS__NOATSECURE, NULL);
}
- /* Note that we must include the legacy uid/gid test below
- to retain it, as the new userland will simply use the
- value passed by AT_SECURE to decide whether to enable
- secure mode. */
- return ( atsecure || current->euid != current->uid ||
- current->egid != current->gid);
+ return (atsecure || secondary_ops->bprm_secureexec(bprm));
}
static void selinux_bprm_free_security(struct linux_binprm *bprm)
{
- struct bprm_security_struct *bsec = bprm->security;
+ kfree(bprm->security);
bprm->security = NULL;
- kfree(bsec);
}
+extern struct vfsmount *selinuxfs_mount;
+extern struct dentry *selinux_null;
+
/* Derived from fs/exec.c:flush_old_files. */
static inline void flush_unauthorized_files(struct files_struct * files)
{
struct avc_audit_data ad;
- struct file *file;
+ struct file *file, *devnull = NULL;
+ struct tty_struct *tty;
+ struct fdtable *fdt;
long j = -1;
+ int drop_tty = 0;
+
+ mutex_lock(&tty_mutex);
+ tty = get_current_tty();
+ if (tty) {
+ file_list_lock();
+ file = list_entry(tty->tty_files.next, typeof(*file), f_u.fu_list);
+ if (file) {
+ /* Revalidate access to controlling tty.
+ Use inode_has_perm on the tty inode directly rather
+ than using file_has_perm, as this particular open
+ file may belong to another process and we are only
+ interested in the inode-based check here. */
+ struct inode *inode = file->f_dentry->d_inode;
+ if (inode_has_perm(current, inode,
+ FILE__READ | FILE__WRITE, NULL)) {
+ drop_tty = 1;
+ }
+ }
+ file_list_unlock();
+
+ /* Reset controlling tty. */
+ if (drop_tty)
+ proc_set_tty(current, NULL);
+ }
+ mutex_unlock(&tty_mutex);
+
+ /* Revalidate access to inherited open files. */
AVC_AUDIT_DATA_INIT(&ad,FS);
spin_lock(&files->file_lock);
for (;;) {
unsigned long set, i;
+ int fd;
j++;
i = j * __NFDBITS;
- if (i >= files->max_fds || i >= files->max_fdset)
+ fdt = files_fdtable(files);
+ if (i >= fdt->max_fds || i >= fdt->max_fdset)
break;
- set = files->open_fds->fds_bits[j];
+ set = fdt->open_fds->fds_bits[j];
if (!set)
continue;
spin_unlock(&files->file_lock);
continue;
if (file_has_perm(current,
file,
- file_to_av(file)))
+ file_to_av(file))) {
sys_close(i);
+ fd = get_unused_fd();
+ if (fd != i) {
+ if (fd >= 0)
+ put_unused_fd(fd);
+ fput(file);
+ continue;
+ }
+ if (devnull) {
+ get_file(devnull);
+ } else {
+ devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
+ if (!devnull) {
+ put_unused_fd(fd);
+ fput(file);
+ continue;
+ }
+ }
+ fd_install(fd, devnull);
+ }
fput(file);
}
}
struct task_security_struct *tsec;
struct bprm_security_struct *bsec;
u32 sid;
- struct av_decision avd;
- struct itimerval itimer;
- struct rlimit *rlim, *initrlim;
- int rc, i;
+ int rc;
secondary_ops->bprm_apply_creds(bprm, unsafe);
sid = bsec->sid;
tsec->osid = tsec->sid;
+ bsec->unsafe = 0;
if (tsec->sid != sid) {
/* Check for shared state. If not ok, leave SID
unchanged and kill. */
if (unsafe & LSM_UNSAFE_SHARE) {
- rc = avc_has_perm_noaudit(tsec->sid, sid,
- SECCLASS_PROCESS, PROCESS__SHARE,
- NULL, &avd);
+ rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+ PROCESS__SHARE, NULL);
if (rc) {
- task_unlock(current);
- avc_audit(tsec->sid, sid, SECCLASS_PROCESS,
- PROCESS__SHARE, &avd, rc, NULL);
- force_sig_specific(SIGKILL, current);
- goto lock_out;
+ bsec->unsafe = 1;
+ return;
}
}
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and kill. */
if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
- rc = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
- SECCLASS_PROCESS, PROCESS__PTRACE,
- NULL, &avd);
- if (!rc)
- tsec->sid = sid;
- task_unlock(current);
- avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
- PROCESS__PTRACE, &avd, rc, NULL);
- if (rc) {
- force_sig_specific(SIGKILL, current);
- goto lock_out;
- }
- } else {
- tsec->sid = sid;
- task_unlock(current);
- }
-
- /* Close files for which the new task SID is not authorized. */
- flush_unauthorized_files(current->files);
-
- /* Check whether the new SID can inherit signal state
- from the old SID. If not, clear itimers to avoid
- subsequent signal generation and flush and unblock
- signals. This must occur _after_ the task SID has
- been updated so that any kill done after the flush
- will be checked against the new SID. */
- rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
- PROCESS__SIGINH, NULL, NULL);
- if (rc) {
- memset(&itimer, 0, sizeof itimer);
- for (i = 0; i < 3; i++)
- do_setitimer(i, &itimer, NULL);
- flush_signals(current);
- spin_lock_irq(¤t->sighand->siglock);
- flush_signal_handlers(current, 1);
- sigemptyset(¤t->blocked);
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
- }
-
- /* Check whether the new SID can inherit resource limits
- from the old SID. If not, reset all soft limits to
- the lower of the current task's hard limit and the init
- task's soft limit. Note that the setting of hard limits
- (even to lower them) can be controlled by the setrlimit
- check. The inclusion of the init task's soft limit into
- the computation is to avoid resetting soft limits higher
- than the default soft limit for cases where the default
- is lower than the hard limit, e.g. RLIMIT_CORE or
- RLIMIT_STACK.*/
- rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
- PROCESS__RLIMITINH, NULL, NULL);
- if (rc) {
- for (i = 0; i < RLIM_NLIMITS; i++) {
- rlim = current->rlim + i;
- initrlim = init_task.rlim+i;
- rlim->rlim_cur = min(rlim->rlim_max,initrlim->rlim_cur);
+ struct task_struct *t;
+
+ rcu_read_lock();
+ t = tracehook_tracer_task(current);
+ if (unlikely(t == NULL))
+ rcu_read_unlock();
+ else {
+ struct task_security_struct *sec = t->security;
+ u32 ptsid = sec->sid;
+ rcu_read_unlock();
+
+ rc = avc_has_perm(ptsid, sid,
+ SECCLASS_PROCESS,
+ PROCESS__PTRACE, NULL);
+ if (rc) {
+ bsec->unsafe = 1;
+ return;
+ }
}
}
-
- /* Wake up the parent if it is waiting so that it can
- recheck wait permission to the new task SID. */
- wake_up_interruptible(¤t->parent->wait_chldexit);
-
-lock_out:
- task_lock(current);
- return;
+ tsec->sid = sid;
}
}
-/* superblock security operations */
+/*
+ * called after apply_creds without the task lock held
+ */
+static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
+{
+ struct task_security_struct *tsec;
+ struct rlimit *rlim, *initrlim;
+ struct itimerval itimer;
+ struct bprm_security_struct *bsec;
+ int rc, i;
+
+ tsec = current->security;
+ bsec = bprm->security;
+
+ if (bsec->unsafe) {
+ force_sig_specific(SIGKILL, current);
+ return;
+ }
+ if (tsec->osid == tsec->sid)
+ return;
+
+ /* Close files for which the new task SID is not authorized. */
+ flush_unauthorized_files(current->files);
+
+ /* Check whether the new SID can inherit signal state
+ from the old SID. If not, clear itimers to avoid
+ subsequent signal generation and flush and unblock
+ signals. This must occur _after_ the task SID has
+ been updated so that any kill done after the flush
+ will be checked against the new SID. */
+ rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
+ PROCESS__SIGINH, NULL);
+ if (rc) {
+ memset(&itimer, 0, sizeof itimer);
+ for (i = 0; i < 3; i++)
+ do_setitimer(i, &itimer, NULL);
+ flush_signals(current);
+ spin_lock_irq(¤t->sighand->siglock);
+ flush_signal_handlers(current, 1);
+ sigemptyset(¤t->blocked);
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
+ }
+
+ /* Check whether the new SID can inherit resource limits
+ from the old SID. If not, reset all soft limits to
+ the lower of the current task's hard limit and the init
+ task's soft limit. Note that the setting of hard limits
+ (even to lower them) can be controlled by the setrlimit
+ check. The inclusion of the init task's soft limit into
+ the computation is to avoid resetting soft limits higher
+ than the default soft limit for cases where the default
+ is lower than the hard limit, e.g. RLIMIT_CORE or
+ RLIMIT_STACK.*/
+ rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
+ PROCESS__RLIMITINH, NULL);
+ if (rc) {
+ for (i = 0; i < RLIM_NLIMITS; i++) {
+ rlim = current->signal->rlim + i;
+ initrlim = init_task.signal->rlim+i;
+ rlim->rlim_cur = min(rlim->rlim_max,initrlim->rlim_cur);
+ }
+ if (current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {
+ /*
+ * This will cause RLIMIT_CPU calculations
+ * to be refigured.
+ */
+ current->it_prof_expires = jiffies_to_cputime(1);
+ }
+ }
+
+ /* Wake up the parent if it is waiting so that it can
+ recheck wait permission to the new task SID. */
+ wake_up_interruptible(¤t->parent->signal->wait_chldexit);
+}
+
+/* superblock security operations */
static int selinux_sb_alloc_security(struct super_block *sb)
{
{
return (match_prefix("context=", sizeof("context=")-1, option, len) ||
match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) ||
- match_prefix("defcontext=", sizeof("defcontext=")-1, option, len));
+ match_prefix("defcontext=", sizeof("defcontext=")-1, option, len) ||
+ match_prefix("rootcontext=", sizeof("rootcontext=")-1, option, len));
}
static inline void take_option(char **to, char *from, int *first, int len)
}
} while (*in_end++);
- copy_page(in_save, nosec_save);
+ strcpy(in_save, nosec_save);
+ free_page((unsigned long)nosec_save);
out:
return rc;
}
return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
}
-static int selinux_sb_statfs(struct super_block *sb)
+static int selinux_sb_statfs(struct dentry *dentry)
{
struct avc_audit_data ad;
AVC_AUDIT_DATA_INIT(&ad,FS);
- ad.u.fs.dentry = sb->s_root;
- return superblock_has_perm(current, sb, FILESYSTEM__GETATTR, &ad);
+ ad.u.fs.dentry = dentry->d_sb->s_root;
+ return superblock_has_perm(current, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
}
static int selinux_mount(char * dev_name,
unsigned long flags,
void * data)
{
+ int rc;
+
+ rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data);
+ if (rc)
+ return rc;
+
if (flags & MS_REMOUNT)
return superblock_has_perm(current, nd->mnt->mnt_sb,
FILESYSTEM__REMOUNT, NULL);
static int selinux_umount(struct vfsmount *mnt, int flags)
{
+ int rc;
+
+ rc = secondary_ops->sb_umount(mnt, flags);
+ if (rc)
+ return rc;
+
return superblock_has_perm(current,mnt->mnt_sb,
FILESYSTEM__UNMOUNT,NULL);
}
inode_free_security(inode);
}
-static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
+static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
+ char **name, void **value,
+ size_t *len)
{
- return may_create(dir, dentry, SECCLASS_FILE);
+ struct task_security_struct *tsec;
+ struct inode_security_struct *dsec;
+ struct superblock_security_struct *sbsec;
+ u32 newsid, clen;
+ int rc;
+ char *namep = NULL, *context;
+
+ tsec = current->security;
+ dsec = dir->i_security;
+ sbsec = dir->i_sb->s_security;
+
+ if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+ newsid = tsec->create_sid;
+ } else {
+ rc = security_transition_sid(tsec->sid, dsec->sid,
+ inode_mode_to_security_class(inode->i_mode),
+ &newsid);
+ if (rc) {
+ printk(KERN_WARNING "%s: "
+ "security_transition_sid failed, rc=%d (dev=%s "
+ "ino=%ld)\n",
+ __FUNCTION__,
+ -rc, inode->i_sb->s_id, inode->i_ino);
+ return rc;
+ }
+ }
+
+ inode_security_set_sid(inode, newsid);
+
+ if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+ return -EOPNOTSUPP;
+
+ if (name) {
+ namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_KERNEL);
+ if (!namep)
+ return -ENOMEM;
+ *name = namep;
+ }
+
+ if (value && len) {
+ rc = security_sid_to_context(newsid, &context, &clen);
+ if (rc) {
+ kfree(namep);
+ return rc;
+ }
+ *value = context;
+ *len = clen;
+ }
+
+ return 0;
}
-static void selinux_inode_post_create(struct inode *dir, struct dentry *dentry, int mask)
+static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
{
- post_create(dir, dentry);
+ return may_create(dir, dentry, SECCLASS_FILE);
}
static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
return may_link(dir, old_dentry, MAY_LINK);
}
-static void selinux_inode_post_link(struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry)
-{
- return;
-}
-
static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
{
+ int rc;
+
+ rc = secondary_ops->inode_unlink(dir, dentry);
+ if (rc)
+ return rc;
return may_link(dir, dentry, MAY_UNLINK);
}
return may_create(dir, dentry, SECCLASS_LNK_FILE);
}
-static void selinux_inode_post_symlink(struct inode *dir, struct dentry *dentry, const char *name)
-{
- post_create(dir, dentry);
-}
-
static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask)
{
return may_create(dir, dentry, SECCLASS_DIR);
}
-static void selinux_inode_post_mkdir(struct inode *dir, struct dentry *dentry, int mask)
-{
- post_create(dir, dentry);
-}
-
static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
{
return may_link(dir, dentry, MAY_RMDIR);
static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
- return may_create(dir, dentry, inode_mode_to_security_class(mode));
-}
+ int rc;
-static void selinux_inode_post_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
-{
- post_create(dir, dentry);
+ rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
+ if (rc)
+ return rc;
+
+ return may_create(dir, dentry, inode_mode_to_security_class(mode));
}
static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
return may_rename(old_inode, old_dentry, new_inode, new_dentry);
}
-static void selinux_inode_post_rename(struct inode *old_inode, struct dentry *old_dentry,
- struct inode *new_inode, struct dentry *new_dentry)
-{
- return;
-}
-
static int selinux_inode_readlink(struct dentry *dentry)
{
return dentry_has_perm(current, NULL, dentry, FILE__READ);
static int selinux_inode_permission(struct inode *inode, int mask,
struct nameidata *nd)
{
+ int rc;
+
+ rc = secondary_ops->inode_permission(inode, mask, nd);
+ if (rc)
+ return rc;
+
if (!mask) {
/* No permission to check. Existence test. */
return 0;
}
return inode_has_perm(current, inode,
- file_mask_to_av(inode->i_mode, mask), NULL, NULL);
+ file_mask_to_av(inode->i_mode, mask), NULL);
}
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
+ int rc;
+
+ rc = secondary_ops->inode_setattr(dentry, iattr);
+ if (rc)
+ return rc;
+
+ if (iattr->ia_valid & ATTR_FORCE)
+ return 0;
+
if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
ATTR_ATIME_SET | ATTR_MTIME_SET))
return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
return -EOPNOTSUPP;
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+
AVC_AUDIT_DATA_INIT(&ad,FS);
ad.u.fs.dentry = dentry;
rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
- FILE__RELABELFROM,
- &isec->avcr, &ad);
+ FILE__RELABELFROM, &ad);
if (rc)
return rc;
return rc;
rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
- FILE__RELABELTO, NULL, &ad);
+ FILE__RELABELTO, &ad);
+ if (rc)
+ return rc;
+
+ rc = security_validate_transition(isec->sid, newsid, tsec->sid,
+ isec->sclass);
if (rc)
return rc;
sbsec->sid,
SECCLASS_FILESYSTEM,
FILESYSTEM__ASSOCIATE,
- NULL,
&ad);
}
static int selinux_inode_getxattr (struct dentry *dentry, char *name)
{
- struct inode *inode = dentry->d_inode;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
-
- if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
- return -EOPNOTSUPP;
-
return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
}
return -EACCES;
}
-static int selinux_inode_getsecurity(struct dentry *dentry, const char *name, void *buffer, size_t size)
+static const char *selinux_inode_xattr_getsuffix(void)
{
- struct inode *inode = dentry->d_inode;
- struct inode_security_struct *isec = inode->i_security;
- char *context;
- unsigned len;
- int rc;
+ return XATTR_SELINUX_SUFFIX;
+}
- /* Permission check handled by selinux_inode_getxattr hook.*/
+/*
+ * Copy the in-core inode security context value to the user. If the
+ * getxattr() prior to this succeeded, check to see if we need to
+ * canonicalize the value to be finally returned to the user.
+ *
+ * Permission check is handled by selinux_inode_getxattr hook.
+ */
+static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
+{
+ struct inode_security_struct *isec = inode->i_security;
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
- rc = security_sid_to_context(isec->sid, &context, &len);
- if (rc)
- return rc;
-
- if (!buffer || !size) {
- kfree(context);
- return len;
- }
- if (size < len) {
- kfree(context);
- return -ERANGE;
- }
- memcpy(buffer, context, len);
- kfree(context);
- return len;
+ return selinux_getsecurity(isec->sid, buffer, size);
}
-static int selinux_inode_setsecurity(struct dentry *dentry, const char *name,
+static int selinux_inode_setsecurity(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
- struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
u32 newsid;
int rc;
return 0;
}
-static int selinux_inode_listsecurity(struct dentry *dentry, char *buffer)
+static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
{
const int len = sizeof(XATTR_NAME_SELINUX);
- if (buffer)
+ if (buffer && len <= buffer_size)
memcpy(buffer, XATTR_NAME_SELINUX, len);
return len;
}
return error;
}
-static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags)
+static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
{
- u32 av;
+ if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
+ /*
+ * We are making executable an anonymous mapping or a
+ * private file mapping that will also be writable.
+ * This has an additional check.
+ */
+ int rc = task_has_perm(current, current, PROCESS__EXECMEM);
+ if (rc)
+ return rc;
+ }
if (file) {
/* read access is always possible with a mapping */
- av = FILE__READ;
+ u32 av = FILE__READ;
/* write access only matters if the mapping is shared */
- if ((flags & MAP_TYPE) == MAP_SHARED && (prot & PROT_WRITE))
+ if (shared && (prot & PROT_WRITE))
av |= FILE__WRITE;
if (prot & PROT_EXEC)
return 0;
}
+static int selinux_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
+{
+ int rc;
+
+ rc = secondary_ops->file_mmap(file, reqprot, prot, flags);
+ if (rc)
+ return rc;
+
+ if (selinux_checkreqprot)
+ prot = reqprot;
+
+ return file_map_prot_check(file, prot,
+ (flags & MAP_TYPE) == MAP_SHARED);
+}
+
static int selinux_file_mprotect(struct vm_area_struct *vma,
+ unsigned long reqprot,
unsigned long prot)
{
- return selinux_file_mmap(vma->vm_file, prot, vma->vm_flags);
+ int rc;
+
+ rc = secondary_ops->file_mprotect(vma, reqprot, prot);
+ if (rc)
+ return rc;
+
+ if (selinux_checkreqprot)
+ prot = reqprot;
+
+ if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
+ rc = 0;
+ if (vma->vm_start >= vma->vm_mm->start_brk &&
+ vma->vm_end <= vma->vm_mm->brk) {
+ rc = task_has_perm(current, current,
+ PROCESS__EXECHEAP);
+ } else if (!vma->vm_file &&
+ vma->vm_start <= vma->vm_mm->start_stack &&
+ vma->vm_end >= vma->vm_mm->start_stack) {
+ rc = task_has_perm(current, current, PROCESS__EXECSTACK);
+ } else if (vma->vm_file && vma->anon_vma) {
+ /*
+ * We are making executable a file mapping that has
+ * had some COW done. Since pages might have been
+ * written, check ability to execute the possibly
+ * modified content. This typically should only
+ * occur for text relocations.
+ */
+ rc = file_has_perm(current, vma->vm_file,
+ FILE__EXECMOD);
+ }
+ if (rc)
+ return rc;
+ }
+
+ return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
}
static int selinux_file_lock(struct file *file, unsigned int cmd)
}
static int selinux_file_send_sigiotask(struct task_struct *tsk,
- struct fown_struct *fown,
- int fd, int reason)
+ struct fown_struct *fown, int signum)
{
struct file *file;
u32 perm;
tsec = tsk->security;
fsec = file->f_security;
- if (!fown->signum)
+ if (!signum)
perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
else
- perm = signal_to_av(fown->signum);
+ perm = signal_to_av(signum);
return avc_has_perm(fsec->fown_sid, tsec->sid,
- SECCLASS_PROCESS, perm, NULL, NULL);
+ SECCLASS_PROCESS, perm, NULL);
}
static int selinux_file_receive(struct file *file)
static int selinux_task_create(unsigned long clone_flags)
{
+ int rc;
+
+ rc = secondary_ops->task_create(clone_flags);
+ if (rc)
+ return rc;
+
return task_has_perm(current, current, PROCESS__FORK);
}
tsec2->osid = tsec1->osid;
tsec2->sid = tsec1->sid;
- /* Retain the exec and create SIDs across fork */
+ /* Retain the exec, fs, key, and sock SIDs across fork */
tsec2->exec_sid = tsec1->exec_sid;
tsec2->create_sid = tsec1->create_sid;
+ tsec2->keycreate_sid = tsec1->keycreate_sid;
+ tsec2->sockcreate_sid = tsec1->sockcreate_sid;
return 0;
}
return task_has_perm(current, p, PROCESS__GETSESSION);
}
+static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
+{
+ selinux_get_task_sid(p, secid);
+}
+
static int selinux_task_setgroups(struct group_info *group_info)
{
/* See the comment for setuid above. */
static int selinux_task_setnice(struct task_struct *p, int nice)
{
+ int rc;
+
+ rc = secondary_ops->task_setnice(p, nice);
+ if (rc)
+ return rc;
+
return task_has_perm(current,p, PROCESS__SETSCHED);
}
+static int selinux_task_setioprio(struct task_struct *p, int ioprio)
+{
+ return task_has_perm(current, p, PROCESS__SETSCHED);
+}
+
+static int selinux_task_getioprio(struct task_struct *p)
+{
+ return task_has_perm(current, p, PROCESS__GETSCHED);
+}
+
static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
{
- struct rlimit *old_rlim = current->rlim + resource;
+ struct rlimit *old_rlim = current->signal->rlim + resource;
+ int rc;
+
+ rc = secondary_ops->task_setrlimit(resource, new_rlim);
+ if (rc)
+ return rc;
/* Control the ability to change the hard limit (whether
lowering or raising it), so that the hard limit can
static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
{
- struct task_security_struct *tsec1, *tsec2;
-
- tsec1 = current->security;
- tsec2 = p->security;
-
- /* No auditing from the setscheduler hook, since the runqueue lock
- is held and the system will deadlock if we try to log an audit
- message. */
- return avc_has_perm_noaudit(tsec1->sid, tsec2->sid,
- SECCLASS_PROCESS, PROCESS__SETSCHED,
- &tsec2->avcr, NULL);
+ return task_has_perm(current, p, PROCESS__SETSCHED);
}
static int selinux_task_getscheduler(struct task_struct *p)
return task_has_perm(current, p, PROCESS__GETSCHED);
}
-static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig)
+static int selinux_task_movememory(struct task_struct *p)
+{
+ return task_has_perm(current, p, PROCESS__SETSCHED);
+}
+
+static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
+ int sig, u32 secid)
{
u32 perm;
+ int rc;
+ struct task_security_struct *tsec;
+
+ rc = secondary_ops->task_kill(p, info, sig, secid);
+ if (rc)
+ return rc;
- if (info && ((unsigned long)info == 1 ||
- (unsigned long)info == 2 || SI_FROMKERNEL(info)))
+ if (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info)))
return 0;
if (!sig)
perm = PROCESS__SIGNULL; /* null signal; existence test */
else
perm = signal_to_av(sig);
-
- return task_has_perm(current, p, perm);
+ tsec = p->security;
+ if (secid)
+ rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
+ else
+ rc = task_has_perm(current, p, perm);
+ return rc;
}
static int selinux_task_prctl(int option,
return;
}
-#ifdef CONFIG_SECURITY_NETWORK
-
/* Returns error only if unable to parse addresses */
static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad)
{
- int offset, ihlen, ret;
- struct iphdr iph;
+ int offset, ihlen, ret = -EINVAL;
+ struct iphdr _iph, *ih;
offset = skb->nh.raw - skb->data;
- ret = skb_copy_bits(skb, offset, &iph, sizeof(iph));
- if (ret)
+ ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
+ if (ih == NULL)
goto out;
- ihlen = iph.ihl * 4;
- if (ihlen < sizeof(iph))
+ ihlen = ih->ihl * 4;
+ if (ihlen < sizeof(_iph))
goto out;
- ad->u.net.v4info.saddr = iph.saddr;
- ad->u.net.v4info.daddr = iph.daddr;
+ ad->u.net.v4info.saddr = ih->saddr;
+ ad->u.net.v4info.daddr = ih->daddr;
+ ret = 0;
- switch (iph.protocol) {
+ switch (ih->protocol) {
case IPPROTO_TCP: {
- struct tcphdr tcph;
+ struct tcphdr _tcph, *th;
- if (ntohs(iph.frag_off) & IP_OFFSET)
+ if (ntohs(ih->frag_off) & IP_OFFSET)
break;
offset += ihlen;
- if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0)
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL)
break;
- ad->u.net.sport = tcph.source;
- ad->u.net.dport = tcph.dest;
+ ad->u.net.sport = th->source;
+ ad->u.net.dport = th->dest;
break;
}
case IPPROTO_UDP: {
- struct udphdr udph;
+ struct udphdr _udph, *uh;
- if (ntohs(iph.frag_off) & IP_OFFSET)
+ if (ntohs(ih->frag_off) & IP_OFFSET)
break;
offset += ihlen;
- if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0)
- break;
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL)
+ break;
- ad->u.net.sport = udph.source;
- ad->u.net.dport = udph.dest;
+ ad->u.net.sport = uh->source;
+ ad->u.net.dport = uh->dest;
break;
}
static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad)
{
u8 nexthdr;
- int ret, offset;
- struct ipv6hdr ipv6h;
+ int ret = -EINVAL, offset;
+ struct ipv6hdr _ipv6h, *ip6;
offset = skb->nh.raw - skb->data;
- ret = skb_copy_bits(skb, offset, &ipv6h, sizeof(ipv6h));
- if (ret)
+ ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
+ if (ip6 == NULL)
goto out;
- ipv6_addr_copy(&ad->u.net.v6info.saddr, &ipv6h.saddr);
- ipv6_addr_copy(&ad->u.net.v6info.daddr, &ipv6h.daddr);
+ ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr);
+ ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr);
+ ret = 0;
- nexthdr = ipv6h.nexthdr;
- offset += sizeof(ipv6h);
- offset = ipv6_skip_exthdr(skb, offset, &nexthdr,
- skb->tail - skb->head - offset);
+ nexthdr = ip6->nexthdr;
+ offset += sizeof(_ipv6h);
+ offset = ipv6_skip_exthdr(skb, offset, &nexthdr);
if (offset < 0)
goto out;
switch (nexthdr) {
case IPPROTO_TCP: {
- struct tcphdr tcph;
+ struct tcphdr _tcph, *th;
- if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0)
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL)
break;
- ad->u.net.sport = tcph.source;
- ad->u.net.dport = tcph.dest;
+ ad->u.net.sport = th->source;
+ ad->u.net.dport = th->dest;
break;
}
case IPPROTO_UDP: {
- struct udphdr udph;
+ struct udphdr _udph, *uh;
- if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0)
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL)
break;
- ad->u.net.sport = udph.source;
- ad->u.net.dport = udph.dest;
+ ad->u.net.sport = uh->source;
+ ad->u.net.dport = uh->dest;
break;
}
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = sock->sk;
- err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
- perms, &isec->avcr, &ad);
+ err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
out:
return err;
{
int err = 0;
struct task_security_struct *tsec;
+ u32 newsid;
if (kern)
goto out;
tsec = current->security;
- err = avc_has_perm(tsec->sid, tsec->sid,
- socket_type_to_security_class(family, type),
- SOCKET__CREATE, NULL, NULL);
+ newsid = tsec->sockcreate_sid ? : tsec->sid;
+ err = avc_has_perm(tsec->sid, newsid,
+ socket_type_to_security_class(family, type,
+ protocol), SOCKET__CREATE, NULL);
out:
return err;
static void selinux_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern)
{
- int err;
struct inode_security_struct *isec;
struct task_security_struct *tsec;
+ u32 newsid;
- err = inode_doinit(SOCK_INODE(sock));
- if (err < 0)
- return;
isec = SOCK_INODE(sock)->i_security;
tsec = current->security;
- isec->sclass = socket_type_to_security_class(family, type);
- isec->sid = kern ? SECINITSID_KERNEL : tsec->sid;
+ newsid = tsec->sockcreate_sid ? : tsec->sid;
+ isec->sclass = socket_type_to_security_class(family, type, protocol);
+ isec->sid = kern ? SECINITSID_KERNEL : newsid;
+ isec->initialized = 1;
return;
}
/*
* If PF_INET or PF_INET6, check name_bind permission for the port.
+ * Multiple address binding for SCTP is not supported yet: we just
+ * check the first address now.
*/
family = sock->sk->sk_family;
if (family == PF_INET || family == PF_INET6) {
goto out;
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sport = htons(snum);
+ ad.u.net.family = family;
err = avc_has_perm(isec->sid, sid,
isec->sclass,
- SOCKET__NAME_BIND, NULL, &ad);
+ SOCKET__NAME_BIND, &ad);
if (err)
goto out;
}
- switch(sk->sk_protocol) {
- case IPPROTO_TCP:
+ switch(isec->sclass) {
+ case SECCLASS_TCP_SOCKET:
node_perm = TCP_SOCKET__NODE_BIND;
break;
- case IPPROTO_UDP:
+ case SECCLASS_UDP_SOCKET:
node_perm = UDP_SOCKET__NODE_BIND;
break;
ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr);
err = avc_has_perm(isec->sid, sid,
- isec->sclass, node_perm, NULL, &ad);
+ isec->sclass, node_perm, &ad);
if (err)
goto out;
}
static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
{
- return socket_has_perm(current, sock, SOCKET__CONNECT);
+ struct inode_security_struct *isec;
+ int err;
+
+ err = socket_has_perm(current, sock, SOCKET__CONNECT);
+ if (err)
+ return err;
+
+ /*
+ * If a TCP socket, check name_connect permission for the port.
+ */
+ isec = SOCK_INODE(sock)->i_security;
+ if (isec->sclass == SECCLASS_TCP_SOCKET) {
+ struct sock *sk = sock->sk;
+ struct avc_audit_data ad;
+ struct sockaddr_in *addr4 = NULL;
+ struct sockaddr_in6 *addr6 = NULL;
+ unsigned short snum;
+ u32 sid;
+
+ if (sk->sk_family == PF_INET) {
+ addr4 = (struct sockaddr_in *)address;
+ if (addrlen < sizeof(struct sockaddr_in))
+ return -EINVAL;
+ snum = ntohs(addr4->sin_port);
+ } else {
+ addr6 = (struct sockaddr_in6 *)address;
+ if (addrlen < SIN6_LEN_RFC2133)
+ return -EINVAL;
+ snum = ntohs(addr6->sin6_port);
+ }
+
+ err = security_port_sid(sk->sk_family, sk->sk_type,
+ sk->sk_protocol, snum, &sid);
+ if (err)
+ goto out;
+
+ AVC_AUDIT_DATA_INIT(&ad,NET);
+ ad.u.net.dport = htons(snum);
+ ad.u.net.family = sk->sk_family;
+ err = avc_has_perm(isec->sid, sid, isec->sclass,
+ TCP_SOCKET__NAME_CONNECT, &ad);
+ if (err)
+ goto out;
+ }
+
+out:
+ return err;
}
static int selinux_socket_listen(struct socket *sock, int backlog)
if (err)
return err;
- err = inode_doinit(SOCK_INODE(newsock));
- if (err < 0)
- return err;
newisec = SOCK_INODE(newsock)->i_security;
isec = SOCK_INODE(sock)->i_security;
newisec->sclass = isec->sclass;
newisec->sid = isec->sid;
+ newisec->initialized = 1;
return 0;
}
struct avc_audit_data ad;
int err;
+ err = secondary_ops->unix_stream_connect(sock, other, newsk);
+ if (err)
+ return err;
+
isec = SOCK_INODE(sock)->i_security;
other_isec = SOCK_INODE(other)->i_security;
err = avc_has_perm(isec->sid, other_isec->sid,
isec->sclass,
- UNIX_STREAM_SOCKET__CONNECTTO,
- &other_isec->avcr, &ad);
+ UNIX_STREAM_SOCKET__CONNECTTO, &ad);
if (err)
return err;
ad.u.net.sk = other->sk;
err = avc_has_perm(isec->sid, other_isec->sid,
- isec->sclass,
- SOCKET__SENDTO,
- &other_isec->avcr, &ad);
+ isec->sclass, SOCKET__SENDTO, &ad);
if (err)
return err;
return 0;
}
-static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
+ struct avc_audit_data *ad, u32 sock_sid, u16 sock_class,
+ u16 family, char *addrp, int len)
{
- u16 family;
- char *addrp;
- int len, err = 0;
- u32 netif_perm, node_perm, node_sid, recv_perm = 0;
- struct socket *sock;
- struct inode *inode;
- struct net_device *dev;
- struct sel_netif *netif;
- struct netif_security_struct *nsec;
- struct inode_security_struct *isec;
- struct avc_audit_data ad;
-
- family = sk->sk_family;
- if (family != PF_INET && family != PF_INET6)
- goto out;
-
- /* Handle mapped IPv4 packets arriving via IPv6 sockets */
- if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
- family = PF_INET;
-
- sock = sk->sk_socket;
-
- /* TCP control messages don't always have a socket. */
- if (!sock)
- goto out;
+ int err = 0;
+ u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
- inode = SOCK_INODE(sock);
- if (!inode)
+ if (!skb->dev)
goto out;
- dev = skb->dev;
- if (!dev)
+ err = sel_netif_sids(skb->dev, &if_sid, NULL);
+ if (err)
goto out;
- netif = sel_netif_lookup(dev);
- if (IS_ERR(netif)) {
- err = PTR_ERR(netif);
- goto out;
- }
-
- nsec = &netif->nsec;
- isec = inode->i_security;
-
- switch (isec->sclass) {
+ switch (sock_class) {
case SECCLASS_UDP_SOCKET:
netif_perm = NETIF__UDP_RECV;
node_perm = NODE__UDP_RECV;
break;
}
- AVC_AUDIT_DATA_INIT(&ad, NET);
- ad.u.net.netif = dev->name;
- ad.u.net.family = family;
-
- err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
- if (err) {
- sel_netif_put(netif);
- goto out;
- }
-
- err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF,
- netif_perm, &nsec->avcr, &ad);
- sel_netif_put(netif);
+ err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
if (err)
goto out;
- /* Fixme: this lookup is inefficient */
err = security_node_sid(family, addrp, len, &node_sid);
if (err)
goto out;
- err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad);
+ err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, ad);
if (err)
goto out;
if (recv_perm) {
u32 port_sid;
- /* Fixme: make this more efficient */
err = security_port_sid(sk->sk_family, sk->sk_type,
- sk->sk_protocol, ntohs(ad.u.net.sport),
+ sk->sk_protocol, ntohs(ad->u.net.sport),
&port_sid);
if (err)
goto out;
- err = avc_has_perm(isec->sid, port_sid, isec->sclass,
- recv_perm, NULL, &ad);
+ err = avc_has_perm(sock_sid, port_sid,
+ sock_class, recv_perm, ad);
}
+
+out:
+ return err;
+}
+
+static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ u16 family;
+ u16 sock_class = 0;
+ char *addrp;
+ int len, err = 0;
+ u32 sock_sid = 0;
+ struct socket *sock;
+ struct avc_audit_data ad;
+
+ family = sk->sk_family;
+ if (family != PF_INET && family != PF_INET6)
+ goto out;
+
+ /* Handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
+ family = PF_INET;
+
+ read_lock_bh(&sk->sk_callback_lock);
+ sock = sk->sk_socket;
+ if (sock) {
+ struct inode *inode;
+ inode = SOCK_INODE(sock);
+ if (inode) {
+ struct inode_security_struct *isec;
+ isec = inode->i_security;
+ sock_sid = isec->sid;
+ sock_class = isec->sclass;
+ }
+ }
+ read_unlock_bh(&sk->sk_callback_lock);
+ if (!sock_sid)
+ goto out;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]";
+ ad.u.net.family = family;
+
+ err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
+ if (err)
+ goto out;
+
+ if (selinux_compat_net)
+ err = selinux_sock_rcv_skb_compat(sk, skb, &ad, sock_sid,
+ sock_class, family,
+ addrp, len);
+ else
+ err = avc_has_perm(sock_sid, skb->secmark, SECCLASS_PACKET,
+ PACKET__RECV, &ad);
+ if (err)
+ goto out;
+
+ err = selinux_xfrm_sock_rcv_skb(sock_sid, skb);
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;
return err;
}
-static int selinux_sk_alloc_security(struct sock *sk, int family, int priority)
+static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
+{
+ u32 peer_secid = SECSID_NULL;
+ int err = 0;
+
+ if (sock && (sock->sk->sk_family == PF_UNIX))
+ selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
+ else if (skb)
+ peer_secid = selinux_socket_getpeer_dgram(skb);
+
+ if (peer_secid == SECSID_NULL)
+ err = -EINVAL;
+ *secid = peer_secid;
+
+ return err;
+}
+
+static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
{
return sk_alloc_security(sk, family, priority);
}
sk_free_security(sk);
}
-#ifdef CONFIG_NETFILTER
-
-static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
- struct sk_buff **pskb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *),
- u16 family)
+static unsigned int selinux_sk_getsid_security(struct sock *sk, struct flowi *fl, u8 dir)
{
- char *addrp;
- int len, err = NF_ACCEPT;
- u32 netif_perm, node_perm, node_sid, send_perm = 0;
- struct sock *sk;
- struct socket *sock;
- struct inode *inode;
- struct sel_netif *netif;
- struct sk_buff *skb = *pskb;
- struct netif_security_struct *nsec;
struct inode_security_struct *isec;
- struct avc_audit_data ad;
- struct net_device *dev = (struct net_device *)out;
-
- sk = skb->sk;
+ u32 sock_sid = SECINITSID_ANY_SOCKET;
+
if (!sk)
- goto out;
-
- sock = sk->sk_socket;
- if (!sock)
- goto out;
-
- inode = SOCK_INODE(sock);
- if (!inode)
- goto out;
+ return selinux_no_sk_sid(fl);
+
+ read_lock_bh(&sk->sk_callback_lock);
+ isec = get_sock_isec(sk);
+
+ if (isec)
+ sock_sid = isec->sid;
+
+ read_unlock_bh(&sk->sk_callback_lock);
+ return sock_sid;
+}
- netif = sel_netif_lookup(dev);
- if (IS_ERR(netif)) {
- err = NF_DROP;
+static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
+{
+ int err = 0;
+ u32 perm;
+ struct nlmsghdr *nlh;
+ struct socket *sock = sk->sk_socket;
+ struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+
+ if (skb->len < NLMSG_SPACE(0)) {
+ err = -EINVAL;
goto out;
}
+ nlh = (struct nlmsghdr *)skb->data;
- nsec = &netif->nsec;
- isec = inode->i_security;
+ err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm);
+ if (err) {
+ if (err == -EINVAL) {
+ audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR,
+ "SELinux: unrecognized netlink message"
+ " type=%hu for sclass=%hu\n",
+ nlh->nlmsg_type, isec->sclass);
+ if (!selinux_enforcing)
+ err = 0;
+ }
+
+ /* Ignore */
+ if (err == -ENOENT)
+ err = 0;
+ goto out;
+ }
+
+ err = socket_has_perm(current, sock, perm);
+out:
+ return err;
+}
+
+#ifdef CONFIG_NETFILTER
+
+static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev,
+ struct inode_security_struct *isec,
+ struct avc_audit_data *ad,
+ u16 family, char *addrp, int len)
+{
+ int err;
+ u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
+ err = sel_netif_sids(dev, &if_sid, NULL);
+ if (err)
+ goto out;
+
switch (isec->sclass) {
case SECCLASS_UDP_SOCKET:
netif_perm = NETIF__UDP_SEND;
break;
}
-
- AVC_AUDIT_DATA_INIT(&ad, NET);
- ad.u.net.netif = dev->name;
- ad.u.net.family = family;
-
- err = selinux_parse_skb(skb, &ad, &addrp,
- &len, 0) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT) {
- sel_netif_put(netif);
- goto out;
- }
-
- err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF,
- netif_perm, &nsec->avcr, &ad) ? NF_DROP : NF_ACCEPT;
- sel_netif_put(netif);
- if (err != NF_ACCEPT)
+ err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
+ if (err)
goto out;
- /* Fixme: this lookup is inefficient */
- err = security_node_sid(family, addrp, len,
- &node_sid) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
+ err = security_node_sid(family, addrp, len, &node_sid);
+ if (err)
goto out;
- err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE,
- node_perm, NULL, &ad) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
+ err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad);
+ if (err)
goto out;
if (send_perm) {
u32 port_sid;
- /* Fixme: make this more efficient */
err = security_port_sid(sk->sk_family,
sk->sk_type,
sk->sk_protocol,
- ntohs(ad.u.net.dport),
- &port_sid) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
+ ntohs(ad->u.net.dport),
+ &port_sid);
+ if (err)
goto out;
err = avc_has_perm(isec->sid, port_sid, isec->sclass,
- send_perm, NULL, &ad) ? NF_DROP : NF_ACCEPT;
+ send_perm, ad);
}
-
out:
return err;
}
+static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *),
+ u16 family)
+{
+ char *addrp;
+ int len, err = 0;
+ struct sock *sk;
+ struct socket *sock;
+ struct inode *inode;
+ struct sk_buff *skb = *pskb;
+ struct inode_security_struct *isec;
+ struct avc_audit_data ad;
+ struct net_device *dev = (struct net_device *)out;
+
+ sk = skb->sk;
+ if (!sk)
+ goto out;
+
+ sock = sk->sk_socket;
+ if (!sock)
+ goto out;
+
+ inode = SOCK_INODE(sock);
+ if (!inode)
+ goto out;
+
+ isec = inode->i_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = dev->name;
+ ad.u.net.family = family;
+
+ err = selinux_parse_skb(skb, &ad, &addrp, &len, 0);
+ if (err)
+ goto out;
+
+ if (selinux_compat_net)
+ err = selinux_ip_postroute_last_compat(sk, dev, isec, &ad,
+ family, addrp, len);
+ else
+ err = avc_has_perm(isec->sid, skb->secmark, SECCLASS_PACKET,
+ PACKET__SEND, &ad);
+
+ if (err)
+ goto out;
+
+ err = selinux_xfrm_postroute_last(isec->sid, skb);
+out:
+ return err ? NF_DROP : NF_ACCEPT;
+}
+
static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
#endif /* CONFIG_NETFILTER */
-#endif /* CONFIG_SECURITY_NETWORK */
+static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+ int err;
+
+ err = secondary_ops->netlink_send(sk, skb);
+ if (err)
+ return err;
+
+ if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
+ err = selinux_nlmsg_perm(sk, skb);
+
+ return err;
+}
+
+static int selinux_netlink_recv(struct sk_buff *skb, int capability)
+{
+ int err;
+ struct avc_audit_data ad;
+
+ err = secondary_ops->netlink_recv(skb, capability);
+ if (err)
+ return err;
+
+ AVC_AUDIT_DATA_INIT(&ad, CAP);
+ ad.u.cap = capability;
+
+ return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid,
+ SECCLASS_CAPABILITY, CAP_TO_MASK(capability), &ad);
+}
static int ipc_alloc_security(struct task_struct *task,
struct kern_ipc_perm *perm,
struct task_security_struct *tsec = task->security;
struct ipc_security_struct *isec;
- isec = kmalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
+ isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
if (!isec)
return -ENOMEM;
- memset(isec, 0, sizeof(struct ipc_security_struct));
- isec->magic = SELINUX_MAGIC;
isec->sclass = sclass;
isec->ipc_perm = perm;
- if (tsec) {
- isec->sid = tsec->sid;
- } else {
- isec->sid = SECINITSID_UNLABELED;
- }
+ isec->sid = tsec->sid;
perm->security = isec;
return 0;
static void ipc_free_security(struct kern_ipc_perm *perm)
{
struct ipc_security_struct *isec = perm->security;
- if (!isec || isec->magic != SELINUX_MAGIC)
- return;
-
perm->security = NULL;
kfree(isec);
}
{
struct msg_security_struct *msec;
- msec = kmalloc(sizeof(struct msg_security_struct), GFP_KERNEL);
+ msec = kzalloc(sizeof(struct msg_security_struct), GFP_KERNEL);
if (!msec)
return -ENOMEM;
- memset(msec, 0, sizeof(struct msg_security_struct));
- msec->magic = SELINUX_MAGIC;
msec->msg = msg;
msec->sid = SECINITSID_UNLABELED;
msg->security = msec;
static void msg_msg_free_security(struct msg_msg *msg)
{
struct msg_security_struct *msec = msg->security;
- if (!msec || msec->magic != SELINUX_MAGIC)
- return;
msg->security = NULL;
kfree(msec);
}
static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
- u16 sclass, u32 perms)
+ u32 perms)
{
struct task_security_struct *tsec;
struct ipc_security_struct *isec;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = ipc_perms->key;
- return avc_has_perm(tsec->sid, isec->sid, sclass,
- perms, &isec->avcr, &ad);
+ return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
}
static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
static void selinux_msg_msg_free_security(struct msg_msg *msg)
{
- return msg_msg_free_security(msg);
+ msg_msg_free_security(msg);
}
/* message queue security operations */
ad.u.ipc_id = msq->q_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
- MSGQ__CREATE, &isec->avcr, &ad);
+ MSGQ__CREATE, &ad);
if (rc) {
ipc_free_security(&msq->q_perm);
return rc;
ad.u.ipc_id = msq->q_perm.key;
return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
- MSGQ__ASSOCIATE, &isec->avcr, &ad);
+ MSGQ__ASSOCIATE, &ad);
}
static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
return 0;
}
- err = ipc_has_perm(&msq->q_perm, SECCLASS_MSGQ, perms);
+ err = ipc_has_perm(&msq->q_perm, perms);
return err;
}
/* Can this process write to the queue? */
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
- MSGQ__WRITE, &isec->avcr, &ad);
+ MSGQ__WRITE, &ad);
if (!rc)
/* Can this process send the message */
rc = avc_has_perm(tsec->sid, msec->sid,
- SECCLASS_MSG, MSG__SEND,
- &msec->avcr, &ad);
+ SECCLASS_MSG, MSG__SEND, &ad);
if (!rc)
/* Can the message be put in the queue? */
rc = avc_has_perm(msec->sid, isec->sid,
- SECCLASS_MSGQ, MSGQ__ENQUEUE,
- &isec->avcr, &ad);
+ SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad);
return rc;
}
ad.u.ipc_id = msq->q_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid,
- SECCLASS_MSGQ, MSGQ__READ,
- &isec->avcr, &ad);
+ SECCLASS_MSGQ, MSGQ__READ, &ad);
if (!rc)
rc = avc_has_perm(tsec->sid, msec->sid,
- SECCLASS_MSG, MSG__RECEIVE,
- &msec->avcr, &ad);
+ SECCLASS_MSG, MSG__RECEIVE, &ad);
return rc;
}
ad.u.ipc_id = shp->shm_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
- SHM__CREATE, &isec->avcr, &ad);
+ SHM__CREATE, &ad);
if (rc) {
ipc_free_security(&shp->shm_perm);
return rc;
ad.u.ipc_id = shp->shm_perm.key;
return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
- SHM__ASSOCIATE, &isec->avcr, &ad);
+ SHM__ASSOCIATE, &ad);
}
/* Note, at this point, shp is locked down */
return 0;
}
- err = ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms);
+ err = ipc_has_perm(&shp->shm_perm, perms);
return err;
}
static int selinux_shm_shmat(struct shmid_kernel *shp,
- char *shmaddr, int shmflg)
+ char __user *shmaddr, int shmflg)
{
u32 perms;
+ int rc;
+
+ rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
+ if (rc)
+ return rc;
if (shmflg & SHM_RDONLY)
perms = SHM__READ;
else
perms = SHM__READ | SHM__WRITE;
- return ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms);
+ return ipc_has_perm(&shp->shm_perm, perms);
}
/* Semaphore security operations */
ad.u.ipc_id = sma->sem_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
- SEM__CREATE, &isec->avcr, &ad);
+ SEM__CREATE, &ad);
if (rc) {
ipc_free_security(&sma->sem_perm);
return rc;
ad.u.ipc_id = sma->sem_perm.key;
return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
- SEM__ASSOCIATE, &isec->avcr, &ad);
+ SEM__ASSOCIATE, &ad);
}
/* Note, at this point, sma is locked down */
return 0;
}
- err = ipc_has_perm(&sma->sem_perm, SECCLASS_SEM, perms);
+ err = ipc_has_perm(&sma->sem_perm, perms);
return err;
}
else
perms = SEM__READ;
- return ipc_has_perm(&sma->sem_perm, SECCLASS_SEM, perms);
+ return ipc_has_perm(&sma->sem_perm, perms);
}
static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
{
- struct ipc_security_struct *isec = ipcp->security;
- u16 sclass = SECCLASS_IPC;
u32 av = 0;
- if (isec && isec->magic == SELINUX_MAGIC)
- sclass = isec->sclass;
-
av = 0;
if (flag & S_IRUGO)
av |= IPC__UNIX_READ;
if (av == 0)
return 0;
- return ipc_has_perm(ipcp, sclass, av);
+ return ipc_has_perm(ipcp, av);
}
/* module stacking operations */
-int selinux_register_security (const char *name, struct security_operations *ops)
+static int selinux_register_security (const char *name, struct security_operations *ops)
{
if (secondary_ops != original_ops) {
printk(KERN_INFO "%s: There is already a secondary security "
return 0;
}
-int selinux_unregister_security (const char *name, struct security_operations *ops)
+static int selinux_unregister_security (const char *name, struct security_operations *ops)
{
if (ops != secondary_ops) {
printk (KERN_INFO "%s: trying to unregister a security module "
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
- u32 sid, len;
- char *context;
+ u32 sid;
int error;
if (current != p) {
return error;
}
- if (!size)
- return -ERANGE;
-
tsec = p->security;
if (!strcmp(name, "current"))
sid = tsec->exec_sid;
else if (!strcmp(name, "fscreate"))
sid = tsec->create_sid;
+ else if (!strcmp(name, "keycreate"))
+ sid = tsec->keycreate_sid;
+ else if (!strcmp(name, "sockcreate"))
+ sid = tsec->sockcreate_sid;
else
return -EINVAL;
if (!sid)
return 0;
- error = security_sid_to_context(sid, &context, &len);
- if (error)
- return error;
- if (len > size) {
- kfree(context);
- return -ERANGE;
- }
- memcpy(value, context, len);
- kfree(context);
- return len;
+ return selinux_getsecurity(sid, value, size);
}
static int selinux_setprocattr(struct task_struct *p,
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
+ struct task_struct *tracer;
u32 sid = 0;
int error;
+ char *str = value;
- if (current != p || !strcmp(name, "current")) {
+ if (current != p) {
/* SELinux only allows a process to change its own
- security attributes, and it only allows the process
- current SID to change via exec. */
+ security attributes. */
return -EACCES;
}
error = task_has_perm(current, p, PROCESS__SETEXEC);
else if (!strcmp(name, "fscreate"))
error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+ else if (!strcmp(name, "keycreate"))
+ error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
+ else if (!strcmp(name, "sockcreate"))
+ error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
+ else if (!strcmp(name, "current"))
+ error = task_has_perm(current, p, PROCESS__SETCURRENT);
else
error = -EINVAL;
if (error)
return error;
/* Obtain a SID for the context, if one was specified. */
- if (size) {
- int error;
+ if (size && str[1] && str[1] != '\n') {
+ if (str[size-1] == '\n') {
+ str[size-1] = 0;
+ size--;
+ }
error = security_context_to_sid(value, size, &sid);
if (error)
return error;
tsec->exec_sid = sid;
else if (!strcmp(name, "fscreate"))
tsec->create_sid = sid;
+ else if (!strcmp(name, "keycreate")) {
+ error = may_create_key(sid, p);
+ if (error)
+ return error;
+ tsec->keycreate_sid = sid;
+ } else if (!strcmp(name, "sockcreate"))
+ tsec->sockcreate_sid = sid;
+ else if (!strcmp(name, "current")) {
+ struct av_decision avd;
+
+ if (sid == 0)
+ return -EINVAL;
+
+ /* Only allow single threaded processes to change context */
+ if (atomic_read(&p->mm->mm_users) != 1) {
+ struct task_struct *g, *t;
+ struct mm_struct *mm = p->mm;
+ read_lock(&tasklist_lock);
+ do_each_thread(g, t)
+ if (t->mm == mm && t != p) {
+ read_unlock(&tasklist_lock);
+ return -EPERM;
+ }
+ while_each_thread(g, t);
+ read_unlock(&tasklist_lock);
+ }
+
+ /* Check permissions for the transition. */
+ error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+ PROCESS__DYNTRANSITION, NULL);
+ if (error)
+ return error;
+
+ /* Check for ptracing, and update the task SID if ok.
+ Otherwise, leave SID unchanged and fail. */
+ task_lock(p);
+ rcu_read_lock();
+ tracer = tracehook_tracer_task(p);
+ if (tracer != NULL) {
+ struct task_security_struct *ptsec = tracer->security;
+ u32 ptsid = ptsec->sid;
+ rcu_read_unlock();
+ error = avc_has_perm_noaudit(ptsid, sid,
+ SECCLASS_PROCESS,
+ PROCESS__PTRACE, &avd);
+ if (!error)
+ tsec->sid = sid;
+ task_unlock(p);
+ avc_audit(ptsid, sid, SECCLASS_PROCESS,
+ PROCESS__PTRACE, &avd, error, NULL);
+ if (error)
+ return error;
+ } else {
+ rcu_read_unlock();
+ tsec->sid = sid;
+ task_unlock(p);
+ }
+ }
else
return -EINVAL;
return size;
}
-struct security_operations selinux_ops = {
+static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+ return security_sid_to_context(secid, secdata, seclen);
+}
+
+static void selinux_release_secctx(char *secdata, u32 seclen)
+{
+ if (secdata)
+ kfree(secdata);
+}
+
+#ifdef CONFIG_KEYS
+
+static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
+ unsigned long flags)
+{
+ struct task_security_struct *tsec = tsk->security;
+ struct key_security_struct *ksec;
+
+ ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
+ if (!ksec)
+ return -ENOMEM;
+
+ ksec->obj = k;
+ if (tsec->keycreate_sid)
+ ksec->sid = tsec->keycreate_sid;
+ else
+ ksec->sid = tsec->sid;
+ k->security = ksec;
+
+ return 0;
+}
+
+static void selinux_key_free(struct key *k)
+{
+ struct key_security_struct *ksec = k->security;
+
+ k->security = NULL;
+ kfree(ksec);
+}
+
+static int selinux_key_permission(key_ref_t key_ref,
+ struct task_struct *ctx,
+ key_perm_t perm)
+{
+ struct key *key;
+ struct task_security_struct *tsec;
+ struct key_security_struct *ksec;
+
+ key = key_ref_to_ptr(key_ref);
+
+ tsec = ctx->security;
+ ksec = key->security;
+
+ /* if no specific permissions are requested, we skip the
+ permission check. No serious, additional covert channels
+ appear to be created. */
+ if (perm == 0)
+ return 0;
+
+ return avc_has_perm(tsec->sid, ksec->sid,
+ SECCLASS_KEY, perm, NULL);
+}
+
+#endif
+
+static struct security_operations selinux_ops = {
.ptrace = selinux_ptrace,
.capget = selinux_capget,
.capset_check = selinux_capset_check,
.bprm_alloc_security = selinux_bprm_alloc_security,
.bprm_free_security = selinux_bprm_free_security,
.bprm_apply_creds = selinux_bprm_apply_creds,
+ .bprm_post_apply_creds = selinux_bprm_post_apply_creds,
.bprm_set_security = selinux_bprm_set_security,
.bprm_check_security = selinux_bprm_check_security,
.bprm_secureexec = selinux_bprm_secureexec,
.inode_alloc_security = selinux_inode_alloc_security,
.inode_free_security = selinux_inode_free_security,
+ .inode_init_security = selinux_inode_init_security,
.inode_create = selinux_inode_create,
- .inode_post_create = selinux_inode_post_create,
.inode_link = selinux_inode_link,
- .inode_post_link = selinux_inode_post_link,
.inode_unlink = selinux_inode_unlink,
.inode_symlink = selinux_inode_symlink,
- .inode_post_symlink = selinux_inode_post_symlink,
.inode_mkdir = selinux_inode_mkdir,
- .inode_post_mkdir = selinux_inode_post_mkdir,
.inode_rmdir = selinux_inode_rmdir,
.inode_mknod = selinux_inode_mknod,
- .inode_post_mknod = selinux_inode_post_mknod,
.inode_rename = selinux_inode_rename,
- .inode_post_rename = selinux_inode_post_rename,
.inode_readlink = selinux_inode_readlink,
.inode_follow_link = selinux_inode_follow_link,
.inode_permission = selinux_inode_permission,
.inode_getxattr = selinux_inode_getxattr,
.inode_listxattr = selinux_inode_listxattr,
.inode_removexattr = selinux_inode_removexattr,
+ .inode_xattr_getsuffix = selinux_inode_xattr_getsuffix,
.inode_getsecurity = selinux_inode_getsecurity,
.inode_setsecurity = selinux_inode_setsecurity,
.inode_listsecurity = selinux_inode_listsecurity,
.task_setpgid = selinux_task_setpgid,
.task_getpgid = selinux_task_getpgid,
.task_getsid = selinux_task_getsid,
+ .task_getsecid = selinux_task_getsecid,
.task_setgroups = selinux_task_setgroups,
.task_setnice = selinux_task_setnice,
+ .task_setioprio = selinux_task_setioprio,
+ .task_getioprio = selinux_task_getioprio,
.task_setrlimit = selinux_task_setrlimit,
.task_setscheduler = selinux_task_setscheduler,
.task_getscheduler = selinux_task_getscheduler,
+ .task_movememory = selinux_task_movememory,
.task_kill = selinux_task_kill,
.task_wait = selinux_task_wait,
.task_prctl = selinux_task_prctl,
.getprocattr = selinux_getprocattr,
.setprocattr = selinux_setprocattr,
-#ifdef CONFIG_SECURITY_NETWORK
+ .secid_to_secctx = selinux_secid_to_secctx,
+ .release_secctx = selinux_release_secctx,
+
.unix_stream_connect = selinux_socket_unix_stream_connect,
.unix_may_send = selinux_socket_unix_may_send,
.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,
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+ .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,
+ .xfrm_policy_clone_security = selinux_xfrm_policy_clone,
+ .xfrm_policy_free_security = selinux_xfrm_policy_free,
+ .xfrm_policy_delete_security = selinux_xfrm_policy_delete,
+ .xfrm_state_alloc_security = selinux_xfrm_state_alloc,
+ .xfrm_state_free_security = selinux_xfrm_state_free,
+ .xfrm_state_delete_security = selinux_xfrm_state_delete,
+ .xfrm_policy_lookup = selinux_xfrm_policy_lookup,
+#endif
+
+#ifdef CONFIG_KEYS
+ .key_alloc = selinux_key_alloc,
+ .key_free = selinux_key_free,
+ .key_permission = selinux_key_permission,
#endif
};
-__init int selinux_init(void)
+static __init int selinux_init(void)
{
struct task_security_struct *tsec;
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;
} else {
printk(KERN_INFO "SELinux: Starting in permissive mode\n");
}
+
+#ifdef CONFIG_KEYS
+ /* Add security information to initial keyrings */
+ selinux_key_alloc(&root_user_keyring, current,
+ KEY_ALLOC_NOT_IN_QUOTA);
+ selinux_key_alloc(&root_session_keyring, current,
+ KEY_ALLOC_NOT_IN_QUOTA);
+#endif
+
return 0;
}
/* Set up any superblocks initialized prior to the policy load. */
printk(KERN_INFO "SELinux: Setting up existing superblocks.\n");
+ spin_lock(&sb_lock);
spin_lock(&sb_security_lock);
next_sb:
if (!list_empty(&superblock_security_head)) {
struct superblock_security_struct,
list);
struct super_block *sb = sbsec->sb;
- spin_lock(&sb_lock);
sb->s_count++;
- spin_unlock(&sb_lock);
spin_unlock(&sb_security_lock);
+ spin_unlock(&sb_lock);
down_read(&sb->s_umount);
if (sb->s_root)
superblock_doinit(sb, NULL);
drop_super(sb);
+ spin_lock(&sb_lock);
spin_lock(&sb_security_lock);
list_del_init(&sbsec->list);
goto next_sb;
}
spin_unlock(&sb_security_lock);
+ spin_unlock(&sb_lock);
}
/* SELinux requires early initialization in order to label
all processes and objects when they are created. */
security_initcall(selinux_init);
-#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_NETFILTER)
+#if defined(CONFIG_NETFILTER)
static struct nf_hook_ops selinux_ipv4_op = {
.hook = selinux_ipv4_postroute_last,
panic("SELinux: nf_register_hook for IPv6: error %d\n", err);
#endif /* IPV6 */
+
out:
return err;
}
}
#endif
-#else /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */
+#else /* CONFIG_NETFILTER */
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
#define selinux_nf_ip_exit()
#endif
-#endif /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */
+#endif /* CONFIG_NETFILTER */
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
int selinux_disable(void)
printk(KERN_INFO "SELinux: Disabled at runtime.\n");
selinux_disabled = 1;
+ selinux_enabled = 0;
/* Reset security_ops to the secondary module, dummy or capability. */
security_ops = secondary_ops;