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