Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | /* kmod, the new module loader (replaces kerneld) Kirk Petersen Reorganized not to be a daemon by Adam Richter, with guidance from Greg Zornetzer. Modified to avoid chroot and file sharing problems. Mikael Pettersson */ #define __KERNEL_SYSCALLS__ #include <linux/sched.h> #include <linux/unistd.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> /* modprobe_path is set via /proc/sys. */ char modprobe_path[256] = "/sbin/modprobe"; static inline void use_init_fs_context(void) { struct fs_struct *our_fs, *init_fs; /* * Make modprobe's fs context be a copy of init's. * * We cannot use the user's fs context, because it * may have a different root than init. * Since init was created with CLONE_FS, we can grab * its fs context from "init_task". * * The fs context has to be a copy. If it is shared * with init, then any chdir() call in modprobe will * also affect init and the other threads sharing * init_task's fs context. * * We created the exec_modprobe thread without CLONE_FS, * so we can update the fields in our fs context freely. */ lock_kernel(); our_fs = current->fs; dput(our_fs->root); dput(our_fs->pwd); init_fs = init_task.fs; our_fs->umask = init_fs->umask; our_fs->root = dget(init_fs->root); our_fs->pwd = dget(init_fs->pwd); unlock_kernel(); } static int exec_modprobe(void * module_name) { 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; current->session = 1; current->pgrp = 1; use_init_fs_context(); /* Prevent parent user process from sending signals to child. Otherwise, if the modprobe program does not exist, it might be possible to get a user defined signal handler to execute as the super user right after the execve fails if you time the signal just right. */ spin_lock_irq(¤t->sigmask_lock); flush_signals(current); flush_signal_handlers(current); spin_unlock_irq(¤t->sigmask_lock); for (i = 0; i < current->files->max_fds; i++ ) { if (current->files->fd[i]) close(i); } /* 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", modprobe_path, (char*) module_name, errno); return -errno; } return 0; } /* request_module: the function that everyone calls when they need a module. */ 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, 0); if (pid < 0) { printk(KERN_ERR "request_module[%s]: fork failed, errno %d\n", module_name, -pid); return pid; } /* Block everything but SIGKILL/SIGSTOP */ spin_lock_irq(¤t->sigmask_lock); tmpsig = current->blocked; siginitsetinv(¤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 "request_module[%s]: waitpid(%d,...) failed, errno %d\n", module_name, pid, -waitpid_result); } return 0; } |