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