linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Add supplementary UIDs, and getusers/setusers system calls
@ 2014-11-16  7:08 Josh Triplett
  2014-11-16  7:08 ` [PATCH manpages] Document supplementary user IDs Josh Triplett
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Josh Triplett @ 2014-11-16  7:08 UTC (permalink / raw)
  To: Andrew Morton, Theodore Ts'o, Kees Cook, mtk.manpages,
	linux-api, linux-man, linux-kernel

Analogous to the supplementary GID list, the supplementary UID list
provides a set of additional user credentials that a process can act as.
A process with CAP_SETUID can set its UID list arbitrarily; a process
without CAP_SETUID can only reduce its UID list.

This allows each user to have a set of UIDs that they can then use to
further sandbox individual child processes without first escalating to
root to change UIDs.  For instance, a PAM module could give each user a
block of UIDs to work with.

Tested via the following test program:

#include <err.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>

static int getusers(int count, uid_t *uids)
{
    return syscall(322, count, uids);
}

static int setusers(int count, const uid_t *uids)
{
    return syscall(323, count, uids);
}

static void show_users(void)
{
    uid_t uids[65536];
    int i, count = getusers(65536, uids);
    if (count < 0)
        err(1, "getusers");
    printf("UIDs:");
    for (i = 0; i < count; i++)
        printf(" %u", (unsigned)uids[i]);
    printf("\n");
}

int main(void)
{
    uid_t list1[] = { 1, 2, 3, 4, 5 };
    uid_t list2[] = { 1, 2, 3, 4 };
    uid_t list3[] = { 2, 3, 4 };
    show_users();
    if (setusers(5, list1) < 0)
        err(1, "setusers 1");
    show_users();
    if (setresgid(1, 1, 1) < 0)
        err(1, "setresgid");
    if (setresuid(1, 1, 1) < 0)
        err(1, "setresuid");
    if (setusers(4, list2) < 0)
        err(1, "setusers 2");
    show_users();
    if (setusers(3, list3) < 0)
        err(1, "setusers 3");
    show_users();
    if (setusers(4, list2) < 0)
        err(1, "setusers 4");
    show_users();
    if (setresuid(2, 2, 2) < 0)
        err(1, "setresuid 2");
    if (setusers(5, list1) < 0)
        err(1, "setusers 5");
    show_users();

    return 0;
}

In this test, all but the last call to setusers succeeds; the last call
fails with EPERM because the unprivileged process attempts to add UID 5
to the supplementary UID list, which it does not currently have.

Signed-off-by: Josh Triplett <josh@joshtriplett.org>
---
 arch/x86/syscalls/syscall_32.tbl  |   2 +
 arch/x86/syscalls/syscall_64.tbl  |   2 +
 include/linux/cred.h              |  66 +++++++++++++++
 include/linux/syscalls.h          |   2 +
 include/uapi/asm-generic/unistd.h |   6 +-
 include/uapi/linux/limits.h       |   1 +
 init/Kconfig                      |   9 ++
 kernel/cred.c                     |   4 +
 kernel/groups.c                   | 173 ++++++++++++++++++++++++++++++++++++++
 kernel/sys.c                      |  21 +++--
 kernel/sys_ni.c                   |   2 +
 11 files changed, 280 insertions(+), 8 deletions(-)

diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl
index 9fe1b5d..55717d7 100644
--- a/arch/x86/syscalls/syscall_32.tbl
+++ b/arch/x86/syscalls/syscall_32.tbl
@@ -364,3 +364,5 @@
 355	i386	getrandom		sys_getrandom
 356	i386	memfd_create		sys_memfd_create
 357	i386	bpf			sys_bpf
+358	i386	getusers		sys_getusers
+359	i386	setusers		sys_setusers
diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl
index 281150b..5572e67 100644
--- a/arch/x86/syscalls/syscall_64.tbl
+++ b/arch/x86/syscalls/syscall_64.tbl
@@ -328,6 +328,8 @@
 319	common	memfd_create		sys_memfd_create
 320	common	kexec_file_load		sys_kexec_file_load
 321	common	bpf			sys_bpf
+322	common	getusers		sys_getusers
+323	common	setusers		sys_setusers
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/include/linux/cred.h b/include/linux/cred.h
index b2d0820..31169fe 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -76,6 +76,8 @@ extern int groups_search(const struct group_info *, kgid_t);
 extern int in_group_p(kgid_t);
 extern int in_egroup_p(kgid_t);
 
+struct user_info;
+
 /*
  * The security context of a task
  *
@@ -135,6 +137,12 @@ struct cred {
 	struct user_struct *user;	/* real user ID subscription */
 	struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
 	struct group_info *group_info;	/* supplementary groups for euid/fsgid */
+#ifdef CONFIG_SUPPLEMENTARY_UIDS
+	struct user_info *user_info;	/* supplementary users */
+#define INIT_USER_INFO .user_info = &init_users,
+#else
+#define INIT_USER_INFO
+#endif
 	struct rcu_head	rcu;		/* RCU deletion hook */
 };
 
@@ -381,4 +389,62 @@ do {						\
 	*(_fsgid) = __cred->fsgid;		\
 } while(0)
 
+#ifdef CONFIG_SUPPLEMENTARY_UIDS
+struct user_info {
+	atomic_t	usage;
+	int		nusers;
+	int		nblocks;
+	kuid_t		small_block[NGROUPS_SMALL];
+	kuid_t		*blocks[0];
+};
+
+#define USER_AT(ui, i) GROUP_AT(ui, i)
+extern struct user_info init_users;
+void users_free(struct user_info *);
+bool has_supplementary_uid(kuid_t);
+
+/**
+ * get_user_info - Get a reference to a user_info structure
+ * @user_info: The user_info to reference
+ *
+ * This gets a reference to a set of supplementary users.
+ *
+ * If the caller is accessing a task's credentials, they must hold the RCU read
+ * lock when reading.
+ */
+static inline struct user_info *get_user_info(struct user_info *ui)
+{
+	atomic_inc(&ui->usage);
+	return ui;
+}
+
+static inline void get_cred_user_info(struct cred *cred)
+{
+	get_user_info(cred->user_info);
+}
+
+/**
+ * put_user_info - Release a reference to a user_info structure
+ * @user_info: The user_info to release
+ */
+static inline void put_user_info(struct user_info *ui)
+{
+	if (atomic_dec_and_test(&ui->usage))
+		users_free(ui);
+}
+
+static inline void put_cred_user_info(struct cred *cred)
+{
+	if (cred->user_info)
+		put_user_info(cred->user_info);
+}
+#else /* CONFIG_SUPPLEMENTARY_UIDS */
+static inline bool has_supplementary_uid(kuid_t uid)
+{
+	return false;
+}
+static inline void get_cred_user_info(struct cred *cred) {}
+static inline void put_cred_user_info(struct cred *cred) {}
+#endif
+
 #endif /* _LINUX_CRED_H */
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index bda9b81..3bde665 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -232,6 +232,7 @@ asmlinkage long sys_getpgid(pid_t pid);
 asmlinkage long sys_getpgrp(void);
 asmlinkage long sys_getsid(pid_t pid);
 asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist);
+asmlinkage long sys_getusers(int uidsetsize, uid_t __user *userlist);
 
 asmlinkage long sys_setregid(gid_t rgid, gid_t egid);
 asmlinkage long sys_setgid(gid_t gid);
@@ -244,6 +245,7 @@ asmlinkage long sys_setfsgid(gid_t gid);
 asmlinkage long sys_setpgid(pid_t pid, pid_t pgid);
 asmlinkage long sys_setsid(void);
 asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist);
+asmlinkage long sys_setusers(int uidsetsize, uid_t __user *grouplist);
 
 asmlinkage long sys_acct(const char __user *name);
 asmlinkage long sys_capget(cap_user_header_t header,
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 22749c1..d6696cf 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -707,9 +707,13 @@ __SYSCALL(__NR_getrandom, sys_getrandom)
 __SYSCALL(__NR_memfd_create, sys_memfd_create)
 #define __NR_bpf 280
 __SYSCALL(__NR_bpf, sys_bpf)
+#define __NR_getusers 281
+__SYSCALL(__NR_getusers, sys_getusers)
+#define __NR_setusers 282
+__SYSCALL(__NR_setusers, sys_setusers)
 
 #undef __NR_syscalls
-#define __NR_syscalls 281
+#define __NR_syscalls 283
 
 /*
  * All syscalls below here should go away really,
diff --git a/include/uapi/linux/limits.h b/include/uapi/linux/limits.h
index 2d0f941..bae1b4c 100644
--- a/include/uapi/linux/limits.h
+++ b/include/uapi/linux/limits.h
@@ -4,6 +4,7 @@
 #define NR_OPEN	        1024
 
 #define NGROUPS_MAX    65536	/* supplemental group IDs are available */
+#define NUSERS_MAX     65536	/* supplemental user IDs available */
 #define ARG_MAX       131072	/* # bytes of args + environ for exec() */
 #define LINK_MAX         127	/* # links a file may have */
 #define MAX_CANON        255	/* size of the canonical input queue */
diff --git a/init/Kconfig b/init/Kconfig
index 3ee28ae..d85b159 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1358,6 +1358,15 @@ config UID16
 	help
 	  This enables the legacy 16-bit UID syscall wrappers.
 
+config SUPPLEMENTARY_UIDS
+       bool "Enable supplementary UIDs and system calls" if EXPERT
+       default y
+       help
+         This option adds a list of supplementary UIDs to each process, along
+         with system calls to manage that list.  If building an embedded
+         system where no applications use this functionality, you can disable
+         this option to save space.
+
 config SGETMASK_SYSCALL
 	bool "sgetmask/ssetmask syscalls support" if EXPERT
 	def_bool PARISC || MN10300 || BLACKFIN || M68K || PPC || MIPS || X86 || SPARC || CRIS || MICROBLAZE || SUPERH
diff --git a/kernel/cred.c b/kernel/cred.c
index e0573a4..1700a03 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -54,6 +54,7 @@ struct cred init_cred = {
 	.user			= INIT_USER,
 	.user_ns		= &init_user_ns,
 	.group_info		= &init_groups,
+	INIT_USER_INFO
 };
 
 static inline void set_cred_subscribers(struct cred *cred, int n)
@@ -112,6 +113,7 @@ static void put_cred_rcu(struct rcu_head *rcu)
 	key_put(cred->request_key_auth);
 	if (cred->group_info)
 		put_group_info(cred->group_info);
+	put_cred_user_info(cred);
 	free_uid(cred->user);
 	put_user_ns(cred->user_ns);
 	kmem_cache_free(cred_jar, cred);
@@ -252,6 +254,7 @@ struct cred *prepare_creds(void)
 	atomic_set(&new->usage, 1);
 	set_cred_subscribers(new, 0);
 	get_group_info(new->group_info);
+	get_cred_user_info(new);
 	get_uid(new->user);
 	get_user_ns(new->user_ns);
 
@@ -607,6 +610,7 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
 	get_uid(new->user);
 	get_user_ns(new->user_ns);
 	get_group_info(new->group_info);
+	get_cred_user_info(new);
 
 #ifdef CONFIG_KEYS
 	new->session_keyring = NULL;
diff --git a/kernel/groups.c b/kernel/groups.c
index 451698f..d5de27d 100644
--- a/kernel/groups.c
+++ b/kernel/groups.c
@@ -269,3 +269,176 @@ int in_egroup_p(kgid_t grp)
 }
 
 EXPORT_SYMBOL(in_egroup_p);
+
+#ifdef CONFIG_SUPPLEMENTARY_UIDS
+/* init to 2 - one for init_task, one to ensure it is never freed */
+struct user_info init_users = { .usage = ATOMIC_INIT(2) };
+
+static struct user_info *users_alloc(int uidsetsize)
+{
+	return (struct user_info *)groups_alloc(uidsetsize);
+}
+
+void users_free(struct user_info *user_info)
+{
+	groups_free((struct group_info *)user_info);
+}
+
+/* export the user_info to a user-space array */
+static int users_to_user(uid_t __user *userlist,
+			 const struct user_info *user_info)
+{
+	struct user_namespace *user_ns = current_user_ns();
+	int i;
+	unsigned int count = user_info->nusers;
+
+	for (i = 0; i < count; i++) {
+		uid_t uid;
+		uid = from_kuid_munged(user_ns, USER_AT(user_info, i));
+		if (put_user(uid, userlist+i))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+/* fill a user_info from a user-space array - it must be allocated already */
+static int users_from_user(struct user_info *user_info, uid_t __user *userlist)
+{
+	struct user_namespace *user_ns = current_user_ns();
+	int i;
+	unsigned int count = user_info->nusers;
+
+	for (i = 0; i < count; i++) {
+		uid_t uid;
+		kuid_t kuid;
+		if (get_user(uid, userlist+i))
+			return -EFAULT;
+
+		kuid = make_kuid(user_ns, uid);
+		if (!uid_valid(kuid))
+			return -EINVAL;
+
+		USER_AT(user_info, i) = kuid;
+	}
+	return 0;
+}
+
+static void users_sort(struct user_info *user_info)
+{
+	groups_sort((struct group_info *)user_info);
+}
+
+static bool users_search(const struct user_info *user_info, kuid_t uid)
+{
+	return groups_search((const struct group_info *)user_info, *(kgid_t *)&uid);
+}
+
+/* Return true if the user_info is a subset of the user_info of the specified
+ * credentials.  Also allow the first user_info to contain the uid, euid, or
+ * suid of the credentials.
+ */
+static bool user_subset(const struct user_info *u1, const struct cred *cred2)
+{
+	const struct user_info *u2 = cred2->user_info;
+	unsigned int i, j;
+
+	for (i = 0, j = 0; i < u1->nusers; i++) {
+		kuid_t uid1 = USER_AT(u1, i);
+		kuid_t uid2;
+		for (; j < u2->nusers; j++) {
+			uid2 = USER_AT(u2, j);
+			if (uid_lte(uid1, uid2))
+				break;
+		}
+		if (j >= u2->nusers || !uid_eq(uid1, uid2)) {
+			if (!uid_eq(uid1, cred2->uid)
+			    && !uid_eq(uid1, cred2->euid)
+			    && !uid_eq(uid1, cred2->suid))
+				return false;
+		} else {
+			j++;
+		}
+	}
+
+	return true;
+}
+
+/**
+ * set_current_users - Change current's supplementary user list
+ * @user_info: The user list to impose
+ *
+ * Validate a user list and, if valid, impose it upon current's task
+ * security record.
+ */
+int set_current_users(struct user_info *user_info)
+{
+	struct cred *new;
+
+	users_sort(user_info);
+	new = prepare_creds();
+	if (!new)
+		return -ENOMEM;
+	if (!(ns_capable(current_user_ns(), CAP_SETUID)
+	      || user_subset(user_info, new))) {
+		abort_creds(new);
+		return -EPERM;
+	}
+
+	put_user_info(new->user_info);
+	get_user_info(user_info);
+	new->user_info = user_info;
+	return commit_creds(new);
+}
+
+SYSCALL_DEFINE2(getusers, int, uidsetsize, uid_t __user *, userlist)
+{
+	const struct cred *cred = current_cred();
+	int i;
+
+	if (uidsetsize < 0)
+		return -EINVAL;
+
+	/* no need to grab task_lock here; it cannot change */
+	i = cred->user_info->nusers;
+	if (uidsetsize) {
+		if (i > uidsetsize) {
+			i = -EINVAL;
+			goto out;
+		}
+		if (users_to_user(userlist, cred->user_info)) {
+			i = -EFAULT;
+			goto out;
+		}
+	}
+out:
+	return i;
+}
+
+SYSCALL_DEFINE2(setusers, int, uidsetsize, uid_t __user *, userlist)
+{
+	struct user_info *user_info;
+	int retval;
+
+	if ((unsigned)uidsetsize > NUSERS_MAX)
+		return -EINVAL;
+
+	user_info = users_alloc(uidsetsize);
+	if (!user_info)
+		return -ENOMEM;
+	retval = users_from_user(user_info, userlist);
+	if (retval) {
+		put_user_info(user_info);
+		return retval;
+	}
+
+	retval = set_current_users(user_info);
+	put_user_info(user_info);
+
+	return retval;
+}
+
+bool has_supplementary_uid(kuid_t uid)
+{
+	return users_search(current_cred()->user_info, uid);
+}
+#endif /* CONFIG_SUPPLEMENTARY_UIDS */
diff --git a/kernel/sys.c b/kernel/sys.c
index 1eaa2f0..412dda9 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -472,7 +472,8 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
 		new->uid = kruid;
 		if (!uid_eq(old->uid, kruid) &&
 		    !uid_eq(old->euid, kruid) &&
-		    !ns_capable(old->user_ns, CAP_SETUID))
+		    !ns_capable(old->user_ns, CAP_SETUID) &&
+		    !has_supplementary_uid(kruid))
 			goto error;
 	}
 
@@ -481,7 +482,8 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
 		if (!uid_eq(old->uid, keuid) &&
 		    !uid_eq(old->euid, keuid) &&
 		    !uid_eq(old->suid, keuid) &&
-		    !ns_capable(old->user_ns, CAP_SETUID))
+		    !ns_capable(old->user_ns, CAP_SETUID) &&
+		    !has_supplementary_uid(keuid))
 			goto error;
 	}
 
@@ -542,7 +544,8 @@ SYSCALL_DEFINE1(setuid, uid_t, uid)
 			if (retval < 0)
 				goto error;
 		}
-	} else if (!uid_eq(kuid, old->uid) && !uid_eq(kuid, new->suid)) {
+	} else if (!uid_eq(kuid, old->uid) && !uid_eq(kuid, new->suid) &&
+		   !has_supplementary_uid(kuid)) {
 		goto error;
 	}
 
@@ -594,13 +597,16 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
 	retval = -EPERM;
 	if (!ns_capable(old->user_ns, CAP_SETUID)) {
 		if (ruid != (uid_t) -1        && !uid_eq(kruid, old->uid) &&
-		    !uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid))
+		    !uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid) &&
+		    !has_supplementary_uid(kruid))
 			goto error;
 		if (euid != (uid_t) -1        && !uid_eq(keuid, old->uid) &&
-		    !uid_eq(keuid, old->euid) && !uid_eq(keuid, old->suid))
+		    !uid_eq(keuid, old->euid) && !uid_eq(keuid, old->suid) &&
+		    !has_supplementary_uid(keuid))
 			goto error;
 		if (suid != (uid_t) -1        && !uid_eq(ksuid, old->uid) &&
-		    !uid_eq(ksuid, old->euid) && !uid_eq(ksuid, old->suid))
+		    !uid_eq(ksuid, old->euid) && !uid_eq(ksuid, old->suid) &&
+		    !has_supplementary_uid(ksuid))
 			goto error;
 	}
 
@@ -750,7 +756,8 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
 
 	if (uid_eq(kuid, old->uid)  || uid_eq(kuid, old->euid)  ||
 	    uid_eq(kuid, old->suid) || uid_eq(kuid, old->fsuid) ||
-	    ns_capable(old->user_ns, CAP_SETUID)) {
+	    ns_capable(old->user_ns, CAP_SETUID) ||
+	    has_supplementary_uid(kuid)) {
 		if (!uid_eq(kuid, old->fsuid)) {
 			new->fsuid = kuid;
 			if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index 02aa418..a8a8f02 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -159,6 +159,8 @@ cond_syscall(sys_uselib);
 cond_syscall(sys_fadvise64);
 cond_syscall(sys_fadvise64_64);
 cond_syscall(sys_madvise);
+cond_syscall(sys_getusers);
+cond_syscall(sys_setusers);
 
 /* arch-specific weak syscall entries */
 cond_syscall(sys_pciconfig_read);
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH manpages] Document supplementary user IDs
  2014-11-16  7:08 [PATCH] Add supplementary UIDs, and getusers/setusers system calls Josh Triplett
@ 2014-11-16  7:08 ` Josh Triplett
  2014-11-16  7:41 ` [PATCH] Add supplementary UIDs, and getusers/setusers system calls Josh Triplett
  2014-11-20 15:14 ` Eric W. Biederman
  2 siblings, 0 replies; 6+ messages in thread
