diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/crypto.c | 60 | ||||
-rw-r--r-- | src/common/crypto.h | 4 | ||||
-rw-r--r-- | src/or/circuitbuild.c | 50 | ||||
-rw-r--r-- | src/or/connection.c | 19 | ||||
-rw-r--r-- | src/or/connection_or.c | 23 | ||||
-rw-r--r-- | src/or/directory.c | 1 | ||||
-rw-r--r-- | src/or/or.h | 12 | ||||
-rw-r--r-- | src/or/router.c | 4 | ||||
-rw-r--r-- | src/or/routerlist.c | 48 | ||||
-rw-r--r-- | src/or/routerparse.c | 5 |
10 files changed, 209 insertions, 17 deletions
diff --git a/src/common/crypto.c b/src/common/crypto.c index 17e71487a8..93a4738cff 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -837,8 +837,8 @@ int crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out) * space). * * Fingerprints are computed as the SHA1 digest of the ASN.1 encoding - * of the public key, converted to hexadecimal, with a space after every - * four digits. + * of the public key, converted to hexadecimal, in upper case, with a + * space after every four digits. */ int crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out) @@ -1433,6 +1433,62 @@ base32_encode(char *dest, int destlen, const char *src, int srclen) return 0; } +int base16_encode(char *dest, int destlen, const char *src, int srclen) +{ + const char *end; + char *cp; + + if (destlen < srclen*2+1) + return -1; + + cp = dest; + end = src+srclen; + while (src<end) { + sprintf(cp,"%02X",*(const uint8_t*)src); + ++src; + cp += 2; + } + *dest = '\0'; + return 0; +} + +static const char HEX_DIGITS[] = "0123456789ABCDEFabcdef"; + +static INLINE int hex_decode_digit(char c) +{ + const char *cp; + int n; + cp = strchr(HEX_DIGITS, c); + if (!cp) + return -1; + n = cp-HEX_DIGITS; + if (n<=15) + return n; /* digit or uppercase */ + else + return n-6; /* lowercase */ +} + +int base16_decode(char *dest, int destlen, const char *src, int srclen) +{ + const char *end; + int v1,v2; + if ((srclen % 2) != 0) + return -1; + if (destlen < srclen/2) + return -1; + end = src+srclen; + while (src<end) { + v1 = hex_decode_digit(*src); + v2 = hex_decode_digit(*(src+1)); + if(v1<0||v2<0) + return -1; + *(uint8_t*)dest = (v1<<4)|v2; + ++dest; + } + *dest = '\0'; + return 0; +} + /* Local Variables: mode:c diff --git a/src/common/crypto.h b/src/common/crypto.h index fa8380075e..09bb50f05a 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -37,6 +37,8 @@ /** Length of encoded public key fingerprints, including space; but not * including terminating NUL. */ #define FINGERPRINT_LEN 49 +/** Length of hex encoding of SHA1 digest, not including final NUL. */ +#define HEX_DIGEST_LEN 40 typedef struct crypto_pk_env_t crypto_pk_env_t; typedef struct crypto_cipher_env_t crypto_cipher_env_t; @@ -91,6 +93,8 @@ int base64_encode(char *dest, int destlen, const char *src, int srclen); int base64_decode(char *dest, int destlen, const char *src, int srclen); #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567" int base32_encode(char *dest, int destlen, const char *src, int srclen); +int base16_encode(char *dest, int destlen, const char *src, int srclen); +int base16_decode(char *dest, int destlen, const char *src, int srclen); /* Key negotiation */ crypto_dh_env_t *crypto_dh_new(); diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 88bdc301e5..1cce48a236 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -292,7 +292,9 @@ int circuit_send_next_onion_skin(circuit_t *circ) { routerinfo_t *router; int r; int circ_id_type; - char payload[2+4+ONIONSKIN_CHALLENGE_LEN]; + char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN]; + char *onionskin; + int payload_len; tor_assert(circ && CIRCUIT_IS_ORIGIN(circ)); @@ -352,7 +354,17 @@ int circuit_send_next_onion_skin(circuit_t *circ) { *(uint32_t*)payload = htonl(hop->addr); *(uint16_t*)(payload+4) = htons(hop->port); - if(onion_skin_create(router->onion_pkey, &(hop->handshake_state), payload+6) < 0) { + if (strncmp(router->platform, "Tor 0.0.7", 9)) { + /* Before 0.0.8, we didn't support the long payload format. */ + memcpy(payload+2+4, hop->identity_digest, DIGEST_LEN); + onionskin = payload+2+4+DIGEST_LEN; + payload_len = 2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN; + } else { + onionskin = payload+2+4; + payload_len = 2+4+ONIONSKIN_CHALLENGE_LEN; + } + + if(onion_skin_create(router->onion_pkey, &(hop->handshake_state), onionskin) < 0) { log_fn(LOG_WARN,"onion_skin_create failed."); return -1; } @@ -361,7 +373,7 @@ int circuit_send_next_onion_skin(circuit_t *circ) { /* send it to hop->prev, because it will transfer * it to a create cell and then send to hop */ if(connection_edge_send_command(NULL, circ, RELAY_COMMAND_EXTEND, - payload, sizeof(payload), hop->prev) < 0) + payload, payload_len, hop->prev) < 0) return 0; /* circuit is closed */ hop->state = CPATH_STATE_AWAITING_KEYS; @@ -377,16 +389,42 @@ int circuit_extend(cell_t *cell, circuit_t *circ) { connection_t *n_conn; int circ_id_type; cell_t newcell; + relay_header_t rh; + int old_format; + char *onionskin; if(circ->n_conn) { log_fn(LOG_WARN,"n_conn already set. Bug/attack. Closing."); return -1; } + relay_header_unpack(&rh, cell->payload); + + if (rh.length == 4+2+ONIONSKIN_CHALLENGE_LEN) { + old_format = 1; + } else if (rh.length == 4+2+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN) { + old_format = 0; + } else { + log_fn(LOG_WARN, "Wrong length on extend cell. Closing circuit."); + circuit_mark_for_close(circ); + return 0; + } + circ->n_addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE)); circ->n_port = ntohs(get_uint16(cell->payload+RELAY_HEADER_SIZE+4)); - n_conn = connection_twin_get_by_addr_port(circ->n_addr,circ->n_port); + if (old_format) { + n_conn = connection_twin_get_by_addr_port(circ->n_addr,circ->n_port); + onionskin = cell->payload+RELAY_HEADER_SIZE+4+2; + } else { + /* XXXX Roger: in this case, we should create the connnection if + * n_conn is null. */ + n_conn = connection_get_by_identity_digest( + cell->payload+RELAY_HEADER_SIZE+4+2, + CONN_TYPE_OR); + onionskin = cell->payload+RELAY_HEADER_SIZE+4+2+DIGEST_LEN; + } + 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. @@ -426,8 +464,7 @@ int circuit_extend(cell_t *cell, circuit_t *circ) { newcell.command = CELL_CREATE; newcell.circ_id = circ->n_circ_id; - memcpy(newcell.payload, cell->payload+RELAY_HEADER_SIZE+2+4, - ONIONSKIN_CHALLENGE_LEN); + memcpy(newcell.payload, onionskin, ONIONSKIN_CHALLENGE_LEN); connection_or_write_cell_to_buf(&newcell, circ->n_conn); return 0; @@ -1079,6 +1116,7 @@ onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t hop->port = choice->or_port; hop->addr = choice->addr; + memcpy(hop->identity_digest, choice->identity_digest, DIGEST_LEN); hop->package_window = CIRCWINDOW_START; hop->deliver_window = CIRCWINDOW_START; diff --git a/src/or/connection.c b/src/or/connection.c index bd8a534cf2..9b7b284954 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -979,6 +979,25 @@ connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port) { return NULL; } +connection_t *connection_get_by_identity_digest(const char *digest, int type) +{ + int i, n; + connection_t *conn, *best=NULL; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;i<n;i++) { + conn = carray[i]; + if (conn->type != type) + continue; + if (!memcmp(conn->identity_digest, digest, DIGEST_LEN) + && !conn->marked_for_close + && (!best || best->timestamp_created < conn->timestamp_created)) + best = conn; + } + return best; +} + /** Return a connection of type <b>type</b> that is not marked for * close. */ diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 028a806bf0..abd8476f75 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -107,11 +107,34 @@ connection_or_init_conn_from_router(connection_t *conn, routerinfo_t *router) { conn->port = router->or_port; conn->receiver_bucket = conn->bandwidth = router->bandwidthburst; conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); + crypto_pk_get_digest(conn->identity_pkey, conn->identity_digest); conn->nickname = tor_strdup(router->nickname); tor_free(conn->address); conn->address = tor_strdup(router->address); } +static void +connection_or_init_conn_from_address(connection_t *conn, + uint32_t addr, uint16_t port, + const char *id_digest) +{ + routerinfo_t *r; + r = router_get_by_digest(id_digest); + if (r) { + connection_or_init_conn_from_router(conn,r); + return; + } + conn->addr = addr; + conn->port = port; + /* This next part isn't really right, but it's good enough for now. */ + conn->receiver_bucket = conn->bandwidth = options.BandwidthBurst; + memcpy(conn->identity_digest, id_digest, DIGEST_LEN); + conn->nickname = tor_malloc(HEX_DIGEST_LEN+1); + base16_encode(conn->nickname, HEX_DIGEST_LEN+1, + conn->identity_digest, DIGEST_LEN); + /* Do something about address? Or is it already set? XXXX NMNM */ +} + /** Launch a new OR connection to <b>router</b>. * * If <b>router</b> is me, do nothing. If we're already connected to <b>router</b>, diff --git a/src/or/directory.c b/src/or/directory.c index 868f614ab5..0d3ffa072f 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -137,6 +137,7 @@ directory_initiate_command(routerinfo_t *router, uint8_t purpose, conn->nickname = tor_strdup(router->nickname); tor_assert(router->identity_pkey); conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); + crypto_pk_get_digest(conn->identity_pkey, conn->identity_digest); conn->purpose = purpose; diff --git a/src/or/or.h b/src/or/or.h index 496109a318..12503e05d2 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -493,8 +493,9 @@ struct connection_t { char *address; /**< FQDN (or IP) of the guy on the other end. * strdup into this, because free_connection frees it. */ - crypto_pk_env_t *identity_pkey; /**> Public RSA key for the other side's + crypto_pk_env_t *identity_pkey; /**< Public RSA key for the other side's * signing key. */ + char identity_digest[DIGEST_LEN]; /**< Hash of identity_pkey */ char *nickname; /**< Nickname of OR on other side (if any). */ /* Used only by OR connections: */ @@ -569,7 +570,7 @@ typedef struct { crypto_pk_env_t *onion_pkey; /**< Public RSA key for onions. */ crypto_pk_env_t *identity_pkey; /**< Public RSA key for signing. */ - + char identity_digest[DIGEST_LEN]; /** Digest of identity key */ char *platform; /**< What software/operating system is this OR using? */ @@ -633,6 +634,8 @@ struct crypt_path_t { uint32_t addr; /** Port of the OR at this step. */ uint16_t port; + /** Identity key digest of the OR at this step. */ + char identity_digest[DIGEST_LEN]; /** Is the circuit built to this step? Must be one of: * - CPATH_STATE_CLOSED (The circuit has not been extended to this step) @@ -1044,6 +1047,7 @@ void connection_write_to_buf(const char *string, int len, connection_t *conn); connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port); connection_t *connection_exact_get_by_addr_port(uint32_t addr, uint16_t port); +connection_t *connection_get_by_identity_digest(const char *digest, int type); connection_t *connection_get_by_type(int type); connection_t *connection_get_by_type_state(int type, int state); @@ -1341,7 +1345,9 @@ void router_add_running_routers_to_smartlist(struct smartlist_t *sl); routerinfo_t *router_choose_random_node(char *preferred, char *excluded, struct smartlist_t *excludedsmartlist); routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port); -routerinfo_t *router_get_by_nickname(char *nickname); +routerinfo_t *router_get_by_nickname(const char *nickname); +routerinfo_t *router_get_by_hexdigest(const char *hexdigest); +routerinfo_t *router_get_by_digest(const char *digest); void router_get_routerlist(routerlist_t **prouterlist); void routerlist_free(routerlist_t *routerlist); void routerlist_clear_trusted_directories(void); diff --git a/src/or/router.c b/src/or/router.c index 6f18d1f414..308a1ad925 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -444,6 +444,10 @@ int router_rebuild_descriptor(void) { ri->published_on = time(NULL); ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from main thread */ ri->identity_pkey = crypto_pk_dup_key(get_identity_key()); + if (crypto_pk_get_digest(ri->identity_pkey, ri->identity_digest)<0) { + routerinfo_free(ri); + return -1; + } get_platform_str(platform, sizeof(platform)); ri->platform = tor_strdup(platform); ri->bandwidthrate = options.BandwidthRate; diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 68ab10a178..24553bc1bf 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -206,21 +206,59 @@ routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) { return NULL; } -/** Return the router in our routerlist whose nickname is <b>nickname</b> - * (case insensitive). Return NULL if no such router is known. +/** Return the router in our routerlist whose (case-insensitive) + * nickname or (case-sensitive) hexadecimal key digest is + * <b>nickname</b>. Return NULL if no such router is known. */ -routerinfo_t *router_get_by_nickname(char *nickname) +routerinfo_t *router_get_by_nickname(const char *nickname) { - int i; + int i, maybedigest; routerinfo_t *router; + char digest[DIGEST_LEN]; tor_assert(nickname); if (!routerlist) return NULL; + maybedigest = (strlen(nickname) == HEX_DIGEST_LEN) && + (base16_decode(digest,DIGEST_LEN,nickname,HEX_DIGEST_LEN) == 0); + + for(i=0;i<smartlist_len(routerlist->routers);i++) { + router = smartlist_get(routerlist->routers, i); + if (0 == strcasecmp(router->nickname, nickname) || + (maybedigest && 0 == memcmp(digest, router->identity_digest, + DIGEST_LEN))) + return router; + } + + return NULL; +} + +/** Return the router in our routerlist whose hexadecimal key digest + * is <b>hexdigest</b>. Return NULL if no such router is known. */ +routerinfo_t *router_get_by_hexdigest(const char *hexdigest) { + char digest[DIGEST_LEN]; + + tor_assert(hexdigest); + if (!routerlist) + return NULL; + if (strlen(hexdigest) != HEX_DIGEST_LEN || + base16_decode(digest,DIGEST_LEN,hexdigest,HEX_DIGEST_LEN) < 0) + return NULL; + + return router_get_by_digest(digest); +} + +/** Return the router in our routerlist whose 20-byte key digest + * is <b>hexdigest</b>. Return NULL if no such router is known. */ +routerinfo_t *router_get_by_digest(const char *digest) { + int i; + routerinfo_t *router; + + tor_assert(digest); for(i=0;i<smartlist_len(routerlist->routers);i++) { router = smartlist_get(routerlist->routers, i); - if (0 == strcasecmp(router->nickname, nickname)) + if (0 == memcmp(router->identity_digest, digest, DIGEST_LEN)) return router; } diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 60daf0e33f..90657e6fae 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -733,11 +733,14 @@ routerinfo_t *router_parse_entry_from_string(const char *s, } if (!(tok = find_first_by_keyword(tokens, K_SIGNING_KEY))) { - log_fn(LOG_WARN, "Missing onion key"); goto err; + log_fn(LOG_WARN, "Missing identity key"); goto err; } /* XXX Check key length */ router->identity_pkey = tok->key; tok->key = NULL; /* Prevent free */ + if (crypto_pk_get_digest(router->identity_pkey,router->identity_digest)){ + log_fn(LOG_WARN, "Couldn't calculate key digest"); goto err; + } if ((tok = find_first_by_keyword(tokens, K_PLATFORM))) { router->platform = tor_strdup(tok->args[0]); |