Added support for TSIG records in message and resolver subsystems, added

tsig to_struct/from_struct
This commit is contained in:
Brian Wellington
1999-08-20 18:56:24 +00:00
parent 382442e200
commit 6d4886fa74
8 changed files with 1162 additions and 34 deletions

View File

@@ -28,6 +28,7 @@ CINCLUDES = -I../isc/unix/include \
-I./include \
-I. \
-I${srcdir}/include \
-I${srcdir}/sec/dst/include \
-I${srcdir}
CDEFINES =
@@ -111,7 +112,7 @@ OBJS = callbacks.@O@ compress.@O@ db.@O@ dbiterator.@O@ \
name.@O@ rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rdata.@O@ \
rdatalist.@O@ rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ \
resolver.@O@ result.@O@ version.@O@ masterdump.@O@ time.@O@ \
ttl.@O@ tcpmsg.@O@ view.@O@ journal.@O@ \
ttl.@O@ tcpmsg.@O@ tsig.@O@ view.@O@ journal.@O@ \
${DSTOBJS} ${OPENSSLOBJS} ${DNSSAFEOBJS}
SRCS = callbacks.c compress.c db.c dbiterator.c \
@@ -119,7 +120,7 @@ SRCS = callbacks.c compress.c db.c dbiterator.c \
name.c rbt.c rbtdb.c rbtdb64.c rdata.c \
rdatalist.c rdataset.c rdatasetiter.c rdataslab.c \
resolver.c result.c version.c masterdump.c time.c \
ttl.c tcpmsg.c view.c journal.c
ttl.c tcpmsg.c tsig.c view.c journal.c
SUBDIRS = include sec
TARGETS = include/dns/enumtype.h include/dns/enumclass.h \

View File

@@ -29,6 +29,7 @@
#include <dns/result.h>
#include <dns/name.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/compress.h>
/*
@@ -139,6 +140,13 @@ struct dns_message {
ISC_LIST(dns_rdata_t) freerdata;
ISC_LIST(dns_rdataset_t) freerdataset;
ISC_LIST(dns_rdatalist_t) freerdatalist;
dns_rcode_t tsigstatus;
dns_rcode_t querytsigstatus;
dns_rdata_any_tsig_t *tsig;
dns_rdata_any_tsig_t *querytsig;
dns_tsig_key_t *tsigkey;
int tsigstart;
};
dns_result_t
@@ -358,6 +366,21 @@ dns_message_rendersection(dns_message_t *msg, dns_section_t section,
* are records remaining for this section.
*/
void
dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target);
/*
* Render the message header. This is implicitly called by
* dns_message_renderend().
*
* Requires:
*
* 'msg' be a valid message.
*
* dns_message_renderbegin() was called.
*
* 'target' is a valid buffer with enough space to hold a message header
*/
dns_result_t
dns_message_renderend(dns_message_t *msg);
/*

154
lib/dns/include/dns/tsig.h Normal file
View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 1999 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
#ifndef DNS_TSIG_H
#define DNS_TSIG_H 1
#include <isc/mem.h>
#include <isc/lang.h>
#include <dns/types.h>
#include <dst/dst.h>
ISC_LANG_BEGINDECLS
/* Standard algorithm */
#define DNS_TSIG_HMACMD5 "HMAC-MD5.SIG-ALG.REG.INT."
extern dns_name_t *dns_tsig_hmacmd5_name;
#define DNS_TSIG_HMACMD5_NAME dns_tsig_hmacmd5_name
/* Default fudge value. */
#define DNS_TSIG_FUDGE 300
struct dns_tsig_key {
unsigned int magic; /* Magic number. */
isc_mem_t *mctx;
dst_key_t *key; /* Key */
dns_name_t name; /* Key name */
dns_name_t algorithm; /* Algorithm name */
ISC_LINK(dns_tsig_key_t) link;
};
#define dns_tsig_emptykey(tsigkey) ((tsigkey)->key == NULL)
isc_result_t
dns_tsig_key_create(dns_name_t *name, dns_name_t *algorithm,
unsigned char *secret, int length, isc_mem_t *mctx,
dns_tsig_key_t **key);
/*
* Creates a tsig key structure pointed to by 'key'.
*
* Requires:
* 'name' is a valid dns_name_t
* 'algorithm' is a valid dns_name_t
* 'secret' is a valid pointer
* 'length' is an integer greater than 0
* 'mctx' is a valid memory context
* 'key' must not be NULL
* '*key' must be NULL
*
* Returns:
* ISC_R_SUCCESS
* DNS_R_NOTIMPLEMENTED - algorithm is not implemented
* ISC_R_NOMEMORY
*/
void
dns_tsig_key_free(dns_tsig_key_t **key);
/*
* Frees the tsig key structure pointed to by 'key'.
*
* Requires:
* 'key' is a valid TSIG key
*/
isc_result_t
dns_tsig_sign(dns_message_t *msg);
/*
* Generates a TSIG record for this message
*
* Requires:
* 'msg' is a valid message
* 'msg->tsigkey' is a valid TSIG key
* 'msg->tsig' is NULL
* 'msg->querytsig' is not NULL if this is a response
*
* Returns:
* ISC_R_SUCCESS
* ISC_R_NOMEMORY
* ISC_R_NOSPACE
*/
isc_result_t
dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg);
/*
* Verified the TSIG record in this message
*
* Requires:
* 'source' is a valid buffer containing the unparsed message
* 'msg' is a valid message containing a TSIG record
* 'msg->tsigkey' is a valid TSIG key
* 'msg->tsig' is NULL
* 'msg->querytsig' is not NULL if this is a response
*
* Returns:
* DNS_R_SUCCESS
* ISC_R_NOMEMORY
* DNS_R_TSIGERRORSET - the TSIG verified but ->error was set
* and this is a query
* DNS_R_TSIGVERIFYFAILURE - the TSIG failed to verify
*/
isc_result_t
dns_tsig_findkey(dns_tsig_key_t **tsigkey, dns_name_t *name,
dns_name_t *algorithm);
/*
* Returns the TSIG key corresponding to this name and algorithm
*
* Requires:
* 'tsigkey' is not NULL
* '*tsigkey' is NULL
* 'name' is a valid dns_name_t
* 'algorithm' is a valid dns_name_t
*
* Returns:
* ISC_R_SUCCESS
* ISC_R_NOTFOUND
*/
isc_result_t
dns_tsig_init(isc_mem_t *mctx);
/*
* Initializes the TSIG subsystem
*
* Returns:
* ISC_R_SUCCESS
* ISC_R_NOMEMORY
*/
void
dns_tsig_destroy(void);
/*
* Frees all data associated with the TSIG subsystem
*/
ISC_LANG_ENDDECLS
#endif /* DNS_TSIG_H */

