]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- xfr-tsig, tsig_verify_shared function.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 26 Jun 2025 13:11:25 +0000 (15:11 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 26 Jun 2025 13:11:25 +0000 (15:11 +0200)
util/tsig.c
util/tsig.h

index 99dfa185491e1bae90f4cc5f60404d691ecace36..6a1fc0ff17d08bd18421301d6897d0c0def032f3 100644 (file)
@@ -358,6 +358,19 @@ tsig_algo_find_name(const char* algo_name)
        return NULL;
 }
 
+struct tsig_algorithm*
+tsig_algo_find_wire(uint8_t* algo)
+{
+       size_t i;
+       for(i=0; i<sizeof(tsig_algorithm_table)/sizeof(*tsig_algorithm_table);
+           i++) {
+               if(query_dname_compare(tsig_algorithm_table[i].wireformat_name,
+                       algo) == 0)
+                       return &tsig_algorithm_table[i];
+       }
+       return NULL;
+}
+
 /**
  * Skip packet query rr.
  * @param pkt: the packet, position before the rr, ends after the rr.
@@ -1180,21 +1193,21 @@ tsig_parse(struct sldns_buffer* pkt, struct tsig_record* rr)
        }
        if(sldns_buffer_remaining(pkt) < 1) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: packet too short");
+               verbose(VERB_ALGO, "tsig_parse: packet too short");
                return LDNS_RCODE_FORMERR;
        }
        rr->tsig_pos = sldns_buffer_position(pkt);
        rr->key_name = sldns_buffer_current(pkt);
        rr->key_name_len = pkt_dname_len(pkt);
        if(rr->key_name_len == 0) {
-               verbose(VERB_ALGO, "tsig_verify_query: tsig name malformed");
+               verbose(VERB_ALGO, "tsig_parse: tsig name malformed");
                return LDNS_RCODE_FORMERR;
        }
 
        if(sldns_buffer_remaining(pkt) < 2 /* type */ + 2 /* class */ +
                4 /* ttl */ + 2 /* rdlength */) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: packet too short");
+               verbose(VERB_ALGO, "tsig_parse: packet too short");
                return LDNS_RCODE_FORMERR;
        }
        type = sldns_buffer_read_u16(pkt);
@@ -1203,29 +1216,29 @@ tsig_parse(struct sldns_buffer* pkt, struct tsig_record* rr)
        rdlength = sldns_buffer_read_u16(pkt);
        if(type != LDNS_RR_TYPE_TSIG) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG RR has wrong RR type, not TSIG");
+               verbose(VERB_ALGO, "tsig_parse: TSIG RR has wrong RR type, not TSIG");
                return LDNS_RCODE_FORMERR;
        }
        if(klass != LDNS_RR_CLASS_ANY) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG RR has wrong RR class, not ANY");
+               verbose(VERB_ALGO, "tsig_parse: TSIG RR has wrong RR class, not ANY");
                return LDNS_RCODE_FORMERR;
        }
        if(ttl != 0) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG RR has wrong TTL, not 0");
+               verbose(VERB_ALGO, "tsig_parse: TSIG RR has wrong TTL, not 0");
                return LDNS_RCODE_FORMERR;
        }
        if(sldns_buffer_remaining(pkt) < rdlength) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: packet too short for rdlength");
+               verbose(VERB_ALGO, "tsig_parse: packet too short for rdlength");
                return LDNS_RCODE_FORMERR;
        }
        if(rdlength < 1 /* algo name first byte */
                + 6 + 2 + 2 /* time,fudge,maclen */
                + 2 + 2 + 2 /* id,error,otherlen */) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG record too short");
+               verbose(VERB_ALGO, "tsig_parse: TSIG record too short");
                return LDNS_RCODE_FORMERR;
        }
 
@@ -1235,14 +1248,14 @@ tsig_parse(struct sldns_buffer* pkt, struct tsig_record* rr)
        if(rr->algorithm_name_len == 0 ||
                rdlength < sldns_buffer_position(pkt)-algopos) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG algorithm name malformed");
