Skip to content

Commit

Permalink
CRED: Differentiate objective and effective subjective credentials on…
Browse files Browse the repository at this point in the history
… a task

Differentiate the objective and real subjective credentials from the effective
subjective credentials on a task by introducing a second credentials pointer
into the task_struct.

task_struct::real_cred then refers to the objective and apparent real
subjective credentials of a task, as perceived by the other tasks in the
system.

task_struct::cred then refers to the effective subjective credentials of a
task, as used by that task when it's actually running.  These are not visible
to the other tasks in the system.

__task_cred(task) then refers to the objective/real credentials of the task in
question.

current_cred() refers to the effective subjective credentials of the current
task.

prepare_creds() uses the objective creds as a base and commit_creds() changes
both pointers in the task_struct (indeed commit_creds() requires them to be the
same).

override_creds() and revert_creds() change the subjective creds pointer only,
and the former returns the old subjective creds.  These are used by NFSD,
faccessat() and do_coredump(), and will by used by CacheFiles.

In SELinux, current_has_perm() is provided as an alternative to
task_has_perm().  This uses the effective subjective context of current,
whereas task_has_perm() uses the objective/real context of the subject.

Signed-off-by: David Howells <[email protected]>
Signed-off-by: James Morris <[email protected]>
  • Loading branch information
dhowells authored and James Morris committed Nov 13, 2008
1 parent 98870ab commit 3b11a1d
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 54 deletions.
5 changes: 4 additions & 1 deletion fs/nfsd/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
int flags = nfsexp_flags(rqstp, exp);
int ret;

/* discard any old override before preparing the new set */
revert_creds(get_cred(current->real_cred));
new = prepare_creds();
if (!new)
return -ENOMEM;
Expand Down Expand Up @@ -82,7 +84,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
else
new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
new->cap_permitted);
return commit_creds(new);
put_cred(override_creds(new));
return 0;

oom:
ret = -ENOMEM;
Expand Down
29 changes: 15 additions & 14 deletions include/linux/cred.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ extern struct cred *prepare_exec_creds(void);
extern struct cred *prepare_usermodehelper_creds(void);
extern int commit_creds(struct cred *);
extern void abort_creds(struct cred *);
extern const struct cred *override_creds(const struct cred *) __deprecated;
extern void revert_creds(const struct cred *) __deprecated;
extern const struct cred *override_creds(const struct cred *);
extern void revert_creds(const struct cred *);
extern void __init cred_init(void);

/**
Expand Down Expand Up @@ -202,32 +202,32 @@ static inline void put_cred(const struct cred *_cred)
}

/**
* current_cred - Access the current task's credentials
* current_cred - Access the current task's subjective credentials
*
* Access the credentials of the current task.
* Access the subjective credentials of the current task.
*/
#define current_cred() \
(current->cred)

/**
* __task_cred - Access another task's credentials
* __task_cred - Access a task's objective credentials
* @task: The task to query
*
* Access the credentials of another task. The caller must hold the
* RCU readlock.
* Access the objective credentials of a task. The caller must hold the RCU
* readlock.
*
* The caller must make sure task doesn't go away, either by holding a ref on
* task or by holding tasklist_lock to prevent it from being unlinked.
*/
#define __task_cred(task) \
((const struct cred *)(rcu_dereference((task)->cred)))
((const struct cred *)(rcu_dereference((task)->real_cred)))

