diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h index 1a325cf017..ec2186a39f 100644 --- a/lib/dns/include/dns/message.h +++ b/lib/dns/include/dns/message.h @@ -34,6 +34,8 @@ #include #include +#include + /* * How this beast works: * @@ -119,7 +121,8 @@ typedef int dns_section_t; #define DNS_SECTION_AUTHORITY 2 #define DNS_SECTION_ADDITIONAL 3 #define DNS_SECTION_TSIG 4 /* pseudo-section */ -#define DNS_SECTION_MAX 5 +#define DNS_SECTION_SIG0 5 /* pseudo-section */ +#define DNS_SECTION_MAX 6 /* * Dynamic update named for these sections. @@ -161,6 +164,8 @@ struct dns_message { unsigned int header_ok : 1; unsigned int question_ok : 1; unsigned int tcp_continuation: 1; + unsigned int response_needs_sig0: 1; + unsigned int verified_sig0: 1; unsigned int reserved; /* reserved space (render) */ @@ -186,7 +191,11 @@ struct dns_message { dns_rdata_any_tsig_t *querytsig; dns_tsigkey_t *tsigkey; void *tsigctx; - int tsigstart; + int sigstart; + + dst_key_t *sig0key; + dns_rcode_t sig0status; + isc_region_t *query; }; dns_result_t @@ -836,37 +845,43 @@ dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer); */ isc_result_t -dns_message_signer(dns_message_t *msg, dns_name_t **signer); +dns_message_signer(dns_message_t *msg, dns_name_t *signer); /* - * If this response message was signed, return the identity of the signer. + * If this message was signed, return the identity of the signer. * Unless ISC_R_NOTFOUND is returned, signer will reflect the name of the * key that signed the message. * * Requires: * - * msg be a valid response message. - * signer != NULL && *signer is NULL + * msg is a valid parsed message. + * signer is a valid name * * Returns: * * ISC_R_SUCCESS - the message was signed, and *signer * contains the signing identity * - * ISC_R_NOTFOUND - no TSIG record or key is present in the + * ISC_R_NOTFOUND - no TSIG or SIG(0) record is present in the * message * - * DNS_R_TSIGVERIFYFAILURE - the message was signed, but the signature - * failed to verify + * DNS_R_TSIGVERIFYFAILURE - the message was signed by a TSIG, but the + * signature failed to verify * - * DNS_R_TSIGERRORSET - the message was signed and verified, but - * the query was rejected by the server + * DNS_R_TSIGERRORSET - the message was signed by a TSIG and + * verified, but the query was rejected by + * the server * - * DNS_R_KEYUNAUTHORIZED - the message was signed and verified, but - * the key has no identity since it was - * generated by an unsigned TKEY process - * (new error code?) + * DNS_R_NOIDENTITY - the message was signed by a TSIG and + * verified, but the key has no identity since + * it was generated by an unsigned TKEY process + * + * DNS_R_SIGINVALID - the message was signed by a SIG(0), but + * the signature failed to verify + * + * DNS_R_SIGNOTVERIFIEDYET - the message was signed by a SIG(0), but + * the signature has not been verified yet */ ISC_LANG_ENDDECLS -#endif /* DNS_DNS_H */ +#endif /* DNS_MESSAGE_H */ diff --git a/lib/dns/message.c b/lib/dns/message.c index 58544a3824..b62cd11297 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -37,6 +37,7 @@ #include #include #include +#include #define DNS_MESSAGE_OPCODE_MASK 0x7800U #define DNS_MESSAGE_OPCODE_SHIFT 11 @@ -289,7 +290,10 @@ msginittsig(dns_message_t *m) m->tsig = m->querytsig = NULL; m->tsigkey = NULL; m->tsigctx = NULL; - m->tsigstart = -1; + m->sigstart = -1; + m->sig0key = NULL; + m->sig0status = dns_rcode_noerror; + m->query = NULL; } /* @@ -305,6 +309,8 @@ msginit(dns_message_t *m) m->header_ok = 0; m->question_ok = 0; m->tcp_continuation = 0; + m->response_needs_sig0 = 0; + m->verified_sig0 = 0; } static inline void @@ -426,16 +432,26 @@ msgreset(dns_message_t *msg, isc_boolean_t everything) 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) + if (msg->tsigkey != NULL) { dns_tsigkey_free(&msg->tsigkey); + msg->tsigkey = NULL; + } + + if (msg->query != NULL) { + isc_mem_put(msg->mctx, msg->query->base, msg->query->length); + isc_mem_put(msg->mctx, msg->query, sizeof(isc_region_t)); + msg->query = NULL; + } /* * cleanup the buffer cleanup list @@ -956,6 +972,7 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, && rdtype != dns_rdatatype_tsig && rdtype != dns_rdatatype_opt && rdtype != dns_rdatatype_key /* XXX in a TKEY query */ + && rdtype != dns_rdatatype_sig /* XXX SIG(0) */ && msg->rdclass != rdclass) return (DNS_R_FORMERR); @@ -973,7 +990,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; + msg->sigstart = recstart; skip_name_search = ISC_TRUE; skip_type_search = ISC_TRUE; } else if (rdtype == dns_rdatatype_opt) { @@ -1041,6 +1058,12 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, attributes = DNS_NAMEATTR_CNAME; else if (covers == dns_rdatatype_dname) attributes = DNS_NAMEATTR_DNAME; + else if (covers == 0) { + msg->sigstart = recstart; + section = &msg->sections[DNS_SECTION_SIG0]; + if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0) + msg->response_needs_sig0 = 1; + } } else covers = 0; @@ -1156,11 +1179,14 @@ dns_message_parse(dns_message_t *msg, isc_buffer_t *source, dns_decompress_t dctx; dns_result_t ret; isc_uint16_t tmpflags; + isc_buffer_t origsource; REQUIRE(DNS_MESSAGE_VALID(msg)); REQUIRE(source != NULL); REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE); + origsource = *source; + msg->header_ok = 0; msg->question_ok = 0; @@ -1226,6 +1252,21 @@ dns_message_parse(dns_message_t *msg, isc_buffer_t *source, if (ret != DNS_R_SUCCESS) return ret; } + else if (msg->response_needs_sig0 == 1) { + msg->query = isc_mem_get(msg->mctx, sizeof(isc_region_t)); + if (msg->query == NULL) + return (ISC_R_NOMEMORY); + isc_buffer_used(&origsource, &r); + msg->query->length = msg->sigstart; + msg->query->base = isc_mem_get(msg->mctx, msg->query->length); + if (msg->query->base == NULL) { + isc_mem_put(msg->mctx, msg->query, + sizeof(isc_region_t)); + msg->query = NULL; + return (ISC_R_NOMEMORY); + } + memcpy(msg->query->base, r.base, msg->query->length); + } return (DNS_R_SUCCESS); } @@ -1440,7 +1481,8 @@ dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) msg->counts[DNS_SECTION_ANSWER] < 65536 && msg->counts[DNS_SECTION_AUTHORITY] < 65536 && (msg->counts[DNS_SECTION_ADDITIONAL] + - msg->counts[DNS_SECTION_TSIG]) < 65536); + msg->counts[DNS_SECTION_TSIG] + + msg->counts[DNS_SECTION_SIG0]) < 65536); isc_buffer_putuint16(target, tmp); isc_buffer_putuint16(target, @@ -1450,7 +1492,8 @@ dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) isc_buffer_putuint16(target, (isc_uint16_t)msg->counts[DNS_SECTION_AUTHORITY]); tmp = msg->counts[DNS_SECTION_ADDITIONAL] - + msg->counts[DNS_SECTION_TSIG]; + + msg->counts[DNS_SECTION_TSIG] + + msg->counts[DNS_SECTION_SIG0]; isc_buffer_putuint16(target, tmp); } @@ -1501,15 +1544,20 @@ dns_message_renderend(dns_message_t *msg) return (result); } - if (msg->tsigkey != NULL || - ((msg->flags & DNS_MESSAGEFLAG_QR) != 0 && - msg->querytsigstatus != dns_rcode_noerror)) - { + if (msg->tsigkey != NULL) { result = dns_tsig_sign(msg); if (result != DNS_R_SUCCESS) return (result); - result = dns_message_rendersection(msg, DNS_SECTION_TSIG, 0, - 0); + result = dns_message_rendersection(msg, DNS_SECTION_TSIG, 0, 0); + if (result != DNS_R_SUCCESS) + return (result); + } + + else if (msg->sig0key != NULL) { + result = dns_dnssec_signmessage(msg, msg->sig0key); + if (result != DNS_R_SUCCESS) + return (result); + result = dns_message_rendersection(msg, DNS_SECTION_SIG0, 0, 0); if (result != DNS_R_SUCCESS) return (result); } @@ -1906,25 +1954,78 @@ dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) } isc_result_t -dns_message_signer(dns_message_t *msg, dns_name_t **signer) { - isc_result_t result; +dns_message_signer(dns_message_t *msg, dns_name_t *signer) { + isc_region_t r; + isc_result_t result = ISC_R_SUCCESS; REQUIRE(DNS_MESSAGE_VALID(msg)); REQUIRE(signer != NULL); - REQUIRE(*signer == NULL); - REQUIRE(msg->flags & DNS_MESSAGEFLAG_QR); + REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE); - if (msg->tsigkey == NULL || msg->tsig == NULL) + if ((msg->tsig == NULL || msg->tsigkey == NULL) && + ISC_LIST_EMPTY(msg->sections[DNS_SECTION_SIG0])) return (ISC_R_NOTFOUND); - if (msg->tsigstatus != dns_rcode_noerror) - result = DNS_R_TSIGVERIFYFAILURE; - else if (msg->tsig->error != dns_rcode_noerror) - result = DNS_R_TSIGERRORSET; - else if (msg->tsigkey->generated) - result = DNS_R_KEYUNAUTHORIZED; - else - result = ISC_R_SUCCESS; - *signer = &msg->tsigkey->name; + if (!dns_name_hasbuffer(signer)) { + isc_buffer_t *dynbuf = NULL; + result = isc_buffer_allocate(msg->mctx, &dynbuf, 512, + ISC_BUFFERTYPE_BINARY); + if (result != ISC_R_SUCCESS) + return (result); + dns_name_setbuffer(signer, dynbuf); + dns_message_takebuffer(msg, &dynbuf); + } + + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_SIG0])) { + dns_rdataset_t *dataset; + dns_rdata_t rdata; + dns_name_t *sig0name; + dns_rdata_generic_sig_t sig; + + result = dns_message_firstname(msg, DNS_SECTION_SIG0); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + sig0name = NULL; + dns_message_currentname(msg, DNS_SECTION_SIG0, &sig0name); + dataset = NULL; + result = dns_message_findtype(sig0name, dns_rdatatype_sig, 0, + &dataset); + if (result != ISC_R_SUCCESS) + return (result); + result = dns_rdataset_first(dataset); + dns_rdataset_current(dataset, &rdata); + + result = dns_rdata_tostruct(&rdata, &sig, msg->mctx); + if (result != ISC_R_SUCCESS) + return (result); + + if (msg->sig0status != dns_rcode_noerror) + result = DNS_R_SIGINVALID; + else if (msg->verified_sig0 == 0) + result = DNS_R_NOTVERIFIEDYET; + else + result = ISC_R_SUCCESS; + dns_name_toregion(&sig.signer, &r); + dns_name_fromregion(signer, &r); + dns_rdata_freestruct(&sig); + } + else { + dns_name_t *identity; + if (msg->tsigstatus != dns_rcode_noerror) + result = DNS_R_TSIGVERIFYFAILURE; + else if (msg->tsig->error != dns_rcode_noerror) + result = DNS_R_TSIGERRORSET; + else + result = ISC_R_SUCCESS; + identity = dns_tsigkey_identity(msg->tsigkey); + if (identity == NULL) { + if (result == ISC_R_SUCCESS) + result = DNS_R_NOIDENTITY; + identity = &msg->tsigkey->name; + } + dns_name_toregion(identity, &r); + dns_name_fromregion(signer, &r); + } + return (result); }