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 | |
| 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')
| -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; |
