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.
This commit is contained in:
Mark Andrews 2003-07-17 06:36:47 +00:00
parent 57fe5960b5
commit edb8af0b14
7 changed files with 185 additions and 13 deletions

View file

@ -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.

View file

@ -15,7 +15,7 @@
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: control.c,v 1.7 2001/05/31 01:21:06 bwelling Exp $ */
/* $Id: control.c,v 1.7.2.1 2003/07/17 06:36:45 marka Exp $ */
#include <config.h>
@ -110,6 +110,8 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) {
result = ns_server_flushcache(ns_g_server, command);
} else if (command_compare(command, NS_COMMAND_STATUS)) {
result = ns_server_status(ns_g_server, text);
} 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,

View file

@ -15,7 +15,7 @@
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: controlconf.c,v 1.28.2.7 2001/11/27 22:38:14 gson Exp $ */
/* $Id: controlconf.c,v 1.28.2.8 2003/07/17 06:36:45 marka Exp $ */
#include <config.h>
@ -45,6 +45,7 @@
#include <isccc/events.h>
#include <isccc/result.h>
#include <isccc/sexpr.h>
#include <isccc/symtab.h>
#include <isccc/util.h>
#include <dns/keyvalues.h>
@ -86,6 +87,7 @@ struct controlconnection {
isc_timer_t * timer;
unsigned char buffer[2048];
controllistener_t * listener;
isc_uint32_t nonce;
ISC_LINK(controlconnection_t) link;
};
@ -107,11 +109,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)
@ -327,6 +332,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);
@ -387,10 +396,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;
@ -416,6 +479,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);
@ -491,6 +559,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,
@ -1230,12 +1299,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);
}
@ -1246,6 +1323,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;
}

View file

@ -15,7 +15,7 @@
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: control.h,v 1.6 2001/05/08 04:09:40 bwelling Exp $ */
/* $Id: control.h,v 1.6.2.1 2003/07/17 06:36:45 marka Exp $ */
#ifndef NAMED_CONTROL_H
#define NAMED_CONTROL_H 1
@ -43,6 +43,7 @@
#define NS_COMMAND_NOTRACE "notrace"
#define NS_COMMAND_FLUSH "flush"
#define NS_COMMAND_STATUS "status"
#define NS_COMMAND_NULL "null"
isc_result_t
ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp);

View file

@ -15,7 +15,7 @@
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: rndc.c,v 1.77.2.3 2003/05/15 01:17:55 marka Exp $ */
/* $Id: rndc.c,v 1.77.2.4 2003/07/17 06:36:46 marka Exp $ */
/*
* Principal Author: DCL
@ -32,6 +32,7 @@
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/netdb.h>
#include <isc/random.h>
#include <isc/socket.h>
#include <isc/stdtime.h>
#include <isc/string.h>
@ -85,6 +86,7 @@ static char *command;
static char *args;
static char program[256];
static isc_socket_t *sock = NULL;
static isc_uint32_t serial;
static void
usage(int status) {
@ -256,6 +258,83 @@ rndc_recvdone(isc_task_t *task, isc_event_t *event) {
isc_app_shutdown();
}
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;
@ -274,13 +353,12 @@ rndc_connected(isc_task_t *task, isc_event_t *event) {
fatal("connect failed: %s", isc_result_totext(sevent->result));
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);
@ -295,13 +373,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
@ -550,6 +627,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));

View file

@ -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.3.2.1 2003/07/17 06:36:47 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

View file

@ -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.3.2.1 2003/07/17 06:36:46 marka Exp $ */
#include <config.h>
@ -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