[syslog-ng][PATCH] chroot and drop root priviledges

Tommi Virtanen tv-nospam-2a55fc@debian.org
22 Apr 2001 23:08:24 +0300


# This is for the Debian bug tracking system, see http://bugs.debian.org/94898
forwarded 94898 syslog-ng@lists.balabit.hu
thanks

	Here's a patch to implement -C, -u and -g command line options,
        for chrooting, changing uid and gid, respectively. My syslog-ng
        is happily chrooted into /var/log/syslog-ng, and runs as 
        syslogng:syslogng.

        Notes:
        - it still needs write access to the logs; I just chowned the tree
          to syslogng
        - it binds to /dev/log etc before chroot/userid switch, so it still
          works; however, if you change sources and try to reload config, it
          probably won't have access to open the new sources.
        - if you store the config outside the chroot, reload won't work.
        - I had to change the child->parent notify mechanism, as the old 
          kill(getppid(), SIGTERM) trick didn't work after the uid switch.
          Now it has a pipe open to parent, parent waits on read(). I took
          the opportunity to make it signal success to the parent by writing
          a nil byte; parent changes exit value based on whether it saw the
          nil or not.

diff -Naur syslog-ng-1.5.4.original/src/main.c syslog-ng-1.5.4/src/main.c
--- syslog-ng-1.5.4.original/src/main.c	Wed Nov 29 13:21:31 2000
+++ syslog-ng-1.5.4/src/main.c	Sun Apr 22 21:36:14 2001
@@ -38,6 +38,8 @@
 #include <errno.h>
 #include <string.h>
 #include <getopt.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include "main.c.x"
 
@@ -186,13 +188,29 @@
 	return 0;
 }
 
