os.c 22.1 KB
Newer Older
Bob Halley's avatar
add  
Bob Halley committed
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2011, 2013, 2014  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.107 2011/03/02 00:02:54 marka 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/resource.h>
46
#include <isc/result.h>
47
#include <isc/strerror.h>
48
#include <isc/string.h>
Bob Halley's avatar
add  
Bob Halley committed
49
50
51

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

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

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

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
69
70
71
#endif

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

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

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

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

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

120
121
122
#ifdef HAVE_SYS_CAPABILITY_H
#include <sys/capability.h>
#else
123
124
125
#ifdef HAVE_LINUX_TYPES_H
#include <linux/types.h>
#endif
126
/*%
127
128
129
130
131
132
 * 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
133
134
135
136
137
138
139
140
141
#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
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
149
150
151
 * here.  This allows setuid() to work on systems running a new enough
 * kernel but with /usr/include/linux pointing to "standard" kernel
 * headers.
 */
152
153
#ifndef PR_SET_KEEPCAPS
#define PR_SET_KEEPCAPS 8
Bob Halley's avatar
Bob Halley committed
154
#endif
155

156
#endif /* HAVE_SYS_PRCTL_H */
Bob Halley's avatar
Bob Halley committed
157

158
159
160
161
162
163
#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
164
165

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

173
	if ((getuid() != 0 && !non_root_caps) || non_root)
Bob Halley's avatar
add  
Bob Halley committed
174
		return;
175
#ifndef HAVE_LIBCAP
Andreas Gustafsson's avatar
Andreas Gustafsson committed
176
	memset(&caphead, 0, sizeof(caphead));
Bob Halley's avatar
add  
Bob Halley committed
177
178
	caphead.version = _LINUX_CAPABILITY_VERSION;
	caphead.pid = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
179
	memset(&cap, 0, sizeof(cap));
Bob Halley's avatar
add  
Bob Halley committed
180
181
	cap.effective = caps;
	cap.permitted = caps;
182
	cap.inheritable = 0;
183
184
185
#endif
#ifdef HAVE_LIBCAP
	if (cap_set_proc(caps) < 0) {
186
187
#else
	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
192
193
194
				   " please ensure that the capset kernel"
				   " module is loaded.  see insmod(8)",
				   strbuf);
	}
Bob Halley's avatar
add  
Bob Halley committed
195
}
196

197
198
199
#ifdef HAVE_LIBCAP
#define SET_CAP(flag) \
	do { \
200
		cap_flag_value_t curval; \
201
		capval = (flag); \
202
		err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval); \
203
204
205
206
207
208
209
210
211
212
213
214
		if (err != -1 && curval) { \
			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); \
			} \
215
216
217
218
219
220
221
222
223
		} \
	} 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); \
		} \
224
225
226
227
228
229
230
231
232
233
		curcaps = cap_get_proc(); \
		if (curcaps == NULL) { \
			isc__strerror(errno, strbuf, sizeof(strbuf)); \
			ns_main_earlyfatal("cap_get_proc failed: %s", strbuf); \
		} \
	} while (0)
#define FREE_CAP \
	{ \
		cap_free(caps); \
		cap_free(curcaps); \
234
235
	} while (0)
#else
236
237
#define SET_CAP(flag) do { caps |= (1 << (flag)); } while (0)
#define INIT_CAP do { caps = 0; } while (0)
238
239
#endif /* HAVE_LIBCAP */

