Check that SIG and RRSIG records for private algorithms are valid

SIG and RRSIG records for private algorithms are supposed to contain
the name / OID of the algorithm used to generate them at the start
of the signature field.
This commit is contained in:
Mark Andrews
2022-04-21 19:49:29 +10:00
committed by Evan Hunt
parent 6d960743a6
commit 3e857065de
5 changed files with 206 additions and 48 deletions

View File

@@ -17,6 +17,8 @@
#include <inttypes.h>
#include <stdbool.h>
#include <openssl/objects.h>
#include <isc/base64.h>
#include <isc/hex.h>
#include <isc/lex.h>
@@ -604,6 +606,46 @@ typemap_test(isc_region_t *sr, bool allow_empty) {
static const char hexdigits[] = "0123456789abcdef";
static const char decdigits[] = "0123456789";
static isc_result_t
check_private(isc_buffer_t *source, dns_secalg_t alg) {
isc_region_t sr;
if (alg == DNS_KEYALG_PRIVATEDNS) {
dns_fixedname_t fixed;
dns_decompress_t dctx;
dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_STRICT);
RETERR(dns_name_fromwire(dns_fixedname_initname(&fixed), source,
&dctx, 0, NULL));
/*
* There should be a public key or signature after the key name.
*/
isc_buffer_activeregion(source, &sr);
if (sr.length == 0) {
return (ISC_R_UNEXPECTEDEND);
}
} else if (alg == DNS_KEYALG_PRIVATEOID) {
/*
* Check that we can extract the OID from the start of the
* key data.
*/
const unsigned char *in = NULL;
ASN1_OBJECT *obj = NULL;
isc_buffer_activeregion(source, &sr);
in = sr.base;
obj = d2i_ASN1_OBJECT(NULL, &in, sr.length);
if (obj == NULL) {
RETERR(DNS_R_FORMERR);
}
ASN1_OBJECT_free(obj);
/* There should be a public key or signature after the OID. */
if (in >= sr.base + sr.length) {
return (ISC_R_UNEXPECTEDEND);
}
}
return (ISC_R_SUCCESS);
}
#include "code.h"
#define META 0x0001

View File

