Skip to content

Commit

Permalink
CRED: Detach the credentials from task_struct
Browse files Browse the repository at this point in the history
Detach the credentials from task_struct, duplicating them in copy_process()
and releasing them in __put_task_struct().

Signed-off-by: David Howells <[email protected]>
Acked-by: James Morris <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: James Morris <[email protected]>
  • Loading branch information
dhowells authored and James Morris committed Nov 13, 2008
1 parent b6dff3e commit f1752ee
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 83 deletions.
29 changes: 29 additions & 0 deletions include/linux/cred.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,33 @@ do { \
*(_gid) = current->cred->fsgid; \
} while(0)

extern void __put_cred(struct cred *);
extern int copy_creds(struct task_struct *, unsigned long);

/**
* get_cred - Get a reference on a set of credentials
* @cred: The credentials to reference
*
* Get a reference on the specified set of credentials. The caller must
* release the reference.
*/
static inline struct cred *get_cred(struct cred *cred)
{
atomic_inc(&cred->usage);
return cred;
}

/**
* put_cred - Release a reference to a set of credentials
* @cred: The credentials to release
*
* Release a reference to a set of credentials, deleting them when the last ref
* is released.
*/
static inline void put_cred(struct cred *cred)
{
if (atomic_dec_and_test(&(cred)->usage))
__put_cred(cred);
}

#endif /* _LINUX_CRED_H */
16 changes: 1 addition & 15 deletions include/linux/init_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,19 +115,6 @@ extern struct group_info init_groups;

extern struct cred init_cred;

#define INIT_CRED(p) \
{ \
.usage = ATOMIC_INIT(3), \
.securebits = SECUREBITS_DEFAULT, \
.cap_inheritable = CAP_INIT_INH_SET, \
.cap_permitted = CAP_FULL_SET, \
.cap_effective = CAP_INIT_EFF_SET, \
.cap_bset = CAP_INIT_BSET, \
.user = INIT_USER, \
.group_info = &init_groups, \
.lock = __SPIN_LOCK_UNLOCKED(p.lock), \
}

/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x1fffff (=2MB)
Expand Down Expand Up @@ -162,8 +149,7 @@ extern struct cred init_cred;
.children = LIST_HEAD_INIT(tsk.children), \
.sibling = LIST_HEAD_INIT(tsk.sibling), \
.group_leader = &tsk, \
.__temp_cred = INIT_CRED(tsk.__temp_cred), \
.cred = &tsk.__temp_cred, \
.cred = &init_cred, \
.comm = "swapper", \
.thread = INIT_THREAD, \
.fs = &init_fs, \
Expand Down
1 change: 0 additions & 1 deletion include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,6 @@ struct task_struct {
struct list_head cpu_timers[3];

/* process credentials */
struct cred __temp_cred __deprecated; /* temporary credentials to be removed */
struct cred *cred; /* actual/objective task credentials */

char comm[TASK_COMM_LEN]; /* executable name excluding path
Expand Down
26 changes: 13 additions & 13 deletions include/linux/security.h
Original file line number Diff line number Diff line change
Expand Up @@ -593,15 +593,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* manual page for definitions of the @clone_flags.
* @clone_flags contains the flags indicating what should be shared.
* Return 0 if permission is granted.
* @task_alloc_security:
* @p contains the task_struct for child process.
* Allocate and attach a security structure to the p->security field. The
* security field is initialized to NULL when the task structure is
* @cred_alloc_security:
* @cred contains the cred struct for child process.
* Allocate and attach a security structure to the cred->security field.
* The security field is initialized to NULL when the task structure is
* allocated.
* Return 0 if operation was successful.
* @task_free_security:
* @p contains the task_struct for process.
* Deallocate and clear the p->security field.
* @cred_free:
* @cred points to the credentials.
* Deallocate and clear the cred->security field in a set of credentials.
* @task_setuid:
* Check permission before setting one or more of the user identity
* attributes of the current process. The @flags parameter indicates
Expand Down Expand Up @@ -1405,8 +1405,8 @@ struct security_operations {
int (*dentry_open) (struct file *file);

int (*task_create) (unsigned long clone_flags);
int (*task_alloc_security) (struct task_struct *p);
void (*task_free_security) (struct task_struct *p);
int (*cred_alloc_security) (struct cred *cred);
void (*cred_free) (struct cred *cred);
int (*task_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);
int (*task_post_setuid) (uid_t old_ruid /* or fsuid */ ,
uid_t old_euid, uid_t old_suid, int flags);
Expand Down Expand Up @@ -1660,8 +1660,8 @@ int security_file_send_sigiotask(struct task_struct *tsk,
int security_file_receive(struct file *file);
int security_dentry_open(struct file *file);
int security_task_create(unsigned long clone_flags);
int security_task_alloc(struct task_struct *p);
void security_task_free(struct task_struct *p);
int security_cred_alloc(struct cred *cred);
void security_cred_free(struct cred *cred);
int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags);
int security_task_post_setuid(uid_t old_ruid, uid_t old_euid,
uid_t old_suid, int flags);
Expand Down Expand Up @@ -2181,12 +2181,12 @@ static inline int security_task_create(unsigned long clone_flags)
return 0;
}