240
241
static void
linux_initialprivs(void) {
242
243
	cap_t caps;
#ifdef HAVE_LIBCAP
244
	cap_t curcaps;
245
246
247
248
	cap_value_t capval;
	char strbuf[ISC_STRERRORSIZE];
	int err;
#endif
249

250
	/*%
Bob Halley's avatar
Bob Halley committed
251
252
253
	 * 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.
254
	 */
255
	INIT_CAP;
Bob Halley's avatar
Bob Halley committed
256
257
258
259

	/*
	 * We need to be able to bind() to privileged ports, notably port 53!
	 */
260
	SET_CAP(CAP_NET_BIND_SERVICE);
Bob Halley's avatar
Bob Halley committed
261
262
263
264

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

267
#if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
Bob Halley's avatar
Bob Halley committed
268
	/*
269
270
271
272
	 * 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
273
	 */
274
	SET_CAP(CAP_SETUID);
Bob Halley's avatar
Bob Halley committed
275
276
#endif

277
278
279
	/*
	 * Since we call initgroups, we need this.
	 */
280
	SET_CAP(CAP_SETGID);
281

282
283
284
285
	/*
	 * Without this, we run into problems reading a configuration file
	 * owned by a non-root user and non-world-readable on startup.
	 */
286
	SET_CAP(CAP_DAC_READ_SEARCH);
287

Bob Halley's avatar
Bob Halley committed
288
289
290
	/*
	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
	 *      clear it would work right given the way linuxthreads work.
291
292
293
	 * 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
294
	 */
295
	SET_CAP(CAP_SYS_RESOURCE);
Bob Halley's avatar
Bob Halley committed
296

297
298
299
300
301
302
	/*
	 * We need to be able to set the ownership of the containing
	 * directory of the pid file when we create it.
	 */
	SET_CAP(CAP_CHOWN);

303
	linux_setcaps(caps);
304
305
306
307

#ifdef HAVE_LIBCAP
	FREE_CAP;
#endif
308
309
310
311
}

static void
linux_minprivs(void) {
312
313
	cap_t caps;
#ifdef HAVE_LIBCAP
314
	cap_t curcaps;
315
316
317
318
	cap_value_t capval;
	char strbuf[ISC_STRERRORSIZE];
	int err;
#endif
319

320
	INIT_CAP;
321
	/*%
Bob Halley's avatar
Bob Halley committed
322
	 * Drop all privileges except the ability to bind() to privileged
Bob Halley's avatar
Bob Halley committed
323
	 * ports.
Bob Halley's avatar
Bob Halley committed
324
325
326
	 *
	 * It's important that we drop CAP_SYS_CHROOT.  If we didn't, it
	 * chroot() could be used to escape from the chrooted area.
327
328
	 */

329
	SET_CAP(CAP_NET_BIND_SERVICE);
330

Mark Andrews's avatar
Mark Andrews committed
331
332
333
334
335
336
337
	/*
	 * 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.
	 */
338
	SET_CAP(CAP_SYS_RESOURCE);
Mark Andrews's avatar
Mark Andrews committed
339

340
	linux_setcaps(caps);
341
342
343
344

#ifdef HAVE_LIBCAP
	FREE_CAP;
#endif
345
346
}

347
#ifdef HAVE_SYS_PRCTL_H
Bob Halley's avatar
Bob Halley committed
348
349
static void
linux_keepcaps(void) {
350
	char strbuf[ISC_STRERRORSIZE];
351
	/*%
Bob Halley's avatar
Bob Halley committed
352
353
354
355
356
	 * Ask the kernel to allow us to keep our capabilities after we
	 * setuid().
	 */

	if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
357
358
359
360
		if (errno != EINVAL) {
			isc__strerror(errno, strbuf, sizeof(strbuf));
			ns_main_earlyfatal("prctl() failed: %s", strbuf);
		}
361
	} else {
Bob Halley's avatar
Bob Halley committed
362
		non_root_caps = ISC_TRUE;
363
364
365
		if (getuid() != 0)
			non_root = ISC_TRUE;
	}
Bob Halley's avatar
Bob Halley committed
366
367
368
}
#endif

369
370
#endif	/* HAVE_LINUX_CAPABILITY_H */

Bob Halley's avatar
add  
Bob Halley committed
371

