Merge pull request #2232 from poettering/dnssec8

Eigth DNSSEC patch set
This commit is contained in:
Tom Gundersen 2015-12-28 15:05:50 +01:00
commit 7fdfc8634e
8 changed files with 359 additions and 120 deletions

View file

@ -408,13 +408,11 @@ static int resolve_record(sd_bus *bus, const char *name) {
r = dns_packet_read_rr(p, &rr, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse RR.");
return log_error_errno(r, "Failed to parse RR: %m");
s = dns_resource_record_to_string(rr);
if (!s) {
log_error("Failed to format RR.");
return -ENOMEM;
}
if (!s)
return log_oom();
ifname[0] = 0;
if (ifindex > 0 && !if_indextoname(ifindex, ifname))

View file

@ -272,6 +272,42 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
return NULL;
}
static usec_t calculate_until(DnsResourceRecord *rr, usec_t timestamp, bool use_soa_minimum) {
uint32_t ttl;
usec_t u;
assert(rr);
ttl = rr->ttl;
if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
/* If this is a SOA RR, and it is requested, clamp to
* the SOA's minimum field. This is used when we do
* negative caching, to determine the TTL for the
* negative caching entry. See RFC 2308, Section
* 5. */
if (ttl > rr->soa.minimum)
ttl = rr->soa.minimum;
}
u = ttl * USEC_PER_SEC;
if (u > CACHE_TTL_MAX_USEC)
u = CACHE_TTL_MAX_USEC;
if (rr->expiry != USEC_INFINITY) {
usec_t left;
/* Make use of the DNSSEC RRSIG expiry info, if we
* have it */
left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
if (u > left)
u = left;
}
return timestamp + u;
}
static void dns_cache_item_update_positive(
DnsCache *c,
DnsCacheItem *i,
@ -302,7 +338,7 @@ static void dns_cache_item_update_positive(
dns_resource_key_unref(i->key);
i->key = dns_resource_key_ref(rr->key);
i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
i->until = calculate_until(rr, timestamp, false);
i->authenticated = authenticated;
i->shared_owner = shared_owner;
@ -383,7 +419,7 @@ static int dns_cache_put_positive(
i->type = DNS_CACHE_POSITIVE;
i->key = dns_resource_key_ref(rr->key);
i->rr = dns_resource_record_ref(rr);
i->until = timestamp + MIN(i->rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
i->until = calculate_until(rr, timestamp, false);
i->authenticated = authenticated;
i->shared_owner = shared_owner;
i->owner_family = owner_family;
@ -412,7 +448,7 @@ static int dns_cache_put_negative(
int rcode,
bool authenticated,
usec_t timestamp,
uint32_t soa_ttl,
DnsResourceRecord *soa,
int owner_family,
const union in_addr_union *owner_address) {
@ -422,6 +458,7 @@ static int dns_cache_put_negative(
assert(c);
assert(key);
assert(soa);
assert(owner_address);
/* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
@ -432,7 +469,7 @@ static int dns_cache_put_negative(
if (dns_type_is_pseudo(key->type))
return 0;
if (soa_ttl <= 0) {
if (soa->soa.minimum <= 0 || soa->ttl <= 0) {
if (log_get_max_level() >= LOG_DEBUG) {
r = dns_resource_key_to_string(key, &key_str);
if (r < 0)
@ -458,7 +495,7 @@ static int dns_cache_put_negative(
return -ENOMEM;
i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
i->until = calculate_until(soa, timestamp, true);
i->authenticated = authenticated;
i->owner_family = owner_family;
i->owner_address = *owner_address;
@ -632,7 +669,7 @@ int dns_cache_put(
rcode,
authenticated,
timestamp,
MIN(soa->soa.minimum, soa->ttl),
soa,
owner_family, owner_address);
if (r < 0)
goto fail;

View file

@ -40,10 +40,9 @@
* - multi-label zone compatibility
* - cname/dname compatibility
* - per-interface DNSSEC setting
* - fix TTL for cache entries to match RRSIG TTL
* - nxdomain on qname
* - retry on failed validation?
* - DSA support
* - EC support?
* - DSA support?
*
* */
@ -77,14 +76,6 @@ static void initialize_libgcrypt(void) {
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
static bool dnssec_algorithm_supported(int algorithm) {
return IN_SET(algorithm,
DNSSEC_ALGORITHM_RSASHA1,
DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
DNSSEC_ALGORITHM_RSASHA256,
DNSSEC_ALGORITHM_RSASHA512);
}
uint16_t dnssec_keytag(DnsResourceRecord *dnskey) {
const uint8_t *p;
uint32_t sum;
@ -136,7 +127,7 @@ static int rr_compare(const void *a, const void *b) {
return 0;
}
static int dnssec_rsa_verify(
static int dnssec_rsa_verify_raw(
const char *hash_algorithm,
const void *signature, size_t signature_size,
const void *data, size_t data_size,
@ -226,6 +217,196 @@ finish:
return r;
}
static int dnssec_rsa_verify(
const char *hash_algorithm,
const void *hash, size_t hash_size,
DnsResourceRecord *rrsig,
DnsResourceRecord *dnskey) {
size_t exponent_size, modulus_size;
void *exponent, *modulus;
assert(hash_algorithm);
assert(hash);
assert(hash_size > 0);
assert(rrsig);
assert(dnskey);
if (*(uint8_t*) dnskey->dnskey.key == 0) {
/* exponent is > 255 bytes long */
exponent = (uint8_t*) dnskey->dnskey.key + 3;
exponent_size =
((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) |
((size_t) ((uint8_t*) dnskey->dnskey.key)[1]);
if (exponent_size < 256)
return -EINVAL;
if (3 + exponent_size >= dnskey->dnskey.key_size)
return -EINVAL;
modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
} else {
/* exponent is <= 255 bytes long */
exponent = (uint8_t*) dnskey->dnskey.key + 1;
exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
if (exponent_size <= 0)
return -EINVAL;
if (1 + exponent_size >= dnskey->dnskey.key_size)
return -EINVAL;
modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
}
return dnssec_rsa_verify_raw(
hash_algorithm,
rrsig->rrsig.signature, rrsig->rrsig.signature_size,
hash, hash_size,
exponent, exponent_size,
modulus, modulus_size);
}
static int dnssec_ecdsa_verify_raw(
const char *hash_algorithm,
const char *curve,
const void *signature_r, size_t signature_r_size,
const void *signature_s, size_t signature_s_size,
const void *data, size_t data_size,
const void *key, size_t key_size) {
gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
gcry_mpi_t q = NULL, r = NULL, s = NULL;
gcry_error_t ge;
int k;
assert(hash_algorithm);
ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
if (ge != 0) {
k = -EIO;
goto finish;
}
ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
if (ge != 0) {
k = -EIO;
goto finish;
}
ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
if (ge != 0) {
k = -EIO;
goto finish;
}
ge = gcry_sexp_build(&signature_sexp,
NULL,
"(sig-val (ecdsa (r %m) (s %m)))",
r,
s);
if (ge != 0) {
k = -EIO;
goto finish;
}
ge = gcry_sexp_build(&data_sexp,
NULL,
"(data (flags rfc6979) (hash %s %b))",
hash_algorithm,
(int) data_size,
data);
if (ge != 0) {
k = -EIO;
goto finish;
}
ge = gcry_sexp_build(&public_key_sexp,
NULL,
"(public-key (ecc (curve %s) (q %m)))",
curve,
q);
if (ge != 0) {
k = -EIO;
goto finish;
}
ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
k = 0;
else if (ge != 0) {
log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
k = -EIO;
} else
k = 1;
finish:
if (r)
gcry_mpi_release(r);
if (s)
gcry_mpi_release(s);
if (q)
gcry_mpi_release(q);
if (public_key_sexp)
gcry_sexp_release(public_key_sexp);
if (signature_sexp)
gcry_sexp_release(signature_sexp);
if (data_sexp)
gcry_sexp_release(data_sexp);
return k;
}
static int dnssec_ecdsa_verify(
const char *hash_algorithm,
int algorithm,
const void *hash, size_t hash_size,
DnsResourceRecord *rrsig,
DnsResourceRecord *dnskey) {
const char *curve;
size_t key_size;
uint8_t *q;
assert(hash);
assert(hash_size);
assert(rrsig);
assert(dnskey);
if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
key_size = 32;
curve = "NIST P-256";
} else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
key_size = 48;
curve = "NIST P-384";
} else
return -EOPNOTSUPP;
if (dnskey->dnskey.key_size != key_size * 2)
return -EINVAL;
if (rrsig->rrsig.signature_size != key_size * 2)
return -EINVAL;
q = alloca(key_size*2 + 1);
q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
memcpy(q+1, dnskey->dnskey.key, key_size*2);
return dnssec_ecdsa_verify_raw(
hash_algorithm,
curve,
rrsig->rrsig.signature, key_size,
(uint8_t*) rrsig->rrsig.signature + key_size, key_size,
hash, hash_size,
q, key_size*2+1);
}
static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
gcry_md_write(md, &v, sizeof(v));
}
@ -275,6 +456,31 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
return realtime < inception || realtime > expiration;
}
static int algorithm_to_gcrypt_md(uint8_t algorithm) {
/* Translates a DNSSEC signature algorithm into a gcrypt digest identifier */
switch (algorithm) {
case DNSSEC_ALGORITHM_RSASHA1:
case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
return GCRY_MD_SHA1;
case DNSSEC_ALGORITHM_RSASHA256:
case DNSSEC_ALGORITHM_ECDSAP256SHA256:
return GCRY_MD_SHA256;
case DNSSEC_ALGORITHM_ECDSAP384SHA384:
return GCRY_MD_SHA384;
case DNSSEC_ALGORITHM_RSASHA512:
return GCRY_MD_SHA512;
default:
return -EOPNOTSUPP;
}
}
int dnssec_verify_rrset(
DnsAnswer *a,
DnsResourceKey *key,
@ -284,12 +490,12 @@ int dnssec_verify_rrset(
DnssecResult *result) {
uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
size_t exponent_size, modulus_size, hash_size;
void *exponent, *modulus, *hash;
size_t hash_size;
void *hash;
DnsResourceRecord **list, *rr;
gcry_md_hd_t md = NULL;
int r, md_algorithm;
size_t k, n = 0;
int r;
assert(key);
assert(rrsig);
@ -302,10 +508,13 @@ int dnssec_verify_rrset(
* using the signature "rrsig" and the key "dnskey". It's
* assumed the RRSIG and DNSKEY match. */
if (!dnssec_algorithm_supported(rrsig->rrsig.algorithm)) {
md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
if (md_algorithm == -EOPNOTSUPP) {
*result = DNSSEC_UNSUPPORTED_ALGORITHM;
return 0;
}
if (md_algorithm < 0)
return md_algorithm;
if (a->n_rrs > VERIFY_RRS_MAX)
return -E2BIG;
@ -342,31 +551,13 @@ int dnssec_verify_rrset(
/* Bring the RRs into canonical order */
qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
/* OK, the RRs are now in canonical order. Let's calculate the digest */
initialize_libgcrypt();
/* OK, the RRs are now in canonical order. Let's calculate the digest */
switch (rrsig->rrsig.algorithm) {
case DNSSEC_ALGORITHM_RSASHA1:
case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
gcry_md_open(&md, GCRY_MD_SHA1, 0);
hash_size = 20;
break;
case DNSSEC_ALGORITHM_RSASHA256:
gcry_md_open(&md, GCRY_MD_SHA256, 0);
hash_size = 32;
break;
case DNSSEC_ALGORITHM_RSASHA512:
gcry_md_open(&md, GCRY_MD_SHA512, 0);
hash_size = 64;
break;
default:
assert_not_reached("Unknown digest");
}
hash_size = gcry_md_get_algo_dlen(md_algorithm);
assert(hash_size > 0);
gcry_md_open(&md, md_algorithm, 0);
if (!md)
return -EIO;
@ -417,53 +608,30 @@ int dnssec_verify_rrset(
goto finish;
}
if (*(uint8_t*) dnskey->dnskey.key == 0) {
/* exponent is > 255 bytes long */
switch (rrsig->rrsig.algorithm) {
exponent = (uint8_t*) dnskey->dnskey.key + 3;
exponent_size =
((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) |
((size_t) ((uint8_t*) dnskey->dnskey.key)[1]);
case DNSSEC_ALGORITHM_RSASHA1:
case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
case DNSSEC_ALGORITHM_RSASHA256:
case DNSSEC_ALGORITHM_RSASHA512:
r = dnssec_rsa_verify(
gcry_md_algo_name(md_algorithm),
hash, hash_size,
rrsig,
dnskey);
break;
if (exponent_size < 256) {
r = -EINVAL;
goto finish;
}
if (3 + exponent_size >= dnskey->dnskey.key_size) {
r = -EINVAL;
goto finish;
}
modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
} else {
/* exponent is <= 255 bytes long */
exponent = (uint8_t*) dnskey->dnskey.key + 1;
exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
if (exponent_size <= 0) {
r = -EINVAL;
goto finish;
}
if (1 + exponent_size >= dnskey->dnskey.key_size) {
r = -EINVAL;
goto finish;
}
modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
case DNSSEC_ALGORITHM_ECDSAP256SHA256:
case DNSSEC_ALGORITHM_ECDSAP384SHA384:
r = dnssec_ecdsa_verify(
gcry_md_algo_name(md_algorithm),
rrsig->rrsig.algorithm,
hash, hash_size,
rrsig,
dnskey);
break;
}
r = dnssec_rsa_verify(
gcry_md_algo_name(gcry_md_get_algo(md)),
rrsig->rrsig.signature, rrsig->rrsig.signature_size,
hash, hash_size,
exponent, exponent_size,
modulus, modulus_size);
if (r < 0)
goto finish;
@ -533,6 +701,30 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig)
return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
}
static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
DnsResourceRecord *rr;
int r;
assert(key);
assert(rrsig);
DNS_ANSWER_FOREACH(rr, a) {
r = dns_resource_key_equal(key, rr->key);
if (r < 0)
return r;
if (r == 0)
continue;
/* Pick the TTL as the minimum of the RR's TTL, the
* RR's original TTL according to the RRSIG and the
* RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
}
return 0;
}
int dnssec_verify_rrset_search(
DnsAnswer *a,
DnsResourceKey *key,
@ -599,7 +791,11 @@ int dnssec_verify_rrset_search(
case DNSSEC_VALIDATED:
/* Yay, the RR has been validated,
* return immediately. */
* return immediately, but fix up the expiry */
r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
if (r < 0)
return r;
*result = DNSSEC_VALIDATED;
return 0;
@ -730,9 +926,9 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
return (int) c;
}
static int digest_to_gcrypt(uint8_t algorithm) {
static int digest_to_gcrypt_md(uint8_t algorithm) {
/* Translates a DNSSEC digest algorithm into a gcrypt digest iedntifier */
/* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
switch (algorithm) {
@ -742,6 +938,9 @@ static int digest_to_gcrypt(uint8_t algorithm) {
case DNSSEC_DIGEST_SHA256:
return GCRY_MD_SHA256;
case DNSSEC_DIGEST_SHA384:
return GCRY_MD_SHA384;
default:
return -EOPNOTSUPP;
}
@ -751,9 +950,8 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
gcry_md_hd_t md = NULL;
size_t hash_size;
int algorithm;
int md_algorithm, r;
void *result;
int r;
assert(dnskey);
assert(ds);
@ -776,11 +974,11 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
initialize_libgcrypt();
algorithm = digest_to_gcrypt(ds->ds.digest_type);
if (algorithm < 0)
return algorithm;
md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
if (md_algorithm < 0)
return md_algorithm;
hash_size = gcry_md_get_algo_dlen(algorithm);
hash_size = gcry_md_get_algo_dlen(md_algorithm);
assert(hash_size > 0);
if (ds->ds.digest_size != hash_size)
@ -790,7 +988,7 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
if (r < 0)
return r;
gcry_md_open(&md, algorithm, 0);
gcry_md_open(&md, md_algorithm, 0);
if (!md)
return -EIO;
@ -866,7 +1064,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
if (nsec3->key->type != DNS_TYPE_NSEC3)
return -EINVAL;
algorithm = digest_to_gcrypt(nsec3->nsec3.algorithm);
algorithm = digest_to_gcrypt_md(nsec3->nsec3.algorithm);
if (algorithm < 0)
return algorithm;

View file

@ -339,6 +339,7 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
rr->n_ref = 1;
rr->key = dns_resource_key_ref(key);
rr->expiry = USEC_INFINITY;
return rr;
}

View file

@ -53,6 +53,8 @@ enum {
DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
DNSSEC_ALGORITHM_RSASHA256 = 8, /* RFC 5702 */
DNSSEC_ALGORITHM_RSASHA512 = 10, /* RFC 5702 */
DNSSEC_ALGORITHM_ECDSAP256SHA256 = 13, /* RFC 6605 */
DNSSEC_ALGORITHM_ECDSAP384SHA384 = 14, /* RFC 6605 */
DNSSEC_ALGORITHM_INDIRECT = 252,
DNSSEC_ALGORITHM_PRIVATEDNS,
DNSSEC_ALGORITHM_PRIVATEOID,
@ -64,6 +66,7 @@ enum {
enum {
DNSSEC_DIGEST_SHA1 = 1,
DNSSEC_DIGEST_SHA256 = 2,
DNSSEC_DIGEST_SHA384 = 4,
_DNSSEC_DIGEST_MAX_DEFINED
};
@ -97,6 +100,7 @@ struct DnsResourceRecord {
DnsResourceKey *key;
char *to_string;
uint32_t ttl;
usec_t expiry; /* RRSIG signature expiry */
bool unparseable:1;
bool wire_format_canonical:1;
void *wire_format;

View file

@ -48,14 +48,10 @@ static void dns_transaction_close_connection(DnsTransaction *t) {
t->dns_udp_fd = safe_close(t->dns_udp_fd);
}
static void dns_transaction_stop(DnsTransaction *t) {
static void dns_transaction_stop_timeout(DnsTransaction *t) {
assert(t);
t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
t->stream = dns_stream_free(t->stream);
/* Note that we do not drop the UDP socket here, as we want to
* reuse it to repeat the interaction. */
}
DnsTransaction* dns_transaction_free(DnsTransaction *t) {
@ -67,7 +63,7 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
return NULL;
dns_transaction_close_connection(t);
dns_transaction_stop(t);
dns_transaction_stop_timeout(t);
dns_packet_unref(t->sent);
dns_transaction_reset_answer(t);
@ -264,7 +260,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
t->state = state;
dns_transaction_close_connection(t);
dns_transaction_stop(t);
dns_transaction_stop_timeout(t);
/* Notify all queries that are interested, but make sure the
* transaction isn't freed while we are still looking at it */
@ -725,7 +721,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
if (r > 0) {
/* There are DNSSEC transactions pending now. Update the state accordingly. */
t->state = DNS_TRANSACTION_VALIDATING;
dns_transaction_stop(t);
dns_transaction_close_connection(t);
dns_transaction_stop_timeout(t);
return;
}
}
@ -869,7 +866,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
assert(t);
dns_transaction_stop(t);
dns_transaction_stop_timeout(t);
if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);

View file

@ -98,8 +98,13 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
((unsigned) (n[1] - '0') * 10) +
((unsigned) (n[2] - '0'));
/* Don't allow CC characters or anything that doesn't fit in 8bit */
if (k < ' ' || k > 255 || k == 127)
/* Don't allow anything that doesn't
* fit in 8bit. Note that we do allow
* control characters, as some servers
* (e.g. cloudflare) are happy to
* generate labels with them
* inside. */
if (k > 255)
return -EINVAL;
if (d)
@ -245,7 +250,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
*(q++) = *p;
sz -= 1;
} else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
} else {
/* Everything else */
@ -259,8 +264,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
sz -= 4;
} else
return -EINVAL;
}
p++;
l--;

View file

@ -168,7 +168,7 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex
static void test_dns_label_escape(void) {
test_dns_label_escape_one("", 0, NULL, -EINVAL);
test_dns_label_escape_one("hallo", 5, "hallo", 5);
test_dns_label_escape_one("hallo", 6, NULL, -EINVAL);
test_dns_label_escape_one("hallo", 6, "hallo\\000", 9);
test_dns_label_escape_one("hallo hallo.foobar,waldi", 24, "hallo\\032hallo\\.foobar\\044waldi", 31);
}
@ -190,7 +190,7 @@ static void test_dns_name_normalize(void) {
test_dns_name_normalize_one("f", "f", 0);
test_dns_name_normalize_one("f.waldi", "f.waldi", 0);
test_dns_name_normalize_one("f \\032.waldi", "f\\032\\032.waldi", 0);
test_dns_name_normalize_one("\\000", NULL, -EINVAL);
test_dns_name_normalize_one("\\000", "\\000", 0);
test_dns_name_normalize_one("..", NULL, -EINVAL);
test_dns_name_normalize_one(".foobar", NULL, -EINVAL);
test_dns_name_normalize_one("foobar.", "foobar", 0);
@ -216,7 +216,7 @@ static void test_dns_name_equal(void) {
test_dns_name_equal_one("abc.def", "CBA.def", false);
test_dns_name_equal_one("", "xxx", false);
test_dns_name_equal_one("ab", "a", false);
test_dns_name_equal_one("\\000", "xxxx", -EINVAL);
test_dns_name_equal_one("\\000", "\\000", true);
test_dns_name_equal_one(".", "", true);
test_dns_name_equal_one(".", ".", true);
test_dns_name_equal_one("..", "..", -EINVAL);