diff --git a/samples/knot.full.conf b/samples/knot.full.conf index 5d66be9b6378848efd7206c68ea0360ad6f06a67..609b23187f7c6fb1260b9bbcba2e6d14c417183f 100644 --- a/samples/knot.full.conf +++ b/samples/knot.full.conf @@ -132,6 +132,21 @@ remotes { } } +# Section 'control' specifies on which interface to listen for RC commands +control { + + # Specifies interface, syntax is exactly the same as in 'interfaces' section + # Note: as of now, it is possible replay commands in a short time frame + # with MitM type attacks, so you should keep the interface on localnet. + # Default port is: 5553 + listen-on { address 127.0.0.1@5553; } + + # Specifies ACL list for remote control + # Same syntax as for ACLs in zones + # List of remotes delimited by comma + allow server0; +} + # Section 'zones' contains information about zones to be served. zones { diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y index 082dde37466448da9990e3659ffebc844507713a..8833073986ae6d1f17e53d32babbcc8925ad6a71 100644 --- a/src/knot/conf/cf-parse.y +++ b/src/knot/conf/cf-parse.y @@ -15,7 +15,6 @@ #include "common/sockaddr.h" #include "libknot/dname.h" #include "knot/conf/conf.h" -#include "knot/ctl/remote.h" #include "libknotd_la-cf-parse.h" /* Automake generated header. */ extern int cf_lex (YYSTYPE *lvalp, void *scanner); @@ -43,7 +42,7 @@ static void conf_init_iface(void *scanner, char* ifname, int port) static void conf_start_iface(void *scanner, char* ifname) { - conf_init_iface(scanner, ifname, CONFIG_DEFAULT_PORT); + conf_init_iface(scanner, ifname, -1); add_tail(&new_config->ifaces, &this_iface->n); ++new_config->ifaces_count; } @@ -313,7 +312,7 @@ interface_start: interface: | interface PORT NUM ';' { - if (this_iface->port != CONFIG_DEFAULT_PORT) { + if (this_iface->port > 0) { cf_error(scanner, "only one port definition is allowed in interface section\n"); } else { this_iface->port = $3.i; @@ -333,7 +332,7 @@ interface: } else { this_iface->address = $3.t; this_iface->family = AF_INET; - if (this_iface->port != CONFIG_DEFAULT_PORT) { + if (this_iface->port > 0) { cf_error(scanner, "only one port definition is allowed in interface section\n"); } else { this_iface->port = $5.i; @@ -354,7 +353,7 @@ interface: } else { this_iface->address = $3.t; this_iface->family = AF_INET6; - if (this_iface->port != CONFIG_DEFAULT_PORT) { + if (this_iface->port > 0) { cf_error(scanner, "only one port definition is allowed in interface section\n"); } else { this_iface->port = $5.i; @@ -822,7 +821,7 @@ log_start: log: LOG '{' log_start log_end; ctl_listen_start: - LISTEN_ON { conf_init_iface(scanner, NULL, REMOTE_DPORT); } + LISTEN_ON { conf_init_iface(scanner, NULL, -1); } ; ctl_allow_start: diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index 5ed187c4ef7e3fd33aeb9681c39ec545472c0e05..c99c11f36086ff6567a2dbff53c928440f10ad71 100644 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -26,6 +26,7 @@ #include <urcu.h> #include "knot/conf/conf.h" #include "knot/common.h" +#include "knot/ctl/remote.h" /* * Defaults. @@ -145,6 +146,17 @@ static int conf_process(conf_t *conf) if (conf->max_conn_reply < 1) { conf->max_conn_reply = CONFIG_REPLY_WD; } + + // Postprocess interfaces + conf_iface_t *cfg_if = NULL; + WALK_LIST(cfg_if, conf->ifaces) { + if (cfg_if->port <= 0) { + cfg_if->port = CONFIG_DEFAULT_PORT; + } + } + if (conf->ctl.iface && conf->ctl.iface->port <= 0) { + conf->ctl.iface->port = REMOTE_DPORT; + } // Postprocess zones int ret = KNOT_EOK; diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c index d61489288c99b8633e08949b46a3c979c46b5f13..ec251f936a839e1b1f2fe7b3b21256943babd621 100644 --- a/src/knot/ctl/knotc_main.c +++ b/src/knot/ctl/knotc_main.c @@ -620,9 +620,11 @@ int main(int argc, char **argv) if (strchr(r_addr, ':')) { /* Dumb way to check for v6 addr. */ ctl_if->family = AF_INET6; } - ctl_if->key = malloc(sizeof(knot_key_t)); - if (ctl_if->key) { - memcpy(ctl_if->key, &r_key, sizeof(knot_key_t)); + if (r_key.name) { + ctl_if->key = malloc(sizeof(knot_key_t)); + if (ctl_if->key) { + memcpy(ctl_if->key, &r_key, sizeof(knot_key_t)); + } } } conf()->ctl.iface = ctl_if; diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c index a9e494c5788d46aa028b7cbc042b818e3344b717..ed86b9973412dbea2f9a9890ce01989882cf9e3c 100644 --- a/src/knot/ctl/remote.c +++ b/src/knot/ctl/remote.c @@ -459,17 +459,41 @@ int remote_process(server_t *s, int r, uint8_t* buf, size_t buflen) /* Check ACL list. */ rcu_read_lock(); + knot_key_t *k = NULL; acl_key_t *m = NULL; + knot_rcode_t ts_rc = 0; + uint16_t ts_trc = 0; + uint64_t ts_tmsigned = 0; + const knot_rrset_t *tsig_rr = knot_packet_tsig(pkt); if (acl_match(conf()->ctl.acl, &a, &m) == ACL_DENY) { knot_packet_free(&pkt); socket_close(c); rcu_read_unlock(); return KNOT_EACCES; } + if (m && m->val) { + k = ((conf_iface_t *)m->val)->key; + } rcu_read_unlock(); /* Check TSIG. */ - /*! \todo #2035 */ + if (k) { + if (!tsig_rr) { + knot_packet_free(&pkt); + socket_close(c); + return KNOT_EACCES; + } + ret = zones_verify_tsig_query(pkt, k, &ts_rc, + &ts_trc, &ts_tmsigned); + if (ret != KNOT_EOK) { + dbg_server("remote: failed to verify TSIG, " + "RC: %u TSIG_RC: %u\n", + ts_rc, ts_trc); + knot_packet_free(&pkt); + socket_close(c); + return KNOT_EACCES; + } + } /* Answer packet. */ wire_len = buflen; diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c index ffc5347c0716cf8ce83813c974f372b1a07ab77d..a93bbc95c6189a244b6db5b82d7811d43cbf7db3 100644 --- a/src/knot/server/zones.c +++ b/src/knot/server/zones.c @@ -1855,117 +1855,6 @@ dbg_zones_exec( /*----------------------------------------------------------------------------*/ -static int zones_verify_tsig_query(const knot_packet_t *query, - const knot_rrset_t *tsig_rr, - const knot_key_t *key, - knot_rcode_t *rcode, uint16_t *tsig_rcode, - uint64_t *tsig_prev_time_signed) -{ - assert(tsig_rr != NULL); - assert(key != NULL); - assert(rcode != NULL); - assert(tsig_rcode != NULL); - - /* - * 1) Check if we support the requested algorithm. - */ - tsig_algorithm_t alg = tsig_rdata_alg(tsig_rr); - if (tsig_alg_digest_length(alg) == 0) { - log_answer_info("Unsupported digest algorithm " - "requested, treating as bad key\n"); - /*! \todo [TSIG] It is unclear from RFC if I - * should treat is as a bad key - * or some other error. - */ - *rcode = KNOT_RCODE_NOTAUTH; - *tsig_rcode = KNOT_TSIG_RCODE_BADKEY; - return KNOT_TSIG_EBADKEY; - } - - const knot_dname_t *kname = knot_rrset_owner(tsig_rr); - assert(kname != NULL); - - /* - * 2) Find the particular key used by the TSIG. - * Check not only name, but also the algorithm. - */ - if (key && kname && knot_dname_compare(key->name, kname) == 0 - && key->algorithm == alg) { - dbg_zones_verb("Found claimed TSIG key for comparison\n"); - } else { - *rcode = KNOT_RCODE_NOTAUTH; - *tsig_rcode = KNOT_TSIG_RCODE_BADKEY; - return KNOT_TSIG_EBADKEY; - } - - /* - * 3) Validate the query with TSIG. - */ - /* Prepare variables for TSIG */ - /*! \todo These need to be saved to the response somehow. */ - //size_t tsig_size = tsig_wire_maxsize(key); - size_t digest_max_size = tsig_alg_digest_length(key->algorithm); - //size_t digest_size = 0; - //uint64_t tsig_prev_time_signed = 0; - //uint8_t *digest = (uint8_t *)malloc(digest_max_size); - //memset(digest, 0 , digest_max_size); - - /* Copy MAC from query. */ - dbg_zones_verb("Validating TSIG from query\n"); - - //const uint8_t* mac = tsig_rdata_mac(tsig_rr); - size_t mac_len = tsig_rdata_mac_length(tsig_rr); - - int ret = KNOT_EOK; - - if (mac_len > digest_max_size) { - *rcode = KNOT_RCODE_FORMERR; - dbg_zones("MAC length %zu exceeds digest " - "maximum size %zu\n", mac_len, digest_max_size); - return KNOT_EMALF; - } else { - //memcpy(digest, mac, mac_len); - //digest_size = mac_len; - - /* Check query TSIG. */ - ret = knot_tsig_server_check(tsig_rr, - knot_packet_wireformat(query), - knot_packet_size(query), key); - dbg_zones_verb("knot_tsig_server_check() returned %s\n", - knot_strerror(ret)); - - /* Evaluate TSIG check results. */ - switch(ret) { - case KNOT_EOK: - *rcode = KNOT_RCODE_NOERROR; - break; - case KNOT_TSIG_EBADKEY: - *tsig_rcode = KNOT_TSIG_RCODE_BADKEY; - *rcode = KNOT_RCODE_NOTAUTH; - break; - case KNOT_TSIG_EBADSIG: - *tsig_rcode = KNOT_TSIG_RCODE_BADSIG; - *rcode = KNOT_RCODE_NOTAUTH; - break; - case KNOT_TSIG_EBADTIME: - *tsig_rcode = KNOT_TSIG_RCODE_BADTIME; - // store the time signed from the query - *tsig_prev_time_signed = tsig_rdata_time_signed(tsig_rr); - *rcode = KNOT_RCODE_NOTAUTH; - break; - case KNOT_EMALF: - *rcode = KNOT_RCODE_FORMERR; - break; - default: - *rcode = KNOT_RCODE_SERVFAIL; - } - } - - return ret; -} - -/*----------------------------------------------------------------------------*/ - static int zones_check_tsig_query(const knot_zone_t *zone, knot_packet_t *query, const sockaddr_t *addr, @@ -2014,7 +1903,7 @@ static int zones_check_tsig_query(const knot_zone_t *zone, if (*tsig_key_zone != NULL) { // everything OK, so check TSIG dbg_zones_verb("Verifying TSIG.\n"); - ret = zones_verify_tsig_query(query, tsig, *tsig_key_zone, + ret = zones_verify_tsig_query(query, *tsig_key_zone, rcode, tsig_rcode, tsig_prev_time_signed); } else { @@ -4128,3 +4017,114 @@ int zones_process_update_response(knot_nameserver_t *ns, *rsize = 0; return ret; } + + +int zones_verify_tsig_query(const knot_packet_t *query, + const knot_key_t *key, + knot_rcode_t *rcode, uint16_t *tsig_rcode, + uint64_t *tsig_prev_time_signed) +{ + assert(key != NULL); + assert(rcode != NULL); + assert(tsig_rcode != NULL); + + const knot_rrset_t *tsig_rr = knot_packet_tsig(query); + assert(tsig_rr != NULL); + + /* + * 1) Check if we support the requested algorithm. + */ + tsig_algorithm_t alg = tsig_rdata_alg(tsig_rr); + if (tsig_alg_digest_length(alg) == 0) { + log_answer_info("Unsupported digest algorithm " + "requested, treating as bad key\n"); + /*! \todo [TSIG] It is unclear from RFC if I + * should treat is as a bad key + * or some other error. + */ + *rcode = KNOT_RCODE_NOTAUTH; + *tsig_rcode = KNOT_TSIG_RCODE_BADKEY; + return KNOT_TSIG_EBADKEY; + } + + const knot_dname_t *kname = knot_rrset_owner(tsig_rr); + assert(kname != NULL); + + /* + * 2) Find the particular key used by the TSIG. + * Check not only name, but also the algorithm. + */ + if (key && kname && knot_dname_compare(key->name, kname) == 0 + && key->algorithm == alg) { + dbg_zones_verb("Found claimed TSIG key for comparison\n"); + } else { + *rcode = KNOT_RCODE_NOTAUTH; + *tsig_rcode = KNOT_TSIG_RCODE_BADKEY; + return KNOT_TSIG_EBADKEY; + } + + /* + * 3) Validate the query with TSIG. + */ + /* Prepare variables for TSIG */ + /*! \todo These need to be saved to the response somehow. */ + //size_t tsig_size = tsig_wire_maxsize(key); + size_t digest_max_size = tsig_alg_digest_length(key->algorithm); + //size_t digest_size = 0; + //uint64_t tsig_prev_time_signed = 0; + //uint8_t *digest = (uint8_t *)malloc(digest_max_size); + //memset(digest, 0 , digest_max_size); + + /* Copy MAC from query. */ + dbg_zones_verb("Validating TSIG from query\n"); + + //const uint8_t* mac = tsig_rdata_mac(tsig_rr); + size_t mac_len = tsig_rdata_mac_length(tsig_rr); + + int ret = KNOT_EOK; + + if (mac_len > digest_max_size) { + *rcode = KNOT_RCODE_FORMERR; + dbg_zones("MAC length %zu exceeds digest " + "maximum size %zu\n", mac_len, digest_max_size); + return KNOT_EMALF; + } else { + //memcpy(digest, mac, mac_len); + //digest_size = mac_len; + + /* Check query TSIG. */ + ret = knot_tsig_server_check(tsig_rr, + knot_packet_wireformat(query), + knot_packet_size(query), key); + dbg_zones_verb("knot_tsig_server_check() returned %s\n", + knot_strerror(ret)); + + /* Evaluate TSIG check results. */ + switch(ret) { + case KNOT_EOK: + *rcode = KNOT_RCODE_NOERROR; + break; + case KNOT_TSIG_EBADKEY: + *tsig_rcode = KNOT_TSIG_RCODE_BADKEY; + *rcode = KNOT_RCODE_NOTAUTH; + break; + case KNOT_TSIG_EBADSIG: + *tsig_rcode = KNOT_TSIG_RCODE_BADSIG; + *rcode = KNOT_RCODE_NOTAUTH; + break; + case KNOT_TSIG_EBADTIME: + *tsig_rcode = KNOT_TSIG_RCODE_BADTIME; + // store the time signed from the query + *tsig_prev_time_signed = tsig_rdata_time_signed(tsig_rr); + *rcode = KNOT_RCODE_NOTAUTH; + break; + case KNOT_EMALF: + *rcode = KNOT_RCODE_FORMERR; + break; + default: + *rcode = KNOT_RCODE_SERVFAIL; + } + } + + return ret; +} diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h index 9797dd8b8a8ad4390a2e2b028828905258df2c90..04d5363350571597381cf6e2c26a6a64e9d079b8 100644 --- a/src/knot/server/zones.h +++ b/src/knot/server/zones.h @@ -340,6 +340,22 @@ int zones_process_update_response(knot_nameserver_t *ns, knot_ns_xfr_t *data, knot_packet_t *packet, uint8_t *rwire, size_t *rsize, size_t maxlen); + +/*! + * \brief Verify TSIG in query. + * + * \param query Query packet. + * \param key TSIG key used for this query. + * \param rcode Dst for resulting RCODE. + * \param tsig_rcode Dst for resulting TSIG RCODE. + * \param tsig_prev_time_signed Dst for previout time signed. + * + * \return KNOT_EOK if verified or error if not. + */ +int zones_verify_tsig_query(const knot_packet_t *query, + const knot_key_t *key, + knot_rcode_t *rcode, uint16_t *tsig_rcode, + uint64_t *tsig_prev_time_signed); #endif // _KNOTD_ZONES_H_ /*! @} */