Bob Halley's avatar
Bob Halley committed
372
static void
373
setup_syslog(const char *progname) {
Bob Halley's avatar
Bob Halley committed
374
375
376
377
378
379
	int options;

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

383
void
384
385
ns_os_init(const char *progname) {
	setup_syslog(progname);
Bob Halley's avatar
add  
Bob Halley committed
386
#ifdef HAVE_LINUX_CAPABILITY_H
387
	linux_initialprivs();
Bob Halley's avatar
add  
Bob Halley committed
388
#endif
Bob Halley's avatar
Bob Halley committed
389
390
391
#ifdef HAVE_LINUXTHREADS
	mainpid = getpid();
#endif
392
393
394
#ifdef SIGXFSZ
	signal(SIGXFSZ, SIG_IGN);
#endif
Bob Halley's avatar
add  
Bob Halley committed
395
396
}

397
void
Bob Halley's avatar
Bob Halley committed
398
399
ns_os_daemonize(void) {
	pid_t pid;
400
	char strbuf[ISC_STRERRORSIZE];
Bob Halley's avatar
Bob Halley committed
401

402
403
404
405
406
	if (pipe(dfd) == -1) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("pipe(): %s", strbuf);
	}

Bob Halley's avatar
Bob Halley committed
407
	pid = fork();
408
409
410
411
	if (pid == -1) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("fork(): %s", strbuf);
	}
412
413
414
415
416
	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
417
		 * were multi-threaded.
418
419
420
421
422
423
424
425
426
427
428
		 */
		(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
429
430
431
432
433

	/*
	 * We're the child.
	 */

Bob Halley's avatar
Bob Halley committed
434
435
436
437
#ifdef HAVE_LINUXTHREADS
	mainpid = getpid();
#endif

Mark Andrews's avatar
Mark Andrews committed
438
	if (setsid() == -1) {
439
440
441
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("setsid(): %s", strbuf);
	}
Bob Halley's avatar
Bob Halley committed
442
443
444
445

	/*
	 * Try to set stdin, stdout, and stderr to /dev/null, but press
	 * on even if it fails.
446
447
448
449
450
451
	 *
	 * 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
452
	 */
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
	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);
		}
	}
}

469
470
471
472
473
void
ns_os_started(void) {
	char buf = 0;

	/*
474
	 * Signal to the parent that we started successfully.
475
476
	 */
	if (dfd[0] != -1 && dfd[1] != -1) {
477
478
479
		if (write(dfd[1], &buf, 1) != 1)
			ns_main_earlyfatal("unable to signal parent that we "
					   "otherwise started successfully.");
480
481
482
483
484
		close(dfd[1]);
		dfd[0] = dfd[1] = -1;
	}
}

485
486
487
488
489
490
491
492
493
494
495
496
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
497
498
499
	}
}

500
501
502
503
504
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
505
		if (!isdigit((*s)&0xff))
506
507
508
509
510
511
512
513
			return (ISC_FALSE);
		s++;
	}
	return (ISC_TRUE);
}

void
ns_os_chroot(const char *root) {
514
	char strbuf[ISC_STRERRORSIZE];
515
516
517
#ifdef HAVE_LIBSCF
	ns_smf_chroot = 0;
#endif
518
	if (root != NULL) {
519
#ifdef HAVE_CHROOT
520
521
522
523
		if (chroot(root) < 0) {
			isc__strerror(errno, strbuf, sizeof(strbuf));
			ns_main_earlyfatal("chroot(): %s", strbuf);
		}
524
525
526
#else
		ns_main_earlyfatal("chroot(): disabled");
#endif
527
528
529
530
		if (chdir("/") < 0) {
			isc__strerror(errno, strbuf, sizeof(strbuf));
			ns_main_earlyfatal("chdir(/): %s", strbuf);
		}
531
532
533
534
#ifdef HAVE_LIBSCF
		/* Set ns_smf_chroot flag on successful chroot. */
		ns_smf_chroot = 1;
#endif
535
536
537
538
	}
}

void
539
ns_os_inituserinfo(const char *username) {
540
	char strbuf[ISC_STRERRORSIZE];
541
	if (username == NULL)
542
543
544
		return;

	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
555
556
557
		if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
			isc__strerror(errno, strbuf, sizeof(strbuf));
			ns_main_earlyfatal("initgroups(): %s", strbuf);
		}
558
559
	}

560
561
562
563
}

void
ns_os_changeuser(void) {
564
	char strbuf[ISC_STRERRORSIZE];
565
	if (runas_pw == NULL || done_setuid)
566
567
		return;

568
569
	done_setuid = ISC_TRUE;

570
#ifdef HAVE_LINUXTHREADS
571
#ifdef HAVE_LINUX_CAPABILITY_H
572
	if (!non_root_caps)
573
574
575
576
577
578
579
		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");
580
#endif
581
#endif
582

583
584
585
586
	if (setgid(runas_pw->pw_gid) < 0) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("setgid(): %s", strbuf);
	}
