Submitted By: Robert Connolly <robert at linuxfromscratch dot org> (ashes)
Date: 2005-01-23
Initial Package Version: 1.4.1
Upstream Status: Not submitted
Origin: http://cvsweb.openwall.com/cgi/cvsweb.cgi/Owl/packages/sysklogd/
        sysklogd-1.4.1-caen-owl-klogd-drop-root.diff    v1.1
        sysklogd-1.4.1-caen-owl-syslogd-drop-root.diff  v1.1
Description: This patch adds privilege seperation for syslogd and klogd.

diff -Naur sysklogd-1.4.1.orig/klogd.8 sysklogd-1.4.1/klogd.8
--- sysklogd-1.4.1.orig/klogd.8	2005-01-22 18:49:55.000000000 +0000
+++ sysklogd-1.4.1/klogd.8	2005-01-23 10:59:57.208384154 +0000
@@ -3,8 +3,9 @@
 .\" Sun Jul 30 01:35:55 MET: Martin Schulze: Updates
 .\" Sun Nov 19 23:22:21 MET: Martin Schulze: Updates
 .\" Mon Aug 19 09:42:08 CDT 1996: Dr. G.W. Wettstein: Updates
+.\" Thu Feb 17 2000: Chris Wing: Unprivileged klogd feature
 .\"
-.TH KLOGD 8 "21 August, 1999" "Version 1.4" "Linux System Administration"
+.TH KLOGD 8 "8 October, 2001" "Version 1.4.1+CAEN/OW" "Linux System Administration"
 .SH NAME
 klogd \- Kernel Log Daemon
 .LP
@@ -17,6 +18,12 @@
 .RB [ " \-f "
 .I fname
 ]
+.RB [ " \-u "
+.I username
+]
+.RB [ " \-j "
+.I chroot_dir
+]
 .RB [ " \-iI " ]
 .RB [ " \-n " ]
 .RB [ " \-o " ]
@@ -46,6 +53,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
diff -Naur sysklogd-1.4.1.orig/klogd.c sysklogd-1.4.1/klogd.c
--- sysklogd-1.4.1.orig/klogd.c	2005-01-22 18:49:55.000000000 +0000
+++ sysklogd-1.4.1/klogd.c	2005-01-23 10:59:57.212383035 +0000
@@ -258,6 +258,8 @@
 #include <stdarg.h>
 #include <paths.h>
 #include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
 #include "klogd.h"
 #include "ksyms.h"
 #ifndef TESTING
@@ -308,6 +310,9 @@
 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);
@@ -528,8 +533,9 @@
 	 * 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);
@@ -977,6 +983,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 (chroot(chroot_dir)) return -1;
+		if (chdir("/")) 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;
@@ -994,7 +1021,7 @@
 	chdir ("/");
 #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:nopsvx2")) != EOF)
 		switch((char)ch)
 		{
 		    case '2':		/* Print lines with symbols twice. */
@@ -1016,6 +1043,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;
@@ -1031,6 +1062,9 @@
 		    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);
@@ -1039,6 +1073,10 @@
 			break;
 		}
 
+	if (chroot_dir && !server_user) {
+		fputs("'-j' is only valid with '-u'", stderr);
+		exit(1);
+	}
 
 	/* Set console logging level. */
 	if ( log_level != (char *) 0 )
@@ -1136,7 +1174,7 @@
 		}
 	}
 	else
-		openlog("kernel", 0, LOG_KERN);
+		openlog("kernel", log_flags, LOG_KERN);
 
 
 	/* Handle one-shot logging. */
@@ -1163,6 +1201,11 @@
 		InitMsyms();
 	}
 
+	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.orig/sysklogd.8 sysklogd-1.4.1/sysklogd.8
--- sysklogd-1.4.1.orig/sysklogd.8	2005-01-22 18:49:55.000000000 +0000
+++ sysklogd-1.4.1/sysklogd.8	2005-01-23 11:00:07.985367732 +0000
@@ -29,6 +29,9 @@
 .RB [ " \-s "
 .I domainlist
 ]
+.RB [ " \-u"
+.IB username
+]
 .RB [ " \-v " ]
 .LP
 .SH DESCRIPTION
@@ -150,6 +153,19 @@
 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
 .B "\-v"
 Print version and exit.
 .LP
diff -Naur sysklogd-1.4.1.orig/syslogd.c sysklogd-1.4.1/syslogd.c
--- sysklogd-1.4.1.orig/syslogd.c	2005-01-22 18:49:55.000000000 +0000
+++ sysklogd-1.4.1/syslogd.c	2005-01-23 11:05:40.747216393 +0000
@@ -491,6 +491,10 @@
 #include <arpa/nameser.h>
 #include <arpa/inet.h>
 #include <resolv.h>
+
+#include <pwd.h>
+#include <grp.h>
+
 #ifndef TESTING
 #include "pidfile.h"
 #endif
@@ -735,6 +739,7 @@
 char	**LocalHosts = NULL;	/* these hosts are logged with their hostname */
 int	NoHops = 1;		/* Can we bounce syslog messages through an
 				   intermediate host. */
+char	*server_user = NULL;	/* user name to run server as */
 
 extern	int errno;
 
@@ -776,6 +781,21 @@
 static int create_inet_socket();
 #endif
 
+static int drop_root(void)
+{
+	struct passwd *pw;
+
+	if (!(pw = getpwnam(server_user))) return -1;
+
+	if (!pw->pw_uid) 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;
@@ -829,7 +849,7 @@
 		funix[i]  = -1;
 	}
 
-	while ((ch = getopt(argc, argv, "a:dhf:l:m:np:rs:v")) != EOF)
+	while ((ch = getopt(argc, argv, "a:dhf:l:m:np:rs:u:v")) != EOF)
 		switch((char)ch) {
 		case 'a':
 			if (nfunix < MAXFUNIX)
@@ -874,6 +894,9 @@
 			}
 			StripDomains = crunch_list(optarg);
 			break;
+		case 'u':
+			server_user = optarg;
+			break;
 		case 'v':
 			printf("syslogd %s.%s\n", VERSION, PATCHLEVEL);
 			exit (0);
@@ -1021,6 +1044,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;
@@ -1175,7 +1203,7 @@
 int usage()
 {
 	fprintf(stderr, "usage: syslogd [-drvh] [-l hostlist] [-m markinterval] [-n] [-p path]\n" \
-		" [-s domainlist] [-f conffile]\n");
+		" [-s domainlist] [-f conffile] [-u username]\n");
 	exit(1);
 }
 
