os.c 16.6 KB
Newer Older
Bob Halley's avatar
add  
Bob Halley committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2008  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1999-2002  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
Bob Halley's avatar
add  
Bob Halley committed
6 7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
Bob Halley's avatar
add  
Bob Halley committed
16 17
 */

18
/* $Id: os.c,v 1.84 2008/05/06 01:30:26 each Exp $ */
19 20

/*! \file */
David Lawrence's avatar
David Lawrence committed
21

Bob Halley's avatar
add  
Bob Halley committed
22
#include <config.h>
23
#include <stdarg.h>
Bob Halley's avatar
add  
Bob Halley committed
24

Mark Andrews's avatar
Mark Andrews committed
25
#include <sys/types.h>	/* dev_t FreeBSD 2.1 */
Bob Halley's avatar
Bob Halley committed
26
#include <sys/stat.h>
Bob Halley's avatar
add  
Bob Halley committed
27

28
#include <ctype.h>
Bob Halley's avatar
add  
Bob Halley committed
29
#include <errno.h>
Bob Halley's avatar
Bob Halley committed
30
#include <fcntl.h>
31
#include <grp.h>		/* Required for initgroups() on IRIX. */
32
#include <pwd.h>
33 34
#include <stdio.h>
#include <stdlib.h>
35
#include <signal.h>
36
#include <syslog.h>
Mark Andrews's avatar
Mark Andrews committed
37 38 39
#ifdef HAVE_TZSET
#include <time.h>
#endif
40
#include <unistd.h>
Bob Halley's avatar
add  
Bob Halley committed
41

42
#include <isc/buffer.h>
43
#include <isc/file.h>
44
#include <isc/print.h>
45
#include <isc/result.h>
46
#include <isc/strerror.h>
47
#include <isc/string.h>
Bob Halley's avatar
add  
Bob Halley committed
48 49 50

#include <named/main.h>
#include <named/os.h>
51 52 53
#ifdef HAVE_LIBSCF
#include <named/ns_smf_globals.h>
#endif
Bob Halley's avatar
add  
Bob Halley committed
54

Bob Halley's avatar
Bob Halley committed
55
static char *pidfile = NULL;
56
static int devnullfd = -1;
57

58 59 60 61
#ifndef ISC_FACILITY
#define ISC_FACILITY LOG_DAEMON
#endif

62
/*
63
 * If there's no <linux/capability.h>, we don't care about <sys/prctl.h>
64 65
 */
#ifndef HAVE_LINUX_CAPABILITY_H
66
#undef HAVE_SYS_PRCTL_H
67 68 69 70
#endif

/*
 * Linux defines:
Mark Andrews's avatar
Mark Andrews committed
71
 *	(T) HAVE_LINUXTHREADS
72
 *	(C) HAVE_SYS_CAPABILITY_H (or HAVE_LINUX_CAPABILITY_H)
Mark Andrews's avatar
Mark Andrews committed
73
 *	(P) HAVE_SYS_PRCTL_H
74
 * The possible cases are:
Mark Andrews's avatar
Mark Andrews committed
75 76 77 78 79 80
 *	none:	setuid() normally
 *	T:	no setuid()
 *	C:	setuid() normally, drop caps (keep CAP_SETUID)
 *	T+C:	no setuid(), drop caps (don't keep CAP_SETUID)
 *	T+C+P:	setuid() early, drop caps (keep CAP_SETUID)
 *	C+P:	setuid() normally, drop caps (keep CAP_SETUID)
81 82 83 84
 *	P:	not possible
 *	T+P:	not possible
 *
 * if (C)
Mark Andrews's avatar
Mark Andrews committed
85 86 87 88 89
 *	caps = BIND_SERVICE + CHROOT + SETGID
 *	if ((T && C && P) || !T)
 *		caps += SETUID
 *	endif
 *	capset(caps)
90 91
 * endif
 * if (T && C && P && -u)
Mark Andrews's avatar
Mark Andrews committed
92
 *	setuid()
93
 * else if (T && -u)
Mark Andrews's avatar
Mark Andrews committed
94
 *	fail
95 96
 * --> start threads
 * if (!T && -u)
Mark Andrews's avatar
Mark Andrews committed
97
 *	setuid()
98
 * if (C && (P || !-u))
Mark Andrews's avatar
Mark Andrews committed
99 100
 *	caps = BIND_SERVICE
 *	capset(caps)
101 102 103 104 105
 * endif
 *
 * It will be nice when Linux threads work properly with setuid().
 */

