*
* 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/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;
return 1;
}
__setup("selinux=", selinux_enabled_setup);
+#else
+int selinux_enabled = 1;
#endif
/* Original (dummy) security module. */
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)
+{
+ 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. */
static int task_alloc_security(struct task_struct *task)
{
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 (seen & (Opt_context|Opt_fscontext)) {
+ if (seen & Opt_fscontext) {
rc = -EINVAL;
printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
goto out_free;
}
- context = match_strdup(&args[0]);
- if (!context) {
+ fscontext = match_strdup(&args[0]);
+ if (!fscontext) {
rc = -ENOMEM;
goto out_free;
}
seen |= Opt_fscontext;
break;
+ case Opt_rootcontext:
+ if (seen & Opt_rootcontext) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_free;
+ }
+ rootcontext = match_strdup(&args[0]);
+ if (!rootcontext) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+ if (!alloc)
+ alloc = 1;
+ seen |= Opt_rootcontext;
+ break;
+
case Opt_defcontext:
if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
rc = -EINVAL;
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);
- 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);
+ 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);
- if (rc)
- goto out_free;
-
- rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__ASSOCIATE, 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 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_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;
return SECCLASS_NETLINK_ROUTE_SOCKET;
case NETLINK_FIREWALL:
return SECCLASS_NETLINK_FIREWALL_SOCKET;
- case NETLINK_TCPDIAG:
+ case NETLINK_INET_DIAG:
return SECCLASS_NETLINK_TCPDIAG_SOCKET;
case NETLINK_NFLOG:
return SECCLASS_NETLINK_NFLOG_SOCKET;
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;
}
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,
- sock->sk->sk_protocol);
- } 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;
}
/* 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;
}
/* 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;
/* Check whether a task has a particular permission to an inode.
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_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;
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)
{
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
#define MAY_UNLINK 1
#define MAY_RMDIR 2
}
/* 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;
}
/* 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;
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,
{
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;
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;
{
struct avc_audit_data ad;
struct file *file, *devnull = NULL;
- struct tty_struct *tty = current->signal->tty;
+ 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_list);
+ 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
struct inode *inode = file->f_dentry->d_inode;
if (inode_has_perm(current, inode,
FILE__READ | FILE__WRITE, NULL)) {
- /* Reset controlling tty. */
- current->signal->tty = NULL;
- current->signal->tty_old_pgrp = 0;
+ 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. */
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 (devnull) {
- atomic_inc(&devnull->f_count);
+ get_file(devnull);
} else {
devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
if (!devnull) {
/* 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(tsec->ptrace_sid, sid,
- SECCLASS_PROCESS, PROCESS__PTRACE,
- NULL);
- if (rc) {
- bsec->unsafe = 1;
- return;
+ 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;
+ }
}
}
tsec->sid = sid;
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
{
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,
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;
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);
return may_create(dir, dentry, inode_mode_to_security_class(mode));
}
-static void selinux_inode_post_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
-{
- post_create(dir, dentry);
-}
-
static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
struct inode *new_inode, struct dentry *new_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);
if (rc)
return rc;
+ rc = security_validate_transition(isec->sid, newsid, tsec->sid,
+ isec->sclass);
+ if (rc)
+ return rc;
+
return avc_has_perm(newsid,
sbsec->sid,
SECCLASS_FILESYSTEM,
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 inode *inode, const char *name, void *buffer, size_t size)
+static const char *selinux_inode_xattr_getsuffix(void)
{
- 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 inode *inode, const char *name,
return 0;
}
-static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags)
+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, prot, flags);
+ 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)
{
int rc;
- rc = secondary_ops->file_mprotect(vma, prot);
+ rc = secondary_ops->file_mprotect(vma, reqprot, prot);
if (rc)
return rc;
- if (vma->vm_file != NULL && vma->anon_vma != NULL && (prot & PROT_EXEC)) {
- /*
- * 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.
- */
- int rc = file_has_perm(current, vma->vm_file, FILE__EXECMOD);
+ 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;
}
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;
-
- /* Retain ptracer SID across fork, if any.
- This will be reset by the ptrace hook upon any
- subsequent ptrace_attach operations. */
- tsec2->ptrace_sid = tsec1->ptrace_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. */
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->signal->rlim + resource;
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);
+ 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)
{
nexthdr = ip6->nexthdr;
offset += sizeof(_ipv6h);
- offset = ipv6_skip_exthdr(skb, offset, &nexthdr,
- skb->tail - skb->head - offset);
+ offset = ipv6_skip_exthdr(skb, offset, &nexthdr);
if (offset < 0)
goto out;
{
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,
+ newsid = tsec->sockcreate_sid ? : tsec->sid;
+ err = avc_has_perm(tsec->sid, newsid,
socket_type_to_security_class(family, type,
protocol), SOCKET__CREATE, NULL);
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;
+ newsid = tsec->sockcreate_sid ? : tsec->sid;
isec->sclass = socket_type_to_security_class(family, type, protocol);
- isec->sid = kern ? SECINITSID_KERNEL : tsec->sid;
+ 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;
}
- 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;
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;
}
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;
+ int err = 0;
u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
- u32 sock_sid = 0;
- u16 sock_class = 0;
- struct socket *sock;
- struct net_device *dev;
- 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;
- dev = skb->dev;
- if (!dev)
+ if (!skb->dev)
goto out;
- err = sel_netif_sids(dev, &if_sid, NULL);
+ err = sel_netif_sids(skb->dev, &if_sid, NULL);
if (err)
goto out;
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)
- goto out;
-
- err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad);
+ 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(sock_sid, node_sid, SECCLASS_NODE, node_perm, &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(sock_sid, port_sid,
- sock_class, recv_perm, &ad);
+ 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);
}
+static unsigned int selinux_sk_getsid_security(struct sock *sk, struct flowi *fl, u8 dir)
+{
+ struct inode_security_struct *isec;
+ u32 sock_sid = SECINITSID_ANY_SOCKET;
+
+ if (!sk)
+ 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;
+}
+
static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
{
int err = 0;
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;
#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 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)
{
- char *addrp;
- int len, err = NF_ACCEPT;
+ int err;
u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 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;
-
err = sel_netif_sids(dev, &if_sid, NULL);
if (err)
goto out;
- isec = inode->i_security;
-
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)
- goto out;
-
- err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF,
- netif_perm, &ad) ? NF_DROP : NF_ACCEPT;
- 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, &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, &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 */
-#else
-
-static inline int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
-{
- return 0;
-}
-
-#endif /* CONFIG_SECURITY_NETWORK */
-
static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
{
- struct task_security_struct *tsec;
- struct av_decision avd;
int err;
err = secondary_ops->netlink_send(sk, skb);
if (err)
return err;
- tsec = current->security;
-
- avd.allowed = 0;
- avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY, ~0, &avd);
- cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed);
-
if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
err = selinux_nlmsg_perm(sk, skb);
return err;
}
-static int selinux_netlink_recv(struct sk_buff *skb)
+static int selinux_netlink_recv(struct sk_buff *skb, int capability)
{
- if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
- return -EPERM;
- return 0;
+ 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 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, &ad);
+ return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
}
static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
return 0;
}
- err = ipc_has_perm(&msq->q_perm, SECCLASS_MSGQ, perms);
+ err = ipc_has_perm(&msq->q_perm, perms);
return err;
}
return 0;
}
- err = ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms);
+ err = ipc_has_perm(&shp->shm_perm, perms);
return err;
}
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 */
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) {
/* SELinux only allows a process to change its own
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
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;
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
task_lock(p);
- if (p->ptrace & PT_PTRACED) {
- error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
+ 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(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
+ 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);
}
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,
.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;