diff --git a/doc/TODO b/doc/TODO index 60bf7bf704..30087cbdf6 100644 --- a/doc/TODO +++ b/doc/TODO @@ -42,16 +42,21 @@ Things we'd like to do in 0.2.0.x: - 105+TLS, if possible. - 105 only - Need to get a finished proposal 105 - - "Pick a version" function + o "Pick a version" function - Have a 'waiting_for_version' state. - - Store version in or_connection_t. - - Generate netinfo cells - - Accept netinfo cells + o Store version in or_connection_t. + o Generate netinfo cells + o Accept netinfo cells + . Add an is_canonical field to or_connection_t. + o Set it when we get a match in the netinfo. + o Set it when we get a match for a routerinfo we have. + - Don't extend a circuit over a noncanonical connection with + mismatched address. - Version negotiation: send a version cell and enter waiting-for-version; when version cell arrives, pick version and send netinfo and be "open". - - On netinfo, warn if there's skew from a server. - - Handle IP addresses in netinfo properly. + o On netinfo, warn if there's skew from a server. + - Learn our outgoing IP address from netinfo cells? - TLS only - Need to get a finished TLS normalization proposal - Revised authentication. diff --git a/doc/spec/proposals/105-handshake-revision.txt b/doc/spec/proposals/105-handshake-revision.txt index f9a8bfe1a6..634c1c50d6 100644 --- a/doc/spec/proposals/105-handshake-revision.txt +++ b/doc/spec/proposals/105-handshake-revision.txt @@ -4,7 +4,7 @@ Version: $Revision$ Last-Modified: $Date$ Author: Nick Mathewson, Roger Dingledine Created: -Status: Open +Status: Accepted Overview: @@ -120,7 +120,7 @@ Proposal: When a Tor connection is established, both parties normally send a VERSIONS cell before sending any other cells. (But see below.) - VersionsLen [1 byte] + VersionsLen [2 byte] Versions [VersionsLen bytes] "Versions" is a sequence of VersionsLen bytes. Each value between 1 and @@ -157,6 +157,8 @@ Proposal: The VERSIONS cell must be sent as a v1 cell (2 bytes of circuitID, 1 byte of command, 509 bytes of payload). + [NOTE: The VERSIONS cell is assigned the command number 7.] + 2.2. MITM-prevention and time checking If we negotiate a v2 connection or higher, the second cell we send SHOULD @@ -195,6 +197,7 @@ Proposal: given in the EXTEND cell is not listed in the NETINFO cell, the first party SHOULD close the connection as a likely MITM attack. + [NOTE: The NETINFO cell is assigned the command number 8.] Discussion: Versions versus feature lists diff --git a/src/or/command.c b/src/or/command.c index d89581216b..4fa098d9a7 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -25,12 +25,16 @@ uint64_t stats_n_create_cells_processed = 0; uint64_t stats_n_created_cells_processed = 0; uint64_t stats_n_relay_cells_processed = 0; uint64_t stats_n_destroy_cells_processed = 0; +uint64_t stats_n_versions_cells_processed = 0; +uint64_t stats_n_netinfo_cells_processed = 0; -/* These are the main four functions for processing cells */ +/* These are the main functions for processing cells */ static void command_process_create_cell(cell_t *cell, or_connection_t *conn); static void command_process_created_cell(cell_t *cell, or_connection_t *conn); static void command_process_relay_cell(cell_t *cell, or_connection_t *conn); static void command_process_destroy_cell(cell_t *cell, or_connection_t *conn); +static void command_process_versions_cell(cell_t *cell, or_connection_t *conn); +static void command_process_netinfo_cell(cell_t *cell, or_connection_t *conn); #ifdef KEEP_TIMING_STATS /** This is a wrapper function around the actual function that processes the @@ -99,6 +103,16 @@ command_process_cell(cell_t *cell, or_connection_t *conn) } #endif +#ifdef KEEP_TIMING_STATS +#define PROCESS_CELL(tp, cl, cn) STMT_BEGIN { \ + ++num ## tp; \ + command_time_process_cell(cl, cn, & tp ## time , \ + command_process_ ## tp ## _cell); \ + } STMT_END +#else +#define PROCESS_CELL(tp, cl, cn) command_process_ ## tp ## _cell(cl, cn) +#endif + switch (cell->command) { case CELL_PADDING: ++stats_n_padding_cells_processed; @@ -107,47 +121,31 @@ command_process_cell(cell_t *cell, or_connection_t *conn) case CELL_CREATE: case CELL_CREATE_FAST: ++stats_n_create_cells_processed; -#ifdef KEEP_TIMING_STATS - ++num_create; - command_time_process_cell(cell, conn, &create_time, - command_process_create_cell); -#else - command_process_create_cell(cell, conn); -#endif + PROCESS_CELL(create, cell, conn); break; case CELL_CREATED: case CELL_CREATED_FAST: ++stats_n_created_cells_processed; -#ifdef KEEP_TIMING_STATS - ++num_created; - command_time_process_cell(cell, conn, &created_time, - command_process_created_cell); -#else - command_process_created_cell(cell, conn); -#endif + PROCESS_CELL(created, cell, conn); break; case CELL_RELAY: ++stats_n_relay_cells_processed; -#ifdef KEEP_TIMING_STATS - ++num_relay; - command_time_process_cell(cell, conn, &relay_time, - command_process_relay_cell); -#else - command_process_relay_cell(cell, conn); -#endif + PROCESS_CELL(relay, cell, conn); break; case CELL_DESTROY: ++stats_n_destroy_cells_processed; -#ifdef KEEP_TIMING_STATS - ++num_destroy; - command_time_process_cell(cell, conn, &destroy_time, - command_process_destroy_cell); -#else - command_process_destroy_cell(cell, conn); -#endif + PROCESS_CELL(destroy, cell, conn); + break; + case CELL_VERSIONS: + ++stats_n_versions_cells_processed; + PROCESS_CELL(versions, cell, conn); + break; + case CELL_NETINFO: + ++stats_n_netinfo_cells_processed; + PROCESS_CELL(netinfo, cell, conn); break; default: - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + log_fn(LOG_INFO, LD_PROTOCOL, "Cell of unknown type (%d) received. Dropping.", cell->command); break; } @@ -389,3 +387,110 @@ command_process_destroy_cell(cell_t *cell, or_connection_t *conn) } } +/** Process a 'versions' cell. The current link protocol version must be 0 + * to indicate that no version has yet been negotiated. DOCDOC say more. */ +static void +command_process_versions_cell(cell_t *cell, or_connection_t *conn) +{ + uint16_t versionslen; + int highest_supported_version = 0; + const char *cp, *end; + if (conn->link_proto != 0) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Received a VERSIONS cell on a connection with its version " + "already set to %d; dropping", (int) conn->link_proto); + return; + } + versionslen = ntohs(get_uint16(cell->payload)); + end = cell->payload + 2 + versionslen; + if (end > cell->payload + CELL_PAYLOAD_SIZE) + end = cell->payload + CELL_PAYLOAD_SIZE; /*XXXX020 warn?*/ + for (cp = cell->payload + 2; cp < end; ++cp) { + uint8_t v = *cp; + if (v == 1) { + if (v > highest_supported_version) + highest_supported_version = v; + } + } + if (!versionslen) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Couldn't find a version in common; defaulting to v1."); + /*XXXX020 or just break the connection?*/ + conn->link_proto = 1; + return; + } +} + +/** Process a 'netinfo' cell. DOCDOC say more. */ +static void +command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) +{ + time_t timestamp; + uint8_t my_addr_type; + uint8_t my_addr_len; + const char *my_addr_ptr; + const char *cp, *end; + uint8_t n_other_addrs; + time_t now = time(NULL); + + /*XXXX020 reject duplicat netinfos. */ + + if (conn->link_proto < 2) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Received a NETINFO cell on %s connection; dropping.", + conn->link_proto == 0 ? "non-versioned" : "a v1"); + return; + } + /* Decode the cell. */ + timestamp = ntohl(get_uint32(cell->payload)); + my_addr_type = (uint8_t) cell->payload[4]; + my_addr_len = (uint8_t) cell->payload[5]; + my_addr_ptr = cell->payload + 6; + /* Possibly learn my address. XXXX020 */ + end = cell->payload + CELL_PAYLOAD_SIZE; + cp = cell->payload + 6 + my_addr_len; + if (cp >= end) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Address too long in netinfo cell; dropping."); + return; + } + + /*XXXX020 magic number 3600 */ + if (abs(timestamp - now) > 3600 && + router_get_by_digest(conn->identity_digest)) { + long delta = now - timestamp; + char dbuf[64]; + /*XXXX020 not always warn!*/ + format_time_interval(dbuf, sizeof(dbuf), delta); + log_fn(LOG_WARN, LD_HTTP, "Received NETINFO cell with skewed time from " + "server at %s:%d. It seems that our clock is %s by %s, or " + "that theirs is %s. Tor requires an accurate clock to work: " + "please check your time and date settings.", + conn->_base.address, (int)conn->_base.port, + delta>0 ? "ahead" : "behind", dbuf, + delta>0 ? "behind" : "ahead"); + control_event_general_status(LOG_WARN, + "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d", + delta, conn->_base.address, conn->_base.port); + } + + n_other_addrs = (uint8_t) *cp++; + while (n_other_addrs && cp < end-2) { + /* Consider all the other addresses; if any matches, this connection is + * "canonical." */ + uint8_t other_addr_type = (uint8_t) *cp++; + uint8_t other_addr_len = (uint8_t) *cp++; + if (cp + other_addr_len >= end) + break; /*XXXX020 protocol warn. */ + if (other_addr_type == RESOLVED_TYPE_IPV4 && other_addr_len == 4) { + uint32_t addr = ntohl(get_uint32(cp)); + if (addr == conn->real_addr) { + conn->is_canonical = 1; + break; + } + } + cp += other_addr_len; + --n_other_addrs; + } +} + diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 6b4c9e663f..1d083e39b3 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -356,11 +356,16 @@ connection_or_init_conn_from_address(or_connection_t *conn, connection_or_set_identity_digest(conn, id_digest); conn->_base.addr = addr; conn->_base.port = port; + conn->real_addr = addr; if (r) { + if (conn->_base.addr == r->addr) + conn->is_canonical = 1; if (!started_here) { /* Override the addr/port, so our log messages will make sense. * This is dangerous, since if we ever try looking up a conn by * its actual addr/port, we won't remember. Careful! */ + /* XXXX020 this is stupid, and it's the reason we need real_addr to + * track is_canonical properly. */ conn->_base.addr = r->addr; conn->_base.port = r->or_port; } @@ -740,6 +745,8 @@ connection_tls_finish_handshake(or_connection_t *conn) } connection_watch_events(TO_CONN(conn), EV_READ); circuit_n_conn_done(conn, 1); /* send the pending creates, if any. */ + + conn->link_proto = 1; /* Version negotiation not yet enabled.XXX020 */ return 0; } @@ -773,7 +780,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn) char buf[CELL_NETWORK_SIZE]; cell_t cell; -loop: + loop: log_debug(LD_OR, "%d: starting, inbuf_datalen %d (%d pending in tls object).", conn->_base.s,(int)buf_datalen(conn->_base.inbuf), @@ -825,3 +832,56 @@ connection_or_send_destroy(uint16_t circ_id, or_connection_t *conn, int reason) return 0; } +/** DOCDOC */ +static int +connection_or_send_versions(or_connection_t *conn) +{ + cell_t cell; + uint8_t versions[] = { 1 }; + int n_versions = sizeof(versions) / sizeof(uint8_t); + int i; + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_VERSIONS; + set_uint16(cell.payload, htons(n_versions)); + for (i = 0; i < n_versions; ++i) { + uint8_t v = versions[i]; + tor_assert(v > 0 && v < 128); + cell.payload[2+i] = v; + } + + connection_or_write_cell_to_buf(&cell, conn); +} + +/** DOCDOC */ +static int +connection_or_send_netinfo(or_connection_t *conn) +{ + cell_t cell; + time_t now = time(NULL); + routerinfo_t *me; + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_NETINFO; + + /* Their address. */ + set_uint32(cell.payload, htonl(now)); + cell.payload[4] = RESOLVED_TYPE_IPV4; + cell.payload[5] = 4; + set_uint32(cell.payload+6, htonl(conn->_base.addr)); + + /* My address. */ + if ((me = router_get_my_routerinfo())) { + cell.payload[10] = 1; /* only one address is supported. */ + cell.payload[11] = RESOLVED_TYPE_IPV4; + cell.payload[12] = 4; + set_uint32(cell.payload+13, htonl(me->addr)); + } else { + cell.payload[10] = 0; + } + + connection_or_write_cell_to_buf(&cell, conn); + + /*XXXX020 remove these once we send netinfo and versions cells. */ + (void) connection_or_send_netinfo; + (void) connection_or_send_versions; +} diff --git a/src/or/or.h b/src/or/or.h index de2c742682..20766ac3c4 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -651,6 +651,8 @@ typedef enum { #define CELL_DESTROY 4 #define CELL_CREATE_FAST 5 #define CELL_CREATED_FAST 6 +#define CELL_VERSIONS 7 +#define CELL_NETINFO 8 /** How long to test reachability before complaining to the user. */ #define TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT (20*60) @@ -868,9 +870,14 @@ typedef struct or_connection_t { * recent, we can rate limit it further. */ time_t client_used; + uint32_t real_addr; /**DOCDOC */ + circ_id_type_t circ_id_type:2; /**< When we send CREATE cells along this * connection, which half of the space should * we use? */ + unsigned int is_canonical; /**< DOCDOC */ + uint8_t link_proto; /**< What protocol version are we using? 0 for + * "none negotiated yet." */ uint16_t next_circ_id; /**< Which circ_id do we try to use next on * this connection? This is always in the * range 0..1<<15-1. */ diff --git a/src/or/router.c b/src/or/router.c index c7bf5efa1f..6a80e393d2 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1570,6 +1570,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, result = tor_snprintf(s, maxlen, "router %s %s %d 0 %d\n" "platform %s\n" + "opt protocols Link 1 Circuit 1\n" "published %s\n" "opt fingerprint %s\n" "uptime %ld\n"