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