Please test: final(?) daemonless kmod patch for 2.1.95

Adam J. Richter (adam@yggdrasil.com)
Tue, 14 Apr 1998 16:15:46 -0700


It seems that the kmod patch that I posted a day ago is
working for everybody. This is the same patch with one minor
security enhancement: modprobe must be owned by root and not
writable by anyone other than root in order for it to be executed.
The patch is against pristine linux-2.1.95. I have already verified
that these changes do work.

The purpose of this security change is to impede attacks using
the chroot'ed anonymous FTP area, so that if an intruder were able to
create the file "~ftp/sbin/modprobe", it still will not be executed,
because the FTP daemon would be running as another user. Even if
the intruder were able to move a file owned by root into that location,
it would have to be a file that could only be written to by root.

It would probably be a good idea to have modprobe extend this
policy to the kernel .o files at some point.

Anyhow, please test this. If I don't hear of any problems by
tomorrow afternoon, I will check to make sure that I've got the nods
of the other kmod developers and then I will ask Linus to put it into
the main kernel distribution.

Adam J. Richter __ ______________ 4880 Stevens Creek Blvd, Suite 205
adam@yggdrasil.com \ / San Jose, California 95129-1034
+1 408 261-6630 | g g d r a s i l United States of America
fax +1 408 261-6631 "Free Software For The Rest Of Us."
----------------------------CUT HERE----------------------------------

--- /tmp/linux-2.1.95/Documentation/kmod.txt Tue Mar 10 14:43:13 1998
+++ linux/Documentation/kmod.txt Mon Apr 13 10:25:55 1998
@@ -12,11 +12,12 @@

echo "/sbin/modprobe" > /proc/sys/kernel/modprobe

-To tell kmod when to unload unused modules, do something like:
+To periodically unload unused modules, do put something like the following
+in root's crontab entry:

- echo "120" > /proc/sys/kernel/kmod_unload_delay
+ 0-59/5 * * * * /sbin/rmmod -a

-Kmod only loads and unloads modules. Kerneld could do more (although
+Kmod only loads modules. Kerneld could do more (although
nothing in the standard kernel used its other features). If you
require features such as request_route, we suggest that you take
a similar approach. A simple request_route function could be called,
--- /tmp/linux-2.1.95/Documentation/sysctl/kernel.txt Thu Mar 19 15:04:30 1998
+++ linux/Documentation/sysctl/kernel.txt Mon Apr 13 10:23:54 1998
@@ -25,7 +25,6 @@
- inode-max
- inode-nr
- inode-state
-- kmod_unload_delay ==> Documentation/kmod.txt
- modprobe ==> Documentation/kmod.txt
- osrelease
- ostype
--- /tmp/linux-2.1.95/include/linux/sysctl.h Wed Apr 1 16:19:57 1998
+++ linux/include/linux/sysctl.h Mon Apr 13 10:18:48 1998
@@ -72,7 +72,6 @@
KERN_STATINODE,
KERN_DENTRY, /* dentry statistics */
KERN_MODPROBE,
- KERN_KMOD_UNLOAD_DELAY
};


--- /tmp/linux-2.1.95/init/main.c Wed Apr 8 18:10:01 1998
+++ linux/init/main.c Mon Apr 13 11:54:55 1998
@@ -1162,13 +1162,6 @@
smp_begin();
#endif

-#ifdef CONFIG_KMOD
- {
- extern int kmod_init(void);
- kmod_init();
- }
-#endif
-
#ifdef CONFIG_UMSDOS_FS
{
/*
--- /tmp/linux-2.1.95/kernel/kmod.c Tue Apr 7 08:05:05 1998
+++ linux/kernel/kmod.c Tue Apr 14 15:53:34 1998
@@ -1,6 +1,9 @@
/*
kmod, the new module loader (replaces kerneld)
Kirk Petersen
+
+ Reorganized not to be a daemon by Adam Richter, with guidance
+ from Greg Zornetzer.
*/

#define __KERNEL_SYSCALLS__
@@ -8,142 +11,82 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/unistd.h>
+#include <linux/stat.h>
+#include <asm/uaccess.h>

/*
- kmod_unload_delay and modprobe_path are set via /proc/sys.
+ modprobe_path is set via /proc/sys.
*/
-int kmod_unload_delay = 60;
char modprobe_path[256] = "/sbin/modprobe";
-static char module_name[64] = "";
-static char * argv[] = { modprobe_path, "-s", "-k", module_name, NULL };
static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL };

-/*
- kmod_queue synchronizes the kmod thread and the rest of the system
- kmod_unload_timer is what we use to unload modules
- after kmod_unload_delay seconds
-*/
-static struct wait_queue * kmod_queue = NULL;
-static struct timer_list kmod_unload_timer;
-
-/*
- It is not easy to implement a full fork in kernel-space on some
- systems (Alpha), and it is not necessary for us here. This is
- a new thread that does the exec.
-*/
-static int kmod_exec_modprobe(void * data)
-{
- sigemptyset(&current->blocked);
- execve(modprobe_path, argv, envp);
- printk(KERN_ERR "kmod: failed to load module %s\n", module_name);
- return 0;
-}
-
-/*
- kmod_thread is the thread that does most of the work. kmod_unload and
- request_module tell it to wake up and do work.
-*/
-static int kmod_thread(void * data)
-{
- int pid;
-
- /*
- Initialize basic thread information
+static void
+security_paranoia(void) {
+ int fd;
+
+ /* 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.
*/
- current->session = 1;
- current->pgrp = 1;
- sprintf(current->comm, "kmod");
+ spin_lock_irq(&current->sigmask_lock);
sigfillset(&current->blocked);
+ spin_unlock_irq(&current->sigmask_lock);

- /*
- This is the main kmod_thread loop. It first sleeps, then
- handles requests from request_module or kmod_unload.
- */
-
- while (1) {
- interruptible_sleep_on(&kmod_queue);
-
- /*
- If request_module woke us up, we should try to
- load module_name. If not, kmod_unload woke us up,
- do call delete_module.
- (if somehow both want us to do something, ignore the
- delete_module request)
- */
- if (module_name[0] == '\0') {
- delete_module(NULL);
- } else {
- pid = kernel_thread(kmod_exec_modprobe, NULL, SIGCHLD);
- if (pid > 0) {
- waitpid(pid, NULL, 0);
- module_name[0] = '\0';
- wake_up(&kmod_queue);
- } else {
- printk(KERN_ERR "kmod: fork failed, errno %d\n", -pid);
- }
- }
+ /* Prevent open file descriptors from being passed to privileged
+ modprobe program. With the stock modprobe, the only problem
+ is the potential annoyance of an error message being inserted
+ into a program's stderr. However, we want to be safe for
+ customized modprobe replacements as well. -adam@yggdrasil.com */
+ for (fd = 0; fd < current->files->max_fds; fd++ ) {
+ close(fd);
}
-
- return 0; /* Never reached. */
}

-/*
- kmod_unload is the function that the kernel calls when
- the kmod_unload_timer expires
-*/
-void kmod_unload(unsigned long x)
+static int exec_modprobe(void * module_name)
{
- /*
- wake up the kmod thread, which does the work
- (we can't call delete_module, as it locks the kernel and
- we are in the bottom half of the kernel (right?))
- once it is awake, reset the timer
- */
- wake_up(&kmod_queue);
- kmod_unload_timer.expires = jiffies + (kmod_unload_delay * HZ);
- add_timer(&kmod_unload_timer);
-}
-
-int kmod_init(void)
-{
- printk("Starting kmod\n");
-
- kernel_thread(kmod_thread, NULL, 0);
-
- kmod_unload_timer.next = NULL;
- kmod_unload_timer.prev = NULL;
- kmod_unload_timer.expires = jiffies + (5 * 60 * HZ);
- kmod_unload_timer.data = 0L;
- kmod_unload_timer.function = kmod_unload;
- add_timer(&kmod_unload_timer);
-
+ char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL};
+ struct stat statbuf;
+ int stat_result;
+ asmlinkage int sys_newstat(char * filename, struct stat * statbuf);
+
+ security_paranoia();
+ set_fs(KERNEL_DS); /* Allow execve args to be in kernel space. */
+ if ((stat_result = sys_newstat(modprobe_path, &statbuf)) < 0) {
+ return stat_result;
+ }
+ if (statbuf.st_uid != 0 || (statbuf.st_mode & (S_IWGRP|S_IWOTH))) {
+ return -EPERM;
+ }
+ current->uid = current->euid = 0;
+ 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 to be loaded
+ request_module: the function that everyone calls when they need
+ a module.
*/
-int request_module(const char * name)
+int request_module(const char * module_name)
{
- /* first, copy the name of the module into module_name */
- /* then wake_up() the kmod daemon */
- /* wait for the kmod daemon to finish (it will wake us up) */
-
- /*
- kmod_thread is sleeping, so start by copying the name of
- the module into module_name. Once that is done, wake up
- kmod_thread.
- */
- strncpy(module_name, name, sizeof(module_name));
- module_name[sizeof(module_name)-1] = '\0';
- wake_up(&kmod_queue);
-
- /*
- Now that we have told kmod_thread what to do, we want to
- go to sleep and let it do its work. It will wake us up,
- at which point we will be done (the module will be loaded).
- */
- interruptible_sleep_on(&kmod_queue);
+ int pid;
+ int waitpid_result;
+
+ pid = kernel_thread(exec_modprobe, (void*) module_name, SIGCHLD);
+ if (pid < 0) {
+ printk(KERN_ERR "kmod: fork failed, errno %d\n", -pid);
+ return pid;
+ }
+ waitpid_result = waitpid(pid, NULL, 0);
+ if (waitpid_result != pid) {
+ printk (KERN_ERR "kmod: waitpid(%d,NULL,0) failed, returning %d.\n",
+ pid, waitpid_result);
+ }
return 0;
}
--- /tmp/linux-2.1.95/kernel/sysctl.c Wed Apr 1 16:26:35 1998
+++ linux/kernel/sysctl.c Mon Apr 13 10:17:42 1998
@@ -43,7 +43,6 @@
extern int sysctl_overcommit_memory;
#ifdef CONFIG_KMOD
extern char modprobe_path[];
-extern int kmod_unload_delay;
#endif
#ifdef CONFIG_CHR_DEV_SG
extern int sg_big_buff;
@@ -180,8 +179,6 @@
#ifdef CONFIG_KMOD
{KERN_MODPROBE, "modprobe", &modprobe_path, 256,
0644, NULL, &proc_dostring, &sysctl_string },
- {KERN_KMOD_UNLOAD_DELAY, "kmod_unload_delay", &kmod_unload_delay,
- sizeof(int), 0644, NULL, &proc_dointvec},
#endif
#ifdef CONFIG_CHR_DEV_SG
{KERN_NRFILE, "sg-big-buff", &sg_big_buff, sizeof (int),

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu