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