]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
add privacy aware cookies. This breaks the rpl test
authorTCY16 <tom@nlnetlabs.nl>
Fri, 2 Sep 2022 13:49:17 +0000 (15:49 +0200)
committerTCY16 <tom@nlnetlabs.nl>
Fri, 2 Sep 2022 13:49:17 +0000 (15:49 +0200)
iterator/iterator.c
services/cache/infra.c
services/cache/infra.h
services/outside_network.c
services/outside_network.h
testcode/fake_event.c

index f1f7228f1b1ea1d7981f140118cffed4767a8ac7..ce4028924b27b37623efec7c3ed2b524c3595ccc 100644 (file)
@@ -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);
index 38724ae79939db70eef86819d72c6c056f9075ec..bbcfd414da28e881a8d6bfb7177a78fc7111313a 100644 (file)
@@ -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;
index faa4213518a2d82576a44b748f831cbce5b30cb5..7ef4b0981ed6a641782d8f9c7653afeedbda14db 100644 (file)
@@ -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.
index 1e53bff47db1ad8acf2f8d05aa6440e2d2be8997..6954065bafeab2842d78834edd12fb61df034d5c 100644 (file)
@@ -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,
index c383b8f09e24a4843c8bc58e7c3e7d4bfa77d586..14d11b9b673c9b3a35ad1d2e22bab5f98cad2344 100644 (file)
@@ -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;
 };
 
 /**
index 504ba6b9da21a85b3dcb2ffca02ed2de140aad81..c4a66d46f92775424d6105f5709a45468c1600a9 100644 (file)
@@ -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 */