Skip to content

Commit

Permalink
CRED: Neuter sys_capset()
Browse files Browse the repository at this point in the history
Take away the ability for sys_capset() to affect processes other than current.

This means that current will not need to lock its own credentials when reading
them against interference by other processes.

This has effectively been the case for a while anyway, since:

 (1) Without LSM enabled, sys_capset() is disallowed.

 (2) With file-based capabilities, sys_capset() is neutered.

Signed-off-by: David Howells <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Acked-by: Andrew G. Morgan <[email protected]>
Acked-by: James Morris <[email protected]>
Signed-off-by: James Morris <[email protected]>
  • Loading branch information
dhowells authored and James Morris committed Nov 13, 2008
1 parent 8bbf497 commit 1cdcbec
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 283 deletions.
12 changes: 1 addition & 11 deletions fs/open.c
Original file line number Diff line number Diff line change
Expand Up @@ -441,17 +441,7 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
current->fsgid = current->gid;

if (!issecure(SECURE_NO_SETUID_FIXUP)) {
/*
* Clear the capabilities if we switch to a non-root user
*/
#ifndef CONFIG_SECURITY_FILE_CAPABILITIES
/*
* FIXME: There is a race here against sys_capset. The
* capabilities can change yet we will restore the old
* value below. We should hold task_capabilities_lock,
* but we cannot because user_path_at can sleep.
*/
#endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */
/* Clear the capabilities if we switch to a non-root user */
if (current->uid)
old_cap = cap_set_effective(__cap_empty_set);
else
Expand Down
48 changes: 16 additions & 32 deletions include/linux/security.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ extern int cap_settime(struct timespec *ts, struct timezone *tz);
extern int cap_ptrace_may_access(struct task_struct *child, unsigned int mode);
extern int cap_ptrace_traceme(struct task_struct *parent);
extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
extern int cap_capset_check(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
extern void cap_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
extern int cap_capset_check(kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
extern void cap_capset_set(kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
extern int cap_bprm_set_security(struct linux_binprm *bprm);
extern void cap_bprm_apply_creds(struct linux_binprm *bprm, int unsafe);
extern int cap_bprm_secureexec(struct linux_binprm *bprm);
Expand Down Expand Up @@ -1191,24 +1191,14 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* Return 0 if the capability sets were successfully obtained.
* @capset_check:
* Check permission before setting the @effective, @inheritable, and
* @permitted capability sets for the @target process.
* Caveat: @target is also set to current if a set of processes is
* specified (i.e. all processes other than current and init or a
* particular process group). Hence, the capset_set hook may need to
* revalidate permission to the actual target process.
* @target contains the task_struct structure for target process.
* @permitted capability sets for the current process.
* @effective contains the effective capability set.
* @inheritable contains the inheritable capability set.
* @permitted contains the permitted capability set.
* Return 0 if permission is granted.
* @capset_set:
* Set the @effective, @inheritable, and @permitted capability sets for
* the @target process. Since capset_check cannot always check permission
* to the real @target process, this hook may also perform permission
* checking to determine if the current process is allowed to set the
* capability sets of the @target process. However, this hook has no way
* of returning an error due to the structure of the sys_capset code.
* @target contains the task_struct structure for target process.
* the current process.
* @effective contains the effective capability set.
* @inheritable contains the inheritable capability set.
* @permitted contains the permitted capability set.
Expand Down Expand Up @@ -1303,12 +1293,10 @@ struct security_operations {
int (*capget) (struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted);
int (*capset_check) (struct task_struct *target,
kernel_cap_t *effective,
int (*capset_check) (kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted);
void (*capset_set) (struct task_struct *target,
kernel_cap_t *effective,
void (*capset_set) (kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted);
int (*capable) (struct task_struct *tsk, int cap, int audit);
Expand Down Expand Up @@ -1572,12 +1560,10 @@ int security_capget(struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted);
int security_capset_check(struct task_struct *target,
kernel_cap_t *effective,
int security_capset_check(kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted);
void security_capset_set(struct task_struct *target,
kernel_cap_t *effective,
void security_capset_set(kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted);
int security_capable(struct task_struct *tsk, int cap);
Expand Down Expand Up @@ -1769,20 +1755,18 @@ static inline int security_capget(struct task_struct *target,
return cap_capget(target, effective, inheritable, permitted);
}

static inline int security_capset_check(struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
static inline int security_capset_check(kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
{
return cap_capset_check(target, effective, inheritable, permitted);
return cap_capset_check(effective, inheritable, permitted);
}

static inline void security_capset_set(struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
static inline void security_capset_set(kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
{
cap_capset_set(target, effective, inheritable, permitted);
cap_capset_set(effective, inheritable, permitted);
}

static inline int security_capable(struct task_struct *tsk, int cap)
Expand Down
227 changes: 23 additions & 204 deletions kernel/capability.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,160 +127,6 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy)
return 0;
}

#ifndef CONFIG_SECURITY_FILE_CAPABILITIES

/*
* Without filesystem capability support, we nominally support one process
* setting the capabilities of another
*/
static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
kernel_cap_t *pIp, kernel_cap_t *pPp)
{
struct task_struct *target;
int ret;

spin_lock(&task_capability_lock);
read_lock(&tasklist_lock);

if (pid && pid != task_pid_vnr(current)) {
target = find_task_by_vpid(pid);
if (!target) {
ret = -ESRCH;
goto out;
}
} else
target = current;

ret = security_capget(target, pEp, pIp, pPp);

out:
read_unlock(&tasklist_lock);
spin_unlock(&task_capability_lock);

return ret;
}

/*
* cap_set_pg - set capabilities for all processes in a given process
* group. We call this holding task_capability_lock and tasklist_lock.
*/
static inline int cap_set_pg(int pgrp_nr, kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
{
struct task_struct *g, *target;
int ret = -EPERM;
int found = 0;
struct pid *pgrp;

spin_lock(&task_capability_lock);
read_lock(&tasklist_lock);

pgrp = find_vpid(pgrp_nr);
do_each_pid_task(pgrp, PIDTYPE_PGID, g) {
target = g;
while_each_thread(g, target) {
if (!security_capset_check(target, effective,
inheritable, permitted)) {
security_capset_set(target, effective,
inheritable, permitted);
ret = 0;
}
found = 1;
}
} while_each_pid_task(pgrp, PIDTYPE_PGID, g);

read_unlock(&tasklist_lock);
spin_unlock(&task_capability_lock);

if (!found)
ret = 0;
return ret;
}

/*
* cap_set_all - set capabilities for all processes other than init
* and self. We call this holding task_capability_lock and tasklist_lock.
*/
static inline int cap_set_all(kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
{
struct task_struct *g, *target;
int ret = -EPERM;
int found = 0;

spin_lock(&task_capability_lock);
read_lock(&tasklist_lock);

do_each_thread(g, target) {
if (target == current
|| is_container_init(target->group_leader))
continue;
found = 1;
if (security_capset_check(target, effective, inheritable,
permitted))
continue;
ret = 0;
security_capset_set(target, effective, inheritable, permitted);
} while_each_thread(g, target);

read_unlock(&tasklist_lock);
spin_unlock(&task_capability_lock);

if (!found)
ret = 0;

return ret;
}

/*
* Given the target pid does not refer to the current process we
* need more elaborate support... (This support is not present when
* filesystem capabilities are configured.)
*/
static inline int do_sys_capset_other_tasks(pid_t pid, kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
{
struct task_struct *target;
int ret;

if (!capable(CAP_SETPCAP))
return -EPERM;

if (pid == -1) /* all procs other than current and init */
return cap_set_all(effective, inheritable, permitted);

else if (pid < 0) /* all procs in process group */
return cap_set_pg(-pid, effective, inheritable, permitted);

/* target != current */
spin_lock(&task_capability_lock);
read_lock(&tasklist_lock);

target = find_task_by_vpid(pid);
if (!target)
ret = -ESRCH;
else {
ret = security_capset_check(target, effective, inheritable,
permitted);

/* having verified that the proposed changes are legal,
we now put them into effect. */
if (!ret)
security_capset_set(target, effective, inheritable,
permitted);
}

read_unlock(&tasklist_lock);
spin_unlock(&task_capability_lock);

return ret;
}

#else /* ie., def CONFIG_SECURITY_FILE_CAPABILITIES */

/*
* If we have configured with filesystem capability support, then the
* only thing that can change the capabilities of the current process
Expand Down Expand Up @@ -314,22 +160,6 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
return ret;
}

/*
* With filesystem capability support configured, the kernel does not
* permit the changing of capabilities in one process by another
* process. (CAP_SETPCAP has much less broad semantics when configured
* this way.)
*/
static inline int do_sys_capset_other_tasks(pid_t pid,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
{
return -EPERM;
}

#endif /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */

/*
* Atomically modify the effective capabilities returning the original
* value. No permission check is performed here - it is assumed that the
Expand Down Expand Up @@ -424,16 +254,14 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
* @data: pointer to struct that contains the effective, permitted,
* and inheritable capabilities
*
* Set capabilities for a given process, all processes, or all
* processes in a given process group.
* Set capabilities for the current process only. The ability to any other
* process(es) has been deprecated and removed.
*
* The restrictions on setting capabilities are specified as:
*
* [pid is for the 'target' task. 'current' is the calling task.]
*
* I: any raised capabilities must be a subset of the (old current) permitted
* P: any raised capabilities must be a subset of the (old current) permitted
* E: must be set to a subset of (new target) permitted
* I: any raised capabilities must be a subset of the old permitted
* P: any raised capabilities must be a subset of the old permitted
* E: must be set to a subset of new permitted
*
* Returns 0 on success and < 0 on error.
*/
Expand All @@ -452,10 +280,13 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
if (get_user(pid, &header->pid))
return -EFAULT;

/* may only affect current now */
if (pid != 0 && pid != task_pid_vnr(current))
return -EPERM;

if (copy_from_user(&kdata, data, tocopy
* sizeof(struct __user_cap_data_struct))) {
* sizeof(struct __user_cap_data_struct)))
return -EFAULT;
}

for (i = 0; i < tocopy; i++) {
effective.cap[i] = kdata[i].effective;
Expand All @@ -473,32 +304,20 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
if (ret)
return ret;

if (pid && (pid != task_pid_vnr(current)))
ret = do_sys_capset_other_tasks(pid, &effective, &inheritable,
&permitted);
else {
/*
* This lock is required even when filesystem
* capability support is configured - it protects the
* sys_capget() call from returning incorrect data in
* the case that the targeted process is not the
* current one.
*/
spin_lock(&task_capability_lock);

ret = security_capset_check(current, &effective, &inheritable,
&permitted);
/*
* Having verified that the proposed changes are
* legal, we now put them into effect.
*/
if (!ret)
security_capset_set(current, &effective, &inheritable,
&permitted);
spin_unlock(&task_capability_lock);
}

/* This lock is required even when filesystem capability support is
* configured - it protects the sys_capget() call from returning
* incorrect data in the case that the targeted process is not the
* current one.
*/
spin_lock(&task_capability_lock);

ret = security_capset_check(&effective, &inheritable, &permitted);
/* Having verified that the proposed changes are legal, we now put them
* into effect.
*/
if (!ret)
security_capset_set(&effective, &inheritable, &permitted);
spin_unlock(&task_capability_lock);
return ret;
}

Expand Down
Loading

0 comments on commit 1cdcbec

Please sign in to comment.