[PATCH 02/24] CRED: Split the task security data and move part of itinto struct cred

From: David Howells
Date: Wed Sep 26 2007 - 10:22:34 EST


Move into the cred struct the part of the task security data that defines how a
task acts upon an object. The part that defines how something acts upon a task
remains attached to the task.

For SELinux this requires some of task_security_struct to be split off into
cred_security_struct which is then attached to struct cred. Note that the
contents of cred_security_struct may not be changed without the generation of a
new struct cred.

The split is as follows:

(*) create_sid, keycreate_sid and sockcreate_sid just move across.

(*) sid is split into victim_sid - which remains - and action_sid - which
migrates.

(*) osid, exec_sid and ptrace_sid remain.

victim_sid is the SID used to govern actions upon the task. action_sid is used
to govern actions made by the task.

When accessing the cred_security_struct of another process, RCU read procedures
must be observed.

Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
---

include/linux/cred.h | 1
include/linux/security.h | 33 ++
kernel/cred.c | 7 +
security/dummy.c | 11 +
security/selinux/exports.c | 6
security/selinux/hooks.c | 497 +++++++++++++++++++++++--------------
security/selinux/include/objsec.h | 16 +
security/selinux/selinuxfs.c | 8 -
security/selinux/xfrm.c | 6
9 files changed, 379 insertions(+), 206 deletions(-)

