summaryrefslogtreecommitdiffstats
path: root/security/selinux/hooks.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r--security/selinux/hooks.c1258
1 files changed, 672 insertions, 586 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f85597a4d733..dbeaa783b2a9 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -156,33 +156,62 @@ static int selinux_secmark_enabled(void)
return (atomic_read(&selinux_secmark_refcount) > 0);
}
-/* Allocate and free functions for each kind of security blob. */
-
-static int task_alloc_security(struct task_struct *task)
+/*
+ * initialise the security for the init task
+ */
+static void cred_init_security(void)
{
+ struct cred *cred = (struct cred *) current->real_cred;
struct task_security_struct *tsec;
tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
if (!tsec)
- return -ENOMEM;
+ panic("SELinux: Failed to initialize initial task.\n");
- tsec->osid = tsec->sid = SECINITSID_UNLABELED;
- task->security = tsec;
+ tsec->osid = tsec->sid = SECINITSID_KERNEL;
+ cred->security = tsec;
+}
- return 0;
+/*
+ * get the security ID of a set of credentials
+ */
+static inline u32 cred_sid(const struct cred *cred)
+{
+ const struct task_security_struct *tsec;
+
+ tsec = cred->security;
+ return tsec->sid;
}
-static void task_free_security(struct task_struct *task)
+/*
+ * get the objective security ID of a task
+ */
+static inline u32 task_sid(const struct task_struct *task)
{
- struct task_security_struct *tsec = task->security;
- task->security = NULL;
- kfree(tsec);
+ u32 sid;
+
+ rcu_read_lock();
+ sid = cred_sid(__task_cred(task));
+ rcu_read_unlock();
+ return sid;
+}
+
+/*
+ * get the subjective security ID of the current task
+ */
+static inline u32 current_sid(void)
+{
+ const struct task_security_struct *tsec = current_cred()->security;
+
+ return tsec->sid;
}
+/* Allocate and free functions for each kind of security blob. */
+
static int inode_alloc_security(struct inode *inode)
{
- struct task_security_struct *tsec = current->security;
struct inode_security_struct *isec;
+ u32 sid = current_sid();
isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
if (!isec)
@@ -193,7 +222,7 @@ static int inode_alloc_security(struct inode *inode)
isec->inode = inode;
isec->sid = SECINITSID_UNLABELED;
isec->sclass = SECCLASS_FILE;
- isec->task_sid = tsec->sid;
+ isec->task_sid = sid;
inode->i_security = isec;
return 0;
@@ -215,15 +244,15 @@ static void inode_free_security(struct inode *inode)
static int file_alloc_security(struct file *file)
{
- struct task_security_struct *tsec = current->security;
struct file_security_struct *fsec;
+ u32 sid = current_sid();
fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
if (!fsec)
return -ENOMEM;
- fsec->sid = tsec->sid;
- fsec->fown_sid = tsec->sid;
+ fsec->sid = sid;
+ fsec->fown_sid = sid;
file->f_security = fsec;
return 0;
@@ -338,8 +367,9 @@ static const match_table_t tokens = {
static int may_context_mount_sb_relabel(u32 sid,
struct superblock_security_struct *sbsec,
- struct task_security_struct *tsec)
+ const struct cred *cred)
{
+ const struct task_security_struct *tsec = cred->security;
int rc;
rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
@@ -354,8 +384,9 @@ static int may_context_mount_sb_relabel(u32 sid,
static int may_context_mount_inode_relabel(u32 sid,
struct superblock_security_struct *sbsec,
- struct task_security_struct *tsec)
+ const struct cred *cred)
{
+ const struct task_security_struct *tsec = cred->security;
int rc;
rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
FILESYSTEM__RELABELFROM, NULL);
@@ -553,8 +584,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
static int selinux_set_mnt_opts(struct super_block *sb,
struct security_mnt_opts *opts)
{
+ const struct cred *cred = current_cred();
int rc = 0, i;
- struct task_security_struct *tsec = current->security;
struct superblock_security_struct *sbsec = sb->s_security;
const char *name = sb->s_type->name;
struct inode *inode = sbsec->sb->s_root->d_inode;
@@ -671,7 +702,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
sbsec->proc = 1;
/* Determine the labeling behavior to use for this filesystem type. */
- rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+ rc = security_fs_use(sbsec->proc ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
if (rc) {
printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
__func__, sb->s_type->name, rc);
@@ -680,8 +711,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
/* sets the context of the superblock for the fs being mounted. */
if (fscontext_sid) {
-
- rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec);
+ rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
if (rc)
goto out;
@@ -695,12 +725,14 @@ static int selinux_set_mnt_opts(struct super_block *sb,
*/
if (context_sid) {
if (!fscontext_sid) {
- rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec);
+ rc = may_context_mount_sb_relabel(context_sid, sbsec,
+ cred);
if (rc)
goto out;
sbsec->sid = context_sid;
} else {
- rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec);
+ rc = may_context_mount_inode_relabel(context_sid, sbsec,
+ cred);
if (rc)
goto out;
}
@@ -712,7 +744,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
}
if (rootcontext_sid) {
- rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec);
+ rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec,
+ cred);
if (rc)
goto out;
@@ -730,7 +763,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
if (defcontext_sid != sbsec->def_sid) {
rc = may_context_mount_inode_relabel(defcontext_sid,
- sbsec, tsec);
+ sbsec, cred);
if (rc)
goto out;
}
@@ -1345,18 +1378,53 @@ static inline u32 signal_to_av(int sig)
return perm;
}
-/* Check permission betweeen a pair of tasks, e.g. signal checks,
- fork check, ptrace check, etc. */
-static int task_has_perm(struct task_struct *tsk1,
- struct task_struct *tsk2,
+/*
+ * Check permission between a pair of credentials
+ * fork check, ptrace check, etc.
+ */
+static int cred_has_perm(const struct cred *actor,
+ const struct cred *target,
u32 perms)
{
- struct task_security_struct *tsec1, *tsec2;
+ u32 asid = cred_sid(actor), tsid = cred_sid(target);
- tsec1 = tsk1->security;
- tsec2 = tsk2->security;
- return avc_has_perm(tsec1->sid, tsec2->sid,
- SECCLASS_PROCESS, perms, NULL);
+ return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL);
+}
+
+/*
+ * Check permission between a pair of tasks, e.g. signal checks,
+ * fork check, ptrace check, etc.
+ * tsk1 is the actor and tsk2 is the target
+ * - this uses the default subjective creds of tsk1
+ */
+static int task_has_perm(const struct task_struct *tsk1,
+ const struct task_struct *tsk2,
+ u32 perms)
+{
+ const struct task_security_struct *__tsec1, *__tsec2;
+ u32 sid1, sid2;
+
+ rcu_read_lock();
+ __tsec1 = __task_cred(tsk1)->security; sid1 = __tsec1->sid;
+ __tsec2 = __task_cred(tsk2)->security; sid2 = __tsec2->sid;
+ rcu_read_unlock();
+ return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL);
+}
+
+/*
+ * Check permission between current and another task, e.g. signal checks,
+ * fork check, ptrace check, etc.
+ * current is the actor and tsk2 is the target
+ * - this uses current's subjective creds
+ */
+static int current_has_perm(const struct task_struct *tsk,
+ u32 perms)
+{
+ u32 sid, tsid;
+
+ sid = current_sid();
+ tsid = task_sid(tsk);
+ return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL);
}
#if CAP_LAST_CAP > 63
@@ -1365,14 +1433,14 @@ static int task_has_perm(struct task_struct *tsk1,
/* Check whether a task is allowed to use a capability. */
static int task_has_capability(struct task_struct *tsk,
- int cap)
+ int cap, int audit)
{
- struct task_security_struct *tsec;
struct avc_audit_data ad;
+ struct av_decision avd;
u16 sclass;
+ u32 sid = task_sid(tsk);
u32 av = CAP_TO_MASK(cap);
-
- tsec = tsk->security;
+ int rc;
AVC_AUDIT_DATA_INIT(&ad, CAP);
ad.tsk = tsk;
@@ -1390,37 +1458,39 @@ static int task_has_capability(struct task_struct *tsk,
"SELinux: out of range capability %d\n", cap);
BUG();
}
- return avc_has_perm(tsec->sid, tsec->sid, sclass, av, &ad);
+
+ rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
+ if (audit == SECURITY_CAP_AUDIT)
+ avc_audit(sid, sid, sclass, av, &avd, rc, &ad);
+ return rc;
}
/* Check whether a task is allowed to use a system operation. */
static int task_has_system(struct task_struct *tsk,
u32 perms)
{
- struct task_security_struct *tsec;
-
- tsec = tsk->security;
+ u32 sid = task_sid(tsk);
- return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
+ return avc_has_perm(sid, SECINITSID_KERNEL,
SECCLASS_SYSTEM, perms, NULL);
}
/* 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). */
-static int inode_has_perm(struct task_struct *tsk,
+static int inode_has_perm(const struct cred *cred,
struct inode *inode,
u32 perms,
struct avc_audit_data *adp)
{
- struct task_security_struct *tsec;
struct inode_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid;
if (unlikely(IS_PRIVATE(inode)))
return 0;
- tsec = tsk->security;
+ sid = cred_sid(cred);
isec = inode->i_security;
if (!adp) {
@@ -1429,23 +1499,24 @@ static int inode_has_perm(struct task_struct *tsk,
ad.u.fs.inode = inode;
}
- return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
+ return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
}
/* Same as inode_has_perm, but pass explicit audit data containing
the dentry to help the auditing code to more easily generate the
pathname if needed. */
-static inline int dentry_has_perm(struct task_struct *tsk,
+static inline int dentry_has_perm(const struct cred *cred,
struct vfsmount *mnt,
struct dentry *dentry,
u32 av)
{
struct inode *inode = dentry->d_inode;
struct avc_audit_data ad;
+
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.mnt = mnt;
ad.u.fs.path.dentry = dentry;
- return inode_has_perm(tsk, inode, av, &ad);
+ return inode_has_perm(cred, inode, av, &ad);
}
/* Check whether a task can use an open file descriptor to
@@ -1456,33 +1527,35 @@ static inline int dentry_has_perm(struct task_struct *tsk,
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 int file_has_perm(struct task_struct *tsk,
- struct file *file,
- u32 av)
+static int file_has_perm(const struct cred *cred,
+ struct file *file,
+ u32 av)
{
- struct task_security_struct *tsec = tsk->security;
struct file_security_struct *fsec = file->f_security;
struct inode *inode = file->f_path.dentry->d_inode;
struct avc_audit_data ad;
+ u32 sid = cred_sid(cred);
int rc;
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path = file->f_path;
- if (tsec->sid != fsec->sid) {
- rc = avc_has_perm(tsec->sid, fsec->sid,
+ if (sid != fsec->sid) {
+ rc = avc_has_perm(sid, fsec->sid,
SECCLASS_FD,
FD__USE,
&ad);
if (rc)
- return rc;
+ goto out;
}
/* av is zero if only checking access to the descriptor. */
+ rc = 0;
if (av)
- return inode_has_perm(tsk, inode, av, &ad);
+ rc = inode_has_perm(cred, inode, av, &ad);
- return 0;
+out:
+ return rc;
}
/* Check whether a task can create a file. */
@@ -1490,36 +1563,36 @@ static int may_create(struct inode *dir,
struct dentry *dentry,
u16 tclass)
{
- struct task_security_struct *tsec;
+ const struct cred *cred = current_cred();
+ const struct task_security_struct *tsec = cred->security;
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
- u32 newsid;
+ u32 sid, newsid;
struct avc_audit_data ad;
int rc;
- tsec = current->security;
dsec = dir->i_security;
sbsec = dir->i_sb->s_security;
+ sid = tsec->sid;
+ newsid = tsec->create_sid;
+
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = dentry;
- rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
+ rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
DIR__ADD_NAME | DIR__SEARCH,
&ad);
if (rc)
return rc;
- if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
- newsid = tsec->create_sid;
- } else {
- rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
- &newsid);
+ if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+ rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
if (rc)
return rc;
}
- rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
+ rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad);
if (rc)
return rc;
@@ -1532,11 +1605,9 @@ static int may_create(struct inode *dir,
static int may_create_key(u32 ksid,
struct task_struct *ctx)
{
- struct task_security_struct *tsec;
-
- tsec = ctx->security;
+ u32 sid = task_sid(ctx);
- return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
+ return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
}
#define MAY_LINK 0
@@ -1549,13 +1620,12 @@ static int may_link(struct inode *dir,
int kind)
{
- struct task_security_struct *tsec;
struct inode_security_struct *dsec, *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
u32 av;
int rc;
- tsec = current->security;
dsec = dir->i_security;
isec = dentry->d_inode->i_security;
@@ -1564,7 +1634,7 @@ static int may_link(struct inode *dir,
av = DIR__SEARCH;
av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
- rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
+ rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
@@ -1584,7 +1654,7 @@ static int may_link(struct inode *dir,
return 0;
}
- rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
+ rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad);
return rc;
}
@@ -1593,14 +1663,13 @@ static inline int may_rename(struct inode *old_dir,
struct inode *new_dir,
struct dentry *new_dentry)
{
- struct task_security_struct *tsec;
struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
u32 av;
int old_is_dir, new_is_dir;
int rc;
- tsec = current->security;
old_dsec = old_dir->i_security;
old_isec = old_dentry->d_inode->i_security;
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
@@ -1609,16 +1678,16 @@ static inline int may_rename(struct inode *old_dir,
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = old_dentry;
- rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
+ rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,
DIR__REMOVE_NAME | DIR__SEARCH, &ad);
if (rc)
return rc;
- rc = avc_has_perm(tsec->sid, old_isec->sid,
+ rc = avc_has_perm(sid, old_isec->sid,
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,
+ rc = avc_has_perm(sid, old_isec->sid,
old_isec->sclass, DIR__REPARENT, &ad);
if (rc)
return rc;
@@ -1628,13 +1697,13 @@ static inline int may_rename(struct inode *old_dir,
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, &ad);
+ rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
if (new_dentry->d_inode) {
new_isec = new_dentry->d_inode->i_security;
new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
- rc = avc_has_perm(tsec->sid, new_isec->sid,
+ rc = avc_has_perm(sid, new_isec->sid,
new_isec->sclass,
(new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
if (rc)
@@ -1645,18 +1714,16 @@ static inline int may_rename(struct inode *old_dir,
}
/* Check whether a task can perform a filesystem operation. */
-static int superblock_has_perm(struct task_struct *tsk,
+static int superblock_has_perm(const struct cred *cred,
struct super_block *sb,
u32 perms,
struct avc_audit_data *ad)
{
- struct task_security_struct *tsec;
struct superblock_security_struct *sbsec;
+ u32 sid = cred_sid(cred);
- tsec = tsk->security;
sbsec = sb->s_security;
- return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- perms, ad);
+ return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);
}
/* Convert a Linux mode and permission mask to an access vector. */
@@ -1687,15 +1754,39 @@ static inline u32 file_mask_to_av(int mode, int mask)
return av;
}
+/* Convert a Linux file to an access vector. */
+static inline u32 file_to_av(struct file *file)
+{
+ u32 av = 0;
+
+ if (file->f_mode & FMODE_READ)
+ av |= FILE__READ;
+ if (file->f_mode & FMODE_WRITE) {
+ if (file->f_flags & O_APPEND)
+ av |= FILE__APPEND;
+ else
+ av |= FILE__WRITE;
+ }
+ if (!av) {
+ /*
+ * Special file opened with flags 3 for ioctl-only use.
+ */
+ av = FILE__IOCTL;
+ }
+
+ return av;
+}
+
/*
- * Convert a file mask to an access vector and include the correct open
+ * Convert a file to an access vector and include the correct open
* open permission.
*/
-static inline u32 open_file_mask_to_av(int mode, int mask)
+static inline u32 open_file_to_av(struct file *file)
{
- u32 av = file_mask_to_av(mode, mask);
+ u32 av = file_to_av(file);
if (selinux_policycap_openperm) {
+ mode_t mode = file->f_path.dentry->d_inode->i_mode;
/*
* lnk files and socks do not really have an 'open'
*/
@@ -1711,31 +1802,8 @@ static inline u32 open_file_mask_to_av(int mode, int mask)
av |= DIR__OPEN;
else
printk(KERN_ERR "SELinux: WARNING: inside %s with "
- "unknown mode:%x\n", __func__, mode);
- }
- return av;
-}
-
-/* Convert a Linux file to an access vector. */
-static inline u32 file_to_av(struct file *file)
-{
- u32 av = 0;
-
- if (file->f_mode & FMODE_READ)
- av |= FILE__READ;
- if (file->f_mode & FMODE_WRITE) {
- if (file->f_flags & O_APPEND)
- av |= FILE__APPEND;
- else
- av |= FILE__WRITE;
+ "unknown mode:%o\n", __func__, mode);
}
- if (!av) {
- /*
- * Special file opened with flags 3 for ioctl-only use.
- */
- av = FILE__IOCTL;
- }
-
return av;
}
@@ -1751,13 +1819,12 @@ static int selinux_ptrace_may_access(struct task_struct *child,
return rc;
if (mode == PTRACE_MODE_READ) {
- struct task_security_struct *tsec = current->security;
- struct task_security_struct *csec = child->security;
- return avc_has_perm(tsec->sid, csec->sid,
- SECCLASS_FILE, FILE__READ, NULL);
+ u32 sid = current_sid();
+ u32 csid = task_sid(child);
+ return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL);
}
- return task_has_perm(current, child, PROCESS__PTRACE);
+ return current_has_perm(child, PROCESS__PTRACE);
}
static int selinux_ptrace_traceme(struct task_struct *parent)
@@ -1776,40 +1843,37 @@ static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
{
int error;
- error = task_has_perm(current, target, PROCESS__GETCAP);
+ error = current_has_perm(target, PROCESS__GETCAP);
if (error)
return error;
return secondary_ops->capget(target, effective, inheritable, permitted);
}
-static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
+static int selinux_capset(struct cred *new, const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted)
{
int error;
- error = secondary_ops->capset_check(target, effective, inheritable, permitted);
+ error = secondary_ops->capset(new, old,
+ effective, inheritable, permitted);
if (error)
return error;
- 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)
-{
- secondary_ops->capset_set(target, effective, inheritable, permitted);
+ return cred_has_perm(old, new, PROCESS__SETCAP);
}
-static int selinux_capable(struct task_struct *tsk, int cap)
+static int selinux_capable(struct task_struct *tsk, int cap, int audit)
{
int rc;
- rc = secondary_ops->capable(tsk, cap);
+ rc = secondary_ops->capable(tsk, cap, audit);
if (rc)
return rc;
- return task_has_capability(tsk, cap);
+ return task_has_capability(tsk, cap, audit);
}
static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
@@ -1857,15 +1921,14 @@ static int selinux_sysctl(ctl_table *table, int op)
{
int error = 0;
u32 av;
- struct task_security_struct *tsec;
- u32 tsid;
+ u32 tsid, sid;
int rc;
rc = secondary_ops->sysctl(table, op);
if (rc)
return rc;
- tsec = current->security;
+ sid = current_sid();
rc = selinux_sysctl_get_sid(table, (op == 0001) ?
SECCLASS_DIR : SECCLASS_FILE, &tsid);
@@ -1877,7 +1940,7 @@ static int selinux_sysctl(ctl_table *table, int op)
/* The op values are "defined" in sysctl.c, thereby creating
* a bad coupling between this module and sysctl.c */
if (op == 001) {
- error = avc_has_perm(tsec->sid, tsid,
+ error = avc_has_perm(sid, tsid,
SECCLASS_DIR, DIR__SEARCH, NULL);
} else {
av = 0;
@@ -1886,7 +1949,7 @@ static int selinux_sysctl(ctl_table *table, int op)
if (op & 002)
av |= FILE__WRITE;
if (av)
- error = avc_has_perm(tsec->sid, tsid,
+ error = avc_has_perm(sid, tsid,
SECCLASS_FILE, av, NULL);
}
@@ -1895,6 +1958,7 @@ static int selinux_sysctl(ctl_table *table, int op)
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
{
+ const struct cred *cred = current_cred();
int rc = 0;
if (!sb)
@@ -1906,14 +1970,12 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
case Q_QUOTAOFF:
case Q_SETINFO:
case Q_SETQUOTA:
- rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAMOD,
- NULL);
+ rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAMOD, NULL);
break;
case Q_GETFMT:
case Q_GETINFO:
case Q_GETQUOTA:
- rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAGET,
- NULL);
+ rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAGET, NULL);
break;
default:
rc = 0; /* let the kernel handle invalid cmds */
@@ -1924,7 +1986,9 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
static int selinux_quota_on(struct dentry *dentry)
{
- return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON);
+ const struct cred *cred = current_cred();
+
+ return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON);
}
static int selinux_syslog(int type)
@@ -1972,16 +2036,8 @@ static int selinux_syslog(int type)
static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
{
int rc, cap_sys_admin = 0;
- struct task_security_struct *tsec = current->security;
-
- 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),
- 0,
- NULL);
+ rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT);
if (rc == 0)
cap_sys_admin = 1;
@@ -1990,59 +2046,45 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
/* binprm security operations */
-static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
+static int selinux_bprm_set_creds(struct linux_binprm *bprm)
{
- struct bprm_security_struct *bsec;
-
- bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL);
- if (!bsec)
- return -ENOMEM;
-
- bsec->sid = SECINITSID_UNLABELED;
- bsec->set = 0;
-
- bprm->security = bsec;
- return 0;
-}
-
-static int selinux_bprm_set_security(struct linux_binprm *bprm)
-{
- struct task_security_struct *tsec;
- struct inode *inode = bprm->file->f_path.dentry->d_inode;
+ const struct task_security_struct *old_tsec;
+ struct task_security_struct *new_tsec;
struct inode_security_struct *isec;
- struct bprm_security_struct *bsec;
- u32 newsid;
struct avc_audit_data ad;
+ struct inode *inode = bprm->file->f_path.dentry->d_inode;
int rc;
- rc = secondary_ops->bprm_set_security(bprm);
+ rc = secondary_ops->bprm_set_creds(bprm);
if (rc)
return rc;
- bsec = bprm->security;
-
- if (bsec->set)
+ /* SELinux context only depends on initial program or script and not
+ * the script interpreter */
+ if (bprm->cred_prepared)
return 0;
- tsec = current->security;
+ old_tsec = current_security();
+ new_tsec = bprm->cred->security;
isec = inode->i_security;
/* Default to the current task SID. */
- bsec->sid = tsec->sid;
+ new_tsec->sid = old_tsec->sid;
+ new_tsec->osid = old_tsec->sid;
/* Reset fs, key, and sock SIDs on execve. */
- tsec->create_sid = 0;
- tsec->keycreate_sid = 0;
- tsec->sockcreate_sid = 0;
+ new_tsec->create_sid = 0;
+ new_tsec->keycreate_sid = 0;
+ new_tsec->sockcreate_sid = 0;
- if (tsec->exec_sid) {
- newsid = tsec->exec_sid;
+ if (old_tsec->exec_sid) {
+ new_tsec->sid = old_tsec->exec_sid;
/* Reset exec SID on execve. */
- tsec->exec_sid = 0;
+ new_tsec->exec_sid = 0;
} else {
/* Check for a default transition on this program. */
- rc = security_transition_sid(tsec->sid, isec->sid,
- SECCLASS_PROCESS, &newsid);
+ rc = security_transition_sid(old_tsec->sid, isec->sid,
+ SECCLASS_PROCESS, &new_tsec->sid);
if (rc)
return rc;
}
@@ -2051,33 +2093,63 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
ad.u.fs.path = bprm->file->f_path;
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
- newsid = tsec->sid;
+ new_tsec->sid = old_tsec->sid;
- if (tsec->sid == newsid) {
- rc = avc_has_perm(tsec->sid, isec->sid,
+ if (new_tsec->sid == old_tsec->sid) {
+ rc = avc_has_perm(old_tsec->sid, isec->sid,
SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
if (rc)
return rc;
} else {
/* Check permissions for the transition. */
- rc = avc_has_perm(tsec->sid, newsid,
+ rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
if (rc)
return rc;
- rc = avc_has_perm(newsid, isec->sid,
+ rc = avc_has_perm(new_tsec->sid, isec->sid,
SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
if (rc)
return rc;
- /* Clear any possibly unsafe personality bits on exec: */
- current->personality &= ~PER_CLEAR_ON_SETID;
+ /* Check for shared state */
+ if (bprm->unsafe & LSM_UNSAFE_SHARE) {
+ rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
+ SECCLASS_PROCESS, PROCESS__SHARE,
+ NULL);
+ if (rc)
+ return -EPERM;
+ }
+
+ /* Make sure that anyone attempting to ptrace over a task that
+ * changes its SID has the appropriate permit */
+ if (bprm->unsafe &
+ (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
+ struct task_struct *tracer;
+ struct task_security_struct *sec;
+ u32 ptsid = 0;
- /* Set the security field to the new SID. */
- bsec->sid = newsid;
+ rcu_read_lock();
+ tracer = tracehook_tracer_task(current);
+ if (likely(tracer != NULL)) {
+ sec = __task_cred(tracer)->security;
+ ptsid = sec->sid;
+ }
+ rcu_read_unlock();
+
+ if (ptsid != 0) {
+ rc = avc_has_perm(ptsid, new_tsec->sid,
+ SECCLASS_PROCESS,
+ PROCESS__PTRACE, NULL);
+ if (rc)
+ return -EPERM;
+ }
+ }
+
+ /* Clear any possibly unsafe personality bits on exec: */
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
}
- bsec->set = 1;
return 0;
}
@@ -2086,35 +2158,34 @@ static int selinux_bprm_check_security(struct linux_binprm *bprm)
return secondary_ops->bprm_check_security(bprm);
}
-
static int selinux_bprm_secureexec(struct linux_binprm *bprm)
{
- struct task_security_struct *tsec = current->security;
+ const struct cred *cred = current_cred();
+ const struct task_security_struct *tsec = cred->security;
+ u32 sid, osid;
int atsecure = 0;
- if (tsec->osid != tsec->sid) {
+ sid = tsec->sid;
+ osid = tsec->osid;
+
+ if (osid != sid) {
/* Enable secure mode for SIDs transitions unless
the noatsecure permission is granted between
the two SIDs, i.e. ahp returns 0. */
- atsecure = avc_has_perm(tsec->osid, tsec->sid,
- SECCLASS_PROCESS,
- PROCESS__NOATSECURE, NULL);
+ atsecure = avc_has_perm(osid, sid,
+ SECCLASS_PROCESS,
+ PROCESS__NOATSECURE, NULL);
}
return (atsecure || secondary_ops->bprm_secureexec(bprm));
}
-static void selinux_bprm_free_security(struct linux_binprm *bprm)
-{
- kfree(bprm->security);
- bprm->security = NULL;
-}
-
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)
+static inline void flush_unauthorized_files(const struct cred *cred,
+ struct files_struct *files)
{
struct avc_audit_data ad;
struct file *file, *devnull = NULL;
@@ -2136,7 +2207,7 @@ static inline void flush_unauthorized_files(struct files_struct *files)
interested in the inode-based check here. */
file = list_first_entry(&tty->tty_files, struct file, f_u.fu_list);
inode = file->f_path.dentry->d_inode;
- if (inode_has_perm(current, inode,
+ if (inode_has_perm(cred, inode,
FILE__READ | FILE__WRITE, NULL)) {
drop_tty = 1;
}
@@ -2171,7 +2242,7 @@ static inline void flush_unauthorized_files(struct files_struct *files)
file = fget(i);
if (!file)
continue;
- if (file_has_perm(current,
+ if (file_has_perm(cred,
file,
file_to_av(file))) {
sys_close(i);
@@ -2185,7 +2256,10 @@ static inline void flush_unauthorized_files(struct files_struct *files)
if (devnull) {
get_file(devnull);
} else {
- devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
+ devnull = dentry_open(
+ dget(selinux_null),
+ mntget(selinuxfs_mount),
+ O_RDWR, cred);
if (IS_ERR(devnull)) {
devnull = NULL;
put_unused_fd(fd);
@@ -2204,94 +2278,78 @@ static inline void flush_unauthorized_files(struct files_struct *files)
spin_unlock(&files->file_lock);
}
-static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+/*
+ * Prepare a process for imminent new credential changes due to exec
+ */
+static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
{
- struct task_security_struct *tsec;
- struct bprm_security_struct *bsec;
- u32 sid;
- int rc;
-
- secondary_ops->bprm_apply_creds(bprm, unsafe);
-
- tsec = current->security;
+ struct task_security_struct *new_tsec;
+ struct rlimit *rlim, *initrlim;
+ int rc, i;
- bsec = bprm->security;
- sid = bsec->sid;
+ secondary_ops->bprm_committing_creds(bprm);
- 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(tsec->sid, sid, SECCLASS_PROCESS,
- PROCESS__SHARE, NULL);
- if (rc) {
- bsec->unsafe = 1;
- return;
- }
- }
+ new_tsec = bprm->cred->security;
+ if (new_tsec->sid == new_tsec->osid)
+ 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)) {
- struct task_struct *tracer;
- struct task_security_struct *sec;
- u32 ptsid = 0;
+ /* Close files for which the new task SID is not authorized. */
+ flush_unauthorized_files(bprm->cred, current->files);
- rcu_read_lock();
- tracer = tracehook_tracer_task(current);
- if (likely(tracer != NULL)) {
- sec = tracer->security;
- ptsid = sec->sid;
- }
- rcu_read_unlock();
+ /* Always clear parent death signal on SID transitions. */
+ current->pdeath_signal = 0;
- if (ptsid != 0) {
- rc = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
- PROCESS__PTRACE, NULL);
- if (rc) {
- bsec->unsafe = 1;
- return;
- }
- }
+ /* 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(new_tsec->osid, new_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);
}
- tsec->sid = sid;
+ update_rlimit_cpu(rlim->rlim_cur);
}
}
/*
- * called after apply_creds without the task lock held
+ * Clean up the process immediately after the installation of new credentials
+ * due to exec
*/
-static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
+static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
{
- struct task_security_struct *tsec;
- struct rlimit *rlim, *initrlim;
+ const struct task_security_struct *tsec = current_security();
struct itimerval itimer;
- struct bprm_security_struct *bsec;
+ struct sighand_struct *psig;
+ u32 osid, sid;
int rc, i;
+ unsigned long flags;
- tsec = current->security;
- bsec = bprm->security;
+ secondary_ops->bprm_committed_creds(bprm);
- if (bsec->unsafe) {
- force_sig_specific(SIGKILL, current);
- return;
- }
- if (tsec->osid == tsec->sid)
+ osid = tsec->osid;
+ sid = tsec->sid;
+
+ if (sid == osid)
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);
+ /* 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(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL);
if (rc) {
memset(&itimer, 0, sizeof itimer);
for (i = 0; i < 3; i++)
@@ -2304,33 +2362,14 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
spin_unlock_irq(&current->sighand->siglock);
}
- /* Always clear parent death signal on SID transitions. */
- current->pdeath_signal = 0;
-
- /* 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);
- }
- update_rlimit_cpu(rlim->rlim_cur);
- }
-
- /* Wake up the parent if it is waiting so that it can
- recheck wait permission to the new task SID. */
+ /* Wake up the parent if it is waiting so that it can recheck
+ * wait permission to the new task SID. */
+ read_lock_irq(&tasklist_lock);
+ psig = current->parent->sighand;
+ spin_lock_irqsave(&psig->siglock, flags);
wake_up_interruptible(&current->parent->signal->wait_chldexit);
+ spin_unlock_irqrestore(&psig->siglock, flags);
+ read_unlock_irq(&tasklist_lock);
}
/* superblock security operations */
@@ -2435,8 +2474,9 @@ out:
return rc;
}
-static int selinux_sb_kern_mount(struct super_block *sb, void *data)
+static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
+ const struct cred *cred = current_cred();
struct avc_audit_data ad;
int rc;
@@ -2444,18 +2484,23 @@ static int selinux_sb_kern_mount(struct super_block *sb, void *data)
if (rc)
return rc;
+ /* Allow all mounts performed by the kernel */
+ if (flags & MS_KERNMOUNT)
+ return 0;
+
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = sb->s_root;
- return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
+ return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad);
}
static int selinux_sb_statfs(struct dentry *dentry)
{
+ const struct cred *cred = current_cred();
struct avc_audit_data ad;
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = dentry->d_sb->s_root;
- return superblock_has_perm(current, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
+ return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
}
static int selinux_mount(char *dev_name,
@@ -2464,6 +2509,7 @@ static int selinux_mount(char *dev_name,
unsigned long flags,
void *data)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->sb_mount(dev_name, path, type, flags, data);
@@ -2471,22 +2517,23 @@ static int selinux_mount(char *dev_name,
return rc;
if (flags & MS_REMOUNT)
- return superblock_has_perm(current, path->mnt->mnt_sb,
+ return superblock_has_perm(cred, path->mnt->mnt_sb,
FILESYSTEM__REMOUNT, NULL);
else
- return dentry_has_perm(current, path->mnt, path->dentry,
+ return dentry_has_perm(cred, path->mnt, path->dentry,
FILE__MOUNTON);
}
static int selinux_umount(struct vfsmount *mnt, int flags)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->sb_umount(mnt, flags);
if (rc)
return rc;
- return superblock_has_perm(current, mnt->mnt_sb,
+ return superblock_has_perm(cred, mnt->mnt_sb,
FILESYSTEM__UNMOUNT, NULL);
}
@@ -2506,21 +2553,22 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
char **name, void **value,
size_t *len)
{
- struct task_security_struct *tsec;
+ const struct cred *cred = current_cred();
+ const struct task_security_struct *tsec = cred->security;
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
- u32 newsid, clen;
+ u32 sid, 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,
+ sid = tsec->sid;
+ newsid = tsec->create_sid;
+
+ if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+ rc = security_transition_sid(sid, dsec->sid,
inode_mode_to_security_class(inode->i_mode),
&newsid);
if (rc) {
@@ -2623,21 +2671,25 @@ static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dent
static int selinux_inode_readlink(struct dentry *dentry)
{
- return dentry_has_perm(current, NULL, dentry, FILE__READ);
+ const struct cred *cred = current_cred();
+
+ return dentry_has_perm(cred, NULL, dentry, FILE__READ);
}
static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->inode_follow_link(dentry, nameidata);
if (rc)
return rc;
- return dentry_has_perm(current, NULL, dentry, FILE__READ);
+ return dentry_has_perm(cred, NULL, dentry, FILE__READ);
}
static int selinux_inode_permission(struct inode *inode, int mask)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->inode_permission(inode, mask);
@@ -2649,12 +2701,13 @@ static int selinux_inode_permission(struct inode *inode, int mask)
return 0;
}
- return inode_has_perm(current, inode,
- open_file_mask_to_av(inode->i_mode, mask), NULL);
+ return inode_has_perm(cred, inode,
+ file_mask_to_av(inode->i_mode, mask), NULL);
}
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->inode_setattr(dentry, iattr);
@@ -2666,18 +2719,22 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
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);
+ return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
- return dentry_has_perm(current, NULL, dentry, FILE__WRITE);
+ return dentry_has_perm(cred, NULL, dentry, FILE__WRITE);
}
static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
{
- return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
+ const struct cred *cred = current_cred();
+
+ return dentry_has_perm(cred, mnt, dentry, FILE__GETATTR);
}
static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
{
+ const struct cred *cred = current_cred();
+
if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof XATTR_SECURITY_PREFIX - 1)) {
if (!strcmp(name, XATTR_NAME_CAPS)) {
@@ -2692,18 +2749,17 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
/* Not an attribute we recognize, so just check the
ordinary setattr permission. */
- return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+ return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
}
static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- struct task_security_struct *tsec = current->security;
struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
struct superblock_security_struct *sbsec;
struct avc_audit_data ad;
- u32 newsid;
+ u32 newsid, sid = current_sid();
int rc = 0;
if (strcmp(name, XATTR_NAME_SELINUX))
@@ -2719,7 +2775,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = dentry;
- rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
+ rc = avc_has_perm(sid, isec->sid, isec->sclass,
FILE__RELABELFROM, &ad);
if (rc)
return rc;
@@ -2733,12 +2789,12 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
if (rc)
return rc;
- rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
+ rc = avc_has_perm(sid, newsid, isec->sclass,
FILE__RELABELTO, &ad);
if (rc)
return rc;
- rc = security_validate_transition(isec->sid, newsid, tsec->sid,
+ rc = security_validate_transition(isec->sid, newsid, sid,
isec->sclass);
if (rc)
return rc;
@@ -2778,12 +2834,16 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
{
- return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+ const struct cred *cred = current_cred();
+
+ return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);
}
static int selinux_inode_listxattr(struct dentry *dentry)
{
- return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+ const struct cred *cred = current_cred();
+
+ return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);
}
static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
@@ -2806,7 +2866,6 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
u32 size;
int error;
char *context = NULL;
- struct task_security_struct *tsec = current->security;
struct inode_security_struct *isec = inode->i_security;
if (strcmp(name, XATTR_SELINUX_SUFFIX))
@@ -2821,13 +2880,7 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
* and lack of permission just means that we fall back to the
* in-core context value, not a denial.
*/
- error = secondary_ops->capable(current, CAP_MAC_ADMIN);
- if (!error)
- error = avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY2,
- CAPABILITY2__MAC_ADMIN,
- 0,
- NULL);
+ error = selinux_capable(current, CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT);
if (!error)
error = security_sid_to_context_force(isec->sid, &context,
&size);
@@ -2894,6 +2947,7 @@ static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
static int selinux_revalidate_file_permission(struct file *file, int mask)
{
+ const struct cred *cred = current_cred();
int rc;
struct inode *inode = file->f_path.dentry->d_inode;
@@ -2906,7 +2960,7 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
mask |= MAY_APPEND;
- rc = file_has_perm(current, file,
+ rc = file_has_perm(cred, file,
file_mask_to_av(inode->i_mode, mask));
if (rc)
return rc;
@@ -2917,16 +2971,16 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
static int selinux_file_permission(struct file *file, int mask)
{
struct inode *inode = file->f_path.dentry->d_inode;
- struct task_security_struct *tsec = current->security;
struct file_security_struct *fsec = file->f_security;
struct inode_security_struct *isec = inode->i_security;
+ u32 sid = current_sid();
if (!mask) {
/* No permission to check. Existence test. */
return 0;
}
- if (tsec->sid == fsec->sid && fsec->isid == isec->sid
+ if (sid == fsec->sid && fsec->isid == isec->sid
&& fsec->pseqno == avc_policy_seqno())
return selinux_netlbl_inode_permission(inode, mask);
@@ -2946,6 +3000,7 @@ static void selinux_file_free_security(struct file *file)
static int selinux_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
+ const struct cred *cred = current_cred();
u32 av = 0;
if (_IOC_DIR(cmd) & _IOC_WRITE)
@@ -2955,11 +3010,14 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
if (!av)
av = FILE__IOCTL;
- return file_has_perm(current, file, av);
+ return file_has_perm(cred, file, av);
}
static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
{
+ const struct cred *cred = current_cred();
+ int rc = 0;
+
#ifndef CONFIG_PPC32
if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
/*
@@ -2967,9 +3025,9 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
* private file mapping that will also be writable.
* This has an additional check.
*/
- int rc = task_has_perm(current, current, PROCESS__EXECMEM);
+ rc = cred_has_perm(cred, cred, PROCESS__EXECMEM);
if (rc)
- return rc;
+ goto error;
}
#endif
@@ -2984,9 +3042,11 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
if (prot & PROT_EXEC)
av |= FILE__EXECUTE;
- return file_has_perm(current, file, av);
+ return file_has_perm(cred, file, av);
}
- return 0;
+
+error:
+ return rc;
}
static int selinux_file_mmap(struct file *file, unsigned long reqprot,
@@ -2994,7 +3054,7 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
unsigned long addr, unsigned long addr_only)
{
int rc = 0;
- u32 sid = ((struct task_security_struct *)(current->security))->sid;
+ u32 sid = current_sid();
if (addr < mmap_min_addr)
rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
@@ -3013,6 +3073,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long reqprot,
unsigned long prot)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->file_mprotect(vma, reqprot, prot);
@@ -3027,12 +3088,11 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
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);
+ rc = cred_has_perm(cred, cred, 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);
+ rc = current_has_perm(current, PROCESS__EXECSTACK);
} else if (vma->vm_file && vma->anon_vma) {
/*
* We are making executable a file mapping that has
@@ -3041,8 +3101,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
* modified content. This typically should only
* occur for text relocations.
*/
- rc = file_has_perm(current, vma->vm_file,
- FILE__EXECMOD);
+ rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
}
if (rc)
return rc;
@@ -3054,12 +3113,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
static int selinux_file_lock(struct file *file, unsigned int cmd)
{
- return file_has_perm(current, file, FILE__LOCK);
+ const struct cred *cred = current_cred();
+
+ return file_has_perm(cred, file, FILE__LOCK);
}
static int selinux_file_fcntl(struct file *file, unsigned int cmd,
unsigned long arg)
{
+ const struct cred *cred = current_cred();
int err = 0;
switch (cmd) {
@@ -3070,7 +3132,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
}
if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) {
- err = file_has_perm(current, file, FILE__WRITE);
+ err = file_has_perm(cred, file, FILE__WRITE);
break;
}
/* fall through */
@@ -3080,7 +3142,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
case F_GETOWN:
case F_GETSIG:
/* Just check FD__USE permission */
- err = file_has_perm(current, file, 0);
+ err = file_has_perm(cred, file, 0);
break;
case F_GETLK:
case F_SETLK:
@@ -3094,7 +3156,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
err = -EINVAL;
break;
}
- err = file_has_perm(current, file, FILE__LOCK);
+ err = file_has_perm(cred, file, FILE__LOCK);
break;
}
@@ -3103,12 +3165,10 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
static int selinux_file_set_fowner(struct file *file)
{
- struct task_security_struct *tsec;
struct file_security_struct *fsec;
- tsec = current->security;
fsec = file->f_security;
- fsec->fown_sid = tsec->sid;
+ fsec->fown_sid = current_sid();
return 0;
}
@@ -3117,14 +3177,13 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int signum)
{
struct file *file;
+ u32 sid = current_sid();
u32 perm;
- struct task_security_struct *tsec;
struct file_security_struct *fsec;
/* struct fown_struct is never outside the context of a struct file */
file = container_of(fown, struct file, f_owner);
- tsec = tsk->security;
fsec = file->f_security;
if (!signum)
@@ -3132,20 +3191,23 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
else
perm = signal_to_av(signum);
- return avc_has_perm(fsec->fown_sid, tsec->sid,
+ return avc_has_perm(fsec->fown_sid, sid,
SECCLASS_PROCESS, perm, NULL);
}
static int selinux_file_receive(struct file *file)
{
- return file_has_perm(current, file, file_to_av(file));
+ const struct cred *cred = current_cred();
+
+ return file_has_perm(cred, file, file_to_av(file));
}
-static int selinux_dentry_open(struct file *file)
+static int selinux_dentry_open(struct file *file, const struct cred *cred)
{
struct file_security_struct *fsec;
struct inode *inode;
struct inode_security_struct *isec;
+
inode = file->f_path.dentry->d_inode;
fsec = file->f_security;
isec = inode->i_security;
@@ -3166,7 +3228,7 @@ static int selinux_dentry_open(struct file *file)
* new inode label or new policy.
* This check is not redundant - do not remove.
*/
- return inode_has_perm(current, inode, file_to_av(file), NULL);
+ return inode_has_perm(cred, inode, open_file_to_av(file), NULL);
}
/* task security operations */
@@ -3179,36 +3241,88 @@ static int selinux_task_create(unsigned long clone_flags)
if (rc)
return rc;
- return task_has_perm(current, current, PROCESS__FORK);
+ return current_has_perm(current, PROCESS__FORK);
}
-static int selinux_task_alloc_security(struct task_struct *tsk)
+/*
+ * detach and free the LSM part of a set of credentials
+ */
+static void selinux_cred_free(struct cred *cred)
{
- struct task_security_struct *tsec1, *tsec2;
- int rc;
-
- tsec1 = current->security;
+ struct task_security_struct *tsec = cred->security;
+ cred->security = NULL;
+ kfree(tsec);
+}
- rc = task_alloc_security(tsk);
- if (rc)
- return rc;
- tsec2 = tsk->security;
+/*
+ * prepare a new set of credentials for modification
+ */
+static int selinux_cred_prepare(struct cred *new, const struct cred *old,
+ gfp_t gfp)
+{
+ const struct task_security_struct *old_tsec;
+ struct task_security_struct *tsec;
- tsec2->osid = tsec1->osid;
- tsec2->sid = tsec1->sid;
+ old_tsec = old->security;
- /* 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;
+ tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
+ if (!tsec)
+ return -ENOMEM;
+ new->security = tsec;
return 0;
}
-static void selinux_task_free_security(struct task_struct *tsk)
+/*
+ * commit new credentials
+ */
+static void selinux_cred_commit(struct cred *new, const struct cred *old)
+{
+ secondary_ops->cred_commit(new, old);
+}
+
+/*
+ * set the security data for a kernel service
+ * - all the creation contexts are set to unlabelled
+ */
+static int selinux_kernel_act_as(struct cred *new, u32 secid)
{
- task_free_security(tsk);
+ struct task_security_struct *tsec = new->security;
+ u32 sid = current_sid();
+ int ret;
+
+ ret = avc_has_perm(sid, secid,
+ SECCLASS_KERNEL_SERVICE,
+ KERNEL_SERVICE__USE_AS_OVERRIDE,
+ NULL);
+ if (ret == 0) {
+ tsec->sid = secid;
+ tsec->create_sid = 0;
+ tsec->keycreate_sid = 0;
+ tsec->sockcreate_sid = 0;
+ }
+ return ret;
+}
+
+/*
+ * set the file creation context in a security record to the same as the
+ * objective context of the specified inode
+ */
+static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+ struct task_security_struct *tsec = new->security;
+ u32 sid = current_sid();
+ int ret;
+
+ ret = avc_has_perm(sid, isec->sid,
+ SECCLASS_KERNEL_SERVICE,
+ KERNEL_SERVICE__CREATE_FILES_AS,
+ NULL);
+
+ if (ret == 0)
+ tsec->create_sid = isec->sid;
+ return 0;
}
static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
@@ -3222,9 +3336,10 @@ static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
return 0;
}
-static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+static int selinux_task_fix_setuid(struct cred *new, const struct cred *old,
+ int flags)
{
- return secondary_ops->task_post_setuid(id0, id1, id2, flags);
+ return secondary_ops->task_fix_setuid(new, old, flags);
}
static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
@@ -3235,23 +3350,22 @@ static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{
- return task_has_perm(current, p, PROCESS__SETPGID);
+ return current_has_perm(p, PROCESS__SETPGID);
}
static int selinux_task_getpgid(struct task_struct *p)
{
- return task_has_perm(current, p, PROCESS__GETPGID);
+ return current_has_perm(p, PROCESS__GETPGID);
}
static int selinux_task_getsid(struct task_struct *p)
{
- return task_has_perm(current, p, PROCESS__GETSESSION);
+ return current_has_perm(p, PROCESS__GETSESSION);
}
static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
{
- struct task_security_struct *tsec = p->security;
- *secid = tsec->sid;
+ *secid = task_sid(p);
}
static int selinux_task_setgroups(struct group_info *group_info)
@@ -3268,7 +3382,7 @@ static int selinux_task_setnice(struct task_struct *p, int nice)
if (rc)
return rc;
- return task_has_perm(current, p, PROCESS__SETSCHED);
+ return current_has_perm(p, PROCESS__SETSCHED);
}
static int selinux_task_setioprio(struct task_struct *p, int ioprio)
@@ -3279,12 +3393,12 @@ static int selinux_task_setioprio(struct task_struct *p, int ioprio)
if (rc)
return rc;
- return task_has_perm(current, p, PROCESS__SETSCHED);
+ return current_has_perm(p, PROCESS__SETSCHED);
}
static int selinux_task_getioprio(struct task_struct *p)
{
- return task_has_perm(current, p, PROCESS__GETSCHED);
+ return current_has_perm(p, PROCESS__GETSCHED);
}
static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
@@ -3299,9 +3413,9 @@ static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim
/* Control the ability to change the hard limit (whether
lowering or raising it), so that the hard limit can
later be used as a safe reset point for the soft limit
- upon context transitions. See selinux_bprm_apply_creds. */
+ upon context transitions. See selinux_bprm_committing_creds. */
if (old_rlim->rlim_max != new_rlim->rlim_max)
- return task_has_perm(current, current, PROCESS__SETRLIMIT);
+ return current_has_perm(current, PROCESS__SETRLIMIT);
return 0;
}
@@ -3314,17 +3428,17 @@ static int selinux_task_setscheduler(struct task_struct *p, int policy, struct s
if (rc)
return rc;
- return task_has_perm(current, p, PROCESS__SETSCHED);
+ return current_has_perm(p, PROCESS__SETSCHED);
}
static int selinux_task_getscheduler(struct task_struct *p)
{
- return task_has_perm(current, p, PROCESS__GETSCHED);
+ return current_has_perm(p, PROCESS__GETSCHED);
}
static int selinux_task_movememory(struct task_struct *p)
{
- return task_has_perm(current, p, PROCESS__SETSCHED);
+ return current_has_perm(p, PROCESS__SETSCHED);
}
static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
@@ -3332,7 +3446,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
{
u32 perm;
int rc;
- struct task_security_struct *tsec;
rc = secondary_ops->task_kill(p, info, sig, secid);
if (rc)
@@ -3342,11 +3455,11 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
perm = PROCESS__SIGNULL; /* null signal; existence test */
else
perm = signal_to_av(sig);
- tsec = p->security;
if (secid)
- rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
+ rc = avc_has_perm(secid, task_sid(p),
+ SECCLASS_PROCESS, perm, NULL);
else
- rc = task_has_perm(current, p, perm);
+ rc = current_has_perm(p, perm);
return rc;
}
@@ -3354,13 +3467,12 @@ static int selinux_task_prctl(int option,
unsigned long arg2,
unsigned long arg3,
unsigned long arg4,
- unsigned long arg5,
- long *rc_p)
+ unsigned long arg5)
{
/* The current prctl operations do not appear to require
any SELinux controls since they merely observe or modify
the state of the current process. */
- return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
+ return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5);
}
static int selinux_task_wait(struct task_struct *p)
@@ -3368,27 +3480,14 @@ static int selinux_task_wait(struct task_struct *p)
return task_has_perm(p, current, PROCESS__SIGCHLD);
}
-static void selinux_task_reparent_to_init(struct task_struct *p)
-{
- struct task_security_struct *tsec;
-
- secondary_ops->task_reparent_to_init(p);
-
- tsec = p->security;
- tsec->osid = tsec->sid;
- tsec->sid = SECINITSID_KERNEL;
- return;
-}
-
static void selinux_task_to_inode(struct task_struct *p,
struct inode *inode)
{
- struct task_security_struct *tsec = p->security;
struct inode_security_struct *isec = inode->i_security;
+ u32 sid = task_sid(p);
- isec->sid = tsec->sid;
+ isec->sid = sid;
isec->initialized = 1;
- return;
}
/* Returns error only if unable to parse addresses */
@@ -3627,19 +3726,19 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
u32 perms)
{
struct inode_security_struct *isec;
- struct task_security_struct *tsec;
struct avc_audit_data ad;
+ u32 sid;
int err = 0;
- tsec = task->security;
isec = SOCK_INODE(sock)->i_security;
if (isec->sid == SECINITSID_KERNEL)
goto out;
+ sid = task_sid(task);
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = sock->sk;
- err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+ err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
out:
return err;
@@ -3648,18 +3747,20 @@ out:
static int selinux_socket_create(int family, int type,
int protocol, int kern)
{
+ const struct cred *cred = current_cred();
+ const struct task_security_struct *tsec = cred->security;
+ u32 sid, newsid;
+ u16 secclass;
int err = 0;
- struct task_security_struct *tsec;
- u32 newsid;
if (kern)
goto out;
- tsec = current->security;
- newsid = tsec->sockcreate_sid ? : tsec->sid;
- err = avc_has_perm(tsec->sid, newsid,
- socket_type_to_security_class(family, type,
- protocol), SOCKET__CREATE, NULL);
+ sid = tsec->sid;
+ newsid = tsec->sockcreate_sid ?: sid;
+
+ secclass = socket_type_to_security_class(family, type, protocol);
+ err = avc_has_perm(sid, newsid, secclass, SOCKET__CREATE, NULL);
out:
return err;
@@ -3668,18 +3769,26 @@ out:
static int selinux_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern)
{
- int err = 0;
+ const struct cred *cred = current_cred();
+ const struct task_security_struct *tsec = cred->security;
struct inode_security_struct *isec;
- struct task_security_struct *tsec;
struct sk_security_struct *sksec;
- u32 newsid;
+ u32 sid, newsid;
+ int err = 0;
+
+ sid = tsec->sid;
+ newsid = tsec->sockcreate_sid;
isec = SOCK_INODE(sock)->i_security;
- tsec = current->security;
- newsid = tsec->sockcreate_sid ? : tsec->sid;
+ if (kern)
+ isec->sid = SECINITSID_KERNEL;
+ else if (newsid)
+ isec->sid = newsid;
+ else
+ isec->sid = sid;
+
isec->sclass = socket_type_to_security_class(family, type, protocol);
- isec->sid = kern ? SECINITSID_KERNEL : newsid;
isec->initialized = 1;
if (sock->sk) {
@@ -3714,7 +3823,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
if (family == PF_INET || family == PF_INET6) {
char *addrp;
struct inode_security_struct *isec;
- struct task_security_struct *tsec;
struct avc_audit_data ad;
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
@@ -3722,7 +3830,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
struct sock *sk = sock->sk;
u32 sid, node_perm;
- tsec = current->security;
isec = SOCK_INODE(sock)->i_security;
if (family == PF_INET) {
@@ -4387,7 +4494,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
"SELinux: unrecognized netlink message"
" type=%hu for sclass=%hu\n",
nlh->nlmsg_type, isec->sclass);
- if (!selinux_enforcing)
+ if (!selinux_enforcing || security_get_allow_unknown())
err = 0;
}
@@ -4628,7 +4735,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
* as fast and as clean as possible. */
if (selinux_compat_net || !selinux_policycap_netpeer)
return selinux_ip_postroute_compat(skb, ifindex, family);
-
+#ifdef CONFIG_XFRM
/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
* packet transformation so allow the packet to pass without any checks
* since we'll have another chance to perform access control checks
@@ -4637,7 +4744,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
* is NULL, in this case go ahead and apply access control. */
if (skb->dst != NULL && skb->dst->xfrm != NULL)
return NF_ACCEPT;
-
+#endif
secmark_active = selinux_secmark_enabled();
peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
if (!secmark_active && !peerlbl_active)
@@ -4763,15 +4870,16 @@ static int ipc_alloc_security(struct task_struct *task,
struct kern_ipc_perm *perm,
u16 sclass)
{
- struct task_security_struct *tsec = task->security;
struct ipc_security_struct *isec;
+ u32 sid;
isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
if (!isec)
return -ENOMEM;
+ sid = task_sid(task);
isec->sclass = sclass;
- isec->sid = tsec->sid;
+ isec->sid = sid;
perm->security = isec;
return 0;
@@ -4809,17 +4917,16 @@ static void msg_msg_free_security(struct msg_msg *msg)
static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
u32 perms)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
- tsec = current->security;
isec = ipc_perms->security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = ipc_perms->key;
- return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+ return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
}
static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
@@ -4835,22 +4942,21 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
/* message queue security operations */
static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
int rc;
rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ);
if (rc)
return rc;
- tsec = current->security;
isec = msq->q_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
MSGQ__CREATE, &ad);
if (rc) {
ipc_free_security(&msq->q_perm);
@@ -4866,17 +4972,16 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)
static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
- tsec = current->security;
isec = msq->q_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
- return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
MSGQ__ASSOCIATE, &ad);
}
@@ -4910,13 +5015,12 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
int rc;
- tsec = current->security;
isec = msq->q_perm.security;
msec = msg->security;
@@ -4928,9 +5032,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
* Compute new sid based on current process and
* message queue this message will be stored in
*/
- rc = security_transition_sid(tsec->sid,
- isec->sid,
- SECCLASS_MSG,
+ rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG,
&msec->sid);
if (rc)
return rc;
@@ -4940,16 +5042,16 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
ad.u.ipc_id = msq->q_perm.key;
/* Can this process write to the queue? */
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
MSGQ__WRITE, &ad);
if (!rc)
/* Can this process send the message */
- rc = avc_has_perm(tsec->sid, msec->sid,
- SECCLASS_MSG, MSG__SEND, &ad);
+ rc = avc_has_perm(sid, msec->sid, 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, &ad);
+ rc = avc_has_perm(msec->sid, isec->sid, SECCLASS_MSGQ,
+ MSGQ__ENQUEUE, &ad);
return rc;
}
@@ -4958,23 +5060,22 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
struct task_struct *target,
long type, int mode)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
struct avc_audit_data ad;
+ u32 sid = task_sid(target);
int rc;
- tsec = target->security;
isec = msq->q_perm.security;
msec = msg->security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid,
+ rc = avc_has_perm(sid, isec->sid,
SECCLASS_MSGQ, MSGQ__READ, &ad);
if (!rc)
- rc = avc_has_perm(tsec->sid, msec->sid,
+ rc = avc_has_perm(sid, msec->sid,
SECCLASS_MSG, MSG__RECEIVE, &ad);
return rc;
}
@@ -4982,22 +5083,21 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
/* Shared Memory security operations */
static int selinux_shm_alloc_security(struct shmid_kernel *shp)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
int rc;
rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM);
if (rc)
return rc;
- tsec = current->security;
isec = shp->shm_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+ rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM,
SHM__CREATE, &ad);
if (rc) {
ipc_free_security(&shp->shm_perm);
@@ -5013,17 +5113,16 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)
static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
- tsec = current->security;
isec = shp->shm_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
- return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+ return avc_has_perm(sid, isec->sid, SECCLASS_SHM,
SHM__ASSOCIATE, &ad);
}
@@ -5081,22 +5180,21 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
/* Semaphore security operations */
static int selinux_sem_alloc_security(struct sem_array *sma)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
int rc;
rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM);
if (rc)
return rc;
- tsec = current->security;
isec = sma->sem_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+ rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM,
SEM__CREATE, &ad);
if (rc) {
ipc_free_security(&sma->sem_perm);
@@ -5112,17 +5210,16 @@ static void selinux_sem_free_security(struct sem_array *sma)
static int selinux_sem_associate(struct sem_array *sma, int semflg)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
- tsec = current->security;
isec = sma->sem_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
- return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+ return avc_has_perm(sid, isec->sid, SECCLASS_SEM,
SEM__ASSOCIATE, &ad);
}
@@ -5212,33 +5309,35 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
static int selinux_getprocattr(struct task_struct *p,
char *name, char **value)
{
- struct task_security_struct *tsec;
+ const struct task_security_struct *__tsec;
u32 sid;
int error;
unsigned len;
if (current != p) {
- error = task_has_perm(current, p, PROCESS__GETATTR);
+ error = current_has_perm(p, PROCESS__GETATTR);
if (error)
return error;
}
- tsec = p->security;
+ rcu_read_lock();
+ __tsec = __task_cred(p)->security;
if (!strcmp(name, "current"))
- sid = tsec->sid;
+ sid = __tsec->sid;
else if (!strcmp(name, "prev"))
- sid = tsec->osid;
+ sid = __tsec->osid;
else if (!strcmp(name, "exec"))
- sid = tsec->exec_sid;
+ sid = __tsec->exec_sid;
else if (!strcmp(name, "fscreate"))
- sid = tsec->create_sid;
+ sid = __tsec->create_sid;
else if (!strcmp(name, "keycreate"))
- sid = tsec->keycreate_sid;
+ sid = __tsec->keycreate_sid;
else if (!strcmp(name, "sockcreate"))
- sid = tsec->sockcreate_sid;
+ sid = __tsec->sockcreate_sid;
else
- return -EINVAL;
+ goto invalid;
+ rcu_read_unlock();
if (!sid)
return 0;
@@ -5247,6 +5346,10 @@ static int selinux_getprocattr(struct task_struct *p,
if (error)
return error;
return len;
+
+invalid:
+ rcu_read_unlock();
+ return -EINVAL;
}
static int selinux_setprocattr(struct task_struct *p,
@@ -5254,7 +5357,8 @@ static int selinux_setprocattr(struct task_struct *p,
{
struct task_security_struct *tsec;
struct task_struct *tracer;
- u32 sid = 0;
+ struct cred *new;
+ u32 sid = 0, ptsid;
int error;
char *str = value;
@@ -5270,15 +5374,15 @@ static int selinux_setprocattr(struct task_struct *p,
* above restriction is ever removed.
*/
if (!strcmp(name, "exec"))
- error = task_has_perm(current, p, PROCESS__SETEXEC);
+ error = current_has_perm(p, PROCESS__SETEXEC);
else if (!strcmp(name, "fscreate"))
- error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+ error = current_has_perm(p, PROCESS__SETFSCREATE);
else if (!strcmp(name, "keycreate"))
- error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
+ error = current_has_perm(p, PROCESS__SETKEYCREATE);
else if (!strcmp(name, "sockcreate"))
- error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
+ error = current_has_perm(p, PROCESS__SETSOCKCREATE);
else if (!strcmp(name, "current"))
- error = task_has_perm(current, p, PROCESS__SETCURRENT);
+ error = current_has_perm(p, PROCESS__SETCURRENT);
else
error = -EINVAL;
if (error)
@@ -5301,87 +5405,75 @@ static int selinux_setprocattr(struct task_struct *p,
return error;
}
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
/* Permission checking based on the specified context is
performed during the actual operation (execve,
open/mkdir/...), when we know the full context of the
- operation. See selinux_bprm_set_security for the execve
+ operation. See selinux_bprm_set_creds for the execve
checks and may_create for the file creation checks. The
operation will then fail if the context is not permitted. */
- tsec = p->security;
- if (!strcmp(name, "exec"))
+ tsec = new->security;
+ if (!strcmp(name, "exec")) {
tsec->exec_sid = sid;
- else if (!strcmp(name, "fscreate"))
+ } else if (!strcmp(name, "fscreate")) {
tsec->create_sid = sid;
- else if (!strcmp(name, "keycreate")) {
+ } else if (!strcmp(name, "keycreate")) {
error = may_create_key(sid, p);
if (error)
- return error;
+ goto abort_change;
tsec->keycreate_sid = sid;
- } else if (!strcmp(name, "sockcreate"))
+ } else if (!strcmp(name, "sockcreate")) {
tsec->sockcreate_sid = sid;
- else if (!strcmp(name, "current")) {
- struct av_decision avd;
-
+ } else if (!strcmp(name, "current")) {
+ error = -EINVAL;
if (sid == 0)
- return -EINVAL;
- /*
- * SELinux allows to change context in the following case only.
- * - Single threaded processes.
- * - Multi threaded processes intend to change its context into
- * more restricted domain (defined by TYPEBOUNDS statement).
- */
- 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);
- error = security_bounded_transition(tsec->sid, sid);
- if (!error)
- goto boundary_ok;
-
- return error;
- }
- } while_each_thread(g, t);
- read_unlock(&tasklist_lock);
+ goto abort_change;
+
+ /* Only allow single threaded processes to change context */
+ error = -EPERM;
+ if (!is_single_threaded(p)) {
+ error = security_bounded_transition(tsec->sid, sid);
+ if (error)
+ goto abort_change;
}
-boundary_ok:
/* Check permissions for the transition. */
error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
PROCESS__DYNTRANSITION, NULL);
if (error)
- return error;
+ goto abort_change;
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
+ ptsid = 0;
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, 0, &avd);
- if (!error)
- tsec->sid = sid;
- task_unlock(p);
- avc_audit(ptsid, sid, SECCLASS_PROCESS,
- PROCESS__PTRACE, &avd, error, NULL);
+ if (tracer)
+ ptsid = task_sid(tracer);
+ task_unlock(p);
+
+ if (tracer) {
+ error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
+ PROCESS__PTRACE, NULL);
if (error)
- return error;
- } else {
- rcu_read_unlock();
- tsec->sid = sid;
- task_unlock(p);
+ goto abort_change;
}
- } else
- return -EINVAL;
+ tsec->sid = sid;
+ } else {
+ error = -EINVAL;
+ goto abort_change;
+ }
+
+ commit_creds(new);
return size;
+
+abort_change:
+ abort_creds(new);
+ return error;
}
static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
@@ -5401,22 +5493,23 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
#ifdef CONFIG_KEYS
-static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
+static int selinux_key_alloc(struct key *k, const struct cred *cred,
unsigned long flags)
{
- struct task_security_struct *tsec = tsk->security;
+ const struct task_security_struct *tsec;
struct key_security_struct *ksec;
ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
if (!ksec)
return -ENOMEM;
+ tsec = cred->security;
if (tsec->keycreate_sid)
ksec->sid = tsec->keycreate_sid;
else
ksec->sid = tsec->sid;
- k->security = ksec;
+ k->security = ksec;
return 0;
}
@@ -5429,17 +5522,12 @@ static void selinux_key_free(struct key *k)
}
static int selinux_key_permission(key_ref_t key_ref,
- struct task_struct *ctx,
- key_perm_t perm)
+ const struct cred *cred,
+ 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;
+ u32 sid;
/* if no specific permissions are requested, we skip the
permission check. No serious, additional covert channels
@@ -5447,8 +5535,12 @@ static int selinux_key_permission(key_ref_t key_ref,
if (perm == 0)
return 0;
- return avc_has_perm(tsec->sid, ksec->sid,
- SECCLASS_KEY, perm, NULL);
+ sid = cred_sid(cred);
+
+ key = key_ref_to_ptr(key_ref);
+ ksec = key->security;
+
+ return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL);
}
static int selinux_key_getsecurity(struct key *key, char **_buffer)
@@ -5473,8 +5565,7 @@ static struct security_operations selinux_ops = {
.ptrace_may_access = selinux_ptrace_may_access,
.ptrace_traceme = selinux_ptrace_traceme,
.capget = selinux_capget,
- .capset_check = selinux_capset_check,
- .capset_set = selinux_capset_set,
+ .capset = selinux_capset,
.sysctl = selinux_sysctl,
.capable = selinux_capable,
.quotactl = selinux_quotactl,
@@ -5485,12 +5576,10 @@ static struct security_operations selinux_ops = {
.netlink_send = selinux_netlink_send,
.netlink_recv = selinux_netlink_recv,
- .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_set_creds = selinux_bprm_set_creds,
.bprm_check_security = selinux_bprm_check_security,
+ .bprm_committing_creds = selinux_bprm_committing_creds,
+ .bprm_committed_creds = selinux_bprm_committed_creds,
.bprm_secureexec = selinux_bprm_secureexec,
.sb_alloc_security = selinux_sb_alloc_security,
@@ -5549,10 +5638,13 @@ static struct security_operations selinux_ops = {
.dentry_open = selinux_dentry_open,
.task_create = selinux_task_create,
- .task_alloc_security = selinux_task_alloc_security,
- .task_free_security = selinux_task_free_security,
+ .cred_free = selinux_cred_free,
+ .cred_prepare = selinux_cred_prepare,
+ .cred_commit = selinux_cred_commit,
+ .kernel_act_as = selinux_kernel_act_as,
+ .kernel_create_files_as = selinux_kernel_create_files_as,
.task_setuid = selinux_task_setuid,
- .task_post_setuid = selinux_task_post_setuid,
+ .task_fix_setuid = selinux_task_fix_setuid,
.task_setgid = selinux_task_setgid,
.task_setpgid = selinux_task_setpgid,
.task_getpgid = selinux_task_getpgid,
@@ -5569,7 +5661,6 @@ static struct security_operations selinux_ops = {
.task_kill = selinux_task_kill,
.task_wait = selinux_task_wait,
.task_prctl = selinux_task_prctl,
- .task_reparent_to_init = selinux_task_reparent_to_init,
.task_to_inode = selinux_task_to_inode,
.ipc_permission = selinux_ipc_permission,
@@ -5665,8 +5756,6 @@ static struct security_operations selinux_ops = {
static __init int selinux_init(void)
{
- struct task_security_struct *tsec;
-
if (!security_module_enable(&selinux_ops)) {
selinux_enabled = 0;
return 0;
@@ -5680,10 +5769,7 @@ static __init int selinux_init(void)
printk(KERN_INFO "SELinux: Initializing.\n");
/* Set the security state for the initial task. */
- if (task_alloc_security(current))
- panic("SELinux: Failed to initialize initial task.\n");
- tsec = current->security;
- tsec->osid = tsec->sid = SECINITSID_KERNEL;
+ cred_init_security();
sel_inode_cache = kmem_cache_create("selinux_inode_security",
sizeof(struct inode_security_struct),