From 730b55c94a485f12e160033e101a08cfc08c653c Mon Sep 17 00:00:00 2001 From: TCY16 Date: Fri, 2 Sep 2022 15:49:17 +0200 Subject: [PATCH] add privacy aware cookies. This breaks the rpl test --- iterator/iterator.c | 16 ++++++++-- services/cache/infra.c | 64 ++++++++++++++++++++++++++++++++++++-- services/cache/infra.h | 17 +++++++--- services/outside_network.c | 28 +++++++++++++---- services/outside_network.h | 2 ++ testcode/fake_event.c | 4 ++- 6 files changed, 114 insertions(+), 17 deletions(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index f1f7228f1..ce4028924 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -3945,6 +3945,17 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, if (edns.opt_list_in && (cookie = edns_list_get_option(edns.opt_list_in, LDNS_EDNS_COOKIE))){ + struct sockaddr_storage bound_addr; + socklen_t bound_addrlen = sizeof(struct sockaddr); + + if(getsockname(qstate->reply->c->fd, + (struct sockaddr *) &bound_addr, + &bound_addrlen) != -1) { + + log_addr(VERB_DETAIL, "!!!!! iterator:udp socket:", &bound_addr, bound_addrlen); + } else { + bound_addrlen = 0; + } /* verify this is a 'complete cookie' (client+server) * (RFC9018) with the length and store the complete * cookie in the infra_cache. Do nothing when the cookie @@ -3953,7 +3964,8 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, if (cookie->opt_len == 24 && infra_set_server_cookie(qstate->env->infra_cache, &qstate->reply->addr, qstate->reply->addrlen, - iq->dp->name, iq->dp->namelen, cookie) >= 0) { + iq->dp->name, iq->dp->namelen, &bound_addr, + bound_addrlen, cookie) >= 0) { /* log_hex() uses the verbosity levels of verbose() */ log_hex("complete cookie: ", cookie->opt_data, cookie->opt_len); @@ -3969,7 +3981,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, } } - /* Copy the edns options we may got from the back end */ + /* Copy the edns options we may have gotten from the back end */ if(edns.opt_list_in) { qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list_in, qstate->region); diff --git a/services/cache/infra.c b/services/cache/infra.c index 38724ae79..bbcfd414d 100644 --- a/services/cache/infra.c +++ b/services/cache/infra.c @@ -705,10 +705,41 @@ infra_edns_update(struct infra_cache* infra, struct sockaddr_storage* addr, return 1; } +/** find the bound addr in the list of interfaces */ +static int +get_bound_ip_if(struct outside_network* outnet, + struct sockaddr_storage bound_addr, socklen_t bound_addrlen, + struct port_if** pif_return) +{ + int i = 0; + struct port_if* pif_list; + int pif_list_len; + + if(addr_is_ip6(&bound_addr, bound_addrlen)) { + pif_list = outnet->ip6_ifs; + pif_list_len = outnet->num_ip6; + } else { + pif_list = outnet->ip4_ifs; + pif_list_len = outnet->num_ip4; + } + + for (i = 0; i < pif_list_len; i++) { + struct port_if *iface = &pif_list[i]; + + if (iface->addrlen == bound_addrlen && + memcmp(&iface->addr, &bound_addr, bound_addrlen)) { + *pif_return = iface; + return 1; + } + } + return 0; +} + int infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name, size_t namelen, - time_t timenow, struct edns_cookie* cookie) + time_t timenow, struct outside_network* outnet, struct port_if** pif, + struct edns_cookie* cookie) { struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, name, namelen, 1); @@ -732,6 +763,22 @@ infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, data = (struct infra_data*) e->data; + if (data->cookie.state == SERVER_COOKIE_LEARNED) { + // lookup bound interface + log_err("!!!!! infra_get_cookie:bound_addrlen: %d", data->cookie.bound_addrlen); + log_addr(VERB_OPS, "!!!!! infra_get_cookie:bound_addr, len:", &data->cookie.bound_addr, + data->cookie.bound_addrlen); + + if (!(get_bound_ip_if(outnet, data->cookie.bound_addr, + data->cookie.bound_addrlen, pif))) { + log_err("!!!!!!!! creating new cookie for changed interface"); + data_entry_init(infra, e, timenow); + } + } + + // @TODO uggo: data has changed + data = (struct infra_data*) e->data; + memcpy(cookie, &data->cookie, sizeof(struct edns_cookie)); if(needtoinsert) { @@ -745,7 +792,9 @@ infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, int infra_set_server_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* name, size_t namelen, struct edns_option* cookie) + socklen_t addrlen, uint8_t* name, size_t namelen, + struct sockaddr_storage* bound_addr, socklen_t bound_addrlen, + struct edns_option* cookie) { struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen, name, namelen, 1); @@ -783,6 +832,12 @@ infra_set_server_cookie(struct infra_cache* infra, struct sockaddr_storage* addr return -1; } + if (!(data->cookie.bound_addrlen == bound_addrlen) && + memcpy(&data->cookie.bound_addr, bound_addr, bound_addrlen)){ + + // @TODO do something? this _should_ only happen on reloads? + } + /* the server cookie has changed, but the client cookie has not * so we update the server cookie */ if (memcmp(data->cookie.data.cookie+8, @@ -817,7 +872,10 @@ infra_set_server_cookie(struct infra_cache* infra, struct sockaddr_storage* addr /* store the server cookie */ memcpy(data->cookie.data.cookie, cookie->opt_data, 24); data->cookie.state = SERVER_COOKIE_LEARNED; - + if (bound_addrlen > 0) { + memcpy(&data->cookie.bound_addr, bound_addr, bound_addrlen); + data->cookie.bound_addrlen = bound_addrlen; + } verbose(VERB_QUERY, "storing received server cookie from upstream"); lock_rw_unlock(&e->lock); return 1; diff --git a/services/cache/infra.h b/services/cache/infra.h index faa421351..7ef4b0981 100644 --- a/services/cache/infra.h +++ b/services/cache/infra.h @@ -49,6 +49,7 @@ #include "util/rtt.h" #include "util/netevent.h" #include "util/data/msgreply.h" +#include "services/outside_network.h" struct slabhash; struct config_file; @@ -83,15 +84,17 @@ enum edns_cookie_state }; /** - * Structure for an EDNS cookie (RFC9018) and it's internal state + * Structure for an EDNS cookie (RFC9018), it's internal state, and the + * the outgoing address that we bind this cookie to for privacy (RFC9018) */ struct edns_cookie { enum edns_cookie_state state; struct edns_cookie_data data; + struct sockaddr_storage bound_addr; + socklen_t bound_addrlen; }; - /** * Host information kept for every server, per zone. */ @@ -377,7 +380,8 @@ int infra_edns_update(struct infra_cache* infra, */ int infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name, size_t namelen, - time_t timenow, struct edns_cookie* cookie); + time_t timenow, struct outside_network* outnet, struct port_if** pif, + struct edns_cookie* cookie); /** * Find the cookie entry in the cache and update it with to make a 'complete cookie' @@ -390,14 +394,17 @@ int infra_get_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, * @param name: name of zone * @param namelen: length of name * @param timenow: what time it is now. + * @param bound_addr: the outgoing address that we bind to this cookie + * @param bound_addr: the length of the bound address * @param cookie: the EDNS cookie option we want to store. * @return -1 if the wrong client cookie is found, 0 if the entry isn't found in * the cache and a new one is inserted, 1 if the complete cookie is inserted * or unchanged. */ int infra_set_server_cookie(struct infra_cache* infra, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* name, size_t namelen, struct edns_option* cookie); - + socklen_t addrlen, uint8_t* name, size_t namelen, + struct sockaddr_storage* bound_addr, socklen_t bound_addrlen, + struct edns_option* cookie); /** * Get Lameness information and average RTT if host is in the cache. diff --git a/services/outside_network.c b/services/outside_network.c index 1e53bff47..6954065ba 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -1382,6 +1382,7 @@ outnet_send_wait_udp(struct outside_network* outnet) pend->pkt_len = 0; log_assert(!pend->sq->busy); pend->sq->busy = 1; + if(!randomize_and_send_udp(pend, outnet->udp_buff, pend->timeout)) { /* callback error on pending */ @@ -2018,7 +2019,6 @@ static int udp_connect_needs_log(int err) return 1; } - /** Select random interface and port */ static int select_ifport(struct outside_network* outnet, struct pending* pend, @@ -2035,8 +2035,17 @@ select_ifport(struct outside_network* outnet, struct pending* pend, log_assert(outnet->unused_fds); tries = 0; while(1) { - my_if = ub_random_max(outnet->rnd, num_if); - pif = &ifs[my_if]; + /* if we have a bound IP address for the EDNS cookie in the + * message, use that interface */ + if (!(pend->sq->bound_interface)) { + my_if = ub_random_max(outnet->rnd, num_if); + pif = &ifs[my_if]; + } else { + pif = pend->sq->bound_interface; + log_err("!!!!! select_ifport:bound_addrlen: %d", pif->addrlen); + log_addr(VERB_OPS, "!!!!! select_ifport:bound_addrlen:", &pif->addr, pif->addrlen); + } + #ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION if(outnet->udp_connect) { /* if we connect() we cannot reuse fds for a port */ @@ -2059,6 +2068,9 @@ select_ifport(struct outside_network* outnet, struct pending* pend, break; } } + + log_err("!!!!! pif->inuse: %d, pif->maxout: %d", pif->inuse, pif->maxout); + /* try to open new port, if fails, loop to try again */ log_assert(pif->inuse < pif->maxout); portno = pif->avail_ports[my_port - pif->inuse]; @@ -2194,6 +2206,7 @@ pending_udp_query(struct serviced_query* sq, struct sldns_buffer* packet, pend->cb = cb; pend->cb_arg = cb_arg; pend->node.key = pend; + pend->timer = comm_timer_create(sq->outnet->base, pending_udp_timer_cb, pend); if(!pend->timer) { @@ -2540,7 +2553,7 @@ serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec, char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, int qtype, struct edns_option* opt_list, size_t pad_queries_block_size, struct alloc_cache* alloc, - struct regional* region) + struct port_if* bound_interface, struct regional* region) { struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq)); struct timeval t; @@ -2603,6 +2616,7 @@ serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec, sq->status = serviced_initial; sq->retry = 0; sq->to_be_deleted = 0; + sq->bound_interface = bound_interface; sq->padding_block_size = pad_queries_block_size; #ifdef UNBOUND_DEBUG ins = @@ -3363,6 +3377,7 @@ outnet_serviced_query(struct outside_network* outnet, struct edns_option* backed_up_opt_list = qstate->edns_opts_back_out; struct edns_option* per_upstream_opt_list = NULL; time_t timenow = 0; + struct port_if* pif; /* If we have an already populated EDNS option list make a copy since * we may now add upstream specific EDNS options. */ @@ -3401,7 +3416,7 @@ outnet_serviced_query(struct outside_network* outnet, if (env->cfg->upstream_cookies && infra_get_cookie(env->infra_cache, addr, addrlen, zone, zonelen, - *env->now, &cookie)) { + *env->now, outnet, &pif, &cookie)) { if (cookie.state == SERVER_COOKIE_LEARNED) { /* We known the complete cookie, so we attach it */ @@ -3412,6 +3427,7 @@ outnet_serviced_query(struct outside_network* outnet, edns_opt_list_append(&per_upstream_opt_list, LDNS_EDNS_COOKIE, 8, cookie.data.cookie, region); } /* We ignore COOKIE_NOT_SUPPORTED */ + } serviced_gen_query(buff, qinfo->qname, qinfo->qname_len, qinfo->qtype, @@ -3446,7 +3462,7 @@ outnet_serviced_query(struct outside_network* outnet, per_upstream_opt_list, ( ssl_upstream && env->cfg->pad_queries ? env->cfg->pad_queries_block_size : 0 ), - env->alloc, region); + env->alloc, pif, region); if(!sq) { if(check_ratelimit) { infra_ratelimit_dec(env->infra_cache, diff --git a/services/outside_network.h b/services/outside_network.h index c383b8f09..14d11b9b6 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -527,6 +527,8 @@ struct serviced_query { struct comm_timer* timer; /** true if serviced_query is currently doing net I/O and may block */ int busy; + /** Interface bound to the EDNS cookie @TODO fix this */ + struct port_if* bound_interface; }; /** diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 504ba6b9d..c4a66d46f 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1227,6 +1227,8 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, qstate->edns_opts_back_out; struct edns_option* per_upstream_opt_list = NULL; struct edns_cookie cookie; + struct port_if* pif; + /* If we have an already populated EDNS option list make a copy * since we may now add upstream specific EDNS options. */ if(qstate->edns_opts_back_out) { @@ -1258,7 +1260,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, if (qstate->env->cfg->upstream_cookies && infra_get_cookie(env->infra_cache, addr, addrlen, - zone, zonelen, *env->now, &cookie)) { + zone, zonelen, *env->now, outnet, &pif, &cookie)) { if (cookie.state == SERVER_COOKIE_LEARNED) { /* We known the complete cookie, so we attach it */ -- 2.47.2