diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2012-12-14 11:50:54 -0500 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2012-12-14 21:36:26 -0500 |
commit | aa6d054e5ce94720797ca260392a74dbced56412 (patch) | |
tree | 4e7093d4e288238f0514187e87b13738504a21df /kernel/cred.c | |
parent | 5e4a08476b50fa39210fca82e03325cc46b9c235 (diff) |
userns: Add a more complete capability subset test to commit_creds
When unsharing a user namespace we reduce our credentials to just what
can be done in that user namespace. This is a subset of the credentials
we previously had. Teach commit_creds to recognize this is a subset
of the credentials we have had before and don't clear the dumpability flag.
This allows an unprivileged program to do:
unshare(CLONE_NEWUSER);
fd = open("/proc/self/uid_map", O_RDWR);
Where previously opening the uid_map writable would fail because
the the task had been made non-dumpable.
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
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 48cea3da6d05..709d521903f6 100644 --- a/kernel/cred.c +++ b/kernel/cred.c | |||
@@ -455,6 +455,31 @@ error_put: | |||
455 | return ret; | 455 | return ret; |
456 | } | 456 | } |
457 | 457 | ||
458 | static bool cred_cap_issubset(const struct cred *set, const struct cred *subset) | ||
459 | { | ||
460 | const struct user_namespace *set_ns = set->user_ns; | ||
461 | const struct user_namespace *subset_ns = subset->user_ns; | ||
462 | |||
463 | /* If the two credentials are in the same user namespace see if | ||
464 | * the capabilities of subset are a subset of set. | ||
465 | */ | ||
466 | if (set_ns == subset_ns) | ||
467 | return cap_issubset(subset->cap_permitted, set->cap_permitted); | ||
468 | |||
469 | /* The credentials are in a different user namespaces | ||
470 | * therefore one is a subset of the other only if a set is an | ||
471 | * ancestor of subset and set->euid is owner of subset or one | ||
472 | * of subsets ancestors. | ||
473 | */ | ||
474 | for (;subset_ns != &init_user_ns; subset_ns = subset_ns->parent) { | ||
475 | if ((set_ns == subset_ns->parent) && | ||
476 | uid_eq(subset_ns->owner, set->euid)) | ||
477 | return true; | ||
478 | } | ||
479 | |||
480 | return false; | ||
481 | } | ||
482 | |||
458 | /** | 483 | /** |
459 | * commit_creds - Install new credentials upon the current task | 484 | * commit_creds - Install new credentials upon the current task |
460 | * @new: The credentials to be assigned | 485 | * @new: The credentials to be assigned |
@@ -493,7 +518,7 @@ int commit_creds(struct cred *new) | |||
493 | !gid_eq(old->egid, new->egid) || | 518 | !gid_eq(old->egid, new->egid) || |
494 | !uid_eq(old->fsuid, new->fsuid) || | 519 | !uid_eq(old->fsuid, new->fsuid) || |
495 | !gid_eq(old->fsgid, new->fsgid) || | 520 | !gid_eq(old->fsgid, new->fsgid) || |
496 | !cap_issubset(new->cap_permitted, old->cap_permitted)) { | 521 | !cred_cap_issubset(old, new)) { |
497 | if (task->mm) | 522 | if (task->mm) |
498 | set_dumpable(task->mm, suid_dumpable); | 523 | set_dumpable(task->mm, suid_dumpable); |
499 | task->pdeath_signal = 0; | 524 | task->pdeath_signal = 0; |