diff options
Diffstat (limited to 'kernel/kmod.c')
-rw-r--r-- | kernel/kmod.c | 57 |
1 files changed, 46 insertions, 11 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index 7468e4382..ec0d85d32 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -14,14 +14,15 @@ #include <linux/sched.h> #include <linux/types.h> #include <linux/unistd.h> -#include <asm/smp_lock.h> +#include <linux/smp_lock.h> +#include <linux/signal.h> + #include <asm/uaccess.h> /* modprobe_path is set via /proc/sys. */ char modprobe_path[256] = "/sbin/modprobe"; -static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; /* exec_modprobe is spawned from a kernel-mode user process, @@ -33,20 +34,22 @@ static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; #define task_init task[smp_num_cpus] static inline void -use_init_file_context(void) { +use_init_file_context(void) +{ lock_kernel(); /* don't use the user's root, use init's root instead */ exit_fs(current); /* current->fs->count--; */ current->fs = task_init->fs; - current->fs->count++; + atomic_inc(¤t->fs->count); unlock_kernel(); } static int exec_modprobe(void * module_name) { - char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL}; + static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; + char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL }; int i; use_init_file_context(); @@ -63,11 +66,21 @@ static int exec_modprobe(void * module_name) spin_unlock_irq(¤t->sigmask_lock); for (i = 0; i < current->files->max_fds; i++ ) { - if (current->files->fd[i]) close(i); + if (current->files->fd[i]) close(i); } - set_fs(KERNEL_DS); /* Allow execve args to be in kernel space. */ + /* Drop the "current user" thing */ + free_uid(current); + + /* Give kmod all privileges.. */ current->uid = current->euid = current->fsuid = 0; + cap_set_full(current->cap_inheritable); + cap_set_full(current->cap_effective); + + /* Allow execve args to be in kernel space. */ + set_fs(KERNEL_DS); + + /* Go, go, go... */ if (execve(modprobe_path, argv, envp) < 0) { printk(KERN_ERR "kmod: failed to exec %s -s -k %s, errno = %d\n", @@ -85,14 +98,36 @@ int request_module(const char * module_name) { int pid; int waitpid_result; + sigset_t tmpsig; + + /* Don't allow request_module() before the root fs is mounted! */ + if ( ! current->fs->root ) { + printk(KERN_ERR "request_module[%s]: Root fs not mounted\n", + module_name); + return -EPERM; + } - pid = kernel_thread(exec_modprobe, (void*) module_name, - CLONE_FS | SIGCHLD); + pid = kernel_thread(exec_modprobe, (void*) module_name, CLONE_FS); if (pid < 0) { - printk(KERN_ERR "kmod: fork failed, errno %d\n", -pid); + printk(KERN_ERR "request_module[%s]: fork failed, errno %d\n", module_name, -pid); return pid; } - waitpid_result = waitpid(pid, NULL, 0); + + /* Block everything but SIGKILL/SIGSTOP */ + spin_lock_irq(¤t->sigmask_lock); + tmpsig = current->blocked; + siginitset(¤t->blocked, ~(sigmask(SIGKILL)|sigmask(SIGSTOP))); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + waitpid_result = waitpid(pid, NULL, __WCLONE); + + /* Allow signals again.. */ + spin_lock_irq(¤t->sigmask_lock); + current->blocked = tmpsig; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + if (waitpid_result != pid) { printk (KERN_ERR "kmod: waitpid(%d,NULL,0) failed, returning %d.\n", pid, waitpid_result); |