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
56static char *pidfile = NULL;
57static 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
108static pid_t mainpid = 0;
109#endif
110
111static struct passwd *runas_pw = NULL;
112static isc_boolean_t done_setuid = ISC_FALSE;
113static int dfd[2] = { -1, -1 };
114
115#ifdef HAVE_LINUX_CAPABILITY_H
116
117static isc_boolean_t non_root = ISC_FALSE;
118static 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
161typedef unsigned int cap_t;
162#define SETCAPS_FUNC "syscall(capset) "
163#endif /* HAVE_LIBCAP */
164
165static void
166linux_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
240static void
241linux_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
310static void
311linux_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
348static void
349linux_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
372static void
373setup_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
383void
384ns_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
397void
398ns_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
469void
470ns_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
485void
486ns_os_opendevnull(void) {
487	devnullfd = open("/dev/null", O_RDWR, 0);
488}
489
490void
491ns_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
500static isc_boolean_t
501all_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
512void
513ns_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
538void
539ns_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
562void
563ns_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
609void
610ns_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
627void
628ns_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
642static int
643safe_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
665static void
666cleanup_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
677static int
678mkdirpath(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
731static void
732setperms(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
773FILE *
774ns_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
852void
853ns_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
903void
904ns_os_shutdown(void) {
905	closelog();
906	cleanup_pidfile();
907}
908
909isc_result_t
910ns_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
917static char *
918next_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
929void
930ns_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
963void
964ns_os_tzset(void) {
965#ifdef HAVE_TZSET
966	tzset();
967#endif
968}
969