From: Josh Triplett @ 2014-11-16  7:08 UTC (permalink / raw)
  To: Andrew Morton, Theodore Ts'o, Kees Cook, mtk.manpages,
	linux-api, linux-man, linux-kernel

Add new manpages for getusers(2) and setusers(2).  Discuss supplementary
UIDs in credentials(7).  Update manpages for seteuid(2), setfsuid(2),
setresuid(2), setreuid(2), and setuid(2).

Signed-off-by: Josh Triplett <josh@joshtriplett.org>
---
 man2/getusers.2    | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 man2/seteuid.2     |  10 ++-
 man2/setfsuid.2    |   4 +-
 man2/setresuid.2   |   8 ++-
 man2/setreuid.2    |  12 +++-
 man2/setuid.2      |   7 ++-
 man2/setusers.2    |   1 +
 man7/credentials.7 |  21 ++++++-
 8 files changed, 229 insertions(+), 11 deletions(-)
 create mode 100644 man2/getusers.2
 create mode 100644 man2/setusers.2

diff --git a/man2/getusers.2 b/man2/getusers.2
new file mode 100644
index 0000000..e5dd371
--- /dev/null
+++ b/man2/getusers.2
@@ -0,0 +1,177 @@
+.\" Copyright 2014 Josh Triplett <josh@joshtriplett.org>
+.\" Based on getgroups.2:
+.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\"
+.\" %%%LICENSE_START(VERBATIM)
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one.
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date.  The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein.  The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\" %%%LICENSE_END
+.\"
+.\" Modified Thu Oct 31 12:04:29 1996 by Eric S. Raymond <esr@thyrsus.com>
+.\" Modified, 27 May 2004, Michael Kerrisk <mtk.manpages@gmail.com>
+.\"     Added notes on capability requirements
+.\" 2008-05-03, mtk, expanded and rewrote parts of DESCRIPTION and RETURN
+.\"     VALUE, made style of page more consistent with man-pages style.
+.\"
+.TH GETUSERS 2 2014-10-20 "Linux" "Linux Programmer's Manual"
+.SH NAME
+getusers, setusers \- get/set list of supplementary user IDs
+.SH SYNOPSIS
+.B #include <sys/types.h>
+.br
+.B #include <unistd.h>
+.sp
+.BI "int getusers(int " size ", uid_t *" list );
+.sp
+.BI "int setusers(int " size ", const uid_t *" list );
+.sp
+.in -4n
+Feature Test Macro Requirements for glibc (see
+.BR feature_test_macros (7)):
+.in
+.sp
+.BR getusers (),
+.BR setusers ():
+_GNU_SOURCE
+.SH DESCRIPTION
+.PP
+.BR getusers ()
+returns the supplementary user IDs of the calling process in
+.IR list .
+The argument
+.I size
+should be set to the maximum number of items that can be stored in the
+buffer pointed to by
+.IR list .
+If the calling process has more than
+.I size
+supplementary user IDs,
+.BR getusers ()
+returns an error.
+The returned list may or may not include the real, effective, saved, or filesystem user ID of the calling process.
+
+If
+.I size
+is zero,
+.I list
+is not modified, but the total number of supplementary user IDs for the
+process is returned.
+This allows the caller to determine the size of a dynamically allocated
+.I list
+to be used in a further call to
+.BR getusers ().
+.PP
+.BR setusers ()
+sets the supplementary user IDs for the calling process.
+The
+.I size
+argument specifies the number of supplementary user IDs
+in the buffer pointed to by
+.IR list .
+
+Any process may drop user IDs from its supplementary list; however, adding user
+IDs not already present in the supplementary list or in the real, effective, or
+saved user ID requires the
+.B CAP_SETUID
+capability.
+.SH RETURN VALUE
+On success,
+.BR getusers ()
+returns the number of supplementary user IDs.
+On error, \-1 is returned, and
+.I errno
+is set appropriately.
+
+On success,
+.BR setusers ()
+returns 0.
+On error, \-1 is returned, and
+.I errno
+is set appropriately.
+.SH ERRORS
+.TP
+.B EFAULT
+.I list
+has an invalid address.
+.PP
+.BR getusers ()
+can additionally fail with the following error:
+.TP
+.B EINVAL
+.I size
+is less than the number of supplementary user IDs, but is not zero.
+.PP
+.BR setusers ()
+can additionally fail with the following errors:
+.TP
+.B EINVAL
+.I size
+is greater than
+.B NUSERS_MAX
+(65536).
+.TP
+.B ENOMEM
+Out of memory.
+.TP
+.B EPERM
+.I list
+contains user IDs not present in the current supplemental user IDs list,
+and not equal to the real, effective, or saved user ID, and the calling
+process does not have
+.BR CAP_SETUID .
+.SH CONFORMING TO
+.BR getusers (),
+.BR setusers (),
+and supplementary user IDs in general are Linux-specific and should not
+be used in programs intended to be portable.
+.SH NOTES
+A process can have up to
+.B NUSERS_MAX
+supplementary user IDs in addition to the real, effective, and saved
+user IDs.
+The constant
+.B NUSERS_MAX
+is defined in
+.IR <limits.h> .
+The set of supplementary user IDs is inherited from the parent process,
+and preserved across an
+.BR execve (2).
+
+The maximum number of supplementary user IDs can be found at run time
+using
+.BR sysconf (3):
+.nf
+
+    long nusers_max;
+    nusers_max = sysconf(_SC_NUSERS_MAX);
+
+.fi
+The maximum return value of
+.BR getusers ()
+cannot be larger than this value.
+.SH SEE ALSO
+.BR capabilities (7),
+.BR credentials (7),
+.BR getgroups (2),
+.BR getresuid (2),
+.BR getuid (2),
+.BR setgroups (2),
+.BR setresuid (2),
+.BR setuid (2)
diff --git a/man2/seteuid.2 b/man2/seteuid.2
index 4690fd6..1710133 100644
--- a/man2/seteuid.2
+++ b/man2/seteuid.2
@@ -1,4 +1,5 @@
 .\" Copyright (C) 2001 Andries Brouwer (aeb@cwi.nl)
