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