+#define READ 0
+#define WRITE 1
 void go_background()
 {
 	pid_t pid;
-	
+	int pipefd[2];
+	char buf;
+
+	if (pipe(pipefd) < 0) {
+		werror("Cannot pipe(), (%z)\n", strerror(errno));
+		exit(1);
+	}
 	pid = fork();
 	if (pid == 0) {
-		int fd = open(pidfilename, O_CREAT | O_WRONLY | O_NOCTTY | O_TRUNC, 0600);
+	  int fd;
+	  if (pipefd[WRITE]!=1) {
+	    if (dup2(pipefd[WRITE], 1) < 0) {
+		werror("Cannot set pipe as stdout, (%z)\n", strerror(errno));
+		exit(1);
+	    }
+	    (void) close(pipefd[WRITE]);
+	  }
+		fd = open(pidfilename, O_CREAT | O_WRONLY | O_NOCTTY | O_TRUNC, 0600);
 		if (fd != -1) {
 			struct ol_string *pid_s = c_format("%i\n", getpid());
 			write(fd, pid_s->data, pid_s->length);
@@ -205,10 +223,13 @@
 		werror("Cannot fork(), (%z)\n", strerror(errno));
 		exit(1);
 	}
-	signal(SIGTERM, sig_term);
-	while (sigtermrecvd == 0)
-		pause();
-	exit(0);
+
+	if (read(pipefd[READ], &buf, 1) == 1
+	    && buf=='\0') {
+	  exit(0);
+	} else {
+	  exit(1);
+	}
 }
 
 void usage(void)
@@ -221,6 +242,9 @@
 	       "  -f <fname>, --cfgfile=<fname>    Set config file name, default=" PATH_SYSLOG_NG_CONF "\n"
 	       "  -V, --version                    Display version number (" PACKAGE " " VERSION ")\n"
 	       "  -p <fname>, --pidfile=<fname>    Set pid file name, default=" PATH_PIDFILE "\n"
+	       "  -C <dir>, --chroot=<dir>	   Chroot to directory\n"
+	       "  -u <user>, --user=<user>	   Switch to user\n"
+	       "  -g <group>, --group=<group>	   Switch to group\n"
 #ifdef YYDEBUG
 	       "  -y, --yydebug                    Turn on yacc debug messages\n"
 #endif
@@ -233,10 +257,45 @@
 extern int yydebug;
 #endif
 
+void nametouidgid(const char* user, uid_t *uid, gid_t *gid)
+{
+	struct passwd* pw;
+	pw = getpwnam(user);
+
+	if (pw) {
+		*uid=pw->pw_uid;
+		if (gid)
+		  *gid=pw->pw_gid;
+	}
+}
+
+void nametogid(const char* group, gid_t *gid)
+{
+	struct group* gr;
+	gr = getgrnam(group);
+
+	if (gr) {
+		*gid=gr->gr_gid;
+	}
+}
+
+int dropprivileges(uid_t uid, gid_t gid)
+{
+  if ( setgid(gid) != 0 || setuid(uid) != 0 ) {
+    return -1;
+  } else {
+    return 0;
+  }  
+}
+
+
 int main(int argc, char **argv)
 {
 	int do_fork = 1;
 	int opt;
+	char *chrootdir = NULL;
+	uid_t uid = 0;
+	gid_t gid = 0;
 
 	NEW(syslog_backend, backend);
 	struct option syslog_ng_options[] = {
@@ -246,6 +305,9 @@
 		{ "verbose", no_argument, NULL, 'v' },
 		{ "help", no_argument, NULL, 'h' },
 		{ "version", no_argument, NULL, 'V' },
+		{ "chroot", required_argument, NULL, 'C' },
+		{ "user", required_argument, NULL, 'u' },
+		{ "group", required_argument, NULL, 'g' },
 #ifdef YYDEBUG
 		{ "yydebug", no_argument, NULL, 'y' },
 #endif
@@ -257,7 +319,7 @@
 	gc_idle_threshold = 100;
 	gc_busy_threshold = 3000;
 
-	while ((opt = getopt_long(argc, argv, "f:p:dvhyV", syslog_ng_options, NULL)) != -1) {
+	while ((opt = getopt_long(argc, argv, "f:p:dvhC:u:yV", syslog_ng_options, NULL)) != -1) {
 		switch (opt) {
 		case 'f':
 			strncpy(cfgfilename, optarg, sizeof(cfgfilename));
@@ -265,6 +327,21 @@
 		case 'p':
 			strncpy(pidfilename, optarg, sizeof(pidfilename));
 			break;
+		case 'C':
+		  	if (!optarg[0])
+		    		usage();
+		  	chrootdir = optarg;
+			break;
+		case 'u':
+			nametouidgid(optarg, &uid, gid ? NULL : &gid);
+     			if (uid == 0 || gid == 0)
+		    		usage();
+			break;
+		case 'g':
+			nametogid(optarg, &gid);
+     			if (gid == 0)
+		    		usage();
+			break;
 		case 'd':
 			debug_flag++;
 			break;
@@ -286,6 +363,8 @@
 		}
 	}
 
+	if (uid == 0 && gid != 0)
+	  werror("Cannot use -g without -u");
 	init_backend(&backend->super);
 	
 	backend->configuration = make_syslog_config(cfgfilename, &backend->super);
@@ -301,17 +380,28 @@
 	}
 	if (!CONFIG_INIT(backend->configuration, NULL)) {
 		werror("Error initializing configuration, exiting.\n");
-		if (do_fork) kill(getppid(), SIGTERM);
 		return 2;
 	}
+	if (chrootdir) {
+		if (chroot(chrootdir) < 0) {
+			werror("Error chrooting, exiting.\n");
+			return 3;
+		}
+	}
+	if (uid) {
+		if (dropprivileges(uid, gid) < 0) {
+			werror("Error in setuid/setgid, exiting.\n");
+			return 4;
+		}
+	}
 
 	if (do_fork) {
 		set_error_internal();
 		close(0);
-		close(1);
 		close(2);
 		setsid();
-		kill(getppid(), SIGTERM); 
+		write(1, "", 1);
+		close(1);
 	}
 	notice("syslog-ng version %z starting\n", VERSION);
 	return main_loop(backend);

-- 
tv@{{hq.yok.utu,havoc,gaeshido}.fi,{debian,wanderer}.org,stonesoft.com}
unix, linux, debian, networks, security, | First snow, then silence.
kernel, TCP/IP, C, perl, free software,  | This thousand dollar screen dies
mail, www, sw devel, unix admin, hacks.  | so beautifully.