+.\" Copyright 2014 Josh Triplett <josh@joshtriplett.org>
 .\"
 .\" %%%LICENSE_START(VERBATIM)
 .\" Permission is granted to make and distribute verbatim copies of this
@@ -54,7 +55,8 @@ _BSD_SOURCE || _POSIX_C_SOURCE\ >=\ 200112L || _XOPEN_SOURCE\ >=\ 600
 .BR seteuid ()
 sets the effective user ID of the calling process.
 Unprivileged user processes may only set the effective user ID to the
-real user ID, the effective user ID or the saved set-user-ID.
+real user ID, the effective user ID, the saved set-user-ID, or a supplementary
+user ID.
 
 Precisely the same holds for
 .BR setegid ()
@@ -95,9 +97,13 @@ and
 (respectively,
 .IR egid )
 is not the real user (group) ID, the effective user (group) ID,
-or the saved set-user-ID (saved set-group-ID).
+the saved set-user-ID (saved set-group-ID), or one of the supplementary
+user (group) IDs.
 .SH CONFORMING TO
 4.3BSD, POSIX.1-2001.
+
+Support for supplementary user IDs is Linux-specific and should not be
+used in programs intended to be portable.
 .SH NOTES
 Setting the effective user (group) ID to the
 saved set-user-ID (saved set-group-ID) is