View File

@@ -36,6 +36,7 @@
#include <dns/rdatatype.h>
#include <dns/rdatalist.h>
#include <dns/compress.h>
#include <dns/tsig.h>
#define DNS_MESSAGE_OPCODE_MASK 0x7800U
#define DNS_MESSAGE_OPCODE_SHIFT 11
@@ -345,6 +346,15 @@ msginitprivate(dns_message_t *m)
m->need_cctx_cleanup = 0;
}
static inline void
msginittsig(dns_message_t *m)
{
m->tsigstatus = m->querytsigstatus = dns_rcode_noerror;
m->tsig = m->querytsig = NULL;
m->tsigkey = NULL;
m->tsigstart = -1;
}
/*
* Init elements to default state. Used both when allocating a new element
* and when resetting one.
@@ -354,6 +364,7 @@ msginit(dns_message_t *m)
{
msginitheader(m);
msginitprivate(m);
msginittsig(m);
m->header_ok = 0;
m->question_ok = 0;
}
@@ -503,6 +514,20 @@ msgreset(dns_message_t *msg, isc_boolean_t everything)
if (msg->need_cctx_cleanup == 1)
dns_compress_invalidate(&msg->cctx);
if (msg->tsig != NULL) {
dns_rdata_freestruct(msg->tsig);
isc_mem_put(msg->mctx, msg->tsig, sizeof(dns_rdata_any_tsig_t));
}
if (msg->querytsig != NULL) {
dns_rdata_freestruct(msg->querytsig);
isc_mem_put(msg->mctx, msg->querytsig,
sizeof(dns_rdata_any_tsig_t));
}
if (msg->tsigkey != NULL && dns_tsig_emptykey(msg->tsigkey))
dns_tsig_key_free(&msg->tsigkey);
/*
* Set other bits to normal default values.
*/
@@ -909,6 +934,7 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
dns_namelist_t *section;
for (count = 0 ; count < msg->counts[sectionid] ; count++) {
int recstart = source->current;
section = &msg->sections[sectionid];
name = newname(msg);
@@ -965,6 +991,7 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
if (rdclass != dns_rdataclass_any)
return (DNS_R_FORMERR);
section = &msg->sections[DNS_SECTION_TSIG];
msg->tsigstart = recstart;
}
/*
@@ -1050,14 +1077,19 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
/*
* Read the rdata from the wire format. Interpret the
* rdata according to its actual class, even if it had a
* DynDNS meta-class in the packet. Then put the meta-class
* back into the finished rdata.
* DynDNS meta-class in the packet (unless this is a TSIG).
* Then put the meta-class back into the finished rdata.
*/
rdata = newrdata(msg);
if (rdata == NULL)
return (DNS_R_NOMEMORY);
result = getrdata(name, source, msg, dctx,
msg->rdclass, rdtype, rdatalen, rdata);
if (rdtype != dns_rdatatype_tsig)
result = getrdata(name, source, msg, dctx,
msg->rdclass, rdtype,
rdatalen, rdata);
else
result = getrdata(name, source, msg, dctx,
rdclass, rdtype, rdatalen, rdata);
if (result != DNS_R_SUCCESS)
return (result);
rdata->rdclass = rdclass;
@@ -1152,9 +1184,11 @@ dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
if (r.length != 0)
return (DNS_R_FORMERR);
/*
* XXXMLG Need to check the tsig(s) here...
*/
if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_TSIG])) {
ret = dns_tsig_verify(source, msg);
if (ret != DNS_R_SUCCESS)
return ret;
}
return (DNS_R_SUCCESS);
}
@@ -1344,33 +1378,60 @@ dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
return (DNS_R_SUCCESS);
}
dns_result_t
dns_message_renderend(dns_message_t *msg)
void
dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target)
{
isc_buffer_t tmpbuf;
isc_region_t r;
isc_uint16_t tmp;
isc_region_t r;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->buffer != NULL);
REQUIRE(target != NULL);
isc_buffer_used(msg->buffer, &r);
isc_buffer_init(&tmpbuf, r.base, r.length, ISC_BUFFERTYPE_BINARY);
isc_buffer_available(target, &r);
REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
isc_buffer_putuint16(&tmpbuf, msg->id);
isc_buffer_putuint16(target, msg->id);
tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT)
& DNS_MESSAGE_OPCODE_MASK);
tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK); /* XXX edns? */
tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
isc_buffer_putuint16(&tmpbuf, tmp);
isc_buffer_putuint16(&tmpbuf, msg->counts[DNS_SECTION_QUESTION]);
isc_buffer_putuint16(&tmpbuf, msg->counts[DNS_SECTION_ANSWER]);
isc_buffer_putuint16(&tmpbuf, msg->counts[DNS_SECTION_AUTHORITY]);
isc_buffer_putuint16(target, tmp);
isc_buffer_putuint16(target, msg->counts[DNS_SECTION_QUESTION]);
isc_buffer_putuint16(target, msg->counts[DNS_SECTION_ANSWER]);
isc_buffer_putuint16(target, msg->counts[DNS_SECTION_AUTHORITY]);
tmp = msg->counts[DNS_SECTION_ADDITIONAL]
+ msg->counts[DNS_SECTION_TSIG];
isc_buffer_putuint16(&tmpbuf, tmp);
isc_buffer_putuint16(target, tmp);
}
dns_result_t
dns_message_renderend(dns_message_t *msg)
{
isc_buffer_t tmpbuf;
isc_region_t r;
int result;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->buffer != NULL);
if (msg->tsigkey != NULL ||
((msg->flags & DNS_MESSAGEFLAG_QR) != 0 &&
msg->querytsigstatus != dns_rcode_noerror))
{
result = dns_tsig_sign(msg);
if (result != DNS_R_SUCCESS)
return (result);
result = dns_message_rendersection(msg, DNS_SECTION_TSIG, 0, 0);
if (result != DNS_R_SUCCESS)
return (result);
}
isc_buffer_used(msg->buffer, &r);
isc_buffer_init(&tmpbuf, r.base, r.length, ISC_BUFFERTYPE_BINARY);
dns_message_renderheader(msg, &tmpbuf);
msg->buffer = NULL; /* forget about this buffer only on success XXX */
@@ -1650,5 +1711,17 @@ dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
msg->flags &= DNS_MESSAGE_REPLYPRESERVE;
msg->flags |= DNS_MESSAGEFLAG_QR;
/*
* This saves the query TSIG information for later use, if there is
* any. This only happens once - that is, if dns_message_reply
* has already moved the variables, this has no effect.
*/
if (msg->tsig != NULL) {
msg->querytsig = msg->tsig;
msg->tsig = NULL;
msg->querytsigstatus = msg->tsigstatus;
msg->tsigstatus = dns_rcode_noerror;
}
return (DNS_R_SUCCESS);
}

