[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r16330: Add replay protection for v3 INTRODUCE2 cells using timestam (in tor/branches/121-hs-authorization: doc/spec src/or)



Author: kloesing
Date: 2008-08-01 11:43:35 -0400 (Fri, 01 Aug 2008)
New Revision: 16330

Modified:
   tor/branches/121-hs-authorization/doc/spec/rend-spec.txt
   tor/branches/121-hs-authorization/src/or/or.h
   tor/branches/121-hs-authorization/src/or/rendclient.c
   tor/branches/121-hs-authorization/src/or/rendservice.c
Log:
Add replay protection for v3 INTRODUCE2 cells using timestamp and first part of DH handshake.

Modified: tor/branches/121-hs-authorization/doc/spec/rend-spec.txt
===================================================================
--- tor/branches/121-hs-authorization/doc/spec/rend-spec.txt	2008-08-01 14:14:00 UTC (rev 16329)
+++ tor/branches/121-hs-authorization/doc/spec/rend-spec.txt	2008-08-01 15:43:35 UTC (rev 16330)
@@ -608,6 +608,7 @@
           AUTHT  The auth type that is supported   [1 octet]
           AUTHL  Length of auth data              [2 octets]
           AUTHD  Auth data                        [variable]
+          TS     Timestamp (sec. since 1-1-1970)  [4 octets]
           IP     Rendezvous point's address       [4 octets]
           PORT   Rendezvous point's OR port       [2 octets]
           ID     Rendezvous point identity ID    [20 octets]

Modified: tor/branches/121-hs-authorization/src/or/or.h
===================================================================
--- tor/branches/121-hs-authorization/src/or/or.h	2008-08-01 14:14:00 UTC (rev 16329)
+++ tor/branches/121-hs-authorization/src/or/or.h	2008-08-01 15:43:35 UTC (rev 16330)
@@ -3807,7 +3807,6 @@
   char *client_name;
   char descriptor_cookie[REND_DESC_COOKIE_LEN];
   crypto_pk_env_t *client_key;
-  smartlist_t *access_history;
 } rend_authorized_client_t;
 
 /** Client-side configuration of authorization for a hidden service. */

Modified: tor/branches/121-hs-authorization/src/or/rendclient.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/rendclient.c	2008-08-01 14:14:00 UTC (rev 16329)
+++ tor/branches/121-hs-authorization/src/or/rendclient.c	2008-08-01 15:43:35 UTC (rev 16330)
@@ -117,7 +117,7 @@
     }
   }
 
-  /* if version is 3, possibly write authentication data */
+  /* if version is 3, write authentication data and timestamp. */
   if (entry->parsed->protocols & (1<<3)) {
     tmp[0] = 3; /* version 3 of the cell format */
     auth_shift = 1;
@@ -126,7 +126,11 @@
       set_uint16(tmp+2, htons(REND_DESC_COOKIE_LEN));
       memcpy(tmp+4, introcirc->rend_desc_cookie, REND_DESC_COOKIE_LEN);
       auth_shift += 2+REND_DESC_COOKIE_LEN;
+    } else {
+      tmp[1] = 0;
     }
