mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-02-18 18:25:10 -05:00
860 lines
24 KiB
C
860 lines
24 KiB
C
/*
|
|
* 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 "util/config_file.h"
|
|
#include "util/net_help.h"
|
|
#include "testcode/unitmain.h"
|
|
#include "sldns/parseutil.h"
|
|
#include "sldns/sbuffer.h"
|
|
#include "sldns/str2wire.h"
|
|
#include "sldns/wire2str.h"
|
|
#include <ctype.h>
|
|
|
|
#define xstr(s) str(s)
|
|
#define str(s) #s
|
|
#define SRCDIRSTR xstr(SRCDIR)
|
|
|
|
/** verbosity for this file, 0 no, 1 print some, 2 print packet dumps */
|
|
static int vtest = 0;
|
|
|
|
/**
|
|
* Content of the TSIG test files.
|
|
*
|
|
* The tsig test files have this syntax. It is made of lines, lines started
|
|
* with # are a comment. empty lines are ignored.
|
|
* file-algorithm <name>
|
|
* The name is like md5, sha1, sha256 and if the algorithm is not
|
|
* supported at the test run time, the file is skipped, silently.
|
|
*
|
|
* tsig-key:
|
|
* name: "key.name"
|
|
* algorithm: "hmac-sha256"
|
|
* secret: "<base64"
|
|
* the following lines define name:, algorithm: and secret:
|
|
* and it adds a tsig-key that can be used.
|
|
* del-key <name>
|
|
* The tsig key is deleted, from the in-memory key table.
|
|
*
|
|
* packet
|
|
* <hex>
|
|
* endpacket
|
|
* A packet in hex dump, on the following lines. Until 'endpacket'.
|
|
* It can be used to sign or verify.
|
|
* check-packet
|
|
* <hex>
|
|
* endpacket
|
|
* A packet in hex dump, on the following lines. Until 'endpacket'.
|
|
* It is compared to the packet buffer, and the test fails if not equal.
|
|
*
|
|
* tsig-sign-query <key> <time> <expected result>
|
|
* It TSIG signs with key name, at timestamp in secs, and the
|
|
* result of the call is compared with the expected result, and
|
|
* the test fails if not equal. The result is in the packet buffer.
|
|
* tsig-verify-query <key> <time> <rcode> <tsigerror> <tsigothertime>
|
|
* It tsig verifies the packet, looks up key in the key table.
|
|
* The verification is at timestamp, in secs. The result is checked,
|
|
* the key with keyname of result, the rcode function result, and
|
|
* if tsig data is returned, the tsigerror and tsigothertime are
|
|
* checked if present. If not equal the test fails.
|
|
* If no tsig data is returned, keyname '.', and 0 and 0 are the
|
|
* tsigerr and tsigothertime values that are checked.
|
|
*
|
|
* tsig-sign-shared <key> <time> <expected result>
|
|
* Looks up key in key_table and signs a reply with it.
|
|
* For a shared packet without prior hash of previous packet, since
|
|
* there is no previous packet. If the result is not the expected
|
|
* result the test fails.
|
|
* tsig-verify-shared <key> <time> <expected result>
|
|
* Looks up key in key_table and verifies a reply with it.
|
|
* For a shared packet without prior hash of previous packet, since
|
|
* there is no previous packet. If the result is not the expected
|
|
* result the test fails.
|
|
*
|
|
*/
|
|
|
|
/** Clean up first keyword */
|
|
static char*
|
|
get_keyword(char* line)
|
|
{
|
|
char* s = line;
|
|
while(isspace(*s))
|
|
s++;
|
|
if(strlen(s)>0 && s[strlen(s)-1] == '\n')
|
|
s[strlen(s)-1] = 0;
|
|
return s;
|
|
}
|
|
|
|
/** Get argument from line */
|
|
static char*
|
|
get_arg_on_line(char* line, char* keyword)
|
|
{
|
|
char* s = line;
|
|
s += strlen(keyword);
|
|
while(isspace(*s))
|
|
s++;
|
|
return s;
|
|
}
|
|
|
|
/** Get next argument from line */
|
|
static char*
|
|
get_next_arg_on_line(char** s)
|
|
{
|
|
char* arg;
|
|
if(!*s)
|
|
return *s;
|
|
while(**s && **s == ' ')
|
|
(*s)++;
|
|
arg = *s;
|
|
if(!**s)
|
|
return arg; /* No arguments */
|
|
*s = strchr(*s, ' ');
|
|
if(!*s) {
|
|
*s = arg+strlen(arg);
|
|
return arg; /* No further arguments */
|
|
}
|
|
if(!**s)
|
|
return arg;
|
|
*(*s)++ = 0;
|
|
while(**s && **s == ' ')
|
|
(*s)++;
|
|
return arg;
|
|
}
|
|
|
|
/** See if algorithm is supported for tsig test */
|
|
static int
|
|
tsig_algo_test(char* algo)
|
|
{
|
|
if(strcmp(algo, "md5") == 0)
|
|
return 1;
|
|
if(strcmp(algo, "sha1") == 0)
|
|
return 1;
|
|
if(strcmp(algo, "sha224") == 0) {
|
|
/* The EVP_sha256 test is also used for sha224. */
|
|
#ifdef HAVE_EVP_SHA256
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
if(strcmp(algo, "sha256") == 0) {
|
|
#ifdef HAVE_EVP_SHA256
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
if(strcmp(algo, "sha384") == 0) {
|
|
/* The EVP_sha512 test is also used for sha384. */
|
|
#ifdef HAVE_EVP_SHA512
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
if(strcmp(algo, "sha512") == 0) {
|
|
#ifdef HAVE_EVP_SHA512
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
if(vtest)
|
|
printf("Unknown tsig test algorithm %s\n", algo);
|
|
return 0;
|
|
}
|
|
|
|
/** Handle the file_algorithm */
|
|
static void
|
|
handle_file_algorithm(char* line, int* break_file)
|
|
{
|
|
char* algo = get_arg_on_line(line, "file-algorithm");
|
|
if(!tsig_algo_test(algo)) {
|
|
if(vtest)
|
|
printf("algorithm not supported\n");
|
|
*break_file = 1;
|
|
return;
|
|
}
|
|
if(vtest)
|
|
printf("algorithm supported\n");
|
|
}
|
|
|
|
/** Removes quotes if any */
|
|
static char*
|
|
quote_removal(char* line)
|
|
{
|
|
if(line[0] == '"') {
|
|
char* s = line+1;
|
|
if(strlen(s)>0 && s[strlen(s)-1] == '"')
|
|
s[strlen(s)-1] = 0;
|
|
return s;
|
|
}
|
|
return line;
|
|
}
|
|
|
|
/** Handle the tsig-key */
|
|
static void
|
|
handle_tsig_key(struct tsig_key_table* key_table, FILE* in, const char* fname)
|
|
{
|
|
char line[1024];
|
|
char* s;
|
|
char* name = NULL, *algorithm = NULL, *secret = NULL;
|
|
struct config_tsig_key k;
|
|
|
|
while(fgets(line, sizeof(line), in)) {
|
|
line[sizeof(line)-1]=0;
|
|
s = get_keyword(line);
|
|
if(strncmp(s, "name:", 5) == 0) {
|
|
name = strdup(quote_removal(
|
|
get_arg_on_line(s, "name:")));
|
|
if(!name)
|
|
fatal_exit("out of memory");
|
|
} else if(strncmp(s, "algorithm:", 10) == 0) {
|
|
algorithm = strdup(quote_removal(
|
|
get_arg_on_line(s, "algorithm:")));
|
|
if(!algorithm)
|
|
fatal_exit("out of memory");
|
|
} else if(strncmp(s, "secret:", 7) == 0) {
|
|
secret = strdup(quote_removal(
|
|
get_arg_on_line(s, "secret:")));
|
|
if(!secret)
|
|
fatal_exit("out of memory");
|
|
} else {
|
|
fatal_exit("unknown tsig-key element %s: %s",
|
|
fname, s);
|
|
}
|
|
|
|
if(name && algorithm && secret)
|
|
break;
|
|
}
|
|
|
|
k.next = NULL;
|
|
k.name = name;
|
|
k.algorithm = algorithm;
|
|
k.secret = secret;
|
|
if(!tsig_key_table_add_key(key_table, &k))
|
|
fatal_exit("could not tsig_key_table_add_key, out of memory");
|
|
if(vtest)
|
|
printf("add key %s %s\n", name, algorithm);
|
|
|
|
free(name);
|
|
free(algorithm);
|
|
explicit_bzero(secret, strlen(secret));
|
|
free(secret);
|
|
}
|
|
|
|
/** Handle the del_key */
|
|
static void
|
|
handle_del_key(char* line, struct tsig_key_table* key_table)
|
|
{
|
|
char* name = get_arg_on_line(line, "del_key");
|
|
tsig_key_table_del_key_fromstr(key_table, name);
|
|
if(vtest)
|
|
printf("deleted key %s\n", name);
|
|
}
|
|
|
|
/** skip whitespace */
|
|
static void
|
|
skip_whites(const char** p)
|
|
{
|
|
while(1) {
|
|
while(isspace((unsigned char)**p))
|
|
(*p)++;
|
|
if(**p == ';' || **p == '#') {
|
|
/* comment, skip until newline */
|
|
while(**p && **p != '\n')
|
|
(*p)++;
|
|
if(**p == '\n')
|
|
(*p)++;
|
|
} else return;
|
|
}
|
|
}
|
|
|
|
/** Read hex part into buffer */
|
|
static int
|
|
read_hex_segment(char* s, struct sldns_buffer* buf)
|
|
{
|
|
uint8_t val;
|
|
const char* p = s;
|
|
while(*p) {
|
|
skip_whites(&p);
|
|
if(!*p) break;
|
|
if(sldns_buffer_position(buf) == sldns_buffer_limit(buf)) {
|
|
printf("read hex: buffer too small\n");
|
|
return 0;
|
|
}
|
|
if(sldns_hexdigit_to_int(*p) == -1) {
|
|
printf("read hex: not hex: '%c'\n", *p);
|
|
return 0;
|
|
}
|
|
val = sldns_hexdigit_to_int(*p++) << 4;
|
|
skip_whites(&p);
|
|
if(!*p) {
|
|
printf("read hex: expected another, second, hex digit\n");
|
|
return 0;
|
|
}
|
|
if(sldns_hexdigit_to_int(*p) == -1) {
|
|
printf("read hex: not hex: '%c'\n", *p);
|
|
return 0;
|
|
}
|
|
val |= sldns_hexdigit_to_int(*p++);
|
|
sldns_buffer_write_u8(buf, (uint8_t)val);
|
|
skip_whites(&p);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/** Read hex packet until 'endpacket' */
|
|
static int
|
|
read_packet_hex(char* line, struct sldns_buffer* buf, FILE* in,
|
|
const char* fname)
|
|
{
|
|
char l[102400];
|
|
char* s;
|
|
sldns_buffer_clear(buf);
|
|
if(!read_hex_segment(line, buf)) {
|
|
printf("Could not read hex %s: %s\n", fname, line);
|
|
return 0;
|
|
}
|
|
while(fgets(l, sizeof(l), in)) {
|
|
l[sizeof(l)-1]=0;
|
|
s = get_keyword(l);
|
|
if(strcmp(s, "endpacket") == 0)
|
|
break;
|
|
if(!read_hex_segment(s, buf)) {
|
|
printf("Could not read hex %s: %s\n", fname, s);
|
|
return 0;
|
|
}
|
|
}
|
|
sldns_buffer_flip(buf);
|
|
return 1;
|
|
}
|
|
|
|
/** Handle the packet */
|
|
static void
|
|
handle_packet(char* line, struct sldns_buffer* pkt, FILE* in,
|
|
const char* fname)
|
|
{
|
|
char* arg = get_arg_on_line(line, "packet");
|
|
if(!read_packet_hex(arg, pkt, in, fname))
|
|
fatal_exit("Could not read packet");
|
|
if(vtest >= 2) {
|
|
char* str = sldns_wire2str_pkt(sldns_buffer_begin(pkt),
|
|
sldns_buffer_limit(pkt));
|
|
if(str)
|
|
printf("packet: %s\n", str);
|
|
else
|
|
printf("could not wire2str_pkt\n");
|
|
free(str);
|
|
}
|
|
}
|
|
|
|
/** Handle the check-packet */
|
|
static void
|
|
handle_check_packet(char* line, struct sldns_buffer* pkt, FILE* in,
|
|
const char* fname)
|
|
{
|
|
char* arg = get_arg_on_line(line, "check-packet");
|
|
uint8_t data[65536];
|
|
sldns_buffer check;
|
|
sldns_buffer_init_frm_data(&check, data, sizeof(data));
|
|
if(!read_packet_hex(arg, &check, in, fname))
|
|
fatal_exit("Could not read check-packet");
|
|
if(vtest >= 2) {
|
|
char* str = sldns_wire2str_pkt(sldns_buffer_begin(&check),
|
|
sldns_buffer_limit(&check));
|
|
if(str)
|
|
printf("check-packet: %s\n", str);
|
|
else
|
|
printf("could not wire2str_pkt\n");
|
|
free(str);
|
|
}
|
|
|
|
/* Compare the packet */
|
|
if(vtest && (sldns_buffer_limit(pkt) != sldns_buffer_limit(&check) ||
|
|
memcmp(sldns_buffer_begin(pkt), sldns_buffer_begin(&check),
|
|
sldns_buffer_limit(pkt)) != 0)) {
|
|
printf("The packet and check-packet are different\n");
|
|
if(sldns_buffer_limit(pkt) != sldns_buffer_limit(&check))
|
|
printf("Lengths are different %d, %d (check)\n",
|
|
(int)sldns_buffer_limit(pkt),
|
|
(int)sldns_buffer_limit(&check));
|
|
else printf("Lengths are the same %d\n",
|
|
(int)sldns_buffer_limit(pkt));
|
|
if(vtest >= 2) {
|
|
char pkthex[64*1024*2+10], checkhex[64*1024*2+10];
|
|
char *pktstr, *checkstr;
|
|
hex_ntop(sldns_buffer_begin(pkt), sldns_buffer_limit(pkt),
|
|
pkthex, sizeof(pkthex));
|
|
hex_ntop(sldns_buffer_begin(&check), sldns_buffer_limit(&check),
|
|
checkhex, sizeof(checkhex));
|
|
printf(" packet: %s\n", pkthex);
|
|
printf("check-packet: %s\n", checkhex);
|
|
pktstr = sldns_wire2str_pkt(sldns_buffer_begin(pkt),
|
|
sldns_buffer_limit(pkt));
|
|
checkstr = sldns_wire2str_pkt(
|
|
sldns_buffer_begin(&check),
|
|
sldns_buffer_limit(&check));
|
|
if(pktstr)
|
|
printf(" packet: %s\n", pktstr);
|
|
else
|
|
printf(" packet: could not wire2str\n");
|
|
if(checkstr)
|
|
printf("check-packet: %s\n", checkstr);
|
|
else
|
|
printf("check-packet: could not wire2str\n");
|
|
free(pktstr);
|
|
free(checkstr);
|
|
}
|
|
}
|
|
unit_assert(sldns_buffer_limit(pkt) == sldns_buffer_limit(&check) &&
|
|
memcmp(sldns_buffer_begin(pkt), sldns_buffer_begin(&check),
|
|
sldns_buffer_limit(pkt)) == 0);
|
|
if(vtest)
|
|
printf("check-packet is equal\n");
|
|
}
|
|
|
|
/** Handle the tsig-sign-query */
|
|
static void
|
|
handle_tsig_sign_query(char* line, struct tsig_key_table* key_table,
|
|
struct sldns_buffer* pkt)
|
|
{
|
|
char* arg = get_arg_on_line(line, "tsig-sign-query");
|
|
char* keyname, *s, *timestr, *expectedstr;
|
|
int expected_result, ret;
|
|
uint64_t timepoint;
|
|
struct tsig_data* tsig;
|
|
size_t pos;
|
|
|
|
s = arg;
|
|
keyname = get_next_arg_on_line(&s);
|
|
timestr = get_next_arg_on_line(&s);
|
|
expectedstr = get_next_arg_on_line(&s);
|
|
|
|
timepoint = (uint64_t)atoll(timestr);
|
|
if(timepoint == 0 && strcmp(timestr, "0") != 0)
|
|
fatal_exit("expected time argument for %s", timestr);
|
|
expected_result = atoi(expectedstr);
|
|
if(expected_result == 0 && strcmp(expectedstr, "0") != 0)
|
|
fatal_exit("expected int argument for %s", expectedstr);
|
|
|
|
if(vtest)
|
|
printf("tsig-sign-query with %s %d %d\n", keyname,
|
|
(int)timepoint, expected_result);
|
|
|
|
tsig = tsig_create_fromstr(key_table, keyname);
|
|
if(!tsig)
|
|
fatal_exit("alloc fail or key not found %s", keyname);
|
|
|
|
/* Put position at the end of the packet to sign it. */
|
|
pos = sldns_buffer_limit(pkt);
|
|
sldns_buffer_clear(pkt);
|
|
sldns_buffer_set_position(pkt, pos);
|
|
|
|
ret = tsig_sign_query(tsig, pkt, key_table, timepoint);
|
|
sldns_buffer_flip(pkt);
|
|
|
|
if(vtest) {
|
|
if(ret == expected_result)
|
|
printf("function ok, %s\n", (ret?"success":"fail"));
|
|
else
|
|
printf("function returned %d, expected result %d\n",
|
|
ret, expected_result);
|
|
}
|
|
unit_assert(ret == expected_result);
|
|
|
|
tsig_delete(tsig);
|
|
}
|
|
|
|
/** Convert RCODE string to number. */
|
|
static int
|
|
str2wire_rcode(const char* str)
|
|
{
|
|
sldns_lookup_table *lt = sldns_lookup_by_name(sldns_rcodes, str);
|
|
if(lt) {
|
|
return (int)lt->id;
|
|
} else if(strncmp(str, "RCODE", 5) == 0) {
|
|
return atoi(str+5);
|
|
}
|
|
/* Try as-is, a number. */
|
|
return atoi(str);
|
|
}
|
|
|
|
/** Convert TSIG error code string to number. */
|
|
static int
|
|
str2wire_tsigerror(const char* str)
|
|
{
|
|
sldns_lookup_table *lt = sldns_lookup_by_name(sldns_tsig_errors, str);
|
|
if(lt) {
|
|
return (int)lt->id;
|
|
}
|
|
/* Try as-is, a number. */
|
|
return atoi(str);
|
|
}
|
|
|
|
/** Print TSIG error code to string */
|
|
static void
|
|
wire2str_tsigerror_buf(int tsigerr, char* buf, size_t len)
|
|
{
|
|
sldns_lookup_table *lt;
|
|
lt = sldns_lookup_by_id(sldns_tsig_errors, tsigerr);
|
|
if(lt && lt->name)
|
|
snprintf(buf, len, "%s", lt->name);
|
|
else snprintf(buf, len, "%d", tsigerr);
|
|
}
|
|
|
|
/** Handle the tsig-verify-query */
|
|
static void
|
|
handle_tsig_verify_query(char* line, struct tsig_key_table* key_table,
|
|
struct sldns_buffer* pkt)
|
|
{
|
|
char* arg = get_arg_on_line(line, "tsig-verify-query");
|
|
char* keyname, *s, *timestr, *expected_rcode_str,
|
|
*expected_tsigerr_str, *expected_other_str;
|
|
int expected_rcode, expected_tsigerr, ret;
|
|
uint64_t timepoint, expected_other;
|
|
struct tsig_data* tsig;
|
|
char keyname_dname[256];
|
|
|
|
s = arg;
|
|
keyname = get_next_arg_on_line(&s);
|
|
timestr = get_next_arg_on_line(&s);
|
|
expected_rcode_str = get_next_arg_on_line(&s);
|
|
expected_tsigerr_str = get_next_arg_on_line(&s);
|
|
expected_other_str = get_next_arg_on_line(&s);
|
|
|
|
timepoint = (uint64_t)atoll(timestr);
|
|
if(timepoint == 0 && strcmp(timestr, "0") != 0)
|
|
fatal_exit("expected time argument for %s", timestr);
|
|
expected_rcode = str2wire_rcode(expected_rcode_str);
|
|
if(expected_rcode == 0 && strcmp(expected_rcode_str, "0") != 0 &&
|
|
strcmp(expected_rcode_str, "NOERROR") != 0 &&
|
|
strcmp(expected_rcode_str, "RCODE0") != 0)
|
|
fatal_exit("expected rcode argument for %s", expected_rcode_str);
|
|
expected_tsigerr = str2wire_tsigerror(expected_tsigerr_str);
|
|
if(expected_tsigerr == 0 && strcmp(expected_tsigerr_str, "0") != 0 &&
|
|
strcmp(expected_tsigerr_str, "NOERROR") != 0)
|
|
fatal_exit("expected tsigerrorcode argument for %s",
|
|
expected_tsigerr_str);
|
|
expected_other = (uint64_t)atoll(expected_other_str);
|
|
if(expected_other == 0 && strcmp(expected_other_str, "0") != 0)
|
|
fatal_exit("expected int argument for %s", expected_other_str);
|
|
if(strlen(keyname) > 0 && keyname[strlen(keyname)-1] == '.')
|
|
snprintf(keyname_dname, sizeof(keyname_dname), "%s", keyname);
|
|
else snprintf(keyname_dname, sizeof(keyname_dname), "%s.", keyname);
|
|
|
|
if(vtest) {
|
|
char bufrc[16], bufte[16];
|
|
sldns_wire2str_rcode_buf(expected_rcode, bufrc, sizeof(bufrc));
|
|
wire2str_tsigerror_buf(expected_tsigerr, bufte, sizeof(bufte));
|
|
printf("tsig-verify-query with %s %d %s %s %llu\n", keyname,
|
|
(int)timepoint, bufrc, bufte,
|
|
(unsigned long long)expected_other);
|
|
}
|
|
|
|
/* Put position before TSIG */
|
|
if(!tsig_find_rr(pkt)) {
|
|
if(vtest)
|
|
printf("tsig-verify-query found no TSIG RR\n");
|
|
unit_assert(0);
|
|
return;
|
|
}
|
|
ret = tsig_parse_verify_query(key_table, pkt, &tsig, NULL, timepoint);
|
|
|
|
if(vtest) {
|
|
char bufrc[16], bufte[16], retrc[16], rette[16];
|
|
sldns_wire2str_rcode_buf(expected_rcode, bufrc, sizeof(bufrc));
|
|
wire2str_tsigerror_buf(expected_tsigerr, bufte, sizeof(bufte));
|
|
sldns_wire2str_rcode_buf(ret, retrc, sizeof(retrc));
|
|
if(tsig)
|
|
wire2str_tsigerror_buf(tsig->error, rette, sizeof(rette));
|
|
else snprintf(rette, sizeof(rette), "none");
|
|
if(ret == expected_rcode)
|
|
printf("function ok, rcode %s\n", retrc);
|
|
else
|
|
printf("function returned %s, expected result %s\n",
|
|
retrc, bufrc);
|
|
if(tsig) {
|
|
char keynm[256];
|
|
if(tsig->error == expected_tsigerr)
|
|
printf("tsig error ok, it is %s\n", bufte);
|
|
else printf("tsig error %s, expected %s\n", rette,
|
|
bufte);
|
|
if(tsig->other_len == 6) {
|
|
if(tsig->other_time == expected_other)
|
|
printf("othererrortime ok, it is %llu\n",
|
|
(unsigned long long)expected_other);
|
|
else printf("othererrortime %llu, expected %llu\n",
|
|
(unsigned long long)tsig->other_time,
|
|
(unsigned long long)expected_other);
|
|
} else {
|
|
if(0 == expected_other)
|
|
printf("othererrortime ok, none\n");
|
|
else printf("othererrortime none, expected %llu\n",
|
|
(unsigned long long)expected_other);
|
|
}
|
|
sldns_wire2str_dname_buf(tsig->key_name,
|
|
tsig->key_name_len, keynm, sizeof(keynm));
|
|
if(strcmp(keynm, keyname_dname) != 0)
|
|
printf("tsig key is %s, expected %s\n",
|
|
keynm, keyname_dname);
|
|
} else {
|
|
if(expected_tsigerr != 0 || expected_other != 0 ||
|
|
strcmp(keyname_dname, ".") != 0) {
|
|
printf("no tsig data returned, but expected it\n");
|
|
}
|
|
}
|
|
}
|
|
unit_assert(ret == expected_rcode);
|
|
if(tsig) {
|
|
char keynm[256];
|
|
unit_assert(tsig->error == expected_tsigerr);
|
|
if(tsig->other_len == 6) {
|
|
unit_assert(tsig->other_time == (uint64_t)expected_other);
|
|
} else {
|
|
unit_assert(0 == expected_other);
|
|
}
|
|
sldns_wire2str_dname_buf(tsig->key_name, tsig->key_name_len,
|
|
keynm, sizeof(keynm));
|
|
unit_assert(strcmp(keynm, keyname_dname) == 0);
|
|
} else {
|
|
unit_assert(0 == expected_tsigerr);
|
|
unit_assert(0 == expected_other);
|
|
unit_assert(strcmp(keyname_dname, ".") == 0);
|
|
}
|
|
|
|
tsig_delete(tsig);
|
|
}
|
|
|
|
/** Handle the tsig-sign-shared */
|
|
static void
|
|
handle_tsig_sign_shared(char* line, struct tsig_key_table* key_table,
|
|
struct sldns_buffer* pkt)
|
|
{
|
|
char* arg = get_arg_on_line(line, "tsig-sign-shared");
|
|
char* keyname, *s, *timestr, *expectedstr;
|
|
int expected_result, ret;
|
|
uint64_t timepoint;
|
|
struct tsig_key* key;
|
|
size_t pos;
|
|
uint8_t keynm[256];
|
|
size_t keynm_len;
|
|
|
|
s = arg;
|
|
keyname = get_next_arg_on_line(&s);
|
|
timestr = get_next_arg_on_line(&s);
|
|
expectedstr = get_next_arg_on_line(&s);
|
|
|
|
timepoint = (uint64_t)atoll(timestr);
|
|
if(timepoint == 0 && strcmp(timestr, "0") != 0)
|
|
fatal_exit("expected time argument for %s", timestr);
|
|
expected_result = atoi(expectedstr);
|
|
if(expected_result == 0 && strcmp(expectedstr, "0") != 0)
|
|
fatal_exit("expected int argument for %s", expectedstr);
|
|
|
|
if(vtest)
|
|
printf("tsig-sign-shared with %s %d %d\n", keyname,
|
|
(int)timepoint, expected_result);
|
|
|
|
keynm_len = sizeof(keynm);
|
|
if(sldns_str2wire_dname_buf(keyname, keynm, &keynm_len) != 0)
|
|
fatal_exit("could not parse '%s'", keyname);
|
|
key = tsig_key_table_search(key_table, keynm, keynm_len);
|
|
if(!key)
|
|
fatal_exit("key not found %s", keyname);
|
|
|
|
/* Put position at the end of the packet to sign it. */
|
|
pos = sldns_buffer_limit(pkt);
|
|
sldns_buffer_clear(pkt);
|
|
sldns_buffer_set_position(pkt, pos);
|
|
|
|
ret = tsig_sign_shared(pkt, key->name, key->algo->wireformat_name,
|
|
key->data, key->data_len, timepoint);
|
|
sldns_buffer_flip(pkt);
|
|
|
|
if(vtest) {
|
|
if(ret == expected_result)
|
|
printf("function ok, ret %d\n", ret);
|
|
else
|
|
printf("function returned %d, expected result %d\n",
|
|
ret, expected_result);
|
|
}
|
|
unit_assert(ret == expected_result);
|
|
}
|
|
|
|
/** Handle the tsig-verify-shared */
|
|
static void
|
|
handle_tsig_verify_shared(char* line, struct tsig_key_table* key_table,
|
|
struct sldns_buffer* pkt)
|
|
{
|
|
char* arg = get_arg_on_line(line, "tsig-verify-shared");
|
|
char* keyname, *s, *timestr, *expectedstr;
|
|
int expected_result, ret;
|
|
uint64_t timepoint;
|
|
struct tsig_key* key;
|
|
uint8_t keynm[256];
|
|
size_t keynm_len, pos;
|
|
|
|
s = arg;
|
|
keyname = get_next_arg_on_line(&s);
|
|
timestr = get_next_arg_on_line(&s);
|
|
expectedstr = get_next_arg_on_line(&s);
|
|
|
|
timepoint = (uint64_t)atoll(timestr);
|
|
if(timepoint == 0 && strcmp(timestr, "0") != 0)
|
|
fatal_exit("expected time argument for %s", timestr);
|
|
expected_result = atoi(expectedstr);
|
|
if(expected_result == 0 && strcmp(expectedstr, "0") != 0)
|
|
fatal_exit("expected int argument for %s", expectedstr);
|
|
|
|
if(vtest)
|
|
printf("tsig-verify-shared with %s %d %d\n", keyname,
|
|
(int)timepoint, expected_result);
|
|
|
|
keynm_len = sizeof(keynm);
|
|
if(sldns_str2wire_dname_buf(keyname, keynm, &keynm_len) != 0)
|
|
fatal_exit("could not parse '%s'", keyname);
|
|
key = tsig_key_table_search(key_table, keynm, keynm_len);
|
|
if(!key)
|
|
fatal_exit("key not found %s", keyname);
|
|
|
|
pos = sldns_buffer_limit(pkt);
|
|
sldns_buffer_clear(pkt);
|
|
sldns_buffer_set_limit(pkt, pos);
|
|
|
|
ret = tsig_verify_shared(pkt, key->name, key->algo->wireformat_name,
|
|
key->data, key->data_len, timepoint);
|
|
|
|
if(vtest) {
|
|
if(ret == expected_result)
|
|
printf("function ok, ret %d\n", ret);
|
|
else
|
|
printf("function returned %d, expected result %d\n",
|
|
ret, expected_result);
|
|
}
|
|
unit_assert(ret == expected_result);
|
|
}
|
|
|
|
|
|
/** Handle one line from the TSIG test file */
|
|
static void
|
|
handle_line(char* line, struct tsig_key_table* key_table,
|
|
struct sldns_buffer* pkt, FILE* in, const char* fname,
|
|
int* break_file)
|
|
{
|
|
char* s = get_keyword(line);
|
|
if(vtest)
|
|
printf("line: %s\n", s);
|
|
if(strncmp(s, "file-algorithm", 14) == 0) {
|
|
handle_file_algorithm(s, break_file);
|
|
} else if(strcmp(s, "tsig-key:") == 0) {
|
|
handle_tsig_key(key_table, in, fname);
|
|
} else if(strncmp(s, "delkey", 6) == 0) {
|
|
handle_del_key(s, key_table);
|
|
} else if(strncmp(s, "packet", 6) == 0) {
|
|
handle_packet(s, pkt, in, fname);
|
|
} else if(strncmp(s, "check-packet", 12) == 0) {
|
|
handle_check_packet(s, pkt, in, fname);
|
|
} else if(strncmp(s, "tsig-sign-query", 15) == 0) {
|
|
handle_tsig_sign_query(s, key_table, pkt);
|
|
} else if(strncmp(s, "tsig-verify-query", 17) == 0) {
|
|
handle_tsig_verify_query(s, key_table, pkt);
|
|
} else if(strncmp(s, "tsig-sign-shared", 16) == 0) {
|
|
handle_tsig_sign_shared(s, key_table, pkt);
|
|
} else if(strncmp(s, "tsig-verify-shared", 18) == 0) {
|
|
handle_tsig_verify_shared(s, key_table, pkt);
|
|
} else if(strncmp(s, "#", 1) == 0) {
|
|
/* skip comment */
|
|
} else if(strcmp(s, "") == 0) {
|
|
/* skip empty lines */
|
|
} else {
|
|
fatal_exit("Unknown tsig line %s: %s", fname, s);
|
|
}
|
|
(void)pkt;
|
|
}
|
|
|
|
/** test tsig */
|
|
static void
|
|
tsig_test_one(const char* fname)
|
|
{
|
|
struct tsig_key_table* key_table;
|
|
sldns_buffer* pkt;
|
|
FILE* in;
|
|
char line[1024];
|
|
int break_file = 0;
|
|
|
|
unit_show_func("tsig", fname);
|
|
key_table = tsig_key_table_create();
|
|
if(!key_table)
|
|
fatal_exit("out of memory");
|
|
pkt = sldns_buffer_new(65536);
|
|
if(!pkt)
|
|
fatal_exit("out of memory");
|
|
sldns_buffer_flip(pkt); /* start with empty buffer */
|
|
in = fopen(fname, "r");
|
|
if(!in)
|
|
fatal_exit("could not open %s: %s", fname, strerror(errno));
|
|
|
|
while(fgets(line, sizeof(line), in)) {
|
|
line[sizeof(line)-1]=0;
|
|
handle_line(line, key_table, pkt, in, fname, &break_file);
|
|
if(break_file)
|
|
break;
|
|
}
|
|
if(ferror(in))
|
|
fatal_exit("error read %s: %s", fname, strerror(errno));
|
|
|
|
tsig_key_table_delete(key_table);
|
|
sldns_buffer_free(pkt);
|
|
fclose(in);
|
|
}
|
|
|
|
/** test tsig code */
|
|
void
|
|
tsig_test(void)
|
|
{
|
|
unit_show_feature("tsig");
|
|
tsig_test_one(SRCDIRSTR "/testdata/tsig_test.1");
|
|
tsig_test_one(SRCDIRSTR "/testdata/tsig_test.2");
|
|
tsig_test_one(SRCDIRSTR "/testdata/tsig_test.3");
|
|
tsig_test_one(SRCDIRSTR "/testdata/tsig_test.4");
|
|
tsig_test_one(SRCDIRSTR "/testdata/tsig_test.5");
|
|
tsig_test_one(SRCDIRSTR "/testdata/tsig_test.6");
|
|
}
|