@@ -16,8 +16,6 @@
#ifndef RDATA_GENERIC_KEY_25_C
#define RDATA_GENERIC_KEY_25_C
#include <openssl/objects.h>
#include <dst/dst.h>
#define RRTYPE_KEY_ATTRIBUTES \
@@ -46,44 +44,6 @@ generic_key_nokey(dns_rdatatype_t type, unsigned int flags) {
}
}
static isc_result_t
check_private(isc_buffer_t *source, dns_secalg_t alg) {
isc_region_t sr;
if (alg == DNS_KEYALG_PRIVATEDNS) {
dns_fixedname_t fixed;
dns_decompress_t dctx;
dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_STRICT);
RETERR(dns_name_fromwire(dns_fixedname_initname(&fixed), source,
&dctx, 0, NULL));
/* There should be a public key after the key name. */
isc_buffer_activeregion(source, &sr);
if (sr.length == 0) {
return (ISC_R_UNEXPECTEDEND);
}
} else if (alg == DNS_KEYALG_PRIVATEOID) {
/*
* Check that we can extract the OID from the start of the
* key data.
*/
const unsigned char *in = NULL;
ASN1_OBJECT *obj = NULL;
isc_buffer_activeregion(source, &sr);
in = sr.base;
obj = d2i_ASN1_OBJECT(NULL, &in, sr.length);
if (obj == NULL) {
RETERR(DNS_R_FORMERR);
}
ASN1_OBJECT_free(obj);
/* There should be a public key after the OID. */
if (in >= sr.base + sr.length) {
return (ISC_R_UNEXPECTEDEND);
}
}
return (ISC_R_SUCCESS);
}
static isc_result_t
generic_fromtext_key(ARGS_FROMTEXT) {
isc_token_t token;

View File

@@ -23,7 +23,7 @@
static isc_result_t
fromtext_rrsig(ARGS_FROMTEXT) {
isc_token_t token;
unsigned char c;
unsigned char alg, c;
long i;
dns_rdatatype_t covered;
char *e;
@@ -31,6 +31,7 @@ fromtext_rrsig(ARGS_FROMTEXT) {
dns_name_t name;
isc_buffer_t buffer;
uint32_t time_signed, time_expire;
unsigned int used;
REQUIRE(type == dns_rdatatype_rrsig);
@@ -61,8 +62,8 @@ fromtext_rrsig(ARGS_FROMTEXT) {
*/
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
false));
RETTOK(dns_secalg_fromtext(&c, &token.value.as_textregion));
RETERR(mem_tobuffer(target, &c, 1));
RETTOK(dns_secalg_fromtext(&alg, &token.value.as_textregion));
RETERR(mem_tobuffer(target, &alg, 1));
/*
* Labels.
@@ -154,7 +155,24 @@ fromtext_rrsig(ARGS_FROMTEXT) {
/*
* Sig.
*/
return (isc_base64_tobuffer(lexer, target, -2));
used = isc_buffer_usedlength(target);
RETERR(isc_base64_tobuffer(lexer, target, -2));
if (alg == DNS_KEYALG_PRIVATEDNS || alg == DNS_KEYALG_PRIVATEOID) {
isc_buffer_t b;
/*
* Set up 'b' so that the signature data can be parsed.
*/
b = *target;
b.active = b.used;
b.current = used;
RETERR(check_private(&b, alg));
}
return (ISC_R_SUCCESS);
}
static isc_result_t
@@ -278,6 +296,7 @@ static isc_result_t
fromwire_rrsig(ARGS_FROMWIRE) {
isc_region_t sr;
dns_name_t name;
unsigned char algorithm;
REQUIRE(type == dns_rdatatype_rrsig);
@@ -300,6 +319,8 @@ fromwire_rrsig(ARGS_FROMWIRE) {
return (ISC_R_UNEXPECTEDEND);
}
algorithm = sr.base[2];
isc_buffer_forward(source, 18);
RETERR(mem_tobuffer(target, sr.base, 18));
@@ -316,6 +337,13 @@ fromwire_rrsig(ARGS_FROMWIRE) {
if (sr.length < 1) {
return (DNS_R_FORMERR);
}
if (algorithm == DNS_KEYALG_PRIVATEDNS ||
algorithm == DNS_KEYALG_PRIVATEOID) {
isc_buffer_t b = *source;
RETERR(check_private(&b, algorithm));
}
isc_buffer_forward(source, sr.length);
return (mem_tobuffer(target, sr.base, sr.length));
}

View File

@@ -21,7 +21,7 @@
static isc_result_t
fromtext_sig(ARGS_FROMTEXT) {
isc_token_t token;
unsigned char c;
unsigned char alg, c;
long i;
dns_rdatatype_t covered;
char *e;
@@ -29,6 +29,7 @@ fromtext_sig(ARGS_FROMTEXT) {
dns_name_t name;
isc_buffer_t buffer;
uint32_t time_signed, time_expire;
unsigned int used;
REQUIRE(type == dns_rdatatype_sig);
@@ -59,8 +60,8 @@ fromtext_sig(ARGS_FROMTEXT) {
*/
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
false));
RETTOK(dns_secalg_fromtext(&c, &token.value.as_textregion));
RETERR(mem_tobuffer(target, &c, 1));
RETTOK(dns_secalg_fromtext(&alg, &token.value.as_textregion));
RETERR(mem_tobuffer(target, &alg, 1));
/*
* Labels.
@@ -118,7 +119,24 @@ fromtext_sig(ARGS_FROMTEXT) {
/*
* Sig.
*/
return (isc_base64_tobuffer(lexer, target, -2));
used = isc_buffer_usedlength(target);
RETERR(isc_base64_tobuffer(lexer, target, -2));
if (alg == DNS_KEYALG_PRIVATEDNS || alg == DNS_KEYALG_PRIVATEOID) {
isc_buffer_t b;
/*
* Set up 'b' so that the signature data can be parsed.
*/
b = *target;
b.active = b.used;
b.current = used;
RETERR(check_private(&b, alg));
}
return (ISC_R_SUCCESS);
}
static isc_result_t
@@ -241,6 +259,7 @@ static isc_result_t
fromwire_sig(ARGS_FROMWIRE) {
isc_region_t sr;
dns_name_t name;
unsigned char algorithm;
REQUIRE(type == dns_rdatatype_sig);
@@ -263,6 +282,8 @@ fromwire_sig(ARGS_FROMWIRE) {
return (ISC_R_UNEXPECTEDEND);
}
algorithm = sr.base[2];
isc_buffer_forward(source, 18);
RETERR(mem_tobuffer(target, sr.base, 18));
@@ -279,6 +300,13 @@ fromwire_sig(ARGS_FROMWIRE) {
if (sr.length == 0) {
return (ISC_R_UNEXPECTEDEND);
}
if (algorithm == DNS_KEYALG_PRIVATEDNS ||
algorithm == DNS_KEYALG_PRIVATEOID) {
isc_buffer_t b = *source;
RETERR(check_private(&b, algorithm));
}
isc_buffer_forward(source, sr.length);
return (mem_tobuffer(target, sr.base, sr.length));
}

View File

@@ -2420,6 +2420,105 @@ rkey(void **state) {
dns_rdatatype_rkey, sizeof(dns_rdata_rkey_t));
}
/*
* rrsig (sig) tests.
*/
static void
sig_rrsig(void **state) {
wire_ok_t wire_ok[] = {
/*
* RDATA is comprised of:
*
* type covered: 2
* algorithm: 1
* labels: 1
* original ttl: 4
* signature expiration: 4
* time signed: 4
* key footprint: 2
* signer: variable
* signature: variable
* - if algorithm is PRIVATEDNS the algorithm name is embedded
* at the start of the signature
* - if algorithm is PRIVATEOID the algorithm OID is embedded
* at the start of the signature
*/
/* PRIVATEDNS example. */
WIRE_INVALID(0x00, 0x01, 253, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x06, 's', 'i', 'g', 'n', 'e', 'r',
0x00, 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
0x00),
/* PRIVATEDNS example. + sigdata */
WIRE_VALID(0x00, 0x01, 253, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 's', 'i', 'g', 'n', 'e', 'r', 0x00, 0x07, 'e',
'x', 'a', 'm', 'p', 'l', 'e', 0x00, 0x00),
/* PRIVATEDNS compression pointer. */
WIRE_INVALID(0x00, 0x00, 0x00, 253, 0xc0, 0x00, 0x00),
/* PRIVATEOID */
WIRE_INVALID(0x00, 0x01, 254, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x06, 's', 'i', 'g', 'n', 'e', 'r',
0x00, 0x00),
/* PRIVATEOID 1.3.6.1.4.1.2495 */
WIRE_INVALID(0x00, 0x01, 254, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x06, 's', 'i', 'g', 'n', 'e', 'r',
0x00, 0x06, 0x07, 0x2b, 0x06, 0x01, 0x04, 0x01,
0x93, 0x3f),
/* PRIVATEOID 1.3.6.1.4.1.2495 + sigdata */
WIRE_VALID(0x00, 0x01, 254, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 's', 'i', 'g', 'n', 'e', 'r', 0x00, 0x06, 0x07,
0x2b, 0x06, 0x01, 0x04, 0x01, 0x93, 0x3f, 0x00),
/* PRIVATEOID malformed OID - high-bit set on last octet */
WIRE_INVALID(0x00, 0x01, 254, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x06, 's', 'i', 'g', 'n', 'e', 'r',
0x00, 0x06, 0x07, 0x2b, 0x06, 0x01, 0x04, 0x01,
0x93, 0xbf, 0x00),
WIRE_SENTINEL()
};
text_ok_t text_ok[] = {
/* PRIVATEDNS example. */
TEXT_INVALID("A 253 1 0 19700101000001 19700101000000 0 "
"signer. B2V4YW1wbGUA"),
/* PRIVATEDNS example. + sigdata */
TEXT_VALID("A 253 1 0 19700101000001 19700101000000 0 signer. "
"B2V4YW1wbGUAAA=="),
/* PRIVATEDNS compression pointer. */
TEXT_INVALID("A 253 1 0 19700101000001 19700101000000 0 "
"signer. wAAA"),
/* PRIVATEOID */
TEXT_INVALID("A 254 1 0 19700101000001 19700101000000 0 "
"signer. AA=="),
/* PRIVATEOID 1.3.6.1.4.1.2495 */
TEXT_INVALID("A 254 1 0 19700101000001 19700101000000 0 "
"signer. BgcrBgEEAZM/"),
/* PRIVATEOID 1.3.6.1.4.1.2495 + sigdata */
TEXT_VALID("A 254 1 0 19700101000001 19700101000000 0 signer. "
"BgcrBgEEAZM/AA=="),
/* PRIVATEOID malformed OID - high-bit set on last octet */
TEXT_INVALID("A 254 1 0 19700101000001 19700101000000 0 "
"signer. BgcrBgEEAZO/AA=="),
/* PRIVATEOID malformed OID - wrong tag */
TEXT_INVALID("A 254 1 0 19700101000001 19700101000000 0 "
"signer. BwcrBgEEAZM/AA=="),
/*
* Sentinel.
*/
TEXT_SENTINEL()
};
UNUSED(state);
check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in,
dns_rdatatype_sig, sizeof(dns_rdata_sig_t));
check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in,
dns_rdatatype_rrsig, sizeof(dns_rdata_rrsig_t));
}
/* SSHFP RDATA manipulations */
static void
sshfp(void **state) {
@@ -3209,6 +3308,7 @@ main(int argc, char **argv) {
cmocka_unit_test_setup_teardown(nsec3, _setup, _teardown),
cmocka_unit_test_setup_teardown(nxt, _setup, _teardown),
cmocka_unit_test_setup_teardown(rkey, _setup, _teardown),
cmocka_unit_test_setup_teardown(sig_rrsig, _setup, _teardown),
cmocka_unit_test_setup_teardown(sshfp, _setup, _teardown),
cmocka_unit_test_setup_teardown(wks, _setup, _teardown),
cmocka_unit_test_setup_teardown(zonemd, _setup, _teardown),