#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).
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
#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 <openssl/evp.h>
+#include <openssl/hmac.h>
+
+
+/**
+ * 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;
+}