15185a700Sflorian /*
25185a700Sflorian * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian *
45185a700Sflorian * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian * copyright notice and this permission notice appear in all copies.
75185a700Sflorian *
85185a700Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian */
165185a700Sflorian
175185a700Sflorian /*
18*e6c7c102Sjsg * $Id: tsig.c,v 1.15 2024/04/23 13:34:50 jsg Exp $
195185a700Sflorian */
205185a700Sflorian /*! \file */
215185a700Sflorian
225185a700Sflorian #include <stdlib.h>
235185a700Sflorian #include <string.h> /* Required for HP/UX (and others?) */
24c576c3baSflorian #include <time.h>
25c576c3baSflorian
265185a700Sflorian #include <isc/util.h>
27c576c3baSflorian #include <isc/buffer.h>
28c576c3baSflorian #include <isc/refcount.h>
295185a700Sflorian
305185a700Sflorian #include <dns/keyvalues.h>
315185a700Sflorian #include <dns/log.h>
325185a700Sflorian #include <dns/message.h>
335185a700Sflorian #include <dns/rdata.h>
345185a700Sflorian #include <dns/rdatalist.h>
355185a700Sflorian #include <dns/rdataset.h>
365185a700Sflorian #include <dns/result.h>
375185a700Sflorian #include <dns/tsig.h>
385185a700Sflorian
395185a700Sflorian #include <dst/result.h>
405185a700Sflorian
415185a700Sflorian #define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
425185a700Sflorian #define algname_is_allocated(algname) \
435185a700Sflorian ((algname) != dns_tsig_hmacsha1_name && \
445185a700Sflorian (algname) != dns_tsig_hmacsha224_name && \
455185a700Sflorian (algname) != dns_tsig_hmacsha256_name && \
465185a700Sflorian (algname) != dns_tsig_hmacsha384_name && \
475185a700Sflorian (algname) != dns_tsig_hmacsha512_name)
485185a700Sflorian
495185a700Sflorian #define BADTIMELEN 6
505185a700Sflorian
515185a700Sflorian static unsigned char hmacsha1_ndata[] = "\011hmac-sha1";
525185a700Sflorian static unsigned char hmacsha1_offsets[] = { 0, 10 };
535185a700Sflorian static dns_name_t hmacsha1 =
545185a700Sflorian DNS_NAME_INITABSOLUTE(hmacsha1_ndata, hmacsha1_offsets);
555185a700Sflorian dns_name_t *dns_tsig_hmacsha1_name = &hmacsha1;
565185a700Sflorian
575185a700Sflorian static unsigned char hmacsha224_ndata[] = "\013hmac-sha224";
585185a700Sflorian static unsigned char hmacsha224_offsets[] = { 0, 12 };
595185a700Sflorian static dns_name_t hmacsha224 =
605185a700Sflorian DNS_NAME_INITABSOLUTE(hmacsha224_ndata, hmacsha224_offsets);
615185a700Sflorian dns_name_t *dns_tsig_hmacsha224_name = &hmacsha224;
625185a700Sflorian
635185a700Sflorian static unsigned char hmacsha256_ndata[] = "\013hmac-sha256";
645185a700Sflorian static unsigned char hmacsha256_offsets[] = { 0, 12 };
655185a700Sflorian static dns_name_t hmacsha256 =
665185a700Sflorian DNS_NAME_INITABSOLUTE(hmacsha256_ndata, hmacsha256_offsets);
675185a700Sflorian dns_name_t *dns_tsig_hmacsha256_name = &hmacsha256;
685185a700Sflorian
695185a700Sflorian static unsigned char hmacsha384_ndata[] = "\013hmac-sha384";
705185a700Sflorian static unsigned char hmacsha384_offsets[] = { 0, 12 };
715185a700Sflorian static dns_name_t hmacsha384 =
725185a700Sflorian DNS_NAME_INITABSOLUTE(hmacsha384_ndata, hmacsha384_offsets);
735185a700Sflorian dns_name_t *dns_tsig_hmacsha384_name = &hmacsha384;
745185a700Sflorian
755185a700Sflorian static unsigned char hmacsha512_ndata[] = "\013hmac-sha512";
765185a700Sflorian static unsigned char hmacsha512_offsets[] = { 0, 12 };
775185a700Sflorian static dns_name_t hmacsha512 =
785185a700Sflorian DNS_NAME_INITABSOLUTE(hmacsha512_ndata, hmacsha512_offsets);
795185a700Sflorian dns_name_t *dns_tsig_hmacsha512_name = &hmacsha512;
805185a700Sflorian
815185a700Sflorian static isc_result_t
825185a700Sflorian tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg);
835185a700Sflorian
845185a700Sflorian static void
855185a700Sflorian tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...)
8687f06ebfSflorian __attribute__((__format__(__printf__, 3, 4)));
875185a700Sflorian
885185a700Sflorian static void
895185a700Sflorian tsigkey_free(dns_tsigkey_t *key);
905185a700Sflorian
915185a700Sflorian static void
tsig_log(dns_tsigkey_t * key,int level,const char * fmt,...)925185a700Sflorian tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
935185a700Sflorian va_list ap;
945185a700Sflorian char message[4096];
955185a700Sflorian char namestr[DNS_NAME_FORMATSIZE];
965185a700Sflorian char creatorstr[DNS_NAME_FORMATSIZE];
975185a700Sflorian
981fb015a8Sflorian if (!isc_log_wouldlog(dns_lctx, level))
995185a700Sflorian return;
1005185a700Sflorian if (key != NULL) {
1015185a700Sflorian dns_name_format(&key->name, namestr, sizeof(namestr));
1025185a700Sflorian } else {
1035185a700Sflorian strlcpy(namestr, "<null>", sizeof(namestr));
1045185a700Sflorian }
1055185a700Sflorian
1065185a700Sflorian if (key != NULL && key->generated && key->creator) {
1075185a700Sflorian dns_name_format(key->creator, creatorstr, sizeof(creatorstr));
1085185a700Sflorian } else {
1095185a700Sflorian strlcpy(creatorstr, "<null>", sizeof(creatorstr));
1105185a700Sflorian }
1115185a700Sflorian
1125185a700Sflorian va_start(ap, fmt);
1135185a700Sflorian vsnprintf(message, sizeof(message), fmt, ap);
1145185a700Sflorian va_end(ap);
1155185a700Sflorian if (key != NULL && key->generated) {
1165185a700Sflorian isc_log_write(dns_lctx,
1175185a700Sflorian DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
1185185a700Sflorian level, "tsig key '%s' (%s): %s",
1195185a700Sflorian namestr, creatorstr, message);
1205185a700Sflorian } else {
1215185a700Sflorian isc_log_write(dns_lctx,
1225185a700Sflorian DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
1235185a700Sflorian level, "tsig key '%s': %s", namestr, message);
1245185a700Sflorian }
1255185a700Sflorian }
1265185a700Sflorian
1275185a700Sflorian isc_result_t
dns_tsigkey_createfromkey(dns_name_t * name,dns_name_t * algorithm,dst_key_t * dstkey,int generated,dns_name_t * creator,time_t inception,time_t expire,dns_tsigkey_t ** key)1285185a700Sflorian dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm,
1291fb015a8Sflorian dst_key_t *dstkey, int generated,
130c576c3baSflorian dns_name_t *creator, time_t inception,
131c576c3baSflorian time_t expire,
1325185a700Sflorian dns_tsigkey_t **key)
1335185a700Sflorian {
1345185a700Sflorian dns_tsigkey_t *tkey;
1355185a700Sflorian isc_result_t ret;
1365185a700Sflorian unsigned int refs = 0;
1375185a700Sflorian
1385185a700Sflorian REQUIRE(key == NULL || *key == NULL);
1395185a700Sflorian REQUIRE(name != NULL);
1405185a700Sflorian REQUIRE(algorithm != NULL);
1415185a700Sflorian REQUIRE(key != NULL);
1425185a700Sflorian
1435185a700Sflorian tkey = (dns_tsigkey_t *) malloc(sizeof(dns_tsigkey_t));
1445185a700Sflorian if (tkey == NULL)
1455185a700Sflorian return (ISC_R_NOMEMORY);
1465185a700Sflorian
1475185a700Sflorian dns_name_init(&tkey->name, NULL);
1485185a700Sflorian ret = dns_name_dup(name, &tkey->name);
1495185a700Sflorian if (ret != ISC_R_SUCCESS)
1505185a700Sflorian goto cleanup_key;
1515185a700Sflorian (void)dns_name_downcase(&tkey->name, &tkey->name, NULL);
1525185a700Sflorian
1535185a700Sflorian if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
1545185a700Sflorian tkey->algorithm = DNS_TSIG_HMACSHA1_NAME;
1555185a700Sflorian if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA1) {
1565185a700Sflorian ret = DNS_R_BADALG;
1575185a700Sflorian goto cleanup_name;
1585185a700Sflorian }
1595185a700Sflorian } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
1605185a700Sflorian tkey->algorithm = DNS_TSIG_HMACSHA224_NAME;
1615185a700Sflorian if (dstkey != NULL &&
1625185a700Sflorian dst_key_alg(dstkey) != DST_ALG_HMACSHA224) {
1635185a700Sflorian ret = DNS_R_BADALG;
1645185a700Sflorian goto cleanup_name;
1655185a700Sflorian }
1665185a700Sflorian } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
1675185a700Sflorian tkey->algorithm = DNS_TSIG_HMACSHA256_NAME;
1685185a700Sflorian if (dstkey != NULL &&
1695185a700Sflorian dst_key_alg(dstkey) != DST_ALG_HMACSHA256) {
1705185a700Sflorian ret = DNS_R_BADALG;
1715185a700Sflorian goto cleanup_name;
1725185a700Sflorian }
1735185a700Sflorian } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
1745185a700Sflorian tkey->algorithm = DNS_TSIG_HMACSHA384_NAME;
1755185a700Sflorian if (dstkey != NULL &&
1765185a700Sflorian dst_key_alg(dstkey) != DST_ALG_HMACSHA384) {
1775185a700Sflorian ret = DNS_R_BADALG;
1785185a700Sflorian goto cleanup_name;
1795185a700Sflorian }
1805185a700Sflorian } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
1815185a700Sflorian tkey->algorithm = DNS_TSIG_HMACSHA512_NAME;
1825185a700Sflorian if (dstkey != NULL &&
1835185a700Sflorian dst_key_alg(dstkey) != DST_ALG_HMACSHA512) {
1845185a700Sflorian ret = DNS_R_BADALG;
1855185a700Sflorian goto cleanup_name;
1865185a700Sflorian }
1875185a700Sflorian } else {
1885185a700Sflorian if (dstkey != NULL) {
1895185a700Sflorian ret = DNS_R_BADALG;
1905185a700Sflorian goto cleanup_name;
1915185a700Sflorian }
1925185a700Sflorian tkey->algorithm = malloc(sizeof(dns_name_t));
1935185a700Sflorian if (tkey->algorithm == NULL) {
1945185a700Sflorian ret = ISC_R_NOMEMORY;
1955185a700Sflorian goto cleanup_name;
1965185a700Sflorian }
1975185a700Sflorian dns_name_init(tkey->algorithm, NULL);
1985185a700Sflorian ret = dns_name_dup(algorithm, tkey->algorithm);
1995185a700Sflorian if (ret != ISC_R_SUCCESS)
2005185a700Sflorian goto cleanup_algorithm;
2015185a700Sflorian (void)dns_name_downcase(tkey->algorithm, tkey->algorithm,
2025185a700Sflorian NULL);
2035185a700Sflorian }
2045185a700Sflorian
2055185a700Sflorian if (creator != NULL) {
2065185a700Sflorian tkey->creator = malloc(sizeof(dns_name_t));
2075185a700Sflorian if (tkey->creator == NULL) {
2085185a700Sflorian ret = ISC_R_NOMEMORY;
2095185a700Sflorian goto cleanup_algorithm;
2105185a700Sflorian }
2115185a700Sflorian dns_name_init(tkey->creator, NULL);
2125185a700Sflorian ret = dns_name_dup(creator, tkey->creator);
2135185a700Sflorian if (ret != ISC_R_SUCCESS) {
2145185a700Sflorian free(tkey->creator);
2155185a700Sflorian goto cleanup_algorithm;
2165185a700Sflorian }
2175185a700Sflorian } else
2185185a700Sflorian tkey->creator = NULL;
2195185a700Sflorian
2205185a700Sflorian tkey->key = NULL;
2215185a700Sflorian if (dstkey != NULL)
2225185a700Sflorian dst_key_attach(dstkey, &tkey->key);
2235185a700Sflorian
2245185a700Sflorian if (key != NULL)
2255185a700Sflorian refs = 1;
2265185a700Sflorian
2275185a700Sflorian ret = isc_refcount_init(&tkey->refs, refs);
2285185a700Sflorian if (ret != ISC_R_SUCCESS)
2295185a700Sflorian goto cleanup_creator;
2305185a700Sflorian
2315185a700Sflorian tkey->generated = generated;
2325185a700Sflorian tkey->inception = inception;
2335185a700Sflorian tkey->expire = expire;
2345185a700Sflorian ISC_LINK_INIT(tkey, link);
2355185a700Sflorian
2365185a700Sflorian /*
2375185a700Sflorian * Ignore this if it's a GSS key, since the key size is meaningless.
2385185a700Sflorian */
2395185a700Sflorian if (dstkey != NULL && dst_key_size(dstkey) < 64) {
2405185a700Sflorian char namestr[DNS_NAME_FORMATSIZE];
2415185a700Sflorian dns_name_format(name, namestr, sizeof(namestr));
2425185a700Sflorian isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
2435185a700Sflorian DNS_LOGMODULE_TSIG, ISC_LOG_INFO,
2445185a700Sflorian "the key '%s' is too short to be secure",
2455185a700Sflorian namestr);
2465185a700Sflorian }
2475185a700Sflorian
2485185a700Sflorian if (key != NULL)
2495185a700Sflorian *key = tkey;
2505185a700Sflorian
2515185a700Sflorian return (ISC_R_SUCCESS);
2525185a700Sflorian
2535185a700Sflorian cleanup_creator:
2545185a700Sflorian if (tkey->key != NULL)
2555185a700Sflorian dst_key_free(&tkey->key);
2565185a700Sflorian if (tkey->creator != NULL) {
2575185a700Sflorian dns_name_free(tkey->creator);
2585185a700Sflorian free(tkey->creator);
2595185a700Sflorian }
2605185a700Sflorian cleanup_algorithm:
2615185a700Sflorian if (algname_is_allocated(tkey->algorithm)) {
2625185a700Sflorian if (dns_name_dynamic(tkey->algorithm))
2635185a700Sflorian dns_name_free(tkey->algorithm);
2645185a700Sflorian free(tkey->algorithm);
2655185a700Sflorian }
2665185a700Sflorian cleanup_name:
2675185a700Sflorian dns_name_free(&tkey->name);
2685185a700Sflorian cleanup_key:
2695185a700Sflorian free(tkey);
2705185a700Sflorian
2715185a700Sflorian return (ret);
2725185a700Sflorian }
2735185a700Sflorian
2745185a700Sflorian isc_result_t
dns_tsigkey_create(dns_name_t * name,dns_name_t * algorithm,unsigned char * secret,int length,int generated,dns_name_t * creator,time_t inception,time_t expire,dns_tsigkey_t ** key)2755185a700Sflorian dns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm,
2761fb015a8Sflorian unsigned char *secret, int length, int generated,
277c576c3baSflorian dns_name_t *creator, time_t inception,
278c576c3baSflorian time_t expire,
2795185a700Sflorian dns_tsigkey_t **key)
2805185a700Sflorian {
2815185a700Sflorian dst_key_t *dstkey = NULL;
2825185a700Sflorian isc_result_t result;
2835185a700Sflorian
2845185a700Sflorian REQUIRE(length >= 0);
2855185a700Sflorian if (length > 0)
2865185a700Sflorian REQUIRE(secret != NULL);
2875185a700Sflorian
2885185a700Sflorian if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
2895185a700Sflorian if (secret != NULL) {
2905185a700Sflorian isc_buffer_t b;
2915185a700Sflorian
2925185a700Sflorian isc_buffer_init(&b, secret, length);
2935185a700Sflorian isc_buffer_add(&b, length);
29427db9c2cSflorian result = dst_key_frombuffer(DST_ALG_HMACSHA1,
2955185a700Sflorian DNS_KEYOWNER_ENTITY,
2965185a700Sflorian DNS_KEYPROTO_DNSSEC,
2975185a700Sflorian &b, &dstkey);
2985185a700Sflorian if (result != ISC_R_SUCCESS)
2995185a700Sflorian return (result);
3005185a700Sflorian }
3015185a700Sflorian } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
3025185a700Sflorian if (secret != NULL) {
3035185a700Sflorian isc_buffer_t b;
3045185a700Sflorian
3055185a700Sflorian isc_buffer_init(&b, secret, length);
3065185a700Sflorian isc_buffer_add(&b, length);
30727db9c2cSflorian result = dst_key_frombuffer(DST_ALG_HMACSHA224,
3085185a700Sflorian DNS_KEYOWNER_ENTITY,
3095185a700Sflorian DNS_KEYPROTO_DNSSEC,
3105185a700Sflorian &b, &dstkey);
3115185a700Sflorian if (result != ISC_R_SUCCESS)
3125185a700Sflorian return (result);
3135185a700Sflorian }
3145185a700Sflorian } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
3155185a700Sflorian if (secret != NULL) {
3165185a700Sflorian isc_buffer_t b;
3175185a700Sflorian
3185185a700Sflorian isc_buffer_init(&b, secret, length);
3195185a700Sflorian isc_buffer_add(&b, length);
32027db9c2cSflorian result = dst_key_frombuffer(DST_ALG_HMACSHA256,
3215185a700Sflorian DNS_KEYOWNER_ENTITY,
3225185a700Sflorian DNS_KEYPROTO_DNSSEC,
3235185a700Sflorian &b, &dstkey);
3245185a700Sflorian if (result != ISC_R_SUCCESS)
3255185a700Sflorian return (result);
3265185a700Sflorian }
3275185a700Sflorian } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
3285185a700Sflorian if (secret != NULL) {
3295185a700Sflorian isc_buffer_t b;
3305185a700Sflorian
3315185a700Sflorian isc_buffer_init(&b, secret, length);
3325185a700Sflorian isc_buffer_add(&b, length);
33327db9c2cSflorian result = dst_key_frombuffer(DST_ALG_HMACSHA384,
3345185a700Sflorian DNS_KEYOWNER_ENTITY,
3355185a700Sflorian DNS_KEYPROTO_DNSSEC,
3365185a700Sflorian &b, &dstkey);
3375185a700Sflorian if (result != ISC_R_SUCCESS)
3385185a700Sflorian return (result);
3395185a700Sflorian }
3405185a700Sflorian } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
3415185a700Sflorian if (secret != NULL) {
3425185a700Sflorian isc_buffer_t b;
3435185a700Sflorian
3445185a700Sflorian isc_buffer_init(&b, secret, length);
3455185a700Sflorian isc_buffer_add(&b, length);
34627db9c2cSflorian result = dst_key_frombuffer(DST_ALG_HMACSHA512,
3475185a700Sflorian DNS_KEYOWNER_ENTITY,
3485185a700Sflorian DNS_KEYPROTO_DNSSEC,
3495185a700Sflorian &b, &dstkey);
3505185a700Sflorian if (result != ISC_R_SUCCESS)
3515185a700Sflorian return (result);
3525185a700Sflorian }
3535185a700Sflorian } else if (length > 0)
3545185a700Sflorian return (DNS_R_BADALG);
3555185a700Sflorian
3565185a700Sflorian result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
3575185a700Sflorian generated, creator,
3585185a700Sflorian inception, expire, key);
3595185a700Sflorian if (dstkey != NULL)
3605185a700Sflorian dst_key_free(&dstkey);
3615185a700Sflorian return (result);
3625185a700Sflorian }
3635185a700Sflorian
3645185a700Sflorian void
dns_tsigkey_attach(dns_tsigkey_t * source,dns_tsigkey_t ** targetp)3655185a700Sflorian dns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) {
3665185a700Sflorian REQUIRE(targetp != NULL && *targetp == NULL);
3675185a700Sflorian
3685185a700Sflorian isc_refcount_increment(&source->refs, NULL);
3695185a700Sflorian *targetp = source;
3705185a700Sflorian }
3715185a700Sflorian
3725185a700Sflorian static void
tsigkey_free(dns_tsigkey_t * key)3735185a700Sflorian tsigkey_free(dns_tsigkey_t *key) {
3745185a700Sflorian dns_name_free(&key->name);
3755185a700Sflorian if (algname_is_allocated(key->algorithm)) {
3765185a700Sflorian dns_name_free(key->algorithm);
3775185a700Sflorian free(key->algorithm);
3785185a700Sflorian }
3795185a700Sflorian if (key->key != NULL)
3805185a700Sflorian dst_key_free(&key->key);
3815185a700Sflorian if (key->creator != NULL) {
3825185a700Sflorian dns_name_free(key->creator);
3835185a700Sflorian free(key->creator);
3845185a700Sflorian }
3855185a700Sflorian isc_refcount_destroy(&key->refs);
3865185a700Sflorian free(key);
3875185a700Sflorian }
3885185a700Sflorian
3895185a700Sflorian void
dns_tsigkey_detach(dns_tsigkey_t ** keyp)3905185a700Sflorian dns_tsigkey_detach(dns_tsigkey_t **keyp) {
3915185a700Sflorian dns_tsigkey_t *key;
3925185a700Sflorian unsigned int refs;
3935185a700Sflorian
3945185a700Sflorian REQUIRE(keyp != NULL);
3955185a700Sflorian
3965185a700Sflorian key = *keyp;
3975185a700Sflorian isc_refcount_decrement(&key->refs, &refs);
3985185a700Sflorian
3995185a700Sflorian if (refs == 0)
4005185a700Sflorian tsigkey_free(key);
4015185a700Sflorian
4025185a700Sflorian *keyp = NULL;
4035185a700Sflorian }
4045185a700Sflorian
4055185a700Sflorian isc_result_t
dns_tsig_sign(dns_message_t * msg)4065185a700Sflorian dns_tsig_sign(dns_message_t *msg) {
4075185a700Sflorian dns_tsigkey_t *key;
4085185a700Sflorian dns_rdata_any_tsig_t tsig, querytsig;
4095185a700Sflorian unsigned char data[128];
4105185a700Sflorian isc_buffer_t databuf, sigbuf;
4115185a700Sflorian isc_buffer_t *dynbuf;
4125185a700Sflorian dns_name_t *owner;
4135185a700Sflorian dns_rdata_t *rdata = NULL;
4145185a700Sflorian dns_rdatalist_t *datalist;
4155185a700Sflorian dns_rdataset_t *dataset;
4165185a700Sflorian isc_region_t r;
417c576c3baSflorian time_t now;
4185185a700Sflorian dst_context_t *ctx = NULL;
4195185a700Sflorian isc_result_t ret;
4205185a700Sflorian unsigned char badtimedata[BADTIMELEN];
4215185a700Sflorian unsigned int sigsize = 0;
4221fb015a8Sflorian int response;
4235185a700Sflorian
4245185a700Sflorian REQUIRE(msg != NULL);
4255185a700Sflorian key = dns_message_gettsigkey(msg);
4265185a700Sflorian
4275185a700Sflorian /*
4285185a700Sflorian * If this is a response, there should be a query tsig.
4295185a700Sflorian */
4305185a700Sflorian response = is_response(msg);
4315185a700Sflorian if (response && msg->querytsig == NULL)
4325185a700Sflorian return (DNS_R_EXPECTEDTSIG);
4335185a700Sflorian
4345185a700Sflorian dynbuf = NULL;
4355185a700Sflorian
4365185a700Sflorian tsig.common.rdclass = dns_rdataclass_any;
4375185a700Sflorian tsig.common.rdtype = dns_rdatatype_tsig;
4385185a700Sflorian ISC_LINK_INIT(&tsig.common, link);
4395185a700Sflorian dns_name_init(&tsig.algorithm, NULL);
4405185a700Sflorian dns_name_clone(key->algorithm, &tsig.algorithm);
4415185a700Sflorian
442c576c3baSflorian time(&now);
4435185a700Sflorian tsig.timesigned = now + msg->timeadjust;
4445185a700Sflorian tsig.fudge = DNS_TSIG_FUDGE;
4455185a700Sflorian
4465185a700Sflorian tsig.originalid = msg->id;
4475185a700Sflorian
4485185a700Sflorian isc_buffer_init(&databuf, data, sizeof(data));
4495185a700Sflorian
4505185a700Sflorian if (response)
4515185a700Sflorian tsig.error = msg->querytsigstatus;
4525185a700Sflorian else
4535185a700Sflorian tsig.error = dns_rcode_noerror;
4545185a700Sflorian
4555185a700Sflorian if (tsig.error != dns_tsigerror_badtime) {
4565185a700Sflorian tsig.otherlen = 0;
4575185a700Sflorian tsig.other = NULL;
4585185a700Sflorian } else {
4595185a700Sflorian isc_buffer_t otherbuf;
4605185a700Sflorian
4615185a700Sflorian tsig.otherlen = BADTIMELEN;
4625185a700Sflorian tsig.other = badtimedata;
4635185a700Sflorian isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen);
4645185a700Sflorian isc_buffer_putuint48(&otherbuf, tsig.timesigned);
4655185a700Sflorian }
4665185a700Sflorian
4675185a700Sflorian if ((key->key != NULL) &&
4685185a700Sflorian (tsig.error != dns_tsigerror_badsig) &&
4695185a700Sflorian (tsig.error != dns_tsigerror_badkey))
4705185a700Sflorian {
4715185a700Sflorian unsigned char header[DNS_MESSAGE_HEADERLEN];
4725185a700Sflorian isc_buffer_t headerbuf;
4735185a700Sflorian uint16_t digestbits;
4745185a700Sflorian
4755185a700Sflorian /*
4765185a700Sflorian * If it is a response, we assume that the request MAC
4775185a700Sflorian * has validated at this point. This is why we include a
4785185a700Sflorian * MAC length > 0 in the reply.
4795185a700Sflorian */
4805185a700Sflorian ret = dst_context_create3(key->key,
4815185a700Sflorian DNS_LOGCATEGORY_DNSSEC,
4821fb015a8Sflorian 1, &ctx);
4835185a700Sflorian if (ret != ISC_R_SUCCESS)
4845185a700Sflorian return (ret);
4855185a700Sflorian
4865185a700Sflorian /*
4875185a700Sflorian * If this is a response, digest the request's MAC.
4885185a700Sflorian */
4895185a700Sflorian if (response) {
4905185a700Sflorian dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
4915185a700Sflorian
4925185a700Sflorian INSIST(msg->verified_sig);
4935185a700Sflorian
4945185a700Sflorian ret = dns_rdataset_first(msg->querytsig);
4955185a700Sflorian if (ret != ISC_R_SUCCESS)
4965185a700Sflorian goto cleanup_context;
4975185a700Sflorian dns_rdataset_current(msg->querytsig, &querytsigrdata);
498ee21177aSflorian ret = dns_rdata_tostruct_tsig(&querytsigrdata,
499ee21177aSflorian &querytsig);
5005185a700Sflorian if (ret != ISC_R_SUCCESS)
5015185a700Sflorian goto cleanup_context;
5025185a700Sflorian isc_buffer_putuint16(&databuf, querytsig.siglen);
5035185a700Sflorian if (isc_buffer_availablelength(&databuf) <
5045185a700Sflorian querytsig.siglen) {
5055185a700Sflorian ret = ISC_R_NOSPACE;
5065185a700Sflorian goto cleanup_context;
5075185a700Sflorian }
5085185a700Sflorian isc_buffer_putmem(&databuf, querytsig.signature,
5095185a700Sflorian querytsig.siglen);
5105185a700Sflorian isc_buffer_usedregion(&databuf, &r);
5115185a700Sflorian ret = dst_context_adddata(ctx, &r);
5125185a700Sflorian if (ret != ISC_R_SUCCESS)
5135185a700Sflorian goto cleanup_context;
5145185a700Sflorian }
5155185a700Sflorian
5165185a700Sflorian /*
5175185a700Sflorian * Digest the header.
5185185a700Sflorian */
5195185a700Sflorian isc_buffer_init(&headerbuf, header, sizeof(header));
5205185a700Sflorian dns_message_renderheader(msg, &headerbuf);
5215185a700Sflorian isc_buffer_usedregion(&headerbuf, &r);
5225185a700Sflorian ret = dst_context_adddata(ctx, &r);
5235185a700Sflorian if (ret != ISC_R_SUCCESS)
5245185a700Sflorian goto cleanup_context;
5255185a700Sflorian
5265185a700Sflorian /*
5275185a700Sflorian * Digest the remainder of the message.
5285185a700Sflorian */
5295185a700Sflorian isc_buffer_usedregion(msg->buffer, &r);
5305185a700Sflorian isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
5315185a700Sflorian ret = dst_context_adddata(ctx, &r);
5325185a700Sflorian if (ret != ISC_R_SUCCESS)
5335185a700Sflorian goto cleanup_context;
5345185a700Sflorian
5355185a700Sflorian if (msg->tcp_continuation == 0) {
5365185a700Sflorian /*
5375185a700Sflorian * Digest the name, class, ttl, alg.
5385185a700Sflorian */
5395185a700Sflorian dns_name_toregion(&key->name, &r);
5405185a700Sflorian ret = dst_context_adddata(ctx, &r);
5415185a700Sflorian if (ret != ISC_R_SUCCESS)
5425185a700Sflorian goto cleanup_context;
5435185a700Sflorian
5445185a700Sflorian isc_buffer_clear(&databuf);
5455185a700Sflorian isc_buffer_putuint16(&databuf, dns_rdataclass_any);
5465185a700Sflorian isc_buffer_putuint32(&databuf, 0); /* ttl */
5475185a700Sflorian isc_buffer_usedregion(&databuf, &r);
5485185a700Sflorian ret = dst_context_adddata(ctx, &r);
5495185a700Sflorian if (ret != ISC_R_SUCCESS)
5505185a700Sflorian goto cleanup_context;
5515185a700Sflorian
5525185a700Sflorian dns_name_toregion(&tsig.algorithm, &r);
5535185a700Sflorian ret = dst_context_adddata(ctx, &r);
5545185a700Sflorian if (ret != ISC_R_SUCCESS)
5555185a700Sflorian goto cleanup_context;
5565185a700Sflorian
5575185a700Sflorian }
5585185a700Sflorian /* Digest the timesigned and fudge */
5595185a700Sflorian isc_buffer_clear(&databuf);
5605185a700Sflorian if (tsig.error == dns_tsigerror_badtime) {
5615185a700Sflorian INSIST(response);
5625185a700Sflorian tsig.timesigned = querytsig.timesigned;
5635185a700Sflorian }
5645185a700Sflorian isc_buffer_putuint48(&databuf, tsig.timesigned);
5655185a700Sflorian isc_buffer_putuint16(&databuf, tsig.fudge);
5665185a700Sflorian isc_buffer_usedregion(&databuf, &r);
5675185a700Sflorian ret = dst_context_adddata(ctx, &r);
5685185a700Sflorian if (ret != ISC_R_SUCCESS)
5695185a700Sflorian goto cleanup_context;
5705185a700Sflorian
5715185a700Sflorian if (msg->tcp_continuation == 0) {
5725185a700Sflorian /*
5735185a700Sflorian * Digest the error and other data length.
5745185a700Sflorian */
5755185a700Sflorian isc_buffer_clear(&databuf);
5765185a700Sflorian isc_buffer_putuint16(&databuf, tsig.error);
5775185a700Sflorian isc_buffer_putuint16(&databuf, tsig.otherlen);
5785185a700Sflorian
5795185a700Sflorian isc_buffer_usedregion(&databuf, &r);
5805185a700Sflorian ret = dst_context_adddata(ctx, &r);
5815185a700Sflorian if (ret != ISC_R_SUCCESS)
5825185a700Sflorian goto cleanup_context;
5835185a700Sflorian
5845185a700Sflorian /*
5855185a700Sflorian * Digest other data.
5865185a700Sflorian */
5875185a700Sflorian if (tsig.otherlen > 0) {
5885185a700Sflorian r.length = tsig.otherlen;
5895185a700Sflorian r.base = tsig.other;
5905185a700Sflorian ret = dst_context_adddata(ctx, &r);
5915185a700Sflorian if (ret != ISC_R_SUCCESS)
5925185a700Sflorian goto cleanup_context;
5935185a700Sflorian }
5945185a700Sflorian }
5955185a700Sflorian
5965185a700Sflorian ret = dst_key_sigsize(key->key, &sigsize);
5975185a700Sflorian if (ret != ISC_R_SUCCESS)
5985185a700Sflorian goto cleanup_context;
5995185a700Sflorian tsig.signature = (unsigned char *) malloc(sigsize);
6005185a700Sflorian if (tsig.signature == NULL) {
6015185a700Sflorian ret = ISC_R_NOMEMORY;
6025185a700Sflorian goto cleanup_context;
6035185a700Sflorian }
6045185a700Sflorian
6055185a700Sflorian isc_buffer_init(&sigbuf, tsig.signature, sigsize);
6065185a700Sflorian ret = dst_context_sign(ctx, &sigbuf);
6075185a700Sflorian if (ret != ISC_R_SUCCESS)
6085185a700Sflorian goto cleanup_signature;
6095185a700Sflorian dst_context_destroy(&ctx);
6105185a700Sflorian digestbits = dst_key_getbits(key->key);
6115185a700Sflorian if (digestbits != 0) {
6125185a700Sflorian /*
6135185a700Sflorian * XXXRAY: Is this correct? What is the
6145185a700Sflorian * expected behavior when digestbits is not an
6155185a700Sflorian * integral multiple of 8? It looks like bytes
6165185a700Sflorian * should either be (digestbits/8) or
6175185a700Sflorian * (digestbits+7)/8.
6185185a700Sflorian *
6195185a700Sflorian * In any case, for current algorithms,
6205185a700Sflorian * digestbits are an integral multiple of 8, so
6215185a700Sflorian * it has the same effect as (digestbits/8).
6225185a700Sflorian */
6235185a700Sflorian unsigned int bytes = (digestbits + 1) / 8;
6245185a700Sflorian if (response && bytes < querytsig.siglen)
6255185a700Sflorian bytes = querytsig.siglen;
6265185a700Sflorian if (bytes > isc_buffer_usedlength(&sigbuf))
6275185a700Sflorian bytes = isc_buffer_usedlength(&sigbuf);
6285185a700Sflorian tsig.siglen = bytes;
6295185a700Sflorian } else
6305185a700Sflorian tsig.siglen = isc_buffer_usedlength(&sigbuf);
6315185a700Sflorian } else {
6325185a700Sflorian tsig.siglen = 0;
6335185a700Sflorian tsig.signature = NULL;
6345185a700Sflorian }
6355185a700Sflorian
6365185a700Sflorian ret = dns_message_gettemprdata(msg, &rdata);
6375185a700Sflorian if (ret != ISC_R_SUCCESS)
6385185a700Sflorian goto cleanup_signature;
6395185a700Sflorian ret = isc_buffer_allocate(&dynbuf, 512);
6405185a700Sflorian if (ret != ISC_R_SUCCESS)
6415185a700Sflorian goto cleanup_rdata;
642a5525167Sflorian ret = dns_rdata_fromstruct_tsig(rdata, dns_rdataclass_any,
6435185a700Sflorian dns_rdatatype_tsig, &tsig, dynbuf);
6445185a700Sflorian if (ret != ISC_R_SUCCESS)
6455185a700Sflorian goto cleanup_dynbuf;
6465185a700Sflorian
6475185a700Sflorian dns_message_takebuffer(msg, &dynbuf);
6485185a700Sflorian
6495185a700Sflorian if (tsig.signature != NULL) {
6505185a700Sflorian free(tsig.signature);
6515185a700Sflorian tsig.signature = NULL;
6525185a700Sflorian }
6535185a700Sflorian
6545185a700Sflorian owner = NULL;
6555185a700Sflorian ret = dns_message_gettempname(msg, &owner);
6565185a700Sflorian if (ret != ISC_R_SUCCESS)
6575185a700Sflorian goto cleanup_rdata;
6585185a700Sflorian dns_name_init(owner, NULL);
6595185a700Sflorian ret = dns_name_dup(&key->name, owner);
6605185a700Sflorian if (ret != ISC_R_SUCCESS)
6615185a700Sflorian goto cleanup_owner;
6625185a700Sflorian
6635185a700Sflorian datalist = NULL;
6645185a700Sflorian ret = dns_message_gettemprdatalist(msg, &datalist);
6655185a700Sflorian if (ret != ISC_R_SUCCESS)
6665185a700Sflorian goto cleanup_owner;
6675185a700Sflorian dataset = NULL;
6685185a700Sflorian ret = dns_message_gettemprdataset(msg, &dataset);
6695185a700Sflorian if (ret != ISC_R_SUCCESS)
6705185a700Sflorian goto cleanup_rdatalist;
6715185a700Sflorian datalist->rdclass = dns_rdataclass_any;
6725185a700Sflorian datalist->type = dns_rdatatype_tsig;
6735185a700Sflorian ISC_LIST_APPEND(datalist->rdata, rdata, link);
6745185a700Sflorian RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset)
6755185a700Sflorian == ISC_R_SUCCESS);
6765185a700Sflorian msg->tsig = dataset;
6775185a700Sflorian msg->tsigname = owner;
6785185a700Sflorian
6795185a700Sflorian /* Windows does not like the tsig name being compressed. */
6805185a700Sflorian msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
6815185a700Sflorian
6825185a700Sflorian return (ISC_R_SUCCESS);
6835185a700Sflorian
6845185a700Sflorian cleanup_rdatalist:
6855185a700Sflorian dns_message_puttemprdatalist(msg, &datalist);
6865185a700Sflorian cleanup_owner:
6875185a700Sflorian dns_message_puttempname(msg, &owner);
6885185a700Sflorian goto cleanup_rdata;
6895185a700Sflorian cleanup_dynbuf:
6905185a700Sflorian isc_buffer_free(&dynbuf);
6915185a700Sflorian cleanup_rdata:
6925185a700Sflorian dns_message_puttemprdata(msg, &rdata);
6935185a700Sflorian cleanup_signature:
6945185a700Sflorian if (tsig.signature != NULL)
6955185a700Sflorian free(tsig.signature);
6965185a700Sflorian cleanup_context:
6975185a700Sflorian if (ctx != NULL)
6985185a700Sflorian dst_context_destroy(&ctx);
6995185a700Sflorian return (ret);
7005185a700Sflorian }
7015185a700Sflorian
7025185a700Sflorian isc_result_t
dns_tsig_verify(isc_buffer_t * source,dns_message_t * msg)7035185a700Sflorian dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg)
7045185a700Sflorian {
7055185a700Sflorian dns_rdata_any_tsig_t tsig, querytsig;
7065185a700Sflorian isc_region_t r, source_r, header_r, sig_r;
7075185a700Sflorian isc_buffer_t databuf;
7085185a700Sflorian unsigned char data[32];
7095185a700Sflorian dns_name_t *keyname;
7105185a700Sflorian dns_rdata_t rdata = DNS_RDATA_INIT;
711c576c3baSflorian time_t now;
7125185a700Sflorian isc_result_t ret;
7135185a700Sflorian dns_tsigkey_t *tsigkey;
7145185a700Sflorian dst_key_t *key = NULL;
7155185a700Sflorian unsigned char header[DNS_MESSAGE_HEADERLEN];
7165185a700Sflorian dst_context_t *ctx = NULL;
7175185a700Sflorian uint16_t addcount, id;
7185185a700Sflorian unsigned int siglen;
7195185a700Sflorian unsigned int alg;
7201fb015a8Sflorian int response;
7215185a700Sflorian
7225185a700Sflorian REQUIRE(source != NULL);
7235185a700Sflorian tsigkey = dns_message_gettsigkey(msg);
7245185a700Sflorian response = is_response(msg);
7255185a700Sflorian
7265185a700Sflorian msg->verify_attempted = 1;
7275185a700Sflorian msg->verified_sig = 0;
7285185a700Sflorian msg->tsigstatus = dns_tsigerror_badsig;
7295185a700Sflorian
7305185a700Sflorian if (msg->tcp_continuation) {
7315185a700Sflorian if (tsigkey == NULL || msg->querytsig == NULL)
7325185a700Sflorian return (DNS_R_UNEXPECTEDTSIG);
7335185a700Sflorian return (tsig_verify_tcp(source, msg));
7345185a700Sflorian }
7355185a700Sflorian
7365185a700Sflorian /*
7375185a700Sflorian * There should be a TSIG record...
7385185a700Sflorian */
7395185a700Sflorian if (msg->tsig == NULL)
7405185a700Sflorian return (DNS_R_EXPECTEDTSIG);
7415185a700Sflorian
7425185a700Sflorian /*
7435185a700Sflorian * If this is a response and there's no key or query TSIG, there
7445185a700Sflorian * shouldn't be one on the response.
7455185a700Sflorian */
7465185a700Sflorian if (response && (tsigkey == NULL || msg->querytsig == NULL))
7475185a700Sflorian return (DNS_R_UNEXPECTEDTSIG);
7485185a700Sflorian
7495185a700Sflorian /*
7505185a700Sflorian * If we're here, we know the message is well formed and contains a
7515185a700Sflorian * TSIG record.
7525185a700Sflorian */
7535185a700Sflorian
7545185a700Sflorian keyname = msg->tsigname;
7555185a700Sflorian ret = dns_rdataset_first(msg->tsig);
7565185a700Sflorian if (ret != ISC_R_SUCCESS)
7575185a700Sflorian return (ret);
7585185a700Sflorian dns_rdataset_current(msg->tsig, &rdata);
759ee21177aSflorian ret = dns_rdata_tostruct_tsig(&rdata, &tsig);
7605185a700Sflorian if (ret != ISC_R_SUCCESS)
7615185a700Sflorian return (ret);
7625185a700Sflorian dns_rdata_reset(&rdata);
7635185a700Sflorian if (response) {
7645185a700Sflorian ret = dns_rdataset_first(msg->querytsig);
7655185a700Sflorian if (ret != ISC_R_SUCCESS)
7665185a700Sflorian return (ret);
7675185a700Sflorian dns_rdataset_current(msg->querytsig, &rdata);
768ee21177aSflorian ret = dns_rdata_tostruct_tsig(&rdata, &querytsig);
7695185a700Sflorian if (ret != ISC_R_SUCCESS)
7705185a700Sflorian return (ret);
7715185a700Sflorian }
7725185a700Sflorian /*
7735185a700Sflorian * Do the key name and algorithm match that of the query?
7745185a700Sflorian */
7755185a700Sflorian if (response &&
7765185a700Sflorian (!dns_name_equal(keyname, &tsigkey->name) ||
7775185a700Sflorian !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))) {
7785185a700Sflorian msg->tsigstatus = dns_tsigerror_badkey;
7795185a700Sflorian tsig_log(msg->tsigkey, 2,
7805185a700Sflorian "key name and algorithm do not match");
7815185a700Sflorian return (DNS_R_TSIGVERIFYFAILURE);
7825185a700Sflorian }
7835185a700Sflorian
7845185a700Sflorian /*
7855185a700Sflorian * Get the current time.
7865185a700Sflorian */
787c576c3baSflorian time(&now);
7885185a700Sflorian
7895185a700Sflorian /*
7905185a700Sflorian * Find dns_tsigkey_t based on keyname.
7915185a700Sflorian */
7925185a700Sflorian if (tsigkey == NULL) {
7935185a700Sflorian ret = ISC_R_NOTFOUND;
7945185a700Sflorian if (ret != ISC_R_SUCCESS) {
7955185a700Sflorian msg->tsigstatus = dns_tsigerror_badkey;
7965185a700Sflorian ret = dns_tsigkey_create(keyname, &tsig.algorithm,
7971fb015a8Sflorian NULL, 0, 0, NULL,
7985185a700Sflorian now, now,
7995185a700Sflorian &msg->tsigkey);
8005185a700Sflorian if (ret != ISC_R_SUCCESS)
8015185a700Sflorian return (ret);
8025185a700Sflorian tsig_log(msg->tsigkey, 2, "unknown key");
8035185a700Sflorian return (DNS_R_TSIGVERIFYFAILURE);
8045185a700Sflorian }
8055185a700Sflorian msg->tsigkey = tsigkey;
8065185a700Sflorian }
8075185a700Sflorian
8085185a700Sflorian key = tsigkey->key;
8095185a700Sflorian
8105185a700Sflorian /*
8115185a700Sflorian * Check digest length.
8125185a700Sflorian */
8135185a700Sflorian alg = dst_key_alg(key);
8145185a700Sflorian ret = dst_key_sigsize(key, &siglen);
8155185a700Sflorian if (ret != ISC_R_SUCCESS)
8165185a700Sflorian return (ret);
8175185a700Sflorian if (
8185185a700Sflorian alg == DST_ALG_HMACSHA1 ||
8195185a700Sflorian alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
8205185a700Sflorian alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512)
8215185a700Sflorian {
8225185a700Sflorian if (tsig.siglen > siglen) {
8235185a700Sflorian tsig_log(msg->tsigkey, 2, "signature length too big");
8245185a700Sflorian return (DNS_R_FORMERR);
8255185a700Sflorian }
8265185a700Sflorian if (tsig.siglen > 0 &&
8275185a700Sflorian (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2)))
8285185a700Sflorian {
8295185a700Sflorian tsig_log(msg->tsigkey, 2,
8305185a700Sflorian "signature length below minimum");
8315185a700Sflorian return (DNS_R_FORMERR);
8325185a700Sflorian }
8335185a700Sflorian }
8345185a700Sflorian
8355185a700Sflorian if (tsig.siglen > 0) {
8365185a700Sflorian uint16_t addcount_n;
8375185a700Sflorian
8385185a700Sflorian sig_r.base = tsig.signature;
8395185a700Sflorian sig_r.length = tsig.siglen;
8405185a700Sflorian
8415185a700Sflorian ret = dst_context_create3(key,
8425185a700Sflorian DNS_LOGCATEGORY_DNSSEC,
8431fb015a8Sflorian 0, &ctx);
8445185a700Sflorian if (ret != ISC_R_SUCCESS)
8455185a700Sflorian return (ret);
8465185a700Sflorian
8475185a700Sflorian if (response) {
8485185a700Sflorian isc_buffer_init(&databuf, data, sizeof(data));
8495185a700Sflorian isc_buffer_putuint16(&databuf, querytsig.siglen);
8505185a700Sflorian isc_buffer_usedregion(&databuf, &r);
8515185a700Sflorian ret = dst_context_adddata(ctx, &r);
8525185a700Sflorian if (ret != ISC_R_SUCCESS)
8535185a700Sflorian goto cleanup_context;
8545185a700Sflorian if (querytsig.siglen > 0) {
8555185a700Sflorian r.length = querytsig.siglen;
8565185a700Sflorian r.base = querytsig.signature;
8575185a700Sflorian ret = dst_context_adddata(ctx, &r);
8585185a700Sflorian if (ret != ISC_R_SUCCESS)
8595185a700Sflorian goto cleanup_context;
8605185a700Sflorian }
8615185a700Sflorian }
8625185a700Sflorian
8635185a700Sflorian /*
8645185a700Sflorian * Extract the header.
8655185a700Sflorian */
8665185a700Sflorian isc_buffer_usedregion(source, &r);
8675185a700Sflorian memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
8685185a700Sflorian isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
8695185a700Sflorian
8705185a700Sflorian /*
8715185a700Sflorian * Decrement the additional field counter.
8725185a700Sflorian */
8735185a700Sflorian memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
8745185a700Sflorian addcount_n = ntohs(addcount);
8755185a700Sflorian addcount = htons((uint16_t)(addcount_n - 1));
8765185a700Sflorian memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
8775185a700Sflorian
8785185a700Sflorian /*
8795185a700Sflorian * Put in the original id.
8805185a700Sflorian */
8815185a700Sflorian id = htons(tsig.originalid);
8825185a700Sflorian memmove(&header[0], &id, 2);
8835185a700Sflorian
8845185a700Sflorian /*
8855185a700Sflorian * Digest the modified header.
8865185a700Sflorian */
8875185a700Sflorian header_r.base = (unsigned char *) header;
8885185a700Sflorian header_r.length = DNS_MESSAGE_HEADERLEN;
8895185a700Sflorian ret = dst_context_adddata(ctx, &header_r);
8905185a700Sflorian if (ret != ISC_R_SUCCESS)
8915185a700Sflorian goto cleanup_context;
8925185a700Sflorian
8935185a700Sflorian /*
8945185a700Sflorian * Digest all non-TSIG records.
8955185a700Sflorian */
8965185a700Sflorian isc_buffer_usedregion(source, &source_r);
8975185a700Sflorian r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
8985185a700Sflorian r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
8995185a700Sflorian ret = dst_context_adddata(ctx, &r);
9005185a700Sflorian if (ret != ISC_R_SUCCESS)
9015185a700Sflorian goto cleanup_context;
9025185a700Sflorian
9035185a700Sflorian /*
9045185a700Sflorian * Digest the key name.
9055185a700Sflorian */
9065185a700Sflorian dns_name_toregion(&tsigkey->name, &r);
9075185a700Sflorian ret = dst_context_adddata(ctx, &r);
9085185a700Sflorian if (ret != ISC_R_SUCCESS)
9095185a700Sflorian goto cleanup_context;
9105185a700Sflorian
9115185a700Sflorian isc_buffer_init(&databuf, data, sizeof(data));
9125185a700Sflorian isc_buffer_putuint16(&databuf, tsig.common.rdclass);
9135185a700Sflorian isc_buffer_putuint32(&databuf, msg->tsig->ttl);
9145185a700Sflorian isc_buffer_usedregion(&databuf, &r);
9155185a700Sflorian ret = dst_context_adddata(ctx, &r);
9165185a700Sflorian if (ret != ISC_R_SUCCESS)
9175185a700Sflorian goto cleanup_context;
9185185a700Sflorian
9195185a700Sflorian /*
9205185a700Sflorian * Digest the key algorithm.
9215185a700Sflorian */
9225185a700Sflorian dns_name_toregion(tsigkey->algorithm, &r);
9235185a700Sflorian ret = dst_context_adddata(ctx, &r);
9245185a700Sflorian if (ret != ISC_R_SUCCESS)
9255185a700Sflorian goto cleanup_context;
9265185a700Sflorian
9275185a700Sflorian isc_buffer_clear(&databuf);
9285185a700Sflorian isc_buffer_putuint48(&databuf, tsig.timesigned);
9295185a700Sflorian isc_buffer_putuint16(&databuf, tsig.fudge);
9305185a700Sflorian isc_buffer_putuint16(&databuf, tsig.error);
9315185a700Sflorian isc_buffer_putuint16(&databuf, tsig.otherlen);
9325185a700Sflorian isc_buffer_usedregion(&databuf, &r);
9335185a700Sflorian ret = dst_context_adddata(ctx, &r);
9345185a700Sflorian if (ret != ISC_R_SUCCESS)
9355185a700Sflorian goto cleanup_context;
9365185a700Sflorian
9375185a700Sflorian if (tsig.otherlen > 0) {
9385185a700Sflorian r.base = tsig.other;
9395185a700Sflorian r.length = tsig.otherlen;
9405185a700Sflorian ret = dst_context_adddata(ctx, &r);
9415185a700Sflorian if (ret != ISC_R_SUCCESS)
9425185a700Sflorian goto cleanup_context;
9435185a700Sflorian }
9445185a700Sflorian
9455185a700Sflorian ret = dst_context_verify(ctx, &sig_r);
9465185a700Sflorian if (ret == DST_R_VERIFYFAILURE) {
9475185a700Sflorian ret = DNS_R_TSIGVERIFYFAILURE;
9485185a700Sflorian tsig_log(msg->tsigkey, 2,
9495185a700Sflorian "signature failed to verify(1)");
9505185a700Sflorian goto cleanup_context;
9515185a700Sflorian } else if (ret != ISC_R_SUCCESS) {
9525185a700Sflorian goto cleanup_context;
9535185a700Sflorian }
9545185a700Sflorian msg->verified_sig = 1;
9555185a700Sflorian } else if (tsig.error != dns_tsigerror_badsig &&
9565185a700Sflorian tsig.error != dns_tsigerror_badkey) {
9575185a700Sflorian tsig_log(msg->tsigkey, 2, "signature was empty");
9585185a700Sflorian return (DNS_R_TSIGVERIFYFAILURE);
9595185a700Sflorian }
9605185a700Sflorian
9615185a700Sflorian /*
9625185a700Sflorian * Here at this point, the MAC has been verified. Even if any of
9635185a700Sflorian * the following code returns a TSIG error, the reply will be
9645185a700Sflorian * signed and WILL always include the request MAC in the digest
9655185a700Sflorian * computation.
9665185a700Sflorian */
9675185a700Sflorian
9685185a700Sflorian /*
9695185a700Sflorian * Is the time ok?
9705185a700Sflorian */
971c576c3baSflorian if (now + msg->timeadjust > (time_t)(tsig.timesigned + tsig.fudge)) {
9725185a700Sflorian msg->tsigstatus = dns_tsigerror_badtime;
9735185a700Sflorian tsig_log(msg->tsigkey, 2, "signature has expired");
9745185a700Sflorian ret = DNS_R_CLOCKSKEW;
9755185a700Sflorian goto cleanup_context;
976c576c3baSflorian } else if (now + msg->timeadjust < (time_t)(tsig.timesigned -
977c576c3baSflorian tsig.fudge)) {
9785185a700Sflorian msg->tsigstatus = dns_tsigerror_badtime;
9795185a700Sflorian tsig_log(msg->tsigkey, 2, "signature is in the future");
9805185a700Sflorian ret = DNS_R_CLOCKSKEW;
9815185a700Sflorian goto cleanup_context;
9825185a700Sflorian }
9835185a700Sflorian
9845185a700Sflorian if (
9855185a700Sflorian alg == DST_ALG_HMACSHA1 ||
9865185a700Sflorian alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
9875185a700Sflorian alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512)
9885185a700Sflorian {
9895185a700Sflorian uint16_t digestbits = dst_key_getbits(key);
9905185a700Sflorian
9915185a700Sflorian /*
9925185a700Sflorian * XXXRAY: Is this correct? What is the expected
9935185a700Sflorian * behavior when digestbits is not an integral multiple
9945185a700Sflorian * of 8? It looks like bytes should either be
9955185a700Sflorian * (digestbits/8) or (digestbits+7)/8.
9965185a700Sflorian *
9975185a700Sflorian * In any case, for current algorithms, digestbits are
9985185a700Sflorian * an integral multiple of 8, so it has the same effect
9995185a700Sflorian * as (digestbits/8).
10005185a700Sflorian */
10015185a700Sflorian if (tsig.siglen > 0 && digestbits != 0 &&
10025185a700Sflorian tsig.siglen < ((digestbits + 1) / 8))
10035185a700Sflorian {
10045185a700Sflorian msg->tsigstatus = dns_tsigerror_badtrunc;
10055185a700Sflorian tsig_log(msg->tsigkey, 2,
10065185a700Sflorian "truncated signature length too small");
10075185a700Sflorian ret = DNS_R_TSIGVERIFYFAILURE;
10085185a700Sflorian goto cleanup_context;
10095185a700Sflorian }
10105185a700Sflorian if (tsig.siglen > 0 && digestbits == 0 &&
10115185a700Sflorian tsig.siglen < siglen)
10125185a700Sflorian {
10135185a700Sflorian msg->tsigstatus = dns_tsigerror_badtrunc;
10145185a700Sflorian tsig_log(msg->tsigkey, 2, "signature length too small");
10155185a700Sflorian ret = DNS_R_TSIGVERIFYFAILURE;
10165185a700Sflorian goto cleanup_context;
10175185a700Sflorian }
10185185a700Sflorian }
10195185a700Sflorian
10205185a700Sflorian if (tsig.error != dns_rcode_noerror) {
10215185a700Sflorian msg->tsigstatus = tsig.error;
10225185a700Sflorian if (tsig.error == dns_tsigerror_badtime)
10235185a700Sflorian ret = DNS_R_CLOCKSKEW;
10245185a700Sflorian else
10255185a700Sflorian ret = DNS_R_TSIGERRORSET;
10265185a700Sflorian goto cleanup_context;
10275185a700Sflorian }
10285185a700Sflorian
10295185a700Sflorian msg->tsigstatus = dns_rcode_noerror;
10305185a700Sflorian ret = ISC_R_SUCCESS;
10315185a700Sflorian
10325185a700Sflorian cleanup_context:
10335185a700Sflorian if (ctx != NULL)
10345185a700Sflorian dst_context_destroy(&ctx);
10355185a700Sflorian
10365185a700Sflorian return (ret);
10375185a700Sflorian }
10385185a700Sflorian
10395185a700Sflorian static isc_result_t
tsig_verify_tcp(isc_buffer_t * source,dns_message_t * msg)10405185a700Sflorian tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
10415185a700Sflorian dns_rdata_any_tsig_t tsig, querytsig;
10425185a700Sflorian isc_region_t r, source_r, header_r, sig_r;
10435185a700Sflorian isc_buffer_t databuf;
10445185a700Sflorian unsigned char data[32];
10455185a700Sflorian dns_name_t *keyname;
10465185a700Sflorian dns_rdata_t rdata = DNS_RDATA_INIT;
1047c576c3baSflorian time_t now;
10485185a700Sflorian isc_result_t ret;
10495185a700Sflorian dns_tsigkey_t *tsigkey;
10505185a700Sflorian dst_key_t *key = NULL;
10515185a700Sflorian unsigned char header[DNS_MESSAGE_HEADERLEN];
10525185a700Sflorian uint16_t addcount, id;
10531fb015a8Sflorian int has_tsig = 0;
10545185a700Sflorian unsigned int siglen;
10555185a700Sflorian unsigned int alg;
10565185a700Sflorian
10575185a700Sflorian REQUIRE(source != NULL);
10585185a700Sflorian REQUIRE(msg != NULL);
10595185a700Sflorian REQUIRE(dns_message_gettsigkey(msg) != NULL);
10605185a700Sflorian REQUIRE(msg->tcp_continuation == 1);
10615185a700Sflorian REQUIRE(msg->querytsig != NULL);
10625185a700Sflorian
10635185a700Sflorian msg->verified_sig = 0;
10645185a700Sflorian msg->tsigstatus = dns_tsigerror_badsig;
10655185a700Sflorian
10665185a700Sflorian if (!is_response(msg))
10675185a700Sflorian return (DNS_R_EXPECTEDRESPONSE);
10685185a700Sflorian
10695185a700Sflorian tsigkey = dns_message_gettsigkey(msg);
10705185a700Sflorian key = tsigkey->key;
10715185a700Sflorian
10725185a700Sflorian /*
10735185a700Sflorian * Extract and parse the previous TSIG
10745185a700Sflorian */
10755185a700Sflorian ret = dns_rdataset_first(msg->querytsig);
10765185a700Sflorian if (ret != ISC_R_SUCCESS)
10775185a700Sflorian return (ret);
10785185a700Sflorian dns_rdataset_current(msg->querytsig, &rdata);
1079ee21177aSflorian ret = dns_rdata_tostruct_tsig(&rdata, &querytsig);
10805185a700Sflorian if (ret != ISC_R_SUCCESS)
10815185a700Sflorian return (ret);
10825185a700Sflorian dns_rdata_reset(&rdata);
10835185a700Sflorian
10845185a700Sflorian /*
10855185a700Sflorian * If there is a TSIG in this message, do some checks.
10865185a700Sflorian */
10875185a700Sflorian if (msg->tsig != NULL) {
10881fb015a8Sflorian has_tsig = 1;
10895185a700Sflorian
10905185a700Sflorian keyname = msg->tsigname;
10915185a700Sflorian ret = dns_rdataset_first(msg->tsig);
10925185a700Sflorian if (ret != ISC_R_SUCCESS)
10935185a700Sflorian goto cleanup_querystruct;
10945185a700Sflorian dns_rdataset_current(msg->tsig, &rdata);
1095ee21177aSflorian ret = dns_rdata_tostruct_tsig(&rdata, &tsig);
10965185a700Sflorian if (ret != ISC_R_SUCCESS)
10975185a700Sflorian goto cleanup_querystruct;
10985185a700Sflorian
10995185a700Sflorian /*
11005185a700Sflorian * Do the key name and algorithm match that of the query?
11015185a700Sflorian */
11025185a700Sflorian if (!dns_name_equal(keyname, &tsigkey->name) ||
11035185a700Sflorian !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))
11045185a700Sflorian {
11055185a700Sflorian msg->tsigstatus = dns_tsigerror_badkey;
11065185a700Sflorian ret = DNS_R_TSIGVERIFYFAILURE;
11075185a700Sflorian tsig_log(msg->tsigkey, 2,
11085185a700Sflorian "key name and algorithm do not match");
11095185a700Sflorian goto cleanup_querystruct;
11105185a700Sflorian }
11115185a700Sflorian
11125185a700Sflorian /*
11135185a700Sflorian * Check digest length.
11145185a700Sflorian */
11155185a700Sflorian alg = dst_key_alg(key);
11165185a700Sflorian ret = dst_key_sigsize(key, &siglen);
11175185a700Sflorian if (ret != ISC_R_SUCCESS)
11185185a700Sflorian goto cleanup_querystruct;
11195185a700Sflorian if (
11205185a700Sflorian alg == DST_ALG_HMACSHA1 ||
11215185a700Sflorian alg == DST_ALG_HMACSHA224 ||
11225185a700Sflorian alg == DST_ALG_HMACSHA256 ||
11235185a700Sflorian alg == DST_ALG_HMACSHA384 ||
11245185a700Sflorian alg == DST_ALG_HMACSHA512)
11255185a700Sflorian {
11265185a700Sflorian if (tsig.siglen > siglen) {
11275185a700Sflorian tsig_log(tsigkey, 2,
11285185a700Sflorian "signature length too big");
11295185a700Sflorian ret = DNS_R_FORMERR;
11305185a700Sflorian goto cleanup_querystruct;
11315185a700Sflorian }
11325185a700Sflorian if (tsig.siglen > 0 &&
11335185a700Sflorian (tsig.siglen < 10 ||
11345185a700Sflorian tsig.siglen < ((siglen + 1) / 2)))
11355185a700Sflorian {
11365185a700Sflorian tsig_log(tsigkey, 2,
11375185a700Sflorian "signature length below minimum");
11385185a700Sflorian ret = DNS_R_FORMERR;
11395185a700Sflorian goto cleanup_querystruct;
11405185a700Sflorian }
11415185a700Sflorian }
11425185a700Sflorian }
11435185a700Sflorian
11445185a700Sflorian if (msg->tsigctx == NULL) {
11455185a700Sflorian ret = dst_context_create3(key,
11465185a700Sflorian DNS_LOGCATEGORY_DNSSEC,
11471fb015a8Sflorian 0, &msg->tsigctx);
11485185a700Sflorian if (ret != ISC_R_SUCCESS)
11495185a700Sflorian goto cleanup_querystruct;
11505185a700Sflorian
11515185a700Sflorian /*
11525185a700Sflorian * Digest the length of the query signature
11535185a700Sflorian */
11545185a700Sflorian isc_buffer_init(&databuf, data, sizeof(data));
11555185a700Sflorian isc_buffer_putuint16(&databuf, querytsig.siglen);
11565185a700Sflorian isc_buffer_usedregion(&databuf, &r);
11575185a700Sflorian ret = dst_context_adddata(msg->tsigctx, &r);
11585185a700Sflorian if (ret != ISC_R_SUCCESS)
11595185a700Sflorian goto cleanup_context;
11605185a700Sflorian
11615185a700Sflorian /*
11625185a700Sflorian * Digest the data of the query signature
11635185a700Sflorian */
11645185a700Sflorian if (querytsig.siglen > 0) {
11655185a700Sflorian r.length = querytsig.siglen;
11665185a700Sflorian r.base = querytsig.signature;
11675185a700Sflorian ret = dst_context_adddata(msg->tsigctx, &r);
11685185a700Sflorian if (ret != ISC_R_SUCCESS)
11695185a700Sflorian goto cleanup_context;
11705185a700Sflorian }
11715185a700Sflorian }
11725185a700Sflorian
11735185a700Sflorian /*
11745185a700Sflorian * Extract the header.
11755185a700Sflorian */
11765185a700Sflorian isc_buffer_usedregion(source, &r);
11775185a700Sflorian memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
11785185a700Sflorian isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
11795185a700Sflorian
11805185a700Sflorian /*
11815185a700Sflorian * Decrement the additional field counter if necessary.
11825185a700Sflorian */
11835185a700Sflorian if (has_tsig) {
11845185a700Sflorian uint16_t addcount_n;
11855185a700Sflorian
11865185a700Sflorian memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
11875185a700Sflorian addcount_n = ntohs(addcount);
11885185a700Sflorian addcount = htons((uint16_t)(addcount_n - 1));
11895185a700Sflorian memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
11905185a700Sflorian
11915185a700Sflorian /*
11925185a700Sflorian * Put in the original id.
11935185a700Sflorian *
11945185a700Sflorian * XXX Can TCP transfers be forwarded? How would that
11955185a700Sflorian * work?
11965185a700Sflorian */
11975185a700Sflorian id = htons(tsig.originalid);
11985185a700Sflorian memmove(&header[0], &id, 2);
11995185a700Sflorian }
12005185a700Sflorian
12015185a700Sflorian /*
12025185a700Sflorian * Digest the modified header.
12035185a700Sflorian */
12045185a700Sflorian header_r.base = (unsigned char *) header;
12055185a700Sflorian header_r.length = DNS_MESSAGE_HEADERLEN;
12065185a700Sflorian ret = dst_context_adddata(msg->tsigctx, &header_r);
12075185a700Sflorian if (ret != ISC_R_SUCCESS)
12085185a700Sflorian goto cleanup_context;
12095185a700Sflorian
12105185a700Sflorian /*
12115185a700Sflorian * Digest all non-TSIG records.
12125185a700Sflorian */
12135185a700Sflorian isc_buffer_usedregion(source, &source_r);
12145185a700Sflorian r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
12155185a700Sflorian if (has_tsig)
12165185a700Sflorian r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
12175185a700Sflorian else
12185185a700Sflorian r.length = source_r.length - DNS_MESSAGE_HEADERLEN;
12195185a700Sflorian ret = dst_context_adddata(msg->tsigctx, &r);
12205185a700Sflorian if (ret != ISC_R_SUCCESS)
12215185a700Sflorian goto cleanup_context;
12225185a700Sflorian
12235185a700Sflorian /*
12245185a700Sflorian * Digest the time signed and fudge.
12255185a700Sflorian */
12265185a700Sflorian if (has_tsig) {
12275185a700Sflorian isc_buffer_init(&databuf, data, sizeof(data));
12285185a700Sflorian isc_buffer_putuint48(&databuf, tsig.timesigned);
12295185a700Sflorian isc_buffer_putuint16(&databuf, tsig.fudge);
12305185a700Sflorian isc_buffer_usedregion(&databuf, &r);
12315185a700Sflorian ret = dst_context_adddata(msg->tsigctx, &r);
12325185a700Sflorian if (ret != ISC_R_SUCCESS)
12335185a700Sflorian goto cleanup_context;
12345185a700Sflorian
12355185a700Sflorian sig_r.base = tsig.signature;
12365185a700Sflorian sig_r.length = tsig.siglen;
12375185a700Sflorian if (tsig.siglen == 0) {
12385185a700Sflorian if (tsig.error != dns_rcode_noerror) {
12395185a700Sflorian msg->tsigstatus = tsig.error;
12405185a700Sflorian if (tsig.error == dns_tsigerror_badtime) {
12415185a700Sflorian ret = DNS_R_CLOCKSKEW;
12425185a700Sflorian } else {
12435185a700Sflorian ret = DNS_R_TSIGERRORSET;
12445185a700Sflorian }
12455185a700Sflorian } else {
12465185a700Sflorian tsig_log(msg->tsigkey, 2,
12475185a700Sflorian "signature is empty");
12485185a700Sflorian ret = DNS_R_TSIGVERIFYFAILURE;
12495185a700Sflorian }
12505185a700Sflorian goto cleanup_context;
12515185a700Sflorian }
12525185a700Sflorian
12535185a700Sflorian ret = dst_context_verify(msg->tsigctx, &sig_r);
12545185a700Sflorian if (ret == DST_R_VERIFYFAILURE) {
12555185a700Sflorian tsig_log(msg->tsigkey, 2,
12565185a700Sflorian "signature failed to verify(2)");
12575185a700Sflorian ret = DNS_R_TSIGVERIFYFAILURE;
12585185a700Sflorian goto cleanup_context;
12595185a700Sflorian } else if (ret != ISC_R_SUCCESS) {
12605185a700Sflorian goto cleanup_context;
12615185a700Sflorian }
12625185a700Sflorian msg->verified_sig = 1;
12635185a700Sflorian
12645185a700Sflorian /*
12655185a700Sflorian * Here at this point, the MAC has been verified. Even
12665185a700Sflorian * if any of the following code returns a TSIG error,
12675185a700Sflorian * the reply will be signed and WILL always include the
12685185a700Sflorian * request MAC in the digest computation.
12695185a700Sflorian */
12705185a700Sflorian
12715185a700Sflorian /*
12725185a700Sflorian * Is the time ok?
12735185a700Sflorian */
1274c576c3baSflorian time(&now);
12755185a700Sflorian
1276c576c3baSflorian if (now + msg->timeadjust > (time_t)(tsig.timesigned +
1277c576c3baSflorian tsig.fudge)) {
12785185a700Sflorian msg->tsigstatus = dns_tsigerror_badtime;
12795185a700Sflorian tsig_log(msg->tsigkey, 2, "signature has expired");
12805185a700Sflorian ret = DNS_R_CLOCKSKEW;
12815185a700Sflorian goto cleanup_context;
1282c576c3baSflorian } else if (now + msg->timeadjust < (time_t)(tsig.timesigned -
1283c576c3baSflorian tsig.fudge)) {
12845185a700Sflorian msg->tsigstatus = dns_tsigerror_badtime;
12855185a700Sflorian tsig_log(msg->tsigkey, 2,
12865185a700Sflorian "signature is in the future");
12875185a700Sflorian ret = DNS_R_CLOCKSKEW;
12885185a700Sflorian goto cleanup_context;
12895185a700Sflorian }
12905185a700Sflorian
12915185a700Sflorian alg = dst_key_alg(key);
12925185a700Sflorian ret = dst_key_sigsize(key, &siglen);
12935185a700Sflorian if (ret != ISC_R_SUCCESS)
12945185a700Sflorian goto cleanup_context;
12955185a700Sflorian if (
12965185a700Sflorian alg == DST_ALG_HMACSHA1 ||
12975185a700Sflorian alg == DST_ALG_HMACSHA224 ||
12985185a700Sflorian alg == DST_ALG_HMACSHA256 ||
12995185a700Sflorian alg == DST_ALG_HMACSHA384 ||
13005185a700Sflorian alg == DST_ALG_HMACSHA512)
13015185a700Sflorian {
13025185a700Sflorian uint16_t digestbits = dst_key_getbits(key);
13035185a700Sflorian
13045185a700Sflorian /*
13055185a700Sflorian * XXXRAY: Is this correct? What is the
13065185a700Sflorian * expected behavior when digestbits is not an
13075185a700Sflorian * integral multiple of 8? It looks like bytes
13085185a700Sflorian * should either be (digestbits/8) or
13095185a700Sflorian * (digestbits+7)/8.
13105185a700Sflorian *
13115185a700Sflorian * In any case, for current algorithms,
13125185a700Sflorian * digestbits are an integral multiple of 8, so
13135185a700Sflorian * it has the same effect as (digestbits/8).
13145185a700Sflorian */
13155185a700Sflorian if (tsig.siglen > 0 && digestbits != 0 &&
13165185a700Sflorian tsig.siglen < ((digestbits + 1) / 8))
13175185a700Sflorian {
13185185a700Sflorian msg->tsigstatus = dns_tsigerror_badtrunc;
13195185a700Sflorian tsig_log(msg->tsigkey, 2,
13205185a700Sflorian "truncated signature length "
13215185a700Sflorian "too small");
13225185a700Sflorian ret = DNS_R_TSIGVERIFYFAILURE;
13235185a700Sflorian goto cleanup_context;
13245185a700Sflorian }
13255185a700Sflorian if (tsig.siglen > 0 && digestbits == 0 &&
13265185a700Sflorian tsig.siglen < siglen)
13275185a700Sflorian {
13285185a700Sflorian msg->tsigstatus = dns_tsigerror_badtrunc;
13295185a700Sflorian tsig_log(msg->tsigkey, 2,
13305185a700Sflorian "signature length too small");
13315185a700Sflorian ret = DNS_R_TSIGVERIFYFAILURE;
13325185a700Sflorian goto cleanup_context;
13335185a700Sflorian }
13345185a700Sflorian }
13355185a700Sflorian
13365185a700Sflorian if (tsig.error != dns_rcode_noerror) {
13375185a700Sflorian msg->tsigstatus = tsig.error;
13385185a700Sflorian if (tsig.error == dns_tsigerror_badtime)
13395185a700Sflorian ret = DNS_R_CLOCKSKEW;
13405185a700Sflorian else
13415185a700Sflorian ret = DNS_R_TSIGERRORSET;
13425185a700Sflorian goto cleanup_context;
13435185a700Sflorian }
13445185a700Sflorian }
13455185a700Sflorian
13465185a700Sflorian msg->tsigstatus = dns_rcode_noerror;
13475185a700Sflorian ret = ISC_R_SUCCESS;
13485185a700Sflorian
13495185a700Sflorian cleanup_context:
13505185a700Sflorian /*
13515185a700Sflorian * Except in error conditions, don't destroy the DST context
13525185a700Sflorian * for unsigned messages; it is a running sum till the next
13535185a700Sflorian * TSIG signed message.
13545185a700Sflorian */
13555185a700Sflorian if ((ret != ISC_R_SUCCESS || has_tsig) && msg->tsigctx != NULL) {
13565185a700Sflorian dst_context_destroy(&msg->tsigctx);
13575185a700Sflorian }
13585185a700Sflorian
13595185a700Sflorian cleanup_querystruct:
1360a0b66ce4Sflorian dns_rdata_freestruct_tsig(&querytsig);
13615185a700Sflorian
13625185a700Sflorian return (ret);
13635185a700Sflorian }
13645185a700Sflorian
1365