Back to index

tor  0.2.3.18-rc
onion.c
Go to the documentation of this file.
00001 /* Copyright (c) 2001 Matej Pfajfar.
00002  * Copyright (c) 2001-2004, Roger Dingledine.
00003  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
00004  * Copyright (c) 2007-2012, The Tor Project, Inc. */
00005 /* See LICENSE for licensing information */
00006 
00013 #include "or.h"
00014 #include "circuitlist.h"
00015 #include "config.h"
00016 #include "onion.h"
00017 #include "rephist.h"
00018 
00021 typedef struct onion_queue_t {
00022   or_circuit_t *circ;
00023   char *onionskin;
00024   time_t when_added;
00025   struct onion_queue_t *next;
00026 } onion_queue_t;
00027 
00029 #define ONIONQUEUE_WAIT_CUTOFF 5
00030 
00034 static onion_queue_t *ol_list=NULL;
00035 static onion_queue_t *ol_tail=NULL;
00038 static int ol_length=0;
00039 
00043 int
00044 onion_pending_add(or_circuit_t *circ, char *onionskin)
00045 {
00046   onion_queue_t *tmp;
00047   time_t now = time(NULL);
00048 
00049   tmp = tor_malloc_zero(sizeof(onion_queue_t));
00050   tmp->circ = circ;
00051   tmp->onionskin = onionskin;
00052   tmp->when_added = now;
00053 
00054   if (!ol_tail) {
00055     tor_assert(!ol_list);
00056     tor_assert(!ol_length);
00057     ol_list = tmp;
00058     ol_tail = tmp;
00059     ol_length++;
00060     return 0;
00061   }
00062 
00063   tor_assert(ol_list);
00064   tor_assert(!ol_tail->next);
00065 
00066   if (ol_length >= get_options()->MaxOnionsPending) {
00067 #define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60)
00068     static ratelim_t last_warned =
00069       RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL);
00070     char *m;
00071     if ((m = rate_limit_log(&last_warned, approx_time()))) {
00072       log_warn(LD_GENERAL,
00073                "Your computer is too slow to handle this many circuit "
00074                "creation requests! Please consider using the "
00075                "MaxAdvertisedBandwidth config option or choosing a more "
00076                "restricted exit policy.%s",m);
00077       tor_free(m);
00078     }
00079     tor_free(tmp);
00080     return -1;
00081   }
00082 
00083   ol_length++;
00084   ol_tail->next = tmp;
00085   ol_tail = tmp;
00086   while ((int)(now - ol_list->when_added) >= ONIONQUEUE_WAIT_CUTOFF) {
00087     /* cull elderly requests. */
00088     circ = ol_list->circ;
00089     onion_pending_remove(ol_list->circ);
00090     log_info(LD_CIRC,
00091              "Circuit create request is too old; canceling due to overload.");
00092     circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
00093   }
00094   return 0;
00095 }
00096 
00100 or_circuit_t *
00101 onion_next_task(char **onionskin_out)
00102 {
00103   or_circuit_t *circ;
00104 
00105   if (!ol_list)
00106     return NULL; /* no onions pending, we're done */
00107 
00108   tor_assert(ol_list->circ);
00109   tor_assert(ol_list->circ->p_conn); /* make sure it's still valid */
00110   tor_assert(ol_length > 0);
00111   circ = ol_list->circ;
00112   *onionskin_out = ol_list->onionskin;
00113   ol_list->onionskin = NULL; /* prevent free. */
00114   onion_pending_remove(ol_list->circ);
00115   return circ;
00116 }
00117 
00121 void
00122 onion_pending_remove(or_circuit_t *circ)
00123 {
00124   onion_queue_t *tmpo, *victim;
00125 
00126   if (!ol_list)
00127     return; /* nothing here. */
00128 
00129   /* first check to see if it's the first entry */
00130   tmpo = ol_list;
00131   if (tmpo->circ == circ) {
00132     /* it's the first one. remove it from the list. */
00133     ol_list = tmpo->next;
00134     if (!ol_list)
00135       ol_tail = NULL;
00136     ol_length--;
00137     victim = tmpo;
00138   } else { /* we need to hunt through the rest of the list */
00139     for ( ;tmpo->next && tmpo->next->circ != circ; tmpo=tmpo->next) ;
00140     if (!tmpo->next) {
00141       log_debug(LD_GENERAL,
00142                 "circ (p_circ_id %d) not in list, probably at cpuworker.",
00143                 circ->p_circ_id);
00144       return;
00145     }
00146     /* now we know tmpo->next->circ == circ */
00147     victim = tmpo->next;
00148     tmpo->next = victim->next;
00149     if (ol_tail == victim)
00150       ol_tail = tmpo;
00151     ol_length--;
00152   }
00153 
00154   /* now victim points to the element that needs to be removed */
00155 
00156   tor_free(victim->onionskin);
00157   tor_free(victim);
00158 }
00159 
00160 /*----------------------------------------------------------------------*/
00161 
00174 int
00175 onion_skin_create(crypto_pk_t *dest_router_key,
00176                   crypto_dh_t **handshake_state_out,
00177                   char *onion_skin_out) /* ONIONSKIN_CHALLENGE_LEN bytes */
00178 {
00179   char challenge[DH_KEY_LEN];
00180   crypto_dh_t *dh = NULL;
00181   int dhbytes, pkbytes;
00182 
00183   tor_assert(dest_router_key);
00184   tor_assert(handshake_state_out);
00185   tor_assert(onion_skin_out);
00186   *handshake_state_out = NULL;
00187   memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN);
00188 
00189   if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT)))
00190     goto err;
00191 
00192   dhbytes = crypto_dh_get_bytes(dh);
00193   pkbytes = (int) crypto_pk_keysize(dest_router_key);
00194   tor_assert(dhbytes == 128);
00195   tor_assert(pkbytes == 128);
00196 
00197   if (crypto_dh_get_public(dh, challenge, dhbytes))
00198     goto err;
00199 
00200   note_crypto_pk_op(ENC_ONIONSKIN);
00201 
00202   /* set meeting point, meeting cookie, etc here. Leave zero for now. */
00203   if (crypto_pk_public_hybrid_encrypt(dest_router_key, onion_skin_out,
00204                                       ONIONSKIN_CHALLENGE_LEN,
00205                                       challenge, DH_KEY_LEN,
00206                                       PK_PKCS1_OAEP_PADDING, 1)<0)
00207     goto err;
00208 
00209   memset(challenge, 0, sizeof(challenge));
00210   *handshake_state_out = dh;
00211 
00212   return 0;
00213  err:
00214   memset(challenge, 0, sizeof(challenge));
00215   if (dh) crypto_dh_free(dh);
00216   return -1;
00217 }
00218 
00224 int
00225 onion_skin_server_handshake(const char *onion_skin, /*ONIONSKIN_CHALLENGE_LEN*/
00226                             crypto_pk_t *private_key,
00227                             crypto_pk_t *prev_private_key,
00228                             char *handshake_reply_out, /*ONIONSKIN_REPLY_LEN*/
00229                             char *key_out,
00230                             size_t key_out_len)
00231 {
00232   char challenge[ONIONSKIN_CHALLENGE_LEN];
00233   crypto_dh_t *dh = NULL;
00234   ssize_t len;
00235   char *key_material=NULL;
00236   size_t key_material_len=0;
00237   int i;
00238   crypto_pk_t *k;
00239 
00240   len = -1;
00241   for (i=0;i<2;++i) {
00242     k = i==0?private_key:prev_private_key;
00243     if (!k)
00244       break;
00245     note_crypto_pk_op(DEC_ONIONSKIN);
00246     len = crypto_pk_private_hybrid_decrypt(k, challenge,
00247                                            ONIONSKIN_CHALLENGE_LEN,
00248                                            onion_skin, ONIONSKIN_CHALLENGE_LEN,
00249                                            PK_PKCS1_OAEP_PADDING,0);
00250     if (len>0)
00251       break;
00252   }
00253   if (len<0) {
00254     log_info(LD_PROTOCOL,
00255              "Couldn't decrypt onionskin: client may be using old onion key");
00256     goto err;
00257   } else if (len != DH_KEY_LEN) {
00258     log_warn(LD_PROTOCOL, "Unexpected onionskin length after decryption: %ld",
00259              (long)len);
00260     goto err;
00261   }
00262 
00263   dh = crypto_dh_new(DH_TYPE_CIRCUIT);
00264   if (!dh) {
00265     log_warn(LD_BUG, "Couldn't allocate DH key");
00266     goto err;
00267   }
00268   if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN)) {
00269     log_info(LD_GENERAL, "crypto_dh_get_public failed.");
00270     goto err;
00271   }
00272 
00273   key_material_len = DIGEST_LEN+key_out_len;
00274   key_material = tor_malloc(key_material_len);
00275   len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, challenge,
00276                                  DH_KEY_LEN, key_material,
00277                                  key_material_len);
00278   if (len < 0) {
00279     log_info(LD_GENERAL, "crypto_dh_compute_secret failed.");
00280     goto err;
00281   }
00282 
00283   /* send back H(K|0) as proof that we learned K. */
00284   memcpy(handshake_reply_out+DH_KEY_LEN, key_material, DIGEST_LEN);
00285 
00286   /* use the rest of the key material for our shared keys, digests, etc */
00287   memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
00288 
00289   memset(challenge, 0, sizeof(challenge));
00290   memset(key_material, 0, key_material_len);
00291   tor_free(key_material);
00292   crypto_dh_free(dh);
00293   return 0;
00294  err:
00295   memset(challenge, 0, sizeof(challenge));
00296   if (key_material) {
00297     memset(key_material, 0, key_material_len);
00298     tor_free(key_material);
00299   }
00300   if (dh) crypto_dh_free(dh);
00301 
00302   return -1;
00303 }
00304 
00314 int
00315 onion_skin_client_handshake(crypto_dh_t *handshake_state,
00316             const char *handshake_reply, /* ONIONSKIN_REPLY_LEN bytes */
00317             char *key_out,
00318             size_t key_out_len)
00319 {
00320   ssize_t len;
00321   char *key_material=NULL;
00322   size_t key_material_len;
00323   tor_assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN);
00324 
00325   key_material_len = DIGEST_LEN + key_out_len;
00326   key_material = tor_malloc(key_material_len);
00327   len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, handshake_state,
00328                                  handshake_reply, DH_KEY_LEN, key_material,
00329                                  key_material_len);
00330   if (len < 0)
00331     goto err;
00332 
00333   if (tor_memneq(key_material, handshake_reply+DH_KEY_LEN, DIGEST_LEN)) {
00334     /* H(K) does *not* match. Something fishy. */
00335     log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on onion handshake. "
00336              "Bug or attack.");
00337     goto err;
00338   }
00339 
00340   /* use the rest of the key material for our shared keys, digests, etc */
00341   memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
00342 
00343   memset(key_material, 0, key_material_len);
00344   tor_free(key_material);
00345   return 0;
00346  err:
00347   memset(key_material, 0, key_material_len);
00348   tor_free(key_material);
00349   return -1;
00350 }
00351 
00359 int
00360 fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */
00361                       uint8_t *handshake_reply_out, /* DIGEST_LEN*2 bytes */
00362                       uint8_t *key_out,
00363                       size_t key_out_len)
00364 {
00365   char tmp[DIGEST_LEN+DIGEST_LEN];
00366   char *out = NULL;
00367   size_t out_len;
00368   int r = -1;
00369 
00370   if (crypto_rand((char*)handshake_reply_out, DIGEST_LEN)<0)
00371     return -1;
00372 
00373   memcpy(tmp, key_in, DIGEST_LEN);
00374   memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
00375   out_len = key_out_len+DIGEST_LEN;
00376   out = tor_malloc(out_len);
00377   if (crypto_expand_key_material(tmp, sizeof(tmp), out, out_len)) {
00378     goto done;
00379   }
00380   memcpy(handshake_reply_out+DIGEST_LEN, out, DIGEST_LEN);
00381   memcpy(key_out, out+DIGEST_LEN, key_out_len);
00382   r = 0;
00383  done:
00384   memset(tmp, 0, sizeof(tmp));
00385   memset(out, 0, out_len);
00386   tor_free(out);
00387   return r;
00388 }
00389 
00402 int
00403 fast_client_handshake(const uint8_t *handshake_state,/*DIGEST_LEN bytes*/
00404                       const uint8_t *handshake_reply_out,/*DIGEST_LEN*2 bytes*/
00405                       uint8_t *key_out,
00406                       size_t key_out_len)
00407 {
00408   char tmp[DIGEST_LEN+DIGEST_LEN];
00409   char *out;
00410   size_t out_len;
00411   int r = -1;
00412 
00413   memcpy(tmp, handshake_state, DIGEST_LEN);
00414   memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
00415   out_len = key_out_len+DIGEST_LEN;
00416   out = tor_malloc(out_len);
00417   if (crypto_expand_key_material(tmp, sizeof(tmp), out, out_len)) {
00418     goto done;
00419   }
00420   if (tor_memneq(out, handshake_reply_out+DIGEST_LEN, DIGEST_LEN)) {
00421     /* H(K) does *not* match. Something fishy. */
00422     log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on fast handshake. "
00423              "Bug or attack.");
00424     goto done;
00425   }
00426   memcpy(key_out, out+DIGEST_LEN, key_out_len);
00427   r = 0;
00428  done:
00429   memset(tmp, 0, sizeof(tmp));
00430   memset(out, 0, out_len);
00431   tor_free(out);
00432   return r;
00433 }
00434 
00436 void
00437 clear_pending_onions(void)
00438 {
00439   while (ol_list) {
00440     onion_queue_t *victim = ol_list;
00441     ol_list = victim->next;
00442     tor_free(victim->onionskin);
00443     tor_free(victim);
00444   }
00445   ol_list = ol_tail = NULL;
00446   ol_length = 0;
00447 }
00448