587

588
589
590
591
	if (setuid(runas_pw->pw_uid) < 0) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("setuid(): %s", strbuf);
	}
592

593
594
595
596
597
#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.
	 */
598
599
600
601
602
603
604
605
	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();
606
#endif
607
}
Bob Halley's avatar
Bob Halley committed
608

609
void
610
ns_os_adjustnofile(void) {
611
612
613
614
615
616
617
618
619
620
621
622
623
#ifdef HAVE_LINUXTHREADS
	isc_result_t result;
	isc_resourcevalue_t newvalue;

	/*
	 * Linux: max number of open files specified by one thread doesn't seem
	 * to apply to other threads on Linux.
	 */
	newvalue = ISC_RESOURCE_UNLIMITED;

	result = isc_resource_setlimit(isc_resource_openfiles, newvalue);
	if (result != ISC_R_SUCCESS)
		ns_main_earlywarning("couldn't adjust limit on open files");
Automatic Updater's avatar
Automatic Updater committed
624
#endif
625
626
}

Bob Halley's avatar
Bob Halley committed
627
void
628
ns_os_minprivs(void) {
629
#ifdef HAVE_SYS_PRCTL_H
630
	linux_keepcaps();
Bob Halley's avatar
Bob Halley committed
631
#endif
632

633
634
635
#ifdef HAVE_LINUXTHREADS
	ns_os_changeuser(); /* Call setuid() before threads are started */
#endif
636

637
638
639
#if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
	linux_minprivs();
#endif
Bob Halley's avatar
Bob Halley committed
640
641
}

Bob Halley's avatar
Bob Halley committed
642
static int
643
safe_open(const char *filename, mode_t mode, isc_boolean_t append) {
644
	int fd;
Mark Andrews's avatar
Mark Andrews committed
645
	struct stat sb;
Bob Halley's avatar
Bob Halley committed
646

Mark Andrews's avatar
Mark Andrews committed
647
648
	if (stat(filename, &sb) == -1) {
		if (errno != ENOENT)
Bob Halley's avatar
Bob Halley committed
649
			return (-1);
Mark Andrews's avatar
Mark Andrews committed
650
	} else if ((sb.st_mode & S_IFREG) == 0) {
651
		errno = EOPNOTSUPP;
Bob Halley's avatar
Bob Halley committed
652
		return (-1);
653
	}
Bob Halley's avatar
Bob Halley committed
654

655
	if (append)
656
		fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, mode);
657
	else {
Mark Andrews's avatar
Mark Andrews committed
658
659
		if (unlink(filename) < 0 && errno != ENOENT)
			return (-1);
660
		fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, mode);
661
662
	}
	return (fd);
Bob Halley's avatar
Bob Halley committed
663
664
665
666
}

static void
cleanup_pidfile(void) {
Mark Andrews's avatar
Mark Andrews committed
667
	int n;
668
	if (pidfile != NULL) {
Mark Andrews's avatar
Mark Andrews committed
669
670
671
		n = unlink(pidfile);
		if (n == -1 && errno != ENOENT)
			ns_main_earlywarning("unlink '%s': failed", pidfile);
672
673
		free(pidfile);
	}
Bob Halley's avatar
Bob Halley committed
674
675
676
	pidfile = NULL;
}