+    set_uint32(tmp+auth_shift+1, htonl(time(NULL)));
+    auth_shift += 4;
   } /* if version 2 only write version number */
   else if (entry->parsed->protocols & (1<<2)) {
     tmp[0] = 2; /* version 2 of the cell format */

Modified: tor/branches/121-hs-authorization/src/or/rendservice.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/rendservice.c	2008-08-01 14:14:00 UTC (rev 16329)
+++ tor/branches/121-hs-authorization/src/or/rendservice.c	2008-08-01 15:43:35 UTC (rev 16330)
@@ -68,12 +68,14 @@
                            * published. */
   smartlist_t *clients; /**< List of rend_authorized_client_t's for
                          * clients that may access our service. */
+  smartlist_t *accepted_intros; /**< List of client_access_event_t's for
+                                 * accepted and answered INTRODUCE2 cells. */
 } rend_service_t;
 
 /** The event of a client accessing our hidden service. */
 typedef struct client_access_event_t {
   time_t access_time;
-  char rendezvous_cookie[DIGEST_LEN];
+  char diffie_hellman_hash[DIGEST_LEN];
 } client_access_event_t;
 
 /** A list of rend_service_t's for services run on this OP.
@@ -95,11 +97,6 @@
 {
   rend_authorized_client_t *client = authorized_client;
   if (!authorized_client) return;
-  if (client->access_history) {
-    SMARTLIST_FOREACH(client->access_history,
-                      client_access_event_t *, ev, tor_free(ev););
-    smartlist_free(client->access_history);
-  }
   if (client->client_key)
     crypto_free_pk_env(client->client_key);
   tor_free(client->client_name);
@@ -672,21 +669,14 @@
 }
 
 /** Check client authorization of a given <b>descriptor_cookie</b> for
- * <b>service</b>, use <b>rendezvous_cookie</b> to detect replays or
- * denial of service attacks, and add this request to the access history.
- * Return 1 for success and 0 for failure. */
+ * <b>service</b>. Return 1 for success and 0 for failure. */
 static int
 rend_check_authorization(rend_service_t *service,
-                         const char *descriptor_cookie,
-                         const char *rendezvous_cookie)
+                         const char *descriptor_cookie)
 {
   rend_authorized_client_t *auth_client = NULL;
-  client_access_event_t *event;
-  time_t now = time(NULL);
-  int num_same_rend_cookie = -1;
   tor_assert(service);
   tor_assert(descriptor_cookie);
-  tor_assert(rendezvous_cookie);
   if (!service->clients) {
     log_warn(LD_BUG, "Can't check authorization for a service that is not "
              "configured to perform such.");
@@ -713,49 +703,8 @@
     return 0;
   }
 
-  /* Add request to access history, including time and rendezvous cookie. */
-  event = tor_malloc_zero(sizeof(client_access_event_t));
-  event->access_time = now;
-  memcpy(event->rendezvous_cookie, rendezvous_cookie, DIGEST_LEN);
-  if (!auth_client->access_history)
-    auth_client->access_history = smartlist_create();
-  smartlist_add(auth_client->access_history, event);
-
-  /* Iterate over past requests, remove those which are older than one
-   * hour, and count the number of requests with same rendezvous cookie. */
-  SMARTLIST_FOREACH(auth_client->access_history, client_access_event_t *,
-                    access, {
-    if (access->access_time + 60 * 60 < now) {
-      tor_free(access);
-      SMARTLIST_DEL_CURRENT(auth_client->access_history, access);
-    } else if (!memcmp(access->rendezvous_cookie, rendezvous_cookie,
-                       DIGEST_LEN)) {
-      num_same_rend_cookie++;
-    }
-  });
-
-  /* If the total number of requests (including this request) within the
-   * last hour exceeds 10, drop this request. */
-  if (smartlist_len(auth_client->access_history) > 10) {
-    log_warn(LD_REND, "Client '%s' has exceeded the maximum number of %d "
-             "requests per hour for service '%s'. Is this an attack? "
-             "Access denied!",
-             auth_client->client_name, 10, service->service_id);
-    return 0;
-  }
-
-  /* If the number of requests with the same rendezvous cookie (including
-   * this request) exceeds 3 , drop this request. */
-  if (num_same_rend_cookie >= 3) {
-    log_warn(LD_REND, "Client '%s' has exceeded the maximum number of %d "
-             "requests using the same rendezvous cookie for service '%s'. "
-             "Is this an attack? Access denied!",
-             auth_client->client_name, 3, service->service_id);
-    return 0;
-  }
-
   /* Allow the request. */
-  log_warn(LD_REND, "Client %s could be identified for service %s.",
+  log_info(LD_REND, "Client %s could be identified for service %s.",
            auth_client->client_name, service->service_id);
   return 1;
 }
@@ -776,7 +725,7 @@
   char buf[RELAY_PAYLOAD_SIZE];
   char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
   rend_service_t *service;
-  int r, i, auth_shift = 0;
+  int r, i, auth_shift = 0, ts_shift = 0;
   size_t len, keylen;
   crypto_dh_env_t *dh = NULL;
   origin_circuit_t *launched = NULL;
@@ -790,6 +739,10 @@
   int auth_type;
   size_t auth_len;
   char *auth_data = NULL;
+  crypto_digest_env_t *digest = NULL;
+  time_t now = time(NULL);
+  char diffie_hellman_hash[DIGEST_LEN];
+  client_access_event_t *event = NULL;
 
   base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
                 circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
@@ -855,6 +808,7 @@
   len = r;
   if (*buf == 3) {
     /* Version 3 INTRODUCE2 cell. */
+    time_t ts = 0, now = time(NULL);
     auth_shift = 1;
     auth_type = buf[1];
     if (auth_type == 1) {
@@ -866,29 +820,41 @@
       }
       auth_data = tor_malloc_zero(REND_DESC_COOKIE_LEN);
       memcpy(auth_data, buf+4, REND_DESC_COOKIE_LEN);
-      auth_shift += 18;
+      auth_shift += 2+REND_DESC_COOKIE_LEN;
     }
+
+    /* Check timestamp. */
+    memcpy((char*)&ts, buf+1+auth_shift, sizeof(uint32_t));
+    ts_shift = 4;
+    ts = ntohl(ts);
+    if ((now - ts) < -30 * 60 || (now - ts) > 30 * 60) {
+      log_warn(LD_REND, "INTRODUCE2 cell is too %s. Discarding.",
+          (now - ts) < 0 ? "old" : "new");
+      return -1;
+    }
   }
   if (*buf == 2 || *buf == 3) {
     /* Version 2 INTRODUCE2 cell. */
     int klen;
     extend_info = tor_malloc_zero(sizeof(extend_info_t));
-    extend_info->addr = ntohl(get_uint32(buf+auth_shift+1));
-    extend_info->port = ntohs(get_uint16(buf+auth_shift+5));
-    memcpy(extend_info->identity_digest, buf+auth_shift+7, DIGEST_LEN);
+    extend_info->addr = ntohl(get_uint32(buf+auth_shift+ts_shift+1));
+    extend_info->port = ntohs(get_uint16(buf+auth_shift+ts_shift+5));
+    memcpy(extend_info->identity_digest, buf+auth_shift+ts_shift+7,
+           DIGEST_LEN);
     extend_info->nickname[0] = '$';
     base16_encode(extend_info->nickname+1, sizeof(extend_info->nickname)-1,
                   extend_info->identity_digest, DIGEST_LEN);
 
-    klen = ntohs(get_uint16(buf+auth_shift+7+DIGEST_LEN));
-    if ((int)len != auth_shift+7+DIGEST_LEN+2+klen+20+128) {
+    klen = ntohs(get_uint16(buf+auth_shift+ts_shift+7+DIGEST_LEN));
+    if ((int)len != auth_shift+ts_shift+7+DIGEST_LEN+2+klen+20+128) {
       log_warn(LD_PROTOCOL, "Bad length %u for version %d INTRODUCE2 cell.",
                (int)len, *buf);
       reason = END_CIRC_REASON_TORPROTOCOL;
       goto err;
     }
     extend_info->onion_key =
-                crypto_pk_asn1_decode(buf+auth_shift+7+DIGEST_LEN+2, klen);
+                crypto_pk_asn1_decode(buf+auth_shift+ts_shift+7+DIGEST_LEN+2,
+                                      klen);
     if (!extend_info->onion_key) {
       log_warn(LD_PROTOCOL,
                "Error decoding onion key in version %d INTRODUCE2 cell.",
@@ -896,8 +862,8 @@
       reason = END_CIRC_REASON_TORPROTOCOL;
       goto err;
     }
-    ptr = buf+auth_shift+7+DIGEST_LEN+2+klen;
-    len -= auth_shift+7+DIGEST_LEN+2+klen;
+    ptr = buf+auth_shift+ts_shift+7+DIGEST_LEN+2+klen;
+    len -= auth_shift+ts_shift+7+DIGEST_LEN+2+klen;
   } else {
     char *rp_nickname;
     size_t nickname_field_len;
@@ -949,16 +915,56 @@
   r_cookie = ptr;
   base16_encode(hexcookie,9,r_cookie,4);
 
-  /* If the cell contains authentication data, check authorization. */
-  if (auth_data) {
-    if (service->clients) {
-      if (!rend_check_authorization(service, auth_data, r_cookie)) {
+  /* Determine hash of Diffie-Hellman, part 1 to detect replays. */
+  digest = crypto_new_digest_env();
+  crypto_digest_add_bytes(digest, ptr+REND_COOKIE_LEN, DH_KEY_LEN);
+  crypto_digest_get_digest(digest, diffie_hellman_hash, DIGEST_LEN);
+  crypto_free_digest_env(digest);
+
+  /* Iterate over past requests, remove those which are older than one
+   * hour, and check check whether there is one with same Diffie-Hellman,
+   * part 1. */
+  if (!service->accepted_intros)
+    service->accepted_intros = smartlist_create();
+  SMARTLIST_FOREACH(service->accepted_intros, client_access_event_t *,
+                    access, {
+    if (access->access_time + 60 * 60 < now) {
+      tor_free(access);
+      SMARTLIST_DEL_CURRENT(service->accepted_intros, access);
+    } else if (!memcmp(access->diffie_hellman_hash, diffie_hellman_hash,
+                       DIGEST_LEN)) {
+      log_warn(LD_REND, "Possible replay detected! We received an "
+               "INTRODUCE2 cell with same first part of Diffie-Hellman "
+               "handshake %d seconds ago. Dropping cell.",
+               (uint32_t) (now - access->access_time));
+      return 0;
+    }
+  });
+
+  /* Add request to access history, including time and hash of
+   * Diffie-Hellman, part 1. */
+  event = tor_malloc_zero(sizeof(client_access_event_t));
+  event->access_time = now;
+  memcpy(event->diffie_hellman_hash, diffie_hellman_hash, DIGEST_LEN);
+  smartlist_add(service->accepted_intros, event);
+
+  /* If the service performs client authorization, check included auth data. */
+  if (service->clients) {
+    if (auth_data) {
+      if (rend_check_authorization(service, auth_data)) {
+        log_info(LD_REND, "Authorization data in INTRODUCE2 cell are valid.");
+      } else {
+        log_info(LD_REND, "The authorization data that are contained in "
+                 "the INTRODUCE2 cell are invalid. Dropping cell.");
         reason = END_CIRC_REASON_CONNECTFAILED;
         goto err;
       }
-    } else
-      log_info(LD_PROTOCOL, "INTRODUCE2 cell contains authentication data, "
-               "but we don't perform access control. Ignoring.");
+    } else {
+      log_info(LD_REND, "INTRODUCE2 cell does not contain authentication "
+               "data, but we require client authorization. Dropping cell.");
+      reason = END_CIRC_REASON_CONNECTFAILED;
+      goto err;
+    }
   }
 
   /* Try DH handshake... */