Bob Halley's avatar
Bob Halley committed
106 107 108
#ifdef HAVE_LINUXTHREADS
static pid_t mainpid = 0;
#endif
Bob Halley's avatar
add  
Bob Halley committed
109

110
static struct passwd *runas_pw = NULL;
111
static isc_boolean_t done_setuid = ISC_FALSE;
112
static int dfd[2] = { -1, -1 };
113

Bob Halley's avatar
add  
Bob Halley committed
114
#ifdef HAVE_LINUX_CAPABILITY_H
115

116 117 118
static isc_boolean_t non_root = ISC_FALSE;
static isc_boolean_t non_root_caps = ISC_FALSE;

119 120 121
#ifdef HAVE_SYS_CAPABILITY_H
#include <sys/capability.h>
#else
122
/*%
123 124 125 126 127 128
 * We define _LINUX_FS_H to prevent it from being included.  We don't need
 * anything from it, and the files it includes cause warnings with 2.2
 * kernels, and compilation failures (due to conflicts between <linux/string.h>
 * and <string.h>) on 2.3 kernels.
 */
#define _LINUX_FS_H
129 130 131 132 133 134 135 136 137
#include <linux/capability.h>
#include <syscall.h>
#ifndef SYS_capset
#ifndef __NR_capset
#include <asm/unistd.h> /* Slackware 4.0 needs this. */
#endif /* __NR_capset */
#define SYS_capset __NR_capset
#endif /* SYS_capset */
#endif /* HAVE_SYS_CAPABILITY_H */
Bob Halley's avatar
add  
Bob Halley committed
138

139
#ifdef HAVE_SYS_PRCTL_H
140
#include <sys/prctl.h>		/* Required for prctl(). */
141 142

/*
143
 * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it
144 145 146 147
 * here.  This allows setuid() to work on systems running a new enough
 * kernel but with /usr/include/linux pointing to "standard" kernel
 * headers.
 */
148 149
#ifndef PR_SET_KEEPCAPS
#define PR_SET_KEEPCAPS 8
Bob Halley's avatar
Bob Halley committed
150
#endif
151

152
#endif /* HAVE_SYS_PRCTL_H */
Bob Halley's avatar
Bob Halley committed
153

154 155 156 157 158 159
#ifdef HAVE_LIBCAP
#define SETCAPS_FUNC "cap_set_proc "
#else
typedef unsigned int cap_t;
#define SETCAPS_FUNC "syscall(capset) "
#endif /* HAVE_LIBCAP */
Bob Halley's avatar
add  
Bob Halley committed
160 161

static void
162 163
linux_setcaps(cap_t caps) {
#ifndef HAVE_LIBCAP
Bob Halley's avatar
add  
Bob Halley committed
164 165
	struct __user_cap_header_struct caphead;
	struct __user_cap_data_struct cap;
166
#endif
167
	char strbuf[ISC_STRERRORSIZE];
Bob Halley's avatar
add  
Bob Halley committed
168

169
	if ((getuid() != 0 && !non_root_caps) || non_root)
Bob Halley's avatar
add  
Bob Halley committed
170
		return;
171
#ifndef HAVE_LIBCAP
Andreas Gustafsson's avatar
Andreas Gustafsson committed
172
	memset(&caphead, 0, sizeof(caphead));
Bob Halley's avatar
add  
Bob Halley committed
173 174
	caphead.version = _LINUX_CAPABILITY_VERSION;
	caphead.pid = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
175
	memset(&cap, 0, sizeof(cap));
Bob Halley's avatar
add  
Bob Halley committed
176 177
	cap.effective = caps;
	cap.permitted = caps;
178
	cap.inheritable = 0;
179 180 181
#endif
#ifdef HAVE_LIBCAP
	if (cap_set_proc(caps) < 0) {
182 183
#else
	if (syscall(SYS_capset, &caphead, &cap) < 0) {
184
#endif
185
		isc__strerror(errno, strbuf, sizeof(strbuf));
186
		ns_main_earlyfatal(SETCAPS_FUNC "failed: %s:"
187 188 189 190
				   " please ensure that the capset kernel"
				   " module is loaded.  see insmod(8)",
				   strbuf);
	}
Bob Halley's avatar
add  
Bob Halley committed
191
}
192

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
#ifdef HAVE_LIBCAP
#define SET_CAP(flag) \
	do { \
		capval = (flag); \
		err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET); \
		if (err == -1) { \
			isc__strerror(errno, strbuf, sizeof(strbuf)); \
			ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
		} \
		\
		err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET); \
		if (err == -1) { \
			isc__strerror(errno, strbuf, sizeof(strbuf)); \
			ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
		} \
	} while (0)
