Browse Source

Implement midpoint functionality for rendezvous points.

svn:r1439
Nick Mathewson 22 years ago
parent
commit
a981c4099a
5 changed files with 211 additions and 24 deletions
  1. 2 2
      doc/rend-spec.txt
  2. 38 6
      src/or/circuit.c
  3. 5 3
      src/or/or.h
  4. 165 0
      src/or/rendmid.c
  5. 1 13
      src/or/rendservice.c

+ 2 - 2
doc/rend-spec.txt

@@ -111,7 +111,7 @@ Tor Rendezvous Spec
          TS    A timestamp                           [4 octets]
          TS    A timestamp                           [4 octets]
          NI    Number of introduction points         [2 octets]
          NI    Number of introduction points         [2 octets]
          Ipt   A list of NUL-terminated OR nicknames [variable]
          Ipt   A list of NUL-terminated OR nicknames [variable]
-         SIG   Signature of above fields             [KL octets]
+         SIG   Signature of above fields             [variable]
 
 
    KL is the length of PK, in octets.  (Currently, KL must be 128.)
    KL is the length of PK, in octets.  (Currently, KL must be 128.)
    TS is the number of seconds elapsed since Jan 1, 1970.
    TS is the number of seconds elapsed since Jan 1, 1970.
@@ -131,7 +131,7 @@ Tor Rendezvous Spec
         KL   Key length                             [2 octets]
         KL   Key length                             [2 octets]
         PK   Bob's public key                       [KL octets]
         PK   Bob's public key                       [KL octets]
         HS   Hash of session info                   [20 octets]
         HS   Hash of session info                   [20 octets]
-        SIG  Signature of above information         [KL octets]
+        SIG  Signature of above information         [variable]
 
 
    To prevent replay attacks, the HS field contains a SHA-1 hash based on the
    To prevent replay attacks, the HS field contains a SHA-1 hash based on the
    shared secret KH between Bob's OP and the introduction point, as
    shared secret KH between Bob's OP and the introduction point, as

+ 38 - 6
src/or/circuit.c

@@ -88,7 +88,6 @@ void circuit_close_all_marked()
   }
   }
 }
 }
 
 
-
 circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn) {
 circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn) {
   circuit_t *circ;
   circuit_t *circ;
 
 
@@ -307,14 +306,21 @@ circuit_t *circuit_get_newest(connection_t *conn,
   return NULL;
   return NULL;
 }
 }
 
 
-/* Return the first circuit in global_circuitlist whose rend_service
- * field is servid and whose purpose is purpose. Returns NULL if no circuit
- * is found.
+/* Return the first circuit in global_circuitlist after 'start' whose
+ * rend_service field is servid and whose purpose is purpose. Returns
+ * NULL if no circuit is found.  If 'start' is null, begin at the start of
+ * the list.
  */
  */
