]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/bind9/bin/named/unix/os.c
MFV r306384:
[FreeBSD/stable/9.git] / contrib / bind9 / bin / named / unix / os.c
1 /*
2  * Copyright (C) 2004-2011, 2013, 2014, 2016  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2002  Internet Software Consortium.
4  *
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.
8  *
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.
16  */
17
18 /*! \file */
19
20 #include <config.h>
21 #include <stdarg.h>
22
23 #include <sys/types.h>  /* dev_t FreeBSD 2.1 */
24 #include <sys/stat.h>
25 #ifdef HAVE_UNAME
26 #include <sys/utsname.h>
27 #endif
28
29 #include <ctype.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <grp.h>                /* Required for initgroups() on IRIX. */
33 #include <pwd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <signal.h>
37 #include <syslog.h>
38 #ifdef HAVE_TZSET
39 #include <time.h>
40 #endif
41 #include <unistd.h>
42
43 #include <isc/buffer.h>
44 #include <isc/file.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>
50
51 #include <named/main.h>
52 #include <named/os.h>
53 #ifdef HAVE_LIBSCF
54 #include <named/ns_smf_globals.h>
55 #endif
56
57 static char *pidfile = NULL;
58 static int devnullfd = -1;
59
60 #ifndef ISC_FACILITY
61 #define ISC_FACILITY LOG_DAEMON
62 #endif
63
64 /*
65  * If there's no <linux/capability.h>, we don't care about <sys/prctl.h>
66  */
67 #ifndef HAVE_LINUX_CAPABILITY_H
68 #undef HAVE_SYS_PRCTL_H
69 #endif
70
71 /*
72  * Linux defines:
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
78  *      T:      no setuid()
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)
83  *      P:      not possible
84  *      T+P:    not possible
85  *
86  * if (C)
87  *      caps = BIND_SERVICE + CHROOT + SETGID
88  *      if ((T && C && P) || !T)
89  *              caps += SETUID
90  *      endif
91  *      capset(caps)
92  * endif
93  * if (T && C && P && -u)
94  *      setuid()
95  * else if (T && -u)
96  *      fail
97  * --> start threads
98  * if (!T && -u)
99  *      setuid()
100  * if (C && (P || !-u))
101  *      caps = BIND_SERVICE
102  *      capset(caps)
103  * endif
104  *
105  * It will be nice when Linux threads work properly with setuid().
106  */
107
108 #ifdef HAVE_LINUXTHREADS
109 static pid_t mainpid = 0;
110 #endif
111
112 static struct passwd *runas_pw = NULL;
113 static isc_boolean_t done_setuid = ISC_FALSE;
114 static int dfd[2] = { -1, -1 };
115
116 #ifdef HAVE_LINUX_CAPABILITY_H
117
118 static isc_boolean_t non_root = ISC_FALSE;
119 static isc_boolean_t non_root_caps = ISC_FALSE;
120
121 #ifdef HAVE_SYS_CAPABILITY_H
122 #include <sys/capability.h>
123 #else
124 #ifdef HAVE_LINUX_TYPES_H
125 #include <linux/types.h>
126 #endif
127 /*%
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.
132  */
133 #define _LINUX_FS_H
134 #include <linux/capability.h>
135 #include <syscall.h>
136 #ifndef SYS_capset
137 #ifndef __NR_capset
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 */
143
144 #ifdef HAVE_SYS_PRCTL_H
145 #include <sys/prctl.h>          /* Required for prctl(). */
146
147 /*
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
151  * headers.
152  */
153 #ifndef PR_SET_KEEPCAPS
154 #define PR_SET_KEEPCAPS 8
155 #endif
156
157 #endif /* HAVE_SYS_PRCTL_H */
158
159 #ifdef HAVE_LIBCAP
160 #define SETCAPS_FUNC "cap_set_proc "
161 #else
162 typedef unsigned int cap_t;
163 #define SETCAPS_FUNC "syscall(capset) "
164 #endif /* HAVE_LIBCAP */
165
166 static void
167 linux_setcaps(cap_t caps) {
168 #ifndef HAVE_LIBCAP
169         struct __user_cap_header_struct caphead;
170         struct __user_cap_data_struct cap;
171 #endif
172         char strbuf[ISC_STRERRORSIZE];
173
174         if ((getuid() != 0 && !non_root_caps) || non_root)
175                 return;
176 #ifndef HAVE_LIBCAP
177         memset(&caphead, 0, sizeof(caphead));
178         caphead.version = _LINUX_CAPABILITY_VERSION;
179         caphead.pid = 0;
180         memset(&cap, 0, sizeof(cap));
181         cap.effective = caps;
182         cap.permitted = caps;
183         cap.inheritable = 0;
184 #endif
185 #ifdef HAVE_LIBCAP
186         if (cap_set_proc(caps) < 0) {
187 #else
188         if (syscall(SYS_capset, &caphead, &cap) < 0) {
189 #endif
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)",
194                                    strbuf);
195         }
196 }
197
198 #ifdef HAVE_LIBCAP
199 #define SET_CAP(flag) \
200         do { \
201                 cap_flag_value_t curval; \
202                 capval = (flag); \
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); \
206                         if (err == -1) { \
207                                 isc__strerror(errno, strbuf, sizeof(strbuf)); \
208                                 ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
209                         } \
210                         \
211                         err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET); \
212                         if (err == -1) { \
213                                 isc__strerror(errno, strbuf, sizeof(strbuf)); \
214                                 ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
215                         } \
216                 } \
217         } while (0)
218 #define INIT_CAP \
219         do { \
220                 caps = cap_init(); \
221                 if (caps == NULL) { \
222                         isc__strerror(errno, strbuf, sizeof(strbuf)); \
223                         ns_main_earlyfatal("cap_init failed: %s", strbuf); \
224                 } \
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); \
229                 } \
230         } while (0)
231 #define FREE_CAP \
232         { \
233                 cap_free(caps); \
234                 cap_free(curcaps); \
235         } while (0)
236 #else
237 #define SET_CAP(flag) do { caps |= (1 << (flag)); } while (0)
238 #define INIT_CAP do { caps = 0; } while (0)
239 #endif /* HAVE_LIBCAP */
240
241 static void
242 linux_initialprivs(void) {
243         cap_t caps;
244 #ifdef HAVE_LIBCAP
245         cap_t curcaps;
246         cap_value_t capval;
247         char strbuf[ISC_STRERRORSIZE];
248         int err;
249 #endif
250
251         /*%
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.
255          */
256         INIT_CAP;
257
258         /*
259          * We need to be able to bind() to privileged ports, notably port 53!
260          */
261         SET_CAP(CAP_NET_BIND_SERVICE);
262
263         /*
264          * We need chroot() initially too.
265          */
266         SET_CAP(CAP_SYS_CHROOT);
267
268 #if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
269         /*
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.
274          */
275         SET_CAP(CAP_SETUID);
276 #endif
277
278         /*
279          * Since we call initgroups, we need this.
280          */
281         SET_CAP(CAP_SETGID);
282
283         /*
284          * Without this, we run into problems reading a configuration file
285          * owned by a non-root user and non-world-readable on startup.
286          */
287         SET_CAP(CAP_DAC_READ_SEARCH);
288
289         /*
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.
295          */
296         SET_CAP(CAP_SYS_RESOURCE);
297
298         /*
299          * We need to be able to set the ownership of the containing
300          * directory of the pid file when we create it.
301          */
302         SET_CAP(CAP_CHOWN);
303
304         linux_setcaps(caps);
305
306 #ifdef HAVE_LIBCAP
307         FREE_CAP;
308 #endif
309 }
310
311 static void
312 linux_minprivs(void) {
313         cap_t caps;
314 #ifdef HAVE_LIBCAP
315         cap_t curcaps;
316         cap_value_t capval;
317         char strbuf[ISC_STRERRORSIZE];
318         int err;
319 #endif
320
321         INIT_CAP;
322         /*%
323          * Drop all privileges except the ability to bind() to privileged
324          * ports.
325          *
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.
328          */
329
330         SET_CAP(CAP_NET_BIND_SERVICE);
331
332         /*
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.
338          */
339         SET_CAP(CAP_SYS_RESOURCE);
340
341         linux_setcaps(caps);
342
343 #ifdef HAVE_LIBCAP
344         FREE_CAP;
345 #endif
346 }
347
348 #ifdef HAVE_SYS_PRCTL_H
349 static void
350 linux_keepcaps(void) {
351         char strbuf[ISC_STRERRORSIZE];
352         /*%
353          * Ask the kernel to allow us to keep our capabilities after we
354          * setuid().
355          */
356
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);
361                 }
362         } else {
363                 non_root_caps = ISC_TRUE;
364                 if (getuid() != 0)
365                         non_root = ISC_TRUE;
366         }
367 }
368 #endif
369
370 #endif  /* HAVE_LINUX_CAPABILITY_H */
371
372
373 static void
374 setup_syslog(const char *progname) {
375         int options;
376
377         options = LOG_PID;
378 #ifdef LOG_NDELAY
379         options |= LOG_NDELAY;
380 #endif
381         openlog(isc_file_basename(progname), options, ISC_FACILITY);
382 }
383
384 void
385 ns_os_init(const char *progname) {
386         setup_syslog(progname);
387 #ifdef HAVE_LINUX_CAPABILITY_H
388         linux_initialprivs();
389 #endif
390 #ifdef HAVE_LINUXTHREADS
391         mainpid = getpid();
392 #endif
393 #ifdef SIGXFSZ
394         signal(SIGXFSZ, SIG_IGN);
395 #endif
396 }
397
398 void
399 ns_os_daemonize(void) {
400         pid_t pid;
401         char strbuf[ISC_STRERRORSIZE];
402
403         if (pipe(dfd) == -1) {
404                 isc__strerror(errno, strbuf, sizeof(strbuf));
405                 ns_main_earlyfatal("pipe(): %s", strbuf);
406         }
407
408         pid = fork();
409         if (pid == -1) {
410                 isc__strerror(errno, strbuf, sizeof(strbuf));
411                 ns_main_earlyfatal("fork(): %s", strbuf);
412         }
413         if (pid != 0) {
414                 int n;
415                 /*
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.
419                  */
420                 (void)close(dfd[1]);
421                 do {
422                         char buf;
423                         n = read(dfd[0], &buf, 1);
424                         if (n == 1)
425                                 _exit(0);
426                 } while (n == -1 && errno == EINTR);
427                 _exit(1);
428         }
429         (void)close(dfd[0]);
430
431         /*
432          * We're the child.
433          */
434
435 #ifdef HAVE_LINUXTHREADS
436         mainpid = getpid();
437 #endif
438
439         if (setsid() == -1) {
440                 isc__strerror(errno, strbuf, sizeof(strbuf));
441                 ns_main_earlyfatal("setsid(): %s", strbuf);
442         }
443
444         /*
445          * Try to set stdin, stdout, and stderr to /dev/null, but press
446          * on even if it fails.
447          *
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.
453          */
454         if (devnullfd != -1) {
455                 if (devnullfd != STDIN_FILENO) {
456                         (void)close(STDIN_FILENO);
457                         (void)dup2(devnullfd, STDIN_FILENO);
458                 }
459                 if (devnullfd != STDOUT_FILENO) {
460                         (void)close(STDOUT_FILENO);
461                         (void)dup2(devnullfd, STDOUT_FILENO);
462                 }
463                 if (devnullfd != STDERR_FILENO) {
464                         (void)close(STDERR_FILENO);
465                         (void)dup2(devnullfd, STDERR_FILENO);
466                 }
467         }
468 }
469
470 void
471 ns_os_started(void) {
472         char buf = 0;
473
474         /*
475          * Signal to the parent that we started successfully.
476          */
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.");
481                 close(dfd[1]);
482                 dfd[0] = dfd[1] = -1;
483         }
484 }
485
486 void
487 ns_os_opendevnull(void) {
488         devnullfd = open("/dev/null", O_RDWR, 0);
489 }
490
491 void
492 ns_os_closedevnull(void) {
493         if (devnullfd != STDIN_FILENO &&
494             devnullfd != STDOUT_FILENO &&
495             devnullfd != STDERR_FILENO) {
496                 close(devnullfd);
497                 devnullfd = -1;
498         }
499 }
500
501 static isc_boolean_t
502 all_digits(const char *s) {
503         if (*s == '\0')
504                 return (ISC_FALSE);
505         while (*s != '\0') {
506                 if (!isdigit((*s)&0xff))
507                         return (ISC_FALSE);
508                 s++;
509         }
510         return (ISC_TRUE);
511 }
512
513 void
514 ns_os_chroot(const char *root) {
515         char strbuf[ISC_STRERRORSIZE];
516 #ifdef HAVE_LIBSCF
517         ns_smf_chroot = 0;
518 #endif
519         if (root != NULL) {
520 #ifdef HAVE_CHROOT
521                 if (chroot(root) < 0) {
522                         isc__strerror(errno, strbuf, sizeof(strbuf));
523                         ns_main_earlyfatal("chroot(): %s", strbuf);
524                 }
525 #else
526                 ns_main_earlyfatal("chroot(): disabled");
527 #endif
528                 if (chdir("/") < 0) {
529                         isc__strerror(errno, strbuf, sizeof(strbuf));
530                         ns_main_earlyfatal("chdir(/): %s", strbuf);
531                 }
532 #ifdef HAVE_LIBSCF
533                 /* Set ns_smf_chroot flag on successful chroot. */
534                 ns_smf_chroot = 1;
535 #endif
536         }
537 }
538
539 void
540 ns_os_inituserinfo(const char *username) {
541         char strbuf[ISC_STRERRORSIZE];
542         if (username == NULL)
543                 return;
544
545         if (all_digits(username))
546                 runas_pw = getpwuid((uid_t)atoi(username));
547         else
548                 runas_pw = getpwnam(username);
549         endpwent();
550
551         if (runas_pw == NULL)
552                 ns_main_earlyfatal("user '%s' unknown", username);
553
554         if (getuid() == 0) {
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);
558                 }
559         }
560
561 }
562
563 void
564 ns_os_changeuser(void) {
565         char strbuf[ISC_STRERRORSIZE];
566         if (runas_pw == NULL || done_setuid)
567                 return;
568
569         done_setuid = ISC_TRUE;
570
571 #ifdef HAVE_LINUXTHREADS
572 #ifdef HAVE_LINUX_CAPABILITY_H
573         if (!non_root_caps)
574                 ns_main_earlyfatal("-u with Linux threads not supported: "
575                                    "requires kernel support for "
576                                    "prctl(PR_SET_KEEPCAPS)");
577 #else
578         ns_main_earlyfatal("-u with Linux threads not supported: "
579                            "no capabilities support or capabilities "
580                            "disabled at build time");
581 #endif
582 #endif
583
584         if (setgid(runas_pw->pw_gid) < 0) {
585                 isc__strerror(errno, strbuf, sizeof(strbuf));
586                 ns_main_earlyfatal("setgid(): %s", strbuf);
587         }
588
589         if (setuid(runas_pw->pw_uid) < 0) {
590                 isc__strerror(errno, strbuf, sizeof(strbuf));
591                 ns_main_earlyfatal("setuid(): %s", strbuf);
592         }
593
594 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
595         /*
596          * Restore the ability of named to drop core after the setuid()
597          * call has disabled it.
598          */
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",
602                                      strbuf);
603         }
604 #endif
605 #if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
606         linux_minprivs();
607 #endif
608 }
609
610 void
611 ns_os_adjustnofile(void) {
612 #ifdef HAVE_LINUXTHREADS
613         isc_result_t result;
614         isc_resourcevalue_t newvalue;
615
616         /*
617          * Linux: max number of open files specified by one thread doesn't seem
618          * to apply to other threads on Linux.
619          */
620         newvalue = ISC_RESOURCE_UNLIMITED;
621
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");
625 #endif
626 }
627
628 void
629 ns_os_minprivs(void) {
630 #ifdef HAVE_SYS_PRCTL_H
631         linux_keepcaps();
632 #endif
633
634 #ifdef HAVE_LINUXTHREADS
635         ns_os_changeuser(); /* Call setuid() before threads are started */
636 #endif
637
638 #if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
639         linux_minprivs();
640 #endif
641 }
642
643 static int
644 safe_open(const char *filename, mode_t mode, isc_boolean_t append) {
645         int fd;
646         struct stat sb;
647
648         if (stat(filename, &sb) == -1) {
649                 if (errno != ENOENT)
650                         return (-1);
651         } else if ((sb.st_mode & S_IFREG) == 0) {
652                 errno = EOPNOTSUPP;
653                 return (-1);
654         }
655
656         if (append)
657                 fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, mode);
658         else {
659                 if (unlink(filename) < 0 && errno != ENOENT)
660                         return (-1);
661                 fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, mode);
662         }
663         return (fd);
664 }
665
666 static void
667 cleanup_pidfile(void) {
668         int n;
669         if (pidfile != NULL) {
670                 n = unlink(pidfile);
671                 if (n == -1 && errno != ENOENT)
672                         ns_main_earlywarning("unlink '%s': failed", pidfile);
673                 free(pidfile);
674         }
675         pidfile = NULL;
676 }
677
678 static int
679 mkdirpath(char *filename, void (*report)(const char *, ...)) {
680         char *slash = strrchr(filename, '/');
681         char strbuf[ISC_STRERRORSIZE];
682         unsigned int mode;
683
684         if (slash != NULL && slash != filename) {
685                 struct stat sb;
686                 *slash = '\0';
687
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,
692                                           strbuf);
693                                 goto error;
694                         }
695                         if (mkdirpath(filename, report) == -1)
696                                 goto error;
697                         /*
698                          * Handle "//", "/./" and "/../" in path.
699                          */
700                         if (!strcmp(slash + 1, "") ||
701                             !strcmp(slash + 1, ".") ||
702                             !strcmp(slash + 1, "..")) {
703                                 *slash = '/';
704                                 return (0);
705                         }
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,
712                                           strbuf);
713                                 goto error;
714                         }
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,
720                                           strbuf);
721                         }
722                 }
723                 *slash = '/';
724         }
725         return (0);
726
727  error:
728         *slash = '/';
729         return (-1);
730 }
731
732 static void
733 setperms(uid_t uid, gid_t gid) {
734         char strbuf[ISC_STRERRORSIZE];
735 #if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID)
736         gid_t oldgid, tmpg;
737 #endif
738 #if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID)
739         uid_t olduid, tmpu;
740 #endif
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",
745                                      (long)gid, strbuf);
746         }
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);
753                 }
754         }
755 #endif
756
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",
761                                      (long)uid, strbuf);
762         }
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);
769                 }
770         }
771 #endif
772 }
773
774 FILE *
775 ns_os_openfile(const char *filename, mode_t mode, isc_boolean_t switch_user) {
776         char strbuf[ISC_STRERRORSIZE], *f;
777         FILE *fp;
778         int fd;
779
780         /*
781          * Make the containing directory if it doesn't exist.
782          */
783         f = strdup(filename);
784         if (f == NULL) {
785                 isc__strerror(errno, strbuf, sizeof(strbuf));
786                 ns_main_earlywarning("couldn't strdup() '%s': %s",
787                                      filename, strbuf);
788                 return (NULL);
789         }
790         if (mkdirpath(f, ns_main_earlywarning) == -1) {
791                 free(f);
792                 return (NULL);
793         }
794         free(f);
795
796         if (switch_user && runas_pw != NULL) {
797 #ifndef HAVE_LINUXTHREADS
798                 gid_t oldgid = getgid();
799 #endif
800                 /* Set UID/GID to the one we'll be running with eventually */
801                 setperms(runas_pw->pw_uid, runas_pw->pw_gid);
802
803                 fd = safe_open(filename, mode, ISC_FALSE);
804
805 #ifndef HAVE_LINUXTHREADS
806                 /* Restore UID/GID to root */
807                 setperms(0, oldgid);
808 #endif /* HAVE_LINUXTHREADS */
809
810                 if (fd == -1) {
811 #ifndef HAVE_LINUXTHREADS
812                         fd = safe_open(filename, mode, ISC_FALSE);
813                         if (fd != -1) {
814                                 ns_main_earlywarning("Required root "
815                                                      "permissions to open "
816                                                      "'%s'.", filename);
817                         } else {
818                                 ns_main_earlywarning("Could not open "
819                                                      "'%s'.", filename);
820                         }
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 "
826                                              "'%s'.", filename);
827                         ns_main_earlywarning("Please check file and "
828                                              "directory permissions "
829                                              "or reconfigure the filename.");
830 #endif /* HAVE_LINUXTHREADS */
831                 }
832         } else {
833                 fd = safe_open(filename, mode, ISC_FALSE);
834         }
835
836         if (fd < 0) {
837                 isc__strerror(errno, strbuf, sizeof(strbuf));
838                 ns_main_earlywarning("could not open file '%s': %s",
839                                      filename, strbuf);
840                 return (NULL);
841         }
842
843         fp = fdopen(fd, "w");
844         if (fp == NULL) {
845                 isc__strerror(errno, strbuf, sizeof(strbuf));
846                 ns_main_earlywarning("could not fdopen() file '%s': %s",
847                                      filename, strbuf);
848         }
849
850         return (fp);
851 }
852
853 void
854 ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
855         FILE *lockfile;
856         pid_t pid;
857         char strbuf[ISC_STRERRORSIZE];
858         void (*report)(const char *, ...);
859
860         /*
861          * The caller must ensure any required synchronization.
862          */
863
864         report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
865
866         cleanup_pidfile();
867
868         if (filename == NULL)
869                 return;
870
871         pidfile = strdup(filename);
872         if (pidfile == NULL) {
873                 isc__strerror(errno, strbuf, sizeof(strbuf));
874                 (*report)("couldn't strdup() '%s': %s", filename, strbuf);
875                 return;
876         }
877
878         lockfile = ns_os_openfile(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
879                                   first_time);
880         if (lockfile == NULL) {
881                 cleanup_pidfile();
882                 return;
883         }
884 #ifdef HAVE_LINUXTHREADS
885         pid = mainpid;
886 #else
887         pid = getpid();
888 #endif
889         if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
890                 (*report)("fprintf() to pid file '%s' failed", filename);
891                 (void)fclose(lockfile);
892                 cleanup_pidfile();
893                 return;
894         }
895         if (fflush(lockfile) == EOF) {
896                 (*report)("fflush() to pid file '%s' failed", filename);
897                 (void)fclose(lockfile);
898                 cleanup_pidfile();
899                 return;
900         }
901         (void)fclose(lockfile);
902 }
903
904 void
905 ns_os_shutdown(void) {
906         closelog();
907         cleanup_pidfile();
908 }
909
910 isc_result_t
911 ns_os_gethostname(char *buf, size_t len) {
912         int n;
913
914         n = gethostname(buf, len);
915         return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
916 }
917
918 static char *
919 next_token(char **stringp, const char *delim) {
920         char *res;
921
922         do {
923                 res = strsep(stringp, delim);
924                 if (res == NULL)
925                         break;
926         } while (*res == '\0');
927         return (res);
928 }
929
930 void
931 ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
932         char *input, *ptr;
933         unsigned int n;
934         pid_t pid;
935
936         input = command;
937
938         /* Skip the command name. */
939         ptr = next_token(&input, " \t");
940         if (ptr == NULL)
941                 return;
942
943         ptr = next_token(&input, " \t");
944         if (ptr == NULL)
945                 return;
946
947         if (strcmp(ptr, "-p") != 0)
948                 return;
949
950 #ifdef HAVE_LINUXTHREADS
951         pid = mainpid;
952 #else
953         pid = getpid();
954 #endif
955
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);
962 }
963
964 void
965 ns_os_tzset(void) {
966 #ifdef HAVE_TZSET
967         tzset();
968 #endif
969 }
970
971 static char unamebuf[BUFSIZ];
972 static char *unamep = NULL;
973
974 static void
975 getuname(void) {
976 #ifdef HAVE_UNAME
977         struct utsname uts;
978
979         memset(&uts, 0, sizeof(uts));
980         if (uname(&uts) < 0) {
981                 strcpy(unamebuf, "unknown architecture");
982                 return;
983         }
984
985         snprintf(unamebuf, sizeof(unamebuf),
986                  "%s %s %s %s",
987                  uts.sysname, uts.machine, uts.release, uts.version);
988 #else
989         strcpy(unamebuf, "unknown architecture");
990 #endif
991         unamep = unamebuf;
992 }
993
994 char *
995 ns_os_uname(void) {
996         if (unamep == NULL)
997                 getuname();
998         return (unamep);
999 }