#define INIT_CAP \
	do { \
		caps = cap_init(); \
		if (caps == NULL) { \
			isc__strerror(errno, strbuf, sizeof(strbuf)); \
			ns_main_earlyfatal("cap_init failed: %s", strbuf); \
		} \
	} while (0)
#else
#define SET_CAP(flag) { caps |= (1 << (flag)); }
#define INIT_CAP { caps = 0; }
#endif /* HAVE_LIBCAP */

222 223
static void
linux_initialprivs(void) {
224 225 226 227 228 229
	cap_t caps;
#ifdef HAVE_LIBCAP
	cap_value_t capval;
	char strbuf[ISC_STRERRORSIZE];
	int err;
#endif
230

231
	/*%
Bob Halley's avatar
Bob Halley committed
232 233 234
	 * We don't need most privileges, so we drop them right away.
	 * Later on linux_minprivs() will be called, which will drop our
	 * capabilities to the minimum needed to run the server.
235
	 */
236
	INIT_CAP;
Bob Halley's avatar
Bob Halley committed
237 238 239 240

	/*
	 * We need to be able to bind() to privileged ports, notably port 53!
	 */
241
	SET_CAP(CAP_NET_BIND_SERVICE);
Bob Halley's avatar
Bob Halley committed
242 243 244 245

	/*
	 * We need chroot() initially too.
	 */
246
	SET_CAP(CAP_SYS_CHROOT);
Bob Halley's avatar
Bob Halley committed
247

248
#if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
Bob Halley's avatar
Bob Halley committed
249
	/*
250 251 252 253
	 * We can setuid() only if either the kernel supports keeping
	 * capabilities after setuid() (which we don't know until we've
	 * tried) or we're not using threads.  If either of these is
	 * true, we want the setuid capability.
Bob Halley's avatar
Bob Halley committed
254
	 */
255
	SET_CAP(CAP_SETUID);
Bob Halley's avatar
Bob Halley committed
256 257
#endif

258 259 260
	/*
	 * Since we call initgroups, we need this.
	 */
261
	SET_CAP(CAP_SETGID);
262

263 264 265 266
	/*
	 * Without this, we run into problems reading a configuration file
	 * owned by a non-root user and non-world-readable on startup.
	 */
267
	SET_CAP(CAP_DAC_READ_SEARCH);
268

Bob Halley's avatar
Bob Halley committed
269 270 271
	/*
	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
	 *      clear it would work right given the way linuxthreads work.
272 273 274
	 * XXXDCL But since we need to be able to set the maximum number
	 * of files, the stack size, data size, and core dump size to
	 * support named.conf options, this is now being added to test.
Bob Halley's avatar
Bob Halley committed
275
	 */
276
	SET_CAP(CAP_SYS_RESOURCE);
Bob Halley's avatar
Bob Halley committed
277

278 279 280 281 282
	linux_setcaps(caps);
}

