Commit 0f80bfec authored by Brian Wellington's avatar Brian Wellington

The message code now has functions to manipulate TSIG and SIG(0), and the

callers use these functions.  Also a lot of TSIG cleanup.
parent 4bf54f18
......@@ -15,7 +15,7 @@
* SOFTWARE.
*/
/* $Id: xfrout.c,v 1.63 2000/05/26 00:16:35 bwelling Exp $ */
/* $Id: xfrout.c,v 1.64 2000/05/30 23:14:48 bwelling Exp $ */
#include <config.h>
......@@ -26,6 +26,7 @@
#include <dns/db.h>
#include <dns/dbiterator.h>
#include <dns/fixedname.h>
#include <dns/journal.h>
#include <dns/message.h>
#include <dns/peer.h>
......@@ -741,7 +742,7 @@ typedef struct {
unsigned int txmemlen;
unsigned int nmsg; /* Number of messages sent */
dns_tsigkey_t *tsigkey; /* Key used to create TSIG */
dns_rdata_any_tsig_t *lasttsig; /* the last TSIG */
isc_buffer_t *lasttsig; /* the last TSIG */
isc_boolean_t many_answers;
int sends; /* Send in progress */
isc_boolean_t shuttingdown;
......@@ -752,7 +753,7 @@ xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client,
unsigned int id, dns_name_t *qname, dns_rdatatype_t qtype,
dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
rrstream_t *stream, dns_tsigkey_t *tsigkey,
dns_rdata_any_tsig_t *lasttsig,
isc_buffer_t *lasttsig,
unsigned int maxtime,
unsigned int idletime,
isc_boolean_t many_answers,
......@@ -810,6 +811,7 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
dns_transfer_format_t format = client->view->transfer_format;
isc_netaddr_t na;
dns_peer_t *peer = NULL;
isc_buffer_t *tsigbuf = NULL;
switch (reqtype) {
case dns_rdatatype_axfr:
......@@ -1022,13 +1024,15 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
data_stream = NULL;
have_stream:
CHECK(dns_message_getquerytsig(request, mctx, &tsigbuf));
/*
* Create the xfrout context object. This transfers the ownership
* of "stream", "db", "ver", and "quota" to the xfrout context object.
*/
CHECK(xfrout_ctx_create(mctx, client, request->id, question_name,
reqtype, db, ver, quota, stream,
dns_message_gettsigkey(request), request->tsig,
dns_message_gettsigkey(request),
tsigbuf,
dns_zone_getmaxxfrout(zone),
dns_zone_getidleout(zone),
(format == dns_many_answers) ?
......@@ -1084,7 +1088,7 @@ xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
dns_name_t *qname, dns_rdatatype_t qtype,
dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
rrstream_t *stream, dns_tsigkey_t *tsigkey,
dns_rdata_any_tsig_t *lasttsig, unsigned int maxtime,
isc_buffer_t *lasttsig, unsigned int maxtime,
unsigned int idletime, isc_boolean_t many_answers,
xfrout_ctx_t **xfrp)
{
......@@ -1215,7 +1219,9 @@ sendstream(xfrout_ctx_t *xfr) {
if ((xfr->client->attributes & NS_CLIENTATTR_RA) != 0)
msg->flags |= DNS_MESSAGEFLAG_RA;
dns_message_settsigkey(msg, xfr->tsigkey);
msg->querytsig = xfr->lasttsig;
CHECK(dns_message_setquerytsig(msg, xfr->lasttsig));
if (xfr->lasttsig != NULL)
isc_buffer_free(&xfr->lasttsig);
/*
* Include a question section in the first message only.
......@@ -1384,17 +1390,7 @@ sendstream(xfrout_ctx_t *xfr) {
}
/* Advance lasttsig to be the last TSIG generated */
xfr->lasttsig = msg->tsig;
/* Clear this field so the TSIG is not destroyed */
msg->tsig = NULL;
/*
* If this is the first message, clear this too, since this is
* also pointed to by the request.
*/
if (xfr->nmsg == 0)
msg->querytsig = NULL;
CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig));
xfr->nmsg++;
......@@ -1426,10 +1422,8 @@ xfrout_ctx_destroy(xfrout_ctx_t **xfrp) {
isc_mem_put(xfr->mctx, xfr->buf.base, xfr->buf.length);
if (xfr->txmem != NULL)
isc_mem_put(xfr->mctx, xfr->txmem, xfr->txmemlen);
if (xfr->lasttsig != NULL) {
dns_rdata_freestruct(xfr->lasttsig);
isc_mem_put(xfr->mctx, xfr->lasttsig, sizeof(*xfr->lasttsig));
}
if (xfr->lasttsig != NULL)
isc_buffer_free(&xfr->lasttsig);
if (xfr->quota != NULL)
isc_quota_detach(&xfr->quota);
if (xfr->ver != NULL)
......
......@@ -26,7 +26,6 @@
#include <isc/magic.h>
#include <dns/compress.h>
#include <dns/rdatastruct.h>
#include <dns/types.h>
#include <dst/dst.h>
......@@ -178,6 +177,7 @@ struct dns_message {
unsigned int verify_attempted : 1;
unsigned int opt_reserved;
unsigned int sig_reserved;
unsigned int reserved; /* reserved space (render) */
isc_buffer_t *buffer;
......@@ -199,8 +199,7 @@ struct dns_message {
dns_rcode_t tsigstatus;
dns_rcode_t querytsigstatus;
dns_name_t *tsigname;
dns_rdata_any_tsig_t *tsig;
dns_rdata_any_tsig_t *querytsig;
dns_rdataset_t *querytsigset;
dns_tsigkey_t *tsigkey;
void *tsigctx;
int sigstart;
......@@ -952,7 +951,7 @@ dns_message_gettsig(dns_message_t *msg, dns_name_t **owner);
* The TSIG rdataset of 'msg', or NULL if there isn't one.
*/
void
isc_result_t
dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key);
/*
* Set the tsig key for 'msg'. This is only necessary for when rendering a
......@@ -961,8 +960,16 @@ dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key);
*
* Requires:
*
* 'msg' is a valid message
* 'msg' is a valid message with rendering intent,
* dns_message_renderbegin() has been called, and no sections have been
* rendered.
* 'key' is a valid tsig key or NULL.
*
* Returns:
*
* ISC_R_SUCCESS -- all is well.
*
* ISC_R_NOSPACE -- there is no space for the TSIG record.
*/
dns_tsigkey_t *
......@@ -975,6 +982,50 @@ dns_message_gettsigkey(dns_message_t *msg);
* 'msg' is a valid message
*/
isc_result_t
dns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig);
/*
* Indicates that 'querytsig' is the TSIG from the signed query for which
* 'msg' is the response. This is also used for chained TSIGs in TCP
* responses.
*
* Requires:
*
* 'querytsig' is a valid buffer as returned by dns_message_getquerytsig()
* or NULL
*
* 'msg' is a valid message
*
* Returns:
*
* ISC_R_SUCCESS
* ISC_R_NOMEMORY
*/
isc_result_t
dns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
isc_buffer_t **querytsig);
/*
* Gets the tsig from the TSIG from the signed query 'msg'. This is also used
* for chained TSIGs in TCP responses. Unlike dns_message_gettsig, this makes
* a copy of the data, so can be used if the message is destroyed.
*
* Requires:
*
* 'msg' is a valid signed message
* 'mctx' is a valid memory context
* querytsig != NULL && *querytsig == NULL
*
* Returns:
*
* ISC_R_SUCCESS
* ISC_R_NOMEMORY
*
* Ensures:
* 'tsig' points to NULL or an allocated buffer which must be freed
* by the caller.
*/
dns_rdataset_t *
dns_message_getsig0(dns_message_t *msg, dns_name_t **owner);
/*
......@@ -990,6 +1041,35 @@ dns_message_getsig0(dns_message_t *msg, dns_name_t **owner);
* The SIG(0) rdataset of 'msg', or NULL if there isn't one.
*/
isc_result_t
dns_message_setsig0key(dns_message_t *msg, dst_key_t *key);
/*
* Set the SIG(0) key for 'msg'.
*
* Requires:
*
* 'msg' is a valid message with rendering intent,
* dns_message_renderbegin() has been called, and no sections have been
* rendered.
* 'key' is a valid sig key or NULL.
*
* Returns:
*
* ISC_R_SUCCESS -- all is well.
*
* ISC_R_NOSPACE -- there is no space for the SIG(0) record.
*/
dst_key_t *
dns_message_getsig0key(dns_message_t *msg);
/*
* Gets the SIG(0) key for 'msg'.
*
* Requires:
*
* 'msg' is a valid message
*/
void
dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer);
/*
......
......@@ -32,6 +32,7 @@
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/result.h>
#include <dns/tsig.h>
#include <dns/view.h>
......@@ -325,6 +326,7 @@ msginitprivate(dns_message_t *m) {
m->tsigname = NULL;
m->state = DNS_SECTION_ANY; /* indicate nothing parsed or rendered */
m->opt_reserved = 0;
m->sig_reserved = 0;
m->reserved = 0;
m->buffer = NULL;
m->need_cctx_cleanup = 0;
......@@ -332,8 +334,8 @@ msginitprivate(dns_message_t *m) {
static inline void
msginittsig(dns_message_t *m) {
m->tsigstatus = m->querytsigstatus = dns_rcode_noerror;
m->tsig = m->querytsig = NULL;
m->tsigstatus = dns_rcode_noerror;
m->querytsigstatus = dns_rcode_noerror;
m->tsigkey = NULL;
m->tsigctx = NULL;
m->sigstart = -1;
......@@ -341,6 +343,7 @@ msginittsig(dns_message_t *m) {
m->sig0status = dns_rcode_noerror;
m->query = NULL;
m->saved = NULL;
m->querytsigset = NULL;
}
/*
......@@ -406,12 +409,26 @@ msgresetopt(dns_message_t *msg)
}
static void
msgresetsigs(dns_message_t *msg) {
msgresetsigs(dns_message_t *msg, isc_boolean_t replying) {
if (msg->sig_reserved > 0) {
dns_message_renderrelease(msg, msg->sig_reserved);
msg->sig_reserved = 0;
}
if (msg->tsigset != NULL) {
INSIST(dns_rdataset_isassociated(msg->tsigset));
INSIST(msg->namepool != NULL);
dns_rdataset_disassociate(msg->tsigset);
isc_mempool_put(msg->rdspool, msg->tsigset);
if (replying) {
INSIST(msg->querytsigset == NULL);
msg->querytsigset = msg->tsigset;
} else {
dns_rdataset_disassociate(msg->tsigset);
isc_mempool_put(msg->rdspool, msg->tsigset);
if (msg->querytsigset != NULL) {
dns_rdataset_disassociate(msg->querytsigset);
isc_mempool_put(msg->rdspool,
msg->querytsigset);
}
}
isc_mempool_put(msg->namepool, msg->tsigname);
msg->tsigset = NULL;
msg->tsigname = NULL;
......@@ -440,7 +457,7 @@ msgreset(dns_message_t *msg, isc_boolean_t everything) {
msgresetnames(msg, 0);
msgresetopt(msg);
msgresetsigs(msg);
msgresetsigs(msg, ISC_FALSE);
/*
* Clean up linked lists.
......@@ -507,20 +524,6 @@ 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));
msg->tsig = NULL;
}
if (msg->querytsig != NULL) {
dns_rdata_freestruct(msg->querytsig);
isc_mem_put(msg->mctx, msg->querytsig,
sizeof(dns_rdata_any_tsig_t));
msg->querytsig = NULL;
}
if (msg->tsigkey != NULL) {
dns_tsigkey_detach(&msg->tsigkey);
msg->tsigkey = NULL;
......@@ -556,6 +559,43 @@ msgreset(dns_message_t *msg, isc_boolean_t everything) {
msginit(msg);
}
static unsigned int
spacefortsig(dns_tsigkey_t *key, int otherlen) {
isc_region_t r1, r2;
unsigned int x;
isc_result_t result;
/*
* The space required for an TSIG record is:
*
* n1 bytes for the name
* 2 bytes for the type
* 2 bytes for the class
* 4 bytes for the ttl
* 6 bytes for the time signed
* 2 bytes for the fudge
* 2 bytes for the MAC size
* x bytes for the MAC
* 2 bytes for the original id
* 2 bytes for the error
* 2 bytes for the other data length
* y bytes for the other data (at most)
* ---------------------------------
* 30 + n1 + n2 + x + y bytes
*/
dns_name_toregion(&key->name, &r1);
dns_name_toregion(&key->algorithm, &r2);
if (key->key == NULL)
x = 0;
else {
result = dst_key_sigsize(key->key, &x);
if (result != ISC_R_SUCCESS)
x = 0;
}
return (24 + r1.length + r2.length + x + otherlen);
}
isc_result_t
dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
{
......@@ -1486,6 +1526,9 @@ dns_message_renderbegin(dns_message_t *msg, isc_buffer_t *buffer) {
isc_buffer_availableregion(buffer, &r);
REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
if (r.length < msg->reserved)
return (ISC_R_NOSPACE);
result = dns_compress_init(&msg->cctx, -1, msg->mctx);
if (result != ISC_R_SUCCESS)
return (result);
......@@ -1533,7 +1576,6 @@ dns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer) {
void
dns_message_renderrelease(dns_message_t *msg, unsigned int space) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->buffer != NULL);
REQUIRE(space <= msg->reserved);
msg->reserved -= space;
......@@ -1544,11 +1586,12 @@ dns_message_renderreserve(dns_message_t *msg, unsigned int space) {
isc_region_t r;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->buffer != NULL);
isc_buffer_availableregion(msg->buffer, &r);
if (r.length < (space + msg->reserved))
return (ISC_R_NOSPACE);
if (msg->buffer != NULL) {
isc_buffer_availableregion(msg->buffer, &r);
if (r.length < (space + msg->reserved))
return (ISC_R_NOSPACE);
}
msg->reserved += space;
......@@ -1772,7 +1815,8 @@ dns_message_renderend(dns_message_t *msg) {
}
if (msg->tsigkey != NULL) {
REQUIRE(msg->tsig == NULL);
dns_message_renderrelease(msg, msg->sig_reserved);
msg->sig_reserved = 0;
result = dns_tsig_sign(msg);
if (result != ISC_R_SUCCESS)
return (result);
......@@ -1785,6 +1829,8 @@ dns_message_renderend(dns_message_t *msg) {
}
else if (msg->sig0key != NULL) {
dns_message_renderrelease(msg, msg->sig_reserved);
msg->sig_reserved = 0;
result = dns_dnssec_signmessage(msg, msg->sig0key);
if (result != ISC_R_SUCCESS)
return (result);
......@@ -2100,6 +2146,7 @@ dns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
isc_result_t
dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
unsigned int first_section;
isc_result_t result;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE((msg->flags & DNS_MESSAGEFLAG_QR) == 0);
......@@ -2118,7 +2165,7 @@ dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
msgresetnames(msg, first_section);
msgresetopt(msg);
msgresetsigs(msg);
msgresetsigs(msg, ISC_TRUE);
msginitprivate(msg);
/*
* We now clear most flags and then set QR, ensuring that the
......@@ -2128,15 +2175,21 @@ dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
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.
* This saves the query TSIG status, if the query was signed, and
* reserves space in the reply for the TSIG.
*/
if (msg->tsig != NULL) {
msg->querytsig = msg->tsig;
msg->tsig = NULL;
if (msg->querytsigset != NULL) {
unsigned int otherlen = 0;
msg->querytsigstatus = msg->tsigstatus;
msg->tsigstatus = dns_rcode_noerror;
if (msg->querytsigstatus == dns_tsigerror_badtime)
otherlen = 6;
msg->sig_reserved = spacefortsig(msg->tsigkey, otherlen);
result = dns_message_renderreserve(msg, msg->sig_reserved);
if (result != ISC_R_SUCCESS) {
msg->sig_reserved = 0;
return (result);
}
}
if (msg->saved != NULL) {
msg->query = msg->saved;
......@@ -2188,7 +2241,6 @@ dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
REQUIRE(msg->state == DNS_SECTION_ANY);
msgresetopt(msg);
msgresetsigs(msg);
result = dns_rdataset_first(opt);
if (result != ISC_R_SUCCESS)
......@@ -2220,18 +2272,31 @@ dns_message_gettsig(dns_message_t *msg, dns_name_t **owner) {
return (msg->tsigset);
}
void
isc_result_t
dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) {
isc_result_t result;
/*
* Set the TSIG key for 'msg'
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->tsigkey == NULL);
REQUIRE(msg->state == DNS_SECTION_ANY);
REQUIRE(msg->tsigkey == NULL && msg->sig0key == NULL);
if (key != NULL)
if (key != NULL) {
dns_tsigkey_attach(key, &msg->tsigkey);
if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) {
msg->sig_reserved = spacefortsig(msg->tsigkey, 0);
result = dns_message_renderreserve(msg,
msg->sig_reserved);
if (result != ISC_R_SUCCESS) {
msg->sig_reserved = 0;
return (result);
}
}
}
return (ISC_R_SUCCESS);
}
dns_tsigkey_t *
......@@ -2246,6 +2311,88 @@ dns_message_gettsigkey(dns_message_t *msg) {
return (msg->tsigkey);
}
isc_result_t
dns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) {
dns_rdata_t *rdata = NULL;
dns_rdatalist_t *list = NULL;
dns_rdataset_t *set = NULL;
isc_buffer_t *buf = NULL;
isc_region_t r;
isc_result_t result;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->querytsigset == NULL);
if (querytsig == NULL)
return (ISC_R_SUCCESS);
result = dns_message_gettemprdata(msg, &rdata);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = dns_message_gettemprdatalist(msg, &list);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = dns_message_gettemprdataset(msg, &set);
if (result != ISC_R_SUCCESS)
goto cleanup;
isc_buffer_usedregion(querytsig, &r);
result = isc_buffer_allocate(msg->mctx, &buf, r.length);
if (result != ISC_R_SUCCESS)
goto cleanup;
isc_buffer_putmem(buf, r.base, r.length);
isc_buffer_usedregion(buf, &r);
dns_rdata_init(rdata);
dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r);
dns_message_takebuffer(msg, &buf);
ISC_LIST_INIT(list->rdata);
ISC_LIST_APPEND(list->rdata, rdata, link);
result = dns_rdatalist_tordataset(list, set);
if (result != ISC_R_SUCCESS)
goto cleanup;
msg->querytsigset = set;
return (result);
cleanup:
if (rdata != NULL)
dns_message_puttemprdata(msg, &rdata);
if (list != NULL)
dns_message_puttemprdatalist(msg, &list);
if (set != NULL)
dns_message_puttemprdataset(msg, &set);
return (ISC_R_NOMEMORY);
}
isc_result_t
dns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
isc_buffer_t **querytsig) {
isc_result_t result;
dns_rdata_t rdata;
isc_region_t r;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(mctx != NULL);
REQUIRE(querytsig != NULL && *querytsig == NULL);
if (msg->tsigset == NULL)
return (ISC_R_SUCCESS);
result = dns_rdataset_first(msg->tsigset);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_current(msg->tsigset, &rdata);
dns_rdata_toregion(&rdata, &r);
result = isc_buffer_allocate(mctx, querytsig, r.length);
if (result != ISC_R_SUCCESS)
return (result);
isc_buffer_putmem(*querytsig, r.base, r.length);
return (ISC_R_SUCCESS);
}
dns_rdataset_t *
dns_message_getsig0(dns_message_t *msg, dns_name_t **owner) {
......@@ -2272,6 +2419,71 @@ dns_message_getsig0(dns_message_t *msg, dns_name_t **owner) {
}
}
isc_result_t
dns_message_setsig0key(dns_message_t *msg, dst_key_t *key) {
isc_region_t r;
unsigned int x;
isc_result_t result;
/*
* Set the SIG(0) key for 'msg'
*/
/*
* The space required for an SIG(0) record is:
*
* 1 byte for the name
* 2 bytes for the type
* 2 bytes for the class
* 4 bytes for the ttl
* 2 bytes for the type covered
* 1 byte for the algorithm
* 1 bytes for the labels
* 4 bytes for the original ttl
* 4 bytes for the signature expiration
* 4 bytes for the signature inception
* 2 bytes for the key tag
* n bytes for the signer's name
* x bytes for the signature
* ---------------------------------
* 27 + n + x bytes
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
REQUIRE(msg->buffer != NULL);
REQUIRE(msg->state == DNS_SECTION_ANY);
REQUIRE(msg->sig0key == NULL && msg->tsigkey == NULL);
msg->sig0key = key;
if (key != NULL) {
dns_name_toregion(dst_key_name(key), &r);
result = dst_key_sigsize(key, &x);
if (result != ISC_R_SUCCESS) {
msg->sig_reserved = 0;
return (result);
}
msg->sig_reserved = 27 + r.length + x;
result = dns_message_renderreserve(msg, msg->sig_reserved);
if (result != ISC_R_SUCCESS) {
msg->sig_reserved = 0;
return (result);