Commit 69030415 authored by Jan Kadlec's avatar Jan Kadlec

ddns: TSIG for DDNS responses.

parent 0670c11e
......@@ -164,63 +164,6 @@ static int query_chaos(knot_pkt_t *pkt, knot_process_t *ctx)
return NS_PROC_DONE;
}
static int process_query_sign_response(knot_pkt_t *pkt, struct query_data *qdata)
{
if (pkt->size == 0) {
// Nothing to sign.
return KNOT_EOK;
}
int ret = KNOT_EOK;
knot_pkt_t *query = qdata->query;
knot_sign_context_t *ctx = &qdata->sign;
/* KEY provided and verified TSIG or BADTIME allows signing. */
if (ctx->tsig_key != NULL && knot_tsig_can_sign(qdata->rcode_tsig)) {
/* Sign query response. */
dbg_ns("%s: signing response using key %p\n", __func__, ctx->tsig_key);
size_t new_digest_len = knot_tsig_digest_length(ctx->tsig_key->algorithm);
if (ctx->pkt_count == 0) {
ret = knot_tsig_sign(pkt->wire, &pkt->size, pkt->max_size,
ctx->tsig_digest, ctx->tsig_digestlen,
ctx->tsig_digest, &new_digest_len,
ctx->tsig_key, qdata->rcode_tsig,
ctx->tsig_time_signed);
} else {
ret = knot_tsig_sign_next(pkt->wire, &pkt->size, pkt->max_size,
ctx->tsig_digest, ctx->tsig_digestlen,
ctx->tsig_digest, &new_digest_len,
ctx->tsig_key,
pkt->wire, pkt->size);
}
if (ret != KNOT_EOK) {
goto fail; /* Failed to sign. */
} else {
++ctx->pkt_count;
}
} else {
/* Copy TSIG from query and set RCODE. */
if (query->tsig_rr && qdata->rcode_tsig != KNOT_RCODE_NOERROR) {
dbg_ns("%s: appending original TSIG\n", __func__);
ret = knot_tsig_add(pkt->wire, &pkt->size, pkt->max_size,
qdata->rcode_tsig, query->tsig_rr);
if (ret != KNOT_EOK) {
goto fail; /* Whatever it is, it's server fail. */
}
}
}
return ret;
/* Server failure in signing. */
fail:
dbg_ns("%s: signing failed (%s)\n", __func__, knot_strerror(ret));
qdata->rcode = KNOT_RCODE_SERVFAIL;
qdata->rcode_tsig = KNOT_RCODE_NOERROR; /* Don't sign again. */
return ret;
}
/*! \brief Find zone for given question. */
static const zone_t *answer_zone_find(const knot_pkt_t *query, knot_zonedb_t *zonedb)
{
......@@ -663,6 +606,63 @@ int process_query_verify(struct query_data *qdata)
return ret;
}
int process_query_sign_response(knot_pkt_t *pkt, struct query_data *qdata)
{
if (pkt->size == 0) {
// Nothing to sign.
return KNOT_EOK;
}
int ret = KNOT_EOK;
knot_pkt_t *query = qdata->query;
knot_sign_context_t *ctx = &qdata->sign;
/* KEY provided and verified TSIG or BADTIME allows signing. */
if (ctx->tsig_key != NULL && knot_tsig_can_sign(qdata->rcode_tsig)) {
/* Sign query response. */
dbg_ns("%s: signing response using key %p\n", __func__, ctx->tsig_key);
size_t new_digest_len = knot_tsig_digest_length(ctx->tsig_key->algorithm);
if (ctx->pkt_count == 0) {
ret = knot_tsig_sign(pkt->wire, &pkt->size, pkt->max_size,
ctx->tsig_digest, ctx->tsig_digestlen,
ctx->tsig_digest, &new_digest_len,
ctx->tsig_key, qdata->rcode_tsig,
ctx->tsig_time_signed);
} else {
ret = knot_tsig_sign_next(pkt->wire, &pkt->size, pkt->max_size,
ctx->tsig_digest, ctx->tsig_digestlen,
ctx->tsig_digest, &new_digest_len,
ctx->tsig_key,
pkt->wire, pkt->size);
}
if (ret != KNOT_EOK) {
goto fail; /* Failed to sign. */
} else {
++ctx->pkt_count;
}
} else {
/* Copy TSIG from query and set RCODE. */
if (query->tsig_rr && qdata->rcode_tsig != KNOT_RCODE_NOERROR) {
dbg_ns("%s: appending original TSIG\n", __func__);
ret = knot_tsig_add(pkt->wire, &pkt->size, pkt->max_size,
qdata->rcode_tsig, query->tsig_rr);
if (ret != KNOT_EOK) {
goto fail; /* Whatever it is, it's server fail. */
}
}
}
return ret;
/* Server failure in signing. */
fail:
dbg_ns("%s: signing failed (%s)\n", __func__, knot_strerror(ret));
qdata->rcode = KNOT_RCODE_SERVFAIL;
qdata->rcode_tsig = KNOT_RCODE_NOERROR; /* Don't sign again. */
return ret;
}
/*! \brief Module implementation. */
static const knot_process_module_t PROCESS_QUERY_MODULE = {
&process_query_begin,
......
......@@ -132,4 +132,14 @@ bool process_query_acl_check(list_t *acl, struct query_data *qdata);
*/
int process_query_verify(struct query_data *qdata);
/*!
* \brief Sign current query using configured TSIG keys.
*
* \param pkt Outgoing message.
* \param qdata Query data.
*
* \retval KNOT_E*
*/
int process_query_sign_response(knot_pkt_t *pkt, struct query_data *qdata);
/*! @} */
......@@ -32,11 +32,12 @@ struct requestor {
mm_ctx_t *mm; /*!< Memory context. */
};
/*! \brief Request data (socket, payload and endpoints). */
/*! \brief Request data (socket, payload, response, TSIG and endpoints). */
struct request_data {
node_t node;
int fd;
struct sockaddr_storage remote, origin;
knot_sign_context_t sign;
knot_pkt_t *query;
knot_pkt_t *resp;
};
......
......@@ -37,6 +37,16 @@
#define UPDATE_LOG(severity, msg...) \
QUERY_LOG(severity, qdata, "DDNS", msg)
/* Creates query_data structure from DDNS request data. */
#define MOCK_QDATA(req, zone) \
struct process_query_param _param = { 0 }; \
_param.remote = &req->remote; \
struct query_data qdata = { 0 }; \
qdata.param = &_param; \
qdata.query = req->query; \
qdata.zone = zone; \
qdata.sign = req->sign;
static bool apex_rr_changed(const zone_contents_t *old_contents,
const zone_contents_t *new_contents,
uint16_t type)
......@@ -154,14 +164,13 @@ static int process_bulk(zone_t *zone, list_t *requests, changeset_t *ddns_ch)
// Walk all the requests and process.
struct request_data *req;
WALK_LIST(req, *requests) {
// Init qdata structure for logging (unique per-request).
struct process_query_param param = { 0 };
param.remote = &req->remote;
struct query_data qdata = { 0 };
qdata.param = &param;
qdata.query = req->query;
qdata.zone = zone;
if (knot_wire_get_rcode(req->resp->wire) != KNOT_RCODE_NOERROR) {
// Skip requests that failed ACL check.
continue;
}
// Init qdata structure for logging (unique per-request).
MOCK_QDATA(req, zone);
int ret = check_prereqs(req, zone, &zone_update, &qdata);
if (ret != KNOT_EOK) {
// Skip updates with failed prereqs.
......@@ -374,7 +383,7 @@ static void forward_requests(zone_t *zone, list_t *requests)
}
}
static int init_update_respones(list_t *updates)
static int init_update_responses(const zone_t *zone, list_t *updates)
{
struct request_data *r = NULL;
WALK_LIST(r, *updates) {
......@@ -385,16 +394,40 @@ static int init_update_respones(list_t *updates)
assert(r->query);
knot_pkt_init_response(r->resp, r->query);
if (zone_master(zone)) {
// Don't check TSIG for forwards.
continue;
}
// Check that ACL is still valid.
MOCK_QDATA(r, zone);
if (!process_query_acl_check(&zone->conf->acl.update_in, &qdata)) {
knot_wire_set_rcode(r->resp->wire, qdata.rcode);
} else {
// Check TSIG validity.
int ret = process_query_verify(&qdata);
if (ret != KNOT_EOK) {
knot_wire_set_rcode(r->resp->wire, qdata.rcode);
}
}
r->sign = qdata.sign;
}
return KNOT_EOK;
}
static void send_update_responses(list_t *updates)
static void send_update_responses(const zone_t *zone, list_t *updates)
{
struct request_data *r, *nxt;
WALK_LIST_DELSAFE(r, nxt, *updates) {
if (r->resp) {
if (!zone_master(zone)) {
// Sign the response with TSIG where applicable
MOCK_QDATA(r, zone);
(void)process_query_sign_response(r->resp, &qdata);
}
if (net_is_connected(r->fd)) {
tcp_send_msg(r->fd, r->resp->wire, r->resp->size);
} else {
......@@ -446,11 +479,11 @@ int updates_execute(zone_t *zone)
}
/* Init updates respones. */
int ret = init_update_respones(&updates);
int ret = init_update_responses(zone, &updates);
if (ret != KNOT_EOK) {
/* Send what responses we can. */
set_rcodes(&updates, KNOT_RCODE_SERVFAIL);
send_update_responses(&updates);
send_update_responses(zone, &updates);
return ret;
}
......@@ -467,7 +500,7 @@ int updates_execute(zone_t *zone)
UNUSED(ret); /* Don't care about the Knot code, RCODEs are set. */
/* Send responses. */
send_update_responses(&updates);
send_update_responses(zone, &updates);
return KNOT_EOK;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment