From 72ddc4cef9c6a6de53aae530dea1ddbb90631131 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 17 Jul 2003 06:24:44 +0000 Subject: [PATCH] 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. --- CHANGES | 6 ++ bin/named/control.c | 4 +- bin/named/controlconf.c | 83 +++++++++++++++++++++++++++- bin/named/include/named/control.h | 3 +- bin/rndc/rndc.c | 91 +++++++++++++++++++++++++++++-- lib/isccc/include/isccc/result.h | 7 ++- lib/isccc/result.c | 5 +- 7 files changed, 186 insertions(+), 13 deletions(-) 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