diff --git a/src/or/circuit.c b/src/or/circuit.c index 81b117dc90..46b0227a41 100644 --- a/src/or/circuit.c +++ b/src/or/circuit.c @@ -65,7 +65,7 @@ circuit_t *circuit_new(aci_t p_aci, connection_t *p_conn) { circ->p_aci = p_aci; circ->p_conn = p_conn; - circ->state = CIRCUIT_STATE_ONION_WAIT; + circ->state = CIRCUIT_STATE_ONIONSKIN_PENDING; /* ACIs */ circ->p_aci = p_aci; @@ -87,8 +87,6 @@ void circuit_free(circuit_t *circ) { if (circ->p_crypto) crypto_free_cipher_env(circ->p_crypto); - if(circ->onion) - free(circ->onion); circuit_free_cpath(circ->cpath); while(circ->relay_queue) { tmpd = circ->relay_queue; @@ -122,6 +120,8 @@ void circuit_free_cpath_node(crypt_path_t *victim) { crypto_free_cipher_env(victim->f_crypto); if(victim->b_crypto) crypto_free_cipher_env(victim->b_crypto); + if(victim->handshake_state) + crypto_dh_free(victim->handshake_state); free(victim); } @@ -155,66 +155,6 @@ try_again: return test_aci; } -int circuit_init(circuit_t *circ, int aci_type, onion_layer_t *layer) { - unsigned char iv[16]; - unsigned char digest1[20]; - unsigned char digest2[20]; - struct timeval start, end; - long time_passed; - - assert(circ && circ->onion); - - log(LOG_DEBUG,"circuit_init(): starting"); - circ->n_port = layer->port; - log(LOG_DEBUG,"circuit_init(): Set port to %u.",circ->n_port); - circ->n_addr = layer->addr; - circ->state = CIRCUIT_STATE_OPEN; - - log(LOG_DEBUG,"circuit_init(): aci_type = %u.",aci_type); - - my_gettimeofday(&start); - - circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, aci_type); - if(!circ->n_aci) { - log(LOG_ERR,"circuit_init(): failed to get unique aci."); - return -1; - } - - my_gettimeofday(&end); - - time_passed = tv_udiff(&start, &end); - if (time_passed > 1000) {/* more than 1ms */ - log(LOG_NOTICE,"circuit_init(): get_unique_aci just took %d us!",time_passed); - } - - log(LOG_DEBUG,"circuit_init(): Chosen ACI %u.",circ->n_aci); - - /* keys */ - memset(iv, 0, 16); - crypto_SHA_digest(layer->keyseed,16,digest1); - crypto_SHA_digest(digest1,20,digest2); - crypto_SHA_digest(digest2,20,digest1); - log(LOG_DEBUG,"circuit_init(): Computed keys."); - - if (!(circ->p_crypto = - crypto_create_init_cipher(DEFAULT_CIPHER,digest2,iv,1))) { - log(LOG_ERR,"Cipher initialization failed (ACI %u).",circ->n_aci); - return -1; - } - - if (!(circ->n_crypto = - crypto_create_init_cipher(DEFAULT_CIPHER,digest1,iv,0))) { - log(LOG_ERR,"Cipher initialization failed (ACI %u).",circ->n_aci); - return -1; - } - - log(LOG_DEBUG,"circuit_init(): Cipher initialization complete."); - - circ->expire = layer->expire; - - return 0; -} - circuit_t *circuit_enumerate_by_naddr_nport(circuit_t *circ, uint32_t naddr, uint16_t nport) { if(!circ) /* use circ if it's defined, else start from the beginning */ @@ -269,13 +209,11 @@ circuit_t *circuit_get_newest_ap(void) { circuit_t *circ, *bestcirc=NULL; for(circ=global_circuitlist;circ;circ = circ->next) { - if(!circ->p_conn || circ->p_conn->type == CONN_TYPE_AP) { - if(circ->state == CIRCUIT_STATE_OPEN && (!bestcirc || - bestcirc->timestamp_created < circ->timestamp_created)) { - log(LOG_DEBUG,"circuit_get_newest_ap(): Choosing n_aci %d.", circ->n_aci); - assert(circ->n_aci); - bestcirc = circ; - } + if(circ->cpath && circ->state == CIRCUIT_STATE_OPEN && (!bestcirc || + bestcirc->timestamp_created < circ->timestamp_created)) { + log(LOG_DEBUG,"circuit_get_newest_ap(): Choosing n_aci %d.", circ->n_aci); + assert(circ->n_aci); + bestcirc = circ; } } return bestcirc; @@ -331,7 +269,7 @@ int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ, buf[0] = cell->length; memcpy(buf+1, cell->payload, CELL_PAYLOAD_SIZE); - log(LOG_DEBUG,"circuit_deliver_relay_cell(): streamid %d before crypt.", *(int*)(cell->payload+1)); + log(LOG_DEBUG,"circuit_deliver_relay_cell(): direction %d, streamid %d before crypt.", cell_direction, *(int*)(cell->payload+1)); if(relay_crypt(circ, buf, 1+CELL_PAYLOAD_SIZE, cell_direction, layer_hint, &recognized, &conn) < 0) { log(LOG_DEBUG,"circuit_deliver_relay_cell(): relay crypt failed. Dropping connection."); @@ -363,6 +301,7 @@ int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ, return 0; } + log(LOG_DEBUG,"circuit_deliver_relay_cell(): Passing on unrecognized cell."); return connection_write_cell_to_buf(cell, conn); } @@ -378,32 +317,42 @@ int relay_crypt(circuit_t *circ, char *in, int inlen, char cell_direction, if(cell_direction == CELL_DIRECTION_IN) { if(circ->cpath) { /* we're at the beginning of the circuit. We'll want to do layered crypts. */ thishop = circ->cpath; + if(thishop->state != CPATH_STATE_OPEN) { + log(LOG_INFO,"relay_crypt(): Relay cell before first created cell?"); + return -1; + } do { /* Remember: cpath is in forward order, that is, first hop first. */ assert(thishop); + log(LOG_DEBUG,"relay_crypt(): before decrypt: %d",*(int*)(in+2)); /* decrypt */ if(crypto_cipher_decrypt(thishop->b_crypto, in, inlen, out)) { log(LOG_ERR,"Error performing decryption:%s",crypto_perror()); return -1; } memcpy(in,out,inlen); + log(LOG_DEBUG,"relay_crypt(): after decrypt: %d",*(int*)(in+2)); if( (*recognized = relay_check_recognized(circ, cell_direction, in+2, conn))) return 0; thishop = thishop->next; - } while(thishop != circ->cpath); + } while(thishop != circ->cpath && thishop->state == CPATH_STATE_OPEN); log(LOG_INFO,"relay_crypt(): in-cell at OP not recognized. Killing circuit."); - return -1; + return 0; +// return -1; } else { /* we're in the middle. Just one crypt. */ + log(LOG_DEBUG,"relay_crypt(): before encrypt: %d",*(int*)(in+2)); if(crypto_cipher_encrypt(circ->p_crypto, in, inlen, out)) { log(LOG_ERR,"circuit_encrypt(): Encryption failed for ACI : %u (%s).", circ->p_aci, crypto_perror()); return -1; } memcpy(in,out,inlen); + log(LOG_DEBUG,"relay_crypt(): after encrypt: %d",*(int*)(in+2)); + log(LOG_DEBUG,"circuit_encrypt(): Skipping recognized check, because we're not the OP."); /* don't check for recognized. only the OP can recognize a stream on the way back. */ } @@ -415,12 +364,13 @@ int relay_crypt(circuit_t *circ, char *in, int inlen, char cell_direction, do { assert(thishop); - /* encrypt */ + log(LOG_DEBUG,"relay_crypt(): before encrypt: %d",*(int*)(in+2)); if(crypto_cipher_encrypt(thishop->f_crypto, in, inlen, out)) { log(LOG_ERR,"Error performing encryption:%s",crypto_perror()); return -1; } memcpy(in,out,inlen); + log(LOG_DEBUG,"relay_crypt(): after encrypt: %d",*(int*)(in+2)); thishop = thishop->prev; } while(thishop != circ->cpath->prev); @@ -450,8 +400,10 @@ int relay_check_recognized(circuit_t *circ, int cell_direction, char *stream, co connection_t *tmpconn; log(LOG_DEBUG,"relay_check_recognized(): entering"); - if(!memcmp(stream,ZERO_STREAM,STREAM_ID_SIZE)) + if(!memcmp(stream,ZERO_STREAM,STREAM_ID_SIZE)) { + log(LOG_DEBUG,"relay_check_recognized(): It's the zero stream. Recognized."); return 1; /* the zero stream is always recognized */ + } if(cell_direction == CELL_DIRECTION_OUT) tmpconn = circ->n_conn; @@ -459,8 +411,10 @@ int relay_check_recognized(circuit_t *circ, int cell_direction, char *stream, co tmpconn = circ->p_conn; log(LOG_DEBUG,"relay_check_recognized(): not the zero stream."); - if(!tmpconn) + if(!tmpconn) { + log(LOG_DEBUG,"relay_check_recognized(): No conns. Not recognized."); return 0; /* no conns? don't recognize it */ + } while(tmpconn && tmpconn->type == CONN_TYPE_OR) { log(LOG_DEBUG,"relay_check_recognized(): skipping over an OR conn"); @@ -723,7 +677,7 @@ retry_circuit: return; } - if(circuit_create_onion() < 0) { + if(circuit_establish_circuit() < 0) { failures++; goto retry_circuit; } @@ -732,50 +686,21 @@ retry_circuit: return; } -int circuit_create_onion(void) { - int routelen; /* length of the route */ - unsigned int *route; /* hops in the route as an array of indexes into rarray */ - unsigned char *onion; /* holds the onion */ - int onionlen; /* onion length in host order */ - crypt_path_t *cpath; /* defines the crypt operations that need to be performed on incoming/outgoing data */ - - /* choose a route */ - route = (unsigned int *)router_new_route(&routelen); - if (!route) { - log(LOG_ERR,"circuit_create_onion(): Error choosing a route through the OR network."); - return -1; - } - log(LOG_DEBUG,"circuit_create_onion(): Chosen a route of length %u : ",routelen); - - /* create an onion and calculate crypto keys */ - onion = router_create_onion(route,routelen,&onionlen, &cpath); - if (!onion) { - log(LOG_ERR,"circuit_create_onion(): Error creating an onion."); - free(route); - return -1; - } - log(LOG_DEBUG,"circuit_create_onion(): Created an onion of size %u bytes.",onionlen); -// log(LOG_DEBUG,"circuit_create_onion(): Crypt path :"); - - return circuit_establish_circuit(route, routelen, onion, onionlen, cpath); -} - -int circuit_establish_circuit(unsigned int *route, int routelen, char *onion, - int onionlen, crypt_path_t *cpath) { +int circuit_establish_circuit(void) { routerinfo_t *firsthop; connection_t *n_conn; circuit_t *circ; - /* now see if we're already connected to the first OR in 'route' */ - firsthop = router_get_first_in_route(route, routelen); - assert(firsthop); /* should always be defined */ - free(route); /* we don't need it anymore */ - circ = circuit_new(0, NULL); /* sets circ->p_aci and circ->p_conn */ circ->state = CIRCUIT_STATE_OR_WAIT; - circ->onion = onion; - circ->onionlen = onionlen; - circ->cpath = cpath; + circ->cpath = onion_generate_cpath(&firsthop); + if(!circ->cpath) { + log(LOG_DEBUG,"circuit_establish_circuit(): Generating cpath failed."); + circuit_close(circ); + return -1; + } + + /* now see if we're already connected to the first OR in 'route' */ log(LOG_DEBUG,"circuit_establish_circuit(): Looking for firsthop '%s:%u'", firsthop->address,firsthop->or_port); @@ -798,14 +723,22 @@ int circuit_establish_circuit(unsigned int *route, int routelen, char *onion, } } + log(LOG_DEBUG,"circuit_establish_circuit(): connecting in progress (or finished). Good."); return 0; /* return success. The onion/circuit/etc will be taken care of automatically * (may already have been) whenever n_conn reaches OR_CONN_STATE_OPEN. */ } else { /* it (or a twin) is already open. use it. */ circ->n_addr = n_conn->addr; circ->n_port = n_conn->port; - return circuit_send_onion(n_conn, circ); + circ->n_conn = n_conn; + log(LOG_DEBUG,"circuit_establish_circuit(): Conn open. Delivering first onion skin."); + if(circuit_send_next_onion_skin(circ) < 0) { + log(LOG_DEBUG,"circuit_establish_circuit(): circuit_send_next_onion_skin failed."); + circuit_close(circ); + return -1; + } } + return 0; } /* find circuits that are waiting on me, if any, and get them to send the onion */ @@ -818,8 +751,9 @@ void circuit_n_conn_open(connection_t *or_conn) { if(!circ) return; - log(LOG_DEBUG,"circuit_n_conn_open(): Found circ, sending onion."); - if(circuit_send_onion(or_conn, circ) < 0) { + log(LOG_DEBUG,"circuit_n_conn_open(): Found circ, sending onion skin."); + circ->n_conn = or_conn; + if(circuit_send_next_onion_skin(circ) < 0) { log(LOG_DEBUG,"circuit_n_conn_open(): circuit marked for closing."); circuit_close(circ); return; /* FIXME will want to try the other circuits too? */ @@ -828,50 +762,182 @@ void circuit_n_conn_open(connection_t *or_conn) { } } -int circuit_send_onion(connection_t *n_conn, circuit_t *circ) { +int circuit_send_next_onion_skin(circuit_t *circ) { cell_t cell; - int tmpbuflen, dataleft; - char *tmpbuf; + crypt_path_t *hop; + routerinfo_t *router; - circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, ACI_TYPE_BOTH); - circ->n_conn = n_conn; - log(LOG_DEBUG,"circuit_send_onion(): n_conn is %s:%u",n_conn->address,n_conn->port); + assert(circ && circ->cpath); - /* deliver the onion as one or more create cells */ - cell.command = CELL_CREATE; - cell.aci = circ->n_aci; + if(circ->cpath->state == CPATH_STATE_CLOSED) { - tmpbuflen = circ->onionlen+4; - tmpbuf = malloc(tmpbuflen); - if(!tmpbuf) - return -1; - *(uint32_t*)tmpbuf = htonl(circ->onionlen); - memcpy(tmpbuf+4, circ->onion, circ->onionlen); + log(LOG_DEBUG,"circuit_send_next_onion_skin(): First skin; sending create cell."); + circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, ACI_TYPE_BOTH); - dataleft = tmpbuflen; - while(dataleft) { + memset(&cell, 0, sizeof(cell_t)); cell.command = CELL_CREATE; cell.aci = circ->n_aci; - log(LOG_DEBUG,"circuit_send_onion(): Sending a create cell for the onion..."); - if(dataleft >= CELL_PAYLOAD_SIZE) { - cell.length = CELL_PAYLOAD_SIZE; - memcpy(cell.payload, tmpbuf + tmpbuflen - dataleft, CELL_PAYLOAD_SIZE); - connection_write_cell_to_buf(&cell, n_conn); - dataleft -= CELL_PAYLOAD_SIZE; - } else { /* last cell */ - cell.length = dataleft; - memcpy(cell.payload, tmpbuf + tmpbuflen - dataleft, dataleft); - /* fill extra space with 0 bytes */ - memset(cell.payload + dataleft, 0, CELL_PAYLOAD_SIZE - dataleft); - connection_write_cell_to_buf(&cell, n_conn); - dataleft = 0; + cell.length = 208; + + if(onion_skin_create(circ->n_conn->pkey, &(circ->cpath->handshake_state), cell.payload) < 0) { + log(LOG_INFO,"circuit_send_next_onion_skin(): onion_skin_create (first hop) failed."); + return -1; + } + + if(connection_write_cell_to_buf(&cell, circ->n_conn) < 0) { + return -1; + } + + circ->cpath->state = CPATH_STATE_AWAITING_KEYS; + circ->state = CIRCUIT_STATE_BUILDING; + log(LOG_DEBUG,"circuit_send_next_onion_skin(): first skin; finished sending create cell."); + } else { + assert(circ->cpath->state == CPATH_STATE_OPEN); + assert(circ->state == CIRCUIT_STATE_BUILDING); + log(LOG_DEBUG,"circuit_send_next_onion_skin(): starting to send subsequent skin."); + for(hop=circ->cpath->next; + hop != circ->cpath && hop->state == CPATH_STATE_OPEN; + hop=hop->next) ; + if(hop == circ->cpath) { /* done building the circuit. whew. */ + circ->state = CIRCUIT_STATE_OPEN; + log(LOG_DEBUG,"circuit_send_next_onion_skin(): circuit built!"); + return 0; + } + + router = router_get_by_addr_port(hop->addr,hop->port); + if(!router) { + log(LOG_INFO,"circuit_send_next_onion_skin(): couldn't lookup router %d:%d",hop->addr,hop->port); + return -1; + } + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_RELAY; + cell.aci = circ->n_aci; + SET_CELL_RELAY_COMMAND(cell, RELAY_COMMAND_EXTEND); + SET_CELL_STREAM_ID(cell, ZERO_STREAM); + + cell.length = RELAY_HEADER_SIZE + 6 + 208; + *(uint32_t*)(cell.payload+RELAY_HEADER_SIZE) = htonl(hop->addr); + *(uint32_t*)(cell.payload+RELAY_HEADER_SIZE+4) = htons(hop->port); + if(onion_skin_create(router->pkey, &(hop->handshake_state), cell.payload+RELAY_HEADER_SIZE+6) < 0) { + log(LOG_INFO,"circuit_send_next_onion_skin(): onion_skin_create failed."); + return -1; + } + + log(LOG_DEBUG,"circuit_send_next_onion_skin(): Sending extend relay cell."); + /* send it to hop->prev, because it will transfer it to a create cell and then send to hop */ + if(circuit_deliver_relay_cell_from_edge(&cell, circ, EDGE_AP, hop->prev) < 0) { + log(LOG_DEBUG,"circuit_send_next_onion_skin(): failed to deliver extend cell. Closing."); + return -1; + } + hop->state = CPATH_STATE_AWAITING_KEYS; + } + return 0; +} + +/* take the 'extend' cell, pull out addr/port plus the onion skin. Connect + * to the next hop, and pass it the onion skin in a create cell. + */ +int circuit_extend(cell_t *cell, circuit_t *circ) { + connection_t *n_conn; + aci_t aci_type; + struct sockaddr_in me; /* my router identity */ + cell_t newcell; + + circ->n_addr = ntohl(*(uint32_t*)(cell->payload+RELAY_HEADER_SIZE)); + circ->n_port = ntohs(*(uint16_t*)(cell->payload+RELAY_HEADER_SIZE+4)); + + if(learn_my_address(&me) < 0) + return -1; + + n_conn = connection_twin_get_by_addr_port(circ->n_addr,circ->n_port); + if(!n_conn || n_conn->type != CONN_TYPE_OR) { + /* i've disabled making connections through OPs, but it's definitely + * possible here. I'm not sure if it would be a bug or a feature. -RD + */ + /* note also that this will close circuits where the onion has the same + * router twice in a row in the path. i think that's ok. -RD + */ + log(LOG_DEBUG,"circuit_extend(): Next router not connected. Closing."); + /* XXX later we should fail more gracefully here, like with a 'truncated' */ + return -1; + } + + circ->n_addr = n_conn->addr; /* these are different if we found a twin instead */ + circ->n_port = n_conn->port; + + circ->n_conn = n_conn; + log(LOG_DEBUG,"circuit_extend(): n_conn is %s:%u",n_conn->address,n_conn->port); + + aci_type = decide_aci_type(ntohl(me.sin_addr.s_addr), ntohs(me.sin_port), + circ->n_addr, circ->n_port); + + log(LOG_DEBUG,"circuit_extend(): aci_type = %u.",aci_type); + circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, aci_type); + if(!circ->n_aci) { + log(LOG_ERR,"circuit_extend(): failed to get unique aci."); + return -1; + } + log(LOG_DEBUG,"circuit_extend(): Chosen ACI %u.",circ->n_aci); + + memset(&newcell, 0, sizeof(cell_t)); + newcell.command = CELL_CREATE; + newcell.aci = circ->n_aci; + newcell.length = 208; + + memcpy(newcell.payload, cell->payload+RELAY_HEADER_SIZE+6, 208); + + if(connection_write_cell_to_buf(&newcell, circ->n_conn) < 0) { + return -1; + } + + return 0; +} + +int circuit_finish_handshake(circuit_t *circ, char *reply) { + unsigned char iv[16]; + unsigned char keys[32]; + crypt_path_t *hop; + + memset(iv, 0, 16); + + assert(circ->cpath); + if(circ->cpath->state == CPATH_STATE_AWAITING_KEYS) + hop = circ->cpath; + else { + for(hop=circ->cpath->next; + hop != circ->cpath && hop->state == CPATH_STATE_OPEN; + hop=hop->next) ; + if(hop == circ->cpath) { /* got an extended when we're all done? */ + log(LOG_INFO,"circuit_finish_handshake(): got extended when circ already built? Weird."); + return 0; } } - free(tmpbuf); + assert(hop->state == CPATH_STATE_AWAITING_KEYS); - circ->state = CIRCUIT_STATE_OPEN; - /* FIXME should set circ->expire to something here */ + if(onion_skin_client_handshake(hop->handshake_state, reply, keys, 32) < 0) { + log(LOG_ERR,"circuit_finish_handshake(): onion_skin_client_handshake failed."); + return -1; + } + crypto_dh_free(hop->handshake_state); /* don't need it anymore */ + hop->handshake_state = NULL; + + log(LOG_DEBUG,"circuit_finish_handshake(): hop %d init cipher forward %d, backward %d.", hop, *(int*)keys, *(int*)(keys+16)); + if (!(hop->f_crypto = + crypto_create_init_cipher(DEFAULT_CIPHER,keys,iv,1))) { + log(LOG_ERR,"Cipher initialization failed."); + return -1; + } + + if (!(hop->b_crypto = + crypto_create_init_cipher(DEFAULT_CIPHER,keys+16,iv,0))) { + log(LOG_ERR,"Cipher initialization failed."); + return -1; + } + + hop->state = CPATH_STATE_OPEN; + log(LOG_DEBUG,"circuit_finish_handshake(): Completed."); return 0; } diff --git a/src/or/command.c b/src/or/command.c index d589e71b1b..0f7830e986 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -28,8 +28,8 @@ void command_time_process_cell(cell_t *cell, connection_t *conn, } void command_process_cell(cell_t *cell, connection_t *conn) { - static int num_create=0, num_relay=0, num_destroy=0, num_sendme=0; - static int create_time=0, relay_time=0, destroy_time=0, sendme_time=0; + static int num_create=0, num_created=0, num_relay=0, num_destroy=0, num_sendme=0; + static int create_time=0, created_time=0, relay_time=0, destroy_time=0, sendme_time=0; static long current_second = 0; /* from previous calls to gettimeofday */ struct timeval now; @@ -39,18 +39,20 @@ void command_process_cell(cell_t *cell, connection_t *conn) { /* print stats */ log(LOG_INFO,"At end of second:"); log(LOG_INFO,"Create: %d (%d ms)", num_create, create_time/1000); + log(LOG_INFO,"Created: %d (%d ms)", num_created, created_time/1000); log(LOG_INFO,"Relay: %d (%d ms)", num_relay, relay_time/1000); log(LOG_INFO,"Destroy: %d (%d ms)", num_destroy, destroy_time/1000); log(LOG_INFO,"Sendme: %d (%d ms)", num_sendme, sendme_time/1000); /* zero out stats */ - num_create = num_relay = num_destroy = num_sendme = 0; - create_time = relay_time = destroy_time = sendme_time = 0; + num_create = num_created = num_relay = num_destroy = num_sendme = 0; + create_time = created_time = relay_time = destroy_time = sendme_time = 0; /* remember which second it is, for next time */ current_second = now.tv_sec; } + log(LOG_DEBUG,"command_process_cell(): Examining cell type %d.", cell->command); switch(cell->command) { case CELL_PADDING: /* do nothing */ @@ -59,6 +61,10 @@ void command_process_cell(cell_t *cell, connection_t *conn) { command_time_process_cell(cell, conn, &num_create, &create_time, command_process_create_cell); break; + case CELL_CREATED: + command_time_process_cell(cell, conn, &num_created, &created_time, + command_process_created_cell); + break; case CELL_RELAY: command_time_process_cell(cell, conn, &num_relay, &relay_time, command_process_relay_cell); @@ -82,61 +88,77 @@ void command_process_create_cell(cell_t *cell, connection_t *conn) { circ = circuit_get_by_aci_conn(cell->aci, conn); - if(circ && circ->state != CIRCUIT_STATE_ONION_WAIT) { - log(LOG_DEBUG,"command_process_create_cell(): received CREATE cell, not in onion_wait. Dropping."); + if(circ) { + log(LOG_DEBUG,"command_process_create_cell(): received CREATE cell for known circ. Dropping."); return; } - if(!circ) { /* if it's not there, create it */ - circ = circuit_new(cell->aci, conn); - circ->state = CIRCUIT_STATE_ONION_WAIT; - circ->onionlen = ntohl(*(int*)cell->payload); - log(LOG_DEBUG,"command_process_create_cell(): Onion length is %u.",circ->onionlen); - if(circ->onionlen > 50000 || circ->onionlen < 1) { /* too big or too small */ - log(LOG_DEBUG,"That's ludicrous. Closing."); - circuit_close(circ); - return; - } - circ->onion = malloc(circ->onionlen); - if(!circ->onion) { - log(LOG_DEBUG,"command_process_create_cell(): Out of memory. Closing."); - circuit_close(circ); - return; - } - if(circ->onionlen < cell->length-4) { /* protect from buffer overflow */ - log(LOG_DEBUG,"command_process_create_cell(): Onion too small. Closing."); - circuit_close(circ); - return; - } - memcpy((void *)circ->onion,(void *)(cell->payload+4),cell->length-4); - circ->recvlen = cell->length-4; - log(LOG_DEBUG,"command_process_create_cell(): Primary create cell handled, have received %d of %d onion bytes.", - circ->recvlen,circ->onionlen); - - } else { /* pull over as much of the onion as we can */ - if(cell->length + circ->recvlen > circ->onionlen) { /* protect from buffer overflow */ - log(LOG_DEBUG,"command_process_create_cell(): payload too big for onion. Closing."); - circuit_close(circ); - return; - } - memcpy((void *)(circ->onion+circ->recvlen),(void *)cell->payload,cell->length); - circ->recvlen += cell->length; - log(LOG_DEBUG,"command_process_create_cell(): Secondary create cell handled, have received %d of %d onion bytes (aci %d)", - circ->recvlen,circ->onionlen,circ->p_aci); - } - - if(circ->recvlen != circ->onionlen) { - log(LOG_DEBUG,"command_process_create_cell(): Onion not all here yet. Ok."); + circ = circuit_new(cell->aci, conn); + circ->state = CIRCUIT_STATE_ONIONSKIN_PENDING; + if(cell->length != 208) { + log(LOG_DEBUG,"command_process_create_cell(): Bad cell length %d. Dropping.", cell->length); + circuit_close(circ); return; } + memcpy(circ->onionskin,cell->payload,cell->length); + /* add it to the pending onions queue, and then return */ - circ->state = CIRCUIT_STATE_ONION_PENDING; - if(onion_pending_add(circ) < 0) { - log(LOG_DEBUG,"command_process_create_cell(): Failed to queue onion. Closing."); + log(LOG_DEBUG,"command_process_create_cell(): Failed to queue onionskin. Closing."); circuit_close(circ); } + log(LOG_DEBUG,"command_process_create_cell(): success: queued onionskin."); + return; +} + +void command_process_created_cell(cell_t *cell, connection_t *conn) { + circuit_t *circ; + cell_t newcell; + + circ = circuit_get_by_aci_conn(cell->aci, conn); + + if(!circ) { + log(LOG_DEBUG,"command_process_created_cell(): received CREATED cell for unknown circ. Dropping."); + return; + } + + if(circ->n_aci != cell->aci) { + log(LOG_DEBUG,"command_process_created_cell(): got created cell from OPward? Dropping."); + return; + } + assert(cell->length == 192); + + if(circ->cpath) { /* we're the OP. Handshake this. */ + log(LOG_DEBUG,"command_process_created_cell(): at OP. Finishing handshake."); + if(circuit_finish_handshake(circ, cell->payload) < 0) { + log(LOG_INFO,"command_process_created_cell(): circuit_finish_handshake failed."); + circuit_close(circ); + return; + } + log(LOG_DEBUG,"command_process_created_cell(): Moving to next skin."); + if(circuit_send_next_onion_skin(circ) < 0) { + log(LOG_INFO,"command_process_created_cell(): circuit_send_next_onion_skin failed."); + circuit_close(circ); + return; + } + } else { /* pack it into an extended relay cell, and send it. */ + memset(&newcell, 0, sizeof(cell_t)); + newcell.command = CELL_RELAY; + newcell.aci = circ->p_aci; + SET_CELL_RELAY_COMMAND(newcell, RELAY_COMMAND_EXTENDED); + SET_CELL_STREAM_ID(newcell, ZERO_STREAM); + + newcell.length = RELAY_HEADER_SIZE + cell->length; + memcpy(newcell.payload+RELAY_HEADER_SIZE, cell->payload, 192); + + log(LOG_DEBUG,"command_process_created_cell(): Sending extended relay cell."); + if(circuit_deliver_relay_cell_from_edge(&newcell, circ, EDGE_EXIT, NULL) < 0) { + log(LOG_DEBUG,"command_process_created_cell(): failed to deliver extended cell. Closing."); + circuit_close(circ); + return; + } + } return; } @@ -150,6 +172,7 @@ void command_process_sendme_cell(cell_t *cell, connection_t *conn) { return; } +#if 0 if(circ->state == CIRCUIT_STATE_ONION_WAIT) { log(LOG_DEBUG,"command_process_sendme_cell(): circuit in onion_wait. Dropping."); return; @@ -158,6 +181,7 @@ void command_process_sendme_cell(cell_t *cell, connection_t *conn) { log(LOG_DEBUG,"command_process_sendme_cell(): circuit in or_wait. Dropping."); return; } +#endif /* at this point both circ->n_conn and circ->p_conn are guaranteed to be set */ @@ -205,7 +229,7 @@ void command_process_relay_cell(cell_t *cell, connection_t *conn) { return; } - if(circ->state == CIRCUIT_STATE_ONION_PENDING) { + if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { log(LOG_DEBUG,"command_process_relay_cell(): circuit in create_wait. Queueing relay cell."); onion_pending_relay_add(circ, cell); return; @@ -213,22 +237,23 @@ void command_process_relay_cell(cell_t *cell, connection_t *conn) { if(cell->aci == circ->p_aci) { /* it's an outgoing cell */ if(--circ->p_receive_circwindow < 0) { /* is it less than 0 after decrement? */ - log(LOG_INFO,"connection_process_relay_cell(): Too many relay cells for out circuit (aci %d). Closing.", circ->p_aci); + log(LOG_INFO,"command_process_relay_cell(): Too many relay cells for out circuit (aci %d). Closing.", circ->p_aci); circuit_close(circ); return; } - log(LOG_DEBUG,"connection_process_relay_cell(): p_receive_circwindow for aci %d is %d.",circ->p_aci,circ->p_receive_circwindow); + log(LOG_DEBUG,"command_process_relay_cell(): p_receive_circwindow for aci %d is %d.",circ->p_aci,circ->p_receive_circwindow); } if(cell->aci == circ->n_aci) { /* it's an ingoing cell */ if(--circ->n_receive_circwindow < 0) { /* is it less than 0 after decrement? */ - log(LOG_INFO,"connection_process_relay_cell(): Too many relay cells for in circuit (aci %d). Closing.", circ->n_aci); + log(LOG_INFO,"command_process_relay_cell(): Too many relay cells for in circuit (aci %d). Closing.", circ->n_aci); circuit_close(circ); return; } - log(LOG_DEBUG,"connection_process_relay_cell(): n_receive_circwindow for aci %d is %d.",circ->n_aci,circ->n_receive_circwindow); + log(LOG_DEBUG,"command_process_relay_cell(): n_receive_circwindow for aci %d is %d.",circ->n_aci,circ->n_receive_circwindow); } +#if 0 if(circ->state == CIRCUIT_STATE_ONION_WAIT) { log(LOG_WARNING,"command_process_relay_cell(): circuit in onion_wait. Dropping relay cell."); return; @@ -237,6 +262,7 @@ void command_process_relay_cell(cell_t *cell, connection_t *conn) { log(LOG_WARNING,"command_process_relay_cell(): circuit in or_wait. Dropping relay cell."); return; } +#endif /* circ->p_conn and n_conn are only null if we're at an edge point with no connections yet */ if(cell->aci == circ->p_aci) { /* it's an outgoing cell */ @@ -267,7 +293,7 @@ void command_process_destroy_cell(cell_t *cell, connection_t *conn) { } log(LOG_DEBUG,"command_process_destroy_cell(): Received for aci %d.",cell->aci); - if(circ->state == CIRCUIT_STATE_ONION_PENDING) { + if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { onion_pending_remove(circ); } diff --git a/src/or/connection.c b/src/or/connection.c index 5b29d2120f..698cee656b 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -607,6 +607,8 @@ int connection_encrypt_cell(char *cellp, connection_t *conn) { printf("\n"); #endif + assert(conn); + if(crypto_cipher_encrypt(conn->f_crypto, cellp, CELL_NETWORK_SIZE, cryptcell)) { log(LOG_ERR,"Could not encrypt cell for connection %s:%u.",conn->address,conn->port); return -1; diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 1f00391370..bae7ae8d0c 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -23,7 +23,7 @@ int connection_edge_process_inbuf(connection_t *conn) { circ = circuit_get_by_conn(conn); if (!circ) return -1; - + memset(&cell, 0, sizeof(cell_t)); cell.command = CELL_RELAY; cell.length = RELAY_HEADER_SIZE; @@ -123,13 +123,12 @@ int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection if(edge_type == EDGE_AP) { log(LOG_INFO,"connection_edge_process_relay_cell(): relay begin request unsupported. Dropping."); return 0; - } else { - if(conn) { - log(LOG_INFO,"connection_edge_process_relay_cell(): begin cell for known stream. Dropping."); - return 0; - } - return connection_exit_begin_conn(cell, circ); } + if(conn) { + log(LOG_INFO,"connection_edge_process_relay_cell(): begin cell for known stream. Dropping."); + return 0; + } + return connection_exit_begin_conn(cell, circ); case RELAY_COMMAND_DATA: if(!conn) { log(LOG_DEBUG,"connection_edge_process_relay_cell(): relay cell dropped, unknown stream %d.",*(int*)conn->stream_id); @@ -166,14 +165,6 @@ int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection } log(LOG_DEBUG,"connection_edge_process_relay_cell(): end cell for stream %d. Removing stream.",*(int*)conn->stream_id); - /* go through and identify who points to conn. remove conn from the list. */ -#if 0 - if(conn == circ->p_conn) { - circ->p_conn = conn->next_stream; - } - for(prevconn = circ->p_conn; prevconn->next_stream != conn; prevconn = prevconn->next_stream) ; - prevconn->next_stream = conn->next_stream; -#endif #ifdef HALF_OPEN conn->done_sending = 1; shutdown(conn->s, 1); /* XXX check return; refactor NM */ @@ -182,6 +173,23 @@ int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection #endif conn->marked_for_close = 1; break; + case RELAY_COMMAND_EXTEND: + if(conn) { + log(LOG_INFO,"connection_edge_process_relay_cell(): 'extend' for non-zero stream. Dropping."); + return 0; + } + return circuit_extend(cell, circ); + case RELAY_COMMAND_EXTENDED: + if(edge_type == EDGE_EXIT) { + log(LOG_INFO,"connection_edge_process_relay_cell(): 'extended' unsupported at exit. Dropping."); + return 0; + } + log(LOG_DEBUG,"connection_edge_process_relay_cell(): Got an extended cell! Yay."); + if(circuit_finish_handshake(circ, cell->payload+RELAY_HEADER_SIZE) < 0) { + log(LOG_INFO,"connection_edge_process_relay_cell(): circuit_finish_handshake failed."); + return -1; + } + return circuit_send_next_onion_skin(circ); case RELAY_COMMAND_CONNECTED: if(edge_type == EDGE_EXIT) { log(LOG_INFO,"connection_edge_process_relay_cell(): 'connected' unsupported at exit. Dropping."); diff --git a/src/or/main.c b/src/or/main.c index 591d568fcd..4201c9d297 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -337,7 +337,6 @@ int prepare_for_poll(int *timeout) { for(i=0;ionions_handled_this_second = 0; /* check connections to see whether we should send a keepalive, expire, or wait */ if(!connection_speaks_cells(tmpconn)) @@ -683,7 +682,6 @@ int tor_main(int argc, char *argv[]) { } } - init_tracked_tree(); /* initialize the replay detection tree */ init_cache_tree(); /* initialize the dns resolve tree */ signal (SIGINT, catch); /* catch kills so we can exit cleanly */ diff --git a/src/or/onion.c b/src/or/onion.c index 35343ea1b1..9a788284ea 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -6,11 +6,8 @@ extern or_options_t options; /* command-line and config-file options */ -static int onion_process(circuit_t *circ); -static int onion_deliver_to_conn(aci_t aci, unsigned char *onion, uint32_t onionlen, connection_t *conn); static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len); -static int find_tracked_onion(unsigned char *onion, uint32_t onionlen, - int expire); +static int onionskin_process(circuit_t *circ); int decide_aci_type(uint32_t local_addr, uint16_t local_port, uint32_t remote_addr, uint16_t remote_port) { @@ -76,17 +73,19 @@ void onion_pending_process_one(void) { if(!ol_list) return; /* no onions pending, we're done */ - assert(ol_list->circ && ol_list->circ->p_conn); + assert(ol_list->circ); + assert(ol_list->circ->p_conn); assert(ol_length > 0); circ = ol_list->circ; - if(onion_process(circ) < 0) { + if(onionskin_process(circ) < 0) { log(LOG_DEBUG,"onion_pending_process_one(): Failed. Closing."); onion_pending_remove(circ); circuit_close(circ); } else { log(LOG_DEBUG,"onion_pending_process_one(): Succeeded. Delivering queued relay cells."); for(tmpd = ol_list->relay_cells; tmpd; tmpd=tmpd->next) { + log(LOG_DEBUG,"onion_pending_process_one(): Delivering relay cell..."); command_process_relay_cell(tmpd->cell, circ->p_conn); } onion_pending_remove(circ); @@ -174,136 +173,48 @@ void onion_pending_relay_add(circuit_t *circ, cell_t *cell) { } } -/* helper function for onion_process */ -static int onion_deliver_to_conn(aci_t aci, unsigned char *onion, uint32_t onionlen, connection_t *conn) { - char *buf; - int buflen, dataleft; +/* learn keys, initialize, then send a created cell back */ +static int onionskin_process(circuit_t *circ) { + unsigned char iv[16]; + unsigned char keys[32]; cell_t cell; - - assert(aci && onion && onionlen); - - buflen = onionlen+4; - buf = malloc(buflen); - if(!buf) - return -1; - - log(LOG_DEBUG,"onion_deliver_to_conn(): Setting onion length to %u.",onionlen); - *(uint32_t*)buf = htonl(onionlen); - memcpy((buf+4),onion,onionlen); - - dataleft = buflen; - while(dataleft > 0) { - memset(&cell,0,sizeof(cell_t)); - cell.command = CELL_CREATE; - cell.aci = aci; - if(dataleft >= CELL_PAYLOAD_SIZE) - cell.length = CELL_PAYLOAD_SIZE; - else - cell.length = dataleft; - memcpy(cell.payload, buf+buflen-dataleft, cell.length); - dataleft -= cell.length; - - log(LOG_DEBUG,"onion_deliver_to_conn(): Delivering create cell, payload %d bytes.",cell.length); - if(connection_write_cell_to_buf(&cell, conn) < 0) { - log(LOG_DEBUG,"onion_deliver_to_conn(): Could not buffer new create cells. Closing."); - free(buf); - return -1; - } - } - free(buf); - return 0; -} -static int onion_process(circuit_t *circ) { - connection_t *n_conn; - int retval; - aci_t aci_type; - struct sockaddr_in me; /* my router identity */ - onion_layer_t layer; + memset(iv, 0, 16); - if(learn_my_address(&me) < 0) - return -1; + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_CREATED; + cell.aci = circ->p_aci; + cell.length = 192; - /* decrypt it in-place */ - if(decrypt_onion(circ->onion,circ->onionlen,getprivatekey(),&layer) < 0) { - log(LOG_DEBUG,"command_process_create_cell(): decrypt_onion() failed, closing circuit."); - return -1; - } - log(LOG_DEBUG,"command_process_create_cell(): Onion decrypted."); + circ->state = CIRCUIT_STATE_OPEN; - /* check freshness */ - if (layer.expire < (uint32_t)time(NULL)) /* expired onion */ /*XXXX*/ - { - log(LOG_NOTICE,"I have just received an expired onion. This could be a replay attack."); + log(LOG_DEBUG,"onionskin_process(): Entering."); + + if(onion_skin_server_handshake(circ->onionskin, getprivatekey(), + cell.payload, keys, 32) < 0) { + log(LOG_ERR,"onionskin_process(): onion_skin_server_handshake failed."); return -1; } - aci_type = decide_aci_type(ntohl(me.sin_addr.s_addr), ntohs(me.sin_port), - layer.addr, layer.port); - - if(circuit_init(circ, aci_type, &layer) < 0) { - log(LOG_ERR,"process_onion(): init_circuit() failed."); + log(LOG_DEBUG,"onionskin_process: init cipher forward %d, backward %d.", *(int*)keys, *(int*)(keys+16)); + + if (!(circ->n_crypto = + crypto_create_init_cipher(DEFAULT_CIPHER,keys,iv,0))) { + log(LOG_ERR,"Cipher initialization failed."); return -1; } - /* check for replay. at the same time, add it to the pile of tracked onions. */ - if(find_tracked_onion(circ->onion, circ->onionlen, layer.expire)) { - log(LOG_NOTICE,"process_onion(): I have just received a replayed onion. This could be a replay attack."); + if (!(circ->p_crypto = + crypto_create_init_cipher(DEFAULT_CIPHER,keys+16,iv,1))) { + log(LOG_ERR,"Cipher initialization failed."); return -1; } - /* now we must send create cells to the next router */ - if(circ->n_addr && circ->n_port) { - n_conn = connection_twin_get_by_addr_port(circ->n_addr,circ->n_port); - if(!n_conn || n_conn->type != CONN_TYPE_OR) { - /* i've disabled making connections through OPs, but it's definitely - * possible here. I'm not sure if it would be a bug or a feature. -RD - */ - /* note also that this will close circuits where the onion has the same - * router twice in a row in the path. i think that's ok. -RD - */ - log(LOG_DEBUG,"command_process_create_cell(): Next router not connected. Closing."); - return -1; - } - - circ->n_addr = n_conn->addr; /* these are different if we found a twin instead */ - circ->n_port = n_conn->port; - - circ->n_conn = n_conn; - log(LOG_DEBUG,"command_process_create_cell(): n_conn is %s:%u",n_conn->address,n_conn->port); - - /* send the CREATE cells on to the next hop */ - pad_onion(circ->onion, circ->onionlen, ONION_LAYER_SIZE); - log(LOG_DEBUG,"command_process_create_cell(): Padded the onion with random data."); - - retval = onion_deliver_to_conn(circ->n_aci, circ->onion, circ->onionlen, n_conn); - free(circ->onion); - circ->onion = NULL; - if (retval == -1) { - log(LOG_DEBUG,"command_process_create_cell(): Could not deliver the onion to next conn. Closing."); - return -1; - } - } else { /* this is destined for an exit */ - log(LOG_DEBUG,"command_process_create_cell(): create cell reached exit. Circuit established."); -#if 0 - log(LOG_DEBUG,"command_process_create_cell(): Creating new exit connection."); - n_conn = connection_new(CONN_TYPE_EXIT); - if(!n_conn) { - log(LOG_DEBUG,"command_process_create_cell(): connection_new failed. Closing."); - return -1; - } - n_conn->state = EXIT_CONN_STATE_CONNECTING_WAIT; - n_conn->receiver_bucket = -1; /* edge connections don't do receiver buckets */ - n_conn->bandwidth = -1; - n_conn->s = -1; /* not yet valid */ - if(connection_add(n_conn) < 0) { /* no space, forget it */ - log(LOG_DEBUG,"command_process_create_cell(): connection_add failed. Closing."); - connection_free(n_conn); - return -1; - } - circ->n_conn = n_conn; -#endif + if(connection_write_cell_to_buf(&cell, circ->p_conn) < 0) { + return -1; } + log(LOG_DEBUG,"onionskin_process(): Finished sending 'created' cell."); + return 0; } @@ -432,402 +343,82 @@ static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len) { return num; } -/* creates a new onion from route, stores it and its length into buf and len respectively */ -unsigned char *create_onion(routerinfo_t **rarray, int rarray_len, unsigned int *route, int routelen, int *len, crypt_path_t **cpath) -{ +crypt_path_t *onion_generate_cpath(routerinfo_t **firsthop) { + int routelen; /* length of the route */ + unsigned int *route; /* hops in the route as an array of indexes into rarray */ + crypt_path_t *cpath=NULL; + routerinfo_t **rarray; + int rarray_len; int i; - char *layerp; crypt_path_t *hop; - unsigned char *buf; routerinfo_t *router; - unsigned char iv[16]; struct in_addr netaddr; - onion_layer_t layer; - assert(rarray && route && len && routelen && cpath); + router_get_rarray(&rarray, &rarray_len); - *cpath = NULL; - - /* calculate the size of the onion */ - *len = routelen * ONION_LAYER_SIZE + ONION_PADDING_SIZE; - /* 28 bytes per layer + 100 bytes padding for the innermost layer */ - log(LOG_DEBUG,"create_onion() : Size of the onion is %u.",*len); - - /* allocate memory for the onion */ - buf = malloc(*len); - if(!buf) { - log(LOG_ERR,"Error allocating memory."); + /* choose a route */ + route = new_route(options.CoinWeight, rarray, rarray_len, &routelen); + if (!route) { + log(LOG_ERR,"onion_generate_cpath(): Error choosing a route through the OR network."); return NULL; } - log(LOG_DEBUG,"create_onion() : Allocated memory for the onion."); + log(LOG_DEBUG,"onion_generate_cpath(): Chosen a route of length %u: ",routelen); + + *firsthop = rarray[route[routelen-1]]; + assert(*firsthop); /* should always be defined */ for(i=0; iaddr); - log(LOG_DEBUG,"create_onion(): %u : %s:%u, %u/%u",routelen-i, + log(LOG_DEBUG,"onion_generate_cpath(): %u : %s:%u, %u/%u",routelen-i, inet_ntoa(netaddr), (rarray[route[i]])->or_port, (rarray[route[i]])->pkey, crypto_pk_keysize((rarray[route[i]])->pkey)); } - layerp = buf + *len - ONION_LAYER_SIZE - ONION_PADDING_SIZE; /* pointer to innermost layer */ - /* create the onion layer by layer, starting with the innermost */ + /* create the cpath layer by layer, starting at the last hop */ for (i=0;ior_port; - layer.addr = rarray[route[i-1]]->addr; - } else { - layer.port = 0; - layer.addr = 0; - } - - /* Expiration Time */ - layer.expire = (uint32_t)(time(NULL) + 86400); /* NOW + 1 day */ - - /* Key Seed Material */ - if(crypto_rand(ONION_KEYSEED_LEN, layer.keyseed)) { /* error */ - log(LOG_ERR,"Error generating random data."); - goto error; - } - - onion_pack(layerp, &layer); - -// log(LOG_DEBUG,"create_onion() : Onion layer %u built : %u, %u, %u, %s, %u.",i+1,layer->zero,layer->backf,layer->forwf,inet_ntoa(*((struct in_addr *)&layer->addr)),layer->port); - /* build up the crypt_path */ hop = (crypt_path_t *)malloc(sizeof(crypt_path_t)); if(!hop) { - log(LOG_ERR,"Error allocating memory."); - goto error; + log(LOG_ERR,"Error allocating crypt path hop memory."); + circuit_free_cpath(cpath); + free(route); + return NULL; } + memset(hop, 0, sizeof(crypt_path_t)); /* link hop into the cpath, at the front */ - hop->next = *cpath; + hop->next = cpath; hop->prev = NULL; - hop->state = CPATH_STATE_OPEN; /* change when we move to incremental paths */ - if(*cpath) { - (*cpath)->prev = hop; + hop->state = CPATH_STATE_CLOSED; + if(cpath) { + cpath->prev = hop; } - *cpath = hop; + cpath = hop; - log(LOG_DEBUG,"create_onion() : Building hop %u of crypt path.",i+1); - - /* calculate keys */ - crypto_SHA_digest(layer.keyseed,16,hop->digest3); - log(LOG_DEBUG,"create_onion() : First SHA pass performed."); - crypto_SHA_digest(hop->digest3,20,hop->digest2); - log(LOG_DEBUG,"create_onion() : Second SHA pass performed."); - crypto_SHA_digest(hop->digest2,20,hop->digest3); - log(LOG_DEBUG,"create_onion() : Third SHA pass performed."); - log(LOG_DEBUG,"create_onion() : Keys generated."); - /* set IV to zero */ - memset((void *)iv,0,16); - - /* initialize cipher engines */ - if (! (hop->f_crypto = - crypto_create_init_cipher(DEFAULT_CIPHER, hop->digest3, iv, 1))) { - /* cipher initialization failed */ - log(LOG_ERR,"Could not create a crypto environment."); - goto error; +#if 0 + if (i) { /* not last hop. (last hop has 0's for these.) */ + hop->port = rarray[route[i-1]]->or_port; + hop->addr = rarray[route[i-1]]->addr; } +#endif + hop->port = rarray[route[i]]->or_port; + hop->addr = rarray[route[i]]->addr; - if (! (hop->b_crypto = - crypto_create_init_cipher(DEFAULT_CIPHER, hop->digest2, iv, 0))) { - /* cipher initialization failed */ - log(LOG_ERR,"Could not create a crypto environment."); - goto error; - } - - log(LOG_DEBUG,"create_onion() : Built corresponding crypt path hop."); - - /* padding if this is the innermost layer */ - if (!i) { - if (crypto_pseudo_rand(ONION_PADDING_SIZE, layerp + ONION_LAYER_SIZE)) { /* error */ - log(LOG_ERR,"Error generating pseudo-random data."); - goto error; - } - log(LOG_DEBUG,"create_onion() : This is the innermost layer. Adding 100 bytes of padding."); - } - - /* encrypt */ - - if(encrypt_onion(layerp,ONION_PADDING_SIZE+(i+1)*ONION_LAYER_SIZE,router->pkey,layer.keyseed) < 0) { - log(LOG_ERR,"Error encrypting onion layer."); - goto error; - } - log(LOG_DEBUG,"create_onion() : Encrypted layer."); - - /* calculate pointer to next layer */ - layerp = buf + (routelen-i-2)*ONION_LAYER_SIZE; + log(LOG_DEBUG,"onion_generate_cpath() : Building hop %u of crypt path.",i+1); } /* now link cpath->prev to the end of cpath */ - for(hop=*cpath; hop->next; hop=hop->next) ; - hop->next = *cpath; - (*cpath)->prev = hop; + for(hop=cpath; hop->next; hop=hop->next) ; + hop->next = cpath; + cpath->prev = hop; - return buf; - - error: - if(buf) - free(buf); - circuit_free_cpath(*cpath); - return NULL; -} - -/* encrypts 128 bytes of the onion with the specified public key, the rest with - * DES OFB with the key as defined in the outter layer */ -int encrypt_onion(unsigned char *onion, uint32_t onionlen, crypto_pk_env_t *pkey, char* keyseed) { - unsigned char *tmpbuf = NULL; /* temporary buffer for crypto operations */ - unsigned char digest[20]; /* stores SHA1 output - 160 bits */ - unsigned char iv[8]; - - crypto_cipher_env_t *crypt_env = NULL; /* crypto environment */ - - assert(onion && pkey); - assert(onionlen >= 128); - - memset(iv,0,8); - -// log(LOG_DEBUG,"Onion layer : %u, %u, %u, %s, %u.",onion->zero,onion->backf,onion->forwf,inet_ntoa(*((struct in_addr *)&onion->addr)),onion->port); - /* allocate space for tmpbuf */ - tmpbuf = (unsigned char *)malloc(onionlen); - if(!tmpbuf) { - log(LOG_ERR,"Could not allocate memory."); - return -1; - } - log(LOG_DEBUG,"encrypt_onion() : allocated %u bytes of memory for the encrypted onion (at %u).",onionlen,tmpbuf); - - /* get key1 = SHA1(KeySeed) */ - if (crypto_SHA_digest(keyseed,16,digest)) { - log(LOG_ERR,"Error computing SHA1 digest."); - goto error; - } - log(LOG_DEBUG,"encrypt_onion() : Computed DES key."); - - log(LOG_DEBUG,"encrypt_onion() : Trying to RSA encrypt."); - /* encrypt 128 bytes with RSA *pkey */ - if (crypto_pk_public_encrypt(pkey, onion, 128, tmpbuf, RSA_NO_PADDING) == -1) { - log(LOG_ERR,"Error RSA-encrypting data :%s",crypto_perror()); - goto error; - } - - log(LOG_DEBUG,"encrypt_onion() : RSA encrypted first 128 bytes of the onion."); - - /* now encrypt the rest with 3DES OFB */ - crypt_env = crypto_create_init_cipher(CRYPTO_CIPHER_3DES, digest, iv, 1); - if (!crypt_env) { - log(LOG_ERR,"Error creating the crypto environment."); - goto error; - } - - if (crypto_cipher_encrypt(crypt_env,onion+128, onionlen-128, (unsigned char *)tmpbuf+128)) { /* error */ - log(LOG_ERR,"Error performing DES encryption:%s",crypto_perror()); - goto error; - } - log(LOG_DEBUG,"encrypt_onion() : 3DES OFB encrypted the rest of the onion."); - - /* now copy tmpbuf to onion */ - memcpy(onion,tmpbuf,onionlen); - log(LOG_DEBUG,"encrypt_onion() : Copied cipher to original onion buffer."); - free(tmpbuf); - crypto_free_cipher_env(crypt_env); - return 0; - - error: - if (tmpbuf) - free(tmpbuf); - if (crypt_env) - crypto_free_cipher_env(crypt_env); - return -1; -} - -/* decrypts the first 128 bytes using RSA and prkey, decrypts the rest with DES OFB with key1 */ -int decrypt_onion(unsigned char *onion, uint32_t onionlen, crypto_pk_env_t *prkey, onion_layer_t *layer) { - void *tmpbuf = NULL; /* temporary buffer for crypto operations */ - unsigned char digest[20]; /* stores SHA1 output - 160 bits */ - unsigned char iv[8]; - - crypto_cipher_env_t *crypt_env =NULL; /* crypto environment */ - - assert(onion && prkey); - - memset(iv,0,8); - - /* allocate space for tmpbuf */ - tmpbuf = malloc(onionlen); - if (!tmpbuf) { - log(LOG_ERR,"Could not allocate memory."); - return -1; - } - log(LOG_DEBUG,"decrypt_onion() : Allocated memory for the temporary buffer."); - - /* decrypt 128 bytes with RSA *prkey */ - if (crypto_pk_private_decrypt(prkey, onion, 128, tmpbuf, RSA_NO_PADDING) == -1) - { - log(LOG_ERR,"Error RSA-decrypting data :%s",crypto_perror()); - goto error; - } - log(LOG_DEBUG,"decrypt_onion() : RSA decryption complete."); - - onion_unpack(layer, tmpbuf); - - /* get key1 = SHA1(KeySeed) */ - if (crypto_SHA_digest(layer->keyseed,16,digest)) { - log(LOG_ERR,"Error computing SHA1 digest."); - goto error; - } - log(LOG_DEBUG,"decrypt_onion() : Computed DES key."); - - /* now decrypt the rest with 3DES OFB */ - crypt_env = crypto_create_init_cipher(CRYPTO_CIPHER_3DES, digest, iv, 0); - if (!crypt_env) { - log(LOG_ERR,"Error creating crypto environment"); - goto error; - } - - if (crypto_cipher_decrypt(crypt_env,onion+128, onionlen-128,tmpbuf+128)) { - log(LOG_ERR,"Error performing DES decryption:%s",crypto_perror()); - goto error; - } - - log(LOG_DEBUG,"decrypt_onion() : DES decryption complete."); - - /* now copy tmpbuf to onion */ - memcpy(onion,tmpbuf,onionlen); - free(tmpbuf); - crypto_free_cipher_env(crypt_env); - return 0; - - error: - if (tmpbuf) - free(tmpbuf); - if (crypt_env) - crypto_free_cipher_env(crypt_env); - return -1; -} - -/* delete first n bytes of the onion and pads the end with n bytes of random data */ -void pad_onion(unsigned char *onion, uint32_t onionlen, int n) -{ - assert(onion); - - memmove(onion,onion+n,onionlen-n); - crypto_pseudo_rand(n, onion+onionlen-n); -} - - - - -/* red black tree using Niels' tree.h. I used -http://www.openbsd.org/cgi-bin/cvsweb/src/regress/sys/sys/tree/rb/ -as my guide */ - -#include "tree.h" - -struct tracked_onion { - RB_ENTRY(tracked_onion) node; - uint32_t expire; - char digest[20]; /* SHA digest of the onion */ - struct tracked_onion *next; -}; - -RB_HEAD(tracked_tree, tracked_onion) tracked_root; - -int compare_tracked_onions(struct tracked_onion *a, struct tracked_onion *b) { - return memcmp(a->digest, b->digest, 20); -} - -RB_PROTOTYPE(tracked_tree, tracked_onion, node, compare_tracked_onions) -RB_GENERATE(tracked_tree, tracked_onion, node, compare_tracked_onions) - -void init_tracked_tree(void) { - RB_INIT(&tracked_root); -} - -/* see if this onion has been seen before. if so, return 1, else - * return 0 and add the sha1 of this onion to the tree. - */ -static int find_tracked_onion(unsigned char *onion, uint32_t onionlen, - int expire) { - static struct tracked_onion *head_tracked_onions = NULL; /* linked list of tracked onions */ - static struct tracked_onion *tail_tracked_onions = NULL; - - uint32_t now = time(NULL); - struct tracked_onion *to; - - /* first take this opportunity to see if there are any expired - * onions in the tree. we know this is fast because the linked list - * 'tracked_onions' is ordered by when they were seen. - */ - while(head_tracked_onions && (head_tracked_onions->expire < now)) { - to = head_tracked_onions; - log(LOG_DEBUG,"find_tracked_onion(): Forgetting old onion (expires %d)", to->expire); - head_tracked_onions = to->next; - if(!head_tracked_onions) /* if there are no more, */ - tail_tracked_onions = NULL; /* then make sure the list's tail knows that too */ - RB_REMOVE(tracked_tree, &tracked_root, to); - free(to); - } - - to = malloc(sizeof(struct tracked_onion)); - - /* compute the SHA digest of the onion */ - crypto_SHA_digest(onion, onionlen, to->digest); - - /* try adding it to the tree. if it's already there it will return it. */ - if(RB_INSERT(tracked_tree, &tracked_root, to)) { - /* yes, it's already there: this is a replay. */ - free(to); - return 1; - } - - /* this is a new onion. add it to the list. */ - - to->expire = expire; /* set the expiration date */ - to->next = NULL; - - if (!head_tracked_onions) { - head_tracked_onions = to; - } else { - tail_tracked_onions->next = to; - } - tail_tracked_onions = to; - - log(LOG_DEBUG,"find_tracked_onion(): Remembered new onion (expires %d)", to->expire); - - return 0; -} - -void -onion_pack(char *dest, onion_layer_t *src) -{ - assert((src->version & 0x80) == 0); - - *(uint8_t*)(dest) = src->version; - *(uint16_t*)(dest+1) = htons(src->port); - *(uint32_t*)(dest+3) = htonl(src->addr); - *(uint32_t*)(dest+7) = htonl(src->expire); - memcpy(dest+11, src->keyseed, ONION_KEYSEED_LEN); - log(LOG_DEBUG,"onion_pack(): version %d, port %d, addr %s, expire %u", src->version, src->port, - inet_ntoa(*((struct in_addr *)(dest+3))), src->expire); -} - -void -onion_unpack(onion_layer_t *dest, char *src) -{ - dest->version = *(uint8_t*)src; - dest->port = ntohs(*(uint16_t*)(src+1)); - dest->addr = ntohl(*(uint32_t*)(src+3)); - dest->expire = ntohl(*(uint32_t*)(src+7)); - memcpy(dest->keyseed, src+11, ONION_KEYSEED_LEN); - - log(LOG_DEBUG,"onion_unpack(): version %d, port %d, addr %s, expire %u", dest->version, dest->port, - inet_ntoa(*((struct in_addr *)(src+3))), dest->expire); + free(route); + return cpath; } /*----------------------------------------------------------------------*/ @@ -843,7 +434,7 @@ onion_unpack(onion_layer_t *dest, char *src) * and the last 80 are encrypted with the symmetric key. */ int -onion_skin_create(crypto_pk_env_t *router_key, +onion_skin_create(crypto_pk_env_t *dest_router_key, crypto_dh_env_t **handshake_state_out, char *onion_skin_out) /* Must be 208 bytes long */ { @@ -861,7 +452,7 @@ onion_skin_create(crypto_pk_env_t *router_key, goto err; dhbytes = crypto_dh_get_bytes(dh); - pkbytes = crypto_pk_keysize(router_key); + pkbytes = crypto_pk_keysize(dest_router_key); assert(dhbytes+16 == 208); if (!(pubkey = malloc(dhbytes+16))) goto err; @@ -896,7 +487,7 @@ onion_skin_create(crypto_pk_env_t *router_key, if (!cipher) goto err; - if (crypto_pk_public_encrypt(router_key, pubkey, pkbytes, + if (crypto_pk_public_encrypt(dest_router_key, pubkey, pkbytes, onion_skin_out, RSA_NO_PADDING)==-1) goto err; diff --git a/src/or/or.h b/src/or/or.h index b2079af31a..7a91f831ad 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -113,9 +113,9 @@ #define DIR_CONN_STATE_COMMAND_WAIT 3 #define DIR_CONN_STATE_WRITING 4 -#define CIRCUIT_STATE_ONION_WAIT 0 /* receiving the onion */ -#define CIRCUIT_STATE_ONION_PENDING 1 /* waiting to process the onion */ -#define CIRCUIT_STATE_OR_WAIT 2 /* I'm at the beginning of the path, my firsthop is still connecting */ +#define CIRCUIT_STATE_BUILDING 0 /* I'm the OP, still haven't done all my handshakes */ +#define CIRCUIT_STATE_ONIONSKIN_PENDING 1 /* waiting to process the onion */ +#define CIRCUIT_STATE_OR_WAIT 2 /* I'm the OP, my firsthop is still connecting */ #define CIRCUIT_STATE_OPEN 3 /* onion processed, ready to send data along the connection */ //#define CIRCUIT_STATE_CLOSE_WAIT1 4 /* sent two "destroy" signals, waiting for acks */ //#define CIRCUIT_STATE_CLOSE_WAIT2 5 /* received one ack, waiting for one more @@ -127,23 +127,14 @@ #define RELAY_COMMAND_END 3 #define RELAY_COMMAND_CONNECTED 4 #define RELAY_COMMAND_SENDME 5 +#define RELAY_COMMAND_EXTEND 6 +#define RELAY_COMMAND_EXTENDED 7 #define RELAY_HEADER_SIZE 8 #define RELAY_STATE_RESOLVING -/* available cipher functions */ -#if 0 -#define ONION_CIPHER_IDENTITY 0 -#define ONION_CIPHER_DES 1 -#define ONION_CIPHER_RC4 2 -#define ONION_CIPHER_3DES 3 -#endif - /* default cipher function */ -#if 0 -#define ONION_DEFAULT_CIPHER ONION_CIPHER_3DES -#endif #define DEFAULT_CIPHER CRYPTO_CIPHER_3DES #define CELL_DIRECTION_IN 1 @@ -160,9 +151,10 @@ /* cell commands */ #define CELL_PADDING 0 #define CELL_CREATE 1 -#define CELL_RELAY 2 -#define CELL_DESTROY 3 -#define CELL_SENDME 4 +#define CELL_CREATED 2 +#define CELL_RELAY 3 +#define CELL_DESTROY 4 +#define CELL_SENDME 5 #define CELL_PAYLOAD_SIZE 248 #define CELL_NETWORK_SIZE 256 @@ -246,8 +238,6 @@ struct connection_t { long timestamp_created; - int onions_handled_this_second; - /* used by OR and OP: */ uint32_t bandwidth; /* connection bandwidth */ @@ -339,16 +329,19 @@ typedef struct { } routerinfo_t; struct crypt_path_t { - char digest2[20]; /* second SHA output for onion_layer_t.keyseed */ - char digest3[20]; /* third SHA output for onion_layer_t.keyseed */ /* crypto environments */ crypto_cipher_env_t *f_crypto; crypto_cipher_env_t *b_crypto; + crypto_dh_env_t *handshake_state; + + uint32_t addr; + uint16_t port; + char state; #define CPATH_STATE_CLOSED 0 -#define CPATH_STATE_AWAITING_KEY 1 +#define CPATH_STATE_AWAITING_KEYS 1 #define CPATH_STATE_OPEN 2 struct crypt_path_t *next; struct crypt_path_t *prev; /* doubly linked list */ @@ -382,15 +375,15 @@ typedef struct { crypt_path_t *cpath; - uint32_t expire; /* expiration time for the corresponding onion */ + char onionskin[208]; /* for storage while onionskin pending */ long timestamp_created; char dirty; /* whether this circuit has been used yet */ int state; - unsigned char *onion; /* stores the onion when state is CONN_STATE_OPEN_WAIT */ - uint32_t onionlen; /* total onion length */ - uint32_t recvlen; /* length of the onion so far */ +// unsigned char *onion; /* stores the onion when state is CONN_STATE_OPEN_WAIT */ +// uint32_t onionlen; /* total onion length */ +// uint32_t recvlen; /* length of the onion so far */ void *next; } circuit_t; @@ -538,17 +531,18 @@ void circuit_dump_by_conn(connection_t *conn); void circuit_expire_unused_circuits(void); void circuit_launch_new(int failure_status); -int circuit_create_onion(void); -int circuit_establish_circuit(unsigned int *route, int routelen, char *onion, - int onionlen, crypt_path_t *cpath); +int circuit_establish_circuit(void); void circuit_n_conn_open(connection_t *or_conn); -int circuit_send_onion(connection_t *or_conn, circuit_t *circ); +int circuit_send_next_onion_skin(circuit_t *circ); +int circuit_extend(cell_t *cell, circuit_t *circ); +int circuit_finish_handshake(circuit_t *circ, char *reply); /********************************* command.c ***************************/ void command_process_cell(cell_t *cell, connection_t *conn); void command_process_create_cell(cell_t *cell, connection_t *conn); +void command_process_created_cell(cell_t *cell, connection_t *conn); void command_process_sendme_cell(cell_t *cell, connection_t *conn); void command_process_relay_cell(cell_t *cell, connection_t *conn); void command_process_destroy_cell(cell_t *cell, connection_t *conn); @@ -775,23 +769,7 @@ int chooselen(double cw); */ unsigned int *new_route(double cw, routerinfo_t **rarray, int rarray_len, int *routelen); -/* creates a new onion from route, stores it and its length into bufp and lenp respectively */ -unsigned char *create_onion(routerinfo_t **rarray, int rarray_len, unsigned int *route, int routelen, int *len, crypt_path_t **cpath); - -/* encrypts 128 bytes of the onion with the specified public key, the rest with - * DES OFB with the key as defined in the outter layer */ -int encrypt_onion(unsigned char *onion, uint32_t onionlen, crypto_pk_env_t *pkey, char *keyseed); - -/* decrypts the first 128 bytes using RSA and prkey, decrypts the rest with DES OFB with key1. Writes the first layer into 'layer' */ -int decrypt_onion(unsigned char *onion, uint32_t onionlen, crypto_pk_env_t *prkey, onion_layer_t *layer); - -/* delete first n bytes of the onion and pads the end with n bytes of random data */ -void pad_onion(unsigned char *onion, uint32_t onionlen, int n); - -void init_tracked_tree(void); - -void onion_pack(char *dest, onion_layer_t *src); -void onion_unpack(onion_layer_t *dest, char *src); +crypt_path_t *onion_generate_cpath(routerinfo_t **firsthop); int onion_skin_create(crypto_pk_env_t *router_key, crypto_dh_env_t **handshake_state_out, @@ -814,10 +792,8 @@ int learn_my_address(struct sockaddr_in *me); void router_retry_connections(void); routerinfo_t *router_pick_directory_server(void); routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port); -unsigned int *router_new_route(int *routelen); -unsigned char *router_create_onion(unsigned int *route, int routelen, int *len, crypt_path_t **cpath); +void router_get_rarray(routerinfo_t ***prouter_array, int *prarray_len); int router_is_me(uint32_t addr, uint16_t port); -routerinfo_t *router_get_first_in_route(unsigned int *route, int routelen); void router_forget_router(uint32_t addr, uint16_t port); int router_get_list_from_file(char *routerfile); int router_get_list_from_string(char *s); diff --git a/src/or/routers.c b/src/or/routers.c index 8adda7c9aa..4447aa0bb4 100644 --- a/src/or/routers.c +++ b/src/or/routers.c @@ -98,18 +98,9 @@ routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) { return NULL; } -routerinfo_t *router_get_first_in_route(unsigned int *route, int routelen) { - return router_array[route[routelen-1]]; -} - -/* a wrapper around new_route. put all these in routers.c perhaps? */ -unsigned int *router_new_route(int *routelen) { - return new_route(options.CoinWeight, router_array, rarray_len, routelen); -} - -/* a wrapper around create_onion */ -unsigned char *router_create_onion(unsigned int *route, int routelen, int *len, crypt_path_t **cpath) { - return create_onion(router_array,rarray_len,route,routelen,len,cpath); +void router_get_rarray(routerinfo_t ***prouter_array, int *prarray_len) { + *prouter_array = router_array; + *prarray_len = rarray_len; } /* return 1 if addr and port corresponds to my addr and my or_listenport. else 0, @@ -132,52 +123,6 @@ int router_is_me(uint32_t addr, uint16_t port) return 0; -#if 0 - /* local host information */ - char localhostname[512]; - struct hostent *localhost; - struct in_addr *a; - char *tmp1; - - char *addr = NULL; - int i = 0; - - /* obtain local host information */ - if (gethostname(localhostname,512) < 0) { - log(LOG_ERR,"router_is_me(): Error obtaining local hostname."); - return -1; - } - localhost = gethostbyname(localhostname); - if (!localhost) { - log(LOG_ERR,"router_is_me(): Error obtaining local host info."); - return -1; - } - - /* check host addresses for a match with or_address above */ - addr = localhost->h_addr_list[i++]; /* set to the first local address */ - while(addr) - { - a = (struct in_addr *)addr; - - tmp1 = strdup(inet_ntoa(*a)); /* can't call inet_ntoa twice in the same - printf, since it overwrites its static - memory each time */ - log(LOG_DEBUG,"router_is_me(): Comparing '%s' to '%s'.",tmp1, - inet_ntoa( *((struct in_addr *)&or_address) ) ); - free(tmp1); - if (!memcmp((void *)&or_address, (void *)addr, sizeof(uint32_t))) { /* addresses match */ - log(LOG_DEBUG,"router_is_me(): Addresses match. Comparing ports."); - if (or_listenport == my_or_listenport) { /* ports also match */ - log(LOG_DEBUG,"router_is_me(): Ports match too."); - return 1; - } - } - - addr = localhost->h_addr_list[i++]; - } - - return 0; -#endif } /* delete a list of routers from memory */