diff --git a/CHANGES b/CHANGES index 7c0c114e9a..ce8a7c244c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +1480. [bug] Provide replay protection for rndc commands. Full + replay protection requires both rndc and named to + be updated. Partial replay protection (limited + exposure after restart) is provided if just named + is updated. + 1479. [bug] cfg_create_tuple() failed to handle out of memory cleanup. parse_list() would leak memory on syntax errors. diff --git a/bin/named/control.c b/bin/named/control.c index 914207fa6d..ed7a4cdc43 100644 --- a/bin/named/control.c +++ b/bin/named/control.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: control.c,v 1.17 2002/09/10 05:07:56 marka Exp $ */ +/* $Id: control.c,v 1.18 2003/07/17 06:24:43 marka Exp $ */ #include @@ -127,6 +127,8 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { } else if (command_compare(command, NS_COMMAND_TIMERPOKE)) { result = ISC_R_SUCCESS; isc_timermgr_poke(ns_g_timermgr); + } else if (command_compare(command, NS_COMMAND_NULL)) { + result = ISC_R_SUCCESS; } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c index 6b57e14651..0b8df3e481 100644 --- a/bin/named/controlconf.c +++ b/bin/named/controlconf.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: controlconf.c,v 1.38 2002/02/20 03:33:14 marka Exp $ */ +/* $Id: controlconf.c,v 1.39 2003/07/17 06:24:43 marka Exp $ */ #include @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include #include @@ -79,6 +81,7 @@ struct controlconnection { isc_timer_t * timer; unsigned char buffer[2048]; controllistener_t * listener; + isc_uint32_t nonce; ISC_LINK(controlconnection_t) link; }; @@ -100,11 +103,14 @@ 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) @@ -320,6 +326,10 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { 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); @@ -380,10 +390,64 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { 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); - isc_stdtime_get(&now); result = isccc_cc_createresponse(request, now, now + 60, &response); if (result != ISC_R_SUCCESS) goto cleanup; @@ -409,6 +473,11 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { } } + _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); @@ -484,6 +553,7 @@ newconnection(controllistener_t *listener, isc_socket_t *sock) { goto cleanup; conn->listener = listener; + conn->nonce = 0; ISC_LINK_INIT(conn, link); result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task, @@ -1223,12 +1293,20 @@ ns_controls_configure(ns_controls_t *cp, cfg_obj_t *config, 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); } @@ -1239,6 +1317,7 @@ ns_controls_destroy(ns_controls_t **ctrlsp) { REQUIRE(ISC_LIST_EMPTY(controls->listeners)); + isccc_symtab_destroy(&controls->symtab); isc_mem_put(controls->server->mctx, controls, sizeof(*controls)); *ctrlsp = NULL; } diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h index 43b26125aa..44049d1546 100644 --- a/bin/named/include/named/control.h +++ b/bin/named/include/named/control.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: control.h,v 1.12 2002/09/10 04:45:54 marka Exp $ */ +/* $Id: control.h,v 1.13 2003/07/17 06:24:43 marka Exp $ */ #ifndef NAMED_CONTROL_H #define NAMED_CONTROL_H 1 @@ -49,6 +49,7 @@ #define NS_COMMAND_UNFREEZE "unfreeze" #define NS_COMMAND_TIMERPOKE "timerpoke" #define NS_COMMAND_RECURSING "recursing" +#define NS_COMMAND_NULL "null" isc_result_t ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp); diff --git a/bin/rndc/rndc.c b/bin/rndc/rndc.c index 9a50816146..f5be1d0451 100644 --- a/bin/rndc/rndc.c +++ b/bin/rndc/rndc.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rndc.c,v 1.94 2002/09/19 02:40:15 marka Exp $ */ +/* $Id: rndc.c,v 1.95 2003/07/17 06:24:43 marka Exp $ */ /* * Principal Author: DCL @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,7 @@ static char *command; static char *args; static char program[256]; static isc_socket_t *sock = NULL; +static isc_uint32_t serial; static void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task); @@ -206,6 +208,83 @@ rndc_recvdone(isc_task_t *task, isc_event_t *event) { RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS); } +static void +rndc_recvnonce(isc_task_t *task, isc_event_t *event) { + isccc_sexpr_t *response = NULL; + isccc_sexpr_t *_ctrl; + isccc_region_t source; + isc_result_t result; + isc_uint32_t nonce; + isccc_sexpr_t *request = NULL; + isccc_time_t now; + isc_region_t r; + isccc_sexpr_t *data; + isccc_region_t message; + isc_uint32_t len; + isc_buffer_t b; + + recvs--; + + if (ccmsg.result == ISC_R_EOF) + fatal("connection to remote host closed\n" + "This may indicate that the remote server is using " + "an older version of \n" + "the command protocol, this host is not authorized " + "to connect,\nor the key is invalid."); + + if (ccmsg.result != ISC_R_SUCCESS) + fatal("recv failed: %s", isc_result_totext(ccmsg.result)); + + source.rstart = isc_buffer_base(&ccmsg.buffer); + source.rend = isc_buffer_used(&ccmsg.buffer); + + DO("parse message", isccc_cc_fromwire(&source, &response, &secret)); + + _ctrl = isccc_alist_lookup(response, "_ctrl"); + if (_ctrl == NULL) + fatal("_ctrl section missing"); + nonce = 0; + if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS) + nonce = 0; + + isc_stdtime_get(&now); + + DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial, + now, now + 60, &request)); + data = isccc_alist_lookup(request, "_data"); + if (data == NULL) + fatal("_data section missing"); + if (isccc_cc_definestring(data, "type", args) == NULL) + fatal("out of memory"); + if (nonce != 0) { + _ctrl = isccc_alist_lookup(request, "_ctrl"); + if (_ctrl == NULL) + fatal("_ctrl section missing"); + if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL) + fatal("out of memory"); + } + message.rstart = databuf + 4; + message.rend = databuf + sizeof(databuf); + DO("render message", isccc_cc_towire(request, &message, &secret)); + len = sizeof(databuf) - REGION_SIZE(message); + isc_buffer_init(&b, databuf, 4); + isc_buffer_putuint32(&b, len - 4); + r.length = len; + r.base = databuf; + + isccc_ccmsg_cancelread(&ccmsg); + DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task, + rndc_recvdone, NULL)); + recvs++; + DO("send message", isc_socket_send(sock, &r, task, rndc_senddone, + NULL)); + sends++; + + isc_event_free(&event); + isccc_sexpr_free(&response); + return; +} + static void rndc_connected(isc_task_t *task, isc_event_t *event) { isc_socketevent_t *sevent = (isc_socketevent_t *)event; @@ -236,13 +315,12 @@ rndc_connected(isc_task_t *task, isc_event_t *event) { } isc_stdtime_get(&now); - srandom(now + isc_thread_self()); - DO("create message", isccc_cc_createmessage(1, NULL, NULL, random(), + DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial, now, now + 60, &request)); data = isccc_alist_lookup(request, "_data"); if (data == NULL) fatal("_data section missing"); - if (isccc_cc_definestring(data, "type", args) == NULL) + if (isccc_cc_definestring(data, "type", "null") == NULL) fatal("out of memory"); message.rstart = databuf + 4; message.rend = databuf + sizeof(databuf); @@ -257,13 +335,12 @@ rndc_connected(isc_task_t *task, isc_event_t *event) { isccc_ccmsg_setmaxsize(&ccmsg, 1024); DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task, - rndc_recvdone, NULL)); + rndc_recvnonce, NULL)); recvs++; DO("send message", isc_socket_send(sock, &r, task, rndc_senddone, NULL)); sends++; isc_event_free(&event); - } static void @@ -520,6 +597,8 @@ main(int argc, char **argv) { if (argc < 1) usage(1); + isc_random_get(&serial); + DO("create memory context", isc_mem_create(0, 0, &mctx)); DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr)); DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr)); diff --git a/lib/isccc/include/isccc/result.h b/lib/isccc/include/isccc/result.h index 1cd3a8d232..9628ae8dda 100644 --- a/lib/isccc/include/isccc/result.h +++ b/lib/isccc/include/isccc/result.h @@ -16,7 +16,7 @@ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.h,v 1.3 2001/03/28 23:11:41 bwelling Exp $ */ +/* $Id: result.h,v 1.4 2003/07/17 06:24:44 marka Exp $ */ #ifndef ISCCC_RESULT_H #define ISCCC_RESULT_H 1 @@ -30,8 +30,11 @@ #define ISCCC_R_UNKNOWNVERSION (ISC_RESULTCLASS_ISCCC + 0) #define ISCCC_R_SYNTAX (ISC_RESULTCLASS_ISCCC + 1) #define ISCCC_R_BADAUTH (ISC_RESULTCLASS_ISCCC + 2) +#define ISCCC_R_EXPIRED (ISC_RESULTCLASS_ISCCC + 3) +#define ISCCC_R_CLOCKSKEW (ISC_RESULTCLASS_ISCCC + 4) +#define ISCCC_R_DUPLICATE (ISC_RESULTCLASS_ISCCC + 5) -#define ISCCC_R_NRESULTS 3 /* Number of results */ +#define ISCCC_R_NRESULTS 6 /* Number of results */ ISC_LANG_BEGINDECLS diff --git a/lib/isccc/result.c b/lib/isccc/result.c index 529e0fe283..58e6f432ca 100644 --- a/lib/isccc/result.c +++ b/lib/isccc/result.c @@ -16,7 +16,7 @@ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.c,v 1.3 2001/03/28 23:11:40 bwelling Exp $ */ +/* $Id: result.c,v 1.4 2003/07/17 06:24:44 marka Exp $ */ #include @@ -30,6 +30,9 @@ static const char *text[ISCCC_R_NRESULTS] = { "unknown version", /* 1 */ "syntax error", /* 2 */ "bad auth", /* 3 */ + "expired", /* 4 */ + "clock skew", /* 5 */ + "duplicate" /* 6 */ }; #define ISCCC_RESULT_RESULTSET 2