diff --git a/man2/setfsuid.2 b/man2/setfsuid.2
index 57b125e..f15a31c 100644
--- a/man2/setfsuid.2
+++ b/man2/setfsuid.2
@@ -1,4 +1,5 @@
 .\" Copyright (C) 1995, Thomas K. Dyas <tdyas@eden.rutgers.edu>
+.\" Copyright 2014 Josh Triplett <josh@joshtriplett.org>
 .\"
 .\" %%%LICENSE_START(VERBATIM)
 .\" Permission is granted to make and distribute verbatim copies of this
@@ -64,7 +65,8 @@ is a security hole that can expose it to unwanted signals.
 will succeed only if the caller is the superuser or if
 .I fsuid
 matches either the caller's real user ID, effective user ID,
-saved set-user-ID, or current filesystem user ID.
+saved set-user-ID, current filesystem user ID, or any supplementary
+user ID.
 .SH RETURN VALUE
 On both success and failure,
 this call returns the previous filesystem user ID of the caller.
diff --git a/man2/setresuid.2 b/man2/setresuid.2
index 99ab94f..cc0ba54 100644
--- a/man2/setresuid.2
+++ b/man2/setresuid.2
@@ -1,4 +1,5 @@
 .\" Copyright (C) 1997 Andries Brouwer (aeb@cwi.nl)
