Commit b5f6271f authored by Mark Andrews's avatar Mark Andrews
Browse files

3744. [experimental] SIT: send and process Source Identity Tokens

                        (which are similar to DNS Cookies by Donald Eastlake)
                        and are designed to help clients detect off path
                        spoofed responses and for servers to detect legitimate
                        clients.

                        SIT use a experimental EDNS option code (65001).

                        SIT can be enabled via --enable-developer or
                        --enable-sit.  It is on by default in Windows.

                        RRL processing as been updated to know about SIT with
                        legitimate clients not being rate limited. [RT #35389]
parent 43c1433e
3744. [experimental] SIT: send and process Source Identity Tokens
(which are similar to DNS Cookies by Donald Eastlake)
and are designed to help clients detect off path
spoofed responses and for servers to detect legitimate
clients.
SIT use a experimental EDNS option code (65001).
SIT can be enabled via --enable-developer or
--enable-sit. It is on by default in Windows.
RRL processing as been updated to know about SIT with
legitimate clients not being rate limited. [RT #35389]
3743. [bug] delegation-only flag wasn't working in forward zone 3743. [bug] delegation-only flag wasn't working in forward zone
declarations despite being documented. This is declarations despite being documented. This is
needed to support turning off forwarding and turning needed to support turning off forwarding and turning
......
...@@ -63,6 +63,9 @@ static char *argv0; ...@@ -63,6 +63,9 @@ static char *argv0;
static int addresscount = 0; static int addresscount = 0;
static char domainopt[DNS_NAME_MAXTEXT]; static char domainopt[DNS_NAME_MAXTEXT];
#ifdef ISC_PLATFORM_USESIT
static char sitvalue[256];
#endif
static isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE, static isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE,
ip6_int = ISC_FALSE, plusquest = ISC_FALSE, pluscomm = ISC_FALSE, ip6_int = ISC_FALSE, plusquest = ISC_FALSE, pluscomm = ISC_FALSE,
...@@ -223,6 +226,9 @@ help(void) { ...@@ -223,6 +226,9 @@ help(void) {
" +[no]trace (Trace delegation down from root [+dnssec])\n" " +[no]trace (Trace delegation down from root [+dnssec])\n"
" +[no]dnssec (Request DNSSEC records)\n" " +[no]dnssec (Request DNSSEC records)\n"
" +[no]nsid (Request Name Server ID)\n" " +[no]nsid (Request Name Server ID)\n"
#ifdef ISC_PLATFORM_USESIT
" +[no]sit (Request a Source Identity Token)\n"
#endif
#ifdef DIG_SIGCHASE #ifdef DIG_SIGCHASE
" +[no]sigchase (Chase DNSSEC signatures)\n" " +[no]sigchase (Chase DNSSEC signatures)\n"
" +trusted-key=#### (Trusted Key when chasing DNSSEC sigs)\n" " +trusted-key=#### (Trusted Key when chasing DNSSEC sigs)\n"
...@@ -1086,14 +1092,34 @@ plus_option(char *option, isc_boolean_t is_batchfile, ...@@ -1086,14 +1092,34 @@ plus_option(char *option, isc_boolean_t is_batchfile,
goto invalid_option; goto invalid_option;
} }
break; break;
case 'i':
switch (cmd[2]) {
#ifdef DIG_SIGCHASE #ifdef DIG_SIGCHASE
case 'i': /* sigchase */ case 'g': /* sigchase */
FULLCHECK("sigchase"); FULLCHECK("sigchase");
lookup->sigchase = state; lookup->sigchase = state;
if (lookup->sigchase) if (lookup->sigchase)
lookup->dnssec = ISC_TRUE; lookup->dnssec = ISC_TRUE;
break; break;
#endif #endif
#ifdef ISC_PLATFORM_USESIT
case 't': /* sit */
FULLCHECK("sit");
if (state && lookup->edns == -1)
lookup->edns = 0;
lookup->sit = state;
if (value != NULL) {
strncpy(sitvalue, value,
sizeof(sitvalue));
lookup->sitvalue = sitvalue;
} else
lookup->sitvalue = NULL;
break;
#endif
default:
goto invalid_option;
}
break;
case 'p': /* split */ case 'p': /* split */
FULLCHECK("split"); FULLCHECK("split");
if (value != NULL && !state) if (value != NULL && !state)
......
...@@ -924,6 +924,18 @@ ...@@ -924,6 +924,18 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>+[no]sit<optional>=####</optional></option></term>
<listitem>
<para>
Send a Source Identity Token EDNS option, with optional value.
Replaying a SIT from a previous response will allow the
server to identify a previous client. The default is
<option>+nosit</option>.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</para> </para>
......
...@@ -74,6 +74,7 @@ ...@@ -74,6 +74,7 @@
#include <isc/base64.h> #include <isc/base64.h>
#include <isc/entropy.h> #include <isc/entropy.h>
#include <isc/file.h> #include <isc/file.h>
#include <isc/hex.h>
#include <isc/lang.h> #include <isc/lang.h>
#include <isc/log.h> #include <isc/log.h>
#include <isc/netaddr.h> #include <isc/netaddr.h>
...@@ -176,6 +177,8 @@ int fatalexit = 0; ...@@ -176,6 +177,8 @@ int fatalexit = 0;
char keynametext[MXNAME]; char keynametext[MXNAME];
char keyfile[MXNAME] = ""; char keyfile[MXNAME] = "";
char keysecret[MXNAME] = ""; char keysecret[MXNAME] = "";
unsigned char cookie_secret[33];
unsigned char cookie[8];
dns_name_t *hmacname = NULL; dns_name_t *hmacname = NULL;
unsigned int digestbits = 0; unsigned int digestbits = 0;
isc_buffer_t *namebuf = NULL; isc_buffer_t *namebuf = NULL;
...@@ -766,6 +769,9 @@ make_empty_lookup(void) { ...@@ -766,6 +769,9 @@ make_empty_lookup(void) {
looknew->besteffort = ISC_TRUE; looknew->besteffort = ISC_TRUE;
looknew->dnssec = ISC_FALSE; looknew->dnssec = ISC_FALSE;
looknew->nsid = ISC_FALSE; looknew->nsid = ISC_FALSE;
#ifdef ISC_PLATFORM_USESIT
looknew->sit = ISC_FALSE;
#endif
#ifdef DIG_SIGCHASE #ifdef DIG_SIGCHASE
looknew->sigchase = ISC_FALSE; looknew->sigchase = ISC_FALSE;
#if DIG_SIGCHASE_TD #if DIG_SIGCHASE_TD
...@@ -801,6 +807,9 @@ make_empty_lookup(void) { ...@@ -801,6 +807,9 @@ make_empty_lookup(void) {
looknew->new_search = ISC_FALSE; looknew->new_search = ISC_FALSE;
looknew->done_as_is = ISC_FALSE; looknew->done_as_is = ISC_FALSE;
looknew->need_search = ISC_FALSE; looknew->need_search = ISC_FALSE;
#ifdef ISC_PLATFORM_USESIT
looknew->sitvalue = NULL;
#endif
ISC_LINK_INIT(looknew, link); ISC_LINK_INIT(looknew, link);
ISC_LIST_INIT(looknew->q); ISC_LIST_INIT(looknew->q);
ISC_LIST_INIT(looknew->connecting); ISC_LIST_INIT(looknew->connecting);
...@@ -847,6 +856,10 @@ clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) { ...@@ -847,6 +856,10 @@ clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
looknew->besteffort = lookold->besteffort; looknew->besteffort = lookold->besteffort;
looknew->dnssec = lookold->dnssec; looknew->dnssec = lookold->dnssec;
looknew->nsid = lookold->nsid; looknew->nsid = lookold->nsid;
#ifdef ISC_PLATFORM_USESIT
looknew->sit = lookold->sit;
looknew->sitvalue = lookold->sitvalue;
#endif
#ifdef DIG_SIGCHASE #ifdef DIG_SIGCHASE
looknew->sigchase = lookold->sigchase; looknew->sigchase = lookold->sigchase;
#if DIG_SIGCHASE_TD #if DIG_SIGCHASE_TD
...@@ -1210,6 +1223,7 @@ setup_system(void) { ...@@ -1210,6 +1223,7 @@ setup_system(void) {
dig_searchlist_t *domain = NULL; dig_searchlist_t *domain = NULL;
lwres_result_t lwresult; lwres_result_t lwresult;
unsigned int lwresflags; unsigned int lwresflags;
isc_result_t result;
debug("setup_system()"); debug("setup_system()");
...@@ -1288,7 +1302,10 @@ setup_system(void) { ...@@ -1288,7 +1302,10 @@ setup_system(void) {
#endif #endif
#endif #endif
result = isc_entropy_getdata(entp, cookie_secret,
sizeof(cookie_secret), NULL, 0);
if (result != ISC_R_SUCCESS)
fatal("unable to generate cookie secret");
} }
/*% /*%
...@@ -1381,46 +1398,18 @@ setup_libs(void) { ...@@ -1381,46 +1398,18 @@ setup_libs(void) {
*/ */
static void static void
add_opt(dns_message_t *msg, isc_uint16_t udpsize, isc_uint16_t edns, add_opt(dns_message_t *msg, isc_uint16_t udpsize, isc_uint16_t edns,
isc_boolean_t dnssec, isc_boolean_t nsid) isc_boolean_t dnssec, dns_ednsopt_t *ednsopts, size_t count)
{ {
dns_rdataset_t *rdataset = NULL; dns_rdataset_t *rdataset = NULL;
dns_rdatalist_t *rdatalist = NULL;
dns_rdata_t *rdata = NULL;
isc_result_t result; isc_result_t result;
unsigned int flags = 0;
debug("add_opt()"); debug("add_opt()");
result = dns_message_gettemprdataset(msg, &rdataset);
check_result(result, "dns_message_gettemprdataset");
dns_rdataset_init(rdataset);
result = dns_message_gettemprdatalist(msg, &rdatalist);
check_result(result, "dns_message_gettemprdatalist");
result = dns_message_gettemprdata(msg, &rdata);
check_result(result, "dns_message_gettemprdata");
debug("setting udp size of %d", udpsize);
rdatalist->type = dns_rdatatype_opt;
rdatalist->covers = 0;
rdatalist->rdclass = udpsize;
rdatalist->ttl = edns << 16;
if (dnssec) if (dnssec)
rdatalist->ttl |= DNS_MESSAGEEXTFLAG_DO; flags |= DNS_MESSAGEEXTFLAG_DO;
if (nsid) { result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags,
isc_buffer_t *b = NULL; ednsopts, count);
check_result(result, "dns_message_buildopt");
result = isc_buffer_allocate(mctx, &b, 4);
check_result(result, "isc_buffer_allocate");
isc_buffer_putuint16(b, DNS_OPT_NSID);
isc_buffer_putuint16(b, 0);
rdata->data = isc_buffer_base(b);
rdata->length = isc_buffer_usedlength(b);
dns_message_takebuffer(msg, &b);
} else {
rdata->data = NULL;
rdata->length = 0;
}
ISC_LIST_INIT(rdatalist->rdata);
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
dns_rdatalist_tordataset(rdatalist, rdataset);
result = dns_message_setopt(msg, rdataset); result = dns_message_setopt(msg, rdataset);
check_result(result, "dns_message_setopt"); check_result(result, "dns_message_setopt");
} }
...@@ -2008,6 +1997,15 @@ insert_soa(dig_lookup_t *lookup) { ...@@ -2008,6 +1997,15 @@ insert_soa(dig_lookup_t *lookup) {
dns_message_addname(lookup->sendmsg, soaname, DNS_SECTION_AUTHORITY); dns_message_addname(lookup->sendmsg, soaname, DNS_SECTION_AUTHORITY);
} }
#ifdef ISC_PLATFORM_USESIT
static void
compute_cookie(unsigned char *cookie, size_t len) {
/* XXXMPA need to fix, should be per server. */
INSIST(len >= 8U);
memcpy(cookie, cookie_secret, 8);
}
#endif
/*% /*%
* Setup the supplied lookup structure, making it ready to start sending * Setup the supplied lookup structure, making it ready to start sending
* queries to servers. Create and initialize the message to be sent as * queries to servers. Create and initialize the message to be sent as
...@@ -2276,12 +2274,44 @@ setup_lookup(dig_lookup_t *lookup) { ...@@ -2276,12 +2274,44 @@ setup_lookup(dig_lookup_t *lookup) {
&lookup->renderbuf); &lookup->renderbuf);
check_result(result, "dns_message_renderbegin"); check_result(result, "dns_message_renderbegin");
if (lookup->udpsize > 0 || lookup->dnssec || lookup->edns > -1) { if (lookup->udpsize > 0 || lookup->dnssec || lookup->edns > -1) {
#define EDNSOPTS 2
dns_ednsopt_t opts[EDNSOPTS];
int i = 0;
if (lookup->udpsize == 0) if (lookup->udpsize == 0)
lookup->udpsize = 4096; lookup->udpsize = 4096;
if (lookup->edns < 0) if (lookup->edns < 0)
lookup->edns = 0; lookup->edns = 0;
if (lookup->nsid) {
INSIST(i < EDNSOPTS);
opts[i].code = DNS_OPT_NSID;
opts[i].length = 0;
opts[i].value = NULL;
i++;
}
#ifdef ISC_PLATFORM_USESIT
if (lookup->sit) {
INSIST(i < EDNSOPTS);
opts[i].code = DNS_OPT_SIT;
if (lookup->sitvalue != NULL) {
char bb[256];
isc_buffer_t b;
isc_buffer_init(&b, bb, sizeof(bb));
result = isc_hex_decodestring(lookup->sitvalue,
&b);
check_result(result, "isc_hex_decodestring");
opts[i].value = isc_buffer_base(&b);
opts[i].length = isc_buffer_usedlength(&b);
} else {
compute_cookie(cookie, sizeof(cookie));
opts[i].length = 8;
opts[i].value = cookie;
}
i++;
}
#endif
add_opt(lookup->sendmsg, lookup->udpsize, add_opt(lookup->sendmsg, lookup->udpsize,
lookup->edns, lookup->dnssec, lookup->nsid); lookup->edns, lookup->dnssec, opts, i);
} }
result = dns_message_rendersection(lookup->sendmsg, result = dns_message_rendersection(lookup->sendmsg,
...@@ -3109,6 +3139,67 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg, ...@@ -3109,6 +3139,67 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg,
return (ISC_TRUE); return (ISC_TRUE);
} }
#ifdef ISC_PLATFORM_USESIT
static void
process_sit(dig_lookup_t *l, isc_buffer_t *optbuf, size_t optlen) {
char bb[256];
isc_buffer_t hexbuf;
size_t len;
const unsigned char *sit;
isc_result_t result;
if (l->sitvalue != NULL) {
isc_buffer_init(&hexbuf, bb, sizeof(bb));
result = isc_hex_decodestring(l->sitvalue, &hexbuf);
check_result(result, "isc_hex_decodestring");
sit = isc_buffer_base(&hexbuf);
len = isc_buffer_usedlength(&hexbuf);
} else {
sit = cookie;
len = sizeof(cookie);
}
if (optlen >= len && optlen >= 8U) {
if (memcmp(isc_buffer_current(optbuf), sit, 8) == 0) {
if (l->comments)
printf(";; SIT client cookie part match\n");
} else
printf(";; Warning: SIT client cookie part mis-match\n");
} else
printf(";; Warning: SIT bad token (too short)\n");
isc_buffer_forward(optbuf, optlen);
}
static void
process_opt(dig_lookup_t *l, dns_rdataset_t *opt) {
dns_rdata_t rdata;
isc_result_t result;
isc_buffer_t optbuf;
isc_uint16_t optcode, optlen;
result = dns_rdataset_first(opt);
if (result == ISC_R_SUCCESS) {
dns_rdata_init(&rdata);
dns_rdataset_current(opt, &rdata);
isc_buffer_init(&optbuf, rdata.data, rdata.length);
isc_buffer_add(&optbuf, rdata.length);
while (isc_buffer_remaininglength(&optbuf) >= 4) {
optcode = isc_buffer_getuint16(&optbuf);
optlen = isc_buffer_getuint16(&optbuf);
switch (optcode) {
case DNS_OPT_SIT:
process_sit(l, &optbuf, optlen);
break;
default:
isc_buffer_forward(&optbuf, optlen);
break;
}
}
}
}
#endif
/*% /*%
* Event handler for recv complete. Perform whatever actions are necessary, * Event handler for recv complete. Perform whatever actions are necessary,
* based on the specifics of the user's request. * based on the specifics of the user's request.
...@@ -3369,7 +3460,8 @@ recv_done(isc_task_t *task, isc_event_t *event) { ...@@ -3369,7 +3460,8 @@ recv_done(isc_task_t *task, isc_event_t *event) {
} }
if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0 && if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0 &&
!l->ignore && !l->tcp_mode) { !l->ignore && !l->tcp_mode) {
printf(";; Truncated, retrying in TCP mode.\n"); if (l->comments)
printf(";; Truncated, retrying in TCP mode.\n");
n = requeue_lookup(l, ISC_TRUE); n = requeue_lookup(l, ISC_TRUE);
n->tcp_mode = ISC_TRUE; n->tcp_mode = ISC_TRUE;
n->origin = query->lookup->origin; n->origin = query->lookup->origin;
...@@ -3401,7 +3493,7 @@ recv_done(isc_task_t *task, isc_event_t *event) { ...@@ -3401,7 +3493,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
*/ */
if ((ISC_LIST_HEAD(l->q) != query) || if ((ISC_LIST_HEAD(l->q) != query) ||
(ISC_LIST_NEXT(query, link) != NULL)) { (ISC_LIST_NEXT(query, link) != NULL)) {
if( l->comments == ISC_TRUE ) if (l->comments)
printf(";; Got %s from %s, " printf(";; Got %s from %s, "
"trying next server\n", "trying next server\n",
msg->rcode == dns_rcode_servfail ? msg->rcode == dns_rcode_servfail ?
...@@ -3469,6 +3561,16 @@ recv_done(isc_task_t *task, isc_event_t *event) { ...@@ -3469,6 +3561,16 @@ recv_done(isc_task_t *task, isc_event_t *event) {
} }
} }
#ifdef ISC_PLATFORM_USESIT
if (l->sitvalue != NULL) {
if (msg->opt == NULL)
printf(";; expected opt record in response\n");
else
process_opt(l, msg->opt);
} else if (l->sit && msg->opt != NULL)
process_opt(l, msg->opt);
#endif
if (!l->doing_xfr || l->xfr_q == query) { if (!l->doing_xfr || l->xfr_q == query) {
if (msg->rcode == dns_rcode_nxdomain && if (msg->rcode == dns_rcode_nxdomain &&
(l->origin != NULL || l->need_search)) { (l->origin != NULL || l->need_search)) {
......
...@@ -130,6 +130,9 @@ struct dig_lookup { ...@@ -130,6 +130,9 @@ struct dig_lookup {
done_as_is, done_as_is,
besteffort, besteffort,
dnssec, dnssec,
#ifdef ISC_PLATFORM_USESIT
sit,
#endif
nsid; /*% Name Server ID (RFC 5001) */ nsid; /*% Name Server ID (RFC 5001) */
#ifdef DIG_SIGCHASE #ifdef DIG_SIGCHASE
isc_boolean_t sigchase; isc_boolean_t sigchase;
...@@ -184,6 +187,9 @@ isc_boolean_t sigchase; ...@@ -184,6 +187,9 @@ isc_boolean_t sigchase;
isc_buffer_t *querysig; isc_buffer_t *querysig;
isc_uint32_t msgcounter; isc_uint32_t msgcounter;
dns_fixedname_t fdomain; dns_fixedname_t fdomain;
#ifdef ISC_PLATFORM_USESIT
char *sitvalue;
#endif
}; };
/*% The dig_query structure */ /*% The dig_query structure */
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include <isc/platform.h> #include <isc/platform.h>
#include <isc/print.h> #include <isc/print.h>
#include <isc/queue.h> #include <isc/queue.h>
#include <isc/random.h>
#include <isc/serial.h>
#include <isc/stats.h> #include <isc/stats.h>
#include <isc/stdio.h> #include <isc/stdio.h>
#include <isc/string.h> #include <isc/string.h>
...@@ -32,6 +34,12 @@ ...@@ -32,6 +34,12 @@
#include <isc/timer.h> #include <isc/timer.h>
#include <isc/util.h> #include <isc/util.h>
#ifdef AES_SIT
#include <isc/aes.h>
#else
#include <isc/hmacsha.h>
#endif
#include <dns/db.h> #include <dns/db.h>
#include <dns/dispatch.h> #include <dns/dispatch.h>
#include <dns/events.h> #include <dns/events.h>
...@@ -113,6 +121,9 @@ ...@@ -113,6 +121,9 @@
*/ */
#endif #endif
#define SIT_SIZE 24 /* 8 + 4 + 4 + 8 */
#define EDNSOPTS 2
/*% nameserver client manager structure */ /*% nameserver client manager structure */
struct ns_clientmgr { struct ns_clientmgr {
/* Unlocked. */ /* Unlocked. */
...@@ -235,6 +246,10 @@ static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, ...@@ -235,6 +246,10 @@ static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
dns_dispatch_t *disp, isc_boolean_t tcp); dns_dispatch_t *disp, isc_boolean_t tcp);
static inline isc_boolean_t static inline isc_boolean_t
allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl); allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl);
#ifdef ISC_PLATFORM_USESIT
static void compute_sit(ns_client_t *client, isc_uint32_t when,
isc_uint32_t nonce, isc_buffer_t *buf);
#endif
void void
ns_client_recursing(ns_client_t *client) { ns_client_recursing(ns_client_t *client) {
...@@ -802,10 +817,24 @@ client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer, ...@@ -802,10 +817,24 @@ client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
} }
} else { } else {
data = sendbuf; data = sendbuf;
#ifdef ISC_PLATFORM_USESIT
if ((client->attributes & NS_CLIENTATTR_HAVESIT) == 0) {
if (client->view != NULL)
bufsize = client->view->situdp;
else
bufsize = 512;
} else
bufsize = client->udpsize;
if (bufsize > client->udpsize)
bufsize = client->udpsize;
if (bufsize > SEND_BUFFER_SIZE)
bufsize = SEND_BUFFER_SIZE;
#else
if (client->udpsize < SEND_BUFFER_SIZE) if (client->udpsize < SEND_BUFFER_SIZE)
bufsize = client->udpsize; bufsize = client->udpsize;
else else
bufsize = SEND_BUFFER_SIZE; bufsize = SEND_BUFFER_SIZE;
#endif
if (length > bufsize) { if (length > bufsize) {
result = ISC_R_NOSPACE; result = ISC_R_NOSPACE;
goto done; goto done;
...@@ -1342,11 +1371,14 @@ ns_client_error(ns_client_t *client, isc_result_t result) { ...@@ -1342,11 +1371,14 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
static inline isc_result_t static inline isc_result_t
client_addopt(ns_client_t *client) { client_addopt(ns_client_t *client) {
char nsid[BUFSIZ], *nsidp; char nsid[BUFSIZ], *nsidp;
#ifdef ISC_PLATFORM_USESIT
unsigned char sit[SIT_SIZE];
#endif
isc_result_t result; isc_result_t result;