incremental path building in; uses ephemeral DH; onions are gone

still need to change circuit-level sendmes


svn:r264
This commit is contained in:
Roger Dingledine 2003-05-05 23:24:46 +00:00
parent 44b4efe34d
commit d7f50337c1
8 changed files with 427 additions and 815 deletions

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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.");

View File

@ -337,7 +337,6 @@ int prepare_for_poll(int *timeout) {
for(i=0;i<nfds;i++) {
tmpconn = connection_array[i];
connection_increment_receiver_bucket(tmpconn);
connection_array[i]->onions_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 */

View File

@ -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);
memset(iv, 0, 16);
buflen = onionlen+4;
buf = malloc(buflen);
if(!buf)
return -1;
memset(&cell, 0, sizeof(cell_t));
cell.command = CELL_CREATED;
cell.aci = circ->p_aci;
cell.length = 192;
log(LOG_DEBUG,"onion_deliver_to_conn(): Setting onion length to %u.",onionlen);
*(uint32_t*)buf = htonl(onionlen);
memcpy((buf+4),onion,onionlen);
circ->state = CIRCUIT_STATE_OPEN;
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,"onionskin_process(): Entering.");
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;
if(learn_my_address(&me) < 0)
return -1;
/* 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.");
/* 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.");
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);
log(LOG_DEBUG,"onionskin_process: init cipher forward %d, backward %d.", *(int*)keys, *(int*)(keys+16));
if(circuit_init(circ, aci_type, &layer) < 0) {
log(LOG_ERR,"process_onion(): init_circuit() failed.");
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; i<routelen; i++) {
netaddr.s_addr = htonl((rarray[route[i]])->addr);
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;i<routelen;i++) {
router = rarray[route[i]];
layer.version = OR_VERSION;
if (i) { /* not last hop */
layer.port = rarray[route[i-1]]->or_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;

View File

@ -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);

View File

@ -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 */