# 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.