From: W.C.A. Wijngaards Date: Thu, 12 Jun 2025 07:25:54 +0000 (+0200) Subject: - xfr-tsig, import the tsig verify code from hackathon/poisonlicious branch. X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=7edc1e0fc40613c940d92fd668bb21e3f2a44624;p=thirdparty%2Funbound.git - xfr-tsig, import the tsig verify code from hackathon/poisonlicious branch. --- diff --git a/sldns/sbuffer.h b/sldns/sbuffer.h index 1b7fe370c..ce995a8f7 100644 --- a/sldns/sbuffer.h +++ b/sldns/sbuffer.h @@ -56,6 +56,18 @@ sldns_read_uint32(const void *src) #endif } +INLINE uint64_t +sldns_read_uint48(const void *src) +{ + const uint8_t *p = (const uint8_t *) src; + return ( ((uint64_t) p[0] << 40) + | ((uint64_t) p[1] << 32) + | ((uint64_t) p[2] << 24) + | ((uint64_t) p[3] << 16) + | ((uint64_t) p[4] << 8) + | (uint64_t) p[5]); +} + /* * Copy data allowing for unaligned accesses in network byte order * (big endian). @@ -693,6 +705,32 @@ sldns_buffer_read_u32(sldns_buffer *buffer) return result; } +/** + * returns the 6-byte integer value at the given position in the buffer + * \param[in] buffer the buffer + * \param[in] at position in the buffer + * \return 6 byte integer + */ +INLINE uint64_t +sldns_buffer_read_u48_at(sldns_buffer *buffer, size_t at) +{ + assert(sldns_buffer_available_at(buffer, at, 6)); + return sldns_read_uint48(buffer->_data + at); +} + +/** + * returns the 6-byte integer value at the current position in the buffer + * \param[in] buffer the buffer + * \return 6 byte integer + */ +INLINE uint64_t +sldns_buffer_read_u48(sldns_buffer *buffer) +{ + uint64_t result = sldns_buffer_read_u48_at(buffer, buffer->_position); + buffer->_position += 6; + return result; +} + /** * returns the status of the buffer * \param[in] buffer diff --git a/util/tsig.c b/util/tsig.c index d8cafacba..053d6ff3e 100644 --- a/util/tsig.c +++ b/util/tsig.c @@ -41,3 +41,111 @@ #include "config.h" #include "util/tsig.h" +#include "util/log.h" +#include "sldns/pkthdr.h" +#include "sldns/rrdef.h" +#include "sldns/sbuffer.h" +#include "util/data/msgparse.h" +#include "util/data/dname.h" +#include +#include + + +/** + * Verify pkt with the name (domain name), algorithm and key in Base64. + * out 0 on success, an error code otherwise. + */ +int +tsig_verify(sldns_buffer* pkt, const uint8_t* name, const uint8_t* alg, + const uint8_t* secret, size_t secret_len, uint64_t now) +{ + size_t n_rrs; + size_t end_of_message; + uint16_t rdlength; + uint8_t mac[1024]; + uint16_t mac_sz; + uint8_t hmac_result[1024]; + unsigned int hmac_result_len; + size_t pos; + uint16_t other_len; + size_t algname_size; + uint64_t time_signed; + uint16_t fudge; + const EVP_MD* digester; + + assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1); + + if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) < 1) { + log_err("No TSIG found (ARCOUNT == 0)"); + return -1; + } + LDNS_ARCOUNT_SET( sldns_buffer_begin(pkt) + , LDNS_ARCOUNT(sldns_buffer_begin(pkt)) - 1); + n_rrs = LDNS_ANCOUNT(sldns_buffer_begin(pkt)) + + LDNS_NSCOUNT(sldns_buffer_begin(pkt)) + + LDNS_ARCOUNT(sldns_buffer_begin(pkt)); + + sldns_buffer_rewind(pkt); + sldns_buffer_skip(pkt, LDNS_HEADER_SIZE); + pkt_dname_len(pkt); /* skip qname */ + sldns_buffer_skip(pkt, 2 * sizeof(uint16_t)); /* skip type and class */ + if(!skip_pkt_rrs(pkt, n_rrs)) /* skip all rrs */ + return -1; + end_of_message = sldns_buffer_position(pkt); + if(query_dname_compare(name, sldns_buffer_current(pkt))) + return LDNS_TSIG_ERROR_BADKEY; + pkt_dname_len(pkt); /* skip TSIG name */ + pos = sldns_buffer_position(pkt); /* Append pos */ + if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_TSIG) { + log_err("No TSIG found!"); + return -1; + } + sldns_buffer_skip(pkt, sizeof(uint16_t) /* skip class */ + + sizeof(uint32_t) /* skip TTLS */ + + sizeof(uint16_t)); /* skip rdlength */ + if(query_dname_compare(alg, sldns_buffer_current(pkt))) + return LDNS_TSIG_ERROR_BADKEY; + algname_size = pkt_dname_len(pkt); /* skip alg name */ + time_signed = sldns_buffer_read_u48(pkt); /* time signed */ + fudge = sldns_buffer_read_u16(pkt); /* fudge */ + mac_sz = sldns_buffer_read_u16(pkt); /* mac size */ + memcpy(mac,sldns_buffer_current(pkt),mac_sz); /* copy mac */ + sldns_buffer_skip(pkt, mac_sz); /* skip mac */ + /* Set ID to original ID */ + sldns_buffer_write_u16_at(pkt, 0, sldns_buffer_read_u16(pkt)); + sldns_buffer_skip(pkt, sizeof(uint16_t)); /* skip error */ + other_len = sldns_buffer_read_u16(pkt); /* other len */ + + /* move CLASS (uint16_t) and TTL (uint32_t) -> TYPE position */ + memmove( sldns_buffer_at(pkt, pos) + , sldns_buffer_at(pkt, pos + sizeof(uint16_t) /* type */) + , sizeof(uint16_t) + sizeof(uint32_t)); + pos += sizeof(uint16_t) + sizeof(uint32_t); + + /* Append Algorithm Name, Time signed (uint48_t), Fudge (uint16_t) */ + memmove( sldns_buffer_at(pkt, pos) + , sldns_buffer_at(pkt, pos + sizeof(uint16_t) /* type */ + + sizeof(uint16_t) /* rdlength */) + , algname_size + 6 /* sizeof(uint48_t) */ + sizeof(uint16_t)); + pos += algname_size + 6 /* sizeof(uint48_t) */ + sizeof(uint16_t); + + /* Append Error (uint16_t), Other Len (uint16_t), Other Data */ + memmove( sldns_buffer_at(pkt, pos) + , sldns_buffer_at(pkt, sldns_buffer_position(pkt) + - 2 * sizeof(uint16_t)) + , 2 * sizeof(uint16_t) + other_len); + pos += 2 * sizeof(uint16_t) + other_len; + + digester = EVP_sha256(); + hmac_result_len = sizeof(hmac_result); + HMAC( digester, secret, secret_len, sldns_buffer_begin(pkt), pos + , hmac_result, &hmac_result_len); + if(memcmp(mac, hmac_result, hmac_result_len) == 0) { + return now > time_signed ? + ( time_signed - now > fudge ? LDNS_TSIG_ERROR_BADTIME : 0 ) + : now - time_signed > fudge ? LDNS_TSIG_ERROR_BADTIME : 0 ; + sldns_buffer_set_position(pkt, end_of_message); + return 0; + } + return LDNS_TSIG_ERROR_BADSIG; +} diff --git a/util/tsig.h b/util/tsig.h index e4613badd..f0f10f99b 100644 --- a/util/tsig.h +++ b/util/tsig.h @@ -41,6 +41,7 @@ #ifndef UTIL_TSIG_H #define UTIL_TSIG_H +struct sldns_buffer; /** * TSIG record, the RR that is in the packet. @@ -115,4 +116,12 @@ struct tsig_key { size_t data_len; }; +/** + * Verify pkt with the name (domain name), algorithm and key. + * out 0 on success, an error code otherwise. + */ +int tsig_verify(struct sldns_buffer* pkt, const uint8_t* name, + const uint8_t* alg, const uint8_t* secret, size_t secret_len, + uint64_t now); + #endif /* UTIL_TSIG_H */