os.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004-2011, 2013-2015  Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 1999-2002  Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and/or distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 /*! \file */
00019 
00020 #include <config.h>
00021 #include <stdarg.h>
00022 
00023 #include <sys/types.h>  /* dev_t FreeBSD 2.1 */
00024 #include <sys/stat.h>
00025 
00026 #include <ctype.h>
00027 #include <errno.h>
00028 #include <fcntl.h>
00029 #include <grp.h>                /* Required for initgroups() on IRIX. */
00030 #include <pwd.h>
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <signal.h>
00034 #include <syslog.h>
00035 #ifdef HAVE_TZSET
00036 #include <time.h>
00037 #endif
00038 #include <unistd.h>
00039 
00040 #include <isc/buffer.h>
00041 #include <isc/file.h>
00042 #include <isc/print.h>
00043 #include <isc/resource.h>
00044 #include <isc/result.h>
00045 #include <isc/strerror.h>
00046 #include <isc/string.h>
00047 
00048 #include <named/main.h>
00049 #include <named/os.h>
00050 #ifdef HAVE_LIBSCF
00051 #include <named/ns_smf_globals.h>
00052 #endif
00053 
00054 static char *pidfile = NULL;
00055 static char *lockfile = NULL;
00056 static int devnullfd = -1;
00057 static int singletonfd = -1;
00058 
00059 #ifndef ISC_FACILITY
00060 #define ISC_FACILITY LOG_DAEMON
00061 #endif
00062 
00063 /*
00064  * If there's no <linux/capability.h>, we don't care about <sys/prctl.h>
00065  */
00066 #ifndef HAVE_LINUX_CAPABILITY_H
00067 #undef HAVE_SYS_PRCTL_H
00068 #endif
00069 
00070 /*
00071  * Linux defines:
00072  *      (T) HAVE_LINUXTHREADS
00073  *      (C) HAVE_SYS_CAPABILITY_H (or HAVE_LINUX_CAPABILITY_H)
00074  *      (P) HAVE_SYS_PRCTL_H
00075  * The possible cases are:
00076  *      none:   setuid() normally
00077  *      T:      no setuid()
00078  *      C:      setuid() normally, drop caps (keep CAP_SETUID)
00079  *      T+C:    no setuid(), drop caps (don't keep CAP_SETUID)
00080  *      T+C+P:  setuid() early, drop caps (keep CAP_SETUID)
00081  *      C+P:    setuid() normally, drop caps (keep CAP_SETUID)
00082  *      P:      not possible
00083  *      T+P:    not possible
00084  *
00085  * if (C)
00086  *      caps = BIND_SERVICE + CHROOT + SETGID
00087  *      if ((T && C && P) || !T)
00088  *              caps += SETUID
00089  *      endif
00090  *      capset(caps)
00091  * endif
00092  * if (T && C && P && -u)
00093  *      setuid()
00094  * else if (T && -u)
00095  *      fail
00096  * --> start threads
00097  * if (!T && -u)
00098  *      setuid()
00099  * if (C && (P || !-u))
00100  *      caps = BIND_SERVICE
00101  *      capset(caps)
00102  * endif
00103  *
00104  * It will be nice when Linux threads work properly with setuid().
00105  */
00106 
00107 #ifdef HAVE_LINUXTHREADS
00108 static pid_t mainpid = 0;
00109 #endif
00110 
00111 static struct passwd *runas_pw = NULL;
00112 static isc_boolean_t done_setuid = ISC_FALSE;
00113 static int dfd[2] = { -1, -1 };
00114 
00115 #ifdef HAVE_LINUX_CAPABILITY_H
00116 
00117 static isc_boolean_t non_root = ISC_FALSE;
00118 static isc_boolean_t non_root_caps = ISC_FALSE;
00119 
00120 #ifdef HAVE_SYS_CAPABILITY_H
00121 #include <sys/capability.h>
00122 #else
00123 #ifdef HAVE_LINUX_TYPES_H
00124 #include <linux/types.h>
00125 #endif
00126 /*%
00127  * We define _LINUX_FS_H to prevent it from being included.  We don't need
00128  * anything from it, and the files it includes cause warnings with 2.2
00129  * kernels, and compilation failures (due to conflicts between <linux/string.h>
00130  * and <string.h>) on 2.3 kernels.
00131  */
00132 #define _LINUX_FS_H
00133 #include <linux/capability.h>
00134 #include <syscall.h>
00135 #ifndef SYS_capset
00136 #ifndef __NR_capset
00137 #include <asm/unistd.h> /* Slackware 4.0 needs this. */
00138 #endif /* __NR_capset */
00139 #define SYS_capset __NR_capset
00140 #endif /* SYS_capset */
00141 #endif /* HAVE_SYS_CAPABILITY_H */
00142 
00143 #ifdef HAVE_SYS_PRCTL_H
00144 #include <sys/prctl.h>          /* Required for prctl(). */
00145 
00146 /*
00147  * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it
00148  * here.  This allows setuid() to work on systems running a new enough
00149  * kernel but with /usr/include/linux pointing to "standard" kernel
00150  * headers.
00151  */
00152 #ifndef PR_SET_KEEPCAPS
00153 #define PR_SET_KEEPCAPS 8
00154 #endif
00155 
00156 #endif /* HAVE_SYS_PRCTL_H */
00157 
00158 #ifdef HAVE_LIBCAP
00159 #define SETCAPS_FUNC "cap_set_proc "
00160 #else
00161 typedef unsigned int cap_t;
00162 #define SETCAPS_FUNC "syscall(capset) "
00163 #endif /* HAVE_LIBCAP */
00164 
00165 static void
00166 linux_setcaps(cap_t caps) {
00167 #ifndef HAVE_LIBCAP
00168         struct __user_cap_header_struct caphead;
00169         struct __user_cap_data_struct cap;
00170 #endif
00171         char strbuf[ISC_STRERRORSIZE];
00172 
00173         if ((getuid() != 0 && !non_root_caps) || non_root)
00174                 return;
00175 #ifndef HAVE_LIBCAP
00176         memset(&caphead, 0, sizeof(caphead));
00177         caphead.version = _LINUX_CAPABILITY_VERSION;
00178         caphead.pid = 0;
00179         memset(&cap, 0, sizeof(cap));
00180         cap.effective = caps;
00181         cap.permitted = caps;
00182         cap.inheritable = 0;
00183 #endif
00184 #ifdef HAVE_LIBCAP
00185         if (cap_set_proc(caps) < 0) {
00186 #else
00187         if (syscall(SYS_capset, &caphead, &cap) < 0) {
00188 #endif
00189                 isc__strerror(errno, strbuf, sizeof(strbuf));
00190                 ns_main_earlyfatal(SETCAPS_FUNC "failed: %s:"
00191                                    " please ensure that the capset kernel"
00192                                    " module is loaded.  see insmod(8)",
00193                                    strbuf);
00194         }
00195 }
00196 
00197 #ifdef HAVE_LIBCAP
00198 #define SET_CAP(flag) \
00199         do { \
00200                 cap_flag_value_t curval; \
00201                 capval = (flag); \
00202                 err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval); \
00203                 if (err != -1 && curval) { \
00204                         err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET); \
00205                         if (err == -1) { \
00206                                 isc__strerror(errno, strbuf, sizeof(strbuf)); \
00207                                 ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
00208                         } \
00209                         \
00210                         err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET); \
00211                         if (err == -1) { \
00212                                 isc__strerror(errno, strbuf, sizeof(strbuf)); \
00213                                 ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
00214                         } \
00215                 } \
00216         } while (0)
00217 #define INIT_CAP \
00218         do { \
00219                 caps = cap_init(); \
00220                 if (caps == NULL) { \
00221                         isc__strerror(errno, strbuf, sizeof(strbuf)); \
00222                         ns_main_earlyfatal("cap_init failed: %s", strbuf); \
00223                 } \
00224                 curcaps = cap_get_proc(); \
00225                 if (curcaps == NULL) { \
00226                         isc__strerror(errno, strbuf, sizeof(strbuf)); \
00227                         ns_main_earlyfatal("cap_get_proc failed: %s", strbuf); \
00228                 } \
00229         } while (0)
00230 #define FREE_CAP \
00231         { \
00232                 cap_free(caps); \
00233                 cap_free(curcaps); \
00234         } while (0)
00235 #else
00236 #define SET_CAP(flag) do { caps |= (1 << (flag)); } while (0)
00237 #define INIT_CAP do { caps = 0; } while (0)
00238 #endif /* HAVE_LIBCAP */
00239 
00240 static void
00241 linux_initialprivs(void) {
00242         cap_t caps;
00243 #ifdef HAVE_LIBCAP
00244         cap_t curcaps;
00245         cap_value_t capval;
00246         char strbuf[ISC_STRERRORSIZE];
00247         int err;
00248 #endif
00249 
00250         /*%
00251          * We don't need most privileges, so we drop them right away.
00252          * Later on linux_minprivs() will be called, which will drop our
00253          * capabilities to the minimum needed to run the server.
00254          */
00255         INIT_CAP;
00256 
00257         /*
00258          * We need to be able to bind() to privileged ports, notably port 53!
00259          */
00260         SET_CAP(CAP_NET_BIND_SERVICE);
00261 
00262         /*
00263          * We need chroot() initially too.
00264          */
00265         SET_CAP(CAP_SYS_CHROOT);
00266 
00267 #if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
00268         /*
00269          * We can setuid() only if either the kernel supports keeping
00270          * capabilities after setuid() (which we don't know until we've
00271          * tried) or we're not using threads.  If either of these is
00272          * true, we want the setuid capability.
00273          */
00274         SET_CAP(CAP_SETUID);
00275 #endif
00276 
00277         /*
00278          * Since we call initgroups, we need this.
00279          */
00280         SET_CAP(CAP_SETGID);
00281 
00282         /*
00283          * Without this, we run into problems reading a configuration file
00284          * owned by a non-root user and non-world-readable on startup.
00285          */
00286         SET_CAP(CAP_DAC_READ_SEARCH);
00287 
00288         /*
00289          * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
00290          *      clear it would work right given the way linuxthreads work.
00291          * XXXDCL But since we need to be able to set the maximum number
00292          * of files, the stack size, data size, and core dump size to
00293          * support named.conf options, this is now being added to test.
00294          */
00295         SET_CAP(CAP_SYS_RESOURCE);
00296 
00297         /*
00298          * We need to be able to set the ownership of the containing
00299          * directory of the pid file when we create it.
00300          */
00301         SET_CAP(CAP_CHOWN);
00302 
00303         linux_setcaps(caps);
00304 
00305 #ifdef HAVE_LIBCAP
00306         FREE_CAP;
00307 #endif
00308 }
00309 
00310 static void
00311 linux_minprivs(void) {
00312         cap_t caps;
00313 #ifdef HAVE_LIBCAP
00314         cap_t curcaps;
00315         cap_value_t capval;
00316         char strbuf[ISC_STRERRORSIZE];
00317         int err;
00318 #endif
00319 
00320         INIT_CAP;
00321         /*%
00322          * Drop all privileges except the ability to bind() to privileged
00323          * ports.
00324          *
00325          * It's important that we drop CAP_SYS_CHROOT.  If we didn't, it
00326          * chroot() could be used to escape from the chrooted area.
00327          */
00328 
00329         SET_CAP(CAP_NET_BIND_SERVICE);
00330 
00331         /*
00332          * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
00333          *      clear it would work right given the way linuxthreads work.
00334          * XXXDCL But since we need to be able to set the maximum number
00335          * of files, the stack size, data size, and core dump size to
00336          * support named.conf options, this is now being added to test.
00337          */
00338         SET_CAP(CAP_SYS_RESOURCE);
00339 
00340         linux_setcaps(caps);
00341 
00342 #ifdef HAVE_LIBCAP
00343         FREE_CAP;
00344 #endif
00345 }
00346 
00347 #ifdef HAVE_SYS_PRCTL_H
00348 static void
00349 linux_keepcaps(void) {
00350         char strbuf[ISC_STRERRORSIZE];
00351         /*%
00352          * Ask the kernel to allow us to keep our capabilities after we
00353          * setuid().
00354          */
00355 
00356         if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
00357                 if (errno != EINVAL) {
00358                         isc__strerror(errno, strbuf, sizeof(strbuf));
00359                         ns_main_earlyfatal("prctl() failed: %s", strbuf);
00360                 }
00361         } else {
00362                 non_root_caps = ISC_TRUE;
00363                 if (getuid() != 0)
00364                         non_root = ISC_TRUE;
00365         }
00366 }
00367 #endif
00368 
00369 #endif  /* HAVE_LINUX_CAPABILITY_H */
00370 
00371 
00372 static void
00373 setup_syslog(const char *progname) {
00374         int options;
00375 
00376         options = LOG_PID;
00377 #ifdef LOG_NDELAY
00378         options |= LOG_NDELAY;
00379 #endif
00380         openlog(isc_file_basename(progname), options, ISC_FACILITY);
00381 }
00382 
00383 void
00384 ns_os_init(const char *progname) {
00385         setup_syslog(progname);
00386 #ifdef HAVE_LINUX_CAPABILITY_H
00387         linux_initialprivs();
00388 #endif
00389 #ifdef HAVE_LINUXTHREADS
00390         mainpid = getpid();
00391 #endif
00392 #ifdef SIGXFSZ
00393         signal(SIGXFSZ, SIG_IGN);
00394 #endif
00395 }
00396 
00397 void
00398 ns_os_daemonize(void) {
00399         pid_t pid;
00400         char strbuf[ISC_STRERRORSIZE];
00401 
00402         if (pipe(dfd) == -1) {
00403                 isc__strerror(errno, strbuf, sizeof(strbuf));
00404                 ns_main_earlyfatal("pipe(): %s", strbuf);
00405         }
00406 
00407         pid = fork();
00408         if (pid == -1) {
00409                 isc__strerror(errno, strbuf, sizeof(strbuf));
00410                 ns_main_earlyfatal("fork(): %s", strbuf);
00411         }
00412         if (pid != 0) {
00413                 int n;
00414                 /*
00415                  * Wait for the child to finish loading for the first time.
00416                  * This would be so much simpler if fork() worked once we
00417                  * were multi-threaded.
00418                  */
00419                 (void)close(dfd[1]);
00420                 do {
00421                         char buf;
00422                         n = read(dfd[0], &buf, 1);
00423                         if (n == 1)
00424                                 _exit(0);
00425                 } while (n == -1 && errno == EINTR);
00426                 _exit(1);
00427         }
00428         (void)close(dfd[0]);
00429 
00430         /*
00431          * We're the child.
00432          */
00433 
00434 #ifdef HAVE_LINUXTHREADS
00435         mainpid = getpid();
00436 #endif
00437 
00438         if (setsid() == -1) {
00439                 isc__strerror(errno, strbuf, sizeof(strbuf));
00440                 ns_main_earlyfatal("setsid(): %s", strbuf);
00441         }
00442 
00443         /*
00444          * Try to set stdin, stdout, and stderr to /dev/null, but press
00445          * on even if it fails.
00446          *
00447          * XXXMLG The close() calls here are unneeded on all but NetBSD, but
00448          * are harmless to include everywhere.  dup2() is supposed to close
00449          * the FD if it is in use, but unproven-pthreads-0.16 is broken
00450          * and will end up closing the wrong FD.  This will be fixed eventually,
00451          * and these calls will be removed.
00452          */
00453         if (devnullfd != -1) {
00454                 if (devnullfd != STDIN_FILENO) {
00455                         (void)close(STDIN_FILENO);
00456                         (void)dup2(devnullfd, STDIN_FILENO);
00457                 }
00458                 if (devnullfd != STDOUT_FILENO) {
00459                         (void)close(STDOUT_FILENO);
00460                         (void)dup2(devnullfd, STDOUT_FILENO);
00461                 }
00462                 if (devnullfd != STDERR_FILENO) {
00463                         (void)close(STDERR_FILENO);
00464                         (void)dup2(devnullfd, STDERR_FILENO);
00465                 }
00466         }
00467 }
00468 
00469 void
00470 ns_os_started(void) {
00471         char buf = 0;
00472 
00473         /*
00474          * Signal to the parent that we started successfully.
00475          */
00476         if (dfd[0] != -1 && dfd[1] != -1) {
00477                 if (write(dfd[1], &buf, 1) != 1)
00478                         ns_main_earlyfatal("unable to signal parent that we "
00479                                            "otherwise started successfully.");
00480                 close(dfd[1]);
00481                 dfd[0] = dfd[1] = -1;
00482         }
00483 }
00484 
00485 void
00486 ns_os_opendevnull(void) {
00487         devnullfd = open("/dev/null", O_RDWR, 0);
00488 }
00489 
00490 void
00491 ns_os_closedevnull(void) {
00492         if (devnullfd != STDIN_FILENO &&
00493             devnullfd != STDOUT_FILENO &&
00494             devnullfd != STDERR_FILENO) {
00495                 close(devnullfd);
00496                 devnullfd = -1;
00497         }
00498 }
00499 
00500 static isc_boolean_t
00501 all_digits(const char *s) {
00502         if (*s == '\0')
00503                 return (ISC_FALSE);
00504         while (*s != '\0') {
00505                 if (!isdigit((*s)&0xff))
00506                         return (ISC_FALSE);
00507                 s++;
00508         }
00509         return (ISC_TRUE);
00510 }
00511 
00512 void
00513 ns_os_chroot(const char *root) {
00514         char strbuf[ISC_STRERRORSIZE];
00515 #ifdef HAVE_LIBSCF
00516         ns_smf_chroot = 0;
00517 #endif
00518         if (root != NULL) {
00519 #ifdef HAVE_CHROOT
00520                 if (chroot(root) < 0) {
00521                         isc__strerror(errno, strbuf, sizeof(strbuf));
00522                         ns_main_earlyfatal("chroot(): %s", strbuf);
00523                 }
00524 #else
00525                 ns_main_earlyfatal("chroot(): disabled");
00526 #endif
00527                 if (chdir("/") < 0) {
00528                         isc__strerror(errno, strbuf, sizeof(strbuf));
00529                         ns_main_earlyfatal("chdir(/): %s", strbuf);
00530                 }
00531 #ifdef HAVE_LIBSCF
00532                 /* Set ns_smf_chroot flag on successful chroot. */
00533                 ns_smf_chroot = 1;
00534 #endif
00535         }
00536 }
00537 
00538 void
00539 ns_os_inituserinfo(const char *username) {
00540         char strbuf[ISC_STRERRORSIZE];
00541         if (username == NULL)
00542                 return;
00543 
00544         if (all_digits(username))
00545                 runas_pw = getpwuid((uid_t)atoi(username));
00546         else
00547                 runas_pw = getpwnam(username);
00548         endpwent();
00549 
00550         if (runas_pw == NULL)
00551                 ns_main_earlyfatal("user '%s' unknown", username);
00552 
00553         if (getuid() == 0) {
00554                 if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
00555                         isc__strerror(errno, strbuf, sizeof(strbuf));
00556                         ns_main_earlyfatal("initgroups(): %s", strbuf);
00557                 }
00558         }
00559 
00560 }
00561 
00562 void
00563 ns_os_changeuser(void) {
00564         char strbuf[ISC_STRERRORSIZE];
00565         if (runas_pw == NULL || done_setuid)
00566                 return;
00567 
00568         done_setuid = ISC_TRUE;
00569 
00570 #ifdef HAVE_LINUXTHREADS
00571 #ifdef HAVE_LINUX_CAPABILITY_H
00572         if (!non_root_caps)
00573                 ns_main_earlyfatal("-u with Linux threads not supported: "
00574                                    "requires kernel support for "
00575                                    "prctl(PR_SET_KEEPCAPS)");
00576 #else
00577         ns_main_earlyfatal("-u with Linux threads not supported: "
00578                            "no capabilities support or capabilities "
00579                            "disabled at build time");
00580 #endif
00581 #endif
00582 
00583         if (setgid(runas_pw->pw_gid) < 0) {
00584                 isc__strerror(errno, strbuf, sizeof(strbuf));
00585                 ns_main_earlyfatal("setgid(): %s", strbuf);
00586         }
00587 
00588         if (setuid(runas_pw->pw_uid) < 0) {
00589                 isc__strerror(errno, strbuf, sizeof(strbuf));
00590                 ns_main_earlyfatal("setuid(): %s", strbuf);
00591         }
00592 
00593 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
00594         /*
00595          * Restore the ability of named to drop core after the setuid()
00596          * call has disabled it.
00597          */
00598         if (prctl(PR_SET_DUMPABLE,1,0,0,0) < 0) {
00599                 isc__strerror(errno, strbuf, sizeof(strbuf));
00600                 ns_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s",
00601                                      strbuf);
00602         }
00603 #endif
00604 #if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
00605         linux_minprivs();
00606 #endif
00607 }
00608 
00609 void
00610 ns_os_adjustnofile(void) {
00611 #ifdef HAVE_LINUXTHREADS
00612         isc_result_t result;
00613         isc_resourcevalue_t newvalue;
00614 
00615         /*
00616          * Linux: max number of open files specified by one thread doesn't seem
00617          * to apply to other threads on Linux.
00618          */
00619         newvalue = ISC_RESOURCE_UNLIMITED;
00620 
00621         result = isc_resource_setlimit(isc_resource_openfiles, newvalue);
00622         if (result != ISC_R_SUCCESS)
00623                 ns_main_earlywarning("couldn't adjust limit on open files");
00624 #endif
00625 }
00626 
00627 void
00628 ns_os_minprivs(void) {
00629 #ifdef HAVE_SYS_PRCTL_H
00630         linux_keepcaps();
00631 #endif
00632 
00633 #ifdef HAVE_LINUXTHREADS
00634         ns_os_changeuser(); /* Call setuid() before threads are started */
00635 #endif
00636 
00637 #if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
00638         linux_minprivs();
00639 #endif
00640 }
00641 
00642 static int
00643 safe_open(const char *filename, mode_t mode, isc_boolean_t append) {
00644         int fd;
00645         struct stat sb;
00646 
00647         if (stat(filename, &sb) == -1) {
00648                 if (errno != ENOENT)
00649                         return (-1);
00650         } else if ((sb.st_mode & S_IFREG) == 0) {
00651                 errno = EOPNOTSUPP;
00652                 return (-1);
00653         }
00654 
00655         if (append)
00656                 fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, mode);
00657         else {
00658                 if (unlink(filename) < 0 && errno != ENOENT)
00659                         return (-1);
00660                 fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, mode);
00661         }
00662         return (fd);
00663 }
00664 
00665 static void
00666 cleanup_pidfile(void) {
00667         int n;
00668         if (pidfile != NULL) {
00669                 n = unlink(pidfile);
00670                 if (n == -1 && errno != ENOENT)
00671                         ns_main_earlywarning("unlink '%s': failed", pidfile);
00672                 free(pidfile);
00673         }
00674         pidfile = NULL;
00675 }
00676 
00677 static void
00678 cleanup_lockfile(void) {
00679         if (singletonfd != -1) {
00680                 close(singletonfd);
00681                 singletonfd = -1;
00682         }
00683 
00684         if (lockfile != NULL) {
00685                 int n = unlink(lockfile);
00686                 if (n == -1 && errno != ENOENT)
00687                         ns_main_earlywarning("unlink '%s': failed", lockfile);
00688                 free(lockfile);
00689                 lockfile = NULL;
00690         }
00691 }
00692 
00693 /*
00694  * Ensure that a directory exists.
00695  * NOTE: This function overwrites the '/' characters in 'filename' with
00696  * nulls. The caller should copy the filename to a fresh buffer first.
00697  */
00698 static int
00699 mkdirpath(char *filename, void (*report)(const char *, ...)) {
00700         char *slash = strrchr(filename, '/');
00701         char strbuf[ISC_STRERRORSIZE];
00702         unsigned int mode;
00703 
00704         if (slash != NULL && slash != filename) {
00705                 struct stat sb;
00706                 *slash = '\0';
00707 
00708                 if (stat(filename, &sb) == -1) {
00709                         if (errno != ENOENT) {
00710                                 isc__strerror(errno, strbuf, sizeof(strbuf));
00711                                 (*report)("couldn't stat '%s': %s", filename,
00712                                           strbuf);
00713                                 goto error;
00714                         }
00715                         if (mkdirpath(filename, report) == -1)
00716                                 goto error;
00717                         /*
00718                          * Handle "//", "/./" and "/../" in path.
00719                          */
00720                         if (!strcmp(slash + 1, "") ||
00721                             !strcmp(slash + 1, ".") ||
00722                             !strcmp(slash + 1, "..")) {
00723                                 *slash = '/';
00724                                 return (0);
00725                         }
00726                         mode = S_IRUSR | S_IWUSR | S_IXUSR;     /* u=rwx */
00727                         mode |= S_IRGRP | S_IXGRP;              /* g=rx */
00728                         mode |= S_IROTH | S_IXOTH;              /* o=rx */
00729                         if (mkdir(filename, mode) == -1) {
00730                                 isc__strerror(errno, strbuf, sizeof(strbuf));
00731                                 (*report)("couldn't mkdir '%s': %s", filename,
00732                                           strbuf);
00733                                 goto error;
00734                         }
00735                         if (runas_pw != NULL &&
00736                             chown(filename, runas_pw->pw_uid,
00737                                   runas_pw->pw_gid) == -1) {
00738                                 isc__strerror(errno, strbuf, sizeof(strbuf));
00739                                 (*report)("couldn't chown '%s': %s", filename,
00740                                           strbuf);
00741                         }
00742                 }
00743                 *slash = '/';
00744         }
00745         return (0);
00746 
00747  error:
00748         *slash = '/';
00749         return (-1);
00750 }
00751 
00752 static void
00753 setperms(uid_t uid, gid_t gid) {
00754         char strbuf[ISC_STRERRORSIZE];
00755 #if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID)
00756         gid_t oldgid, tmpg;
00757 #endif
00758 #if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID)
00759         uid_t olduid, tmpu;
00760 #endif
00761 #if defined(HAVE_SETEGID)
00762         if (getegid() != gid && setegid(gid) == -1) {
00763                 isc__strerror(errno, strbuf, sizeof(strbuf));
00764                 ns_main_earlywarning("unable to set effective gid to %ld: %s",
00765                                      (long)gid, strbuf);
00766         }
00767 #elif defined(HAVE_SETRESGID)
00768         if (getresgid(&tmpg, &oldgid, &tmpg) == -1 || oldgid != gid) {
00769                 if (setresgid(-1, gid, -1) == -1) {
00770                         isc__strerror(errno, strbuf, sizeof(strbuf));
00771                         ns_main_earlywarning("unable to set effective "
00772                                              "gid to %d: %s", gid, strbuf);
00773                 }
00774         }
00775 #endif
00776 
00777 #if defined(HAVE_SETEUID)
00778         if (geteuid() != uid && seteuid(uid) == -1) {
00779                 isc__strerror(errno, strbuf, sizeof(strbuf));
00780                 ns_main_earlywarning("unable to set effective uid to %ld: %s",
00781                                      (long)uid, strbuf);
00782         }
00783 #elif defined(HAVE_SETRESUID)
00784         if (getresuid(&tmpu, &olduid, &tmpu) == -1 || olduid != uid) {
00785                 if (setresuid(-1, uid, -1) == -1) {
00786                         isc__strerror(errno, strbuf, sizeof(strbuf));
00787                         ns_main_earlywarning("unable to set effective "
00788                                              "uid to %d: %s", uid, strbuf);
00789                 }
00790         }
00791 #endif
00792 }
00793 
00794 FILE *
00795 ns_os_openfile(const char *filename, mode_t mode, isc_boolean_t switch_user) {
00796         char strbuf[ISC_STRERRORSIZE], *f;
00797         FILE *fp;
00798         int fd;
00799 
00800         /*
00801          * Make the containing directory if it doesn't exist.
00802          */
00803         f = strdup(filename);
00804         if (f == NULL) {
00805                 isc__strerror(errno, strbuf, sizeof(strbuf));
00806                 ns_main_earlywarning("couldn't strdup() '%s': %s",
00807                                      filename, strbuf);
00808                 return (NULL);
00809         }
00810         if (mkdirpath(f, ns_main_earlywarning) == -1) {
00811                 free(f);
00812                 return (NULL);
00813         }
00814         free(f);
00815 
00816         if (switch_user && runas_pw != NULL) {
00817 #ifndef HAVE_LINUXTHREADS
00818                 gid_t oldgid = getgid();
00819 #endif
00820                 /* Set UID/GID to the one we'll be running with eventually */
00821                 setperms(runas_pw->pw_uid, runas_pw->pw_gid);
00822 
00823                 fd = safe_open(filename, mode, ISC_FALSE);
00824 
00825 #ifndef HAVE_LINUXTHREADS
00826                 /* Restore UID/GID to root */
00827                 setperms(0, oldgid);
00828 #endif /* HAVE_LINUXTHREADS */
00829 
00830                 if (fd == -1) {
00831 #ifndef HAVE_LINUXTHREADS
00832                         fd = safe_open(filename, mode, ISC_FALSE);
00833                         if (fd != -1) {
00834                                 ns_main_earlywarning("Required root "
00835                                                      "permissions to open "
00836                                                      "'%s'.", filename);
00837                         } else {
00838                                 ns_main_earlywarning("Could not open "
00839                                                      "'%s'.", filename);
00840                         }
00841                         ns_main_earlywarning("Please check file and "
00842                                              "directory permissions "
00843                                              "or reconfigure the filename.");
00844 #else /* HAVE_LINUXTHREADS */
00845                         ns_main_earlywarning("Could not open "
00846                                              "'%s'.", filename);
00847                         ns_main_earlywarning("Please check file and "
00848                                              "directory permissions "
00849                                              "or reconfigure the filename.");
00850 #endif /* HAVE_LINUXTHREADS */
00851                 }
00852         } else {
00853                 fd = safe_open(filename, mode, ISC_FALSE);
00854         }
00855 
00856         if (fd < 0) {
00857                 isc__strerror(errno, strbuf, sizeof(strbuf));
00858                 ns_main_earlywarning("could not open file '%s': %s",
00859                                      filename, strbuf);
00860                 return (NULL);
00861         }
00862 
00863         fp = fdopen(fd, "w");
00864         if (fp == NULL) {
00865                 isc__strerror(errno, strbuf, sizeof(strbuf));
00866                 ns_main_earlywarning("could not fdopen() file '%s': %s",
00867                                      filename, strbuf);
00868         }
00869 
00870         return (fp);
00871 }
00872 
00873 void
00874 ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
00875         FILE *fh;
00876         pid_t pid;
00877         char strbuf[ISC_STRERRORSIZE];
00878         void (*report)(const char *, ...);
00879 
00880         /*
00881          * The caller must ensure any required synchronization.
00882          */
00883 
00884         report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
00885 
00886         cleanup_pidfile();
00887 
00888         if (filename == NULL)
00889                 return;
00890 
00891         pidfile = strdup(filename);
00892         if (pidfile == NULL) {
00893                 isc__strerror(errno, strbuf, sizeof(strbuf));
00894                 (*report)("couldn't strdup() '%s': %s", filename, strbuf);
00895                 return;
00896         }
00897 
00898         fh = ns_os_openfile(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
00899                                   first_time);
00900         if (fh == NULL) {
00901                 cleanup_pidfile();
00902                 return;
00903         }
00904 #ifdef HAVE_LINUXTHREADS
00905         pid = mainpid;
00906 #else
00907         pid = getpid();
00908 #endif
00909         if (fprintf(fh, "%ld\n", (long)pid) < 0) {
00910                 (*report)("fprintf() to pid file '%s' failed", filename);
00911                 (void)fclose(fh);
00912                 cleanup_pidfile();
00913                 return;
00914         }
00915         if (fflush(fh) == EOF) {
00916                 (*report)("fflush() to pid file '%s' failed", filename);
00917                 (void)fclose(fh);
00918                 cleanup_pidfile();
00919                 return;
00920         }
00921         (void)fclose(fh);
00922 }
00923 
00924 isc_boolean_t
00925 ns_os_issingleton(const char *filename) {
00926         char strbuf[ISC_STRERRORSIZE];
00927         struct flock lock;
00928 
00929         if (singletonfd != -1)
00930                 return (ISC_TRUE);
00931 
00932         if (strcasecmp(filename, "none") == 0)
00933                 return (ISC_TRUE);
00934 
00935         /*
00936          * Make the containing directory if it doesn't exist.
00937          */
00938         lockfile = strdup(filename);
00939         if (lockfile == NULL) {
00940                 isc__strerror(errno, strbuf, sizeof(strbuf));
00941                 ns_main_earlyfatal("couldn't allocate memory for '%s': %s",
00942                                    filename, strbuf);
00943         } else {
00944                 int ret = mkdirpath(lockfile, ns_main_earlywarning);
00945                 if (ret == -1) {
00946                         ns_main_earlywarning("couldn't create '%s'", filename);
00947                         cleanup_lockfile();
00948                         return (ISC_FALSE);
00949                 }
00950         }
00951 
00952         /*
00953          * ns_os_openfile() uses safeopen() which removes any existing
00954          * files. We can't use that here.
00955          */
00956         singletonfd = open(filename, O_WRONLY | O_CREAT,
00957                            S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
00958         if (singletonfd == -1) {
00959                 cleanup_lockfile();
00960                 return (ISC_FALSE);
00961         }
00962 
00963         memset(&lock, 0, sizeof(lock));
00964         lock.l_type = F_WRLCK;
00965         lock.l_whence = SEEK_SET;
00966         lock.l_start = 0;
00967         lock.l_len = 1;
00968 
00969         /* Non-blocking (does not wait for lock) */
00970         if (fcntl(singletonfd, F_SETLK, &lock) == -1) {
00971                 close(singletonfd);
00972                 singletonfd = -1;
00973                 return (ISC_FALSE);
00974         }
00975 
00976         return (ISC_TRUE);
00977 }
00978 
00979 void
00980 ns_os_shutdown(void) {
00981         closelog();
00982         cleanup_pidfile();
00983         cleanup_lockfile();
00984 }
00985 
00986 isc_result_t
00987 ns_os_gethostname(char *buf, size_t len) {
00988         int n;
00989 
00990         n = gethostname(buf, len);
00991         return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
00992 }
00993 
00994 static char *
00995 next_token(char **stringp, const char *delim) {
00996         char *res;
00997 
00998         do {
00999                 res = strsep(stringp, delim);
01000                 if (res == NULL)
01001                         break;
01002         } while (*res == '\0');
01003         return (res);
01004 }
01005 
01006 void
01007 ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
01008         char *input, *ptr;
01009         unsigned int n;
01010         pid_t pid;
01011 
01012         input = command;
01013 
01014         /* Skip the command name. */
01015         ptr = next_token(&input, " \t");
01016         if (ptr == NULL)
01017                 return;
01018 
01019         ptr = next_token(&input, " \t");
01020         if (ptr == NULL)
01021                 return;
01022 
01023         if (strcmp(ptr, "-p") != 0)
01024                 return;
01025 
01026 #ifdef HAVE_LINUXTHREADS
01027         pid = mainpid;
01028 #else
01029         pid = getpid();
01030 #endif
01031 
01032         n = snprintf((char *)isc_buffer_used(text),
01033                      isc_buffer_availablelength(text),
01034                      "pid: %ld", (long)pid);
01035         /* Only send a message if it is complete. */
01036         if (n > 0 && n < isc_buffer_availablelength(text))
01037                 isc_buffer_add(text, n);
01038 }
01039 
01040 void
01041 ns_os_tzset(void) {
01042 #ifdef HAVE_TZSET
01043         tzset();
01044 #endif
01045 }

Generated on Tue Apr 28 17:41:05 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha