p9auth fs: introduction
Subsystems (4)
cover p9auth fs: introduction
Hi,
Here is an updated version of the p9auth setuid capability module, which hopefully addresses all previous feedback. It is now a separate filesystem instead of a device, as per Eric's suggestion.
During the last round, Alan Cox made a great suggestion of sending credentials over AF_UNIX sock allowing the recipient to 'become me.' I think that's still an interesting idea, and intend to pursue it as Eric pushes the patches to translate userids across user namespaces.
The tradeoffs are worth discussing. On the one
hand, p9auth requires a scary CAP_GRANT_ID capability, while a SO_PASSAUTH would be more akin to an extension of CAP_SETUID. Also, SO_PASSAUTH would be usable by any unprivileged app, while one would hope there would be only one p9auth service for the whole system. On the other hand, the p9auth API appears to be pretty well settled and understood, and only provides for a very simple setting of all uids and all gids to one value, plus some auxiliary groups, which is perfect for use by simple login servers.
p9auth and SO_PASSAUTH don't appear to be mutually exclusive. I don't know how painful it would be for plan-9 folks to make use of the SO_PASSAUTH feature. (It should definately be possible) But in any case here is the next iteration of p9auth fs for discussion and consideration.
thanks,
-serge
PATCH 1/3 p9auth: split core function out of some set*{u,g}id functions
Break the core functionality of set{fs,res}{u,g}id into cred_setX which performs the access checks based on current_cred(), but performs the requested change on a passed-in cred.
Export the helpers, since p9auth can be compiled as a module. It might be worth not allowing modular p9auth to avoid having to export them.
Really the setfs{u,g}id helper isn't needed, but move it as well to keep the code consistent.
This patch also changes set_user() to use new->user->user_ns. While technically not needed as all callers should have new->user->user_ns equal to current_userns(), it is more correct and may prevent surprises in the future.
Changelog:
Apr 24: (David Howells) make cred_setresuid etc extern, and
document the helpers in Documentation/credentials.txt.
--- Documentation/credentials.txt | 18 ++++++ include/linux/cred.h | 12 ++++ kernel/cred.c | 119 +++++++++++++++++++++++++++++++++++++ kernel/sys.c | 131 ++++++---------------------------------- 4 files changed, 169 insertions(+), 111 deletions(-)
Documentation/credentials.txt +18 -0
@@ -529,6 +529,24 @@ A typical credentials alteration function would look something like this: return commit_creds(new); } +SETUID/SETGID HELPERS +--------------------- + +Helpers exist to perform the core of uid and gid alterations: + +cred_setresuid(struct cred *new, uid_t ruid, uid_t euid, uid_t suid, + int force); +cred_setresgid(struct cred *new, gid_t rgid, gid_t egid, gid_t sgid, + int force); +cred_setfsuid(struct cred *new, uid_t uid, uid_t *old_fsuid); +cred_setfsgid(struct cred *new, gid_t gid, gid_t *old_fsgid); + +The force argument means that while the caller does not have CAP_SETUID +or CAP_SETUID, the credentials were received from a task with CAP_GRANT_ID. + +These helpers are used in kernel/sys.c for the analogous syscalls. +As can be seen in those examples, these helpers are to be wrapped +between calls to prepare_creds() and commit_creds() or abort_creds(). MANAGING CREDENTIALS --------------------
include/linux/cred.h +12 -0
@@ -22,6 +22,9 @@ struct user_struct;struct cred ;struct inode ; +/* defined in sys.c, used in cred_setresuid */ +extern int set_user (struct cred *new); +/* * COW Supplementary groups list */
@@ -396,4 +399,13 @@ do { \ *(_fsgid) = __cred->fsgid; \ } while(0) +#define CRED_SETID_NOFORCE 0 +#define CRED_SETID_FORCE 1 +extern int cred_setresuid (struct cred *new,uid_t ruid,uid_t euid,uid_t suid, +int force); +extern int cred_setresgid (struct cred *new,gid_t rgid,gid_t egid,gid_t sgid, +int force); +extern int cred_setfsuid (struct cred *new,uid_t uid,uid_t *old_fsuid); +extern int cred_setfsgid (struct cred *new,gid_t gid,gid_t *old_fsgid); +#endif /* _LINUX_CRED_H */
kernel/cred.c +119 -0
125 lines not on this page · raw message
kernel/sys.c +20 -111
195 lines not on this page · raw message
Signature · 1 line
1.7.0.4
Re: [PATCH 1/3] p9auth: split core function out of some set*{u,g}id functions
Quoted 16 lines · Serge E. Hallyn
Break the core functionality of set{fs,res}{u,g}id into
cred_setXwhich performs the access checks based oncurrent_cred(), but performs the requested change on a passed-in cred.Export the helpers, since p9auth can be compiled as a module. It might be worth not allowing modular p9auth to avoid having to export them.
Really the setfs{u,g}id helper isn't needed, but move it as well to keep the code consistent.
This patch also changes
set_user()to usenew->user->user_ns. While technically not needed as all callers should havenew->user->user_nsequal tocurrent_userns(), it is more correct and may prevent surprises in the future.
Quoted 5 lines
Changelog:
Apr 24: (David Howells) makecred_setresuidetc extern, and
document the helpers inDocumentation/credentials.txt.Signed-off-by Serge E. Hallyn <[email protected]>
PATCH 2/3 p9auth: add CAP_GRANT_ID to authorize use of /dev/caphash
In reply to Serge E. HallynGranting userid capabilities to another task is a dangerous privilege. Don't just let file permissions authorize it. Define CAP_GRANT_ID as a new capability needed to write to /dev/caphash.
For one thing this lets us start a factotum server early on in init, then have init drop CAP_GRANT_ID from its bounding set so the rest of the system cannot regain it.
(This patch is only useful if the next patch, introducing p9auth fs, is upstreamed)
TODO - patch for capabilities.7 manpage
--- include/linux/capability.h | 6 +++++- security/selinux/include/classmap.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-)
include/linux/capability.h +5 -1
@@ -355,7 +355,11 @@ struct cpu_vfs_cap_data {#define CAP_MAC_ADMIN 33 -#define CAP_LAST_CAP CAP_MAC_ADMIN +/* Allow granting setuid capabilities through p9auth /dev/caphash */ + +#define CAP_GRANT_ID 34 + +#define CAP_LAST_CAP CAP_GRANT_ID#define cap_valid (x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
security/selinux/include/classmap.h +1 -1
@@ -142,7 +142,7 @@ struct security_class_mapping secclass_map[] = { "node_bind", "name_connect", NULL } }, { "memprotect", { "mmap_zero", NULL } }, { "peer", { "recv", NULL } }, - { "capability2", { "mac_override", "mac_admin", NULL } }, + { "capability2", { "mac_override", "mac_admin", "grant_id", NULL } }, { "kernel_service", { "use_as_override", "create_files_as", NULL } }, { "tun_socket", { COMMON_SOCK_PERMS, NULL } },
Signature · 1 line
1.7.0.4
PATCH 3/3 RFC: p9auth: add p9auth fs
In reply to Serge E. HallynThis introduces a Plan 9 style setuid capability filesystem. See Documentation/p9auth.txt for a description of how to use this.
This fs allows the implementation of completely unprivileged login daemons. However, doing so requires a fundamental change regarding linux userids: a server privileged with the new CAP_GRANT_ID capability can create a one-time setuid capability allowing another process to change to one specific new userid. This is a change which must be discussed. The use of this privilege can be completely prevented by having init remove CAP_GRANT_ID from its capability bounding set before forking any processes.
Changelog
Apr 24:
return commit_creds (David Howells)
switch from dev to fs (Eric Biederman)
and move p9auth from drivers/char into kernel/
--- Documentation/p9auth.txt | 42 ++++ MAINTAINERS | 6 + init/Kconfig | 2 + kernel/Kconfig.p9auth | 9 + kernel/Makefile | 1 + kernel/p9auth.c | 464 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 524 insertions(+), 0 deletions(-) create mode 100644 Documentation/p9auth.txt create mode 100644 kernel/Kconfig.p9auth create mode 100644 kernel/p9auth.c
Documentation/p9auth.txt new file +42 -0
@@ -0,0 +1,42 @@ +The p9auth filesystem provides a plan-9 factotum-like setuid capability +API. Tasks which are privileged (authorized by possession of the +CAP_GRANT_ID privilege (POSIX capability)) can write new capabilities to +the p9authfs file called cred_grant. The kernel then stores these until +a task uses them by writing to the cred_use file. Each capability +represents the ability for a task running as userid X to switch to +userid Y and some set of groups. Each capability may be used only once, +and unused capabilities are cleared after two minutes. + +The following examples shows how to use the API. Shell 1 contains a +privileged root shell. Shell 2 contains an unprivileged shell as user +501 in the same user namespace. If not already done, the privileged +shell should mount the p9auth filesystem: + + mkdir /mnt/p9auth + mount -t p9auth p9auth /mnt/p9auth + +Now shell 2 somehow communicates to shell 1 that it possesses valid +login credentials to switch to userid 502. Shell 2 then looks up the +groups which uid 502 is a member of, and builds a capability string to +pass to the kernel. It does this by concatenating the old userid, new +userid, new primary group, number of auxiliary groups, and each +auxiliary group, all as integers separated by '@'. The resulting string +is hashed with a random string. In our example, userid 501 may +transition to userid 502, with primary group 502 and auxiliary group 29. + + capstr="501@502@502@1@29" + echo -n "$capstr" > /tmp/txtfile + randstr=`dd if=/dev/urandom count=1 2>/dev/null | \ + uuencode -m - | head -n 2 | tail -n 1 | cut -c -8 ` + openssl sha1 -hmac "$randstr" /tmp/txtfile | awk '{ print $2 '} \ + > /tmp/hex + ./unhex < /tmp/hex > /mnt/p9auth/cred_grant + +Note that to use an empty set of auxiliary groups, you may use + capstr = "501@502@02@0" + +The source for unhex.c can be found in the ltp testsuite under +ltp-dev/testcases/kernel/security/p9auth. To shell 2 it passes $capstr +and $randstr. Shell 2 can then transition to the new userid by doing + + echo -n "$capstr@$randstr" > /mnt/p9auth/cred_use
MAINTAINERS +6 -0
@@ -4209,6 +4209,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mwu/mac80211-drivers.git S: Maintained F: drivers/net/wireless/p54/ +P9AUTH setuid capability filesystem +M: [email protected] +L: [email protected] (suggested Cc:) +S: Maintained +F: kernel/p9auth.c + PA SEMI ETHERNET DRIVER M: Olof Johansson <[email protected]> L: [email protected]
init/Kconfig +2 -0
@@ -715,6 +715,8 @@ config NET_NS Allow user space to create what appear to be multiple instances of the network stack. +source "kernel/Kconfig.p9auth" + config BLK_DEV_INITRD bool "Initial RAM filesystem and RAM disk (initramfs/initrd) support" depends on BROKEN || !FRV
kernel/Kconfig.p9auth new file +9 -0
@@ -0,0 +1,9 @@ +config PLAN9AUTH + tristate "Plan 9 style capability device implementation" + default n + depends on CRYPTO + help + This module implements the Plan 9 style capability device. + + To compile this driver as a module, choose + M here: the module will be called p9auth.
kernel/Makefile +1 -0
@@ -105,6 +105,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_event.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o obj-$(CONFIG_PADATA) += padata.o +obj-$(CONFIG_PLAN9AUTH) += p9auth.o ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/p9auth.c new file +464 -0
464 lines not on this page · raw message
Signature · 1 line
1.7.0.4
Re: [PATCH 3/3] RFC: p9auth: add p9auth fs
Quoted 3 lines · Serge E. Hallyn
This introduces a Plan 9 style setuid capability filesystem. See
Documentation/p9auth.txtfor a description of how to use this.
Can't comment these changes due to the lack of knowledge, just a couple of minor nits.
kernel/p9auth.c line 390
Quoted 8 lines
kernel/p9auth.c new file +8
-0+static ssize_t p9auth_use_write (struct file *file,const char __user *buffer, +size_t count,loff_t *ppos) +{ +ssize_t retval = -ENOMEM ; +char *user_buf; + +if (mutex_lock_interruptible (&cap_mutex)) +return -EINTR ;
EINTR doesn't look exactly right here, especially if TIF_SIGPENDING is spurious. Probably ERESTARTNOINTR makes more sense. Or mutex_lock_killable().
Review
Quoted 1 line
(quoted diff) +1
-0+ user_buf = kzalloc(count+1, GFP_KERNEL);
Probably this is OK, but it looks a bit strange we do no check that count is not too large.
kernel/p9auth.c line 401
Quoted 6 lines
kernel/p9auth.c new file +6
-0+if (copy_from_user (user_buf, buffer, count)) { + retval = -EFAULT ; + goto out; + } + + retval =use_setuid_capability (user_buf);
It seems that use_setuid_capability() pathes assume that user_buf is null terminated? Say, parse_user_capability() does kstrdup(user_buf).
Oleg.
Re: [PATCH 3/3] RFC: p9auth: add p9auth fs
Quoted 7 lines · Oleg Nesterov
Quoted 3 lines · Serge E. Hallyn
This introduces a Plan 9 style setuid capability filesystem. See
Documentation/p9auth.txtfor a description of how to use this.Can't comment these changes due to the lack of knowledge, just a couple of minor nits.
Thanks, Oleg.
Quoted 11 lines
kernel/p9auth.c line 390
Quoted 8 lines
kernel/p9auth.c new file +8
-0+static ssize_t p9auth_use_write (struct file *file,const char __user *buffer, +size_t count,loff_t *ppos) +{ +ssize_t retval = -ENOMEM ; +char *user_buf; + +if (mutex_lock_interruptible (&cap_mutex)) +return -EINTR ;EINTR doesn't look exactly right here, especially if
TIF_SIGPENDINGis spurious. Probably ERESTARTNOINTR makes more sense. Ormutex_lock_killable().
Ashwin had had this as ERESTARTSYS I believe. I'd read something about userspace should only see -EINTR so I changed it. Sounds like I need to follow the caller chain some more and learn a thing or two, before I repost.
Quoted 4 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ user_buf = kzalloc(count+1, GFP_KERNEL);Probably this is OK, but it looks a bit strange we do no check that count is not too large.
Yes, I should check that, thanks!
Quoted 9 lines
kernel/p9auth.c line 401
Quoted 6 lines
kernel/p9auth.c new file +6
-0+if (copy_from_user (user_buf, buffer, count)) { + retval = -EFAULT ; + goto out; + } + + retval =use_setuid_capability (user_buf);It seems that
use_setuid_capability()pathes assume thatuser_bufis null terminated? Say,parse_user_capability()doeskstrdup(user_buf).
I kzalloc()d to count+1 before, and only copy_from_user() count bytes, so the last byte should always be 0.
Thanks again,
-serge
Re: [PATCH 3/3] RFC: p9auth: add p9auth fs
Quoted 15 lines · Serge E. Hallyn
Quoted 11 lines · Oleg Nesterov
kernel/p9auth.c line 390
Quoted 8 lines
kernel/p9auth.c new file +8
-0+static ssize_t p9auth_use_write (struct file *file,const char __user *buffer, +size_t count,loff_t *ppos) +{ +ssize_t retval = -ENOMEM ; +char *user_buf; + +if (mutex_lock_interruptible (&cap_mutex)) +return -EINTR ;EINTR doesn't look exactly right here, especially if
TIF_SIGPENDINGis spurious. Probably ERESTARTNOINTR makes more sense. Ormutex_lock_killable().
Quoted 2 lines
Ashwin had had this as ERESTARTSYS I believe. I'd read something about userspace should only see
-EINTRso I changed it.
Yes, ERESTARTxxx should not be visible to the user-space. But it is OK to return it from the syscall if signal_pending() is true, in this case it will be changed to EINTR or the system call will be restarted.
ERESTARTNOINTR always restarts the syscall, perphaps after handling the signal if it is really pending.
Quoted 10 lines
Quoted 9 lines
kernel/p9auth.c line 401
Quoted 6 lines
kernel/p9auth.c new file +6
-0+if (copy_from_user (user_buf, buffer, count)) { + retval = -EFAULT ; + goto out; + } + + retval =use_setuid_capability (user_buf);It seems that
use_setuid_capability()pathes assume thatuser_bufis null terminated? Say,parse_user_capability()doeskstrdup(user_buf).
Quoted 2 lines
I kzalloc()d to count+1 before, and only
copy_from_user()count bytes, so the last byte should always be 0.
Ah, indeed, thanks.
Oleg.
Re: [PATCH 3/3] RFC: p9auth: add p9auth fs
Thanks for the comments, Oleg. I will fold this patch into the main patch, but I'll spare everyone a full repost right now.
thanks,
-serge
From: Serge E. Hallyn <[email protected]>
Subject: [PATCH 1/1] p9auth: address feedback
Address two comments by Oleg:
Return -ERESTARTNOINTR rather than -EINTR if we are interrupted while waiting on cap_mutex. And check size of input buffer for something sane. A user with hundreds of auxilliary groups could end up with a > PAGE_SIZE message, so maybe this is a bit too stringent, but I'm not sure how realistic that is.
--- kernel/p9auth.c | 10 ++++++++-- 1 files changed, 8 insertions(+), 2 deletions(-)
kernel/p9auth.c +8 -2
@@ -354,7 +354,10 @@ static ssize_t p9auth_grant_write(struct file *file, const char __user *buffer,char *user_buf;if (mutex_lock_interruptible (&cap_mutex)) -return -EINTR ; +return -ERESTARTNOINTR ; + +if (count >PAGE_SIZE ) +return -E2BIG ; user_buf =kzalloc (count+1 ,GFP_KERNEL );if (!user_buf)
@@ -387,7 +390,10 @@ static ssize_t p9auth_use_write(struct file *file, const char __user *buffer,char *user_buf;if (mutex_lock_interruptible (&cap_mutex)) -return -EINTR ; +return -ERESTARTNOINTR ; + +if (count >PAGE_SIZE ) +return -E2BIG ; user_buf =kzalloc (count+1 ,GFP_KERNEL );if (!user_buf)
Signature · 1 line
1.7.0.4
Re: [PATCH 3/3] RFC: p9auth: add p9auth fs
In reply to Serge E. Hallynkernel/p9auth.c line 97
Quoted 3 lines · Serge E. Hallyn
kernel/p9auth.c new file +2
-0+ result =kzalloc (MAX_DIGEST_SIZE ,GFP_KERNEL ); + ret =crypto_hash_digest (&desc, &sg, plain_text_size, result);
Does result have to be pre-cleared?
kernel/p9auth.c line 363
Quoted 5 lines
kernel/p9auth.c new file +5
-0+ user_buf =kzalloc (count+1 ,GFP_KERNEL ); +if (!user_buf) + goto out; + +if (copy_from_user (user_buf, buffer, count)) {
user_buf doesn't need preclearing. It's just a waste of time. This occurs three times.
David
Re: [PATCH 3/3] RFC: p9auth: add p9auth fs
Quoted 8 lines · David Howells
kernel/p9auth.c line 97
Quoted 3 lines · Serge E. Hallyn
kernel/p9auth.c new file +2
-0+ result =kzalloc (MAX_DIGEST_SIZE ,GFP_KERNEL ); + ret =crypto_hash_digest (&desc, &sg, plain_text_size, result);Does result have to be pre-cleared?
Quoted 8 lines
kernel/p9auth.c line 363
Quoted 5 lines
kernel/p9auth.c new file +5
-0+ user_buf =kzalloc (count+1 ,GFP_KERNEL ); +if (!user_buf) + goto out; + +if (copy_from_user (user_buf, buffer, count)) {
user_bufdoesn't need preclearing. It's just a waste of time. This occurs three times.
Hm, yeah, the first doesn't need it at all, and the latter two should just kmalloc and set user_buf[count] = '\0'.
thanks,
-serge