diff --git a/Knot.files b/Knot.files index b60215cb63c396049aa8bf6c6305cb345be984c0..66d882fb000232b9068d4f214098be48a010093e 100644 --- a/Knot.files +++ b/Knot.files @@ -127,8 +127,8 @@ src/knot/nameserver/query_module.c src/knot/nameserver/query_module.h src/knot/nameserver/requestor.c src/knot/nameserver/requestor.h -src/knot/nameserver/requestor_tsig.c -src/knot/nameserver/requestor_tsig.h +src/knot/nameserver/tsig_ctx.c +src/knot/nameserver/tsig_ctx.h src/knot/nameserver/update.c src/knot/nameserver/update.h src/knot/other/debug.h diff --git a/src/Makefile.am b/src/Makefile.am index d3ce53a3bede5e07458910cbb5ef98dca26cad14..e61a2e18048f9fb18bd873a57d4288d04afec34a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -240,14 +240,14 @@ libknotd_la_SOURCES = \ knot/nameserver/process_answer.h \ knot/nameserver/requestor.c \ knot/nameserver/requestor.h \ - knot/nameserver/requestor_tsig.c \ - knot/nameserver/requestor_tsig.h \ knot/nameserver/query_module.c \ knot/nameserver/query_module.h \ knot/nameserver/update.c \ knot/nameserver/update.h \ knot/nameserver/notify.c \ knot/nameserver/notify.h \ + knot/nameserver/tsig_ctx.c \ + knot/nameserver/tsig_ctx.h \ knot/modules/synth_record.c \ knot/modules/synth_record.h \ knot/other/debug.h \ diff --git a/src/knot/nameserver/axfr.c b/src/knot/nameserver/axfr.c index da336d6cb14239bdb6077d8f1bdc766aad3f3cf9..57d4a25199e7d44274ce76c98c7eadbbf1c46163 100644 --- a/src/knot/nameserver/axfr.c +++ b/src/knot/nameserver/axfr.c @@ -318,10 +318,14 @@ int axfr_process_answer(knot_pkt_t *pkt, struct answer_data *data) /* Initialize processing with first packet. */ int ret = KNOT_EOK; if (data->ext == NULL) { + NS_NEED_TSIG_SIGNED(&data->param->tsig_ctx, 0); + ret = axfr_answer_init(data); if (ret != KNOT_EOK) { return NS_PROC_FAIL; } + } else { + NS_NEED_TSIG_SIGNED(&data->param->tsig_ctx, 100); } /* Process answer packet. */ diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c index e27274618d0e36d6fb7225023fbc5f547a577114..d1f2c1c66dc3d070cbb6c007d00f805265a63e3f 100644 --- a/src/knot/nameserver/internet.c +++ b/src/knot/nameserver/internet.c @@ -899,6 +899,8 @@ int internet_process_answer(knot_pkt_t *pkt, struct answer_data *data) return NS_PROC_FAIL; } + NS_NEED_TSIG_SIGNED(&data->param->tsig_ctx, 0); + /* As of now, server can only issue SOA queries. */ switch(knot_pkt_qtype(pkt)) { case KNOT_RRTYPE_SOA: diff --git a/src/knot/nameserver/internet.h b/src/knot/nameserver/internet.h index 6a74d124cdd8e1f5d070347e73c881b19367d79c..fe785a9e9dbb8c6e1b2c3710f4d14ff58bd96492 100644 --- a/src/knot/nameserver/internet.h +++ b/src/knot/nameserver/internet.h @@ -127,6 +127,11 @@ int ns_put_rr(knot_pkt_t *pkt, const knot_rrset_t *rr, } \ } +#define NS_NEED_TSIG_SIGNED(tsig_ctx, max_unsigned) \ + if (tsig_unsigned_count(tsig_ctx) > max_unsigned) { \ + return NS_PROC_FAIL; \ + } + #endif /* _KNOT_INTERNET_H_ */ /*! @} */ diff --git a/src/knot/nameserver/notify.c b/src/knot/nameserver/notify.c index bd51cb23fc3c42ce90ceabae03f3a779334e002a..acddf4f7cf11cdd857541bf609694f3a2d685bf1 100644 --- a/src/knot/nameserver/notify.c +++ b/src/knot/nameserver/notify.c @@ -33,6 +33,8 @@ #include "knot/nameserver/internet.h" #include "common/debug.h" #include "knot/nameserver/process_query.h" +#include "knot/nameserver/tsig_ctx.h" +#include "knot/nameserver/process_answer.h" #include "libknot/dnssec/random.h" #include "libknot/rdata/soa.h" @@ -84,5 +86,7 @@ int notify_query(knot_pkt_t *pkt, struct query_data *qdata) int notify_process_answer(knot_pkt_t *pkt, struct answer_data *data) { + NS_NEED_TSIG_SIGNED(&data->param->tsig_ctx, 0); + return NS_PROC_DONE; /* No processing. */ } diff --git a/src/knot/nameserver/process_answer.c b/src/knot/nameserver/process_answer.c index 93be7568201f17fb8e5630c9712390d59b87b90d..692b3d30b854d54614e4325fd71768e4ad50e8d6 100644 --- a/src/knot/nameserver/process_answer.c +++ b/src/knot/nameserver/process_answer.c @@ -103,6 +103,12 @@ static int process_answer(knot_pkt_t *pkt, knot_process_t *ctx) /* Class specific answer processing. */ ANSWER_REQUIRES(knot_pkt_qclass(pkt) == KNOT_CLASS_IN, NS_PROC_NOOP); + /* Verify incoming packet. */ + int ret = tsig_verify_packet(&data->param->tsig_ctx, pkt); + if (ret != KNOT_EOK) { + return NS_PROC_FAIL; + } + /* Call appropriate processing handler. */ int next_state = NS_PROC_NOOP; switch(knot_pkt_type(pkt)) { diff --git a/src/knot/nameserver/process_answer.h b/src/knot/nameserver/process_answer.h index 4d751dacb6ef7160f7bf7773c8d3fc93954ce10e..ec4495d15f48dffbbb60adcec9d956fb8c9aca62 100644 --- a/src/knot/nameserver/process_answer.h +++ b/src/knot/nameserver/process_answer.h @@ -17,6 +17,7 @@ #pragma once #include "knot/nameserver/process_query.h" +#include "knot/nameserver/tsig_ctx.h" /* Query processing module implementation. */ const knot_process_module_t *process_answer_get_module(void); @@ -35,6 +36,7 @@ struct process_answer_param { zone_t *zone; const knot_pkt_t *query; const struct sockaddr_storage *remote; + tsig_ctx_t tsig_ctx; }; struct answer_data diff --git a/src/knot/nameserver/requestor.c b/src/knot/nameserver/requestor.c index fc6eae93d4def610abe558818d0b21524ac03d69..453e8d30ec1c6dd74cd11fd374d773a7825cf82f 100644 --- a/src/knot/nameserver/requestor.c +++ b/src/knot/nameserver/requestor.c @@ -55,7 +55,6 @@ static void request_close(mm_ctx_t *mm, struct request *request) knot_process_finish(&request->process); rem_node(&request->data.node); - requestor_tsig_cleanup(&request->data.tsig_ctx); close(request->data.fd); knot_pkt_free(&request->data.query); mm_free(mm, request->pkt_buf); @@ -155,11 +154,12 @@ struct request *requestor_make(struct requestor *requestor, return NULL; } + memcpy(&request->data.origin, &remote->via, sizeof(remote->via)); + memcpy(&request->data.remote, &remote->addr, sizeof(remote->addr)); + request->state = NS_PROC_DONE; - request->data.remote = remote; request->data.fd = -1; request->data.query = query; - requestor_tsig_init(&request->data.tsig_ctx, remote->key); return request; } @@ -170,8 +170,8 @@ int requestor_enqueue(struct requestor *requestor, struct request * request, voi } /* Fetch a bound socket. */ - int fd = net_connected_socket(SOCK_STREAM, &request->data.remote->addr, - &request->data.remote->via, O_NONBLOCK); + int fd = net_connected_socket(SOCK_STREAM, &request->data.remote, + &request->data.origin, O_NONBLOCK); if (fd < 0) { return KNOT_ECONN; } @@ -199,39 +199,12 @@ int requestor_dequeue(struct requestor *requestor) return KNOT_EOK; } -/*! - * \brief Sign outbound packet using TSIG. - */ -static int request_sign(struct request *request) -{ - assert(request); - - return requestor_tsig_sign_packet(&request->data.tsig_ctx, - request->data.query); -} - -/*! - * \brief Check inbound packet TSIG signature. - */ -static int request_verify(struct request *request) -{ - assert(request); - - return requestor_tsig_verify_packet(&request->data.tsig_ctx, - request->data.query); -} - static int exec_request(struct request *last, struct timeval *timeout) { int ret = KNOT_EOK; /* Process any pending data. */ if (last->state == NS_PROC_FULL) { - ret = request_sign(last); - if (ret != KNOT_EOK) { - return ret; - } - ret = request_send(last, timeout); if (ret != KNOT_EOK) { return ret; @@ -246,11 +219,6 @@ static int exec_request(struct request *last, struct timeval *timeout) return rcvd; } - ret = request_verify(last); - if (ret != KNOT_EOK) { - return ret; - } - last->state = knot_process_in(last->pkt_buf, rcvd, &last->process); if (last->state == NS_PROC_FAIL) { return KNOT_EMALF; diff --git a/src/knot/nameserver/requestor.h b/src/knot/nameserver/requestor.h index 1f1ba2d5ef7d298f988633db430f723c70a9b82e..e849a3d03454a0fe5488e569f503230d0094137e 100644 --- a/src/knot/nameserver/requestor.h +++ b/src/knot/nameserver/requestor.h @@ -18,7 +18,6 @@ #include "knot/nameserver/process_query.h" #include "knot/nameserver/process_answer.h" -#include "knot/nameserver/requestor_tsig.h" #include "common/lists.h" struct request; @@ -41,9 +40,8 @@ struct requestor { struct request_data { node_t node; int fd; - const conf_iface_t *remote; + struct sockaddr_storage remote, origin; knot_pkt_t *query; - requestor_tsig_ctx_t tsig_ctx; }; /*! diff --git a/src/knot/nameserver/requestor_tsig.c b/src/knot/nameserver/tsig_ctx.c similarity index 57% rename from src/knot/nameserver/requestor_tsig.c rename to src/knot/nameserver/tsig_ctx.c index 5ad8f2eabf56b3dbbb966e45d900a58de9765b5d..9f2c6dae45109b7f9591589c545c0e930d4ccb62 100644 --- a/src/knot/nameserver/requestor_tsig.c +++ b/src/knot/nameserver/tsig_ctx.c @@ -21,9 +21,9 @@ #include "common/errcode.h" #include "libknot/rdata/tsig.h" #include "libknot/tsig-op.h" -#include "knot/nameserver/requestor_tsig.h" +#include "knot/nameserver/tsig_ctx.h" -void requestor_tsig_init(requestor_tsig_ctx_t *ctx, const knot_tsig_key_t *key) +void tsig_init(tsig_ctx_t *ctx, const knot_tsig_key_t *key) { if (!ctx) { return; @@ -31,19 +31,12 @@ void requestor_tsig_init(requestor_tsig_ctx_t *ctx, const knot_tsig_key_t *key) memset(ctx, 0, sizeof(*ctx)); ctx->key = key; -} -void requestor_tsig_cleanup(requestor_tsig_ctx_t *ctx) -{ - if (!ctx) { - return; - } - free(ctx->digest); - memset(ctx, 0, sizeof(*ctx)); + } -int requestor_tsig_sign_packet(requestor_tsig_ctx_t *ctx, knot_pkt_t *packet) +int tsig_sign_packet(tsig_ctx_t *ctx, knot_pkt_t *packet) { if (!ctx || !packet) { return KNOT_EINVAL; @@ -54,14 +47,8 @@ int requestor_tsig_sign_packet(requestor_tsig_ctx_t *ctx, knot_pkt_t *packet) } int ret = KNOT_ERROR; - if (ctx->digest_size == 0) { ctx->digest_size = knot_tsig_digest_length(ctx->key->algorithm); - ctx->digest = malloc(ctx->digest_size); - if (!ctx->digest) { - return KNOT_ENOMEM; - } - ret = knot_tsig_sign(packet->wire, &packet->size, packet->max_size, NULL, 0, ctx->digest, &ctx->digest_size, @@ -79,7 +66,23 @@ int requestor_tsig_sign_packet(requestor_tsig_ctx_t *ctx, knot_pkt_t *packet) return ret; } -int requestor_tsig_verify_packet(requestor_tsig_ctx_t *ctx, knot_pkt_t *packet) +static int update_ctx_after_verify(tsig_ctx_t *ctx, knot_rrset_t *tsig_rr) +{ + assert(ctx); + assert(tsig_rr); + + if (ctx->digest_size != tsig_rdata_mac_length(tsig_rr)) { + return KNOT_EMALF; + } + + memcpy(ctx->digest, tsig_rdata_mac(tsig_rr), ctx->digest_size); + ctx->last_signed = tsig_rdata_time_signed(tsig_rr); + ctx->unsigned_count = 0; + + return KNOT_EOK; +} + +int tsig_verify_packet(tsig_ctx_t *ctx, knot_pkt_t *packet) { if (!ctx || !packet) { return KNOT_EINVAL; @@ -89,7 +92,40 @@ int requestor_tsig_verify_packet(requestor_tsig_ctx_t *ctx, knot_pkt_t *packet) return KNOT_EOK; } - #warning "TODO: TSIG verify invocation." - //return KNOT_ENOTSUP; + if (packet->tsig_rr == NULL) { + ctx->unsigned_count += 1; + return KNOT_EOK; + } + + int ret = KNOT_ERROR; + if (ctx->last_signed == 0) { + ret = knot_tsig_client_check(packet->tsig_rr, packet->wire, + packet->size, ctx->digest, + ctx->digest_size, ctx->key, 0); + } else { + ret = knot_tsig_client_check_next(packet->tsig_rr, packet->wire, + packet->size, ctx->digest, + ctx->digest_size, ctx->key, + ctx->last_signed); + } + + if (ret != KNOT_EOK) { + return ret; + } + + ret = update_ctx_after_verify(ctx, packet->tsig_rr); + if (ret != KNOT_EOK) { + return ret; + } + return KNOT_EOK; } + +unsigned tsig_unsigned_count(tsig_ctx_t *ctx) +{ + if (!ctx) { + return -1; + } + + return ctx->unsigned_count; +} diff --git a/src/knot/nameserver/requestor_tsig.h b/src/knot/nameserver/tsig_ctx.h similarity index 64% rename from src/knot/nameserver/requestor_tsig.h rename to src/knot/nameserver/tsig_ctx.h index cb2ea25c934bfa6bc3b6e05e0bcb60b7678371cd..cba3693c9317388fe1e52ac1764ecc2617c894f1 100644 --- a/src/knot/nameserver/requestor_tsig.h +++ b/src/knot/nameserver/tsig_ctx.h @@ -21,17 +21,26 @@ #include "libknot/packet/pkt.h" #include "libknot/rdata/tsig.h" +#include "libknot/tsig-op.h" -typedef struct requestor_tsig_ctx { +#define TSIG_MAX_DIGEST_SIZE 64 + +typedef struct tsig_ctx { const knot_tsig_key_t *key; - uint8_t *digest; + uint8_t digest[TSIG_MAX_DIGEST_SIZE]; size_t digest_size; -} requestor_tsig_ctx_t; -void requestor_tsig_init(requestor_tsig_ctx_t *ctx, const knot_tsig_key_t *key); + uint64_t last_signed; + unsigned unsigned_count; +} tsig_ctx_t; + +void tsig_init(tsig_ctx_t *ctx, const knot_tsig_key_t *key); -void requestor_tsig_cleanup(requestor_tsig_ctx_t *ctx); +int tsig_sign_packet(tsig_ctx_t *ctx, knot_pkt_t *packet); -int requestor_tsig_sign_packet(requestor_tsig_ctx_t *ctx, knot_pkt_t *packet); +int tsig_verify_packet(tsig_ctx_t *ctx, knot_pkt_t *packet); -int requestor_tsig_verify_packet(requestor_tsig_ctx_t *ctx, knot_pkt_t *packet); +/*! + * \brief Get number of unsigned packets since the last signed one. + */ +unsigned tsig_unsigned_count(tsig_ctx_t *ctx); diff --git a/src/knot/zone/events.c b/src/knot/zone/events.c index 6aea40834e199109085d82e620913f8d7d30f4d1..2e0e35d38e772f9a82ed383c4c9c1c9bf80ec7d0 100644 --- a/src/knot/zone/events.c +++ b/src/knot/zone/events.c @@ -34,6 +34,7 @@ #include "knot/nameserver/update.h" #include "knot/nameserver/notify.h" #include "knot/nameserver/requestor.h" +#include "knot/nameserver/tsig_ctx.h" #include "knot/nameserver/process_answer.h" /* ------------------------- bootstrap timer logic -------------------------- */ @@ -108,6 +109,19 @@ static int zone_query_execute(zone_t *zone, uint16_t pkt_type, const conf_iface_ knot_pkt_put(query, COMPR_HINT_QNAME, &soa_rr, 0); } + /* Answer processing parameters. */ + struct process_answer_param param = { 0 }; + param.zone = zone; + param.query = query; + param.remote = &remote->addr; + tsig_init(¶m.tsig_ctx, remote->key); + + ret = tsig_sign_packet(¶m.tsig_ctx, query); + if (ret != KNOT_EOK) { + mp_delete(mm.ctx); + return ret; + } + /* Create requestor instance. */ struct requestor re; requestor_init(&re, NS_PROC_ANSWER, &mm); @@ -115,13 +129,10 @@ static int zone_query_execute(zone_t *zone, uint16_t pkt_type, const conf_iface_ /* Create a request. */ struct request *req = requestor_make(&re, remote, query); if (req == NULL) { + mp_delete(mm.ctx); return KNOT_ENOMEM; } - struct process_answer_param param; - param.zone = zone; - param.query = query; - param.remote = &remote->addr; requestor_enqueue(&re, req, ¶m); /* Send the queries and process responses. */ @@ -330,7 +341,7 @@ static int event_update(zone_t *zone) /* Create minimal query data context. */ struct process_query_param param = {0}; - param.remote = &update->remote->addr; + param.remote = &update->remote; struct query_data qdata = {0}; qdata.param = ¶m; qdata.query = update->query;