2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 10:10:06 +00:00
bind/lib/dns/rcode.c
Ondřej Surý bd4576b3ce Remove TKEY Mode 2 (Diffie-Hellman)
Completely remove the TKEY Mode 2 (Diffie-Hellman Exchanged Keying) from
BIND 9 (from named, named.conf and all the tools).  The TKEY usage is
fringe at best and in all known cases, GSSAPI is being used as it should.

The draft-eastlake-dnsop-rfc2930bis-tkey specifies that:

    4.2 Diffie-Hellman Exchanged Keying (Deprecated)

       The use of this mode (#2) is NOT RECOMMENDED for the following two
       reasons but the specification is still included in Appendix A in case
       an implementation is needed for compatibility with old TKEY
       implementations. See Section 4.6 on ECDH Exchanged Keying.

          The mixing function used does not meet current cryptographic
          standards because it uses MD5 [RFC6151].

          RSA keys must be excessively long to achieve levels of security
          required by current standards.

We might optionally implement Elliptic Curve Diffie-Hellman (ECDH) key
exchange mode 6 if the draft ever reaches the RFC status.  Meanwhile the
insecure DH mode needs to be removed.
2023-03-08 08:36:25 +01:00

587 lines
16 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <isc/ascii.h>
#include <isc/buffer.h>
#include <isc/parseint.h>
#include <isc/region.h>
#include <isc/result.h>
#include <isc/stdio.h>
#include <isc/string.h>
#include <isc/types.h>
#include <isc/util.h>
#include <dns/cert.h>
#include <dns/ds.h>
#include <dns/dsdigest.h>
#include <dns/keyflags.h>
#include <dns/keyvalues.h>
#include <dns/rcode.h>
#include <dns/rdataclass.h>
#include <dns/secalg.h>
#include <dns/secproto.h>
#define RETERR(x) \
do { \
isc_result_t _r = (x); \
if (_r != ISC_R_SUCCESS) \
return ((_r)); \
} while (0)
#define NUMBERSIZE sizeof("037777777777") /* 2^32-1 octal + NUL */
#define TOTEXTONLY 0x01
#define RCODENAMES \
/* standard rcodes */ \
{ dns_rcode_noerror, "NOERROR", 0 }, \
{ dns_rcode_formerr, "FORMERR", 0 }, \
{ dns_rcode_servfail, "SERVFAIL", 0 }, \
{ dns_rcode_nxdomain, "NXDOMAIN", 0 }, \
{ dns_rcode_notimp, "NOTIMP", 0 }, \
{ dns_rcode_refused, "REFUSED", 0 }, \
{ dns_rcode_yxdomain, "YXDOMAIN", 0 }, \
{ dns_rcode_yxrrset, "YXRRSET", 0 }, \
{ dns_rcode_nxrrset, "NXRRSET", 0 }, \
{ dns_rcode_notauth, "NOTAUTH", 0 }, \
{ dns_rcode_notzone, "NOTZONE", 0 }, \
{ 11, "RESERVED11", TOTEXTONLY }, \
{ 12, "RESERVED12", TOTEXTONLY }, \
{ 13, "RESERVED13", TOTEXTONLY }, \
{ 14, "RESERVED14", TOTEXTONLY }, \
{ 15, "RESERVED15", TOTEXTONLY },
#define ERCODENAMES \
/* extended rcodes */ \
{ dns_rcode_badvers, "BADVERS", 0 }, \
{ dns_rcode_badcookie, "BADCOOKIE", 0 }, { \
0, NULL, 0 \
}
#define TSIGRCODENAMES \
/* extended rcodes */ \
{ dns_tsigerror_badsig, "BADSIG", 0 }, \
{ dns_tsigerror_badkey, "BADKEY", 0 }, \
{ dns_tsigerror_badtime, "BADTIME", 0 }, \
{ dns_tsigerror_badmode, "BADMODE", 0 }, \
{ dns_tsigerror_badname, "BADNAME", 0 }, \
{ dns_tsigerror_badalg, "BADALG", 0 }, \
{ dns_tsigerror_badtrunc, "BADTRUNC", 0 }, { \
0, NULL, 0 \
}
/* RFC4398 section 2.1 */
#define CERTNAMES \
{ 1, "PKIX", 0 }, { 2, "SPKI", 0 }, { 3, "PGP", 0 }, \
{ 4, "IPKIX", 0 }, { 5, "ISPKI", 0 }, { 6, "IPGP", 0 }, \
{ 7, "ACPKIX", 0 }, { 8, "IACPKIX", 0 }, { 253, "URI", 0 }, \
{ 254, "OID", 0 }, { \
0, NULL, 0 \
}
/* RFC2535 section 7, RFC3110 */
#define SECALGNAMES \
{ DNS_KEYALG_RSAMD5, "RSAMD5", 0 }, \
{ DNS_KEYALG_DH_DEPRECATED, "DH", 0 }, \
{ DNS_KEYALG_DSA, "DSA", 0 }, \
{ DNS_KEYALG_RSASHA1, "RSASHA1", 0 }, \
{ DNS_KEYALG_NSEC3DSA, "NSEC3DSA", 0 }, \
{ DNS_KEYALG_NSEC3RSASHA1, "NSEC3RSASHA1", 0 }, \
{ DNS_KEYALG_RSASHA256, "RSASHA256", 0 }, \
{ DNS_KEYALG_RSASHA512, "RSASHA512", 0 }, \
{ DNS_KEYALG_ECCGOST, "ECCGOST", 0 }, \
{ DNS_KEYALG_ECDSA256, "ECDSAP256SHA256", 0 }, \
{ DNS_KEYALG_ECDSA256, "ECDSA256", 0 }, \
{ DNS_KEYALG_ECDSA384, "ECDSAP384SHA384", 0 }, \
{ DNS_KEYALG_ECDSA384, "ECDSA384", 0 }, \
{ DNS_KEYALG_ED25519, "ED25519", 0 }, \
{ DNS_KEYALG_ED448, "ED448", 0 }, \
{ DNS_KEYALG_INDIRECT, "INDIRECT", 0 }, \
{ DNS_KEYALG_PRIVATEDNS, "PRIVATEDNS", 0 }, \
{ DNS_KEYALG_PRIVATEOID, "PRIVATEOID", 0 }, { \
0, NULL, 0 \
}
/* RFC2535 section 7.1 */
#define SECPROTONAMES \
{ 0, "NONE", 0 }, { 1, "TLS", 0 }, { 2, "EMAIL", 0 }, \
{ 3, "DNSSEC", 0 }, { 4, "IPSEC", 0 }, { 255, "ALL", 0 }, { \
0, NULL, 0 \
}
#define HASHALGNAMES \
{ 1, "SHA-1", 0 }, { 0, NULL, 0 }
/* RFC3658, RFC4509, RFC5933, RFC6605 */
#define DSDIGESTNAMES \
{ DNS_DSDIGEST_SHA1, "SHA-1", 0 }, { DNS_DSDIGEST_SHA1, "SHA1", 0 }, \
{ DNS_DSDIGEST_SHA256, "SHA-256", 0 }, \
{ DNS_DSDIGEST_SHA256, "SHA256", 0 }, \
{ DNS_DSDIGEST_GOST, "GOST", 0 }, \
{ DNS_DSDIGEST_SHA384, "SHA-384", 0 }, \
{ DNS_DSDIGEST_SHA384, "SHA384", 0 }, { \
0, NULL, 0 \
}
struct tbl {
unsigned int value;
const char *name;
int flags;
};
static struct tbl rcodes[] = { RCODENAMES ERCODENAMES };
static struct tbl tsigrcodes[] = { RCODENAMES TSIGRCODENAMES };
static struct tbl certs[] = { CERTNAMES };
static struct tbl secalgs[] = { SECALGNAMES };
static struct tbl secprotos[] = { SECPROTONAMES };
static struct tbl hashalgs[] = { HASHALGNAMES };
static struct tbl dsdigests[] = { DSDIGESTNAMES };
static struct keyflag {
const char *name;
unsigned int value;
unsigned int mask;
} keyflags[] = { { "NOCONF", 0x4000, 0xC000 },
{ "NOAUTH", 0x8000, 0xC000 },
{ "NOKEY", 0xC000, 0xC000 },
{ "FLAG2", 0x2000, 0x2000 },
{ "EXTEND", 0x1000, 0x1000 },
{ "FLAG4", 0x0800, 0x0800 },
{ "FLAG5", 0x0400, 0x0400 },
{ "USER", 0x0000, 0x0300 },
{ "ZONE", 0x0100, 0x0300 },
{ "HOST", 0x0200, 0x0300 },
{ "NTYP3", 0x0300, 0x0300 },
{ "FLAG8", 0x0080, 0x0080 },
{ "FLAG9", 0x0040, 0x0040 },
{ "FLAG10", 0x0020, 0x0020 },
{ "FLAG11", 0x0010, 0x0010 },
{ "SIG0", 0x0000, 0x000F },
{ "SIG1", 0x0001, 0x000F },
{ "SIG2", 0x0002, 0x000F },
{ "SIG3", 0x0003, 0x000F },
{ "SIG4", 0x0004, 0x000F },
{ "SIG5", 0x0005, 0x000F },
{ "SIG6", 0x0006, 0x000F },
{ "SIG7", 0x0007, 0x000F },
{ "SIG8", 0x0008, 0x000F },
{ "SIG9", 0x0009, 0x000F },
{ "SIG10", 0x000A, 0x000F },
{ "SIG11", 0x000B, 0x000F },
{ "SIG12", 0x000C, 0x000F },
{ "SIG13", 0x000D, 0x000F },
{ "SIG14", 0x000E, 0x000F },
{ "SIG15", 0x000F, 0x000F },
{ "KSK", DNS_KEYFLAG_KSK, DNS_KEYFLAG_KSK },
{ NULL, 0, 0 } };
static isc_result_t
str_totext(const char *source, isc_buffer_t *target) {
unsigned int l;
isc_region_t region;
isc_buffer_availableregion(target, &region);
l = strlen(source);
if (l > region.length) {
return (ISC_R_NOSPACE);
}
memmove(region.base, source, l);
isc_buffer_add(target, l);
return (ISC_R_SUCCESS);
}
static isc_result_t
maybe_numeric(unsigned int *valuep, isc_textregion_t *source, unsigned int max,
bool hex_allowed) {
isc_result_t result;
uint32_t n;
char buffer[NUMBERSIZE];
int v;
if (!isdigit((unsigned char)source->base[0]) ||
source->length > NUMBERSIZE - 1)
{
return (ISC_R_BADNUMBER);
}
/*
* We have a potential number. Try to parse it with
* isc_parse_uint32(). isc_parse_uint32() requires
* null termination, so we must make a copy.
*/
v = snprintf(buffer, sizeof(buffer), "%.*s", (int)source->length,
source->base);
if (v < 0 || (unsigned int)v != source->length) {
return (ISC_R_BADNUMBER);
}
INSIST(buffer[source->length] == '\0');
result = isc_parse_uint32(&n, buffer, 10);
if (result == ISC_R_BADNUMBER && hex_allowed) {
result = isc_parse_uint32(&n, buffer, 16);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
if (n > max) {
return (ISC_R_RANGE);
}
*valuep = n;
return (ISC_R_SUCCESS);
}
static isc_result_t
dns_mnemonic_fromtext(unsigned int *valuep, isc_textregion_t *source,
struct tbl *table, unsigned int max) {
isc_result_t result;
int i;
result = maybe_numeric(valuep, source, max, false);
if (result != ISC_R_BADNUMBER) {
return (result);
}
for (i = 0; table[i].name != NULL; i++) {
unsigned int n;
n = strlen(table[i].name);
if (n == source->length && (table[i].flags & TOTEXTONLY) == 0 &&
strncasecmp(source->base, table[i].name, n) == 0)
{
*valuep = table[i].value;
return (ISC_R_SUCCESS);
}
}
return (DNS_R_UNKNOWN);
}
static isc_result_t
dns_mnemonic_totext(unsigned int value, isc_buffer_t *target,
struct tbl *table) {
int i = 0;
char buf[sizeof("4294967296")];
while (table[i].name != NULL) {
if (table[i].value == value) {
return (str_totext(table[i].name, target));
}
i++;
}
snprintf(buf, sizeof(buf), "%u", value);
return (str_totext(buf, target));
}
isc_result_t
dns_rcode_fromtext(dns_rcode_t *rcodep, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, rcodes, 0xffff));
*rcodep = value;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_rcode_totext(dns_rcode_t rcode, isc_buffer_t *target) {
return (dns_mnemonic_totext(rcode, target, rcodes));
}
isc_result_t
dns_tsigrcode_fromtext(dns_rcode_t *rcodep, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, tsigrcodes, 0xffff));
*rcodep = value;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_tsigrcode_totext(dns_rcode_t rcode, isc_buffer_t *target) {
return (dns_mnemonic_totext(rcode, target, tsigrcodes));
}
isc_result_t
dns_cert_fromtext(dns_cert_t *certp, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, certs, 0xffff));
*certp = value;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_cert_totext(dns_cert_t cert, isc_buffer_t *target) {
return (dns_mnemonic_totext(cert, target, certs));
}
isc_result_t
dns_secalg_fromtext(dns_secalg_t *secalgp, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, secalgs, 0xff));
*secalgp = value;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_secalg_totext(dns_secalg_t secalg, isc_buffer_t *target) {
return (dns_mnemonic_totext(secalg, target, secalgs));
}
void
dns_secalg_format(dns_secalg_t alg, char *cp, unsigned int size) {
isc_buffer_t b;
isc_region_t r;
isc_result_t result;
REQUIRE(cp != NULL && size > 0);
isc_buffer_init(&b, cp, size - 1);
result = dns_secalg_totext(alg, &b);
isc_buffer_usedregion(&b, &r);
r.base[r.length] = 0;
if (result != ISC_R_SUCCESS) {
r.base[0] = 0;
}
}
isc_result_t
dns_secproto_fromtext(dns_secproto_t *secprotop, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, secprotos, 0xff));
*secprotop = value;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_secproto_totext(dns_secproto_t secproto, isc_buffer_t *target) {
return (dns_mnemonic_totext(secproto, target, secprotos));
}
isc_result_t
dns_hashalg_fromtext(unsigned char *hashalg, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, hashalgs, 0xff));
*hashalg = value;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_keyflags_fromtext(dns_keyflags_t *flagsp, isc_textregion_t *source) {
isc_result_t result;
char *text, *end;
unsigned int value = 0;
#ifdef notyet
unsigned int mask = 0;
#endif /* ifdef notyet */
result = maybe_numeric(&value, source, 0xffff, true);
if (result == ISC_R_SUCCESS) {
*flagsp = value;
return (ISC_R_SUCCESS);
}
if (result != ISC_R_BADNUMBER) {
return (result);
}
text = source->base;
end = source->base + source->length;
while (text < end) {
struct keyflag *p;
unsigned int len;
char *delim = memchr(text, '|', end - text);
if (delim != NULL) {
len = (unsigned int)(delim - text);
} else {
len = (unsigned int)(end - text);
}
for (p = keyflags; p->name != NULL; p++) {
if (strncasecmp(p->name, text, len) == 0) {
break;
}
}
if (p->name == NULL) {
return (DNS_R_UNKNOWNFLAG);
}
value |= p->value;
#ifdef notyet
if ((mask & p->mask) != 0) {
warn("overlapping key flags");
}
mask |= p->mask;
#endif /* ifdef notyet */
text += len;
if (delim != NULL) {
text++; /* Skip "|" */
}
}
*flagsp = value;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_dsdigest_fromtext(dns_dsdigest_t *dsdigestp, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, dsdigests, 0xff));
*dsdigestp = value;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_dsdigest_totext(dns_dsdigest_t dsdigest, isc_buffer_t *target) {
return (dns_mnemonic_totext(dsdigest, target, dsdigests));
}
void
dns_dsdigest_format(dns_dsdigest_t typ, char *cp, unsigned int size) {
isc_buffer_t b;
isc_region_t r;
isc_result_t result;
REQUIRE(cp != NULL && size > 0);
isc_buffer_init(&b, cp, size - 1);
result = dns_dsdigest_totext(typ, &b);
isc_buffer_usedregion(&b, &r);
r.base[r.length] = 0;
if (result != ISC_R_SUCCESS) {
r.base[0] = 0;
}
}
/*
* This uses lots of hard coded values, but how often do we actually
* add classes?
*/
isc_result_t
dns_rdataclass_fromtext(dns_rdataclass_t *classp, isc_textregion_t *source) {
#define COMPARE(string, rdclass) \
if (((sizeof(string) - 1) == source->length) && \
(strncasecmp(source->base, string, source->length) == 0)) \
{ \
*classp = rdclass; \
return (ISC_R_SUCCESS); \
}
switch (isc_ascii_tolower(source->base[0])) {
case 'a':
COMPARE("any", dns_rdataclass_any);
break;
case 'c':
/*
* RFC1035 says the mnemonic for the CHAOS class is CH,
* but historical BIND practice is to call it CHAOS.
* We will accept both forms, but only generate CH.
*/
COMPARE("ch", dns_rdataclass_chaos);
COMPARE("chaos", dns_rdataclass_chaos);
if (source->length > 5 &&
source->length < (5 + sizeof("65000")) &&
strncasecmp("class", source->base, 5) == 0)
{
char buf[sizeof("65000")];
char *endp;
unsigned int val;
/*
* source->base is not required to be NUL terminated.
* Copy up to remaining bytes and NUL terminate.
*/
snprintf(buf, sizeof(buf), "%.*s",
(int)(source->length - 5), source->base + 5);
val = strtoul(buf, &endp, 10);
if (*endp == '\0' && val <= 0xffff) {
*classp = (dns_rdataclass_t)val;
return (ISC_R_SUCCESS);
}
}
break;
case 'h':
COMPARE("hs", dns_rdataclass_hs);
COMPARE("hesiod", dns_rdataclass_hs);
break;
case 'i':
COMPARE("in", dns_rdataclass_in);
break;
case 'n':
COMPARE("none", dns_rdataclass_none);
break;
case 'r':
COMPARE("reserved0", dns_rdataclass_reserved0);
break;
}
#undef COMPARE
return (DNS_R_UNKNOWN);
}
isc_result_t
dns_rdataclass_totext(dns_rdataclass_t rdclass, isc_buffer_t *target) {
switch (rdclass) {
case dns_rdataclass_any:
return (str_totext("ANY", target));
case dns_rdataclass_chaos:
return (str_totext("CH", target));
case dns_rdataclass_hs:
return (str_totext("HS", target));
case dns_rdataclass_in:
return (str_totext("IN", target));
case dns_rdataclass_none:
return (str_totext("NONE", target));
case dns_rdataclass_reserved0:
return (str_totext("RESERVED0", target));
default:
return (dns_rdataclass_tounknowntext(rdclass, target));
}
}
isc_result_t
dns_rdataclass_tounknowntext(dns_rdataclass_t rdclass, isc_buffer_t *target) {
char buf[sizeof("CLASS65535")];
snprintf(buf, sizeof(buf), "CLASS%u", rdclass);
return (str_totext(buf, target));
}
void
dns_rdataclass_format(dns_rdataclass_t rdclass, char *array,
unsigned int size) {
isc_result_t result;
isc_buffer_t buf;
if (size == 0U) {
return;
}
isc_buffer_init(&buf, array, size);
result = dns_rdataclass_totext(rdclass, &buf);
/*
* Null terminate.
*/
if (result == ISC_R_SUCCESS) {
if (isc_buffer_availablelength(&buf) >= 1) {
isc_buffer_putuint8(&buf, 0);
} else {
result = ISC_R_NOSPACE;
}
}
if (result != ISC_R_SUCCESS) {
strlcpy(array, "<unknown>", size);
}
}