+.\" Copyright 2014 Josh Triplett <josh@joshtriplett.org>
 .\"
 .\" %%%LICENSE_START(VERBATIM)
 .\" Permission is granted to make and distribute verbatim copies of this
@@ -42,8 +43,8 @@ saved set-user-ID of the calling process.
 Unprivileged user processes
 may change the real UID,
 effective UID, and saved set-user-ID, each to one of:
-the current real UID, the current effective UID or the
-current saved set-user-ID.
+the current real UID, the current effective UID, the
+current saved set-user-ID, or any supplementary UID.
 
 Privileged processes (on Linux, those having the \fBCAP_SETUID\fP capability)
 may set the real UID, effective UID, and
@@ -109,6 +110,9 @@ These calls are available under Linux since Linux 2.1.44.
 .SH CONFORMING TO
 These calls are nonstandard;
 they also appear on HP-UX and some of the BSDs.
+
+Support for supplementary user IDs is Linux-specific and should not be
+used in programs intended to be portable.
 .SH NOTES
 Under HP-UX and FreeBSD, the prototype is found in
 .IR <unistd.h> .
diff --git a/man2/setreuid.2 b/man2/setreuid.2
index c6597e9..3b19b8d 100644
--- a/man2/setreuid.2
+++ b/man2/setreuid.2
@@ -1,4 +1,5 @@
 .\" Copyright (c) 1983, 1991 The Regents of the University of California.
