Commit b5f6271f authored by Mark Andrews's avatar Mark Andrews

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
declarations despite being documented. This is
needed to support turning off forwarding and turning
......
......@@ -63,6 +63,9 @@ static char *argv0;
static int addresscount = 0;
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,
ip6_int = ISC_FALSE, plusquest = ISC_FALSE, pluscomm = ISC_FALSE,
......@@ -223,6 +226,9 @@ help(void) {
" +[no]trace (Trace delegation down from root [+dnssec])\n"
" +[no]dnssec (Request DNSSEC records)\n"
" +[no]nsid (Request Name Server ID)\n"
#ifdef ISC_PLATFORM_USESIT
" +[no]sit (Request a Source Identity Token)\n"
#endif
#ifdef DIG_SIGCHASE
" +[no]sigchase (Chase DNSSEC signatures)\n"
" +trusted-key=#### (Trusted Key when chasing DNSSEC sigs)\n"
......@@ -1086,14 +1092,34 @@ plus_option(char *option, isc_boolean_t is_batchfile,
goto invalid_option;
}
break;
case 'i':
switch (cmd[2]) {
#ifdef DIG_SIGCHASE
case 'i': /* sigchase */
FULLCHECK("sigchase");
lookup->sigchase = state;
if (lookup->sigchase)
lookup->dnssec = ISC_TRUE;
break;
case 'g': /* sigchase */
FULLCHECK("sigchase");
lookup->sigchase = state;
if (lookup->sigchase)
lookup->dnssec = ISC_TRUE;
break;
#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 */
FULLCHECK("split");
if (value != NULL && !state)
......
......@@ -924,6 +924,18 @@
</listitem>
</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>
</para>
......
......@@ -74,6 +74,7 @@
#include <isc/base64.h>
#include <isc/entropy.h>
#include <isc/file.h>
#include <isc/hex.h>
#include <isc/lang.h>
#include <isc/log.h>
#include <isc/netaddr.h>
......@@ -176,6 +177,8 @@ int fatalexit = 0;
char keynametext[MXNAME];
char keyfile[MXNAME] = "";
char keysecret[MXNAME] = "";
unsigned char cookie_secret[33];
unsigned char cookie[8];
dns_name_t *hmacname = NULL;
unsigned int digestbits = 0;
isc_buffer_t *namebuf = NULL;
......@@ -766,6 +769,9 @@ make_empty_lookup(void) {
looknew->besteffort = ISC_TRUE;
looknew->dnssec = ISC_FALSE;
looknew->nsid = ISC_FALSE;
#ifdef ISC_PLATFORM_USESIT
looknew->sit = ISC_FALSE;
#endif
#ifdef DIG_SIGCHASE
looknew->sigchase = ISC_FALSE;
#if DIG_SIGCHASE_TD
......@@ -801,6 +807,9 @@ make_empty_lookup(void) {
looknew->new_search = ISC_FALSE;
looknew->done_as_is = ISC_FALSE;
looknew->need_search = ISC_FALSE;
#ifdef ISC_PLATFORM_USESIT
looknew->sitvalue = NULL;
#endif
ISC_LINK_INIT(looknew, link);
ISC_LIST_INIT(looknew->q);
ISC_LIST_INIT(looknew->connecting);
......@@ -847,6 +856,10 @@ clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
looknew->besteffort = lookold->besteffort;
looknew->dnssec = lookold->dnssec;
looknew->nsid = lookold->nsid;
#ifdef ISC_PLATFORM_USESIT
looknew->sit = lookold->sit;
looknew->sitvalue = lookold->sitvalue;
#endif
#ifdef DIG_SIGCHASE
looknew->sigchase = lookold->sigchase;
#if DIG_SIGCHASE_TD
......@@ -1210,6 +1223,7 @@ setup_system(void) {
dig_searchlist_t *domain = NULL;
lwres_result_t lwresult;
unsigned int lwresflags;
isc_result_t result;
debug("setup_system()");
......@@ -1288,7 +1302,10 @@ setup_system(void) {
#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) {
*/
static void
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_rdatalist_t *rdatalist = NULL;
dns_rdata_t *rdata = NULL;
isc_result_t result;
unsigned int flags = 0;
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)
rdatalist->ttl |= DNS_MESSAGEEXTFLAG_DO;
if (nsid) {
isc_buffer_t *b = NULL;
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);
flags |= DNS_MESSAGEEXTFLAG_DO;
result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags,
ednsopts, count);
check_result(result, "dns_message_buildopt");
result = dns_message_setopt(msg, rdataset);
check_result(result, "dns_message_setopt");
}
......@@ -2008,6 +1997,15 @@ insert_soa(dig_lookup_t *lookup) {
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
* queries to servers. Create and initialize the message to be sent as
......@@ -2276,12 +2274,44 @@ setup_lookup(dig_lookup_t *lookup) {
&lookup->renderbuf);
check_result(result, "dns_message_renderbegin");
if (lookup->udpsize > 0 || lookup->dnssec || lookup->edns > -1) {
#define EDNSOPTS 2
dns_ednsopt_t opts[EDNSOPTS];
int i = 0;
if (lookup->udpsize == 0)
lookup->udpsize = 4096;
if (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,
lookup->edns, lookup->dnssec, lookup->nsid);
lookup->edns, lookup->dnssec, opts, i);
}
result = dns_message_rendersection(lookup->sendmsg,
......@@ -3109,6 +3139,67 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg,
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,
* based on the specifics of the user's request.
......@@ -3369,7 +3460,8 @@ recv_done(isc_task_t *task, isc_event_t *event) {
}
if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0 &&
!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->tcp_mode = ISC_TRUE;
n->origin = query->lookup->origin;
......@@ -3401,7 +3493,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
*/
if ((ISC_LIST_HEAD(l->q) != query) ||
(ISC_LIST_NEXT(query, link) != NULL)) {
if( l->comments == ISC_TRUE )
if (l->comments)
printf(";; Got %s from %s, "
"trying next server\n",
msg->rcode == dns_rcode_servfail ?
......@@ -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 (msg->rcode == dns_rcode_nxdomain &&
(l->origin != NULL || l->need_search)) {
......
......@@ -130,6 +130,9 @@ struct dig_lookup {
done_as_is,
besteffort,
dnssec,
#ifdef ISC_PLATFORM_USESIT
sit,
#endif
nsid; /*% Name Server ID (RFC 5001) */
#ifdef DIG_SIGCHASE
isc_boolean_t sigchase;
......@@ -184,6 +187,9 @@ isc_boolean_t sigchase;
isc_buffer_t *querysig;
isc_uint32_t msgcounter;
dns_fixedname_t fdomain;
#ifdef ISC_PLATFORM_USESIT
char *sitvalue;
#endif
};
/*% The dig_query structure */
......
......@@ -25,6 +25,8 @@
#include <isc/platform.h>
#include <isc/print.h>
#include <isc/queue.h>
#include <isc/random.h>
#include <isc/serial.h>
#include <isc/stats.h>
#include <isc/stdio.h>
#include <isc/string.h>
......@@ -32,6 +34,12 @@
#include <isc/timer.h>
#include <isc/util.h>
#ifdef AES_SIT
#include <isc/aes.h>
#else
#include <isc/hmacsha.h>
#endif
#include <dns/db.h>
#include <dns/dispatch.h>
#include <dns/events.h>
......@@ -113,6 +121,9 @@
*/
#endif
#define SIT_SIZE 24 /* 8 + 4 + 4 + 8 */
#define EDNSOPTS 2
/*% nameserver client manager structure */
struct ns_clientmgr {
/* Unlocked. */
......@@ -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);
static inline isc_boolean_t
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
ns_client_recursing(ns_client_t *client) {
......@@ -802,10 +817,24 @@ client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
}
} else {
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)
bufsize = client->udpsize;
else
bufsize = SEND_BUFFER_SIZE;
#endif
if (length > bufsize) {
result = ISC_R_NOSPACE;
goto done;
......@@ -1342,11 +1371,14 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
static inline isc_result_t
client_addopt(ns_client_t *client) {
char nsid[BUFSIZ], *nsidp;
#ifdef ISC_PLATFORM_USESIT
unsigned char sit[SIT_SIZE];
#endif
isc_result_t result;
dns_view_t *view;
dns_resolver_t *resolver;
isc_uint16_t udpsize;
dns_ednsopt_t ednsopts[2];
dns_ednsopt_t ednsopts[EDNSOPTS];
int count = 0;
unsigned int flags;
......@@ -1375,12 +1407,33 @@ client_addopt(ns_client_t *client) {
} else
nsidp = ns_g_server->server_id;
INSIST(count < EDNSOPTS);
ednsopts[count].code = DNS_OPT_NSID;
ednsopts[count].length = strlen(nsidp);
ednsopts[count].value = (unsigned char *)nsidp;
count++;
}
no_nsid:
#ifdef ISC_PLATFORM_USESIT
if ((client->attributes & NS_CLIENTATTR_WANTSIT) != 0) {
isc_buffer_t buf;
isc_stdtime_t now;
isc_uint32_t nonce;
isc_buffer_init(&buf, sit, sizeof(sit));
isc_stdtime_get(&now);
isc_random_get(&nonce);
compute_sit(client, now, nonce, &buf);
INSIST(count < EDNSOPTS);
ednsopts[count].code = DNS_OPT_SIT;
ednsopts[count].length = SIT_SIZE;
ednsopts[count].value = sit;
count++;
}
#endif
result = dns_message_buildopt(client->message, &client->opt, 0,
udpsize, flags, ednsopts, count);
return (result);
......@@ -1464,6 +1517,179 @@ ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
return (ISC_TF(view == myview));
}
#ifdef ISC_PLATFORM_USESIT
static void
compute_sit(ns_client_t *client, isc_uint32_t when, isc_uint32_t nonce,
isc_buffer_t *buf)
{
#ifdef AES_SIT
unsigned char digest[ISC_AES_BLOCK_LENGTH];
unsigned char input[4 + 4 + 16];
isc_netaddr_t netaddr;
unsigned char *cp;
unsigned int i;
cp = isc_buffer_used(buf);
isc_buffer_putmem(buf, client->cookie, 8);
isc_buffer_putuint32(buf, nonce);
isc_buffer_putuint32(buf, when);
memcpy(input, cp, 8);
isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
switch (netaddr.family) {
case AF_INET:
memcpy(input + 8, (unsigned char *)&netaddr.type.in, 4);
memset(input + 12, 0, 4);
isc_aes128_crypt(ns_g_server->secret, input, digest);
break;
case AF_INET6:
memcpy(input + 8, (unsigned char *)&netaddr.type.in6, 16);
isc_aes128_crypt(ns_g_server->secret, input, digest);
for (i = 0; i < 8; i++)
input[i + 8] = digest[i] ^ digest[i + 8];
isc_aes128_crypt(ns_g_server->secret, input + 8, digest);
break;
}
memcpy(input, client->cookie, 8);
for (i = 0; i < 8; i++)
input[i + 8] = digest[i] ^ digest[i + 8];
isc_aes128_crypt(ns_g_server->secret, input, digest);
for (i = 0; i < 8; i++)
digest[i] ^= digest[i + 8];
isc_buffer_putmem(buf, digest, 8);
#endif
#ifdef HMAC_SHA1_SIT
unsigned char digest[ISC_SHA1_DIGESTLENGTH];
isc_netaddr_t netaddr;
unsigned char *cp;
isc_hmacsha1_t hmacsha1;
cp = isc_buffer_used(buf);
isc_buffer_putuint32(buf, nonce);
isc_buffer_putuint32(buf, when);
isc_hmacsha1_init(&hmacsha1,
ns_g_server->secret,
ISC_SHA1_DIGESTLENGTH);
isc_hmacsha1_update(&hmacsha1, cp, 8);
isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
switch (netaddr.family) {
case AF_INET:
isc_hmacsha1_update(&hmacsha1,
(unsigned char *)&netaddr.type.in, 4);
break;
case AF_INET6:
isc_hmacsha1_update(&hmacsha1,
(unsigned char *)&netaddr.type.in6, 16);
break;
}
isc_hmacsha1_update(&hmacsha1, client->cookie, sizeof(client->cookie));
isc_hmacsha1_sign(&hmacsha1, digest, sizeof(digest));
isc_buffer_putmem(buf, digest, 8);
isc_hmacsha1_invalidate(&hmacsha1);
#endif
#ifdef HMAC_SHA256_SIT
unsigned char digest[ISC_SHA256_DIGESTLENGTH];
isc_netaddr_t netaddr;
unsigned char *cp;
isc_hmacsha256_t hmacsha256;
cp = isc_buffer_used(buf);
isc_buffer_putuint32(buf, nonce);
isc_buffer_putuint32(buf, when);
isc_hmacsha256_init(&hmacsha256,
ns_g_server->secret,
ISC_SHA256_DIGESTLENGTH);
isc_hmacsha256_update(&hmacsha256, cp, 8);
isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
switch (netaddr.family) {
case AF_INET:
isc_hmacsha256_update(&hmacsha256,
(unsigned char *)&netaddr.type.in, 4);
break;
case AF_INET6:
isc_hmacsha256_update(&hmacsha256,
(unsigned char *)&netaddr.type.in6, 16);
break;
}
isc_hmacsha256_update(&hmacsha256, client->cookie,
sizeof(client->cookie));
isc_hmacsha256_sign(&hmacsha256, digest, sizeof(digest));
isc_buffer_putmem(buf, digest, 8);
isc_hmacsha256_invalidate(&hmacsha256);
#endif
}
static void
process_sit(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
unsigned char dbuf[SIT_SIZE];
unsigned char *old;
isc_stdtime_t now;
isc_uint32_t when;
isc_uint32_t nonce;
isc_buffer_t db;
client->attributes |= NS_CLIENTATTR_WANTSIT;
isc_stats_increment(ns_g_server->nsstats,