-circuit_t *circuit_get_by_service_and_purpose(const char *servid, int purpose)
+circuit_t *circuit_get_next_by_service_and_purpose(circuit_t *start,
+                                        const char *servid, int purpose)
 {
 {
   circuit_t *circ;
   circuit_t *circ;
-  for(circ=global_circuitlist; circ; circ = circ->next) {
+  if (start == NULL)
+    circ = global_circuitlist;
+  else
+    circ = start->next;
+
+  for( ; circ; circ = circ->next) {
     if (circ->marked_for_close)
     if (circ->marked_for_close)
       continue;
       continue;
     if (circ->purpose != purpose)
     if (circ->purpose != purpose)
@@ -325,6 +331,21 @@ circuit_t *circuit_get_by_service_and_purpose(const char *servid, int purpose)
   return NULL;
   return NULL;
 }
 }
 
 
+/* Return the circuit waiting for a rendezvous with the provided cookie.
+ * Return NULL if no such circuit is found.
+ */
+circuit_t *circuit_get_rendezvous(const char *cookie)
+{
+  circuit_t *circ;
+  for (circ = global_circuitlist; circ; circ = circ->next) {
+    if (! circ->marked_for_close &&
+        circ->purpose == CIRCUIT_PURPOSE_REND_POINT_WAITING &&
+        ! memcmp(circ->rend_cookie, cookie, REND_COOKIE_LEN) )
+      return circ;
+  }
+  return NULL;
+}
+
 #define MIN_SECONDS_BEFORE_EXPIRING_CIRC 10
 #define MIN_SECONDS_BEFORE_EXPIRING_CIRC 10
 /* circuits that were born at the end of their second might be expired
 /* circuits that were born at the end of their second might be expired
  * after 10.1 seconds; circuits born at the beginning might be expired
  * after 10.1 seconds; circuits born at the beginning might be expired
@@ -516,6 +537,17 @@ int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
   }
   }
 
 
   if(!conn) {
   if(!conn) {
+    if (circ->rend_splice && cell_direction == CELL_DIRECTION_OUT) {
+      assert(circ->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
+      assert(circ->rend_splice->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
+      cell->circ_id = circ->rend_splice->p_circ_id;
+      if (circuit_receive_relay_cell(cell, circ->rend_splice, CELL_DIRECTION_IN)<0) {
+        log_fn(LOG_WARN, "Error relaying cell across rendezvous; closing circuits");
+        circuit_mark_for_close(circ); /* XXXX Do this here, or just return -1? */
+        return -1;
+      }
+      return 0;
+    }
     log_fn(LOG_WARN,"Didn't recognize cell, but circ stops here! Closing circ.");
     log_fn(LOG_WARN,"Didn't recognize cell, but circ stops here! Closing circ.");
     return -1;
     return -1;
   }
   }

+ 5 - 3
src/or/or.h

@@ -560,8 +560,8 @@ struct circuit_t {
   char rend_service[CRYPTO_SHA1_DIGEST_LEN];
   char rend_service[CRYPTO_SHA1_DIGEST_LEN];
 
 
   /* Holds rendezvous cookie if purpose is REND_POINT_WAITING or
   /* Holds rendezvous cookie if purpose is REND_POINT_WAITING or
-   * S_RENDEZVOUSING or C_ESTABLISH_REND. Filled with zeroes otherwise.
-  */
+   * C_ESTABLISH_REND. Filled with zeroes otherwise.
+   */
   char rend_cookie[REND_COOKIE_LEN];
   char rend_cookie[REND_COOKIE_LEN];
 
 
   /* Points to spliced circuit if purpose is REND_ESTABLISHED, and circuit
   /* Points to spliced circuit if purpose is REND_ESTABLISHED, and circuit
@@ -692,7 +692,9 @@ circuit_t *circuit_get_by_circ_id_conn(uint16_t circ_id, connection_t *conn);
 circuit_t *circuit_get_by_conn(connection_t *conn);
 circuit_t *circuit_get_by_conn(connection_t *conn);
 circuit_t *circuit_get_newest(connection_t *conn,
 circuit_t *circuit_get_newest(connection_t *conn,
                               int must_be_open, int must_be_clean);
                               int must_be_open, int must_be_clean);
-circuit_t *circuit_get_by_service_and_purpose(const char *servid, int purpose);
+circuit_t *circuit_get_next_by_service_and_purpose(circuit_t *circuit,
+                                             const char *servid, int purpose);
+circuit_t *circuit_get_rendezvous(const char *cookie);
 
 
 void circuit_expire_building(void);
 void circuit_expire_building(void);
 int circuit_count_building(void);
 int circuit_count_building(void);

+ 165 - 0
src/or/rendmid.c

@@ -4,28 +4,193 @@
 
 
 #include "or.h"
 #include "or.h"
 
 
+/* Respond to an ESTABLISH_INTRO cell by setting the circuit's purpose and
+ * rendevous service.
+ */
 int
 int
 rend_mid_establish_intro(circuit_t *circ, char *request, int request_len)
 rend_mid_establish_intro(circuit_t *circ, char *request, int request_len)
 {
 {
+  crypto_pk_env_t *pk = NULL;
+  char buf[20+9];
+  char expected_digest[20];
+  char pk_digest[20];
+  int asn1len;
+  circuit_t *c;
+
+  if (circ->purpose != CIRCUIT_PURPOSE_INTERMEDIATE) {
+    log_fn(LOG_WARN, "Rejecting ESTABLISH_INTRO on non-intermediate circuit");
+    goto err;
+  }
+  if (request_len < 22)
+    goto truncated;
+  /* First 2 bytes: length of asn1-encoded key. */
+  asn1len = get_uint16(request);
+
+  /* Next asn1len bytes: asn1-encoded key. */
+  if (request_len < 22+asn1len)
+    goto truncated;
+  pk = crypto_pk_asn1_decode(request+2, asn1len);
+  if (!pk) {
+    log_fn(LOG_WARN, "Couldn't decode public key");
+    goto err;
+  }
+
+  /* Next 20 bytes: Hash of handshake_digest | "INTRODUCE" */
+  memcpy(buf, circ->handshake_digest, 20);
+  memcpy(buf+20, "INTRODUCE", 9);
+  if (crypto_SHA_digest(buf, 29, expected_digest)<0) {
+    log_fn(LOG_WARN, "Error computing digest");
+    goto err;
+  }
+  if (memcmp(expected_digest, buf+2+asn1len, 20)) {
+    log_fn(LOG_WARN, "Hash of session info was not as expected");
+    goto err;
+  }
+
+  /* Rest of body: signature of previous data */
+  if (crypto_pk_public_checksig_digest(pk, buf, 22+asn1len,
+                          buf+22+asn1len, request_len-(22+asn1len))<0) {
+    log_fn(LOG_WARN, "Incorrect signature on ESTABLISH_INTRO cell; rejecting");
+    goto err;
+  }
+
+  /* The request is valid.  First, compute the hash of Bob's PK.*/
+  if (crypto_pk_get_digest(pk, pk_digest)<0) {
+    log_fn(LOG_WARN, "Couldn't hash public key.");
+    goto err;
+  }
+
+  /* Close any other intro circuits with the same pk. */
+  c = NULL;
+  while ((c = circuit_get_next_by_service_and_purpose(
+                                c,pk_digest,CIRCUIT_PURPOSE_INTRO_POINT))) {
+    circuit_mark_for_close(c);
+  }
+
+  /* Now, set up this circuit. */
+  circ->purpose = CIRCUIT_PURPOSE_INTRO_POINT;
+  memcpy(circ->rend_service, pk_digest, 20);
+
   return 0;
   return 0;
+ truncated:
+  log_fn(LOG_WARN, "Rejecting truncated ESTABLISH_INTRO cell");
+ err:
+  if (pk) crypto_free_pk_env(pk);
+  circuit_mark_for_close(circ);
+  return -1;
 }
 }
 
 
+/* Process an INTRODUCE1 cell by finding the corresponding introduction
+ * circuit, and relaying the body of the INTRODUCE1 cell inside an
+ * INTRODUCE2 cell.
+ */
 int
 int
 rend_mid_introduce(circuit_t *circ, char *request, int request_len)
 rend_mid_introduce(circuit_t *circ, char *request, int request_len)
 {
 {
+  circuit_t *intro_circ;
+
+  if (request_len < 276) {
+    log_fn(LOG_WARN, "Impossibly short INTRODUCE2 cell; dropping.");
+    goto err;
+  }
+
+  /* The first 20 bytes are all we look at: they have a hash of Bob's PK. */
+  intro_circ = circuit_get_next_by_service_and_purpose(
+                             NULL, request, CIRCUIT_PURPOSE_INTRO_POINT);
+  if (!intro_circ) {
+    log_fn(LOG_WARN,
+           "No introduction circuit matching INTRODUCE2 cell; dropping");
+    goto err;
+  }
+
+  /* Great.  Now we just relay the cell down the circuit. */
+  if (connection_edge_send_command(NULL, intro_circ,
+                                   RELAY_COMMAND_INTRODUCE2,
+                                   request, request_len, NULL)) {
+    log_fn(LOG_WARN, "Unable to send INTRODUCE2 cell to OP.");
+    goto err;
+  }
+
   return 0;
   return 0;
+ err:
+  circuit_mark_for_close(circ); /* Is this right? */
+  return -1;
 }
 }
 
 
+/* Process an ESTABLISH_RENDEZVOUS cell by settingthe circuit's purpose and
+ * rendezvous cookie.
+ */
 int
 int
 rend_mid_establish_rendezvous(circuit_t *circ, char *request, int request_len)
 rend_mid_establish_rendezvous(circuit_t *circ, char *request, int request_len)
 {
 {
+  if (circ->purpose != CIRCUIT_PURPOSE_INTERMEDIATE) {
+    log_fn(LOG_WARN, "Tried to establish rendezvous on non-intermediate circuit");
+    goto err;
+  }
+
+  if (request_len != REND_COOKIE_LEN) {
+    log_fn(LOG_WARN, "Invalid length on ESTABLISH_RENDEZVOUS");
+    goto err;
+  }
+
+  if (circuit_get_rendezvous(request)) {
+    log_fn(LOG_WARN, "Duplicate rendezvous cookie in ESTABLISH_RENDEZVOUS");
+    goto err;
+  }
+
+  circ->purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING;
+  memcpy(circ->rend_cookie, request, REND_COOKIE_LEN);
+
   return 0;
   return 0;
+ err:
+  circuit_mark_for_close(circ);
+  return -1;
 }
 }
 
 
+/* Process a RENDEZVOUS1 cell by looking up the correct rendezvous circuit by its
+ * relaying the cell's body in a RENDEZVOUS2 cell, and connecting the two circuits.
+ */
 int
 int
 rend_mid_rendezvous(circuit_t *circ, char *request, int request_len)
 rend_mid_rendezvous(circuit_t *circ, char *request, int request_len)
 {
 {
+  circuit_t *rend_circ;
+
+  if (circ->purpose != CIRCUIT_PURPOSE_INTERMEDIATE) {
+    log_fn(LOG_WARN, "Tried to complete rendezvous on non-intermediate circuit");
+    goto err;
+  }
+
+  if (request_len < 20+128+20) {
+    log_fn(LOG_WARN, "Rejecting impossibly short RENDEZVOUS1 cell");
+    goto err;
+  }
+
+  rend_circ = circuit_get_rendezvous(request);
+  if (!rend_circ) {
+    log_fn(LOG_WARN, "Rejecting RENDEZVOUS1 cell with unrecognized rendezvous cookie");
+    goto err;
+  }
+
+  /* Send the RENDEZVOUS2 cell to Alice. */
+  if (connection_edge_send_command(NULL, rend_circ,
+                                   RELAY_COMMAND_RENDEZVOUS2,
+                                   request+20, request_len-20, NULL)) {
+    log_fn(LOG_WARN, "Unable to send RENDEZVOUS2 cell to OP.");
+    goto err;
+  }
+
+  /* Join the circuits. */
+  circ->purpose = CIRCUIT_PURPOSE_REND_ESTABLISHED;
+  rend_circ->purpose = CIRCUIT_PURPOSE_REND_ESTABLISHED;
+  memset(circ->rend_cookie, 0, 20);
+
+  rend_circ->rend_splice = circ;
+  circ->rend_splice = rend_circ;
+
   return 0;
   return 0;
+ err:
+  circuit_mark_for_close(circ);
+  return -1;
 }
 }
 
 
 /*
 /*

+ 1 - 13
src/or/rendservice.c

@@ -235,8 +235,7 @@ int rend_service_init_keys(void)
   return 0;
   return 0;
 }
 }
 
 
-/*DOCDOC*/
-rend_service_t *
+static rend_service_t *
 rend_service_get_by_pk_digest(const char* digest)
 rend_service_get_by_pk_digest(const char* digest)
 {
 {
   int i;
   int i;
@@ -253,17 +252,6 @@ rend_service_get_by_pk_digest(const char* digest)
  * Handle cells
  * Handle cells
  ******/
  ******/
 
 
-typedef struct rend_introduction_t {
-  /* Digest of the hidden service's PK. */
-  char key_digest[20];
-  /* Nickname of OR running rendezvous point. */
-  char *rendezvous_point;
-  /* Cookie that we'll use to recognize the rendezvous point. */
-  char cookie[20];
-  /* g^xy */
-  char shared_secret[128];
-} rend_introduction_t;
-
 /* Respond to an INTRODUCE2 cell by launching a circuit to the chosen
 /* Respond to an INTRODUCE2 cell by launching a circuit to the chosen
  * rendezvous points.
  * rendezvous points.
  */
  */