Commit d178bc3a708f39cbfefc3fab37032d3f2511b4ec
Committed by
Greg Kroah-Hartman
1 parent
edb2b255a0
Exists in
master
and in
6 other branches
user namespace: usb: make usb urbs user namespace aware (v2)
Add to the dev_state and alloc_async structures the user namespace corresponding to the uid and euid. Pass these to kill_pid_info_as_uid(), which can then implement a proper, user-namespace-aware uid check. Changelog: Sep 20: Per Oleg's suggestion: Instead of caching and passing user namespace, uid, and euid each separately, pass a struct cred. Sep 26: Address Alan Stern's comments: don't define a struct cred at usbdev_open(), and take and put a cred at async_completed() to ensure it lasts for the duration of kill_pid_info_as_cred(). Signed-off-by: Serge Hallyn <serge.hallyn@canonical.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Showing 3 changed files with 31 additions and 26 deletions Side-by-side Diff
drivers/usb/core/devio.c
... | ... | @@ -46,6 +46,7 @@ |
46 | 46 | #include <linux/cdev.h> |
47 | 47 | #include <linux/notifier.h> |
48 | 48 | #include <linux/security.h> |
49 | +#include <linux/user_namespace.h> | |
49 | 50 | #include <asm/uaccess.h> |
50 | 51 | #include <asm/byteorder.h> |
51 | 52 | #include <linux/moduleparam.h> |
... | ... | @@ -68,7 +69,7 @@ |
68 | 69 | wait_queue_head_t wait; /* wake up if a request completed */ |
69 | 70 | unsigned int discsignr; |
70 | 71 | struct pid *disc_pid; |
71 | - uid_t disc_uid, disc_euid; | |
72 | + const struct cred *cred; | |
72 | 73 | void __user *disccontext; |
73 | 74 | unsigned long ifclaimed; |
74 | 75 | u32 secid; |
... | ... | @@ -79,7 +80,7 @@ |
79 | 80 | struct list_head asynclist; |
80 | 81 | struct dev_state *ps; |
81 | 82 | struct pid *pid; |
82 | - uid_t uid, euid; | |
83 | + const struct cred *cred; | |
83 | 84 | unsigned int signr; |
84 | 85 | unsigned int ifnum; |
85 | 86 | void __user *userbuffer; |
... | ... | @@ -248,6 +249,7 @@ |
248 | 249 | static void free_async(struct async *as) |
249 | 250 | { |
250 | 251 | put_pid(as->pid); |
252 | + put_cred(as->cred); | |
251 | 253 | kfree(as->urb->transfer_buffer); |
252 | 254 | kfree(as->urb->setup_packet); |
253 | 255 | usb_free_urb(as->urb); |
254 | 256 | |
... | ... | @@ -393,9 +395,8 @@ |
393 | 395 | struct dev_state *ps = as->ps; |
394 | 396 | struct siginfo sinfo; |
395 | 397 | struct pid *pid = NULL; |
396 | - uid_t uid = 0; | |
397 | - uid_t euid = 0; | |
398 | 398 | u32 secid = 0; |
399 | + const struct cred *cred = NULL; | |
399 | 400 | int signr; |
400 | 401 | |
401 | 402 | spin_lock(&ps->lock); |
... | ... | @@ -408,8 +409,7 @@ |
408 | 409 | sinfo.si_code = SI_ASYNCIO; |
409 | 410 | sinfo.si_addr = as->userurb; |
410 | 411 | pid = get_pid(as->pid); |
411 | - uid = as->uid; | |
412 | - euid = as->euid; | |
412 | + cred = get_cred(as->cred); | |
413 | 413 | secid = as->secid; |
414 | 414 | } |
415 | 415 | snoop(&urb->dev->dev, "urb complete\n"); |
416 | 416 | |
... | ... | @@ -423,9 +423,9 @@ |
423 | 423 | spin_unlock(&ps->lock); |
424 | 424 | |
425 | 425 | if (signr) { |
426 | - kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, | |
427 | - euid, secid); | |
426 | + kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid); | |
428 | 427 | put_pid(pid); |
428 | + put_cred(cred); | |
429 | 429 | } |
430 | 430 | |
431 | 431 | wake_up(&ps->wait); |
... | ... | @@ -672,7 +672,6 @@ |
672 | 672 | { |
673 | 673 | struct usb_device *dev = NULL; |
674 | 674 | struct dev_state *ps; |
675 | - const struct cred *cred = current_cred(); | |
676 | 675 | int ret; |
677 | 676 | |
678 | 677 | ret = -ENOMEM; |
... | ... | @@ -722,8 +721,7 @@ |
722 | 721 | init_waitqueue_head(&ps->wait); |
723 | 722 | ps->discsignr = 0; |
724 | 723 | ps->disc_pid = get_pid(task_pid(current)); |
725 | - ps->disc_uid = cred->uid; | |
726 | - ps->disc_euid = cred->euid; | |
724 | + ps->cred = get_current_cred(); | |
727 | 725 | ps->disccontext = NULL; |
728 | 726 | ps->ifclaimed = 0; |
729 | 727 | security_task_getsecid(current, &ps->secid); |
... | ... | @@ -765,6 +763,7 @@ |
765 | 763 | usb_unlock_device(dev); |
766 | 764 | usb_put_dev(dev); |
767 | 765 | put_pid(ps->disc_pid); |
766 | + put_cred(ps->cred); | |
768 | 767 | |
769 | 768 | as = async_getcompleted(ps); |
770 | 769 | while (as) { |
... | ... | @@ -1065,7 +1064,6 @@ |
1065 | 1064 | struct usb_host_endpoint *ep; |
1066 | 1065 | struct async *as; |
1067 | 1066 | struct usb_ctrlrequest *dr = NULL; |
1068 | - const struct cred *cred = current_cred(); | |
1069 | 1067 | unsigned int u, totlen, isofrmlen; |
1070 | 1068 | int ret, ifnum = -1; |
1071 | 1069 | int is_in; |
... | ... | @@ -1279,8 +1277,7 @@ |
1279 | 1277 | as->signr = uurb->signr; |
1280 | 1278 | as->ifnum = ifnum; |
1281 | 1279 | as->pid = get_pid(task_pid(current)); |
1282 | - as->uid = cred->uid; | |
1283 | - as->euid = cred->euid; | |
1280 | + as->cred = get_current_cred(); | |
1284 | 1281 | security_task_getsecid(current, &as->secid); |
1285 | 1282 | if (!is_in && uurb->buffer_length > 0) { |
1286 | 1283 | if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, |
... | ... | @@ -1998,9 +1995,8 @@ |
1998 | 1995 | sinfo.si_errno = EPIPE; |
1999 | 1996 | sinfo.si_code = SI_ASYNCIO; |
2000 | 1997 | sinfo.si_addr = ps->disccontext; |
2001 | - kill_pid_info_as_uid(ps->discsignr, &sinfo, | |
2002 | - ps->disc_pid, ps->disc_uid, | |
2003 | - ps->disc_euid, ps->secid); | |
1998 | + kill_pid_info_as_cred(ps->discsignr, &sinfo, | |
1999 | + ps->disc_pid, ps->cred, ps->secid); | |
2004 | 2000 | } |
2005 | 2001 | } |
2006 | 2002 | } |
include/linux/sched.h
... | ... | @@ -2166,7 +2166,8 @@ |
2166 | 2166 | extern int force_sig_info(int, struct siginfo *, struct task_struct *); |
2167 | 2167 | extern int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp); |
2168 | 2168 | extern int kill_pid_info(int sig, struct siginfo *info, struct pid *pid); |
2169 | -extern int kill_pid_info_as_uid(int, struct siginfo *, struct pid *, uid_t, uid_t, u32); | |
2169 | +extern int kill_pid_info_as_cred(int, struct siginfo *, struct pid *, | |
2170 | + const struct cred *, u32); | |
2170 | 2171 | extern int kill_pgrp(struct pid *pid, int sig, int priv); |
2171 | 2172 | extern int kill_pid(struct pid *pid, int sig, int priv); |
2172 | 2173 | extern int kill_proc_info(int, struct siginfo *, pid_t); |
kernel/signal.c
... | ... | @@ -1344,13 +1344,24 @@ |
1344 | 1344 | return error; |
1345 | 1345 | } |
1346 | 1346 | |
1347 | +static int kill_as_cred_perm(const struct cred *cred, | |
1348 | + struct task_struct *target) | |
1349 | +{ | |
1350 | + const struct cred *pcred = __task_cred(target); | |
1351 | + if (cred->user_ns != pcred->user_ns) | |
1352 | + return 0; | |
1353 | + if (cred->euid != pcred->suid && cred->euid != pcred->uid && | |
1354 | + cred->uid != pcred->suid && cred->uid != pcred->uid) | |
1355 | + return 0; | |
1356 | + return 1; | |
1357 | +} | |
1358 | + | |
1347 | 1359 | /* like kill_pid_info(), but doesn't use uid/euid of "current" */ |
1348 | -int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, | |
1349 | - uid_t uid, uid_t euid, u32 secid) | |
1360 | +int kill_pid_info_as_cred(int sig, struct siginfo *info, struct pid *pid, | |
1361 | + const struct cred *cred, u32 secid) | |
1350 | 1362 | { |
1351 | 1363 | int ret = -EINVAL; |
1352 | 1364 | struct task_struct *p; |
1353 | - const struct cred *pcred; | |
1354 | 1365 | unsigned long flags; |
1355 | 1366 | |
1356 | 1367 | if (!valid_signal(sig)) |
... | ... | @@ -1362,10 +1373,7 @@ |
1362 | 1373 | ret = -ESRCH; |
1363 | 1374 | goto out_unlock; |
1364 | 1375 | } |
1365 | - pcred = __task_cred(p); | |
1366 | - if (si_fromuser(info) && | |
1367 | - euid != pcred->suid && euid != pcred->uid && | |
1368 | - uid != pcred->suid && uid != pcred->uid) { | |
1376 | + if (si_fromuser(info) && !kill_as_cred_perm(cred, p)) { | |
1369 | 1377 | ret = -EPERM; |
1370 | 1378 | goto out_unlock; |
1371 | 1379 | } |
... | ... | @@ -1384,7 +1392,7 @@ |
1384 | 1392 | rcu_read_unlock(); |
1385 | 1393 | return ret; |
1386 | 1394 | } |
1387 | -EXPORT_SYMBOL_GPL(kill_pid_info_as_uid); | |
1395 | +EXPORT_SYMBOL_GPL(kill_pid_info_as_cred); | |
1388 | 1396 | |
1389 | 1397 | /* |
1390 | 1398 | * kill_something_info() interprets pid in interesting ways just like kill(2). |