- xfr-tsig, tsig_sign_query.

This commit is contained in:
W.C.A. Wijngaards
2025-06-18 15:00:18 +02:00
parent 8b95785b8c
commit dd4ee42eb6
6 changed files with 251 additions and 3 deletions
+4 -2
View File
@@ -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 \
+1
View File
@@ -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();
+2
View File
@@ -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 */
+49
View File
@@ -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");
}
+168
View File
@@ -54,6 +54,9 @@
#include <openssl/evp.h>
#include <openssl/hmac.h>
/** 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:
* <packet with original query id and ARCOUNT without TSIG>
* <name> TSIG owner is key name
* u16 class, u32 TTL, <name> algorithm_name, u48 time_signed,
* u16 fudge, u16 error, u16 other_len, <data> 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;
}
+27 -1
View File
@@ -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 */