Browse Source

The tor client will now attempt to fetch the PIR params before looking up an hsdesc

Ian Goldberg 3 years ago
parent
commit
74ad26f7cd

+ 36 - 0
src/feature/dircache/dircache.c

@@ -355,6 +355,8 @@ static int handle_get_hs_descriptor_v2(dir_connection_t *conn,
                                        const get_handler_args_t *args);
 static int handle_get_robots(dir_connection_t *conn,
                                 const get_handler_args_t *args);
+static int handle_get_pirserver_params(dir_connection_t *conn,
+                            const get_handler_args_t *args);
 static int handle_get_networkstatus_bridges(dir_connection_t *conn,
                                 const get_handler_args_t *args);
 
@@ -370,6 +372,7 @@ static const url_table_ent_t url_table[] = {
   { "/tor/keys/", 1, handle_get_keys },
   { "/tor/rendezvous2/", 1, handle_get_hs_descriptor_v2 },
   { "/tor/hs/3/", 1, handle_get_hs_descriptor_v3 },
+  { "/tor/pironion/params", 0, handle_get_pirserver_params },
   { "/tor/robots.txt", 0, handle_get_robots },
   { "/tor/networkstatus-bridges", 0, handle_get_networkstatus_bridges },
   { NULL, 0, NULL },
@@ -1382,14 +1385,20 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn,
    * pubkey. */
   tor_assert(!strcmpstart(url, "/tor/hs/3/"));
   pubkey_str = url + strlen("/tor/hs/3/");
+
+  log_info(LD_REND, "PIRSERVER Received non-private lookup for %s",
+        pubkey_str);
+
   retval = hs_cache_lookup_as_dir(HS_VERSION_THREE,
                                   pubkey_str, &desc_str);
   if (retval <= 0 || desc_str == NULL) {
+    log_info(LD_REND, "PIRSERVER Replying with failure");
     write_short_http_response(conn, 404, "Not found");
     goto done;
   }
 
   /* Found requested descriptor! Pass it to this nice client. */
+  log_info(LD_REND, "PIRSERVER Replying with descriptor");
   write_http_response_header(conn, strlen(desc_str), NO_METHOD, 0);
   connection_buf_add(desc_str, strlen(desc_str), TO_CONN(conn));
 
@@ -1397,6 +1406,33 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn,
   return 0;
 }
 
+/** Helper function for GET /tor/pironion/params.
+ */
+static int
+handle_get_pirserver_params(dir_connection_t *conn,
+                            ATTR_UNUSED const get_handler_args_t *args)
+{
+    log_info(LD_REND, "PIRSERVER Received request for params");
+    if (hs_cache_pirserver_get_params(conn) < 0) {
+        write_short_http_response(conn, 404, "Not found");
+    }
+    return 0;
+}
+
+/** Callback function when we have the PIR params to send back to the
+ *  client */
+void
+dircache_pirserver_reply_params(dir_connection_t *conn,
+    const char *params, size_t params_len)
+{
+    log_info(LD_REND, "PIRSERVER Responding with params");
+    write_http_response_header_impl(conn, params_len,
+            "application/octet-stream",
+            compression_method_get_name(NO_METHOD),
+            NULL, 0);
+    connection_buf_add(params, params_len, TO_CONN(conn));
+}
+
 /** Helper function for GET /tor/networkstatus-bridges
  */
 static int

+ 3 - 0
src/feature/dircache/dircache.h

