]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- xfr-tsig, import the tsig verify code from hackathon/poisonlicious branch.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 12 Jun 2025 07:25:54 +0000 (09:25 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 12 Jun 2025 07:25:54 +0000 (09:25 +0200)
sldns/sbuffer.h
util/tsig.c
util/tsig.h

index 1b7fe370cc4e17f2976b8eaaf2b14ff3acad10ad..ce995a8f7e3583a57e8b4043ca9f5b56f800ef6c 100644 (file)
@@ -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
index d8cafacba7f01522ac6358d76217179ea59b698e..053d6ff3edfc5fcb52e08608cd2ca9ac4085a3fc 100644 (file)
 
 #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;
+}
index e4613badd984d0b24f8c40c7ed5c0902b17453d9..f0f10f99b45279f8edce9afda0e53f32c314cd90 100644 (file)
@@ -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 */