1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12 /*! \file */
13
14 #include <config.h>
15 #include <stdarg.h>
16 #include <stdbool.h>
17
18 #include <sys/types.h> /* dev_t FreeBSD 2.1 */
19 #include <sys/stat.h>
20 #ifdef HAVE_UNAME
21 #include <sys/utsname.h>
22 #endif
23
24 #include <ctype.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <grp.h> /* Required for initgroups() on IRIX. */
28 #include <pwd.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <signal.h>
32 #include <syslog.h>
33 #ifdef HAVE_TZSET
34 #include <time.h>
35 #endif
36 #include <unistd.h>
37
38 #include <isc/buffer.h>
39 #include <isc/file.h>
40 #include <isc/print.h>
41 #include <isc/resource.h>
42 #include <isc/result.h>
43 #include <isc/strerror.h>
44 #include <isc/string.h>
45 #include <isc/util.h>
46
47 #include <named/globals.h>
48 #include <named/main.h>
49 #include <named/os.h>
50 #ifdef HAVE_LIBSCF
51 #include <named/ns_smf_globals.h>
52 #endif
53
54 static char *pidfile = NULL;
55 static char *lockfile = NULL;
56 static int devnullfd = -1;
57 static int singletonfd = -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 bool done_setuid = false;
113 static int dfd[2] = { -1, -1 };
114
115 #ifdef HAVE_LINUX_CAPABILITY_H
116
117 static bool non_root = false;
118 static bool non_root_caps = 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
linux_setcaps(cap_t caps)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 cap_flag_value_t curval; \
201 capval = (flag); \
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) \
237 do { \
238 if (curcaps & (1 << (flag))) { \
239 caps |= (1 << (flag)); \
240 } \
241 } while (0)
242 #define INIT_CAP do { caps = 0; } while (0)
243 #endif /* HAVE_LIBCAP */
244
245 #ifndef HAVE_LIBCAP
246 /*%
247 * Store the bitmask representing the permitted capability set in 'capsp'. To
248 * match libcap-enabled behavior, capget() syscall errors are not reported,
249 * they just cause 'capsp' to be set to 0, which effectively prevents any
250 * capability from being subsequently requested.
251 */
252 static void
253 linux_getpermittedcaps(cap_t *capsp) {
254 struct __user_cap_header_struct caphead;
255 struct __user_cap_data_struct curcaps;
256
257 memset(&caphead, 0, sizeof(caphead));
258 caphead.version = _LINUX_CAPABILITY_VERSION;
259 caphead.pid = 0;
260 memset(&curcaps, 0, sizeof(curcaps));
261 syscall(SYS_capget, &caphead, &curcaps);
262
263 *capsp = curcaps.permitted;
264 }
265 #endif /* HAVE_LIBCAP */
266
267 static void
268 linux_initialprivs(void) {
269 cap_t curcaps;
270 cap_t caps;
271 #ifdef HAVE_LIBCAP
272 cap_value_t capval;
273 char strbuf[ISC_STRERRORSIZE];
274 int err;
275 #else
276 linux_getpermittedcaps(&curcaps);
277 #endif
278
279 /*%
280 * We don't need most privileges, so we drop them right away.
281 * Later on linux_minprivs() will be called, which will drop our
282 * capabilities to the minimum needed to run the server.
283 */
284 INIT_CAP;
285
286 /*
287 * We need to be able to bind() to privileged ports, notably port 53!
288 */
289 SET_CAP(CAP_NET_BIND_SERVICE);
290
291 /*
292 * We need chroot() initially too.
293 */
294 SET_CAP(CAP_SYS_CHROOT);
295
296 #if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
297 /*
298 * We can setuid() only if either the kernel supports keeping
299 * capabilities after setuid() (which we don't know until we've
300 * tried) or we're not using threads. If either of these is
301 * true, we want the setuid capability.
302 */
303 SET_CAP(CAP_SETUID);
304 #endif
305
306 /*
307 * Since we call initgroups, we need this.
308 */
309 SET_CAP(CAP_SETGID);
310
311 /*
312 * Without this, we run into problems reading a configuration file
313 * owned by a non-root user and non-world-readable on startup.
314 */
315 SET_CAP(CAP_DAC_READ_SEARCH);
316
317 /*
318 * XXX We might want to add CAP_SYS_RESOURCE, though it's not
319 * clear it would work right given the way linuxthreads work.
320 * XXXDCL But since we need to be able to set the maximum number
321 * of files, the stack size, data size, and core dump size to
322 * support named.conf options, this is now being added to test.
323 */
324 SET_CAP(CAP_SYS_RESOURCE);
325
326 /*
327 * We need to be able to set the ownership of the containing
328 * directory of the pid file when we create it.
329 */
330 SET_CAP(CAP_CHOWN);
331
332 linux_setcaps(caps);
333
334 #ifdef HAVE_LIBCAP
335 FREE_CAP;
336 #endif
337 }
338
339 static void
340 linux_minprivs(void) {
341 cap_t curcaps;
342 cap_t caps;
343 #ifdef HAVE_LIBCAP
344 cap_value_t capval;
345 char strbuf[ISC_STRERRORSIZE];
346 int err;
347 #else
348 linux_getpermittedcaps(&curcaps);
349 #endif
350
351 INIT_CAP;
352 /*%
353 * Drop all privileges except the ability to bind() to privileged
354 * ports.
355 *
356 * It's important that we drop CAP_SYS_CHROOT. If we didn't, it
357 * chroot() could be used to escape from the chrooted area.
358 */
359
360 SET_CAP(CAP_NET_BIND_SERVICE);
361
362 /*
363 * XXX We might want to add CAP_SYS_RESOURCE, though it's not
364 * clear it would work right given the way linuxthreads work.
365 * XXXDCL But since we need to be able to set the maximum number
366 * of files, the stack size, data size, and core dump size to
367 * support named.conf options, this is now being added to test.
368 */
369 SET_CAP(CAP_SYS_RESOURCE);
370
371 linux_setcaps(caps);
372
373 #ifdef HAVE_LIBCAP
374 FREE_CAP;
375 #endif
376 }
377
378 #ifdef HAVE_SYS_PRCTL_H
379 static void
380 linux_keepcaps(void) {
381 char strbuf[ISC_STRERRORSIZE];
382 /*%
383 * Ask the kernel to allow us to keep our capabilities after we
384 * setuid().
385 */
386
387 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
388 if (errno != EINVAL) {
389 isc__strerror(errno, strbuf, sizeof(strbuf));
390 ns_main_earlyfatal("prctl() failed: %s", strbuf);
391 }
392 } else {
393 non_root_caps = true;
394 if (getuid() != 0)
395 non_root = true;
396 }
397 }
398 #endif
399
400 #endif /* HAVE_LINUX_CAPABILITY_H */
401
402
403 static void
404 setup_syslog(const char *progname) {
405 int options;
406
407 options = LOG_PID;
408 #ifdef LOG_NDELAY
409 options |= LOG_NDELAY;
410 #endif
411 openlog(isc_file_basename(progname), options, ISC_FACILITY);
412 }
413
414 void
415 ns_os_init(const char *progname) {
416 setup_syslog(progname);
417 #ifdef HAVE_LINUX_CAPABILITY_H
418 linux_initialprivs();
419 #endif
420 #ifdef HAVE_LINUXTHREADS
421 mainpid = getpid();
422 #endif
423 #ifdef SIGXFSZ
424 signal(SIGXFSZ, SIG_IGN);
425 #endif
426 }
427
428 void
429 ns_os_daemonize(void) {
430 pid_t pid;
431 char strbuf[ISC_STRERRORSIZE];
432
433 if (pipe(dfd) == -1) {
434 isc__strerror(errno, strbuf, sizeof(strbuf));
435 ns_main_earlyfatal("pipe(): %s", strbuf);
436 }
437
438 pid = fork();
439 if (pid == -1) {
440 isc__strerror(errno, strbuf, sizeof(strbuf));
441 ns_main_earlyfatal("fork(): %s", strbuf);
442 }
443 if (pid != 0) {
444 int n;
445 /*
446 * Wait for the child to finish loading for the first time.
447 * This would be so much simpler if fork() worked once we
448 * were multi-threaded.
449 */
450 (void)close(dfd[1]);
451 do {
452 char buf;
453 n = read(dfd[0], &buf, 1);
454 if (n == 1)
455 _exit(0);
456 } while (n == -1 && errno == EINTR);
457 _exit(1);
458 }
459 (void)close(dfd[0]);
460
461 /*
462 * We're the child.
463 */
464
465 #ifdef HAVE_LINUXTHREADS
466 mainpid = getpid();
467 #endif
468
469 if (setsid() == -1) {
470 isc__strerror(errno, strbuf, sizeof(strbuf));
471 ns_main_earlyfatal("setsid(): %s", strbuf);
472 }
473
474 /*
475 * Try to set stdin, stdout, and stderr to /dev/null, but press
476 * on even if it fails.
477 *
478 * XXXMLG The close() calls here are unneeded on all but NetBSD, but
479 * are harmless to include everywhere. dup2() is supposed to close
480 * the FD if it is in use, but unproven-pthreads-0.16 is broken
481 * and will end up closing the wrong FD. This will be fixed eventually,
482 * and these calls will be removed.
483 */
484 if (devnullfd != -1) {
485 if (devnullfd != STDIN_FILENO) {
486 (void)close(STDIN_FILENO);
487 (void)dup2(devnullfd, STDIN_FILENO);
488 }
489 if (devnullfd != STDOUT_FILENO) {
490 (void)close(STDOUT_FILENO);
491 (void)dup2(devnullfd, STDOUT_FILENO);
492 }
493 if (devnullfd != STDERR_FILENO && !ns_g_keepstderr) {
494 (void)close(STDERR_FILENO);
495 (void)dup2(devnullfd, STDERR_FILENO);
496 }
497 }
498 }
499
500 void
501 ns_os_started(void) {
502 char buf = 0;
503
504 /*
505 * Signal to the parent that we started successfully.
506 */
507 if (dfd[0] != -1 && dfd[1] != -1) {
508 if (write(dfd[1], &buf, 1) != 1)
509 ns_main_earlyfatal("unable to signal parent that we "
510 "otherwise started successfully.");
511 close(dfd[1]);
512 dfd[0] = dfd[1] = -1;
513 }
514 }
515
516 void
517 ns_os_opendevnull(void) {
518 devnullfd = open("/dev/null", O_RDWR, 0);
519 }
520
521 void
522 ns_os_closedevnull(void) {
523 if (devnullfd != STDIN_FILENO &&
524 devnullfd != STDOUT_FILENO &&
525 devnullfd != STDERR_FILENO) {
526 close(devnullfd);
527 devnullfd = -1;
528 }
529 }
530
531 static bool
532 all_digits(const char *s) {
533 if (*s == '\0')
534 return (false);
535 while (*s != '\0') {
536 if (!isdigit((*s)&0xff))
537 return (false);
538 s++;
539 }
540 return (true);
541 }
542
543 void
544 ns_os_chroot(const char *root) {
545 char strbuf[ISC_STRERRORSIZE];
546 #ifdef HAVE_LIBSCF
547 ns_smf_chroot = 0;
548 #endif
549 if (root != NULL) {
550 #ifdef HAVE_CHROOT
551 if (chroot(root) < 0) {
552 isc__strerror(errno, strbuf, sizeof(strbuf));
553 ns_main_earlyfatal("chroot(): %s", strbuf);
554 }
555 #else
556 ns_main_earlyfatal("chroot(): disabled");
557 #endif
558 if (chdir("/") < 0) {
559 isc__strerror(errno, strbuf, sizeof(strbuf));
560 ns_main_earlyfatal("chdir(/): %s", strbuf);
561 }
562 #ifdef HAVE_LIBSCF
563 /* Set ns_smf_chroot flag on successful chroot. */
564 ns_smf_chroot = 1;
565 #endif
566 }
567 }
568
569 void
570 ns_os_inituserinfo(const char *username) {
571 char strbuf[ISC_STRERRORSIZE];
572 if (username == NULL)
573 return;
574
575 if (all_digits(username))
576 runas_pw = getpwuid((uid_t)atoi(username));
577 else
578 runas_pw = getpwnam(username);
579 endpwent();
580
581 if (runas_pw == NULL)
582 ns_main_earlyfatal("user '%s' unknown", username);
583
584 if (getuid() == 0) {
585 if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
586 isc__strerror(errno, strbuf, sizeof(strbuf));
587 ns_main_earlyfatal("initgroups(): %s", strbuf);
588 }
589 }
590
591 }
592
593 void
594 ns_os_changeuser(void) {
595 char strbuf[ISC_STRERRORSIZE];
596 if (runas_pw == NULL || done_setuid)
597 return;
598
599 done_setuid = true;
600
601 #ifdef HAVE_LINUXTHREADS
602 #ifdef HAVE_LINUX_CAPABILITY_H
603 if (!non_root_caps)
604 ns_main_earlyfatal("-u with Linux threads not supported: "
605 "requires kernel support for "
606 "prctl(PR_SET_KEEPCAPS)");
607 #else
608 ns_main_earlyfatal("-u with Linux threads not supported: "
609 "no capabilities support or capabilities "
610 "disabled at build time");
611 #endif
612 #endif
613
614 if (setgid(runas_pw->pw_gid) < 0) {
615 isc__strerror(errno, strbuf, sizeof(strbuf));
616 ns_main_earlyfatal("setgid(): %s", strbuf);
617 }
618
619 if (setuid(runas_pw->pw_uid) < 0) {
620 isc__strerror(errno, strbuf, sizeof(strbuf));
621 ns_main_earlyfatal("setuid(): %s", strbuf);
622 }
623
624 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
625 /*
626 * Restore the ability of named to drop core after the setuid()
627 * call has disabled it.
628 */
629 if (prctl(PR_SET_DUMPABLE,1,0,0,0) < 0) {
630 isc__strerror(errno, strbuf, sizeof(strbuf));
631 ns_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s",
632 strbuf);
633 }
634 #endif
635 #if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
636 linux_minprivs();
637 #endif
638 }
639
640 uid_t
641 ns_os_uid(void) {
642 if (runas_pw == NULL)
643 return (0);
644 return (runas_pw->pw_uid);
645 }
646
647 void
648 ns_os_adjustnofile(void) {
649 #ifdef HAVE_LINUXTHREADS
650 isc_result_t result;
651 isc_resourcevalue_t newvalue;
652
653 /*
654 * Linux: max number of open files specified by one thread doesn't seem
655 * to apply to other threads on Linux.
656 */
657 newvalue = ISC_RESOURCE_UNLIMITED;
658
659 result = isc_resource_setlimit(isc_resource_openfiles, newvalue);
660 if (result != ISC_R_SUCCESS)
661 ns_main_earlywarning("couldn't adjust limit on open files");
662 #endif
663 }
664
665 void
666 ns_os_minprivs(void) {
667 #ifdef HAVE_SYS_PRCTL_H
668 linux_keepcaps();
669 #endif
670
671 #ifdef HAVE_LINUXTHREADS
672 ns_os_changeuser(); /* Call setuid() before threads are started */
673 #endif
674
675 #if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
676 linux_minprivs();
677 #endif
678 }
679
680 static int
681 safe_open(const char *filename, mode_t mode, bool append) {
682 int fd;
683 struct stat sb;
684
685 if (stat(filename, &sb) == -1) {
686 if (errno != ENOENT)
687 return (-1);
688 } else if ((sb.st_mode & S_IFREG) == 0) {
689 errno = EOPNOTSUPP;
690 return (-1);
691 }
692
693 if (append)
694 fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, mode);
695 else {
696 if (unlink(filename) < 0 && errno != ENOENT)
697 return (-1);
698 fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, mode);
699 }
700 return (fd);
701 }
702
703 static void
704 cleanup_pidfile(void) {
705 int n;
706 if (pidfile != NULL) {
707 n = unlink(pidfile);
708 if (n == -1 && errno != ENOENT)
709 ns_main_earlywarning("unlink '%s': failed", pidfile);
710 free(pidfile);
711 }
712 pidfile = NULL;
713 }
714
715 static void
716 cleanup_lockfile(void) {
717 if (singletonfd != -1) {
718 close(singletonfd);
719 singletonfd = -1;
720 }
721
722 if (lockfile != NULL) {
723 int n = unlink(lockfile);
724 if (n == -1 && errno != ENOENT)
725 ns_main_earlywarning("unlink '%s': failed", lockfile);
726 free(lockfile);
727 lockfile = NULL;
728 }
729 }
730
731 /*
732 * Ensure that a directory exists.
733 * NOTE: This function overwrites the '/' characters in 'filename' with
734 * nulls. The caller should copy the filename to a fresh buffer first.
735 */
736 static int
737 mkdirpath(char *filename, void (*report)(const char *, ...)) {
738 char *slash = strrchr(filename, '/');
739 char strbuf[ISC_STRERRORSIZE];
740 unsigned int mode;
741
742 if (slash != NULL && slash != filename) {
743 struct stat sb;
744 *slash = '\0';
745
746 if (stat(filename, &sb) == -1) {
747 if (errno != ENOENT) {
748 isc__strerror(errno, strbuf, sizeof(strbuf));
749 (*report)("couldn't stat '%s': %s", filename,
750 strbuf);
751 goto error;
752 }
753 if (mkdirpath(filename, report) == -1)
754 goto error;
755 /*
756 * Handle "//", "/./" and "/../" in path.
757 */
758 if (!strcmp(slash + 1, "") ||
759 !strcmp(slash + 1, ".") ||
760 !strcmp(slash + 1, "..")) {
761 *slash = '/';
762 return (0);
763 }
764 mode = S_IRUSR | S_IWUSR | S_IXUSR; /* u=rwx */
765 mode |= S_IRGRP | S_IXGRP; /* g=rx */
766 mode |= S_IROTH | S_IXOTH; /* o=rx */
767 if (mkdir(filename, mode) == -1) {
768 isc__strerror(errno, strbuf, sizeof(strbuf));
769 (*report)("couldn't mkdir '%s': %s", filename,
770 strbuf);
771 goto error;
772 }
773 if (runas_pw != NULL &&
774 chown(filename, runas_pw->pw_uid,
775 runas_pw->pw_gid) == -1) {
776 isc__strerror(errno, strbuf, sizeof(strbuf));
777 (*report)("couldn't chown '%s': %s", filename,
778 strbuf);
779 }
780 }
781 *slash = '/';
782 }
783 return (0);
784
785 error:
786 *slash = '/';
787 return (-1);
788 }
789
790 #ifndef HAVE_LINUXTHREADS
791 static void
792 setperms(uid_t uid, gid_t gid) {
793 #if defined(HAVE_SETEGID) || defined(HAVE_SETRESGID)
794 char strbuf[ISC_STRERRORSIZE];
795 #endif
796 #if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID)
797 gid_t oldgid, tmpg;
798 #endif
799 #if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID)
800 uid_t olduid, tmpu;
801 #endif
802 #if defined(HAVE_SETEGID)
803 if (getegid() != gid && setegid(gid) == -1) {
804 isc__strerror(errno, strbuf, sizeof(strbuf));
805 ns_main_earlywarning("unable to set effective gid to %ld: %s",
806 (long)gid, strbuf);
807 }
808 #elif defined(HAVE_SETRESGID)
809 if (getresgid(&tmpg, &oldgid, &tmpg) == -1 || oldgid != gid) {
810 if (setresgid(-1, gid, -1) == -1) {
811 isc__strerror(errno, strbuf, sizeof(strbuf));
812 ns_main_earlywarning("unable to set effective "
813 "gid to %d: %s", gid, strbuf);
814 }
815 }
816 #endif
817
818 #if defined(HAVE_SETEUID)
819 if (geteuid() != uid && seteuid(uid) == -1) {
820 isc__strerror(errno, strbuf, sizeof(strbuf));
821 ns_main_earlywarning("unable to set effective uid to %ld: %s",
822 (long)uid, strbuf);
823 }
824 #elif defined(HAVE_SETRESUID)
825 if (getresuid(&tmpu, &olduid, &tmpu) == -1 || olduid != uid) {
826 if (setresuid(-1, uid, -1) == -1) {
827 isc__strerror(errno, strbuf, sizeof(strbuf));
828 ns_main_earlywarning("unable to set effective "
829 "uid to %d: %s", uid, strbuf);
830 }
831 }
832 #endif
833 }
834 #endif /* !HAVE_LINUXTHREADS */
835
836 FILE *
837 ns_os_openfile(const char *filename, mode_t mode, bool switch_user) {
838 char strbuf[ISC_STRERRORSIZE], *f;
839 FILE *fp;
840 int fd;
841
842 /*
843 * Make the containing directory if it doesn't exist.
844 */
845 f = strdup(filename);
846 if (f == NULL) {
847 isc__strerror(errno, strbuf, sizeof(strbuf));
848 ns_main_earlywarning("couldn't strdup() '%s': %s",
849 filename, strbuf);
850 return (NULL);
851 }
852 if (mkdirpath(f, ns_main_earlywarning) == -1) {
853 free(f);
854 return (NULL);
855 }
856 free(f);
857
858 if (switch_user && runas_pw != NULL) {
859 uid_t olduid = getuid();
860 gid_t oldgid = getgid();
861 #ifdef HAVE_LINUXTHREADS
862 REQUIRE(olduid == runas_pw->pw_uid);
863 REQUIRE(oldgid == runas_pw->pw_gid);
864 #else
865 /* Set UID/GID to the one we'll be running with eventually */
866 setperms(runas_pw->pw_uid, runas_pw->pw_gid);
867 #endif
868 fd = safe_open(filename, mode, false);
869
870 #ifndef HAVE_LINUXTHREADS
871 /* Restore UID/GID to root */
872 setperms(olduid, oldgid);
873 #endif /* HAVE_LINUXTHREADS */
874
875 if (fd == -1) {
876 #ifndef HAVE_LINUXTHREADS
877 fd = safe_open(filename, mode, false);
878 if (fd != -1) {
879 ns_main_earlywarning("Required root "
880 "permissions to open "
881 "'%s'.", filename);
882 } else {
883 ns_main_earlywarning("Could not open "
884 "'%s'.", filename);
885 }
886 ns_main_earlywarning("Please check file and "
887 "directory permissions "
888 "or reconfigure the filename.");
889 #else /* HAVE_LINUXTHREADS */
890 ns_main_earlywarning("Could not open "
891 "'%s'.", filename);
892 ns_main_earlywarning("Please check file and "
893 "directory permissions "
894 "or reconfigure the filename.");
895 #endif /* HAVE_LINUXTHREADS */
896 }
897 } else {
898 fd = safe_open(filename, mode, false);
899 }
900
901 if (fd < 0) {
902 isc__strerror(errno, strbuf, sizeof(strbuf));
903 ns_main_earlywarning("could not open file '%s': %s",
904 filename, strbuf);
905 return (NULL);
906 }
907
908 fp = fdopen(fd, "w");
909 if (fp == NULL) {
910 isc__strerror(errno, strbuf, sizeof(strbuf));
911 ns_main_earlywarning("could not fdopen() file '%s': %s",
912 filename, strbuf);
913 }
914
915 return (fp);
916 }
917
918 void
919 ns_os_writepidfile(const char *filename, bool first_time) {
920 FILE *fh;
921 pid_t pid;
922 char strbuf[ISC_STRERRORSIZE];
923 void (*report)(const char *, ...);
924
925 /*
926 * The caller must ensure any required synchronization.
927 */
928
929 report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
930
931 cleanup_pidfile();
932
933 if (filename == NULL)
934 return;
935
936 pidfile = strdup(filename);
937 if (pidfile == NULL) {
938 isc__strerror(errno, strbuf, sizeof(strbuf));
939 (*report)("couldn't strdup() '%s': %s", filename, strbuf);
940 return;
941 }
942
943 fh = ns_os_openfile(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
944 first_time);
945 if (fh == NULL) {
946 cleanup_pidfile();
947 return;
948 }
949 #ifdef HAVE_LINUXTHREADS
950 pid = mainpid;
951 #else
952 pid = getpid();
953 #endif
954 if (fprintf(fh, "%ld\n", (long)pid) < 0) {
955 (*report)("fprintf() to pid file '%s' failed", filename);
956 (void)fclose(fh);
957 cleanup_pidfile();
958 return;
959 }
960 if (fflush(fh) == EOF) {
961 (*report)("fflush() to pid file '%s' failed", filename);
962 (void)fclose(fh);
963 cleanup_pidfile();
964 return;
965 }
966 (void)fclose(fh);
967 }
968
969 bool
970 ns_os_issingleton(const char *filename) {
971 char strbuf[ISC_STRERRORSIZE];
972 struct flock lock;
973
974 if (singletonfd != -1)
975 return (true);
976
977 if (strcasecmp(filename, "none") == 0)
978 return (true);
979
980 /*
981 * Make the containing directory if it doesn't exist.
982 */
983 lockfile = strdup(filename);
984 if (lockfile == NULL) {
985 isc__strerror(errno, strbuf, sizeof(strbuf));
986 ns_main_earlyfatal("couldn't allocate memory for '%s': %s",
987 filename, strbuf);
988 } else {
989 int ret = mkdirpath(lockfile, ns_main_earlywarning);
990 if (ret == -1) {
991 ns_main_earlywarning("couldn't create '%s'", filename);
992 cleanup_lockfile();
993 return (false);
994 }
995 }
996
997 /*
998 * ns_os_openfile() uses safeopen() which removes any existing
999 * files. We can't use that here.
1000 */
1001 singletonfd = open(filename, O_WRONLY | O_CREAT,
1002 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
1003 if (singletonfd == -1) {
1004 cleanup_lockfile();
1005 return (false);
1006 }
1007
1008 memset(&lock, 0, sizeof(lock));
1009 lock.l_type = F_WRLCK;
1010 lock.l_whence = SEEK_SET;
1011 lock.l_start = 0;
1012 lock.l_len = 1;
1013
1014 /* Non-blocking (does not wait for lock) */
1015 if (fcntl(singletonfd, F_SETLK, &lock) == -1) {
1016 close(singletonfd);
1017 singletonfd = -1;
1018 return (false);
1019 }
1020
1021 return (true);
1022 }
1023
1024 void
1025 ns_os_shutdown(void) {
1026 closelog();
1027 cleanup_pidfile();
1028 cleanup_lockfile();
1029 }
1030
1031 isc_result_t
1032 ns_os_gethostname(char *buf, size_t len) {
1033 int n;
1034
1035 n = gethostname(buf, len);
1036 return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
1037 }
1038
1039 static char *
1040 next_token(char **stringp, const char *delim) {
1041 char *res;
1042
1043 do {
1044 res = strsep(stringp, delim);
1045 if (res == NULL)
1046 break;
1047 } while (*res == '\0');
1048 return (res);
1049 }
1050
1051 void
1052 ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
1053 char *input, *ptr;
1054 unsigned int n;
1055 pid_t pid;
1056
1057 input = command;
1058
1059 /* Skip the command name. */
1060 ptr = next_token(&input, " \t");
1061 if (ptr == NULL)
1062 return;
1063
1064 ptr = next_token(&input, " \t");
1065 if (ptr == NULL)
1066 return;
1067
1068 if (strcmp(ptr, "-p") != 0)
1069 return;
1070
1071 #ifdef HAVE_LINUXTHREADS
1072 pid = mainpid;
1073 #else
1074 pid = getpid();
1075 #endif
1076
1077 n = snprintf((char *)isc_buffer_used(text),
1078 isc_buffer_availablelength(text),
1079 "pid: %ld", (long)pid);
1080 /* Only send a message if it is complete. */
1081 if (n > 0 && n < isc_buffer_availablelength(text))
1082 isc_buffer_add(text, n);
1083 }
1084
1085 void
1086 ns_os_tzset(void) {
1087 #ifdef HAVE_TZSET
1088 tzset();
1089 #endif
1090 }
1091
1092 #ifdef HAVE_UNAME
1093 static char unamebuf[sizeof(struct utsname)];
1094 #else
1095 static const char unamebuf[] = { "unknown architecture" };
1096 #endif
1097 static const char *unamep = NULL;
1098
1099 static void
1100 getuname(void) {
1101 #ifdef HAVE_UNAME
1102 struct utsname uts;
1103
1104 memset(&uts, 0, sizeof(uts));
1105 if (uname(&uts) < 0) {
1106 snprintf(unamebuf, sizeof(unamebuf), "unknown architecture");
1107 return;
1108 }
1109
1110 snprintf(unamebuf, sizeof(unamebuf), "%s %s %s %s", uts.sysname,
1111 uts.machine, uts.release, uts.version);
1112 #endif /* ifdef HAVE_UNAME */
1113 unamep = unamebuf;
1114 }
1115
1116 const char *
1117 ns_os_uname(void) {
1118 if (unamep == NULL) {
1119 getuname();
1120 }
1121 return (unamep);
1122 }
1123