@@ -40,4 +40,7 @@ STATIC int parse_hs_version_from_post(const char *url, const char *prefix,
 STATIC unsigned parse_accept_encoding_header(const char *h);
 #endif
 
+void dircache_pirserver_reply_params(dir_connection_t *conn,
+    const char *params, size_t params_len);
+
 #endif /* !defined(TOR_DIRCACHE_H) */

+ 84 - 0
src/feature/dirclient/dirclient.c

@@ -1692,8 +1692,37 @@ directory_send_command(dir_connection_t *conn,
       tor_assert(resource);
       tor_assert(strlen(resource) <= ED25519_BASE64_LEN);
       tor_assert(!payload);
+      if (getenv("PIR_CLIENT_PATH")) {
+        HSClientPIRParams *pirparams =
+            hs_client_pirparams_lookup(req->routerstatus->identity_digest);
+        if (!pirparams) {
+            /* First try to get the pironion params if we want them
+             * but don't have them yet. */
+            conn->pirclient_state = PIRCLIENT_STATE_AWAITING_PARAMS;
+            httpcommand = "GET";
+            tor_asprintf(&url, "/tor/pironion/params");
+            log_info(LD_DIR, "PIRCLIENT Fetching params from %s",
+                safe_str_client(routerstatus_describe(req->routerstatus)));
+            break;
+        } else if (pirparams->size > 0) {
+            /* We have the params; privately fetch the resource */
+            /* PIRONION TODO: fill this in, but for now, fetch it not
+             * privately. */
+            // conn->pirclient_state = PIRCLIENT_STATE_AWAITING_PIRREPLY;
+            httpcommand = "GET";
+            tor_asprintf(&url, "/tor/hs/3/%s", resource);
+            log_info(LD_DIR, "PIRCLIENT PIR query (well, not yet) to %s",
+                safe_str_client(routerstatus_describe(req->routerstatus)));
+            break;
+        }
+      }
+      /* Just fetch it normally, either because we're not configured to
+       * try PIR at all, or because we asked, and the hsdir didn't reply
+       * with PIR params. */
       httpcommand = "GET";
       tor_asprintf(&url, "/tor/hs/3/%s", resource);
+      log_info(LD_DIR, "PIRCLIENT non-PIR hs query for %s to %s",
+        resource, safe_str_client(routerstatus_describe(req->routerstatus)));
       break;
     case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
       tor_assert(!resource);
@@ -1866,6 +1895,12 @@ dir_client_decompress_response_body(char **bodyp, size_t *bodylenp,
 
   int plausible = body_is_plausible(body, body_len, conn->base_.purpose);
 
+  /* If we're getting PIR stuff back, it'll be binary but not compressed. */
+  if (conn->base_.purpose == DIR_PURPOSE_FETCH_HSDESC &&
+        conn->pirclient_state != PIRCLIENT_STATE_NONE) {
+    plausible = 1;
+  }
+
   if (plausible && compression == NO_METHOD) {
     return 0;
   }
@@ -2713,12 +2748,61 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
   const char *reason = args->reason;
   const char *body = args->body;
   const size_t body_len = args->body_len;
+  const node_t *node = node_get_by_id(conn->identity_digest);
+  const routerstatus_t *rstat = NULL;
+  if (node) {
+      rstat = node->rs;
+  }
 
   tor_assert(conn->hs_ident);
 
   log_info(LD_REND,"Received v3 hsdesc (body size %d, status %d (%s))",
            (int)body_len, status_code, escaped(reason));
 
+  /* If we're waiting for PIR parameters, see if we got them. */
+  if (conn->pirclient_state == PIRCLIENT_STATE_AWAITING_PARAMS) {
+    if (status_code == 200 && body_len > 0) {
+        /* Record the PIR params */
+        log_info(LD_REND,"PIRCLIENT received pirparams from %s",
+            safe_str_client(routerstatus_describe(rstat)));
+        hs_client_pirparams_insert(conn->identity_digest, body_len, body);
+    } else {
+        /* Record that we did not receive PIR params when we asked */
+        log_info(LD_REND,"PIRCLIENT failed to receive pirparams from %s",
+            safe_str_client(routerstatus_describe(rstat)));
+        hs_client_pirparams_insert(conn->identity_digest, 0, NULL);
+    }
+    /* Setup and send a new directory request for the same resouce to
+     * the same hsdir; this time, it won't ask for params, but will do a
+     * PIR request (if there are params) or a non-private request (if
+     * not). */
+    if (rstat) {
+        directory_request_t *req =
+            directory_request_new(DIR_PURPOSE_FETCH_HSDESC);
+        directory_request_set_routerstatus(req, rstat);
+        directory_request_set_indirection(req, DIRIND_ANONYMOUS);
+        directory_request_set_resource(req, conn->requested_resource);
+        directory_request_fetch_set_hs_ident(req, conn->hs_ident);
+        log_info(LD_REND, "PIRCLIENT Refetching %s from %s",
+            conn->requested_resource,
+            safe_str_client(routerstatus_describe(req->routerstatus)));
+        directory_initiate_request(req);
+        directory_request_free(req);
+    }
+
+    return 0;
+  }
+
+  /* If we're waiting for a PIR response, decode it. */
+  if (conn->pirclient_state == PIRCLIENT_STATE_AWAITING_PIRREPLY) {
+    /* PIRONION TODO: Fill this in */
+        log_info(LD_REND,"PIRCLIENT received PIR reply from %s",
+            safe_str_client(routerstatus_describe(rstat)));
+  }
+
+  log_info(LD_REND,"PIRCLIENT received hsdesc from %s",
+      safe_str_client(routerstatus_describe(rstat)));
+
   switch (status_code) {
   case 200:
     /* We got something: Try storing it in the cache. */

+ 8 - 0
src/feature/dircommon/dir_connection_st.h

@@ -11,6 +11,12 @@
 
 struct tor_compress_state_t;
 
+typedef enum {
+    PIRCLIENT_STATE_NONE,
+    PIRCLIENT_STATE_AWAITING_PARAMS,
+    PIRCLIENT_STATE_AWAITING_PIRREPLY
+} dir_connection_pirclient_state_t;
+
 /** Subtype of connection_t for an "directory connection" -- that is, an HTTP
  * connection to retrieve or serve directory material. */
 struct dir_connection_t {
@@ -55,6 +61,8 @@ struct dir_connection_t {
    * needs this for the incoming side, so it's moved here. */
   uint64_t dirreq_id;
 
+  dir_connection_pirclient_state_t pirclient_state;
+
 #ifdef MEASUREMENTS_21206
   /** Number of RELAY_DATA cells received. */
   uint32_t data_cells_received;

+ 34 - 1
src/feature/hs/hs_cache.c

@@ -22,6 +22,8 @@
 
 #include "feature/hs/hs_cache.h"
 
+#include "feature/dircache/dircache.h"
+
 #include "feature/nodelist/networkstatus_st.h"
 
 #define SUBPROCESS_PRIVATE
@@ -1002,7 +1004,13 @@ hs_cache_pirserver_received(const unsigned char *hdrbuf,
     const char *bodybuf, size_t bodylen)
 {
     /* PIRONION TODO: actually deliver the received message */
-    log_info(LD_DIRSERV,"PIRSERVER response header %p flag %d body len %ld body %s", *(void**)hdrbuf, hdrbuf[8], bodylen, escaped(bodybuf));
+    log_info(LD_DIRSERV,"PIRSERVER response header %p type %02x body len %ld body %s", *(void**)hdrbuf, hdrbuf[8], bodylen, escaped(bodybuf));
+    if (hdrbuf[8] == PIRSERVER_RESPONSE_PARAMS) {
+        /* Send the params back to the client */
+        dir_connection_t *conn;
+        memmove((char *)(&conn), hdrbuf, sizeof(conn));
+        dircache_pirserver_reply_params(conn, bodybuf, bodylen);
+    }
 }
 
 /* This is called when the pirserver has output for us. */
@@ -1264,6 +1272,31 @@ hs_cache_pirserver_insert_desc(hs_cache_dir_descriptor_t *desc)
     return written;
 }
 
+/** Helper function for GET /tor/pironion/params.
+ */
+int
+hs_cache_pirserver_get_params(dir_connection_t *conn)
+{
+    /* Ask the pirserver for the params */
+
+    int res;
+    unsigned char hdr[PIRSERVER_HDR_SIZE];
+
+    /* PIRONION TODO: For now, the request id is just literally the
+     * dir_connection_t pointer itself, so that when we get the
+     * response, we can write to it.  But of course it's possible that
+     * object will have disappeared between now and then.  Fix this
+     * later by hooking the callback that closes a dir_connection_t. */
+    memmove(hdr, (const char *)(&conn), sizeof(conn));
+    hdr[8] = PIRSERVER_REQUEST_PARAMS;
+    memmove(hdr+9, "\0\0\0\0", 4);
+    res = hs_cache_pirserver_send(hdr, PIRSERVER_HDR_SIZE);
+    if (res < PIRSERVER_HDR_SIZE) {
+        return -1;
+    }
+    return 0;
+}
+
 /* Initialize the hidden service cache subsystem. */
 void
 hs_cache_init(void)

+ 2 - 0
src/feature/hs/hs_cache.h

@@ -99,6 +99,8 @@ const hs_cache_intro_state_t *hs_cache_client_intro_state_find(
 void hs_cache_client_intro_state_clean(time_t now);
 void hs_cache_client_intro_state_purge(void);
 
+int hs_cache_pirserver_get_params(dir_connection_t *conn);
+
 #ifdef HS_CACHE_PRIVATE
 #include "lib/crypt_ops/crypto_ed25519.h"
 

+ 75 - 0
src/feature/hs/hs_client.c

@@ -43,6 +43,8 @@
 #include "core/or/extend_info_st.h"
 #include "core/or/origin_circuit_st.h"
 
+#include "lib/wallclock/approx_time.h"
+
 /* Client-side authorizations for hidden services; map of service identity
  * public key to hs_client_service_authorization_t *. */
 static digest256map_t *client_auths = NULL;
@@ -1894,6 +1896,76 @@ hs_client_close_intro_circuits_from_desc(const hs_descriptor_t *desc)
   }
 }
 
+static digestmap_t *hsdir_pir_params;
+// The maximum time (in seconds) a HSClientPIRParams is valid
+#define PIRPARAMS_MAX_AGE 300
+
+static HSClientPIRParams*
+pirparams_new(size_t size, const char *params)
+{
+    HSClientPIRParams *p = tor_malloc(sizeof(HSClientPIRParams));
+    if (!p) return NULL;
+
+    p->timestamp = approx_time();
+    p->size = size;
+    if (size > 0) {
+        p->params = tor_malloc(size);
+        if (p->params == NULL) {
+            tor_free(p);
+            return NULL;
+        }
+        memmove(p->params, params, size);
+    } else {
+        p->params = NULL;
+    }
+    return p;
+}
+
+static void
+pirparams_free(void *ptr)
+{
+    HSClientPIRParams *p = (HSClientPIRParams *)ptr;
+    tor_free(p->params);
+    tor_free(p);
+}
+
+void
+hs_client_pirparams_insert(const char *digest, size_t size, const char *params)
+{
+    HSClientPIRParams *oldval;
+    HSClientPIRParams *p = pirparams_new(size, params);
+    if (!p) return;
+    oldval = digestmap_set(hsdir_pir_params, digest, p);
+    if (oldval) {
+        pirparams_free(oldval);
+    }
+}
+
+HSClientPIRParams*
+hs_client_pirparams_lookup(const char *digest)
+{
+    HSClientPIRParams *p = digestmap_get(hsdir_pir_params, digest);
+    if (!p) {
+        return NULL;
+    }
+    if ((approx_time() - p->timestamp) > PIRPARAMS_MAX_AGE) {
+        digestmap_remove(hsdir_pir_params, digest);
+        return NULL;
+    }
+    return p;
+}
+
+static void
+pirparams_clear(void)
+{
+    digestmap_free(hsdir_pir_params, pirparams_free);
+}
+
+void hs_client_pir_init(void)
+{
+    hsdir_pir_params = digestmap_new();
+}
+
 /* Release all the storage held by the client subsystem. */
 void
 hs_client_free_all(void)
@@ -1901,6 +1973,7 @@ hs_client_free_all(void)
   /* Purge the hidden service request cache. */
   hs_purge_last_hid_serv_requests();
   client_service_authorization_free_all();
+  pirparams_clear();
 }
 
 /* Purge all potentially remotely-detectable state held in the hidden
@@ -1921,6 +1994,8 @@ hs_client_purge_state(void)
   /* Purge the last hidden service request cache. */
   hs_purge_last_hid_serv_requests();
 
+  pirparams_clear();
+
   log_info(LD_REND, "Hidden service client state has been purged.");
 }
 

+ 11 - 0
src/feature/hs/hs_client.h

@@ -83,6 +83,17 @@ void hs_client_purge_state(void);
 
 void hs_client_free_all(void);
 
+typedef struct {
+    time_t timestamp;
+    size_t size;
+    char *params;
+} HSClientPIRParams;
+
+void hs_client_pir_init(void);
+
+void hs_client_pirparams_insert(const char *digest, size_t size, const char *params);
+HSClientPIRParams* hs_client_pirparams_lookup(const char *digest);
+
 #ifdef HS_CLIENT_PRIVATE
 
 STATIC int auth_key_filename_is_valid(const char *filename);

+ 1 - 0
src/feature/hs/hs_common.c

@@ -1781,6 +1781,7 @@ hs_init(void)
   hs_circuitmap_init();
   hs_service_init();
   hs_cache_init();
+  hs_client_pir_init();
 }
 
 /* Release and cleanup all memory of the HS subsystem (all version). This is