"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.17.5/bin/named/unix/os.c" (4 Sep 2020, 22331 Bytes) of package /linux/misc/dns/bind9/9.17.5/bind-9.17.5.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "os.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.17.2_vs_9.17.3.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 /*! \file */
   13 #include <stdarg.h>
   14 #include <stdbool.h>
   15 #include <sys/stat.h>
   16 #include <sys/types.h> /* dev_t FreeBSD 2.1 */
   17 #ifdef HAVE_UNAME
   18 #include <sys/utsname.h>
   19 #endif /* ifdef HAVE_UNAME */
   20 
   21 #include <ctype.h>
   22 #include <errno.h>
   23 #include <fcntl.h>
   24 #include <grp.h>
   25 #include <pwd.h>
   26 #include <signal.h>
   27 #include <stdio.h>
   28 #include <stdlib.h>
   29 #include <syslog.h>
   30 #ifdef HAVE_TZSET
   31 #include <time.h>
   32 #endif /* ifdef HAVE_TZSET */
   33 #include <unistd.h>
   34 
   35 #include <isc/buffer.h>
   36 #include <isc/file.h>
   37 #include <isc/print.h>
   38 #include <isc/resource.h>
   39 #include <isc/result.h>
   40 #include <isc/strerr.h>
   41 #include <isc/string.h>
   42 #include <isc/util.h>
   43 
   44 #include <named/globals.h>
   45 #include <named/main.h>
   46 #include <named/os.h>
   47 #ifdef HAVE_LIBSCF
   48 #include <named/smf_globals.h>
   49 #endif /* ifdef HAVE_LIBSCF */
   50 
   51 static char *pidfile = NULL;
   52 static char *lockfile = NULL;
   53 static int devnullfd = -1;
   54 static int singletonfd = -1;
   55 
   56 #ifndef ISC_FACILITY
   57 #define ISC_FACILITY LOG_DAEMON
   58 #endif /* ifndef ISC_FACILITY */
   59 
   60 static struct passwd *runas_pw = NULL;
   61 static bool done_setuid = false;
   62 static int dfd[2] = { -1, -1 };
   63 
   64 #ifdef HAVE_SYS_CAPABILITY_H
   65 
   66 static bool non_root = false;
   67 static bool non_root_caps = false;
   68 
   69 #include <sys/capability.h>
   70 #include <sys/prctl.h>
   71 
   72 static void
   73 linux_setcaps(cap_t caps) {
   74     char strbuf[ISC_STRERRORSIZE];
   75 
   76     if ((getuid() != 0 && !non_root_caps) || non_root) {
   77         return;
   78     }
   79     if (cap_set_proc(caps) < 0) {
   80         strerror_r(errno, strbuf, sizeof(strbuf));
   81         named_main_earlyfatal("cap_set_proc() failed: %s:"
   82                       " please ensure that the capset kernel"
   83                       " module is loaded.  see insmod(8)",
   84                       strbuf);
   85     }
   86 }
   87 
   88 #define SET_CAP(flag)                                                         \
   89     do {                                                                  \
   90         cap_flag_value_t curval;                                      \
   91         capval = (flag);                                              \
   92         err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval);  \
   93         if (err != -1 && curval) {                                    \
   94             err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval,   \
   95                        CAP_SET);                          \
   96             if (err == -1) {                                      \
   97                 strerror_r(errno, strbuf, sizeof(strbuf));    \
   98                 named_main_earlyfatal("cap_set_proc failed: " \
   99                               "%s",                   \
  100                               strbuf);                \
  101             }                                                     \
  102                                                                               \
  103             err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval,   \
  104                        CAP_SET);                          \
  105             if (err == -1) {                                      \
  106                 strerror_r(errno, strbuf, sizeof(strbuf));    \
  107                 named_main_earlyfatal("cap_set_proc failed: " \
  108                               "%s",                   \
  109                               strbuf);                \
  110             }                                                     \
  111         }                                                             \
  112     } while (0)
  113 #define INIT_CAP                                                              \
  114     do {                                                                  \
  115         caps = cap_init();                                            \
  116         if (caps == NULL) {                                           \
  117             strerror_r(errno, strbuf, sizeof(strbuf));            \
  118             named_main_earlyfatal("cap_init failed: %s", strbuf); \
  119         }                                                             \
  120         curcaps = cap_get_proc();                                     \
  121         if (curcaps == NULL) {                                        \
  122             strerror_r(errno, strbuf, sizeof(strbuf));            \
  123             named_main_earlyfatal("cap_get_proc failed: %s",      \
  124                           strbuf);                        \
  125         }                                                             \
  126     } while (0)
  127 #define FREE_CAP                   \
  128     {                          \
  129         cap_free(caps);    \
  130         cap_free(curcaps); \
  131     }                          \
  132     while (0)
  133 
  134 static void
  135 linux_initialprivs(void) {
  136     cap_t caps;
  137     cap_t curcaps;
  138     cap_value_t capval;
  139     char strbuf[ISC_STRERRORSIZE];
  140     int err;
  141 
  142     /*%
  143      * We don't need most privileges, so we drop them right away.
  144      * Later on linux_minprivs() will be called, which will drop our
  145      * capabilities to the minimum needed to run the server.
  146      */
  147     INIT_CAP;
  148 
  149     /*
  150      * We need to be able to bind() to privileged ports, notably port 53!
  151      */
  152     SET_CAP(CAP_NET_BIND_SERVICE);
  153 
  154     /*
  155      * We need chroot() initially too.
  156      */
  157     SET_CAP(CAP_SYS_CHROOT);
  158 
  159     /*
  160      * We need setuid() as the kernel supports keeping capabilities after
  161      * setuid().
  162      */
  163     SET_CAP(CAP_SETUID);
  164 
  165     /*
  166      * Since we call initgroups, we need this.
  167      */
  168     SET_CAP(CAP_SETGID);
  169 
  170     /*
  171      * Without this, we run into problems reading a configuration file
  172      * owned by a non-root user and non-world-readable on startup.
  173      */
  174     SET_CAP(CAP_DAC_READ_SEARCH);
  175 
  176     /*
  177      * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
  178      *      clear it would work right given the way linuxthreads work.
  179      * XXXDCL But since we need to be able to set the maximum number
  180      * of files, the stack size, data size, and core dump size to
  181      * support named.conf options, this is now being added to test.
  182      */
  183     SET_CAP(CAP_SYS_RESOURCE);
  184 
  185     /*
  186      * We need to be able to set the ownership of the containing
  187      * directory of the pid file when we create it.
  188      */
  189     SET_CAP(CAP_CHOWN);
  190 
  191     linux_setcaps(caps);
  192 
  193     FREE_CAP;
  194 }
  195 
  196 static void
  197 linux_minprivs(void) {
  198     cap_t caps;
  199     cap_t curcaps;
  200     cap_value_t capval;
  201     char strbuf[ISC_STRERRORSIZE];
  202     int err;
  203 
  204     INIT_CAP;
  205     /*%
  206      * Drop all privileges except the ability to bind() to privileged
  207      * ports.
  208      *
  209      * It's important that we drop CAP_SYS_CHROOT.  If we didn't, it
  210      * chroot() could be used to escape from the chrooted area.
  211      */
  212 
  213     SET_CAP(CAP_NET_BIND_SERVICE);
  214 
  215     /*
  216      * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
  217      *      clear it would work right given the way linuxthreads work.
  218      * XXXDCL But since we need to be able to set the maximum number
  219      * of files, the stack size, data size, and core dump size to
  220      * support named.conf options, this is now being added to test.
  221      */
  222     SET_CAP(CAP_SYS_RESOURCE);
  223 
  224     linux_setcaps(caps);
  225 
  226     FREE_CAP;
  227 }
  228 
  229 static void
  230 linux_keepcaps(void) {
  231     char strbuf[ISC_STRERRORSIZE];
  232     /*%
  233      * Ask the kernel to allow us to keep our capabilities after we
  234      * setuid().
  235      */
  236 
  237     if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
  238         if (errno != EINVAL) {
  239             strerror_r(errno, strbuf, sizeof(strbuf));
  240             named_main_earlyfatal("prctl() failed: %s", strbuf);
  241         }
  242     } else {
  243         non_root_caps = true;
  244         if (getuid() != 0) {
  245             non_root = true;
  246         }
  247     }
  248 }
  249 
  250 #endif /* HAVE_SYS_CAPABILITY_H */
  251 
  252 static void
  253 setup_syslog(const char *progname) {
  254     int options;
  255 
  256     options = LOG_PID;
  257 #ifdef LOG_NDELAY
  258     options |= LOG_NDELAY;
  259 #endif /* ifdef LOG_NDELAY */
  260     openlog(isc_file_basename(progname), options, ISC_FACILITY);
  261 }
  262 
  263 void
  264 named_os_init(const char *progname) {
  265     setup_syslog(progname);
  266 #ifdef HAVE_SYS_CAPABILITY_H
  267     linux_initialprivs();
  268 #endif /* ifdef HAVE_SYS_CAPABILITY_H */
  269 #ifdef SIGXFSZ
  270     signal(SIGXFSZ, SIG_IGN);
  271 #endif /* ifdef SIGXFSZ */
  272 }
  273 
  274 void
  275 named_os_daemonize(void) {
  276     pid_t pid;
  277     char strbuf[ISC_STRERRORSIZE];
  278 
  279     if (pipe(dfd) == -1) {
  280         strerror_r(errno, strbuf, sizeof(strbuf));
  281         named_main_earlyfatal("pipe(): %s", strbuf);
  282     }
  283 
  284     pid = fork();
  285     if (pid == -1) {
  286         strerror_r(errno, strbuf, sizeof(strbuf));
  287         named_main_earlyfatal("fork(): %s", strbuf);
  288     }
  289     if (pid != 0) {
  290         int n;
  291         /*
  292          * Wait for the child to finish loading for the first time.
  293          * This would be so much simpler if fork() worked once we
  294          * were multi-threaded.
  295          */
  296         (void)close(dfd[1]);
  297         do {
  298             char buf;
  299             n = read(dfd[0], &buf, 1);
  300             if (n == 1) {
  301                 _exit(0);
  302             }
  303         } while (n == -1 && errno == EINTR);
  304         _exit(1);
  305     }
  306     (void)close(dfd[0]);
  307 
  308     /*
  309      * We're the child.
  310      */
  311 
  312     if (setsid() == -1) {
  313         strerror_r(errno, strbuf, sizeof(strbuf));
  314         named_main_earlyfatal("setsid(): %s", strbuf);
  315     }
  316 
  317     /*
  318      * Try to set stdin, stdout, and stderr to /dev/null, but press
  319      * on even if it fails.
  320      *
  321      * XXXMLG The close() calls here are unneeded on all but NetBSD, but
  322      * are harmless to include everywhere.  dup2() is supposed to close
  323      * the FD if it is in use, but unproven-pthreads-0.16 is broken
  324      * and will end up closing the wrong FD.  This will be fixed eventually,
  325      * and these calls will be removed.
  326      */
  327     if (devnullfd != -1) {
  328         if (devnullfd != STDIN_FILENO) {
  329             (void)close(STDIN_FILENO);
  330             (void)dup2(devnullfd, STDIN_FILENO);
  331         }
  332         if (devnullfd != STDOUT_FILENO) {
  333             (void)close(STDOUT_FILENO);
  334             (void)dup2(devnullfd, STDOUT_FILENO);
  335         }
  336         if (devnullfd != STDERR_FILENO && !named_g_keepstderr) {
  337             (void)close(STDERR_FILENO);
  338             (void)dup2(devnullfd, STDERR_FILENO);
  339         }
  340     }
  341 }
  342 
  343 void
  344 named_os_started(void) {
  345     char buf = 0;
  346 
  347     /*
  348      * Signal to the parent that we started successfully.
  349      */
  350     if (dfd[0] != -1 && dfd[1] != -1) {
  351         if (write(dfd[1], &buf, 1) != 1) {
  352             named_main_earlyfatal("unable to signal parent that we "
  353                           "otherwise started "
  354                           "successfully.");
  355         }
  356         close(dfd[1]);
  357         dfd[0] = dfd[1] = -1;
  358     }
  359 }
  360 
  361 void
  362 named_os_opendevnull(void) {
  363     devnullfd = open("/dev/null", O_RDWR, 0);
  364 }
  365 
  366 void
  367 named_os_closedevnull(void) {
  368     if (devnullfd != STDIN_FILENO && devnullfd != STDOUT_FILENO &&
  369         devnullfd != STDERR_FILENO)
  370     {
  371         close(devnullfd);
  372         devnullfd = -1;
  373     }
  374 }
  375 
  376 static bool
  377 all_digits(const char *s) {
  378     if (*s == '\0') {
  379         return (false);
  380     }
  381     while (*s != '\0') {
  382         if (!isdigit((*s) & 0xff)) {
  383             return (false);
  384         }
  385         s++;
  386     }
  387     return (true);
  388 }
  389 
  390 void
  391 named_os_chroot(const char *root) {
  392     char strbuf[ISC_STRERRORSIZE];
  393 #ifdef HAVE_LIBSCF
  394     named_smf_chroot = 0;
  395 #endif /* ifdef HAVE_LIBSCF */
  396     if (root != NULL) {
  397 #ifdef HAVE_CHROOT
  398         if (chroot(root) < 0) {
  399             strerror_r(errno, strbuf, sizeof(strbuf));
  400             named_main_earlyfatal("chroot(): %s", strbuf);
  401         }
  402 #else  /* ifdef HAVE_CHROOT */
  403         named_main_earlyfatal("chroot(): disabled");
  404 #endif /* ifdef HAVE_CHROOT */
  405         if (chdir("/") < 0) {
  406             strerror_r(errno, strbuf, sizeof(strbuf));
  407             named_main_earlyfatal("chdir(/): %s", strbuf);
  408         }
  409 #ifdef HAVE_LIBSCF
  410         /* Set named_smf_chroot flag on successful chroot. */
  411         named_smf_chroot = 1;
  412 #endif /* ifdef HAVE_LIBSCF */
  413     }
  414 }
  415 
  416 void
  417 named_os_inituserinfo(const char *username) {
  418     if (username == NULL) {
  419         return;
  420     }
  421 
  422     if (all_digits(username)) {
  423         runas_pw = getpwuid((uid_t)atoi(username));
  424     } else {
  425         runas_pw = getpwnam(username);
  426     }
  427     endpwent();
  428 
  429     if (runas_pw == NULL) {
  430         named_main_earlyfatal("user '%s' unknown", username);
  431     }
  432 
  433     if (getuid() == 0) {
  434         char strbuf[ISC_STRERRORSIZE];
  435         if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
  436             strerror_r(errno, strbuf, sizeof(strbuf));
  437             named_main_earlyfatal("initgroups(): %s", strbuf);
  438         }
  439     }
  440 }
  441 
  442 void
  443 named_os_changeuser(void) {
  444     char strbuf[ISC_STRERRORSIZE];
  445     if (runas_pw == NULL || done_setuid) {
  446         return;
  447     }
  448 
  449     done_setuid = true;
  450 
  451     if (setgid(runas_pw->pw_gid) < 0) {
  452         strerror_r(errno, strbuf, sizeof(strbuf));
  453         named_main_earlyfatal("setgid(): %s", strbuf);
  454     }
  455 
  456     if (setuid(runas_pw->pw_uid) < 0) {
  457         strerror_r(errno, strbuf, sizeof(strbuf));
  458         named_main_earlyfatal("setuid(): %s", strbuf);
  459     }
  460 
  461 #if defined(HAVE_SYS_CAPABILITY_H)
  462     /*
  463      * Restore the ability of named to drop core after the setuid()
  464      * call has disabled it.
  465      */
  466     if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
  467         strerror_r(errno, strbuf, sizeof(strbuf));
  468         named_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s",
  469                     strbuf);
  470     }
  471 
  472     linux_minprivs();
  473 #endif /* if defined(HAVE_SYS_CAPABILITY_H) */
  474 }
  475 
  476 uid_t
  477 ns_os_uid(void) {
  478     if (runas_pw == NULL) {
  479         return (0);
  480     }
  481     return (runas_pw->pw_uid);
  482 }
  483 
  484 void
  485 named_os_adjustnofile(void) {
  486 #if defined(__linux__)
  487     isc_result_t result;
  488     isc_resourcevalue_t newvalue;
  489 
  490     /*
  491      * Linux: max number of open files specified by one thread doesn't seem
  492      * to apply to other threads on Linux.
  493      */
  494     newvalue = ISC_RESOURCE_UNLIMITED;
  495 
  496     result = isc_resource_setlimit(isc_resource_openfiles, newvalue);
  497     if (result != ISC_R_SUCCESS) {
  498         named_main_earlywarning("couldn't adjust limit on open files");
  499     }
  500 #endif /* if defined(__linux__) */
  501 }
  502 
  503 void
  504 named_os_minprivs(void) {
  505 #if defined(HAVE_SYS_CAPABILITY_H)
  506     linux_keepcaps();
  507     named_os_changeuser();
  508     linux_minprivs();
  509 #endif /* if defined(HAVE_SYS_CAPABILITY_H) */
  510 }
  511 
  512 static int
  513 safe_open(const char *filename, mode_t mode, bool append) {
  514     int fd;
  515     struct stat sb;
  516 
  517     if (stat(filename, &sb) == -1) {
  518         if (errno != ENOENT) {
  519             return (-1);
  520         }
  521     } else if ((sb.st_mode & S_IFREG) == 0) {
  522         errno = EOPNOTSUPP;
  523         return (-1);
  524     }
  525 
  526     if (append) {
  527         fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, mode);
  528     } else {
  529         if (unlink(filename) < 0 && errno != ENOENT) {
  530             return (-1);
  531         }
  532         fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, mode);
  533     }
  534     return (fd);
  535 }
  536 
  537 static void
  538 cleanup_pidfile(void) {
  539     int n;
  540     if (pidfile != NULL) {
  541         n = unlink(pidfile);
  542         if (n == -1 && errno != ENOENT) {
  543             named_main_earlywarning("unlink '%s': failed", pidfile);
  544         }
  545         free(pidfile);
  546     }
  547     pidfile = NULL;
  548 }
  549 
  550 static void
  551 cleanup_lockfile(void) {
  552     if (singletonfd != -1) {
  553         close(singletonfd);
  554         singletonfd = -1;
  555     }
  556 
  557     if (lockfile != NULL) {
  558         int n = unlink(lockfile);
  559         if (n == -1 && errno != ENOENT) {
  560             named_main_earlywarning("unlink '%s': failed",
  561                         lockfile);
  562         }
  563         free(lockfile);
  564         lockfile = NULL;
  565     }
  566 }
  567 
  568 /*
  569  * Ensure that a directory exists.
  570  * NOTE: This function overwrites the '/' characters in 'filename' with
  571  * nulls. The caller should copy the filename to a fresh buffer first.
  572  */
  573 static int
  574 mkdirpath(char *filename, void (*report)(const char *, ...)) {
  575     char *slash = strrchr(filename, '/');
  576     char strbuf[ISC_STRERRORSIZE];
  577     unsigned int mode;
  578 
  579     if (slash != NULL && slash != filename) {
  580         struct stat sb;
  581         *slash = '\0';
  582 
  583         if (stat(filename, &sb) == -1) {
  584             if (errno != ENOENT) {
  585                 strerror_r(errno, strbuf, sizeof(strbuf));
  586                 (*report)("couldn't stat '%s': %s", filename,
  587                       strbuf);
  588                 goto error;
  589             }
  590             if (mkdirpath(filename, report) == -1) {
  591                 goto error;
  592             }
  593             /*
  594              * Handle "//", "/./" and "/../" in path.
  595              */
  596             if (!strcmp(slash + 1, "") || !strcmp(slash + 1, ".") ||
  597                 !strcmp(slash + 1, ".."))
  598             {
  599                 *slash = '/';
  600                 return (0);
  601             }
  602             mode = S_IRUSR | S_IWUSR | S_IXUSR; /* u=rwx */
  603             mode |= S_IRGRP | S_IXGRP;      /* g=rx */
  604             mode |= S_IROTH | S_IXOTH;      /* o=rx */
  605             if (mkdir(filename, mode) == -1) {
  606                 strerror_r(errno, strbuf, sizeof(strbuf));
  607                 (*report)("couldn't mkdir '%s': %s", filename,
  608                       strbuf);
  609                 goto error;
  610             }
  611             if (runas_pw != NULL &&
  612                 chown(filename, runas_pw->pw_uid,
  613                   runas_pw->pw_gid) == -1)
  614             {
  615                 strerror_r(errno, strbuf, sizeof(strbuf));
  616                 (*report)("couldn't chown '%s': %s", filename,
  617                       strbuf);
  618             }
  619         }
  620         *slash = '/';
  621     }
  622     return (0);
  623 
  624 error:
  625     *slash = '/';
  626     return (-1);
  627 }
  628 
  629 #if !HAVE_SYS_CAPABILITY_H
  630 static void
  631 setperms(uid_t uid, gid_t gid) {
  632 #if defined(HAVE_SETEGID) || defined(HAVE_SETRESGID)
  633     char strbuf[ISC_STRERRORSIZE];
  634 #endif /* if defined(HAVE_SETEGID) || defined(HAVE_SETRESGID) */
  635 #if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID)
  636     gid_t oldgid, tmpg;
  637 #endif /* if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) */
  638 #if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID)
  639     uid_t olduid, tmpu;
  640 #endif /* if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID) */
  641 #if defined(HAVE_SETEGID)
  642     if (getegid() != gid && setegid(gid) == -1) {
  643         strerror_r(errno, strbuf, sizeof(strbuf));
  644         named_main_earlywarning("unable to set effective "
  645                     "gid to %ld: %s",
  646                     (long)gid, strbuf);
  647     }
  648 #elif defined(HAVE_SETRESGID)
  649     if (getresgid(&tmpg, &oldgid, &tmpg) == -1 || oldgid != gid) {
  650         if (setresgid(-1, gid, -1) == -1) {
  651             strerror_r(errno, strbuf, sizeof(strbuf));
  652             named_main_earlywarning("unable to set effective "
  653                         "gid to %d: %s",
  654                         gid, strbuf);
  655         }
  656     }
  657 #endif /* if defined(HAVE_SETEGID) */
  658 
  659 #if defined(HAVE_SETEUID)
  660     if (geteuid() != uid && seteuid(uid) == -1) {
  661         strerror_r(errno, strbuf, sizeof(strbuf));
  662         named_main_earlywarning("unable to set effective "
  663                     "uid to %ld: %s",
  664                     (long)uid, strbuf);
  665     }
  666 #elif defined(HAVE_SETRESUID)
  667     if (getresuid(&tmpu, &olduid, &tmpu) == -1 || olduid != uid) {
  668         if (setresuid(-1, uid, -1) == -1) {
  669             strerror_r(errno, strbuf, sizeof(strbuf));
  670             named_main_earlywarning("unable to set effective "
  671                         "uid to %d: %s",
  672                         uid, strbuf);
  673         }
  674     }
  675 #endif /* if defined(HAVE_SETEUID) */
  676 }
  677 #endif /* !HAVE_SYS_CAPABILITY_H */
  678 
  679 FILE *
  680 named_os_openfile(const char *filename, mode_t mode, bool switch_user) {
  681     char strbuf[ISC_STRERRORSIZE], *f;
  682     FILE *fp;
  683     int fd;
  684 
  685     /*
  686      * Make the containing directory if it doesn't exist.
  687      */
  688     f = strdup(filename);
  689     if (f == NULL) {
  690         strerror_r(errno, strbuf, sizeof(strbuf));
  691         named_main_earlywarning("couldn't strdup() '%s': %s", filename,
  692                     strbuf);
  693         return (NULL);
  694     }
  695     if (mkdirpath(f, named_main_earlywarning) == -1) {
  696         free(f);
  697         return (NULL);
  698     }
  699     free(f);
  700 
  701     if (switch_user && runas_pw != NULL) {
  702         uid_t olduid = getuid();
  703         gid_t oldgid = getgid();
  704 #if HAVE_SYS_CAPABILITY_H
  705         REQUIRE(olduid == runas_pw->pw_uid);
  706         REQUIRE(oldgid == runas_pw->pw_gid);
  707 #else /* HAVE_SYS_CAPABILITY_H */
  708         /* Set UID/GID to the one we'll be running with eventually */
  709         setperms(runas_pw->pw_uid, runas_pw->pw_gid);
  710 #endif
  711         fd = safe_open(filename, mode, false);
  712 
  713 #if !HAVE_SYS_CAPABILITY_H
  714         /* Restore UID/GID to previous uid/gid */
  715         setperms(olduid, oldgid);
  716 #endif
  717 
  718         if (fd == -1) {
  719             fd = safe_open(filename, mode, false);
  720             if (fd != -1) {
  721                 named_main_earlywarning("Required root "
  722                             "permissions to open "
  723                             "'%s'.",
  724                             filename);
  725             } else {
  726                 named_main_earlywarning("Could not open "
  727                             "'%s'.",
  728                             filename);
  729             }
  730             named_main_earlywarning("Please check file and "
  731                         "directory permissions "
  732                         "or reconfigure the filename.");
  733         }
  734     } else {
  735         fd = safe_open(filename, mode, false);
  736     }
  737 
  738     if (fd < 0) {
  739         strerror_r(errno, strbuf, sizeof(strbuf));
  740         named_main_earlywarning("could not open file '%s': %s",
  741                     filename, strbuf);
  742         return (NULL);
  743     }
  744 
  745     fp = fdopen(fd, "w");
  746     if (fp == NULL) {
  747         strerror_r(errno, strbuf, sizeof(strbuf));
  748         named_main_earlywarning("could not fdopen() file '%s': %s",
  749                     filename, strbuf);
  750     }
  751 
  752     return (fp);
  753 }
  754 
  755 void
  756 named_os_writepidfile(const char *filename, bool first_time) {
  757     FILE *fh;
  758     pid_t pid;
  759     char strbuf[ISC_STRERRORSIZE];
  760     void (*report)(const char *, ...);
  761 
  762     /*
  763      * The caller must ensure any required synchronization.
  764      */
  765 
  766     report = first_time ? named_main_earlyfatal : named_main_earlywarning;
  767 
  768     cleanup_pidfile();
  769 
  770     if (filename == NULL) {
  771         return;
  772     }
  773 
  774     pidfile = strdup(filename);
  775     if (pidfile == NULL) {
  776         strerror_r(errno, strbuf, sizeof(strbuf));
  777         (*report)("couldn't strdup() '%s': %s", filename, strbuf);
  778         return;
  779     }
  780 
  781     fh = named_os_openfile(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH,
  782                    first_time);
  783     if (fh == NULL) {
  784         cleanup_pidfile();
  785         return;
  786     }
  787     pid = getpid();
  788     if (fprintf(fh, "%ld\n", (long)pid) < 0) {
  789         (*report)("fprintf() to pid file '%s' failed", filename);
  790         (void)fclose(fh);
  791         cleanup_pidfile();
  792         return;
  793     }
  794     if (fflush(fh) == EOF) {
  795         (*report)("fflush() to pid file '%s' failed", filename);
  796         (void)fclose(fh);
  797         cleanup_pidfile();
  798         return;
  799     }
  800     (void)fclose(fh);
  801 }
  802 
  803 bool
  804 named_os_issingleton(const char *filename) {
  805     char strbuf[ISC_STRERRORSIZE];
  806     struct flock lock;
  807 
  808     if (singletonfd != -1) {
  809         return (true);
  810     }
  811 
  812     if (strcasecmp(filename, "none") == 0) {
  813         return (true);
  814     }
  815 
  816     /*
  817      * Make the containing directory if it doesn't exist.
  818      */
  819     lockfile = strdup(filename);
  820     if (lockfile == NULL) {
  821         strerror_r(errno, strbuf, sizeof(strbuf));
  822         named_main_earlyfatal("couldn't allocate memory for '%s': %s",
  823                       filename, strbuf);
  824     } else {
  825         int ret = mkdirpath(lockfile, named_main_earlywarning);
  826         if (ret == -1) {
  827             named_main_earlywarning("couldn't create '%s'",
  828                         filename);
  829             cleanup_lockfile();
  830             return (false);
  831         }
  832     }
  833 
  834     /*
  835      * named_os_openfile() uses safeopen() which removes any existing
  836      * files. We can't use that here.
  837      */
  838     singletonfd = open(filename, O_WRONLY | O_CREAT,
  839                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  840     if (singletonfd == -1) {
  841         cleanup_lockfile();
  842         return (false);
  843     }
  844 
  845     memset(&lock, 0, sizeof(lock));
  846     lock.l_type = F_WRLCK;
  847     lock.l_whence = SEEK_SET;
  848     lock.l_start = 0;
  849     lock.l_len = 1;
  850 
  851     /* Non-blocking (does not wait for lock) */
  852     if (fcntl(singletonfd, F_SETLK, &lock) == -1) {
  853         close(singletonfd);
  854         singletonfd = -1;
  855         return (false);
  856     }
  857 
  858     return (true);
  859 }
  860 
  861 void
  862 named_os_shutdown(void) {
  863     closelog();
  864     cleanup_pidfile();
  865     cleanup_lockfile();
  866 }
  867 
  868 isc_result_t
  869 named_os_gethostname(char *buf, size_t len) {
  870     int n;
  871 
  872     n = gethostname(buf, len);
  873     return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
  874 }
  875 
  876 void
  877 named_os_shutdownmsg(char *command, isc_buffer_t *text) {
  878     char *last, *ptr;
  879     pid_t pid;
  880 
  881     /* Skip the command name. */
  882     if (strtok_r(command, " \t", &last) == NULL) {
  883         return;
  884     }
  885 
  886     if ((ptr = strtok_r(NULL, " \t", &last)) == NULL) {
  887         return;
  888     }
  889 
  890     if (strcmp(ptr, "-p") != 0) {
  891         return;
  892     }
  893 
  894     pid = getpid();
  895 
  896     (void)isc_buffer_printf(text, "pid: %ld", (long)pid);
  897 }
  898 
  899 void
  900 named_os_tzset(void) {
  901 #ifdef HAVE_TZSET
  902     tzset();
  903 #endif /* ifdef HAVE_TZSET */
  904 }
  905 
  906 #ifdef HAVE_UNAME
  907 static char unamebuf[sizeof(struct utsname)];
  908 #else
  909 static const char unamebuf[] = { "unknown architecture" };
  910 #endif
  911 static const char *unamep = NULL;
  912 
  913 static void
  914 getuname(void) {
  915 #ifdef HAVE_UNAME
  916     struct utsname uts;
  917 
  918     memset(&uts, 0, sizeof(uts));
  919     if (uname(&uts) < 0) {
  920         snprintf(unamebuf, sizeof(unamebuf), "unknown architecture");
  921         return;
  922     }
  923 
  924     snprintf(unamebuf, sizeof(unamebuf), "%s %s %s %s", uts.sysname,
  925          uts.machine, uts.release, uts.version);
  926 #endif /* ifdef HAVE_UNAME */
  927     unamep = unamebuf;
  928 }
  929 
  930 const char *
  931 named_os_uname(void) {
  932     if (unamep == NULL) {
  933         getuname();
  934     }
  935     return (unamep);
  936 }