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