@@ -6,6 +6,7 @@
struct mnt_namespace {
atomic_t count;
+ int flags;
struct ns_common ns;
struct mount * root;
struct list_head list;
@@ -2774,6 +2774,7 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)
INIT_LIST_HEAD(&new_ns->list);
init_waitqueue_head(&new_ns->poll);
new_ns->event = 0;
+ new_ns->flags = 0;
new_ns->user_ns = get_user_ns(user_ns);
return new_ns;
}
@@ -2801,6 +2802,25 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
if (IS_ERR(new_ns))
return new_ns;
+ if (flags & CLONE_MNTNS_SHIFT_UIDGID) {
+ /*
+ * If parent has the CLONE_MNTNS_SHIFT_UIDGID flag set
+ * or current is capable in init_user_ns, then we set the
+ * CLONE_MNTNS_SHIFT_UIDGID flag and allow mounts inside
+ * this namespace to shift their UID and GID.
+ *
+ * We check the init_user_ns here since we always start from
+ * that user namespace and mounts are by default available to all
+ * users. In this regard, only CAP_SYS_ADMIN in init_user_ns is
+ * allowed to start and propagate the CLONE_MNTNS_SHIFT_UIDGID
+ * flag to new mount namespaces.
+ */
+ if ((ns->flags & CLONE_MNTNS_SHIFT_UIDGID) || capable(CAP_SYS_ADMIN))
+ new_ns->flags |= CLONE_MNTNS_SHIFT_UIDGID;
+ else
+ return ERR_PTR(-EPERM);
+ }
+
namespace_lock();
/* First pass: copy the tree topology */
copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE;
@@ -19,6 +19,7 @@
#define CLONE_PARENT_SETTID 0x00100000 /* set the TID in the parent */
#define CLONE_CHILD_CLEARTID 0x00200000 /* clear the TID in the child */
#define CLONE_DETACHED 0x00400000 /* Unused, ignored */
+#define CLONE_MNTNS_SHIFT_UIDGID 0x00400000 /* If set allows to shift UID and GID for mounts that support it */
#define CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force CLONE_PTRACE on this clone */
#define CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */
#define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */
@@ -1264,6 +1264,10 @@ static struct task_struct *copy_process(unsigned long clone_flags,
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);
+ if ((clone_flags & CLONE_MNTNS_SHIFT_UIDGID) &&
+ !(clone_flags & CLONE_NEWNS))
+ return ERR_PTR(-EINVAL);
+
if ((clone_flags & (CLONE_NEWUSER|CLONE_FS)) == (CLONE_NEWUSER|CLONE_FS))
return ERR_PTR(-EINVAL);