diff options
Diffstat (limited to 'kernel/kmod.c')
-rw-r--r-- | kernel/kmod.c | 124 |
1 files changed, 114 insertions, 10 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index 9cd0591c96a2..47613dfb7b28 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/kmod.h> | 25 | #include <linux/kmod.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/completion.h> | 27 | #include <linux/completion.h> |
28 | #include <linux/cred.h> | ||
28 | #include <linux/file.h> | 29 | #include <linux/file.h> |
29 | #include <linux/fdtable.h> | 30 | #include <linux/fdtable.h> |
30 | #include <linux/workqueue.h> | 31 | #include <linux/workqueue.h> |
@@ -43,6 +44,13 @@ extern int max_threads; | |||
43 | 44 | ||
44 | static struct workqueue_struct *khelper_wq; | 45 | static struct workqueue_struct *khelper_wq; |
45 | 46 | ||
47 | #define CAP_BSET (void *)1 | ||
48 | #define CAP_PI (void *)2 | ||
49 | |||
50 | static kernel_cap_t usermodehelper_bset = CAP_FULL_SET; | ||
51 | static kernel_cap_t usermodehelper_inheritable = CAP_FULL_SET; | ||
52 | static DEFINE_SPINLOCK(umh_sysctl_lock); | ||
53 | |||
46 | #ifdef CONFIG_MODULES | 54 | #ifdef CONFIG_MODULES |
47 | 55 | ||
48 | /* | 56 | /* |
@@ -132,6 +140,7 @@ EXPORT_SYMBOL(__request_module); | |||
132 | static int ____call_usermodehelper(void *data) | 140 | static int ____call_usermodehelper(void *data) |
133 | { | 141 | { |
134 | struct subprocess_info *sub_info = data; | 142 | struct subprocess_info *sub_info = data; |
143 | struct cred *new; | ||
135 | int retval; | 144 | int retval; |
136 | 145 | ||
137 | spin_lock_irq(¤t->sighand->siglock); | 146 | spin_lock_irq(¤t->sighand->siglock); |
@@ -147,12 +156,27 @@ static int ____call_usermodehelper(void *data) | |||
147 | */ | 156 | */ |
148 | set_user_nice(current, 0); | 157 | set_user_nice(current, 0); |
149 | 158 | ||
159 | retval = -ENOMEM; | ||
160 | new = prepare_kernel_cred(current); | ||
161 | if (!new) | ||
162 | goto fail; | ||
163 | |||
164 | spin_lock(&umh_sysctl_lock); | ||
165 | new->cap_bset = cap_intersect(usermodehelper_bset, new->cap_bset); | ||
166 | new->cap_inheritable = cap_intersect(usermodehelper_inheritable, | ||
167 | new->cap_inheritable); | ||
168 | spin_unlock(&umh_sysctl_lock); | ||
169 | |||
150 | if (sub_info->init) { | 170 | if (sub_info->init) { |
151 | retval = sub_info->init(sub_info); | 171 | retval = sub_info->init(sub_info, new); |
152 | if (retval) | 172 | if (retval) { |
173 | abort_creds(new); | ||
153 | goto fail; | 174 | goto fail; |
175 | } | ||
154 | } | 176 | } |
155 | 177 | ||
178 | commit_creds(new); | ||
179 | |||
156 | retval = kernel_execve(sub_info->path, | 180 | retval = kernel_execve(sub_info->path, |
157 | (const char *const *)sub_info->argv, | 181 | (const char *const *)sub_info->argv, |
158 | (const char *const *)sub_info->envp); | 182 | (const char *const *)sub_info->envp); |
@@ -245,7 +269,6 @@ static void __call_usermodehelper(struct work_struct *work) | |||
245 | } | 269 | } |
246 | } | 270 | } |
247 | 271 | ||
248 | #ifdef CONFIG_PM_SLEEP | ||
249 | /* | 272 | /* |
250 | * If set, call_usermodehelper_exec() will exit immediately returning -EBUSY | 273 | * If set, call_usermodehelper_exec() will exit immediately returning -EBUSY |
251 | * (used for preventing user land processes from being created after the user | 274 | * (used for preventing user land processes from being created after the user |
@@ -301,6 +324,15 @@ void usermodehelper_enable(void) | |||
301 | usermodehelper_disabled = 0; | 324 | usermodehelper_disabled = 0; |
302 | } | 325 | } |
303 | 326 | ||
327 | /** | ||
328 | * usermodehelper_is_disabled - check if new helpers are allowed to be started | ||
329 | */ | ||
330 | bool usermodehelper_is_disabled(void) | ||
331 | { | ||
332 | return usermodehelper_disabled; | ||
333 | } | ||
334 | EXPORT_SYMBOL_GPL(usermodehelper_is_disabled); | ||
335 | |||
304 | static void helper_lock(void) | 336 | static void helper_lock(void) |
305 | { | 337 | { |
306 | atomic_inc(&running_helpers); | 338 | atomic_inc(&running_helpers); |
@@ -312,12 +344,6 @@ static void helper_unlock(void) | |||
312 | if (atomic_dec_and_test(&running_helpers)) | 344 | if (atomic_dec_and_test(&running_helpers)) |
313 | wake_up(&running_helpers_waitq); | 345 | wake_up(&running_helpers_waitq); |
314 | } | 346 | } |
315 | #else /* CONFIG_PM_SLEEP */ | ||
316 | #define usermodehelper_disabled 0 | ||
317 | |||
318 | static inline void helper_lock(void) {} | ||
319 | static inline void helper_unlock(void) {} | ||
320 | #endif /* CONFIG_PM_SLEEP */ | ||
321 | 347 | ||
322 | /** | 348 | /** |
323 | * call_usermodehelper_setup - prepare to call a usermode helper | 349 | * call_usermodehelper_setup - prepare to call a usermode helper |
@@ -364,7 +390,7 @@ EXPORT_SYMBOL(call_usermodehelper_setup); | |||
364 | * context in which call_usermodehelper_exec is called. | 390 | * context in which call_usermodehelper_exec is called. |
365 | */ | 391 | */ |
366 | void call_usermodehelper_setfns(struct subprocess_info *info, | 392 | void call_usermodehelper_setfns(struct subprocess_info *info, |
367 | int (*init)(struct subprocess_info *info), | 393 | int (*init)(struct subprocess_info *info, struct cred *new), |
368 | void (*cleanup)(struct subprocess_info *info), | 394 | void (*cleanup)(struct subprocess_info *info), |
369 | void *data) | 395 | void *data) |
370 | { | 396 | { |
@@ -418,6 +444,84 @@ unlock: | |||
418 | } | 444 | } |
419 | EXPORT_SYMBOL(call_usermodehelper_exec); | 445 | EXPORT_SYMBOL(call_usermodehelper_exec); |
420 | 446 | ||
447 | static int proc_cap_handler(struct ctl_table *table, int write, | ||
448 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
449 | { | ||
450 | struct ctl_table t; | ||
451 | unsigned long cap_array[_KERNEL_CAPABILITY_U32S]; | ||
452 | kernel_cap_t new_cap; | ||
453 | int err, i; | ||
454 | |||
455 | if (write && (!capable(CAP_SETPCAP) || | ||
456 | !capable(CAP_SYS_MODULE))) | ||
457 | return -EPERM; | ||
458 | |||
459 | /* | ||
460 | * convert from the global kernel_cap_t to the ulong array to print to | ||
461 | * userspace if this is a read. | ||
462 | */ | ||
463 | spin_lock(&umh_sysctl_lock); | ||
464 | for (i = 0; i < _KERNEL_CAPABILITY_U32S; i++) { | ||
465 | if (table->data == CAP_BSET) | ||
466 | cap_array[i] = usermodehelper_bset.cap[i]; | ||
467 | else if (table->data == CAP_PI) | ||
468 | cap_array[i] = usermodehelper_inheritable.cap[i]; | ||
469 | else | ||
470 | BUG(); | ||
471 | } | ||
472 | spin_unlock(&umh_sysctl_lock); | ||
473 | |||
474 | t = *table; | ||
475 | t.data = &cap_array; | ||
476 | |||
477 | /* | ||
478 | * actually read or write and array of ulongs from userspace. Remember | ||
479 | * these are least significant 32 bits first | ||
480 | */ | ||
481 | err = proc_doulongvec_minmax(&t, write, buffer, lenp, ppos); | ||
482 | if (err < 0) | ||
483 | return err; | ||
484 | |||
485 | /* | ||
486 | * convert from the sysctl array of ulongs to the kernel_cap_t | ||
487 | * internal representation | ||
488 | */ | ||
489 | for (i = 0; i < _KERNEL_CAPABILITY_U32S; i++) | ||
490 | new_cap.cap[i] = cap_array[i]; | ||
491 | |||
492 | /* | ||
493 | * Drop everything not in the new_cap (but don't add things) | ||
494 | */ | ||
495 | spin_lock(&umh_sysctl_lock); | ||
496 | if (write) { | ||
497 | if (table->data == CAP_BSET) | ||
498 | usermodehelper_bset = cap_intersect(usermodehelper_bset, new_cap); | ||
499 | if (table->data == CAP_PI) | ||
500 | usermodehelper_inheritable = cap_intersect(usermodehelper_inheritable, new_cap); | ||
501 | } | ||
502 | spin_unlock(&umh_sysctl_lock); | ||
503 | |||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | struct ctl_table usermodehelper_table[] = { | ||
508 | { | ||
509 | .procname = "bset", | ||
510 | .data = CAP_BSET, | ||
511 | .maxlen = _KERNEL_CAPABILITY_U32S * sizeof(unsigned long), | ||
512 | .mode = 0600, | ||
513 | .proc_handler = proc_cap_handler, | ||
514 | }, | ||
515 | { | ||
516 | .procname = "inheritable", | ||
517 | .data = CAP_PI, | ||
518 | .maxlen = _KERNEL_CAPABILITY_U32S * sizeof(unsigned long), | ||
519 | .mode = 0600, | ||
520 | .proc_handler = proc_cap_handler, | ||
521 | }, | ||
522 | { } | ||
523 | }; | ||
524 | |||
421 | void __init usermodehelper_init(void) | 525 | void __init usermodehelper_init(void) |
422 | { | 526 | { |
423 | khelper_wq = create_singlethread_workqueue("khelper"); | 527 | khelper_wq = create_singlethread_workqueue("khelper"); |