static void
linux_minprivs(void) {
283 284 285 286 287 288
	cap_t caps;
#ifdef HAVE_LIBCAP
	cap_value_t capval;
	char strbuf[ISC_STRERRORSIZE];
	int err;
#endif
289

290
	INIT_CAP;
291
	/*%
Bob Halley's avatar
Bob Halley committed
292
	 * Drop all privileges except the ability to bind() to privileged
Bob Halley's avatar
Bob Halley committed
293
	 * ports.
Bob Halley's avatar
Bob Halley committed
294 295 296
	 *
	 * It's important that we drop CAP_SYS_CHROOT.  If we didn't, it
	 * chroot() could be used to escape from the chrooted area.
297 298
	 */

299
	SET_CAP(CAP_NET_BIND_SERVICE);
300

Mark Andrews's avatar
Mark Andrews committed
301 302 303 304 305 306 307
	/*
	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
	 *      clear it would work right given the way linuxthreads work.
	 * XXXDCL But since we need to be able to set the maximum number
	 * of files, the stack size, data size, and core dump size to
	 * support named.conf options, this is now being added to test.
	 */
308
	SET_CAP(CAP_SYS_RESOURCE);
Mark Andrews's avatar
Mark Andrews committed
309

310 311 312
	linux_setcaps(caps);
}

313
#ifdef HAVE_SYS_PRCTL_H
Bob Halley's avatar
Bob Halley committed
314 315
static void
linux_keepcaps(void) {
316
	char strbuf[ISC_STRERRORSIZE];
317
	/*%
Bob Halley's avatar
Bob Halley committed
318 319 320 321 322
	 * Ask the kernel to allow us to keep our capabilities after we
	 * setuid().
	 */

	if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
323 324 325 326
		if (errno != EINVAL) {
			isc__strerror(errno, strbuf, sizeof(strbuf));
			ns_main_earlyfatal("prctl() failed: %s", strbuf);
		}
327
	} else {
Bob Halley's avatar
Bob Halley committed
328
		non_root_caps = ISC_TRUE;
329 330 331
		if (getuid() != 0)
			non_root = ISC_TRUE;
	}
Bob Halley's avatar
Bob Halley committed
332 333 334
}
#endif

335 336
#endif	/* HAVE_LINUX_CAPABILITY_H */

Bob Halley's avatar
add  
Bob Halley committed
337

Bob Halley's avatar
Bob Halley committed
338
static void
339
setup_syslog(const char *progname) {
Bob Halley's avatar
Bob Halley committed
340 341 342 343 344 345
	int options;

	options = LOG_PID;
#ifdef LOG_NDELAY
	options |= LOG_NDELAY;
#endif
346
	openlog(isc_file_basename(progname), options, ISC_FACILITY);
Bob Halley's avatar
Bob Halley committed
347
}
Bob Halley's avatar
add  
Bob Halley committed
348

349
void
350 351
ns_os_init(const char *progname) {
	setup_syslog(progname);
Bob Halley's avatar
add  
Bob Halley committed
352
#ifdef HAVE_LINUX_CAPABILITY_H
353
	linux_initialprivs();
Bob Halley's avatar
add  
Bob Halley committed
354
#endif
Bob Halley's avatar
Bob Halley committed
355 356 357
#ifdef HAVE_LINUXTHREADS
	mainpid = getpid();
#endif
358 359 360
#ifdef SIGXFSZ
	signal(SIGXFSZ, SIG_IGN);
#endif
Bob Halley's avatar
add  
Bob Halley committed
361 362
}

363
void
Bob Halley's avatar
Bob Halley committed
364 365
ns_os_daemonize(void) {
	pid_t pid;
366
	char strbuf[ISC_STRERRORSIZE];
Bob Halley's avatar
Bob Halley committed
367

368 369 370 371 372
	if (pipe(dfd) == -1) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("pipe(): %s", strbuf);
	}

Bob Halley's avatar
Bob Halley committed
373
	pid = fork();
374 375 376 377
	if (pid == -1) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("fork(): %s", strbuf);
	}
378 379 380 381 382
	if (pid != 0) {
		int n;
		/*
		 * Wait for the child to finish loading for the first time.
		 * This would be so much simpler if fork() worked once we
Automatic Updater's avatar
Automatic Updater committed
383
		 * were multi-threaded.
384 385 386 387 388 389 390 391 392 393 394
		 */
		(void)close(dfd[1]);
		do {
			char buf;
			n = read(dfd[0], &buf, 1);
			if (n == 1)
				_exit(0);
		} while (n == -1 && errno == EINTR);
		_exit(1);
	}
	(void)close(dfd[0]);