+.\" Copyright 2014 Josh Triplett <josh@joshtriplett.org>
 .\" All rights reserved.
 .\"
 .\" %%%LICENSE_START(BSD_4_CLAUSE_UCB)
@@ -75,10 +76,10 @@ Supplying a value of \-1 for either the real or effective user ID forces
 the system to leave that ID unchanged.
 
 Unprivileged processes may only set the effective user ID to the real user ID,
-the effective user ID, or the saved set-user-ID.
+the effective user ID, the saved set-user-ID, or a supplementary user ID.
 
 Unprivileged users may only set the real user ID to
-the real user ID or the effective user ID.
+the real user ID, the effective user ID, or a supplementary user ID.
 
 If the real user ID is set (i.e.,
 .I ruid
@@ -144,13 +145,18 @@ and a change other than (i)
 swapping the effective user (group) ID with the real user (group) ID,
 or (ii) setting one to the value of the other or (iii) setting the
 effective user (group) ID to the value of the
-saved set-user-ID (saved set-group-ID) was specified.
+saved set-user-ID (saved set-group-ID) or (iv) setting the real or
+effective user (group) ID to a supplementary user (group) ID was
+specified.
 .SH CONFORMING TO
 POSIX.1-2001, 4.3BSD (the
 .BR setreuid ()
 and
 .BR setregid ()
 function calls first appeared in 4.2BSD).
+
+Support for supplementary user IDs is Linux-specific and should not be
+used in programs intended to be portable.
 .SH NOTES
 Setting the effective user (group) ID to the
 saved set-user-ID (saved set-group-ID) is
diff --git a/man2/setuid.2 b/man2/setuid.2
index c6f870e..e2082be 100644
--- a/man2/setuid.2
+++ b/man2/setuid.2
@@ -1,4 +1,5 @@
 .\" Copyright (C), 1994, Graeme W. Wilford (Wilf).
+.\" Copyright 2014 Josh Triplett <josh@joshtriplett.org>
 .\"
 .\" %%%LICENSE_START(VERBATIM)
 .\" Permission is granted to make and distribute verbatim copies of this
@@ -113,11 +114,15 @@ The user is not privileged (Linux: does not have the
 .B CAP_SETUID
 capability) and
 .I uid
-does not match the real UID or saved set-user-ID of the calling process.
+does not match the real UID, saved set-user-ID, or any supplementary
+user ID of the calling process.
 .SH CONFORMING TO
 SVr4, POSIX.1-2001.
 Not quite compatible with the 4.4BSD call, which
 sets all of the real, saved, and effective user IDs.
+
+Support for supplementary user IDs is Linux-specific and should not be
+used in programs intended to be portable.
 .\" SVr4 documents an additional EINVAL error condition.
 .SH NOTES
 Linux has the concept of the filesystem user ID, normally equal to the
diff --git a/man2/setusers.2 b/man2/setusers.2
new file mode 100644
index 0000000..8044989
--- /dev/null
+++ b/man2/setusers.2
@@ -0,0 +1 @@
+.so man2/getusers.2
diff --git a/man7/credentials.7 b/man7/credentials.7
index acb799d..df601e2 100644
--- a/man7/credentials.7
+++ b/man7/credentials.7
@@ -1,4 +1,5 @@
 .\" Copyright (c) 2007 by Michael Kerrisk <mtk.manpages@gmail.com>
+.\" Copyright 2014 Josh Triplett <josh@joshtriplett.org>
 .\"
 .\" %%%LICENSE_START(VERBATIM)
 .\" Permission is granted to make and distribute verbatim copies of this
@@ -232,6 +233,19 @@ by calling
 and
 .BR setfsgid (2).
 .IP *
+Supplementary user IDs.
+This is a set of additional user IDs that the process has
+permission to act as.
+A process can have up to 65536 supplementary user IDs.
+The call
+.I sysconf(_SC_NUSERS_MAX)
+can be used to determine the number of supplementary user IDs
+a process may have.
+A process can obtain its set of supplementary user IDs using
+.BR getusers (2),
+and can modify the set using
+.BR setusers (2).
+.IP *
 Supplementary group IDs.
 This is a set of additional group IDs that are used for permission
 checks when accessing files and other shared resources.
@@ -256,7 +270,7 @@ A child process created by
 inherits copies of its parent's user and groups IDs.
 During an
 .BR execve (2),
-a process's real user and group ID and supplementary
+a process's real user and group ID and supplementary user and
 group IDs are preserved;
 the effective and saved set IDs may be changed, as described in
 .BR execve (2).
@@ -289,7 +303,8 @@ Process IDs, parent process IDs, process group IDs, and session IDs
 are specified in POSIX.1-2001.
 The real, effective, and saved set user and groups IDs,
 and the supplementary group IDs, are specified in POSIX.1-2001.
-The filesystem user and group IDs are a Linux extension.
+The filesystem user and group IDs, and the supplementary user IDs, are
+Linux extensions.
 .SH NOTES
 The POSIX threads specification requires that
 credentials are shared by all of the threads in a process.
@@ -314,6 +329,7 @@ is carried through to all of the POSIX threads in a process.
 .BR getpid (2),
 .BR getppid (2),
 .BR getsid (2),
+.BR getusers (2),
 .BR kill (2),
 .BR killpg (2),
 .BR setegid (2),
@@ -325,6 +341,7 @@ is carried through to all of the POSIX threads in a process.
 .BR setresgid (2),
 .BR setresuid (2),
 .BR setuid (2),
+.BR setusers (2),
 .BR waitpid (2),
 .BR euidaccess (3),
 .BR initgroups (3),
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] Add supplementary UIDs, and getusers/setusers system calls
  2014-11-16  7:08 [PATCH] Add supplementary UIDs, and getusers/setusers system calls Josh Triplett
  2014-11-16  7:08 ` [PATCH manpages] Document supplementary user IDs Josh Triplett
@ 2014-11-16  7:41 ` Josh Triplett
  2014-11-20 15:14 ` Eric W. Biederman
  2 siblings, 0 replies; 6+ messages in thread
From: Josh Triplett @ 2014-11-16  7:41 UTC (permalink / raw)
  To: Andrew Morton, Theodore Ts'o, Kees Cook, mtk.manpages,
	linux-api, linux-man, linux-kernel

On Sat, Nov 15, 2014 at 11:08:31PM -0800, Josh Triplett wrote:
>  asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist);
> +asmlinkage long sys_setusers(int uidsetsize, uid_t __user *grouplist);

Obvious typo here: s/grouplist/userlist/.  Will fix in a v2, but I'll
wait for other feedback on v1 first.

- Josh Triplett

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Add supplementary UIDs, and getusers/setusers system calls
  2014-11-16  7:08 [PATCH] Add supplementary UIDs, and getusers/setusers system calls Josh Triplett
  2014-11-16  7:08 ` [PATCH manpages] Document supplementary user IDs Josh Triplett
  2014-11-16  7:41 ` [PATCH] Add supplementary UIDs, and getusers/setusers system calls Josh Triplett
@ 2014-11-20 15:14 ` Eric W. Biederman
  2014-11-20 17:59   ` josh
  2014-11-20 18:18   ` Andy Lutomirski
  2 siblings, 2 replies; 6+ messages in thread
