1 /* $NetBSD: os.c,v 1.8 2014/12/10 04:37:52 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2004-2011, 2013, 2014 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 1999-2002 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* Id: os.c,v 1.107 2011/03/02 00:02:54 marka Exp */ 21 22 /*! \file */ 23 24 #include <config.h> 25 #include <stdarg.h> 26 27 #include <sys/types.h> /* dev_t FreeBSD 2.1 */ 28 #include <sys/stat.h> 29 30 #include <ctype.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <grp.h> /* Required for initgroups() on IRIX. */ 34 #include <pwd.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <signal.h> 38 #include <syslog.h> 39 #ifdef HAVE_TZSET 40 #include <time.h> 41 #endif 42 #include <unistd.h> 43 44 #include <isc/buffer.h> 45 #include <isc/file.h> 46 #include <isc/print.h> 47 #include <isc/resource.h> 48 #include <isc/result.h> 49 #include <isc/strerror.h> 50 #include <isc/string.h> 51 52 #include <named/main.h> 53 #include <named/os.h> 54 #ifdef HAVE_LIBSCF 55 #include <named/ns_smf_globals.h> 56 #endif 57 58 static char *pidfile = NULL; 59 static int devnullfd = -1; 60 61 #ifndef ISC_FACILITY 62 #define ISC_FACILITY LOG_DAEMON 63 #endif 64 65 /* 66 * If there's no <linux/capability.h>, we don't care about <sys/prctl.h> 67 */ 68 #ifndef HAVE_LINUX_CAPABILITY_H 69 #undef HAVE_SYS_PRCTL_H 70 #endif 71 72 /* 73 * Linux defines: 74 * (T) HAVE_LINUXTHREADS 75 * (C) HAVE_SYS_CAPABILITY_H (or HAVE_LINUX_CAPABILITY_H) 76 * (P) HAVE_SYS_PRCTL_H 77 * The possible cases are: 78 * none: setuid() normally 79 * T: no setuid() 80 * C: setuid() normally, drop caps (keep CAP_SETUID) 81 * T+C: no setuid(), drop caps (don't keep CAP_SETUID) 82 * T+C+P: setuid() early, drop caps (keep CAP_SETUID) 83 * C+P: setuid() normally, drop caps (keep CAP_SETUID) 84 * P: not possible 85 * T+P: not possible 86 * 87 * if (C) 88 * caps = BIND_SERVICE + CHROOT + SETGID 89 * if ((T && C && P) || !T) 90 * caps += SETUID 91 * endif 92 * capset(caps) 93 * endif 94 * if (T && C && P && -u) 95 * setuid() 96 * else if (T && -u) 97 * fail 98 * --> start threads 99 * if (!T && -u) 100 * setuid() 101 * if (C && (P || !-u)) 102 * caps = BIND_SERVICE 103 * capset(caps) 104 * endif 105 * 106 * It will be nice when Linux threads work properly with setuid(). 107 */ 108 109 #ifdef HAVE_LINUXTHREADS 110 static pid_t mainpid = 0; 111 #endif 112 113 static struct passwd *runas_pw = NULL; 114 static isc_boolean_t done_setuid = ISC_FALSE; 115 static int dfd[2] = { -1, -1 }; 116 117 #ifdef HAVE_LINUX_CAPABILITY_H 118 119 static isc_boolean_t non_root = ISC_FALSE; 120 static isc_boolean_t non_root_caps = ISC_FALSE; 121 122 #ifdef HAVE_SYS_CAPABILITY_H 123 #include <sys/capability.h> 124 #else 125 #ifdef HAVE_LINUX_TYPES_H 126 #include <linux/types.h> 127 #endif 128 /*% 129 * We define _LINUX_FS_H to prevent it from being included. We don't need 130 * anything from it, and the files it includes cause warnings with 2.2 131 * kernels, and compilation failures (due to conflicts between <linux/string.h> 132 * and <string.h>) on 2.3 kernels. 133 */ 134 #define _LINUX_FS_H 135 #include <linux/capability.h> 136 #include <syscall.h> 137 #ifndef SYS_capset 138 #ifndef __NR_capset 139 #include <asm/unistd.h> /* Slackware 4.0 needs this. */ 140 #endif /* __NR_capset */ 141 #define SYS_capset __NR_capset 142 #endif /* SYS_capset */ 143 #endif /* HAVE_SYS_CAPABILITY_H */ 144 145 #ifdef HAVE_SYS_PRCTL_H 146 #include <sys/prctl.h> /* Required for prctl(). */ 147 148 /* 149 * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it 150 * here. This allows setuid() to work on systems running a new enough 151 * kernel but with /usr/include/linux pointing to "standard" kernel 152 * headers. 153 */ 154 #ifndef PR_SET_KEEPCAPS 155 #define PR_SET_KEEPCAPS 8 156 #endif 157 158 #endif /* HAVE_SYS_PRCTL_H */ 159 160 #ifdef HAVE_LIBCAP 161 #define SETCAPS_FUNC "cap_set_proc " 162 #else 163 typedef unsigned int cap_t; 164 #define SETCAPS_FUNC "syscall(capset) " 165 #endif /* HAVE_LIBCAP */ 166 167 static void 168 linux_setcaps(cap_t caps) { 169 #ifndef HAVE_LIBCAP 170 struct __user_cap_header_struct caphead; 171 struct __user_cap_data_struct cap; 172 #endif 173 char strbuf[ISC_STRERRORSIZE]; 174 175 if ((getuid() != 0 && !non_root_caps) || non_root) 176 return; 177 #ifndef HAVE_LIBCAP 178 memset(&caphead, 0, sizeof(caphead)); 179 caphead.version = _LINUX_CAPABILITY_VERSION; 180 caphead.pid = 0; 181 memset(&cap, 0, sizeof(cap)); 182 cap.effective = caps; 183 cap.permitted = caps; 184 cap.inheritable = 0; 185 #endif 186 #ifdef HAVE_LIBCAP 187 if (cap_set_proc(caps) < 0) { 188 #else 189 if (syscall(SYS_capset, &caphead, &cap) < 0) { 190 #endif 191 isc__strerror(errno, strbuf, sizeof(strbuf)); 192 ns_main_earlyfatal(SETCAPS_FUNC "failed: %s:" 193 " please ensure that the capset kernel" 194 " module is loaded. see insmod(8)", 195 strbuf); 196 } 197 } 198 199 #ifdef HAVE_LIBCAP 200 #define SET_CAP(flag) \ 201 do { \ 202 cap_flag_value_t curval; \ 203 capval = (flag); \ 204 err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval); \ 205 if (err != -1 && curval) { \ 206 err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET); \ 207 if (err == -1) { \ 208 isc__strerror(errno, strbuf, sizeof(strbuf)); \ 209 ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \ 210 } \ 211 \ 212 err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET); \ 213 if (err == -1) { \ 214 isc__strerror(errno, strbuf, sizeof(strbuf)); \ 215 ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \ 216 } \ 217 } \ 218 } while (/*CONSTCOND*/0) 219 #define INIT_CAP \ 220 do { \ 221 caps = cap_init(); \ 222 if (caps == NULL) { \ 223 isc__strerror(errno, strbuf, sizeof(strbuf)); \ 224 ns_main_earlyfatal("cap_init failed: %s", strbuf); \ 225 } \ 226 curcaps = cap_get_proc(); \ 227 if (curcaps == NULL) { \ 228 isc__strerror(errno, strbuf, sizeof(strbuf)); \ 229 ns_main_earlyfatal("cap_get_proc failed: %s", strbuf); \ 230 } \ 231 } while (/*CONSTCOND*/0) 232 #define FREE_CAP \ 233 { \ 234 cap_free(caps); \ 235 cap_free(curcaps); \ 236 } while (/*CONSTCOND*/0) 237 #else 238 #define SET_CAP(flag) do { caps |= (1 << (flag)); } while (/*CONSTCOND*/0) 239 #define INIT_CAP do { caps = 0; } while (/*CONSTCOND*/0) 240 #endif /* HAVE_LIBCAP */ 241 242 static void 243 linux_initialprivs(void) { 244 cap_t caps; 245 #ifdef HAVE_LIBCAP 246 cap_t curcaps; 247 cap_value_t capval; 248 char strbuf[ISC_STRERRORSIZE]; 249 int err; 250 #endif 251 252 /*% 253 * We don't need most privileges, so we drop them right away. 254 * Later on linux_minprivs() will be called, which will drop our 255 * capabilities to the minimum needed to run the server. 256 */ 257 INIT_CAP; 258 259 /* 260 * We need to be able to bind() to privileged ports, notably port 53! 261 */ 262 SET_CAP(CAP_NET_BIND_SERVICE); 263 264 /* 265 * We need chroot() initially too. 266 */ 267 SET_CAP(CAP_SYS_CHROOT); 268 269 #if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS) 270 /* 271 * We can setuid() only if either the kernel supports keeping 272 * capabilities after setuid() (which we don't know until we've 273 * tried) or we're not using threads. If either of these is 274 * true, we want the setuid capability. 275 */ 276 SET_CAP(CAP_SETUID); 277 #endif 278 279 /* 280 * Since we call initgroups, we need this. 281 */ 282 SET_CAP(CAP_SETGID); 283 284 /* 285 * Without this, we run into problems reading a configuration file 286 * owned by a non-root user and non-world-readable on startup. 287 */ 288 SET_CAP(CAP_DAC_READ_SEARCH); 289 290 /* 291 * XXX We might want to add CAP_SYS_RESOURCE, though it's not 292 * clear it would work right given the way linuxthreads work. 293 * XXXDCL But since we need to be able to set the maximum number 294 * of files, the stack size, data size, and core dump size to 295 * support named.conf options, this is now being added to test. 296 */ 297 SET_CAP(CAP_SYS_RESOURCE); 298 299 /* 300 * We need to be able to set the ownership of the containing 301 * directory of the pid file when we create it. 302 */ 303 SET_CAP(CAP_CHOWN); 304 305 linux_setcaps(caps); 306 307 #ifdef HAVE_LIBCAP 308 FREE_CAP; 309 #endif 310 } 311 312 static void 313 linux_minprivs(void) { 314 cap_t caps; 315 #ifdef HAVE_LIBCAP 316 cap_t curcaps; 317 cap_value_t capval; 318 char strbuf[ISC_STRERRORSIZE]; 319 int err; 320 #endif 321 322 INIT_CAP; 323 /*% 324 * Drop all privileges except the ability to bind() to privileged 325 * ports. 326 * 327 * It's important that we drop CAP_SYS_CHROOT. If we didn't, it 328 * chroot() could be used to escape from the chrooted area. 329 */ 330 331 SET_CAP(CAP_NET_BIND_SERVICE); 332 333 /* 334 * XXX We might want to add CAP_SYS_RESOURCE, though it's not 335 * clear it would work right given the way linuxthreads work. 336 * XXXDCL But since we need to be able to set the maximum number 337 * of files, the stack size, data size, and core dump size to 338 * support named.conf options, this is now being added to test. 339 */ 340 SET_CAP(CAP_SYS_RESOURCE); 341 342 linux_setcaps(caps); 343 344 #ifdef HAVE_LIBCAP 345 FREE_CAP; 346 #endif 347 } 348 349 #ifdef HAVE_SYS_PRCTL_H 350 static void 351 linux_keepcaps(void) { 352 char strbuf[ISC_STRERRORSIZE]; 353 /*% 354 * Ask the kernel to allow us to keep our capabilities after we 355 * setuid(). 356 */ 357 358 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { 359 if (errno != EINVAL) { 360 isc__strerror(errno, strbuf, sizeof(strbuf)); 361 ns_main_earlyfatal("prctl() failed: %s", strbuf); 362 } 363 } else { 364 non_root_caps = ISC_TRUE; 365 if (getuid() != 0) 366 non_root = ISC_TRUE; 367 } 368 } 369 #endif 370 371 #endif /* HAVE_LINUX_CAPABILITY_H */ 372 373 374 static void 375 setup_syslog(const char *progname) { 376 int options; 377 378 options = LOG_PID; 379 #ifdef LOG_NDELAY 380 options |= LOG_NDELAY; 381 #endif 382 openlog(isc_file_basename(progname), options, ISC_FACILITY); 383 } 384 385 void 386 ns_os_init(const char *progname) { 387 setup_syslog(progname); 388 #ifdef HAVE_LINUX_CAPABILITY_H 389 linux_initialprivs(); 390 #endif 391 #ifdef HAVE_LINUXTHREADS 392 mainpid = getpid(); 393 #endif 394 #ifdef SIGXFSZ 395 signal(SIGXFSZ, SIG_IGN); 396 #endif 397 } 398 399 void 400 ns_os_daemonize(void) { 401 pid_t pid; 402 char strbuf[ISC_STRERRORSIZE]; 403 404 if (pipe(dfd) == -1) { 405 isc__strerror(errno, strbuf, sizeof(strbuf)); 406 ns_main_earlyfatal("pipe(): %s", strbuf); 407 } 408 409 pid = fork(); 410 if (pid == -1) { 411 isc__strerror(errno, strbuf, sizeof(strbuf)); 412 ns_main_earlyfatal("fork(): %s", strbuf); 413 } 414 if (pid != 0) { 415 int n; 416 /* 417 * Wait for the child to finish loading for the first time. 418 * This would be so much simpler if fork() worked once we 419 * were multi-threaded. 420 */ 421 (void)close(dfd[1]); 422 do { 423 char buf; 424 n = read(dfd[0], &buf, 1); 425 if (n == 1) 426 _exit(0); 427 } while (n == -1 && errno == EINTR); 428 _exit(1); 429 } 430 (void)close(dfd[0]); 431 432 /* 433 * We're the child. 434 */ 435 436 #ifdef HAVE_LINUXTHREADS 437 mainpid = getpid(); 438 #endif 439 440 if (setsid() == -1) { 441 isc__strerror(errno, strbuf, sizeof(strbuf)); 442 ns_main_earlyfatal("setsid(): %s", strbuf); 443 } 444 445 /* 446 * Try to set stdin, stdout, and stderr to /dev/null, but press 447 * on even if it fails. 448 * 449 * XXXMLG The close() calls here are unneeded on all but NetBSD, but 450 * are harmless to include everywhere. dup2() is supposed to close 451 * the FD if it is in use, but unproven-pthreads-0.16 is broken 452 * and will end up closing the wrong FD. This will be fixed eventually, 453 * and these calls will be removed. 454 */ 455 if (devnullfd != -1) { 456 if (devnullfd != STDIN_FILENO) { 457 (void)close(STDIN_FILENO); 458 (void)dup2(devnullfd, STDIN_FILENO); 459 } 460 if (devnullfd != STDOUT_FILENO) { 461 (void)close(STDOUT_FILENO); 462 (void)dup2(devnullfd, STDOUT_FILENO); 463 } 464 if (devnullfd != STDERR_FILENO) { 465 (void)close(STDERR_FILENO); 466 (void)dup2(devnullfd, STDERR_FILENO); 467 } 468 } 469 } 470 471 void 472 ns_os_started(void) { 473 char buf = 0; 474 475 /* 476 * Signal to the parent that we started successfully. 477 */ 478 if (dfd[0] != -1 && dfd[1] != -1) { 479 if (write(dfd[1], &buf, 1) != 1) 480 ns_main_earlyfatal("unable to signal parent that we " 481 "otherwise started successfully."); 482 close(dfd[1]); 483 dfd[0] = dfd[1] = -1; 484 } 485 } 486 487 void 488 ns_os_opendevnull(void) { 489 devnullfd = open("/dev/null", O_RDWR, 0); 490 } 491 492 void 493 ns_os_closedevnull(void) { 494 if (devnullfd != STDIN_FILENO && 495 devnullfd != STDOUT_FILENO && 496 devnullfd != STDERR_FILENO) { 497 close(devnullfd); 498 devnullfd = -1; 499 } 500 } 501 502 static isc_boolean_t 503 all_digits(const char *s) { 504 if (*s == '\0') 505 return (ISC_FALSE); 506 while (*s != '\0') { 507 if (!isdigit((*s)&0xff)) 508 return (ISC_FALSE); 509 s++; 510 } 511 return (ISC_TRUE); 512 } 513 514 void 515 ns_os_chroot(const char *root) { 516 char strbuf[ISC_STRERRORSIZE]; 517 #ifdef HAVE_LIBSCF 518 ns_smf_chroot = 0; 519 #endif 520 if (root != NULL) { 521 #ifdef HAVE_CHROOT 522 if (chroot(root) < 0) { 523 isc__strerror(errno, strbuf, sizeof(strbuf)); 524 ns_main_earlyfatal("chroot(): %s", strbuf); 525 } 526 #else 527 ns_main_earlyfatal("chroot(): disabled"); 528 #endif 529 if (chdir("/") < 0) { 530 isc__strerror(errno, strbuf, sizeof(strbuf)); 531 ns_main_earlyfatal("chdir(/): %s", strbuf); 532 } 533 #ifdef HAVE_LIBSCF 534 /* Set ns_smf_chroot flag on successful chroot. */ 535 ns_smf_chroot = 1; 536 #endif 537 } 538 } 539 540 void 541 ns_os_inituserinfo(const char *username) { 542 char strbuf[ISC_STRERRORSIZE]; 543 if (username == NULL) 544 return; 545 546 if (all_digits(username)) 547 runas_pw = getpwuid((uid_t)atoi(username)); 548 else 549 runas_pw = getpwnam(username); 550 endpwent(); 551 552 if (runas_pw == NULL) 553 ns_main_earlyfatal("user '%s' unknown", username); 554 555 if (getuid() == 0) { 556 if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) { 557 isc__strerror(errno, strbuf, sizeof(strbuf)); 558 ns_main_earlyfatal("initgroups(): %s", strbuf); 559 } 560 } 561 562 } 563 564 void 565 ns_os_changeuser(void) { 566 char strbuf[ISC_STRERRORSIZE]; 567 if (runas_pw == NULL || done_setuid) 568 return; 569 570 done_setuid = ISC_TRUE; 571 572 #ifdef HAVE_LINUXTHREADS 573 #ifdef HAVE_LINUX_CAPABILITY_H 574 if (!non_root_caps) 575 ns_main_earlyfatal("-u with Linux threads not supported: " 576 "requires kernel support for " 577 "prctl(PR_SET_KEEPCAPS)"); 578 #else 579 ns_main_earlyfatal("-u with Linux threads not supported: " 580 "no capabilities support or capabilities " 581 "disabled at build time"); 582 #endif 583 #endif 584 585 if (setgid(runas_pw->pw_gid) < 0) { 586 isc__strerror(errno, strbuf, sizeof(strbuf)); 587 ns_main_earlyfatal("setgid(): %s", strbuf); 588 } 589 590 if (setuid(runas_pw->pw_uid) < 0) { 591 isc__strerror(errno, strbuf, sizeof(strbuf)); 592 ns_main_earlyfatal("setuid(): %s", strbuf); 593 } 594 595 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) 596 /* 597 * Restore the ability of named to drop core after the setuid() 598 * call has disabled it. 599 */ 600 if (prctl(PR_SET_DUMPABLE,1,0,0,0) < 0) { 601 isc__strerror(errno, strbuf, sizeof(strbuf)); 602 ns_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s", 603 strbuf); 604 } 605 #endif 606 #if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS) 607 linux_minprivs(); 608 #endif 609 } 610 611 void 612 ns_os_adjustnofile(void) { 613 #ifdef HAVE_LINUXTHREADS 614 isc_result_t result; 615 isc_resourcevalue_t newvalue; 616 617 /* 618 * Linux: max number of open files specified by one thread doesn't seem 619 * to apply to other threads on Linux. 620 */ 621 newvalue = ISC_RESOURCE_UNLIMITED; 622 623 result = isc_resource_setlimit(isc_resource_openfiles, newvalue); 624 if (result != ISC_R_SUCCESS) 625 ns_main_earlywarning("couldn't adjust limit on open files"); 626 #endif 627 } 628 629 void 630 ns_os_minprivs(void) { 631 #ifdef HAVE_SYS_PRCTL_H 632 linux_keepcaps(); 633 #endif 634 635 #ifdef HAVE_LINUXTHREADS 636 ns_os_changeuser(); /* Call setuid() before threads are started */ 637 #endif 638 639 #if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS) 640 linux_minprivs(); 641 #endif 642 } 643 644 static int 645 safe_open(const char *filename, mode_t mode, isc_boolean_t append) { 646 int fd; 647 struct stat sb; 648 649 if (stat(filename, &sb) == -1) { 650 if (errno != ENOENT) 651 return (-1); 652 } else if ((sb.st_mode & S_IFREG) == 0) { 653 errno = EOPNOTSUPP; 654 return (-1); 655 } 656 657 if (append) 658 fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, mode); 659 else { 660 if (unlink(filename) < 0 && errno != ENOENT) 661 return (-1); 662 fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, mode); 663 } 664 return (fd); 665 } 666 667 static void 668 cleanup_pidfile(void) { 669 int n; 670 if (pidfile != NULL) { 671 n = unlink(pidfile); 672 if (n == -1 && errno != ENOENT) 673 ns_main_earlywarning("unlink '%s': failed", pidfile); 674 free(pidfile); 675 } 676 pidfile = NULL; 677 } 678 679 static int 680 mkdirpath(char *filename, void (*report)(const char *, ...)) { 681 char *slash = strrchr(filename, '/'); 682 char strbuf[ISC_STRERRORSIZE]; 683 unsigned int mode; 684 685 if (slash != NULL && slash != filename) { 686 struct stat sb; 687 *slash = '\0'; 688 689 if (stat(filename, &sb) == -1) { 690 if (errno != ENOENT) { 691 isc__strerror(errno, strbuf, sizeof(strbuf)); 692 (*report)("couldn't stat '%s': %s", filename, 693 strbuf); 694 goto error; 695 } 696 if (mkdirpath(filename, report) == -1) 697 goto error; 698 /* 699 * Handle "//", "/./" and "/../" in path. 700 */ 701 if (!strcmp(slash + 1, "") || 702 !strcmp(slash + 1, ".") || 703 !strcmp(slash + 1, "..")) { 704 *slash = '/'; 705 return (0); 706 } 707 mode = S_IRUSR | S_IWUSR | S_IXUSR; /* u=rwx */ 708 mode |= S_IRGRP | S_IXGRP; /* g=rx */ 709 mode |= S_IROTH | S_IXOTH; /* o=rx */ 710 if (mkdir(filename, mode) == -1) { 711 isc__strerror(errno, strbuf, sizeof(strbuf)); 712 (*report)("couldn't mkdir '%s': %s", filename, 713 strbuf); 714 goto error; 715 } 716 if (runas_pw != NULL && 717 chown(filename, runas_pw->pw_uid, 718 runas_pw->pw_gid) == -1) { 719 isc__strerror(errno, strbuf, sizeof(strbuf)); 720 (*report)("couldn't chown '%s': %s", filename, 721 strbuf); 722 } 723 } 724 *slash = '/'; 725 } 726 return (0); 727 728 error: 729 *slash = '/'; 730 return (-1); 731 } 732 733 static void 734 setperms(uid_t uid, gid_t gid) { 735 char strbuf[ISC_STRERRORSIZE]; 736 #if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) 737 gid_t oldgid, tmpg; 738 #endif 739 #if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID) 740 uid_t olduid, tmpu; 741 #endif 742 #if defined(HAVE_SETEGID) 743 if (getegid() != gid && setegid(gid) == -1) { 744 isc__strerror(errno, strbuf, sizeof(strbuf)); 745 ns_main_earlywarning("unable to set effective gid to %ld: %s", 746 (long)gid, strbuf); 747 } 748 #elif defined(HAVE_SETRESGID) 749 if (getresgid(&tmpg, &oldgid, &tmpg) == -1 || oldgid != gid) { 750 if (setresgid(-1, gid, -1) == -1) { 751 isc__strerror(errno, strbuf, sizeof(strbuf)); 752 ns_main_earlywarning("unable to set effective " 753 "gid to %d: %s", gid, strbuf); 754 } 755 } 756 #endif 757 758 #if defined(HAVE_SETEUID) 759 if (geteuid() != uid && seteuid(uid) == -1) { 760 isc__strerror(errno, strbuf, sizeof(strbuf)); 761 ns_main_earlywarning("unable to set effective uid to %ld: %s", 762 (long)uid, strbuf); 763 } 764 #elif defined(HAVE_SETRESUID) 765 if (getresuid(&tmpu, &olduid, &tmpu) == -1 || olduid != uid) { 766 if (setresuid(-1, uid, -1) == -1) { 767 isc__strerror(errno, strbuf, sizeof(strbuf)); 768 ns_main_earlywarning("unable to set effective " 769 "uid to %d: %s", uid, strbuf); 770 } 771 } 772 #endif 773 } 774 775 FILE * 776 ns_os_openfile(const char *filename, mode_t mode, isc_boolean_t switch_user) { 777 char strbuf[ISC_STRERRORSIZE], *f; 778 FILE *fp; 779 int fd; 780 781 /* 782 * Make the containing directory if it doesn't exist. 783 */ 784 f = strdup(filename); 785 if (f == NULL) { 786 isc__strerror(errno, strbuf, sizeof(strbuf)); 787 ns_main_earlywarning("couldn't strdup() '%s': %s", 788 filename, strbuf); 789 return (NULL); 790 } 791 if (mkdirpath(f, ns_main_earlywarning) == -1) { 792 free(f); 793 return (NULL); 794 } 795 free(f); 796 797 if (switch_user && runas_pw != NULL) { 798 #ifndef HAVE_LINUXTHREADS 799 gid_t oldgid = getgid(); 800 #endif 801 /* Set UID/GID to the one we'll be running with eventually */ 802 setperms(runas_pw->pw_uid, runas_pw->pw_gid); 803 804 fd = safe_open(filename, mode, ISC_FALSE); 805 806 #ifndef HAVE_LINUXTHREADS 807 /* Restore UID/GID to root */ 808 setperms(0, oldgid); 809 #endif /* HAVE_LINUXTHREADS */ 810 811 if (fd == -1) { 812 #ifndef HAVE_LINUXTHREADS 813 fd = safe_open(filename, mode, ISC_FALSE); 814 if (fd != -1) { 815 ns_main_earlywarning("Required root " 816 "permissions to open " 817 "'%s'.", filename); 818 } else { 819 ns_main_earlywarning("Could not open " 820 "'%s'.", filename); 821 } 822 ns_main_earlywarning("Please check file and " 823 "directory permissions " 824 "or reconfigure the filename."); 825 #else /* HAVE_LINUXTHREADS */ 826 ns_main_earlywarning("Could not open " 827 "'%s'.", filename); 828 ns_main_earlywarning("Please check file and " 829 "directory permissions " 830 "or reconfigure the filename."); 831 #endif /* HAVE_LINUXTHREADS */ 832 } 833 } else { 834 fd = safe_open(filename, mode, ISC_FALSE); 835 } 836 837 if (fd < 0) { 838 isc__strerror(errno, strbuf, sizeof(strbuf)); 839 ns_main_earlywarning("could not open file '%s': %s", 840 filename, strbuf); 841 return (NULL); 842 } 843 844 fp = fdopen(fd, "w"); 845 if (fp == NULL) { 846 isc__strerror(errno, strbuf, sizeof(strbuf)); 847 ns_main_earlywarning("could not fdopen() file '%s': %s", 848 filename, strbuf); 849 } 850 851 return (fp); 852 } 853 854 void 855 ns_os_writepidfile(const char *filename, isc_boolean_t first_time) { 856 FILE *lockfile; 857 pid_t pid; 858 char strbuf[ISC_STRERRORSIZE]; 859 void (*report)(const char *, ...); 860 861 /* 862 * The caller must ensure any required synchronization. 863 */ 864 865 report = first_time ? ns_main_earlyfatal : ns_main_earlywarning; 866 867 cleanup_pidfile(); 868 869 if (filename == NULL) 870 return; 871 872 pidfile = strdup(filename); 873 if (pidfile == NULL) { 874 isc__strerror(errno, strbuf, sizeof(strbuf)); 875 (*report)("couldn't strdup() '%s': %s", filename, strbuf); 876 return; 877 } 878 879 lockfile = ns_os_openfile(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, 880 first_time); 881 if (lockfile == NULL) { 882 cleanup_pidfile(); 883 return; 884 } 885 #ifdef HAVE_LINUXTHREADS 886 pid = mainpid; 887 #else 888 pid = getpid(); 889 #endif 890 if (fprintf(lockfile, "%ld\n", (long)pid) < 0) { 891 (*report)("fprintf() to pid file '%s' failed", filename); 892 (void)fclose(lockfile); 893 cleanup_pidfile(); 894 return; 895 } 896 if (fflush(lockfile) == EOF) { 897 (*report)("fflush() to pid file '%s' failed", filename); 898 (void)fclose(lockfile); 899 cleanup_pidfile(); 900 return; 901 } 902 (void)fclose(lockfile); 903 } 904 905 void 906 ns_os_shutdown(void) { 907 closelog(); 908 cleanup_pidfile(); 909 } 910 911 isc_result_t 912 ns_os_gethostname(char *buf, size_t len) { 913 int n; 914 915 n = gethostname(buf, len); 916 return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE); 917 } 918 919 static char * 920 next_token(char **stringp, const char *delim) { 921 char *res; 922 923 do { 924 res = strsep(stringp, delim); 925 if (res == NULL) 926 break; 927 } while (*res == '\0'); 928 return (res); 929 } 930 931 void 932 ns_os_shutdownmsg(char *command, isc_buffer_t *text) { 933 char *input, *ptr; 934 unsigned int n; 935 pid_t pid; 936 937 input = command; 938 939 /* Skip the command name. */ 940 ptr = next_token(&input, " \t"); 941 if (ptr == NULL) 942 return; 943 944 ptr = next_token(&input, " \t"); 945 if (ptr == NULL) 946 return; 947 948 if (strcmp(ptr, "-p") != 0) 949 return; 950 951 #ifdef HAVE_LINUXTHREADS 952 pid = mainpid; 953 #else 954 pid = getpid(); 955 #endif 956 957 n = snprintf((char *)isc_buffer_used(text), 958 isc_buffer_availablelength(text), 959 "pid: %ld", (long)pid); 960 /* Only send a message if it is complete. */ 961 if (n > 0 && n < isc_buffer_availablelength(text)) 962 isc_buffer_add(text, n); 963 } 964 965 void 966 ns_os_tzset(void) { 967 #ifdef HAVE_TZSET 968 tzset(); 969 #endif 970 } 971