os.c revision 186462
1/*
2 * Copyright (C) 2004-2006, 2008  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.66.18.17 2008/10/24 01:43:17 tbox 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_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#if defined(HAVE_CAPSET)
121#undef _POSIX_SOURCE
122#ifdef HAVE_SYS_CAPABILITY_H
123#include <sys/capability.h>
124#else
125#include <linux/capability.h>
126int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
127#endif
128#include <sys/prctl.h>
129#else
130/*%
131 * We define _LINUX_FS_H to prevent it from being included.  We don't need
132 * anything from it, and the files it includes cause warnings with 2.2
133 * kernels, and compilation failures (due to conflicts between <linux/string.h>
134 * and <string.h>) on 2.3 kernels.
135 */
136#define _LINUX_FS_H
137
138#include <sys/syscall.h>	/* Required for syscall(). */
139#include <linux/capability.h>	/* Required for _LINUX_CAPABILITY_VERSION. */
140
141#ifdef HAVE_SYS_PRCTL_H
142#include <sys/prctl.h>		/* Required for prctl(). */
143
144/*
145 * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it
146 * here.  This allows setuid() to work on systems running a new enough
147 * kernel but with /usr/include/linux pointing to "standard" kernel
148 * headers.
149 */
150#ifndef PR_SET_KEEPCAPS
151#define PR_SET_KEEPCAPS 8
152#endif
153
154#endif /* HAVE_SYS_PRCTL_H */
155
156#ifndef SYS_capset
157#ifndef __NR_capset
158#include <asm/unistd.h> /* Slackware 4.0 needs this. */
159#endif
160#define SYS_capset __NR_capset
161#endif
162#endif
163
164static void
165linux_setcaps(unsigned int caps) {
166	struct __user_cap_header_struct caphead;
167	struct __user_cap_data_struct cap;
168	char strbuf[ISC_STRERRORSIZE];
169
170	if ((getuid() != 0 && !non_root_caps) || non_root)
171		return;
172
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#ifdef HAVE_CAPSET
181	if (capset(&caphead, &cap) < 0 ) {
182		isc__strerror(errno, strbuf, sizeof(strbuf));
183		ns_main_earlyfatal("capset failed: %s:"
184				   " please ensure that the capset kernel"
185				   " module is loaded.  see insmod(8)",
186				   strbuf);
187	}
188#else
189	if (syscall(SYS_capset, &caphead, &cap) < 0) {
190		isc__strerror(errno, strbuf, sizeof(strbuf));
191		ns_main_earlyfatal("syscall(capset) failed: %s:"
192				   " please ensure that the capset kernel"
193				   " module is loaded.  see insmod(8)",
194				   strbuf);
195	}
196#endif
197}
198
199static void
200linux_initialprivs(void) {
201	unsigned int caps;
202
203	/*%
204	 * We don't need most privileges, so we drop them right away.
205	 * Later on linux_minprivs() will be called, which will drop our
206	 * capabilities to the minimum needed to run the server.
207	 */
208
209	caps = 0;
210
211	/*
212	 * We need to be able to bind() to privileged ports, notably port 53!
213	 */
214	caps |= (1 << CAP_NET_BIND_SERVICE);
215
216	/*
217	 * We need chroot() initially too.
218	 */
219	caps |= (1 << CAP_SYS_CHROOT);
220
221#if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
222	/*
223	 * We can setuid() only if either the kernel supports keeping
224	 * capabilities after setuid() (which we don't know until we've
225	 * tried) or we're not using threads.  If either of these is
226	 * true, we want the setuid capability.
227	 */
228	caps |= (1 << CAP_SETUID);
229#endif
230
231	/*
232	 * Since we call initgroups, we need this.
233	 */
234	caps |= (1 << CAP_SETGID);
235
236	/*
237	 * Without this, we run into problems reading a configuration file
238	 * owned by a non-root user and non-world-readable on startup.
239	 */
240	caps |= (1 << CAP_DAC_READ_SEARCH);
241
242	/*
243	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
244	 *      clear it would work right given the way linuxthreads work.
245	 * XXXDCL But since we need to be able to set the maximum number
246	 * of files, the stack size, data size, and core dump size to
247	 * support named.conf options, this is now being added to test.
248	 */
249	caps |= (1 << CAP_SYS_RESOURCE);
250
251	linux_setcaps(caps);
252}
253
254static void
255linux_minprivs(void) {
256	unsigned int caps;
257
258	/*%
259	 * Drop all privileges except the ability to bind() to privileged
260	 * ports.
261	 *
262	 * It's important that we drop CAP_SYS_CHROOT.  If we didn't, it
263	 * chroot() could be used to escape from the chrooted area.
264	 */
265
266	caps = 0;
267	caps |= (1 << CAP_NET_BIND_SERVICE);
268
269	/*
270	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
271	 *      clear it would work right given the way linuxthreads work.
272	 * XXXDCL But since we need to be able to set the maximum number
273	 * of files, the stack size, data size, and core dump size to
274	 * support named.conf options, this is now being added to test.
275	 */
276	caps |= (1 << CAP_SYS_RESOURCE);
277
278	linux_setcaps(caps);
279}
280
281#ifdef HAVE_SYS_PRCTL_H
282static void
283linux_keepcaps(void) {
284	char strbuf[ISC_STRERRORSIZE];
285	/*%
286	 * Ask the kernel to allow us to keep our capabilities after we
287	 * setuid().
288	 */
289
290	if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
291		if (errno != EINVAL) {
292			isc__strerror(errno, strbuf, sizeof(strbuf));
293			ns_main_earlyfatal("prctl() failed: %s", strbuf);
294		}
295	} else {
296		non_root_caps = ISC_TRUE;
297		if (getuid() != 0)
298			non_root = ISC_TRUE;
299	}
300}
301#endif
302
303#endif	/* HAVE_LINUX_CAPABILITY_H */
304
305
306static void
307setup_syslog(const char *progname) {
308	int options;
309
310	options = LOG_PID;
311#ifdef LOG_NDELAY
312	options |= LOG_NDELAY;
313#endif
314	openlog(isc_file_basename(progname), options, ISC_FACILITY);
315}
316
317void
318ns_os_init(const char *progname) {
319	setup_syslog(progname);
320#ifdef HAVE_LINUX_CAPABILITY_H
321	linux_initialprivs();
322#endif
323#ifdef HAVE_LINUXTHREADS
324	mainpid = getpid();
325#endif
326#ifdef SIGXFSZ
327	signal(SIGXFSZ, SIG_IGN);
328#endif
329}
330
331void
332ns_os_daemonize(void) {
333	pid_t pid;
334	char strbuf[ISC_STRERRORSIZE];
335
336	if (pipe(dfd) == -1) {
337		isc__strerror(errno, strbuf, sizeof(strbuf));
338		ns_main_earlyfatal("pipe(): %s", strbuf);
339	}
340
341	pid = fork();
342	if (pid == -1) {
343		isc__strerror(errno, strbuf, sizeof(strbuf));
344		ns_main_earlyfatal("fork(): %s", strbuf);
345	}
346	if (pid != 0) {
347		int n;
348		/*
349		 * Wait for the child to finish loading for the first time.
350		 * This would be so much simpler if fork() worked once we
351		 * were multi-threaded.
352		 */
353		(void)close(dfd[1]);
354		do {
355			char buf;
356			n = read(dfd[0], &buf, 1);
357			if (n == 1)
358				_exit(0);
359		} while (n == -1 && errno == EINTR);
360		_exit(1);
361	}
362	(void)close(dfd[0]);
363
364	/*
365	 * We're the child.
366	 */
367
368#ifdef HAVE_LINUXTHREADS
369	mainpid = getpid();
370#endif
371
372	if (setsid() == -1) {
373		isc__strerror(errno, strbuf, sizeof(strbuf));
374		ns_main_earlyfatal("setsid(): %s", strbuf);
375	}
376
377	/*
378	 * Try to set stdin, stdout, and stderr to /dev/null, but press
379	 * on even if it fails.
380	 *
381	 * XXXMLG The close() calls here are unneeded on all but NetBSD, but
382	 * are harmless to include everywhere.  dup2() is supposed to close
383	 * the FD if it is in use, but unproven-pthreads-0.16 is broken
384	 * and will end up closing the wrong FD.  This will be fixed eventually,
385	 * and these calls will be removed.
386	 */
387	if (devnullfd != -1) {
388		if (devnullfd != STDIN_FILENO) {
389			(void)close(STDIN_FILENO);
390			(void)dup2(devnullfd, STDIN_FILENO);
391		}
392		if (devnullfd != STDOUT_FILENO) {
393			(void)close(STDOUT_FILENO);
394			(void)dup2(devnullfd, STDOUT_FILENO);
395		}
396		if (devnullfd != STDERR_FILENO) {
397			(void)close(STDERR_FILENO);
398			(void)dup2(devnullfd, STDERR_FILENO);
399		}
400	}
401}
402
403void
404ns_os_started(void) {
405	char buf = 0;
406
407	/*
408	 * Signal to the parent that we stated successfully.
409	 */
410	if (dfd[0] != -1 && dfd[1] != -1) {
411		write(dfd[1], &buf, 1);
412		close(dfd[1]);
413		dfd[0] = dfd[1] = -1;
414	}
415}
416
417void
418ns_os_opendevnull(void) {
419	devnullfd = open("/dev/null", O_RDWR, 0);
420}
421
422void
423ns_os_closedevnull(void) {
424	if (devnullfd != STDIN_FILENO &&
425	    devnullfd != STDOUT_FILENO &&
426	    devnullfd != STDERR_FILENO) {
427		close(devnullfd);
428		devnullfd = -1;
429	}
430}
431
432static isc_boolean_t
433all_digits(const char *s) {
434	if (*s == '\0')
435		return (ISC_FALSE);
436	while (*s != '\0') {
437		if (!isdigit((*s)&0xff))
438			return (ISC_FALSE);
439		s++;
440	}
441	return (ISC_TRUE);
442}
443
444void
445ns_os_chroot(const char *root) {
446	char strbuf[ISC_STRERRORSIZE];
447#ifdef HAVE_LIBSCF
448	ns_smf_chroot = 0;
449#endif
450	if (root != NULL) {
451		if (chroot(root) < 0) {
452			isc__strerror(errno, strbuf, sizeof(strbuf));
453			ns_main_earlyfatal("chroot(): %s", strbuf);
454		}
455		if (chdir("/") < 0) {
456			isc__strerror(errno, strbuf, sizeof(strbuf));
457			ns_main_earlyfatal("chdir(/): %s", strbuf);
458		}
459#ifdef HAVE_LIBSCF
460		/* Set ns_smf_chroot flag on successful chroot. */
461		ns_smf_chroot = 1;
462#endif
463	}
464}
465
466void
467ns_os_inituserinfo(const char *username) {
468	char strbuf[ISC_STRERRORSIZE];
469	if (username == NULL)
470		return;
471
472	if (all_digits(username))
473		runas_pw = getpwuid((uid_t)atoi(username));
474	else
475		runas_pw = getpwnam(username);
476	endpwent();
477
478	if (runas_pw == NULL)
479		ns_main_earlyfatal("user '%s' unknown", username);
480
481	if (getuid() == 0) {
482		if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
483			isc__strerror(errno, strbuf, sizeof(strbuf));
484			ns_main_earlyfatal("initgroups(): %s", strbuf);
485		}
486	}
487
488}
489
490void
491ns_os_changeuser(void) {
492	char strbuf[ISC_STRERRORSIZE];
493	if (runas_pw == NULL || done_setuid)
494		return;
495
496	done_setuid = ISC_TRUE;
497
498#ifdef HAVE_LINUXTHREADS
499#ifdef HAVE_LINUX_CAPABILITY_H
500	if (!non_root_caps)
501		ns_main_earlyfatal("-u with Linux threads not supported: "
502				   "requires kernel support for "
503				   "prctl(PR_SET_KEEPCAPS)");
504#else
505	ns_main_earlyfatal("-u with Linux threads not supported: "
506			   "no capabilities support or capabilities "
507			   "disabled at build time");
508#endif
509#endif
510
511	if (setgid(runas_pw->pw_gid) < 0) {
512		isc__strerror(errno, strbuf, sizeof(strbuf));
513		ns_main_earlyfatal("setgid(): %s", strbuf);
514	}
515
516	if (setuid(runas_pw->pw_uid) < 0) {
517		isc__strerror(errno, strbuf, sizeof(strbuf));
518		ns_main_earlyfatal("setuid(): %s", strbuf);
519	}
520
521#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
522	/*
523	 * Restore the ability of named to drop core after the setuid()
524	 * call has disabled it.
525	 */
526	if (prctl(PR_SET_DUMPABLE,1,0,0,0) < 0) {
527		isc__strerror(errno, strbuf, sizeof(strbuf));
528		ns_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s",
529				     strbuf);
530	}
531#endif
532#if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
533	linux_minprivs();
534#endif
535}
536
537void
538ns_os_adjustnofile() {
539#ifdef HAVE_LINUXTHREADS
540	isc_result_t result;
541	isc_resourcevalue_t newvalue;
542
543	/*
544	 * Linux: max number of open files specified by one thread doesn't seem
545	 * to apply to other threads on Linux.
546	 */
547	newvalue = ISC_RESOURCE_UNLIMITED;
548
549	result = isc_resource_setlimit(isc_resource_openfiles, newvalue);
550	if (result != ISC_R_SUCCESS)
551		ns_main_earlywarning("couldn't adjust limit on open files");
552#endif
553}
554
555void
556ns_os_minprivs(void) {
557#ifdef HAVE_SYS_PRCTL_H
558	linux_keepcaps();
559#endif
560
561#ifdef HAVE_LINUXTHREADS
562	ns_os_changeuser(); /* Call setuid() before threads are started */
563#endif
564
565#if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
566	linux_minprivs();
567#endif
568}
569
570static int
571safe_open(const char *filename, isc_boolean_t append) {
572	int fd;
573	struct stat sb;
574
575	if (stat(filename, &sb) == -1) {
576		if (errno != ENOENT)
577			return (-1);
578	} else if ((sb.st_mode & S_IFREG) == 0) {
579		errno = EOPNOTSUPP;
580		return (-1);
581	}
582
583	if (append)
584		fd = open(filename, O_WRONLY|O_CREAT|O_APPEND,
585			  S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
586	else {
587		(void)unlink(filename);
588		fd = open(filename, O_WRONLY|O_CREAT|O_EXCL,
589			  S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
590	}
591	return (fd);
592}
593
594static void
595cleanup_pidfile(void) {
596	if (pidfile != NULL) {
597		(void)unlink(pidfile);
598		free(pidfile);
599	}
600	pidfile = NULL;
601}
602
603void
604ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
605	int fd;
606	FILE *lockfile;
607	size_t len;
608	pid_t pid;
609	char strbuf[ISC_STRERRORSIZE];
610	void (*report)(const char *, ...);
611
612	/*
613	 * The caller must ensure any required synchronization.
614	 */
615
616	report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
617
618	cleanup_pidfile();
619
620	if (filename == NULL)
621		return;
622
623	len = strlen(filename);
624	pidfile = malloc(len + 1);
625	if (pidfile == NULL) {
626		isc__strerror(errno, strbuf, sizeof(strbuf));
627		(*report)("couldn't malloc '%s': %s", filename, strbuf);
628		return;
629	}
630	/* This is safe. */
631	strcpy(pidfile, filename);
632
633	fd = safe_open(filename, ISC_FALSE);
634	if (fd < 0) {
635		isc__strerror(errno, strbuf, sizeof(strbuf));
636		(*report)("couldn't open pid file '%s': %s", filename, strbuf);
637		free(pidfile);
638		pidfile = NULL;
639		return;
640	}
641	lockfile = fdopen(fd, "w");
642	if (lockfile == NULL) {
643		isc__strerror(errno, strbuf, sizeof(strbuf));
644		(*report)("could not fdopen() pid file '%s': %s",
645			  filename, strbuf);
646		(void)close(fd);
647		cleanup_pidfile();
648		return;
649	}
650#ifdef HAVE_LINUXTHREADS
651	pid = mainpid;
652#else
653	pid = getpid();
654#endif
655	if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
656		(*report)("fprintf() to pid file '%s' failed", filename);
657		(void)fclose(lockfile);
658		cleanup_pidfile();
659		return;
660	}
661	if (fflush(lockfile) == EOF) {
662		(*report)("fflush() to pid file '%s' failed", filename);
663		(void)fclose(lockfile);
664		cleanup_pidfile();
665		return;
666	}
667	(void)fclose(lockfile);
668}
669
670void
671ns_os_shutdown(void) {
672	closelog();
673	cleanup_pidfile();
674}
675
676isc_result_t
677ns_os_gethostname(char *buf, size_t len) {
678	int n;
679
680	n = gethostname(buf, len);
681	return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
682}
683
684static char *
685next_token(char **stringp, const char *delim) {
686	char *res;
687
688	do {
689		res = strsep(stringp, delim);
690		if (res == NULL)
691			break;
692	} while (*res == '\0');
693	return (res);
694}
695
696void
697ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
698	char *input, *ptr;
699	unsigned int n;
700	pid_t pid;
701
702	input = command;
703
704	/* Skip the command name. */
705	ptr = next_token(&input, " \t");
706	if (ptr == NULL)
707		return;
708
709	ptr = next_token(&input, " \t");
710	if (ptr == NULL)
711		return;
712
713	if (strcmp(ptr, "-p") != 0)
714		return;
715
716#ifdef HAVE_LINUXTHREADS
717	pid = mainpid;
718#else
719	pid = getpid();
720#endif
721
722	n = snprintf((char *)isc_buffer_used(text),
723		     isc_buffer_availablelength(text),
724		     "pid: %ld", (long)pid);
725	/* Only send a message if it is complete. */
726	if (n < isc_buffer_availablelength(text))
727		isc_buffer_add(text, n);
728}
729
730void
731ns_os_tzset(void) {
732#ifdef HAVE_TZSET
733	tzset();
734#endif
735}
736