static inline int security_task_alloc(struct task_struct *p)
static inline int security_cred_alloc(struct cred *cred)
{
return 0;
}

static inline void security_task_free(struct task_struct *p)
static inline void security_cred_free(struct cred *cred)
{ }

static inline int security_task_setuid(uid_t id0, uid_t id1, uid_t id2,
Expand Down
2 changes: 1 addition & 1 deletion kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o \
rcupdate.o extable.o params.o posix-timers.o \
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
notifier.o ksysfs.o pm_qos_params.o sched_clock.o
notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o

CFLAGS_REMOVE_sched.o = -mno-spe

Expand Down
96 changes: 96 additions & 0 deletions kernel/cred.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* Task credentials management
*
* Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells ([email protected])
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/cred.h>
#include <linux/sched.h>
#include <linux/key.h>
#include <linux/keyctl.h>
#include <linux/init_task.h>
#include <linux/security.h>

/*
* The initial credentials for the initial task
*/
struct cred init_cred = {
.usage = ATOMIC_INIT(3),
.securebits = SECUREBITS_DEFAULT,
.cap_inheritable = CAP_INIT_INH_SET,
.cap_permitted = CAP_FULL_SET,
.cap_effective = CAP_INIT_EFF_SET,
.cap_bset = CAP_INIT_BSET,
.user = INIT_USER,
.group_info = &init_groups,
};

/*
* The RCU callback to actually dispose of a set of credentials
*/
static void put_cred_rcu(struct rcu_head *rcu)
{
struct cred *cred = container_of(rcu, struct cred, rcu);

BUG_ON(atomic_read(&cred->usage) != 0);

key_put(cred->thread_keyring);
key_put(cred->request_key_auth);
put_group_info(cred->group_info);
free_uid(cred->user);
security_cred_free(cred);
kfree(cred);
}

/**
* __put_cred - Destroy a set of credentials
* @sec: The record to release
*
* Destroy a set of credentials on which no references remain.
*/
void __put_cred(struct cred *cred)
{
call_rcu(&cred->rcu, put_cred_rcu);
}
EXPORT_SYMBOL(__put_cred);

/*
* Copy credentials for the new process created by fork()
*/
int copy_creds(struct task_struct *p, unsigned long clone_flags)
{
struct cred *pcred;
int ret;

pcred = kmemdup(p->cred, sizeof(*p->cred), GFP_KERNEL);
if (!pcred)
return -ENOMEM;

#ifdef CONFIG_SECURITY
pcred->security = NULL;
#endif

ret = security_cred_alloc(pcred);
if (ret < 0) {
kfree(pcred);
return ret;
}

atomic_set(&pcred->usage, 1);
get_group_info(pcred->group_info);
get_uid(pcred->user);
key_get(pcred->thread_keyring);
key_get(pcred->request_key_auth);

atomic_inc(&pcred->user->processes);

/* RCU assignment is unneeded here as no-one can have accessed this
* pointer yet, barring us */
p->cred = pcred;
return 0;
}
24 changes: 6 additions & 18 deletions kernel/fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,7 @@ void __put_task_struct(struct task_struct *tsk)
WARN_ON(atomic_read(&tsk->usage));
WARN_ON(tsk == current);

security_task_free(tsk);
free_uid(tsk->__temp_cred.user);
put_group_info(tsk->__temp_cred.group_info);
put_cred(tsk->cred);
delayacct_tsk_free(tsk);

if (!profile_handoff_task(tsk))
Expand Down Expand Up @@ -969,7 +967,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
p->cred = &p->__temp_cred;
retval = -EAGAIN;
if (atomic_read(&p->cred->user->processes) >=
p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
Expand All @@ -978,9 +975,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
goto bad_fork_free;
}

atomic_inc(&p->cred->user->__count);
atomic_inc(&p->cred->user->processes);
get_group_info(p->cred->group_info);
retval = copy_creds(p, clone_flags);
if (retval < 0)
goto bad_fork_free;

/*
* If multiple threads are within copy_process(), then this check
Expand Down Expand Up @@ -1035,9 +1032,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
do_posix_clock_monotonic_gettime(&p->start_time);
p->real_start_time = p->start_time;
monotonic_to_bootbased(&p->real_start_time);
#ifdef CONFIG_SECURITY
p->cred->security = NULL;
#endif
p->io_context = NULL;
p->audit_context = NULL;
cgroup_fork(p);
Expand Down Expand Up @@ -1082,10 +1076,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
/* Perform scheduler related setup. Assign this task to a CPU. */
sched_fork(p, clone_flags);

if ((retval = security_task_alloc(p)))
goto bad_fork_cleanup_policy;
if ((retval = audit_alloc(p)))
goto bad_fork_cleanup_security;
goto bad_fork_cleanup_policy;
/* copy all the process information */
if ((retval = copy_semundo(clone_flags, p)))
goto bad_fork_cleanup_audit;
Expand Down Expand Up @@ -1284,8 +1276,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
exit_sem(p);
bad_fork_cleanup_audit:
audit_free(p);
bad_fork_cleanup_security:
security_task_free(p);
bad_fork_cleanup_policy:
#ifdef CONFIG_NUMA
mpol_put(p->mempolicy);
Expand All @@ -1298,9 +1288,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
bad_fork_cleanup_put_domain:
module_put(task_thread_info(p)->exec_domain->module);
bad_fork_cleanup_count:
put_group_info(p->cred->group_info);
atomic_dec(&p->cred->user->processes);
free_uid(p->cred->user);
put_cred(p->cred);
bad_fork_free:
free_task(p);
fork_out:
Expand Down
8 changes: 4 additions & 4 deletions security/capability.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,12 @@ static int cap_task_create(unsigned long clone_flags)
return 0;
}

