2 * Copyright (C) 2004-2011, 2013, 2014, 2016 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2002 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
23 #include <sys/types.h> /* dev_t FreeBSD 2.1 */
26 #include <sys/utsname.h>
32 #include <grp.h> /* Required for initgroups() on IRIX. */
43 #include <isc/buffer.h>
45 #include <isc/print.h>
46 #include <isc/resource.h>
47 #include <isc/result.h>
48 #include <isc/strerror.h>
49 #include <isc/string.h>
51 #include <named/main.h>
54 #include <named/ns_smf_globals.h>
57 static char *pidfile = NULL;
58 static int devnullfd = -1;
61 #define ISC_FACILITY LOG_DAEMON
65 * If there's no <linux/capability.h>, we don't care about <sys/prctl.h>
67 #ifndef HAVE_LINUX_CAPABILITY_H
68 #undef HAVE_SYS_PRCTL_H
73 * (T) HAVE_LINUXTHREADS
74 * (C) HAVE_SYS_CAPABILITY_H (or HAVE_LINUX_CAPABILITY_H)
75 * (P) HAVE_SYS_PRCTL_H
76 * The possible cases are:
77 * none: setuid() normally
79 * C: setuid() normally, drop caps (keep CAP_SETUID)
80 * T+C: no setuid(), drop caps (don't keep CAP_SETUID)
81 * T+C+P: setuid() early, drop caps (keep CAP_SETUID)
82 * C+P: setuid() normally, drop caps (keep CAP_SETUID)
87 * caps = BIND_SERVICE + CHROOT + SETGID
88 * if ((T && C && P) || !T)
93 * if (T && C && P && -u)
100 * if (C && (P || !-u))
101 * caps = BIND_SERVICE
105 * It will be nice when Linux threads work properly with setuid().
108 #ifdef HAVE_LINUXTHREADS
109 static pid_t mainpid = 0;
112 static struct passwd *runas_pw = NULL;
113 static isc_boolean_t done_setuid = ISC_FALSE;
114 static int dfd[2] = { -1, -1 };
116 #ifdef HAVE_LINUX_CAPABILITY_H
118 static isc_boolean_t non_root = ISC_FALSE;
119 static isc_boolean_t non_root_caps = ISC_FALSE;
121 #ifdef HAVE_SYS_CAPABILITY_H
122 #include <sys/capability.h>
124 #ifdef HAVE_LINUX_TYPES_H
125 #include <linux/types.h>
128 * We define _LINUX_FS_H to prevent it from being included. We don't need
129 * anything from it, and the files it includes cause warnings with 2.2
130 * kernels, and compilation failures (due to conflicts between <linux/string.h>
131 * and <string.h>) on 2.3 kernels.
134 #include <linux/capability.h>
138 #include <asm/unistd.h> /* Slackware 4.0 needs this. */
139 #endif /* __NR_capset */
140 #define SYS_capset __NR_capset
141 #endif /* SYS_capset */
142 #endif /* HAVE_SYS_CAPABILITY_H */
144 #ifdef HAVE_SYS_PRCTL_H
145 #include <sys/prctl.h> /* Required for prctl(). */
148 * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it
149 * here. This allows setuid() to work on systems running a new enough
150 * kernel but with /usr/include/linux pointing to "standard" kernel
153 #ifndef PR_SET_KEEPCAPS
154 #define PR_SET_KEEPCAPS 8
157 #endif /* HAVE_SYS_PRCTL_H */
160 #define SETCAPS_FUNC "cap_set_proc "
162 typedef unsigned int cap_t;
163 #define SETCAPS_FUNC "syscall(capset) "
164 #endif /* HAVE_LIBCAP */
167 linux_setcaps(cap_t caps) {
169 struct __user_cap_header_struct caphead;
170 struct __user_cap_data_struct cap;
172 char strbuf[ISC_STRERRORSIZE];
174 if ((getuid() != 0 && !non_root_caps) || non_root)
177 memset(&caphead, 0, sizeof(caphead));
178 caphead.version = _LINUX_CAPABILITY_VERSION;
180 memset(&cap, 0, sizeof(cap));
181 cap.effective = caps;
182 cap.permitted = caps;
186 if (cap_set_proc(caps) < 0) {
188 if (syscall(SYS_capset, &caphead, &cap) < 0) {
190 isc__strerror(errno, strbuf, sizeof(strbuf));
191 ns_main_earlyfatal(SETCAPS_FUNC "failed: %s:"
192 " please ensure that the capset kernel"
193 " module is loaded. see insmod(8)",
199 #define SET_CAP(flag) \
201 cap_flag_value_t curval; \
203 err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval); \
204 if (err != -1 && curval) { \
205 err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET); \
207 isc__strerror(errno, strbuf, sizeof(strbuf)); \
208 ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
211 err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET); \
213 isc__strerror(errno, strbuf, sizeof(strbuf)); \
214 ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
221 if (caps == NULL) { \
222 isc__strerror(errno, strbuf, sizeof(strbuf)); \
223 ns_main_earlyfatal("cap_init failed: %s", strbuf); \
225 curcaps = cap_get_proc(); \
226 if (curcaps == NULL) { \
227 isc__strerror(errno, strbuf, sizeof(strbuf)); \
228 ns_main_earlyfatal("cap_get_proc failed: %s", strbuf); \
237 #define SET_CAP(flag) do { caps |= (1 << (flag)); } while (0)
238 #define INIT_CAP do { caps = 0; } while (0)
239 #endif /* HAVE_LIBCAP */
242 linux_initialprivs(void) {
247 char strbuf[ISC_STRERRORSIZE];
252 * We don't need most privileges, so we drop them right away.
253 * Later on linux_minprivs() will be called, which will drop our
254 * capabilities to the minimum needed to run the server.
259 * We need to be able to bind() to privileged ports, notably port 53!
261 SET_CAP(CAP_NET_BIND_SERVICE);
264 * We need chroot() initially too.
266 SET_CAP(CAP_SYS_CHROOT);
268 #if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
270 * We can setuid() only if either the kernel supports keeping
271 * capabilities after setuid() (which we don't know until we've
272 * tried) or we're not using threads. If either of these is
273 * true, we want the setuid capability.
279 * Since we call initgroups, we need this.
284 * Without this, we run into problems reading a configuration file
285 * owned by a non-root user and non-world-readable on startup.
287 SET_CAP(CAP_DAC_READ_SEARCH);
290 * XXX We might want to add CAP_SYS_RESOURCE, though it's not
291 * clear it would work right given the way linuxthreads work.
292 * XXXDCL But since we need to be able to set the maximum number
293 * of files, the stack size, data size, and core dump size to
294 * support named.conf options, this is now being added to test.
296 SET_CAP(CAP_SYS_RESOURCE);
299 * We need to be able to set the ownership of the containing
300 * directory of the pid file when we create it.
312 linux_minprivs(void) {
317 char strbuf[ISC_STRERRORSIZE];
323 * Drop all privileges except the ability to bind() to privileged
326 * It's important that we drop CAP_SYS_CHROOT. If we didn't, it
327 * chroot() could be used to escape from the chrooted area.
330 SET_CAP(CAP_NET_BIND_SERVICE);
333 * XXX We might want to add CAP_SYS_RESOURCE, though it's not
334 * clear it would work right given the way linuxthreads work.
335 * XXXDCL But since we need to be able to set the maximum number
336 * of files, the stack size, data size, and core dump size to
337 * support named.conf options, this is now being added to test.
339 SET_CAP(CAP_SYS_RESOURCE);
348 #ifdef HAVE_SYS_PRCTL_H
350 linux_keepcaps(void) {
351 char strbuf[ISC_STRERRORSIZE];
353 * Ask the kernel to allow us to keep our capabilities after we
357 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
358 if (errno != EINVAL) {
359 isc__strerror(errno, strbuf, sizeof(strbuf));
360 ns_main_earlyfatal("prctl() failed: %s", strbuf);
363 non_root_caps = ISC_TRUE;
370 #endif /* HAVE_LINUX_CAPABILITY_H */
374 setup_syslog(const char *progname) {
379 options |= LOG_NDELAY;
381 openlog(isc_file_basename(progname), options, ISC_FACILITY);
385 ns_os_init(const char *progname) {
386 setup_syslog(progname);
387 #ifdef HAVE_LINUX_CAPABILITY_H
388 linux_initialprivs();
390 #ifdef HAVE_LINUXTHREADS
394 signal(SIGXFSZ, SIG_IGN);
399 ns_os_daemonize(void) {
401 char strbuf[ISC_STRERRORSIZE];
403 if (pipe(dfd) == -1) {
404 isc__strerror(errno, strbuf, sizeof(strbuf));
405 ns_main_earlyfatal("pipe(): %s", strbuf);
410 isc__strerror(errno, strbuf, sizeof(strbuf));
411 ns_main_earlyfatal("fork(): %s", strbuf);
416 * Wait for the child to finish loading for the first time.
417 * This would be so much simpler if fork() worked once we
418 * were multi-threaded.
423 n = read(dfd[0], &buf, 1);
426 } while (n == -1 && errno == EINTR);
435 #ifdef HAVE_LINUXTHREADS
439 if (setsid() == -1) {
440 isc__strerror(errno, strbuf, sizeof(strbuf));
441 ns_main_earlyfatal("setsid(): %s", strbuf);
445 * Try to set stdin, stdout, and stderr to /dev/null, but press
446 * on even if it fails.
448 * XXXMLG The close() calls here are unneeded on all but NetBSD, but
449 * are harmless to include everywhere. dup2() is supposed to close
450 * the FD if it is in use, but unproven-pthreads-0.16 is broken
451 * and will end up closing the wrong FD. This will be fixed eventually,
452 * and these calls will be removed.
454 if (devnullfd != -1) {
455 if (devnullfd != STDIN_FILENO) {
456 (void)close(STDIN_FILENO);
457 (void)dup2(devnullfd, STDIN_FILENO);
459 if (devnullfd != STDOUT_FILENO) {
460 (void)close(STDOUT_FILENO);
461 (void)dup2(devnullfd, STDOUT_FILENO);
463 if (devnullfd != STDERR_FILENO) {
464 (void)close(STDERR_FILENO);
465 (void)dup2(devnullfd, STDERR_FILENO);
471 ns_os_started(void) {
475 * Signal to the parent that we started successfully.
477 if (dfd[0] != -1 && dfd[1] != -1) {
478 if (write(dfd[1], &buf, 1) != 1)
479 ns_main_earlyfatal("unable to signal parent that we "
480 "otherwise started successfully.");
482 dfd[0] = dfd[1] = -1;
487 ns_os_opendevnull(void) {
488 devnullfd = open("/dev/null", O_RDWR, 0);
492 ns_os_closedevnull(void) {
493 if (devnullfd != STDIN_FILENO &&
494 devnullfd != STDOUT_FILENO &&
495 devnullfd != STDERR_FILENO) {
502 all_digits(const char *s) {
506 if (!isdigit((*s)&0xff))
514 ns_os_chroot(const char *root) {
515 char strbuf[ISC_STRERRORSIZE];
521 if (chroot(root) < 0) {
522 isc__strerror(errno, strbuf, sizeof(strbuf));
523 ns_main_earlyfatal("chroot(): %s", strbuf);
526 ns_main_earlyfatal("chroot(): disabled");
528 if (chdir("/") < 0) {
529 isc__strerror(errno, strbuf, sizeof(strbuf));
530 ns_main_earlyfatal("chdir(/): %s", strbuf);
533 /* Set ns_smf_chroot flag on successful chroot. */
540 ns_os_inituserinfo(const char *username) {
541 char strbuf[ISC_STRERRORSIZE];
542 if (username == NULL)
545 if (all_digits(username))
546 runas_pw = getpwuid((uid_t)atoi(username));
548 runas_pw = getpwnam(username);
551 if (runas_pw == NULL)
552 ns_main_earlyfatal("user '%s' unknown", username);
555 if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
556 isc__strerror(errno, strbuf, sizeof(strbuf));
557 ns_main_earlyfatal("initgroups(): %s", strbuf);
564 ns_os_changeuser(void) {
565 char strbuf[ISC_STRERRORSIZE];
566 if (runas_pw == NULL || done_setuid)
569 done_setuid = ISC_TRUE;
571 #ifdef HAVE_LINUXTHREADS
572 #ifdef HAVE_LINUX_CAPABILITY_H
574 ns_main_earlyfatal("-u with Linux threads not supported: "
575 "requires kernel support for "
576 "prctl(PR_SET_KEEPCAPS)");
578 ns_main_earlyfatal("-u with Linux threads not supported: "
579 "no capabilities support or capabilities "
580 "disabled at build time");
584 if (setgid(runas_pw->pw_gid) < 0) {
585 isc__strerror(errno, strbuf, sizeof(strbuf));
586 ns_main_earlyfatal("setgid(): %s", strbuf);
589 if (setuid(runas_pw->pw_uid) < 0) {
590 isc__strerror(errno, strbuf, sizeof(strbuf));
591 ns_main_earlyfatal("setuid(): %s", strbuf);
594 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
596 * Restore the ability of named to drop core after the setuid()
597 * call has disabled it.
599 if (prctl(PR_SET_DUMPABLE,1,0,0,0) < 0) {
600 isc__strerror(errno, strbuf, sizeof(strbuf));
601 ns_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s",
605 #if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
611 ns_os_adjustnofile(void) {
612 #ifdef HAVE_LINUXTHREADS
614 isc_resourcevalue_t newvalue;
617 * Linux: max number of open files specified by one thread doesn't seem
618 * to apply to other threads on Linux.
620 newvalue = ISC_RESOURCE_UNLIMITED;
622 result = isc_resource_setlimit(isc_resource_openfiles, newvalue);
623 if (result != ISC_R_SUCCESS)
624 ns_main_earlywarning("couldn't adjust limit on open files");
629 ns_os_minprivs(void) {
630 #ifdef HAVE_SYS_PRCTL_H
634 #ifdef HAVE_LINUXTHREADS
635 ns_os_changeuser(); /* Call setuid() before threads are started */
638 #if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
644 safe_open(const char *filename, mode_t mode, isc_boolean_t append) {
648 if (stat(filename, &sb) == -1) {
651 } else if ((sb.st_mode & S_IFREG) == 0) {
657 fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, mode);
659 if (unlink(filename) < 0 && errno != ENOENT)
661 fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, mode);
667 cleanup_pidfile(void) {
669 if (pidfile != NULL) {
671 if (n == -1 && errno != ENOENT)
672 ns_main_earlywarning("unlink '%s': failed", pidfile);
679 mkdirpath(char *filename, void (*report)(const char *, ...)) {
680 char *slash = strrchr(filename, '/');
681 char strbuf[ISC_STRERRORSIZE];
684 if (slash != NULL && slash != filename) {
688 if (stat(filename, &sb) == -1) {
689 if (errno != ENOENT) {
690 isc__strerror(errno, strbuf, sizeof(strbuf));
691 (*report)("couldn't stat '%s': %s", filename,
695 if (mkdirpath(filename, report) == -1)
698 * Handle "//", "/./" and "/../" in path.
700 if (!strcmp(slash + 1, "") ||
701 !strcmp(slash + 1, ".") ||
702 !strcmp(slash + 1, "..")) {
706 mode = S_IRUSR | S_IWUSR | S_IXUSR; /* u=rwx */
707 mode |= S_IRGRP | S_IXGRP; /* g=rx */
708 mode |= S_IROTH | S_IXOTH; /* o=rx */
709 if (mkdir(filename, mode) == -1) {
710 isc__strerror(errno, strbuf, sizeof(strbuf));
711 (*report)("couldn't mkdir '%s': %s", filename,
715 if (runas_pw != NULL &&
716 chown(filename, runas_pw->pw_uid,
717 runas_pw->pw_gid) == -1) {
718 isc__strerror(errno, strbuf, sizeof(strbuf));
719 (*report)("couldn't chown '%s': %s", filename,
733 setperms(uid_t uid, gid_t gid) {
734 char strbuf[ISC_STRERRORSIZE];
735 #if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID)
738 #if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID)
741 #if defined(HAVE_SETEGID)
742 if (getegid() != gid && setegid(gid) == -1) {
743 isc__strerror(errno, strbuf, sizeof(strbuf));
744 ns_main_earlywarning("unable to set effective gid to %ld: %s",
747 #elif defined(HAVE_SETRESGID)
748 if (getresgid(&tmpg, &oldgid, &tmpg) == -1 || oldgid != gid) {
749 if (setresgid(-1, gid, -1) == -1) {
750 isc__strerror(errno, strbuf, sizeof(strbuf));
751 ns_main_earlywarning("unable to set effective "
752 "gid to %d: %s", gid, strbuf);
757 #if defined(HAVE_SETEUID)
758 if (geteuid() != uid && seteuid(uid) == -1) {
759 isc__strerror(errno, strbuf, sizeof(strbuf));
760 ns_main_earlywarning("unable to set effective uid to %ld: %s",
763 #elif defined(HAVE_SETRESUID)
764 if (getresuid(&tmpu, &olduid, &tmpu) == -1 || olduid != uid) {
765 if (setresuid(-1, uid, -1) == -1) {
766 isc__strerror(errno, strbuf, sizeof(strbuf));
767 ns_main_earlywarning("unable to set effective "
768 "uid to %d: %s", uid, strbuf);
775 ns_os_openfile(const char *filename, mode_t mode, isc_boolean_t switch_user) {
776 char strbuf[ISC_STRERRORSIZE], *f;
781 * Make the containing directory if it doesn't exist.
783 f = strdup(filename);
785 isc__strerror(errno, strbuf, sizeof(strbuf));
786 ns_main_earlywarning("couldn't strdup() '%s': %s",
790 if (mkdirpath(f, ns_main_earlywarning) == -1) {
796 if (switch_user && runas_pw != NULL) {
797 #ifndef HAVE_LINUXTHREADS
798 gid_t oldgid = getgid();
800 /* Set UID/GID to the one we'll be running with eventually */
801 setperms(runas_pw->pw_uid, runas_pw->pw_gid);
803 fd = safe_open(filename, mode, ISC_FALSE);
805 #ifndef HAVE_LINUXTHREADS
806 /* Restore UID/GID to root */
808 #endif /* HAVE_LINUXTHREADS */
811 #ifndef HAVE_LINUXTHREADS
812 fd = safe_open(filename, mode, ISC_FALSE);
814 ns_main_earlywarning("Required root "
815 "permissions to open "
818 ns_main_earlywarning("Could not open "
821 ns_main_earlywarning("Please check file and "
822 "directory permissions "
823 "or reconfigure the filename.");
824 #else /* HAVE_LINUXTHREADS */
825 ns_main_earlywarning("Could not open "
827 ns_main_earlywarning("Please check file and "
828 "directory permissions "
829 "or reconfigure the filename.");
830 #endif /* HAVE_LINUXTHREADS */
833 fd = safe_open(filename, mode, ISC_FALSE);
837 isc__strerror(errno, strbuf, sizeof(strbuf));
838 ns_main_earlywarning("could not open file '%s': %s",
843 fp = fdopen(fd, "w");
845 isc__strerror(errno, strbuf, sizeof(strbuf));
846 ns_main_earlywarning("could not fdopen() file '%s': %s",
854 ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
857 char strbuf[ISC_STRERRORSIZE];
858 void (*report)(const char *, ...);
861 * The caller must ensure any required synchronization.
864 report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
868 if (filename == NULL)
871 pidfile = strdup(filename);
872 if (pidfile == NULL) {
873 isc__strerror(errno, strbuf, sizeof(strbuf));
874 (*report)("couldn't strdup() '%s': %s", filename, strbuf);
878 lockfile = ns_os_openfile(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
880 if (lockfile == NULL) {
884 #ifdef HAVE_LINUXTHREADS
889 if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
890 (*report)("fprintf() to pid file '%s' failed", filename);
891 (void)fclose(lockfile);
895 if (fflush(lockfile) == EOF) {
896 (*report)("fflush() to pid file '%s' failed", filename);
897 (void)fclose(lockfile);
901 (void)fclose(lockfile);
905 ns_os_shutdown(void) {
911 ns_os_gethostname(char *buf, size_t len) {
914 n = gethostname(buf, len);
915 return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
919 next_token(char **stringp, const char *delim) {
923 res = strsep(stringp, delim);
926 } while (*res == '\0');
931 ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
938 /* Skip the command name. */
939 ptr = next_token(&input, " \t");
943 ptr = next_token(&input, " \t");
947 if (strcmp(ptr, "-p") != 0)
950 #ifdef HAVE_LINUXTHREADS
956 n = snprintf((char *)isc_buffer_used(text),
957 isc_buffer_availablelength(text),
958 "pid: %ld", (long)pid);
959 /* Only send a message if it is complete. */
960 if (n > 0 && n < isc_buffer_availablelength(text))
961 isc_buffer_add(text, n);
971 static char unamebuf[BUFSIZ];
972 static char *unamep = NULL;
979 memset(&uts, 0, sizeof(uts));
980 if (uname(&uts) < 0) {
981 strcpy(unamebuf, "unknown architecture");
985 snprintf(unamebuf, sizeof(unamebuf),
987 uts.sysname, uts.machine, uts.release, uts.version);
989 strcpy(unamebuf, "unknown architecture");