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