Bob Halley's avatar
Bob Halley committed
395 396 397 398 399

	/*
	 * We're the child.
	 */

Bob Halley's avatar
Bob Halley committed
400 401 402 403
#ifdef HAVE_LINUXTHREADS
	mainpid = getpid();
#endif

Mark Andrews's avatar
Mark Andrews committed
404
	if (setsid() == -1) {
405 406 407
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("setsid(): %s", strbuf);
	}
Bob Halley's avatar
Bob Halley committed
408 409 410 411

	/*
	 * Try to set stdin, stdout, and stderr to /dev/null, but press
	 * on even if it fails.
412 413 414 415 416 417
	 *
	 * XXXMLG The close() calls here are unneeded on all but NetBSD, but
	 * are harmless to include everywhere.  dup2() is supposed to close
	 * the FD if it is in use, but unproven-pthreads-0.16 is broken
	 * and will end up closing the wrong FD.  This will be fixed eventually,
	 * and these calls will be removed.
Bob Halley's avatar
Bob Halley committed
418
	 */
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
	if (devnullfd != -1) {
		if (devnullfd != STDIN_FILENO) {
			(void)close(STDIN_FILENO);
			(void)dup2(devnullfd, STDIN_FILENO);
		}
		if (devnullfd != STDOUT_FILENO) {
			(void)close(STDOUT_FILENO);
			(void)dup2(devnullfd, STDOUT_FILENO);
		}
		if (devnullfd != STDERR_FILENO) {
			(void)close(STDERR_FILENO);
			(void)dup2(devnullfd, STDERR_FILENO);
		}
	}
}

435 436 437 438 439 440 441 442 443 444 445 446 447 448
void
ns_os_started(void) {
	char buf = 0;

	/*
	 * Signal to the parent that we stated successfully.
	 */
	if (dfd[0] != -1 && dfd[1] != -1) {
		write(dfd[1], &buf, 1);
		close(dfd[1]);
		dfd[0] = dfd[1] = -1;
	}
}

449 450 451 452 453 454 455 456 457 458 459 460
void
ns_os_opendevnull(void) {
	devnullfd = open("/dev/null", O_RDWR, 0);
}

void
ns_os_closedevnull(void) {
	if (devnullfd != STDIN_FILENO &&
	    devnullfd != STDOUT_FILENO &&
	    devnullfd != STDERR_FILENO) {
		close(devnullfd);
		devnullfd = -1;
Bob Halley's avatar
Bob Halley committed
461 462 463
	}
}

464 465 466 467 468
static isc_boolean_t
all_digits(const char *s) {
	if (*s == '\0')
		return (ISC_FALSE);
	while (*s != '\0') {
Mark Andrews's avatar
lint  
Mark Andrews committed
469
		if (!isdigit((*s)&0xff))
470 471 472 473 474 475 476 477
			return (ISC_FALSE);
		s++;
	}
	return (ISC_TRUE);
}

void
ns_os_chroot(const char *root) {
478
	char strbuf[ISC_STRERRORSIZE];
479 480 481
#ifdef HAVE_LIBSCF
	ns_smf_chroot = 0;
#endif
482
	if (root != NULL) {
483 484 485 486 487 488 489 490
		if (chroot(root) < 0) {
			isc__strerror(errno, strbuf, sizeof(strbuf));
			ns_main_earlyfatal("chroot(): %s", strbuf);
		}
		if (chdir("/") < 0) {
			isc__strerror(errno, strbuf, sizeof(strbuf));
			ns_main_earlyfatal("chdir(/): %s", strbuf);
		}
491 492 493 494
#ifdef HAVE_LIBSCF
		/* Set ns_smf_chroot flag on successful chroot. */
		ns_smf_chroot = 1;
#endif
495 496 497 498
	}
}

void
499
ns_os_inituserinfo(const char *username) {
500
	char strbuf[ISC_STRERRORSIZE];
501
	if (username == NULL)
502 503 504
		return;

	if (all_digits(username))
505
		runas_pw = getpwuid((uid_t)atoi(username));
506
	else
507
		runas_pw = getpwnam(username);
508
	endpwent();
509

510
	if (runas_pw == NULL)
511
		ns_main_earlyfatal("user '%s' unknown", username);
512 513

	if (getuid() == 0) {
514 515 516 517
		if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
			isc__strerror(errno, strbuf, sizeof(strbuf));
			ns_main_earlyfatal("initgroups(): %s", strbuf);
		}
518 519
	}