+               verbose(VERB_ALGO, "tsig_parse: TSIG algorithm name malformed");
                return LDNS_RCODE_FORMERR;
        }
 
        if(sldns_buffer_remaining(pkt) < 6 + 2 + 2 /* time,fudge,maclen */ ||
                rdlength < sldns_buffer_position(pkt)-algopos) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG record too short");
+               verbose(VERB_ALGO, "tsig_parse: TSIG record too short");
                return LDNS_RCODE_FORMERR;
        }
        rr->signed_time = sldns_buffer_read_u48(pkt);
@@ -1252,12 +1265,12 @@ tsig_parse(struct sldns_buffer* pkt, struct tsig_record* rr)
                + 2 + 2 + 2 /* id,error,otherlen */ ||
                rdlength < sldns_buffer_position(pkt)-algopos) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG record too short");
+               verbose(VERB_ALGO, "tsig_parse: TSIG record too short");
                return LDNS_RCODE_FORMERR;
        }
        if(rr->mac_size > 16384) {
                /* the hash should not be too big, really 512/8=64 bytes */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG mac_size too large");
+               verbose(VERB_ALGO, "tsig_parse: TSIG mac_size too large");
                return LDNS_RCODE_FORMERR;
        }
        rr->mac_data = sldns_buffer_current(pkt);
@@ -1271,12 +1284,12 @@ tsig_parse(struct sldns_buffer* pkt, struct tsig_record* rr)
        if(sldns_buffer_remaining(pkt) < rr->other_size ||
                rdlength < sldns_buffer_position(pkt)-algopos) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG record too short");
+               verbose(VERB_ALGO, "tsig_parse: TSIG record too short");
                return LDNS_RCODE_FORMERR;
        }
        if(rr->other_size > 16) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: other_size too large");
+               verbose(VERB_ALGO, "tsig_parse: other_size too large");
                return LDNS_RCODE_FORMERR;
        }
        if(rr->other_size == 6)
@@ -1286,13 +1299,13 @@ tsig_parse(struct sldns_buffer* pkt, struct tsig_record* rr)
 
        if(rdlength != sldns_buffer_position(pkt)-algopos) {
                /* The packet is malformed */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG record has trailing data");
+               verbose(VERB_ALGO, "tsig_parse: TSIG record has trailing data");
                return LDNS_RCODE_FORMERR;
        }
        if(sldns_buffer_remaining(pkt) > 0) {
                /* The packet is malformed */
                /* Trailing bytes after the RR, or more RRs after the TSIG. */
-               verbose(VERB_ALGO, "tsig_verify_query: TSIG record has trailing data after it");
+               verbose(VERB_ALGO, "tsig_parse: TSIG record has trailing data after it");
                return LDNS_RCODE_FORMERR;
        }
 
@@ -1626,3 +1639,91 @@ tsig_sign_reply(struct tsig_data* tsig, struct sldns_buffer* pkt,
        lock_rw_unlock(&key_table->lock);
        return 1;
 }