static int cap_task_alloc_security(struct task_struct *p)
static int cap_cred_alloc_security(struct cred *cred)
{
return 0;
}

static void cap_task_free_security(struct task_struct *p)
static void cap_cred_free(struct cred *cred)
{
}

Expand Down Expand Up @@ -890,8 +890,8 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, file_receive);
set_to_cap_if_null(ops, dentry_open);
set_to_cap_if_null(ops, task_create);
set_to_cap_if_null(ops, task_alloc_security);
set_to_cap_if_null(ops, task_free_security);
set_to_cap_if_null(ops, cred_alloc_security);
set_to_cap_if_null(ops, cred_free);
set_to_cap_if_null(ops, task_setuid);
set_to_cap_if_null(ops, task_post_setuid);
set_to_cap_if_null(ops, task_setgid);
Expand Down
8 changes: 4 additions & 4 deletions security/security.c
Original file line number Diff line number Diff line change
Expand Up @@ -616,14 +616,14 @@ int security_task_create(unsigned long clone_flags)
return security_ops->task_create(clone_flags);
}

int security_task_alloc(struct task_struct *p)
int security_cred_alloc(struct cred *cred)
{
return security_ops->task_alloc_security(p);
return security_ops->cred_alloc_security(cred);
}

void security_task_free(struct task_struct *p)
void security_cred_free(struct cred *cred)
{
security_ops->task_free_security(p);
security_ops->cred_free(cred);
}

int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
Expand Down
Loading

0 comments on commit f1752ee

Please sign in to comment.