userns: targeted capabilities v5
Subsystems (6)
Serge E. Hallyn
maintainerHere is a repost of my previous user namespace patch, ported onto last night's git head.
It fixes several things I was doing wrong in the last (v4) posting, in particular:
1. don't set uts_ns->user_ns to current's when !CLONE_NEWUTS
2. add a ipc_ns->user_ns which owns ipc_ns, and use that to decide CAP_IPC_OWNER
3. fix logic flaw caused by bad parantheses
4. allow do_prlimit to current
5. don't always give root full privs to init_user_ns
The expected course of development for user namespaces is laid out at https://wiki.ubuntu.com/UserNamespace. Bugs aside, this patchset is supposed to not at all affect systems which are not actively using user namespaces, and only restrict what tasks in child user namespace can do. They begin to limit privilege to a user namespace, so that root in a container cannot kill or ptrace tasks in the parent user namespace, and can only get world access rights to files. Since all files currently belong to the initila user namespace, that means that child user namespaces can only get world access rights to *all* files. While this temporarily makes user namespaces bad for system containers, it starts to get useful for some sandboxing.
I've run the 'runltplite.sh' with and without this patchset and found no difference. So all in all, this is the first version of this patchset for which I feel comfortable asking: please consider applying.
thanks,
-serge
Serge E. Hallyn
maintainerPATCH 1/9 Add a user_namespace as creator/owner of uts_namespace
copy_process() handles CLONE_NEWUSER before the rest of the namespaces. So in the case of clone(CLONE_NEWUSER|CLONE_NEWUTS) the new uts namespace will have the new user namespace as its owner. That is what we want, since we want root in that new userns to be able to have privilege over it.
Changelog:
Feb 15: don't set uts_ns->user_ns if we didn't create a new uts_ns.
--- include/linux/utsname.h | 3 +++ init/version.c | 2 ++ kernel/nsproxy.c | 5 +++++ kernel/user.c | 8 ++++++-- kernel/utsname.c | 4 ++++ 5 files changed, 20 insertions(+), 2 deletions(-)
include/linux/utsname.h +3 -0
@@ -37,9 +37,12 @@ struct new_utsname {#include <linux/nsproxy.h> #include <linux/err.h> +struct user_namespace ; +struct uts_namespace {struct kref kref;struct new_utsname name; +struct user_namespace *user_ns; };extern struct uts_namespace init_uts_ns;
init/version.c +2 -0
@@ -21,6 +21,7 @@ extern int version_string(LINUX_VERSION_CODE);int version_string (LINUX_VERSION_CODE );#endif +extern struct user_namespace init_user_ns;struct uts_namespace init_uts_ns = { .kref = { .refcount =ATOMIC_INIT (2 ),
@@ -33,6 +34,7 @@ struct uts_namespace init_uts_ns = { .machine = UTS_MACHINE, .domainname = UTS_DOMAINNAME, }, + .user_ns = &init_user_ns, };EXPORT_SYMBOL_GPL (init_uts_ns);
kernel/nsproxy.c +5 -0
@@ -74,6 +74,11 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, err =PTR_ERR (new_nsp->uts_ns); goto out_uts; } +if (new_nsp->uts_ns != tsk->nsproxy->uts_ns) { +put_user_ns (new_nsp->uts_ns->user_ns); + new_nsp->uts_ns->user_ns =task_cred_xxx (tsk, user)->user_ns; +get_user_ns (new_nsp->uts_ns->user_ns); + } new_nsp->ipc_ns =copy_ipcs (flags, tsk->nsproxy->ipc_ns);if (IS_ERR (new_nsp->ipc_ns)) {
kernel/user.c +6 -2
@@ -17,9 +17,13 @@#include <linux/module.h> #include <linux/user_namespace.h> +/* +* userns count is 1 for root user, 1 for init_uts_ns, +* and 1 for... ? +*/ struct user_namespace init_user_ns = { .kref = { - .refcount =ATOMIC_INIT (2 ), + .refcount =ATOMIC_INIT (3 ), }, .creator = &root_user, };
@@ -47,7 +51,7 @@ static struct kmem_cache *uid_cachep;*/ static DEFINE_SPINLOCK (uidhash_lock); -/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->creator */ +/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->user_ns */ struct user_struct root_user = { .__count =ATOMIC_INIT (2 ), .processes =ATOMIC_INIT (1 ),
kernel/utsname.c +4 -0
@@ -14,6 +14,7 @@#include <linux/utsname.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/user_namespace.h> static struct uts_namespace *create_uts_ns (void ) {
@@ -40,6 +41,8 @@ static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns)down_read (&uts_sem);memcpy (&ns->name, &old_ns->name,sizeof (ns->name)); + ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns);up_read (&uts_sem);return ns; }
@@ -71,5 +74,6 @@ void free_uts_ns(struct kref *kref)struct uts_namespace *ns; ns = container_of(kref, struct uts_namespace, kref); +put_user_ns (ns->user_ns);kfree (ns); }
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Eric W. Biederman
Acked-byRe: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
Quoted 5 lines · Serge E. Hallyn
copy_process()handlesCLONE_NEWUSERbefore the rest of the namespaces. So in the case ofclone(CLONE_NEWUSER|CLONE_NEWUTS)the new uts namespace will have the new user namespace as its owner. That is what we want, since we want root in that new userns to be able to have privilege over it.
Quoted 123 lines
Changelog:
Feb 15: don't set uts_ns->user_ns if we didn't create a new uts_ns.Signed-off-by Serge E. Hallyn <[email protected]>--- include/linux/utsname.h | 3 +++ init/version.c | 2 ++ kernel/nsproxy.c | 5 +++++ kernel/user.c | 8 ++++++-- kernel/utsname.c | 4 ++++ 5 files changed, 20 insertions(+), 2 deletions(-)include/linux/utsname.h +3
-0@@ -37,9 +37,12 @@ struct new_utsname {#include <linux/nsproxy.h> #include <linux/err.h> +struct user_namespace ; +struct uts_namespace {struct kref kref;struct new_utsname name; +struct user_namespace *user_ns; };extern struct uts_namespace init_uts_ns;init/version.c +2
-0@@ -21,6 +21,7 @@ extern int version_string(LINUX_VERSION_CODE);int version_string (LINUX_VERSION_CODE );#endif +extern struct user_namespace init_user_ns;struct uts_namespace init_uts_ns = { .kref = { .refcount =ATOMIC_INIT (2 ),@@ -33,6 +34,7 @@ struct uts_namespace init_uts_ns = { .machine = UTS_MACHINE, .domainname = UTS_DOMAINNAME, }, + .user_ns = &init_user_ns, };EXPORT_SYMBOL_GPL (init_uts_ns);kernel/nsproxy.c +5
-0@@ -74,6 +74,11 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, err =PTR_ERR (new_nsp->uts_ns); goto out_uts; } +if (new_nsp->uts_ns != tsk->nsproxy->uts_ns) { +put_user_ns (new_nsp->uts_ns->user_ns); + new_nsp->uts_ns->user_ns =task_cred_xxx (tsk, user)->user_ns; +get_user_ns (new_nsp->uts_ns->user_ns); + } new_nsp->ipc_ns =copy_ipcs (flags, tsk->nsproxy->ipc_ns);if (IS_ERR (new_nsp->ipc_ns)) {kernel/user.c +6
-2@@ -17,9 +17,13 @@#include <linux/module.h> #include <linux/user_namespace.h> +/* +* userns count is 1 for root user, 1 for init_uts_ns, +* and 1 for... ? +*/ struct user_namespace init_user_ns = { .kref = { - .refcount =ATOMIC_INIT (2 ), + .refcount =ATOMIC_INIT (3 ), }, .creator = &root_user, };@@ -47,7 +51,7 @@ static struct kmem_cache *uid_cachep;*/ static DEFINE_SPINLOCK (uidhash_lock); -/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->creator */ +/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->user_ns */ struct user_struct root_user = { .__count =ATOMIC_INIT (2 ), .processes =ATOMIC_INIT (1 ),kernel/utsname.c +4
-0@@ -14,6 +14,7 @@#include <linux/utsname.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/user_namespace.h> static struct uts_namespace *create_uts_ns (void ) {@@ -40,6 +41,8 @@ static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns)down_read (&uts_sem);memcpy (&ns->name, &old_ns->name,sizeof (ns->name)); + ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns);up_read (&uts_sem);return ns; }@@ -71,5 +74,6 @@ void free_uts_ns(struct kref *kref)struct uts_namespace *ns; ns = container_of(kref, struct uts_namespace, kref); +put_user_ns (ns->user_ns);kfree (ns); }
Daniel Lezcano
Acked-byRe: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
In reply to a message not in the archiveQuoted 11 lines · Serge E. Hallyn
copy_process()handlesCLONE_NEWUSERbefore the rest of the namespaces. So in the case ofclone(CLONE_NEWUSER|CLONE_NEWUTS)the new uts namespace will have the new user namespace as its owner. That is what we want, since we want root in that new userns to be able to have privilege over it.Changelog:
Feb 15: don't set uts_ns->user_ns if we didn't create a new uts_ns.Signed-off-by Serge E. Hallyn<[email protected]>
A couple of comments.
Quoted 50 lines
--- include/linux/utsname.h | 3 +++ init/version.c | 2 ++ kernel/nsproxy.c | 5 +++++ kernel/user.c | 8 ++++++-- kernel/utsname.c | 4 ++++ 5 files changed, 20 insertions(+), 2 deletions(-)include/linux/utsname.h +3
-0@@ -37,9 +37,12 @@ struct new_utsname {#include <linux/nsproxy.h> #include <linux/err.h> +struct user_namespace ; +struct uts_namespace {struct kref kref;struct new_utsname name; +struct user_namespace *user_ns; };extern struct uts_namespace init_uts_ns;init/version.c +2
-0@@ -21,6 +21,7 @@ extern int version_string(LINUX_VERSION_CODE);int version_string (LINUX_VERSION_CODE );#endif +extern struct user_namespace init_user_ns;struct uts_namespace init_uts_ns = { .kref = { .refcount =ATOMIC_INIT (2 ),@@ -33,6 +34,7 @@ struct uts_namespace init_uts_ns = { .machine = UTS_MACHINE, .domainname = UTS_DOMAINNAME, }, + .user_ns =&init_user_ns, };EXPORT_SYMBOL_GPL (init_uts_ns);kernel/nsproxy.c +0
-0
kernel/nsproxy.c static struct nsproxy *create_new_namespaces(unsigned long flags, · line 81
Quoted 8 lines
kernel/nsproxy.c +5
-0@@ -74,6 +74,11 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, err =PTR_ERR (new_nsp->uts_ns); goto out_uts; } +if (new_nsp->uts_ns != tsk->nsproxy->uts_ns) { +put_user_ns (new_nsp->uts_ns->user_ns); + new_nsp->uts_ns->user_ns =task_cred_xxx (tsk, user)->user_ns; +get_user_ns (new_nsp->uts_ns->user_ns); + }
IMO you should add a comment telling this code assume create_user_ns was called before (via copy_cred).
Quoted 3 lines
kernel/nsproxy.c +0
-0new_nsp->ipc_ns =copy_ipcs (flags, tsk->nsproxy->ipc_ns);if (IS_ERR (new_nsp->ipc_ns)) {
[ ... ]
kernel/nsproxy.c static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns) · line 45
Quoted 8 lines
static struct uts_namespace *create_uts_ns(void) {kernel/nsproxy.c +2
-0@@ -40,6 +41,8 @@ static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns)down_read (&uts_sem);memcpy (&ns->name,&old_ns->name,sizeof (ns->name)); + ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns);
ns->user_ns = get_user_ns(old_ns->user_ns);
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
In reply to a message not in the archiveQuoted 4 lines · On Thu, 17 Feb 2011 15:02:57 +0000 "Serge E. Hallyn"…
+/*
+ * userns count is 1 for root user, 1 forinit_uts_ns,
+ * and 1 for... ?
+ */
?
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
In reply to a message not in the archiveinclude/linux/utsname.h struct new_utsname { · line 46
Quoted 5 lines · Serge E. Hallyn
struct
uts_namespace{include/linux/utsname.h +1
-0struct kref kref;struct new_utsname name; +struct user_namespace *user_ns; };
If a uts_namespace belongs to a user_namespace, should CLONE_NEWUSER then imply CLONE_NEWUTS?
Or is uts_namespace::user_ns more an implication that the set of users in user_namespace are the only ones authorised to alter the uts data.
I presume that the uts_namespace of a process must be owned by one of the user_namespaces in the alternating inheritance chain of namespaces and their creators leading from current_user_ns() to init_user_ns.
With that in mind, looking at patch 3:
- if (!capable(CAP_SYS_ADMIN))
+ if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))
what is it you're actually asking? I presume it's 'does this user have CAP_SYS_ADMIN capability over objects belonging to the uts_namespace's user_namespace?'
So, to look at the important bit of patch 2:
-int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap,
- int audit)
+int cap_capable(struct task_struct *tsk, const struct cred *cred,
+ struct user_namespace *targ_ns, int cap, int audit)
{
- return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
+ for (;;) {
+ /* The creator of the user namespace has all caps. */
+ if (targ_ns != &init_user_ns && targ_ns->creator == cred->user)
+ return 0;
+
+ /* Do we have the necessary capabilities? */
+ if (targ_ns == cred->user->user_ns)
+ return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
+
+ /* Have we tried all of the parent namespaces? */
+ if (targ_ns == &init_user_ns)
+ return -EPERM;
+
+ /* If you have the capability in a parent user ns you have it
+ * in the over all children user namespaces as well, so see
+ * if this process has the capability in the parent user
+ * namespace.
+ */
+ targ_ns = targ_ns->creator->user_ns;
+ }
+
+ /* We never get here */
+ return -EPERM;
}
On entry, as we're called from ns_capable(), cred->user is the user that the current process is running as, and, as such, may be in a separate namespace from uts_namespace - which may itself be in a separate namespace from init_user_ns.
So, assume for the sake of argument that there are three user_namespaces along the chain from the calling process to the root, and that the uts_namespace belongs to the middle one.
if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) return 0;
Can never match because targ_ns->creator cannot be cred->user; even if the uts_namespace belongs to our namespace, given that the creator lies outside our namespace.
if (targ_ns == cred->user->user_ns) return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
Can only match if we are in the target user_namespace (ie. the one to which uts_namespace belongs), whether or not we have CAP_SYS_ADMIN.
Which means that unless the uts_namespace belongs to our user_namespace, we cannot change it. Is that correct?
So ns_capable() restricts you to only doing interesting things to objects that belong to a user_namespace if they are in your own user_namespace. Is that correct?
If that is so, is the loop required for ns_capable()?
Looking further at patch 2:
#define nsown_capable(cap) (ns_capable(current_user_ns(), (cap)))
Given what I've said above, I presume the loop isn't necessary here either.
I think you're using ns_capable() in two different ways:
(1) You're using it to see if a process has power over its descendents in a user_namespace that can be traced back to a clone() that it did with CLONE_NEWUSER.
For example, automatically granting a process permission to kill descendents in a namespace it created.
(2) You're using it to see if a process can access objects that might be outside its own user_namespace.
For example, setting the hostname.
Is it worth giving two different interfaces to make this clearer (even if they actually do the same thing)?
Sorry if this seems rambly, but I'm trying to get my head round your code.
David
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
In reply to Serge E. HallynQuoted 5 lines · Serge E. Hallyn
copy_process()handlesCLONE_NEWUSERbefore the rest of the namespaces. So in the case ofclone(CLONE_NEWUSER|CLONE_NEWUTS)the new uts namespace will have the new user namespace as its owner. That is what we want, since we want root in that new userns to be able to have privilege over it.
Quoted 123 lines
Changelog:
Feb 15: don't set uts_ns->user_ns if we didn't create a new uts_ns.Signed-off-by Serge E. Hallyn <[email protected]>--- include/linux/utsname.h | 3 +++ init/version.c | 2 ++ kernel/nsproxy.c | 5 +++++ kernel/user.c | 8 ++++++-- kernel/utsname.c | 4 ++++ 5 files changed, 20 insertions(+), 2 deletions(-)include/linux/utsname.h +3
-0@@ -37,9 +37,12 @@ struct new_utsname {#include <linux/nsproxy.h> #include <linux/err.h> +struct user_namespace ; +struct uts_namespace {struct kref kref;struct new_utsname name; +struct user_namespace *user_ns; };extern struct uts_namespace init_uts_ns;init/version.c +2
-0@@ -21,6 +21,7 @@ extern int version_string(LINUX_VERSION_CODE);int version_string (LINUX_VERSION_CODE );#endif +extern struct user_namespace init_user_ns;struct uts_namespace init_uts_ns = { .kref = { .refcount =ATOMIC_INIT (2 ),@@ -33,6 +34,7 @@ struct uts_namespace init_uts_ns = { .machine = UTS_MACHINE, .domainname = UTS_DOMAINNAME, }, + .user_ns = &init_user_ns, };EXPORT_SYMBOL_GPL (init_uts_ns);kernel/nsproxy.c +5
-0@@ -74,6 +74,11 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, err =PTR_ERR (new_nsp->uts_ns); goto out_uts; } +if (new_nsp->uts_ns != tsk->nsproxy->uts_ns) { +put_user_ns (new_nsp->uts_ns->user_ns); + new_nsp->uts_ns->user_ns =task_cred_xxx (tsk, user)->user_ns; +get_user_ns (new_nsp->uts_ns->user_ns); + } new_nsp->ipc_ns =copy_ipcs (flags, tsk->nsproxy->ipc_ns);if (IS_ERR (new_nsp->ipc_ns)) {kernel/user.c +6
-2@@ -17,9 +17,13 @@#include <linux/module.h> #include <linux/user_namespace.h> +/* +* userns count is 1 for root user, 1 for init_uts_ns, +* and 1 for... ? +*/ struct user_namespace init_user_ns = { .kref = { - .refcount =ATOMIC_INIT (2 ), + .refcount =ATOMIC_INIT (3 ), }, .creator = &root_user, };@@ -47,7 +51,7 @@ static struct kmem_cache *uid_cachep;*/ static DEFINE_SPINLOCK (uidhash_lock); -/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->creator */ +/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->user_ns */ struct user_struct root_user = { .__count =ATOMIC_INIT (2 ), .processes =ATOMIC_INIT (1 ),kernel/utsname.c +4
-0@@ -14,6 +14,7 @@#include <linux/utsname.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/user_namespace.h> static struct uts_namespace *create_uts_ns (void ) {@@ -40,6 +41,8 @@ static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns)down_read (&uts_sem);memcpy (&ns->name, &old_ns->name,sizeof (ns->name)); + ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns);up_read (&uts_sem);return ns; }@@ -71,5 +74,6 @@ void free_uts_ns(struct kref *kref)struct uts_namespace *ns; ns = container_of(kref, struct uts_namespace, kref); +put_user_ns (ns->user_ns);kfree (ns); }
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
In reply to Serge E. HallynQuoted 11 lines · Serge E. Hallyn
copy_process()handlesCLONE_NEWUSERbefore the rest of the namespaces. So in the case ofclone(CLONE_NEWUSER|CLONE_NEWUTS)the new uts namespace will have the new user namespace as its owner. That is what we want, since we want root in that new userns to be able to have privilege over it.Changelog:
Feb 15: don't set uts_ns->user_ns if we didn't create a new uts_ns.Signed-off-by Serge E. Hallyn<[email protected]>
A couple of comments.
Quoted 50 lines
--- include/linux/utsname.h | 3 +++ init/version.c | 2 ++ kernel/nsproxy.c | 5 +++++ kernel/user.c | 8 ++++++-- kernel/utsname.c | 4 ++++ 5 files changed, 20 insertions(+), 2 deletions(-)include/linux/utsname.h +3
-0@@ -37,9 +37,12 @@ struct new_utsname {#include <linux/nsproxy.h> #include <linux/err.h> +struct user_namespace ; +struct uts_namespace {struct kref kref;struct new_utsname name; +struct user_namespace *user_ns; };extern struct uts_namespace init_uts_ns;init/version.c +2
-0@@ -21,6 +21,7 @@ extern int version_string(LINUX_VERSION_CODE);int version_string (LINUX_VERSION_CODE );#endif +extern struct user_namespace init_user_ns;struct uts_namespace init_uts_ns = { .kref = { .refcount =ATOMIC_INIT (2 ),@@ -33,6 +34,7 @@ struct uts_namespace init_uts_ns = { .machine = UTS_MACHINE, .domainname = UTS_DOMAINNAME, }, + .user_ns =&init_user_ns, };EXPORT_SYMBOL_GPL (init_uts_ns);kernel/nsproxy.c +0
-0
kernel/nsproxy.c static struct nsproxy *create_new_namespaces(unsigned long flags, · line 81
Quoted 8 lines
kernel/nsproxy.c +5
-0@@ -74,6 +74,11 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, err =PTR_ERR (new_nsp->uts_ns); goto out_uts; } +if (new_nsp->uts_ns != tsk->nsproxy->uts_ns) { +put_user_ns (new_nsp->uts_ns->user_ns); + new_nsp->uts_ns->user_ns =task_cred_xxx (tsk, user)->user_ns; +get_user_ns (new_nsp->uts_ns->user_ns); + }
IMO you should add a comment telling this code assume create_user_ns was called before (via copy_cred).
Quoted 3 lines
kernel/nsproxy.c +0
-0new_nsp->ipc_ns =copy_ipcs (flags, tsk->nsproxy->ipc_ns);if (IS_ERR (new_nsp->ipc_ns)) {
[ ... ]
kernel/nsproxy.c static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns) · line 45
Quoted 8 lines
static struct uts_namespace *create_uts_ns(void) {kernel/nsproxy.c +2
-0@@ -40,6 +41,8 @@ static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns)down_read (&uts_sem);memcpy (&ns->name,&old_ns->name,sizeof (ns->name)); + ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns);
ns->user_ns = get_user_ns(old_ns->user_ns);
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
In reply to Serge E. HallynQuoted 4 lines · On Thu, 17 Feb 2011 15:02:57 +0000 "Serge E. Hallyn" <[email protected]> wrote:
+/*
+ * userns count is 1 for root user, 1 forinit_uts_ns,
+ * and 1 for... ?
+ */
?
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
In reply to Serge E. Hallyninclude/linux/utsname.h struct new_utsname { · line 46
Quoted 5 lines · Serge E. Hallyn
struct
uts_namespace{include/linux/utsname.h +1
-0struct kref kref;struct new_utsname name; +struct user_namespace *user_ns; };
If a uts_namespace belongs to a user_namespace, should CLONE_NEWUSER then imply CLONE_NEWUTS?
Or is uts_namespace::user_ns more an implication that the set of users in user_namespace are the only ones authorised to alter the uts data.
I presume that the uts_namespace of a process must be owned by one of the user_namespaces in the alternating inheritance chain of namespaces and their creators leading from current_user_ns() to init_user_ns.
With that in mind, looking at patch 3:
- if (!capable(CAP_SYS_ADMIN))
+ if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))
what is it you're actually asking? I presume it's 'does this user have CAP_SYS_ADMIN capability over objects belonging to the uts_namespace's user_namespace?'
So, to look at the important bit of patch 2:
-int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap,
- int audit)
+int cap_capable(struct task_struct *tsk, const struct cred *cred,
+ struct user_namespace *targ_ns, int cap, int audit)
{
- return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
+ for (;;) {
+ /* The creator of the user namespace has all caps. */
+ if (targ_ns != &init_user_ns && targ_ns->creator == cred->user)
+ return 0;
+
+ /* Do we have the necessary capabilities? */
+ if (targ_ns == cred->user->user_ns)
+ return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
+
+ /* Have we tried all of the parent namespaces? */
+ if (targ_ns == &init_user_ns)
+ return -EPERM;
+
+ /* If you have the capability in a parent user ns you have it
+ * in the over all children user namespaces as well, so see
+ * if this process has the capability in the parent user
+ * namespace.
+ */
+ targ_ns = targ_ns->creator->user_ns;
+ }
+
+ /* We never get here */
+ return -EPERM;
}
On entry, as we're called from ns_capable(), cred->user is the user that the current process is running as, and, as such, may be in a separate namespace from uts_namespace - which may itself be in a separate namespace from init_user_ns.
So, assume for the sake of argument that there are three user_namespaces along the chain from the calling process to the root, and that the uts_namespace belongs to the middle one.
if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) return 0;
Can never match because targ_ns->creator cannot be cred->user; even if the uts_namespace belongs to our namespace, given that the creator lies outside our namespace.
if (targ_ns == cred->user->user_ns) return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
Can only match if we are in the target user_namespace (ie. the one to which uts_namespace belongs), whether or not we have CAP_SYS_ADMIN.
Which means that unless the uts_namespace belongs to our user_namespace, we cannot change it. Is that correct?
So ns_capable() restricts you to only doing interesting things to objects that belong to a user_namespace if they are in your own user_namespace. Is that correct?
If that is so, is the loop required for ns_capable()?
Looking further at patch 2:
#define nsown_capable(cap) (ns_capable(current_user_ns(), (cap)))
Given what I've said above, I presume the loop isn't necessary here either.
I think you're using ns_capable() in two different ways:
(1) You're using it to see if a process has power over its descendents in a user_namespace that can be traced back to a clone() that it did with CLONE_NEWUSER.
For example, automatically granting a process permission to kill descendents in a namespace it created.
(2) You're using it to see if a process can access objects that might be outside its own user_namespace.
For example, setting the hostname.
Is it worth giving two different interfaces to make this clearer (even if they actually do the same thing)?
Sorry if this seems rambly, but I'm trying to get my head round your code.
David
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
Quoted 11 lines · David Howells
include/linux/utsname.h struct new_utsname { · line 46
Quoted 5 lines · Serge E. Hallyn
struct
uts_namespace{include/linux/utsname.h +1
-0struct kref kref;struct new_utsname name; +struct user_namespace *user_ns; };If a
uts_namespacebelongs to auser_namespace, shouldCLONE_NEWUSERthen implyCLONE_NEWUTS?
Quoted 2 lines
Or is
uts_namespace::user_nsmore an implication that the set of users inuser_namespaceare the only ones authorised to alter the uts data.
The later.
Quoted 9 lines
I presume that the
uts_namespaceof a process must be owned by one of theuser_namespacesin the alternating inheritance chain of namespaces and their creators leading fromcurrent_user_ns()toinit_user_ns.With that in mind, looking at patch 3:
(quoted diff) +1
-1- if (!capable(CAP_SYS_ADMIN)) + if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))
Quoted 3 lines
what is it you're actually asking? I presume it's 'does this user have
CAP_SYS_ADMINcapability over objects belonging to the uts_namespace'suser_namespace?'
Yes.
Quoted 33 lines
So, to look at the important bit of patch 2:
(quoted diff) +25
-3-int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, - int audit) +int cap_capable(struct task_struct *tsk, const struct cred *cred, + struct user_namespace *targ_ns, int cap, int audit) { - return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; + for (;;) { + /* The creator of the user namespace has all caps. */ + if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) + return 0; + + /* Do we have the necessary capabilities? */ + if (targ_ns == cred->user->user_ns) + return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; + + /* Have we tried all of the parent namespaces? */ + if (targ_ns == &init_user_ns) + return -EPERM; + + /* If you have the capability in a parent user ns you have it + * in the over all children user namespaces as well, so see + * if this process has the capability in the parent user + * namespace. + */ + targ_ns = targ_ns->creator->user_ns; + } + + /* We never get here */ + return -EPERM; }
Quoted 8 lines
On entry, as we're called from
ns_capable(),cred->useris the user that the current process is running as, and, as such, may be in a separate namespace fromuts_namespace- which may itself be in a separate namespace frominit_user_ns.So, assume for the sake of argument that there are three
user_namespacesalong the chain from the calling process to the root, and that theuts_namespacebelongs to the middle one.
So we have the nested stack of:user_ns3->creator->user_ns == user_ns2user_ns2->creator->user_ns == &init_user_nsuts_ns2->user_ns == user_ns2
Quoted 7 lines
if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) return 0;Can never match because
targ_ns->creatorcannot becred->user; even if theuts_namespacebelongs to our namespace, given that the creator lies outside our namespace.
Initially we come in with targ_ns == user_ns2 and cred->user->user_ns in one of (user_ns3, user_ns2, or &init_user_ns).
targ_ns takes on values user_ns2 and &init_user_ns.
So when targ_ns becomes &init_user_ns. If the user in question is uts_ns2->user_ns->creator. This check will indeed match.
Quoted 5 lines
if (targ_ns == cred->user->user_ns) return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;Can only match if we are in the target
user_namespace(ie. the one to whichuts_namespacebelongs), whether or not we haveCAP_SYS_ADMIN.
As before targ_ns takes on values of user_ns2 and &init_user_ns.
Which means this check will match if we have CAP_SYS_ADMIN in &init_user_ns or in user_ns2.
Quoted 2 lines
Which means that unless the
uts_namespacebelongs to ouruser_namespace, we cannot change it. Is that correct?
No. If you are root in a parent namespace you can also change it.
Quoted 3 lines
So
ns_capable()restricts you to only doing interesting things to objects that belong to auser_namespaceif they are in your ownuser_namespace. Is that correct?
No. Root outside your user namespace is also allowed to do interesting things.
Quoted 1 line
If that is so, is the loop required for
ns_capable()?
Yes.
Quoted 19 lines
Looking further at patch 2:
#define nsown_capable(cap) (ns_capable(current_user_ns(), (cap)))Given what I've said above, I presume the loop isn't necessary here either.
I think you're using
ns_capable()in two different ways:(1) You're using it to see if a process has power over its descendents in a
user_namespacethat can be traced back to aclone()that it did withCLONE_NEWUSER.For example, automatically granting a process permission to kill descendents in a namespace it created.
(2) You're using it to see if a process can access objects that might be outside its own
user_namespace.
Quoted 8 lines
For example, setting the hostname.
Is it worth giving two different interfaces to make this clearer (even if they actually do the same thing)?
Sorry if this seems rambly, but I'm trying to get my head round your code.
I am all for making that loop a little clearer, because it is something you have to pause and think about to understand but so far I don't think the loop is wrong, and it is simple.
Eric
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
In reply to David HowellsQuoted 11 lines · David Howells
include/linux/utsname.h struct new_utsname { · line 46
Quoted 5 lines · Serge E. Hallyn
struct
uts_namespace{include/linux/utsname.h +1
-0struct kref kref;struct new_utsname name; +struct user_namespace *user_ns; };If a
uts_namespacebelongs to auser_namespace, shouldCLONE_NEWUSERthen implyCLONE_NEWUTS?
Quoted 2 lines
Or is
uts_namespace::user_nsmore an implication that the set of users inuser_namespaceare the only ones authorised to alter the uts data.
The later.
Quoted 9 lines
I presume that the
uts_namespaceof a process must be owned by one of theuser_namespacesin the alternating inheritance chain of namespaces and their creators leading fromcurrent_user_ns()toinit_user_ns.With that in mind, looking at patch 3:
(quoted diff) +1
-1- if (!capable(CAP_SYS_ADMIN)) + if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))
Quoted 3 lines
what is it you're actually asking? I presume it's 'does this user have
CAP_SYS_ADMINcapability over objects belonging to the uts_namespace'suser_namespace?'
Yes.
Quoted 33 lines
So, to look at the important bit of patch 2:
(quoted diff) +25
-3-int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, - int audit) +int cap_capable(struct task_struct *tsk, const struct cred *cred, + struct user_namespace *targ_ns, int cap, int audit) { - return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; + for (;;) { + /* The creator of the user namespace has all caps. */ + if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) + return 0; + + /* Do we have the necessary capabilities? */ + if (targ_ns == cred->user->user_ns) + return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; + + /* Have we tried all of the parent namespaces? */ + if (targ_ns == &init_user_ns) + return -EPERM; + + /* If you have the capability in a parent user ns you have it + * in the over all children user namespaces as well, so see + * if this process has the capability in the parent user + * namespace. + */ + targ_ns = targ_ns->creator->user_ns; + } + + /* We never get here */ + return -EPERM; }
Quoted 8 lines
On entry, as we're called from
ns_capable(),cred->useris the user that the current process is running as, and, as such, may be in a separate namespace fromuts_namespace- which may itself be in a separate namespace frominit_user_ns.So, assume for the sake of argument that there are three
user_namespacesalong the chain from the calling process to the root, and that theuts_namespacebelongs to the middle one.
So we have the nested stack of:user_ns3->creator->user_ns == user_ns2user_ns2->creator->user_ns == &init_user_nsuts_ns2->user_ns == user_ns2
Quoted 7 lines
if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) return 0;Can never match because
targ_ns->creatorcannot becred->user; even if theuts_namespacebelongs to our namespace, given that the creator lies outside our namespace.
Initially we come in with targ_ns == user_ns2 and cred->user->user_ns in one of (user_ns3, user_ns2, or &init_user_ns).
targ_ns takes on values user_ns2 and &init_user_ns.
So when targ_ns becomes &init_user_ns. If the user in question is uts_ns2->user_ns->creator. This check will indeed match.
Quoted 5 lines
if (targ_ns == cred->user->user_ns) return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;Can only match if we are in the target
user_namespace(ie. the one to whichuts_namespacebelongs), whether or not we haveCAP_SYS_ADMIN.
As before targ_ns takes on values of user_ns2 and &init_user_ns.
Which means this check will match if we have CAP_SYS_ADMIN in &init_user_ns or in user_ns2.
Quoted 2 lines
Which means that unless the
uts_namespacebelongs to ouruser_namespace, we cannot change it. Is that correct?
No. If you are root in a parent namespace you can also change it.
Quoted 3 lines
So
ns_capable()restricts you to only doing interesting things to objects that belong to auser_namespaceif they are in your ownuser_namespace. Is that correct?
No. Root outside your user namespace is also allowed to do interesting things.
Quoted 1 line
If that is so, is the loop required for
ns_capable()?
Yes.
Quoted 19 lines
Looking further at patch 2:
#define nsown_capable(cap) (ns_capable(current_user_ns(), (cap)))Given what I've said above, I presume the loop isn't necessary here either.
I think you're using
ns_capable()in two different ways:(1) You're using it to see if a process has power over its descendents in a
user_namespacethat can be traced back to aclone()that it did withCLONE_NEWUSER.For example, automatically granting a process permission to kill descendents in a namespace it created.
(2) You're using it to see if a process can access objects that might be outside its own
user_namespace.
Quoted 8 lines
For example, setting the hostname.
Is it worth giving two different interfaces to make this clearer (even if they actually do the same thing)?
Sorry if this seems rambly, but I'm trying to get my head round your code.
I am all for making that loop a little clearer, because it is something you have to pause and think about to understand but so far I don't think the loop is wrong, and it is simple.
Eric
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
Quoted 4 lines · Eric W. Biederman
Quoted 2 lines
Which means that unless the
uts_namespacebelongs to ouruser_namespace, we cannot change it. Is that correct?No. If you are root in a parent namespace you can also change it.
But surely, by definition, if you're a user in this namespace, you can't also be root in a parent namespace...
For the case I worked through current_user() is a member of current_user_ns() and can't also be a member of its parent, grandparent, etc. - or can it?
David
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
Quoted 9 lines · David Howells
Quoted 4 lines · Eric W. Biederman
Quoted 2 lines
Which means that unless the
uts_namespacebelongs to ouruser_namespace, we cannot change it. Is that correct?No. If you are root in a parent namespace you can also change it.
But surely, by definition, if you're a user in this namespace, you can't also be root in a parent namespace...
To be clear the case you looked at was:
Quoted 6 lines
(quoted diff) +1
-1- if (!capable(CAP_SYS_ADMIN)) + if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))what is it you're actually asking? I presume it's 'does this user have
CAP_SYS_ADMINcapability over objects belonging to the uts_namespace'suser_namespace?'
Here "current->nsproxy->uts_ns->user_ns" (the target_ns value) is only refers to the uts_ns we are talking about.
The user itself comes from current_user().
Quoted 3 lines
For the case I worked through
current_user()is a member ofcurrent_user_ns()and can't also be a member of its parent, grandparent, etc. - or can it?
Right now if you are looking at current_user() because of limitations in the creation ordering I think you are correct.
However in the near term pile of changes to merge, are the syscalls for joining an existing namespace. At which point there is no reason in general to suppose the current limitations of creation apply.
Although it is conceivable that unshare of namespaces can also get you to someplace similar to joining prexisting namespaces.
Eric
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
In reply to David HowellsQuoted 9 lines · David Howells
Quoted 4 lines · Eric W. Biederman
Quoted 2 lines
Which means that unless the
uts_namespacebelongs to ouruser_namespace, we cannot change it. Is that correct?No. If you are root in a parent namespace you can also change it.
But surely, by definition, if you're a user in this namespace, you can't also be root in a parent namespace...
To be clear the case you looked at was:
Quoted 6 lines
(quoted diff) +1
-1- if (!capable(CAP_SYS_ADMIN)) + if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))what is it you're actually asking? I presume it's 'does this user have
CAP_SYS_ADMINcapability over objects belonging to the uts_namespace'suser_namespace?'
Here "current->nsproxy->uts_ns->user_ns" (the target_ns value) is only refers to the uts_ns we are talking about.
The user itself comes from current_user().
Quoted 3 lines
For the case I worked through
current_user()is a member ofcurrent_user_ns()and can't also be a member of its parent, grandparent, etc. - or can it?
Right now if you are looking at current_user() because of limitations in the creation ordering I think you are correct.
However in the near term pile of changes to merge, are the syscalls for joining an existing namespace. At which point there is no reason in general to suppose the current limitations of creation apply.
Although it is conceivable that unshare of namespaces can also get you to someplace similar to joining prexisting namespaces.
Eric
A message that is not in the archive (sent off-list or not yet received): m1lj16ih0n.fsf-+imSwln9KH6u2/kzUuoCbdi2O/[email protected]
Re: [PATCH 1/9] Add a user_namespace as creator/owner of uts_namespace
Quoted 4 lines · Eric W. Biederman
Quoted 2 lines
Which means that unless the
uts_namespacebelongs to ouruser_namespace, we cannot change it. Is that correct?No. If you are root in a parent namespace you can also change it.
But surely, by definition, if you're a user in this namespace, you can't also be root in a parent namespace...
For the case I worked through current_user() is a member of current_user_ns() and can't also be a member of its parent, grandparent, etc. - or can it?
David
Serge E. Hallyn
maintainerPATCH 2/9 security: Make capabilities relative to the user namespace.
In reply to Serge E. Hallyn- Introduce ns_capable to test for a capability in a non-default user namespace.
- Teach cap_capable to handle capabilities in a non-default user namespace.
The motivation is to get to the unprivileged creation of new namespaces. It looks like this gets us 90% of the way there, with only potential uid confusion issues left.
I still need to handle getting all caps after creation but otherwise I think I have a good starter patch that achieves all of your goals.
Changelog:
11/05/2010: [serge] add apparmor
12/14/2010: [serge] fix capabilities to created user namespaces Without this, if user serge creates a user_ns, he won't have capabilities to the user_ns he created. THis is because we were first checking whether his effective caps had the caps
he needed and returning -EPERM if not, and THEN checking whether he was the creator. Reverse those checks.
12/16/2010: [serge] security_real_capable needs ns argument in !security case 01/11/2011: [serge] add task_ns_capable helper
01/11/2011: [serge] add nsown_capable() helper per Bastian Blank suggestion 02/16/2011: [serge] fix a logic bug: the root user is always creator of
init_user_ns, but should not always have capabilities to it! Fix the check in cap_capable().
--- include/linux/capability.h | 10 ++++++++-- include/linux/security.h | 25 ++++++++++++++----------- kernel/capability.c | 32 ++++++++++++++++++++++++++++++-- security/apparmor/lsm.c | 5 +++-- security/commoncap.c | 40 +++++++++++++++++++++++++++++++++------- security/security.c | 16 ++++++++++------ security/selinux/hooks.c | 14 +++++++++----- 7 files changed, 107 insertions(+), 35 deletions(-)
include/linux/capability.h +8 -2
@@ -544,7 +544,7 @@ extern const kernel_cap_t __cap_init_eff_set;* * Note that this does not set PF_SUPERPRIV on the task. */ -#define has_capability (t, cap) (security_real_capable((t), (cap)) == 0) +#define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited)
@@ -558,9 +558,15 @@ extern const kernel_cap_t __cap_init_eff_set;* Note that this does not set PF_SUPERPRIV on the task. */ #define has_capability_noaudit (t, cap) \ - (security_real_capable_noaudit((t), (cap)) == 0) + (security_real_capable_noaudit((t), &init_user_ns, (cap)) == 0) +struct user_namespace ; +extern struct user_namespace init_user_ns;extern int capable (int cap); +extern int ns_capable (struct user_namespace *ns,int cap); +extern int task_ns_capable (struct task_struct *t,int cap); + +#define nsown_capable (cap) (ns_capable(current_user_ns(), (cap)))/* audit system wants to get cap info from files as well */ struct dentry ;
include/linux/security.h +14 -11
@@ -46,13 +46,14 @@struct ctl_table ;struct audit_krule ; +struct user_namespace ;/* * These functions are in security/capability.c and are used * as the default capabilities functions */ extern int cap_capable (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);extern int cap_settime (struct timespec *ts,struct timezone *tz);extern int cap_ptrace_access_check (struct task_struct *child,unsigned int mode);extern int cap_ptrace_traceme (struct task_struct *parent);
@@ -1254,6 +1255,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)* credentials. * @tsk contains the task_struct for the process. * @cred contains the credentials to use. +* @ns contains the user namespace we want the capability in * @cap contains the capability <include/linux/capability.h>. * @audit: Whether to write an audit message or not * Return 0 if the capability is granted for @tsk.
@@ -1382,7 +1384,7 @@ struct security_operations {const kernel_cap_t *inheritable, const kernel_cap_t *permitted);int (*capable) (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);int (*sysctl) (struct ctl_table *table,int op);int (*quotactl) (int cmds,int type,int id,struct super_block *sb);int (*quota_on) (struct dentry *dentry);
@@ -1662,9 +1664,9 @@ int security_capset(struct cred *new, const struct cred *old,const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); -int security_capable (const struct cred *cred,int cap); -int security_real_capable (struct task_struct *tsk,int cap); -int security_real_capable_noaudit (struct task_struct *tsk,int cap); +int security_capable (struct user_namespace *ns,const struct cred *cred,int cap); +int security_real_capable (struct task_struct *tsk,struct user_namespace *ns,int cap); +int security_real_capable_noaudit (struct task_struct *tsk,struct user_namespace *ns,int cap);int security_sysctl (struct ctl_table *table,int op);int security_quotactl (int cmds,int type,int id,struct super_block *sb);int security_quota_on (struct dentry *dentry);
@@ -1856,28 +1858,29 @@ static inline int security_capset(struct cred *new,return cap_capset (new, old, effective, inheritable, permitted); } -static inline int security_capable (const struct cred *cred,int cap) +static inline int security_capable (struct user_namespace *ns, +const struct cred *cred,int cap) { -return cap_capable (current, cred, cap,SECURITY_CAP_AUDIT ); +return cap_capable (current, cred, ns, cap,SECURITY_CAP_AUDIT ); } -static inline int security_real_capable (struct task_struct *tsk,int cap) +static inline int security_real_capable (struct task_struct *tsk,struct user_namespace *ns,int cap) {int ret;rcu_read_lock (); - ret =cap_capable (tsk,__task_cred (tsk), cap,SECURITY_CAP_AUDIT ); + ret =cap_capable (tsk,__task_cred (tsk), ns, cap,SECURITY_CAP_AUDIT );rcu_read_unlock ();return ret; }static inline -int security_real_capable_noaudit (struct task_struct *tsk,int cap) +int security_real_capable_noaudit (struct task_struct *tsk,struct user_namespace *ns,int cap) {int ret;rcu_read_lock (); - ret =cap_capable (tsk,__task_cred (tsk), cap, + ret =cap_capable (tsk,__task_cred (tsk), ns, cap,SECURITY_CAP_NOAUDIT );rcu_read_unlock ();return ret;
kernel/capability.c +30 -2
@@ -14,6 +14,7 @@#include <linux/security.h> #include <linux/syscalls.h> #include <linux/pid_namespace.h> +#include <linux/user_namespace.h> #include <asm/uaccess.h> /*
@@ -301,15 +302,42 @@ error:*/ int capable (int cap) { +return ns_capable (&init_user_ns, cap); +} +EXPORT_SYMBOL (capable); + +/** +* ns_capable - Determine if the current task has a superior capability in effect +* @ns: The usernamespace we want the capability in +* @cap: The capability to be tested for +* +* Return true if the current task has the given superior capability currently +* available for use, false if not. +* +* This sets PF_SUPERPRIV on the task if the capability is available on the +* assumption that it's about to be used. +*/ +int ns_capable (struct user_namespace *ns,int cap) +{if (unlikely (!cap_valid (cap))) {printk (KERN_CRIT "capable() called with invalid cap=%u\n" , cap);BUG (); } -if (security_capable (current_cred (), cap) ==0 ) { +if (security_capable (ns,current_cred (), cap) ==0 ) { current->flags |=PF_SUPERPRIV ;return 1 ; }return 0 ; } -EXPORT_SYMBOL (capable); +EXPORT_SYMBOL (ns_capable); + +/* +* does current have capability 'cap' to the user namespace of task +* 't'. Return true if it does, false otherwise. +*/ +int task_ns_capable (struct task_struct *t,int cap) +{ +return ns_capable (task_cred_xxx (t, user)->user_ns, cap); +} +EXPORT_SYMBOL (task_ns_capable);
security/apparmor/lsm.c +3 -2
@@ -22,6 +22,7 @@#include <linux/ctype.h> #include <linux/sysctl.h> #include <linux/audit.h> +#include <linux/user_namespace.h> #include <net/sock.h> #include "include/apparmor.h"
@@ -136,11 +137,11 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, }static int apparmor_capable (struct task_struct *task,const struct cred *cred, -int cap,int audit) +struct user_namespace *ns,int cap,int audit) {struct aa_profile *profile;/* cap_capable returns 0 on success, else -EPERM */ -int error =cap_capable (task, cred, cap, audit); +int error =cap_capable (task, cred, ns, cap, audit);if (!error) { profile =aa_cred_profile (cred);if (!unconfined (profile))
security/commoncap.c +33 -7
@@ -27,6 +27,7 @@#include <linux/sched.h> #include <linux/prctl.h> #include <linux/securebits.h> +#include <linux/user_namespace.h> /* * If a non-root user executes a setuid-root binary in
@@ -68,6 +69,7 @@ EXPORT_SYMBOL(cap_netlink_recv);* cap_capable - Determine whether a task has a particular effective capability * @tsk: The task to query * @cred: The credentials to use +* @ns: The user namespace in which we need the capability * @cap: The capability to check for * @audit: Whether to write an audit message or not *
@@ -79,10 +81,32 @@ EXPORT_SYMBOL(cap_netlink_recv);* cap_has_capability() returns 0 when a task has a capability, but the * kernel's capable() and has_capability() returns 1 for this case. */ -int cap_capable (struct task_struct *tsk,const struct cred *cred,int cap, -int audit) +int cap_capable (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *targ_ns,int cap,int audit) { -return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; +for (;;) { +/* The creator of the user namespace has all caps. */ +if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +return 0 ; + +/* Do we have the necessary capabilities? */ +if (targ_ns == cred->user->user_ns) +return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; + +/* Have we tried all of the parent namespaces? */ +if (targ_ns == &init_user_ns) +return -EPERM ; + +/* If you have the capability in a parent user ns you have it +* in the over all children user namespaces as well, so see +* if this process has the capability in the parent user +* namespace. +*/ + targ_ns = targ_ns->creator->user_ns; + } + +/* We never get here */ +return -EPERM ; }/**
@@ -177,7 +201,8 @@ static inline int cap_inh_is_capped(void)/* they are so limited unless the current task has the CAP_SETPCAP * capability */ -if (cap_capable (current,current_cred (),CAP_SETPCAP , +if (cap_capable (current,current_cred (), +current_cred ()->user->user_ns,CAP_SETPCAP ,SECURITY_CAP_AUDIT ) ==0 )return 0 ;return 1 ;
@@ -829,7 +854,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, & (new->securebits ^ arg2)) /*[1]*/ || ((new->securebits &SECURE_ALL_LOCKS & ~arg2))/*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS |SECURE_ALL_BITS ))/*[3]*/ - || (cap_capable (current,current_cred (),CAP_SETPCAP , + || (cap_capable (current,current_cred (), +current_cred ()->user->user_ns,CAP_SETPCAP ,SECURITY_CAP_AUDIT ) !=0 )/*[4]*/ /* * [1] no changing of bits that are locked
@@ -894,7 +920,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) {int cap_sys_admin =0 ; -if (cap_capable (current,current_cred (),CAP_SYS_ADMIN , +if (cap_capable (current,current_cred (), &init_user_ns,CAP_SYS_ADMIN ,SECURITY_CAP_NOAUDIT ) ==0 ) cap_sys_admin =1 ;return __vm_enough_memory (mm, pages, cap_sys_admin);
@@ -921,7 +947,7 @@ int cap_file_mmap(struct file *file, unsigned long reqprot,int ret =0 ;if (addr < dac_mmap_min_addr) { - ret =cap_capable (current,current_cred (),CAP_SYS_RAWIO , + ret =cap_capable (current,current_cred (), &init_user_ns,CAP_SYS_RAWIO ,SECURITY_CAP_AUDIT );/* set PF_SUPERPRIV if it turns out we allow the low mmap */ if (ret ==0 )
security/security.c +10 -6
@@ -154,29 +154,33 @@ int security_capset(struct cred *new, const struct cred *old, effective, inheritable, permitted); } -int security_capable (const struct cred *cred,int cap) +int security_capable (struct user_namespace *ns,const struct cred *cred, +int cap) { -return security_ops->capable (current, cred, cap,SECURITY_CAP_AUDIT ); +return security_ops->capable (current, cred, ns, cap, +SECURITY_CAP_AUDIT ); } -int security_real_capable (struct task_struct *tsk,int cap) +int security_real_capable (struct task_struct *tsk,struct user_namespace *ns, +int cap) {const struct cred *cred;int ret; cred =get_task_cred (tsk); - ret = security_ops->capable (tsk, cred, cap,SECURITY_CAP_AUDIT ); + ret = security_ops->capable (tsk, cred, ns, cap,SECURITY_CAP_AUDIT );put_cred (cred);return ret; } -int security_real_capable_noaudit (struct task_struct *tsk,int cap) +int security_real_capable_noaudit (struct task_struct *tsk, +struct user_namespace *ns,int cap) {const struct cred *cred;int ret; cred =get_task_cred (tsk); - ret = security_ops->capable (tsk, cred, cap,SECURITY_CAP_NOAUDIT ); + ret = security_ops->capable (tsk, cred, ns, cap,SECURITY_CAP_NOAUDIT );put_cred (cred);return ret; }
security/selinux/hooks.c +9 -5
@@ -77,6 +77,7 @@#include <linux/mutex.h> #include <linux/posix-timers.h> #include <linux/syslog.h> +#include <linux/user_namespace.h> #include "avc.h" #include "objsec.h"
@@ -1423,6 +1424,7 @@ static int current_has_perm(const struct task_struct *tsk,/* Check whether a task is allowed to use a capability. */ static int task_has_capability (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *ns,int cap,int audit) {struct common_audit_data ad;
@@ -1851,15 +1853,15 @@ static int selinux_capset(struct cred *new, const struct cred *old,*/ static int selinux_capable (struct task_struct *tsk,const struct cred *cred, -int cap,int audit) +struct user_namespace *ns,int cap,int audit) {int rc; - rc =cap_capable (tsk, cred, cap, audit); + rc =cap_capable (tsk, cred, ns, cap, audit);if (rc)return rc; -return task_has_capability (tsk, cred, cap, audit); +return task_has_capability (tsk, cred, ns, cap, audit); }static int selinux_sysctl_get_sid (ctl_table *table,u16 tclass,u32 *sid)
@@ -2012,7 +2014,8 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) {int rc, cap_sys_admin =0 ; - rc =selinux_capable (current,current_cred (),CAP_SYS_ADMIN , + rc =selinux_capable (current,current_cred (), + &init_user_ns,CAP_SYS_ADMIN ,SECURITY_CAP_NOAUDIT );if (rc ==0 ) cap_sys_admin =1 ;
@@ -2829,7 +2832,8 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name* and lack of permission just means that we fall back to the * in-core context value, not a denial. */ - error =selinux_capable (current,current_cred (),CAP_MAC_ADMIN , + error =selinux_capable (current,current_cred (), + &init_user_ns,CAP_MAC_ADMIN ,SECURITY_CAP_NOAUDIT );if (!error) error = security_sid_to_context_force(isec->sid, &context,
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Eric W. Biederman
Acked-byRe: [PATCH 2/9] security: Make capabilities relative to the user namespace.
Quoted 11 lines · Serge E. Hallyn
- Introduce
ns_capableto test for a capability in a non-default user namespace.
- Teachcap_capableto handle capabilities in a non-default user namespace.The motivation is to get to the unprivileged creation of new namespaces. It looks like this gets us 90% of the way there, with only potential uid confusion issues left.
I still need to handle getting all caps after creation but otherwise I think I have a good starter patch that achieves all of your goals.
Quoted 424 lines
Changelog:
11/05/2010: [serge] add apparmor
12/14/2010: [serge] fix capabilities to created user namespaces Without this, if user serge creates auser_ns, he won't have capabilities to theuser_nshe created. THis is because we were first checking whether his effective caps had the caps
he needed and returning-EPERMif not, and THEN checking whether he was the creator. Reverse those checks.
12/16/2010: [serge]security_real_capableneeds ns argument in !security case 01/11/2011: [serge] addtask_ns_capablehelper
01/11/2011: [serge] addnsown_capable()helper per Bastian Blank suggestion 02/16/2011: [serge] fix a logic bug: the root user is always creator of
init_user_ns, but should not always have capabilities to it! Fix the check incap_capable().Signed-off-by Serge E. Hallyn <[email protected]>--- include/linux/capability.h | 10 ++++++++-- include/linux/security.h | 25 ++++++++++++++----------- kernel/capability.c | 32 ++++++++++++++++++++++++++++++-- security/apparmor/lsm.c | 5 +++-- security/commoncap.c | 40 +++++++++++++++++++++++++++++++++------- security/security.c | 16 ++++++++++------ security/selinux/hooks.c | 14 +++++++++----- 7 files changed, 107 insertions(+), 35 deletions(-)include/linux/capability.h +8
-2@@ -544,7 +544,7 @@ extern const kernel_cap_t __cap_init_eff_set;* * Note that this does not set PF_SUPERPRIV on the task. */ -#define has_capability (t, cap) (security_real_capable((t), (cap)) == 0) +#define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) @@ -558,9 +558,15 @@ extern const kernel_cap_t __cap_init_eff_set;* Note that this does not set PF_SUPERPRIV on the task. */ #define has_capability_noaudit (t, cap) \ - (security_real_capable_noaudit((t), (cap)) == 0) + (security_real_capable_noaudit((t), &init_user_ns, (cap)) == 0) +struct user_namespace ; +extern struct user_namespace init_user_ns;extern int capable (int cap); +extern int ns_capable (struct user_namespace *ns,int cap); +extern int task_ns_capable (struct task_struct *t,int cap); + +#define nsown_capable (cap) (ns_capable(current_user_ns(), (cap)))/* audit system wants to get cap info from files as well */ struct dentry ;include/linux/security.h +14
-11@@ -46,13 +46,14 @@struct ctl_table ;struct audit_krule ; +struct user_namespace ;/* * These functions are in security/capability.c and are used * as the default capabilities functions */ extern int cap_capable (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);extern int cap_settime (struct timespec *ts,struct timezone *tz);extern int cap_ptrace_access_check (struct task_struct *child,unsigned int mode);extern int cap_ptrace_traceme (struct task_struct *parent);@@ -1254,6 +1255,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)* credentials. * @tsk contains the task_struct for the process. * @cred contains the credentials to use. +* @ns contains the user namespace we want the capability in * @cap contains the capability <include/linux/capability.h>. * @audit: Whether to write an audit message or not * Return 0 if the capability is granted for @tsk. @@ -1382,7 +1384,7 @@ struct security_operations {const kernel_cap_t *inheritable, const kernel_cap_t *permitted);int (*capable) (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);int (*sysctl) (struct ctl_table *table,int op);int (*quotactl) (int cmds,int type,int id,struct super_block *sb);int (*quota_on) (struct dentry *dentry);@@ -1662,9 +1664,9 @@ int security_capset(struct cred *new, const struct cred *old,const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); -int security_capable (const struct cred *cred,int cap); -int security_real_capable (struct task_struct *tsk,int cap); -int security_real_capable_noaudit (struct task_struct *tsk,int cap); +int security_capable (struct user_namespace *ns,const struct cred *cred,int cap); +int security_real_capable (struct task_struct *tsk,struct user_namespace *ns,int cap); +int security_real_capable_noaudit (struct task_struct *tsk,struct user_namespace *ns,int cap);int security_sysctl (struct ctl_table *table,int op);int security_quotactl (int cmds,int type,int id,struct super_block *sb);int security_quota_on (struct dentry *dentry);@@ -1856,28 +1858,29 @@ static inline int security_capset(struct cred *new,return cap_capset (new, old, effective, inheritable, permitted); } -static inline int security_capable (const struct cred *cred,int cap) +static inline int security_capable (struct user_namespace *ns, +const struct cred *cred,int cap) { -return cap_capable (current, cred, cap,SECURITY_CAP_AUDIT ); +return cap_capable (current, cred, ns, cap,SECURITY_CAP_AUDIT ); } -static inline int security_real_capable (struct task_struct *tsk,int cap) +static inline int security_real_capable (struct task_struct *tsk,struct user_namespace *ns,int cap) {int ret;rcu_read_lock (); - ret =cap_capable (tsk,__task_cred (tsk), cap,SECURITY_CAP_AUDIT ); + ret =cap_capable (tsk,__task_cred (tsk), ns, cap,SECURITY_CAP_AUDIT );rcu_read_unlock ();return ret; }static inline -int security_real_capable_noaudit (struct task_struct *tsk,int cap) +int security_real_capable_noaudit (struct task_struct *tsk,struct user_namespace *ns,int cap) {int ret;rcu_read_lock (); - ret =cap_capable (tsk,__task_cred (tsk), cap, + ret =cap_capable (tsk,__task_cred (tsk), ns, cap,SECURITY_CAP_NOAUDIT );rcu_read_unlock ();return ret;kernel/capability.c +30
-2@@ -14,6 +14,7 @@#include <linux/security.h> #include <linux/syscalls.h> #include <linux/pid_namespace.h> +#include <linux/user_namespace.h> #include <asm/uaccess.h> /* @@ -301,15 +302,42 @@ error:*/ int capable (int cap) { +return ns_capable (&init_user_ns, cap); +} +EXPORT_SYMBOL (capable); + +/** +* ns_capable - Determine if the current task has a superior capability in effect +* @ns: The usernamespace we want the capability in +* @cap: The capability to be tested for +* +* Return true if the current task has the given superior capability currently +* available for use, false if not. +* +* This sets PF_SUPERPRIV on the task if the capability is available on the +* assumption that it's about to be used. +*/ +int ns_capable (struct user_namespace *ns,int cap) +{if (unlikely (!cap_valid (cap))) {printk (KERN_CRIT "capable() called with invalid cap=%u\n" , cap);BUG (); } -if (security_capable (current_cred (), cap) ==0 ) { +if (security_capable (ns,current_cred (), cap) ==0 ) { current->flags |=PF_SUPERPRIV ;return 1 ; }return 0 ; } -EXPORT_SYMBOL (capable); +EXPORT_SYMBOL (ns_capable); + +/* +* does current have capability 'cap' to the user namespace of task +* 't'. Return true if it does, false otherwise. +*/ +int task_ns_capable (struct task_struct *t,int cap) +{ +return ns_capable (task_cred_xxx (t, user)->user_ns, cap); +} +EXPORT_SYMBOL (task_ns_capable);security/apparmor/lsm.c +3
-2@@ -22,6 +22,7 @@#include <linux/ctype.h> #include <linux/sysctl.h> #include <linux/audit.h> +#include <linux/user_namespace.h> #include <net/sock.h> #include "include/apparmor.h" @@ -136,11 +137,11 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, }static int apparmor_capable (struct task_struct *task,const struct cred *cred, -int cap,int audit) +struct user_namespace *ns,int cap,int audit) {struct aa_profile *profile;/* cap_capable returns 0 on success, else -EPERM */ -int error =cap_capable (task, cred, cap, audit); +int error =cap_capable (task, cred, ns, cap, audit);if (!error) { profile =aa_cred_profile (cred);if (!unconfined (profile))security/commoncap.c +33
-7@@ -27,6 +27,7 @@#include <linux/sched.h> #include <linux/prctl.h> #include <linux/securebits.h> +#include <linux/user_namespace.h> /* * If a non-root user executes a setuid-root binary in @@ -68,6 +69,7 @@ EXPORT_SYMBOL(cap_netlink_recv);* cap_capable - Determine whether a task has a particular effective capability * @tsk: The task to query * @cred: The credentials to use +* @ns: The user namespace in which we need the capability * @cap: The capability to check for * @audit: Whether to write an audit message or not * @@ -79,10 +81,32 @@ EXPORT_SYMBOL(cap_netlink_recv);* cap_has_capability() returns 0 when a task has a capability, but the * kernel's capable() and has_capability() returns 1 for this case. */ -int cap_capable (struct task_struct *tsk,const struct cred *cred,int cap, -int audit) +int cap_capable (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *targ_ns,int cap,int audit) { -return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; +for (;;) { +/* The creator of the user namespace has all caps. */ +if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +return 0 ; + +/* Do we have the necessary capabilities? */ +if (targ_ns == cred->user->user_ns) +return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; + +/* Have we tried all of the parent namespaces? */ +if (targ_ns == &init_user_ns) +return -EPERM ; + +/* If you have the capability in a parent user ns you have it +* in the over all children user namespaces as well, so see +* if this process has the capability in the parent user +* namespace. +*/ + targ_ns = targ_ns->creator->user_ns; + } + +/* We never get here */ +return -EPERM ; }/** @@ -177,7 +201,8 @@ static inline int cap_inh_is_capped(void)/* they are so limited unless the current task has the CAP_SETPCAP * capability */ -if (cap_capable (current,current_cred (),CAP_SETPCAP , +if (cap_capable (current,current_cred (), +current_cred ()->user->user_ns,CAP_SETPCAP ,SECURITY_CAP_AUDIT ) ==0 )return 0 ;return 1 ;@@ -829,7 +854,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, & (new->securebits ^ arg2)) /*[1]*/ || ((new->securebits &SECURE_ALL_LOCKS & ~arg2))/*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS |SECURE_ALL_BITS ))/*[3]*/ - || (cap_capable (current,current_cred (),CAP_SETPCAP , + || (cap_capable (current,current_cred (), +current_cred ()->user->user_ns,CAP_SETPCAP ,SECURITY_CAP_AUDIT ) !=0 )/*[4]*/ /* * [1] no changing of bits that are locked @@ -894,7 +920,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) {int cap_sys_admin =0 ; -if (cap_capable (current,current_cred (),CAP_SYS_ADMIN , +if (cap_capable (current,current_cred (), &init_user_ns,CAP_SYS_ADMIN ,SECURITY_CAP_NOAUDIT ) ==0 ) cap_sys_admin =1 ;return __vm_enough_memory (mm, pages, cap_sys_admin);@@ -921,7 +947,7 @@ int cap_file_mmap(struct file *file, unsigned long reqprot,int ret =0 ;if (addr < dac_mmap_min_addr) { - ret =cap_capable (current,current_cred (),CAP_SYS_RAWIO , + ret =cap_capable (current,current_cred (), &init_user_ns,CAP_SYS_RAWIO ,SECURITY_CAP_AUDIT );/* set PF_SUPERPRIV if it turns out we allow the low mmap */ if (ret ==0 )security/security.c +10
-6@@ -154,29 +154,33 @@ int security_capset(struct cred *new, const struct cred *old, effective, inheritable, permitted); } -int security_capable (const struct cred *cred,int cap) +int security_capable (struct user_namespace *ns,const struct cred *cred, +int cap) { -return security_ops->capable (current, cred, cap,SECURITY_CAP_AUDIT ); +return security_ops->capable (current, cred, ns, cap, +SECURITY_CAP_AUDIT ); } -int security_real_capable (struct task_struct *tsk,int cap) +int security_real_capable (struct task_struct *tsk,struct user_namespace *ns, +int cap) {const struct cred *cred;int ret; cred =get_task_cred (tsk); - ret = security_ops->capable (tsk, cred, cap,SECURITY_CAP_AUDIT ); + ret = security_ops->capable (tsk, cred, ns, cap,SECURITY_CAP_AUDIT );put_cred (cred);return ret; } -int security_real_capable_noaudit (struct task_struct *tsk,int cap) +int security_real_capable_noaudit (struct task_struct *tsk, +struct user_namespace *ns,int cap) {const struct cred *cred;int ret; cred =get_task_cred (tsk); - ret = security_ops->capable (tsk, cred, cap,SECURITY_CAP_NOAUDIT ); + ret = security_ops->capable (tsk, cred, ns, cap,SECURITY_CAP_NOAUDIT );put_cred (cred);return ret; }security/selinux/hooks.c +9
-5@@ -77,6 +77,7 @@#include <linux/mutex.h> #include <linux/posix-timers.h> #include <linux/syslog.h> +#include <linux/user_namespace.h> #include "avc.h" #include "objsec.h" @@ -1423,6 +1424,7 @@ static int current_has_perm(const struct task_struct *tsk,/* Check whether a task is allowed to use a capability. */ static int task_has_capability (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *ns,int cap,int audit) {struct common_audit_data ad;@@ -1851,15 +1853,15 @@ static int selinux_capset(struct cred *new, const struct cred *old,*/ static int selinux_capable (struct task_struct *tsk,const struct cred *cred, -int cap,int audit) +struct user_namespace *ns,int cap,int audit) {int rc; - rc =cap_capable (tsk, cred, cap, audit); + rc =cap_capable (tsk, cred, ns, cap, audit);if (rc)return rc; -return task_has_capability (tsk, cred, cap, audit); +return task_has_capability (tsk, cred, ns, cap, audit); }static int selinux_sysctl_get_sid (ctl_table *table,u16 tclass,u32 *sid)@@ -2012,7 +2014,8 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) {int rc, cap_sys_admin =0 ; - rc =selinux_capable (current,current_cred (),CAP_SYS_ADMIN , + rc =selinux_capable (current,current_cred (), + &init_user_ns,CAP_SYS_ADMIN ,SECURITY_CAP_NOAUDIT );if (rc ==0 ) cap_sys_admin =1 ;@@ -2829,7 +2832,8 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name* and lack of permission just means that we fall back to the * in-core context value, not a denial. */ - error =selinux_capable (current,current_cred (),CAP_MAC_ADMIN , + error =selinux_capable (current,current_cred (), + &init_user_ns,CAP_MAC_ADMIN ,SECURITY_CAP_NOAUDIT );if (!error) error = security_sid_to_context_force(isec->sid, &context,
Daniel Lezcano
Acked-byRe: [PATCH 2/9] security: Make capabilities relative to the user namespace.
In reply to a message not in the archiveQuoted 27 lines · Serge E. Hallyn
- Introduce
ns_capableto test for a capability in a non-default user namespace.
- Teachcap_capableto handle capabilities in a non-default user namespace.The motivation is to get to the unprivileged creation of new namespaces. It looks like this gets us 90% of the way there, with only potential uid confusion issues left.
I still need to handle getting all caps after creation but otherwise I think I have a good starter patch that achieves all of your goals.
Changelog:
11/05/2010: [serge] add apparmor
12/14/2010: [serge] fix capabilities to created user namespaces Without this, if user serge creates auser_ns, he won't have capabilities to theuser_nshe created. THis is because we were first checking whether his effective caps had the caps
he needed and returning-EPERMif not, and THEN checking whether he was the creator. Reverse those checks.
12/16/2010: [serge]security_real_capableneeds ns argument in !security case 01/11/2011: [serge] addtask_ns_capablehelper
01/11/2011: [serge] addnsown_capable()helper per Bastian Blank suggestion 02/16/2011: [serge] fix a logic bug: the root user is always creator of
init_user_ns, but should not always have capabilities to it! Fix the check incap_capable().
Quoted 3 lines
Signed-off-by Serge E. Hallyn<[email protected]>---
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
In reply to a message not in the archiveQuoted 27 lines · On Thu, 17 Feb 2011 15:03:06 +0000 "Serge E. Hallyn"…
- Introduce
ns_capableto test for a capability in a non-default user namespace.
- Teachcap_capableto handle capabilities in a non-default user namespace.The motivation is to get to the unprivileged creation of new namespaces. It looks like this gets us 90% of the way there, with only potential uid confusion issues left.
I still need to handle getting all caps after creation but otherwise I think I have a good starter patch that achieves all of your goals.
...
include/linux/capability.h +1
-1@@ -544,7 +544,7 @@ extern const kernel_cap_t __cap_init_eff_set;* * Note that this does not set PF_SUPERPRIV on the task. */ -#define has_capability (t, cap) (security_real_capable((t), (cap)) == 0) +#define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited)
include/linux/capability.h extern const kernel_cap_t __cap_init_eff_set; · line 564
Quoted 8 lines
include/linux/capability.h +3
-1@@ -558,9 +558,15 @@ extern const kernel_cap_t __cap_init_eff_set;* Note that this does not set PF_SUPERPRIV on the task. */ #define has_capability_noaudit (t, cap) \ - (security_real_capable_noaudit((t), (cap)) == 0) + (security_real_capable_noaudit((t), &init_user_ns, (cap)) == 0) +struct user_namespace ; +extern struct user_namespace init_user_ns;
Two icky-should-be-written-in-C macros which reference init_user_ns, followed by the declaration of init_user_ns and its type. Declarations which duplicate those in other header files. It's ripe for some upcleaning, methinks?
Also, please ensure that the forward struct declarations are all at top-of-file (as in include/linux/security.h). Otherwise we can end up accumulating multiple forward declarations of the same thing in the one file.
include/linux/capability.h extern const kernel_cap_t __cap_init_eff_set; · line 569
Quoted 5 lines
include/linux/capability.h +4
-0extern int capable (int cap); +extern int ns_capable (struct user_namespace *ns,int cap); +extern int task_ns_capable (struct task_struct *t,int cap); + +#define nsown_capable (cap) (ns_capable(current_user_ns(), (cap)))
macroitis!
Quoted 5 lines
include/linux/capability.h +1
-0@@ -301,15 +302,42 @@ error:*/ int capable (int cap) { +return ns_capable (&init_user_ns, cap);
include/linux/capability.h error: · line 315
Quoted 10 lines
include/linux/capability.h +10
-0+} +EXPORT_SYMBOL (capable); + +/** +* ns_capable - Determine if the current task has a superior capability in effect +* @ns: The usernamespace we want the capability in +* @cap: The capability to be tested for +* +* Return true if the current task has the given superior capability currently +* available for use, false if not.
Actually it doesn't return true or false - it returns 1 or 0. Using a `bool' return type would fix the comment :)
Quoted 14 lines
include/linux/capability.h +6
-1+* This sets PF_SUPERPRIV on the task if the capability is available on the +* assumption that it's about to be used. +*/ +int ns_capable (struct user_namespace *ns,int cap) +{if (unlikely (!cap_valid (cap))) {printk (KERN_CRIT "capable() called with invalid cap=%u\n" , cap);BUG (); } -if (security_capable (current_cred (), cap) ==0 ) { +if (security_capable (ns,current_cred (), cap) ==0 ) { current->flags |=PF_SUPERPRIV ;return 1 ;
include/linux/capability.h error: · line 337
Quoted 10 lines
include/linux/capability.h +6
-1}return 0 ; } -EXPORT_SYMBOL (capable); +EXPORT_SYMBOL (ns_capable); + +/* +* does current have capability 'cap' to the user namespace of task +* 't'. Return true if it does, false otherwise. +*/
Other comments were kerneldocified.
include/linux/capability.h error: · line 342
Quoted 5 lines
include/linux/capability.h +5
-0+int task_ns_capable (struct task_struct *t,int cap) +{ +return ns_capable (task_cred_xxx (t, user)->user_ns, cap); +} +EXPORT_SYMBOL (task_ns_capable);
Could return bool.
Quoted 20 lines
...
include/linux/capability.h +15
-1+int cap_capable (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *targ_ns,int cap,int audit) { -return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; +for (;;) { +/* The creator of the user namespace has all caps. */ +if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +return 0 ; + +/* Do we have the necessary capabilities? */ +if (targ_ns == cred->user->user_ns) +return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; + +/* Have we tried all of the parent namespaces? */ +if (targ_ns == &init_user_ns) +return -EPERM ; +
include/linux/capability.h
Quoted 10 lines
include/linux/capability.h +10
-0+/* If you have the capability in a parent user ns you have it +* in the over all children user namespaces as well, so see +* if this process has the capability in the parent user +* namespace. +*/ + targ_ns = targ_ns->creator->user_ns; + } + +/* We never get here */ +return -EPERM ;
So delete the code? Or does the compiler warn? If so, it's pretty busted.
Quoted 6 lines
}
/**
...
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
In reply to a message not in the archiveinclude/linux/security.h struct security_operations { · line 1387
Quoted 3 lines · Serge E. Hallyn
include/linux/security.h +1
-1int (*capable) (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);
Hmmm... A chunk of the contents of the cred struct are user-namespaced. Could you add the user_namespace pointer to the cred struct and thus avoid passing it as an argument to other things.
In fact, I think you probably have to do this so that cachefiles, for example, can override the user namespace when it operates on behalf of a process that's in another namespace.
In fact, looking later on in your patch, I see:
security/commoncap.c int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, · line 858
Quoted 3 lines
security/commoncap.c +2
-1- || (cap_capable(current, current_cred(), CAP_SETPCAP, + || (cap_capable(current, current_cred(), + current_cred()->user->user_ns, CAP_SETPCAP,
so the user_ns _is_ already available through the creds. So is there really a need to pass it as an argument to anything that already takes a cred?
Quoted 3 lines
* @cred contains the credentials to use.
+ * @ns contains the user namespace we want the capability in
* @cap contains the capability <include/linux/capability.h>.
That should be tabbed to match the lines either side.
David
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
In reply to a message not in the archivesecurity/commoncap.c EXPORT_SYMBOL(cap_netlink_recv); · line 104
Quoted 5 lines · Serge E. Hallyn
security/commoncap.c +5
-0+/* If you have the capability in a parent user ns you have it +* in the over all children user namespaces as well, so see +* if this process has the capability in the parent user +* namespace. +*/
"... in the over all children user namespaces ..." I think need a couple of words dropping.
David
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
In reply to Serge E. HallynQuoted 11 lines · Serge E. Hallyn
- Introduce
ns_capableto test for a capability in a non-default user namespace.
- Teachcap_capableto handle capabilities in a non-default user namespace.The motivation is to get to the unprivileged creation of new namespaces. It looks like this gets us 90% of the way there, with only potential uid confusion issues left.
I still need to handle getting all caps after creation but otherwise I think I have a good starter patch that achieves all of your goals.
Quoted 424 lines
Changelog:
11/05/2010: [serge] add apparmor
12/14/2010: [serge] fix capabilities to created user namespaces Without this, if user serge creates auser_ns, he won't have capabilities to theuser_nshe created. THis is because we were first checking whether his effective caps had the caps
he needed and returning-EPERMif not, and THEN checking whether he was the creator. Reverse those checks.
12/16/2010: [serge]security_real_capableneeds ns argument in !security case 01/11/2011: [serge] addtask_ns_capablehelper
01/11/2011: [serge] addnsown_capable()helper per Bastian Blank suggestion 02/16/2011: [serge] fix a logic bug: the root user is always creator of
init_user_ns, but should not always have capabilities to it! Fix the check incap_capable().Signed-off-by Eric W. Biederman <[email protected]>Signed-off-by Serge E. Hallyn <[email protected]>--- include/linux/capability.h | 10 ++++++++-- include/linux/security.h | 25 ++++++++++++++----------- kernel/capability.c | 32 ++++++++++++++++++++++++++++++-- security/apparmor/lsm.c | 5 +++-- security/commoncap.c | 40 +++++++++++++++++++++++++++++++++------- security/security.c | 16 ++++++++++------ security/selinux/hooks.c | 14 +++++++++----- 7 files changed, 107 insertions(+), 35 deletions(-)include/linux/capability.h +8
-2@@ -544,7 +544,7 @@ extern const kernel_cap_t __cap_init_eff_set;* * Note that this does not set PF_SUPERPRIV on the task. */ -#define has_capability (t, cap) (security_real_capable((t), (cap)) == 0) +#define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) @@ -558,9 +558,15 @@ extern const kernel_cap_t __cap_init_eff_set;* Note that this does not set PF_SUPERPRIV on the task. */ #define has_capability_noaudit (t, cap) \ - (security_real_capable_noaudit((t), (cap)) == 0) + (security_real_capable_noaudit((t), &init_user_ns, (cap)) == 0) +struct user_namespace ; +extern struct user_namespace init_user_ns;extern int capable (int cap); +extern int ns_capable (struct user_namespace *ns,int cap); +extern int task_ns_capable (struct task_struct *t,int cap); + +#define nsown_capable (cap) (ns_capable(current_user_ns(), (cap)))/* audit system wants to get cap info from files as well */ struct dentry ;include/linux/security.h +14
-11@@ -46,13 +46,14 @@struct ctl_table ;struct audit_krule ; +struct user_namespace ;/* * These functions are in security/capability.c and are used * as the default capabilities functions */ extern int cap_capable (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);extern int cap_settime (struct timespec *ts,struct timezone *tz);extern int cap_ptrace_access_check (struct task_struct *child,unsigned int mode);extern int cap_ptrace_traceme (struct task_struct *parent);@@ -1254,6 +1255,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)* credentials. * @tsk contains the task_struct for the process. * @cred contains the credentials to use. +* @ns contains the user namespace we want the capability in * @cap contains the capability <include/linux/capability.h>. * @audit: Whether to write an audit message or not * Return 0 if the capability is granted for @tsk. @@ -1382,7 +1384,7 @@ struct security_operations {const kernel_cap_t *inheritable, const kernel_cap_t *permitted);int (*capable) (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);int (*sysctl) (struct ctl_table *table,int op);int (*quotactl) (int cmds,int type,int id,struct super_block *sb);int (*quota_on) (struct dentry *dentry);@@ -1662,9 +1664,9 @@ int security_capset(struct cred *new, const struct cred *old,const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); -int security_capable (const struct cred *cred,int cap); -int security_real_capable (struct task_struct *tsk,int cap); -int security_real_capable_noaudit (struct task_struct *tsk,int cap); +int security_capable (struct user_namespace *ns,const struct cred *cred,int cap); +int security_real_capable (struct task_struct *tsk,struct user_namespace *ns,int cap); +int security_real_capable_noaudit (struct task_struct *tsk,struct user_namespace *ns,int cap);int security_sysctl (struct ctl_table *table,int op);int security_quotactl (int cmds,int type,int id,struct super_block *sb);int security_quota_on (struct dentry *dentry);@@ -1856,28 +1858,29 @@ static inline int security_capset(struct cred *new,return cap_capset (new, old, effective, inheritable, permitted); } -static inline int security_capable (const struct cred *cred,int cap) +static inline int security_capable (struct user_namespace *ns, +const struct cred *cred,int cap) { -return cap_capable (current, cred, cap,SECURITY_CAP_AUDIT ); +return cap_capable (current, cred, ns, cap,SECURITY_CAP_AUDIT ); } -static inline int security_real_capable (struct task_struct *tsk,int cap) +static inline int security_real_capable (struct task_struct *tsk,struct user_namespace *ns,int cap) {int ret;rcu_read_lock (); - ret =cap_capable (tsk,__task_cred (tsk), cap,SECURITY_CAP_AUDIT ); + ret =cap_capable (tsk,__task_cred (tsk), ns, cap,SECURITY_CAP_AUDIT );rcu_read_unlock ();return ret; }static inline -int security_real_capable_noaudit (struct task_struct *tsk,int cap) +int security_real_capable_noaudit (struct task_struct *tsk,struct user_namespace *ns,int cap) {int ret;rcu_read_lock (); - ret =cap_capable (tsk,__task_cred (tsk), cap, + ret =cap_capable (tsk,__task_cred (tsk), ns, cap,SECURITY_CAP_NOAUDIT );rcu_read_unlock ();return ret;kernel/capability.c +30
-2@@ -14,6 +14,7 @@#include <linux/security.h> #include <linux/syscalls.h> #include <linux/pid_namespace.h> +#include <linux/user_namespace.h> #include <asm/uaccess.h> /* @@ -301,15 +302,42 @@ error:*/ int capable (int cap) { +return ns_capable (&init_user_ns, cap); +} +EXPORT_SYMBOL (capable); + +/** +* ns_capable - Determine if the current task has a superior capability in effect +* @ns: The usernamespace we want the capability in +* @cap: The capability to be tested for +* +* Return true if the current task has the given superior capability currently +* available for use, false if not. +* +* This sets PF_SUPERPRIV on the task if the capability is available on the +* assumption that it's about to be used. +*/ +int ns_capable (struct user_namespace *ns,int cap) +{if (unlikely (!cap_valid (cap))) {printk (KERN_CRIT "capable() called with invalid cap=%u\n" , cap);BUG (); } -if (security_capable (current_cred (), cap) ==0 ) { +if (security_capable (ns,current_cred (), cap) ==0 ) { current->flags |=PF_SUPERPRIV ;return 1 ; }return 0 ; } -EXPORT_SYMBOL (capable); +EXPORT_SYMBOL (ns_capable); + +/* +* does current have capability 'cap' to the user namespace of task +* 't'. Return true if it does, false otherwise. +*/ +int task_ns_capable (struct task_struct *t,int cap) +{ +return ns_capable (task_cred_xxx (t, user)->user_ns, cap); +} +EXPORT_SYMBOL (task_ns_capable);security/apparmor/lsm.c +3
-2@@ -22,6 +22,7 @@#include <linux/ctype.h> #include <linux/sysctl.h> #include <linux/audit.h> +#include <linux/user_namespace.h> #include <net/sock.h> #include "include/apparmor.h" @@ -136,11 +137,11 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, }static int apparmor_capable (struct task_struct *task,const struct cred *cred, -int cap,int audit) +struct user_namespace *ns,int cap,int audit) {struct aa_profile *profile;/* cap_capable returns 0 on success, else -EPERM */ -int error =cap_capable (task, cred, cap, audit); +int error =cap_capable (task, cred, ns, cap, audit);if (!error) { profile =aa_cred_profile (cred);if (!unconfined (profile))security/commoncap.c +33
-7@@ -27,6 +27,7 @@#include <linux/sched.h> #include <linux/prctl.h> #include <linux/securebits.h> +#include <linux/user_namespace.h> /* * If a non-root user executes a setuid-root binary in @@ -68,6 +69,7 @@ EXPORT_SYMBOL(cap_netlink_recv);* cap_capable - Determine whether a task has a particular effective capability * @tsk: The task to query * @cred: The credentials to use +* @ns: The user namespace in which we need the capability * @cap: The capability to check for * @audit: Whether to write an audit message or not * @@ -79,10 +81,32 @@ EXPORT_SYMBOL(cap_netlink_recv);* cap_has_capability() returns 0 when a task has a capability, but the * kernel's capable() and has_capability() returns 1 for this case. */ -int cap_capable (struct task_struct *tsk,const struct cred *cred,int cap, -int audit) +int cap_capable (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *targ_ns,int cap,int audit) { -return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; +for (;;) { +/* The creator of the user namespace has all caps. */ +if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +return 0 ; + +/* Do we have the necessary capabilities? */ +if (targ_ns == cred->user->user_ns) +return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; + +/* Have we tried all of the parent namespaces? */ +if (targ_ns == &init_user_ns) +return -EPERM ; + +/* If you have the capability in a parent user ns you have it +* in the over all children user namespaces as well, so see +* if this process has the capability in the parent user +* namespace. +*/ + targ_ns = targ_ns->creator->user_ns; + } + +/* We never get here */ +return -EPERM ; }/** @@ -177,7 +201,8 @@ static inline int cap_inh_is_capped(void)/* they are so limited unless the current task has the CAP_SETPCAP * capability */ -if (cap_capable (current,current_cred (),CAP_SETPCAP , +if (cap_capable (current,current_cred (), +current_cred ()->user->user_ns,CAP_SETPCAP ,SECURITY_CAP_AUDIT ) ==0 )return 0 ;return 1 ;@@ -829,7 +854,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, & (new->securebits ^ arg2)) /*[1]*/ || ((new->securebits &SECURE_ALL_LOCKS & ~arg2))/*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS |SECURE_ALL_BITS ))/*[3]*/ - || (cap_capable (current,current_cred (),CAP_SETPCAP , + || (cap_capable (current,current_cred (), +current_cred ()->user->user_ns,CAP_SETPCAP ,SECURITY_CAP_AUDIT ) !=0 )/*[4]*/ /* * [1] no changing of bits that are locked @@ -894,7 +920,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) {int cap_sys_admin =0 ; -if (cap_capable (current,current_cred (),CAP_SYS_ADMIN , +if (cap_capable (current,current_cred (), &init_user_ns,CAP_SYS_ADMIN ,SECURITY_CAP_NOAUDIT ) ==0 ) cap_sys_admin =1 ;return __vm_enough_memory (mm, pages, cap_sys_admin);@@ -921,7 +947,7 @@ int cap_file_mmap(struct file *file, unsigned long reqprot,int ret =0 ;if (addr < dac_mmap_min_addr) { - ret =cap_capable (current,current_cred (),CAP_SYS_RAWIO , + ret =cap_capable (current,current_cred (), &init_user_ns,CAP_SYS_RAWIO ,SECURITY_CAP_AUDIT );/* set PF_SUPERPRIV if it turns out we allow the low mmap */ if (ret ==0 )security/security.c +10
-6@@ -154,29 +154,33 @@ int security_capset(struct cred *new, const struct cred *old, effective, inheritable, permitted); } -int security_capable (const struct cred *cred,int cap) +int security_capable (struct user_namespace *ns,const struct cred *cred, +int cap) { -return security_ops->capable (current, cred, cap,SECURITY_CAP_AUDIT ); +return security_ops->capable (current, cred, ns, cap, +SECURITY_CAP_AUDIT ); } -int security_real_capable (struct task_struct *tsk,int cap) +int security_real_capable (struct task_struct *tsk,struct user_namespace *ns, +int cap) {const struct cred *cred;int ret; cred =get_task_cred (tsk); - ret = security_ops->capable (tsk, cred, cap,SECURITY_CAP_AUDIT ); + ret = security_ops->capable (tsk, cred, ns, cap,SECURITY_CAP_AUDIT );put_cred (cred);return ret; } -int security_real_capable_noaudit (struct task_struct *tsk,int cap) +int security_real_capable_noaudit (struct task_struct *tsk, +struct user_namespace *ns,int cap) {const struct cred *cred;int ret; cred =get_task_cred (tsk); - ret = security_ops->capable (tsk, cred, cap,SECURITY_CAP_NOAUDIT ); + ret = security_ops->capable (tsk, cred, ns, cap,SECURITY_CAP_NOAUDIT );put_cred (cred);return ret; }security/selinux/hooks.c +9
-5@@ -77,6 +77,7 @@#include <linux/mutex.h> #include <linux/posix-timers.h> #include <linux/syslog.h> +#include <linux/user_namespace.h> #include "avc.h" #include "objsec.h" @@ -1423,6 +1424,7 @@ static int current_has_perm(const struct task_struct *tsk,/* Check whether a task is allowed to use a capability. */ static int task_has_capability (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *ns,int cap,int audit) {struct common_audit_data ad;@@ -1851,15 +1853,15 @@ static int selinux_capset(struct cred *new, const struct cred *old,*/ static int selinux_capable (struct task_struct *tsk,const struct cred *cred, -int cap,int audit) +struct user_namespace *ns,int cap,int audit) {int rc; - rc =cap_capable (tsk, cred, cap, audit); + rc =cap_capable (tsk, cred, ns, cap, audit);if (rc)return rc; -return task_has_capability (tsk, cred, cap, audit); +return task_has_capability (tsk, cred, ns, cap, audit); }static int selinux_sysctl_get_sid (ctl_table *table,u16 tclass,u32 *sid)@@ -2012,7 +2014,8 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) {int rc, cap_sys_admin =0 ; - rc =selinux_capable (current,current_cred (),CAP_SYS_ADMIN , + rc =selinux_capable (current,current_cred (), + &init_user_ns,CAP_SYS_ADMIN ,SECURITY_CAP_NOAUDIT );if (rc ==0 ) cap_sys_admin =1 ;@@ -2829,7 +2832,8 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name* and lack of permission just means that we fall back to the * in-core context value, not a denial. */ - error =selinux_capable (current,current_cred (),CAP_MAC_ADMIN , + error =selinux_capable (current,current_cred (), + &init_user_ns,CAP_MAC_ADMIN ,SECURITY_CAP_NOAUDIT );if (!error) error = security_sid_to_context_force(isec->sid, &context,
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
In reply to Serge E. HallynQuoted 27 lines · Serge E. Hallyn
- Introduce
ns_capableto test for a capability in a non-default user namespace.
- Teachcap_capableto handle capabilities in a non-default user namespace.The motivation is to get to the unprivileged creation of new namespaces. It looks like this gets us 90% of the way there, with only potential uid confusion issues left.
I still need to handle getting all caps after creation but otherwise I think I have a good starter patch that achieves all of your goals.
Changelog:
11/05/2010: [serge] add apparmor
12/14/2010: [serge] fix capabilities to created user namespaces Without this, if user serge creates auser_ns, he won't have capabilities to theuser_nshe created. THis is because we were first checking whether his effective caps had the caps
he needed and returning-EPERMif not, and THEN checking whether he was the creator. Reverse those checks.
12/16/2010: [serge]security_real_capableneeds ns argument in !security case 01/11/2011: [serge] addtask_ns_capablehelper
01/11/2011: [serge] addnsown_capable()helper per Bastian Blank suggestion 02/16/2011: [serge] fix a logic bug: the root user is always creator of
init_user_ns, but should not always have capabilities to it! Fix the check incap_capable().
Quoted 3 lines
Signed-off-by Eric W. Biederman<[email protected]>Signed-off-by Serge E. Hallyn<[email protected]>---
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
In reply to Serge E. HallynQuoted 27 lines · On Thu, 17 Feb 2011 15:03:06 +0000 "Serge E. Hallyn" <[email protected]> wrote:
- Introduce
ns_capableto test for a capability in a non-default user namespace.
- Teachcap_capableto handle capabilities in a non-default user namespace.The motivation is to get to the unprivileged creation of new namespaces. It looks like this gets us 90% of the way there, with only potential uid confusion issues left.
I still need to handle getting all caps after creation but otherwise I think I have a good starter patch that achieves all of your goals.
...
include/linux/capability.h +1
-1@@ -544,7 +544,7 @@ extern const kernel_cap_t __cap_init_eff_set;* * Note that this does not set PF_SUPERPRIV on the task. */ -#define has_capability (t, cap) (security_real_capable((t), (cap)) == 0) +#define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited)
include/linux/capability.h extern const kernel_cap_t __cap_init_eff_set; · line 564
Quoted 8 lines
include/linux/capability.h +3
-1@@ -558,9 +558,15 @@ extern const kernel_cap_t __cap_init_eff_set;* Note that this does not set PF_SUPERPRIV on the task. */ #define has_capability_noaudit (t, cap) \ - (security_real_capable_noaudit((t), (cap)) == 0) + (security_real_capable_noaudit((t), &init_user_ns, (cap)) == 0) +struct user_namespace ; +extern struct user_namespace init_user_ns;
Two icky-should-be-written-in-C macros which reference init_user_ns, followed by the declaration of init_user_ns and its type. Declarations which duplicate those in other header files. It's ripe for some upcleaning, methinks?
Also, please ensure that the forward struct declarations are all at top-of-file (as in include/linux/security.h). Otherwise we can end up accumulating multiple forward declarations of the same thing in the one file.
include/linux/capability.h extern const kernel_cap_t __cap_init_eff_set; · line 569
Quoted 5 lines
include/linux/capability.h +4
-0extern int capable (int cap); +extern int ns_capable (struct user_namespace *ns,int cap); +extern int task_ns_capable (struct task_struct *t,int cap); + +#define nsown_capable (cap) (ns_capable(current_user_ns(), (cap)))
macroitis!
Quoted 5 lines
include/linux/capability.h +1
-0@@ -301,15 +302,42 @@ error:*/ int capable (int cap) { +return ns_capable (&init_user_ns, cap);
include/linux/capability.h error: · line 315
Quoted 10 lines
include/linux/capability.h +10
-0+} +EXPORT_SYMBOL (capable); + +/** +* ns_capable - Determine if the current task has a superior capability in effect +* @ns: The usernamespace we want the capability in +* @cap: The capability to be tested for +* +* Return true if the current task has the given superior capability currently +* available for use, false if not.
Actually it doesn't return true or false - it returns 1 or 0. Using a `bool' return type would fix the comment :)
Quoted 14 lines
include/linux/capability.h +6
-1+* This sets PF_SUPERPRIV on the task if the capability is available on the +* assumption that it's about to be used. +*/ +int ns_capable (struct user_namespace *ns,int cap) +{if (unlikely (!cap_valid (cap))) {printk (KERN_CRIT "capable() called with invalid cap=%u\n" , cap);BUG (); } -if (security_capable (current_cred (), cap) ==0 ) { +if (security_capable (ns,current_cred (), cap) ==0 ) { current->flags |=PF_SUPERPRIV ;return 1 ;
include/linux/capability.h error: · line 337
Quoted 10 lines
include/linux/capability.h +6
-1}return 0 ; } -EXPORT_SYMBOL (capable); +EXPORT_SYMBOL (ns_capable); + +/* +* does current have capability 'cap' to the user namespace of task +* 't'. Return true if it does, false otherwise. +*/
Other comments were kerneldocified.
include/linux/capability.h error: · line 342
Quoted 5 lines
include/linux/capability.h +5
-0+int task_ns_capable (struct task_struct *t,int cap) +{ +return ns_capable (task_cred_xxx (t, user)->user_ns, cap); +} +EXPORT_SYMBOL (task_ns_capable);
Could return bool.
Quoted 20 lines
...
include/linux/capability.h +15
-1+int cap_capable (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *targ_ns,int cap,int audit) { -return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; +for (;;) { +/* The creator of the user namespace has all caps. */ +if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +return 0 ; + +/* Do we have the necessary capabilities? */ +if (targ_ns == cred->user->user_ns) +return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; + +/* Have we tried all of the parent namespaces? */ +if (targ_ns == &init_user_ns) +return -EPERM ; +
include/linux/capability.h
Quoted 10 lines
include/linux/capability.h +10
-0+/* If you have the capability in a parent user ns you have it +* in the over all children user namespaces as well, so see +* if this process has the capability in the parent user +* namespace. +*/ + targ_ns = targ_ns->creator->user_ns; + } + +/* We never get here */ +return -EPERM ;
So delete the code? Or does the compiler warn? If so, it's pretty busted.
Quoted 6 lines
}
/**
...
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
In reply to Serge E. Hallyninclude/linux/security.h struct security_operations { · line 1387
Quoted 3 lines · Serge E. Hallyn
include/linux/security.h +1
-1int (*capable) (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);
Hmmm... A chunk of the contents of the cred struct are user-namespaced. Could you add the user_namespace pointer to the cred struct and thus avoid passing it as an argument to other things.
In fact, I think you probably have to do this so that cachefiles, for example, can override the user namespace when it operates on behalf of a process that's in another namespace.
In fact, looking later on in your patch, I see:
security/commoncap.c int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, · line 858
Quoted 3 lines
security/commoncap.c +2
-1- || (cap_capable(current, current_cred(), CAP_SETPCAP, + || (cap_capable(current, current_cred(), + current_cred()->user->user_ns, CAP_SETPCAP,
so the user_ns _is_ already available through the creds. So is there really a need to pass it as an argument to anything that already takes a cred?
Quoted 3 lines
* @cred contains the credentials to use.
+ * @ns contains the user namespace we want the capability in
* @cap contains the capability <include/linux/capability.h>.
That should be tabbed to match the lines either side.
David
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
Quoted 7 lines · David Howells
include/linux/security.h struct security_operations { · line 1387
Quoted 3 lines
include/linux/security.h +1
-1int (*capable) (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);Hmmm... A chunk of the contents of the cred struct are user-namespaced. Could you add the
user_namespacepointer to the cred struct and thus avoid passing it as an argument to other things.
Ah, no... Ignore that, I think I see that you do need it.
security/commoncap.c EXPORT_SYMBOL(cap_netlink_recv); · line 90
Quoted 8 lines
security/commoncap.c +6
-1+int cap_capable (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *targ_ns,int cap,int audit) { -return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; +for (;;) { +/* The creator of the user namespace has all caps. */ +if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +return 0 ;
Why is that last comment so? Why should the creating namespace sport all possible capabilities? Do you have to have all capabilities available to you to be permitted create a new user namespace?
Also, would it be worth having a separate cap_ns_capable()? Wouldn't most calls to cap_capable() only be checking the caps granted in the current user namespace?
David
Serge E. Hallyn
maintainerRe: [PATCH 2/9] security: Make capabilities relative to the user namespace.
Quoted 11 lines · David Howells
Quoted 7 lines · David Howells
include/linux/security.h struct security_operations { · line 1387
Quoted 3 lines
include/linux/security.h +1
-1int (*capable) (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);Hmmm... A chunk of the contents of the cred struct are user-namespaced. Could you add the
user_namespacepointer to the cred struct and thus avoid passing it as an argument to other things.Ah, no... Ignore that, I think I see that you do need it.
Thanks for reviewing, David.
Quoted 4 lines
Quoted 4 lines
security/commoncap.c +2
-1+int cap_capable (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *targ_ns,int cap,int audit) { -return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ;
Quoted 8 lines
security/commoncap.c EXPORT_SYMBOL(cap_netlink_recv); · line 90
Quoted 4 lines
security/commoncap.c +4
-0+for (;;) { +/* The creator of the user namespace has all caps. */ +if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +return 0 ;Why is that last comment so? Why should the creating namespace sport all possible capabilities? Do you have to have all capabilities available to you to be permitted create a new user namespace?
It's not the creating namespace, but the creating user, which has all caps.
So for instance, if uid 500 in init_user_ns creates a namespace, then:
a. uid 500 in init_user_ns has all caps to the child user_ns, so it can
kill the tasks in the userns, clean up, etc.
b. uid X in any other child user_ns has no caps to the child user_ns. c. root in init_user_ns has whatever capabilities are in his pE to the
child user_ns. Again, this is so that the admin in any user_ns can clean up any messes made by users in his user_ns.
One of the goals of the user namespaces it to make it safe to allow unprivileged users to create child user namespaces in which they have targeted privilege. Anything which happens in that child user namespace should be:
a. constrained to resources which the user can control anyway b. able to be cleaned up by the user
c. (especially) able to be cleaned up by the privileged user in the
parent user_ns.
Quoted 3 lines
Also, would it be worth having a separate
cap_ns_capable()? Wouldn't most calls tocap_capable()only be checking the caps granted in the current user namespace?
Hm. There is nsown_capable() which is targeted to current_userns(), but that still needs to enable the caps for the privileged ancestors as described above.
thanks,
-serge
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
Quoted 11 lines · David Howells
Quoted 7 lines · David Howells
include/linux/security.h struct security_operations { · line 1387
Quoted 3 lines
include/linux/security.h +1
-1int (*capable) (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);Hmmm... A chunk of the contents of the cred struct are user-namespaced. Could you add the
user_namespacepointer to the cred struct and thus avoid passing it as an argument to other things.Ah, no... Ignore that, I think I see that you do need it.
Thanks for reviewing, David.
Quoted 4 lines
Quoted 4 lines
security/commoncap.c +2
-1+int cap_capable (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *targ_ns,int cap,int audit) { -return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ;
Quoted 8 lines
security/commoncap.c EXPORT_SYMBOL(cap_netlink_recv); · line 90
Quoted 4 lines
security/commoncap.c +4
-0+for (;;) { +/* The creator of the user namespace has all caps. */ +if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +return 0 ;Why is that last comment so? Why should the creating namespace sport all possible capabilities? Do you have to have all capabilities available to you to be permitted create a new user namespace?
It's not the creating namespace, but the creating user, which has all caps.
So for instance, if uid 500 in init_user_ns creates a namespace, then:
a. uid 500 in init_user_ns has all caps to the child user_ns, so it can
kill the tasks in the userns, clean up, etc.
b. uid X in any other child user_ns has no caps to the child user_ns. c. root in init_user_ns has whatever capabilities are in his pE to the
child user_ns. Again, this is so that the admin in any user_ns can clean up any messes made by users in his user_ns.
One of the goals of the user namespaces it to make it safe to allow unprivileged users to create child user namespaces in which they have targeted privilege. Anything which happens in that child user namespace should be:
a. constrained to resources which the user can control anyway b. able to be cleaned up by the user
c. (especially) able to be cleaned up by the privileged user in the
parent user_ns.
Quoted 3 lines
Also, would it be worth having a separate
cap_ns_capable()? Wouldn't most calls tocap_capable()only be checking the caps granted in the current user namespace?
Hm. There is nsown_capable() which is targeted to current_userns(), but that still needs to enable the caps for the privileged ancestors as described above.
thanks,
-serge
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
In reply to Serge E. Hallynsecurity/commoncap.c EXPORT_SYMBOL(cap_netlink_recv); · line 104
Quoted 5 lines · Serge E. Hallyn
security/commoncap.c +5
-0+/* If you have the capability in a parent user ns you have it +* in the over all children user namespaces as well, so see +* if this process has the capability in the parent user +* namespace. +*/
"... in the over all children user namespaces ..." I think need a couple of words dropping.
David
Serge E. Hallyn
maintainerPATCH 3/9 allow sethostname in a container
In reply to Serge E. Hallyn--- kernel/sys.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/sys.c +1 -1
@@ -1177,7 +1177,7 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!capable (CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN ))return -EPERM ;if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Eric W. Biederman
Acked-byRe: [PATCH 3/9] allow sethostname in a container
"Serge E. Hallyn" <[email protected]> writes:
Quoted 18 lines
Signed-off-by Serge E. Hallyn <[email protected]>--- kernel/sys.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)kernel/sys.c +1
-1@@ -1177,7 +1177,7 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!capable (CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN ))return -EPERM ;if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;
Daniel Lezcano
Acked-byRe: [PATCH 3/9] allow sethostname in a container
In reply to a message not in the archiveQuoted 2 lines · Serge E. Hallyn
Signed-off-by Serge E. Hallyn<[email protected]>---
Re: [PATCH 3/9] allow sethostname in a container
In reply to Serge E. Hallyn"Serge E. Hallyn" <[email protected]> writes:
Quoted 18 lines
Signed-off-by Serge E. Hallyn <[email protected]>--- kernel/sys.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)kernel/sys.c +1
-1@@ -1177,7 +1177,7 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!capable (CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN ))return -EPERM ;if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;
Re: [PATCH 3/9] allow sethostname in a container
In reply to Serge E. HallynQuoted 2 lines · Serge E. Hallyn
Signed-off-by Serge E. Hallyn<[email protected]>---
Serge E. Hallyn
maintainerPATCH 4/9 allow killing tasks in your own or child userns
In reply to Serge E. HallynChangelog:
Dec 8: Fixed bug in my check_kill_permission pointed out by
Eric Biederman.
Dec 13: Apply Eric's suggestion to pass target task into kill_ok_by_cred()
for clarity
Dec 31: address comment by Eric Biederman:
don't need cred/tcred in check_kill_permission.
Jan 1: use const cred struct.
Jan 11: Per Bastian Blank's advice, clean up kill_ok_by_cred(). Feb 16: kill_ok_by_cred: fix bad parentheses
--- kernel/signal.c | 30 ++++++++++++++++++++++-------- 1 files changed, 22 insertions(+), 8 deletions(-)
kernel/signal.c +22 -8
@@ -636,13 +636,33 @@ static inline bool si_fromuser(const struct siginfo *info) }/* +* called with RCU read lock from check_kill_permission() +*/ +static inline int kill_ok_by_cred (struct task_struct *t) +{ +const struct cred *cred =current_cred (); +const struct cred *tcred =__task_cred (t); + +if (cred->user->user_ns == tcred->user->user_ns && + (cred->euid == tcred->suid || + cred->euid == tcred->uid || + cred->uid == tcred->suid || + cred->uid == tcred->uid)) +return 1 ; + +if (ns_capable (tcred->user->user_ns,CAP_KILL )) +return 1 ; + +return 0 ; +} + +/* * Bad permissions for sending the signal * - the caller must hold the RCU read lock */ static int check_kill_permission (int sig,struct siginfo *info,struct task_struct *t) { -const struct cred *cred, *tcred;struct pid *sid;int error;
@@ -656,14 +676,8 @@ static int check_kill_permission(int sig, struct siginfo *info,if (error)return error; - cred =current_cred (); - tcred =__task_cred (t);if (!same_thread_group (current, t) && - (cred->euid ^ tcred->suid) && - (cred->euid ^ tcred->uid) && - (cred->uid ^ tcred->suid) && - (cred->uid ^ tcred->uid) && - !capable (CAP_KILL )) { + !kill_ok_by_cred (t)) {switch (sig) {case SIGCONT : sid =task_session (t);
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Eric W. Biederman
Acked-byRe: [PATCH 4/9] allow killing tasks in your own or child userns
Quoted 10 lines · Serge E. Hallyn
Changelog:
Dec 8: Fixed bug in mycheck_kill_permissionpointed out by
Eric Biederman.
Dec 13: Apply Eric's suggestion to pass target task intokill_ok_by_cred()
for clarity
Dec 31: address comment by Eric Biederman:
don't need cred/tcred incheck_kill_permission.
Jan 1: use const cred struct.
Jan 11: Per Bastian Blank's advice, clean upkill_ok_by_cred(). Feb 16:kill_ok_by_cred: fix bad parentheses
Quoted 61 lines
Signed-off-by Serge E. Hallyn <[email protected]>--- kernel/signal.c | 30 ++++++++++++++++++++++-------- 1 files changed, 22 insertions(+), 8 deletions(-)kernel/signal.c +22
-8@@ -636,13 +636,33 @@ static inline bool si_fromuser(const struct siginfo *info) }/* +* called with RCU read lock from check_kill_permission() +*/ +static inline int kill_ok_by_cred (struct task_struct *t) +{ +const struct cred *cred =current_cred (); +const struct cred *tcred =__task_cred (t); + +if (cred->user->user_ns == tcred->user->user_ns && + (cred->euid == tcred->suid || + cred->euid == tcred->uid || + cred->uid == tcred->suid || + cred->uid == tcred->uid)) +return 1 ; + +if (ns_capable (tcred->user->user_ns,CAP_KILL )) +return 1 ; + +return 0 ; +} + +/* * Bad permissions for sending the signal * - the caller must hold the RCU read lock */ static int check_kill_permission (int sig,struct siginfo *info,struct task_struct *t) { -const struct cred *cred, *tcred;struct pid *sid;int error;@@ -656,14 +676,8 @@ static int check_kill_permission(int sig, struct siginfo *info,if (error)return error; - cred =current_cred (); - tcred =__task_cred (t);if (!same_thread_group (current, t) && - (cred->euid ^ tcred->suid) && - (cred->euid ^ tcred->uid) && - (cred->uid ^ tcred->suid) && - (cred->uid ^ tcred->uid) && - !capable (CAP_KILL )) { + !kill_ok_by_cred (t)) {switch (sig) {case SIGCONT : sid =task_session (t);
Re: [PATCH 4/9] allow killing tasks in your own or child userns
In reply to a message not in the archiveQuoted 10 lines · On Thu, 17 Feb 2011 15:03:25 +0000 "Serge E. Hallyn"…
/*
+ * called with RCU read lock fromcheck_kill_permission()
+ */kernel/signal.c +7
-0+static inline int kill_ok_by_cred (struct task_struct *t) +{ +const struct cred *cred =current_cred (); +const struct cred *tcred =__task_cred (t); + + if (cred->user->user_ns == tcred->user->user_ns && + (cred->euid == tcred->suid ||
kernel/signal.c static inline bool si_fromuser(const struct siginfo *info) · line 657
Quoted 10 lines
kernel/signal.c +10
-0+ cred->euid == tcred->uid || + cred->uid == tcred->suid || + cred->uid == tcred->uid)) + return 1; + +if (ns_capable (tcred->user->user_ns,CAP_KILL )) +return 1 ; + +return 0 ; +}
The compiler will inline this for us.
Quoted 27 lines
kernel/signal.c +2
-8+/* * Bad permissions for sending the signal * - the caller must hold the RCU read lock */ static int check_kill_permission (int sig,struct siginfo *info,struct task_struct *t) { -const struct cred *cred, *tcred;struct pid *sid;int error;@@ -656,14 +676,8 @@ static int check_kill_permission(int sig, struct siginfo *info,if (error)return error; - cred =current_cred (); - tcred =__task_cred (t);if (!same_thread_group (current, t) && - (cred->euid ^ tcred->suid) && - (cred->euid ^ tcred->uid) && - (cred->uid ^ tcred->suid) && - (cred->uid ^ tcred->uid) && - !capable (CAP_KILL )) { + !kill_ok_by_cred (t)) {switch (sig) {case SIGCONT : sid =task_session (t);
Re: [PATCH 4/9] allow killing tasks in your own or child userns
In reply to Serge E. HallynQuoted 10 lines · Serge E. Hallyn
Changelog:
Dec 8: Fixed bug in mycheck_kill_permissionpointed out by
Eric Biederman.
Dec 13: Apply Eric's suggestion to pass target task intokill_ok_by_cred()
for clarity
Dec 31: address comment by Eric Biederman:
don't need cred/tcred incheck_kill_permission.
Jan 1: use const cred struct.
Jan 11: Per Bastian Blank's advice, clean upkill_ok_by_cred(). Feb 16:kill_ok_by_cred: fix bad parentheses
Quoted 61 lines
Signed-off-by Serge E. Hallyn <[email protected]>--- kernel/signal.c | 30 ++++++++++++++++++++++-------- 1 files changed, 22 insertions(+), 8 deletions(-)kernel/signal.c +22
-8@@ -636,13 +636,33 @@ static inline bool si_fromuser(const struct siginfo *info) }/* +* called with RCU read lock from check_kill_permission() +*/ +static inline int kill_ok_by_cred (struct task_struct *t) +{ +const struct cred *cred =current_cred (); +const struct cred *tcred =__task_cred (t); + +if (cred->user->user_ns == tcred->user->user_ns && + (cred->euid == tcred->suid || + cred->euid == tcred->uid || + cred->uid == tcred->suid || + cred->uid == tcred->uid)) +return 1 ; + +if (ns_capable (tcred->user->user_ns,CAP_KILL )) +return 1 ; + +return 0 ; +} + +/* * Bad permissions for sending the signal * - the caller must hold the RCU read lock */ static int check_kill_permission (int sig,struct siginfo *info,struct task_struct *t) { -const struct cred *cred, *tcred;struct pid *sid;int error;@@ -656,14 +676,8 @@ static int check_kill_permission(int sig, struct siginfo *info,if (error)return error; - cred =current_cred (); - tcred =__task_cred (t);if (!same_thread_group (current, t) && - (cred->euid ^ tcred->suid) && - (cred->euid ^ tcred->uid) && - (cred->uid ^ tcred->suid) && - (cred->uid ^ tcred->uid) && - !capable (CAP_KILL )) { + !kill_ok_by_cred (t)) {switch (sig) {case SIGCONT : sid =task_session (t);
Re: [PATCH 4/9] allow killing tasks in your own or child userns
In reply to Serge E. HallynQuoted 10 lines · On Thu, 17 Feb 2011 15:03:25 +0000 "Serge E. Hallyn" <[email protected]> wrote:
/*
+ * called with RCU read lock fromcheck_kill_permission()
+ */kernel/signal.c +7
-0+static inline int kill_ok_by_cred (struct task_struct *t) +{ +const struct cred *cred =current_cred (); +const struct cred *tcred =__task_cred (t); + + if (cred->user->user_ns == tcred->user->user_ns && + (cred->euid == tcred->suid ||
kernel/signal.c static inline bool si_fromuser(const struct siginfo *info) · line 657
Quoted 10 lines
kernel/signal.c +10
-0+ cred->euid == tcred->uid || + cred->uid == tcred->suid || + cred->uid == tcred->uid)) + return 1; + +if (ns_capable (tcred->user->user_ns,CAP_KILL )) +return 1 ; + +return 0 ; +}
The compiler will inline this for us.
Quoted 27 lines
kernel/signal.c +2
-8+/* * Bad permissions for sending the signal * - the caller must hold the RCU read lock */ static int check_kill_permission (int sig,struct siginfo *info,struct task_struct *t) { -const struct cred *cred, *tcred;struct pid *sid;int error;@@ -656,14 +676,8 @@ static int check_kill_permission(int sig, struct siginfo *info,if (error)return error; - cred =current_cred (); - tcred =__task_cred (t);if (!same_thread_group (current, t) && - (cred->euid ^ tcred->suid) && - (cred->euid ^ tcred->uid) && - (cred->uid ^ tcred->suid) && - (cred->uid ^ tcred->uid) && - !capable (CAP_KILL )) { + !kill_ok_by_cred (t)) {switch (sig) {case SIGCONT : sid =task_session (t);
Serge E. Hallyn
maintainerRe: [PATCH 4/9] allow killing tasks in your own or child userns
Quoted 17 lines · Andrew Morton
Quoted 14 lines · On Thu, 17 Feb 2011 15:03:25 +0000 "Serge E. Hallyn" <[email protected]> wrote:
/*
+ * called with RCU read lock fromcheck_kill_permission()
+ */(quoted diff) +11
-0+static inline int kill_ok_by_cred(struct task_struct *t) +{ + const struct cred *cred = current_cred(); + const struct cred *tcred = __task_cred(t); + + if (cred->user->user_ns == tcred->user->user_ns && + (cred->euid == tcred->suid || + cred->euid == tcred->uid || + cred->uid == tcred->suid || + cred->uid == tcred->uid)) + return 1;
Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ + if (ns_capable(tcred->user->user_ns, CAP_KILL)) + return 1; + + return 0; +}The compiler will inline this for us.
Is that simply true with everything (worth inlining) nowadays, or is there a particular implicit hint to the compiler that'll make that happen?
Not that I guess it's even particularly important in this case.
From: Serge E. Hallyn <[email protected]>
Date: Thu, 24 Feb 2011 00:26:02 +0000
Subject: [PATCH 1/2] userns: let compiler inline kill_ok_by_cred (per akpm)
--- kernel/signal.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/signal.c +1 -1
@@ -638,7 +638,7 @@ static inline bool si_fromuser(const struct siginfo *info)/* * called with RCU read lock from check_kill_permission() */ -static inline int kill_ok_by_cred (struct task_struct *t) +static int kill_ok_by_cred (struct task_struct *t) {const struct cred *cred =current_cred ();const struct cred *tcred =__task_cred (t);
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 4/9] allow killing tasks in your own or child userns
Quoted 27 lines · On Thu, 24 Feb 2011 00:48:18 +0000 "Serge E. Hallyn"…
Quoted 17 lines · Andrew Morton
Quoted 14 lines · On Thu, 17 Feb 2011 15:03:25 +0000 "Serge E. Hallyn"…
/*
+ * called with RCU read lock fromcheck_kill_permission()
+ */(quoted diff) +11
-0+static inline int kill_ok_by_cred(struct task_struct *t) +{ + const struct cred *cred = current_cred(); + const struct cred *tcred = __task_cred(t); + + if (cred->user->user_ns == tcred->user->user_ns && + (cred->euid == tcred->suid || + cred->euid == tcred->uid || + cred->uid == tcred->suid || + cred->uid == tcred->uid)) + return 1;Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ + if (ns_capable(tcred->user->user_ns, CAP_KILL)) + return 1; + + return 0; +}The compiler will inline this for us.
Quoted 3 lines
Is that simply true with everything (worth inlining) nowadays, or is there a particular implicit hint to the compiler that'll make that happen?
We've basically stopped inlining things nowadays. gcc inlines aggressively and sometimes we have to use noinline to stop it. Also, modern gcc's like to ignore the inline directive anwyay, so we have to resort to __always_inline when we disagree.
Re: [PATCH 4/9] allow killing tasks in your own or child userns
In reply to Serge E. HallynQuoted 27 lines · On Thu, 24 Feb 2011 00:48:18 +0000 "Serge E. Hallyn" <[email protected]> wrote:
Quoted 17 lines · Andrew Morton
Quoted 14 lines · On Thu, 17 Feb 2011 15:03:25 +0000 "Serge E. Hallyn" <[email protected]> wrote:
/*
+ * called with RCU read lock fromcheck_kill_permission()
+ */(quoted diff) +11
-0+static inline int kill_ok_by_cred(struct task_struct *t) +{ + const struct cred *cred = current_cred(); + const struct cred *tcred = __task_cred(t); + + if (cred->user->user_ns == tcred->user->user_ns && + (cred->euid == tcred->suid || + cred->euid == tcred->uid || + cred->uid == tcred->suid || + cred->uid == tcred->uid)) + return 1;Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ + if (ns_capable(tcred->user->user_ns, CAP_KILL)) + return 1; + + return 0; +}The compiler will inline this for us.
Quoted 3 lines
Is that simply true with everything (worth inlining) nowadays, or is there a particular implicit hint to the compiler that'll make that happen?
We've basically stopped inlining things nowadays. gcc inlines aggressively and sometimes we have to use noinline to stop it. Also, modern gcc's like to ignore the inline directive anwyay, so we have to resort to __always_inline when we disagree.
A message that is not in the archive (sent off-list or not yet received): 20110218155921.440f1137.akpm-de/[email protected]
Re: [PATCH 4/9] allow killing tasks in your own or child userns
Quoted 17 lines · Andrew Morton
Quoted 14 lines · On Thu, 17 Feb 2011 15:03:25 +0000 "Serge E. Hallyn"…
/*
+ * called with RCU read lock fromcheck_kill_permission()
+ */(quoted diff) +11
-0+static inline int kill_ok_by_cred(struct task_struct *t) +{ + const struct cred *cred = current_cred(); + const struct cred *tcred = __task_cred(t); + + if (cred->user->user_ns == tcred->user->user_ns && + (cred->euid == tcred->suid || + cred->euid == tcred->uid || + cred->uid == tcred->suid || + cred->uid == tcred->uid)) + return 1;
Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ + if (ns_capable(tcred->user->user_ns, CAP_KILL)) + return 1; + + return 0; +}The compiler will inline this for us.
Is that simply true with everything (worth inlining) nowadays, or is there a particular implicit hint to the compiler that'll make that happen?
Not that I guess it's even particularly important in this case.
From: Serge E. Hallyn <[email protected]>
Date: Thu, 24 Feb 2011 00:26:02 +0000
Subject: [PATCH 1/2] userns: let compiler inline kill_ok_by_cred (per akpm)
--- kernel/signal.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/signal.c +1 -1
@@ -638,7 +638,7 @@ static inline bool si_fromuser(const struct siginfo *info)/* * called with RCU read lock from check_kill_permission() */ -static inline int kill_ok_by_cred (struct task_struct *t) +static int kill_ok_by_cred (struct task_struct *t) {const struct cred *cred =current_cred ();const struct cred *tcred =__task_cred (t);
Signature · 1 line
1.7.0.4
Re: [PATCH 4/9] allow killing tasks in your own or child userns
In reply to Serge E. HallynQuoted 11 lines · Serge E. Hallyn
Changelog:
Dec 8: Fixed bug in mycheck_kill_permissionpointed out by
Eric Biederman.
Dec 13: Apply Eric's suggestion to pass target task intokill_ok_by_cred()
for clarity
Dec 31: address comment by Eric Biederman:
don't need cred/tcred incheck_kill_permission.
Jan 1: use const cred struct.
Jan 11: Per Bastian Blank's advice, clean upkill_ok_by_cred(). Feb 16:kill_ok_by_cred: fix bad parentheses
Quoted 1 line
Signed-off-by Serge E. Hallyn<[email protected]>
Serge E. Hallyn
maintainerPATCH 5/9 Allow ptrace from non-init user namespaces
In reply to Serge E. Hallynptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has CAP_SYS_PTRACE capability.
Changelog:
Dec 31: Address feedback by Eric:
. Correct ptrace uid check
. Rename may_ptrace_ns to ptrace_capable . Also fix the cap_ptrace checks.
Jan 1: Use const cred struct
Jan 11: use task_ns_capable() in place of ptrace_capable().
--- include/linux/capability.h | 2 + include/linux/user_namespace.h | 9 +++++++ kernel/ptrace.c | 27 ++++++++++++---------- kernel/user_namespace.c | 16 +++++++++++++ security/commoncap.c | 48 +++++++++++++++++++++++++++++++++------ 5 files changed, 82 insertions(+), 20 deletions(-)
include/linux/capability.h +2 -0
@@ -546,6 +546,8 @@ extern const kernel_cap_t __cap_init_eff_set;*/ #define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0) +#define has_ns_capability (t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0) +/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) * @t: The task in question
include/linux/user_namespace.h +9 -0
@@ -39,6 +39,9 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim); +#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)
@@ -66,6 +69,12 @@ static inline gid_t user_ns_map_gid(struct user_namespace *to,return gid; } +static inline int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +return 1 ; +} +#endif #endif /* _LINUX_USER_H */
kernel/ptrace.c +15 -12
@@ -134,21 +134,24 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)return 0 ;rcu_read_lock (); tcred =__task_cred (task); -if ((cred->uid != tcred->euid || - cred->uid != tcred->suid || - cred->uid != tcred->uid || - cred->gid != tcred->egid || - cred->gid != tcred->sgid || - cred->gid != tcred->gid) && - !capable (CAP_SYS_PTRACE )) { -rcu_read_unlock (); -return -EPERM ; - } +if (cred->user->user_ns == tcred->user->user_ns && + (cred->uid == tcred->euid && + cred->uid == tcred->suid && + cred->uid == tcred->uid && + cred->gid == tcred->egid && + cred->gid == tcred->sgid && + cred->gid == tcred->gid)) + goto ok; +if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto ok; +rcu_read_unlock (); +return -EPERM ; +ok:rcu_read_unlock ();smp_rmb ();if (task->mm) dumpable =get_dumpable (task->mm); -if (!dumpable && !capable (CAP_SYS_PTRACE )) +if (!dumpable && !task_ns_capable (task,CAP_SYS_PTRACE ))return -EPERM ;return security_ptrace_access_check (task, mode);
@@ -198,7 +201,7 @@ int ptrace_attach(struct task_struct *task) goto unlock_tasklist; task->ptrace =PT_PTRACED ; -if (capable (CAP_SYS_PTRACE )) +if (task_ns_capable (task,CAP_SYS_PTRACE )) task->ptrace |=PT_PTRACE_CAP ;__ptrace_link (task, current);
kernel/user_namespace.c +16 -0
@@ -129,6 +129,22 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns; +struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; +for (;;) { +if (u1 == u2) +return 1 ; +if (u1 == &init_user_ns) +return 0 ; + u1 = u1->creator->user_ns; + } +/* We never get here */ +return 0 ; +} +static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );
security/commoncap.c +40 -8
76 lines not on this page · raw message
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
Quoted 4 lines · Serge E. Hallyn
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has
CAP_SYS_PTRACEcapability.
I don't see how it can go wrong at the moment but same_or_ancestore_user_ns is too permissive and potentially inefficient. Can you please replace it with a simple user namespace equality check.
Eric
Quoted 159 lines
Changelog:
Dec 31: Address feedback by Eric:
. Correct ptrace uid check
. Renamemay_ptrace_nstoptrace_capable. Also fix thecap_ptracechecks.
Jan 1: Use const cred struct
Jan 11: usetask_ns_capable()in place ofptrace_capable().Signed-off-by Serge E. Hallyn <[email protected]>--- include/linux/capability.h | 2 + include/linux/user_namespace.h | 9 +++++++ kernel/ptrace.c | 27 ++++++++++++---------- kernel/user_namespace.c | 16 +++++++++++++ security/commoncap.c | 48 +++++++++++++++++++++++++++++++++------ 5 files changed, 82 insertions(+), 20 deletions(-)include/linux/capability.h +2
-0@@ -546,6 +546,8 @@ extern const kernel_cap_t __cap_init_eff_set;*/ #define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0) +#define has_ns_capability (t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0) +/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) * @t: The task in question include/linux/user_namespace.h +9
-0@@ -39,6 +39,9 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim); +#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)@@ -66,6 +69,12 @@ static inline gid_t user_ns_map_gid(struct user_namespace *to,return gid; } +static inline int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +return 1 ; +} +#endif #endif /* _LINUX_USER_H */ kernel/ptrace.c +15
-12@@ -134,21 +134,24 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)return 0 ;rcu_read_lock (); tcred =__task_cred (task); -if ((cred->uid != tcred->euid || - cred->uid != tcred->suid || - cred->uid != tcred->uid || - cred->gid != tcred->egid || - cred->gid != tcred->sgid || - cred->gid != tcred->gid) && - !capable (CAP_SYS_PTRACE )) { -rcu_read_unlock (); -return -EPERM ; - } +if (cred->user->user_ns == tcred->user->user_ns && + (cred->uid == tcred->euid && + cred->uid == tcred->suid && + cred->uid == tcred->uid && + cred->gid == tcred->egid && + cred->gid == tcred->sgid && + cred->gid == tcred->gid)) + goto ok; +if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto ok; +rcu_read_unlock (); +return -EPERM ; +ok:rcu_read_unlock ();smp_rmb ();if (task->mm) dumpable =get_dumpable (task->mm); -if (!dumpable && !capable (CAP_SYS_PTRACE )) +if (!dumpable && !task_ns_capable (task,CAP_SYS_PTRACE ))return -EPERM ;return security_ptrace_access_check (task, mode);@@ -198,7 +201,7 @@ int ptrace_attach(struct task_struct *task) goto unlock_tasklist; task->ptrace =PT_PTRACED ; -if (capable (CAP_SYS_PTRACE )) +if (task_ns_capable (task,CAP_SYS_PTRACE )) task->ptrace |=PT_PTRACE_CAP ;__ptrace_link (task, current);kernel/user_namespace.c +16
-0@@ -129,6 +129,22 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns; +struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; +for (;;) { +if (u1 == u2) +return 1 ; +if (u1 == &init_user_ns) +return 0 ; + u1 = u1->creator->user_ns; + } +/* We never get here */ +return 0 ; +} +static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );security/commoncap.c +7
-3@@ -130,18 +130,34 @@ int cap_settime(struct timespec *ts, struct timezone *tz)* @child: The process to be accessed * @mode: The mode of attachment. * +* If we are in the same or an ancestor user_ns and have all the target +* task's capabilities, then ptrace access is allowed. +* If we have the ptrace capability to the target user_ns, then ptrace +* access is allowed. +* Else denied. +* * Determine whether a process may access another, returning 0 if permission * granted, -ve if denied. */ int cap_ptrace_access_check (struct task_struct *child,unsigned int mode) {int ret =0 ; +const struct cred *cred, *tcred;rcu_read_lock (); -if (!cap_issubset (__task_cred (child)->cap_permitted, -current_cred ()->cap_permitted) && - !capable (CAP_SYS_PTRACE ))
security/commoncap.c int cap_settime(struct timespec *ts, struct timezone *tz) · line 156
Quoted 10 lines
security/commoncap.c +9
-1- ret = -EPERM ; + cred =current_cred (); + tcred =__task_cred (child); +/* +* The ancestor user_ns check may be gratuitous, as I think +* we've already guaranteed that in kernel/ptrace.c. +*/ +if (same_or_ancestor_user_ns (current, child) && +cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + goto out;
I have commented on this before but I took a good hard look this time, and can comment more intelligently.
The cap_issubset check is for the case where we don't use the CAP_SYS_PTRACE capability, as such is only valid in the same user namespace. Furthermore capabilities really are not comparable between different user namespaces. So can you please replace the same_or_ancestor_user_ns with a simple namespace equality check. Having the wrong logic in here will just be confusing in the future.
Also could you name tcred child_cread I think that would be clearer in the test below.
security/commoncap.c int cap_settime(struct timespec *ts, struct timezone *tz) · line 162
Quoted 6 lines
security/commoncap.c +4
-0+if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto out; + ret = -EPERM ; +out:rcu_read_unlock ();return ret;
I also find it strange that we allow CAP_SYS_PTRACE to allow us to ptrace processes with more capabilities than ourselves. But that is an entirely different issue.
Quoted 40 lines
security/commoncap.c +20
-4}@@ -150,18 +166,34 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)* cap_ptrace_traceme - Determine whether another process may trace the current * @parent: The task proposed to be the tracer * +* If parent is in the same or an ancestor user_ns and has all current's +* capabilities, then ptrace access is allowed. +* If parent has the ptrace capability to current's user_ns, then ptrace +* access is allowed. +* Else denied. +* * Determine whether the nominated task is permitted to trace the current * process, returning 0 if permission is granted, -ve if denied. */ int cap_ptrace_traceme (struct task_struct *parent) {int ret =0 ; +const struct cred *cred, *tcred;rcu_read_lock (); -if (!cap_issubset (current_cred ()->cap_permitted, -__task_cred (parent)->cap_permitted) && - !has_capability (parent,CAP_SYS_PTRACE )) - ret = -EPERM ; + cred =__task_cred (parent); + tcred =current_cred (); +/* +* The ancestor user_ns check may be gratuitous, as I think +* we've already guaranteed that in kernel/ptrace.c. +*/ +if (same_or_ancestor_user_ns (parent, current) && +cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + goto out; +if (has_ns_capability (parent, tcred->user->user_ns,CAP_SYS_PTRACE )) + goto out; + ret = -EPERM ; +out:rcu_read_unlock ();return ret; }
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
In reply to a message not in the archiveQuoted 11 lines · On Thu, 17 Feb 2011 15:03:33 +0000 "Serge E. Hallyn"…
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has
CAP_SYS_PTRACEcapability....
include/linux/capability.h extern const kernel_cap_t __cap_init_eff_set; · line 549
Quoted 4 lines
include/linux/capability.h +1
-0@@ -546,6 +546,8 @@ extern const kernel_cap_t __cap_init_eff_set;*/ #define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0) +#define has_ns_capability (t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0)
macroitis.
include/linux/user_namespace.h static inline void put_user_ns(struct user_namespace *ns) · line 43
Quoted 13 lines
include/linux/capability.h +0
-0/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) * @t: The task in question include/linux/user_namespace.h +2
-0@@ -39,6 +39,9 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim);
bool.
Quoted 16 lines
include/linux/user_namespace.h +0
-0#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)kernel/user_namespace.c +4
-0@@ -129,6 +129,22 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns;
kernel/user_namespace.c gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t · line 145
Quoted 10 lines
kernel/user_namespace.c +10
-0+struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; +for (;;) { +if (u1 == u2) +return 1 ; +if (u1 == &init_user_ns) +return 0 ; + u1 = u1->creator->user_ns; + } +/* We never get here */ +return 0 ;
Remove?
Quoted 14 lines
kernel/user_namespace.c +3
-0+} +static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );int cap_ptrace_access_check (struct task_struct *child,unsigned int mode) {int ret =0 ; +const struct cred *cred, *tcred;rcu_read_lock ();
kernel/user_namespace.c gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t
Quoted 10 lines
kernel/user_namespace.c +6
-4-if (!cap_issubset (__task_cred (child)->cap_permitted, -current_cred ()->cap_permitted) && - !capable (CAP_SYS_PTRACE )) - ret = -EPERM ; + cred =current_cred (); + tcred =__task_cred (child); +/* +* The ancestor user_ns check may be gratuitous, as I think +* we've already guaranteed that in kernel/ptrace.c. +*/
?
Quoted 13 lines
kernel/user_namespace.c +7
-0+if (same_or_ancestor_user_ns (current, child) && +cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + goto out; +if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto out; + ret = -EPERM ; +out:rcu_read_unlock ();return ret; }
Daniel Lezcano
Acked-byRe: [PATCH 5/9] Allow ptrace from non-init user namespaces
In reply to a message not in the archiveQuoted 13 lines · Serge E. Hallyn
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has
CAP_SYS_PTRACEcapability.Changelog:
Dec 31: Address feedback by Eric:
. Correct ptrace uid check
. Renamemay_ptrace_nstoptrace_capable. Also fix thecap_ptracechecks.
Jan 1: Use const cred struct
Jan 11: usetask_ns_capable()in place ofptrace_capable().
Quoted 1 line
Signed-off-by Serge E. Hallyn<[email protected]>
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
In reply to a message not in the archiveQuoted 1 line · Serge E. Hallyn
ptrace is allowed to tasks in the same user namespace according to
There's a verb missing after 'allowed to' - presumably 'trace'.
David
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
In reply to a message not in the archivekernel/user_namespace.c gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t · line 136
Quoted 5 lines · Serge E. Hallyn
kernel/user_namespace.c +5
-0+int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns; +struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns;
Hmmm. task_cred_xxx() uses task->real_cred, which is correct for victim (the object), but normally you'd use task->cred for task (the subject). However, in this case, I think it's probably okay.
David
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
In reply to Serge E. HallynQuoted 4 lines · Serge E. Hallyn
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has
CAP_SYS_PTRACEcapability.
I don't see how it can go wrong at the moment but same_or_ancestore_user_ns is too permissive and potentially inefficient. Can you please replace it with a simple user namespace equality check.
Eric
Quoted 159 lines
Changelog:
Dec 31: Address feedback by Eric:
. Correct ptrace uid check
. Renamemay_ptrace_nstoptrace_capable. Also fix thecap_ptracechecks.
Jan 1: Use const cred struct
Jan 11: usetask_ns_capable()in place ofptrace_capable().Signed-off-by Serge E. Hallyn <[email protected]>--- include/linux/capability.h | 2 + include/linux/user_namespace.h | 9 +++++++ kernel/ptrace.c | 27 ++++++++++++---------- kernel/user_namespace.c | 16 +++++++++++++ security/commoncap.c | 48 +++++++++++++++++++++++++++++++++------ 5 files changed, 82 insertions(+), 20 deletions(-)include/linux/capability.h +2
-0@@ -546,6 +546,8 @@ extern const kernel_cap_t __cap_init_eff_set;*/ #define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0) +#define has_ns_capability (t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0) +/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) * @t: The task in question include/linux/user_namespace.h +9
-0@@ -39,6 +39,9 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim); +#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)@@ -66,6 +69,12 @@ static inline gid_t user_ns_map_gid(struct user_namespace *to,return gid; } +static inline int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +return 1 ; +} +#endif #endif /* _LINUX_USER_H */ kernel/ptrace.c +15
-12@@ -134,21 +134,24 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)return 0 ;rcu_read_lock (); tcred =__task_cred (task); -if ((cred->uid != tcred->euid || - cred->uid != tcred->suid || - cred->uid != tcred->uid || - cred->gid != tcred->egid || - cred->gid != tcred->sgid || - cred->gid != tcred->gid) && - !capable (CAP_SYS_PTRACE )) { -rcu_read_unlock (); -return -EPERM ; - } +if (cred->user->user_ns == tcred->user->user_ns && + (cred->uid == tcred->euid && + cred->uid == tcred->suid && + cred->uid == tcred->uid && + cred->gid == tcred->egid && + cred->gid == tcred->sgid && + cred->gid == tcred->gid)) + goto ok; +if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto ok; +rcu_read_unlock (); +return -EPERM ; +ok:rcu_read_unlock ();smp_rmb ();if (task->mm) dumpable =get_dumpable (task->mm); -if (!dumpable && !capable (CAP_SYS_PTRACE )) +if (!dumpable && !task_ns_capable (task,CAP_SYS_PTRACE ))return -EPERM ;return security_ptrace_access_check (task, mode);@@ -198,7 +201,7 @@ int ptrace_attach(struct task_struct *task) goto unlock_tasklist; task->ptrace =PT_PTRACED ; -if (capable (CAP_SYS_PTRACE )) +if (task_ns_capable (task,CAP_SYS_PTRACE )) task->ptrace |=PT_PTRACE_CAP ;__ptrace_link (task, current);kernel/user_namespace.c +16
-0@@ -129,6 +129,22 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns; +struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; +for (;;) { +if (u1 == u2) +return 1 ; +if (u1 == &init_user_ns) +return 0 ; + u1 = u1->creator->user_ns; + } +/* We never get here */ +return 0 ; +} +static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );security/commoncap.c +7
-3@@ -130,18 +130,34 @@ int cap_settime(struct timespec *ts, struct timezone *tz)* @child: The process to be accessed * @mode: The mode of attachment. * +* If we are in the same or an ancestor user_ns and have all the target +* task's capabilities, then ptrace access is allowed. +* If we have the ptrace capability to the target user_ns, then ptrace +* access is allowed. +* Else denied. +* * Determine whether a process may access another, returning 0 if permission * granted, -ve if denied. */ int cap_ptrace_access_check (struct task_struct *child,unsigned int mode) {int ret =0 ; +const struct cred *cred, *tcred;rcu_read_lock (); -if (!cap_issubset (__task_cred (child)->cap_permitted, -current_cred ()->cap_permitted) && - !capable (CAP_SYS_PTRACE ))
security/commoncap.c int cap_settime(struct timespec *ts, struct timezone *tz) · line 156
Quoted 10 lines
security/commoncap.c +9
-1- ret = -EPERM ; + cred =current_cred (); + tcred =__task_cred (child); +/* +* The ancestor user_ns check may be gratuitous, as I think +* we've already guaranteed that in kernel/ptrace.c. +*/ +if (same_or_ancestor_user_ns (current, child) && +cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + goto out;
I have commented on this before but I took a good hard look this time, and can comment more intelligently.
The cap_issubset check is for the case where we don't use the CAP_SYS_PTRACE capability, as such is only valid in the same user namespace. Furthermore capabilities really are not comparable between different user namespaces. So can you please replace the same_or_ancestor_user_ns with a simple namespace equality check. Having the wrong logic in here will just be confusing in the future.
Also could you name tcred child_cread I think that would be clearer in the test below.
security/commoncap.c int cap_settime(struct timespec *ts, struct timezone *tz) · line 162
Quoted 6 lines
security/commoncap.c +4
-0+if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto out; + ret = -EPERM ; +out:rcu_read_unlock ();return ret;
I also find it strange that we allow CAP_SYS_PTRACE to allow us to ptrace processes with more capabilities than ourselves. But that is an entirely different issue.
Quoted 40 lines
security/commoncap.c +20
-4}@@ -150,18 +166,34 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)* cap_ptrace_traceme - Determine whether another process may trace the current * @parent: The task proposed to be the tracer * +* If parent is in the same or an ancestor user_ns and has all current's +* capabilities, then ptrace access is allowed. +* If parent has the ptrace capability to current's user_ns, then ptrace +* access is allowed. +* Else denied. +* * Determine whether the nominated task is permitted to trace the current * process, returning 0 if permission is granted, -ve if denied. */ int cap_ptrace_traceme (struct task_struct *parent) {int ret =0 ; +const struct cred *cred, *tcred;rcu_read_lock (); -if (!cap_issubset (current_cred ()->cap_permitted, -__task_cred (parent)->cap_permitted) && - !has_capability (parent,CAP_SYS_PTRACE )) - ret = -EPERM ; + cred =__task_cred (parent); + tcred =current_cred (); +/* +* The ancestor user_ns check may be gratuitous, as I think +* we've already guaranteed that in kernel/ptrace.c. +*/ +if (same_or_ancestor_user_ns (parent, current) && +cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + goto out; +if (has_ns_capability (parent, tcred->user->user_ns,CAP_SYS_PTRACE )) + goto out; + ret = -EPERM ; +out:rcu_read_unlock ();return ret; }
A message that is not in the archive (sent off-list or not yet received): m1aahu9hea.fsf-+imSwln9KH6u2/kzUuoCbdi2O/[email protected]
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
Quoted 179 lines · Eric W. Biederman
Quoted 4 lines · Serge E. Hallyn
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has
CAP_SYS_PTRACEcapability.I don't see how it can go wrong at the moment but
same_or_ancestore_user_nsis too permissive and potentially inefficient. Can you please replace it with a simple user namespace equality check.Eric
Quoted 164 lines
Changelog:
Dec 31: Address feedback by Eric:
. Correct ptrace uid check
. Renamemay_ptrace_nstoptrace_capable. Also fix thecap_ptracechecks.
Jan 1: Use const cred struct
Jan 11: usetask_ns_capable()in place ofptrace_capable().Signed-off-by Serge E. Hallyn <[email protected]>--- include/linux/capability.h | 2 + include/linux/user_namespace.h | 9 +++++++ kernel/ptrace.c | 27 ++++++++++++---------- kernel/user_namespace.c | 16 +++++++++++++ security/commoncap.c | 48 +++++++++++++++++++++++++++++++++------ 5 files changed, 82 insertions(+), 20 deletions(-)include/linux/capability.h +2
-0@@ -546,6 +546,8 @@ extern const kernel_cap_t __cap_init_eff_set;*/ #define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0) +#define has_ns_capability (t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0) +/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) * @t: The task in question include/linux/user_namespace.h +9
-0@@ -39,6 +39,9 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim); +#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)@@ -66,6 +69,12 @@ static inline gid_t user_ns_map_gid(struct user_namespace *to,return gid; } +static inline int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +return 1 ; +} +#endif #endif /* _LINUX_USER_H */ kernel/ptrace.c +15
-12@@ -134,21 +134,24 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)return 0 ;rcu_read_lock (); tcred =__task_cred (task); -if ((cred->uid != tcred->euid || - cred->uid != tcred->suid || - cred->uid != tcred->uid || - cred->gid != tcred->egid || - cred->gid != tcred->sgid || - cred->gid != tcred->gid) && - !capable (CAP_SYS_PTRACE )) { -rcu_read_unlock (); -return -EPERM ; - } +if (cred->user->user_ns == tcred->user->user_ns && + (cred->uid == tcred->euid && + cred->uid == tcred->suid && + cred->uid == tcred->uid && + cred->gid == tcred->egid && + cred->gid == tcred->sgid && + cred->gid == tcred->gid)) + goto ok; +if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto ok; +rcu_read_unlock (); +return -EPERM ; +ok:rcu_read_unlock ();smp_rmb ();if (task->mm) dumpable =get_dumpable (task->mm); -if (!dumpable && !capable (CAP_SYS_PTRACE )) +if (!dumpable && !task_ns_capable (task,CAP_SYS_PTRACE ))return -EPERM ;return security_ptrace_access_check (task, mode);@@ -198,7 +201,7 @@ int ptrace_attach(struct task_struct *task) goto unlock_tasklist; task->ptrace =PT_PTRACED ; -if (capable (CAP_SYS_PTRACE )) +if (task_ns_capable (task,CAP_SYS_PTRACE )) task->ptrace |=PT_PTRACE_CAP ;__ptrace_link (task, current);kernel/user_namespace.c +16
-0@@ -129,6 +129,22 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns; +struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; +for (;;) { +if (u1 == u2) +return 1 ; +if (u1 == &init_user_ns) +return 0 ; + u1 = u1->creator->user_ns; + } +/* We never get here */ +return 0 ; +} +static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );security/commoncap.c +11
-4@@ -130,18 +130,34 @@ int cap_settime(struct timespec *ts, struct timezone *tz)* @child: The process to be accessed * @mode: The mode of attachment. * +* If we are in the same or an ancestor user_ns and have all the target +* task's capabilities, then ptrace access is allowed. +* If we have the ptrace capability to the target user_ns, then ptrace +* access is allowed. +* Else denied. +* * Determine whether a process may access another, returning 0 if permission * granted, -ve if denied. */ int cap_ptrace_access_check (struct task_struct *child,unsigned int mode) {int ret =0 ; +const struct cred *cred, *tcred;rcu_read_lock (); -if (!cap_issubset (__task_cred (child)->cap_permitted, -current_cred ()->cap_permitted) && - !capable (CAP_SYS_PTRACE )) - ret = -EPERM ; + cred =current_cred (); + tcred =__task_cred (child); +/* +* The ancestor user_ns check may be gratuitous, as I think
Quoted 8 lines
security/commoncap.c int cap_settime(struct timespec *ts, struct timezone *tz) · line 156
Quoted 5 lines
security/commoncap.c +5
-0+* we've already guaranteed that in kernel/ptrace.c. +*/ +if (same_or_ancestor_user_ns (current, child) && +cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + goto out;I have commented on this before but I took a good hard look this time, and can comment more intelligently.
Thanks, Eric.
Quoted 6 lines
The
cap_issubsetcheck is for the case where we don't use theCAP_SYS_PTRACEcapability, as such is only valid in the same user namespace. Furthermore capabilities really are not comparable between different user namespaces. So can you please replace thesame_or_ancestor_user_nswith a simple namespace equality check. Having the wrong logic in here will just be confusing in the future.
I see. You're right, what's there is conceptually not quite right. So I think we can just do:
if (current_user_ns() == tcred->user->userns) && cap_issubset(child, current) goto out; if ns_capable(tcred->user->user_ns, CAP_SYS_PTRACE) goto out;
This suffices since
root in an ancestor ns will have CAP_SYS_PTRACE the user who created the user_ns will have CAP_SYS_PTRACE
Any user in an ancestor ns who does not have CAP_SYS_PTRACE will be denied.
Quoted 2 lines
Also could you name tcred
child_creadI think that would be clearer in the test below.
Yes, will do, that'll be clearer. Thanks.
Quoted 10 lines
security/commoncap.c int cap_settime(struct timespec *ts, struct timezone *tz) · line 162
Quoted 6 lines
security/commoncap.c +4
-0+if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto out; + ret = -EPERM ; +out:rcu_read_unlock ();return ret;I also find it strange that we allow
CAP_SYS_PTRACEto allow us to ptrace processes with more capabilities than ourselves. But that is an entirely different issue.
I concur. I'm pretty sure it must stay, but it does feel icky.
I'll send out a new version
thanks
-serge
Serge E. Hallyn
maintainerRe: [PATCH 5/9] Allow ptrace from non-init user namespaces
In reply to Eric W. BiedermanQuoted 179 lines · Eric W. Biederman
Quoted 4 lines · Serge E. Hallyn
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has
CAP_SYS_PTRACEcapability.I don't see how it can go wrong at the moment but
same_or_ancestore_user_nsis too permissive and potentially inefficient. Can you please replace it with a simple user namespace equality check.Eric
Quoted 164 lines
Changelog:
Dec 31: Address feedback by Eric:
. Correct ptrace uid check
. Renamemay_ptrace_nstoptrace_capable. Also fix thecap_ptracechecks.
Jan 1: Use const cred struct
Jan 11: usetask_ns_capable()in place ofptrace_capable().Signed-off-by Serge E. Hallyn <[email protected]>--- include/linux/capability.h | 2 + include/linux/user_namespace.h | 9 +++++++ kernel/ptrace.c | 27 ++++++++++++---------- kernel/user_namespace.c | 16 +++++++++++++ security/commoncap.c | 48 +++++++++++++++++++++++++++++++++------ 5 files changed, 82 insertions(+), 20 deletions(-)include/linux/capability.h +2
-0@@ -546,6 +546,8 @@ extern const kernel_cap_t __cap_init_eff_set;*/ #define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0) +#define has_ns_capability (t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0) +/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) * @t: The task in question include/linux/user_namespace.h +9
-0@@ -39,6 +39,9 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim); +#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)@@ -66,6 +69,12 @@ static inline gid_t user_ns_map_gid(struct user_namespace *to,return gid; } +static inline int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +return 1 ; +} +#endif #endif /* _LINUX_USER_H */ kernel/ptrace.c +15
-12@@ -134,21 +134,24 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)return 0 ;rcu_read_lock (); tcred =__task_cred (task); -if ((cred->uid != tcred->euid || - cred->uid != tcred->suid || - cred->uid != tcred->uid || - cred->gid != tcred->egid || - cred->gid != tcred->sgid || - cred->gid != tcred->gid) && - !capable (CAP_SYS_PTRACE )) { -rcu_read_unlock (); -return -EPERM ; - } +if (cred->user->user_ns == tcred->user->user_ns && + (cred->uid == tcred->euid && + cred->uid == tcred->suid && + cred->uid == tcred->uid && + cred->gid == tcred->egid && + cred->gid == tcred->sgid && + cred->gid == tcred->gid)) + goto ok; +if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto ok; +rcu_read_unlock (); +return -EPERM ; +ok:rcu_read_unlock ();smp_rmb ();if (task->mm) dumpable =get_dumpable (task->mm); -if (!dumpable && !capable (CAP_SYS_PTRACE )) +if (!dumpable && !task_ns_capable (task,CAP_SYS_PTRACE ))return -EPERM ;return security_ptrace_access_check (task, mode);@@ -198,7 +201,7 @@ int ptrace_attach(struct task_struct *task) goto unlock_tasklist; task->ptrace =PT_PTRACED ; -if (capable (CAP_SYS_PTRACE )) +if (task_ns_capable (task,CAP_SYS_PTRACE )) task->ptrace |=PT_PTRACE_CAP ;__ptrace_link (task, current);kernel/user_namespace.c +16
-0@@ -129,6 +129,22 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns; +struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; +for (;;) { +if (u1 == u2) +return 1 ; +if (u1 == &init_user_ns) +return 0 ; + u1 = u1->creator->user_ns; + } +/* We never get here */ +return 0 ; +} +static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );security/commoncap.c +11
-4@@ -130,18 +130,34 @@ int cap_settime(struct timespec *ts, struct timezone *tz)* @child: The process to be accessed * @mode: The mode of attachment. * +* If we are in the same or an ancestor user_ns and have all the target +* task's capabilities, then ptrace access is allowed. +* If we have the ptrace capability to the target user_ns, then ptrace +* access is allowed. +* Else denied. +* * Determine whether a process may access another, returning 0 if permission * granted, -ve if denied. */ int cap_ptrace_access_check (struct task_struct *child,unsigned int mode) {int ret =0 ; +const struct cred *cred, *tcred;rcu_read_lock (); -if (!cap_issubset (__task_cred (child)->cap_permitted, -current_cred ()->cap_permitted) && - !capable (CAP_SYS_PTRACE )) - ret = -EPERM ; + cred =current_cred (); + tcred =__task_cred (child); +/* +* The ancestor user_ns check may be gratuitous, as I think
Quoted 8 lines
security/commoncap.c int cap_settime(struct timespec *ts, struct timezone *tz) · line 156
Quoted 5 lines
security/commoncap.c +5
-0+* we've already guaranteed that in kernel/ptrace.c. +*/ +if (same_or_ancestor_user_ns (current, child) && +cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + goto out;I have commented on this before but I took a good hard look this time, and can comment more intelligently.
Thanks, Eric.
Quoted 6 lines
The
cap_issubsetcheck is for the case where we don't use theCAP_SYS_PTRACEcapability, as such is only valid in the same user namespace. Furthermore capabilities really are not comparable between different user namespaces. So can you please replace thesame_or_ancestor_user_nswith a simple namespace equality check. Having the wrong logic in here will just be confusing in the future.
I see. You're right, what's there is conceptually not quite right. So I think we can just do:
if (current_user_ns() == tcred->user->userns) && cap_issubset(child, current) goto out; if ns_capable(tcred->user->user_ns, CAP_SYS_PTRACE) goto out;
This suffices since
root in an ancestor ns will have CAP_SYS_PTRACE the user who created the user_ns will have CAP_SYS_PTRACE
Any user in an ancestor ns who does not have CAP_SYS_PTRACE will be denied.
Quoted 2 lines
Also could you name tcred
child_creadI think that would be clearer in the test below.
Yes, will do, that'll be clearer. Thanks.
Quoted 10 lines
security/commoncap.c int cap_settime(struct timespec *ts, struct timezone *tz) · line 162
Quoted 6 lines
security/commoncap.c +4
-0+if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto out; + ret = -EPERM ; +out:rcu_read_unlock ();return ret;I also find it strange that we allow
CAP_SYS_PTRACEto allow us to ptrace processes with more capabilities than ourselves. But that is an entirely different issue.
I concur. I'm pretty sure it must stay, but it does feel icky.
I'll send out a new version
thanks
-serge
Serge E. Hallyn
maintainerPATCH 1/1 userns: ptrace: incorporate feedback from Eric
same_or_ancestore_user_ns() was not an appropriate check to constrain cap_issubset. Rather, cap_issubset() only is meaningful when both capsets are in the same user_ns.
--- include/linux/user_namespace.h | 9 --------- kernel/user_namespace.c | 16 ---------------- security/commoncap.c | 28 ++++++++++------------------ 3 files changed, 10 insertions(+), 43 deletions(-)
include/linux/user_namespace.h +0 -9
@@ -39,9 +39,6 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); -int same_or_ancestor_user_ns (struct task_struct *task, -struct task_struct *victim); -#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)
@@ -69,12 +66,6 @@ static inline gid_t user_ns_map_gid(struct user_namespace *to,return gid; } -static inline int same_or_ancestor_user_ns (struct task_struct *task, -struct task_struct *victim) -{ -return 1 ; -} -#endif #endif /* _LINUX_USER_H */
kernel/user_namespace.c +0 -16
@@ -129,22 +129,6 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } -int same_or_ancestor_user_ns (struct task_struct *task, -struct task_struct *victim) -{ -struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns; -struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; -for (;;) { -if (u1 == u2) -return 1 ; -if (u1 == &init_user_ns) -return 0 ; - u1 = u1->creator->user_ns; - } -/* We never get here */ -return 0 ; -} -static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );
security/commoncap.c +10 -18
@@ -142,19 +142,15 @@ int cap_settime(struct timespec *ts, struct timezone *tz)int cap_ptrace_access_check (struct task_struct *child,unsigned int mode) {int ret =0 ; -const struct cred *cred, *tcred; +const struct cred *cred, *child_cred;rcu_read_lock (); cred =current_cred (); - tcred =__task_cred (child); -/* -* The ancestor user_ns check may be gratuitous, as I think -* we've already guaranteed that in kernel/ptrace.c. -*/ -if (same_or_ancestor_user_ns (current, child) && -cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + child_cred =__task_cred (child); +if (cred->user->user_ns == child_cred->user->user_ns && +cap_issubset (child_cred->cap_permitted, cred->cap_permitted)) goto out; -if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) +if (ns_capable (child_cred->user->user_ns,CAP_SYS_PTRACE )) goto out; ret = -EPERM ; out:
@@ -178,19 +174,15 @@ out:int cap_ptrace_traceme (struct task_struct *parent) {int ret =0 ; -const struct cred *cred, *tcred; +const struct cred *cred, *child_cred;rcu_read_lock (); cred =__task_cred (parent); - tcred =current_cred (); -/* -* The ancestor user_ns check may be gratuitous, as I think -* we've already guaranteed that in kernel/ptrace.c. -*/ -if (same_or_ancestor_user_ns (parent, current) && -cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + child_cred =current_cred (); +if (cred->user->user_ns == child_cred->user->user_ns && +cap_issubset (child_cred->cap_permitted, cred->cap_permitted)) goto out; -if (has_ns_capability (parent, tcred->user->user_ns,CAP_SYS_PTRACE )) +if (has_ns_capability (parent, child_cred->user->user_ns,CAP_SYS_PTRACE )) goto out; ret = -EPERM ; out:
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH] userns: ptrace: incorporate feedback from Eric
Quoted 3 lines · On Thu, 24 Feb 2011 00:49:01 +0000 "Serge E. Hallyn"…
same_or_ancestore_user_ns()was not an appropriate check to constraincap_issubset. Rather,cap_issubset()only is meaningful when both capsets are in the sameuser_ns.
I queued this as a fix against userns-allow-ptrace-from-non-init-user-namespaces.patch, but I get the feeling that it would be better to just drop everything and start again?
Re: [PATCH] userns: ptrace: incorporate feedback from Eric
In reply to Serge E. HallynQuoted 3 lines · On Thu, 24 Feb 2011 00:49:01 +0000 "Serge E. Hallyn" <[email protected]> wrote:
same_or_ancestore_user_ns()was not an appropriate check to constraincap_issubset. Rather,cap_issubset()only is meaningful when both capsets are in the sameuser_ns.
I queued this as a fix against userns-allow-ptrace-from-non-init-user-namespaces.patch, but I get the feeling that it would be better to just drop everything and start again?
Serge E. Hallyn
maintainerRe: [PATCH] userns: ptrace: incorporate feedback from Eric
Quoted 11 lines · Andrew Morton
Quoted 3 lines · On Thu, 24 Feb 2011 00:49:01 +0000 "Serge E. Hallyn" <[email protected]> wrote:
same_or_ancestore_user_ns()was not an appropriate check to constraincap_issubset. Rather,cap_issubset()only is meaningful when both capsets are in the sameuser_ns.I queued this as a fix against userns-allow-ptrace-from-non-init-user-namespaces.patch, but I get the feeling that it would be better to just drop everything and start again?
Sure, I'll rebase and resend. I wonder if I should trim the Cc list for the next round.
thanks,
-serge
A message that is not in the archive (sent off-list or not yet received): 20110223165651.cf248f3b.akpm-de/[email protected]
Re: [PATCH] userns: ptrace: incorporate feedback from Eric
Quoted 11 lines · Andrew Morton
Quoted 3 lines · On Thu, 24 Feb 2011 00:49:01 +0000 "Serge E. Hallyn"…
same_or_ancestore_user_ns()was not an appropriate check to constraincap_issubset. Rather,cap_issubset()only is meaningful when both capsets are in the sameuser_ns.I queued this as a fix against userns-allow-ptrace-from-non-init-user-namespaces.patch, but I get the feeling that it would be better to just drop everything and start again?
Sure, I'll rebase and resend. I wonder if I should trim the Cc list for the next round.
thanks,
-serge
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/1 userns: ptrace: incorporate feedback from Eric
same_or_ancestore_user_ns() was not an appropriate check to constrain cap_issubset. Rather, cap_issubset() only is meaningful when both capsets are in the same user_ns.
--- include/linux/user_namespace.h | 9 --------- kernel/user_namespace.c | 16 ---------------- security/commoncap.c | 28 ++++++++++------------------ 3 files changed, 10 insertions(+), 43 deletions(-)
include/linux/user_namespace.h +0 -9
@@ -39,9 +39,6 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); -int same_or_ancestor_user_ns (struct task_struct *task, -struct task_struct *victim); -#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)
@@ -69,12 +66,6 @@ static inline gid_t user_ns_map_gid(struct user_namespace *to,return gid; } -static inline int same_or_ancestor_user_ns (struct task_struct *task, -struct task_struct *victim) -{ -return 1 ; -} -#endif #endif /* _LINUX_USER_H */
kernel/user_namespace.c +0 -16
@@ -129,22 +129,6 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } -int same_or_ancestor_user_ns (struct task_struct *task, -struct task_struct *victim) -{ -struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns; -struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; -for (;;) { -if (u1 == u2) -return 1 ; -if (u1 == &init_user_ns) -return 0 ; - u1 = u1->creator->user_ns; - } -/* We never get here */ -return 0 ; -} -static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );
security/commoncap.c +10 -18
@@ -142,19 +142,15 @@ int cap_settime(struct timespec *ts, struct timezone *tz)int cap_ptrace_access_check (struct task_struct *child,unsigned int mode) {int ret =0 ; -const struct cred *cred, *tcred; +const struct cred *cred, *child_cred;rcu_read_lock (); cred =current_cred (); - tcred =__task_cred (child); -/* -* The ancestor user_ns check may be gratuitous, as I think -* we've already guaranteed that in kernel/ptrace.c. -*/ -if (same_or_ancestor_user_ns (current, child) && -cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + child_cred =__task_cred (child); +if (cred->user->user_ns == child_cred->user->user_ns && +cap_issubset (child_cred->cap_permitted, cred->cap_permitted)) goto out; -if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) +if (ns_capable (child_cred->user->user_ns,CAP_SYS_PTRACE )) goto out; ret = -EPERM ; out:
@@ -178,19 +174,15 @@ out:int cap_ptrace_traceme (struct task_struct *parent) {int ret =0 ; -const struct cred *cred, *tcred; +const struct cred *cred, *child_cred;rcu_read_lock (); cred =__task_cred (parent); - tcred =current_cred (); -/* -* The ancestor user_ns check may be gratuitous, as I think -* we've already guaranteed that in kernel/ptrace.c. -*/ -if (same_or_ancestor_user_ns (parent, current) && -cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + child_cred =current_cred (); +if (cred->user->user_ns == child_cred->user->user_ns && +cap_issubset (child_cred->cap_permitted, cred->cap_permitted)) goto out; -if (has_ns_capability (parent, tcred->user->user_ns,CAP_SYS_PTRACE )) +if (has_ns_capability (parent, child_cred->user->user_ns,CAP_SYS_PTRACE )) goto out; ret = -EPERM ; out:
Signature · 1 line
1.7.0.4
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
In reply to Serge E. HallynQuoted 11 lines · On Thu, 17 Feb 2011 15:03:33 +0000 "Serge E. Hallyn" <[email protected]> wrote:
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has
CAP_SYS_PTRACEcapability....
include/linux/capability.h extern const kernel_cap_t __cap_init_eff_set; · line 549
Quoted 4 lines
include/linux/capability.h +1
-0@@ -546,6 +546,8 @@ extern const kernel_cap_t __cap_init_eff_set;*/ #define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0) +#define has_ns_capability (t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0)
macroitis.
include/linux/user_namespace.h static inline void put_user_ns(struct user_namespace *ns) · line 43
Quoted 13 lines
include/linux/capability.h +0
-0/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) * @t: The task in question include/linux/user_namespace.h +2
-0@@ -39,6 +39,9 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim);
bool.
Quoted 16 lines
include/linux/user_namespace.h +0
-0#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)kernel/user_namespace.c +4
-0@@ -129,6 +129,22 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns;
kernel/user_namespace.c gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t · line 145
Quoted 10 lines
kernel/user_namespace.c +10
-0+struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; +for (;;) { +if (u1 == u2) +return 1 ; +if (u1 == &init_user_ns) +return 0 ; + u1 = u1->creator->user_ns; + } +/* We never get here */ +return 0 ;
Remove?
Quoted 14 lines
kernel/user_namespace.c +3
-0+} +static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );int cap_ptrace_access_check (struct task_struct *child,unsigned int mode) {int ret =0 ; +const struct cred *cred, *tcred;rcu_read_lock ();
kernel/user_namespace.c gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t
Quoted 10 lines
kernel/user_namespace.c +6
-4-if (!cap_issubset (__task_cred (child)->cap_permitted, -current_cred ()->cap_permitted) && - !capable (CAP_SYS_PTRACE )) - ret = -EPERM ; + cred =current_cred (); + tcred =__task_cred (child); +/* +* The ancestor user_ns check may be gratuitous, as I think +* we've already guaranteed that in kernel/ptrace.c. +*/
?
Quoted 13 lines
kernel/user_namespace.c +7
-0+if (same_or_ancestor_user_ns (current, child) && +cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + goto out; +if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto out; + ret = -EPERM ; +out:rcu_read_unlock ();return ret; }
Serge E. Hallyn
maintainerRe: [PATCH 5/9] Allow ptrace from non-init user namespaces
Quoted 17 lines · Andrew Morton
Quoted 14 lines · On Thu, 17 Feb 2011 15:03:33 +0000 "Serge E. Hallyn" <[email protected]> wrote:
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has
CAP_SYS_PTRACEcapability....
include/linux/capability.h +0
-0@@ -546,6 +546,8 @@ extern const kernel_cap_t __cap_init_eff_set;*/ #define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)
Quoted 3 lines
include/linux/capability.h extern const kernel_cap_t __cap_init_eff_set; · line 549
Quoted 1 line
include/linux/capability.h +1
-0+#define has_ns_capability (t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0)macroitis.
Thanks for the review, Andrew. Unfortunately this one is hard to turn into a function beecause it uses security_real_capable(), which is sometimes defined in security/security.c as a real function, and other times as a static inline in include/linux/security.h. So I'd have to #include security.h in capability.h, but security.h already #includes capability.h.
All the other comments affect same_or_ancestor_user_ns(), which following Eric's feedback is going away.
Quoted 86 lines
include/linux/user_namespace.h static inline void put_user_ns(struct user_namespace *ns) · line 43
Quoted 13 lines
include/linux/capability.h +0
-0/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) * @t: The task in question include/linux/user_namespace.h +2
-0@@ -39,6 +39,9 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim);bool.
Quoted 16 lines
include/linux/user_namespace.h +0
-0#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)kernel/user_namespace.c +4
-0@@ -129,6 +129,22 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns;kernel/user_namespace.c gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t · line 145
Quoted 10 lines
kernel/user_namespace.c +10
-0+struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; +for (;;) { +if (u1 == u2) +return 1 ; +if (u1 == &init_user_ns) +return 0 ; + u1 = u1->creator->user_ns; + } +/* We never get here */ +return 0 ;Remove?
Quoted 14 lines
kernel/user_namespace.c +3
-0+} +static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );int cap_ptrace_access_check (struct task_struct *child,unsigned int mode) {int ret =0 ; +const struct cred *cred, *tcred;rcu_read_lock ();kernel/user_namespace.c gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t
Quoted 10 lines
kernel/user_namespace.c +6
-4-if (!cap_issubset (__task_cred (child)->cap_permitted, -current_cred ()->cap_permitted) && - !capable (CAP_SYS_PTRACE )) - ret = -EPERM ; + cred =current_cred (); + tcred =__task_cred (child); +/* +* The ancestor user_ns check may be gratuitous, as I think +* we've already guaranteed that in kernel/ptrace.c. +*/ ?
Quoted 13 lines
kernel/user_namespace.c +7
-0+if (same_or_ancestor_user_ns (current, child) && +cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + goto out; +if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto out; + ret = -EPERM ; +out:rcu_read_unlock ();return ret; }
A message that is not in the archive (sent off-list or not yet received): 20110218155925.f7d30a52.akpm-de/[email protected]
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
Quoted 17 lines · Andrew Morton
Quoted 14 lines · On Thu, 17 Feb 2011 15:03:33 +0000 "Serge E. Hallyn"…
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has
CAP_SYS_PTRACEcapability....
include/linux/capability.h +0
-0@@ -546,6 +546,8 @@ extern const kernel_cap_t __cap_init_eff_set;*/ #define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)
Quoted 3 lines
include/linux/capability.h extern const kernel_cap_t __cap_init_eff_set; · line 549
Quoted 1 line
include/linux/capability.h +1
-0+#define has_ns_capability (t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0)macroitis.
Thanks for the review, Andrew. Unfortunately this one is hard to turn into a function beecause it uses security_real_capable(), which is sometimes defined in security/security.c as a real function, and other times as a static inline in include/linux/security.h. So I'd have to #include security.h in capability.h, but security.h already #includes capability.h.
All the other comments affect same_or_ancestor_user_ns(), which following Eric's feedback is going away.
Quoted 86 lines
include/linux/user_namespace.h static inline void put_user_ns(struct user_namespace *ns) · line 43
Quoted 13 lines
include/linux/capability.h +0
-0/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) * @t: The task in question include/linux/user_namespace.h +2
-0@@ -39,6 +39,9 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim);bool.
Quoted 16 lines
include/linux/user_namespace.h +0
-0#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)kernel/user_namespace.c +4
-0@@ -129,6 +129,22 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns;kernel/user_namespace.c gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t · line 145
Quoted 10 lines
kernel/user_namespace.c +10
-0+struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; +for (;;) { +if (u1 == u2) +return 1 ; +if (u1 == &init_user_ns) +return 0 ; + u1 = u1->creator->user_ns; + } +/* We never get here */ +return 0 ;Remove?
Quoted 14 lines
kernel/user_namespace.c +3
-0+} +static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );int cap_ptrace_access_check (struct task_struct *child,unsigned int mode) {int ret =0 ; +const struct cred *cred, *tcred;rcu_read_lock ();kernel/user_namespace.c gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t
Quoted 10 lines
kernel/user_namespace.c +6
-4-if (!cap_issubset (__task_cred (child)->cap_permitted, -current_cred ()->cap_permitted) && - !capable (CAP_SYS_PTRACE )) - ret = -EPERM ; + cred =current_cred (); + tcred =__task_cred (child); +/* +* The ancestor user_ns check may be gratuitous, as I think +* we've already guaranteed that in kernel/ptrace.c. +*/ ?
Quoted 13 lines
kernel/user_namespace.c +7
-0+if (same_or_ancestor_user_ns (current, child) && +cap_issubset (tcred->cap_permitted, cred->cap_permitted)) + goto out; +if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto out; + ret = -EPERM ; +out:rcu_read_unlock ();return ret; }
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
In reply to Serge E. HallynQuoted 13 lines · Serge E. Hallyn
ptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has
CAP_SYS_PTRACEcapability.Changelog:
Dec 31: Address feedback by Eric:
. Correct ptrace uid check
. Renamemay_ptrace_nstoptrace_capable. Also fix thecap_ptracechecks.
Jan 1: Use const cred struct
Jan 11: usetask_ns_capable()in place ofptrace_capable().
Quoted 1 line
Signed-off-by Serge E. Hallyn<[email protected]>
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
In reply to Serge E. HallynQuoted 1 line · Serge E. Hallyn
ptrace is allowed to tasks in the same user namespace according to
There's a verb missing after 'allowed to' - presumably 'trace'.
David
Re: [PATCH 5/9] Allow ptrace from non-init user namespaces
In reply to Serge E. Hallynkernel/user_namespace.c gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t · line 136
Quoted 5 lines · Serge E. Hallyn
kernel/user_namespace.c +5
-0+int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns; +struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns;
Hmmm. task_cred_xxx() uses task->real_cred, which is correct for victim (the object), but normally you'd use task->cred for task (the subject). However, in this case, I think it's probably okay.
David
Serge E. Hallyn
maintainerPATCH 6/9 user namespaces: convert all capable checks in kernel/sys.c
In reply to Serge E. HallynThis allows setuid/setgid in containers. It also fixes some corner cases where kernel logic foregoes capability checks when uids are equivalent. The latter will need to be done throughout the whole kernel.
Changelog:
Jan 11: Use nsown_capable() as suggested by Bastian Blank. Jan 11: Fix logic errors in uid checks pointed out by Bastian. Feb 15: allow prlimit to current (was regression in previous version)
--- kernel/sys.c | 74 ++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 47 insertions(+), 27 deletions(-)
kernel/sys.c +47 -27
@@ -118,17 +118,29 @@ EXPORT_SYMBOL(cad_pid);void (*pm_power_off_prepare)(void ); +/* called with rcu_read_lock, creds are safe */ +static inline int set_one_prio_perm (struct task_struct *p) +{ +const struct cred *cred =current_cred (), *pcred =__task_cred (p); + +if (pcred->user->user_ns == cred->user->user_ns && + (pcred->uid == cred->euid || + pcred->euid == cred->euid)) +return 1 ; +if (ns_capable (pcred->user->user_ns,CAP_SYS_NICE )) +return 1 ; +return 0 ; +} +/* * set the priority of a task * - the caller must hold the RCU read lock */ static int set_one_prio (struct task_struct *p,int niceval,int error) { -const struct cred *cred =current_cred (), *pcred =__task_cred (p);int no_nice; -if (pcred->uid != cred->euid && - pcred->euid != cred->euid && !capable (CAP_SYS_NICE )) { +if (!set_one_prio_perm (p)) { error = -EPERM ; goto out; }
@@ -502,7 +514,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)if (rgid != (gid_t) -1 ) {if (old->gid == rgid || old->egid == rgid || -capable (CAP_SETGID )) +nsown_capable (CAP_SETGID )) new->gid = rgid;else goto error;
@@ -511,7 +523,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)if (old->gid == egid || old->egid == egid || old->sgid == egid || -capable (CAP_SETGID )) +nsown_capable (CAP_SETGID )) new->egid = egid;else goto error;
@@ -546,7 +558,7 @@ SYSCALL_DEFINE1(setgid, gid_t, gid) old =current_cred (); retval = -EPERM ; -if (capable (CAP_SETGID )) +if (nsown_capable (CAP_SETGID )) new->gid = new->egid = new->sgid = new->fsgid = gid;else if (gid == old->gid || gid == old->sgid) new->egid = new->fsgid = gid;
@@ -613,7 +625,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid) new->uid = ruid;if (old->uid != ruid && old->euid != ruid && - !capable (CAP_SETUID )) + !nsown_capable (CAP_SETUID )) goto error; }
@@ -622,7 +634,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)if (old->uid != euid && old->euid != euid && old->suid != euid && - !capable (CAP_SETUID )) + !nsown_capable (CAP_SETUID )) goto error; }
@@ -670,7 +682,7 @@ SYSCALL_DEFINE1(setuid, uid_t, uid) old =current_cred (); retval = -EPERM ; -if (capable (CAP_SETUID )) { +if (nsown_capable (CAP_SETUID )) { new->suid = new->uid = uid;if (uid != old->uid) { retval =set_user (new);
@@ -712,7 +724,7 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) old =current_cred (); retval = -EPERM ; -if (!capable (CAP_SETUID )) { +if (!nsown_capable (CAP_SETUID )) {if (ruid != (uid_t) -1 && ruid != old->uid && ruid != old->euid && ruid != old->suid) goto error;
@@ -776,7 +788,7 @@ SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid) old =current_cred (); retval = -EPERM ; -if (!capable (CAP_SETGID )) { +if (!nsown_capable (CAP_SETGID )) {if (rgid != (gid_t) -1 && rgid != old->gid && rgid != old->egid && rgid != old->sgid) goto error;
@@ -836,7 +848,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)if (uid == old->uid || uid == old->euid || uid == old->suid || uid == old->fsuid || -capable (CAP_SETUID )) { +nsown_capable (CAP_SETUID )) {if (uid != old_fsuid) { new->fsuid = uid;if (security_task_fix_setuid (new, old,LSM_SETID_FS ) ==0 )
@@ -869,7 +881,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)if (gid == old->gid || gid == old->egid || gid == old->sgid || gid == old->fsgid || -capable (CAP_SETGID )) { +nsown_capable (CAP_SETGID )) {if (gid != old_fsgid) { new->fsgid = gid; goto change_okay;
@@ -1177,8 +1189,11 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) { +printk (KERN_NOTICE "%s: did not have CAP_SYS_ADMIN\n" , __func__);return -EPERM ; + } +printk (KERN_NOTICE "%s: did have CAP_SYS_ADMIN\n" , __func__);if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;down_write (&uts_sem);
@@ -1226,7 +1241,7 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!capable (CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN ))return -EPERM ;if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;
@@ -1341,6 +1356,8 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource, rlim = tsk->signal->rlim + resource;task_lock (tsk->group_leader);if (new_rlim) { +/* Keep the capable check against init_user_ns until +cgroups can contain all limits */ if (new_rlim->rlim_max > rlim->rlim_max && !capable (CAP_SYS_RESOURCE )) retval = -EPERM ;
@@ -1384,19 +1401,22 @@ static int check_prlimit_permission(struct task_struct *task) {const struct cred *cred =current_cred (), *tcred; - tcred =__task_cred (task); -if (current != task && - (cred->uid != tcred->euid || - cred->uid != tcred->suid || - cred->uid != tcred->uid || - cred->gid != tcred->egid || - cred->gid != tcred->sgid || - cred->gid != tcred->gid) && - !capable (CAP_SYS_RESOURCE )) { -return -EPERM ; - } +if (current == task) +return 0 ; -return 0 ; + tcred =__task_cred (task); +if (cred->user->user_ns == tcred->user->user_ns && + (cred->uid == tcred->euid && + cred->uid == tcred->suid && + cred->uid == tcred->uid && + cred->gid == tcred->egid && + cred->gid == tcred->sgid && + cred->gid == tcred->gid)) +return 0 ; +if (ns_capable (tcred->user->user_ns,CAP_SYS_RESOURCE )) +return 0 ; + +return -EPERM ; } SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Eric W. Biederman
Acked-byRe: [PATCH 6/9] user namespaces: convert all capable checks in kernel/sys.c
Quoted 4 lines · Serge E. Hallyn
This allows setuid/setgid in containers. It also fixes some corner cases where kernel logic foregoes capability checks when uids are equivalent. The latter will need to be done throughout the whole kernel.
Except for the extra printk in sethostname this looks good.
Quoted 139 lines
Changelog:
Jan 11: Usensown_capable()as suggested by Bastian Blank. Jan 11: Fix logic errors in uid checks pointed out by Bastian. Feb 15: allow prlimit to current (was regression in previous version)Signed-off-by Serge E. Hallyn <[email protected]>--- kernel/sys.c | 74 ++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 47 insertions(+), 27 deletions(-)kernel/sys.c +25
-13@@ -118,17 +118,29 @@ EXPORT_SYMBOL(cad_pid);void (*pm_power_off_prepare)(void ); +/* called with rcu_read_lock, creds are safe */ +static inline int set_one_prio_perm (struct task_struct *p) +{ +const struct cred *cred =current_cred (), *pcred =__task_cred (p); + +if (pcred->user->user_ns == cred->user->user_ns && + (pcred->uid == cred->euid || + pcred->euid == cred->euid)) +return 1 ; +if (ns_capable (pcred->user->user_ns,CAP_SYS_NICE )) +return 1 ; +return 0 ; +} +/* * set the priority of a task * - the caller must hold the RCU read lock */ static int set_one_prio (struct task_struct *p,int niceval,int error) { -const struct cred *cred =current_cred (), *pcred =__task_cred (p);int no_nice; -if (pcred->uid != cred->euid && - pcred->euid != cred->euid && !capable (CAP_SYS_NICE )) { +if (!set_one_prio_perm (p)) { error = -EPERM ; goto out; }@@ -502,7 +514,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)if (rgid != (gid_t) -1 ) {if (old->gid == rgid || old->egid == rgid || -capable (CAP_SETGID )) +nsown_capable (CAP_SETGID )) new->gid = rgid;else goto error;@@ -511,7 +523,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)if (old->gid == egid || old->egid == egid || old->sgid == egid || -capable (CAP_SETGID )) +nsown_capable (CAP_SETGID )) new->egid = egid;else goto error;@@ -546,7 +558,7 @@ SYSCALL_DEFINE1(setgid, gid_t, gid) old =current_cred (); retval = -EPERM ; -if (capable (CAP_SETGID )) +if (nsown_capable (CAP_SETGID )) new->gid = new->egid = new->sgid = new->fsgid = gid;else if (gid == old->gid || gid == old->sgid) new->egid = new->fsgid = gid;@@ -613,7 +625,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid) new->uid = ruid;if (old->uid != ruid && old->euid != ruid && - !capable (CAP_SETUID )) + !nsown_capable (CAP_SETUID )) goto error; }@@ -622,7 +634,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)if (old->uid != euid && old->euid != euid && old->suid != euid && - !capable (CAP_SETUID )) + !nsown_capable (CAP_SETUID )) goto error; }@@ -670,7 +682,7 @@ SYSCALL_DEFINE1(setuid, uid_t, uid) old =current_cred (); retval = -EPERM ; -if (capable (CAP_SETUID )) { +if (nsown_capable (CAP_SETUID )) { new->suid = new->uid = uid;if (uid != old->uid) { retval =set_user (new);@@ -712,7 +724,7 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) old =current_cred (); retval = -EPERM ; -if (!capable (CAP_SETUID )) { +if (!nsown_capable (CAP_SETUID )) {if (ruid != (uid_t) -1 && ruid != old->uid && ruid != old->euid && ruid != old->suid) goto error;@@ -776,7 +788,7 @@ SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid) old =current_cred (); retval = -EPERM ; -if (!capable (CAP_SETGID )) { +if (!nsown_capable (CAP_SETGID )) {if (rgid != (gid_t) -1 && rgid != old->gid && rgid != old->egid && rgid != old->sgid) goto error;@@ -836,7 +848,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)if (uid == old->uid || uid == old->euid || uid == old->suid || uid == old->fsuid || -capable (CAP_SETUID )) { +nsown_capable (CAP_SETUID )) {if (uid != old_fsuid) { new->fsuid = uid;if (security_task_fix_setuid (new, old,LSM_SETID_FS ) ==0 )@@ -869,7 +881,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)if (gid == old->gid || gid == old->egid || gid == old->sgid || gid == old->fsgid || -capable (CAP_SETGID )) { +nsown_capable (CAP_SETGID )) {if (gid != old_fsgid) { new->fsgid = gid; goto change_okay;
kernel/sys.c SYSCALL_DEFINE2(sethostname, char __user *, name, int, len) · line 1196
Quoted 9 lines
kernel/sys.c +4
-1@@ -1177,8 +1189,11 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) { +printk (KERN_NOTICE "%s: did not have CAP_SYS_ADMIN\n" , __func__);return -EPERM ; + } +printk (KERN_NOTICE "%s: did have CAP_SYS_ADMIN\n" , __func__);
Ouch! This new print statement could be really annoying if an unprivileged user calls sethostname. Could you remove it?
Quoted 56 lines
kernel/sys.c +18
-13if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;down_write (&uts_sem);@@ -1226,7 +1241,7 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!capable (CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN ))return -EPERM ;if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;@@ -1341,6 +1356,8 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource, rlim = tsk->signal->rlim + resource;task_lock (tsk->group_leader);if (new_rlim) { +/* Keep the capable check against init_user_ns until +cgroups can contain all limits */ if (new_rlim->rlim_max > rlim->rlim_max && !capable (CAP_SYS_RESOURCE )) retval = -EPERM ;@@ -1384,19 +1401,22 @@ static int check_prlimit_permission(struct task_struct *task) {const struct cred *cred =current_cred (), *tcred; - tcred =__task_cred (task); -if (current != task && - (cred->uid != tcred->euid || - cred->uid != tcred->suid || - cred->uid != tcred->uid || - cred->gid != tcred->egid || - cred->gid != tcred->sgid || - cred->gid != tcred->gid) && - !capable (CAP_SYS_RESOURCE )) { -return -EPERM ; - } +if (current == task) +return 0 ; -return 0 ; + tcred =__task_cred (task); +if (cred->user->user_ns == tcred->user->user_ns && + (cred->uid == tcred->euid && + cred->uid == tcred->suid && + cred->uid == tcred->uid && + cred->gid == tcred->egid && + cred->gid == tcred->sgid && + cred->gid == tcred->gid)) +return 0 ; +if (ns_capable (tcred->user->user_ns,CAP_SYS_RESOURCE )) +return 0 ; + +return -EPERM ; } SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
Re: [PATCH 6/9] user namespaces: convert all capable checks in kernel/sys.c
In reply to a message not in the archiveQuoted 17 lines · On Thu, 17 Feb 2011 15:03:42 +0000 "Serge E. Hallyn"…
This allows setuid/setgid in containers. It also fixes some corner cases where kernel logic foregoes capability checks when uids are equivalent. The latter will need to be done throughout the whole kernel.
...
kernel/sys.c +3
-0@@ -118,17 +118,29 @@ EXPORT_SYMBOL(cad_pid);void (*pm_power_off_prepare)(void ); +/* called with rcu_read_lock, creds are safe */ +static inline int set_one_prio_perm (struct task_struct *p) +{
kernel/sys.c EXPORT_SYMBOL(cad_pid); · line 133
Quoted 10 lines
kernel/sys.c +10
-0+const struct cred *cred =current_cred (), *pcred =__task_cred (p); + +if (pcred->user->user_ns == cred->user->user_ns && + (pcred->uid == cred->euid || + pcred->euid == cred->euid)) +return 1 ; +if (ns_capable (pcred->user->user_ns,CAP_SYS_NICE )) +return 1 ; +return 0 ; +}
uninline. Document return value?
Quoted 3 lines
...
Re: [PATCH 6/9] user namespaces: convert all capable checks in kernel/sys.c
In reply to a message not in the archivekernel/sys.c SYSCALL_DEFINE2(sethostname, char __user *, name, int, len) · line 1199
Quoted 13 lines · On Thu, 17 Feb 2011 15:03:42 +0000 "Serge E. Hallyn"…
kernel/sys.c +4
-1@@ -1177,8 +1189,11 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) { +printk (KERN_NOTICE "%s: did not have CAP_SYS_ADMIN\n" , __func__);return -EPERM ; + } +printk (KERN_NOTICE "%s: did have CAP_SYS_ADMIN\n" , __func__);if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;down_write (&uts_sem);
Left over debugging printks?
Daniel Lezcano
Acked-byRe: [PATCH 6/9] user namespaces: convert all capable checks in kernel/sys.c
In reply to a message not in the archiveQuoted 11 lines · Serge E. Hallyn
This allows setuid/setgid in containers. It also fixes some corner cases where kernel logic foregoes capability checks when uids are equivalent. The latter will need to be done throughout the whole kernel.
Changelog:
Jan 11: Usensown_capable()as suggested by Bastian Blank. Jan 11: Fix logic errors in uid checks pointed out by Bastian. Feb 15: allow prlimit to current (was regression in previous version)Signed-off-by Serge E. Hallyn<[email protected]>
kernel/sys.c SYSCALL_DEFINE2(sethostname, char __user *, name, int, len) · line 1196
Quoted 7 lines
kernel/sys.c +4
-1-if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) { +printk (KERN_NOTICE "%s: did not have CAP_SYS_ADMIN\n" , __func__);return -EPERM ; + } +printk (KERN_NOTICE "%s: did have CAP_SYS_ADMIN\n" , __func__);
A couple of printk left here.
Re: [PATCH 6/9] user namespaces: convert all capable checks in kernel/sys.c
In reply to Serge E. HallynQuoted 4 lines · Serge E. Hallyn
This allows setuid/setgid in containers. It also fixes some corner cases where kernel logic foregoes capability checks when uids are equivalent. The latter will need to be done throughout the whole kernel.
Except for the extra printk in sethostname this looks good.
Quoted 139 lines
Changelog:
Jan 11: Usensown_capable()as suggested by Bastian Blank. Jan 11: Fix logic errors in uid checks pointed out by Bastian. Feb 15: allow prlimit to current (was regression in previous version)Signed-off-by Serge E. Hallyn <[email protected]>--- kernel/sys.c | 74 ++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 47 insertions(+), 27 deletions(-)kernel/sys.c +25
-13@@ -118,17 +118,29 @@ EXPORT_SYMBOL(cad_pid);void (*pm_power_off_prepare)(void ); +/* called with rcu_read_lock, creds are safe */ +static inline int set_one_prio_perm (struct task_struct *p) +{ +const struct cred *cred =current_cred (), *pcred =__task_cred (p); + +if (pcred->user->user_ns == cred->user->user_ns && + (pcred->uid == cred->euid || + pcred->euid == cred->euid)) +return 1 ; +if (ns_capable (pcred->user->user_ns,CAP_SYS_NICE )) +return 1 ; +return 0 ; +} +/* * set the priority of a task * - the caller must hold the RCU read lock */ static int set_one_prio (struct task_struct *p,int niceval,int error) { -const struct cred *cred =current_cred (), *pcred =__task_cred (p);int no_nice; -if (pcred->uid != cred->euid && - pcred->euid != cred->euid && !capable (CAP_SYS_NICE )) { +if (!set_one_prio_perm (p)) { error = -EPERM ; goto out; }@@ -502,7 +514,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)if (rgid != (gid_t) -1 ) {if (old->gid == rgid || old->egid == rgid || -capable (CAP_SETGID )) +nsown_capable (CAP_SETGID )) new->gid = rgid;else goto error;@@ -511,7 +523,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)if (old->gid == egid || old->egid == egid || old->sgid == egid || -capable (CAP_SETGID )) +nsown_capable (CAP_SETGID )) new->egid = egid;else goto error;@@ -546,7 +558,7 @@ SYSCALL_DEFINE1(setgid, gid_t, gid) old =current_cred (); retval = -EPERM ; -if (capable (CAP_SETGID )) +if (nsown_capable (CAP_SETGID )) new->gid = new->egid = new->sgid = new->fsgid = gid;else if (gid == old->gid || gid == old->sgid) new->egid = new->fsgid = gid;@@ -613,7 +625,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid) new->uid = ruid;if (old->uid != ruid && old->euid != ruid && - !capable (CAP_SETUID )) + !nsown_capable (CAP_SETUID )) goto error; }@@ -622,7 +634,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)if (old->uid != euid && old->euid != euid && old->suid != euid && - !capable (CAP_SETUID )) + !nsown_capable (CAP_SETUID )) goto error; }@@ -670,7 +682,7 @@ SYSCALL_DEFINE1(setuid, uid_t, uid) old =current_cred (); retval = -EPERM ; -if (capable (CAP_SETUID )) { +if (nsown_capable (CAP_SETUID )) { new->suid = new->uid = uid;if (uid != old->uid) { retval =set_user (new);@@ -712,7 +724,7 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) old =current_cred (); retval = -EPERM ; -if (!capable (CAP_SETUID )) { +if (!nsown_capable (CAP_SETUID )) {if (ruid != (uid_t) -1 && ruid != old->uid && ruid != old->euid && ruid != old->suid) goto error;@@ -776,7 +788,7 @@ SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid) old =current_cred (); retval = -EPERM ; -if (!capable (CAP_SETGID )) { +if (!nsown_capable (CAP_SETGID )) {if (rgid != (gid_t) -1 && rgid != old->gid && rgid != old->egid && rgid != old->sgid) goto error;@@ -836,7 +848,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)if (uid == old->uid || uid == old->euid || uid == old->suid || uid == old->fsuid || -capable (CAP_SETUID )) { +nsown_capable (CAP_SETUID )) {if (uid != old_fsuid) { new->fsuid = uid;if (security_task_fix_setuid (new, old,LSM_SETID_FS ) ==0 )@@ -869,7 +881,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)if (gid == old->gid || gid == old->egid || gid == old->sgid || gid == old->fsgid || -capable (CAP_SETGID )) { +nsown_capable (CAP_SETGID )) {if (gid != old_fsgid) { new->fsgid = gid; goto change_okay;
kernel/sys.c SYSCALL_DEFINE2(sethostname, char __user *, name, int, len) · line 1196
Quoted 9 lines
kernel/sys.c +4
-1@@ -1177,8 +1189,11 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) { +printk (KERN_NOTICE "%s: did not have CAP_SYS_ADMIN\n" , __func__);return -EPERM ; + } +printk (KERN_NOTICE "%s: did have CAP_SYS_ADMIN\n" , __func__);
Ouch! This new print statement could be really annoying if an unprivileged user calls sethostname. Could you remove it?
Quoted 56 lines
kernel/sys.c +18
-13if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;down_write (&uts_sem);@@ -1226,7 +1241,7 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!capable (CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN ))return -EPERM ;if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;@@ -1341,6 +1356,8 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource, rlim = tsk->signal->rlim + resource;task_lock (tsk->group_leader);if (new_rlim) { +/* Keep the capable check against init_user_ns until +cgroups can contain all limits */ if (new_rlim->rlim_max > rlim->rlim_max && !capable (CAP_SYS_RESOURCE )) retval = -EPERM ;@@ -1384,19 +1401,22 @@ static int check_prlimit_permission(struct task_struct *task) {const struct cred *cred =current_cred (), *tcred; - tcred =__task_cred (task); -if (current != task && - (cred->uid != tcred->euid || - cred->uid != tcred->suid || - cred->uid != tcred->uid || - cred->gid != tcred->egid || - cred->gid != tcred->sgid || - cred->gid != tcred->gid) && - !capable (CAP_SYS_RESOURCE )) { -return -EPERM ; - } +if (current == task) +return 0 ; -return 0 ; + tcred =__task_cred (task); +if (cred->user->user_ns == tcred->user->user_ns && + (cred->uid == tcred->euid && + cred->uid == tcred->suid && + cred->uid == tcred->uid && + cred->gid == tcred->egid && + cred->gid == tcred->sgid && + cred->gid == tcred->gid)) +return 0 ; +if (ns_capable (tcred->user->user_ns,CAP_SYS_RESOURCE )) +return 0 ; + +return -EPERM ; } SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
Re: [PATCH 6/9] user namespaces: convert all capable checks in kernel/sys.c
In reply to Serge E. HallynQuoted 17 lines · On Thu, 17 Feb 2011 15:03:42 +0000 "Serge E. Hallyn" <[email protected]> wrote:
This allows setuid/setgid in containers. It also fixes some corner cases where kernel logic foregoes capability checks when uids are equivalent. The latter will need to be done throughout the whole kernel.
...
kernel/sys.c +3
-0@@ -118,17 +118,29 @@ EXPORT_SYMBOL(cad_pid);void (*pm_power_off_prepare)(void ); +/* called with rcu_read_lock, creds are safe */ +static inline int set_one_prio_perm (struct task_struct *p) +{
kernel/sys.c EXPORT_SYMBOL(cad_pid); · line 133
Quoted 10 lines
kernel/sys.c +10
-0+const struct cred *cred =current_cred (), *pcred =__task_cred (p); + +if (pcred->user->user_ns == cred->user->user_ns && + (pcred->uid == cred->euid || + pcred->euid == cred->euid)) +return 1 ; +if (ns_capable (pcred->user->user_ns,CAP_SYS_NICE )) +return 1 ; +return 0 ; +}
uninline. Document return value?
Quoted 3 lines
...
Re: [PATCH 6/9] user namespaces: convert all capable checks in kernel/sys.c
In reply to Serge E. Hallynkernel/sys.c SYSCALL_DEFINE2(sethostname, char __user *, name, int, len) · line 1199
Quoted 13 lines · On Thu, 17 Feb 2011 15:03:42 +0000 "Serge E. Hallyn" <[email protected]> wrote:
kernel/sys.c +4
-1@@ -1177,8 +1189,11 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) { +printk (KERN_NOTICE "%s: did not have CAP_SYS_ADMIN\n" , __func__);return -EPERM ; + } +printk (KERN_NOTICE "%s: did have CAP_SYS_ADMIN\n" , __func__);if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;down_write (&uts_sem);
Left over debugging printks?
Re: [PATCH 6/9] user namespaces: convert all capable checks in kernel/sys.c
In reply to Serge E. HallynQuoted 11 lines · Serge E. Hallyn
This allows setuid/setgid in containers. It also fixes some corner cases where kernel logic foregoes capability checks when uids are equivalent. The latter will need to be done throughout the whole kernel.
Changelog:
Jan 11: Usensown_capable()as suggested by Bastian Blank. Jan 11: Fix logic errors in uid checks pointed out by Bastian. Feb 15: allow prlimit to current (was regression in previous version)Signed-off-by Serge E. Hallyn<[email protected]>
kernel/sys.c SYSCALL_DEFINE2(sethostname, char __user *, name, int, len) · line 1196
Quoted 7 lines
kernel/sys.c +4
-1-if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) { +printk (KERN_NOTICE "%s: did not have CAP_SYS_ADMIN\n" , __func__);return -EPERM ; + } +printk (KERN_NOTICE "%s: did have CAP_SYS_ADMIN\n" , __func__);
A couple of printk left here.
Serge E. Hallyn
maintainerPATCH 7/9 add a user namespace owner of ipc ns
In reply to Serge E. HallynChangelog:
Feb 15: Don't set new ipc->user_ns if we didn't create a new ipc_ns.
--- include/linux/ipc_namespace.h | 3 +++ ipc/msgutil.c | 3 +++ ipc/namespace.c | 9 +++++++-- kernel/nsproxy.c | 5 +++++ 4 files changed, 18 insertions(+), 2 deletions(-)
include/linux/ipc_namespace.h +3 -0
@@ -24,6 +24,7 @@ struct ipc_ids {struct idr ipcs_idr; }; +struct user_namespace ;struct ipc_namespace {atomic_t count;struct ipc_ids ids[3 ];
@@ -56,6 +57,8 @@ struct ipc_namespace {unsigned int mq_msg_max;/* initialized to DFLT_MSGMAX */ unsigned int mq_msgsize_max;/* initialized to DFLT_MSGSIZEMAX */ +/* user_ns which owns the ipc ns */ +struct user_namespace *user_ns; };extern struct ipc_namespace init_ipc_ns;
ipc/msgutil.c +3 -0
@@ -20,6 +20,8 @@DEFINE_SPINLOCK (mq_lock); +extern struct user_namespace init_user_ns; +/* * The next 2 defines are here bc this is the only file * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
@@ -32,6 +34,7 @@ struct ipc_namespace init_ipc_ns = { .mq_msg_max = DFLT_MSGMAX, .mq_msgsize_max = DFLT_MSGSIZEMAX, #endif + .user_ns = &init_user_ns, };atomic_t nr_ipc_ns =ATOMIC_INIT (1 );
ipc/namespace.c +7 -2
@@ -11,10 +11,11 @@#include <linux/slab.h> #include <linux/fs.h> #include <linux/mount.h> +#include <linux/user_namespace.h> #include "util.h" -static struct ipc_namespace *create_ipc_ns (void ) +static struct ipc_namespace *create_ipc_ns (struct ipc_namespace *old_ns) {struct ipc_namespace *ns;int err;
@@ -43,6 +44,9 @@ static struct ipc_namespace *create_ipc_ns(void)ipcns_notify (IPCNS_CREATED );register_ipcns_notifier (ns); + ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns); +return ns; }
@@ -50,7 +54,7 @@ struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns) {if (!(flags &CLONE_NEWIPC ))return get_ipc_ns (ns); -return create_ipc_ns (); +return create_ipc_ns (ns); }/*
@@ -105,6 +109,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)* order to have a correct value when recomputing msgmni. */ ipcns_notify (IPCNS_REMOVED ); +put_user_ns (ns->user_ns); }/*
kernel/nsproxy.c +5 -0
@@ -85,6 +85,11 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, err =PTR_ERR (new_nsp->ipc_ns); goto out_ipc; } +if (new_nsp->ipc_ns != tsk->nsproxy->ipc_ns) { +put_user_ns (new_nsp->ipc_ns->user_ns); + new_nsp->ipc_ns->user_ns =task_cred_xxx (tsk, user)->user_ns; +get_user_ns (new_nsp->ipc_ns->user_ns); + } new_nsp->pid_ns =copy_pid_ns (flags,task_active_pid_ns (tsk));if (IS_ERR (new_nsp->pid_ns)) {
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Eric W. Biederman
Acked-byRe: [PATCH 7/9] add a user namespace owner of ipc ns
Quoted 3 lines · Serge E. Hallyn
Changelog:
Feb 15: Don't set new ipc->user_ns if we didn't create a new ipc_ns.
Quoted 111 lines
Signed-off-by Serge E. Hallyn <[email protected]>--- include/linux/ipc_namespace.h | 3 +++ ipc/msgutil.c | 3 +++ ipc/namespace.c | 9 +++++++-- kernel/nsproxy.c | 5 +++++ 4 files changed, 18 insertions(+), 2 deletions(-)include/linux/ipc_namespace.h +3
-0@@ -24,6 +24,7 @@ struct ipc_ids {struct idr ipcs_idr; }; +struct user_namespace ;struct ipc_namespace {atomic_t count;struct ipc_ids ids[3 ];@@ -56,6 +57,8 @@ struct ipc_namespace {unsigned int mq_msg_max;/* initialized to DFLT_MSGMAX */ unsigned int mq_msgsize_max;/* initialized to DFLT_MSGSIZEMAX */ +/* user_ns which owns the ipc ns */ +struct user_namespace *user_ns; };extern struct ipc_namespace init_ipc_ns;ipc/msgutil.c +3
-0@@ -20,6 +20,8 @@DEFINE_SPINLOCK (mq_lock); +extern struct user_namespace init_user_ns; +/* * The next 2 defines are here bc this is the only file * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE @@ -32,6 +34,7 @@ struct ipc_namespace init_ipc_ns = { .mq_msg_max = DFLT_MSGMAX, .mq_msgsize_max = DFLT_MSGSIZEMAX, #endif + .user_ns = &init_user_ns, };atomic_t nr_ipc_ns =ATOMIC_INIT (1 );ipc/namespace.c +7
-2@@ -11,10 +11,11 @@#include <linux/slab.h> #include <linux/fs.h> #include <linux/mount.h> +#include <linux/user_namespace.h> #include "util.h" -static struct ipc_namespace *create_ipc_ns (void ) +static struct ipc_namespace *create_ipc_ns (struct ipc_namespace *old_ns) {struct ipc_namespace *ns;int err;@@ -43,6 +44,9 @@ static struct ipc_namespace *create_ipc_ns(void)ipcns_notify (IPCNS_CREATED );register_ipcns_notifier (ns); + ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns); +return ns; }@@ -50,7 +54,7 @@ struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns) {if (!(flags &CLONE_NEWIPC ))return get_ipc_ns (ns); -return create_ipc_ns (); +return create_ipc_ns (ns); }/* @@ -105,6 +109,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)* order to have a correct value when recomputing msgmni. */ ipcns_notify (IPCNS_REMOVED ); +put_user_ns (ns->user_ns); }/* kernel/nsproxy.c +5
-0@@ -85,6 +85,11 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, err =PTR_ERR (new_nsp->ipc_ns); goto out_ipc; } +if (new_nsp->ipc_ns != tsk->nsproxy->ipc_ns) { +put_user_ns (new_nsp->ipc_ns->user_ns); + new_nsp->ipc_ns->user_ns =task_cred_xxx (tsk, user)->user_ns; +get_user_ns (new_nsp->ipc_ns->user_ns); + } new_nsp->pid_ns =copy_pid_ns (flags,task_active_pid_ns (tsk));if (IS_ERR (new_nsp->pid_ns)) {
Re: [PATCH 7/9] add a user namespace owner of ipc ns
In reply to a message not in the archiveinclude/linux/ipc_namespace.h struct ipc_ids { · line 27
Quoted 10 lines · On Thu, 17 Feb 2011 15:03:49 +0000 "Serge E. Hallyn"…
...
include/linux/ipc_namespace.h +1
-0@@ -24,6 +24,7 @@ struct ipc_ids {struct idr ipcs_idr; }; +struct user_namespace ;
Move to top of file.
Quoted 17 lines
include/linux/ipc_namespace.h +2
-0struct ipc_namespace {atomic_t count;struct ipc_ids ids[3 ];@@ -56,6 +57,8 @@ struct ipc_namespace {unsigned int mq_msg_max;/* initialized to DFLT_MSGMAX */ unsigned int mq_msgsize_max;/* initialized to DFLT_MSGSIZEMAX */ +/* user_ns which owns the ipc ns */ +struct user_namespace *user_ns; };extern struct ipc_namespace init_ipc_ns;ipc/msgutil.c +0
-0
ipc/msgutil.c line 23
Quoted 4 lines
ipc/msgutil.c +1
-0@@ -20,6 +20,8 @@DEFINE_SPINLOCK (mq_lock); +extern struct user_namespace init_user_ns;
Should be declared in .h, not in .c.
Quoted 6 lines
ipc/msgutil.c +0
-0/* * The next 2 defines are here bc this is the only file * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
Daniel Lezcano
Acked-byRe: [PATCH 7/9] add a user namespace owner of ipc ns
In reply to a message not in the archiveQuoted 6 lines · Serge E. Hallyn
Changelog:
Feb 15: Don't set new ipc->user_ns if we didn't create a new ipc_ns.Signed-off-by Serge E. Hallyn<[email protected]>---
[ ... ]
ipc/namespace.c static struct ipc_namespace *create_ipc_ns(void) · line 48
Quoted 2 lines
ipc/namespace.c +2
-0+ ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns);
A mindless change.
ns->user_ns = get_user_ns(old_ns->user_ns);
Re: [PATCH 7/9] add a user namespace owner of ipc ns
In reply to Serge E. HallynQuoted 3 lines · Serge E. Hallyn
Changelog:
Feb 15: Don't set new ipc->user_ns if we didn't create a new ipc_ns.
Quoted 111 lines
Signed-off-by Serge E. Hallyn <[email protected]>--- include/linux/ipc_namespace.h | 3 +++ ipc/msgutil.c | 3 +++ ipc/namespace.c | 9 +++++++-- kernel/nsproxy.c | 5 +++++ 4 files changed, 18 insertions(+), 2 deletions(-)include/linux/ipc_namespace.h +3
-0@@ -24,6 +24,7 @@ struct ipc_ids {struct idr ipcs_idr; }; +struct user_namespace ;struct ipc_namespace {atomic_t count;struct ipc_ids ids[3 ];@@ -56,6 +57,8 @@ struct ipc_namespace {unsigned int mq_msg_max;/* initialized to DFLT_MSGMAX */ unsigned int mq_msgsize_max;/* initialized to DFLT_MSGSIZEMAX */ +/* user_ns which owns the ipc ns */ +struct user_namespace *user_ns; };extern struct ipc_namespace init_ipc_ns;ipc/msgutil.c +3
-0@@ -20,6 +20,8 @@DEFINE_SPINLOCK (mq_lock); +extern struct user_namespace init_user_ns; +/* * The next 2 defines are here bc this is the only file * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE @@ -32,6 +34,7 @@ struct ipc_namespace init_ipc_ns = { .mq_msg_max = DFLT_MSGMAX, .mq_msgsize_max = DFLT_MSGSIZEMAX, #endif + .user_ns = &init_user_ns, };atomic_t nr_ipc_ns =ATOMIC_INIT (1 );ipc/namespace.c +7
-2@@ -11,10 +11,11 @@#include <linux/slab.h> #include <linux/fs.h> #include <linux/mount.h> +#include <linux/user_namespace.h> #include "util.h" -static struct ipc_namespace *create_ipc_ns (void ) +static struct ipc_namespace *create_ipc_ns (struct ipc_namespace *old_ns) {struct ipc_namespace *ns;int err;@@ -43,6 +44,9 @@ static struct ipc_namespace *create_ipc_ns(void)ipcns_notify (IPCNS_CREATED );register_ipcns_notifier (ns); + ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns); +return ns; }@@ -50,7 +54,7 @@ struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns) {if (!(flags &CLONE_NEWIPC ))return get_ipc_ns (ns); -return create_ipc_ns (); +return create_ipc_ns (ns); }/* @@ -105,6 +109,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)* order to have a correct value when recomputing msgmni. */ ipcns_notify (IPCNS_REMOVED ); +put_user_ns (ns->user_ns); }/* kernel/nsproxy.c +5
-0@@ -85,6 +85,11 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, err =PTR_ERR (new_nsp->ipc_ns); goto out_ipc; } +if (new_nsp->ipc_ns != tsk->nsproxy->ipc_ns) { +put_user_ns (new_nsp->ipc_ns->user_ns); + new_nsp->ipc_ns->user_ns =task_cred_xxx (tsk, user)->user_ns; +get_user_ns (new_nsp->ipc_ns->user_ns); + } new_nsp->pid_ns =copy_pid_ns (flags,task_active_pid_ns (tsk));if (IS_ERR (new_nsp->pid_ns)) {
Re: [PATCH 7/9] add a user namespace owner of ipc ns
In reply to Serge E. Hallyninclude/linux/ipc_namespace.h struct ipc_ids { · line 27
Quoted 10 lines · On Thu, 17 Feb 2011 15:03:49 +0000 "Serge E. Hallyn" <[email protected]> wrote:
...
include/linux/ipc_namespace.h +1
-0@@ -24,6 +24,7 @@ struct ipc_ids {struct idr ipcs_idr; }; +struct user_namespace ;
Move to top of file.
Quoted 17 lines
include/linux/ipc_namespace.h +2
-0struct ipc_namespace {atomic_t count;struct ipc_ids ids[3 ];@@ -56,6 +57,8 @@ struct ipc_namespace {unsigned int mq_msg_max;/* initialized to DFLT_MSGMAX */ unsigned int mq_msgsize_max;/* initialized to DFLT_MSGSIZEMAX */ +/* user_ns which owns the ipc ns */ +struct user_namespace *user_ns; };extern struct ipc_namespace init_ipc_ns;ipc/msgutil.c +0
-0
ipc/msgutil.c line 23
Quoted 4 lines
ipc/msgutil.c +1
-0@@ -20,6 +20,8 @@DEFINE_SPINLOCK (mq_lock); +extern struct user_namespace init_user_ns;
Should be declared in .h, not in .c.
Quoted 6 lines
ipc/msgutil.c +0
-0/* * The next 2 defines are here bc this is the only file * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
Re: [PATCH 7/9] add a user namespace owner of ipc ns
In reply to Serge E. HallynQuoted 6 lines · Serge E. Hallyn
Changelog:
Feb 15: Don't set new ipc->user_ns if we didn't create a new ipc_ns.Signed-off-by Serge E. Hallyn<[email protected]>---
[ ... ]
ipc/namespace.c static struct ipc_namespace *create_ipc_ns(void) · line 48
Quoted 2 lines
ipc/namespace.c +2
-0+ ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns);
A mindless change.
ns->user_ns = get_user_ns(old_ns->user_ns);
Serge E. Hallyn
maintainerPATCH 8/9 user namespaces: convert several capable() calls
In reply to Serge E. HallynCAP_IPC_OWNER and CAP_IPC_LOCK can be checked against current_user_ns(), because the resource comes from current's own ipc namespace.
setuid/setgid are to uids in own namespace, so again checks can be against current_user_ns().
Changelog:
Jan 11: Use task_ns_capable() in place of sched_capable(). Jan 11: Use nsown_capable() as suggested by Bastian Blank. Jan 11: Clarify (hopefully) some logic in futex and sched.c Feb 15: use ns_capable for ipc, not nsown_capable
--- ipc/shm.c | 2 +- ipc/util.c | 5 +++-- kernel/futex.c | 11 ++++++++++- kernel/futex_compat.c | 11 ++++++++++- kernel/groups.c | 2 +- kernel/sched.c | 9 ++++++--- kernel/uid16.c | 2 +- 7 files changed, 32 insertions(+), 10 deletions(-)
ipc/shm.c +1 -1
@@ -773,7 +773,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)audit_ipc_obj (&(shp->shm_perm)); -if (!capable (CAP_IPC_LOCK )) { +if (!ns_capable (ns->user_ns,CAP_IPC_LOCK )) {uid_t euid =current_euid (); err = -EPERM ; if (euid != shp->shm_perm.uid &&
ipc/util.c +3 -2
@@ -627,7 +627,7 @@ int ipcperms (struct kern_ipc_perm *ipcp, short flag) granted_mode >>=3 ;/* is there some bit set in requested_mode but not in granted_mode? */ if ((requested_mode & ~granted_mode &0007 ) && - !capable (CAP_IPC_OWNER )) + !ns_capable (current->nsproxy->ipc_ns->user_ns,CAP_IPC_OWNER ))return -1 ;return security_ipc_permission (ipcp, flag);
@@ -800,7 +800,8 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, euid =current_euid ();if (euid == ipcp->cuid || - euid == ipcp->uid ||capable (CAP_SYS_ADMIN )) + euid == ipcp->uid || +ns_capable (current->nsproxy->ipc_ns->user_ns,CAP_SYS_ADMIN ))return ipcp; err = -EPERM ;
kernel/futex.c +10 -1
@@ -2421,10 +2421,19 @@ SYSCALL_DEFINE3(get_robust_list, int, pid, goto err_unlock; ret = -EPERM ; pcred =__task_cred (p); +/* If victim is in different user_ns, then uids are not +comparable, so we must have CAP_SYS_PTRACE */ +if (cred->user->user_ns != pcred->user->user_ns) { +if (!ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) + goto err_unlock; + goto ok; + } +/* If victim is in same user_ns, then uids are comparable */ if (cred->euid != pcred->euid && cred->euid != pcred->uid && - !capable (CAP_SYS_PTRACE )) + !ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) goto err_unlock; +ok: head = p->robust_list;rcu_read_unlock (); }
kernel/futex_compat.c +10 -1
@@ -153,10 +153,19 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, goto err_unlock; ret = -EPERM ; pcred =__task_cred (p); +/* If victim is in different user_ns, then uids are not +comparable, so we must have CAP_SYS_PTRACE */ +if (cred->user->user_ns != pcred->user->user_ns) { +if (!ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) + goto err_unlock; + goto ok; + } +/* If victim is in same user_ns, then uids are comparable */ if (cred->euid != pcred->euid && cred->euid != pcred->uid && - !capable (CAP_SYS_PTRACE )) + !ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) goto err_unlock; +ok: head = p->compat_robust_list;rcu_read_unlock (); }
kernel/groups.c +1 -1
@@ -233,7 +233,7 @@ SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)struct group_info *group_info;int retval; -if (!capable (CAP_SETGID )) +if (!nsown_capable (CAP_SETGID ))return -EPERM ;if ((unsigned )gidsetsize >NGROUPS_MAX )return -EINVAL ;
kernel/sched.c +6 -3
@@ -4761,8 +4761,11 @@ static bool check_same_owner(struct task_struct *p)rcu_read_lock (); pcred =__task_cred (p); - match = (cred->euid == pcred->euid || - cred->euid == pcred->uid); +if (cred->user->user_ns == pcred->user->user_ns) + match = (cred->euid == pcred->euid || + cred->euid == pcred->uid); +else + match = false;rcu_read_unlock ();return match; }
@@ -5088,7 +5091,7 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask) goto out_free_cpus_allowed; } retval = -EPERM ; -if (!check_same_owner (p) && !capable (CAP_SYS_NICE )) +if (!check_same_owner (p) && !task_ns_capable (p,CAP_SYS_NICE )) goto out_unlock; retval =security_task_setscheduler (p);
kernel/uid16.c +1 -1
@@ -189,7 +189,7 @@ SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist)struct group_info *group_info;int retval; -if (!capable (CAP_SETGID )) +if (!nsown_capable (CAP_SETGID ))return -EPERM ;if ((unsigned )gidsetsize >NGROUPS_MAX )return -EINVAL ;
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Eric W. Biederman
Acked-byRe: [PATCH 8/9] user namespaces: convert several capable() calls
Quoted 5 lines · Serge E. Hallyn
CAP_IPC_OWNERandCAP_IPC_LOCKcan be checked againstcurrent_user_ns(), because the resource comes from current's own ipc namespace.setuid/setgid are to uids in own namespace, so again checks can be against
current_user_ns().
Some nits below. But this generally looks good if a little bit all over the map for a single patch.
Quoted 35 lines
Changelog:
Jan 11: Usetask_ns_capable()in place ofsched_capable(). Jan 11: Usensown_capable()as suggested by Bastian Blank. Jan 11: Clarify (hopefully) some logic in futex andsched.cFeb 15: usens_capablefor ipc, notnsown_capableSigned-off-by Serge E. Hallyn <[email protected]>--- ipc/shm.c | 2 +- ipc/util.c | 5 +++-- kernel/futex.c | 11 ++++++++++- kernel/futex_compat.c | 11 ++++++++++- kernel/groups.c | 2 +- kernel/sched.c | 9 ++++++--- kernel/uid16.c | 2 +- 7 files changed, 32 insertions(+), 10 deletions(-)ipc/shm.c +1
-1@@ -773,7 +773,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)audit_ipc_obj (&(shp->shm_perm)); -if (!capable (CAP_IPC_LOCK )) { +if (!ns_capable (ns->user_ns,CAP_IPC_LOCK )) {uid_t euid =current_euid (); err = -EPERM ; if (euid != shp->shm_perm.uid &&ipc/util.c +0
-0
ipc/util.c int ipcperms (struct kern_ipc_perm *ipcp, short flag) · line 631
Quoted 6 lines
ipc/util.c +1
-1@@ -627,7 +627,7 @@ int ipcperms (struct kern_ipc_perm *ipcp, short flag) granted_mode >>=3 ;/* is there some bit set in requested_mode but not in granted_mode? */ if ((requested_mode & ~granted_mode &0007 ) && - !capable (CAP_IPC_OWNER )) + !ns_capable (current->nsproxy->ipc_ns->user_ns,CAP_IPC_OWNER ))return -1 ;
Serge can we please modify the code to pass the ns down from ipcget_public to ipcperms. It is passed in and dropping the value and going back to current to get it just feels wrong.
Strictly speaking this code is correct but it requires an audit of all of the callers to know that, which is unfortunate.
ipc/util.c struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, · line 805
Quoted 9 lines
ipc/util.c +2
-1return security_ipc_permission (ipcp, flag);@@ -800,7 +800,8 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, euid =current_euid ();if (euid == ipcp->cuid || - euid == ipcp->uid ||capable (CAP_SYS_ADMIN )) + euid == ipcp->uid || +ns_capable (current->nsproxy->ipc_ns->user_ns,CAP_SYS_ADMIN ))return ipcp;
Like the other ipc call can we please pass the ipc_ns into ipcctl_pre_down.
The code as constructed appears correct but because we are passing the namespace into the caller of this function always using current to get the ipc_ns seems confusing and unnecessary.
Quoted 104 lines
ipc/util.c +0
-0err = -EPERM ;kernel/futex.c +10
-1@@ -2421,10 +2421,19 @@ SYSCALL_DEFINE3(get_robust_list, int, pid, goto err_unlock; ret = -EPERM ; pcred =__task_cred (p); +/* If victim is in different user_ns, then uids are not +comparable, so we must have CAP_SYS_PTRACE */ +if (cred->user->user_ns != pcred->user->user_ns) { +if (!ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) + goto err_unlock; + goto ok; + } +/* If victim is in same user_ns, then uids are comparable */ if (cred->euid != pcred->euid && cred->euid != pcred->uid && - !capable (CAP_SYS_PTRACE )) + !ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) goto err_unlock; +ok: head = p->robust_list;rcu_read_unlock (); }kernel/futex_compat.c +10
-1@@ -153,10 +153,19 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, goto err_unlock; ret = -EPERM ; pcred =__task_cred (p); +/* If victim is in different user_ns, then uids are not +comparable, so we must have CAP_SYS_PTRACE */ +if (cred->user->user_ns != pcred->user->user_ns) { +if (!ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) + goto err_unlock; + goto ok; + } +/* If victim is in same user_ns, then uids are comparable */ if (cred->euid != pcred->euid && cred->euid != pcred->uid && - !capable (CAP_SYS_PTRACE )) + !ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) goto err_unlock; +ok: head = p->compat_robust_list;rcu_read_unlock (); }kernel/groups.c +1
-1@@ -233,7 +233,7 @@ SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)struct group_info *group_info;int retval; -if (!capable (CAP_SETGID )) +if (!nsown_capable (CAP_SETGID ))return -EPERM ;if ((unsigned )gidsetsize >NGROUPS_MAX )return -EINVAL ;kernel/sched.c +6
-3@@ -4761,8 +4761,11 @@ static bool check_same_owner(struct task_struct *p)rcu_read_lock (); pcred =__task_cred (p); - match = (cred->euid == pcred->euid || - cred->euid == pcred->uid); +if (cred->user->user_ns == pcred->user->user_ns) + match = (cred->euid == pcred->euid || + cred->euid == pcred->uid); +else + match = false;rcu_read_unlock ();return match; }@@ -5088,7 +5091,7 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask) goto out_free_cpus_allowed; } retval = -EPERM ; -if (!check_same_owner (p) && !capable (CAP_SYS_NICE )) +if (!check_same_owner (p) && !task_ns_capable (p,CAP_SYS_NICE )) goto out_unlock; retval =security_task_setscheduler (p);kernel/uid16.c +1
-1@@ -189,7 +189,7 @@ SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist)struct group_info *group_info;int retval; -if (!capable (CAP_SETGID )) +if (!nsown_capable (CAP_SETGID ))return -EPERM ;if ((unsigned )gidsetsize >NGROUPS_MAX )return -EINVAL ;
Daniel Lezcano
Acked-byRe: [PATCH 8/9] user namespaces: convert several capable() calls
In reply to a message not in the archiveQuoted 6 lines · Serge E. Hallyn
CAP_IPC_OWNERandCAP_IPC_LOCKcan be checked againstcurrent_user_ns(), because the resource comes from current's own ipc namespace.setuid/setgid are to uids in own namespace, so again checks can be against
current_user_ns().
Quoted 8 lines
Changelog:
Jan 11: Usetask_ns_capable()in place ofsched_capable(). Jan 11: Usensown_capable()as suggested by Bastian Blank. Jan 11: Clarify (hopefully) some logic in futex andsched.cFeb 15: usens_capablefor ipc, notnsown_capableSigned-off-by Serge E. Hallyn<[email protected]>---
Re: [PATCH 8/9] user namespaces: convert several capable() calls
In reply to Serge E. HallynQuoted 5 lines · Serge E. Hallyn
CAP_IPC_OWNERandCAP_IPC_LOCKcan be checked againstcurrent_user_ns(), because the resource comes from current's own ipc namespace.setuid/setgid are to uids in own namespace, so again checks can be against
current_user_ns().
Some nits below. But this generally looks good if a little bit all over the map for a single patch.
Quoted 35 lines
Changelog:
Jan 11: Usetask_ns_capable()in place ofsched_capable(). Jan 11: Usensown_capable()as suggested by Bastian Blank. Jan 11: Clarify (hopefully) some logic in futex andsched.cFeb 15: usens_capablefor ipc, notnsown_capableSigned-off-by Serge E. Hallyn <[email protected]>--- ipc/shm.c | 2 +- ipc/util.c | 5 +++-- kernel/futex.c | 11 ++++++++++- kernel/futex_compat.c | 11 ++++++++++- kernel/groups.c | 2 +- kernel/sched.c | 9 ++++++--- kernel/uid16.c | 2 +- 7 files changed, 32 insertions(+), 10 deletions(-)ipc/shm.c +1
-1@@ -773,7 +773,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)audit_ipc_obj (&(shp->shm_perm)); -if (!capable (CAP_IPC_LOCK )) { +if (!ns_capable (ns->user_ns,CAP_IPC_LOCK )) {uid_t euid =current_euid (); err = -EPERM ; if (euid != shp->shm_perm.uid &&ipc/util.c +0
-0
ipc/util.c int ipcperms (struct kern_ipc_perm *ipcp, short flag) · line 631
Quoted 6 lines
ipc/util.c +1
-1@@ -627,7 +627,7 @@ int ipcperms (struct kern_ipc_perm *ipcp, short flag) granted_mode >>=3 ;/* is there some bit set in requested_mode but not in granted_mode? */ if ((requested_mode & ~granted_mode &0007 ) && - !capable (CAP_IPC_OWNER )) + !ns_capable (current->nsproxy->ipc_ns->user_ns,CAP_IPC_OWNER ))return -1 ;
Serge can we please modify the code to pass the ns down from ipcget_public to ipcperms. It is passed in and dropping the value and going back to current to get it just feels wrong.
Strictly speaking this code is correct but it requires an audit of all of the callers to know that, which is unfortunate.
ipc/util.c struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, · line 805
Quoted 9 lines
ipc/util.c +2
-1return security_ipc_permission (ipcp, flag);@@ -800,7 +800,8 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, euid =current_euid ();if (euid == ipcp->cuid || - euid == ipcp->uid ||capable (CAP_SYS_ADMIN )) + euid == ipcp->uid || +ns_capable (current->nsproxy->ipc_ns->user_ns,CAP_SYS_ADMIN ))return ipcp;
Like the other ipc call can we please pass the ipc_ns into ipcctl_pre_down.
The code as constructed appears correct but because we are passing the namespace into the caller of this function always using current to get the ipc_ns seems confusing and unnecessary.
Quoted 104 lines
ipc/util.c +0
-0err = -EPERM ;kernel/futex.c +10
-1@@ -2421,10 +2421,19 @@ SYSCALL_DEFINE3(get_robust_list, int, pid, goto err_unlock; ret = -EPERM ; pcred =__task_cred (p); +/* If victim is in different user_ns, then uids are not +comparable, so we must have CAP_SYS_PTRACE */ +if (cred->user->user_ns != pcred->user->user_ns) { +if (!ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) + goto err_unlock; + goto ok; + } +/* If victim is in same user_ns, then uids are comparable */ if (cred->euid != pcred->euid && cred->euid != pcred->uid && - !capable (CAP_SYS_PTRACE )) + !ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) goto err_unlock; +ok: head = p->robust_list;rcu_read_unlock (); }kernel/futex_compat.c +10
-1@@ -153,10 +153,19 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, goto err_unlock; ret = -EPERM ; pcred =__task_cred (p); +/* If victim is in different user_ns, then uids are not +comparable, so we must have CAP_SYS_PTRACE */ +if (cred->user->user_ns != pcred->user->user_ns) { +if (!ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) + goto err_unlock; + goto ok; + } +/* If victim is in same user_ns, then uids are comparable */ if (cred->euid != pcred->euid && cred->euid != pcred->uid && - !capable (CAP_SYS_PTRACE )) + !ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) goto err_unlock; +ok: head = p->compat_robust_list;rcu_read_unlock (); }kernel/groups.c +1
-1@@ -233,7 +233,7 @@ SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)struct group_info *group_info;int retval; -if (!capable (CAP_SETGID )) +if (!nsown_capable (CAP_SETGID ))return -EPERM ;if ((unsigned )gidsetsize >NGROUPS_MAX )return -EINVAL ;kernel/sched.c +6
-3@@ -4761,8 +4761,11 @@ static bool check_same_owner(struct task_struct *p)rcu_read_lock (); pcred =__task_cred (p); - match = (cred->euid == pcred->euid || - cred->euid == pcred->uid); +if (cred->user->user_ns == pcred->user->user_ns) + match = (cred->euid == pcred->euid || + cred->euid == pcred->uid); +else + match = false;rcu_read_unlock ();return match; }@@ -5088,7 +5091,7 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask) goto out_free_cpus_allowed; } retval = -EPERM ; -if (!check_same_owner (p) && !capable (CAP_SYS_NICE )) +if (!check_same_owner (p) && !task_ns_capable (p,CAP_SYS_NICE )) goto out_unlock; retval =security_task_setscheduler (p);kernel/uid16.c +1
-1@@ -189,7 +189,7 @@ SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist)struct group_info *group_info;int retval; -if (!capable (CAP_SETGID )) +if (!nsown_capable (CAP_SETGID ))return -EPERM ;if ((unsigned )gidsetsize >NGROUPS_MAX )return -EINVAL ;
Re: [PATCH 8/9] user namespaces: convert several capable() calls
In reply to Serge E. HallynQuoted 6 lines · Serge E. Hallyn
CAP_IPC_OWNERandCAP_IPC_LOCKcan be checked againstcurrent_user_ns(), because the resource comes from current's own ipc namespace.setuid/setgid are to uids in own namespace, so again checks can be against
current_user_ns().
Quoted 8 lines
Changelog:
Jan 11: Usetask_ns_capable()in place ofsched_capable(). Jan 11: Usensown_capable()as suggested by Bastian Blank. Jan 11: Clarify (hopefully) some logic in futex andsched.cFeb 15: usens_capablefor ipc, notnsown_capableSigned-off-by Serge E. Hallyn<[email protected]>---
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/9 Add a user_namespace as creator/owner of uts_namespace
copy_process() handles CLONE_NEWUSER before the rest of the namespaces. So in the case of clone(CLONE_NEWUSER|CLONE_NEWUTS) the new uts namespace will have the new user namespace as its owner. That is what we want, since we want root in that new userns to be able to have privilege over it.
Changelog:
Feb 15: don't set uts_ns->user_ns if we didn't create a new uts_ns.
--- include/linux/utsname.h | 3 +++ init/version.c | 2 ++ kernel/nsproxy.c | 5 +++++ kernel/user.c | 8 ++++++-- kernel/utsname.c | 4 ++++ 5 files changed, 20 insertions(+), 2 deletions(-)
include/linux/utsname.h +3 -0
@@ -37,9 +37,12 @@ struct new_utsname {#include <linux/nsproxy.h> #include <linux/err.h> +struct user_namespace ; +struct uts_namespace {struct kref kref;struct new_utsname name; +struct user_namespace *user_ns; };extern struct uts_namespace init_uts_ns;
init/version.c +2 -0
@@ -21,6 +21,7 @@ extern int version_string(LINUX_VERSION_CODE);int version_string (LINUX_VERSION_CODE );#endif +extern struct user_namespace init_user_ns;struct uts_namespace init_uts_ns = { .kref = { .refcount =ATOMIC_INIT (2 ),
@@ -33,6 +34,7 @@ struct uts_namespace init_uts_ns = { .machine = UTS_MACHINE, .domainname = UTS_DOMAINNAME, }, + .user_ns = &init_user_ns, };EXPORT_SYMBOL_GPL (init_uts_ns);
kernel/nsproxy.c +5 -0
@@ -74,6 +74,11 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, err =PTR_ERR (new_nsp->uts_ns); goto out_uts; } +if (new_nsp->uts_ns != tsk->nsproxy->uts_ns) { +put_user_ns (new_nsp->uts_ns->user_ns); + new_nsp->uts_ns->user_ns =task_cred_xxx (tsk, user)->user_ns; +get_user_ns (new_nsp->uts_ns->user_ns); + } new_nsp->ipc_ns =copy_ipcs (flags, tsk->nsproxy->ipc_ns);if (IS_ERR (new_nsp->ipc_ns)) {
kernel/user.c +6 -2
@@ -17,9 +17,13 @@#include <linux/module.h> #include <linux/user_namespace.h> +/* +* userns count is 1 for root user, 1 for init_uts_ns, +* and 1 for... ? +*/ struct user_namespace init_user_ns = { .kref = { - .refcount =ATOMIC_INIT (2 ), + .refcount =ATOMIC_INIT (3 ), }, .creator = &root_user, };
@@ -47,7 +51,7 @@ static struct kmem_cache *uid_cachep;*/ static DEFINE_SPINLOCK (uidhash_lock); -/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->creator */ +/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->user_ns */ struct user_struct root_user = { .__count =ATOMIC_INIT (2 ), .processes =ATOMIC_INIT (1 ),
kernel/utsname.c +4 -0
@@ -14,6 +14,7 @@#include <linux/utsname.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/user_namespace.h> static struct uts_namespace *create_uts_ns (void ) {
@@ -40,6 +41,8 @@ static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns)down_read (&uts_sem);memcpy (&ns->name, &old_ns->name,sizeof (ns->name)); + ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns);up_read (&uts_sem);return ns; }
@@ -71,5 +74,6 @@ void free_uts_ns(struct kref *kref)struct uts_namespace *ns; ns = container_of(kref, struct uts_namespace, kref); +put_user_ns (ns->user_ns);kfree (ns); }
Signature · 1 line
1.7.0.4
PATCH 2/9 security: Make capabilities relative to the user namespace.
In reply to a message not in the archive- Introduce ns_capable to test for a capability in a non-default user namespace.
- Teach cap_capable to handle capabilities in a non-default user namespace.
The motivation is to get to the unprivileged creation of new namespaces. It looks like this gets us 90% of the way there, with only potential uid confusion issues left.
I still need to handle getting all caps after creation but otherwise I think I have a good starter patch that achieves all of your goals.
Changelog:
11/05/2010: [serge] add apparmor
12/14/2010: [serge] fix capabilities to created user namespaces Without this, if user serge creates a user_ns, he won't have capabilities to the user_ns he created. THis is because we were first checking whether his effective caps had the caps
he needed and returning -EPERM if not, and THEN checking whether he was the creator. Reverse those checks.
12/16/2010: [serge] security_real_capable needs ns argument in !security case 01/11/2011: [serge] add task_ns_capable helper
01/11/2011: [serge] add nsown_capable() helper per Bastian Blank suggestion 02/16/2011: [serge] fix a logic bug: the root user is always creator of
init_user_ns, but should not always have capabilities to it! Fix the check in cap_capable().
--- include/linux/capability.h | 10 ++++++++-- include/linux/security.h | 25 ++++++++++++++----------- kernel/capability.c | 32 ++++++++++++++++++++++++++++++-- security/apparmor/lsm.c | 5 +++-- security/commoncap.c | 40 +++++++++++++++++++++++++++++++++------- security/security.c | 16 ++++++++++------ security/selinux/hooks.c | 14 +++++++++----- 7 files changed, 107 insertions(+), 35 deletions(-)
include/linux/capability.h +8 -2
@@ -544,7 +544,7 @@ extern const kernel_cap_t __cap_init_eff_set;* * Note that this does not set PF_SUPERPRIV on the task. */ -#define has_capability (t, cap) (security_real_capable((t), (cap)) == 0) +#define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited)
@@ -558,9 +558,15 @@ extern const kernel_cap_t __cap_init_eff_set;* Note that this does not set PF_SUPERPRIV on the task. */ #define has_capability_noaudit (t, cap) \ - (security_real_capable_noaudit((t), (cap)) == 0) + (security_real_capable_noaudit((t), &init_user_ns, (cap)) == 0) +struct user_namespace ; +extern struct user_namespace init_user_ns;extern int capable (int cap); +extern int ns_capable (struct user_namespace *ns,int cap); +extern int task_ns_capable (struct task_struct *t,int cap); + +#define nsown_capable (cap) (ns_capable(current_user_ns(), (cap)))/* audit system wants to get cap info from files as well */ struct dentry ;
include/linux/security.h +14 -11
@@ -46,13 +46,14 @@struct ctl_table ;struct audit_krule ; +struct user_namespace ;/* * These functions are in security/capability.c and are used * as the default capabilities functions */ extern int cap_capable (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);extern int cap_settime (struct timespec *ts,struct timezone *tz);extern int cap_ptrace_access_check (struct task_struct *child,unsigned int mode);extern int cap_ptrace_traceme (struct task_struct *parent);
@@ -1254,6 +1255,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)* credentials. * @tsk contains the task_struct for the process. * @cred contains the credentials to use. +* @ns contains the user namespace we want the capability in * @cap contains the capability <include/linux/capability.h>. * @audit: Whether to write an audit message or not * Return 0 if the capability is granted for @tsk.
@@ -1382,7 +1384,7 @@ struct security_operations {const kernel_cap_t *inheritable, const kernel_cap_t *permitted);int (*capable) (struct task_struct *tsk,const struct cred *cred, -int cap,int audit); +struct user_namespace *ns,int cap,int audit);int (*sysctl) (struct ctl_table *table,int op);int (*quotactl) (int cmds,int type,int id,struct super_block *sb);int (*quota_on) (struct dentry *dentry);
@@ -1662,9 +1664,9 @@ int security_capset(struct cred *new, const struct cred *old,const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); -int security_capable (const struct cred *cred,int cap); -int security_real_capable (struct task_struct *tsk,int cap); -int security_real_capable_noaudit (struct task_struct *tsk,int cap); +int security_capable (struct user_namespace *ns,const struct cred *cred,int cap); +int security_real_capable (struct task_struct *tsk,struct user_namespace *ns,int cap); +int security_real_capable_noaudit (struct task_struct *tsk,struct user_namespace *ns,int cap);int security_sysctl (struct ctl_table *table,int op);int security_quotactl (int cmds,int type,int id,struct super_block *sb);int security_quota_on (struct dentry *dentry);
@@ -1856,28 +1858,29 @@ static inline int security_capset(struct cred *new,return cap_capset (new, old, effective, inheritable, permitted); } -static inline int security_capable (const struct cred *cred,int cap) +static inline int security_capable (struct user_namespace *ns, +const struct cred *cred,int cap) { -return cap_capable (current, cred, cap,SECURITY_CAP_AUDIT ); +return cap_capable (current, cred, ns, cap,SECURITY_CAP_AUDIT ); } -static inline int security_real_capable (struct task_struct *tsk,int cap) +static inline int security_real_capable (struct task_struct *tsk,struct user_namespace *ns,int cap) {int ret;rcu_read_lock (); - ret =cap_capable (tsk,__task_cred (tsk), cap,SECURITY_CAP_AUDIT ); + ret =cap_capable (tsk,__task_cred (tsk), ns, cap,SECURITY_CAP_AUDIT );rcu_read_unlock ();return ret; }static inline -int security_real_capable_noaudit (struct task_struct *tsk,int cap) +int security_real_capable_noaudit (struct task_struct *tsk,struct user_namespace *ns,int cap) {int ret;rcu_read_lock (); - ret =cap_capable (tsk,__task_cred (tsk), cap, + ret =cap_capable (tsk,__task_cred (tsk), ns, cap,SECURITY_CAP_NOAUDIT );rcu_read_unlock ();return ret;
kernel/capability.c +30 -2
@@ -14,6 +14,7 @@#include <linux/security.h> #include <linux/syscalls.h> #include <linux/pid_namespace.h> +#include <linux/user_namespace.h> #include <asm/uaccess.h> /*
@@ -301,15 +302,42 @@ error:*/ int capable (int cap) { +return ns_capable (&init_user_ns, cap); +} +EXPORT_SYMBOL (capable); + +/** +* ns_capable - Determine if the current task has a superior capability in effect +* @ns: The usernamespace we want the capability in +* @cap: The capability to be tested for +* +* Return true if the current task has the given superior capability currently +* available for use, false if not. +* +* This sets PF_SUPERPRIV on the task if the capability is available on the +* assumption that it's about to be used. +*/ +int ns_capable (struct user_namespace *ns,int cap) +{if (unlikely (!cap_valid (cap))) {printk (KERN_CRIT "capable() called with invalid cap=%u\n" , cap);BUG (); } -if (security_capable (current_cred (), cap) ==0 ) { +if (security_capable (ns,current_cred (), cap) ==0 ) { current->flags |=PF_SUPERPRIV ;return 1 ; }return 0 ; } -EXPORT_SYMBOL (capable); +EXPORT_SYMBOL (ns_capable); + +/* +* does current have capability 'cap' to the user namespace of task +* 't'. Return true if it does, false otherwise. +*/ +int task_ns_capable (struct task_struct *t,int cap) +{ +return ns_capable (task_cred_xxx (t, user)->user_ns, cap); +} +EXPORT_SYMBOL (task_ns_capable);
security/apparmor/lsm.c +3 -2
@@ -22,6 +22,7 @@#include <linux/ctype.h> #include <linux/sysctl.h> #include <linux/audit.h> +#include <linux/user_namespace.h> #include <net/sock.h> #include "include/apparmor.h"
@@ -136,11 +137,11 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, }static int apparmor_capable (struct task_struct *task,const struct cred *cred, -int cap,int audit) +struct user_namespace *ns,int cap,int audit) {struct aa_profile *profile;/* cap_capable returns 0 on success, else -EPERM */ -int error =cap_capable (task, cred, cap, audit); +int error =cap_capable (task, cred, ns, cap, audit);if (!error) { profile =aa_cred_profile (cred);if (!unconfined (profile))
security/commoncap.c +33 -7
@@ -27,6 +27,7 @@#include <linux/sched.h> #include <linux/prctl.h> #include <linux/securebits.h> +#include <linux/user_namespace.h> /* * If a non-root user executes a setuid-root binary in
@@ -68,6 +69,7 @@ EXPORT_SYMBOL(cap_netlink_recv);* cap_capable - Determine whether a task has a particular effective capability * @tsk: The task to query * @cred: The credentials to use +* @ns: The user namespace in which we need the capability * @cap: The capability to check for * @audit: Whether to write an audit message or not *
@@ -79,10 +81,32 @@ EXPORT_SYMBOL(cap_netlink_recv);* cap_has_capability() returns 0 when a task has a capability, but the * kernel's capable() and has_capability() returns 1 for this case. */ -int cap_capable (struct task_struct *tsk,const struct cred *cred,int cap, -int audit) +int cap_capable (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *targ_ns,int cap,int audit) { -return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; +for (;;) { +/* The creator of the user namespace has all caps. */ +if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +return 0 ; + +/* Do we have the necessary capabilities? */ +if (targ_ns == cred->user->user_ns) +return cap_raised (cred->cap_effective, cap) ?0 : -EPERM ; + +/* Have we tried all of the parent namespaces? */ +if (targ_ns == &init_user_ns) +return -EPERM ; + +/* If you have the capability in a parent user ns you have it +* in the over all children user namespaces as well, so see +* if this process has the capability in the parent user +* namespace. +*/ + targ_ns = targ_ns->creator->user_ns; + } + +/* We never get here */ +return -EPERM ; }/**
@@ -177,7 +201,8 @@ static inline int cap_inh_is_capped(void)/* they are so limited unless the current task has the CAP_SETPCAP * capability */ -if (cap_capable (current,current_cred (),CAP_SETPCAP , +if (cap_capable (current,current_cred (), +current_cred ()->user->user_ns,CAP_SETPCAP ,SECURITY_CAP_AUDIT ) ==0 )return 0 ;return 1 ;
@@ -829,7 +854,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, & (new->securebits ^ arg2)) /*[1]*/ || ((new->securebits &SECURE_ALL_LOCKS & ~arg2))/*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS |SECURE_ALL_BITS ))/*[3]*/ - || (cap_capable (current,current_cred (),CAP_SETPCAP , + || (cap_capable (current,current_cred (), +current_cred ()->user->user_ns,CAP_SETPCAP ,SECURITY_CAP_AUDIT ) !=0 )/*[4]*/ /* * [1] no changing of bits that are locked
@@ -894,7 +920,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) {int cap_sys_admin =0 ; -if (cap_capable (current,current_cred (),CAP_SYS_ADMIN , +if (cap_capable (current,current_cred (), &init_user_ns,CAP_SYS_ADMIN ,SECURITY_CAP_NOAUDIT ) ==0 ) cap_sys_admin =1 ;return __vm_enough_memory (mm, pages, cap_sys_admin);
@@ -921,7 +947,7 @@ int cap_file_mmap(struct file *file, unsigned long reqprot,int ret =0 ;if (addr < dac_mmap_min_addr) { - ret =cap_capable (current,current_cred (),CAP_SYS_RAWIO , + ret =cap_capable (current,current_cred (), &init_user_ns,CAP_SYS_RAWIO ,SECURITY_CAP_AUDIT );/* set PF_SUPERPRIV if it turns out we allow the low mmap */ if (ret ==0 )
security/security.c +10 -6
@@ -154,29 +154,33 @@ int security_capset(struct cred *new, const struct cred *old, effective, inheritable, permitted); } -int security_capable (const struct cred *cred,int cap) +int security_capable (struct user_namespace *ns,const struct cred *cred, +int cap) { -return security_ops->capable (current, cred, cap,SECURITY_CAP_AUDIT ); +return security_ops->capable (current, cred, ns, cap, +SECURITY_CAP_AUDIT ); } -int security_real_capable (struct task_struct *tsk,int cap) +int security_real_capable (struct task_struct *tsk,struct user_namespace *ns, +int cap) {const struct cred *cred;int ret; cred =get_task_cred (tsk); - ret = security_ops->capable (tsk, cred, cap,SECURITY_CAP_AUDIT ); + ret = security_ops->capable (tsk, cred, ns, cap,SECURITY_CAP_AUDIT );put_cred (cred);return ret; } -int security_real_capable_noaudit (struct task_struct *tsk,int cap) +int security_real_capable_noaudit (struct task_struct *tsk, +struct user_namespace *ns,int cap) {const struct cred *cred;int ret; cred =get_task_cred (tsk); - ret = security_ops->capable (tsk, cred, cap,SECURITY_CAP_NOAUDIT ); + ret = security_ops->capable (tsk, cred, ns, cap,SECURITY_CAP_NOAUDIT );put_cred (cred);return ret; }
security/selinux/hooks.c +9 -5
@@ -77,6 +77,7 @@#include <linux/mutex.h> #include <linux/posix-timers.h> #include <linux/syslog.h> +#include <linux/user_namespace.h> #include "avc.h" #include "objsec.h"
@@ -1423,6 +1424,7 @@ static int current_has_perm(const struct task_struct *tsk,/* Check whether a task is allowed to use a capability. */ static int task_has_capability (struct task_struct *tsk,const struct cred *cred, +struct user_namespace *ns,int cap,int audit) {struct common_audit_data ad;
@@ -1851,15 +1853,15 @@ static int selinux_capset(struct cred *new, const struct cred *old,*/ static int selinux_capable (struct task_struct *tsk,const struct cred *cred, -int cap,int audit) +struct user_namespace *ns,int cap,int audit) {int rc; - rc =cap_capable (tsk, cred, cap, audit); + rc =cap_capable (tsk, cred, ns, cap, audit);if (rc)return rc; -return task_has_capability (tsk, cred, cap, audit); +return task_has_capability (tsk, cred, ns, cap, audit); }static int selinux_sysctl_get_sid (ctl_table *table,u16 tclass,u32 *sid)
@@ -2012,7 +2014,8 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) {int rc, cap_sys_admin =0 ; - rc =selinux_capable (current,current_cred (),CAP_SYS_ADMIN , + rc =selinux_capable (current,current_cred (), + &init_user_ns,CAP_SYS_ADMIN ,SECURITY_CAP_NOAUDIT );if (rc ==0 ) cap_sys_admin =1 ;
@@ -2829,7 +2832,8 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name* and lack of permission just means that we fall back to the * in-core context value, not a denial. */ - error =selinux_capable (current,current_cred (),CAP_MAC_ADMIN , + error =selinux_capable (current,current_cred (), + &init_user_ns,CAP_MAC_ADMIN ,SECURITY_CAP_NOAUDIT );if (!error) error = security_sid_to_context_force(isec->sid, &context,
Signature · 1 line
1.7.0.4
PATCH 3/9 allow sethostname in a container
In reply to a message not in the archive--- kernel/sys.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/sys.c +1 -1
@@ -1177,7 +1177,7 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!capable (CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN ))return -EPERM ;if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;
Signature · 1 line
1.7.0.4
PATCH 4/9 allow killing tasks in your own or child userns
In reply to a message not in the archiveChangelog:
Dec 8: Fixed bug in my check_kill_permission pointed out by
Eric Biederman.
Dec 13: Apply Eric's suggestion to pass target task into kill_ok_by_cred()
for clarity
Dec 31: address comment by Eric Biederman:
don't need cred/tcred in check_kill_permission.
Jan 1: use const cred struct.
Jan 11: Per Bastian Blank's advice, clean up kill_ok_by_cred(). Feb 16: kill_ok_by_cred: fix bad parentheses
--- kernel/signal.c | 30 ++++++++++++++++++++++-------- 1 files changed, 22 insertions(+), 8 deletions(-)
kernel/signal.c +22 -8
@@ -636,13 +636,33 @@ static inline bool si_fromuser(const struct siginfo *info) }/* +* called with RCU read lock from check_kill_permission() +*/ +static inline int kill_ok_by_cred (struct task_struct *t) +{ +const struct cred *cred =current_cred (); +const struct cred *tcred =__task_cred (t); + +if (cred->user->user_ns == tcred->user->user_ns && + (cred->euid == tcred->suid || + cred->euid == tcred->uid || + cred->uid == tcred->suid || + cred->uid == tcred->uid)) +return 1 ; + +if (ns_capable (tcred->user->user_ns,CAP_KILL )) +return 1 ; + +return 0 ; +} + +/* * Bad permissions for sending the signal * - the caller must hold the RCU read lock */ static int check_kill_permission (int sig,struct siginfo *info,struct task_struct *t) { -const struct cred *cred, *tcred;struct pid *sid;int error;
@@ -656,14 +676,8 @@ static int check_kill_permission(int sig, struct siginfo *info,if (error)return error; - cred =current_cred (); - tcred =__task_cred (t);if (!same_thread_group (current, t) && - (cred->euid ^ tcred->suid) && - (cred->euid ^ tcred->uid) && - (cred->uid ^ tcred->suid) && - (cred->uid ^ tcred->uid) && - !capable (CAP_KILL )) { + !kill_ok_by_cred (t)) {switch (sig) {case SIGCONT : sid =task_session (t);
Signature · 1 line
1.7.0.4
PATCH 5/9 Allow ptrace from non-init user namespaces
In reply to a message not in the archiveptrace is allowed to tasks in the same user namespace according to the usual rules (i.e. the same rules as for two tasks in the init user namespace). ptrace is also allowed to a user namespace to which the current task the has CAP_SYS_PTRACE capability.
Changelog:
Dec 31: Address feedback by Eric:
. Correct ptrace uid check
. Rename may_ptrace_ns to ptrace_capable . Also fix the cap_ptrace checks.
Jan 1: Use const cred struct
Jan 11: use task_ns_capable() in place of ptrace_capable().
--- include/linux/capability.h | 2 + include/linux/user_namespace.h | 9 +++++++ kernel/ptrace.c | 27 ++++++++++++---------- kernel/user_namespace.c | 16 +++++++++++++ security/commoncap.c | 48 +++++++++++++++++++++++++++++++++------ 5 files changed, 82 insertions(+), 20 deletions(-)
include/linux/capability.h +2 -0
@@ -546,6 +546,8 @@ extern const kernel_cap_t __cap_init_eff_set;*/ #define has_capability (t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0) +#define has_ns_capability (t, ns, cap) (security_real_capable((t), (ns), (cap)) == 0) +/** * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) * @t: The task in question
include/linux/user_namespace.h +9 -0
@@ -39,6 +39,9 @@ static inline void put_user_ns(struct user_namespace *ns)uid_t user_ns_map_uid (struct user_namespace *to,const struct cred *cred,uid_t uid);gid_t user_ns_map_gid (struct user_namespace *to,const struct cred *cred,gid_t gid); +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim); +#else static inline struct user_namespace *get_user_ns (struct user_namespace *ns)
@@ -66,6 +69,12 @@ static inline gid_t user_ns_map_gid(struct user_namespace *to,return gid; } +static inline int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +return 1 ; +} +#endif #endif /* _LINUX_USER_H */
kernel/ptrace.c +15 -12
@@ -134,21 +134,24 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)return 0 ;rcu_read_lock (); tcred =__task_cred (task); -if ((cred->uid != tcred->euid || - cred->uid != tcred->suid || - cred->uid != tcred->uid || - cred->gid != tcred->egid || - cred->gid != tcred->sgid || - cred->gid != tcred->gid) && - !capable (CAP_SYS_PTRACE )) { -rcu_read_unlock (); -return -EPERM ; - } +if (cred->user->user_ns == tcred->user->user_ns && + (cred->uid == tcred->euid && + cred->uid == tcred->suid && + cred->uid == tcred->uid && + cred->gid == tcred->egid && + cred->gid == tcred->sgid && + cred->gid == tcred->gid)) + goto ok; +if (ns_capable (tcred->user->user_ns,CAP_SYS_PTRACE )) + goto ok; +rcu_read_unlock (); +return -EPERM ; +ok:rcu_read_unlock ();smp_rmb ();if (task->mm) dumpable =get_dumpable (task->mm); -if (!dumpable && !capable (CAP_SYS_PTRACE )) +if (!dumpable && !task_ns_capable (task,CAP_SYS_PTRACE ))return -EPERM ;return security_ptrace_access_check (task, mode);
@@ -198,7 +201,7 @@ int ptrace_attach(struct task_struct *task) goto unlock_tasklist; task->ptrace =PT_PTRACED ; -if (capable (CAP_SYS_PTRACE )) +if (task_ns_capable (task,CAP_SYS_PTRACE )) task->ptrace |=PT_PTRACE_CAP ;__ptrace_link (task, current);
kernel/user_namespace.c +16 -0
@@ -129,6 +129,22 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_treturn overflowgid; } +int same_or_ancestor_user_ns (struct task_struct *task, +struct task_struct *victim) +{ +struct user_namespace *u1 =task_cred_xxx (task, user)->user_ns; +struct user_namespace *u2 =task_cred_xxx (victim, user)->user_ns; +for (;;) { +if (u1 == u2) +return 1 ; +if (u1 == &init_user_ns) +return 0 ; + u1 = u1->creator->user_ns; + } +/* We never get here */ +return 0 ; +} +static __init int user_namespaces_init (void ) { user_ns_cachep =KMEM_CACHE (user_namespace,SLAB_PANIC );
security/commoncap.c +40 -8
76 lines not on this page · raw message
Signature · 1 line
1.7.0.4
PATCH 6/9 user namespaces: convert all capable checks in kernel/sys.c
In reply to a message not in the archiveThis allows setuid/setgid in containers. It also fixes some corner cases where kernel logic foregoes capability checks when uids are equivalent. The latter will need to be done throughout the whole kernel.
Changelog:
Jan 11: Use nsown_capable() as suggested by Bastian Blank. Jan 11: Fix logic errors in uid checks pointed out by Bastian. Feb 15: allow prlimit to current (was regression in previous version)
--- kernel/sys.c | 74 ++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 47 insertions(+), 27 deletions(-)
kernel/sys.c +47 -27
@@ -118,17 +118,29 @@ EXPORT_SYMBOL(cad_pid);void (*pm_power_off_prepare)(void ); +/* called with rcu_read_lock, creds are safe */ +static inline int set_one_prio_perm (struct task_struct *p) +{ +const struct cred *cred =current_cred (), *pcred =__task_cred (p); + +if (pcred->user->user_ns == cred->user->user_ns && + (pcred->uid == cred->euid || + pcred->euid == cred->euid)) +return 1 ; +if (ns_capable (pcred->user->user_ns,CAP_SYS_NICE )) +return 1 ; +return 0 ; +} +/* * set the priority of a task * - the caller must hold the RCU read lock */ static int set_one_prio (struct task_struct *p,int niceval,int error) { -const struct cred *cred =current_cred (), *pcred =__task_cred (p);int no_nice; -if (pcred->uid != cred->euid && - pcred->euid != cred->euid && !capable (CAP_SYS_NICE )) { +if (!set_one_prio_perm (p)) { error = -EPERM ; goto out; }
@@ -502,7 +514,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)if (rgid != (gid_t) -1 ) {if (old->gid == rgid || old->egid == rgid || -capable (CAP_SETGID )) +nsown_capable (CAP_SETGID )) new->gid = rgid;else goto error;
@@ -511,7 +523,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)if (old->gid == egid || old->egid == egid || old->sgid == egid || -capable (CAP_SETGID )) +nsown_capable (CAP_SETGID )) new->egid = egid;else goto error;
@@ -546,7 +558,7 @@ SYSCALL_DEFINE1(setgid, gid_t, gid) old =current_cred (); retval = -EPERM ; -if (capable (CAP_SETGID )) +if (nsown_capable (CAP_SETGID )) new->gid = new->egid = new->sgid = new->fsgid = gid;else if (gid == old->gid || gid == old->sgid) new->egid = new->fsgid = gid;
@@ -613,7 +625,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid) new->uid = ruid;if (old->uid != ruid && old->euid != ruid && - !capable (CAP_SETUID )) + !nsown_capable (CAP_SETUID )) goto error; }
@@ -622,7 +634,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)if (old->uid != euid && old->euid != euid && old->suid != euid && - !capable (CAP_SETUID )) + !nsown_capable (CAP_SETUID )) goto error; }
@@ -670,7 +682,7 @@ SYSCALL_DEFINE1(setuid, uid_t, uid) old =current_cred (); retval = -EPERM ; -if (capable (CAP_SETUID )) { +if (nsown_capable (CAP_SETUID )) { new->suid = new->uid = uid;if (uid != old->uid) { retval =set_user (new);
@@ -712,7 +724,7 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) old =current_cred (); retval = -EPERM ; -if (!capable (CAP_SETUID )) { +if (!nsown_capable (CAP_SETUID )) {if (ruid != (uid_t) -1 && ruid != old->uid && ruid != old->euid && ruid != old->suid) goto error;
@@ -776,7 +788,7 @@ SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid) old =current_cred (); retval = -EPERM ; -if (!capable (CAP_SETGID )) { +if (!nsown_capable (CAP_SETGID )) {if (rgid != (gid_t) -1 && rgid != old->gid && rgid != old->egid && rgid != old->sgid) goto error;
@@ -836,7 +848,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)if (uid == old->uid || uid == old->euid || uid == old->suid || uid == old->fsuid || -capable (CAP_SETUID )) { +nsown_capable (CAP_SETUID )) {if (uid != old_fsuid) { new->fsuid = uid;if (security_task_fix_setuid (new, old,LSM_SETID_FS ) ==0 )
@@ -869,7 +881,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)if (gid == old->gid || gid == old->egid || gid == old->sgid || gid == old->fsgid || -capable (CAP_SETGID )) { +nsown_capable (CAP_SETGID )) {if (gid != old_fsgid) { new->fsgid = gid; goto change_okay;
@@ -1177,8 +1189,11 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN )) { +printk (KERN_NOTICE "%s: did not have CAP_SYS_ADMIN\n" , __func__);return -EPERM ; + } +printk (KERN_NOTICE "%s: did have CAP_SYS_ADMIN\n" , __func__);if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;down_write (&uts_sem);
@@ -1226,7 +1241,7 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)int errno;char tmp[__NEW_UTS_LEN]; -if (!capable (CAP_SYS_ADMIN )) +if (!ns_capable (current->nsproxy->uts_ns->user_ns,CAP_SYS_ADMIN ))return -EPERM ;if (len <0 || len > __NEW_UTS_LEN)return -EINVAL ;
@@ -1341,6 +1356,8 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource, rlim = tsk->signal->rlim + resource;task_lock (tsk->group_leader);if (new_rlim) { +/* Keep the capable check against init_user_ns until +cgroups can contain all limits */ if (new_rlim->rlim_max > rlim->rlim_max && !capable (CAP_SYS_RESOURCE )) retval = -EPERM ;
@@ -1384,19 +1401,22 @@ static int check_prlimit_permission(struct task_struct *task) {const struct cred *cred =current_cred (), *tcred; - tcred =__task_cred (task); -if (current != task && - (cred->uid != tcred->euid || - cred->uid != tcred->suid || - cred->uid != tcred->uid || - cred->gid != tcred->egid || - cred->gid != tcred->sgid || - cred->gid != tcred->gid) && - !capable (CAP_SYS_RESOURCE )) { -return -EPERM ; - } +if (current == task) +return 0 ; -return 0 ; + tcred =__task_cred (task); +if (cred->user->user_ns == tcred->user->user_ns && + (cred->uid == tcred->euid && + cred->uid == tcred->suid && + cred->uid == tcred->uid && + cred->gid == tcred->egid && + cred->gid == tcred->sgid && + cred->gid == tcred->gid)) +return 0 ; +if (ns_capable (tcred->user->user_ns,CAP_SYS_RESOURCE )) +return 0 ; + +return -EPERM ; } SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
Signature · 1 line
1.7.0.4
PATCH 7/9 add a user namespace owner of ipc ns
In reply to a message not in the archiveChangelog:
Feb 15: Don't set new ipc->user_ns if we didn't create a new ipc_ns.
--- include/linux/ipc_namespace.h | 3 +++ ipc/msgutil.c | 3 +++ ipc/namespace.c | 9 +++++++-- kernel/nsproxy.c | 5 +++++ 4 files changed, 18 insertions(+), 2 deletions(-)
include/linux/ipc_namespace.h +3 -0
@@ -24,6 +24,7 @@ struct ipc_ids {struct idr ipcs_idr; }; +struct user_namespace ;struct ipc_namespace {atomic_t count;struct ipc_ids ids[3 ];
@@ -56,6 +57,8 @@ struct ipc_namespace {unsigned int mq_msg_max;/* initialized to DFLT_MSGMAX */ unsigned int mq_msgsize_max;/* initialized to DFLT_MSGSIZEMAX */ +/* user_ns which owns the ipc ns */ +struct user_namespace *user_ns; };extern struct ipc_namespace init_ipc_ns;
ipc/msgutil.c +3 -0
@@ -20,6 +20,8 @@DEFINE_SPINLOCK (mq_lock); +extern struct user_namespace init_user_ns; +/* * The next 2 defines are here bc this is the only file * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
@@ -32,6 +34,7 @@ struct ipc_namespace init_ipc_ns = { .mq_msg_max = DFLT_MSGMAX, .mq_msgsize_max = DFLT_MSGSIZEMAX, #endif + .user_ns = &init_user_ns, };atomic_t nr_ipc_ns =ATOMIC_INIT (1 );
ipc/namespace.c +7 -2
@@ -11,10 +11,11 @@#include <linux/slab.h> #include <linux/fs.h> #include <linux/mount.h> +#include <linux/user_namespace.h> #include "util.h" -static struct ipc_namespace *create_ipc_ns (void ) +static struct ipc_namespace *create_ipc_ns (struct ipc_namespace *old_ns) {struct ipc_namespace *ns;int err;
@@ -43,6 +44,9 @@ static struct ipc_namespace *create_ipc_ns(void)ipcns_notify (IPCNS_CREATED );register_ipcns_notifier (ns); + ns->user_ns = old_ns->user_ns; +get_user_ns (ns->user_ns); +return ns; }
@@ -50,7 +54,7 @@ struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns) {if (!(flags &CLONE_NEWIPC ))return get_ipc_ns (ns); -return create_ipc_ns (); +return create_ipc_ns (ns); }/*
@@ -105,6 +109,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)* order to have a correct value when recomputing msgmni. */ ipcns_notify (IPCNS_REMOVED ); +put_user_ns (ns->user_ns); }/*
kernel/nsproxy.c +5 -0
@@ -85,6 +85,11 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, err =PTR_ERR (new_nsp->ipc_ns); goto out_ipc; } +if (new_nsp->ipc_ns != tsk->nsproxy->ipc_ns) { +put_user_ns (new_nsp->ipc_ns->user_ns); + new_nsp->ipc_ns->user_ns =task_cred_xxx (tsk, user)->user_ns; +get_user_ns (new_nsp->ipc_ns->user_ns); + } new_nsp->pid_ns =copy_pid_ns (flags,task_active_pid_ns (tsk));if (IS_ERR (new_nsp->pid_ns)) {
Signature · 1 line
1.7.0.4
PATCH 8/9 user namespaces: convert several capable() calls
In reply to a message not in the archiveCAP_IPC_OWNER and CAP_IPC_LOCK can be checked against current_user_ns(), because the resource comes from current's own ipc namespace.
setuid/setgid are to uids in own namespace, so again checks can be against current_user_ns().
Changelog:
Jan 11: Use task_ns_capable() in place of sched_capable(). Jan 11: Use nsown_capable() as suggested by Bastian Blank. Jan 11: Clarify (hopefully) some logic in futex and sched.c Feb 15: use ns_capable for ipc, not nsown_capable
--- ipc/shm.c | 2 +- ipc/util.c | 5 +++-- kernel/futex.c | 11 ++++++++++- kernel/futex_compat.c | 11 ++++++++++- kernel/groups.c | 2 +- kernel/sched.c | 9 ++++++--- kernel/uid16.c | 2 +- 7 files changed, 32 insertions(+), 10 deletions(-)
ipc/shm.c +1 -1
@@ -773,7 +773,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)audit_ipc_obj (&(shp->shm_perm)); -if (!capable (CAP_IPC_LOCK )) { +if (!ns_capable (ns->user_ns,CAP_IPC_LOCK )) {uid_t euid =current_euid (); err = -EPERM ; if (euid != shp->shm_perm.uid &&
ipc/util.c +3 -2
@@ -627,7 +627,7 @@ int ipcperms (struct kern_ipc_perm *ipcp, short flag) granted_mode >>=3 ;/* is there some bit set in requested_mode but not in granted_mode? */ if ((requested_mode & ~granted_mode &0007 ) && - !capable (CAP_IPC_OWNER )) + !ns_capable (current->nsproxy->ipc_ns->user_ns,CAP_IPC_OWNER ))return -1 ;return security_ipc_permission (ipcp, flag);
@@ -800,7 +800,8 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, euid =current_euid ();if (euid == ipcp->cuid || - euid == ipcp->uid ||capable (CAP_SYS_ADMIN )) + euid == ipcp->uid || +ns_capable (current->nsproxy->ipc_ns->user_ns,CAP_SYS_ADMIN ))return ipcp; err = -EPERM ;
kernel/futex.c +10 -1
@@ -2421,10 +2421,19 @@ SYSCALL_DEFINE3(get_robust_list, int, pid, goto err_unlock; ret = -EPERM ; pcred =__task_cred (p); +/* If victim is in different user_ns, then uids are not +comparable, so we must have CAP_SYS_PTRACE */ +if (cred->user->user_ns != pcred->user->user_ns) { +if (!ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) + goto err_unlock; + goto ok; + } +/* If victim is in same user_ns, then uids are comparable */ if (cred->euid != pcred->euid && cred->euid != pcred->uid && - !capable (CAP_SYS_PTRACE )) + !ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) goto err_unlock; +ok: head = p->robust_list;rcu_read_unlock (); }
kernel/futex_compat.c +10 -1
@@ -153,10 +153,19 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, goto err_unlock; ret = -EPERM ; pcred =__task_cred (p); +/* If victim is in different user_ns, then uids are not +comparable, so we must have CAP_SYS_PTRACE */ +if (cred->user->user_ns != pcred->user->user_ns) { +if (!ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) + goto err_unlock; + goto ok; + } +/* If victim is in same user_ns, then uids are comparable */ if (cred->euid != pcred->euid && cred->euid != pcred->uid && - !capable (CAP_SYS_PTRACE )) + !ns_capable (pcred->user->user_ns,CAP_SYS_PTRACE )) goto err_unlock; +ok: head = p->compat_robust_list;rcu_read_unlock (); }
kernel/groups.c +1 -1
@@ -233,7 +233,7 @@ SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)struct group_info *group_info;int retval; -if (!capable (CAP_SETGID )) +if (!nsown_capable (CAP_SETGID ))return -EPERM ;if ((unsigned )gidsetsize >NGROUPS_MAX )return -EINVAL ;
kernel/sched.c +6 -3
@@ -4761,8 +4761,11 @@ static bool check_same_owner(struct task_struct *p)rcu_read_lock (); pcred =__task_cred (p); - match = (cred->euid == pcred->euid || - cred->euid == pcred->uid); +if (cred->user->user_ns == pcred->user->user_ns) + match = (cred->euid == pcred->euid || + cred->euid == pcred->uid); +else + match = false;rcu_read_unlock ();return match; }
@@ -5088,7 +5091,7 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask) goto out_free_cpus_allowed; } retval = -EPERM ; -if (!check_same_owner (p) && !capable (CAP_SYS_NICE )) +if (!check_same_owner (p) && !task_ns_capable (p,CAP_SYS_NICE )) goto out_unlock; retval =security_task_setscheduler (p);
kernel/uid16.c +1 -1
@@ -189,7 +189,7 @@ SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist)struct group_info *group_info;int retval; -if (!capable (CAP_SETGID )) +if (!nsown_capable (CAP_SETGID ))return -EPERM ;if ((unsigned )gidsetsize >NGROUPS_MAX )return -EINVAL ;
Signature · 1 line
1.7.0.4
PATCH 9/9 userns: check user namespace for task->file uid equivalence checks
In reply to a message not in the archiveCheat for now and say all files belong to init_user_ns. Next step will be to let superblocks belong to a user_ns, and derive inode_userns(inode) from inode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements.
Changelog:
Feb 15: make is_owner_or_cap take const struct inode
--- fs/inode.c | 17 +++++++++++++++++ fs/namei.c | 20 +++++++++++++++----- include/linux/fs.h | 9 +++++++-- 3 files changed, 39 insertions(+), 7 deletions(-)
fs/inode.c +17 -0
@@ -25,6 +25,7 @@#include <linux/async.h> #include <linux/posix_acl.h> #include <linux/ima.h> +#include <linux/cred.h> /* * This is needed for the following functions:
@@ -1722,3 +1723,19 @@ void inode_init_owner(struct inode *inode, const struct inode *dir, inode->i_mode = mode; }EXPORT_SYMBOL (inode_init_owner); + +/* +* return 1 if current either has CAP_FOWNER to the +* file, or owns the file. +*/ +int is_owner_or_cap (const struct inode *inode) +{ +struct user_namespace *ns =inode_userns (inode); + +if (current_user_ns () == ns &¤t_fsuid () == inode->i_uid) +return 1 ; +if (ns_capable (ns,CAP_FOWNER )) +return 1 ; +return 0 ; +} +EXPORT_SYMBOL (is_owner_or_cap);
fs/namei.c +15 -5
@@ -176,6 +176,9 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag mask &=MAY_READ |MAY_WRITE |MAY_EXEC ; +if (current_user_ns () !=inode_userns (inode)) + goto other_perms; +if (current_fsuid () == inode->i_uid) mode >>=6 ;else {
@@ -189,6 +192,7 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag mode >>=3 ; } +other_perms:/* * If the DACs are ok we don't need any capability check. */
@@ -230,7 +234,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,* Executable DACs are overridable if at least one exec bit is set. */ if (!(mask &MAY_EXEC ) ||execute_ok (inode)) -if (capable (CAP_DAC_OVERRIDE )) +if (ns_capable (inode_userns (inode),CAP_DAC_OVERRIDE ))return 0 ;/*
@@ -238,7 +242,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,*/ mask &=MAY_READ |MAY_WRITE |MAY_EXEC ;if (mask ==MAY_READ || (S_ISDIR (inode->i_mode) && !(mask &MAY_WRITE ))) -if (capable (CAP_DAC_READ_SEARCH )) +if (ns_capable (inode_userns (inode),CAP_DAC_READ_SEARCH ))return 0 ;return -EACCES ;
@@ -675,6 +679,7 @@ force_reval_path(struct path *path, struct nameidata *nd)static inline int exec_permission (struct inode *inode,unsigned int flags) {int ret; +struct user_namespace *ns =inode_userns (inode);if (inode->i_op->permission) { ret = inode->i_op->permission (inode,MAY_EXEC , flags);
@@ -687,7 +692,7 @@ static inline int exec_permission(struct inode *inode, unsigned int flags)if (ret == -ECHILD )return ret; -if (capable (CAP_DAC_OVERRIDE ) ||capable (CAP_DAC_READ_SEARCH )) +if (ns_capable (ns,CAP_DAC_OVERRIDE ) ||ns_capable (ns,CAP_DAC_READ_SEARCH )) goto ok;return ret;
@@ -1940,11 +1945,15 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)if (!(dir->i_mode &S_ISVTX ))return 0 ; +if (current_user_ns () !=inode_userns (inode)) + goto other_userns;if (inode->i_uid == fsuid)return 0 ;if (dir->i_uid == fsuid)return 0 ; -return !capable (CAP_FOWNER ); + +other_userns: +return !ns_capable (inode_userns (inode),CAP_FOWNER ); }/*
@@ -2635,7 +2644,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)if (error)return error; -if ((S_ISCHR (mode) ||S_ISBLK (mode)) && !capable (CAP_MKNOD )) +if ((S_ISCHR (mode) ||S_ISBLK (mode)) && + !ns_capable (inode_userns (dir),CAP_MKNOD ))return -EPERM ;if (!dir->i_op->mknod)
include/linux/fs.h +7 -2
@@ -1446,8 +1446,13 @@ enum {#define put_fs_excl () atomic_dec(¤t->fs_excl)#define has_fs_excl () atomic_read(¤t->fs_excl) -#define is_owner_or_cap (inode) \ - ((current_fsuid() == (inode)->i_uid) || capable(CAP_FOWNER)) +/* +* until VFS tracks user namespaces for inodes, just make all files +* belong to init_user_ns +*/ +extern struct user_namespace init_user_ns; +#define inode_userns (inode) (&init_user_ns) +extern int is_owner_or_cap (const struct inode *inode);/* not quite ready to be deprecated, but... */ extern void lock_super (struct super_block *);
Signature · 1 line
1.7.0.4
Quoted 13 lines · On Thu, 17 Feb 2011 15:02:24 +0000 "Serge E. Hallyn"…
Here is a repost of my previous user namespace patch, ported onto last night's git head.
It fixes several things I was doing wrong in the last (v4) posting, in particular:
1. don't set
uts_ns->user_nsto current's when !CLONE_NEWUTS
2. add aipc_ns->user_nswhich ownsipc_ns, and use that to decideCAP_IPC_OWNER
3. fix logic flaw caused by bad parantheses
4. allowdo_prlimitto current
5. don't always give root full privs toinit_user_ns
Quoted 2 lines
The expected course of development for user namespaces is laid out at https://wiki.ubuntu.com/UserNamespace.
Seems like a nice feature to be developing.
I worry about the maturity of it all at this stage. How far along is it *really*?
Is anyone else working with you on developing and reviewing this work?
User namespaces and keys
In reply to a message not in the archiveI guess we need to look at how to mix keys and namespaces again.
Possibly the trickiest problem with keys is how to upcall key construction to /sbin/request-key when the keys may be of a different user namespace.
David
Serge E. Hallyn
maintainerPATCH 9/9 userns: check user namespace for task->file uid equivalence checks
In reply to Serge E. HallynCheat for now and say all files belong to init_user_ns. Next step will be to let superblocks belong to a user_ns, and derive inode_userns(inode) from inode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements.
Changelog:
Feb 15: make is_owner_or_cap take const struct inode
--- fs/inode.c | 17 +++++++++++++++++ fs/namei.c | 20 +++++++++++++++----- include/linux/fs.h | 9 +++++++-- 3 files changed, 39 insertions(+), 7 deletions(-)
fs/inode.c +17 -0
@@ -25,6 +25,7 @@#include <linux/async.h> #include <linux/posix_acl.h> #include <linux/ima.h> +#include <linux/cred.h> /* * This is needed for the following functions:
@@ -1722,3 +1723,19 @@ void inode_init_owner(struct inode *inode, const struct inode *dir, inode->i_mode = mode; }EXPORT_SYMBOL (inode_init_owner); + +/* +* return 1 if current either has CAP_FOWNER to the +* file, or owns the file. +*/ +int is_owner_or_cap (const struct inode *inode) +{ +struct user_namespace *ns =inode_userns (inode); + +if (current_user_ns () == ns &¤t_fsuid () == inode->i_uid) +return 1 ; +if (ns_capable (ns,CAP_FOWNER )) +return 1 ; +return 0 ; +} +EXPORT_SYMBOL (is_owner_or_cap);
fs/namei.c +15 -5
@@ -176,6 +176,9 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag mask &=MAY_READ |MAY_WRITE |MAY_EXEC ; +if (current_user_ns () !=inode_userns (inode)) + goto other_perms; +if (current_fsuid () == inode->i_uid) mode >>=6 ;else {
@@ -189,6 +192,7 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag mode >>=3 ; } +other_perms:/* * If the DACs are ok we don't need any capability check. */
@@ -230,7 +234,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,* Executable DACs are overridable if at least one exec bit is set. */ if (!(mask &MAY_EXEC ) ||execute_ok (inode)) -if (capable (CAP_DAC_OVERRIDE )) +if (ns_capable (inode_userns (inode),CAP_DAC_OVERRIDE ))return 0 ;/*
@@ -238,7 +242,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,*/ mask &=MAY_READ |MAY_WRITE |MAY_EXEC ;if (mask ==MAY_READ || (S_ISDIR (inode->i_mode) && !(mask &MAY_WRITE ))) -if (capable (CAP_DAC_READ_SEARCH )) +if (ns_capable (inode_userns (inode),CAP_DAC_READ_SEARCH ))return 0 ;return -EACCES ;
@@ -675,6 +679,7 @@ force_reval_path(struct path *path, struct nameidata *nd)static inline int exec_permission (struct inode *inode,unsigned int flags) {int ret; +struct user_namespace *ns =inode_userns (inode);if (inode->i_op->permission) { ret = inode->i_op->permission (inode,MAY_EXEC , flags);
@@ -687,7 +692,7 @@ static inline int exec_permission(struct inode *inode, unsigned int flags)if (ret == -ECHILD )return ret; -if (capable (CAP_DAC_OVERRIDE ) ||capable (CAP_DAC_READ_SEARCH )) +if (ns_capable (ns,CAP_DAC_OVERRIDE ) ||ns_capable (ns,CAP_DAC_READ_SEARCH )) goto ok;return ret;
@@ -1940,11 +1945,15 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)if (!(dir->i_mode &S_ISVTX ))return 0 ; +if (current_user_ns () !=inode_userns (inode)) + goto other_userns;if (inode->i_uid == fsuid)return 0 ;if (dir->i_uid == fsuid)return 0 ; -return !capable (CAP_FOWNER ); + +other_userns: +return !ns_capable (inode_userns (inode),CAP_FOWNER ); }/*
@@ -2635,7 +2644,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)if (error)return error; -if ((S_ISCHR (mode) ||S_ISBLK (mode)) && !capable (CAP_MKNOD )) +if ((S_ISCHR (mode) ||S_ISBLK (mode)) && + !ns_capable (inode_userns (dir),CAP_MKNOD ))return -EPERM ;if (!dir->i_op->mknod)
include/linux/fs.h +7 -2
@@ -1446,8 +1446,13 @@ enum {#define put_fs_excl () atomic_dec(¤t->fs_excl)#define has_fs_excl () atomic_read(¤t->fs_excl) -#define is_owner_or_cap (inode) \ - ((current_fsuid() == (inode)->i_uid) || capable(CAP_FOWNER)) +/* +* until VFS tracks user namespaces for inodes, just make all files +* belong to init_user_ns +*/ +extern struct user_namespace init_user_ns; +#define inode_userns (inode) (&init_user_ns) +extern int is_owner_or_cap (const struct inode *inode);/* not quite ready to be deprecated, but... */ extern void lock_super (struct super_block *);
Signature · 1 line
1.7.0.4
A message that is not in the archive (sent off-list or not yet received): [email protected]
Eric W. Biederman
Acked-byRe: [PATCH 9/9] userns: check user namespace for task->file uid equivalence checks
Quoted 4 lines · Serge E. Hallyn
Cheat for now and say all files belong to
init_user_ns. Next step will be to let superblocks belong to auser_ns, and deriveinode_userns(inode)frominode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements.
This looks good. I am a little worried that a concept like inode_user_ns will imply that there is only ever one.
However this looks like a good place to start and it will only be strange filesystems that implement a notion of permissions that is namespace aware so I don't expect the generic code needs to handle that case other than allowing the permission checks to be overridden.
Quoted 147 lines
Changelog:
Feb 15: makeis_owner_or_captake const struct inodeSigned-off-by Serge E. Hallyn <[email protected]>--- fs/inode.c | 17 +++++++++++++++++ fs/namei.c | 20 +++++++++++++++----- include/linux/fs.h | 9 +++++++-- 3 files changed, 39 insertions(+), 7 deletions(-)fs/inode.c +17
-0@@ -25,6 +25,7 @@#include <linux/async.h> #include <linux/posix_acl.h> #include <linux/ima.h> +#include <linux/cred.h> /* * This is needed for the following functions: @@ -1722,3 +1723,19 @@ void inode_init_owner(struct inode *inode, const struct inode *dir, inode->i_mode = mode; }EXPORT_SYMBOL (inode_init_owner); + +/* +* return 1 if current either has CAP_FOWNER to the +* file, or owns the file. +*/ +int is_owner_or_cap (const struct inode *inode) +{ +struct user_namespace *ns =inode_userns (inode); + +if (current_user_ns () == ns &¤t_fsuid () == inode->i_uid) +return 1 ; +if (ns_capable (ns,CAP_FOWNER )) +return 1 ; +return 0 ; +} +EXPORT_SYMBOL (is_owner_or_cap);fs/namei.c +15
-5@@ -176,6 +176,9 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag mask &=MAY_READ |MAY_WRITE |MAY_EXEC ; +if (current_user_ns () !=inode_userns (inode)) + goto other_perms; +if (current_fsuid () == inode->i_uid) mode >>=6 ;else {@@ -189,6 +192,7 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag mode >>=3 ; } +other_perms:/* * If the DACs are ok we don't need any capability check. */ @@ -230,7 +234,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,* Executable DACs are overridable if at least one exec bit is set. */ if (!(mask &MAY_EXEC ) ||execute_ok (inode)) -if (capable (CAP_DAC_OVERRIDE )) +if (ns_capable (inode_userns (inode),CAP_DAC_OVERRIDE ))return 0 ;/* @@ -238,7 +242,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,*/ mask &=MAY_READ |MAY_WRITE |MAY_EXEC ;if (mask ==MAY_READ || (S_ISDIR (inode->i_mode) && !(mask &MAY_WRITE ))) -if (capable (CAP_DAC_READ_SEARCH )) +if (ns_capable (inode_userns (inode),CAP_DAC_READ_SEARCH ))return 0 ;return -EACCES ;@@ -675,6 +679,7 @@ force_reval_path(struct path *path, struct nameidata *nd)static inline int exec_permission (struct inode *inode,unsigned int flags) {int ret; +struct user_namespace *ns =inode_userns (inode);if (inode->i_op->permission) { ret = inode->i_op->permission (inode,MAY_EXEC , flags);@@ -687,7 +692,7 @@ static inline int exec_permission(struct inode *inode, unsigned int flags)if (ret == -ECHILD )return ret; -if (capable (CAP_DAC_OVERRIDE ) ||capable (CAP_DAC_READ_SEARCH )) +if (ns_capable (ns,CAP_DAC_OVERRIDE ) ||ns_capable (ns,CAP_DAC_READ_SEARCH )) goto ok;return ret;@@ -1940,11 +1945,15 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)if (!(dir->i_mode &S_ISVTX ))return 0 ; +if (current_user_ns () !=inode_userns (inode)) + goto other_userns;if (inode->i_uid == fsuid)return 0 ;if (dir->i_uid == fsuid)return 0 ; -return !capable (CAP_FOWNER ); + +other_userns: +return !ns_capable (inode_userns (inode),CAP_FOWNER ); }/* @@ -2635,7 +2644,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)if (error)return error; -if ((S_ISCHR (mode) ||S_ISBLK (mode)) && !capable (CAP_MKNOD )) +if ((S_ISCHR (mode) ||S_ISBLK (mode)) && + !ns_capable (inode_userns (dir),CAP_MKNOD ))return -EPERM ;if (!dir->i_op->mknod)include/linux/fs.h +7
-2@@ -1446,8 +1446,13 @@ enum {#define put_fs_excl () atomic_dec(¤t->fs_excl)#define has_fs_excl () atomic_read(¤t->fs_excl) -#define is_owner_or_cap (inode) \ - ((current_fsuid() == (inode)->i_uid) || capable(CAP_FOWNER)) +/* +* until VFS tracks user namespaces for inodes, just make all files +* belong to init_user_ns +*/ +extern struct user_namespace init_user_ns; +#define inode_userns (inode) (&init_user_ns) +extern int is_owner_or_cap (const struct inode *inode);/* not quite ready to be deprecated, but... */ extern void lock_super (struct super_block *);
Re: [PATCH 9/9] userns: check user namespace for task->file uid equivalence checks
In reply to a message not in the archiveQuoted 13 lines · On Thu, 17 Feb 2011 15:04:07 +0000 "Serge E. Hallyn"…
Cheat for now and say all files belong to
init_user_ns. Next step will be to let superblocks belong to auser_ns, and deriveinode_userns(inode)frominode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements....
fs/inode.c +5
-0+ +/* +* return 1 if current either has CAP_FOWNER to the +* file, or owns the file. +*/
fs/inode.c void inode_init_owner(struct inode *inode, const struct inode *dir, · line 1740
Quoted 10 lines
fs/inode.c +10
-0+int is_owner_or_cap (const struct inode *inode) +{ +struct user_namespace *ns =inode_userns (inode); + +if (current_user_ns () == ns &¤t_fsuid () == inode->i_uid) +return 1 ; +if (ns_capable (ns,CAP_FOWNER )) +return 1 ; +return 0 ; +}
bool?
Quoted 1 line
+EXPORT_SYMBOL(is_owner_or_cap);
There's a fairly well adhered to convention that global symbols (and often static symbols) have a prefix which identifies the subsystem to which they belong. This patchset rather scorns that convention.
Most of these identifiers are pretty obviously from the capability subsystem, but still...
Quoted 3 lines
...
Daniel Lezcano
Acked-byRe: [PATCH 9/9] userns: check user namespace for task->file uid equivalence checks
In reply to a message not in the archiveQuoted 10 lines · Serge E. Hallyn
Cheat for now and say all files belong to
init_user_ns. Next step will be to let superblocks belong to auser_ns, and deriveinode_userns(inode)frominode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements.Changelog:
Feb 15: makeis_owner_or_captake const struct inodeSigned-off-by Serge E. Hallyn<[email protected]>---
Re: [PATCH 9/9] userns: check user namespace for task->file uid equivalence checks
In reply to Serge E. HallynQuoted 4 lines · Serge E. Hallyn
Cheat for now and say all files belong to
init_user_ns. Next step will be to let superblocks belong to auser_ns, and deriveinode_userns(inode)frominode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements.
This looks good. I am a little worried that a concept like inode_user_ns will imply that there is only ever one.
However this looks like a good place to start and it will only be strange filesystems that implement a notion of permissions that is namespace aware so I don't expect the generic code needs to handle that case other than allowing the permission checks to be overridden.
Quoted 147 lines
Changelog:
Feb 15: makeis_owner_or_captake const struct inodeSigned-off-by Serge E. Hallyn <[email protected]>--- fs/inode.c | 17 +++++++++++++++++ fs/namei.c | 20 +++++++++++++++----- include/linux/fs.h | 9 +++++++-- 3 files changed, 39 insertions(+), 7 deletions(-)fs/inode.c +17
-0@@ -25,6 +25,7 @@#include <linux/async.h> #include <linux/posix_acl.h> #include <linux/ima.h> +#include <linux/cred.h> /* * This is needed for the following functions: @@ -1722,3 +1723,19 @@ void inode_init_owner(struct inode *inode, const struct inode *dir, inode->i_mode = mode; }EXPORT_SYMBOL (inode_init_owner); + +/* +* return 1 if current either has CAP_FOWNER to the +* file, or owns the file. +*/ +int is_owner_or_cap (const struct inode *inode) +{ +struct user_namespace *ns =inode_userns (inode); + +if (current_user_ns () == ns &¤t_fsuid () == inode->i_uid) +return 1 ; +if (ns_capable (ns,CAP_FOWNER )) +return 1 ; +return 0 ; +} +EXPORT_SYMBOL (is_owner_or_cap);fs/namei.c +15
-5@@ -176,6 +176,9 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag mask &=MAY_READ |MAY_WRITE |MAY_EXEC ; +if (current_user_ns () !=inode_userns (inode)) + goto other_perms; +if (current_fsuid () == inode->i_uid) mode >>=6 ;else {@@ -189,6 +192,7 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag mode >>=3 ; } +other_perms:/* * If the DACs are ok we don't need any capability check. */ @@ -230,7 +234,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,* Executable DACs are overridable if at least one exec bit is set. */ if (!(mask &MAY_EXEC ) ||execute_ok (inode)) -if (capable (CAP_DAC_OVERRIDE )) +if (ns_capable (inode_userns (inode),CAP_DAC_OVERRIDE ))return 0 ;/* @@ -238,7 +242,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,*/ mask &=MAY_READ |MAY_WRITE |MAY_EXEC ;if (mask ==MAY_READ || (S_ISDIR (inode->i_mode) && !(mask &MAY_WRITE ))) -if (capable (CAP_DAC_READ_SEARCH )) +if (ns_capable (inode_userns (inode),CAP_DAC_READ_SEARCH ))return 0 ;return -EACCES ;@@ -675,6 +679,7 @@ force_reval_path(struct path *path, struct nameidata *nd)static inline int exec_permission (struct inode *inode,unsigned int flags) {int ret; +struct user_namespace *ns =inode_userns (inode);if (inode->i_op->permission) { ret = inode->i_op->permission (inode,MAY_EXEC , flags);@@ -687,7 +692,7 @@ static inline int exec_permission(struct inode *inode, unsigned int flags)if (ret == -ECHILD )return ret; -if (capable (CAP_DAC_OVERRIDE ) ||capable (CAP_DAC_READ_SEARCH )) +if (ns_capable (ns,CAP_DAC_OVERRIDE ) ||ns_capable (ns,CAP_DAC_READ_SEARCH )) goto ok;return ret;@@ -1940,11 +1945,15 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)if (!(dir->i_mode &S_ISVTX ))return 0 ; +if (current_user_ns () !=inode_userns (inode)) + goto other_userns;if (inode->i_uid == fsuid)return 0 ;if (dir->i_uid == fsuid)return 0 ; -return !capable (CAP_FOWNER ); + +other_userns: +return !ns_capable (inode_userns (inode),CAP_FOWNER ); }/* @@ -2635,7 +2644,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)if (error)return error; -if ((S_ISCHR (mode) ||S_ISBLK (mode)) && !capable (CAP_MKNOD )) +if ((S_ISCHR (mode) ||S_ISBLK (mode)) && + !ns_capable (inode_userns (dir),CAP_MKNOD ))return -EPERM ;if (!dir->i_op->mknod)include/linux/fs.h +7
-2@@ -1446,8 +1446,13 @@ enum {#define put_fs_excl () atomic_dec(¤t->fs_excl)#define has_fs_excl () atomic_read(¤t->fs_excl) -#define is_owner_or_cap (inode) \ - ((current_fsuid() == (inode)->i_uid) || capable(CAP_FOWNER)) +/* +* until VFS tracks user namespaces for inodes, just make all files +* belong to init_user_ns +*/ +extern struct user_namespace init_user_ns; +#define inode_userns (inode) (&init_user_ns) +extern int is_owner_or_cap (const struct inode *inode);/* not quite ready to be deprecated, but... */ extern void lock_super (struct super_block *);
Re: [PATCH 9/9] userns: check user namespace for task->file uid equivalence checks
In reply to Serge E. HallynQuoted 13 lines · On Thu, 17 Feb 2011 15:04:07 +0000 "Serge E. Hallyn" <[email protected]> wrote:
Cheat for now and say all files belong to
init_user_ns. Next step will be to let superblocks belong to auser_ns, and deriveinode_userns(inode)frominode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements....
fs/inode.c +5
-0+ +/* +* return 1 if current either has CAP_FOWNER to the +* file, or owns the file. +*/
fs/inode.c void inode_init_owner(struct inode *inode, const struct inode *dir, · line 1740
Quoted 10 lines
fs/inode.c +10
-0+int is_owner_or_cap (const struct inode *inode) +{ +struct user_namespace *ns =inode_userns (inode); + +if (current_user_ns () == ns &¤t_fsuid () == inode->i_uid) +return 1 ; +if (ns_capable (ns,CAP_FOWNER )) +return 1 ; +return 0 ; +}
bool?
Quoted 1 line
+EXPORT_SYMBOL(is_owner_or_cap);
There's a fairly well adhered to convention that global symbols (and often static symbols) have a prefix which identifies the subsystem to which they belong. This patchset rather scorns that convention.
Most of these identifiers are pretty obviously from the capability subsystem, but still...
Quoted 3 lines
...
Serge E. Hallyn
maintainerRe: [PATCH 9/9] userns: check user namespace for task->file uid equivalence checks
Quoted 29 lines · Andrew Morton
Quoted 13 lines · On Thu, 17 Feb 2011 15:04:07 +0000 "Serge E. Hallyn" <[email protected]> wrote:
Cheat for now and say all files belong to
init_user_ns. Next step will be to let superblocks belong to auser_ns, and deriveinode_userns(inode)frominode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements....
fs/inode.c +5
-0+ +/* +* return 1 if current either has CAP_FOWNER to the +* file, or owns the file. +*/ fs/inode.c void inode_init_owner(struct inode *inode, const struct inode *dir, · line 1740
Quoted 10 lines
fs/inode.c +10
-0+int is_owner_or_cap (const struct inode *inode) +{ +struct user_namespace *ns =inode_userns (inode); + +if (current_user_ns () == ns &¤t_fsuid () == inode->i_uid) +return 1 ; +if (ns_capable (ns,CAP_FOWNER )) +return 1 ; +return 0 ; +}bool?
Quoted 8 lines
Quoted 1 line
+EXPORT_SYMBOL(is_owner_or_cap);
There's a fairly well adhered to convention that global symbols (and often static symbols) have a prefix which identifies the subsystem to which they belong. This patchset rather scorns that convention.
Most of these identifiers are pretty obviously from the capability subsystem, but still...
Would 'inode_owner_or_capable' be better and and make sense?
-serge
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 9/9] userns: check user namespace for task->file uid equivalence checks
Quoted 4 lines · On Thu, 24 Feb 2011 03:24:16 +0000 "Serge E. Hallyn"…
Quoted 3 lines · Andrew Morton
On Thu, 17 Feb 2011 15:04:07 +0000
"Serge E. Hallyn" <[email protected]> wrote:
Quoted 9 lines
Quoted 6 lines
There's a fairly well adhered to convention that global symbols (and often static symbols) have a prefix which identifies the subsystem to which they belong. This patchset rather scorns that convention.
Most of these identifiers are pretty obviously from the capability subsystem, but still...
Would '
inode_owner_or_capable' be better and and make sense?
I suppose so. We've totally screwed that pooch in the VFS (grep EXPORT fs/inode.c). But it's never to late to start.
Re: [PATCH 9/9] userns: check user namespace for task->file uid equivalence checks
In reply to Serge E. HallynQuoted 4 lines · On Thu, 24 Feb 2011 03:24:16 +0000 "Serge E. Hallyn" <[email protected]> wrote:
Quoted 3 lines · Andrew Morton
On Thu, 17 Feb 2011 15:04:07 +0000
"Serge E. Hallyn" <[email protected]> wrote:
Quoted 9 lines
Quoted 6 lines
There's a fairly well adhered to convention that global symbols (and often static symbols) have a prefix which identifies the subsystem to which they belong. This patchset rather scorns that convention.
Most of these identifiers are pretty obviously from the capability subsystem, but still...
Would '
inode_owner_or_capable' be better and and make sense?
I suppose so. We've totally screwed that pooch in the VFS (grep EXPORT fs/inode.c). But it's never to late to start.
A message that is not in the archive (sent off-list or not yet received): 20110218155935.66e7782d.akpm-de/[email protected]
Re: [PATCH 9/9] userns: check user namespace for task->file uid equivalence checks
Quoted 29 lines · Andrew Morton
Quoted 13 lines · On Thu, 17 Feb 2011 15:04:07 +0000 "Serge E. Hallyn"…
Cheat for now and say all files belong to
init_user_ns. Next step will be to let superblocks belong to auser_ns, and deriveinode_userns(inode)frominode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements....
fs/inode.c +5
-0+ +/* +* return 1 if current either has CAP_FOWNER to the +* file, or owns the file. +*/ fs/inode.c void inode_init_owner(struct inode *inode, const struct inode *dir, · line 1740
Quoted 10 lines
fs/inode.c +10
-0+int is_owner_or_cap (const struct inode *inode) +{ +struct user_namespace *ns =inode_userns (inode); + +if (current_user_ns () == ns &¤t_fsuid () == inode->i_uid) +return 1 ; +if (ns_capable (ns,CAP_FOWNER )) +return 1 ; +return 0 ; +}bool?
Quoted 8 lines
Quoted 1 line
+EXPORT_SYMBOL(is_owner_or_cap);
There's a fairly well adhered to convention that global symbols (and often static symbols) have a prefix which identifies the subsystem to which they belong. This patchset rather scorns that convention.
Most of these identifiers are pretty obviously from the capability subsystem, but still...
Would 'inode_owner_or_capable' be better and and make sense?
-serge
Re: [PATCH 9/9] userns: check user namespace for task->file uid equivalence checks
In reply to Serge E. HallynQuoted 10 lines · Serge E. Hallyn
Cheat for now and say all files belong to
init_user_ns. Next step will be to let superblocks belong to auser_ns, and deriveinode_userns(inode)frominode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements.Changelog:
Feb 15: makeis_owner_or_captake const struct inodeSigned-off-by Serge E. Hallyn<[email protected]>---
Quoted 13 lines · On Thu, 17 Feb 2011 15:02:24 +0000 "Serge E. Hallyn" <[email protected]> wrote:
Here is a repost of my previous user namespace patch, ported onto last night's git head.
It fixes several things I was doing wrong in the last (v4) posting, in particular:
1. don't set
uts_ns->user_nsto current's when !CLONE_NEWUTS
2. add aipc_ns->user_nswhich ownsipc_ns, and use that to decideCAP_IPC_OWNER
3. fix logic flaw caused by bad parantheses
4. allowdo_prlimitto current
5. don't always give root full privs toinit_user_ns
Quoted 2 lines
The expected course of development for user namespaces is laid out at https://wiki.ubuntu.com/UserNamespace.
Seems like a nice feature to be developing.
I worry about the maturity of it all at this stage. How far along is it *really*?
Is anyone else working with you on developing and reviewing this work?
A message that is not in the archive (sent off-list or not yet received): 20110217162146.1b8e45e0.akpm-de/[email protected]
Quoted 19 lines · Andrew Morton
Quoted 13 lines · On Thu, 17 Feb 2011 15:02:24 +0000 "Serge E. Hallyn"…
Here is a repost of my previous user namespace patch, ported onto last night's git head.
It fixes several things I was doing wrong in the last (v4) posting, in particular:
1. don't set
uts_ns->user_nsto current's when !CLONE_NEWUTS
2. add aipc_ns->user_nswhich ownsipc_ns, and use that to decideCAP_IPC_OWNER
3. fix logic flaw caused by bad parantheses
4. allowdo_prlimitto current
5. don't always give root full privs toinit_user_nsQuoted 2 lines
The expected course of development for user namespaces is laid out at https://wiki.ubuntu.com/UserNamespace.
Quoted 6 lines
Seems like a nice feature to be developing.
I worry about the maturity of it all at this stage. How far along is it *really*?
Is anyone else working with you on developing and reviewing this work?
I did a lot of the initial design and I have been reviewing as I have time.
Andrew at a practical level we have to merge this incrementally. Anything much bigger than Serge's current patchset will be too big to review. The first really bit step is making capabilities local to the user namespace, and that is what this patchset does along with using that localness in some good places.
Eric
Quoted 19 lines · Andrew Morton
Quoted 13 lines · On Thu, 17 Feb 2011 15:02:24 +0000 "Serge E. Hallyn"…
Here is a repost of my previous user namespace patch, ported onto last night's git head.
It fixes several things I was doing wrong in the last (v4) posting, in particular:
1. don't set
uts_ns->user_nsto current's when !CLONE_NEWUTS
2. add aipc_ns->user_nswhich ownsipc_ns, and use that to decideCAP_IPC_OWNER
3. fix logic flaw caused by bad parantheses
4. allowdo_prlimitto current
5. don't always give root full privs toinit_user_nsQuoted 2 lines
The expected course of development for user namespaces is laid out at https://wiki.ubuntu.com/UserNamespace.
Quoted 6 lines
Seems like a nice feature to be developing.
I worry about the maturity of it all at this stage. How far along is it *really*?
Is anyone else working with you on developing and reviewing this work?
Thanks, Andrew. I'm not sure what definition of 'maturity' you were looking for here. If you meant completeness of the feature, it's definately not there. Of the goals for user namespaces sandboxing will be the quickest to mature. Completing that will largely be an exercise of running the breadth of the kernel looking for simple uid/gid comparisons and making them namespace aware.
The design has been meshed around (publicly) on and off for many years by eric and I. This particular patchset has gotten some great reviews by Eric Biederman and Bastian Blank (to who, unfortunately, to this day I cannot send a direct email - they're always bounced).
As Eric said, this feature will have to go in incrementally. Furthermore, each piece touches scary code so it's likely to go pretty slowly. My hope is less than a year for sandboxing, and two years for containers. It might go way faster, but experience tells me that's unlikely :)
thanks,
-serge
Quoted 19 lines · Andrew Morton
Quoted 13 lines · On Thu, 17 Feb 2011 15:02:24 +0000 "Serge E. Hallyn" <[email protected]> wrote:
Here is a repost of my previous user namespace patch, ported onto last night's git head.
It fixes several things I was doing wrong in the last (v4) posting, in particular:
1. don't set
uts_ns->user_nsto current's when !CLONE_NEWUTS
2. add aipc_ns->user_nswhich ownsipc_ns, and use that to decideCAP_IPC_OWNER
3. fix logic flaw caused by bad parantheses
4. allowdo_prlimitto current
5. don't always give root full privs toinit_user_nsQuoted 2 lines
The expected course of development for user namespaces is laid out at https://wiki.ubuntu.com/UserNamespace.
Quoted 6 lines
Seems like a nice feature to be developing.
I worry about the maturity of it all at this stage. How far along is it *really*?
Is anyone else working with you on developing and reviewing this work?
I did a lot of the initial design and I have been reviewing as I have time.
Andrew at a practical level we have to merge this incrementally. Anything much bigger than Serge's current patchset will be too big to review. The first really bit step is making capabilities local to the user namespace, and that is what this patchset does along with using that localness in some good places.
Eric
Serge E. Hallyn
maintainerQuoted 19 lines · Andrew Morton
Quoted 13 lines · On Thu, 17 Feb 2011 15:02:24 +0000 "Serge E. Hallyn" <[email protected]> wrote:
Here is a repost of my previous user namespace patch, ported onto last night's git head.
It fixes several things I was doing wrong in the last (v4) posting, in particular:
1. don't set
uts_ns->user_nsto current's when !CLONE_NEWUTS
2. add aipc_ns->user_nswhich ownsipc_ns, and use that to decideCAP_IPC_OWNER
3. fix logic flaw caused by bad parantheses
4. allowdo_prlimitto current
5. don't always give root full privs toinit_user_nsQuoted 2 lines
The expected course of development for user namespaces is laid out at https://wiki.ubuntu.com/UserNamespace.
Quoted 6 lines
Seems like a nice feature to be developing.
I worry about the maturity of it all at this stage. How far along is it *really*?
Is anyone else working with you on developing and reviewing this work?
Thanks, Andrew. I'm not sure what definition of 'maturity' you were looking for here. If you meant completeness of the feature, it's definately not there. Of the goals for user namespaces sandboxing will be the quickest to mature. Completing that will largely be an exercise of running the breadth of the kernel looking for simple uid/gid comparisons and making them namespace aware.
The design has been meshed around (publicly) on and off for many years by eric and I. This particular patchset has gotten some great reviews by Eric Biederman and Bastian Blank (to who, unfortunately, to this day I cannot send a direct email - they're always bounced).
As Eric said, this feature will have to go in incrementally. Furthermore, each piece touches scary code so it's likely to go pretty slowly. My hope is less than a year for sandboxing, and two years for containers. It might go way faster, but experience tells me that's unlikely :)
thanks,
-serge
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 2/9] security: Make capabilities relative to the user namespace.
Quoted 7 lines · David Howells
Review
Quoted 3 lines
(quoted diff) +1
-1int (*capable) (struct task_struct *tsk, const struct cred *cred, - int cap, int audit); + struct user_namespace *ns, int cap, int audit);Hmmm... A chunk of the contents of the cred struct are user-namespaced. Could you add the
user_namespacepointer to the cred struct and thus avoid passing it as an argument to other things.
Ah, no... Ignore that, I think I see that you do need it.
Review
Quoted 8 lines
(quoted diff) +6
-1+int cap_capable(struct task_struct *tsk, const struct cred *cred, + struct user_namespace *targ_ns, int cap, int audit) { - return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; + for (;;) { + /* The creator of the user namespace has all caps. */ + if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) + return 0;
Why is that last comment so? Why should the creating namespace sport all possible capabilities? Do you have to have all capabilities available to you to be permitted create a new user namespace?
Also, would it be worth having a separate cap_ns_capable()? Wouldn't most calls to cap_capable() only be checking the caps granted in the current user namespace?
David
User namespaces and keys
In reply to Serge E. HallynI guess we need to look at how to mix keys and namespaces again.
Possibly the trickiest problem with keys is how to upcall key construction to /sbin/request-key when the keys may be of a different user namespace.
David
Serge E. Hallyn
maintainerRe: User namespaces and keys
Quoted 4 lines · David Howells
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only
by the user in your own user namespace.
We may want to look at this again, but for now I think that would be a safe enough default. Later, we'll probably want the user creating a child_user_ns to allow his keys to be inherited by the child user_ns. Though, as I type that, it seems to me that that'll just become a maintenance pain, and it's just plain safer to have the user re-enter his keys, sharing them over a file if needed.
I'm going to not consider the TPM at the moment :)
Quoted 2 lines
Possibly the trickiest problem with keys is how to upcall key construction to
/sbin/request-keywhen the keys may be of a different user namespace.
Hm, jinkeys, yes.
-serge
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: User namespaces and keys
Quoted 14 lines · Serge E. Hallyn
Quoted 4 lines · David Howells
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only
by the user in your own user namespace.
We may want to look at this again, but for now I think that would be a safe enough default. Later, we'll probably want the user creating a
child_user_nsto allow his keys to be inherited by the childuser_ns. Though, as I type that, it seems to me that that'll just become a maintenance pain, and it's just plain safer to have the user re-enter his keys, sharing them over a file if needed.
Quoted 6 lines
I'm going to not consider the TPM at the moment :)
Quoted 2 lines
Possibly the trickiest problem with keys is how to upcall key construction to
/sbin/request-keywhen the keys may be of a different user namespace.Hm, jinkeys, yes.
Serge short term this is where I think we need to add a check or two so that keys only work in the init_user_ns. When the rest of the details are sorted out we can open up the use of keys some more.
As the first stage of converting the network stack we added patches
that turned off everything in non init namespaces. Those patches
were trivial and easy to review, and it made the conversion process a lot easier. I suspect for keys and possibly security modules and anything else that does is related we want to turn off by default.
Then once the core of user namespaces is safe to unshare without privilege we can come back and get the more difficult bits.
Eric
Re: User namespaces and keys
In reply to a message not in the archiveQuoted 4 lines · Serge E. Hallyn
Quoted 1 line
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only by the user in your own user namespace.
I'm not sure that's currently completely true. Key quota maintenance is namespaced, and the key's owner UID/GID belong to that namespace, so that's okay, but:
(*) key_task_permission() does not distinguish UIDs and GIDs from different
namespaces.
(*) A key can be referred to by its serial number, no matter whose namespace
it is in, and will yield up its given UID/GID, even if these aren't actually meaningful in your namespace.
This means request_key() can successfully upcall at the moment.
I wonder if I should make the following changes:
(1) If the key and the accessor are in different user namespaces, then skip the UID and GID comparisons in key_task_permission(). That means that to be able to access the key you'd have to possess the key and the key would have to grant you Possessor access, or the key would have to grant you Other access.
(2) If the key and someone viewing the key description are in different namespaces, then indicate that the UID and the GID are -1, irrespective of the actual values.
(3) When an upcall is attempting to instantiate a key, it is allowed to access the keys of requestor using the requestor's credentials (UID, GID, groups, security label). Ensure that this will be done in the requestor's user namespace.
Nothing should need to be done here, since search_process_keyrings() switches to the requestor's creds.
Oh, and are security labels user-namespaced?
Quoted 3 lines
We may want to look at this again, but for now I think that would be a safe enough default. Later, we'll probably want the user creating a
child_user_nsto allow his keys to be inherited by the childuser_ns.
That depends what you mean by 'allow his keys to be inherited'. Do you mean copying all the creator's keys en mass? You may find all you need to do is to provide the new intended user with a new session keyring with a link back to the creator's session keyring.
Quoted 3 lines
Though, as I type that, it seems to me that that'll just become a maintenance pain, and it's just plain safer to have the user re-enter his keys,
That would certainly be simpler.
Quoted 1 line
sharing them over a file if needed.
I'm not sure what you mean by that. Do you mean some sort of cred passing similar to that that can be done over AF_UNIX sockets with fds?
Quoted 1 line
I'm going to not consider the TPM at the moment :)
I'm not sure the TPM is that much of a problem, assuming you're just referring to its keystore capability...
David
Re: User namespaces and keys
In reply to Serge E. HallynQuoted 14 lines · Serge E. Hallyn
Quoted 4 lines · David Howells
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only
by the user in your own user namespace.
We may want to look at this again, but for now I think that would be a safe enough default. Later, we'll probably want the user creating a
child_user_nsto allow his keys to be inherited by the childuser_ns. Though, as I type that, it seems to me that that'll just become a maintenance pain, and it's just plain safer to have the user re-enter his keys, sharing them over a file if needed.
Quoted 6 lines
I'm going to not consider the TPM at the moment :)
Quoted 2 lines
Possibly the trickiest problem with keys is how to upcall key construction to
/sbin/request-keywhen the keys may be of a different user namespace.Hm, jinkeys, yes.
Serge short term this is where I think we need to add a check or two so that keys only work in the init_user_ns. When the rest of the details are sorted out we can open up the use of keys some more.
As the first stage of converting the network stack we added patches
that turned off everything in non init namespaces. Those patches
were trivial and easy to review, and it made the conversion process a lot easier. I suspect for keys and possibly security modules and anything else that does is related we want to turn off by default.
Then once the core of user namespaces is safe to unshare without privilege we can come back and get the more difficult bits.
Eric
Re: User namespaces and keys
In reply to Serge E. HallynQuoted 4 lines · Serge E. Hallyn
Quoted 1 line
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only by the user in your own user namespace.
I'm not sure that's currently completely true. Key quota maintenance is namespaced, and the key's owner UID/GID belong to that namespace, so that's okay, but:
(*) key_task_permission() does not distinguish UIDs and GIDs from different
namespaces.
(*) A key can be referred to by its serial number, no matter whose namespace
it is in, and will yield up its given UID/GID, even if these aren't actually meaningful in your namespace.
This means request_key() can successfully upcall at the moment.
I wonder if I should make the following changes:
(1) If the key and the accessor are in different user namespaces, then skip the UID and GID comparisons in key_task_permission(). That means that to be able to access the key you'd have to possess the key and the key would have to grant you Possessor access, or the key would have to grant you Other access.
(2) If the key and someone viewing the key description are in different namespaces, then indicate that the UID and the GID are -1, irrespective of the actual values.
(3) When an upcall is attempting to instantiate a key, it is allowed to access the keys of requestor using the requestor's credentials (UID, GID, groups, security label). Ensure that this will be done in the requestor's user namespace.
Nothing should need to be done here, since search_process_keyrings() switches to the requestor's creds.
Oh, and are security labels user-namespaced?
Quoted 3 lines
We may want to look at this again, but for now I think that would be a safe enough default. Later, we'll probably want the user creating a
child_user_nsto allow his keys to be inherited by the childuser_ns.
That depends what you mean by 'allow his keys to be inherited'. Do you mean copying all the creator's keys en mass? You may find all you need to do is to provide the new intended user with a new session keyring with a link back to the creator's session keyring.
Quoted 3 lines
Though, as I type that, it seems to me that that'll just become a maintenance pain, and it's just plain safer to have the user re-enter his keys,
That would certainly be simpler.
Quoted 1 line
sharing them over a file if needed.
I'm not sure what you mean by that. Do you mean some sort of cred passing similar to that that can be done over AF_UNIX sockets with fds?
Quoted 1 line
I'm going to not consider the TPM at the moment :)
I'm not sure the TPM is that much of a problem, assuming you're just referring to its keystore capability...
David
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: User namespaces and keys
Quoted 37 lines · David Howells
Quoted 4 lines · Serge E. Hallyn
Quoted 1 line
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only by the user in your own user namespace.
I'm not sure that's currently completely true. Key quota maintenance is namespaced, and the key's owner UID/GID belong to that namespace, so that's okay, but:
(*)
key_task_permission()does not distinguish UIDs and GIDs from different
namespaces.(*) A key can be referred to by its serial number, no matter whose namespace
it is in, and will yield up its given UID/GID, even if these aren't actually meaningful in your namespace.This means
request_key()can successfully upcall at the moment.I wonder if I should make the following changes:
(1) If the key and the accessor are in different user namespaces, then skip the UID and GID comparisons in
key_task_permission(). That means that to be able to access the key you'd have to possess the key and the key would have to grant you Possessor access, or the key would have to grant you Other access.(2) If the key and someone viewing the key description are in different namespaces, then indicate that the UID and the GID are -1, irrespective of the actual values.
(3) When an upcall is attempting to instantiate a key, it is allowed to access the keys of requestor using the requestor's credentials (UID, GID, groups, security label). Ensure that this will be done in the requestor's user namespace.
Quoted 4 lines
Nothing should need to be done here, since
search_process_keyrings()switches to the requestor's creds.Oh, and are security labels user-namespaced?
Not at this time. The user namespace as currently merged is little more than a place holder for a proper implementation. Serge is busily fleshing out that proper implementation.
Until we reach the point where all checks that have historically been "if (uid1 == uid2)" become "if ((uidns1 == uidns2) && (uid1 == uid2))" there will be problems.
The security labels and probably lsm's in general need to be per user namespace but we simply have not gotten that far. For the short term I will be happy when we get a minimally usable user namespace.
Eric
Re: User namespaces and keys
In reply to David HowellsQuoted 37 lines · David Howells
Quoted 4 lines · Serge E. Hallyn
Quoted 1 line
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only by the user in your own user namespace.
I'm not sure that's currently completely true. Key quota maintenance is namespaced, and the key's owner UID/GID belong to that namespace, so that's okay, but:
(*)
key_task_permission()does not distinguish UIDs and GIDs from different
namespaces.(*) A key can be referred to by its serial number, no matter whose namespace
it is in, and will yield up its given UID/GID, even if these aren't actually meaningful in your namespace.This means
request_key()can successfully upcall at the moment.I wonder if I should make the following changes:
(1) If the key and the accessor are in different user namespaces, then skip the UID and GID comparisons in
key_task_permission(). That means that to be able to access the key you'd have to possess the key and the key would have to grant you Possessor access, or the key would have to grant you Other access.(2) If the key and someone viewing the key description are in different namespaces, then indicate that the UID and the GID are -1, irrespective of the actual values.
(3) When an upcall is attempting to instantiate a key, it is allowed to access the keys of requestor using the requestor's credentials (UID, GID, groups, security label). Ensure that this will be done in the requestor's user namespace.
Quoted 4 lines
Nothing should need to be done here, since
search_process_keyrings()switches to the requestor's creds.Oh, and are security labels user-namespaced?
Not at this time. The user namespace as currently merged is little more than a place holder for a proper implementation. Serge is busily fleshing out that proper implementation.
Until we reach the point where all checks that have historically been "if (uid1 == uid2)" become "if ((uidns1 == uidns2) && (uid1 == uid2))" there will be problems.
The security labels and probably lsm's in general need to be per user namespace but we simply have not gotten that far. For the short term I will be happy when we get a minimally usable user namespace.
Eric
A message that is not in the archive (sent off-list or not yet received): m162sasqj6.fsf-+imSwln9KH6u2/kzUuoCbdi2O/[email protected]
Re: User namespaces and keys
Quoted 48 lines · Eric W. Biederman
Quoted 37 lines · David Howells
Quoted 4 lines · Serge E. Hallyn
Quoted 1 line
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only by the user in your own user namespace.
I'm not sure that's currently completely true. Key quota maintenance is namespaced, and the key's owner UID/GID belong to that namespace, so that's okay, but:
(*)
key_task_permission()does not distinguish UIDs and GIDs from different
namespaces.(*) A key can be referred to by its serial number, no matter whose namespace
it is in, and will yield up its given UID/GID, even if these aren't actually meaningful in your namespace.This means
request_key()can successfully upcall at the moment.I wonder if I should make the following changes:
(1) If the key and the accessor are in different user namespaces, then skip the UID and GID comparisons in
key_task_permission(). That means that to be able to access the key you'd have to possess the key and the key would have to grant you Possessor access, or the key would have to grant you Other access.(2) If the key and someone viewing the key description are in different namespaces, then indicate that the UID and the GID are -1, irrespective of the actual values.
(3) When an upcall is attempting to instantiate a key, it is allowed to access the keys of requestor using the requestor's credentials (UID, GID, groups, security label). Ensure that this will be done in the requestor's user namespace.
Quoted 4 lines
Nothing should need to be done here, since
search_process_keyrings()switches to the requestor's creds.Oh, and are security labels user-namespaced?
Not at this time. The user namespace as currently merged is little more than a place holder for a proper implementation. Serge is busily fleshing out that proper implementation.
Quoted 7 lines
Until we reach the point where all checks that have historically been "if (uid1 == uid2)" become "if ((uidns1 == uidns2) && (uid1 == uid2))" there will be problems.
The security labels and probably lsm's in general need to be per user namespace but we simply have not gotten that far. For the short term I will be happy when we get a minimally usable user namespace.
Note also that when Eric brought this up at the LSM mini-conf two or three years ago, there was pretty general, strong objection to the idea.
Like Eric says, I think that'll have to wait. In the meantime, isolating user namespace sandboxes (or containers) using simple LSM configurations is a very good idea.
-serge
Re: User namespaces and keys
In reply to Eric W. BiedermanQuoted 48 lines · Eric W. Biederman
Quoted 37 lines · David Howells
Quoted 4 lines · Serge E. Hallyn
Quoted 1 line
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only by the user in your own user namespace.
I'm not sure that's currently completely true. Key quota maintenance is namespaced, and the key's owner UID/GID belong to that namespace, so that's okay, but:
(*)
key_task_permission()does not distinguish UIDs and GIDs from different
namespaces.(*) A key can be referred to by its serial number, no matter whose namespace
it is in, and will yield up its given UID/GID, even if these aren't actually meaningful in your namespace.This means
request_key()can successfully upcall at the moment.I wonder if I should make the following changes:
(1) If the key and the accessor are in different user namespaces, then skip the UID and GID comparisons in
key_task_permission(). That means that to be able to access the key you'd have to possess the key and the key would have to grant you Possessor access, or the key would have to grant you Other access.(2) If the key and someone viewing the key description are in different namespaces, then indicate that the UID and the GID are -1, irrespective of the actual values.
(3) When an upcall is attempting to instantiate a key, it is allowed to access the keys of requestor using the requestor's credentials (UID, GID, groups, security label). Ensure that this will be done in the requestor's user namespace.
Quoted 4 lines
Nothing should need to be done here, since
search_process_keyrings()switches to the requestor's creds.Oh, and are security labels user-namespaced?
Not at this time. The user namespace as currently merged is little more than a place holder for a proper implementation. Serge is busily fleshing out that proper implementation.
Quoted 7 lines
Until we reach the point where all checks that have historically been "if (uid1 == uid2)" become "if ((uidns1 == uidns2) && (uid1 == uid2))" there will be problems.
The security labels and probably lsm's in general need to be per user namespace but we simply have not gotten that far. For the short term I will be happy when we get a minimally usable user namespace.
Note also that when Eric brought this up at the LSM mini-conf two or three years ago, there was pretty general, strong objection to the idea.
Like Eric says, I think that'll have to wait. In the meantime, isolating user namespace sandboxes (or containers) using simple LSM configurations is a very good idea.
-serge
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: User namespaces and keys
Quoted 53 lines · Serge E. Hallyn
Quoted 45 lines · Eric W. Biederman
Quoted 35 lines · David Howells
Quoted 3 lines · Serge E. Hallyn
Quoted 1 line
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only by the user in your own user namespace.
I'm not sure that's currently completely true. Key quota maintenance is namespaced, and the key's owner UID/GID belong to that namespace, so that's okay, but:
(*)
key_task_permission()does not distinguish UIDs and GIDs from different
namespaces.(*) A key can be referred to by its serial number, no matter whose namespace
it is in, and will yield up its given UID/GID, even if these aren't actually meaningful in your namespace.This means
request_key()can successfully upcall at the moment.I wonder if I should make the following changes:
(1) If the key and the accessor are in different user namespaces, then skip the UID and GID comparisons in
key_task_permission(). That means that to be able to access the key you'd have to possess the key and the key would have to grant you Possessor access, or the key would have to grant you Other access.(2) If the key and someone viewing the key description are in different namespaces, then indicate that the UID and the GID are -1, irrespective of the actual values.
(3) When an upcall is attempting to instantiate a key, it is allowed to access the keys of requestor using the requestor's credentials (UID, GID, groups, security label). Ensure that this will be done in the requestor's user namespace.
Quoted 4 lines
Nothing should need to be done here, since
search_process_keyrings()switches to the requestor's creds.Oh, and are security labels user-namespaced?
Not at this time. The user namespace as currently merged is little more than a place holder for a proper implementation. Serge is busily fleshing out that proper implementation.
Quoted 7 lines
Until we reach the point where all checks that have historically been "if (uid1 == uid2)" become "if ((uidns1 == uidns2) && (uid1 == uid2))" there will be problems.
The security labels and probably lsm's in general need to be per user namespace but we simply have not gotten that far. For the short term I will be happy when we get a minimally usable user namespace.
Quoted 6 lines
Note also that when Eric brought this up at the LSM mini-conf two or three years ago, there was pretty general, strong objection to the idea.
Like Eric says, I think that'll have to wait. In the meantime, isolating user namespace sandboxes (or containers) using simple LSM configurations is a very good idea.
I confess that I remain less well educated on namespaces than I probably should be, but with what I do know it seems that the relationships between user namespaces and LSMs are bound to be strained from the beginning. Some LSMs (SELinux and Smack) are providing similar sandbox capabilities to what you get from user namespaces, but from different directions and with different use cases.
Quoted 8 lines
-serge
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Re: User namespaces and keys
In reply to Serge E. HallynQuoted 53 lines · Serge E. Hallyn
Quoted 45 lines · Eric W. Biederman
Quoted 35 lines · David Howells
Quoted 3 lines · Serge E. Hallyn
Quoted 1 line
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only by the user in your own user namespace.
I'm not sure that's currently completely true. Key quota maintenance is namespaced, and the key's owner UID/GID belong to that namespace, so that's okay, but:
(*)
key_task_permission()does not distinguish UIDs and GIDs from different
namespaces.(*) A key can be referred to by its serial number, no matter whose namespace
it is in, and will yield up its given UID/GID, even if these aren't actually meaningful in your namespace.This means
request_key()can successfully upcall at the moment.I wonder if I should make the following changes:
(1) If the key and the accessor are in different user namespaces, then skip the UID and GID comparisons in
key_task_permission(). That means that to be able to access the key you'd have to possess the key and the key would have to grant you Possessor access, or the key would have to grant you Other access.(2) If the key and someone viewing the key description are in different namespaces, then indicate that the UID and the GID are -1, irrespective of the actual values.
(3) When an upcall is attempting to instantiate a key, it is allowed to access the keys of requestor using the requestor's credentials (UID, GID, groups, security label). Ensure that this will be done in the requestor's user namespace.
Quoted 4 lines
Nothing should need to be done here, since
search_process_keyrings()switches to the requestor's creds.Oh, and are security labels user-namespaced?
Not at this time. The user namespace as currently merged is little more than a place holder for a proper implementation. Serge is busily fleshing out that proper implementation.
Quoted 7 lines
Until we reach the point where all checks that have historically been "if (uid1 == uid2)" become "if ((uidns1 == uidns2) && (uid1 == uid2))" there will be problems.
The security labels and probably lsm's in general need to be per user namespace but we simply have not gotten that far. For the short term I will be happy when we get a minimally usable user namespace.
Quoted 6 lines
Note also that when Eric brought this up at the LSM mini-conf two or three years ago, there was pretty general, strong objection to the idea.
Like Eric says, I think that'll have to wait. In the meantime, isolating user namespace sandboxes (or containers) using simple LSM configurations is a very good idea.
I confess that I remain less well educated on namespaces than I probably should be, but with what I do know it seems that the relationships between user namespaces and LSMs are bound to be strained from the beginning. Some LSMs (SELinux and Smack) are providing similar sandbox capabilities to what you get from user namespaces, but from different directions and with different use cases.
Quoted 8 lines
-serge
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: User namespaces and keys
Quoted 7 lines · Casey Schaufler
I confess that I remain less well educated on namespaces than I probably should be, but with what I do know it seems that the relationships between user namespaces and LSMs are bound to be strained from the beginning. Some LSMs (SELinux and Smack) are providing similar sandbox capabilities to what you get from user namespaces, but from different directions and with different use cases.
Casey I won't argue about the possibility of things being strained, but I think if we focus on the semantics and not on the end goal of exactly how the pieces are to be used there can be some reasonable dialog.
From 10,000 feet an user namespace represents one administrative domain. uids, gids and other external tokens are all controlled by a single group with a single security policy. In that single administrative domain things like nfs are expected to work without translating uids and gids. Before the implementation of user namespaces a single kernel could not even express the ability of dealing with multiple user namespaces simultaneously (nfs has usually punted and said despite being multiple machines you still have to be in the same administrative domain so the same user identifiers can be used throughout).
From that principle we have the concept that use assigned/visible identifiers uid, gid, capabilities, security keys?, security labels? logically belong to the user namespace.
Which means in implementing there are two pieces of work in implementing the user namespace.
- Find all of the interesting comparisons and change them from "if (id == id)" to "if (userns == userns) && (id == id)".
- Potentially define and handle what happens when you mix user namespaces.
I think for the first round of this we simply want to define lsms and the user namespace as not mixing or the lsm interfaces only being visible if you are in the initial user namespace. Thus reserving the definition of what happens when you have lsms and multiple user namespaces as something we can define later. I expect the proper definition is something that would allow nested lsm policy.
Regardless. The namespaces are all about making the identifiers that processes deal with local to the namespace, while the underlying object manipulations should not care.
The big advantage of the user namespace is that it is the only way I can see to get us out of the bind we are in with suid root executables, where we can not enable new features to untrusted applications that can change the environment for a suid root executable, because it might confuse those suid root executables to misusing their power. Once inside a user namespace nothing has dangerous powers because even a root owned process with a full set of capabilities is less powerful than
a guest user outside of the namespace. No process having dangerous
powers allows us to enable unsharing of other namespaces and potentially other things that today are restricted with capabilities only because they can be used to confuse a privileged executable to do something malicious.
Eric
Re: User namespaces and keys
In reply to Casey SchauflerQuoted 7 lines · Casey Schaufler
I confess that I remain less well educated on namespaces than I probably should be, but with what I do know it seems that the relationships between user namespaces and LSMs are bound to be strained from the beginning. Some LSMs (SELinux and Smack) are providing similar sandbox capabilities to what you get from user namespaces, but from different directions and with different use cases.
Casey I won't argue about the possibility of things being strained, but I think if we focus on the semantics and not on the end goal of exactly how the pieces are to be used there can be some reasonable dialog.
Quoted 1 line
From 10,000 feet an user namespace represents one administrative domain.
uids, gids and other external tokens are all controlled by a single group with a single security policy. In that single administrative domain things like nfs are expected to work without translating uids and gids. Before the implementation of user namespaces a single kernel could not even express the ability of dealing with multiple user namespaces simultaneously (nfs has usually punted and said despite being multiple machines you still have to be in the same administrative domain so the same user identifiers can be used throughout).
Quoted 1 line
From that principle we have the concept that use assigned/visible
identifiers uid, gid, capabilities, security keys?, security labels? logically belong to the user namespace.
Which means in implementing there are two pieces of work in implementing the user namespace.
- Find all of the interesting comparisons and change them from "if (id == id)" to "if (userns == userns) && (id == id)".
- Potentially define and handle what happens when you mix user namespaces.
I think for the first round of this we simply want to define lsms and the user namespace as not mixing or the lsm interfaces only being visible if you are in the initial user namespace. Thus reserving the definition of what happens when you have lsms and multiple user namespaces as something we can define later. I expect the proper definition is something that would allow nested lsm policy.
Regardless. The namespaces are all about making the identifiers that processes deal with local to the namespace, while the underlying object manipulations should not care.
The big advantage of the user namespace is that it is the only way I can see to get us out of the bind we are in with suid root executables, where we can not enable new features to untrusted applications that can change the environment for a suid root executable, because it might confuse those suid root executables to misusing their power. Once inside a user namespace nothing has dangerous powers because even a root owned process with a full set of capabilities is less powerful than
a guest user outside of the namespace. No process having dangerous
powers allows us to enable unsharing of other namespaces and potentially other things that today are restricted with capabilities only because they can be used to confuse a privileged executable to do something malicious.
Eric
A message that is not in the archive (sent off-list or not yet received): m1k4gqlbdm.fsf-+imSwln9KH6u2/kzUuoCbdi2O/[email protected]
Re: User namespaces and keys
Quoted 9 lines · Eric W. Biederman
Quoted 7 lines · Casey Schaufler
I confess that I remain less well educated on namespaces than I probably should be, but with what I do know it seems that the relationships between user namespaces and LSMs are bound to be strained from the beginning. Some LSMs (SELinux and Smack) are providing similar sandbox capabilities to what you get from user namespaces, but from different directions and with different use cases.
Quoted 3 lines
Casey I won't argue about the possibility of things being strained, but I think if we focus on the semantics and not on the end goal of exactly how the pieces are to be used there can be some reasonable dialog.
I'm sure that there will be cases where they will work together like horses in a troika. Making sensible semantics for the interactions is key, and it is entirely possible that in some cases a comparison of semantics and behaviors will lead an end user to chose either an LSM or namespaces over the combination. Just like I expect that even when we allow multiple LSMs the SELinux and Smack combination will be rare among the sane.
Re: User namespaces and keys
In reply to Eric W. BiedermanQuoted 9 lines · Eric W. Biederman
Quoted 7 lines · Casey Schaufler
I confess that I remain less well educated on namespaces than I probably should be, but with what I do know it seems that the relationships between user namespaces and LSMs are bound to be strained from the beginning. Some LSMs (SELinux and Smack) are providing similar sandbox capabilities to what you get from user namespaces, but from different directions and with different use cases.
Quoted 3 lines
Casey I won't argue about the possibility of things being strained, but I think if we focus on the semantics and not on the end goal of exactly how the pieces are to be used there can be some reasonable dialog.
I'm sure that there will be cases where they will work together like horses in a troika. Making sensible semantics for the interactions is key, and it is entirely possible that in some cases a comparison of semantics and behaviors will lead an end user to chose either an LSM or namespaces over the combination. Just like I expect that even when we allow multiple LSMs the SELinux and Smack combination will be rare among the sane.
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: User namespaces and keys
Quoted 14 lines · Casey Schaufler
Quoted 9 lines · Eric W. Biederman
Quoted 7 lines · Casey Schaufler
I confess that I remain less well educated on namespaces than I probably should be, but with what I do know it seems that the relationships between user namespaces and LSMs are bound to be strained from the beginning. Some LSMs (SELinux and Smack) are providing similar sandbox capabilities to what you get from user namespaces, but from different directions and with different use cases.
Quoted 3 lines
Casey I won't argue about the possibility of things being strained, but I think if we focus on the semantics and not on the end goal of exactly how the pieces are to be used there can be some reasonable dialog.
Quoted 7 lines
I'm sure that there will be cases where they will work together like horses in a troika. Making sensible semantics for the interactions is key, and it is entirely possible that in some cases a comparison of semantics and behaviors will lead an end user to chose either an LSM or namespaces over the combination. Just like I expect that even when we allow multiple LSMs the SELinux and Smack combination will be rare among the sane.
That sounds about right.
Eric
Re: User namespaces and keys
In reply to Casey SchauflerQuoted 14 lines · Casey Schaufler
Quoted 9 lines · Eric W. Biederman
Quoted 7 lines · Casey Schaufler
I confess that I remain less well educated on namespaces than I probably should be, but with what I do know it seems that the relationships between user namespaces and LSMs are bound to be strained from the beginning. Some LSMs (SELinux and Smack) are providing similar sandbox capabilities to what you get from user namespaces, but from different directions and with different use cases.
Quoted 3 lines
Casey I won't argue about the possibility of things being strained, but I think if we focus on the semantics and not on the end goal of exactly how the pieces are to be used there can be some reasonable dialog.
Quoted 7 lines
I'm sure that there will be cases where they will work together like horses in a troika. Making sensible semantics for the interactions is key, and it is entirely possible that in some cases a comparison of semantics and behaviors will lead an end user to chose either an LSM or namespaces over the combination. Just like I expect that even when we allow multiple LSMs the SELinux and Smack combination will be rare among the sane.
That sounds about right.
Eric
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: User namespaces and keys
Quoted 2 lines · David Howells
I guess we need to look at how to mix keys and namespaces again.
From strictly kernel pov, at the moment, keys are strictly usable only by the user in your own user namespace.
We may want to look at this again, but for now I think that would be a safe enough default. Later, we'll probably want the user creating a child_user_ns to allow his keys to be inherited by the child user_ns. Though, as I type that, it seems to me that that'll just become a maintenance pain, and it's just plain safer to have the user re-enter his keys, sharing them over a file if needed.
I'm going to not consider the TPM at the moment :)
Quoted 2 lines
Possibly the trickiest problem with keys is how to upcall key construction to
/sbin/request-keywhen the keys may be of a different user namespace.
Hm, jinkeys, yes.
-serge