diff --git a/bin/named/main.c b/bin/named/main.c new file mode 100644 index 0000000000..73c2698daf --- /dev/null +++ b/bin/named/main.c @@ -0,0 +1,629 @@ +/* + * Copyright (C) 1999-2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: main.c,v 1.119.2.3 2003/07/25 03:31:41 marka Exp $ */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +/* + * Defining NS_MAIN provides storage declarations (rather than extern) + * for variables in named/globals.h. + */ +#define NS_MAIN 1 + +#include +#include /* Explicit, though named/log.h includes it. */ +#include +#include +#include +#include +#include +#include + +/* + * Include header files for database drivers here. + */ +/* #include "xxdb.h" */ + +static isc_boolean_t want_stats = ISC_FALSE; +static char program_name[ISC_DIR_NAMEMAX] = "named"; +static char absolute_conffile[ISC_DIR_PATHMAX]; +static char saved_command_line[512]; + +void +ns_main_earlywarning(const char *format, ...) { + va_list args; + + va_start(args, format); + if (ns_g_lctx != NULL) { + isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_WARNING, + format, args); + } else { + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + va_end(args); +} + +void +ns_main_earlyfatal(const char *format, ...) { + va_list args; + + va_start(args, format); + if (ns_g_lctx != NULL) { + isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + format, args); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "exiting (due to early fatal error)"); + } else { + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + va_end(args); + + exit(1); +} + +static void +assertion_failed(const char *file, int line, isc_assertiontype_t type, + const char *cond) +{ + /* + * Handle assertion failures. + */ + + if (ns_g_lctx != NULL) { + /* + * Reset the assetion callback in case it is the log + * routines causing the assertion. + */ + isc_assertion_setcallback(NULL); + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "%s:%d: %s(%s) failed", file, line, + isc_assertion_typetotext(type), cond); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "exiting (due to assertion failure)"); + } else { + fprintf(stderr, "%s:%d: %s(%s) failed\n", + file, line, isc_assertion_typetotext(type), cond); + fflush(stderr); + } + + if (ns_g_coreok) + abort(); + exit(1); +} + +static void +library_fatal_error(const char *file, int line, const char *format, + va_list args) ISC_FORMAT_PRINTF(3, 0); + +static void +library_fatal_error(const char *file, int line, const char *format, + va_list args) +{ + /* + * Handle isc_error_fatal() calls from our libraries. + */ + + if (ns_g_lctx != NULL) { + /* + * Reset the error callback in case it is the log + * routines causing the assertion. + */ + isc_error_setfatal(NULL); + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "%s:%d: fatal error:", file, line); + isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + format, args); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "exiting (due to fatal error in library)"); + } else { + fprintf(stderr, "%s:%d: fatal error: ", file, line); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + + if (ns_g_coreok) + abort(); + exit(1); +} + +static void +library_unexpected_error(const char *file, int line, const char *format, + va_list args) ISC_FORMAT_PRINTF(3, 0); + +static void +library_unexpected_error(const char *file, int line, const char *format, + va_list args) +{ + /* + * Handle isc_error_unexpected() calls from our libraries. + */ + + if (ns_g_lctx != NULL) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_ERROR, + "%s:%d: unexpected error:", file, line); + isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_ERROR, + format, args); + } else { + fprintf(stderr, "%s:%d: fatal error: ", file, line); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } +} + +static void +lwresd_usage(void) { + fprintf(stderr, + "usage: lwresd [-c conffile | -C resolvconffile] " + "[-d debuglevel] [-f|-g]\n" + " [-n number_of_cpus] [-p port]" + "[-P listen-port] [-s]\n" + " [-t chrootdir] [-u username] [-i pidfile]\n"); +} + +static void +usage(void) { + if (ns_g_lwresdonly) { + lwresd_usage(); + return; + } + fprintf(stderr, + "usage: named [-c conffile] [-d debuglevel] " + "[-f|-g] [-n number_of_cpus]\n" + " [-p port] [-s] [-t chrootdir] [-u username]\n"); +} + +static void +save_command_line(int argc, char *argv[]) { + int i; + char *src; + char *dst; + char *eob; + const char truncated[] = "..."; + isc_boolean_t quoted = ISC_FALSE; + + dst = saved_command_line; + eob = saved_command_line + sizeof(saved_command_line); + + for (i = 1; i < argc && dst < eob; i++) { + *dst++ = ' '; + + src = argv[i]; + while (*src != '\0' && dst < eob) { + /* + * This won't perfectly produce a shell-independent + * pastable command line in all circumstances, but + * comes close, and for practical purposes will + * nearly always be fine. + */ + if (quoted || isalnum(*src & 0xff) || + *src == '-' || *src == '_' || + *src == '.' || *src == '/') { + *dst++ = *src++; + quoted = ISC_FALSE; + } else { + *dst++ = '\\'; + quoted = ISC_TRUE; + } + } + } + + INSIST(sizeof(saved_command_line) >= sizeof(truncated)); + + if (dst == eob) + strcpy(eob - sizeof(truncated), truncated); + else + *dst = '\0'; +} + +static int +parse_int(char *arg, const char *desc) { + char *endp; + int tmp; + long int ltmp; + + ltmp = strtol(arg, &endp, 10); + tmp = (int) ltmp; + if (*endp != '\0') + ns_main_earlyfatal("%s '%s' must be numeric", desc, arg); + if (tmp < 0 || tmp != ltmp) + ns_main_earlyfatal("%s '%s' out of range", desc, arg); + return (tmp); +} + +static void +parse_command_line(int argc, char *argv[]) { + int ch; + int port; + + save_command_line(argc, argv); + + isc_commandline_errprint = ISC_FALSE; + while ((ch = isc_commandline_parse(argc, argv, + "c:C:d:fgi:ln:N:p:P:st:u:vx:")) != + -1) { + switch (ch) { + case 'c': + ns_g_conffile = isc_commandline_argument; + lwresd_g_conffile = isc_commandline_argument; + if (lwresd_g_useresolvconf) + ns_main_earlyfatal("cannot specify -c and -C"); + ns_g_conffileset = ISC_TRUE; + break; + case 'C': + lwresd_g_resolvconffile = isc_commandline_argument; + if (ns_g_conffileset) + ns_main_earlyfatal("cannot specify -c and -C"); + lwresd_g_useresolvconf = ISC_TRUE; + break; + case 'd': + ns_g_debuglevel = parse_int(isc_commandline_argument, + "debug level"); + break; + case 'f': + ns_g_foreground = ISC_TRUE; + break; + case 'g': + ns_g_foreground = ISC_TRUE; + ns_g_logstderr = ISC_TRUE; + break; + /* XXXBEW -i should be removed */ + case 'i': + lwresd_g_defaultpidfile = isc_commandline_argument; + break; + case 'l': + ns_g_lwresdonly = ISC_TRUE; + break; + case 'N': /* Deprecated. */ + case 'n': + ns_g_cpus = parse_int(isc_commandline_argument, + "number of cpus"); + if (ns_g_cpus == 0) + ns_g_cpus = 1; + break; + case 'p': + port = parse_int(isc_commandline_argument, "port"); + if (port < 1 || port > 65535) + ns_main_earlyfatal("port '%s' out of range", + isc_commandline_argument); + ns_g_port = port; + break; + /* XXXBEW Should -P be removed? */ + case 'P': + port = parse_int(isc_commandline_argument, "port"); + if (port < 1 || port > 65535) + ns_main_earlyfatal("port '%s' out of range", + isc_commandline_argument); + lwresd_g_listenport = port; + break; + case 's': + /* XXXRTH temporary syntax */ + want_stats = ISC_TRUE; + break; + case 't': + /* XXXJAB should we make a copy? */ + ns_g_chrootdir = isc_commandline_argument; + break; + case 'u': + ns_g_username = isc_commandline_argument; + break; + case 'v': + printf("BIND %s\n", ns_g_version); + exit(0); + case '?': + usage(); + ns_main_earlyfatal("unknown option '-%c'", + isc_commandline_option); + default: + ns_main_earlyfatal("parsing options returned %d", ch); + } + } + + argc -= isc_commandline_index; + argv += isc_commandline_index; + + if (argc > 0) { + usage(); + ns_main_earlyfatal("extra command line arguments"); + } + + +} + +static isc_result_t +create_managers(void) { + isc_result_t result; + +#ifdef ISC_PLATFORM_USETHREADS + if (ns_g_cpus == 0) + ns_g_cpus = isc_os_ncpus(); +#else + ns_g_cpus = 1; +#endif + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + ISC_LOG_INFO, "using %u CPU%s", + ns_g_cpus, ns_g_cpus == 1 ? "" : "s"); + result = isc_taskmgr_create(ns_g_mctx, ns_g_cpus, 0, &ns_g_taskmgr); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "ns_taskmgr_create() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + result = isc_timermgr_create(ns_g_mctx, &ns_g_timermgr); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "ns_timermgr_create() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + result = isc_socketmgr_create(ns_g_mctx, &ns_g_socketmgr); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_socketmgr_create() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + result = isc_entropy_create(ns_g_mctx, &ns_g_entropy); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_entropy_create() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + result = isc_hash_create(ns_g_mctx, ns_g_entropy, DNS_NAME_MAXWIRE); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_hash_create() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + +static void +destroy_managers(void) { + ns_lwresd_shutdown(); + + isc_hash_destroy(); + isc_entropy_detach(&ns_g_entropy); + /* + * isc_taskmgr_destroy() will block until all tasks have exited, + */ + isc_taskmgr_destroy(&ns_g_taskmgr); + isc_timermgr_destroy(&ns_g_timermgr); + isc_socketmgr_destroy(&ns_g_socketmgr); +} + +static void +setup(void) { + isc_result_t result; + + /* + * Get the user and group information before changing the root + * directory, so the administrator does not need to keep a copy + * of the user and group databases in the chroot'ed environment. + */ + ns_os_inituserinfo(ns_g_username); + + ns_os_chroot(ns_g_chrootdir); + + /* + * For operating systems which have a capability mechanism, now + * is the time to switch to minimal privs and change our user id. + * On traditional UNIX systems, this call will be a no-op, and we + * will change the user ID after reading the config file the first + * time. (We need to read the config file to know which possibly + * privileged ports to bind() to.) + */ + ns_os_minprivs(); + + result = ns_log_init(ISC_TF(ns_g_username != NULL)); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("ns_log_init() failed: %s", + isc_result_totext(result)); + + /* + * Now is the time to daemonize (if we're not running in the + * foreground). We waited until now because we wanted to get + * a valid logging context setup. We cannot daemonize any later, + * because calling create_managers() will create threads, which + * would be lost after fork(). + */ + if (!ns_g_foreground) + ns_os_daemonize(); + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN, + ISC_LOG_NOTICE, "starting BIND %s%s", ns_g_version, + saved_command_line); + + /* + * Get the initial resource limits. + */ + (void)isc_resource_getlimit(isc_resource_stacksize, + &ns_g_initstacksize); + (void)isc_resource_getlimit(isc_resource_datasize, + &ns_g_initdatasize); + (void)isc_resource_getlimit(isc_resource_coresize, + &ns_g_initcoresize); + (void)isc_resource_getlimit(isc_resource_openfiles, + &ns_g_initopenfiles); + + /* + * If the named configuration filename is relative, prepend the current + * directory's name before possibly changing to another directory. + */ + if (! isc_file_isabsolute(ns_g_conffile)) { + result = isc_file_absolutepath(ns_g_conffile, + absolute_conffile, + sizeof(absolute_conffile)); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("could not construct absolute path of " + "configuration file: %s", + isc_result_totext(result)); + ns_g_conffile = absolute_conffile; + } + + result = create_managers(); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("create_managers() failed: %s", + isc_result_totext(result)); + + /* + * Add calls to register sdb drivers here. + */ + /* xxdb_init(); */ + + ns_server_create(ns_g_mctx, &ns_g_server); +} + +static void +cleanup(void) { + destroy_managers(); + + ns_server_destroy(&ns_g_server); + + /* + * Add calls to unregister sdb drivers here. + */ + /* xxdb_clear(); */ + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN, + ISC_LOG_NOTICE, "exiting"); + ns_log_shutdown(); +} + +int +main(int argc, char *argv[]) { + isc_result_t result; + + result = isc_file_progname(*argv, program_name, sizeof(program_name)); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("program name too long"); + + if (strcmp(program_name, "lwresd") == 0) + ns_g_lwresdonly = ISC_TRUE; + + isc_assertion_setcallback(assertion_failed); + isc_error_setfatal(library_fatal_error); + isc_error_setunexpected(library_unexpected_error); + + ns_os_init(program_name); + + result = isc_app_start(); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("isc_app_start() failed: %s", + isc_result_totext(result)); + + result = isc_mem_create(0, 0, &ns_g_mctx); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("isc_mem_create() failed: %s", + isc_result_totext(result)); + + dns_result_register(); + dst_result_register(); + isccc_result_register(); + + parse_command_line(argc, argv); + + setup(); + + /* + * Start things running and then wait for a shutdown request + * or reload. + */ + do { + result = isc_app_run(); + + if (result == ISC_R_RELOAD) { + ns_server_reloadwanted(ns_g_server); + } else if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_run(): %s", + isc_result_totext(result)); + /* + * Force exit. + */ + result = ISC_R_SUCCESS; + } + } while (result != ISC_R_SUCCESS); + + cleanup(); + + if (want_stats) { + isc_mem_stats(ns_g_mctx, stdout); + isc_mutex_stats(stdout); + } + isc_mem_destroy(&ns_g_mctx); + + isc_app_finish(); + + ns_os_shutdown(); + + return (0); +} diff --git a/bin/named/unix/include/named/os.h b/bin/named/unix/include/named/os.h new file mode 100644 index 0000000000..3850f6c009 --- /dev/null +++ b/bin/named/unix/include/named/os.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999-2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: os.h,v 1.14.2.2 2002/08/05 06:57:03 marka Exp $ */ + +#ifndef NS_OS_H +#define NS_OS_H 1 + +#include + +void +ns_os_init(const char *progname); + +void +ns_os_daemonize(void); + +void +ns_os_chroot(const char *root); + +void +ns_os_inituserinfo(const char *username); + +void +ns_os_changeuser(void); + +void +ns_os_minprivs(void); + +void +ns_os_writepidfile(const char *filename, isc_boolean_t first_time); + +void +ns_os_shutdown(void); + +#endif /* NS_OS_H */ diff --git a/bin/named/unix/os.c b/bin/named/unix/os.c new file mode 100644 index 0000000000..bde3edf31c --- /dev/null +++ b/bin/named/unix/os.c @@ -0,0 +1,539 @@ +/* + * Copyright (C) 1999-2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: os.c,v 1.46.2.4 2002/08/05 06:57:03 marka Exp $ */ + +#include +#include + +#include /* dev_t FreeBSD 2.1 */ +#include + +#include +#include +#include +#include /* Required for initgroups() on IRIX. */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +static char *pidfile = NULL; + +/* + * If there's no , we don't care about + */ +#ifndef HAVE_LINUX_CAPABILITY_H +#undef HAVE_SYS_PRCTL_H +#endif + +/* + * Linux defines: + * (T) HAVE_LINUXTHREADS + * (C) HAVE_LINUX_CAPABILITY_H + * (P) HAVE_SYS_PRCTL_H + * The possible cases are: + * none: setuid() normally + * T: no setuid() + * C: setuid() normally, drop caps (keep CAP_SETUID) + * T+C: no setuid(), drop caps (don't keep CAP_SETUID) + * T+C+P: setuid() early, drop caps (keep CAP_SETUID) + * C+P: setuid() normally, drop caps (keep CAP_SETUID) + * P: not possible + * T+P: not possible + * + * if (C) + * caps = BIND_SERVICE + CHROOT + SETGID + * if ((T && C && P) || !T) + * caps += SETUID + * endif + * capset(caps) + * endif + * if (T && C && P && -u) + * setuid() + * else if (T && -u) + * fail + * --> start threads + * if (!T && -u) + * setuid() + * if (C && (P || !-u)) + * caps = BIND_SERVICE + * capset(caps) + * endif + * + * It will be nice when Linux threads work properly with setuid(). + */ + +#ifdef HAVE_LINUXTHREADS +static pid_t mainpid = 0; +#endif + +static struct passwd *runas_pw = NULL; +static isc_boolean_t done_setuid = ISC_FALSE; + +#ifdef HAVE_LINUX_CAPABILITY_H + +static isc_boolean_t non_root = ISC_FALSE; +static isc_boolean_t non_root_caps = ISC_FALSE; + +/* + * We define _LINUX_FS_H to prevent it from being included. We don't need + * anything from it, and the files it includes cause warnings with 2.2 + * kernels, and compilation failures (due to conflicts between + * and ) on 2.3 kernels. + */ +#define _LINUX_FS_H + +#include /* Required for syscall(). */ +#include /* Required for _LINUX_CAPABILITY_VERSION. */ + +#ifdef HAVE_SYS_PRCTL_H +#include /* Required for prctl(). */ + +/* + * If the value of PR_SET_KEEPCAPS is not in , define it + * here. This allows setuid() to work on systems running a new enough + * kernel but with /usr/include/linux pointing to "standard" kernel + * headers. + */ +#ifndef PR_SET_KEEPCAPS +#define PR_SET_KEEPCAPS 8 +#endif + +#endif /* HAVE_SYS_PRCTL_H */ + +#ifndef SYS_capset +#ifndef __NR_capset +#include /* Slackware 4.0 needs this. */ +#endif +#define SYS_capset __NR_capset +#endif + +static void +linux_setcaps(unsigned int caps) { + struct __user_cap_header_struct caphead; + struct __user_cap_data_struct cap; + char strbuf[ISC_STRERRORSIZE]; + + if ((getuid() != 0 && !non_root_caps) || non_root) + return; + + memset(&caphead, 0, sizeof caphead); + caphead.version = _LINUX_CAPABILITY_VERSION; + caphead.pid = 0; + memset(&cap, 0, sizeof cap); + cap.effective = caps; + cap.permitted = caps; + cap.inheritable = caps; + if (syscall(SYS_capset, &caphead, &cap) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlyfatal("capset failed: %s", strbuf); + } +} + +static void +linux_initialprivs(void) { + unsigned int caps; + + /* + * We don't need most privileges, so we drop them right away. + * Later on linux_minprivs() will be called, which will drop our + * capabilities to the minimum needed to run the server. + */ + + caps = 0; + + /* + * We need to be able to bind() to privileged ports, notably port 53! + */ + caps |= (1 << CAP_NET_BIND_SERVICE); + + /* + * We need chroot() initially too. + */ + caps |= (1 << CAP_SYS_CHROOT); + +#if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS) + /* + * We can setuid() only if either the kernel supports keeping + * capabilities after setuid() (which we don't know until we've + * tried) or we're not using threads. If either of these is + * true, we want the setuid capability. + */ + caps |= (1 << CAP_SETUID); +#endif + + /* + * Since we call initgroups, we need this. + */ + caps |= (1 << CAP_SETGID); + + /* + * Without this, we run into problems reading a configuration file + * owned by a non-root user and non-world-readable on startup. + */ + caps |= (1 << CAP_DAC_READ_SEARCH); + + /* + * XXX We might want to add CAP_SYS_RESOURCE, though it's not + * clear it would work right given the way linuxthreads work. + * XXXDCL But since we need to be able to set the maximum number + * of files, the stack size, data size, and core dump size to + * support named.conf options, this is now being added to test. + */ + caps |= (1 << CAP_SYS_RESOURCE); + + linux_setcaps(caps); +} + +static void +linux_minprivs(void) { + unsigned int caps; + + /* + * Drop all privileges except the ability to bind() to privileged + * ports. + * + * It's important that we drop CAP_SYS_CHROOT. If we didn't, it + * chroot() could be used to escape from the chrooted area. + */ + + caps = 0; + caps |= (1 << CAP_NET_BIND_SERVICE); + + /* + * XXX We might want to add CAP_SYS_RESOURCE, though it's not + * clear it would work right given the way linuxthreads work. + * XXXDCL But since we need to be able to set the maximum number + * of files, the stack size, data size, and core dump size to + * support named.conf options, this is now being added to test. + */ + caps |= (1 << CAP_SYS_RESOURCE); + + linux_setcaps(caps); +} + +#ifdef HAVE_SYS_PRCTL_H +static void +linux_keepcaps(void) { + char strbuf[ISC_STRERRORSIZE]; + /* + * Ask the kernel to allow us to keep our capabilities after we + * setuid(). + */ + + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { + if (errno != EINVAL) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlyfatal("prctl() failed: %s", strbuf); + } + } else { + non_root_caps = ISC_TRUE; + if (getuid() != 0) + non_root = ISC_TRUE; + } +} +#endif + +#endif /* HAVE_LINUX_CAPABILITY_H */ + + +static void +setup_syslog(const char *progname) { + int options; + + options = LOG_PID; +#ifdef LOG_NDELAY + options |= LOG_NDELAY; +#endif + + openlog(isc_file_basename(progname), options, LOG_DAEMON); +} + +void +ns_os_init(const char *progname) { + setup_syslog(progname); +#ifdef HAVE_LINUX_CAPABILITY_H + linux_initialprivs(); +#endif +#ifdef HAVE_LINUXTHREADS + mainpid = getpid(); +#endif +} + +void +ns_os_daemonize(void) { + pid_t pid; + int fd; + char strbuf[ISC_STRERRORSIZE]; + + pid = fork(); + if (pid == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlyfatal("fork(): %s", strbuf); + } + if (pid != 0) + _exit(0); + + /* + * We're the child. + */ + +#ifdef HAVE_LINUXTHREADS + mainpid = getpid(); +#endif + + if (setsid() == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlyfatal("setsid(): %s", strbuf); + } + + /* + * Try to set stdin, stdout, and stderr to /dev/null, but press + * on even if it fails. + * + * XXXMLG The close() calls here are unneeded on all but NetBSD, but + * are harmless to include everywhere. dup2() is supposed to close + * the FD if it is in use, but unproven-pthreads-0.16 is broken + * and will end up closing the wrong FD. This will be fixed eventually, + * and these calls will be removed. + */ + fd = open("/dev/null", O_RDWR, 0); + if (fd != -1) { + close(STDIN_FILENO); + (void)dup2(fd, STDIN_FILENO); + close(STDOUT_FILENO); + (void)dup2(fd, STDOUT_FILENO); + close(STDERR_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd != STDIN_FILENO && + fd != STDOUT_FILENO && + fd != STDERR_FILENO) + (void)close(fd); + } +} + +static isc_boolean_t +all_digits(const char *s) { + if (*s == '\0') + return (ISC_FALSE); + while (*s != '\0') { + if (!isdigit((*s)&0xff)) + return (ISC_FALSE); + s++; + } + return (ISC_TRUE); +} + +void +ns_os_chroot(const char *root) { + char strbuf[ISC_STRERRORSIZE]; + if (root != NULL) { + if (chroot(root) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlyfatal("chroot(): %s", strbuf); + } + if (chdir("/") < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlyfatal("chdir(/): %s", strbuf); + } + } +} + +void +ns_os_inituserinfo(const char *username) { + char strbuf[ISC_STRERRORSIZE]; + if (username == NULL) + return; + + if (all_digits(username)) + runas_pw = getpwuid((uid_t)atoi(username)); + else + runas_pw = getpwnam(username); + endpwent(); + + if (runas_pw == NULL) + ns_main_earlyfatal("user '%s' unknown", username); + + if (getuid() == 0) { + if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlyfatal("initgroups(): %s", strbuf); + } + } + +} + +void +ns_os_changeuser(void) { + char strbuf[ISC_STRERRORSIZE]; + if (runas_pw == NULL || done_setuid) + return; + + done_setuid = ISC_TRUE; + +#ifdef HAVE_LINUXTHREADS +#ifdef HAVE_LINUX_CAPABILITY_H + if (!non_root_caps) +#endif + ns_main_earlyfatal( + "-u not supported on Linux kernels older than " + "2.3.99-pre3 or 2.2.18 when using threads"); +#endif + + if (setgid(runas_pw->pw_gid) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlyfatal("setgid(): %s", strbuf); + } + + if (setuid(runas_pw->pw_uid) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlyfatal("setuid(): %s", strbuf); + } + +#if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS) + linux_minprivs(); +#endif +} + +void +ns_os_minprivs(void) { +#ifdef HAVE_SYS_PRCTL_H + linux_keepcaps(); +#endif + +#ifdef HAVE_LINUXTHREADS + ns_os_changeuser(); /* Call setuid() before threads are started */ +#endif + +#if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS) + linux_minprivs(); +#endif +} + +static int +safe_open(const char *filename, isc_boolean_t append) { + int fd; + struct stat sb; + + if (stat(filename, &sb) == -1) { + if (errno != ENOENT) + return (-1); + } else if ((sb.st_mode & S_IFREG) == 0) { + errno = EOPNOTSUPP; + return (-1); + } + + if (append) + fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + else { + (void)unlink(filename); + fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + } + return (fd); +} + +static void +cleanup_pidfile(void) { + if (pidfile != NULL) { + (void)unlink(pidfile); + free(pidfile); + } + pidfile = NULL; +} + +void +ns_os_writepidfile(const char *filename, isc_boolean_t first_time) { + int fd; + FILE *lockfile; + size_t len; + pid_t pid; + char strbuf[ISC_STRERRORSIZE]; + void (*report)(const char *, ...); + + /* + * The caller must ensure any required synchronization. + */ + + report = first_time ? ns_main_earlyfatal : ns_main_earlywarning; + + cleanup_pidfile(); + + len = strlen(filename); + pidfile = malloc(len + 1); + if (pidfile == NULL) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + (*report)("couldn't malloc '%s': %s", filename, strbuf); + return; + } + /* This is safe. */ + strcpy(pidfile, filename); + + fd = safe_open(filename, ISC_FALSE); + if (fd < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + (*report)("couldn't open pid file '%s': %s", filename, strbuf); + free(pidfile); + pidfile = NULL; + return; + } + lockfile = fdopen(fd, "w"); + if (lockfile == NULL) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + (*report)("could not fdopen() pid file '%s': %s", + filename, strbuf); + (void)close(fd); + cleanup_pidfile(); + return; + } +#ifdef HAVE_LINUXTHREADS + pid = mainpid; +#else + pid = getpid(); +#endif + if (fprintf(lockfile, "%ld\n", (long)pid) < 0) { + (*report)("fprintf() to pid file '%s' failed", filename); + (void)fclose(lockfile); + cleanup_pidfile(); + return; + } + if (fflush(lockfile) == EOF) { + (*report)("fflush() to pid file '%s' failed", filename); + (void)fclose(lockfile); + cleanup_pidfile(); + return; + } + (void)fclose(lockfile); +} + +void +ns_os_shutdown(void) { + closelog(); + cleanup_pidfile(); +} diff --git a/bin/named/win32/include/named/os.h b/bin/named/win32/include/named/os.h new file mode 100644 index 0000000000..531e9128fc --- /dev/null +++ b/bin/named/win32/include/named/os.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999-2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: os.h,v 1.1.2.2 2002/08/05 06:57:04 marka Exp $ */ + +#ifndef NS_OS_H +#define NS_OS_H 1 + +#include + +void +ns_os_init(const char *progname); + +void +ns_os_daemonize(void); + +void +ns_os_chroot(const char *root); + +void +ns_os_inituserinfo(const char *username); + +void +ns_os_changeuser(void); + +void +ns_os_minprivs(void); + +void +ns_os_writepidfile(const char *filename, isc_boolean_t first_time); + +void +ns_os_shutdown(void); + +#endif /* NS_OS_H */ diff --git a/bin/named/win32/os.c b/bin/named/win32/os.c new file mode 100644 index 0000000000..57587aae14 --- /dev/null +++ b/bin/named/win32/os.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 1999-2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: os.c,v 1.5.2.3 2002/08/08 19:15:19 mayer Exp $ */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +//#include +#include + +#include +#include +#include +#include + + +static char *pidfile = NULL; + +static BOOL Initialized = FALSE; + +void +ns_paths_init() { + if (!Initialized) + isc_ntpaths_init(); + + ns_g_conffile = isc_ntpaths_get(NAMED_CONF_PATH); + lwresd_g_conffile = isc_ntpaths_get(LWRES_CONF_PATH); + lwresd_g_resolvconffile = isc_ntpaths_get(RESOLV_CONF_PATH); + ns_g_conffile = isc_ntpaths_get(NAMED_CONF_PATH); + ns_g_defaultpidfile = isc_ntpaths_get(NAMED_PID_PATH); + lwresd_g_defaultpidfile = isc_ntpaths_get(LWRESD_PID_PATH); + ns_g_keyfile = isc_ntpaths_get(RNDC_KEY_PATH); + + Initialized = TRUE; +} + + +static void +setup_syslog(const char *progname) { + int options; + + options = LOG_PID; +#ifdef LOG_NDELAY + options |= LOG_NDELAY; +#endif + + openlog(progname, options, LOG_DAEMON); +} + +void +ns_os_init(const char *progname) { + ns_paths_init(); + setup_syslog(progname); + ntservice_init(); +} + +void +ns_os_daemonize(void) { + int fd; + + /* + * Try to set stdin, stdout, and stderr to /dev/null, but press + * on even if it fails. + * + * XXXMLG The close() calls here are unneeded on all but NetBSD, but + * are harmless to include everywhere. dup2() is supposed to close + * the FD if it is in use, but unproven-pthreads-0.16 is broken + * and will end up closing the wrong FD. This will be fixed eventually, + * and these calls will be removed. + */ + fd = open("NUL", O_RDWR, 0); + if (fd != -1) { + close(_fileno(stdin)); + (void)_dup2(fd, _fileno(stdin)); + close(_fileno(stdout)); + (void)_dup2(fd, _fileno(stdout)); + close(_fileno(stderr)); + (void)_dup2(fd, _fileno(stderr)); + if (fd != _fileno(stdin) && + fd != _fileno(stdout) && + fd != _fileno(stderr)) + (void)close(fd); + } +} + +void +ns_os_chroot(const char *root) { +} + +void +ns_os_inituserinfo(const char *username) { +} + +void +ns_os_changeuser(void) { +} + +void +ns_os_minprivs(void) { +} + +static int +safe_open(const char *filename, isc_boolean_t append) { + int fd; + struct stat sb; + + if (stat(filename, &sb) == -1) { + if (errno != ENOENT) + return (-1); + } else if ((sb.st_mode & S_IFREG) == 0) + return (-1); + + if (append) + fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + else { + (void)unlink(filename); + fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + } + return (fd); +} + +static void +cleanup_pidfile(void) { + if (pidfile != NULL) { + (void)unlink(pidfile); + free(pidfile); + } + pidfile = NULL; +} + +void +ns_os_writepidfile(const char *filename, isc_boolean_t first_time) { + int fd; + FILE *lockfile; + size_t len; + pid_t pid; + void (*report)(const char *, ...); + + /* + * The caller must ensure any required synchronization. + */ + + report = first_time ? ns_main_earlyfatal : ns_main_earlywarning; + + cleanup_pidfile(); + + len = strlen(filename); + pidfile = malloc(len + 1); + if (pidfile == NULL) { + (*report)("couldn't malloc '%s': %s", filename, + strerror(errno)); + return; + } + /* This is safe. */ + strcpy(pidfile, filename); + + fd = safe_open(filename, ISC_FALSE); + if (fd < 0) { + (*report)("couldn't open pid file '%s': %s", filename, + strerror(errno)); + free(pidfile); + pidfile = NULL; + return; + } + lockfile = fdopen(fd, "w"); + if (lockfile == NULL) { + (*report)("could not fdopen() pid file '%s': %s", filename, + strerror(errno)); + (void)close(fd); + cleanup_pidfile(); + return; + } + + pid = getpid(); + + if (fprintf(lockfile, "%ld\n", (long)pid) < 0) { + (*report)("fprintf() to pid file '%s' failed", filename); + (void)fclose(lockfile); + cleanup_pidfile(); + return; + } + if (fflush(lockfile) == EOF) { + (*report)("fflush() to pid file '%s' failed", filename); + (void)fclose(lockfile); + cleanup_pidfile(); + return; + } + (void)fclose(lockfile); +} + +void +ns_os_shutdown(void) { + closelog(); + cleanup_pidfile(); + ntservice_shutdown(); /* This MUST be the last thing done */ +}