Commit 6d4886fa authored by Brian Wellington's avatar Brian Wellington

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

tsig to_struct/from_struct
parent 382442e2
......@@ -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);
/*
......
/*
* 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;