diff options
Diffstat (limited to 'kernel/cred.c')
| -rw-r--r-- | kernel/cred.c | 27 | 
1 files changed, 26 insertions, 1 deletions
diff --git a/kernel/cred.c b/kernel/cred.c index 48cea3da6d0..709d521903f 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -455,6 +455,31 @@ error_put:  	return ret;  } +static bool cred_cap_issubset(const struct cred *set, const struct cred *subset) +{ +	const struct user_namespace *set_ns = set->user_ns; +	const struct user_namespace *subset_ns = subset->user_ns; + +	/* If the two credentials are in the same user namespace see if +	 * the capabilities of subset are a subset of set. +	 */ +	if (set_ns == subset_ns) +		return cap_issubset(subset->cap_permitted, set->cap_permitted); + +	/* The credentials are in a different user namespaces +	 * therefore one is a subset of the other only if a set is an +	 * ancestor of subset and set->euid is owner of subset or one +	 * of subsets ancestors. +	 */ +	for (;subset_ns != &init_user_ns; subset_ns = subset_ns->parent) { +		if ((set_ns == subset_ns->parent)  && +		    uid_eq(subset_ns->owner, set->euid)) +			return true; +	} + +	return false; +} +  /**   * commit_creds - Install new credentials upon the current task   * @new: The credentials to be assigned @@ -493,7 +518,7 @@ int commit_creds(struct cred *new)  	    !gid_eq(old->egid, new->egid) ||  	    !uid_eq(old->fsuid, new->fsuid) ||  	    !gid_eq(old->fsgid, new->fsgid) || -	    !cap_issubset(new->cap_permitted, old->cap_permitted)) { +	    !cred_cap_issubset(old, new)) {  		if (task->mm)  			set_dumpable(task->mm, suid_dumpable);  		task->pdeath_signal = 0;  |