From: W.C.A. Wijngaards Date: Wed, 18 Jun 2025 13:00:18 +0000 (+0200) Subject: - xfr-tsig, tsig_sign_query. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dd4ee42eb6cdb963cb57b24154d6e6b59dc1ed20;p=thirdparty%2Funbound.git - xfr-tsig, tsig_sign_query. --- diff --git a/Makefile.in b/Makefile.in index 095717e16..417e8c4ef 100644 --- a/Makefile.in +++ b/Makefile.in @@ -179,11 +179,12 @@ testcode/unitlruhash.c testcode/unitmain.c testcode/unitmsgparse.c \ testcode/unitneg.c testcode/unitregional.c testcode/unitslabhash.c \ testcode/unitverify.c testcode/readhex.c testcode/testpkts.c testcode/unitldns.c \ testcode/unitecs.c testcode/unitauth.c testcode/unitzonemd.c \ -testcode/unittcpreuse.c testcode/unitdoq.c testcode/unitinfra.c +testcode/unittcpreuse.c testcode/unitdoq.c testcode/unitinfra.c \ +testcode/unittsig.c UNITTEST_OBJ=unitanchor.lo unitdname.lo unitlruhash.lo unitmain.lo \ unitmsgparse.lo unitneg.lo unitregional.lo unitslabhash.lo unitverify.lo \ readhex.lo testpkts.lo unitldns.lo unitecs.lo unitauth.lo unitzonemd.lo \ -unittcpreuse.lo unitdoq.lo unitinfra.lo +unittcpreuse.lo unitdoq.lo unitinfra.lo unittsig.lo UNITTEST_OBJ_LINK=$(UNITTEST_OBJ) worker_cb.lo $(COMMON_OBJ) $(SLDNS_OBJ) \ $(COMPAT_OBJ) DAEMON_SRC=daemon/acl_list.c daemon/cachedump.c daemon/daemon.c \ @@ -1267,6 +1268,7 @@ unitzonemd.lo unitzonemd.o: $(srcdir)/testcode/unitzonemd.c config.h $(srcdir)/u unittcpreuse.lo unittcpreuse.o: $(srcdir)/testcode/unittcpreuse.c config.h $(srcdir)/services/outside_network.h \ $(srcdir)/util/random.h unitinfra.lo unitinfra.o: $(srcdir)/testcode/unitinfra.c config.h $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/iterator/iterator.h +unittsig.lo unittsig.o: $(srcdir)/testcode/unittsig.c config.h $(srcdir)/util/tsig.h $(srcdir)/testcode/unitmain.h acl_list.lo acl_list.o: $(srcdir)/daemon/acl_list.c config.h $(srcdir)/daemon/acl_list.h \ $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/services/view.h $(srcdir)/util/locks.h \ $(srcdir)/util/log.h $(srcdir)/util/regional.h $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h \ diff --git a/testcode/unitmain.c b/testcode/unitmain.c index 07c016d7b..059206678 100644 --- a/testcode/unitmain.c +++ b/testcode/unitmain.c @@ -1333,6 +1333,7 @@ main(int argc, char* argv[]) if(NSS_NoDB_Init(".") != SECSuccess) fatal_exit("could not init NSS"); #endif /* HAVE_SSL or HAVE_NSS*/ + tsig_test(); authzone_test(); neg_test(); rnd_test(); diff --git a/testcode/unitmain.h b/testcode/unitmain.h index 00b5a6220..d4aa83a1c 100644 --- a/testcode/unitmain.h +++ b/testcode/unitmain.h @@ -88,5 +88,7 @@ void tcpreuse_test(void); void doq_test(void); /** unit test for infra cache functions */ void infra_test(void); +/** unit test for tsig functions */ +void tsig_test(void); #endif /* TESTCODE_UNITMAIN_H */ diff --git a/testcode/unittsig.c b/testcode/unittsig.c new file mode 100644 index 000000000..2156c2237 --- /dev/null +++ b/testcode/unittsig.c @@ -0,0 +1,49 @@ +/* + * testcode/unittsig.c - unit test for TSIG signatures. + * + * Copyright (c) 2025, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** + * \file + * Unit test for tsig code. + */ +#include "config.h" +#include "util/tsig.h" +#include "testcode/unitmain.h" + +/** test tsig code */ +void +tsig_test(void) +{ + unit_show_feature("tsig"); +} diff --git a/util/tsig.c b/util/tsig.c index 984e3ba5b..69094e088 100644 --- a/util/tsig.c +++ b/util/tsig.c @@ -54,6 +54,9 @@ #include #include +/** Fudge time to allow in signed time for TSIG records. In seconds. */ +#define TSIG_FUDGE_TIME 300 + /** * The list of TSIG algorithms. It has short_name, wireformat_name, * wireformat_name_len, digest, max_digest_size. @@ -613,6 +616,15 @@ tsig_create(struct tsig_key_table* key_table, uint8_t* name, size_t namelen) return NULL; } lock_rw_unlock(&key_table->lock); + + tsig->original_query_id = 0; + tsig->klass = LDNS_RR_CLASS_ANY; + tsig->ttl = 0; + tsig->time_signed = 0; + tsig->fudge = TSIG_FUDGE_TIME; /* seconds */ + tsig->error = 0; + tsig->other_len = 0; + tsig->other_time = 0; return tsig; } @@ -636,3 +648,159 @@ tsig_delete(struct tsig_data* tsig) free(tsig->mac); free(tsig); } + +size_t +tsig_reserved_space(struct tsig_data* tsig) +{ + if(!tsig) + return 0; + return + tsig->key_name_len /* Owner */ + + sizeof(uint16_t) /* Type */ + + sizeof(uint16_t) /* Class */ + + sizeof(uint32_t) /* TTL */ + + sizeof(uint16_t) /* RDATA length */ + + tsig->algorithm_name_len /* Algorithm */ + + 6 /* 48bit */ /* Signed time */ + + sizeof(uint16_t) /* Fudge */ + + sizeof(uint16_t) /* Mac size */ + + tsig->mac_size /* Mac data */ + + sizeof(uint16_t) /* Original query ID */ + + sizeof(uint16_t) /* Error code */ + + sizeof(uint16_t) /* Other size */ + + 6; /* 48bit in case of Other data */ +} + +/** + * Calculate the digest for the TSIG algorithm over the packet. + * Called must hold locks, on key. This routine performs one-host + * calculation. + * @param key: the key, and algorithm. + * @param pkt: packet with contents. + * @param tsig: where to store the result. + * @return false on failure. + */ +static int +tsig_algo_calc(struct tsig_key* key, struct sldns_buffer* pkt, + struct tsig_data* tsig) +{ + const EVP_MD* evp_md; + unsigned int hmac_result_len; + unsigned char* hmac_result; + + /* Setup digester algorithm */ + evp_md = EVP_get_digestbyname(key->algo->digest); + if(!evp_md) { + /* Could not fetch algorithm. */ + char name[256], buf[1024]; + dname_str(key->name, name); + snprintf(buf, sizeof(buf), "EVP_get_digestbyname failed for %s %s", + name, key->algo->digest); + log_crypto_err(buf); + return 0; + } + + /* Perform calculation */ + hmac_result_len = tsig->mac_size; + hmac_result = HMAC(evp_md, key->data, key->data_len, + sldns_buffer_begin(pkt), sldns_buffer_position(pkt), + tsig->mac, &hmac_result_len); + if(!hmac_result) { + /* The HMAC calculation failed. */ + char name[256], buf[1024]; + dname_str(key->name, name); + snprintf(buf, sizeof(buf), "HMAC failed for %s %s", + name, key->algo->digest); + log_crypto_err(buf); + return 0; + } + return 1; +} + +int +tsig_sign_query(struct tsig_data* tsig, struct sldns_buffer* pkt, + struct tsig_key_table* key_table, uint64_t now) +{ + size_t aftername_pos; + struct tsig_key* key; + if(!tsig) + return 0; + tsig->time_signed = now; + tsig->fudge = TSIG_FUDGE_TIME; /* seconds */ + if(sldns_buffer_remaining(pkt) < tsig_reserved_space(tsig)) { + /* Not enough space in buffer for packet and TSIG. */ + return 0; + } + lock_rw_rdlock(&key_table->lock); + key = tsig_key_table_search(key_table, tsig->key_name, + tsig->key_name_len); + if(!key) { + /* The tsig key has disappeared from the key table. */ + lock_rw_unlock(&key_table->lock); + return 0; + } + + tsig->original_query_id = sldns_buffer_read_u16_at(pkt, 0); + + /* What is signed is this buffer: + * + * TSIG owner is key name + * u16 class, u32 TTL, algorithm_name, u48 time_signed, + * u16 fudge, u16 error, u16 other_len, other_data. */ + /* That fits in the current buffer, since the reserved space for + * the TSIG record is larger. */ + + /* Write uncompressed TSIG owner, it is the key name. */ + sldns_buffer_write(pkt, tsig->key_name, tsig->key_name_len); + aftername_pos = sldns_buffer_position(pkt); + sldns_buffer_write_u16(pkt, tsig->klass); + sldns_buffer_write_u32(pkt, tsig->ttl); + sldns_buffer_write(pkt, key->algo->wireformat_name, + key->algo->wireformat_name_len); + sldns_buffer_write_u48(pkt, tsig->time_signed); + sldns_buffer_write_u16(pkt, tsig->fudge); + sldns_buffer_write_u16(pkt, tsig->error); + sldns_buffer_write_u16(pkt, tsig->other_len); + if(tsig->other_len != 0) + sldns_buffer_write_u48(pkt, tsig->other_time); + + /* Sign it */ + if(!tsig_algo_calc(key, pkt, tsig)) { + /* Failure to calculate digest. */ + lock_rw_unlock(&key_table->lock); + return 0; + } + + /* Append TSIG record */ + /* The record appended consists of: + * owner name, u16 type, u16 class, u32 TTL, u16 rdlength, + * algo name, u48 signed_time, u16 fudge, u16 mac_len, mac data, + * u16 original_query_id, u16 error, u16 other_len, other data. + */ + sldns_buffer_set_position(pkt, aftername_pos); + sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_TSIG); + sldns_buffer_write_u16(pkt, tsig->klass); + sldns_buffer_write_u32(pkt, tsig->ttl); + + /* rdlength */ + sldns_buffer_write_u16(pkt, key->algo->wireformat_name_len + + 6 + 2 + 2 /* time,fudge,maclen */ + tsig->mac_size + + 2 + 2 + 2 /* id,error,otherlen */ + tsig->other_len); + sldns_buffer_write(pkt, key->algo->wireformat_name, + key->algo->wireformat_name_len); + sldns_buffer_write_u48(pkt, tsig->time_signed); + sldns_buffer_write_u16(pkt, tsig->fudge); + sldns_buffer_write_u16(pkt, tsig->mac_size); + sldns_buffer_write(pkt, tsig->mac, tsig->mac_size); + sldns_buffer_write_u16(pkt, tsig->original_query_id); + sldns_buffer_write_u16(pkt, tsig->error); + sldns_buffer_write_u16(pkt, tsig->other_len); + if(tsig->other_len == 6) + sldns_buffer_write_u48(pkt, tsig->other_time); + + LDNS_ARCOUNT_SET(sldns_buffer_begin(pkt), + LDNS_ARCOUNT(sldns_buffer_begin(pkt))+1); + + lock_rw_unlock(&key_table->lock); + return 1; +} diff --git a/util/tsig.h b/util/tsig.h index d982d840a..b63ecd530 100644 --- a/util/tsig.h +++ b/util/tsig.h @@ -95,6 +95,22 @@ struct tsig_data { size_t mac_size; /** digest buffer */ uint8_t* mac; + /** original query ID */ + uint16_t original_query_id; + /** the TSIG class */ + uint16_t klass; + /** the TSIG TTL */ + uint16_t ttl; + /** the time signed, 48bit */ + uint64_t time_signed; + /** fudge amount of time_signed */ + uint16_t fudge; + /** the TSIG error code */ + uint16_t error; + /** other data length, 6 for other_time as failed time. */ + uint16_t other_len; + /** if other len 6, this is 48bit time of error. */ + uint64_t other_time; }; /** @@ -247,9 +263,12 @@ void tsig_delete(struct tsig_data* tsig); * Sign a query with TSIG. Appends the TSIG record. * @param tsig: the tsig data, keeps state to verify reply. * @param pkt: query packet. position must be at end of packet. + * @param key_table: the tsig key table is used to fetch the key details. + * @param now: time to sign the query, the current time. * @return false on failure. */ -int tsig_sign_query(struct tsig_data* tsig, struct sldns_buffer* pkt); +int tsig_sign_query(struct tsig_data* tsig, struct sldns_buffer* pkt, + struct tsig_key_table* key_table, uint64_t now); /** * Verify a query with TSIG. @@ -308,4 +327,11 @@ int tsig_sign_reply(struct tsig_data* tsig, struct sldns_buffer* pkt); */ int tsig_verify_reply(struct tsig_data* tsig, struct sldns_buffer* pkt); +/** + * Calculate reserved space for TSIG. + * @param tsig: the tsig data + * @return number of bytes to keep reserved for the TSIG added. + */ +size_t tsig_reserved_space(struct tsig_data* tsig); + #endif /* UTIL_TSIG_H */