From: Eric W. Biederman @ 2014-11-20 15:14 UTC (permalink / raw)
  To: Josh Triplett
  Cc: Andrew Morton, Theodore Ts'o, Kees Cook, mtk.manpages,
	linux-api, linux-man, linux-kernel

Josh Triplett <josh@joshtriplett.org> writes:

> Analogous to the supplementary GID list, the supplementary UID list
> provides a set of additional user credentials that a process can act as.
> A process with CAP_SETUID can set its UID list arbitrarily; a process
> without CAP_SETUID can only reduce its UID list.
>
> This allows each user to have a set of UIDs that they can then use to
> further sandbox individual child processes without first escalating to
> root to change UIDs.  For instance, a PAM module could give each user a
> block of UIDs to work with.

A couple of quick comments on this patch.

1) user namespaces already allow you to do this.

2) After having looked at the group case I am afraid this intersects in
   an unfortunate way with user namespaces.

3) This intersects in a very unfortunate way with setresuid.
   Applications that today know they are dropping all privileges
   won't be dropping all privielges with this change.  Which sounds like
   a recipe for a security exploit to me.

Eric

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Add supplementary UIDs, and getusers/setusers system calls
  2014-11-20 15:14 ` Eric W. Biederman
@ 2014-11-20 17:59   ` josh
  2014-11-20 18:18   ` Andy Lutomirski
  1 sibling, 0 replies; 6+ messages in thread
From: josh @ 2014-11-20 17:59 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: Andrew Morton, Theodore Ts'o, Kees Cook, mtk.manpages,
	linux-api, linux-man, linux-kernel

On Thu, Nov 20, 2014 at 09:14:50AM -0600, Eric W. Biederman wrote:
> Josh Triplett <josh@joshtriplett.org> writes:
> > Analogous to the supplementary GID list, the supplementary UID list
> > provides a set of additional user credentials that a process can act as.
> > A process with CAP_SETUID can set its UID list arbitrarily; a process
> > without CAP_SETUID can only reduce its UID list.
> >
> > This allows each user to have a set of UIDs that they can then use to
> > further sandbox individual child processes without first escalating to
> > root to change UIDs.  For instance, a PAM module could give each user a
> > block of UIDs to work with.
> 
> A couple of quick comments on this patch.

Thanks for your feedback; I'll make sure that the patch description in
v2 provides more detail on use cases and security implications.

> 1) user namespaces already allow you to do this.

No, user namespaces don't handle this case.  User namespaces let you
invent an entirely *new* set of UIDs, all of which map to your one and
only UID on the host.  Only root can map other UIDs on the host to UIDs
in the user namespace.

I wrote this patch in large part *because* of user namespaces; this will
serve as infrastructure that will allow unprivileged users to set up UID
maps, and set up containers whose filesystem doesn't live on a separate
device.  (In the future, this infrastructure will also support
dynamically allocated users and user-mounted filesystems that
distinguish between multiple UIDs, using a filesystem UID map.)

Today, if I have a chroot filesystem containing multiple distinct UIDs
that need to remain distinct (for instance, my Chrome OS build chroot),
I have a few choices, none of them optimal.  Either I hide the
filesystem inside a container and *never* access it from the host, or I
use root to set it up and enter it.  And many approaches interact poorly
with some home directory policies, such as forcing home directory mounts
to only contain files owned by the user.

With this change, together with a change to user namespaces to allow
using supplementary UIDs in a UID map, I could set up and run a build
chroot *entirely* as an unprivileged user.

> 2) After having looked at the group case I am afraid this intersects in
>    an unfortunate way with user namespaces.

In some specific way, or as a general concern that this will need
careful review?

> 3) This intersects in a very unfortunate way with setresuid.
>    Applications that today know they are dropping all privileges
>    won't be dropping all privielges with this change.  Which sounds like
>    a recipe for a security exploit to me.

Root has to hand a user a set of UIDs in the first place, by calling
setusers().  Once it does so, the user can act as any of those UIDs.  If
the user wants to limit permissions to a single UID, they need to switch
to that UID and drop the rest; otherwise, they're handing on all their
UIDs to a child process.  That doesn't seem like an exploit; that seems
like "I've given you some UIDs so you have those UIDs".

That said, given that the use cases this infrastructure supports would
involve new tools that understand supplementary users, I could live with
adding a new setresuid2() system call with a "keep supplementary UIDs"
flag, and if you don't pass that flag (or you use the existing
setuid/setresuid/etc calls) the call will automatically drop all
supplementary UIDs.  Then, if a process that doesn't know about
supplementary UIDs changes its UID, it'll lose all supplementary UIDs.
Would that address your concern?

- Josh Triplett

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Add supplementary UIDs, and getusers/setusers system calls
  2014-11-20 15:14 ` Eric W. Biederman
  2014-11-20 17:59   ` josh
@ 2014-11-20 18:18   ` Andy Lutomirski
  1 sibling, 0 replies; 6+ messages in thread
