Submitted By: Robert Connolly (ashes) Date: 2007-05-25 Initial Package Version: 1.4.1 Upstream Status: Submitted - Partially.. the Owl patches were submitted http://www.infodrom.org/projects/sysklogd/download/patches/ Origin: http://cvsweb.openwall.com/cgi/cvsweb.cgi/Owl/packages/sysklogd/ sysklogd-1.4.2-caen-owl-klogd-drop-root.diff v1.2 sysklogd-1.4.2-caen-owl-syslogd-drop-root.diff v1.1 sysklogd-1.4.2-alt-syslogd-chroot.diff v1.2 and the '-P' option from sysklogd_1.4.1-16ubuntu6.diff Description: This patch adds the '-j' option to syslogd and klogd, so they can chroot into a jail. This patch also adds the '-u' option to syslogd and klogd, so they can change to the specified username before entering the jail. This patch also adds '-P' to klogd so it can use an alternative /proc/kmsg file, which is nescessary with Linux-2.6 and also works with Linux-2.4. This patch depends on the 'sysklogd-1.4.1-community_fixes.patch'. Read the 'README.priv_sep' file for setup tips. diff -Naur sysklogd-1.4.1.community_fixes/README.priv_sep sysklogd-1.4.1/README.priv_sep --- sysklogd-1.4.1.community_fixes/README.priv_sep 1970-01-01 00:00:00.000000000 +0000 +++ sysklogd-1.4.1/README.priv_sep 2007-05-25 07:01:05.000000000 +0000 @@ -0,0 +1,74 @@ +# May 25th, 2007 - Robert Connolly + +# To get klogd to drop privileges and work with Linux-2.6, root must make a +# second copy of /proc/kmsg because the kernel checks for the CAP_SYS_ADMIN +# capability before every read to /proc/kmsg. In Linux-2.4 the capability is +# only checked when /proc/kmsg is first opened. The following setup will work +# with both Linux-2.6 and Linux-2.4. + +# Set up syslogd and klogd as regular users in a chroot jail like this: + +groupadd syslogd +groupadd klogd +useradd -d /var/lib/syslogd -s /bin/false -g syslogd syslogd +useradd -d /var/lib/klogd -s /bin/false -g klogd klogd + +install -d -m0000 /var/lib/syslogd +install -d -m0000 /var/lib/klogd + +# This is a snippet of my /etc/rc.d/init.d/sysklogd file: + + start) + boot_mesg "Starting system log daemon..." + loadproc /usr/sbin/syslogd -m 0 -u syslogd -j /var/lib/syslogd + + boot_mesg "Starting kernel log daemon..." + /usr/bin/mkfifo -m 700 /var/tmp/kmsg + /bin/chown klogd:klogd /var/tmp/kmsg + env -i /bin/sh -c -- \ + '/bin/dd bs=1 if=/proc/kmsg of=/var/tmp/kmsg &' + loadproc /usr/sbin/klogd -x -u klogd -j /var/lib/klogd \ + -P /var/tmp/kmsg + ;; + + stop) + boot_mesg "Stopping kernel log daemon..." + killproc /usr/sbin/klogd + /bin/fuser -s -k /var/tmp/kmsg + /bin/rm -f /var/tmp/kmsg + + boot_mesg "Stopping system log daemon..." + killproc /usr/sbin/syslogd + ;; + +# Remove the 'reload)' function, it will not work correctly after the daemons +# have dropped to regular users. For the same reason, do not use 'kill -HUP' +# with syslogd or klogd. + +# The klogd daemon does not need to every be root, but root privileges are +# needed to chroot the klogd daemon. If you prefer klogd can be started with +# '/bin/su', or the 'runas' program: +# http://www.ibiblio.org/pub/Linux/system/security/runas-1.01.tar.gz +# Also make sure the klogd user is able to read and execute /usr/sbin/klogd. + +# The 'dd' program will be running constantly. There is nothing necessarily +# wrong with this, but you may want to install a miniature 'dd', such as the +# one included in embutils: http://www.fefe.de/embutils/ +# The 'dd.c' file will need: + +#include +#include +#include +#include + +# to build with Glibc without warnings. Furthermore, the write(2) function at +# the end of the file could be replaced from this: + +# if (obn) write(out,obuf,obn); + +# To this: + +# if ((obn) && (write(out,obuf,obn))) + +# To supress -D_FORTIFY_SOURCE=2 warnings. This 'dd' is over 6 times smaller +# GNU's 'dd', if compiled with -Os. diff -Naur sysklogd-1.4.1.community_fixes/klogd.8 sysklogd-1.4.1/klogd.8 --- sysklogd-1.4.1.community_fixes/klogd.8 2001-03-11 19:35:51.000000000 +0000 +++ sysklogd-1.4.1/klogd.8 2007-05-25 06:38:02.000000000 +0000 @@ -17,10 +17,19 @@ .RB [ " \-f " .I fname ] +.RB [ " \-u " +.I username +] +.RB [ " \-j " +.I chroot_dir +] .RB [ " \-iI " ] .RB [ " \-n " ] .RB [ " \-o " ] .RB [ " \-p " ] +.RB [ " \-P " +.I named_pipe +] .RB [ " \-s " ] .RB [ " \-k " .I fname @@ -46,6 +55,20 @@ .BI "\-f " file Log messages to the specified filename rather than to the syslog facility. .TP +.BI "\-u " username +Tells klogd to become the specified user and drop root privileges before +starting logging. +.TP +.BI "\-j " chroot_dir +Tells klogd to +.BR chroot (2) +into this directory after initializing. +This option is only valid if the \-u option is also used to run klogd +without root privileges. +Note that the use of this option will prevent \-i and \-I from working +unless you set up the chroot directory in such a way that klogd can still +read the kernel module symbols. +.TP .BI "\-i \-I" Signal the currently executing klogd daemon. Both of these switches control the loading/reloading of symbol information. The \-i switch signals the @@ -69,6 +92,13 @@ symbol information whenever an Oops string is detected in the kernel message stream. .TP +.B "-P" +Read kernel messages from an alternative location, instead of from +.IR /proc/kmsg . +If \- is the argument then messages will be read from \fBstdin\fP, otherwise +the argument must be the path of a FIFO named pipe created with +.BR mkfifo (1) . +.TP .B "-s" Force \fBklogd\fP to use the system call interface to the kernel message buffers. diff -Naur sysklogd-1.4.1.community_fixes/klogd.c sysklogd-1.4.1/klogd.c --- sysklogd-1.4.1.community_fixes/klogd.c 2007-05-25 06:37:56.000000000 +0000 +++ sysklogd-1.4.1/klogd.c 2007-05-25 06:38:02.000000000 +0000 @@ -258,6 +258,8 @@ #include #include #include +#include +#include #include "klogd.h" #include "ksyms.h" #ifndef TESTING @@ -307,11 +309,16 @@ static FILE *output_file = (FILE *) 0; +static char *kmsg_file = NULL; /* NULL means default /proc/kmsg */ + static enum LOGSRC {none, proc, kernel} logsrc; int debugging = 0; int symbols_twice = 0; +char *server_user = NULL; +char *chroot_dir = NULL; +int log_flags = 0; /* Function prototypes. */ extern int ksyslog(int type, char *buf, int len); @@ -527,12 +534,29 @@ ksyslog(6, NULL, 0); } + /* Do we read kernel messages from a pipe? */ + if ( kmsg_file ) { + if ( !strcmp(kmsg_file, "-") ) + kmsg = fileno(stdin); + else { + if ( (kmsg = open(kmsg_file, O_RDONLY)) < 0 ) + { + fprintf(stderr, "klogd: Cannot open kmsg file, " \ + "%d - %s.\n", errno, strerror(errno)); + ksyslog(7, NULL, 0); + exit(1); + } + } + return proc; + } + /* * First do a stat to determine whether or not the proc based * file system is available to get kernel messages from. */ - if ( use_syscall || - ((stat(_PATH_KLOG, &sb) < 0) && (errno == ENOENT)) ) + if (!server_user && + (use_syscall || + ((stat(_PATH_KLOG, &sb) < 0) && (errno == ENOENT)))) { /* Initialize kernel logging. */ ksyslog(1, NULL, 0); @@ -962,6 +986,27 @@ } +static int drop_root(void) +{ + struct passwd *pw; + + if (!(pw = getpwnam(server_user))) return -1; + + if (!pw->pw_uid) return -1; + + if (chroot_dir) { + if (chdir(chroot_dir)) return -1; + if (chroot(".")) return -1; + } + + if (setgroups(0, NULL)) return -1; + if (setgid(pw->pw_gid)) return -1; + if (setuid(pw->pw_uid)) return -1; + + return 0; +} + + int main(argc, argv) int argc; @@ -982,7 +1027,7 @@ } #endif /* Parse the command-line. */ - while ((ch = getopt(argc, argv, "c:df:iIk:nopsvx2")) != EOF) + while ((ch = getopt(argc, argv, "c:df:u:j:iIk:nopP:svx2")) != EOF) switch((char)ch) { case '2': /* Print lines with symbols twice. */ @@ -1004,6 +1049,10 @@ case 'I': SignalDaemon(SIGUSR2); return(0); + case 'j': /* chroot 'j'ail */ + chroot_dir = optarg; + log_flags |= LOG_NDELAY; + break; case 'k': /* Kernel symbol file. */ symfile = optarg; break; @@ -1016,9 +1065,15 @@ case 'p': SetParanoiaLevel(1); /* Load symbols on oops. */ break; + case 'P': /* Alternative kmsg file path */ + kmsg_file = strdup(optarg); + break; case 's': /* Use syscall interface. */ use_syscall = 1; break; + case 'u': /* Run as this user */ + server_user = optarg; + break; case 'v': printf("klogd %s.%s\n", VERSION, PATCHLEVEL); exit (1); @@ -1027,6 +1082,10 @@ break; } + if (chroot_dir && !server_user) { + fputs("'-j' is only valid with '-u'\n", stderr); + exit(1); + } /* Set console logging level. */ if ( log_level != (char *) 0 ) @@ -1124,7 +1183,7 @@ } } else - openlog("kernel", 0, LOG_KERN); + openlog("kernel", log_flags, LOG_KERN); /* Handle one-shot logging. */ @@ -1157,6 +1216,11 @@ } } + if (server_user && drop_root()) { + syslog(LOG_ALERT, "klogd: failed to drop root"); + Terminate(); + } + /* The main loop. */ while (1) { diff -Naur sysklogd-1.4.1.community_fixes/sysklogd.8 sysklogd-1.4.1/sysklogd.8 --- sysklogd-1.4.1.community_fixes/sysklogd.8 2007-05-25 06:37:56.000000000 +0000 +++ sysklogd-1.4.1/sysklogd.8 2007-05-25 06:38:02.000000000 +0000 @@ -30,6 +30,12 @@ .RB [ " \-s " .I domainlist ] +.RB [ " \-u" +.IB username +] +.RB [ " \-j " +.I chroot_dir +] .RB [ " \-v " ] .RB [ " \-x " ] .LP @@ -172,6 +178,32 @@ no domain would be cut, you will have to specify two domains like: .BR "\-s north.de:infodrom.north.de" . .TP +.BI "\-u " "username" +This causes the +.B syslogd +daemon to become the named user before starting up logging. + +Note that when this option is in use, +.B syslogd +will open all log files as root when the daemon is first started; +however, after a +.B SIGHUP +the files will be reopened as the non-privileged user. You should +take this into account when deciding the ownership of the log files. +.TP +.BI "\-j " chroot_dir +Tells +.B syslogd +daemon to +.BR chroot (2) +into this directory after initializing. +This option is only valid if the \-u option is also used to run +.B syslogd +without root privileges. +Note that the use of this option will prevent +.B SIGHUP +from working which makes daemon reload practically impossible. +.TP .B "\-v" Print version and exit. .TP diff -Naur sysklogd-1.4.1.community_fixes/syslogd.c sysklogd-1.4.1/syslogd.c --- sysklogd-1.4.1.community_fixes/syslogd.c 2007-05-25 06:37:56.000000000 +0000 +++ sysklogd-1.4.1/syslogd.c 2007-05-25 06:38:02.000000000 +0000 @@ -494,6 +494,9 @@ #include #include +#include +#include + #include #include #include @@ -756,6 +759,9 @@ int NoHops = 1; /* Can we bounce syslog messages through an intermediate host. */ +char *server_user = NULL; /* user name to run server as */ +char *chroot_dir = NULL; /* user name to run server as */ + extern int errno; /* Function prototypes. */ @@ -803,6 +809,26 @@ static int hosts_equal(struct addrinfo *ai1, struct addrinfo *ai2); static struct addrinfo *not_local_address( char *host ); +static int drop_root(void) +{ + struct passwd *pw; + + if (!(pw = getpwnam(server_user))) return -1; + + if (!pw->pw_uid) return -1; + + if (chroot_dir) { + if (chdir(chroot_dir)) return -1; + if (chroot(".")) return -1; + } + + if (initgroups(server_user, pw->pw_gid)) return -1; + if (setgid(pw->pw_gid)) return -1; + if (setuid(pw->pw_uid)) return -1; + + return 0; +} + int main(argc, argv) int argc; char **argv; @@ -860,7 +886,7 @@ funix[i] = -1; } - while ((ch = getopt(argc, argv, "46Aa:dhf:l:m:np:rs:vx")) != EOF) + while ((ch = getopt(argc, argv, "46Aa:dhf:j:l:m:np:rs:u:vx")) != EOF) switch((char)ch) { case '4': family = PF_INET; @@ -888,9 +914,12 @@ case 'h': NoHops = 0; break; + case 'j': + chroot_dir = optarg; + break; case 'l': if (LocalHosts) { - fprintf (stderr, "Only one -l argument allowed," \ + fprintf(stderr, "Only one -l argument allowed, " "the first one is taken.\n"); break; } @@ -916,6 +945,9 @@ } StripDomains = crunch_list(optarg); break; + case 'u': + server_user = optarg; + break; case 'v': printf("syslogd %s.%s\n", VERSION, PATCHLEVEL); exit (0); @@ -929,6 +961,10 @@ if ((argc -= optind)) usage(); + if (chroot_dir && !server_user) { + fputs("'-j' is only valid with '-u'\n", stderr); + exit(1); + } #ifndef TESTING if ( !(Debug || NoFork) ) { @@ -1079,6 +1115,11 @@ kill (ppid, SIGTERM); #endif + if (server_user && drop_root()) { + dprintf("syslogd: failed to drop root\n"); + exit(1); + } + /* Main loop begins here. */ for (;;) { int nfds; @@ -1247,7 +1288,7 @@ int usage() { fprintf(stderr, "usage: syslogd [-46Adrvxh] [-l hostlist] [-m markinterval] [-n] [-p path]\n" \ - " [-s domainlist] [-f conffile]\n"); + " [-s domainlist] [-f conffile] [-j chrootjail] [-u username]\n"); exit(1); }