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