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