commit 36c968491f424b4a9d4ad2b3eb69a29f9102a749 Author: Andrea Shepard andrea@persephoneslair.org Date: Tue Jul 10 19:18:47 2012 -0700
Use new replaycache_t structure for replay detection in rend_service_introduce() --- src/or/or.h | 16 ++++--- src/or/rendcommon.c | 2 +- src/or/rendservice.c | 121 +++++++++++++++++-------------------------------- 3 files changed, 53 insertions(+), 86 deletions(-)
diff --git a/src/or/or.h b/src/or/or.h index b6cffd4..2ce95c6 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -98,6 +98,7 @@ #include "address.h" #include "compat_libevent.h" #include "ht.h" +#include "replaycache.h"
/* These signals are defined to help handle_control_signal work. */ @@ -4214,12 +4215,15 @@ typedef struct rend_intro_point_t { * intro point. */ unsigned int rend_service_note_removing_intro_point_called : 1;
- /** (Service side only) A digestmap recording the INTRODUCE2 cells - * this intro point's circuit has received. Each key is the digest - * of the RSA-encrypted part of a received INTRODUCE2 cell; each - * value is a pointer to the time_t at which the cell was received. - * This digestmap is used to prevent replay attacks. */ - digestmap_t *accepted_intro_rsa_parts; + /** (Service side only) A replay cache recording the RSA-encrypted parts + * of INTRODUCE2 cells this intro point's circuit has received. This is + * used to prevent replay attacks. */ + replaycache_t *accepted_intro_rsa_parts; + + /** (Service side only) Count of INTRODUCE2 cells accepted from this + * intro point. + */ + int accepted_introduce2_count;
/** (Service side only) The time at which this intro point was first * published, or -1 if this intro point has not yet been diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 4722690..f6b1bf9 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -439,7 +439,7 @@ rend_intro_point_free(rend_intro_point_t *intro) crypto_pk_free(intro->intro_key);
if (intro->accepted_intro_rsa_parts != NULL) { - digestmap_free(intro->accepted_intro_rsa_parts, _tor_free); + replaycache_free(intro->accepted_intro_rsa_parts); }
tor_free(intro); diff --git a/src/or/rendservice.c b/src/or/rendservice.c index daa1651..9c8017c 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -21,6 +21,7 @@ #include "router.h" #include "relay.h" #include "rephist.h" +#include "replaycache.h" #include "routerlist.h" #include "routerparse.h"
@@ -95,16 +96,12 @@ typedef struct rend_service_t { * up-to-date. */ time_t next_upload_time; /**< Scheduled next hidden service descriptor * upload time. */ - /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t - * of when they were received. Clients may send INTRODUCE1 cells - * for the same rendezvous point through two or more different - * introduction points; when they do, this digestmap keeps us from - * launching multiple simultaneous attempts to connect to the same - * rend point. */ - digestmap_t *accepted_intro_dh_parts; - /** Time at which we last removed expired values from - * accepted_intro_dh_parts. */ - time_t last_cleaned_accepted_intro_dh_parts; + /** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, to + * detect repeats. Clients may send INTRODUCE1 cells for the same + * rendezvous point through two or more different introduction points; + * when they do, this keeps us from launching multiple simultaneous attempts + * to connect to the same rend point. */ + replaycache_t *accepted_intro_dh_parts; } rend_service_t;
/** A list of rend_service_t's for services run on this OP. @@ -177,7 +174,9 @@ rend_service_free(rend_service_t *service) rend_authorized_client_free(c);); smartlist_free(service->clients); } - digestmap_free(service->accepted_intro_dh_parts, _tor_free); + if (service->accepted_intro_dh_parts) { + replaycache_free(service->accepted_intro_dh_parts); + } tor_free(service); }
@@ -955,26 +954,6 @@ rend_check_authorization(rend_service_t *service, return 1; }
-/** Remove elements from <b>service</b>'s replay cache that are old enough to - * be noticed by timestamp checking. */ -static void -clean_accepted_intro_dh_parts(rend_service_t *service, time_t now) -{ - const time_t cutoff = now - REND_REPLAY_TIME_INTERVAL; - - service->last_cleaned_accepted_intro_dh_parts = now; - if (!service->accepted_intro_dh_parts) - return; - - DIGESTMAP_FOREACH_MODIFY(service->accepted_intro_dh_parts, digest, - time_t *, t) { - if (*t < cutoff) { - tor_free(t); - MAP_DEL_CURRENT(digest); - } - } DIGESTMAP_FOREACH_END; -} - /** Called when <b>intro</b> will soon be removed from * <b>service</b>'s list of intro points. */ static void @@ -1108,10 +1087,10 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, int auth_type; size_t auth_len = 0; char auth_data[REND_DESC_COOKIE_LEN]; - crypto_digest_t *digest = NULL; time_t now = time(NULL); char diffie_hellman_hash[DIGEST_LEN]; - time_t *access_time; + time_t elapsed; + int replay; const or_options_t *options = get_options();
if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) { @@ -1177,30 +1156,32 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, goto err; }
- if (!service->accepted_intro_dh_parts) - service->accepted_intro_dh_parts = digestmap_new(); + if (!service->accepted_intro_dh_parts) { + service->accepted_intro_dh_parts = + replaycache_new(REND_REPLAY_TIME_INTERVAL, + REND_REPLAY_TIME_INTERVAL); + }
- if (!intro_point->accepted_intro_rsa_parts) - intro_point->accepted_intro_rsa_parts = digestmap_new(); + if (!intro_point->accepted_intro_rsa_parts) { + intro_point->accepted_intro_rsa_parts = replaycache_new(0, 0); + }
- { - char pkpart_digest[DIGEST_LEN]; - /* Check for replay of PK-encrypted portion. */ - crypto_digest(pkpart_digest, (char*)request+DIGEST_LEN, keylen); - access_time = digestmap_get(intro_point->accepted_intro_rsa_parts, - pkpart_digest); - if (access_time != NULL) { - log_warn(LD_REND, "Possible replay detected! We received an " - "INTRODUCE2 cell with same PK-encrypted part %d seconds ago. " - "Dropping cell.", (int)(now-*access_time)); - goto err; - } - access_time = tor_malloc(sizeof(time_t)); - *access_time = now; - digestmap_set(intro_point->accepted_intro_rsa_parts, - pkpart_digest, access_time); + /* Check for replay of PK-encrypted portion. */ + replay = replaycache_add_test_and_elapsed( + intro_point->accepted_intro_rsa_parts, + ((char*)request)+DIGEST_LEN, keylen, + &elapsed); + + if (replay) { + log_warn(LD_REND, "Possible replay detected! We received an " + "INTRODUCE2 cell with same PK-encrypted part %d seconds ago. " + "Dropping cell.", (int)elapsed); + goto err; }
+ /* Increment INTRODUCE2 counter */ + ++(intro_point->accepted_introduce2_count); + /* Next N bytes is encrypted with service key */ note_crypto_pk_op(REND_SERVER); r = crypto_pk_private_hybrid_decrypt( @@ -1327,17 +1308,14 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, r_cookie = ptr; base16_encode(hexcookie,9,r_cookie,4);
- /* Determine hash of Diffie-Hellman, part 1 to detect replays. */ - digest = crypto_digest_new(); - crypto_digest_add_bytes(digest, ptr+REND_COOKIE_LEN, DH_KEY_LEN); - crypto_digest_get_digest(digest, diffie_hellman_hash, DIGEST_LEN); - crypto_digest_free(digest); - /* Check whether there is a past request with the same Diffie-Hellman, * part 1. */ - access_time = digestmap_get(service->accepted_intro_dh_parts, - diffie_hellman_hash); - if (access_time != NULL) { + replay = replaycache_add_test_and_elapsed( + service->accepted_intro_dh_parts, + ptr+REND_COOKIE_LEN, DH_KEY_LEN, + &elapsed); + + if (replay) { /* A Tor client will send a new INTRODUCE1 cell with the same rend * cookie and DH public key as its previous one if its intro circ * times out while in state CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT . @@ -1349,21 +1327,10 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, "INTRODUCE2 cell with same first part of " "Diffie-Hellman handshake %d seconds ago. Dropping " "cell.", - (int) (now - *access_time)); + (int) elapsed); goto err; }
- /* Add request to access history, including time and hash of Diffie-Hellman, - * part 1, and possibly remove requests from the history that are older than - * one hour. */ - access_time = tor_malloc(sizeof(time_t)); - *access_time = now; - digestmap_set(service->accepted_intro_dh_parts, - diffie_hellman_hash, access_time); - if (service->last_cleaned_accepted_intro_dh_parts + REND_REPLAY_TIME_INTERVAL - < now) - clean_accepted_intro_dh_parts(service, now); - /* If the service performs client authorization, check included auth data. */ if (service->clients) { if (auth_len > 0) { @@ -2164,11 +2131,7 @@ upload_service_descriptor(rend_service_t *service) static int intro_point_accepted_intro_count(rend_intro_point_t *intro) { - if (intro->accepted_intro_rsa_parts == NULL) { - return 0; - } else { - return digestmap_size(intro->accepted_intro_rsa_parts); - } + return intro->accepted_introduce2_count; }
/** Return non-zero iff <b>intro</b> should 'expire' now (i.e. we
tor-commits@lists.torproject.org