#include "util/data/dname.h"
#include "util/fptr_wlist.h"
#include "util/proxy_protocol.h"
+#include "util/tsig.h"
#include "util/tube.h"
#include "util/edns.h"
#include "util/timeval_func.h"
int rcode = LDNS_RCODE_NOERROR;
uint32_t serial = 0;
int has_serial;
+ struct tsig_data* tsig = NULL;
+ int tsig_rcode = 0;
if(!w->env.auth_zones) return;
has_serial = auth_zone_parse_notify_serial(pkt, &serial);
if(auth_zones_notify(w->env.auth_zones, &w->env, qinfo->qname,
- qinfo->qname_len, qinfo->qclass, addr,
- addrlen, has_serial, serial, &refused)) {
+ qinfo->qname_len, qinfo->qclass, addr, addrlen, has_serial,
+ serial, &refused, pkt, &tsig, &tsig_rcode, w->scratchpad)) {
rcode = LDNS_RCODE_NOERROR;
} else {
- if(refused)
+ if(tsig_rcode != 0) {
+ rcode = tsig_rcode;
+ } else if(refused) {
rcode = LDNS_RCODE_REFUSED;
- else rcode = LDNS_RCODE_SERVFAIL;
+ } else {
+ rcode = LDNS_RCODE_SERVFAIL;
+ }
}
if(verbosity >= VERB_DETAIL) {
else if(rcode == LDNS_RCODE_SERVFAIL)
snprintf(buf, sizeof(buf),
"servfail for NOTIFY %sfor %s from", sr, zname);
+ else if(rcode != LDNS_RCODE_NOERROR)
+ snprintf(buf, sizeof(buf),
+ "error for NOTIFY %sfor %s from", sr, zname);
else snprintf(buf, sizeof(buf),
"received NOTIFY %sfor %s from", sr, zname);
log_addr(VERB_DETAIL, buf, addr, addrlen);
*(uint16_t*)(void *)sldns_buffer_begin(pkt),
sldns_buffer_read_u16_at(pkt, 2), edns);
LDNS_OPCODE_SET(sldns_buffer_begin(pkt), LDNS_PACKET_NOTIFY);
+ if(tsig) {
+ size_t pos = sldns_buffer_limit(pkt);
+ sldns_buffer_clear(pkt);
+ sldns_buffer_set_position(pkt, pos);
+ if(!tsig_sign_reply(tsig, pkt, w->env.tsig_key_table,
+ (uint64_t)*w->env.now)) {
+ /* Failed to TSIG sign the reply */
+ verbose(VERB_ALGO, "Failed to TSIG sign notify reply");
+ error_encode(pkt, LDNS_RCODE_SERVFAIL, qinfo,
+ *(uint16_t*)(void *)sldns_buffer_begin(pkt),
+ sldns_buffer_read_u16_at(pkt, 2), edns);
+ LDNS_OPCODE_SET(sldns_buffer_begin(pkt), LDNS_PACKET_NOTIFY);
+ }
+ /* The tsig veriable is allocated in the scratch region. */
+ }
}
static int
auth_free_masters(xfr->task_probe->masters);
comm_point_delete(xfr->task_probe->cp);
comm_timer_delete(xfr->task_probe->timer);
+ tsig_delete(xfr->task_probe->tsig);
free(xfr->task_probe);
}
if(xfr->task_transfer) {
auth_free_masters(xfr->task_transfer->masters);
comm_point_delete(xfr->task_transfer->cp);
comm_timer_delete(xfr->task_transfer->timer);
+ tsig_delete(xfr->task_transfer->tsig);
if(xfr->task_transfer->chunks_first) {
auth_chunks_delete(xfr->task_transfer);
}
* addresses in the addr list) */
static int
addr_matches_master(struct auth_master* master, struct sockaddr_storage* addr,
- socklen_t addrlen, struct auth_master** fromhost)
+ socklen_t addrlen, struct auth_master** fromhost,
+ struct tsig_data* tsig)
{
struct sockaddr_storage a;
socklen_t alen = 0;
int net = 0;
+ if(master->tsig_key_name && master->tsig_key_name[0]) {
+ uint8_t keyname[LDNS_MAX_DOMAINLEN+1];
+ size_t keynamelen = sizeof(keyname);
+ if(!tsig) {
+ /* This needs a TSIG key, but no TSIG present. */
+ return 0;
+ }
+ if(sldns_str2wire_dname_buf(master->tsig_key_name, keyname,
+ &keynamelen) != 0) {
+ verbose(VERB_ALGO, "could not parse allow-notify-tsig '%s'",
+ master->tsig_key_name);
+ return 0;
+ }
+ if(query_dname_compare(keyname, tsig->key_name) != 0) {
+ /* The TSIG is a different key name, not matched. */
+ return 0;
+ }
+ }
if(addr_in_list(master->list, addr, addrlen)) {
*fromhost = master;
return 1;
/** check access list for notifies */
static int
az_xfr_allowed_notify(struct auth_xfer* xfr, struct sockaddr_storage* addr,
- socklen_t addrlen, struct auth_master** fromhost)
+ socklen_t addrlen, struct auth_master** fromhost,
+ struct tsig_data* tsig)
{
struct auth_master* p;
for(p=xfr->allow_notify_list; p; p=p->next) {
- if(addr_matches_master(p, addr, addrlen, fromhost)) {
+ if(addr_matches_master(p, addr, addrlen, fromhost, tsig)) {
return 1;
}
}
int auth_zones_notify(struct auth_zones* az, struct module_env* env,
uint8_t* nm, size_t nmlen, uint16_t dclass,
struct sockaddr_storage* addr, socklen_t addrlen, int has_serial,
- uint32_t serial, int* refused)
+ uint32_t serial, int* refused, struct sldns_buffer* pkt,
+ struct tsig_data** tsig, int* tsig_rcode, struct regional* scratchpad)
{
struct auth_xfer* xfr;
struct auth_master* fromhost = NULL;
}
lock_basic_lock(&xfr->lock);
lock_rw_unlock(&az->lock);
-
+
+ /* check tsig */
+ if(tsig_in_packet(pkt)) {
+ *tsig_rcode = tsig_parse_verify_query(env->tsig_key_table,
+ pkt, tsig, scratchpad, (uint64_t)*env->now);
+ if(*tsig_rcode != 0) {
+ /* The tsig failed to verify. */
+ lock_basic_unlock(&xfr->lock);
+ return 0;
+ }
+ }
+
/* check access list for notifies */
- if(!az_xfr_allowed_notify(xfr, addr, addrlen, &fromhost)) {
+ if(!az_xfr_allowed_notify(xfr, addr, addrlen, &fromhost, *tsig)) {
lock_basic_unlock(&xfr->lock);
/* notify not allowed, refuse the notify */
*refused = 1;
sldns_buffer_write_u16_at(buf, 0, id);
}
+/** sign a query for xfr. */
+static int
+xfr_sign_query(struct tsig_data** tsig, sldns_buffer* pkt,
+ struct module_env* env, char* tsig_key_name)
+{
+ size_t pos;
+ if(*tsig) {
+ tsig_delete(*tsig);
+ *tsig = NULL;
+ }
+ *tsig = tsig_create_fromstr(env->tsig_key_table, tsig_key_name);
+ if(!*tsig) {
+ log_err("tsig key '%s' not found or out of memory",
+ tsig_key_name);
+ return 0;
+ }
+
+ /* Position the buffer after the packet contents. */
+ pos = sldns_buffer_limit(pkt);
+ sldns_buffer_clear(pkt);
+ sldns_buffer_set_position(pkt, pos);
+ if(!tsig_sign_query(*tsig, pkt, env->tsig_key_table,
+ (uint64_t)*env->now)) {
+ sldns_buffer_flip(pkt);
+ log_err("tsig key '%s': could not sign query", tsig_key_name);
+ return 0;
+ }
+ sldns_buffer_flip(pkt);
+ return 1;
+}
+
/** create IXFR/AXFR packet for xfr */
static void
xfr_create_ixfr_packet(struct auth_xfer* xfr, sldns_buffer* buf, uint16_t id,
/** check if returned packet is OK */
static int
check_packet_ok(sldns_buffer* pkt, uint16_t qtype, struct auth_xfer* xfr,
- uint32_t* serial)
+ uint32_t* serial, struct module_env* env)
{
/* parse to see if packet worked, valid reply */
return 0;
*serial = sldns_buffer_read_u32(pkt);
}
+
+ if(xfr->task_probe->tsig) {
+ /* There could be authority or additional RRs in the reply for the
+ * SOA query, if so skip them by tsig_find_rr. */
+ if(!tsig_find_rr(pkt)) {
+ verbose(VERB_ALGO, "TSIG expected, but not found in reply");
+ return 0;
+ }
+ if(!tsig_parse_verify_reply(xfr->task_probe->tsig, pkt,
+ env->tsig_key_table, (uint64_t)*env->now)) {
+ verbose(VERB_ALGO, "valid TSIG expected in SOA probe reply, but it was not valid");
+ return 0;
+ }
+ }
return 1;
}
/* remove the commpoint */
comm_point_delete(xfr->task_transfer->cp);
xfr->task_transfer->cp = NULL;
+ /* remove the tsig data */
+ tsig_delete(xfr->task_transfer->tsig);
+ xfr->task_transfer->tsig = NULL;
/* we don't own this item anymore */
xfr->task_transfer->worker = NULL;
xfr->task_transfer->env = NULL;
xfr->task_transfer->id = GET_RANDOM_ID(env->rnd);
xfr_create_ixfr_packet(xfr, env->scratch_buffer,
xfr->task_transfer->id, master);
+ if(master->tsig_key_name) {
+ if(!xfr_sign_query(&xfr->task_transfer->tsig,
+ env->scratch_buffer, env, master->tsig_key_name)) {
+ char zname[LDNS_MAX_DOMAINLEN], as[256];
+ dname_str(xfr->name, zname);
+ addr_port_to_str(&addr, addrlen, as, sizeof(as));
+ verbose(VERB_ALGO, "failed to TSIG sign xfr "
+ "for %s to %s", zname, as);
+ return 0;
+ }
+ }
/* connect on fd */
xfr->task_transfer->cp = outnet_comm_point_for_tcp(env->outnet,
*/
static int
check_xfer_packet(sldns_buffer* pkt, struct auth_xfer* xfr,
- int* gonextonfail, int* transferdone)
+ struct module_env* env, int* gonextonfail, int* transferdone)
{
uint8_t* wire = sldns_buffer_begin(pkt);
int i;
xfr->task_transfer->master->host);
return 0;
}
+ /* check tsig */
+ if(xfr->task_transfer->tsig) {
+ if(xfr->task_transfer->rr_scan_num == 0) {
+ /* Check TSIG reply on first packet. */
+ if(!tsig_find_rr(pkt)) {
+ verbose(VERB_ALGO, "TSIG expected, but not found in reply for xfr to %s",
+ xfr->task_transfer->master->host);
+ return 0;
+ }
+ if(!tsig_parse_verify_reply(xfr->task_transfer->tsig,
+ pkt, env->tsig_key_table,
+ (uint64_t)*env->now)) {
+ verbose(VERB_ALGO, "valid TSIG expected in xfr reply to %s, but it was not valid",
+ xfr->task_transfer->master->host);
+ return 0;
+ }
+ }
+ sldns_buffer_rewind(pkt);
+ }
if(LDNS_RCODE_WIRE(wire) != LDNS_RCODE_NOERROR) {
char rcode[32];
sldns_wire2str_rcode_buf((int)LDNS_RCODE_WIRE(wire), rcode,
/* handle returned packet */
/* if it fails, cleanup and end this transfer */
/* if it needs to fallback from IXFR to AXFR, do that */
- if(!check_xfer_packet(c->buffer, xfr, &gonextonfail, &transferdone)) {
+ if(!check_xfer_packet(c->buffer, xfr, env, &gonextonfail,
+ &transferdone)) {
goto failed;
}
/* if it is good, link it into the list of data */
/* remove the commpoint */
comm_point_delete(xfr->task_probe->cp);
xfr->task_probe->cp = NULL;
+ /* remove the tsig data */
+ tsig_delete(xfr->task_probe->tsig);
+ xfr->task_probe->tsig = NULL;
/* we don't own this item anymore */
xfr->task_probe->worker = NULL;
xfr->task_probe->env = NULL;
xfr->task_probe->id = GET_RANDOM_ID(env->rnd);
xfr_create_soa_probe_packet(xfr, env->scratch_buffer,
xfr->task_probe->id);
+ if(master->tsig_key_name) {
+ if(!xfr_sign_query(&xfr->task_probe->tsig, env->scratch_buffer,
+ env, master->tsig_key_name)) {
+ char zname[LDNS_MAX_DOMAINLEN], as[256];
+ dname_str(xfr->name, zname);
+ addr_port_to_str(&addr, addrlen, as, sizeof(as));
+ verbose(VERB_ALGO, "failed to TSIG sign soa probe "
+ "for %s to %s", zname, as);
+ return 0;
+ }
+ }
/* we need to remove the cp if we have a different ip4/ip6 type now */
if(xfr->task_probe->cp &&
((xfr->task_probe->cp_is_ip6 && !addr_is_ip6(&addr, addrlen)) ||
if(err == NETEVENT_NOERROR) {
uint32_t serial = 0;
if(check_packet_ok(c->buffer, LDNS_RR_TYPE_SOA, xfr,
- &serial)) {
+ &serial, env)) {
/* successful lookup */
if(verbosity >= VERB_ALGO) {
char buf[LDNS_MAX_DOMAINLEN];
/* delete commpoint so a new one is created, with a fresh port nr */
comm_point_delete(xfr->task_probe->cp);
xfr->task_probe->cp = NULL;
+ /* remove the tsig data */
+ tsig_delete(xfr->task_probe->tsig);
+ xfr->task_probe->tsig = NULL;
/* if the result was not a successful probe, we need
* to send the next one */