520 521 522 523
}

void
ns_os_changeuser(void) {
524
	char strbuf[ISC_STRERRORSIZE];
525
	if (runas_pw == NULL || done_setuid)
526 527
		return;

528 529
	done_setuid = ISC_TRUE;

530
#ifdef HAVE_LINUXTHREADS
531
#ifdef HAVE_LINUX_CAPABILITY_H
532
	if (!non_root_caps)
533 534 535 536 537 538 539
		ns_main_earlyfatal("-u with Linux threads not supported: "
				   "requires kernel support for "
				   "prctl(PR_SET_KEEPCAPS)");
#else
	ns_main_earlyfatal("-u with Linux threads not supported: "
			   "no capabilities support or capabilities "
			   "disabled at build time");
540
#endif
541
#endif
542

543 544 545 546
	if (setgid(runas_pw->pw_gid) < 0) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("setgid(): %s", strbuf);
	}
547

548 549 550 551
	if (setuid(runas_pw->pw_uid) < 0) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("setuid(): %s", strbuf);
	}
552

553 554 555 556 557
#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
	/*
	 * Restore the ability of named to drop core after the setuid()
	 * call has disabled it.
	 */
558 559 560 561 562 563 564 565
	if (prctl(PR_SET_DUMPABLE,1,0,0,0) < 0) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s",
				     strbuf);
	}
#endif
#if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
	linux_minprivs();
566
#endif
567
}
Bob Halley's avatar
Bob Halley committed
568

Bob Halley's avatar
Bob Halley committed
569
void
570
ns_os_minprivs(void) {
571
#ifdef HAVE_SYS_PRCTL_H
572
	linux_keepcaps();
Bob Halley's avatar
Bob Halley committed
573
#endif
574

575 576 577
#ifdef HAVE_LINUXTHREADS
	ns_os_changeuser(); /* Call setuid() before threads are started */
#endif
578

579 580 581
#if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
	linux_minprivs();
#endif
Bob Halley's avatar
Bob Halley committed
582 583
}

