controlconf.c revision 165071
1135446Strhodes/* 2165071Sdougb * Copyright (C) 2004, 2006 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 2001-2003 Internet Software Consortium. 4135446Strhodes * 5135446Strhodes * Permission to use, copy, modify, and distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18165071Sdougb/* $Id: controlconf.c,v 1.28.2.9.2.10 2006/02/28 06:32:53 marka Exp $ */ 19135446Strhodes 20135446Strhodes#include <config.h> 21135446Strhodes 22135446Strhodes#include <isc/base64.h> 23135446Strhodes#include <isc/buffer.h> 24135446Strhodes#include <isc/event.h> 25135446Strhodes#include <isc/mem.h> 26135446Strhodes#include <isc/net.h> 27135446Strhodes#include <isc/netaddr.h> 28135446Strhodes#include <isc/random.h> 29135446Strhodes#include <isc/result.h> 30135446Strhodes#include <isc/stdtime.h> 31135446Strhodes#include <isc/string.h> 32135446Strhodes#include <isc/timer.h> 33135446Strhodes#include <isc/util.h> 34135446Strhodes 35135446Strhodes#include <isccfg/namedconf.h> 36135446Strhodes 37135446Strhodes#include <bind9/check.h> 38135446Strhodes 39135446Strhodes#include <isccc/alist.h> 40135446Strhodes#include <isccc/cc.h> 41135446Strhodes#include <isccc/ccmsg.h> 42135446Strhodes#include <isccc/events.h> 43135446Strhodes#include <isccc/result.h> 44135446Strhodes#include <isccc/sexpr.h> 45135446Strhodes#include <isccc/symtab.h> 46135446Strhodes#include <isccc/util.h> 47135446Strhodes 48135446Strhodes#include <dns/result.h> 49135446Strhodes 50135446Strhodes#include <named/config.h> 51135446Strhodes#include <named/control.h> 52135446Strhodes#include <named/log.h> 53135446Strhodes#include <named/server.h> 54135446Strhodes 55135446Strhodes/* 56135446Strhodes * Note: Listeners and connections are not locked. All event handlers are 57135446Strhodes * executed by the server task, and all callers of exported routines must 58135446Strhodes * be running under the server task. 59135446Strhodes */ 60135446Strhodes 61135446Strhodestypedef struct controlkey controlkey_t; 62135446Strhodestypedef ISC_LIST(controlkey_t) controlkeylist_t; 63135446Strhodes 64135446Strhodestypedef struct controlconnection controlconnection_t; 65135446Strhodestypedef ISC_LIST(controlconnection_t) controlconnectionlist_t; 66135446Strhodes 67135446Strhodestypedef struct controllistener controllistener_t; 68135446Strhodestypedef ISC_LIST(controllistener_t) controllistenerlist_t; 69135446Strhodes 70135446Strhodesstruct controlkey { 71135446Strhodes char * keyname; 72135446Strhodes isc_region_t secret; 73135446Strhodes ISC_LINK(controlkey_t) link; 74135446Strhodes}; 75135446Strhodes 76135446Strhodesstruct controlconnection { 77135446Strhodes isc_socket_t * sock; 78135446Strhodes isccc_ccmsg_t ccmsg; 79135446Strhodes isc_boolean_t ccmsg_valid; 80135446Strhodes isc_boolean_t sending; 81135446Strhodes isc_timer_t * timer; 82135446Strhodes unsigned char buffer[2048]; 83135446Strhodes controllistener_t * listener; 84135446Strhodes isc_uint32_t nonce; 85135446Strhodes ISC_LINK(controlconnection_t) link; 86135446Strhodes}; 87135446Strhodes 88135446Strhodesstruct controllistener { 89135446Strhodes ns_controls_t * controls; 90135446Strhodes isc_mem_t * mctx; 91135446Strhodes isc_task_t * task; 92135446Strhodes isc_sockaddr_t address; 93135446Strhodes isc_socket_t * sock; 94135446Strhodes dns_acl_t * acl; 95135446Strhodes isc_boolean_t listening; 96135446Strhodes isc_boolean_t exiting; 97135446Strhodes controlkeylist_t keys; 98135446Strhodes controlconnectionlist_t connections; 99135446Strhodes ISC_LINK(controllistener_t) link; 100135446Strhodes}; 101135446Strhodes 102135446Strhodesstruct ns_controls { 103135446Strhodes ns_server_t *server; 104135446Strhodes controllistenerlist_t listeners; 105135446Strhodes isc_boolean_t shuttingdown; 106135446Strhodes isccc_symtab_t *symtab; 107135446Strhodes}; 108135446Strhodes 109135446Strhodesstatic void control_newconn(isc_task_t *task, isc_event_t *event); 110135446Strhodesstatic void control_recvmessage(isc_task_t *task, isc_event_t *event); 111135446Strhodes 112135446Strhodes#define CLOCKSKEW 300 113135446Strhodes 114135446Strhodesstatic void 115135446Strhodesfree_controlkey(controlkey_t *key, isc_mem_t *mctx) { 116135446Strhodes if (key->keyname != NULL) 117135446Strhodes isc_mem_free(mctx, key->keyname); 118135446Strhodes if (key->secret.base != NULL) 119135446Strhodes isc_mem_put(mctx, key->secret.base, key->secret.length); 120135446Strhodes isc_mem_put(mctx, key, sizeof(*key)); 121135446Strhodes} 122135446Strhodes 123135446Strhodesstatic void 124135446Strhodesfree_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) { 125135446Strhodes while (!ISC_LIST_EMPTY(*keylist)) { 126135446Strhodes controlkey_t *key = ISC_LIST_HEAD(*keylist); 127135446Strhodes ISC_LIST_UNLINK(*keylist, key, link); 128135446Strhodes free_controlkey(key, mctx); 129135446Strhodes } 130135446Strhodes} 131135446Strhodes 132135446Strhodesstatic void 133135446Strhodesfree_listener(controllistener_t *listener) { 134135446Strhodes INSIST(listener->exiting); 135135446Strhodes INSIST(!listener->listening); 136135446Strhodes INSIST(ISC_LIST_EMPTY(listener->connections)); 137135446Strhodes 138135446Strhodes if (listener->sock != NULL) 139135446Strhodes isc_socket_detach(&listener->sock); 140135446Strhodes 141135446Strhodes free_controlkeylist(&listener->keys, listener->mctx); 142135446Strhodes 143135446Strhodes if (listener->acl != NULL) 144135446Strhodes dns_acl_detach(&listener->acl); 145135446Strhodes 146135446Strhodes isc_mem_put(listener->mctx, listener, sizeof(*listener)); 147135446Strhodes} 148135446Strhodes 149135446Strhodesstatic void 150135446Strhodesmaybe_free_listener(controllistener_t *listener) { 151135446Strhodes if (listener->exiting && 152135446Strhodes !listener->listening && 153135446Strhodes ISC_LIST_EMPTY(listener->connections)) 154135446Strhodes free_listener(listener); 155135446Strhodes} 156135446Strhodes 157135446Strhodesstatic void 158135446Strhodesmaybe_free_connection(controlconnection_t *conn) { 159135446Strhodes controllistener_t *listener = conn->listener; 160135446Strhodes 161135446Strhodes if (conn->timer != NULL) 162135446Strhodes isc_timer_detach(&conn->timer); 163135446Strhodes 164135446Strhodes if (conn->ccmsg_valid) { 165135446Strhodes isccc_ccmsg_cancelread(&conn->ccmsg); 166135446Strhodes return; 167135446Strhodes } 168135446Strhodes 169135446Strhodes if (conn->sending) { 170135446Strhodes isc_socket_cancel(conn->sock, listener->task, 171135446Strhodes ISC_SOCKCANCEL_SEND); 172135446Strhodes return; 173135446Strhodes } 174135446Strhodes 175135446Strhodes ISC_LIST_UNLINK(listener->connections, conn, link); 176135446Strhodes isc_mem_put(listener->mctx, conn, sizeof(*conn)); 177135446Strhodes} 178135446Strhodes 179135446Strhodesstatic void 180135446Strhodesshutdown_listener(controllistener_t *listener) { 181135446Strhodes controlconnection_t *conn; 182135446Strhodes controlconnection_t *next; 183135446Strhodes 184135446Strhodes if (!listener->exiting) { 185135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 186135446Strhodes 187135446Strhodes ISC_LIST_UNLINK(listener->controls->listeners, listener, link); 188135446Strhodes 189135446Strhodes isc_sockaddr_format(&listener->address, socktext, 190135446Strhodes sizeof(socktext)); 191135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 192135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 193135446Strhodes "stopping command channel on %s", socktext); 194135446Strhodes listener->exiting = ISC_TRUE; 195135446Strhodes } 196135446Strhodes 197135446Strhodes for (conn = ISC_LIST_HEAD(listener->connections); 198135446Strhodes conn != NULL; 199135446Strhodes conn = next) 200135446Strhodes { 201135446Strhodes next = ISC_LIST_NEXT(conn, link); 202135446Strhodes maybe_free_connection(conn); 203135446Strhodes } 204135446Strhodes 205135446Strhodes if (listener->listening) 206135446Strhodes isc_socket_cancel(listener->sock, listener->task, 207135446Strhodes ISC_SOCKCANCEL_ACCEPT); 208135446Strhodes 209135446Strhodes maybe_free_listener(listener); 210135446Strhodes} 211135446Strhodes 212135446Strhodesstatic isc_boolean_t 213135446Strhodesaddress_ok(isc_sockaddr_t *sockaddr, dns_acl_t *acl) { 214135446Strhodes isc_netaddr_t netaddr; 215135446Strhodes isc_result_t result; 216135446Strhodes int match; 217135446Strhodes 218135446Strhodes isc_netaddr_fromsockaddr(&netaddr, sockaddr); 219135446Strhodes 220135446Strhodes result = dns_acl_match(&netaddr, NULL, acl, 221135446Strhodes &ns_g_server->aclenv, &match, NULL); 222135446Strhodes 223135446Strhodes if (result != ISC_R_SUCCESS || match <= 0) 224135446Strhodes return (ISC_FALSE); 225135446Strhodes else 226135446Strhodes return (ISC_TRUE); 227135446Strhodes} 228135446Strhodes 229135446Strhodesstatic isc_result_t 230135446Strhodescontrol_accept(controllistener_t *listener) { 231135446Strhodes isc_result_t result; 232135446Strhodes result = isc_socket_accept(listener->sock, 233135446Strhodes listener->task, 234135446Strhodes control_newconn, listener); 235135446Strhodes if (result != ISC_R_SUCCESS) 236135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 237135446Strhodes "isc_socket_accept() failed: %s", 238135446Strhodes isc_result_totext(result)); 239135446Strhodes else 240135446Strhodes listener->listening = ISC_TRUE; 241135446Strhodes return (result); 242135446Strhodes} 243135446Strhodes 244135446Strhodesstatic isc_result_t 245135446Strhodescontrol_listen(controllistener_t *listener) { 246135446Strhodes isc_result_t result; 247135446Strhodes 248135446Strhodes result = isc_socket_listen(listener->sock, 0); 249135446Strhodes if (result != ISC_R_SUCCESS) 250135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 251135446Strhodes "isc_socket_listen() failed: %s", 252135446Strhodes isc_result_totext(result)); 253135446Strhodes return (result); 254135446Strhodes} 255135446Strhodes 256135446Strhodesstatic void 257135446Strhodescontrol_next(controllistener_t *listener) { 258135446Strhodes (void)control_accept(listener); 259135446Strhodes} 260135446Strhodes 261135446Strhodesstatic void 262135446Strhodescontrol_senddone(isc_task_t *task, isc_event_t *event) { 263135446Strhodes isc_socketevent_t *sevent = (isc_socketevent_t *) event; 264135446Strhodes controlconnection_t *conn = event->ev_arg; 265135446Strhodes controllistener_t *listener = conn->listener; 266135446Strhodes isc_socket_t *sock = (isc_socket_t *)sevent->ev_sender; 267135446Strhodes isc_result_t result; 268135446Strhodes 269135446Strhodes REQUIRE(conn->sending); 270135446Strhodes 271135446Strhodes UNUSED(task); 272135446Strhodes 273135446Strhodes conn->sending = ISC_FALSE; 274135446Strhodes 275135446Strhodes if (sevent->result != ISC_R_SUCCESS && 276135446Strhodes sevent->result != ISC_R_CANCELED) 277135446Strhodes { 278135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 279135446Strhodes isc_sockaddr_t peeraddr; 280135446Strhodes 281135446Strhodes (void)isc_socket_getpeername(sock, &peeraddr); 282135446Strhodes isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 283135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 284135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 285135446Strhodes "error sending command response to %s: %s", 286135446Strhodes socktext, isc_result_totext(sevent->result)); 287135446Strhodes } 288135446Strhodes isc_event_free(&event); 289135446Strhodes 290135446Strhodes result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task, 291135446Strhodes control_recvmessage, conn); 292135446Strhodes if (result != ISC_R_SUCCESS) { 293135446Strhodes isc_socket_detach(&conn->sock); 294135446Strhodes maybe_free_connection(conn); 295135446Strhodes maybe_free_listener(listener); 296135446Strhodes } 297135446Strhodes} 298135446Strhodes 299135446Strhodesstatic inline void 300135446Strhodeslog_invalid(isccc_ccmsg_t *ccmsg, isc_result_t result) { 301135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 302135446Strhodes isc_sockaddr_t peeraddr; 303135446Strhodes 304135446Strhodes (void)isc_socket_getpeername(ccmsg->sock, &peeraddr); 305135446Strhodes isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 306135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 307135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_ERROR, 308135446Strhodes "invalid command from %s: %s", 309135446Strhodes socktext, isc_result_totext(result)); 310135446Strhodes} 311135446Strhodes 312135446Strhodesstatic void 313135446Strhodescontrol_recvmessage(isc_task_t *task, isc_event_t *event) { 314135446Strhodes controlconnection_t *conn; 315135446Strhodes controllistener_t *listener; 316135446Strhodes controlkey_t *key; 317135446Strhodes isccc_sexpr_t *request = NULL; 318135446Strhodes isccc_sexpr_t *response = NULL; 319135446Strhodes isccc_region_t ccregion; 320135446Strhodes isccc_region_t secret; 321135446Strhodes isc_stdtime_t now; 322135446Strhodes isc_buffer_t b; 323135446Strhodes isc_region_t r; 324135446Strhodes isc_uint32_t len; 325135446Strhodes isc_buffer_t text; 326135446Strhodes char textarray[1024]; 327135446Strhodes isc_result_t result; 328135446Strhodes isc_result_t eresult; 329135446Strhodes isccc_sexpr_t *_ctrl; 330135446Strhodes isccc_time_t sent; 331135446Strhodes isccc_time_t exp; 332135446Strhodes isc_uint32_t nonce; 333135446Strhodes 334135446Strhodes REQUIRE(event->ev_type == ISCCC_EVENT_CCMSG); 335135446Strhodes 336135446Strhodes conn = event->ev_arg; 337135446Strhodes listener = conn->listener; 338135446Strhodes secret.rstart = NULL; 339135446Strhodes 340135446Strhodes /* Is the server shutting down? */ 341135446Strhodes if (listener->controls->shuttingdown) 342135446Strhodes goto cleanup; 343135446Strhodes 344135446Strhodes if (conn->ccmsg.result != ISC_R_SUCCESS) { 345135446Strhodes if (conn->ccmsg.result != ISC_R_CANCELED && 346135446Strhodes conn->ccmsg.result != ISC_R_EOF) 347135446Strhodes log_invalid(&conn->ccmsg, conn->ccmsg.result); 348135446Strhodes goto cleanup; 349135446Strhodes } 350135446Strhodes 351135446Strhodes request = NULL; 352135446Strhodes 353135446Strhodes for (key = ISC_LIST_HEAD(listener->keys); 354135446Strhodes key != NULL; 355135446Strhodes key = ISC_LIST_NEXT(key, link)) 356135446Strhodes { 357135446Strhodes ccregion.rstart = isc_buffer_base(&conn->ccmsg.buffer); 358135446Strhodes ccregion.rend = isc_buffer_used(&conn->ccmsg.buffer); 359165071Sdougb if (secret.rstart != NULL) 360165071Sdougb isc_mem_put(listener->mctx, secret.rstart, 361165071Sdougb REGION_SIZE(secret)); 362135446Strhodes secret.rstart = isc_mem_get(listener->mctx, key->secret.length); 363135446Strhodes if (secret.rstart == NULL) 364135446Strhodes goto cleanup; 365135446Strhodes memcpy(secret.rstart, key->secret.base, key->secret.length); 366135446Strhodes secret.rend = secret.rstart + key->secret.length; 367135446Strhodes result = isccc_cc_fromwire(&ccregion, &request, &secret); 368135446Strhodes if (result == ISC_R_SUCCESS) 369135446Strhodes break; 370135446Strhodes else if (result == ISCCC_R_BADAUTH) { 371135446Strhodes /* 372135446Strhodes * For some reason, request is non-NULL when 373135446Strhodes * isccc_cc_fromwire returns ISCCC_R_BADAUTH. 374135446Strhodes */ 375135446Strhodes if (request != NULL) 376135446Strhodes isccc_sexpr_free(&request); 377135446Strhodes } else { 378135446Strhodes log_invalid(&conn->ccmsg, result); 379135446Strhodes goto cleanup; 380135446Strhodes } 381135446Strhodes } 382135446Strhodes 383135446Strhodes if (key == NULL) { 384135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); 385135446Strhodes goto cleanup; 386135446Strhodes } 387135446Strhodes 388135446Strhodes /* We shouldn't be getting a reply. */ 389135446Strhodes if (isccc_cc_isreply(request)) { 390135446Strhodes log_invalid(&conn->ccmsg, ISC_R_FAILURE); 391135446Strhodes goto cleanup; 392135446Strhodes } 393135446Strhodes 394135446Strhodes isc_stdtime_get(&now); 395135446Strhodes 396135446Strhodes /* 397135446Strhodes * Limit exposure to replay attacks. 398135446Strhodes */ 399135446Strhodes _ctrl = isccc_alist_lookup(request, "_ctrl"); 400135446Strhodes if (_ctrl == NULL) { 401135446Strhodes log_invalid(&conn->ccmsg, ISC_R_FAILURE); 402135446Strhodes goto cleanup; 403135446Strhodes } 404135446Strhodes 405135446Strhodes if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) { 406135446Strhodes if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) { 407135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW); 408135446Strhodes goto cleanup; 409135446Strhodes } 410135446Strhodes } else { 411135446Strhodes log_invalid(&conn->ccmsg, ISC_R_FAILURE); 412135446Strhodes goto cleanup; 413135446Strhodes } 414135446Strhodes 415135446Strhodes /* 416135446Strhodes * Expire messages that are too old. 417135446Strhodes */ 418135446Strhodes if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS && 419135446Strhodes now > exp) { 420135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED); 421135446Strhodes goto cleanup; 422135446Strhodes } 423135446Strhodes 424135446Strhodes /* 425135446Strhodes * Duplicate suppression (required for UDP). 426135446Strhodes */ 427135446Strhodes isccc_cc_cleansymtab(listener->controls->symtab, now); 428135446Strhodes result = isccc_cc_checkdup(listener->controls->symtab, request, now); 429135446Strhodes if (result != ISC_R_SUCCESS) { 430135446Strhodes if (result == ISC_R_EXISTS) 431135446Strhodes result = ISCCC_R_DUPLICATE; 432135446Strhodes log_invalid(&conn->ccmsg, result); 433135446Strhodes goto cleanup; 434135446Strhodes } 435135446Strhodes 436135446Strhodes if (conn->nonce != 0 && 437135446Strhodes (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS || 438135446Strhodes conn->nonce != nonce)) { 439135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); 440135446Strhodes goto cleanup; 441135446Strhodes } 442135446Strhodes 443135446Strhodes /* 444135446Strhodes * Establish nonce. 445135446Strhodes */ 446135446Strhodes while (conn->nonce == 0) 447135446Strhodes isc_random_get(&conn->nonce); 448135446Strhodes 449135446Strhodes isc_buffer_init(&text, textarray, sizeof(textarray)); 450135446Strhodes eresult = ns_control_docommand(request, &text); 451135446Strhodes 452135446Strhodes result = isccc_cc_createresponse(request, now, now + 60, &response); 453135446Strhodes if (result != ISC_R_SUCCESS) 454135446Strhodes goto cleanup; 455135446Strhodes if (eresult != ISC_R_SUCCESS) { 456135446Strhodes isccc_sexpr_t *data; 457135446Strhodes 458135446Strhodes data = isccc_alist_lookup(response, "_data"); 459135446Strhodes if (data != NULL) { 460135446Strhodes const char *estr = isc_result_totext(eresult); 461135446Strhodes if (isccc_cc_definestring(data, "err", estr) == NULL) 462135446Strhodes goto cleanup; 463135446Strhodes } 464135446Strhodes } 465135446Strhodes 466135446Strhodes if (isc_buffer_usedlength(&text) > 0) { 467135446Strhodes isccc_sexpr_t *data; 468135446Strhodes 469135446Strhodes data = isccc_alist_lookup(response, "_data"); 470135446Strhodes if (data != NULL) { 471135446Strhodes char *str = (char *)isc_buffer_base(&text); 472135446Strhodes if (isccc_cc_definestring(data, "text", str) == NULL) 473135446Strhodes goto cleanup; 474135446Strhodes } 475135446Strhodes } 476135446Strhodes 477135446Strhodes _ctrl = isccc_alist_lookup(response, "_ctrl"); 478135446Strhodes if (_ctrl == NULL || 479135446Strhodes isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL) 480135446Strhodes goto cleanup; 481135446Strhodes 482135446Strhodes ccregion.rstart = conn->buffer + 4; 483135446Strhodes ccregion.rend = conn->buffer + sizeof(conn->buffer); 484135446Strhodes result = isccc_cc_towire(response, &ccregion, &secret); 485135446Strhodes if (result != ISC_R_SUCCESS) 486135446Strhodes goto cleanup; 487135446Strhodes isc_buffer_init(&b, conn->buffer, 4); 488135446Strhodes len = sizeof(conn->buffer) - REGION_SIZE(ccregion); 489135446Strhodes isc_buffer_putuint32(&b, len - 4); 490135446Strhodes r.base = conn->buffer; 491135446Strhodes r.length = len; 492135446Strhodes 493135446Strhodes result = isc_socket_send(conn->sock, &r, task, control_senddone, conn); 494135446Strhodes if (result != ISC_R_SUCCESS) 495135446Strhodes goto cleanup; 496135446Strhodes conn->sending = ISC_TRUE; 497135446Strhodes 498135446Strhodes if (secret.rstart != NULL) 499135446Strhodes isc_mem_put(listener->mctx, secret.rstart, 500135446Strhodes REGION_SIZE(secret)); 501135446Strhodes if (request != NULL) 502135446Strhodes isccc_sexpr_free(&request); 503135446Strhodes if (response != NULL) 504135446Strhodes isccc_sexpr_free(&response); 505135446Strhodes return; 506135446Strhodes 507135446Strhodes cleanup: 508135446Strhodes if (secret.rstart != NULL) 509135446Strhodes isc_mem_put(listener->mctx, secret.rstart, 510135446Strhodes REGION_SIZE(secret)); 511135446Strhodes isc_socket_detach(&conn->sock); 512135446Strhodes isccc_ccmsg_invalidate(&conn->ccmsg); 513135446Strhodes conn->ccmsg_valid = ISC_FALSE; 514135446Strhodes maybe_free_connection(conn); 515135446Strhodes maybe_free_listener(listener); 516135446Strhodes if (request != NULL) 517135446Strhodes isccc_sexpr_free(&request); 518135446Strhodes if (response != NULL) 519135446Strhodes isccc_sexpr_free(&response); 520135446Strhodes} 521135446Strhodes 522135446Strhodesstatic void 523135446Strhodescontrol_timeout(isc_task_t *task, isc_event_t *event) { 524135446Strhodes controlconnection_t *conn = event->ev_arg; 525135446Strhodes 526135446Strhodes UNUSED(task); 527135446Strhodes 528135446Strhodes isc_timer_detach(&conn->timer); 529135446Strhodes maybe_free_connection(conn); 530135446Strhodes 531135446Strhodes isc_event_free(&event); 532135446Strhodes} 533135446Strhodes 534135446Strhodesstatic isc_result_t 535135446Strhodesnewconnection(controllistener_t *listener, isc_socket_t *sock) { 536135446Strhodes controlconnection_t *conn; 537135446Strhodes isc_interval_t interval; 538135446Strhodes isc_result_t result; 539135446Strhodes 540135446Strhodes conn = isc_mem_get(listener->mctx, sizeof(*conn)); 541135446Strhodes if (conn == NULL) 542135446Strhodes return (ISC_R_NOMEMORY); 543135446Strhodes 544135446Strhodes conn->sock = sock; 545135446Strhodes isccc_ccmsg_init(listener->mctx, sock, &conn->ccmsg); 546135446Strhodes conn->ccmsg_valid = ISC_TRUE; 547135446Strhodes conn->sending = ISC_FALSE; 548135446Strhodes conn->timer = NULL; 549135446Strhodes isc_interval_set(&interval, 60, 0); 550135446Strhodes result = isc_timer_create(ns_g_timermgr, isc_timertype_once, 551135446Strhodes NULL, &interval, listener->task, 552135446Strhodes control_timeout, conn, &conn->timer); 553135446Strhodes if (result != ISC_R_SUCCESS) 554135446Strhodes goto cleanup; 555135446Strhodes 556135446Strhodes conn->listener = listener; 557135446Strhodes conn->nonce = 0; 558135446Strhodes ISC_LINK_INIT(conn, link); 559135446Strhodes 560135446Strhodes result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task, 561135446Strhodes control_recvmessage, conn); 562135446Strhodes if (result != ISC_R_SUCCESS) 563135446Strhodes goto cleanup; 564135446Strhodes isccc_ccmsg_setmaxsize(&conn->ccmsg, 2048); 565135446Strhodes 566135446Strhodes ISC_LIST_APPEND(listener->connections, conn, link); 567135446Strhodes return (ISC_R_SUCCESS); 568135446Strhodes 569135446Strhodes cleanup: 570135446Strhodes isccc_ccmsg_invalidate(&conn->ccmsg); 571135446Strhodes if (conn->timer != NULL) 572135446Strhodes isc_timer_detach(&conn->timer); 573135446Strhodes isc_mem_put(listener->mctx, conn, sizeof(*conn)); 574135446Strhodes return (result); 575135446Strhodes} 576135446Strhodes 577135446Strhodesstatic void 578135446Strhodescontrol_newconn(isc_task_t *task, isc_event_t *event) { 579135446Strhodes isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event; 580135446Strhodes controllistener_t *listener = event->ev_arg; 581135446Strhodes isc_socket_t *sock; 582135446Strhodes isc_sockaddr_t peeraddr; 583135446Strhodes isc_result_t result; 584135446Strhodes 585135446Strhodes UNUSED(task); 586135446Strhodes 587135446Strhodes listener->listening = ISC_FALSE; 588135446Strhodes 589135446Strhodes if (nevent->result != ISC_R_SUCCESS) { 590135446Strhodes if (nevent->result == ISC_R_CANCELED) { 591135446Strhodes shutdown_listener(listener); 592135446Strhodes goto cleanup; 593135446Strhodes } 594135446Strhodes goto restart; 595135446Strhodes } 596135446Strhodes 597135446Strhodes sock = nevent->newsocket; 598135446Strhodes (void)isc_socket_getpeername(sock, &peeraddr); 599135446Strhodes if (!address_ok(&peeraddr, listener->acl)) { 600135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 601135446Strhodes isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 602135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 603135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 604135446Strhodes "rejected command channel message from %s", 605135446Strhodes socktext); 606135446Strhodes isc_socket_detach(&sock); 607135446Strhodes goto restart; 608135446Strhodes } 609135446Strhodes 610135446Strhodes result = newconnection(listener, sock); 611135446Strhodes if (result != ISC_R_SUCCESS) { 612135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 613135446Strhodes isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 614135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 615135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 616135446Strhodes "dropped command channel from %s: %s", 617135446Strhodes socktext, isc_result_totext(result)); 618135446Strhodes isc_socket_detach(&sock); 619135446Strhodes goto restart; 620135446Strhodes } 621135446Strhodes 622135446Strhodes restart: 623135446Strhodes control_next(listener); 624135446Strhodes cleanup: 625135446Strhodes isc_event_free(&event); 626135446Strhodes} 627135446Strhodes 628135446Strhodesstatic void 629135446Strhodescontrols_shutdown(ns_controls_t *controls) { 630135446Strhodes controllistener_t *listener; 631135446Strhodes controllistener_t *next; 632135446Strhodes 633135446Strhodes for (listener = ISC_LIST_HEAD(controls->listeners); 634135446Strhodes listener != NULL; 635135446Strhodes listener = next) 636135446Strhodes { 637135446Strhodes /* 638135446Strhodes * This is asynchronous. As listeners shut down, they will 639135446Strhodes * call their callbacks. 640135446Strhodes */ 641135446Strhodes next = ISC_LIST_NEXT(listener, link); 642135446Strhodes shutdown_listener(listener); 643135446Strhodes } 644135446Strhodes} 645135446Strhodes 646135446Strhodesvoid 647135446Strhodesns_controls_shutdown(ns_controls_t *controls) { 648135446Strhodes controls_shutdown(controls); 649135446Strhodes controls->shuttingdown = ISC_TRUE; 650135446Strhodes} 651135446Strhodes 652135446Strhodesstatic isc_result_t 653165071Sdougbcfgkeylist_find(const cfg_obj_t *keylist, const char *keyname, 654165071Sdougb const cfg_obj_t **objp) 655165071Sdougb{ 656165071Sdougb const cfg_listelt_t *element; 657135446Strhodes const char *str; 658165071Sdougb const cfg_obj_t *obj; 659135446Strhodes 660135446Strhodes for (element = cfg_list_first(keylist); 661135446Strhodes element != NULL; 662135446Strhodes element = cfg_list_next(element)) 663135446Strhodes { 664135446Strhodes obj = cfg_listelt_value(element); 665135446Strhodes str = cfg_obj_asstring(cfg_map_getname(obj)); 666135446Strhodes if (strcasecmp(str, keyname) == 0) 667135446Strhodes break; 668135446Strhodes } 669135446Strhodes if (element == NULL) 670135446Strhodes return (ISC_R_NOTFOUND); 671135446Strhodes obj = cfg_listelt_value(element); 672135446Strhodes *objp = obj; 673135446Strhodes return (ISC_R_SUCCESS); 674135446Strhodes} 675135446Strhodes 676135446Strhodesstatic isc_result_t 677165071Sdougbcontrolkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx, 678135446Strhodes controlkeylist_t *keyids) 679135446Strhodes{ 680165071Sdougb const cfg_listelt_t *element; 681135446Strhodes char *newstr = NULL; 682135446Strhodes const char *str; 683165071Sdougb const cfg_obj_t *obj; 684135446Strhodes controlkey_t *key = NULL; 685135446Strhodes 686135446Strhodes for (element = cfg_list_first(keylist); 687135446Strhodes element != NULL; 688135446Strhodes element = cfg_list_next(element)) 689135446Strhodes { 690135446Strhodes obj = cfg_listelt_value(element); 691135446Strhodes str = cfg_obj_asstring(obj); 692135446Strhodes newstr = isc_mem_strdup(mctx, str); 693135446Strhodes if (newstr == NULL) 694135446Strhodes goto cleanup; 695135446Strhodes key = isc_mem_get(mctx, sizeof(*key)); 696135446Strhodes if (key == NULL) 697135446Strhodes goto cleanup; 698135446Strhodes key->keyname = newstr; 699135446Strhodes key->secret.base = NULL; 700135446Strhodes key->secret.length = 0; 701135446Strhodes ISC_LINK_INIT(key, link); 702135446Strhodes ISC_LIST_APPEND(*keyids, key, link); 703135446Strhodes key = NULL; 704135446Strhodes newstr = NULL; 705135446Strhodes } 706135446Strhodes return (ISC_R_SUCCESS); 707135446Strhodes 708135446Strhodes cleanup: 709135446Strhodes if (newstr != NULL) 710135446Strhodes isc_mem_free(mctx, newstr); 711135446Strhodes if (key != NULL) 712135446Strhodes isc_mem_put(mctx, key, sizeof(*key)); 713135446Strhodes free_controlkeylist(keyids, mctx); 714135446Strhodes return (ISC_R_NOMEMORY); 715135446Strhodes} 716135446Strhodes 717135446Strhodesstatic void 718165071Sdougbregister_keys(const cfg_obj_t *control, const cfg_obj_t *keylist, 719135446Strhodes controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext) 720135446Strhodes{ 721135446Strhodes controlkey_t *keyid, *next; 722165071Sdougb const cfg_obj_t *keydef; 723135446Strhodes char secret[1024]; 724135446Strhodes isc_buffer_t b; 725135446Strhodes isc_result_t result; 726135446Strhodes 727135446Strhodes /* 728135446Strhodes * Find the keys corresponding to the keyids used by this listener. 729135446Strhodes */ 730135446Strhodes for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) { 731135446Strhodes next = ISC_LIST_NEXT(keyid, link); 732135446Strhodes 733135446Strhodes result = cfgkeylist_find(keylist, keyid->keyname, &keydef); 734135446Strhodes if (result != ISC_R_SUCCESS) { 735135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 736135446Strhodes "couldn't find key '%s' for use with " 737135446Strhodes "command channel %s", 738135446Strhodes keyid->keyname, socktext); 739135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 740135446Strhodes free_controlkey(keyid, mctx); 741135446Strhodes } else { 742165071Sdougb const cfg_obj_t *algobj = NULL; 743165071Sdougb const cfg_obj_t *secretobj = NULL; 744165071Sdougb const char *algstr = NULL; 745165071Sdougb const char *secretstr = NULL; 746135446Strhodes 747135446Strhodes (void)cfg_map_get(keydef, "algorithm", &algobj); 748135446Strhodes (void)cfg_map_get(keydef, "secret", &secretobj); 749135446Strhodes INSIST(algobj != NULL && secretobj != NULL); 750135446Strhodes 751135446Strhodes algstr = cfg_obj_asstring(algobj); 752135446Strhodes secretstr = cfg_obj_asstring(secretobj); 753135446Strhodes 754135446Strhodes if (ns_config_getkeyalgorithm(algstr, NULL) != 755135446Strhodes ISC_R_SUCCESS) 756135446Strhodes { 757135446Strhodes cfg_obj_log(control, ns_g_lctx, 758135446Strhodes ISC_LOG_WARNING, 759135446Strhodes "unsupported algorithm '%s' in " 760135446Strhodes "key '%s' for use with command " 761135446Strhodes "channel %s", 762135446Strhodes algstr, keyid->keyname, socktext); 763135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 764135446Strhodes free_controlkey(keyid, mctx); 765135446Strhodes continue; 766135446Strhodes } 767135446Strhodes 768135446Strhodes isc_buffer_init(&b, secret, sizeof(secret)); 769135446Strhodes result = isc_base64_decodestring(secretstr, &b); 770135446Strhodes 771135446Strhodes if (result != ISC_R_SUCCESS) { 772135446Strhodes cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING, 773135446Strhodes "secret for key '%s' on " 774135446Strhodes "command channel %s: %s", 775135446Strhodes keyid->keyname, socktext, 776135446Strhodes isc_result_totext(result)); 777135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 778135446Strhodes free_controlkey(keyid, mctx); 779135446Strhodes continue; 780135446Strhodes } 781135446Strhodes 782135446Strhodes keyid->secret.length = isc_buffer_usedlength(&b); 783135446Strhodes keyid->secret.base = isc_mem_get(mctx, 784135446Strhodes keyid->secret.length); 785135446Strhodes if (keyid->secret.base == NULL) { 786135446Strhodes cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING, 787135446Strhodes "couldn't register key '%s': " 788135446Strhodes "out of memory", keyid->keyname); 789135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 790135446Strhodes free_controlkey(keyid, mctx); 791135446Strhodes break; 792135446Strhodes } 793135446Strhodes memcpy(keyid->secret.base, isc_buffer_base(&b), 794135446Strhodes keyid->secret.length); 795135446Strhodes } 796135446Strhodes } 797135446Strhodes} 798135446Strhodes 799135446Strhodes#define CHECK(x) \ 800135446Strhodes do { \ 801135446Strhodes result = (x); \ 802135446Strhodes if (result != ISC_R_SUCCESS) \ 803135446Strhodes goto cleanup; \ 804135446Strhodes } while (0) 805135446Strhodes 806135446Strhodesstatic isc_result_t 807135446Strhodesget_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) { 808135446Strhodes isc_result_t result; 809135446Strhodes cfg_parser_t *pctx = NULL; 810135446Strhodes cfg_obj_t *config = NULL; 811165071Sdougb const cfg_obj_t *key = NULL; 812165071Sdougb const cfg_obj_t *algobj = NULL; 813165071Sdougb const cfg_obj_t *secretobj = NULL; 814165071Sdougb const char *algstr = NULL; 815165071Sdougb const char *secretstr = NULL; 816135446Strhodes controlkey_t *keyid = NULL; 817135446Strhodes char secret[1024]; 818135446Strhodes isc_buffer_t b; 819135446Strhodes 820135446Strhodes CHECK(cfg_parser_create(mctx, ns_g_lctx, &pctx)); 821135446Strhodes CHECK(cfg_parse_file(pctx, ns_g_keyfile, &cfg_type_rndckey, &config)); 822135446Strhodes CHECK(cfg_map_get(config, "key", &key)); 823135446Strhodes 824135446Strhodes keyid = isc_mem_get(mctx, sizeof(*keyid)); 825135446Strhodes if (keyid == NULL) 826135446Strhodes CHECK(ISC_R_NOMEMORY); 827135446Strhodes keyid->keyname = isc_mem_strdup(mctx, 828135446Strhodes cfg_obj_asstring(cfg_map_getname(key))); 829135446Strhodes keyid->secret.base = NULL; 830135446Strhodes keyid->secret.length = 0; 831135446Strhodes ISC_LINK_INIT(keyid, link); 832135446Strhodes if (keyid->keyname == NULL) 833135446Strhodes CHECK(ISC_R_NOMEMORY); 834135446Strhodes 835135446Strhodes CHECK(bind9_check_key(key, ns_g_lctx)); 836135446Strhodes 837135446Strhodes (void)cfg_map_get(key, "algorithm", &algobj); 838135446Strhodes (void)cfg_map_get(key, "secret", &secretobj); 839135446Strhodes INSIST(algobj != NULL && secretobj != NULL); 840135446Strhodes 841135446Strhodes algstr = cfg_obj_asstring(algobj); 842135446Strhodes secretstr = cfg_obj_asstring(secretobj); 843135446Strhodes 844135446Strhodes if (ns_config_getkeyalgorithm(algstr, NULL) != ISC_R_SUCCESS) { 845135446Strhodes cfg_obj_log(key, ns_g_lctx, 846135446Strhodes ISC_LOG_WARNING, 847135446Strhodes "unsupported algorithm '%s' in " 848135446Strhodes "key '%s' for use with command " 849135446Strhodes "channel", 850135446Strhodes algstr, keyid->keyname); 851135446Strhodes goto cleanup; 852135446Strhodes } 853135446Strhodes 854135446Strhodes isc_buffer_init(&b, secret, sizeof(secret)); 855135446Strhodes result = isc_base64_decodestring(secretstr, &b); 856135446Strhodes 857135446Strhodes if (result != ISC_R_SUCCESS) { 858135446Strhodes cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, 859135446Strhodes "secret for key '%s' on command channel: %s", 860135446Strhodes keyid->keyname, isc_result_totext(result)); 861135446Strhodes CHECK(result); 862135446Strhodes } 863135446Strhodes 864135446Strhodes keyid->secret.length = isc_buffer_usedlength(&b); 865135446Strhodes keyid->secret.base = isc_mem_get(mctx, 866135446Strhodes keyid->secret.length); 867135446Strhodes if (keyid->secret.base == NULL) { 868135446Strhodes cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, 869135446Strhodes "couldn't register key '%s': " 870135446Strhodes "out of memory", keyid->keyname); 871135446Strhodes CHECK(ISC_R_NOMEMORY); 872135446Strhodes } 873135446Strhodes memcpy(keyid->secret.base, isc_buffer_base(&b), 874135446Strhodes keyid->secret.length); 875135446Strhodes ISC_LIST_APPEND(*keyids, keyid, link); 876135446Strhodes keyid = NULL; 877135446Strhodes result = ISC_R_SUCCESS; 878135446Strhodes 879135446Strhodes cleanup: 880135446Strhodes if (keyid != NULL) 881135446Strhodes free_controlkey(keyid, mctx); 882135446Strhodes if (config != NULL) 883135446Strhodes cfg_obj_destroy(pctx, &config); 884135446Strhodes if (pctx != NULL) 885135446Strhodes cfg_parser_destroy(&pctx); 886135446Strhodes return (result); 887135446Strhodes} 888135446Strhodes 889135446Strhodes/* 890135446Strhodes * Ensures that both '*global_keylistp' and '*control_keylistp' are 891135446Strhodes * valid or both are NULL. 892135446Strhodes */ 893135446Strhodesstatic void 894165071Sdougbget_key_info(const cfg_obj_t *config, const cfg_obj_t *control, 895165071Sdougb const cfg_obj_t **global_keylistp, 896165071Sdougb const cfg_obj_t **control_keylistp) 897135446Strhodes{ 898135446Strhodes isc_result_t result; 899165071Sdougb const cfg_obj_t *control_keylist = NULL; 900165071Sdougb const cfg_obj_t *global_keylist = NULL; 901135446Strhodes 902135446Strhodes REQUIRE(global_keylistp != NULL && *global_keylistp == NULL); 903135446Strhodes REQUIRE(control_keylistp != NULL && *control_keylistp == NULL); 904135446Strhodes 905135446Strhodes control_keylist = cfg_tuple_get(control, "keys"); 906135446Strhodes 907135446Strhodes if (!cfg_obj_isvoid(control_keylist) && 908135446Strhodes cfg_list_first(control_keylist) != NULL) { 909135446Strhodes result = cfg_map_get(config, "key", &global_keylist); 910135446Strhodes 911135446Strhodes if (result == ISC_R_SUCCESS) { 912135446Strhodes *global_keylistp = global_keylist; 913135446Strhodes *control_keylistp = control_keylist; 914135446Strhodes } 915135446Strhodes } 916135446Strhodes} 917135446Strhodes 918135446Strhodesstatic void 919165071Sdougbupdate_listener(ns_controls_t *cp, controllistener_t **listenerp, 920165071Sdougb const cfg_obj_t *control, const cfg_obj_t *config, 921165071Sdougb isc_sockaddr_t *addr, ns_aclconfctx_t *aclconfctx, 922165071Sdougb const char *socktext) 923135446Strhodes{ 924135446Strhodes controllistener_t *listener; 925165071Sdougb const cfg_obj_t *allow; 926165071Sdougb const cfg_obj_t *global_keylist = NULL; 927165071Sdougb const cfg_obj_t *control_keylist = NULL; 928135446Strhodes dns_acl_t *new_acl = NULL; 929135446Strhodes controlkeylist_t keys; 930135446Strhodes isc_result_t result = ISC_R_SUCCESS; 931135446Strhodes 932135446Strhodes for (listener = ISC_LIST_HEAD(cp->listeners); 933135446Strhodes listener != NULL; 934135446Strhodes listener = ISC_LIST_NEXT(listener, link)) 935135446Strhodes if (isc_sockaddr_equal(addr, &listener->address)) 936135446Strhodes break; 937135446Strhodes 938135446Strhodes if (listener == NULL) { 939135446Strhodes *listenerp = NULL; 940135446Strhodes return; 941135446Strhodes } 942135446Strhodes 943135446Strhodes /* 944135446Strhodes * There is already a listener for this sockaddr. 945135446Strhodes * Update the access list and key information. 946135446Strhodes * 947135446Strhodes * First try to deal with the key situation. There are a few 948135446Strhodes * possibilities: 949135446Strhodes * (a) It had an explicit keylist and still has an explicit keylist. 950135446Strhodes * (b) It had an automagic key and now has an explicit keylist. 951135446Strhodes * (c) It had an explicit keylist and now needs an automagic key. 952135446Strhodes * (d) It has an automagic key and still needs the automagic key. 953135446Strhodes * 954135446Strhodes * (c) and (d) are the annoying ones. The caller needs to know 955135446Strhodes * that it should use the automagic configuration for key information 956135446Strhodes * in place of the named.conf configuration. 957135446Strhodes * 958135446Strhodes * XXXDCL There is one other hazard that has not been dealt with, 959135446Strhodes * the problem that if a key change is being caused by a control 960135446Strhodes * channel reload, then the response will be with the new key 961135446Strhodes * and not able to be decrypted by the client. 962135446Strhodes */ 963135446Strhodes if (control != NULL) 964135446Strhodes get_key_info(config, control, &global_keylist, 965135446Strhodes &control_keylist); 966135446Strhodes 967135446Strhodes if (control_keylist != NULL) { 968135446Strhodes INSIST(global_keylist != NULL); 969135446Strhodes 970135446Strhodes ISC_LIST_INIT(keys); 971135446Strhodes result = controlkeylist_fromcfg(control_keylist, 972135446Strhodes listener->mctx, &keys); 973135446Strhodes if (result == ISC_R_SUCCESS) { 974135446Strhodes free_controlkeylist(&listener->keys, listener->mctx); 975135446Strhodes listener->keys = keys; 976135446Strhodes register_keys(control, global_keylist, &listener->keys, 977135446Strhodes listener->mctx, socktext); 978135446Strhodes } 979135446Strhodes } else { 980135446Strhodes free_controlkeylist(&listener->keys, listener->mctx); 981135446Strhodes result = get_rndckey(listener->mctx, &listener->keys); 982135446Strhodes } 983135446Strhodes 984165071Sdougb if (result != ISC_R_SUCCESS && global_keylist != NULL) { 985135446Strhodes /* 986135446Strhodes * This message might be a little misleading since the 987135446Strhodes * "new keys" might in fact be identical to the old ones, 988135446Strhodes * but tracking whether they are identical just for the 989135446Strhodes * sake of avoiding this message would be too much trouble. 990135446Strhodes */ 991165071Sdougb if (control != NULL) 992165071Sdougb cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 993165071Sdougb "couldn't install new keys for " 994165071Sdougb "command channel %s: %s", 995165071Sdougb socktext, isc_result_totext(result)); 996165071Sdougb else 997165071Sdougb isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 998165071Sdougb NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 999165071Sdougb "couldn't install new keys for " 1000165071Sdougb "command channel %s: %s", 1001165071Sdougb socktext, isc_result_totext(result)); 1002165071Sdougb } 1003135446Strhodes 1004135446Strhodes /* 1005135446Strhodes * Now, keep the old access list unless a new one can be made. 1006135446Strhodes */ 1007135446Strhodes if (control != NULL) { 1008135446Strhodes allow = cfg_tuple_get(control, "allow"); 1009135446Strhodes result = ns_acl_fromconfig(allow, config, aclconfctx, 1010135446Strhodes listener->mctx, &new_acl); 1011135446Strhodes } else { 1012135446Strhodes result = dns_acl_any(listener->mctx, &new_acl); 1013135446Strhodes } 1014135446Strhodes 1015135446Strhodes if (result == ISC_R_SUCCESS) { 1016135446Strhodes dns_acl_detach(&listener->acl); 1017135446Strhodes dns_acl_attach(new_acl, &listener->acl); 1018135446Strhodes dns_acl_detach(&new_acl); 1019135446Strhodes /* XXXDCL say the old acl is still used? */ 1020165071Sdougb } else if (control != NULL) 1021135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1022135446Strhodes "couldn't install new acl for " 1023135446Strhodes "command channel %s: %s", 1024135446Strhodes socktext, isc_result_totext(result)); 1025165071Sdougb else 1026165071Sdougb isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 1027165071Sdougb NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 1028165071Sdougb "couldn't install new acl for " 1029165071Sdougb "command channel %s: %s", 1030165071Sdougb socktext, isc_result_totext(result)); 1031135446Strhodes 1032135446Strhodes *listenerp = listener; 1033135446Strhodes} 1034135446Strhodes 1035135446Strhodesstatic void 1036135446Strhodesadd_listener(ns_controls_t *cp, controllistener_t **listenerp, 1037165071Sdougb const cfg_obj_t *control, const cfg_obj_t *config, 1038165071Sdougb isc_sockaddr_t *addr, ns_aclconfctx_t *aclconfctx, 1039165071Sdougb const char *socktext) 1040135446Strhodes{ 1041135446Strhodes isc_mem_t *mctx = cp->server->mctx; 1042135446Strhodes controllistener_t *listener; 1043165071Sdougb const cfg_obj_t *allow; 1044165071Sdougb const cfg_obj_t *global_keylist = NULL; 1045165071Sdougb const cfg_obj_t *control_keylist = NULL; 1046135446Strhodes dns_acl_t *new_acl = NULL; 1047135446Strhodes isc_result_t result = ISC_R_SUCCESS; 1048135446Strhodes 1049135446Strhodes listener = isc_mem_get(mctx, sizeof(*listener)); 1050135446Strhodes if (listener == NULL) 1051135446Strhodes result = ISC_R_NOMEMORY; 1052135446Strhodes 1053135446Strhodes if (result == ISC_R_SUCCESS) { 1054135446Strhodes listener->controls = cp; 1055135446Strhodes listener->mctx = mctx; 1056135446Strhodes listener->task = cp->server->task; 1057135446Strhodes listener->address = *addr; 1058135446Strhodes listener->sock = NULL; 1059135446Strhodes listener->listening = ISC_FALSE; 1060135446Strhodes listener->exiting = ISC_FALSE; 1061135446Strhodes listener->acl = NULL; 1062135446Strhodes ISC_LINK_INIT(listener, link); 1063135446Strhodes ISC_LIST_INIT(listener->keys); 1064135446Strhodes ISC_LIST_INIT(listener->connections); 1065135446Strhodes 1066135446Strhodes /* 1067135446Strhodes * Make the acl. 1068135446Strhodes */ 1069135446Strhodes if (control != NULL) { 1070135446Strhodes allow = cfg_tuple_get(control, "allow"); 1071135446Strhodes result = ns_acl_fromconfig(allow, config, aclconfctx, 1072135446Strhodes mctx, &new_acl); 1073135446Strhodes } else { 1074135446Strhodes result = dns_acl_any(mctx, &new_acl); 1075135446Strhodes } 1076135446Strhodes } 1077135446Strhodes 1078135446Strhodes if (result == ISC_R_SUCCESS) { 1079135446Strhodes dns_acl_attach(new_acl, &listener->acl); 1080135446Strhodes dns_acl_detach(&new_acl); 1081135446Strhodes 1082135446Strhodes if (config != NULL) 1083135446Strhodes get_key_info(config, control, &global_keylist, 1084135446Strhodes &control_keylist); 1085135446Strhodes 1086135446Strhodes if (control_keylist != NULL) { 1087135446Strhodes result = controlkeylist_fromcfg(control_keylist, 1088135446Strhodes listener->mctx, 1089135446Strhodes &listener->keys); 1090135446Strhodes if (result == ISC_R_SUCCESS) 1091135446Strhodes register_keys(control, global_keylist, 1092135446Strhodes &listener->keys, 1093135446Strhodes listener->mctx, socktext); 1094135446Strhodes } else 1095135446Strhodes result = get_rndckey(mctx, &listener->keys); 1096135446Strhodes 1097135446Strhodes if (result != ISC_R_SUCCESS && control != NULL) 1098135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1099135446Strhodes "couldn't install keys for " 1100135446Strhodes "command channel %s: %s", 1101135446Strhodes socktext, isc_result_totext(result)); 1102135446Strhodes } 1103135446Strhodes 1104135446Strhodes if (result == ISC_R_SUCCESS) { 1105135446Strhodes int pf = isc_sockaddr_pf(&listener->address); 1106135446Strhodes if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) || 1107135446Strhodes (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) 1108135446Strhodes result = ISC_R_FAMILYNOSUPPORT; 1109135446Strhodes } 1110135446Strhodes 1111135446Strhodes if (result == ISC_R_SUCCESS) 1112135446Strhodes result = isc_socket_create(ns_g_socketmgr, 1113135446Strhodes isc_sockaddr_pf(&listener->address), 1114135446Strhodes isc_sockettype_tcp, 1115135446Strhodes &listener->sock); 1116135446Strhodes 1117135446Strhodes if (result == ISC_R_SUCCESS) 1118135446Strhodes result = isc_socket_bind(listener->sock, 1119135446Strhodes &listener->address); 1120135446Strhodes 1121135446Strhodes if (result == ISC_R_SUCCESS) 1122135446Strhodes result = control_listen(listener); 1123135446Strhodes 1124135446Strhodes if (result == ISC_R_SUCCESS) 1125135446Strhodes result = control_accept(listener); 1126135446Strhodes 1127135446Strhodes if (result == ISC_R_SUCCESS) { 1128135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 1129135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 1130135446Strhodes "command channel listening on %s", socktext); 1131135446Strhodes *listenerp = listener; 1132135446Strhodes 1133135446Strhodes } else { 1134135446Strhodes if (listener != NULL) { 1135135446Strhodes listener->exiting = ISC_TRUE; 1136135446Strhodes free_listener(listener); 1137135446Strhodes } 1138135446Strhodes 1139135446Strhodes if (control != NULL) 1140135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1141135446Strhodes "couldn't add command channel %s: %s", 1142135446Strhodes socktext, isc_result_totext(result)); 1143135446Strhodes else 1144135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 1145135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 1146135446Strhodes "couldn't add command channel %s: %s", 1147135446Strhodes socktext, isc_result_totext(result)); 1148135446Strhodes 1149135446Strhodes *listenerp = NULL; 1150135446Strhodes } 1151135446Strhodes 1152135446Strhodes /* XXXDCL return error results? fail hard? */ 1153135446Strhodes} 1154135446Strhodes 1155135446Strhodesisc_result_t 1156165071Sdougbns_controls_configure(ns_controls_t *cp, const cfg_obj_t *config, 1157135446Strhodes ns_aclconfctx_t *aclconfctx) 1158135446Strhodes{ 1159135446Strhodes controllistener_t *listener; 1160135446Strhodes controllistenerlist_t new_listeners; 1161165071Sdougb const cfg_obj_t *controlslist = NULL; 1162165071Sdougb const cfg_listelt_t *element, *element2; 1163135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 1164135446Strhodes 1165135446Strhodes ISC_LIST_INIT(new_listeners); 1166135446Strhodes 1167135446Strhodes /* 1168135446Strhodes * Get the list of named.conf 'controls' statements. 1169135446Strhodes */ 1170135446Strhodes (void)cfg_map_get(config, "controls", &controlslist); 1171135446Strhodes 1172135446Strhodes /* 1173135446Strhodes * Run through the new control channel list, noting sockets that 1174135446Strhodes * are already being listened on and moving them to the new list. 1175135446Strhodes * 1176135446Strhodes * Identifying duplicate addr/port combinations is left to either 1177135446Strhodes * the underlying config code, or to the bind attempt getting an 1178135446Strhodes * address-in-use error. 1179135446Strhodes */ 1180135446Strhodes if (controlslist != NULL) { 1181135446Strhodes for (element = cfg_list_first(controlslist); 1182135446Strhodes element != NULL; 1183135446Strhodes element = cfg_list_next(element)) { 1184165071Sdougb const cfg_obj_t *controls; 1185165071Sdougb const cfg_obj_t *inetcontrols = NULL; 1186135446Strhodes 1187135446Strhodes controls = cfg_listelt_value(element); 1188135446Strhodes (void)cfg_map_get(controls, "inet", &inetcontrols); 1189135446Strhodes if (inetcontrols == NULL) 1190135446Strhodes continue; 1191135446Strhodes 1192135446Strhodes for (element2 = cfg_list_first(inetcontrols); 1193135446Strhodes element2 != NULL; 1194135446Strhodes element2 = cfg_list_next(element2)) { 1195165071Sdougb const cfg_obj_t *control; 1196165071Sdougb const cfg_obj_t *obj; 1197165071Sdougb isc_sockaddr_t addr; 1198135446Strhodes 1199135446Strhodes /* 1200135446Strhodes * The parser handles BIND 8 configuration file 1201135446Strhodes * syntax, so it allows unix phrases as well 1202135446Strhodes * inet phrases with no keys{} clause. 1203135446Strhodes * 1204135446Strhodes * "unix" phrases have been reported as 1205135446Strhodes * unsupported by the parser. 1206135446Strhodes */ 1207135446Strhodes control = cfg_listelt_value(element2); 1208135446Strhodes 1209135446Strhodes obj = cfg_tuple_get(control, "address"); 1210165071Sdougb addr = *cfg_obj_assockaddr(obj); 1211165071Sdougb if (isc_sockaddr_getport(&addr) == 0) 1212165071Sdougb isc_sockaddr_setport(&addr, 1213135446Strhodes NS_CONTROL_PORT); 1214135446Strhodes 1215165071Sdougb isc_sockaddr_format(&addr, socktext, 1216135446Strhodes sizeof(socktext)); 1217135446Strhodes 1218135446Strhodes isc_log_write(ns_g_lctx, 1219135446Strhodes NS_LOGCATEGORY_GENERAL, 1220135446Strhodes NS_LOGMODULE_CONTROL, 1221135446Strhodes ISC_LOG_DEBUG(9), 1222135446Strhodes "processing control channel %s", 1223135446Strhodes socktext); 1224135446Strhodes 1225135446Strhodes update_listener(cp, &listener, control, config, 1226165071Sdougb &addr, aclconfctx, socktext); 1227135446Strhodes 1228135446Strhodes if (listener != NULL) 1229135446Strhodes /* 1230135446Strhodes * Remove the listener from the old 1231135446Strhodes * list, so it won't be shut down. 1232135446Strhodes */ 1233135446Strhodes ISC_LIST_UNLINK(cp->listeners, 1234135446Strhodes listener, link); 1235135446Strhodes else 1236135446Strhodes /* 1237135446Strhodes * This is a new listener. 1238135446Strhodes */ 1239135446Strhodes add_listener(cp, &listener, control, 1240165071Sdougb config, &addr, aclconfctx, 1241135446Strhodes socktext); 1242135446Strhodes 1243135446Strhodes if (listener != NULL) 1244135446Strhodes ISC_LIST_APPEND(new_listeners, 1245135446Strhodes listener, link); 1246135446Strhodes } 1247135446Strhodes } 1248135446Strhodes } else { 1249135446Strhodes int i; 1250135446Strhodes 1251135446Strhodes for (i = 0; i < 2; i++) { 1252135446Strhodes isc_sockaddr_t addr; 1253135446Strhodes 1254135446Strhodes if (i == 0) { 1255135446Strhodes struct in_addr localhost; 1256135446Strhodes 1257135446Strhodes if (isc_net_probeipv4() != ISC_R_SUCCESS) 1258135446Strhodes continue; 1259135446Strhodes localhost.s_addr = htonl(INADDR_LOOPBACK); 1260135446Strhodes isc_sockaddr_fromin(&addr, &localhost, 0); 1261135446Strhodes } else { 1262135446Strhodes if (isc_net_probeipv6() != ISC_R_SUCCESS) 1263135446Strhodes continue; 1264135446Strhodes isc_sockaddr_fromin6(&addr, 1265135446Strhodes &in6addr_loopback, 0); 1266135446Strhodes } 1267135446Strhodes isc_sockaddr_setport(&addr, NS_CONTROL_PORT); 1268135446Strhodes 1269135446Strhodes isc_sockaddr_format(&addr, socktext, sizeof(socktext)); 1270135446Strhodes 1271135446Strhodes update_listener(cp, &listener, NULL, NULL, 1272135446Strhodes &addr, NULL, socktext); 1273135446Strhodes 1274135446Strhodes if (listener != NULL) 1275135446Strhodes /* 1276135446Strhodes * Remove the listener from the old 1277135446Strhodes * list, so it won't be shut down. 1278135446Strhodes */ 1279135446Strhodes ISC_LIST_UNLINK(cp->listeners, 1280135446Strhodes listener, link); 1281135446Strhodes else 1282135446Strhodes /* 1283135446Strhodes * This is a new listener. 1284135446Strhodes */ 1285135446Strhodes add_listener(cp, &listener, NULL, NULL, 1286135446Strhodes &addr, NULL, socktext); 1287135446Strhodes 1288135446Strhodes if (listener != NULL) 1289135446Strhodes ISC_LIST_APPEND(new_listeners, 1290135446Strhodes listener, link); 1291135446Strhodes } 1292135446Strhodes } 1293135446Strhodes 1294135446Strhodes /* 1295135446Strhodes * ns_control_shutdown() will stop whatever is on the global 1296135446Strhodes * listeners list, which currently only has whatever sockaddrs 1297135446Strhodes * were in the previous configuration (if any) that do not 1298135446Strhodes * remain in the current configuration. 1299135446Strhodes */ 1300135446Strhodes controls_shutdown(cp); 1301135446Strhodes 1302135446Strhodes /* 1303135446Strhodes * Put all of the valid listeners on the listeners list. 1304135446Strhodes * Anything already on listeners in the process of shutting 1305135446Strhodes * down will be taken care of by listen_done(). 1306135446Strhodes */ 1307135446Strhodes ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link); 1308135446Strhodes return (ISC_R_SUCCESS); 1309135446Strhodes} 1310135446Strhodes 1311135446Strhodesisc_result_t 1312135446Strhodesns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp) { 1313135446Strhodes isc_mem_t *mctx = server->mctx; 1314135446Strhodes isc_result_t result; 1315135446Strhodes ns_controls_t *controls = isc_mem_get(mctx, sizeof(*controls)); 1316135446Strhodes 1317135446Strhodes if (controls == NULL) 1318135446Strhodes return (ISC_R_NOMEMORY); 1319135446Strhodes controls->server = server; 1320135446Strhodes ISC_LIST_INIT(controls->listeners); 1321135446Strhodes controls->shuttingdown = ISC_FALSE; 1322135446Strhodes controls->symtab = NULL; 1323135446Strhodes result = isccc_cc_createsymtab(&controls->symtab); 1324135446Strhodes if (result != ISC_R_SUCCESS) { 1325135446Strhodes isc_mem_put(server->mctx, controls, sizeof(*controls)); 1326135446Strhodes return (result); 1327135446Strhodes } 1328135446Strhodes *ctrlsp = controls; 1329135446Strhodes return (ISC_R_SUCCESS); 1330135446Strhodes} 1331135446Strhodes 1332135446Strhodesvoid 1333135446Strhodesns_controls_destroy(ns_controls_t **ctrlsp) { 1334135446Strhodes ns_controls_t *controls = *ctrlsp; 1335135446Strhodes 1336135446Strhodes REQUIRE(ISC_LIST_EMPTY(controls->listeners)); 1337135446Strhodes 1338135446Strhodes isccc_symtab_destroy(&controls->symtab); 1339135446Strhodes isc_mem_put(controls->server->mctx, controls, sizeof(*controls)); 1340135446Strhodes *ctrlsp = NULL; 1341135446Strhodes} 1342