diff --git a/include/linux/cred.h b/include/linux/cred.h
index 0cc4400..7e35b2f 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -26,6 +26,7 @@ struct cred {
gid_t gid; /* fsgid as was */
struct rcu_head exterminate; /* cred destroyer */
struct group_info *group_info;
+ void *security;

/* caches for references to the three task keyrings
* - note that key_ref_t isn't typedef'd at this point, hence the odd
diff --git a/include/linux/security.h b/include/linux/security.h
index 1a15526..74cc204 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -504,6 +504,17 @@ struct request_sock;
* @file contains the file structure being received.
* Return 0 if permission is granted.
*
+ * Security hooks for credential structure operations.
+ *
+ * @cred_dup:
+ * Duplicate the credentials onto a duplicated cred structure.
+ * @cred points to the credentials structure. cred->security points to the
+ * security struct that was attached to the original cred struct, but it
+ * lacks a reference for the duplication if reference counting is needed.
+ * @cred_destroy:
+ * Destroy the credentials attached to a cred structure.
+ * @cred points to the credentials structure that is to be destroyed.
+ *
* Security hooks for task operations.
*
* @task_create:
@@ -1257,6 +1268,9 @@ struct security_operations {
struct fown_struct * fown, int sig);
int (*file_receive) (struct file * file);

+ int (*cred_dup)(struct cred *cred);
+ void (*cred_destroy)(struct cred *cred);
+
int (*task_create) (unsigned long clone_flags);
int (*task_alloc_security) (struct task_struct * p);
void (*task_free_security) (struct task_struct * p);
@@ -1864,6 +1878,16 @@ static inline int security_file_receive (struct file *file)
return security_ops->file_receive (file);
}

+static inline int security_cred_dup(struct cred *cred)
+{
+ return security_ops->cred_dup(cred);
+}
+
+static inline void security_cred_destroy(struct cred *cred)
+{
+ return security_ops->cred_destroy(cred);
+}
+
static inline int security_task_create (unsigned long clone_flags)
{
return security_ops->task_create (clone_flags);
@@ -2546,6 +2570,15 @@ static inline int security_file_receive (struct file *file)
return 0;
}

+static inline int security_cred_dup(struct cred *cred)
+{
+ return 0;
+}
+
+static inline void security_cred_destroy(struct cred *cred)
+{
+}
+
static inline int security_task_create (unsigned long clone_flags)
{
return 0;
diff --git a/kernel/cred.c b/kernel/cred.c
index 5b56b2b..9868eef 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -94,6 +94,12 @@ struct cred *dup_cred(const struct cred *pcred)
if (likely(cred)) {
*cred = *pcred;
atomic_set(&cred->usage, 1);
+
+ if (security_cred_dup(cred) < 0) {
+ kfree(cred);
+ return NULL;
+ }
+
get_group_info(cred->group_info);
#ifdef CONFIG_KEYS
key_get(key_ref_to_ptr(cred->session_keyring));
@@ -113,6 +119,7 @@ static void put_cred_rcu(struct rcu_head *rcu)
{
struct cred *cred = container_of(rcu, struct cred, exterminate);

+ security_cred_destroy(cred);
put_group_info(cred->group_info);
key_ref_put(cred->session_keyring);
key_ref_put(cred->process_keyring);
diff --git a/security/dummy.c b/security/dummy.c
index 62de89c..f535cc6 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -468,6 +468,15 @@ static int dummy_file_receive (struct file *file)
return 0;
}

+static int dummy_cred_dup(struct cred *cred)
+{
+ return 0;
+}
+
+static void dummy_cred_destroy(struct cred *cred)
+{
+}
+
static int dummy_task_create (unsigned long clone_flags)
{
return 0;
@@ -1038,6 +1047,8 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, file_set_fowner);
set_to_dummy_if_null(ops, file_send_sigiotask);
set_to_dummy_if_null(ops, file_receive);
+ set_to_dummy_if_null(ops, cred_dup);
+ set_to_dummy_if_null(ops, cred_destroy);
set_to_dummy_if_null(ops, task_create);
set_to_dummy_if_null(ops, task_alloc_security);
set_to_dummy_if_null(ops, task_free_security);
diff --git a/security/selinux/exports.c b/security/selinux/exports.c
index b6f9694..29cb87a 100644
--- a/security/selinux/exports.c
+++ b/security/selinux/exports.c
@@ -57,7 +57,7 @@ void selinux_get_task_sid(struct task_struct *tsk, u32 *sid)
{
if (selinux_enabled) {
struct task_security_struct *tsec = tsk->security;
- *sid = tsec->sid;
+ *sid = tsec->victim_sid;
return;
}
*sid = 0;
@@ -77,9 +77,9 @@ EXPORT_SYMBOL_GPL(selinux_string_to_sid);
int selinux_relabel_packet_permission(u32 sid)
{
if (selinux_enabled) {
- struct task_security_struct *tsec = current->security;
+ struct cred_security_struct *csec = current->cred->security;

- return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET,
+ return avc_has_perm(csec->action_sid, sid, SECCLASS_PACKET,
PACKET__RELABELTO, NULL);
}
return 0;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 0753b20..4e72dbb 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -162,7 +162,8 @@ static int task_alloc_security(struct task_struct *task)
return -ENOMEM;

tsec->task = task;
- tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
+ tsec->osid = tsec->victim_sid = tsec->ptrace_sid =
+ SECINITSID_UNLABELED;
task->security = tsec;

return 0;
@@ -177,7 +178,7 @@ static void task_free_security(struct task_struct *task)

static int inode_alloc_security(struct inode *inode)
{
- struct task_security_struct *tsec = current->security;
+ struct cred_security_struct *csec = current->cred->security;
struct inode_security_struct *isec;

isec = kmem_cache_zalloc(sel_inode_cache, GFP_KERNEL);
@@ -189,7 +190,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 = csec->action_sid;
inode->i_security = isec;

return 0;
@@ -211,7 +212,7 @@ static void inode_free_security(struct inode *inode)

static int file_alloc_security(struct file *file)
{
- struct task_security_struct *tsec = current->security;
+ struct cred_security_struct *csec = current->cred->security;
struct file_security_struct *fsec;

fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
@@ -219,8 +220,8 @@ static int file_alloc_security(struct file *file)
return -ENOMEM;

fsec->file = file;
- fsec->sid = tsec->sid;
- fsec->fown_sid = tsec->sid;
+ fsec->sid = csec->action_sid;
+ fsec->fown_sid = csec->action_sid;
file->f_security = fsec;

return 0;
@@ -335,26 +336,26 @@ static match_table_t tokens = {

static int may_context_mount_sb_relabel(u32 sid,
struct superblock_security_struct *sbsec,
- struct task_security_struct *tsec)
+ struct cred_security_struct *csec)
{
int rc;

- rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ rc = avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
FILESYSTEM__RELABELFROM, NULL);
if (rc)
return rc;

- rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
+ rc = avc_has_perm(csec->action_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)
+ struct cred_security_struct *csec)
{
int rc;
- rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ rc = avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
FILESYSTEM__RELABELFROM, NULL);
if (rc)
return rc;
@@ -371,7 +372,7 @@ static int try_context_mount(struct super_block *sb, void *data)
const char *name;
u32 sid;
int alloc = 0, rc = 0, seen = 0;
- struct task_security_struct *tsec = current->security;
+ struct cred_security_struct *csec = current->cred->security;
struct superblock_security_struct *sbsec = sb->s_security;

if (!data)
@@ -503,7 +504,7 @@ static int try_context_mount(struct super_block *sb, void *data)
goto out_free;
}

- rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+ rc = may_context_mount_sb_relabel(sid, sbsec, csec);
if (rc)
goto out_free;

@@ -525,12 +526,12 @@ static int try_context_mount(struct super_block *sb, void *data)
}

if (!fscontext) {
- rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+ rc = may_context_mount_sb_relabel(sid, sbsec, csec);
if (rc)
goto out_free;
sbsec->sid = sid;
} else {
- rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
+ rc = may_context_mount_inode_relabel(sid, sbsec, csec);
if (rc)
goto out_free;
}
@@ -550,7 +551,7 @@ static int try_context_mount(struct super_block *sb, void *data)
goto out_free;
}

- rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
+ rc = may_context_mount_inode_relabel(sid, sbsec, csec);
if (rc)
goto out_free;

@@ -570,7 +571,7 @@ static int try_context_mount(struct super_block *sb, void *data)
if (sid == sbsec->def_sid)
goto out_free;

- rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
+ rc = may_context_mount_inode_relabel(sid, sbsec, csec);
if (rc)
goto out_free;

@@ -1025,15 +1026,22 @@ static inline u32 signal_to_av(int sig)

/* 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,
+static int task_has_perm(struct task_struct *actor,
+ struct task_struct *victim,
u32 perms)
{
- struct task_security_struct *tsec1, *tsec2;
+ struct cred_security_struct *csec;
+ struct task_security_struct *tsec;
+ u32 action_sid;
+
+ /* the actor may not be the current task */
+ rcu_read_lock();
+ csec = task_cred(actor)->security;
+ action_sid = csec->action_sid;
+ rcu_read_unlock();

- tsec1 = tsk1->security;
- tsec2 = tsk2->security;
- return avc_has_perm(tsec1->sid, tsec2->sid,
+ tsec = victim->security;
+ return avc_has_perm(action_sid, tsec->victim_sid,
SECCLASS_PROCESS, perms, NULL);
}

@@ -1041,16 +1049,16 @@ static int task_has_perm(struct task_struct *tsk1,
static int task_has_capability(struct task_struct *tsk,
int cap)
{
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct avc_audit_data ad;

- tsec = tsk->security;
+ csec = tsk->cred->security;

AVC_AUDIT_DATA_INIT(&ad,CAP);
ad.tsk = tsk;
ad.u.cap = cap;

- return avc_has_perm(tsec->sid, tsec->sid,
+ return avc_has_perm(csec->action_sid, csec->action_sid,
SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
}

@@ -1058,11 +1066,11 @@ static int task_has_capability(struct task_struct *tsk,
static int task_has_system(struct task_struct *tsk,
u32 perms)
{
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;

- tsec = tsk->security;
+ csec = tsk->cred->security;

- return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
+ return avc_has_perm(csec->action_sid, SECINITSID_KERNEL,
SECCLASS_SYSTEM, perms, NULL);
}

@@ -1074,14 +1082,14 @@ static int inode_has_perm(struct task_struct *tsk,
u32 perms,
struct avc_audit_data *adp)
{
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct inode_security_struct *isec;
struct avc_audit_data ad;

if (unlikely (IS_PRIVATE (inode)))
return 0;

- tsec = tsk->security;
+ csec = tsk->cred->security;
isec = inode->i_security;

if (!adp) {
@@ -1090,7 +1098,8 @@ 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(csec->action_sid, isec->sid, isec->sclass, perms,
+ adp);
}

/* Same as inode_has_perm, but pass explicit audit data containing
@@ -1121,7 +1130,7 @@ static int file_has_perm(struct task_struct *tsk,
struct file *file,
u32 av)
{
- struct task_security_struct *tsec = tsk->security;
+ struct cred_security_struct *csec = tsk->cred->security;
struct file_security_struct *fsec = file->f_security;
struct vfsmount *mnt = file->f_path.mnt;
struct dentry *dentry = file->f_path.dentry;
@@ -1133,8 +1142,8 @@ static int file_has_perm(struct task_struct *tsk,
ad.u.fs.mnt = mnt;
ad.u.fs.dentry = dentry;

- if (tsec->sid != fsec->sid) {
- rc = avc_has_perm(tsec->sid, fsec->sid,
+ if (csec->action_sid != fsec->sid) {
+ rc = avc_has_perm(csec->action_sid, fsec->sid,
SECCLASS_FD,
FD__USE,
&ad);
@@ -1154,36 +1163,36 @@ static int may_create(struct inode *dir,
struct dentry *dentry,
u16 tclass)
{
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
u32 newsid;
struct avc_audit_data ad;
int rc;

- tsec = current->security;
+ csec = current->cred->security;
dsec = dir->i_security;
sbsec = dir->i_sb->s_security;

AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.dentry = dentry;

- rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
+ rc = avc_has_perm(csec->action_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;
+ if (csec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+ newsid = csec->create_sid;
} else {
- rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
- &newsid);
+ rc = security_transition_sid(csec->action_sid, dsec->sid,
+ tclass, &newsid);
if (rc)
return rc;
}

- rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
+ rc = avc_has_perm(csec->action_sid, newsid, tclass, FILE__CREATE, &ad);
if (rc)
return rc;

@@ -1196,11 +1205,12 @@ static int may_create(struct inode *dir,
static int may_create_key(u32 ksid,
struct task_struct *ctx)
{
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;

- tsec = ctx->security;
+ csec = ctx->cred->security;

- return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
+ return avc_has_perm(csec->action_sid, ksid, SECCLASS_KEY, KEY__CREATE,
+ NULL);
}

#define MAY_LINK 0
@@ -1213,13 +1223,13 @@ static int may_link(struct inode *dir,
int kind)

{
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct inode_security_struct *dsec, *isec;
struct avc_audit_data ad;
u32 av;
int rc;

- tsec = current->security;
+ csec = current->cred->security;
dsec = dir->i_security;
isec = dentry->d_inode->i_security;

@@ -1228,7 +1238,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(csec->action_sid, dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;

@@ -1247,7 +1257,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(csec->action_sid, isec->sid, isec->sclass, av, &ad);
return rc;
}

@@ -1256,14 +1266,14 @@ static inline int may_rename(struct inode *old_dir,
struct inode *new_dir,
struct dentry *new_dentry)
{
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
struct avc_audit_data ad;
u32 av;
int old_is_dir, new_is_dir;
int rc;

- tsec = current->security;
+ csec = current->cred->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);
@@ -1272,16 +1282,16 @@ static inline int may_rename(struct inode *old_dir,
AVC_AUDIT_DATA_INIT(&ad, FS);

ad.u.fs.dentry = old_dentry;
- rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
+ rc = avc_has_perm(csec->action_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(csec->action_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(csec->action_sid, old_isec->sid,
old_isec->sclass, DIR__REPARENT, &ad);
if (rc)
return rc;
@@ -1291,15 +1301,17 @@ 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(csec->action_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(csec->action_sid, new_isec->sid,
new_isec->sclass,
- (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
+ (new_is_dir ? DIR__RMDIR : FILE__UNLINK),
+ &ad);
if (rc)
return rc;
}
@@ -1313,12 +1325,12 @@ static int superblock_has_perm(struct task_struct *tsk,
u32 perms,
struct avc_audit_data *ad)
{
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct superblock_security_struct *sbsec;

- tsec = tsk->security;
+ csec = tsk->cred->security;
sbsec = sb->s_security;
- return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ return avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
perms, ad);
}

@@ -1371,7 +1383,7 @@ static inline u32 file_to_av(struct file *file)

static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
{
- struct task_security_struct *psec = parent->security;
+ struct cred_security_struct *psec;
struct task_security_struct *csec = child->security;
int rc;

@@ -1381,8 +1393,12 @@ static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)

rc = task_has_perm(parent, child, PROCESS__PTRACE);
/* Save the SID of the tracing process for later use in apply_creds. */
- if (!(child->ptrace & PT_PTRACED) && !rc)
- csec->ptrace_sid = psec->sid;
+ if (!(child->ptrace & PT_PTRACED) && !rc) {
+ rcu_read_lock();
+ psec = task_cred(parent)->security;
+ csec->ptrace_sid = psec->action_sid;
+ rcu_read_unlock();
+ }
return rc;
}

@@ -1472,7 +1488,7 @@ static int selinux_sysctl(ctl_table *table, int op)
{
int error = 0;
u32 av;
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
u32 tsid;
int rc;

@@ -1480,7 +1496,7 @@ static int selinux_sysctl(ctl_table *table, int op)
if (rc)
return rc;

- tsec = current->security;
+ csec = current->cred->security;

rc = selinux_sysctl_get_sid(table, (op == 0001) ?
SECCLASS_DIR : SECCLASS_FILE, &tsid);
@@ -1492,7 +1508,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(csec->action_sid, tsid,
SECCLASS_DIR, DIR__SEARCH, NULL);
} else {
av = 0;
@@ -1501,7 +1517,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(csec->action_sid, tsid,
SECCLASS_FILE, av, NULL);
}

@@ -1589,11 +1605,11 @@ 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;
+ struct cred_security_struct *csec = current->cred->security;

rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
if (rc == 0)
- rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
+ rc = avc_has_perm_noaudit(csec->action_sid, csec->action_sid,
SECCLASS_CAPABILITY,
CAP_TO_MASK(CAP_SYS_ADMIN),
0,
@@ -1626,6 +1642,7 @@ static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
static int selinux_bprm_set_security(struct linux_binprm *bprm)
{
struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct inode *inode = bprm->file->f_path.dentry->d_inode;
struct inode_security_struct *isec;
struct bprm_security_struct *bsec;
@@ -1643,15 +1660,16 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
return 0;

tsec = current->security;
+ csec = bprm->cred->security;
isec = inode->i_security;

/* Default to the current task SID. */
- bsec->sid = tsec->sid;
+ bsec->sid = csec->action_sid;

/* Reset fs, key, and sock SIDs on execve. */
- tsec->create_sid = 0;
- tsec->keycreate_sid = 0;
- tsec->sockcreate_sid = 0;
+ csec->create_sid = 0;
+ csec->keycreate_sid = 0;
+ csec->sockcreate_sid = 0;

if (tsec->exec_sid) {
newsid = tsec->exec_sid;
@@ -1659,7 +1677,7 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
tsec->exec_sid = 0;
} else {
/* Check for a default transition on this program. */
- rc = security_transition_sid(tsec->sid, isec->sid,
+ rc = security_transition_sid(csec->action_sid, isec->sid,
SECCLASS_PROCESS, &newsid);
if (rc)
return rc;
@@ -1670,16 +1688,16 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
ad.u.fs.dentry = bprm->file->f_path.dentry;

if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
- newsid = tsec->sid;
+ newsid = csec->action_sid;

- if (tsec->sid == newsid) {
- rc = avc_has_perm(tsec->sid, isec->sid,
+ if (csec->action_sid == newsid) {
+ rc = avc_has_perm(csec->action_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(csec->action_sid, newsid,
SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
if (rc)
return rc;
@@ -1711,11 +1729,11 @@ static int selinux_bprm_secureexec (struct linux_binprm *bprm)
struct task_security_struct *tsec = current->security;
int atsecure = 0;

- if (tsec->osid != tsec->sid) {
+ if (tsec->osid != tsec->victim_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,
+ atsecure = avc_has_perm(tsec->osid, tsec->victim_sid,
SECCLASS_PROCESS,
PROCESS__NOATSECURE, NULL);
}
@@ -1825,6 +1843,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
{
struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct bprm_security_struct *bsec;
u32 sid;
int rc;
@@ -1832,17 +1851,17 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
secondary_ops->bprm_apply_creds(bprm, unsafe);

tsec = current->security;
-
+ csec = bprm->cred->security;
bsec = bprm->security;
sid = bsec->sid;

- tsec->osid = tsec->sid;
+ tsec->osid = tsec->victim_sid;
bsec->unsafe = 0;
- if (tsec->sid != sid) {
+ if (tsec->victim_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,
+ rc = avc_has_perm(tsec->victim_sid, sid, SECCLASS_PROCESS,
PROCESS__SHARE, NULL);
if (rc) {
bsec->unsafe = 1;
@@ -1861,7 +1880,9 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
return;
}
}
- tsec->sid = sid;
+ if (csec->action_sid == tsec->victim_sid)
+ csec->action_sid = sid;
+ tsec->victim_sid = sid;
}
}

@@ -1883,7 +1904,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
force_sig_specific(SIGKILL, current);
return;
}
- if (tsec->osid == tsec->sid)
+ if (tsec->osid == tsec->victim_sid)
return;

/* Close files for which the new task SID is not authorized. */
@@ -1895,7 +1916,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
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,
+ rc = avc_has_perm(tsec->osid, tsec->victim_sid, SECCLASS_PROCESS,
PROCESS__SIGINH, NULL);
if (rc) {
memset(&itimer, 0, sizeof itimer);
@@ -1922,7 +1943,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
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,
+ rc = avc_has_perm(tsec->osid, tsec->victim_sid, SECCLASS_PROCESS,
PROCESS__RLIMITINH, NULL);
if (rc) {
for (i = 0; i < RLIM_NLIMITS; i++) {
@@ -2124,21 +2145,21 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
char **name, void **value,
size_t *len)
{
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
u32 newsid, clen;
int rc;
char *namep = NULL, *context;

- tsec = current->security;
+ csec = current->cred->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;
+ if (csec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+ newsid = csec->create_sid;
} else {
- rc = security_transition_sid(tsec->sid, dsec->sid,
+ rc = security_transition_sid(csec->action_sid, dsec->sid,
inode_mode_to_security_class(inode->i_mode),
&newsid);
if (rc) {
@@ -2297,7 +2318,7 @@ static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)

static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
{
- struct task_security_struct *tsec = current->security;
+ struct cred_security_struct *csec = current->cred->security;
struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
struct superblock_security_struct *sbsec;
@@ -2329,7 +2350,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
AVC_AUDIT_DATA_INIT(&ad,FS);
ad.u.fs.dentry = dentry;

- rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
+ rc = avc_has_perm(csec->action_sid, isec->sid, isec->sclass,
FILE__RELABELFROM, &ad);
if (rc)
return rc;
@@ -2338,12 +2359,12 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
if (rc)
return rc;

- rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
+ rc = avc_has_perm(csec->action_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, csec->action_sid,
isec->sclass);
if (rc)
return rc;
@@ -2577,8 +2598,9 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags,
unsigned long addr, unsigned long addr_only)
{
+ struct cred_security_struct *csec = current->cred->security;
int rc = 0;
- u32 sid = ((struct task_security_struct*)(current->security))->sid;
+ u32 sid = csec->action_sid;

if (addr < mmap_min_addr)
rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
@@ -2692,7 +2714,7 @@ static int selinux_file_set_fowner(struct file *file)

tsec = current->security;
fsec = file->f_security;
- fsec->fown_sid = tsec->sid;
+ fsec->fown_sid = tsec->victim_sid;

return 0;
}
@@ -2716,7 +2738,7 @@ 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, tsec->victim_sid,
SECCLASS_PROCESS, perm, NULL);
}

@@ -2725,6 +2747,31 @@ static int selinux_file_receive(struct file *file)
return file_has_perm(current, file, file_to_av(file));
}

+/* credential security operations */
+
+/*
+ * duplicate the security information attached to a credentials record that is
+ * itself undergoing duplication
+ */
+static int selinux_cred_dup(struct cred *cred)
+{
+ cred->security = kmemdup(cred->security,
+ sizeof(struct cred_security_struct),
+ GFP_KERNEL);
+ return cred->security ? 0 : -ENOMEM;
+}
+
+/*
+ * destroy the security information attached to a credentials record
+ * - this is done under RCU, and may not be associated with the task that set it
+ * up
+ */
+static void selinux_cred_destroy(struct cred *cred)
+{
+ kfree(cred->security);
+}
+
+
/* task security operations */

static int selinux_task_create(unsigned long clone_flags)
@@ -2751,13 +2798,10 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
tsec2 = tsk->security;

tsec2->osid = tsec1->osid;
- tsec2->sid = tsec1->sid;
+ tsec2->victim_sid = tsec1->victim_sid;

- /* Retain the exec, fs, key, and sock SIDs across fork */
+ /* Retain the exec SID 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;

/* Retain ptracer SID across fork, if any.
This will be reset by the ptrace hook upon any
@@ -2895,7 +2939,8 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
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, tsec->victim_sid,
+ SECCLASS_PROCESS, perm, NULL);
else
rc = task_has_perm(current, p, perm);
return rc;
@@ -2929,8 +2974,8 @@ static void selinux_task_reparent_to_init(struct task_struct *p)
secondary_ops->task_reparent_to_init(p);

tsec = p->security;
- tsec->osid = tsec->sid;
- tsec->sid = SECINITSID_KERNEL;
+ tsec->osid = tsec->victim_sid;
+ tsec->victim_sid = SECINITSID_KERNEL;
return;
}

@@ -2940,7 +2985,7 @@ static void selinux_task_to_inode(struct task_struct *p,
struct task_security_struct *tsec = p->security;
struct inode_security_struct *isec = inode->i_security;

- isec->sid = tsec->sid;
+ isec->sid = tsec->victim_sid;
isec->initialized = 1;
return;
}
@@ -3165,11 +3210,11 @@ 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 cred_security_struct *csec;
struct avc_audit_data ad;
int err = 0;

- tsec = task->security;
+ csec = task->cred->security;
isec = SOCK_INODE(sock)->i_security;

if (isec->sid == SECINITSID_KERNEL)
@@ -3177,7 +3222,8 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,

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(csec->action_sid, isec->sid, isec->sclass, perms,
+ &ad);

out:
return err;
@@ -3187,15 +3233,15 @@ static int selinux_socket_create(int family, int type,
int protocol, int kern)
{
int err = 0;
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
u32 newsid;

if (kern)
goto out;

- tsec = current->security;
- newsid = tsec->sockcreate_sid ? : tsec->sid;
- err = avc_has_perm(tsec->sid, newsid,
+ csec = current->cred->security;
+ newsid = csec->sockcreate_sid ? : csec->action_sid;
+ err = avc_has_perm(csec->action_sid, newsid,
socket_type_to_security_class(family, type,
protocol), SOCKET__CREATE, NULL);

@@ -3208,14 +3254,14 @@ static int selinux_socket_post_create(struct socket *sock, int family,
{
int err = 0;
struct inode_security_struct *isec;
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct sk_security_struct *sksec;
u32 newsid;

isec = SOCK_INODE(sock)->i_security;

- tsec = current->security;
- newsid = tsec->sockcreate_sid ? : tsec->sid;
+ csec = current->cred->security;
+ newsid = csec->sockcreate_sid ? : csec->action_sid;
isec->sclass = socket_type_to_security_class(family, type, protocol);
isec->sid = kern ? SECINITSID_KERNEL : newsid;
isec->initialized = 1;
@@ -4029,7 +4075,7 @@ static int ipc_alloc_security(struct task_struct *task,
struct kern_ipc_perm *perm,
u16 sclass)
{
- struct task_security_struct *tsec = task->security;
+ struct cred_security_struct *csec = task->cred->security;
struct ipc_security_struct *isec;

isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
@@ -4038,7 +4084,7 @@ static int ipc_alloc_security(struct task_struct *task,

isec->sclass = sclass;
isec->ipc_perm = perm;
- isec->sid = tsec->sid;
+ isec->sid = csec->action_sid;
perm->security = isec;

return 0;
@@ -4077,17 +4123,18 @@ 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 cred_security_struct *csec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;

- tsec = current->security;
+ csec = current->cred->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(csec->action_sid, isec->sid, isec->sclass, perms,
+ &ad);
}

static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
@@ -4103,7 +4150,7 @@ 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 cred_security_struct *csec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
int rc;
@@ -4112,13 +4159,13 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
if (rc)
return rc;

- tsec = current->security;
+ csec = current->cred->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(csec->action_sid, isec->sid, SECCLASS_MSGQ,
MSGQ__CREATE, &ad);
if (rc) {
ipc_free_security(&msq->q_perm);
@@ -4134,17 +4181,17 @@ 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 cred_security_struct *csec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;

- tsec = current->security;
+ csec = current->cred->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(csec->action_sid, isec->sid, SECCLASS_MSGQ,
MSGQ__ASSOCIATE, &ad);
}

@@ -4178,13 +4225,13 @@ 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 cred_security_struct *csec;
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
struct avc_audit_data ad;
int rc;

- tsec = current->security;
+ csec = current->cred->security;
isec = msq->q_perm.security;
msec = msg->security;

@@ -4196,7 +4243,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,
+ rc = security_transition_sid(csec->action_sid,
isec->sid,
SECCLASS_MSG,
&msec->sid);
@@ -4208,11 +4255,11 @@ 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(csec->action_sid, isec->sid, SECCLASS_MSGQ,
MSGQ__WRITE, &ad);
if (!rc)
/* Can this process send the message */
- rc = avc_has_perm(tsec->sid, msec->sid,
+ rc = avc_has_perm(csec->action_sid, msec->sid,
SECCLASS_MSG, MSG__SEND, &ad);
if (!rc)
/* Can the message be put in the queue? */
@@ -4239,10 +4286,10 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
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(tsec->victim_sid, isec->sid,
SECCLASS_MSGQ, MSGQ__READ, &ad);
if (!rc)
- rc = avc_has_perm(tsec->sid, msec->sid,
+ rc = avc_has_perm(tsec->victim_sid, msec->sid,
SECCLASS_MSG, MSG__RECEIVE, &ad);
return rc;
}
@@ -4250,7 +4297,7 @@ 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 cred_security_struct *csec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
int rc;
@@ -4259,13 +4306,13 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
if (rc)
return rc;

- tsec = current->security;
+ csec = current->cred->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(csec->action_sid, isec->sid, SECCLASS_SHM,
SHM__CREATE, &ad);
if (rc) {
ipc_free_security(&shp->shm_perm);
@@ -4281,17 +4328,17 @@ 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 cred_security_struct *csec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;

- tsec = current->security;
+ csec = current->cred->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(csec->action_sid, isec->sid, SECCLASS_SHM,
SHM__ASSOCIATE, &ad);
}

@@ -4349,7 +4396,7 @@ 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 cred_security_struct *csec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
int rc;
@@ -4358,13 +4405,13 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
if (rc)
return rc;

- tsec = current->security;
+ csec = current->cred->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(csec->action_sid, isec->sid, SECCLASS_SEM,
SEM__CREATE, &ad);
if (rc) {
ipc_free_security(&sma->sem_perm);
@@ -4380,17 +4427,17 @@ 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 cred_security_struct *csec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;

- tsec = current->security;
+ csec = current->cred->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(csec->action_sid, isec->sid, SECCLASS_SEM,
SEM__ASSOCIATE, &ad);
}

@@ -4506,6 +4553,7 @@ static int selinux_getprocattr(struct task_struct *p,
char *name, char **value)
{
struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
u32 sid;
int error;
unsigned len;
@@ -4517,22 +4565,25 @@ static int selinux_getprocattr(struct task_struct *p,
}

tsec = p->security;
+ rcu_read_lock();
+ csec = task_cred(p)->security;

if (!strcmp(name, "current"))
- sid = tsec->sid;
+ sid = tsec->victim_sid;
else if (!strcmp(name, "prev"))
sid = tsec->osid;
else if (!strcmp(name, "exec"))
sid = tsec->exec_sid;
else if (!strcmp(name, "fscreate"))
- sid = tsec->create_sid;
+ sid = csec->create_sid;
else if (!strcmp(name, "keycreate"))
- sid = tsec->keycreate_sid;
+ sid = csec->keycreate_sid;
else if (!strcmp(name, "sockcreate"))
- sid = tsec->sockcreate_sid;
+ sid = csec->sockcreate_sid;
else
- return -EINVAL;
+ goto invalid;

+ rcu_read_unlock();
if (!sid)
return 0;

@@ -4540,13 +4591,20 @@ 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,
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
- u32 sid = 0;
+ struct cred_security_struct *csec;
+ struct av_decision avd;
+ struct cred *cred;
+ u32 sid = 0, perm;
int error;
char *str = value;

@@ -4562,17 +4620,19 @@ 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);
+ perm = PROCESS__SETEXEC;
else if (!strcmp(name, "fscreate"))
- error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+ perm = PROCESS__SETFSCREATE;
else if (!strcmp(name, "keycreate"))
- error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
+ perm = PROCESS__SETKEYCREATE;
else if (!strcmp(name, "sockcreate"))
- error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
+ perm = PROCESS__SETSOCKCREATE;
else if (!strcmp(name, "current"))
- error = task_has_perm(current, p, PROCESS__SETCURRENT);
+ perm = PROCESS__SETCURRENT;
else
- error = -EINVAL;
+ return -EINVAL;
+
+ error = task_has_perm(current, p, perm);
if (error)
return error;

@@ -4594,20 +4654,37 @@ static int selinux_setprocattr(struct task_struct *p,
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"))
+ csec = p->cred->security;
+ switch (perm) {
+ case PROCESS__SETEXEC:
tsec->exec_sid = sid;
- else if (!strcmp(name, "fscreate"))
- tsec->create_sid = sid;
- else if (!strcmp(name, "keycreate")) {
+ break;
+
+ case PROCESS__SETKEYCREATE:
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;
+ case PROCESS__SETFSCREATE:
+ case PROCESS__SETSOCKCREATE:
+ cred = dup_cred(current->cred);
+ if (!cred)
+ return -ENOMEM;
+ csec = cred->security;
+ switch (perm) {
+ case PROCESS__SETKEYCREATE:
+ csec->keycreate_sid = sid;
+ break;
+ case PROCESS__SETFSCREATE:
+ csec->create_sid = sid;
+ break;
+ case PROCESS__SETSOCKCREATE:
+ csec->sockcreate_sid = sid;
+ break;
+ }
+ set_current_cred(cred);
+ break;

+ case PROCESS__SETCURRENT:
if (sid == 0)
return -EINVAL;

@@ -4626,11 +4703,16 @@ static int selinux_setprocattr(struct task_struct *p,
}

/* Check permissions for the transition. */
- error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+ error = avc_has_perm(csec->action_sid, sid, SECCLASS_PROCESS,
PROCESS__DYNTRANSITION, NULL);
if (error)
return error;

+ cred = dup_cred(current->cred);
+ if (!cred)
+ return -ENOMEM;
+ csec = cred->security;
+
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
task_lock(p);
@@ -4638,20 +4720,25 @@ static int selinux_setprocattr(struct task_struct *p,
error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
SECCLASS_PROCESS,
PROCESS__PTRACE, 0, &avd);
- if (!error)
- tsec->sid = sid;
+ if (!error) {
+ csec->action_sid = tsec->victim_sid = sid;
+ }
task_unlock(p);
avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, &avd, error, NULL);
- if (error)
+ if (error) {
+ put_cred(cred);
return error;
+ }
} else {
- tsec->sid = sid;
+ csec->action_sid = tsec->victim_sid = sid;
task_unlock(p);
}
- }
- else
+ set_current_cred(cred);
+ break;
+ default:
return -EINVAL;
+ }

return size;
}
@@ -4671,18 +4758,21 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
unsigned long flags)
{
- struct task_security_struct *tsec = tsk->security;
+ struct cred_security_struct *csec;
struct key_security_struct *ksec;

ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
if (!ksec)
return -ENOMEM;

+ rcu_read_lock();
+ csec = task_cred(tsk)->security;
ksec->obj = k;
- if (tsec->keycreate_sid)
- ksec->sid = tsec->keycreate_sid;
+ if (csec->keycreate_sid)
+ ksec->sid = csec->keycreate_sid;
else
- ksec->sid = tsec->sid;
+ ksec->sid = csec->action_sid;
+ rcu_read_unlock();
k->security = ksec;

return 0;
@@ -4697,17 +4787,13 @@ 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)
+ struct task_struct *ctx,
+ key_perm_t perm)
{
struct key *key;
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
struct key_security_struct *ksec;
-
- key = key_ref_to_ptr(key_ref);
-
- tsec = ctx->security;
- ksec = key->security;
+ u32 action_sid;

/* if no specific permissions are requested, we skip the
permission check. No serious, additional covert channels
@@ -4715,7 +4801,16 @@ static int selinux_key_permission(key_ref_t key_ref,
if (perm == 0)
return 0;

- return avc_has_perm(tsec->sid, ksec->sid,
+ key = key_ref_to_ptr(key_ref);
+
+ rcu_read_lock();
+ csec = task_cred(ctx)->security;
+ action_sid = csec->action_sid;
+ rcu_read_unlock();
+
+ ksec = key->security;
+
+ return avc_has_perm(action_sid, ksec->sid,
SECCLASS_KEY, perm, NULL);
}

@@ -4790,6 +4885,9 @@ static struct security_operations selinux_ops = {
.file_send_sigiotask = selinux_file_send_sigiotask,
.file_receive = selinux_file_receive,

+ .cred_dup = selinux_cred_dup,
+ .cred_destroy = selinux_cred_destroy,
+
.task_create = selinux_task_create,
.task_alloc_security = selinux_task_alloc_security,
.task_free_security = selinux_task_free_security,
@@ -4898,6 +4996,17 @@ static struct security_operations selinux_ops = {
#endif
};

+/*
+ * initial security credentials
+ * - attached to init_cred which is never released
+ */
+static struct cred_security_struct init_cred_sec = {
+ .action_sid = SECINITSID_KERNEL,
+ .create_sid = SECINITSID_UNLABELED,
+ .keycreate_sid = SECINITSID_UNLABELED,
+ .sockcreate_sid = SECINITSID_UNLABELED,
+};
+
static __init int selinux_init(void)
{
struct task_security_struct *tsec;
@@ -4909,11 +5018,15 @@ static __init int selinux_init(void)

printk(KERN_INFO "SELinux: Initializing.\n");

+ /* Set the security state for the initial credentials */
+ init_cred.security = &init_cred_sec;
+ BUG_ON(current->cred != &init_cred);
+
/* 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;
+ tsec->osid = tsec->victim_sid = SECINITSID_KERNEL;

sel_inode_cache = kmem_cache_create("selinux_inode_security",
sizeof(struct inode_security_struct),
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 91b88f0..a1dbc1c 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -27,14 +27,22 @@
#include "flask.h"
#include "avc.h"

+/*
+ * the security parameters associated with the credentials record structure
+ * (struct cred::security)
+ */
+struct cred_security_struct {
+ u32 action_sid; /* perform action as SID */
+ u32 create_sid; /* filesystem object creation as SID */
+ u32 keycreate_sid; /* key creation as SID */
+ u32 sockcreate_sid; /* socket creation as SID */
+};
+
struct task_security_struct {
struct task_struct *task; /* back pointer to task object */
u32 osid; /* SID prior to last execve */
- u32 sid; /* current SID */
+ u32 victim_sid; /* current SID affecting victimisation of this task */
u32 exec_sid; /* exec SID */
- u32 create_sid; /* fscreate SID */
- u32 keycreate_sid; /* keycreate SID */
- u32 sockcreate_sid; /* fscreate SID */
u32 ptrace_sid; /* SID of ptrace parent */
};

diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index c9e92da..9c6737f 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -77,13 +77,13 @@ extern void selnl_notify_setenforce(int val);
static int task_has_security(struct task_struct *tsk,
u32 perms)
{
- struct task_security_struct *tsec;
+ struct cred_security_struct *csec;

- tsec = tsk->security;
- if (!tsec)
+ csec = tsk->cred->security;
+ if (!csec)
return -EACCES;

- return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
+ return avc_has_perm(csec->action_sid, SECINITSID_SECURITY,
SECCLASS_SECURITY, perms, NULL);
}

diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index ba715f4..902d302 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -240,7 +240,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
/*
* Does the subject have permission to set security context?
*/
- rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
if (rc)
@@ -341,7 +341,7 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
int rc = 0;

if (ctx)
- rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);

@@ -383,7 +383,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
int rc = 0;

if (ctx)
- rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/