1 /* $NetBSD: os.c,v 1.5 2025/07/17 19:01:43 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /*! \file */ 17 #include <stdarg.h> 18 #include <stdbool.h> 19 #include <sys/resource.h> 20 #include <sys/stat.h> 21 #include <sys/types.h> /* dev_t FreeBSD 2.1 */ 22 #ifdef HAVE_UNAME 23 #include <sys/utsname.h> 24 #endif /* ifdef HAVE_UNAME */ 25 26 #include <ctype.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <grp.h> 30 #include <pwd.h> 31 #include <signal.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <syslog.h> 35 #ifdef HAVE_TZSET 36 #include <time.h> 37 #endif /* ifdef HAVE_TZSET */ 38 #include <unistd.h> 39 40 #include <isc/buffer.h> 41 #include <isc/errno.h> 42 #include <isc/file.h> 43 #include <isc/result.h> 44 #include <isc/strerr.h> 45 #include <isc/string.h> 46 #include <isc/util.h> 47 48 #include <named/globals.h> 49 #include <named/log.h> 50 #include <named/main.h> 51 #include <named/os.h> 52 #ifdef HAVE_LIBSCF 53 #include <named/smf_globals.h> 54 #endif /* ifdef HAVE_LIBSCF */ 55 56 static char *pidfile = NULL; 57 static int devnullfd = -1; 58 59 #ifndef ISC_FACILITY 60 #define ISC_FACILITY LOG_DAEMON 61 #endif /* ifndef ISC_FACILITY */ 62 63 static struct passwd *runas_pw = NULL; 64 static bool done_setuid = false; 65 static int dfd[2] = { -1, -1 }; 66 static int notify_fd = -1; 67 68 static uid_t saved_uid = (uid_t)-1; 69 static gid_t saved_gid = (gid_t)-1; 70 71 #if HAVE_LIBCAP 72 73 static bool non_root = false; 74 static bool non_root_caps = false; 75 76 #include <sys/capability.h> 77 #include <sys/prctl.h> 78 79 static void 80 linux_setcaps(cap_t caps) { 81 char strbuf[ISC_STRERRORSIZE]; 82 83 if ((getuid() != 0 && !non_root_caps) || non_root) { 84 return; 85 } 86 if (cap_set_proc(caps) < 0) { 87 strerror_r(errno, strbuf, sizeof(strbuf)); 88 named_main_earlyfatal("cap_set_proc() failed: %s:" 89 " please ensure that the capset kernel" 90 " module is loaded. see insmod(8)", 91 strbuf); 92 } 93 } 94 95 #define SET_CAP(flag) \ 96 do { \ 97 cap_flag_value_t curval; \ 98 capval = (flag); \ 99 err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval); \ 100 if (err != -1 && curval) { \ 101 err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, \ 102 CAP_SET); \ 103 if (err == -1) { \ 104 strerror_r(errno, strbuf, sizeof(strbuf)); \ 105 named_main_earlyfatal("cap_set_proc failed: " \ 106 "%s", \ 107 strbuf); \ 108 } \ 109 \ 110 err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval, \ 111 CAP_SET); \ 112 if (err == -1) { \ 113 strerror_r(errno, strbuf, sizeof(strbuf)); \ 114 named_main_earlyfatal("cap_set_proc failed: " \ 115 "%s", \ 116 strbuf); \ 117 } \ 118 } \ 119 } while (0) 120 #define INIT_CAP \ 121 do { \ 122 caps = cap_init(); \ 123 if (caps == NULL) { \ 124 strerror_r(errno, strbuf, sizeof(strbuf)); \ 125 named_main_earlyfatal("cap_init failed: %s", strbuf); \ 126 } \ 127 curcaps = cap_get_proc(); \ 128 if (curcaps == NULL) { \ 129 strerror_r(errno, strbuf, sizeof(strbuf)); \ 130 named_main_earlyfatal("cap_get_proc failed: %s", \ 131 strbuf); \ 132 } \ 133 } while (0) 134 #define FREE_CAP \ 135 { \ 136 cap_free(caps); \ 137 cap_free(curcaps); \ 138 } \ 139 while (0) 140 141 static void 142 linux_initialprivs(void) { 143 cap_t caps; 144 cap_t curcaps; 145 cap_value_t capval; 146 char strbuf[ISC_STRERRORSIZE]; 147 int err; 148 149 /*% 150 * We don't need most privileges, so we drop them right away. 151 * Later on linux_minprivs() will be called, which will drop our 152 * capabilities to the minimum needed to run the server. 153 */ 154 INIT_CAP; 155 156 /* 157 * We need to be able to bind() to privileged ports, notably port 53! 158 */ 159 SET_CAP(CAP_NET_BIND_SERVICE); 160 161 /* 162 * We need chroot() initially too. 163 */ 164 SET_CAP(CAP_SYS_CHROOT); 165 166 /* 167 * We need setuid() as the kernel supports keeping capabilities after 168 * setuid(). 169 */ 170 SET_CAP(CAP_SETUID); 171 172 /* 173 * Since we call initgroups, we need this. 174 */ 175 SET_CAP(CAP_SETGID); 176 177 /* 178 * Without this, we run into problems reading a configuration file 179 * owned by a non-root user and non-world-readable on startup. 180 */ 181 SET_CAP(CAP_DAC_READ_SEARCH); 182 183 /* 184 * XXX We might want to add CAP_SYS_RESOURCE, though it's not 185 * clear it would work right given the way linuxthreads work. 186 * XXXDCL But since we need to be able to set the maximum number 187 * of files, the stack size, data size, and core dump size to 188 * support named.conf options, this is now being added to test. 189 */ 190 SET_CAP(CAP_SYS_RESOURCE); 191 192 /* 193 * We need to be able to set the ownership of the containing 194 * directory of the pid file when we create it. 195 */ 196 SET_CAP(CAP_CHOWN); 197 198 linux_setcaps(caps); 199 200 FREE_CAP; 201 } 202 203 static void 204 linux_minprivs(void) { 205 cap_t caps; 206 cap_t curcaps; 207 cap_value_t capval; 208 char strbuf[ISC_STRERRORSIZE]; 209 int err; 210 211 INIT_CAP; 212 /*% 213 * Drop all privileges except the ability to bind() to privileged 214 * ports. 215 * 216 * It's important that we drop CAP_SYS_CHROOT. If we didn't, it 217 * chroot() could be used to escape from the chrooted area. 218 */ 219 220 SET_CAP(CAP_NET_BIND_SERVICE); 221 222 /* 223 * XXX We might want to add CAP_SYS_RESOURCE, though it's not 224 * clear it would work right given the way linuxthreads work. 225 * XXXDCL But since we need to be able to set the maximum number 226 * of files, the stack size, data size, and core dump size to 227 * support named.conf options, this is now being added to test. 228 */ 229 SET_CAP(CAP_SYS_RESOURCE); 230 231 linux_setcaps(caps); 232 233 FREE_CAP; 234 } 235 236 static void 237 linux_keepcaps(void) { 238 char strbuf[ISC_STRERRORSIZE]; 239 /*% 240 * Ask the kernel to allow us to keep our capabilities after we 241 * setuid(). 242 */ 243 244 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { 245 if (errno != EINVAL) { 246 strerror_r(errno, strbuf, sizeof(strbuf)); 247 named_main_earlyfatal("prctl() failed: %s", strbuf); 248 } 249 } else { 250 non_root_caps = true; 251 if (getuid() != 0) { 252 non_root = true; 253 } 254 } 255 } 256 257 #endif /* HAVE_LIBCAP */ 258 259 static void 260 setperms(uid_t uid, gid_t gid) { 261 char strbuf[ISC_STRERRORSIZE]; 262 263 /* 264 * Drop the gid privilege first, because in some cases the gid privilege 265 * cannot be dropped after the uid privilege has been dropped. 266 */ 267 if (setegid(gid) == -1) { 268 strerror_r(errno, strbuf, sizeof(strbuf)); 269 named_main_earlywarning("unable to set effective gid to %d: %s", 270 gid, strbuf); 271 } 272 273 if (seteuid(uid) == -1) { 274 strerror_r(errno, strbuf, sizeof(strbuf)); 275 named_main_earlywarning("unable to set effective uid to %d: %s", 276 uid, strbuf); 277 } 278 } 279 280 #ifdef __linux__ 281 static isc_result_t 282 connect_systemd_notify_unix(char *path) { 283 struct sockaddr_un addr; 284 isc_result_t result = ISC_R_SUCCESS; 285 size_t len; 286 287 addr.sun_family = AF_UNIX; 288 289 len = strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); 290 if (len >= sizeof(addr.sun_path)) { 291 return ISC_R_NOSPACE; 292 } 293 294 /* Convert abstract unix sockets */ 295 if (addr.sun_path[0] == '@') { 296 addr.sun_path[0] = '\0'; 297 } 298 299 notify_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); 300 if (notify_fd < 0) { 301 return isc_errno_toresult(errno); 302 } 303 304 if (connect(notify_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 305 result = isc_errno_toresult(errno); 306 close(notify_fd); 307 notify_fd = -1; 308 } 309 310 return result; 311 } 312 313 static void 314 setup_systemd_notify(void) { 315 isc_result_t result; 316 char *path; 317 318 REQUIRE(notify_fd == -1); 319 320 path = getenv("NOTIFY_SOCKET"); 321 322 /* systemd notification is not set, oh well. */ 323 if (path == NULL) { 324 return; 325 } 326 327 if (path[0] == '/' || path[0] == '@') { 328 result = connect_systemd_notify_unix(path); 329 } else if (strncmp(path, "vsock", 5) == 0) { 330 result = ISC_R_FAMILYNOSUPPORT; 331 } else { 332 result = ISC_R_INVALIDPROTO; 333 } 334 335 if (result != ISC_R_SUCCESS) { 336 RUNTIME_CHECK(notify_fd == -1); 337 named_main_earlywarning( 338 "failed to connect to notification socket '%s': %s", 339 path, isc_result_totext(result)); 340 } else { 341 RUNTIME_CHECK(notify_fd != -1); 342 } 343 } 344 #else /* __linux__ */ 345 #define setup_systemd_notify(...) 346 #endif /* __linux__ */ 347 348 static void 349 setup_syslog(const char *progname) { 350 int options; 351 352 options = LOG_PID; 353 #ifdef LOG_NDELAY 354 options |= LOG_NDELAY; 355 #endif /* ifdef LOG_NDELAY */ 356 openlog(isc_file_basename(progname), options, ISC_FACILITY); 357 } 358 359 void 360 named_os_init(const char *progname) { 361 setup_syslog(progname); 362 setup_systemd_notify(); 363 #if HAVE_LIBCAP 364 linux_initialprivs(); 365 #endif /* HAVE_LIBCAP */ 366 #ifdef SIGXFSZ 367 signal(SIGXFSZ, SIG_IGN); 368 #endif /* ifdef SIGXFSZ */ 369 } 370 371 void 372 named_os_daemonize(void) { 373 pid_t pid; 374 char strbuf[ISC_STRERRORSIZE]; 375 376 if (pipe(dfd) == -1) { 377 strerror_r(errno, strbuf, sizeof(strbuf)); 378 named_main_earlyfatal("pipe(): %s", strbuf); 379 } 380 381 pid = fork(); 382 if (pid == -1) { 383 strerror_r(errno, strbuf, sizeof(strbuf)); 384 named_main_earlyfatal("fork(): %s", strbuf); 385 } 386 if (pid != 0) { 387 int n; 388 /* 389 * Wait for the child to finish loading for the first time. 390 * This would be so much simpler if fork() worked once we 391 * were multi-threaded. 392 */ 393 (void)close(dfd[1]); 394 do { 395 char buf; 396 n = read(dfd[0], &buf, 1); 397 if (n == 1) { 398 _exit(EXIT_SUCCESS); 399 } 400 } while (n == -1 && errno == EINTR); 401 _exit(EXIT_FAILURE); 402 } 403 (void)close(dfd[0]); 404 405 /* 406 * We're the child. 407 */ 408 409 if (setsid() == -1) { 410 strerror_r(errno, strbuf, sizeof(strbuf)); 411 named_main_earlyfatal("setsid(): %s", strbuf); 412 } 413 414 /* 415 * Try to set stdin, stdout, and stderr to /dev/null, but press 416 * on even if it fails. 417 * 418 * XXXMLG The close() calls here are unneeded on all but NetBSD, but 419 * are harmless to include everywhere. dup2() is supposed to close 420 * the FD if it is in use, but unproven-pthreads-0.16 is broken 421 * and will end up closing the wrong FD. This will be fixed eventually, 422 * and these calls will be removed. 423 */ 424 if (devnullfd != -1) { 425 if (devnullfd != STDIN_FILENO) { 426 (void)close(STDIN_FILENO); 427 (void)dup2(devnullfd, STDIN_FILENO); 428 } 429 if (devnullfd != STDOUT_FILENO) { 430 (void)close(STDOUT_FILENO); 431 (void)dup2(devnullfd, STDOUT_FILENO); 432 } 433 if (devnullfd != STDERR_FILENO && !named_g_keepstderr) { 434 (void)close(STDERR_FILENO); 435 (void)dup2(devnullfd, STDERR_FILENO); 436 } 437 } 438 439 /* 440 * Will be closed from SOCK_CLOEXEC but should be set to -1 again before 441 * reconnecting to the notification socket. 442 */ 443 notify_fd = -1; 444 setup_systemd_notify(); 445 } 446 447 void 448 named_os_started(void) { 449 char buf = 0; 450 451 /* 452 * Signal to the parent that we started successfully. 453 */ 454 if (dfd[0] != -1 && dfd[1] != -1) { 455 if (write(dfd[1], &buf, 1) != 1) { 456 named_main_earlyfatal("unable to signal parent that we " 457 "otherwise started " 458 "successfully."); 459 } 460 close(dfd[1]); 461 dfd[0] = dfd[1] = -1; 462 } 463 } 464 465 void 466 named_os_opendevnull(void) { 467 devnullfd = open("/dev/null", O_RDWR, 0); 468 } 469 470 void 471 named_os_closedevnull(void) { 472 if (devnullfd != STDIN_FILENO && devnullfd != STDOUT_FILENO && 473 devnullfd != STDERR_FILENO) 474 { 475 close(devnullfd); 476 devnullfd = -1; 477 } 478 } 479 480 static bool 481 all_digits(const char *s) { 482 if (*s == '\0') { 483 return false; 484 } 485 while (*s != '\0') { 486 if (!isdigit((unsigned char)(*s))) { 487 return false; 488 } 489 s++; 490 } 491 return true; 492 } 493 494 void 495 named_os_chroot(const char *root) { 496 char strbuf[ISC_STRERRORSIZE]; 497 #ifdef HAVE_LIBSCF 498 named_smf_chroot = 0; 499 #endif /* ifdef HAVE_LIBSCF */ 500 if (root != NULL) { 501 #ifdef HAVE_CHROOT 502 if (chroot(root) < 0) { 503 strerror_r(errno, strbuf, sizeof(strbuf)); 504 named_main_earlyfatal("chroot(): %s", strbuf); 505 } 506 #else /* ifdef HAVE_CHROOT */ 507 named_main_earlyfatal("chroot(): disabled"); 508 #endif /* ifdef HAVE_CHROOT */ 509 if (chdir("/") < 0) { 510 strerror_r(errno, strbuf, sizeof(strbuf)); 511 named_main_earlyfatal("chdir(/): %s", strbuf); 512 } 513 #ifdef HAVE_LIBSCF 514 /* Set named_smf_chroot flag on successful chroot. */ 515 named_smf_chroot = 1; 516 #endif /* ifdef HAVE_LIBSCF */ 517 } 518 } 519 520 void 521 named_os_inituserinfo(const char *username) { 522 if (username == NULL) { 523 return; 524 } 525 526 if (all_digits(username)) { 527 runas_pw = getpwuid((uid_t)atoi(username)); 528 } else { 529 runas_pw = getpwnam(username); 530 } 531 endpwent(); 532 533 if (runas_pw == NULL) { 534 named_main_earlyfatal("user '%s' unknown", username); 535 } 536 537 if (getuid() == 0) { 538 char strbuf[ISC_STRERRORSIZE]; 539 if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) { 540 strerror_r(errno, strbuf, sizeof(strbuf)); 541 named_main_earlyfatal("initgroups(): %s", strbuf); 542 } 543 } 544 } 545 546 void 547 named_os_restoreuser(void) { 548 if (runas_pw == NULL || done_setuid) { 549 return; 550 } 551 552 REQUIRE(saved_uid != (uid_t)-1); 553 REQUIRE(saved_gid != (gid_t)-1); 554 555 setperms(saved_uid, saved_gid); 556 } 557 558 void 559 named_os_changeuser(bool permanent) { 560 char strbuf[ISC_STRERRORSIZE]; 561 if (runas_pw == NULL || done_setuid) { 562 return; 563 } 564 565 if (!permanent) { 566 saved_uid = getuid(); 567 saved_gid = getgid(); 568 569 setperms(runas_pw->pw_uid, runas_pw->pw_gid); 570 571 return; 572 } 573 574 done_setuid = true; 575 576 if (setgid(runas_pw->pw_gid) == -1) { 577 strerror_r(errno, strbuf, sizeof(strbuf)); 578 named_main_earlyfatal("setgid(): %s", strbuf); 579 } 580 581 if (setuid(runas_pw->pw_uid) == -1) { 582 strerror_r(errno, strbuf, sizeof(strbuf)); 583 named_main_earlyfatal("setuid(): %s", strbuf); 584 } 585 586 #if HAVE_LIBCAP 587 /* 588 * Restore the ability of named to drop core after the setuid() 589 * call has disabled it. 590 */ 591 if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { 592 strerror_r(errno, strbuf, sizeof(strbuf)); 593 named_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s", 594 strbuf); 595 } 596 597 linux_minprivs(); 598 #endif /* HAVE_LIBCAP */ 599 } 600 601 uid_t 602 named_os_uid(void) { 603 if (runas_pw == NULL) { 604 return 0; 605 } 606 return runas_pw->pw_uid; 607 } 608 609 void 610 named_os_adjustnofile(void) { 611 int r; 612 struct rlimit rl; 613 rlim_t rlim_old; 614 char strbuf[ISC_STRERRORSIZE]; 615 616 r = getrlimit(RLIMIT_NOFILE, &rl); 617 if (r != 0) { 618 goto fail; 619 } 620 621 rlim_old = rl.rlim_cur; 622 623 if (rl.rlim_cur == rl.rlim_max) { 624 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 625 NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, 626 "the limit on open files is already at the " 627 "maximum allowed value: " 628 "%" PRIu64, 629 (uint64_t)rl.rlim_max); 630 return; 631 } 632 633 rl.rlim_cur = rl.rlim_max; 634 r = setrlimit(RLIMIT_NOFILE, &rl); 635 if (r != 0) { 636 goto fail; 637 } 638 639 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 640 NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, 641 "adjusted limit on open files from " 642 "%" PRIu64 " to " 643 "%" PRIu64, 644 (uint64_t)rlim_old, (uint64_t)rl.rlim_cur); 645 return; 646 647 fail: 648 strerror_r(errno, strbuf, sizeof(strbuf)); 649 named_main_earlywarning("adjusting limit on open files failed: %s", 650 strbuf); 651 return; 652 } 653 654 void 655 named_os_minprivs(void) { 656 #if HAVE_LIBCAP 657 linux_keepcaps(); 658 named_os_changeuser(true); 659 linux_minprivs(); 660 #endif /* HAVE_LIBCAP */ 661 } 662 663 static int 664 safe_open(const char *filename, mode_t mode, bool append) { 665 int fd; 666 struct stat sb; 667 668 if (stat(filename, &sb) == -1) { 669 if (errno != ENOENT) { 670 return -1; 671 } 672 } else if ((sb.st_mode & S_IFREG) == 0) { 673 errno = EOPNOTSUPP; 674 return -1; 675 } 676 677 if (append) { 678 fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, mode); 679 } else { 680 if (unlink(filename) < 0 && errno != ENOENT) { 681 return -1; 682 } 683 fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, mode); 684 } 685 return fd; 686 } 687 688 static void 689 cleanup_pidfile(void) { 690 int n; 691 if (pidfile != NULL) { 692 n = unlink(pidfile); 693 if (n == -1 && errno != ENOENT) { 694 named_main_earlywarning("unlink '%s': failed", pidfile); 695 } 696 free(pidfile); 697 } 698 pidfile = NULL; 699 } 700 701 /* 702 * Ensure that a directory exists. 703 * NOTE: This function overwrites the '/' characters in 'filename' with 704 * nulls. The caller should copy the filename to a fresh buffer first. 705 */ 706 static int 707 mkdirpath(char *filename, void (*report)(const char *, ...)) { 708 char *slash = strrchr(filename, '/'); 709 char strbuf[ISC_STRERRORSIZE]; 710 unsigned int mode; 711 712 if (slash != NULL && slash != filename) { 713 struct stat sb; 714 *slash = '\0'; 715 716 if (stat(filename, &sb) == -1) { 717 if (errno != ENOENT) { 718 strerror_r(errno, strbuf, sizeof(strbuf)); 719 (*report)("couldn't stat '%s': %s", filename, 720 strbuf); 721 goto error; 722 } 723 if (mkdirpath(filename, report) == -1) { 724 goto error; 725 } 726 /* 727 * Handle "//", "/./" and "/../" in path. 728 */ 729 if (!strcmp(slash + 1, "") || !strcmp(slash + 1, ".") || 730 !strcmp(slash + 1, "..")) 731 { 732 *slash = '/'; 733 return 0; 734 } 735 mode = S_IRUSR | S_IWUSR | S_IXUSR; /* u=rwx */ 736 mode |= S_IRGRP | S_IXGRP; /* g=rx */ 737 mode |= S_IROTH | S_IXOTH; /* o=rx */ 738 if (mkdir(filename, mode) == -1) { 739 strerror_r(errno, strbuf, sizeof(strbuf)); 740 (*report)("couldn't mkdir '%s': %s", filename, 741 strbuf); 742 goto error; 743 } 744 if (runas_pw != NULL && 745 chown(filename, runas_pw->pw_uid, 746 runas_pw->pw_gid) == -1) 747 { 748 strerror_r(errno, strbuf, sizeof(strbuf)); 749 (*report)("couldn't chown '%s': %s", filename, 750 strbuf); 751 } 752 } 753 *slash = '/'; 754 } 755 return 0; 756 757 error: 758 *slash = '/'; 759 return -1; 760 } 761 762 FILE * 763 named_os_openfile(const char *filename, mode_t mode, bool switch_user) { 764 char strbuf[ISC_STRERRORSIZE], *f; 765 FILE *fp; 766 int fd; 767 768 /* 769 * Make the containing directory if it doesn't exist. 770 */ 771 f = strdup(filename); 772 if (f == NULL) { 773 strerror_r(errno, strbuf, sizeof(strbuf)); 774 named_main_earlywarning("couldn't strdup() '%s': %s", filename, 775 strbuf); 776 return NULL; 777 } 778 if (mkdirpath(f, named_main_earlywarning) == -1) { 779 free(f); 780 return NULL; 781 } 782 free(f); 783 784 if (switch_user && runas_pw != NULL) { 785 /* 786 * Temporarily set UID/GID to the one we'll be running with 787 * eventually. 788 */ 789 named_os_changeuser(false); 790 791 fd = safe_open(filename, mode, false); 792 793 /* Restore UID/GID to previous uid/gid */ 794 named_os_restoreuser(); 795 796 if (fd == -1) { 797 fd = safe_open(filename, mode, false); 798 if (fd != -1) { 799 named_main_earlywarning("Required root " 800 "permissions to open " 801 "'%s'.", 802 filename); 803 } else { 804 named_main_earlywarning("Could not open " 805 "'%s'.", 806 filename); 807 } 808 named_main_earlywarning("Please check file and " 809 "directory permissions " 810 "or reconfigure the filename."); 811 } 812 } else { 813 fd = safe_open(filename, mode, false); 814 } 815 816 if (fd < 0) { 817 strerror_r(errno, strbuf, sizeof(strbuf)); 818 named_main_earlywarning("could not open file '%s': %s", 819 filename, strbuf); 820 return NULL; 821 } 822 823 fp = fdopen(fd, "w"); 824 if (fp == NULL) { 825 strerror_r(errno, strbuf, sizeof(strbuf)); 826 named_main_earlywarning("could not fdopen() file '%s': %s", 827 filename, strbuf); 828 } 829 830 return fp; 831 } 832 833 void 834 named_os_writepidfile(const char *filename, bool first_time) { 835 FILE *fh; 836 pid_t pid; 837 char strbuf[ISC_STRERRORSIZE]; 838 void (*report)(const char *, ...); 839 840 /* 841 * The caller must ensure any required synchronization. 842 */ 843 844 report = first_time ? named_main_earlyfatal : named_main_earlywarning; 845 846 cleanup_pidfile(); 847 848 if (filename == NULL) { 849 return; 850 } 851 852 pidfile = strdup(filename); 853 if (pidfile == NULL) { 854 strerror_r(errno, strbuf, sizeof(strbuf)); 855 (*report)("couldn't strdup() '%s': %s", filename, strbuf); 856 return; 857 } 858 859 fh = named_os_openfile(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 860 first_time); 861 if (fh == NULL) { 862 cleanup_pidfile(); 863 return; 864 } 865 pid = getpid(); 866 if (fprintf(fh, "%ld\n", (long)pid) < 0) { 867 (*report)("fprintf() to pid file '%s' failed", filename); 868 (void)fclose(fh); 869 cleanup_pidfile(); 870 return; 871 } 872 if (fflush(fh) == EOF) { 873 (*report)("fflush() to pid file '%s' failed", filename); 874 (void)fclose(fh); 875 cleanup_pidfile(); 876 return; 877 } 878 (void)fclose(fh); 879 } 880 881 void 882 named_os_shutdown(void) { 883 closelog(); 884 cleanup_pidfile(); 885 } 886 887 void 888 named_os_shutdownmsg(char *command, isc_buffer_t *text) { 889 char *last, *ptr; 890 pid_t pid; 891 892 /* Skip the command name. */ 893 if (strtok_r(command, " \t", &last) == NULL) { 894 return; 895 } 896 897 if ((ptr = strtok_r(NULL, " \t", &last)) == NULL) { 898 return; 899 } 900 901 if (strcmp(ptr, "-p") != 0) { 902 return; 903 } 904 905 pid = getpid(); 906 907 (void)isc_buffer_printf(text, "pid: %ld", (long)pid); 908 } 909 910 void 911 named_os_tzset(void) { 912 #ifdef HAVE_TZSET 913 tzset(); 914 #endif /* ifdef HAVE_TZSET */ 915 } 916 917 #ifdef HAVE_UNAME 918 static char unamebuf[sizeof(struct utsname)]; 919 #else 920 static const char unamebuf[] = { "unknown architecture" }; 921 #endif 922 static const char *unamep = NULL; 923 924 static void 925 getuname(void) { 926 #ifdef HAVE_UNAME 927 struct utsname uts; 928 929 memset(&uts, 0, sizeof(uts)); 930 if (uname(&uts) < 0) { 931 snprintf(unamebuf, sizeof(unamebuf), "unknown architecture"); 932 return; 933 } 934 935 snprintf(unamebuf, sizeof(unamebuf), "%s %s %s %s", uts.sysname, 936 uts.machine, uts.release, uts.version); 937 #endif /* ifdef HAVE_UNAME */ 938 unamep = unamebuf; 939 } 940 941 const char * 942 named_os_uname(void) { 943 if (unamep == NULL) { 944 getuname(); 945 } 946 return unamep; 947 } 948 949 #ifdef __linux__ 950 void 951 named_os_notify_systemd(const char *restrict format, ...) { 952 char buffer[512]; 953 va_list ap; 954 int len; 955 956 if (notify_fd == -1 || format == NULL) { 957 return; 958 } 959 960 va_start(ap, format); 961 len = vsnprintf(buffer, sizeof(buffer), format, ap); 962 va_end(ap); 963 964 /* Be very loud if notification is too long */ 965 RUNTIME_CHECK(len > 0 && len < (int)sizeof(buffer)); 966 967 if (write(notify_fd, buffer, len) != len) { 968 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 969 NAMED_LOGMODULE_MAIN, ISC_LOG_ERROR, 970 "failed writing to notification socket '%s'", 971 isc_result_totext(isc_errno_toresult(errno))); 972 } 973 } 974 975 void 976 named_os_notify_close(void) { 977 if (notify_fd != -1) { 978 close(notify_fd); 979 notify_fd = -1; 980 } 981 } 982 #endif /* __linux__ */ 983