From 6c1e00fc3f50bbb407cb4efaff3cc49f2a786e07 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Mon, 21 Dec 2015 22:11:16 +0100 Subject: [PATCH] Send TSIG --- configure.ac | 2 +- src/gldns/gbuffer.h | 39 ++++++++++++++ src/request-internal.c | 116 ++++++++++++++++++++++++++++++++++++++++- src/stub.c | 6 +-- src/types-internal.h | 3 ++ 5 files changed, 160 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 5e6d6632..eecff2b2 100644 --- a/configure.ac +++ b/configure.ac @@ -206,7 +206,7 @@ else fi AC_CHECK_HEADERS([openssl/conf.h],,, [AC_INCLUDES_DEFAULT]) AC_CHECK_HEADERS([openssl/engine.h],,, [AC_INCLUDES_DEFAULT]) -AC_CHECK_FUNCS([OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode]) +AC_CHECK_FUNCS([OPENSSL_config EVP_md5 EVP_sha1 EVP_sha224 EVP_sha256 EVP_sha384 EVP_sha512 FIPS_mode]) AC_CHECK_DECLS([SSL_COMP_get_compression_methods,sk_SSL_COMP_pop_free,SSL_CTX_set_ecdh_auto], [], [], [ AC_INCLUDES_DEFAULT #ifdef HAVE_OPENSSL_ERR_H diff --git a/src/gldns/gbuffer.h b/src/gldns/gbuffer.h index 56e19c78..333e1176 100644 --- a/src/gldns/gbuffer.h +++ b/src/gldns/gbuffer.h @@ -87,6 +87,19 @@ gldns_write_uint32(void *dst, uint32_t data) } +INLINE void +gldns_write_uint48(void *dst, uint64_t data) +{ + uint8_t *p = (uint8_t *) dst; + p[0] = (uint8_t) ((data >> 40) & 0xff); + p[1] = (uint8_t) ((data >> 32) & 0xff); + p[2] = (uint8_t) ((data >> 24) & 0xff); + p[3] = (uint8_t) ((data >> 16) & 0xff); + p[4] = (uint8_t) ((data >> 8) & 0xff); + p[5] = (uint8_t) (data & 0xff); +} + + /** * \file gbuffer.h * @@ -534,6 +547,20 @@ gldns_buffer_write_u32_at(gldns_buffer *buffer, size_t at, uint32_t data) gldns_write_uint32(buffer->_data + at, data); } +/** + * writes the given 6 byte integer at the given position in the buffer + * \param[in] buffer the buffer + * \param[in] at the position in the buffer + * \param[in] data the (lower) 48 bits to write + */ +INLINE void +gldns_buffer_write_u48_at(gldns_buffer *buffer, size_t at, uint64_t data) +{ + if (buffer->_fixed && at + 6 > buffer->_limit) return; + assert(gldns_buffer_available_at(buffer, at, 6)); + gldns_write_uint48(buffer->_data + at, data); +} + /** * writes the given 4 byte integer at the current position in the buffer * \param[in] buffer the buffer @@ -546,6 +573,18 @@ gldns_buffer_write_u32(gldns_buffer *buffer, uint32_t data) buffer->_position += sizeof(data); } +/** + * writes the given 6 byte integer at the current position in the buffer + * \param[in] buffer the buffer + * \param[in] data the 48 bits to write + */ +INLINE void +gldns_buffer_write_u48(gldns_buffer *buffer, uint64_t data) +{ + gldns_buffer_write_u48_at(buffer, buffer->_position, data); + buffer->_position += 6; +} + /** * copies count bytes of data at the given position to the given data-array * \param[in] buffer the buffer diff --git a/src/request-internal.c b/src/request-internal.c index 8d847ab6..f873c96e 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -41,6 +41,26 @@ #include "gldns/gbuffer.h" #include "gldns/pkthdr.h" #include "dict.h" +#include "debug.h" + +/* MAXIMUM_TSIG_SPACE = TSIG name (dname) : 256 + * TSIG type (uint16_t) : 2 + * TSIG class (uint16_t) : 2 + * TSIG TTL (uint32_t) : 4 + * RdLen (uint16_t) : 2 + * Algorithm name (dname) : 256 + * Time Signed (uint48_t) : 6 + * Fudge (uint16_t) : 2 + * Mac Size (uint16_t) : 2 + * Mac (variable) : EVP_MAX_MD_SIZE + * Original Id (uint16_t) : 2 + * Error (uint16_t) : 2 + * Other Len (uint16_t) : 2 + * Other Data (nothing) : 0 + * ---- + + * 538 + EVP_MAX_MD_SIZE + */ +#define MAXIMUM_TSIG_SPACE (538 + EVP_MAX_MD_SIZE) getdns_dict dnssec_ok_checking_disabled_spc = { { RBTREE_NULL, 0, (int (*)(const void *, const void *)) strcmp }, @@ -249,7 +269,7 @@ _getdns_network_req_add_upstream_option(getdns_network_req * req, uint16_t code, /* no overflow allowed for OPT size either (maybe this is overkill given the above check?) */ - oldlen = gldns_read_uint16(req->opt + 9); + oldlen = gldns_read_uint16(req->opt + 9); newlen = oldlen + 4 + sz; if (newlen > UINT16_MAX) return GETDNS_RETURN_GENERIC_ERROR; @@ -277,6 +297,98 @@ _getdns_network_req_add_upstream_option(getdns_network_req * req, uint16_t code, return GETDNS_RETURN_GOOD; } +size_t +_getdns_network_req_add_tsig(getdns_network_req *req) +{ + getdns_upstream *upstream = req->upstream; + gldns_buffer gbuf; + uint16_t arcount; + const getdns_tsig_info *tsig_info; + uint8_t md_buf[EVP_MAX_MD_SIZE]; + unsigned int md_len = EVP_MAX_MD_SIZE; + const EVP_MD *digester; + + if (upstream->tsig_alg == GETDNS_NO_TSIG || !upstream->tsig_dname_len) + return req->response - req->query; + + arcount = gldns_read_uint16(req->query + 10); + +#if defined(STUB_DEBUG) && STUB_DEBUG + /* TSIG should not have been written yet. */ + if (req->opt) { + assert(arcount == 1); + assert(req->opt + 11 + gldns_read_uint16(req->opt + 9) + == req->response); + } else + assert(arcount == 0); +#endif + tsig_info = _getdns_get_tsig_info(upstream->tsig_alg); + + gldns_buffer_init_frm_data(&gbuf, req->response, MAXIMUM_TSIG_SPACE); + gldns_buffer_write(&gbuf, + upstream->tsig_dname, upstream->tsig_dname_len); /* Name */ + gldns_buffer_write_u16(&gbuf, GETDNS_RRCLASS_ANY); /* Class */ + gldns_buffer_write_u32(&gbuf, 0); /* TTL */ + gldns_buffer_write(&gbuf, + tsig_info->dname, tsig_info->dname_len); /* Algorithm Name */ + gldns_buffer_write_u48(&gbuf, time(NULL)); /* Time Signed */ + gldns_buffer_write_u16(&gbuf, 300); /* Fudge */ + gldns_buffer_write_u16(&gbuf, 0); /* Error */ + gldns_buffer_write_u16(&gbuf, 0); /* Other len */ + + switch (upstream->tsig_alg) { +#ifdef HAVE_EVP_MD5 + case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break; +#endif +#ifdef HAVE_EVP_SHA1 + case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break; +#endif +#ifdef HAVE_EVP_SHA224 + case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break; +#endif +#ifdef HAVE_EVP_SHA256 + case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break; +#endif +#ifdef HAVE_EVP_SHA384 + case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break; +#endif +#ifdef HAVE_EVP_SHA512 + case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break; +#endif + default : return req->response - req->query; + } + + (void) HMAC(digester, upstream->tsig_key, upstream->tsig_size, + (void *)req->query, gldns_buffer_current(&gbuf) - req->query, + md_buf, &md_len); + + gldns_buffer_rewind(&gbuf); + gldns_buffer_write(&gbuf, + upstream->tsig_dname, upstream->tsig_dname_len); /* Name */ + gldns_buffer_write_u16(&gbuf, GETDNS_RRTYPE_TSIG); /* Type*/ + gldns_buffer_write_u16(&gbuf, GETDNS_RRCLASS_ANY); /* Class */ + gldns_buffer_write_u32(&gbuf, 0); /* TTL */ + gldns_buffer_write_u16(&gbuf, + tsig_info->dname_len + 10 + md_len + 6); /* RdLen */ + gldns_buffer_write(&gbuf, + tsig_info->dname, tsig_info->dname_len); /* Algorithm Name */ + gldns_buffer_write_u48(&gbuf, time(NULL)); /* Time Signed */ + gldns_buffer_write_u16(&gbuf, 300); /* Fudge */ + gldns_buffer_write_u16(&gbuf, md_len); /* MAC Size */ + gldns_buffer_write(&gbuf, md_buf, md_len); /* MAC*/ + gldns_buffer_write(&gbuf, req->query, 2); /* Original ID */ + gldns_buffer_write_u16(&gbuf, 0); /* Error */ + gldns_buffer_write_u16(&gbuf, 0); /* Other len */ + + if (gldns_buffer_position(&gbuf) > gldns_buffer_limit(&gbuf)) + return req->response - req->query; + + DEBUG_STUB("Sending with TSIG, mac length: %d\n", (int)md_len); + gldns_write_uint16(req->query + 10, arcount + 1); + req->response = gldns_buffer_current(&gbuf); + return req->response - req->query; +} + void _getdns_dns_req_free(getdns_dns_req * req) { @@ -439,7 +551,7 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, + strlen(name) + 1 + 4 /* dname always smaller then strlen(name) + 1 */ + 12 + opt_options_size /* space needed for OPT (if needed) */ + MAXIMUM_UPSTREAM_OPTION_SPACE - /* TODO: TSIG */ + + MAXIMUM_TSIG_SPACE + 7) / 8 * 8; } max_response_sz = (( edns_maximum_udp_payload_size != -1 diff --git a/src/stub.c b/src/stub.c index dc87418e..dfabccec 100644 --- a/src/stub.c +++ b/src/stub.c @@ -737,7 +737,7 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) if (attach_edns_client_subnet_private(netreq)) return STUB_OUT_OF_OPTIONS; } - pkt_len = netreq->response - netreq->query; + pkt_len = _getdns_network_req_add_tsig(netreq); /* We have an initialized packet buffer. * Lets see how much of it we can write */ @@ -1212,7 +1212,7 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp, } } - pkt_len = netreq->response - netreq->query; + pkt_len = _getdns_network_req_add_tsig(netreq); /* We have an initialized packet buffer. * Lets see how much of it we can write */ @@ -1337,7 +1337,7 @@ stub_udp_write_cb(void *userarg) if (attach_edns_client_subnet_private(netreq)) return; /* too many upstream options */ } - pkt_len = netreq->response - netreq->query; + pkt_len = _getdns_network_req_add_tsig(netreq); if ((ssize_t)pkt_len != sendto(netreq->fd, netreq->query, pkt_len, 0, (struct sockaddr *)&netreq->upstream->addr, netreq->upstream->addr_len)) { diff --git a/src/types-internal.h b/src/types-internal.h index ef5a118e..7d3eb190 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -379,5 +379,8 @@ getdns_return_t _getdns_network_req_add_upstream_option(getdns_network_req * req uint16_t code, uint16_t sz, const void* data); void _getdns_network_req_clear_upstream_options(getdns_network_req * req); +/* Adds TSIG signature (if needed) and returns query length */ +size_t _getdns_network_req_add_tsig(getdns_network_req *req); + #endif /* types-internal.h */