677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
static int
mkdirpath(char *filename, void (*report)(const char *, ...)) {
	char *slash = strrchr(filename, '/');
	char strbuf[ISC_STRERRORSIZE];
	unsigned int mode;

	if (slash != NULL && slash != filename) {
		struct stat sb;
		*slash = '\0';

		if (stat(filename, &sb) == -1) {
			if (errno != ENOENT) {
				isc__strerror(errno, strbuf, sizeof(strbuf));
				(*report)("couldn't stat '%s': %s", filename,
					  strbuf);
				goto error;
			}
Automatic Updater's avatar
Automatic Updater committed
694
			if (mkdirpath(filename, report) == -1)
695
				goto error;
696
697
698
699
700
701
702
703
704
			/*
			 * Handle "//", "/./" and "/../" in path.
			 */
			if (!strcmp(slash + 1, "") ||
			    !strcmp(slash + 1, ".") ||
			    !strcmp(slash + 1, "..")) {
				*slash = '/';
				return (0);
			}
705
706
707
708
709
710
711
712
713
			mode = S_IRUSR | S_IWUSR | S_IXUSR;	/* u=rwx */
			mode |= S_IRGRP | S_IXGRP;		/* g=rx */
			mode |= S_IROTH | S_IXOTH;		/* o=rx */
			if (mkdir(filename, mode) == -1) {
				isc__strerror(errno, strbuf, sizeof(strbuf));
				(*report)("couldn't mkdir '%s': %s", filename,
					  strbuf);
				goto error;
			}
714
715
716
717
718
719
720
			if (runas_pw != NULL &&
			    chown(filename, runas_pw->pw_uid,
				  runas_pw->pw_gid) == -1) {
				isc__strerror(errno, strbuf, sizeof(strbuf));
				(*report)("couldn't chown '%s': %s", filename,
					  strbuf);
			}
721
722
723
724
725
726
727
728
729
730
		}
		*slash = '/';
	}
	return (0);

 error:
	*slash = '/';
	return (-1);
}

731
static void
732
setperms(uid_t uid, gid_t gid) {
733
	char strbuf[ISC_STRERRORSIZE];
734
735
736
737
738
739
#if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID)
	gid_t oldgid, tmpg;
#endif
#if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID)
	uid_t olduid, tmpu;
#endif
740
#if defined(HAVE_SETEGID)
741
	if (getegid() != gid && setegid(gid) == -1) {
Automatic Updater's avatar
Automatic Updater committed
742
		isc__strerror(errno, strbuf, sizeof(strbuf));
743
744
		ns_main_earlywarning("unable to set effective gid to %ld: %s",
				     (long)gid, strbuf);
Automatic Updater's avatar
Automatic Updater committed
745
	}
746
#elif defined(HAVE_SETRESGID)
747
748
749
750
751
752
	if (getresgid(&tmpg, &oldgid, &tmpg) == -1 || oldgid != gid) {
		if (setresgid(-1, gid, -1) == -1) {
			isc__strerror(errno, strbuf, sizeof(strbuf));
			ns_main_earlywarning("unable to set effective "
					     "gid to %d: %s", gid, strbuf);
		}
Automatic Updater's avatar
Automatic Updater committed
753
	}
754
755
756
#endif

#if defined(HAVE_SETEUID)
757
	if (geteuid() != uid && seteuid(uid) == -1) {
Automatic Updater's avatar
Automatic Updater committed
758
		isc__strerror(errno, strbuf, sizeof(strbuf));
759
760
		ns_main_earlywarning("unable to set effective uid to %ld: %s",
				     (long)uid, strbuf);
Automatic Updater's avatar
Automatic Updater committed
761
	}
762
#elif defined(HAVE_SETRESUID)
763
764
765
766
767
768
	if (getresuid(&tmpu, &olduid, &tmpu) == -1 || olduid != uid) {
		if (setresuid(-1, uid, -1) == -1) {
			isc__strerror(errno, strbuf, sizeof(strbuf));
			ns_main_earlywarning("unable to set effective "
					     "uid to %d: %s", uid, strbuf);
		}
Automatic Updater's avatar
Automatic Updater committed
769
	}
770
771
772
#endif
}