+
+/**
+ * Verify pkt with the name (domain name), algorithm and key in Base64.
+ * out 0 on success, an error code otherwise.
+ * For a shared packet with contents.
+ */
+int
+tsig_verify_shared(sldns_buffer* pkt, const uint8_t* name, const uint8_t* alg,
+               const uint8_t* secret, size_t secret_len, uint64_t now)
+{
+       int ret;
+       struct tsig_record rr;
+       struct tsig_key key;
+       struct tsig_data tsig;
+       uint8_t bufname[256];
+       size_t bufname_len;
+       uint8_t macbuf[1024];
+
+       if(!tsig_find_rr(pkt)) {
+               verbose(VERB_ALGO, "tsig_verify_shared: No TSIG found");
+               return -1;
+       }
+
+       /* Parse the TSIG RR from the query. */
+       ret = tsig_parse(pkt, &rr);
+       if(ret != 0) {
+               if(ret == LDNS_RCODE_SERVFAIL ||
+                       ret == LDNS_RCODE_FORMERR)
+                       return -1;
+               return ret;
+       }
+
+       if(rr.key_name_len > sizeof(bufname)) {
+               verbose(VERB_ALGO, "tsig_verify_shared: key name too long");
+               return -1;
+       }
+       dname_pkt_copy(pkt, bufname, rr.key_name);
+       query_dname_tolower(bufname);
+       bufname_len = rr.key_name_len;
+
+       if(query_dname_compare(bufname, name) != 0) {
+               verbose(VERB_ALGO, "tsig_verify_shared: wrong key");
+               return LDNS_TSIG_ERROR_BADKEY;
+       }
+       if(query_dname_compare(rr.algorithm_name, alg) != 0) {
+               verbose(VERB_ALGO, "tsig_verify_shared: wrong algorithm");
+               return LDNS_TSIG_ERROR_BADKEY;
+       }
+
+       memset(&tsig, 0, sizeof(tsig));
+       tsig.fudge = TSIG_FUDGE_TIME; /* seconds */
+       tsig.key_name = bufname;
+       tsig.key_name_len = bufname_len;
+       tsig.algo_name = NULL;
+       tsig.algo_name_len = rr.algorithm_name_len;
+
+       memset(&key, 0, sizeof(key));
+       key.name = bufname;
+       key.name_len = bufname_len;
+       key.data = (uint8_t*)secret;
+       key.data_len = secret_len;
+       key.algo = tsig_algo_find_wire((uint8_t*)alg);
+       if(!key.algo) {
+               verbose(VERB_ALGO, "tsig_verify_shared: unknown algorithm");
+               return LDNS_TSIG_ERROR_BADKEY;
+       }
+       if(key.algo->max_digest_size != rr.mac_size) {
+               verbose(VERB_ALGO, "tsig_verify_shared: wrong mac size");
+               return -1;
+       }
+       if(key.algo->max_digest_size > sizeof(macbuf)) {
+               verbose(VERB_ALGO, "tsig_verify_shared: mac size too large");
+               return -1;
+       }
+       tsig.mac_size = key.algo->max_digest_size;
+       tsig.mac = macbuf;
+
+       /* Verify the TSIG. */
+       ret = tsig_verify_query(&tsig, pkt, &key, &rr, now);
+
+       if(ret != 0) {
+               if(ret == LDNS_RCODE_SERVFAIL ||
+                       ret == LDNS_RCODE_FORMERR)
+                       return -1;
+               return ret;
+       }
+       return 0;
+}
index b13063ff01eb19beb3aefba2058a3dade37ea216..d96c3fd33e6946c23fd721c024ae4f370ecee37c 100644 (file)
@@ -237,6 +237,13 @@ int tsig_algo_check_name(const char* algo_name);
  */
 struct tsig_algorithm* tsig_algo_find_name(const char* algo_name);
 
+/**
+ * Get the TSIG algorithm for the algorithm wireformat name.
+ * @param algo: wireformat algorithm name to find.
+ * @return NULL on failure, tsig algorithm structure.
+ */
+struct tsig_algorithm* tsig_algo_find_wire(uint8_t* algo);
+
 /**
  * Verify pkt with the name (domain name), algorithm and key.
  * out 0 on success, on failure:
@@ -247,6 +254,18 @@ 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);
 
+/**
+ * Verify pkt with the name (domain name), algorithm and key in Base64.
+ * out 0 on success, an error code otherwise.
+ * For a shared packet with contents.
+ * out 0 on success, on failure:
+ * -1 for malformed, no tsig RR, or too large for buffer.
+ * >0 rcode with a TSIG error code otherwise.
+ */
+int tsig_verify_shared(struct sldns_buffer* pkt, const uint8_t* name,
+       const uint8_t* alg, const uint8_t* secret, size_t secret_len,
+       uint64_t now);
+
 /** Compare function for the key table keys. */
 int tsig_key_compare(const void* v1, const void* v2);