Bob Halley's avatar
Bob Halley committed
584
static int
585 586
safe_open(const char *filename, isc_boolean_t append) {
	int fd;
Mark Andrews's avatar
Mark Andrews committed
587
	struct stat sb;
Bob Halley's avatar
Bob Halley committed
588

Mark Andrews's avatar
Mark Andrews committed
589 590
	if (stat(filename, &sb) == -1) {
		if (errno != ENOENT)
Bob Halley's avatar
Bob Halley committed
591
			return (-1);
Mark Andrews's avatar
Mark Andrews committed
592
	} else if ((sb.st_mode & S_IFREG) == 0) {
593
		errno = EOPNOTSUPP;
Bob Halley's avatar
Bob Halley committed
594
		return (-1);
595
	}
Bob Halley's avatar
Bob Halley committed
596

597 598
	if (append)
		fd = open(filename, O_WRONLY|O_CREAT|O_APPEND,
Mark Andrews's avatar
Mark Andrews committed
599
			  S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
600 601 602
	else {
		(void)unlink(filename);
		fd = open(filename, O_WRONLY|O_CREAT|O_EXCL,
Mark Andrews's avatar
Mark Andrews committed
603
			  S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
604 605
	}
	return (fd);
Bob Halley's avatar
Bob Halley committed
606 607 608 609
}

static void
cleanup_pidfile(void) {
610
	if (pidfile != NULL) {
Bob Halley's avatar
Bob Halley committed
611
		(void)unlink(pidfile);
612 613
		free(pidfile);
	}
Bob Halley's avatar
Bob Halley committed
614 615 616 617
	pidfile = NULL;
}

void
618
ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
Mark Andrews's avatar
Mark Andrews committed
619
	int fd;
Bob Halley's avatar
Bob Halley committed
620 621 622
	FILE *lockfile;
	size_t len;
	pid_t pid;
623
	char strbuf[ISC_STRERRORSIZE];
624
	void (*report)(const char *, ...);
Bob Halley's avatar
Bob Halley committed
625 626 627 628 629

	/*
	 * The caller must ensure any required synchronization.
	 */

630 631
	report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;

Bob Halley's avatar
Bob Halley committed
632 633
	cleanup_pidfile();

634
	if (filename == NULL)
635
		return;
636

Bob Halley's avatar
Bob Halley committed
637 638
	len = strlen(filename);
	pidfile = malloc(len + 1);
639 640
	if (pidfile == NULL) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
Mark Andrews's avatar
Mark Andrews committed
641
		(*report)("couldn't malloc '%s': %s", filename, strbuf);
642
		return;
643
	}
Bob Halley's avatar
Bob Halley committed
644 645 646
	/* This is safe. */
	strcpy(pidfile, filename);

Mark Andrews's avatar
Mark Andrews committed
647 648
	fd = safe_open(filename, ISC_FALSE);
	if (fd < 0) {
649
		isc__strerror(errno, strbuf, sizeof(strbuf));
Mark Andrews's avatar
Mark Andrews committed
650
		(*report)("couldn't open pid file '%s': %s", filename, strbuf);
651 652 653
		free(pidfile);
		pidfile = NULL;
		return;
654
	}
Mark Andrews's avatar
Mark Andrews committed
655 656
	lockfile = fdopen(fd, "w");
	if (lockfile == NULL) {
657
		isc__strerror(errno, strbuf, sizeof(strbuf));
658 659 660 661 662
		(*report)("could not fdopen() pid file '%s': %s",
			  filename, strbuf);
		(void)close(fd);
		cleanup_pidfile();
		return;
663
	}
Bob Halley's avatar
Bob Halley committed
664 665 666 667 668
#ifdef HAVE_LINUXTHREADS
	pid = mainpid;
#else
	pid = getpid();
#endif
Mark Andrews's avatar
Mark Andrews committed
669 670
	if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
		(*report)("fprintf() to pid file '%s' failed", filename);
671 672 673 674
		(void)fclose(lockfile);
		cleanup_pidfile();
		return;
	}
Mark Andrews's avatar
Mark Andrews committed
675 676
	if (fflush(lockfile) == EOF) {
		(*report)("fflush() to pid file '%s' failed", filename);
677 678 679 680
		(void)fclose(lockfile);
		cleanup_pidfile();
		return;
	}
Bob Halley's avatar
Bob Halley committed
681 682 683 684 685 686 687 688
	(void)fclose(lockfile);
}

void
ns_os_shutdown(void) {
	closelog();
	cleanup_pidfile();
}
689 690 691

isc_result_t
ns_os_gethostname(char *buf, size_t len) {
Mark Andrews's avatar
Mark Andrews committed
692
	int n;
693 694 695 696 697

	n = gethostname(buf, len);
	return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
}

698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
static char *
next_token(char **stringp, const char *delim) {
	char *res;

	do {
		res = strsep(stringp, delim);
		if (res == NULL)
			break;
	} while (*res == '\0');
	return (res);
}

void
ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
	char *input, *ptr;
	unsigned int n;
	pid_t pid;

	input = command;

	/* Skip the command name. */
	ptr = next_token(&input, " \t");
	if (ptr == NULL)
		return;

	ptr = next_token(&input, " \t");
	if (ptr == NULL)
		return;
Automatic Updater's avatar
Automatic Updater committed
726

727 728 729 730 731 732 733 734 735 736 737
	if (strcmp(ptr, "-p") != 0)
		return;

#ifdef HAVE_LINUXTHREADS
	pid = mainpid;
#else
	pid = getpid();
#endif

	n = snprintf((char *)isc_buffer_used(text),
		     isc_buffer_availablelength(text),
738
		     "pid: %ld", (long)pid);
739 740 741 742
	/* Only send a message if it is complete. */
	if (n < isc_buffer_availablelength(text))
		isc_buffer_add(text, n);
}
743 744 745 746 747 748 749

void
ns_os_tzset(void) {
#ifdef HAVE_TZSET
	tzset();
#endif
}