View File

@@ -15,7 +15,7 @@
* SOFTWARE.
*/
/* $Id: tsig_250.c,v 1.14 1999/08/12 01:32:29 halley Exp $ */
/* $Id: tsig_250.c,v 1.15 1999/08/20 18:56:24 bwelling Exp $ */
/* draft-ietf-dnsind-tsig-07.txt */
@@ -292,6 +292,9 @@ static inline dns_result_t
fromstruct_any_tsig(dns_rdataclass_t rdclass, dns_rdatatype_t type,
void *source, isc_buffer_t *target)
{
isc_region_t tr;
dns_rdata_any_tsig_t *tsig;
dns_compress_t cctx;
REQUIRE(type == 250);
REQUIRE(rdclass == 255);
@@ -299,11 +302,68 @@ fromstruct_any_tsig(dns_rdataclass_t rdclass, dns_rdatatype_t type,
source = source;
target = target;
return (DNS_R_NOTIMPLEMENTED);
tsig = (dns_rdata_any_tsig_t *) source;
REQUIRE(tsig->mctx != NULL);
/* Algorithm Name */
RETERR(dns_compress_init(&cctx, -1, tsig->mctx));
dns_compress_setmethods(&cctx, DNS_COMPRESS_NONE);
RETERR(dns_name_towire(tsig->algorithm, &cctx, target));
dns_compress_invalidate(&cctx);
isc_buffer_available(target, &tr);
if (tr.length < 6 + 2 + 2)
return (DNS_R_NOSPACE);
/* Time Signed: 48 bits */
RETERR(uint16_tobuffer(tsig->timesigned >> 32, target));
RETERR(uint32_tobuffer(tsig->timesigned & 0xffffffff, target));
/* Fudge */
RETERR(uint16_tobuffer(tsig->fudge, target));
/* Signature Size */
RETERR(uint16_tobuffer(tsig->siglen, target));
/* Signature */
if (tsig->siglen > 0) {
isc_buffer_available(target, &tr);
if (tr.length < tsig->siglen)
return (DNS_R_NOSPACE);
memcpy(tr.base, tsig->signature, tsig->siglen);
isc_buffer_add(target, tsig->siglen);
}
isc_buffer_available(target, &tr);
if (tr.length < 2 + 2 + 2)
return (DNS_R_NOSPACE);
/* Original ID */
RETERR(uint16_tobuffer(tsig->originalid, target));
/* Error */
RETERR(uint16_tobuffer(tsig->error, target));
/* Other Len */
RETERR(uint16_tobuffer(tsig->otherlen, target));
/* Other Data */
if (tsig->otherlen > 0) {
isc_buffer_available(target, &tr);
if (tr.length < tsig->otherlen)
return (DNS_R_NOSPACE);
memcpy(tr.base, tsig->other, tsig->otherlen);
isc_buffer_add(target, tsig->otherlen);
}
return (DNS_R_SUCCESS);
}
static inline dns_result_t
tostruct_any_tsig(dns_rdata_t *rdata, void *target, isc_mem_t *mctx) {
dns_rdata_any_tsig_t *tsig;
dns_name_t alg;
isc_region_t sr;
REQUIRE(rdata->type == 250);
REQUIRE(rdata->rdclass == 255);
@@ -311,18 +371,90 @@ tostruct_any_tsig(dns_rdata_t *rdata, void *target, isc_mem_t *mctx) {
target = target;
mctx = mctx;
return (DNS_R_NOTIMPLEMENTED);
tsig = (dns_rdata_any_tsig_t *) target;
tsig->common.rdclass = rdata->rdclass;
tsig->common.rdtype = rdata->type;
ISC_LINK_INIT(&tsig->common, link);
tsig->mctx = mctx;
dns_rdata_toregion(rdata, &sr);
/* Algorithm Name */
dns_name_init(&alg, NULL);
dns_name_fromregion(&alg, &sr);
tsig->algorithm = (dns_name_t *) isc_mem_get(mctx, sizeof(dns_name_t));
if (tsig->algorithm == NULL)
return (DNS_R_NOMEMORY);
dns_name_init(tsig->algorithm, NULL);
RETERR(dns_name_dup(&alg, mctx, tsig->algorithm));
isc_region_consume(&sr, name_length(tsig->algorithm));
/* Time Signed */
tsig->timesigned = ((isc_uint64_t)sr.base[0] << 40) |
((isc_uint64_t)sr.base[1] << 32) |
(sr.base[2] << 24) | (sr.base[3] << 16) |
(sr.base[4] << 8) | sr.base[5];
isc_region_consume(&sr, 6);
/* Fudge */
tsig->fudge = uint16_fromregion(&sr);
isc_region_consume(&sr, 2);
/* Signature Size */
tsig->siglen = uint16_fromregion(&sr);
isc_region_consume(&sr, 2);
/* Signature */
if (tsig->siglen > 0) {
tsig->signature = isc_mem_get(mctx, tsig->siglen);
if (tsig->signature == NULL)
return (DNS_R_NOMEMORY);
memcpy(tsig->signature, sr.base, tsig->siglen);
isc_region_consume(&sr, tsig->siglen);
}
else
tsig->signature = NULL;
/* Original ID */
tsig->originalid = uint16_fromregion(&sr);
isc_region_consume(&sr, 2);
/* Error */
tsig->error = uint16_fromregion(&sr);
isc_region_consume(&sr, 2);
/* Other Size */
tsig->otherlen = uint16_fromregion(&sr);
isc_region_consume(&sr, 2);
/* Other */
if (tsig->otherlen > 0) {
tsig->other = isc_mem_get(mctx, tsig->otherlen);
if (tsig->other == NULL)
return (DNS_R_NOMEMORY);
memcpy(tsig->other, sr.base, tsig->otherlen);
isc_region_consume(&sr, tsig->otherlen);
}
else
tsig->other = NULL;
return (DNS_R_SUCCESS);
}
static inline void
freestruct_any_tsig(void *source) {
dns_rdata_any_tsig_t *tsig = source;
dns_rdata_any_tsig_t *tsig = (dns_rdata_any_tsig_t *) source;
REQUIRE(source != NULL);
REQUIRE(tsig->common.rdclass == 255);
REQUIRE(tsig->common.rdtype == 250);
REQUIRE(ISC_FALSE);
dns_name_free(tsig->algorithm, tsig->mctx);
isc_mem_put(tsig->mctx, tsig->algorithm, sizeof(dns_name_t));
if (tsig->siglen > 0)
isc_mem_put(tsig->mctx, tsig->signature, tsig->siglen);
if (tsig->other != NULL)
isc_mem_put(tsig->mctx, tsig->other, tsig->otherlen);
}
static inline dns_result_t

View File

@@ -15,11 +15,20 @@
* SOFTWARE.
*/
/* $Id: tsig_250.h,v 1.9 1999/05/07 03:24:05 marka Exp $ */
/* $Id: tsig_250.h,v 1.10 1999/08/20 18:56:24 bwelling Exp $ */
/* draft-ietf-dnsind-tsig-07.txt */
/* draft-ietf-dnsind-tsig-10.txt */
typedef struct dns_rdata_any_tsig {
dns_rdatacommon_t common;
/*XXX*/
isc_mem_t * mctx;
dns_name_t * algorithm;
isc_uint64_t timesigned;
isc_uint16_t fudge;
isc_uint16_t siglen;
unsigned char * signature;
isc_uint16_t originalid;
isc_uint16_t error;
isc_uint16_t otherlen;
unsigned char * other;
} dns_rdata_any_tsig_t;

View File

@@ -16,7 +16,11 @@
#include <dns/message.h>
#include <dns/dispatch.h>
#include <dns/resolver.h>
#include <dns/rdata.h>
#include <dns/rdataset.h>
#include <dns/tsig.h>
#include <dst/dst.h>
#include "../isc/util.h" /* XXX */
@@ -49,6 +53,8 @@ typedef struct query {
dns_dispentry_t * dispentry; /* XXX name */
ISC_LINK(struct query) link;
isc_buffer_t buffer;
dns_rdata_any_tsig_t *tsig;
dns_tsig_key_t *tsigkey;
unsigned char data[512];
} resquery_t;
@@ -266,6 +272,8 @@ fctx_sendquery(fetchctx_t *fctx) {
if (result != DNS_R_SUCCESS)
goto cleanup_query;
query->fctx = fctx;
query->tsig = NULL;
query->tsigkey = NULL;
query->magic = QUERY_MAGIC;
fctx->qmessage->opcode = dns_opcode_query;
@@ -303,14 +311,16 @@ fctx_sendquery(fetchctx_t *fctx) {
DNS_SECTION_ADDITIONAL, 0, 0);
if (result != DNS_R_SUCCESS)
goto cleanup_message;
result = dns_message_rendersection(fctx->qmessage,
DNS_SECTION_TSIG, 0, 0);
if (result != DNS_R_SUCCESS)
goto cleanup_message;
result = dns_message_renderend(fctx->qmessage);
if (result != DNS_R_SUCCESS)
goto cleanup_message;
if (fctx->qmessage->tsigkey != NULL) {
query->tsigkey = fctx->qmessage->tsigkey;
query->tsig = fctx->qmessage->tsig;
fctx->qmessage->tsig = NULL;
}
/*
* We're now done with the query message.
*
@@ -376,6 +386,8 @@ fctx_cancelquery(resquery_t *query, dns_dispatchevent_t **deventp) {
deventp);
ISC_LIST_UNLINK(fctx->queries, query, link);
query->magic = 0;
if (query->tsig != NULL)
dns_rdata_freestruct(query->tsig);
isc_mem_put(fctx->res->mctx, query, sizeof *query);
}
@@ -803,6 +815,8 @@ query_response(isc_task_t *task, isc_event_t *event) {
INSIST(fctx->state == fetchstate_active);
message = fctx->rmessage;
message->querytsig = query->tsig;
message->tsigkey = query->tsigkey;
result = dns_message_parse(message, &devent->buffer, ISC_FALSE);
if (result != DNS_R_SUCCESS) {
switch (result) {
@@ -859,6 +873,7 @@ query_response(isc_task_t *task, isc_event_t *event) {
goto done;
}
query->tsig = NULL;
fctx_stoptimer(fctx);
fctx_cancelquery(query, &devent);

721
lib/dns/tsig.c Normal file
View File

@@ -0,0 +1,721 @@
/*
* Copyright (C) 1999 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* $Id: tsig.c,v 1.1 1999/08/20 18:56:23 bwelling Exp $
* Principal Author: Brian Wellington
*/
#include <config.h>
#include <stdlib.h>
#include <isc/assertions.h>
#include <isc/buffer.h>
#include <isc/error.h>
#include <isc/list.h>
#include <isc/net.h>
#include <isc/result.h>
#include <isc/rwlock.h>
#include <isc/time.h>
#include <isc/types.h>
#include <dns/keyvalues.h>
#include <dns/name.h>
#include <dns/message.h>
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/tsig.h>
#include <dst/dst.h>
#include <dst/result.h>
#define TSIG_MAGIC 0x54534947 /* TSIG */
#define VALID_TSIG_KEY(x) ((x) != NULL && (x)->magic == TSIG_MAGIC)
/* XXXBEW If an unsorted list isn't good enough, this can be updated */
static ISC_LIST(dns_tsig_key_t) tsigkeys;
static isc_rwlock_t tsiglock;
static isc_mem_t *tsig_mctx = NULL;
dns_name_t *dns_tsig_hmacmd5_name = NULL;
#define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
isc_result_t
dns_tsig_key_create(dns_name_t *name, dns_name_t *algorithm,
unsigned char *secret, int length,
isc_mem_t *mctx, dns_tsig_key_t **key)
{
isc_buffer_t b, nameb;
char namestr[1024];
isc_uint16_t alg;
dns_tsig_key_t *tkey;
isc_result_t ret;
REQUIRE(key != NULL);
REQUIRE(*key == NULL);
REQUIRE(name != NULL);
REQUIRE(algorithm != NULL);
REQUIRE(length >= 0);
if (length > 0)
REQUIRE(secret != NULL);
REQUIRE(mctx != NULL);
if (!dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME))
return (ISC_R_NOTFOUND);
else
alg = DST_ALG_HMAC_MD5;
*key = (dns_tsig_key_t *) isc_mem_get(mctx, sizeof(dns_tsig_key_t));
if (*key == NULL)
return (ISC_R_NOMEMORY);
tkey = *key;
dns_name_init(&tkey->name, NULL);
ret = dns_name_dup(name, mctx, &tkey->name);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
dns_name_downcase(&tkey->name);
dns_name_init(&tkey->algorithm, NULL);
ret = dns_name_dup(algorithm, mctx, &tkey->algorithm);
if (ret != ISC_R_SUCCESS)
goto cleanup_name;
dns_name_downcase(&tkey->algorithm);
isc_buffer_init(&nameb, namestr, sizeof(namestr), ISC_BUFFERTYPE_TEXT);
ret = dns_name_totext(name, ISC_FALSE, &nameb);
if (ret != ISC_R_SUCCESS)
goto cleanup_algorithm;
if (length > 0) {
isc_buffer_init(&b, secret, length, ISC_BUFFERTYPE_BINARY);
isc_buffer_add(&b, length);
ret = dst_key_frombuffer(namestr, alg,
NS_KEY_NAME_ENTITY,
NS_KEY_PROT_DNSSEC,
&b, mctx, &tkey->key);
if (ret != ISC_R_SUCCESS)
goto cleanup_algorithm;
}
else
tkey->key = NULL;
ISC_LINK_INIT(tkey, link);
isc_rwlock_lock(&tsiglock, isc_rwlocktype_write);
ISC_LIST_APPEND(tsigkeys, tkey, link);
isc_rwlock_unlock(&tsiglock, isc_rwlocktype_write);
tkey->mctx = mctx;
tkey->magic = TSIG_MAGIC;
return (ISC_R_SUCCESS);
cleanup_algorithm:
dns_name_free(&tkey->algorithm, mctx);
cleanup_name:
dns_name_free(&tkey->name, mctx);
cleanup_key:
isc_mem_put(mctx, *key, sizeof(dns_tsig_key_t));
return (ret);
}
/* Caller must be sure that this key is not in use. */
void
dns_tsig_key_free(dns_tsig_key_t **key) {
dns_tsig_key_t *tkey;
REQUIRE(key != NULL);
REQUIRE(VALID_TSIG_KEY(*key));
tkey = *key;
tkey->magic = 0;
isc_rwlock_lock(&tsiglock, isc_rwlocktype_write);
ISC_LIST_UNLINK(tsigkeys, tkey, link);
isc_rwlock_unlock(&tsiglock, isc_rwlocktype_write);
dns_name_free(&tkey->name, tkey->mctx);
dns_name_free(&tkey->algorithm, tkey->mctx);
if (tkey->key != NULL)
dst_key_free(tkey->key);
isc_mem_put(tkey->mctx, tkey, sizeof(dns_tsig_key_t));
}
isc_result_t
dns_tsig_sign(dns_message_t *msg) {
dns_tsig_key_t *key;
dns_rdata_any_tsig_t *tsig;
unsigned char data[128];
isc_buffer_t databuf, sigbuf, rdatabuf;
isc_dynbuffer_t *dynbuf;
dns_name_t *owner;
dns_rdata_t *rdata;
dns_rdatalist_t *datalist;
dns_rdataset_t *dataset;
isc_region_t r;
isc_time_t now;
dst_context_t ctx;
isc_mem_t *mctx;
int tries;
isc_result_t ret;
REQUIRE(msg != NULL);
if (msg->tsigkey != NULL)
REQUIRE(VALID_TSIG_KEY(msg->tsigkey));
REQUIRE(msg->tsig == NULL);
if (is_response(msg))
REQUIRE(msg->querytsig != NULL);
mctx = msg->mctx;
key = msg->tsigkey;
tsig = (dns_rdata_any_tsig_t *)
isc_mem_get(mctx, sizeof(dns_rdata_any_tsig_t));
if (tsig == NULL)
return (ISC_R_NOMEMORY);
tsig->mctx = mctx;
tsig->common.rdclass = dns_rdataclass_any;
tsig->common.rdtype = dns_rdatatype_tsig;
ISC_LINK_INIT(&tsig->common, link);
tsig->algorithm = (dns_name_t *) isc_mem_get(mctx, sizeof(dns_name_t));
if (tsig->algorithm == NULL) {
ret = ISC_R_NOMEMORY;
goto cleanup_struct;
}
dns_name_init(tsig->algorithm, NULL);
ret = dns_name_dup(&key->algorithm, mctx, tsig->algorithm);
if (ret != ISC_R_SUCCESS)
goto cleanup_struct;
ret = isc_time_now(&now);
if (ret != ISC_R_SUCCESS)
goto cleanup_algorithm;
tsig->timesigned = now.seconds;
tsig->fudge = DNS_TSIG_FUDGE;
tsig->originalid = msg->id;
isc_buffer_init(&databuf, data, sizeof(data), ISC_BUFFERTYPE_BINARY);
if (!dns_tsig_emptykey(key)) {
ret = dst_sign(DST_SIG_MODE_INIT, key->key, &ctx, NULL, NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_algorithm;
}
if (is_response(msg)) {
if (!dns_tsig_emptykey(key)) {
isc_buffer_putuint16(&databuf, msg->querytsig->siglen);
isc_buffer_available(&databuf, &r);
if (r.length < msg->querytsig->siglen)
return (ISC_R_NOSPACE);
memcpy(r.base, msg->querytsig->signature,
msg->querytsig->siglen);
isc_buffer_add(&databuf, msg->querytsig->siglen);
isc_buffer_used(&databuf, &r);
ret = dst_sign(DST_SIG_MODE_UPDATE, key->key, &ctx, &r,
NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_algorithm;
}
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 = 6;
tsig->other = (unsigned char *) isc_mem_get(mctx, 6);
if (tsig->other == NULL) {
ret = ISC_R_NOMEMORY;
goto cleanup_other;
}
isc_buffer_init(&otherbuf, tsig->other, tsig->otherlen = 6,
ISC_BUFFERTYPE_BINARY);
isc_buffer_putuint16(&otherbuf, tsig->timesigned >> 32);
isc_buffer_putuint32(&otherbuf, tsig->timesigned & 0xFFFFFFFF);
}
if (!dns_tsig_emptykey(key)) {
unsigned char header[DNS_MESSAGE_HEADERLEN];
isc_buffer_t headerbuf;
isc_buffer_init(&headerbuf, header, sizeof header,
ISC_BUFFERTYPE_BINARY);
dns_message_renderheader(msg, &headerbuf);
isc_buffer_used(&headerbuf, &r);
ret = dst_sign(DST_SIG_MODE_UPDATE, key->key, &ctx, &r, NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_other;
isc_buffer_used(msg->buffer, &r);
isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
ret = dst_sign(DST_SIG_MODE_UPDATE, key->key, &ctx, &r, NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_other;
/* Digest the name, class, ttl, alg */
dns_name_toregion(&key->name, &r);
ret = dst_sign(DST_SIG_MODE_UPDATE, key->key, &ctx, &r, NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_other;
isc_buffer_clear(&databuf);
isc_buffer_putuint16(&databuf, dns_rdataclass_any);
isc_buffer_putuint32(&databuf, 0); /* ttl */
isc_buffer_used(&databuf, &r);
ret = dst_sign(DST_SIG_MODE_UPDATE, key->key, &ctx, &r, NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_other;
dns_name_toregion(tsig->algorithm, &r);
ret = dst_sign(DST_SIG_MODE_UPDATE, key->key, &ctx, &r, NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_other;
isc_buffer_clear(&databuf);
if (tsig->error != dns_tsigerror_badtime) {
isc_buffer_putuint16(&databuf,
tsig->timesigned >> 32);
isc_buffer_putuint32(&databuf,
tsig->timesigned & 0xFFFFFFFF);
}
else {
isc_uint64_t querysigned = msg->querytsig->timesigned;
isc_buffer_putuint16(&databuf,
querysigned >> 32);
isc_buffer_putuint32(&databuf,
querysigned & 0xFFFFFFFF);
}
isc_buffer_putuint16(&databuf, tsig->fudge);
isc_buffer_putuint16(&databuf, tsig->error);
isc_buffer_putuint16(&databuf, tsig->otherlen);
isc_buffer_used(&databuf, &r);
ret = dst_sign(DST_SIG_MODE_UPDATE, key->key, &ctx, &r, NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_other;
if (tsig->otherlen > 0) {
r.length = tsig->otherlen;
r.base = tsig->other;
ret = dst_sign(DST_SIG_MODE_UPDATE, key->key, &ctx, &r,
NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_other;
}
tsig->siglen = dst_sig_size(key->key);
tsig->signature = (unsigned char *)
isc_mem_get(mctx, tsig->siglen);
if (tsig->signature == NULL) {
ret = ISC_R_NOMEMORY;
goto cleanup_other;
}
isc_buffer_init(&sigbuf, tsig->signature, tsig->siglen,
ISC_BUFFERTYPE_BINARY);
ret = dst_sign(DST_SIG_MODE_FINAL, key->key, &ctx, NULL,
&sigbuf);
if (ret != ISC_R_SUCCESS)
goto cleanup_signature;
}
else {
tsig->siglen = 0;
tsig->signature = NULL;
}
/* There should be a better way of accessing msg->scratchpad */
rdata = NULL;
ret = dns_message_gettemprdata(msg, &rdata);
if (ret != ISC_R_SUCCESS)
goto cleanup_signature;
tries = 0;
dynbuf = ISC_LIST_TAIL(msg->scratchpad);
INSIST(dynbuf != NULL);
rdatabuf = dynbuf->buffer;
while (tries < 2) {
ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any,
dns_rdatatype_tsig, tsig, &rdatabuf);
if (ret == ISC_R_SUCCESS)
break;
else if (ret == ISC_R_NOSPACE) {
if (++tries == 2)
return (ISC_R_NOMEMORY);
ret = isc_dynbuffer_allocate(msg->mctx, &dynbuf, 512,
ISC_BUFFERTYPE_BINARY);
if (ret != ISC_R_SUCCESS)
goto cleanup_signature;
ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
rdatabuf = dynbuf->buffer;
}
else
goto cleanup_signature;
}
msg->tsig = tsig;
owner = NULL;
ret = dns_message_gettempname(msg, &owner);
if (ret != ISC_R_SUCCESS)
goto cleanup_signature;
dns_name_init(owner, NULL);
dns_name_clone(&key->name, owner);
datalist = NULL;
ret = dns_message_gettemprdatalist(msg, &datalist);
if (ret != ISC_R_SUCCESS)
goto cleanup_signature;
datalist->rdclass = dns_rdataclass_any;
datalist->type = dns_rdatatype_tsig;
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_signature;
dns_rdataset_init(dataset);
dns_rdatalist_tordataset(datalist, dataset);
ISC_LIST_APPEND(owner->list, dataset, link);
dns_message_addname(msg, owner, DNS_SECTION_TSIG);
return (ISC_R_SUCCESS);
cleanup_signature:
if (tsig->signature != NULL)
isc_mem_put(mctx, tsig->signature, tsig->siglen);
cleanup_other:
if (tsig->other != NULL)
isc_mem_put(mctx, tsig->other, tsig->otherlen);
cleanup_algorithm:
dns_name_free(tsig->algorithm, mctx);
cleanup_struct:
msg->tsig = NULL;
isc_mem_put(mctx, tsig, sizeof(dns_rdata_any_tsig_t));
return (ret);
}
isc_result_t
dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg) {
dns_rdata_any_tsig_t *tsig;
isc_region_t r, source_r, header_r, sig_r;
isc_buffer_t databuf;
unsigned char data[32];
dns_name_t *keyname;
dns_rdataset_t *dataset;
dns_rdata_t rdata;
isc_time_t now;
isc_result_t ret;
dns_tsig_key_t *tsigkey = NULL;
dst_key_t *key = NULL;
unsigned char header[DNS_MESSAGE_HEADERLEN];
dst_context_t ctx;
isc_mem_t *mctx;
isc_uint16_t addcount, id;
REQUIRE(source != NULL);
REQUIRE(msg != NULL);
REQUIRE(msg->tsigkey != NULL);
REQUIRE(msg->tsig == NULL);
REQUIRE(!(ISC_LIST_EMPTY(msg->sections[DNS_SECTION_TSIG])));
if (is_response(msg))
REQUIRE(msg->querytsig != NULL);
mctx = msg->mctx;
/*
* If we're here, we know the message is well formed and contains a
* TSIG record.
*/
ret = dns_message_firstname(msg, DNS_SECTION_TSIG);
if (ret != ISC_R_SUCCESS)
return (ret);
keyname = NULL;
dns_message_currentname(msg, DNS_SECTION_TSIG, &keyname);
dataset = ISC_LIST_HEAD(keyname->list);
ret = dns_rdataset_first(dataset);
if (ret != ISC_R_SUCCESS)
return (ret);
dns_rdataset_current(dataset, &rdata);
tsig = (dns_rdata_any_tsig_t *)
isc_mem_get(mctx, sizeof(dns_rdata_any_tsig_t));
if (tsig == NULL)
return (ISC_R_NOMEMORY);
msg->tsig = tsig;
ret = dns_rdata_tostruct(&rdata, tsig, mctx);
if (ret != ISC_R_SUCCESS)
goto cleanup_emptystruct;
isc_buffer_used(source, &r);
memcpy(header, r.base, DNS_MESSAGE_HEADERLEN);
isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
/* Do the key name and algorithm match that of the query? */
if (is_response(msg) &&
(!dns_name_equal(keyname, &msg->tsigkey->name) ||
!dns_name_equal(tsig->algorithm, msg->querytsig->algorithm)))
{
msg->tsigstatus = dns_tsigerror_badkey;
return (DNS_R_TSIGVERIFYFAILURE);
}
/* Find dns_tsig_key_t based on keyname */
ret = dns_tsig_findkey(&tsigkey, keyname, tsig->algorithm);
if (ret != ISC_R_SUCCESS) {
msg->tsigstatus = dns_tsigerror_badkey;
msg->tsigkey = NULL;
/*
* this key must be deleted later - an empty key can be found
* by calling dns_tsig_emptykey()
*/
ret = dns_tsig_key_create(keyname, tsig->algorithm, NULL, 0,
mctx, &msg->tsigkey);
if (ret != ISC_R_SUCCESS)
goto cleanup_struct;
return (DNS_R_TSIGVERIFYFAILURE);
}
msg->tsigkey = tsigkey;
key = tsigkey->key;
/* Is the time ok? */
ret = isc_time_now(&now);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
if (abs(now.seconds - tsig->timesigned) > tsig->fudge) {
msg->tsigstatus = dns_tsigerror_badtime;
return (DNS_R_TSIGVERIFYFAILURE);
}
if (tsig->siglen > 0) {
sig_r.base = tsig->signature;
sig_r.length = tsig->siglen;
ret = dst_verify(DST_SIG_MODE_INIT, key, &ctx, NULL, &sig_r);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
if (is_response(msg)) {
isc_buffer_init(&databuf, data, sizeof(data),
ISC_BUFFERTYPE_BINARY);
isc_buffer_putuint16(&databuf, msg->querytsig->siglen);
isc_buffer_used(&databuf, &r);
ret = dst_verify(DST_SIG_MODE_UPDATE, key, &ctx, &r,
NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
if (msg->querytsig->siglen > 0) {
r.length = msg->querytsig->siglen;
r.base = msg->querytsig->signature;
ret = dst_verify(DST_SIG_MODE_UPDATE, key,
&ctx, &r, NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
}
}
/* 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_verify(DST_SIG_MODE_UPDATE, key, &ctx, &header_r,
&sig_r);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
/* Digest all non-TSIG records. */
isc_buffer_used(source, &source_r);
r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
r.length = msg->tsigstart - DNS_MESSAGE_HEADERLEN;
ret = dst_verify(DST_SIG_MODE_UPDATE, key, &ctx, &r, &sig_r);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
/* Digest the key name */
dns_name_toregion(&tsigkey->name, &r);
ret = dst_verify(DST_SIG_MODE_UPDATE, key, &ctx, &r, &sig_r);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
isc_buffer_init(&databuf, data, sizeof(data),
ISC_BUFFERTYPE_BINARY);
isc_buffer_putuint16(&databuf, tsig->common.rdclass);
isc_buffer_putuint32(&databuf, dataset->ttl);
isc_buffer_used(&databuf, &r);
ret = dst_verify(DST_SIG_MODE_UPDATE, key, &ctx, &r, &sig_r);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
/* Digest the key algorithm */
dns_name_toregion(&tsigkey->algorithm, &r);
ret = dst_verify(DST_SIG_MODE_UPDATE, key, &ctx, &r, &sig_r);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
isc_buffer_clear(&databuf);
isc_buffer_putuint16(&databuf, tsig->timesigned >> 32);
isc_buffer_putuint32(&databuf, tsig->timesigned & 0xFFFFFFFF);
isc_buffer_putuint16(&databuf, tsig->fudge);
isc_buffer_putuint16(&databuf, tsig->error);
isc_buffer_putuint16(&databuf, tsig->otherlen);
isc_buffer_used(&databuf, &r);
ret = dst_verify(DST_SIG_MODE_UPDATE, key, &ctx, &r, &sig_r);
if (tsig->otherlen > 0) {
r.base = tsig->other;
r.length = tsig->otherlen;
ret = dst_verify(DST_SIG_MODE_UPDATE, key, &ctx, &r,
&sig_r);
if (ret != ISC_R_SUCCESS)
goto cleanup_key;
}
ret = dst_verify(DST_SIG_MODE_FINAL, key, &ctx, NULL, &sig_r);
if (ret == DST_R_VERIFY_FINAL_FAILURE) {
msg->tsigstatus = dns_tsigerror_badsig;
return (DNS_R_TSIGVERIFYFAILURE);
}
else if (ret != ISC_R_SUCCESS)
goto cleanup_key;
}
else if (tsig->error != dns_tsigerror_badsig &&
tsig->error != dns_tsigerror_badkey)
{
msg->tsigstatus = dns_tsigerror_badsig;
return (DNS_R_TSIGVERIFYFAILURE);
}
msg->tsigstatus = dns_rcode_noerror;
if (tsig->error != dns_rcode_noerror) {
if (is_response(msg)) {
/* XXXBEW Log a message */
return (ISC_R_SUCCESS);
}
else
return (DNS_R_TSIGERRORSET);
}
return (ISC_R_SUCCESS);
cleanup_key:
if (dns_tsig_emptykey(msg->tsigkey)) {
dns_tsig_key_free(&msg->tsigkey);
msg->tsigkey = NULL;
}
cleanup_struct:
dns_rdata_freestruct(tsig);
cleanup_emptystruct:
msg->tsig = NULL;
isc_mem_put(mctx, tsig, sizeof(dns_rdata_any_tsig_t));
return (ret);
}
isc_result_t
dns_tsig_findkey(dns_tsig_key_t **tsigkey, dns_name_t *name,
dns_name_t *algorithm)
{
dns_tsig_key_t *key;
REQUIRE(tsigkey != NULL);
REQUIRE(name != NULL);
REQUIRE(algorithm != NULL);
isc_rwlock_lock(&tsiglock, isc_rwlocktype_read);
key = ISC_LIST_HEAD(tsigkeys);
while (key != NULL) {
if (dns_name_equal(&key->name, name) &&
dns_name_equal(&key->algorithm, algorithm))
{
*tsigkey = key;
isc_rwlock_unlock(&tsiglock, isc_rwlocktype_read);
return (ISC_R_SUCCESS);
}
key = ISC_LIST_NEXT(key, link);
}
isc_rwlock_unlock(&tsiglock, isc_rwlocktype_read);
*tsigkey = NULL;
return (ISC_R_NOTFOUND);
}
isc_result_t
dns_tsig_init(isc_mem_t *mctx) {
isc_buffer_t hmacsrc, namebuf;
isc_result_t ret;
dns_name_t hmac_name;
unsigned char data[32];
ret = isc_rwlock_init(&tsiglock, 0, 0);
if (ret != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_rwlock_init() failed: %s",
isc_result_totext(ret));
return (DNS_R_UNEXPECTED);
}
ISC_LIST_INIT(tsigkeys);
isc_buffer_init(&hmacsrc, DNS_TSIG_HMACMD5,
strlen(DNS_TSIG_HMACMD5), ISC_BUFFERTYPE_TEXT);
isc_buffer_add(&hmacsrc, strlen(DNS_TSIG_HMACMD5));
isc_buffer_init(&namebuf, data, sizeof(data), ISC_BUFFERTYPE_BINARY);
dns_name_init(&hmac_name, NULL);
ret = dns_name_fromtext(&hmac_name, &hmacsrc, NULL, ISC_TRUE, &namebuf);
if (ret != ISC_R_SUCCESS)
return (ret);
dns_tsig_hmacmd5_name = isc_mem_get(mctx, sizeof(dns_name_t));
if (dns_tsig_hmacmd5_name == NULL)
return (ISC_R_NOMEMORY);
dns_name_init(dns_tsig_hmacmd5_name, NULL);
ret = dns_name_dup(&hmac_name, mctx, dns_tsig_hmacmd5_name);
if (ret != ISC_R_SUCCESS) {
isc_mem_put(mctx, dns_tsig_hmacmd5_name, sizeof(dns_name_t));
return (ret);
}
tsig_mctx = mctx;
return (ISC_R_SUCCESS);
}
void
dns_tsig_destroy() {
while (!ISC_LIST_EMPTY(tsigkeys)) {
dns_tsig_key_t *key = ISC_LIST_HEAD(tsigkeys);
dns_tsig_key_free(&key);
}
dns_name_free(dns_tsig_hmacmd5_name, tsig_mctx);
isc_mem_put(tsig_mctx, dns_tsig_hmacmd5_name, sizeof(dns_name_t));
}