Added support for TSIG records in message and resolver subsystems, added
tsig to_struct/from_struct
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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
154
lib/dns/include/dns/tsig.h
Normal 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 */
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
721
lib/dns/tsig.c
Normal 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));
|
||||
}
|
||||
Reference in New Issue
Block a user