773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
FILE *
ns_os_openfile(const char *filename, mode_t mode, isc_boolean_t switch_user) {
	char strbuf[ISC_STRERRORSIZE], *f;
	FILE *fp;
	int fd;

	/*
	 * Make the containing directory if it doesn't exist.
	 */
	f = strdup(filename);
	if (f == NULL) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlywarning("couldn't strdup() '%s': %s",
				     filename, strbuf);
		return (NULL);
	}
	if (mkdirpath(f, ns_main_earlywarning) == -1) {
		free(f);
		return (NULL);
	}
	free(f);

	if (switch_user && runas_pw != NULL) {
796
797
798
#ifndef HAVE_LINUXTHREADS
		gid_t oldgid = getgid();
#endif
799
800
801
802
803
804
805
		/* Set UID/GID to the one we'll be running with eventually */
		setperms(runas_pw->pw_uid, runas_pw->pw_gid);

		fd = safe_open(filename, mode, ISC_FALSE);

#ifndef HAVE_LINUXTHREADS
		/* Restore UID/GID to root */
806
		setperms(0, oldgid);
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
#endif /* HAVE_LINUXTHREADS */

		if (fd == -1) {
#ifndef HAVE_LINUXTHREADS
			fd = safe_open(filename, mode, ISC_FALSE);
			if (fd != -1) {
				ns_main_earlywarning("Required root "
						     "permissions to open "
						     "'%s'.", filename);
			} else {
				ns_main_earlywarning("Could not open "
						     "'%s'.", filename);
			}
			ns_main_earlywarning("Please check file and "
					     "directory permissions "
					     "or reconfigure the filename.");
#else /* HAVE_LINUXTHREADS */
			ns_main_earlywarning("Could not open "
					     "'%s'.", filename);
			ns_main_earlywarning("Please check file and "
					     "directory permissions "
					     "or reconfigure the filename.");
#endif /* HAVE_LINUXTHREADS */
		}
	} else {
		fd = safe_open(filename, mode, ISC_FALSE);
	}

	if (fd < 0) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlywarning("could not open file '%s': %s",
				     filename, strbuf);
		return (NULL);
	}

	fp = fdopen(fd, "w");
	if (fp == NULL) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlywarning("could not fdopen() file '%s': %s",
				     filename, strbuf);
	}

	return (fp);
}

Bob Halley's avatar
Bob Halley committed
852
void
853
ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
Bob Halley's avatar
Bob Halley committed
854
855
	FILE *lockfile;
	pid_t pid;
856
	char strbuf[ISC_STRERRORSIZE];
857
	void (*report)(const char *, ...);
Bob Halley's avatar
Bob Halley committed
858
859
860
861
862

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

863
864
	report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;

Bob Halley's avatar
Bob Halley committed
865
866
	cleanup_pidfile();

867
	if (filename == NULL)
868
		return;
869

870
	pidfile = strdup(filename);
871
872
	if (pidfile == NULL) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
873
		(*report)("couldn't strdup() '%s': %s", filename, strbuf);
874
		return;
875
	}
876

877
878
	lockfile = ns_os_openfile(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
				  first_time);
Mark Andrews's avatar
Mark Andrews committed
879
	if (lockfile == NULL) {
880
881
		cleanup_pidfile();
		return;
882
	}
Bob Halley's avatar
Bob Halley committed
883
884
885
886
887
#ifdef HAVE_LINUXTHREADS
	pid = mainpid;
#else
	pid = getpid();
#endif
Mark Andrews's avatar
Mark Andrews committed
888
889
	if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
		(*report)("fprintf() to pid file '%s' failed", filename);
890
891
892
893
		(void)fclose(lockfile);
		cleanup_pidfile();
		return;
	}
Mark Andrews's avatar
Mark Andrews committed
894
895
	if (fflush(lockfile) == EOF) {
		(*report)("fflush() to pid file '%s' failed", filename);
896
897
898
899
		(void)fclose(lockfile);
		cleanup_pidfile();
		return;
	}
Bob Halley's avatar
Bob Halley committed
900
901
902
903
904
905
906
907
	(void)fclose(lockfile);
}

void
ns_os_shutdown(void) {
	closelog();
	cleanup_pidfile();
}
908
909
910

isc_result_t
ns_os_gethostname(char *buf, size_t len) {
Mark Andrews's avatar
Mark Andrews committed
911
	int n;
912
913
914
915
916

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

917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
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
945

946
947
948
949
950
951
952
953
954
955
956
	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),
957
		     "pid: %ld", (long)pid);
958
	/* Only send a message if it is complete. */
Mark Andrews's avatar
Mark Andrews committed
959
	if (n > 0 && n < isc_buffer_availablelength(text))
960
961
		isc_buffer_add(text, n);
}
962
963
964
965
966
967
968

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