From 9dd64e357f485272bc58d3c65b13728d4e475503 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Thu, 24 Apr 2008 12:37:01 +0000 Subject: [PATCH] chroot more tests and more documentation. git-svn-id: file:///svn/unbound/trunk@1067 be551aaa-1e26-0410-a405-d3ace91eadb9 --- daemon/unbound.c | 24 +++++++++++++++++------- doc/Changelog | 4 ++++ doc/example.conf | 20 ++++++++++++++++++-- doc/unbound.conf.5 | 25 ++++++++++++++++++++----- smallapp/unbound-checkconf.c | 27 +++++++++++++++++---------- testdata/07-confroot.tpkg | Bin 1054 -> 2312 bytes 6 files changed, 76 insertions(+), 24 deletions(-) diff --git a/daemon/unbound.c b/daemon/unbound.c index 74b49bae2..9a328ceec 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -295,13 +295,6 @@ do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode, log_assert(cfg); /* daemonize last to be able to print error to user */ - if(cfg->directory && cfg->directory[0]) { - if(chdir(cfg->directory)) { - fatal_exit("Could not chdir to %s: %s", - cfg->directory, strerror(errno)); - } - verbose(VERB_QUERY, "chdir to %s", cfg->directory); - } if(cfg->username && cfg->username[0]) { struct passwd *pwd; if((pwd = getpwnam(cfg->username)) == NULL) @@ -311,6 +304,11 @@ do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode, endpwent(); } if(cfg->chrootdir && cfg->chrootdir[0]) { + if(chdir(cfg->chrootdir)) { + fatal_exit("unable to chdir to chroot %s: %s", + cfg->chrootdir, strerror(errno)); + } + verbose(VERB_QUERY, "chdir to %s", cfg->chrootdir); if(chroot(cfg->chrootdir)) fatal_exit("unable to chroot to %s: %s", cfg->chrootdir, strerror(errno)); @@ -319,6 +317,18 @@ do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode, strlen(cfg->chrootdir)) == 0) (*cfgfile) += strlen(cfg->chrootdir); } + if(cfg->directory && cfg->directory[0]) { + char* dir = cfg->directory; + if(cfg->chrootdir && cfg->chrootdir[0] && + strncmp(dir, cfg->chrootdir, + strlen(cfg->chrootdir)) == 0) + dir += strlen(cfg->chrootdir); + if(chdir(dir)) { + fatal_exit("Could not chdir to %s: %s", + dir, strerror(errno)); + } + verbose(VERB_QUERY, "chdir to %s", dir); + } if(cfg->username && cfg->username[0]) { if(setgid(gid) != 0) fatal_exit("unable to set group id of %s: %s", diff --git a/doc/Changelog b/doc/Changelog index 28da373aa..af2251213 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +24 April 2008: Wouter + - chroot checks improved. working directory relative to chroot. + checks if config file path is inside chroot. Documentation on it. + 23 April 2008: Wouter - parseunbound.pl contrib update from Kai Storbeck for threads. - iana ports update diff --git a/doc/example.conf b/doc/example.conf index 0d2f5c5ff..694a53ee5 100644 --- a/doc/example.conf +++ b/doc/example.conf @@ -144,7 +144,23 @@ server: # if given, a chroot(2) is done to the given directory. # i.e. you can chroot to the working directory, for example, # for extra security, but make sure all files are in that directory. - # If you give "" no chroot is performed. + # + # If chroot is enabled, you should pass the configfile (from the + # commandline) as a full path from the original root. After the + # chroot has been performed the now defunct portion of the config + # file path is removed to be able to reread the config after a reload. + # + # All other file paths (working dir, pidfile, logfile, roothints, + # key files) can be specified in several ways: + # o as an absolute path relative to the new root. + # o as a relative path to the working directory. + # o as an absolute path relative to the original root. + # In the last case the path is adjusted to remove the unused portion. + # + # Additionally, unbound may need to access /dev/random (for entropy) + # and to /dev/log (if you use syslog) from inside the chroot. + # + # If you give "" no chroot is performed. The path must not end in a /. # chroot: "/usr/local/etc/unbound" # if given, user privileges are dropped (after binding port), @@ -152,7 +168,7 @@ server: # If you give "" no privileges are dropped. # username: "unbound" - # the working directory. + # the working directory. "" disables. # directory: "/usr/local/etc/unbound" # the log file, "" means log to stderr. diff --git a/doc/unbound.conf.5 b/doc/unbound.conf.5 index 3caca1e0c..22bcc43c5 100644 --- a/doc/unbound.conf.5 +++ b/doc/unbound.conf.5 @@ -238,8 +238,23 @@ is not designed to handle dropped packets due to policy, and dropping may result in (possibly excessive) retried queries. .TP .B chroot: \fI +If chroot is enabled, you should pass the configfile (from the +commandline) as a full path from the original root. After the +chroot has been performed the now defunct portion of the config +file path is removed to be able to reread the config after a reload. +.IP +All other file paths (working dir, pidfile, logfile, roothints, +key files) can be specified in several ways: +as an absolute path relative to the new root, +as a relative path to the working directory, or +as an absolute path relative to the original root. +In the last case the path is adjusted to remove the unused portion. +.IP +Additionally, unbound may need to access /dev/random (for entropy) +and to /dev/log (if you use syslog) from inside the chroot. +.IP If given a chroot is done to the given directory. The default is -"/etc/unbound". If you give "" no chroot is performed. +"/usr/local/etc/unbound". If you give "" no chroot is performed. .TP .B username: \fI If given, after binding the port the user privileges are dropped. Default is @@ -271,14 +286,14 @@ The logfile setting is overridden when use\-syslog is turned on. The default is to log to syslog. .TP .B pidfile: \fI -The process id is written to the file. Default is "/etc/unbound/unbound.pid". +The process id is written to the file. Default is "/usr/local/etc/unbound/unbound.pid". So, .nf -kill \-HUP `cat /etc/unbound/unbound.pid` +kill \-HUP `cat /usr/local/etc/unbound/unbound.pid` .fi triggers a reload, .nf -kill \-QUIT `cat /etc/unbound/unbound.pid` +kill \-QUIT `cat /usr/local/etc/unbound/unbound.pid` .fi gracefully terminates. .TP @@ -648,7 +663,7 @@ server: .fi .SH "FILES" .TP -.I /etc/unbound +.I /usr/local/etc/unbound default unbound working directory and default \fIchroot\fR(2) location. diff --git a/smallapp/unbound-checkconf.c b/smallapp/unbound-checkconf.c index 73aaea300..44e734464 100644 --- a/smallapp/unbound-checkconf.c +++ b/smallapp/unbound-checkconf.c @@ -297,7 +297,7 @@ check_chroot_filelist(const char* desc, struct config_strlist* list, /** check configuration for errors */ static void -morechecks(struct config_file* cfg) +morechecks(struct config_file* cfg, char* fname) { warn_hosts("stub-host", cfg->stubs); warn_hosts("forward-host", cfg->forwards); @@ -321,14 +321,21 @@ morechecks(struct config_file* cfg) !is_dir(cfg->chrootdir)) { fatal_exit("bad chroot directory"); } - if((cfg->chrootdir && cfg->chrootdir[0]) - && (cfg->directory && cfg->directory[0]) - && strncmp(cfg->chrootdir, cfg->directory, - strlen(cfg->chrootdir)) != 0) { - fatal_exit("chdir directory '%s' not inside the chroot " - "directory '%s'", cfg->directory, cfg->chrootdir); + if(cfg->chrootdir && cfg->chrootdir[0]) { + char buf[10240]; + buf[0] = 0; + if(fname[0] != '/') { + if(getcwd(buf, sizeof(buf)) == NULL) + fatal_exit("getcwd: %s", strerror(errno)); + strncat(buf, "/", sizeof(buf)); + } + strncat(buf, fname, sizeof(buf)); + if(strncmp(buf, cfg->chrootdir, strlen(cfg->chrootdir)) != 0) + fatal_exit("config file %s is not inside chroot %s", + buf, cfg->chrootdir); } - if(cfg->directory && cfg->directory[0] && !is_dir(cfg->directory)) { + if(cfg->directory && cfg->directory[0] && !is_dir( + fname_after_chroot(cfg->directory, cfg, 0))) { fatal_exit("bad chdir directory"); } if( (cfg->chrootdir && cfg->chrootdir[0]) || @@ -341,7 +348,7 @@ morechecks(struct config_file* cfg) if(cfg->logfile && cfg->logfile[0] && basedir(cfg->logfile, cfg) && !is_dir(basedir(cfg->logfile, cfg))) { - fatal_exit("pidfile directory does not exist"); + fatal_exit("logfile directory does not exist"); } } @@ -382,7 +389,7 @@ checkconf(char* cfgfile) config_delete(cfg); exit(1); } - morechecks(cfg); + morechecks(cfg, cfgfile); check_mod(cfg, iter_get_funcblock()); check_mod(cfg, val_get_funcblock()); config_delete(cfg); diff --git a/testdata/07-confroot.tpkg b/testdata/07-confroot.tpkg index e25b089baeca0301af5fa82d908f448ed67daaef..21a97efcd20229a5adadd0ded5834f8dda10575d 100644 GIT binary patch literal 2312 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 Ysgg=6sicxhDtRjTAMFU10{|!h0EtE&hyVZp