tsig.c revision 4755b174df8221dff7e872f21d42b3572a74bf2f
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews/*
499b34cea04a46823d003d4c0520c8b03e8513cbBrian Wellington * Copyright (C) 1999-2001 Internet Software Consortium.
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence *
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews * Permission to use, copy, modify, and distribute this software for any
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews * purpose with or without fee is hereby granted, provided that the above
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews * copyright notice and this permission notice appear in all copies.
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence *
15a44745412679c30a6d022733925af70a38b715David Lawrence * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
15a44745412679c30a6d022733925af70a38b715David Lawrence * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
15a44745412679c30a6d022733925af70a38b715David Lawrence * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
15a44745412679c30a6d022733925af70a38b715David Lawrence * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
15a44745412679c30a6d022733925af70a38b715David Lawrence * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
15a44745412679c30a6d022733925af70a38b715David Lawrence * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15a44745412679c30a6d022733925af70a38b715David Lawrence * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
15a44745412679c30a6d022733925af70a38b715David Lawrence * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews */
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
91cd0f93ad34d23e8b09dca337120f64fbe8f0a1Andreas Gustafsson/*
96805adfc95a9e0c5b800869dd4afe55b5616f12James Brister * $Id: tsig.c,v 1.103 2001/01/11 21:07:21 gson Exp $
96805adfc95a9e0c5b800869dd4afe55b5616f12James Brister * Principal Author: Brian Wellington
3761c433912beabe43abeed2c3513b6201c59f64Mark Andrews */
854d0238dbc2908490197984b3b9d558008a53dfMark Andrews
854d0238dbc2908490197984b3b9d558008a53dfMark Andrews#include <config.h>
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <stdlib.h> /* Required for abs(). */
6324997211a5e2d82528dcde98e8981190a35faeMichael Graff
6324997211a5e2d82528dcde98e8981190a35faeMichael Graff#include <isc/buffer.h>
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff#include <isc/mem.h>
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence#include <isc/print.h>
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <isc/string.h> /* Required for HP/UX (and others?) */
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <isc/util.h>
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <dns/keyvalues.h>
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews#include <dns/log.h>
f7b99290c31abeb20c55fc55391510450ce60423Mark Andrews#include <dns/message.h>
ae114ded82e773a4d9058f833f964a17514712a8Brian Wellington#include <dns/rbt.h>
96805adfc95a9e0c5b800869dd4afe55b5616f12James Brister#include <dns/rdata.h>
bddfe77128b0f16af263ff149db40f0d885f43d0Mark Andrews#include <dns/rdatalist.h>
f7b99290c31abeb20c55fc55391510450ce60423Mark Andrews#include <dns/rdataset.h>
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington#include <dns/rdatastruct.h>
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington#include <dns/result.h>
c661868379d709416c773f6d53b5cec69c88e00fMark Andrews#include <dns/tsig.h>
9ac7076ebad044afb15e9e2687e3696868778538Mark Andrews
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews#include <dst/result.h>
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington#define TSIG_MAGIC 0x54534947 /* TSIG */
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington#define VALID_TSIG_KEY(x) ISC_MAGIC_VALID(x, TSIG_MAGIC)
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence#define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#define algname_is_allocated(algname) \
9ac7076ebad044afb15e9e2687e3696868778538Mark Andrews ((algname) != dns_tsig_hmacmd5_name && \
9ac7076ebad044afb15e9e2687e3696868778538Mark Andrews (algname) != dns_tsig_gssapi_name && \
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews (algname) != dns_tsig_gssapims_name)
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff#define BADTIMELEN 6
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsstatic unsigned char hmacmd5_ndata[] = "\010hmac-md5\007sig-alg\003reg\003int";
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsstatic unsigned char hmacmd5_offsets[] = { 0, 9, 17, 21, 25 };
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsstatic dns_name_t hmacmd5 = {
91cd0f93ad34d23e8b09dca337120f64fbe8f0a1Andreas Gustafsson DNS_NAME_MAGIC,
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews hmacmd5_ndata, 26, 5,
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews hmacmd5_offsets, NULL,
b589e90689c6e87bf9608424ca8d99571c18bc61Mark Andrews {(void *)-1, (void *)-1},
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews {NULL, NULL}
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews};
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsdns_name_t *dns_tsig_hmacmd5_name = &hmacmd5;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsstatic unsigned char gsstsig_ndata[] = "\010gss-tsig";
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsstatic unsigned char gsstsig_offsets[] = { 0, 9 };
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrewsstatic dns_name_t gsstsig = {
62c1de9d9485003ea5af13061f1d30f081442ee9Michael Graff DNS_NAME_MAGIC,
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews gsstsig_ndata, 10, 2,
62c1de9d9485003ea5af13061f1d30f081442ee9Michael Graff DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews gsstsig_offsets, NULL,
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson {(void *)-1, (void *)-1},
96805adfc95a9e0c5b800869dd4afe55b5616f12James Brister {NULL, NULL}
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews};
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graffdns_name_t *dns_tsig_gssapi_name = &gsstsig;
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews/* It's nice of Microsoft to conform to their own standard. */
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsstatic unsigned char gsstsigms_ndata[] = "\003gss\011microsoft\003com";
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsstatic unsigned char gsstsigms_offsets[] = { 0, 4, 14, 18 };
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews
1ef8965366d91e02a4672c35a187d30aa4a4c72cMark Andrewsstatic dns_name_t gsstsigms = {
ae114ded82e773a4d9058f833f964a17514712a8Brian Wellington DNS_NAME_MAGIC,
96805adfc95a9e0c5b800869dd4afe55b5616f12James Brister gsstsigms_ndata, 19, 4,
1ef8965366d91e02a4672c35a187d30aa4a4c72cMark Andrews DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
94a08e09db3dc844b6ee4841c368a2d7074a9c3fAndreas Gustafsson gsstsigms_offsets, NULL,
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence {(void *)-1, (void *)-1},
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews {NULL, NULL}
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews};
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsdns_name_t *dns_tsig_gssapims_name = &gsstsigms;
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff
62c1de9d9485003ea5af13061f1d30f081442ee9Michael Graffstatic isc_result_t
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewstsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsstatic void
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewstsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff va_list ap;
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence char message[4096];
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews char namestr[DNS_NAME_FORMATSIZE];
cdc50af0bff41accc02c613b9c6d8cd41b171ffeBrian Wellington
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews return;
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews if (key != NULL)
b589e90689c6e87bf9608424ca8d99571c18bc61Mark Andrews dns_name_format(&key->name, namestr, sizeof(namestr));
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews else
94a08e09db3dc844b6ee4841c368a2d7074a9c3fAndreas Gustafsson strcpy(namestr, "<null>");
52637f592f705ca93fadc218e403fd55e8ce4aeaMark Andrews va_start(ap, fmt);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews vsnprintf(message, sizeof(message), fmt, ap);
62c1de9d9485003ea5af13061f1d30f081442ee9Michael Graff va_end(ap);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews level, "tsig key '%s': %s", namestr, message);
cdc50af0bff41accc02c613b9c6d8cd41b171ffeBrian Wellington}
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsisc_result_t
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrewsdns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm,
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews dst_key_t *dstkey, isc_boolean_t generated,
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews dns_name_t *creator, isc_stdtime_t inception,
4529cdaedaf1a0a5f8ff89aeca510b7a4475446cBob Halley isc_stdtime_t expire, isc_mem_t *mctx,
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence dns_tsig_keyring_t *ring, dns_tsigkey_t **key)
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews{
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews dns_tsigkey_t *tkey;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_result_t ret;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
41aad56b6cc458cbf7b8483576d990a77ae9bac2Andreas Gustafsson REQUIRE(key == NULL || *key == NULL);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews REQUIRE(name != NULL);
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews REQUIRE(algorithm != NULL);
d981ca645597116d227a48bf37cc5edc061c854dBob Halley REQUIRE(mctx != NULL);
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews
b589e90689c6e87bf9608424ca8d99571c18bc61Mark Andrews tkey = (dns_tsigkey_t *) isc_mem_get(mctx, sizeof(dns_tsigkey_t));
b589e90689c6e87bf9608424ca8d99571c18bc61Mark Andrews if (tkey == NULL)
f7b99290c31abeb20c55fc55391510450ce60423Mark Andrews return (ISC_R_NOMEMORY);
41aad56b6cc458cbf7b8483576d990a77ae9bac2Andreas Gustafsson
41aad56b6cc458cbf7b8483576d990a77ae9bac2Andreas Gustafsson dns_name_init(&tkey->name, NULL);
41aad56b6cc458cbf7b8483576d990a77ae9bac2Andreas Gustafsson ret = dns_name_dup(name, mctx, &tkey->name);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if (ret != ISC_R_SUCCESS)
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews goto cleanup_key;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews dns_name_downcase(&tkey->name, &tkey->name, NULL);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME))
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews tkey->algorithm = DNS_TSIG_HMACMD5_NAME;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME))
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews tkey->algorithm = DNS_TSIG_GSSAPI_NAME;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME))
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews tkey->algorithm = DNS_TSIG_GSSAPIMS_NAME;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews else {
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if (key != NULL) {
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews ret = DNS_R_BADALG;
7c0539bea56022274da04263eb41fbb5b8835c38Mark Andrews goto cleanup_name;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews }
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews tkey->algorithm = isc_mem_get(mctx, sizeof(dns_name_t));
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff if (tkey->algorithm == NULL) {
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence ret = ISC_R_NOMEMORY;
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews goto cleanup_name;
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews }
f7b99290c31abeb20c55fc55391510450ce60423Mark Andrews dns_name_init(tkey->algorithm, NULL);
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews ret = dns_name_dup(algorithm, mctx, tkey->algorithm);
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews if (ret != ISC_R_SUCCESS)
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews goto cleanup_algorithm;
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews dns_name_downcase(tkey->algorithm, tkey->algorithm, NULL);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews }
ae114ded82e773a4d9058f833f964a17514712a8Brian Wellington
63cef8bde8b92aeb30ccdcf21d4e44c9be9cc6e3Andreas Gustafsson if (creator != NULL) {
63cef8bde8b92aeb30ccdcf21d4e44c9be9cc6e3Andreas Gustafsson tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t));
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews if (tkey->creator == NULL) {
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews ret = ISC_R_NOMEMORY;
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews goto cleanup_algorithm;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews }
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews dns_name_init(tkey->creator, NULL);
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff ret = dns_name_dup(creator, mctx, tkey->creator);
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence if (ret != ISC_R_SUCCESS) {
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews goto cleanup_algorithm;
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews }
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews } else
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews tkey->creator = NULL;
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews
b589e90689c6e87bf9608424ca8d99571c18bc61Mark Andrews tkey->key = dstkey;
f7b99290c31abeb20c55fc55391510450ce60423Mark Andrews tkey->ring = ring;
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews tkey->refs = 0;
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews if (ring != NULL) {
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews RWLOCK(&ring->lock, isc_rwlocktype_write);
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews ret = dns_rbt_addname(ring->keys, name, tkey);
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews if (ret != ISC_R_SUCCESS) {
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews RWUNLOCK(&ring->lock, isc_rwlocktype_write);
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews goto cleanup_algorithm;
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews }
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews tkey->refs++;
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews RWUNLOCK(&ring->lock, isc_rwlocktype_write);
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews }
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if (key != NULL)
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews tkey->refs++;
4529cdaedaf1a0a5f8ff89aeca510b7a4475446cBob Halley tkey->generated = generated;
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence tkey->inception = inception;
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews tkey->expire = expire;
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews tkey->mctx = mctx;
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews ret = isc_mutex_init(&tkey->lock);
f1f3bb3aa0ef5c4811e09953c4528a2f28a2a5b2David Lawrence if (ret != ISC_R_SUCCESS) {
82ca33427bdd4f3bc4ed3431e86bd810fe751674Andreas Gustafsson UNEXPECTED_ERROR(__FILE__, __LINE__,
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews "isc_mutex_init() failed: %s",
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews isc_result_totext(ret));
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews return (ISC_R_UNEXPECTED);
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews }
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews tkey->magic = TSIG_MAGIC;
d981ca645597116d227a48bf37cc5edc061c854dBob Halley
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff if (dstkey != NULL && dst_key_size(dstkey) < 64) {
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence char namestr[DNS_NAME_FORMATSIZE];
f1b0e9107d5fc7669920b76b4e32f93e9d16c85cBob Halley dns_name_format(name, namestr, sizeof(namestr));
cdc50af0bff41accc02c613b9c6d8cd41b171ffeBrian Wellington isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
f1b0e9107d5fc7669920b76b4e32f93e9d16c85cBob Halley DNS_LOGMODULE_TSIG, ISC_LOG_INFO,
f1b0e9107d5fc7669920b76b4e32f93e9d16c85cBob Halley "the TSIG key for '%s' is too short to "
d981ca645597116d227a48bf37cc5edc061c854dBob Halley "be secure", namestr);
d981ca645597116d227a48bf37cc5edc061c854dBob Halley }
cdc50af0bff41accc02c613b9c6d8cd41b171ffeBrian Wellington if (key != NULL)
f1b0e9107d5fc7669920b76b4e32f93e9d16c85cBob Halley *key = tkey;
f1b0e9107d5fc7669920b76b4e32f93e9d16c85cBob Halley
f1b0e9107d5fc7669920b76b4e32f93e9d16c85cBob Halley return (ISC_R_SUCCESS);
d981ca645597116d227a48bf37cc5edc061c854dBob Halley
f1b0e9107d5fc7669920b76b4e32f93e9d16c85cBob Halley cleanup_algorithm:
d981ca645597116d227a48bf37cc5edc061c854dBob Halley if (algname_is_allocated(tkey->algorithm)) {
d981ca645597116d227a48bf37cc5edc061c854dBob Halley if (dns_name_dynamic(tkey->algorithm))
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff dns_name_free(tkey->algorithm, mctx);
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence isc_mem_put(mctx, tkey->algorithm, sizeof(dns_name_t));
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley }
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley cleanup_name:
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley dns_name_free(&tkey->name, mctx);
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley cleanup_key:
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley isc_mem_put(mctx, tkey, sizeof(dns_tsigkey_t));
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley return (ret);
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley}
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley
62c1de9d9485003ea5af13061f1d30f081442ee9Michael Graffisc_result_t
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halleydns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm,
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley unsigned char *secret, int length, isc_boolean_t generated,
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley dns_name_t *creator, isc_stdtime_t inception,
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley isc_stdtime_t expire, isc_mem_t *mctx,
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley dns_tsig_keyring_t *ring, dns_tsigkey_t **key)
854d0238dbc2908490197984b3b9d558008a53dfMark Andrews{
dst_key_t *dstkey = NULL;
isc_result_t result;
REQUIRE(length >= 0);
if (length > 0)
REQUIRE(secret != NULL);
if (!dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME) && length > 0)
return (DNS_R_BADALG);
if (length > 0) {
isc_buffer_t b;
isc_buffer_init(&b, secret, length);
isc_buffer_add(&b, length);
result = dst_key_frombuffer(name, DST_ALG_HMACMD5,
DNS_KEYOWNER_ENTITY,
DNS_KEYPROTO_DNSSEC,
dns_rdataclass_in,
&b, mctx, &dstkey);
if (result != ISC_R_SUCCESS)
return (result);
}
result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
generated, creator,
inception, expire, mctx, ring, key);
if (result != ISC_R_SUCCESS && dstkey != NULL)
dst_key_free(&dstkey);
return (result);
}
void
dns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) {
REQUIRE(VALID_TSIG_KEY(source));
REQUIRE(targetp != NULL && *targetp == NULL);
LOCK(&source->lock);
source->refs++;
UNLOCK(&source->lock);
*targetp = source;
}
static void
tsigkey_free(dns_tsigkey_t *key) {
REQUIRE(VALID_TSIG_KEY(key));
key->magic = 0;
dns_name_free(&key->name, key->mctx);
if (algname_is_allocated(key->algorithm)) {
dns_name_free(key->algorithm, key->mctx);
isc_mem_put(key->mctx, key->algorithm, sizeof(dns_name_t));
}
if (key->key != NULL)
dst_key_free(&key->key);
if (key->creator != NULL) {
dns_name_free(key->creator, key->mctx);
isc_mem_put(key->mctx, key->creator, sizeof(dns_name_t));
}
DESTROYLOCK(&key->lock);
isc_mem_put(key->mctx, key, sizeof(dns_tsigkey_t));
}
void
dns_tsigkey_detach(dns_tsigkey_t **keyp) {
dns_tsigkey_t *key;
isc_boolean_t should_free = ISC_FALSE;
REQUIRE(keyp != NULL);
REQUIRE(VALID_TSIG_KEY(*keyp));
key = *keyp;
*keyp = NULL;
LOCK(&key->lock);
key->refs--;
if (key->refs == 0)
should_free = ISC_TRUE;
UNLOCK(&key->lock);
if (should_free)
tsigkey_free(key);
}
void
dns_tsigkey_setdeleted(dns_tsigkey_t *key) {
REQUIRE(VALID_TSIG_KEY(key));
REQUIRE(key->ring != NULL);
RWLOCK(&key->ring->lock, isc_rwlocktype_write);
(void)dns_rbt_deletename(key->ring->keys, &key->name, ISC_FALSE);
RWUNLOCK(&key->ring->lock, isc_rwlocktype_write);
}
static void
buffer_putuint48(isc_buffer_t *b, isc_uint64_t val) {
isc_uint16_t valhi;
isc_uint32_t vallo;
valhi = (isc_uint16_t)(val >> 32);
vallo = (isc_uint32_t)(val & 0xFFFFFFFF);
isc_buffer_putuint16(b, valhi);
isc_buffer_putuint32(b, vallo);
}
isc_result_t
dns_tsig_sign(dns_message_t *msg) {
dns_tsigkey_t *key;
dns_rdata_any_tsig_t tsig, querytsig;
unsigned char data[128];
isc_buffer_t databuf, sigbuf;
isc_buffer_t *dynbuf;
dns_name_t *owner;
dns_rdata_t *rdata;
dns_rdatalist_t *datalist;
dns_rdataset_t *dataset;
isc_region_t r;
isc_stdtime_t now;
isc_mem_t *mctx;
dst_context_t *ctx = NULL;
isc_result_t ret;
unsigned char badtimedata[BADTIMELEN];
unsigned int sigsize = 0;
REQUIRE(msg != NULL);
REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg)));
/*
* If this is a response, there should be a query tsig.
*/
if (is_response(msg) && msg->querytsig == NULL)
return (DNS_R_EXPECTEDTSIG);
dynbuf = NULL;
mctx = msg->mctx;
key = dns_message_gettsigkey(msg);
tsig.mctx = mctx;
tsig.common.rdclass = dns_rdataclass_any;
tsig.common.rdtype = dns_rdatatype_tsig;
ISC_LINK_INIT(&tsig.common, link);
dns_name_init(&tsig.algorithm, NULL);
dns_name_clone(key->algorithm, &tsig.algorithm);
isc_stdtime_get(&now);
tsig.timesigned = now + msg->timeadjust;
tsig.fudge = DNS_TSIG_FUDGE;
tsig.originalid = msg->id;
isc_buffer_init(&databuf, data, sizeof(data));
if (is_response(msg))
tsig.error = msg->querytsigstatus;
else
tsig.error = dns_rcode_noerror;
if (tsig.error != dns_tsigerror_badtime) {
tsig.otherlen = 0;
tsig.other = NULL;
} else {
isc_buffer_t otherbuf;
tsig.otherlen = BADTIMELEN;
tsig.other = badtimedata;
isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen);
buffer_putuint48(&otherbuf, tsig.timesigned);
}
if (key->key != NULL && tsig.error != dns_tsigerror_badsig) {
unsigned char header[DNS_MESSAGE_HEADERLEN];
isc_buffer_t headerbuf;
ret = dst_context_create(key->key, mctx, &ctx);
if (ret != ISC_R_SUCCESS)
return (ret);
/*
* If this is a response, digest the query signature.
*/
if (is_response(msg)) {
dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
ret = dns_rdataset_first(msg->querytsig);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
dns_rdataset_current(msg->querytsig, &querytsigrdata);
ret = dns_rdata_tostruct(&querytsigrdata, &querytsig,
NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
isc_buffer_putuint16(&databuf, querytsig.siglen);
if (isc_buffer_availablelength(&databuf) <
querytsig.siglen)
{
ret = ISC_R_NOSPACE;
goto cleanup_context;
}
isc_buffer_putmem(&databuf, querytsig.signature,
querytsig.siglen);
isc_buffer_usedregion(&databuf, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
}
/*
* Digest the header.
*/
isc_buffer_init(&headerbuf, header, sizeof(header));
dns_message_renderheader(msg, &headerbuf);
isc_buffer_usedregion(&headerbuf, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
/*
* Digest the remainder of the message.
*/
isc_buffer_usedregion(msg->buffer, &r);
isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
if (msg->tcp_continuation == 0) {
/*
* Digest the name, class, ttl, alg.
*/
dns_name_toregion(&key->name, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
isc_buffer_clear(&databuf);
isc_buffer_putuint16(&databuf, dns_rdataclass_any);
isc_buffer_putuint32(&databuf, 0); /* ttl */
isc_buffer_usedregion(&databuf, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
dns_name_toregion(&tsig.algorithm, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
}
/* Digest the timesigned and fudge */
isc_buffer_clear(&databuf);
if (tsig.error == dns_tsigerror_badtime)
tsig.timesigned = querytsig.timesigned;
buffer_putuint48(&databuf, tsig.timesigned);
isc_buffer_putuint16(&databuf, tsig.fudge);
isc_buffer_usedregion(&databuf, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
if (msg->tcp_continuation == 0) {
/*
* Digest the error and other data length.
*/
isc_buffer_clear(&databuf);
isc_buffer_putuint16(&databuf, tsig.error);
isc_buffer_putuint16(&databuf, tsig.otherlen);
isc_buffer_usedregion(&databuf, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
/*
* Digest the error and other data.
*/
if (tsig.otherlen > 0) {
r.length = tsig.otherlen;
r.base = tsig.other;
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
}
}
ret = dst_key_sigsize(key->key, &sigsize);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize);
if (tsig.signature == NULL) {
ret = ISC_R_NOMEMORY;
goto cleanup_context;
}
isc_buffer_init(&sigbuf, tsig.signature, sigsize);
ret = dst_context_sign(ctx, &sigbuf);
if (ret != ISC_R_SUCCESS)
goto cleanup_signature;
dst_context_destroy(&ctx);
tsig.siglen = isc_buffer_usedlength(&sigbuf);
} else {
tsig.siglen = 0;
tsig.signature = NULL;
}
rdata = NULL;
ret = dns_message_gettemprdata(msg, &rdata);
if (ret != ISC_R_SUCCESS)
goto cleanup_signature;
ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
if (ret != ISC_R_SUCCESS)
goto cleanup_signature;
ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any,
dns_rdatatype_tsig, &tsig, dynbuf);
if (ret != ISC_R_SUCCESS)
goto cleanup_dynbuf;
dns_message_takebuffer(msg, &dynbuf);
if (tsig.signature != NULL) {
isc_mem_put(mctx, tsig.signature, sigsize);
tsig.signature = NULL;
}
owner = NULL;
ret = dns_message_gettempname(msg, &owner);
if (ret != ISC_R_SUCCESS)
goto cleanup_dynbuf;
dns_name_init(owner, NULL);
ret = dns_name_dup(&key->name, msg->mctx, owner);
if (ret != ISC_R_SUCCESS)
goto cleanup_owner;
datalist = NULL;
ret = dns_message_gettemprdatalist(msg, &datalist);
if (ret != ISC_R_SUCCESS)
goto cleanup_owner;
datalist->rdclass = dns_rdataclass_any;
datalist->type = dns_rdatatype_tsig;
datalist->covers = 0;
datalist->ttl = 0;
ISC_LIST_INIT(datalist->rdata);
ISC_LIST_APPEND(datalist->rdata, rdata, link);
dataset = NULL;
ret = dns_message_gettemprdataset(msg, &dataset);
if (ret != ISC_R_SUCCESS)
goto cleanup_owner;
dns_rdataset_init(dataset);
dns_rdatalist_tordataset(datalist, dataset);
msg->tsig = dataset;
msg->tsigname = owner;
return (ISC_R_SUCCESS);
cleanup_owner:
if (owner != NULL)
dns_message_puttempname(msg, &owner);
cleanup_dynbuf:
if (dynbuf != NULL)
isc_buffer_free(&dynbuf);
cleanup_signature:
if (tsig.signature != NULL)
isc_mem_put(mctx, tsig.signature, sigsize);
cleanup_context:
if (ctx != NULL)
dst_context_destroy(&ctx);
return (ret);
}
isc_result_t
dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2)
{
dns_rdata_any_tsig_t tsig, querytsig;
isc_region_t r, source_r, header_r, sig_r;
isc_buffer_t databuf;
unsigned char data[32];
dns_name_t *keyname;
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_stdtime_t now;
isc_result_t ret;
dns_tsigkey_t *tsigkey;
dst_key_t *key = NULL;
unsigned char header[DNS_MESSAGE_HEADERLEN];
dst_context_t *ctx = NULL;
isc_mem_t *mctx;
isc_uint16_t addcount, id;
REQUIRE(source != NULL);
REQUIRE(DNS_MESSAGE_VALID(msg));
tsigkey = dns_message_gettsigkey(msg);
REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
msg->verify_attempted = 1;
if (msg->tcp_continuation)
return (tsig_verify_tcp(source, msg));
/*
* There should be a TSIG record...
*/
if (msg->tsig == NULL)
return (DNS_R_EXPECTEDTSIG);
/*
* If this is a response and there's no key or query TSIG, there
* shouldn't be one on the response.
*/
if (is_response(msg) &&
(tsigkey == NULL || msg->querytsig == NULL))
return (DNS_R_UNEXPECTEDTSIG);
mctx = msg->mctx;
/*
* If we're here, we know the message is well formed and contains a
* TSIG record.
*/
keyname = msg->tsigname;
ret = dns_rdataset_first(msg->tsig);
if (ret != ISC_R_SUCCESS)
return (ret);
dns_rdataset_current(msg->tsig, &rdata);
ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
if (ret != ISC_R_SUCCESS)
return (ret);
dns_rdata_reset(&rdata);
if (is_response(msg)) {
ret = dns_rdataset_first(msg->querytsig);
if (ret != ISC_R_SUCCESS)
return (ret);
dns_rdataset_current(msg->querytsig, &rdata);
ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
if (ret != ISC_R_SUCCESS)
return (ret);
}
/*
* Do the key name and algorithm match that of the query?
*/
if (is_response(msg) &&
(!dns_name_equal(keyname, &tsigkey->name) ||
!dns_name_equal(&tsig.algorithm, &querytsig.algorithm)))
{
msg->tsigstatus = dns_tsigerror_badkey;
tsig_log(msg->tsigkey, 2,
"key name and algorithm do not match");
return (DNS_R_TSIGVERIFYFAILURE);
}
/*
* Get the current time.
*/
isc_stdtime_get(&now);
/*
* Find dns_tsigkey_t based on keyname.
*/
if (tsigkey == NULL) {
ret = ISC_R_NOTFOUND;
if (ring1 != NULL)
ret = dns_tsigkey_find(&tsigkey, keyname,
&tsig.algorithm, ring1);
if (ret == ISC_R_NOTFOUND && ring2 != NULL)
ret = dns_tsigkey_find(&tsigkey, keyname,
&tsig.algorithm, ring2);
if (ret != ISC_R_SUCCESS) {
msg->tsigstatus = dns_tsigerror_badkey;
ret = dns_tsigkey_create(keyname, &tsig.algorithm,
NULL, 0, ISC_FALSE, NULL,
now, now,
mctx, NULL, &msg->tsigkey);
if (ret != ISC_R_SUCCESS)
return (ret);
tsig_log(msg->tsigkey, 2, "unknown key");
return (DNS_R_TSIGVERIFYFAILURE);
}
msg->tsigkey = tsigkey;
}
key = tsigkey->key;
/*
* Is the time ok?
*/
if (abs(now + msg->timeadjust - tsig.timesigned) > tsig.fudge) {
msg->tsigstatus = dns_tsigerror_badtime;
if (now + msg->timeadjust > tsig.timesigned + tsig.fudge)
tsig_log(msg->tsigkey, 2,
"signature has expired");
else
tsig_log(msg->tsigkey, 2,
"signature is in the future");
return (DNS_R_TSIGVERIFYFAILURE);
}
if (tsig.siglen > 0) {
sig_r.base = tsig.signature;
sig_r.length = tsig.siglen;
ret = dst_context_create(key, mctx, &ctx);
if (ret != ISC_R_SUCCESS)
return (ret);
if (is_response(msg)) {
isc_buffer_init(&databuf, data, sizeof(data));
isc_buffer_putuint16(&databuf, querytsig.siglen);
isc_buffer_usedregion(&databuf, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
if (querytsig.siglen > 0) {
r.length = querytsig.siglen;
r.base = querytsig.signature;
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
}
}
/*
* Extract the header.
*/
isc_buffer_usedregion(source, &r);
memcpy(header, r.base, DNS_MESSAGE_HEADERLEN);
isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
/*
* Decrement the additional field counter.
*/
memcpy(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
addcount = htons(ntohs(addcount) - 1);
memcpy(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
/*
* Put in the original id.
*/
id = htons(tsig.originalid);
memcpy(&header[0], &id, 2);
/*
* Digest the modified header.
*/
header_r.base = (unsigned char *) header;
header_r.length = DNS_MESSAGE_HEADERLEN;
ret = dst_context_adddata(ctx, &header_r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
/*
* Digest all non-TSIG records.
*/
isc_buffer_usedregion(source, &source_r);
r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
/*
* Digest the key name.
*/
dns_name_toregion(&tsigkey->name, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
isc_buffer_init(&databuf, data, sizeof(data));
isc_buffer_putuint16(&databuf, tsig.common.rdclass);
isc_buffer_putuint32(&databuf, msg->tsig->ttl);
isc_buffer_usedregion(&databuf, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
/*
* Digest the key algorithm.
*/
dns_name_toregion(tsigkey->algorithm, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
isc_buffer_clear(&databuf);
buffer_putuint48(&databuf, tsig.timesigned);
isc_buffer_putuint16(&databuf, tsig.fudge);
isc_buffer_putuint16(&databuf, tsig.error);
isc_buffer_putuint16(&databuf, tsig.otherlen);
isc_buffer_usedregion(&databuf, &r);
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
if (tsig.otherlen > 0) {
r.base = tsig.other;
r.length = tsig.otherlen;
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
}
ret = dst_context_verify(ctx, &sig_r);
if (ret == DST_R_VERIFYFAILURE) {
msg->tsigstatus = dns_tsigerror_badsig;
ret = DNS_R_TSIGVERIFYFAILURE;
tsig_log(msg->tsigkey, 2,
"signature failed to verify");
goto cleanup_context;
} else if (ret != ISC_R_SUCCESS)
goto cleanup_context;
dst_context_destroy(&ctx);
} else if (tsig.error != dns_tsigerror_badsig &&
tsig.error != dns_tsigerror_badkey)
{
msg->tsigstatus = dns_tsigerror_badsig;
tsig_log(msg->tsigkey, 2, "signature was empty");
return (DNS_R_TSIGVERIFYFAILURE);
}
msg->tsigstatus = dns_rcode_noerror;
if (tsig.error != dns_rcode_noerror)
return (DNS_R_TSIGERRORSET);
msg->verified_sig = 1;
return (ISC_R_SUCCESS);
cleanup_context:
if (ctx != NULL)
dst_context_destroy(&ctx);
return (ret);
}
static isc_result_t
tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
dns_rdata_any_tsig_t tsig, querytsig;
isc_region_t r, source_r, header_r, sig_r;
isc_buffer_t databuf;
unsigned char data[32];
dns_name_t *keyname;
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_stdtime_t now;
isc_result_t ret;
dns_tsigkey_t *tsigkey;
dst_key_t *key = NULL;
unsigned char header[DNS_MESSAGE_HEADERLEN];
isc_uint16_t addcount, id;
isc_boolean_t has_tsig = ISC_FALSE;
isc_mem_t *mctx;
REQUIRE(source != NULL);
REQUIRE(msg != NULL);
REQUIRE(dns_message_gettsigkey(msg) != NULL);
REQUIRE(msg->tcp_continuation == 1);
REQUIRE(is_response(msg));
REQUIRE(msg->querytsig != NULL);
mctx = msg->mctx;
tsigkey = dns_message_gettsigkey(msg);
/*
* Extract and parse the previous TSIG
*/
ret = dns_rdataset_first(msg->querytsig);
if (ret != ISC_R_SUCCESS)
return (ret);
dns_rdataset_current(msg->querytsig, &rdata);
ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
if (ret != ISC_R_SUCCESS)
return (ret);
dns_rdata_reset(&rdata);
/*
* If there is a TSIG in this message, do some checks.
*/
if (msg->tsig != NULL) {
has_tsig = ISC_TRUE;
keyname = msg->tsigname;
ret = dns_rdataset_first(msg->tsig);
if (ret != ISC_R_SUCCESS)
goto cleanup_querystruct;
dns_rdataset_current(msg->tsig, &rdata);
ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_querystruct;
/*
* Do the key name and algorithm match that of the query?
*/
if (!dns_name_equal(keyname, &tsigkey->name) ||
!dns_name_equal(&tsig.algorithm, &querytsig.algorithm))
{
msg->tsigstatus = dns_tsigerror_badkey;
ret = DNS_R_TSIGVERIFYFAILURE;
tsig_log(msg->tsigkey, 2,
"key name and algorithm do not match");
goto cleanup_querystruct;
}
/*
* Is the time ok?
*/
isc_stdtime_get(&now);
if (abs(now - tsig.timesigned) > tsig.fudge) {
msg->tsigstatus = dns_tsigerror_badtime;
ret = DNS_R_TSIGVERIFYFAILURE;
if (now > tsig.timesigned + tsig.fudge)
tsig_log(msg->tsigkey, 2,
"signature has expired");
else
tsig_log(msg->tsigkey, 2,
"signature is in the future");
goto cleanup_querystruct;
}
}
key = tsigkey->key;
if (msg->tsigctx == NULL) {
ret = dst_context_create(key, mctx, &msg->tsigctx);
if (ret != ISC_R_SUCCESS)
goto cleanup_querystruct;
/*
* Digest the length of the query signature
*/
isc_buffer_init(&databuf, data, sizeof(data));
isc_buffer_putuint16(&databuf, querytsig.siglen);
isc_buffer_usedregion(&databuf, &r);
ret = dst_context_adddata(msg->tsigctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
/*
* Digest the data of the query signature
*/
if (querytsig.siglen > 0) {
r.length = querytsig.siglen;
r.base = querytsig.signature;
ret = dst_context_adddata(msg->tsigctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
}
}
/*
* Extract the header.
*/
isc_buffer_usedregion(source, &r);
memcpy(header, r.base, DNS_MESSAGE_HEADERLEN);
isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
/*
* Decrement the additional field counter if necessary.
*/
if (has_tsig) {
memcpy(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
addcount = htons(ntohs(addcount) - 1);
memcpy(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
}
/*
* Put in the original id.
*/
/* XXX Can TCP transfers be forwarded? How would that work? */
if (has_tsig) {
id = htons(tsig.originalid);
memcpy(&header[0], &id, 2);
}
/*
* Digest the modified header.
*/
header_r.base = (unsigned char *) header;
header_r.length = DNS_MESSAGE_HEADERLEN;
ret = dst_context_adddata(msg->tsigctx, &header_r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
/*
* Digest all non-TSIG records.
*/
isc_buffer_usedregion(source, &source_r);
r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
if (has_tsig)
r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
else
r.length = source_r.length - DNS_MESSAGE_HEADERLEN;
ret = dst_context_adddata(msg->tsigctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
/*
* Digest the time signed and fudge.
*/
if (has_tsig) {
isc_buffer_init(&databuf, data, sizeof(data));
buffer_putuint48(&databuf, tsig.timesigned);
isc_buffer_putuint16(&databuf, tsig.fudge);
isc_buffer_usedregion(&databuf, &r);
ret = dst_context_adddata(msg->tsigctx, &r);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
sig_r.base = tsig.signature;
sig_r.length = tsig.siglen;
if (tsig.siglen == 0) {
if (tsig.error != dns_rcode_noerror)
ret = DNS_R_TSIGERRORSET;
else {
tsig_log(msg->tsigkey, 2,
"signature is empty");
ret = DNS_R_TSIGVERIFYFAILURE;
}
goto cleanup_context;
}
ret = dst_context_verify(msg->tsigctx, &sig_r);
if (ret == DST_R_VERIFYFAILURE) {
msg->tsigstatus = dns_tsigerror_badsig;
tsig_log(msg->tsigkey, 2,
"signature failed to verify");
ret = DNS_R_TSIGVERIFYFAILURE;
goto cleanup_context;
}
else if (ret != ISC_R_SUCCESS)
goto cleanup_context;
dst_context_destroy(&msg->tsigctx);
}
msg->tsigstatus = dns_rcode_noerror;
return (ISC_R_SUCCESS);
cleanup_context:
dst_context_destroy(&msg->tsigctx);
cleanup_querystruct:
dns_rdata_freestruct(&querytsig);
return (ret);
}
isc_result_t
dns_tsigkey_find(dns_tsigkey_t **tsigkey, dns_name_t *name,
dns_name_t *algorithm, dns_tsig_keyring_t *ring)
{
dns_tsigkey_t *key;
isc_stdtime_t now;
isc_result_t result;
REQUIRE(tsigkey != NULL);
REQUIRE(*tsigkey == NULL);
REQUIRE(name != NULL);
REQUIRE(ring != NULL);
isc_stdtime_get(&now);
RWLOCK(&ring->lock, isc_rwlocktype_read);
key = NULL;
result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key);
if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) {
RWUNLOCK(&ring->lock, isc_rwlocktype_read);
return (ISC_R_NOTFOUND);
}
if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) {
RWUNLOCK(&ring->lock, isc_rwlocktype_read);
return (ISC_R_NOTFOUND);
}
if (key->inception != key->expire && key->expire < now) {
/*
* The key has expired.
*/
RWUNLOCK(&ring->lock, isc_rwlocktype_read);
LOCK(&key->lock);
key->refs--;
UNLOCK(&key->lock);
RWLOCK(&ring->lock, isc_rwlocktype_write);
(void) dns_rbt_deletename(ring->keys, name, ISC_FALSE);
RWUNLOCK(&ring->lock, isc_rwlocktype_write);
return (ISC_R_NOTFOUND);
}
LOCK(&key->lock);
key->refs++;
UNLOCK(&key->lock);
RWUNLOCK(&ring->lock, isc_rwlocktype_read);
*tsigkey = key;
return (ISC_R_SUCCESS);
}
static void
free_tsignode(void *node, void *_unused) {
dns_tsigkey_t *key;
UNUSED(_unused);
REQUIRE(node != NULL);
key = node;
dns_tsigkey_detach(&key);
}
isc_result_t
dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp) {
isc_result_t result;
dns_tsig_keyring_t *ring;
REQUIRE(mctx != NULL);
REQUIRE(ringp != NULL);
REQUIRE(*ringp == NULL);
ring = isc_mem_get(mctx, sizeof(dns_tsig_keyring_t));
if (ring == NULL)
return (ISC_R_NOMEMORY);
result = isc_rwlock_init(&ring->lock, 0, 0);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_rwlock_init() failed: %s",
isc_result_totext(result));
return (ISC_R_UNEXPECTED);
}
ring->keys = NULL;
result = dns_rbt_create(mctx, free_tsignode, NULL, &ring->keys);
if (result != ISC_R_SUCCESS) {
isc_rwlock_destroy(&ring->lock);
isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
return (result);
}
ring->mctx = mctx;
*ringp = ring;
return (ISC_R_SUCCESS);
}
void
dns_tsigkeyring_destroy(dns_tsig_keyring_t **ringp) {
dns_tsig_keyring_t *ring;
REQUIRE(ringp != NULL);
REQUIRE(*ringp != NULL);
ring = *ringp;
*ringp = NULL;
dns_rbt_destroy(&ring->keys);
isc_rwlock_destroy(&ring->lock);
isc_mem_put(ring->mctx, ring, sizeof(dns_tsig_keyring_t));
}