diff options
Diffstat (limited to 'contrib/bind9/bin/named/controlconf.c')
-rw-r--r-- | contrib/bind9/bin/named/controlconf.c | 1461 |
1 files changed, 0 insertions, 1461 deletions
diff --git a/contrib/bind9/bin/named/controlconf.c b/contrib/bind9/bin/named/controlconf.c deleted file mode 100644 index 3e36446..0000000 --- a/contrib/bind9/bin/named/controlconf.c +++ /dev/null @@ -1,1461 +0,0 @@ -/* - * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC") - * Copyright (C) 2001-2003 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 ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC 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: controlconf.c,v 1.40.18.10 2006/12/07 04:53:02 marka Exp $ */ - -/*! \file */ - -#include <config.h> - -#include <isc/base64.h> -#include <isc/buffer.h> -#include <isc/event.h> -#include <isc/mem.h> -#include <isc/net.h> -#include <isc/netaddr.h> -#include <isc/random.h> -#include <isc/result.h> -#include <isc/stdtime.h> -#include <isc/string.h> -#include <isc/timer.h> -#include <isc/util.h> - -#include <isccfg/namedconf.h> - -#include <bind9/check.h> - -#include <isccc/alist.h> -#include <isccc/cc.h> -#include <isccc/ccmsg.h> -#include <isccc/events.h> -#include <isccc/result.h> -#include <isccc/sexpr.h> -#include <isccc/symtab.h> -#include <isccc/util.h> - -#include <dns/result.h> - -#include <named/config.h> -#include <named/control.h> -#include <named/log.h> -#include <named/server.h> - -/* - * Note: Listeners and connections are not locked. All event handlers are - * executed by the server task, and all callers of exported routines must - * be running under the server task. - */ - -typedef struct controlkey controlkey_t; -typedef ISC_LIST(controlkey_t) controlkeylist_t; - -typedef struct controlconnection controlconnection_t; -typedef ISC_LIST(controlconnection_t) controlconnectionlist_t; - -typedef struct controllistener controllistener_t; -typedef ISC_LIST(controllistener_t) controllistenerlist_t; - -struct controlkey { - char * keyname; - isc_region_t secret; - ISC_LINK(controlkey_t) link; -}; - -struct controlconnection { - isc_socket_t * sock; - isccc_ccmsg_t ccmsg; - isc_boolean_t ccmsg_valid; - isc_boolean_t sending; - isc_timer_t * timer; - unsigned char buffer[2048]; - controllistener_t * listener; - isc_uint32_t nonce; - ISC_LINK(controlconnection_t) link; -}; - -struct controllistener { - ns_controls_t * controls; - isc_mem_t * mctx; - isc_task_t * task; - isc_sockaddr_t address; - isc_socket_t * sock; - dns_acl_t * acl; - isc_boolean_t listening; - isc_boolean_t exiting; - controlkeylist_t keys; - controlconnectionlist_t connections; - isc_sockettype_t type; - isc_uint32_t perm; - isc_uint32_t owner; - isc_uint32_t group; - ISC_LINK(controllistener_t) link; -}; - -struct ns_controls { - ns_server_t *server; - controllistenerlist_t listeners; - isc_boolean_t shuttingdown; - isccc_symtab_t *symtab; -}; - -static void control_newconn(isc_task_t *task, isc_event_t *event); -static void control_recvmessage(isc_task_t *task, isc_event_t *event); - -#define CLOCKSKEW 300 - -static void -free_controlkey(controlkey_t *key, isc_mem_t *mctx) { - if (key->keyname != NULL) - isc_mem_free(mctx, key->keyname); - if (key->secret.base != NULL) - isc_mem_put(mctx, key->secret.base, key->secret.length); - isc_mem_put(mctx, key, sizeof(*key)); -} - -static void -free_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) { - while (!ISC_LIST_EMPTY(*keylist)) { - controlkey_t *key = ISC_LIST_HEAD(*keylist); - ISC_LIST_UNLINK(*keylist, key, link); - free_controlkey(key, mctx); - } -} - -static void -free_listener(controllistener_t *listener) { - INSIST(listener->exiting); - INSIST(!listener->listening); - INSIST(ISC_LIST_EMPTY(listener->connections)); - - if (listener->sock != NULL) - isc_socket_detach(&listener->sock); - - free_controlkeylist(&listener->keys, listener->mctx); - - if (listener->acl != NULL) - dns_acl_detach(&listener->acl); - - isc_mem_put(listener->mctx, listener, sizeof(*listener)); -} - -static void -maybe_free_listener(controllistener_t *listener) { - if (listener->exiting && - !listener->listening && - ISC_LIST_EMPTY(listener->connections)) - free_listener(listener); -} - -static void -maybe_free_connection(controlconnection_t *conn) { - controllistener_t *listener = conn->listener; - - if (conn->timer != NULL) - isc_timer_detach(&conn->timer); - - if (conn->ccmsg_valid) { - isccc_ccmsg_cancelread(&conn->ccmsg); - return; - } - - if (conn->sending) { - isc_socket_cancel(conn->sock, listener->task, - ISC_SOCKCANCEL_SEND); - return; - } - - ISC_LIST_UNLINK(listener->connections, conn, link); - isc_mem_put(listener->mctx, conn, sizeof(*conn)); -} - -static void -shutdown_listener(controllistener_t *listener) { - controlconnection_t *conn; - controlconnection_t *next; - - if (!listener->exiting) { - char socktext[ISC_SOCKADDR_FORMATSIZE]; - - ISC_LIST_UNLINK(listener->controls->listeners, listener, link); - - isc_sockaddr_format(&listener->address, socktext, - sizeof(socktext)); - isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, - "stopping command channel on %s", socktext); - if (listener->type == isc_sockettype_unix) - isc_socket_cleanunix(&listener->address, ISC_TRUE); - listener->exiting = ISC_TRUE; - } - - for (conn = ISC_LIST_HEAD(listener->connections); - conn != NULL; - conn = next) - { - next = ISC_LIST_NEXT(conn, link); - maybe_free_connection(conn); - } - - if (listener->listening) - isc_socket_cancel(listener->sock, listener->task, - ISC_SOCKCANCEL_ACCEPT); - - maybe_free_listener(listener); -} - -static isc_boolean_t -address_ok(isc_sockaddr_t *sockaddr, dns_acl_t *acl) { - isc_netaddr_t netaddr; - isc_result_t result; - int match; - - isc_netaddr_fromsockaddr(&netaddr, sockaddr); - - result = dns_acl_match(&netaddr, NULL, acl, - &ns_g_server->aclenv, &match, NULL); - - if (result != ISC_R_SUCCESS || match <= 0) - return (ISC_FALSE); - else - return (ISC_TRUE); -} - -static isc_result_t -control_accept(controllistener_t *listener) { - isc_result_t result; - result = isc_socket_accept(listener->sock, - listener->task, - control_newconn, listener); - if (result != ISC_R_SUCCESS) - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_socket_accept() failed: %s", - isc_result_totext(result)); - else - listener->listening = ISC_TRUE; - return (result); -} - -static isc_result_t -control_listen(controllistener_t *listener) { - isc_result_t result; - - result = isc_socket_listen(listener->sock, 0); - if (result != ISC_R_SUCCESS) - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_socket_listen() failed: %s", - isc_result_totext(result)); - return (result); -} - -static void -control_next(controllistener_t *listener) { - (void)control_accept(listener); -} - -static void -control_senddone(isc_task_t *task, isc_event_t *event) { - isc_socketevent_t *sevent = (isc_socketevent_t *) event; - controlconnection_t *conn = event->ev_arg; - controllistener_t *listener = conn->listener; - isc_socket_t *sock = (isc_socket_t *)sevent->ev_sender; - isc_result_t result; - - REQUIRE(conn->sending); - - UNUSED(task); - - conn->sending = ISC_FALSE; - - if (sevent->result != ISC_R_SUCCESS && - sevent->result != ISC_R_CANCELED) - { - char socktext[ISC_SOCKADDR_FORMATSIZE]; - isc_sockaddr_t peeraddr; - - (void)isc_socket_getpeername(sock, &peeraddr); - isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); - isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, - "error sending command response to %s: %s", - socktext, isc_result_totext(sevent->result)); - } - isc_event_free(&event); - - result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task, - control_recvmessage, conn); - if (result != ISC_R_SUCCESS) { - isc_socket_detach(&conn->sock); - maybe_free_connection(conn); - maybe_free_listener(listener); - } -} - -static inline void -log_invalid(isccc_ccmsg_t *ccmsg, isc_result_t result) { - char socktext[ISC_SOCKADDR_FORMATSIZE]; - isc_sockaddr_t peeraddr; - - (void)isc_socket_getpeername(ccmsg->sock, &peeraddr); - isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); - isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, ISC_LOG_ERROR, - "invalid command from %s: %s", - socktext, isc_result_totext(result)); -} - -static void -control_recvmessage(isc_task_t *task, isc_event_t *event) { - controlconnection_t *conn; - controllistener_t *listener; - controlkey_t *key; - isccc_sexpr_t *request = NULL; - isccc_sexpr_t *response = NULL; - isccc_region_t ccregion; - isccc_region_t secret; - isc_stdtime_t now; - isc_buffer_t b; - isc_region_t r; - isc_uint32_t len; - isc_buffer_t text; - char textarray[1024]; - isc_result_t result; - isc_result_t eresult; - isccc_sexpr_t *_ctrl; - isccc_time_t sent; - isccc_time_t exp; - isc_uint32_t nonce; - - REQUIRE(event->ev_type == ISCCC_EVENT_CCMSG); - - conn = event->ev_arg; - listener = conn->listener; - secret.rstart = NULL; - - /* Is the server shutting down? */ - if (listener->controls->shuttingdown) - goto cleanup; - - if (conn->ccmsg.result != ISC_R_SUCCESS) { - if (conn->ccmsg.result != ISC_R_CANCELED && - conn->ccmsg.result != ISC_R_EOF) - log_invalid(&conn->ccmsg, conn->ccmsg.result); - goto cleanup; - } - - request = NULL; - - for (key = ISC_LIST_HEAD(listener->keys); - key != NULL; - key = ISC_LIST_NEXT(key, link)) - { - ccregion.rstart = isc_buffer_base(&conn->ccmsg.buffer); - ccregion.rend = isc_buffer_used(&conn->ccmsg.buffer); - if (secret.rstart != NULL) - isc_mem_put(listener->mctx, secret.rstart, - REGION_SIZE(secret)); - secret.rstart = isc_mem_get(listener->mctx, key->secret.length); - if (secret.rstart == NULL) - goto cleanup; - memcpy(secret.rstart, key->secret.base, key->secret.length); - secret.rend = secret.rstart + key->secret.length; - result = isccc_cc_fromwire(&ccregion, &request, &secret); - if (result == ISC_R_SUCCESS) - break; - else if (result == ISCCC_R_BADAUTH) { - /* - * For some reason, request is non-NULL when - * isccc_cc_fromwire returns ISCCC_R_BADAUTH. - */ - if (request != NULL) - isccc_sexpr_free(&request); - } else { - log_invalid(&conn->ccmsg, result); - goto cleanup; - } - } - - if (key == NULL) { - log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); - goto cleanup; - } - - /* We shouldn't be getting a reply. */ - if (isccc_cc_isreply(request)) { - log_invalid(&conn->ccmsg, ISC_R_FAILURE); - goto cleanup; - } - - isc_stdtime_get(&now); - - /* - * Limit exposure to replay attacks. - */ - _ctrl = isccc_alist_lookup(request, "_ctrl"); - if (_ctrl == NULL) { - log_invalid(&conn->ccmsg, ISC_R_FAILURE); - goto cleanup; - } - - if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) { - if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) { - log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW); - goto cleanup; - } - } else { - log_invalid(&conn->ccmsg, ISC_R_FAILURE); - goto cleanup; - } - - /* - * Expire messages that are too old. - */ - if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS && - now > exp) { - log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED); - goto cleanup; - } - - /* - * Duplicate suppression (required for UDP). - */ - isccc_cc_cleansymtab(listener->controls->symtab, now); - result = isccc_cc_checkdup(listener->controls->symtab, request, now); - if (result != ISC_R_SUCCESS) { - if (result == ISC_R_EXISTS) - result = ISCCC_R_DUPLICATE; - log_invalid(&conn->ccmsg, result); - goto cleanup; - } - - if (conn->nonce != 0 && - (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS || - conn->nonce != nonce)) { - log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); - goto cleanup; - } - - /* - * Establish nonce. - */ - while (conn->nonce == 0) - isc_random_get(&conn->nonce); - - isc_buffer_init(&text, textarray, sizeof(textarray)); - eresult = ns_control_docommand(request, &text); - - result = isccc_cc_createresponse(request, now, now + 60, &response); - if (result != ISC_R_SUCCESS) - goto cleanup; - if (eresult != ISC_R_SUCCESS) { - isccc_sexpr_t *data; - - data = isccc_alist_lookup(response, "_data"); - if (data != NULL) { - const char *estr = isc_result_totext(eresult); - if (isccc_cc_definestring(data, "err", estr) == NULL) - goto cleanup; - } - } - - if (isc_buffer_usedlength(&text) > 0) { - isccc_sexpr_t *data; - - data = isccc_alist_lookup(response, "_data"); - if (data != NULL) { - char *str = (char *)isc_buffer_base(&text); - if (isccc_cc_definestring(data, "text", str) == NULL) - goto cleanup; - } - } - - _ctrl = isccc_alist_lookup(response, "_ctrl"); - if (_ctrl == NULL || - isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL) - goto cleanup; - - ccregion.rstart = conn->buffer + 4; - ccregion.rend = conn->buffer + sizeof(conn->buffer); - result = isccc_cc_towire(response, &ccregion, &secret); - if (result != ISC_R_SUCCESS) - goto cleanup; - isc_buffer_init(&b, conn->buffer, 4); - len = sizeof(conn->buffer) - REGION_SIZE(ccregion); - isc_buffer_putuint32(&b, len - 4); - r.base = conn->buffer; - r.length = len; - - result = isc_socket_send(conn->sock, &r, task, control_senddone, conn); - if (result != ISC_R_SUCCESS) - goto cleanup; - conn->sending = ISC_TRUE; - - if (secret.rstart != NULL) - isc_mem_put(listener->mctx, secret.rstart, - REGION_SIZE(secret)); - if (request != NULL) - isccc_sexpr_free(&request); - if (response != NULL) - isccc_sexpr_free(&response); - return; - - cleanup: - if (secret.rstart != NULL) - isc_mem_put(listener->mctx, secret.rstart, - REGION_SIZE(secret)); - isc_socket_detach(&conn->sock); - isccc_ccmsg_invalidate(&conn->ccmsg); - conn->ccmsg_valid = ISC_FALSE; - maybe_free_connection(conn); - maybe_free_listener(listener); - if (request != NULL) - isccc_sexpr_free(&request); - if (response != NULL) - isccc_sexpr_free(&response); -} - -static void -control_timeout(isc_task_t *task, isc_event_t *event) { - controlconnection_t *conn = event->ev_arg; - - UNUSED(task); - - isc_timer_detach(&conn->timer); - maybe_free_connection(conn); - - isc_event_free(&event); -} - -static isc_result_t -newconnection(controllistener_t *listener, isc_socket_t *sock) { - controlconnection_t *conn; - isc_interval_t interval; - isc_result_t result; - - conn = isc_mem_get(listener->mctx, sizeof(*conn)); - if (conn == NULL) - return (ISC_R_NOMEMORY); - - conn->sock = sock; - isccc_ccmsg_init(listener->mctx, sock, &conn->ccmsg); - conn->ccmsg_valid = ISC_TRUE; - conn->sending = ISC_FALSE; - conn->timer = NULL; - isc_interval_set(&interval, 60, 0); - result = isc_timer_create(ns_g_timermgr, isc_timertype_once, - NULL, &interval, listener->task, - control_timeout, conn, &conn->timer); - if (result != ISC_R_SUCCESS) - goto cleanup; - - conn->listener = listener; - conn->nonce = 0; - ISC_LINK_INIT(conn, link); - - result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task, - control_recvmessage, conn); - if (result != ISC_R_SUCCESS) - goto cleanup; - isccc_ccmsg_setmaxsize(&conn->ccmsg, 2048); - - ISC_LIST_APPEND(listener->connections, conn, link); - return (ISC_R_SUCCESS); - - cleanup: - isccc_ccmsg_invalidate(&conn->ccmsg); - if (conn->timer != NULL) - isc_timer_detach(&conn->timer); - isc_mem_put(listener->mctx, conn, sizeof(*conn)); - return (result); -} - -static void -control_newconn(isc_task_t *task, isc_event_t *event) { - isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event; - controllistener_t *listener = event->ev_arg; - isc_socket_t *sock; - isc_sockaddr_t peeraddr; - isc_result_t result; - - UNUSED(task); - - listener->listening = ISC_FALSE; - - if (nevent->result != ISC_R_SUCCESS) { - if (nevent->result == ISC_R_CANCELED) { - shutdown_listener(listener); - goto cleanup; - } - goto restart; - } - - sock = nevent->newsocket; - (void)isc_socket_getpeername(sock, &peeraddr); - if (listener->type == isc_sockettype_tcp && - !address_ok(&peeraddr, listener->acl)) { - char socktext[ISC_SOCKADDR_FORMATSIZE]; - isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); - isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, - "rejected command channel message from %s", - socktext); - isc_socket_detach(&sock); - goto restart; - } - - result = newconnection(listener, sock); - if (result != ISC_R_SUCCESS) { - char socktext[ISC_SOCKADDR_FORMATSIZE]; - isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); - isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, - "dropped command channel from %s: %s", - socktext, isc_result_totext(result)); - isc_socket_detach(&sock); - goto restart; - } - - restart: - control_next(listener); - cleanup: - isc_event_free(&event); -} - -static void -controls_shutdown(ns_controls_t *controls) { - controllistener_t *listener; - controllistener_t *next; - - for (listener = ISC_LIST_HEAD(controls->listeners); - listener != NULL; - listener = next) - { - /* - * This is asynchronous. As listeners shut down, they will - * call their callbacks. - */ - next = ISC_LIST_NEXT(listener, link); - shutdown_listener(listener); - } -} - -void -ns_controls_shutdown(ns_controls_t *controls) { - controls_shutdown(controls); - controls->shuttingdown = ISC_TRUE; -} - -static isc_result_t -cfgkeylist_find(const cfg_obj_t *keylist, const char *keyname, - const cfg_obj_t **objp) -{ - const cfg_listelt_t *element; - const char *str; - const cfg_obj_t *obj; - - for (element = cfg_list_first(keylist); - element != NULL; - element = cfg_list_next(element)) - { - obj = cfg_listelt_value(element); - str = cfg_obj_asstring(cfg_map_getname(obj)); - if (strcasecmp(str, keyname) == 0) - break; - } - if (element == NULL) - return (ISC_R_NOTFOUND); - obj = cfg_listelt_value(element); - *objp = obj; - return (ISC_R_SUCCESS); -} - -static isc_result_t -controlkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx, - controlkeylist_t *keyids) -{ - const cfg_listelt_t *element; - char *newstr = NULL; - const char *str; - const cfg_obj_t *obj; - controlkey_t *key; - - for (element = cfg_list_first(keylist); - element != NULL; - element = cfg_list_next(element)) - { - obj = cfg_listelt_value(element); - str = cfg_obj_asstring(obj); - newstr = isc_mem_strdup(mctx, str); - if (newstr == NULL) - goto cleanup; - key = isc_mem_get(mctx, sizeof(*key)); - if (key == NULL) - goto cleanup; - key->keyname = newstr; - key->secret.base = NULL; - key->secret.length = 0; - ISC_LINK_INIT(key, link); - ISC_LIST_APPEND(*keyids, key, link); - newstr = NULL; - } - return (ISC_R_SUCCESS); - - cleanup: - if (newstr != NULL) - isc_mem_free(mctx, newstr); - free_controlkeylist(keyids, mctx); - return (ISC_R_NOMEMORY); -} - -static void -register_keys(const cfg_obj_t *control, const cfg_obj_t *keylist, - controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext) -{ - controlkey_t *keyid, *next; - const cfg_obj_t *keydef; - char secret[1024]; - isc_buffer_t b; - isc_result_t result; - - /* - * Find the keys corresponding to the keyids used by this listener. - */ - for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) { - next = ISC_LIST_NEXT(keyid, link); - - result = cfgkeylist_find(keylist, keyid->keyname, &keydef); - if (result != ISC_R_SUCCESS) { - cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, - "couldn't find key '%s' for use with " - "command channel %s", - keyid->keyname, socktext); - ISC_LIST_UNLINK(*keyids, keyid, link); - free_controlkey(keyid, mctx); - } else { - const cfg_obj_t *algobj = NULL; - const cfg_obj_t *secretobj = NULL; - const char *algstr = NULL; - const char *secretstr = NULL; - - (void)cfg_map_get(keydef, "algorithm", &algobj); - (void)cfg_map_get(keydef, "secret", &secretobj); - INSIST(algobj != NULL && secretobj != NULL); - - algstr = cfg_obj_asstring(algobj); - secretstr = cfg_obj_asstring(secretobj); - - if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != - ISC_R_SUCCESS) - { - cfg_obj_log(control, ns_g_lctx, - ISC_LOG_WARNING, - "unsupported algorithm '%s' in " - "key '%s' for use with command " - "channel %s", - algstr, keyid->keyname, socktext); - ISC_LIST_UNLINK(*keyids, keyid, link); - free_controlkey(keyid, mctx); - continue; - } - - isc_buffer_init(&b, secret, sizeof(secret)); - result = isc_base64_decodestring(secretstr, &b); - - if (result != ISC_R_SUCCESS) { - cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING, - "secret for key '%s' on " - "command channel %s: %s", - keyid->keyname, socktext, - isc_result_totext(result)); - ISC_LIST_UNLINK(*keyids, keyid, link); - free_controlkey(keyid, mctx); - continue; - } - - keyid->secret.length = isc_buffer_usedlength(&b); - keyid->secret.base = isc_mem_get(mctx, - keyid->secret.length); - if (keyid->secret.base == NULL) { - cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING, - "couldn't register key '%s': " - "out of memory", keyid->keyname); - ISC_LIST_UNLINK(*keyids, keyid, link); - free_controlkey(keyid, mctx); - break; - } - memcpy(keyid->secret.base, isc_buffer_base(&b), - keyid->secret.length); - } - } -} - -#define CHECK(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - -static isc_result_t -get_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) { - isc_result_t result; - cfg_parser_t *pctx = NULL; - cfg_obj_t *config = NULL; - const cfg_obj_t *key = NULL; - const cfg_obj_t *algobj = NULL; - const cfg_obj_t *secretobj = NULL; - const char *algstr = NULL; - const char *secretstr = NULL; - controlkey_t *keyid = NULL; - char secret[1024]; - isc_buffer_t b; - - CHECK(cfg_parser_create(mctx, ns_g_lctx, &pctx)); - CHECK(cfg_parse_file(pctx, ns_g_keyfile, &cfg_type_rndckey, &config)); - CHECK(cfg_map_get(config, "key", &key)); - - keyid = isc_mem_get(mctx, sizeof(*keyid)); - if (keyid == NULL) - CHECK(ISC_R_NOMEMORY); - keyid->keyname = isc_mem_strdup(mctx, - cfg_obj_asstring(cfg_map_getname(key))); - keyid->secret.base = NULL; - keyid->secret.length = 0; - ISC_LINK_INIT(keyid, link); - if (keyid->keyname == NULL) - CHECK(ISC_R_NOMEMORY); - - CHECK(bind9_check_key(key, ns_g_lctx)); - - (void)cfg_map_get(key, "algorithm", &algobj); - (void)cfg_map_get(key, "secret", &secretobj); - INSIST(algobj != NULL && secretobj != NULL); - - algstr = cfg_obj_asstring(algobj); - secretstr = cfg_obj_asstring(secretobj); - - if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != ISC_R_SUCCESS) { - cfg_obj_log(key, ns_g_lctx, - ISC_LOG_WARNING, - "unsupported algorithm '%s' in " - "key '%s' for use with command " - "channel", - algstr, keyid->keyname); - goto cleanup; - } - - isc_buffer_init(&b, secret, sizeof(secret)); - result = isc_base64_decodestring(secretstr, &b); - - if (result != ISC_R_SUCCESS) { - cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, - "secret for key '%s' on command channel: %s", - keyid->keyname, isc_result_totext(result)); - CHECK(result); - } - - keyid->secret.length = isc_buffer_usedlength(&b); - keyid->secret.base = isc_mem_get(mctx, - keyid->secret.length); - if (keyid->secret.base == NULL) { - cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, - "couldn't register key '%s': " - "out of memory", keyid->keyname); - CHECK(ISC_R_NOMEMORY); - } - memcpy(keyid->secret.base, isc_buffer_base(&b), - keyid->secret.length); - ISC_LIST_APPEND(*keyids, keyid, link); - keyid = NULL; - result = ISC_R_SUCCESS; - - cleanup: - if (keyid != NULL) - free_controlkey(keyid, mctx); - if (config != NULL) - cfg_obj_destroy(pctx, &config); - if (pctx != NULL) - cfg_parser_destroy(&pctx); - return (result); -} - -/* - * Ensures that both '*global_keylistp' and '*control_keylistp' are - * valid or both are NULL. - */ -static void -get_key_info(const cfg_obj_t *config, const cfg_obj_t *control, - const cfg_obj_t **global_keylistp, - const cfg_obj_t **control_keylistp) -{ - isc_result_t result; - const cfg_obj_t *control_keylist = NULL; - const cfg_obj_t *global_keylist = NULL; - - REQUIRE(global_keylistp != NULL && *global_keylistp == NULL); - REQUIRE(control_keylistp != NULL && *control_keylistp == NULL); - - control_keylist = cfg_tuple_get(control, "keys"); - - if (!cfg_obj_isvoid(control_keylist) && - cfg_list_first(control_keylist) != NULL) { - result = cfg_map_get(config, "key", &global_keylist); - - if (result == ISC_R_SUCCESS) { - *global_keylistp = global_keylist; - *control_keylistp = control_keylist; - } - } -} - -static void -update_listener(ns_controls_t *cp, controllistener_t **listenerp, - const cfg_obj_t *control, const cfg_obj_t *config, - isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, - const char *socktext, isc_sockettype_t type) -{ - controllistener_t *listener; - const cfg_obj_t *allow; - const cfg_obj_t *global_keylist = NULL; - const cfg_obj_t *control_keylist = NULL; - dns_acl_t *new_acl = NULL; - controlkeylist_t keys; - isc_result_t result = ISC_R_SUCCESS; - - for (listener = ISC_LIST_HEAD(cp->listeners); - listener != NULL; - listener = ISC_LIST_NEXT(listener, link)) - if (isc_sockaddr_equal(addr, &listener->address)) - break; - - if (listener == NULL) { - *listenerp = NULL; - return; - } - - /* - * There is already a listener for this sockaddr. - * Update the access list and key information. - * - * First try to deal with the key situation. There are a few - * possibilities: - * (a) It had an explicit keylist and still has an explicit keylist. - * (b) It had an automagic key and now has an explicit keylist. - * (c) It had an explicit keylist and now needs an automagic key. - * (d) It has an automagic key and still needs the automagic key. - * - * (c) and (d) are the annoying ones. The caller needs to know - * that it should use the automagic configuration for key information - * in place of the named.conf configuration. - * - * XXXDCL There is one other hazard that has not been dealt with, - * the problem that if a key change is being caused by a control - * channel reload, then the response will be with the new key - * and not able to be decrypted by the client. - */ - if (control != NULL) - get_key_info(config, control, &global_keylist, - &control_keylist); - - if (control_keylist != NULL) { - INSIST(global_keylist != NULL); - - ISC_LIST_INIT(keys); - result = controlkeylist_fromcfg(control_keylist, - listener->mctx, &keys); - if (result == ISC_R_SUCCESS) { - free_controlkeylist(&listener->keys, listener->mctx); - listener->keys = keys; - register_keys(control, global_keylist, &listener->keys, - listener->mctx, socktext); - } - } else { - free_controlkeylist(&listener->keys, listener->mctx); - result = get_rndckey(listener->mctx, &listener->keys); - } - - if (result != ISC_R_SUCCESS && global_keylist != NULL) { - /* - * This message might be a little misleading since the - * "new keys" might in fact be identical to the old ones, - * but tracking whether they are identical just for the - * sake of avoiding this message would be too much trouble. - */ - if (control != NULL) - cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, - "couldn't install new keys for " - "command channel %s: %s", - socktext, isc_result_totext(result)); - else - isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, - "couldn't install new keys for " - "command channel %s: %s", - socktext, isc_result_totext(result)); - } - - /* - * Now, keep the old access list unless a new one can be made. - */ - if (control != NULL && type == isc_sockettype_tcp) { - allow = cfg_tuple_get(control, "allow"); - result = cfg_acl_fromconfig(allow, config, ns_g_lctx, - aclconfctx, listener->mctx, - &new_acl); - } else { - result = dns_acl_any(listener->mctx, &new_acl); - } - - if (result == ISC_R_SUCCESS) { - dns_acl_detach(&listener->acl); - dns_acl_attach(new_acl, &listener->acl); - dns_acl_detach(&new_acl); - /* XXXDCL say the old acl is still used? */ - } else if (control != NULL) - cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, - "couldn't install new acl for " - "command channel %s: %s", - socktext, isc_result_totext(result)); - else - isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, - "couldn't install new acl for " - "command channel %s: %s", - socktext, isc_result_totext(result)); - - if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) { - isc_uint32_t perm, owner, group; - perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm")); - owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner")); - group = cfg_obj_asuint32(cfg_tuple_get(control, "group")); - result = ISC_R_SUCCESS; - if (listener->perm != perm || listener->owner != owner || - listener->group != group) - result = isc_socket_permunix(&listener->address, perm, - owner, group); - if (result == ISC_R_SUCCESS) { - listener->perm = perm; - listener->owner = owner; - listener->group = group; - } else if (control != NULL) - cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, - "couldn't update ownership/permission for " - "command channel %s", socktext); - } - - *listenerp = listener; -} - -static void -add_listener(ns_controls_t *cp, controllistener_t **listenerp, - const cfg_obj_t *control, const cfg_obj_t *config, - isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, - const char *socktext, isc_sockettype_t type) -{ - isc_mem_t *mctx = cp->server->mctx; - controllistener_t *listener; - const cfg_obj_t *allow; - const cfg_obj_t *global_keylist = NULL; - const cfg_obj_t *control_keylist = NULL; - dns_acl_t *new_acl = NULL; - isc_result_t result = ISC_R_SUCCESS; - - listener = isc_mem_get(mctx, sizeof(*listener)); - if (listener == NULL) - result = ISC_R_NOMEMORY; - - if (result == ISC_R_SUCCESS) { - listener->controls = cp; - listener->mctx = mctx; - listener->task = cp->server->task; - listener->address = *addr; - listener->sock = NULL; - listener->listening = ISC_FALSE; - listener->exiting = ISC_FALSE; - listener->acl = NULL; - listener->type = type; - listener->perm = 0; - listener->owner = 0; - listener->group = 0; - ISC_LINK_INIT(listener, link); - ISC_LIST_INIT(listener->keys); - ISC_LIST_INIT(listener->connections); - - /* - * Make the acl. - */ - if (control != NULL && type == isc_sockettype_tcp) { - allow = cfg_tuple_get(control, "allow"); - result = cfg_acl_fromconfig(allow, config, ns_g_lctx, - aclconfctx, mctx, &new_acl); - } else { - result = dns_acl_any(mctx, &new_acl); - } - } - - if (result == ISC_R_SUCCESS) { - dns_acl_attach(new_acl, &listener->acl); - dns_acl_detach(&new_acl); - - if (config != NULL) - get_key_info(config, control, &global_keylist, - &control_keylist); - - if (control_keylist != NULL) { - result = controlkeylist_fromcfg(control_keylist, - listener->mctx, - &listener->keys); - if (result == ISC_R_SUCCESS) - register_keys(control, global_keylist, - &listener->keys, - listener->mctx, socktext); - } else - result = get_rndckey(mctx, &listener->keys); - - if (result != ISC_R_SUCCESS && control != NULL) - cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, - "couldn't install keys for " - "command channel %s: %s", - socktext, isc_result_totext(result)); - } - - if (result == ISC_R_SUCCESS) { - int pf = isc_sockaddr_pf(&listener->address); - if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) || -#ifdef ISC_PLATFORM_HAVESYSUNH - (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) || -#endif - (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) - result = ISC_R_FAMILYNOSUPPORT; - } - - if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) - isc_socket_cleanunix(&listener->address, ISC_FALSE); - - if (result == ISC_R_SUCCESS) - result = isc_socket_create(ns_g_socketmgr, - isc_sockaddr_pf(&listener->address), - type, &listener->sock); - - if (result == ISC_R_SUCCESS) - result = isc_socket_bind(listener->sock, - &listener->address); - - if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) { - listener->perm = cfg_obj_asuint32(cfg_tuple_get(control, - "perm")); - listener->owner = cfg_obj_asuint32(cfg_tuple_get(control, - "owner")); - listener->group = cfg_obj_asuint32(cfg_tuple_get(control, - "group")); - result = isc_socket_permunix(&listener->address, listener->perm, - listener->owner, listener->group); - } - if (result == ISC_R_SUCCESS) - result = control_listen(listener); - - if (result == ISC_R_SUCCESS) - result = control_accept(listener); - - if (result == ISC_R_SUCCESS) { - isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, - "command channel listening on %s", socktext); - *listenerp = listener; - - } else { - if (listener != NULL) { - listener->exiting = ISC_TRUE; - free_listener(listener); - } - - if (control != NULL) - cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, - "couldn't add command channel %s: %s", - socktext, isc_result_totext(result)); - else - isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, - "couldn't add command channel %s: %s", - socktext, isc_result_totext(result)); - - *listenerp = NULL; - } - - /* XXXDCL return error results? fail hard? */ -} - -isc_result_t -ns_controls_configure(ns_controls_t *cp, const cfg_obj_t *config, - cfg_aclconfctx_t *aclconfctx) -{ - controllistener_t *listener; - controllistenerlist_t new_listeners; - const cfg_obj_t *controlslist = NULL; - const cfg_listelt_t *element, *element2; - char socktext[ISC_SOCKADDR_FORMATSIZE]; - - ISC_LIST_INIT(new_listeners); - - /* - * Get the list of named.conf 'controls' statements. - */ - (void)cfg_map_get(config, "controls", &controlslist); - - /* - * Run through the new control channel list, noting sockets that - * are already being listened on and moving them to the new list. - * - * Identifying duplicate addr/port combinations is left to either - * the underlying config code, or to the bind attempt getting an - * address-in-use error. - */ - if (controlslist != NULL) { - for (element = cfg_list_first(controlslist); - element != NULL; - element = cfg_list_next(element)) { - const cfg_obj_t *controls; - const cfg_obj_t *inetcontrols = NULL; - - controls = cfg_listelt_value(element); - (void)cfg_map_get(controls, "inet", &inetcontrols); - if (inetcontrols == NULL) - continue; - - for (element2 = cfg_list_first(inetcontrols); - element2 != NULL; - element2 = cfg_list_next(element2)) { - const cfg_obj_t *control; - const cfg_obj_t *obj; - isc_sockaddr_t addr; - - /* - * The parser handles BIND 8 configuration file - * syntax, so it allows unix phrases as well - * inet phrases with no keys{} clause. - */ - control = cfg_listelt_value(element2); - - obj = cfg_tuple_get(control, "address"); - addr = *cfg_obj_assockaddr(obj); - if (isc_sockaddr_getport(&addr) == 0) - isc_sockaddr_setport(&addr, - NS_CONTROL_PORT); - - isc_sockaddr_format(&addr, socktext, - sizeof(socktext)); - - isc_log_write(ns_g_lctx, - NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, - ISC_LOG_DEBUG(9), - "processing control channel %s", - socktext); - - update_listener(cp, &listener, control, config, - &addr, aclconfctx, socktext, - isc_sockettype_tcp); - - if (listener != NULL) - /* - * Remove the listener from the old - * list, so it won't be shut down. - */ - ISC_LIST_UNLINK(cp->listeners, - listener, link); - else - /* - * This is a new listener. - */ - add_listener(cp, &listener, control, - config, &addr, aclconfctx, - socktext, - isc_sockettype_tcp); - - if (listener != NULL) - ISC_LIST_APPEND(new_listeners, - listener, link); - } - } - for (element = cfg_list_first(controlslist); - element != NULL; - element = cfg_list_next(element)) { - const cfg_obj_t *controls; - const cfg_obj_t *unixcontrols = NULL; - - controls = cfg_listelt_value(element); - (void)cfg_map_get(controls, "unix", &unixcontrols); - if (unixcontrols == NULL) - continue; - - for (element2 = cfg_list_first(unixcontrols); - element2 != NULL; - element2 = cfg_list_next(element2)) { - const cfg_obj_t *control; - const cfg_obj_t *path; - isc_sockaddr_t addr; - isc_result_t result; - - /* - * The parser handles BIND 8 configuration file - * syntax, so it allows unix phrases as well - * inet phrases with no keys{} clause. - */ - control = cfg_listelt_value(element2); - - path = cfg_tuple_get(control, "path"); - result = isc_sockaddr_frompath(&addr, - cfg_obj_asstring(path)); - if (result != ISC_R_SUCCESS) { - isc_log_write(ns_g_lctx, - NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, - ISC_LOG_DEBUG(9), - "control channel '%s': %s", - cfg_obj_asstring(path), - isc_result_totext(result)); - continue; - } - - isc_log_write(ns_g_lctx, - NS_LOGCATEGORY_GENERAL, - NS_LOGMODULE_CONTROL, - ISC_LOG_DEBUG(9), - "processing control channel '%s'", - cfg_obj_asstring(path)); - - update_listener(cp, &listener, control, config, - &addr, aclconfctx, - cfg_obj_asstring(path), - isc_sockettype_unix); - - if (listener != NULL) - /* - * Remove the listener from the old - * list, so it won't be shut down. - */ - ISC_LIST_UNLINK(cp->listeners, - listener, link); - else - /* - * This is a new listener. - */ - add_listener(cp, &listener, control, - config, &addr, aclconfctx, - cfg_obj_asstring(path), - isc_sockettype_unix); - - if (listener != NULL) - ISC_LIST_APPEND(new_listeners, - listener, link); - } - } - } else { - int i; - - for (i = 0; i < 2; i++) { - isc_sockaddr_t addr; - - if (i == 0) { - struct in_addr localhost; - - if (isc_net_probeipv4() != ISC_R_SUCCESS) - continue; - localhost.s_addr = htonl(INADDR_LOOPBACK); - isc_sockaddr_fromin(&addr, &localhost, 0); - } else { - if (isc_net_probeipv6() != ISC_R_SUCCESS) - continue; - isc_sockaddr_fromin6(&addr, - &in6addr_loopback, 0); - } - isc_sockaddr_setport(&addr, NS_CONTROL_PORT); - - isc_sockaddr_format(&addr, socktext, sizeof(socktext)); - - update_listener(cp, &listener, NULL, NULL, - &addr, NULL, socktext, - isc_sockettype_tcp); - - if (listener != NULL) - /* - * Remove the listener from the old - * list, so it won't be shut down. - */ - ISC_LIST_UNLINK(cp->listeners, - listener, link); - else - /* - * This is a new listener. - */ - add_listener(cp, &listener, NULL, NULL, - &addr, NULL, socktext, - isc_sockettype_tcp); - - if (listener != NULL) - ISC_LIST_APPEND(new_listeners, - listener, link); - } - } - - /* - * ns_control_shutdown() will stop whatever is on the global - * listeners list, which currently only has whatever sockaddrs - * were in the previous configuration (if any) that do not - * remain in the current configuration. - */ - controls_shutdown(cp); - - /* - * Put all of the valid listeners on the listeners list. - * Anything already on listeners in the process of shutting - * down will be taken care of by listen_done(). - */ - ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link); - return (ISC_R_SUCCESS); -} - -isc_result_t -ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp) { - isc_mem_t *mctx = server->mctx; - isc_result_t result; - ns_controls_t *controls = isc_mem_get(mctx, sizeof(*controls)); - - if (controls == NULL) - return (ISC_R_NOMEMORY); - controls->server = server; - ISC_LIST_INIT(controls->listeners); - controls->shuttingdown = ISC_FALSE; - controls->symtab = NULL; - result = isccc_cc_createsymtab(&controls->symtab); - if (result != ISC_R_SUCCESS) { - isc_mem_put(server->mctx, controls, sizeof(*controls)); - return (result); - } - *ctrlsp = controls; - return (ISC_R_SUCCESS); -} - -void -ns_controls_destroy(ns_controls_t **ctrlsp) { - ns_controls_t *controls = *ctrlsp; - - REQUIRE(ISC_LIST_EMPTY(controls->listeners)); - - isccc_symtab_destroy(&controls->symtab); - isc_mem_put(controls->server->mctx, controls, sizeof(*controls)); - *ctrlsp = NULL; -} |