/**
* get_task_cred - Get another task's credentials
* get_task_cred - Get another task's objective credentials
* @task: The task to query
*
* Get the credentials of a task, pinning them so that they can't go away.
* Accessing a task's credentials directly is not permitted.
* Get the objective credentials of a task, pinning them so that they can't go
* away. Accessing a task's credentials directly is not permitted.
*
* The caller must make sure task doesn't go away, either by holding a ref on
* task or by holding tasklist_lock to prevent it from being unlinked.
Expand All @@ -243,10 +243,11 @@ static inline void put_cred(const struct cred *_cred)
})

/**
* get_current_cred - Get the current task's credentials
* get_current_cred - Get the current task's subjective credentials
*
* Get the credentials of the current task, pinning them so that they can't go
* away. Accessing the current task's credentials directly is not permitted.
* Get the subjective credentials of the current task, pinning them so that
* they can't go away. Accessing the current task's credentials directly is
* not permitted.
*/
#define get_current_cred() \
(get_cred(current_cred()))
Expand Down
1 change: 1 addition & 0 deletions include/linux/init_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ extern struct cred init_cred;
.children = LIST_HEAD_INIT(tsk.children), \
.sibling = LIST_HEAD_INIT(tsk.sibling), \
.group_leader = &tsk, \
.real_cred = &init_cred, \
.cred = &init_cred, \
.cred_exec_mutex = \
__MUTEX_INITIALIZER(tsk.cred_exec_mutex), \
Expand Down
5 changes: 4 additions & 1 deletion include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -1145,7 +1145,10 @@ struct task_struct {
struct list_head cpu_timers[3];

/* process credentials */
const struct cred *cred; /* actual/objective task credentials (COW) */
const struct cred *real_cred; /* objective and real subjective task
* credentials (COW) */
const struct cred *cred; /* effective (overridable) subjective task
* credentials (COW) */
struct mutex cred_exec_mutex; /* execve vs ptrace cred calculation mutex */

char comm[TASK_COMM_LEN]; /* executable name excluding path
Expand Down
38 changes: 26 additions & 12 deletions kernel/cred.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static struct thread_group_cred init_tgcred = {
* The initial credentials for the initial task
*/
struct cred init_cred = {
.usage = ATOMIC_INIT(3),
.usage = ATOMIC_INIT(4),
.securebits = SECUREBITS_DEFAULT,
.cap_inheritable = CAP_INIT_INH_SET,
.cap_permitted = CAP_FULL_SET,
Expand Down Expand Up @@ -120,6 +120,8 @@ EXPORT_SYMBOL(__put_cred);
* prepare a new copy, which the caller then modifies and then commits by
* calling commit_creds().
*
* Preparation involves making a copy of the objective creds for modification.
*
* Returns a pointer to the new creds-to-be if successful, NULL otherwise.
*
* Call commit_creds() or abort_creds() to clean up.
Expand All @@ -130,7 +132,7 @@ struct cred *prepare_creds(void)
const struct cred *old;
struct cred *new;

BUG_ON(atomic_read(&task->cred->usage) < 1);
BUG_ON(atomic_read(&task->real_cred->usage) < 1);

new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
if (!new)
Expand Down Expand Up @@ -262,6 +264,9 @@ struct cred *prepare_usermodehelper_creds(void)
*
* We share if we can, but under some circumstances we have to generate a new
* set.
*
* The new process gets the current process's subjective credentials as its
* objective and subjective credentials
*/
int copy_creds(struct task_struct *p, unsigned long clone_flags)
{
Expand All @@ -278,6 +283,7 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
#endif
clone_flags & CLONE_THREAD
) {
p->real_cred = get_cred(p->cred);
get_cred(p->cred);
atomic_inc(&p->cred->user->processes);
return 0;
Expand Down Expand Up @@ -317,7 +323,7 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
#endif

atomic_inc(&new->user->processes);
p->cred = new;
p->cred = p->real_cred = get_cred(new);
return 0;
}

Expand All @@ -326,7 +332,9 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
* @new: The credentials to be assigned
*
* Install a new set of credentials to the current task, using RCU to replace
* the old set.
* the old set. Both the objective and the subjective credentials pointers are
* updated. This function may not be called if the subjective credentials are
* in an overridden state.
*
* This function eats the caller's reference to the new credentials.
*
Expand All @@ -338,12 +346,15 @@ int commit_creds(struct cred *new)
struct task_struct *task = current;
const struct cred *old;

BUG_ON(task->cred != task->real_cred);
BUG_ON(atomic_read(&task->real_cred->usage) < 2);
BUG_ON(atomic_read(&new->usage) < 1);
BUG_ON(atomic_read(&task->cred->usage) < 1);

old = task->cred;
old = task->real_cred;
security_commit_creds(new, old);

get_cred(new); /* we will require a ref for the subj creds too */

/* dumpability changes */
if (old->euid != new->euid ||
old->egid != new->egid ||
Expand All @@ -369,6 +380,7 @@ int commit_creds(struct cred *new)
*/
if (new->user != old->user)
atomic_inc(&new->user->processes);
rcu_assign_pointer(task->real_cred, new);
rcu_assign_pointer(task->cred, new);
if (new->user != old->user)
atomic_dec(&old->user->processes);
Expand All @@ -388,6 +400,8 @@ int commit_creds(struct cred *new)
new->fsgid != old->fsgid)
proc_id_connector(task, PROC_EVENT_GID);

/* release the old obj and subj refs both */
put_cred(old);
put_cred(old);
return 0;
}
Expand All @@ -408,11 +422,11 @@ void abort_creds(struct cred *new)
EXPORT_SYMBOL(abort_creds);

/**
* override_creds - Temporarily override the current process's credentials
* override_creds - Override the current process's subjective credentials
* @new: The credentials to be assigned
*
* Install a set of temporary override credentials on the current process,
* returning the old set for later reversion.
* Install a set of temporary override subjective credentials on the current
* process, returning the old set for later reversion.
*/
const struct cred *override_creds(const struct cred *new)
{
Expand All @@ -424,11 +438,11 @@ const struct cred *override_creds(const struct cred *new)
EXPORT_SYMBOL(override_creds);

/**
* revert_creds - Revert a temporary credentials override
* revert_creds - Revert a temporary subjective credentials override
* @old: The credentials to be restored
*
* Revert a temporary set of override credentials to an old set, discarding the
* override set.
* Revert a temporary set of override subjective credentials to an old set,
* discarding the override set.
*/
void revert_creds(const struct cred *old)
{
Expand Down
6 changes: 4 additions & 2 deletions kernel/fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ void __put_task_struct(struct task_struct *tsk)
WARN_ON(atomic_read(&tsk->usage));
WARN_ON(tsk == current);

put_cred(tsk->real_cred);
put_cred(tsk->cred);
delayacct_tsk_free(tsk);

Expand Down Expand Up @@ -961,10 +962,10 @@ static struct task_struct *copy_process(unsigned long clone_flags,
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
retval = -EAGAIN;
if (atomic_read(&p->cred->user->processes) >=
if (atomic_read(&p->real_cred->user->processes) >=
p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
p->cred->user != current->nsproxy->user_ns->root_user)
p->real_cred->user != current->nsproxy->user_ns->root_user)
goto bad_fork_free;
}

Expand Down Expand Up @@ -1278,6 +1279,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
module_put(task_thread_info(p)->exec_domain->module);
bad_fork_cleanup_count:
atomic_dec(&p->cred->user->processes);
put_cred(p->real_cred);
put_cred(p->cred);
bad_fork_free:
free_task(p);
Expand Down
Loading

0 comments on commit 3b11a1d

Please sign in to comment.