From: Andy Lutomirski @ 2014-11-20 18:18 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: linux-man, Michael Kerrisk, Theodore Ts'o, Josh Triplett,
	Andrew Morton, Kees Cook, Linux API, linux-kernel

On Nov 20, 2014 7:16 AM, "Eric W. Biederman" <ebiederm@xmission.com> wrote:
>
> Josh Triplett <josh@joshtriplett.org> writes:
>
> > Analogous to the supplementary GID list, the supplementary UID list
> > provides a set of additional user credentials that a process can act as.
> > A process with CAP_SETUID can set its UID list arbitrarily; a process
> > without CAP_SETUID can only reduce its UID list.
> >
> > This allows each user to have a set of UIDs that they can then use to
> > further sandbox individual child processes without first escalating to
> > root to change UIDs.  For instance, a PAM module could give each user a
> > block of UIDs to work with.
>
> A couple of quick comments on this patch.
>
> 1) user namespaces already allow you to do this.

I thought you could only map your fsuid. Can you set fsuid to a
supplementary group?

>
> 2) After having looked at the group case I am afraid this intersects in
>    an unfortunate way with user namespaces.
>
> 3) This intersects in a very unfortunate way with setresuid.
>    Applications that today know they are dropping all privileges
>    won't be dropping all privielges with this change.  Which sounds like
>    a recipe for a security exploit to me.
>
> Eric
> --
> To unsubscribe from this list: send the line "unsubscribe linux-api" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2014-11-20 18:18 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-16  7:08 [PATCH] Add supplementary UIDs, and getusers/setusers system calls Josh Triplett
2014-11-16  7:08 ` [PATCH manpages] Document supplementary user IDs Josh Triplett
2014-11-16  7:41 ` [PATCH] Add supplementary UIDs, and getusers/setusers system calls Josh Triplett
2014-11-20 15:14 ` Eric W. Biederman
2014-11-20 17:59   ` josh
2014-11-20 18:18   ` Andy Lutomirski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).