/* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file onion.c * \brief Functions to queue create cells, * and parse and create the CREATE cell and its allies. * * This module has a few functions, all related to the CREATE/CREATED * handshake that we use on links in order to create a circuit, and the * related EXTEND/EXTENDED handshake that we use over circuits in order to * extend them an additional hop. * * Clients invoke these functions when creating or extending a circuit, * from circuitbuild.c. * * Relays invoke these functions when they receive a CREATE or EXTEND * cell in command.c or relay.c, in order to queue the pending request. * They also invoke them from cpuworker.c, which handles dispatching * onionskin requests to different worker threads. * *
* * This module also handles: * **/ #include "core/or/or.h" #include "app/config/config.h" #include "core/crypto/onion_crypto.h" #include "core/crypto/onion_fast.h" #include "core/crypto/onion_ntor.h" #include "core/crypto/onion_tap.h" #include "core/or/onion.h" #include "feature/nodelist/networkstatus.h" #include "core/or/cell_st.h" // trunnel #include "trunnel/ed25519_cert.h" /** Helper: return 0 if cell appears valid, -1 otherwise. If * unknown_ok is true, allow cells with handshake types we don't * recognize. */ static int check_create_cell(const create_cell_t *cell, int unknown_ok) { switch (cell->cell_type) { case CELL_CREATE: if (cell->handshake_type != ONION_HANDSHAKE_TYPE_TAP && cell->handshake_type != ONION_HANDSHAKE_TYPE_NTOR) return -1; break; case CELL_CREATE_FAST: if (cell->handshake_type != ONION_HANDSHAKE_TYPE_FAST) return -1; break; case CELL_CREATE2: break; default: return -1; } switch (cell->handshake_type) { case ONION_HANDSHAKE_TYPE_TAP: if (cell->handshake_len != TAP_ONIONSKIN_CHALLENGE_LEN) return -1; break; case ONION_HANDSHAKE_TYPE_FAST: if (cell->handshake_len != CREATE_FAST_LEN) return -1; break; case ONION_HANDSHAKE_TYPE_NTOR: if (cell->handshake_len != NTOR_ONIONSKIN_LEN) return -1; break; default: if (! unknown_ok) return -1; } return 0; } /** Write the various parameters into the create cell. Separate from * create_cell_parse() to make unit testing easier. */ void create_cell_init(create_cell_t *cell_out, uint8_t cell_type, uint16_t handshake_type, uint16_t handshake_len, const uint8_t *onionskin) { memset(cell_out, 0, sizeof(*cell_out)); cell_out->cell_type = cell_type; cell_out->handshake_type = handshake_type; cell_out->handshake_len = handshake_len; memcpy(cell_out->onionskin, onionskin, handshake_len); } /** Helper: parse the CREATE2 payload at p, which could be up to * p_len bytes long, and use it to fill the fields of * cell_out. Return 0 on success and -1 on failure. * * Note that part of the body of an EXTEND2 cell is a CREATE2 payload, so * this function is also used for parsing those. */ static int parse_create2_payload(create_cell_t *cell_out, const uint8_t *p, size_t p_len) { uint16_t handshake_type, handshake_len; if (p_len < 4) return -1; handshake_type = ntohs(get_uint16(p)); handshake_len = ntohs(get_uint16(p+2)); if (handshake_len > CELL_PAYLOAD_SIZE - 4 || handshake_len > p_len - 4) return -1; if (handshake_type == ONION_HANDSHAKE_TYPE_FAST) return -1; create_cell_init(cell_out, CELL_CREATE2, handshake_type, handshake_len, p+4); return 0; } /** Magic string which, in a CREATE or EXTEND cell, indicates that a seeming * TAP payload is really an ntor payload. We'd do away with this if every * relay supported EXTEND2, but we want to be able to extend from A to B with * ntor even when A doesn't understand EXTEND2 and so can't generate a * CREATE2 cell. **/ #define NTOR_CREATE_MAGIC "ntorNTORntorNTOR" /** Parse a CREATE, CREATE_FAST, or CREATE2 cell from cell_in into * cell_out. Return 0 on success, -1 on failure. (We reject some * syntactically valid CREATE2 cells that we can't generate or react to.) */ int create_cell_parse(create_cell_t *cell_out, const cell_t *cell_in) { switch (cell_in->command) { case CELL_CREATE: if (tor_memeq(cell_in->payload, NTOR_CREATE_MAGIC, 16)) { create_cell_init(cell_out, CELL_CREATE, ONION_HANDSHAKE_TYPE_NTOR, NTOR_ONIONSKIN_LEN, cell_in->payload+16); } else { create_cell_init(cell_out, CELL_CREATE, ONION_HANDSHAKE_TYPE_TAP, TAP_ONIONSKIN_CHALLENGE_LEN, cell_in->payload); } break; case CELL_CREATE_FAST: create_cell_init(cell_out, CELL_CREATE_FAST, ONION_HANDSHAKE_TYPE_FAST, CREATE_FAST_LEN, cell_in->payload); break; case CELL_CREATE2: if (parse_create2_payload(cell_out, cell_in->payload, CELL_PAYLOAD_SIZE) < 0) return -1; break; default: return -1; } return check_create_cell(cell_out, 0); } /** Helper: return 0 if cell appears valid, -1 otherwise. */ static int check_created_cell(const created_cell_t *cell) { switch (cell->cell_type) { case CELL_CREATED: if (cell->handshake_len != TAP_ONIONSKIN_REPLY_LEN && cell->handshake_len != NTOR_REPLY_LEN) return -1; break; case CELL_CREATED_FAST: if (cell->handshake_len != CREATED_FAST_LEN) return -1; break; case CELL_CREATED2: if (cell->handshake_len > RELAY_PAYLOAD_SIZE-2) return -1; break; } return 0; } /** Parse a CREATED, CREATED_FAST, or CREATED2 cell from cell_in into * cell_out. Return 0 on success, -1 on failure. */ int created_cell_parse(created_cell_t *cell_out, const cell_t *cell_in) { memset(cell_out, 0, sizeof(*cell_out)); switch (cell_in->command) { case CELL_CREATED: cell_out->cell_type = CELL_CREATED; cell_out->handshake_len = TAP_ONIONSKIN_REPLY_LEN; memcpy(cell_out->reply, cell_in->payload, TAP_ONIONSKIN_REPLY_LEN); break; case CELL_CREATED_FAST: cell_out->cell_type = CELL_CREATED_FAST; cell_out->handshake_len = CREATED_FAST_LEN; memcpy(cell_out->reply, cell_in->payload, CREATED_FAST_LEN); break; case CELL_CREATED2: { const uint8_t *p = cell_in->payload; cell_out->cell_type = CELL_CREATED2; cell_out->handshake_len = ntohs(get_uint16(p)); if (cell_out->handshake_len > CELL_PAYLOAD_SIZE - 2) return -1; memcpy(cell_out->reply, p+2, cell_out->handshake_len); break; } } return check_created_cell(cell_out); } /** Helper: return 0 if cell appears valid, -1 otherwise. */ static int check_extend_cell(const extend_cell_t *cell) { if (tor_digest_is_zero((const char*)cell->node_id)) return -1; /* We don't currently allow EXTEND2 cells without an IPv4 address */ if (tor_addr_family(&cell->orport_ipv4.addr) == AF_UNSPEC) return -1; if (cell->create_cell.cell_type == CELL_CREATE) { if (cell->cell_type != RELAY_COMMAND_EXTEND) return -1; } else if (cell->create_cell.cell_type == CELL_CREATE2) { if (cell->cell_type != RELAY_COMMAND_EXTEND2 && cell->cell_type != RELAY_COMMAND_EXTEND) return -1; } else { /* In particular, no CREATE_FAST cells are allowed */ return -1; } if (cell->create_cell.handshake_type == ONION_HANDSHAKE_TYPE_FAST) return -1; return check_create_cell(&cell->create_cell, 1); } static int extend_cell_from_extend1_cell_body(extend_cell_t *cell_out, const extend1_cell_body_t *cell) { tor_assert(cell_out); tor_assert(cell); memset(cell_out, 0, sizeof(*cell_out)); tor_addr_make_unspec(&cell_out->orport_ipv4.addr); tor_addr_make_unspec(&cell_out->orport_ipv6.addr); cell_out->cell_type = RELAY_COMMAND_EXTEND; tor_addr_from_ipv4h(&cell_out->orport_ipv4.addr, cell->ipv4addr); cell_out->orport_ipv4.port = cell->port; if (tor_memeq(cell->onionskin, NTOR_CREATE_MAGIC, 16)) { cell_out->create_cell.cell_type = CELL_CREATE2; cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_NTOR; cell_out->create_cell.handshake_len = NTOR_ONIONSKIN_LEN; memcpy(cell_out->create_cell.onionskin, cell->onionskin + 16, NTOR_ONIONSKIN_LEN); } else { cell_out->create_cell.cell_type = CELL_CREATE; cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_TAP; cell_out->create_cell.handshake_len = TAP_ONIONSKIN_CHALLENGE_LEN; memcpy(cell_out->create_cell.onionskin, cell->onionskin, TAP_ONIONSKIN_CHALLENGE_LEN); } memcpy(cell_out->node_id, cell->identity, DIGEST_LEN); return 0; } static int create_cell_from_create2_cell_body(create_cell_t *cell_out, const create2_cell_body_t *cell) { tor_assert(cell_out); tor_assert(cell); memset(cell_out, 0, sizeof(create_cell_t)); if (BUG(cell->handshake_len > sizeof(cell_out->onionskin))) { /* This should be impossible because there just isn't enough room in the * input cell to make the handshake_len this large and provide a * handshake_data to match. */ return -1; } cell_out->cell_type = CELL_CREATE2; cell_out->handshake_type = cell->handshake_type; cell_out->handshake_len = cell->handshake_len; memcpy(cell_out->onionskin, create2_cell_body_getconstarray_handshake_data(cell), cell->handshake_len); return 0; } static int extend_cell_from_extend2_cell_body(extend_cell_t *cell_out, const extend2_cell_body_t *cell) { tor_assert(cell_out); tor_assert(cell); int found_ipv4 = 0, found_ipv6 = 0, found_rsa_id = 0, found_ed_id = 0; memset(cell_out, 0, sizeof(*cell_out)); tor_addr_make_unspec(&cell_out->orport_ipv4.addr); tor_addr_make_unspec(&cell_out->orport_ipv6.addr); cell_out->cell_type = RELAY_COMMAND_EXTEND2; unsigned i; for (i = 0; i < cell->n_spec; ++i) { const link_specifier_t *ls = extend2_cell_body_getconst_ls(cell, i); switch (ls->ls_type) { case LS_IPV4: if (found_ipv4) continue; found_ipv4 = 1; tor_addr_from_ipv4h(&cell_out->orport_ipv4.addr, ls->un_ipv4_addr); cell_out->orport_ipv4.port = ls->un_ipv4_port; break; case LS_IPV6: if (found_ipv6) continue; found_ipv6 = 1; tor_addr_from_ipv6_bytes(&cell_out->orport_ipv6.addr, (const char *)ls->un_ipv6_addr); cell_out->orport_ipv6.port = ls->un_ipv6_port; break; case LS_LEGACY_ID: if (found_rsa_id) return -1; found_rsa_id = 1; memcpy(cell_out->node_id, ls->un_legacy_id, 20); break; case LS_ED25519_ID: if (found_ed_id) return -1; found_ed_id = 1; memcpy(cell_out->ed_pubkey.pubkey, ls->un_ed25519_id, 32); break; default: /* Ignore this, whatever it is. */ break; } } if (!found_rsa_id || !found_ipv4) /* These are mandatory */ return -1; return create_cell_from_create2_cell_body(&cell_out->create_cell, cell->create2); } /** Parse an EXTEND or EXTEND2 cell (according to command) from the * payload_length bytes of payload into cell_out. Return * 0 on success, -1 on failure. */ int extend_cell_parse(extend_cell_t *cell_out, const uint8_t command, const uint8_t *payload, size_t payload_length) { tor_assert(cell_out); tor_assert(payload); if (payload_length > RELAY_PAYLOAD_SIZE) return -1; switch (command) { case RELAY_COMMAND_EXTEND: { extend1_cell_body_t *cell = NULL; if (extend1_cell_body_parse(&cell, payload, payload_length)<0 || cell == NULL) { if (cell) extend1_cell_body_free(cell); return -1; } int r = extend_cell_from_extend1_cell_body(cell_out, cell); extend1_cell_body_free(cell); if (r < 0) return r; } break; case RELAY_COMMAND_EXTEND2: { extend2_cell_body_t *cell = NULL; if (extend2_cell_body_parse(&cell, payload, payload_length) < 0 || cell == NULL) { if (cell) extend2_cell_body_free(cell); return -1; } int r = extend_cell_from_extend2_cell_body(cell_out, cell); extend2_cell_body_free(cell); if (r < 0) return r; } break; default: return -1; } return check_extend_cell(cell_out); } /** Helper: return 0 if cell appears valid, -1 otherwise. */ static int check_extended_cell(const extended_cell_t *cell) { tor_assert(cell); if (cell->created_cell.cell_type == CELL_CREATED) { if (cell->cell_type != RELAY_COMMAND_EXTENDED) return -1; } else if (cell->created_cell.cell_type == CELL_CREATED2) { if (cell->cell_type != RELAY_COMMAND_EXTENDED2) return -1; } else { return -1; } return check_created_cell(&cell->created_cell); } /** Parse an EXTENDED or EXTENDED2 cell (according to command) from the * payload_length bytes of payload into cell_out. Return * 0 on success, -1 on failure. */ int extended_cell_parse(extended_cell_t *cell_out, const uint8_t command, const uint8_t *payload, size_t payload_len) { tor_assert(cell_out); tor_assert(payload); memset(cell_out, 0, sizeof(*cell_out)); if (payload_len > RELAY_PAYLOAD_SIZE) return -1; switch (command) { case RELAY_COMMAND_EXTENDED: if (payload_len != TAP_ONIONSKIN_REPLY_LEN) return -1; cell_out->cell_type = RELAY_COMMAND_EXTENDED; cell_out->created_cell.cell_type = CELL_CREATED; cell_out->created_cell.handshake_len = TAP_ONIONSKIN_REPLY_LEN; memcpy(cell_out->created_cell.reply, payload, TAP_ONIONSKIN_REPLY_LEN); break; case RELAY_COMMAND_EXTENDED2: { cell_out->cell_type = RELAY_COMMAND_EXTENDED2; cell_out->created_cell.cell_type = CELL_CREATED2; cell_out->created_cell.handshake_len = ntohs(get_uint16(payload)); if (cell_out->created_cell.handshake_len > RELAY_PAYLOAD_SIZE - 2 || cell_out->created_cell.handshake_len > payload_len - 2) return -1; memcpy(cell_out->created_cell.reply, payload+2, cell_out->created_cell.handshake_len); } break; default: return -1; } return check_extended_cell(cell_out); } /** Fill cell_out with a correctly formatted version of the * CREATE{,_FAST,2} cell in cell_in. Return 0 on success, -1 on * failure. This is a cell we didn't originate if relayed is true. */ static int create_cell_format_impl(cell_t *cell_out, const create_cell_t *cell_in, int relayed) { uint8_t *p; size_t space; if (check_create_cell(cell_in, relayed) < 0) return -1; memset(cell_out->payload, 0, sizeof(cell_out->payload)); cell_out->command = cell_in->cell_type; p = cell_out->payload; space = sizeof(cell_out->payload); switch (cell_in->cell_type) { case CELL_CREATE: if (cell_in->handshake_type == ONION_HANDSHAKE_TYPE_NTOR) { memcpy(p, NTOR_CREATE_MAGIC, 16); p += 16; space -= 16; } /* Fall through */ case CELL_CREATE_FAST: tor_assert(cell_in->handshake_len <= space); memcpy(p, cell_in->onionskin, cell_in->handshake_len); break; case CELL_CREATE2: tor_assert(cell_in->handshake_len <= sizeof(cell_out->payload)-4); set_uint16(cell_out->payload, htons(cell_in->handshake_type)); set_uint16(cell_out->payload+2, htons(cell_in->handshake_len)); memcpy(cell_out->payload + 4, cell_in->onionskin, cell_in->handshake_len); break; default: return -1; } return 0; } int create_cell_format(cell_t *cell_out, const create_cell_t *cell_in) { return create_cell_format_impl(cell_out, cell_in, 0); } int create_cell_format_relayed(cell_t *cell_out, const create_cell_t *cell_in) { return create_cell_format_impl(cell_out, cell_in, 1); } /** Fill cell_out with a correctly formatted version of the * CREATED{,_FAST,2} cell in cell_in. Return 0 on success, -1 on * failure. */ int created_cell_format(cell_t *cell_out, const created_cell_t *cell_in) { if (check_created_cell(cell_in) < 0) return -1; memset(cell_out->payload, 0, sizeof(cell_out->payload)); cell_out->command = cell_in->cell_type; switch (cell_in->cell_type) { case CELL_CREATED: case CELL_CREATED_FAST: tor_assert(cell_in->handshake_len <= sizeof(cell_out->payload)); memcpy(cell_out->payload, cell_in->reply, cell_in->handshake_len); break; case CELL_CREATED2: tor_assert(cell_in->handshake_len <= sizeof(cell_out->payload)-2); set_uint16(cell_out->payload, htons(cell_in->handshake_len)); memcpy(cell_out->payload + 2, cell_in->reply, cell_in->handshake_len); break; default: return -1; } return 0; } /** Return true iff we are configured (by torrc or by the networkstatus * parameters) to use Ed25519 identities in our Extend2 cells. */ static int should_include_ed25519_id_extend_cells(const networkstatus_t *ns, const or_options_t *options) { if (options->ExtendByEd25519ID != -1) return options->ExtendByEd25519ID; /* The user has an opinion. */ return (int) networkstatus_get_param(ns, "ExtendByEd25519ID", 0 /* default */, 0 /* min */, 1 /*max*/); } /** Format the EXTEND{,2} cell in cell_in, storing its relay payload in * payload_out, the number of bytes used in *len_out, and the * relay command in *command_out. The payload_out must have * RELAY_PAYLOAD_SIZE bytes available. Return 0 on success, -1 on failure. */ int extend_cell_format(uint8_t *command_out, uint16_t *len_out, uint8_t *payload_out, const extend_cell_t *cell_in) { uint8_t *p; if (check_extend_cell(cell_in) < 0) return -1; p = payload_out; memset(p, 0, RELAY_PAYLOAD_SIZE); switch (cell_in->cell_type) { case RELAY_COMMAND_EXTEND: { *command_out = RELAY_COMMAND_EXTEND; *len_out = 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN; set_uint32(p, tor_addr_to_ipv4n(&cell_in->orport_ipv4.addr)); set_uint16(p+4, htons(cell_in->orport_ipv4.port)); if (cell_in->create_cell.handshake_type == ONION_HANDSHAKE_TYPE_NTOR) { memcpy(p+6, NTOR_CREATE_MAGIC, 16); memcpy(p+22, cell_in->create_cell.onionskin, NTOR_ONIONSKIN_LEN); } else { memcpy(p+6, cell_in->create_cell.onionskin, TAP_ONIONSKIN_CHALLENGE_LEN); } memcpy(p+6+TAP_ONIONSKIN_CHALLENGE_LEN, cell_in->node_id, DIGEST_LEN); } break; case RELAY_COMMAND_EXTEND2: { uint8_t n_specifiers = 2; *command_out = RELAY_COMMAND_EXTEND2; extend2_cell_body_t *cell = extend2_cell_body_new(); link_specifier_t *ls; { /* IPv4 specifier first. */ ls = link_specifier_new(); extend2_cell_body_add_ls(cell, ls); ls->ls_type = LS_IPV4; ls->ls_len = 6; ls->un_ipv4_addr = tor_addr_to_ipv4h(&cell_in->orport_ipv4.addr); ls->un_ipv4_port = cell_in->orport_ipv4.port; } { /* Then RSA id */ ls = link_specifier_new(); extend2_cell_body_add_ls(cell, ls); ls->ls_type = LS_LEGACY_ID; ls->ls_len = DIGEST_LEN; memcpy(ls->un_legacy_id, cell_in->node_id, DIGEST_LEN); } if (should_include_ed25519_id_extend_cells(NULL, get_options()) && !ed25519_public_key_is_zero(&cell_in->ed_pubkey)) { /* Then, maybe, the ed25519 id! */ ++n_specifiers; ls = link_specifier_new(); extend2_cell_body_add_ls(cell, ls); ls->ls_type = LS_ED25519_ID; ls->ls_len = 32; memcpy(ls->un_ed25519_id, cell_in->ed_pubkey.pubkey, 32); } cell->n_spec = n_specifiers; /* Now, the handshake */ cell->create2 = create2_cell_body_new(); cell->create2->handshake_type = cell_in->create_cell.handshake_type; cell->create2->handshake_len = cell_in->create_cell.handshake_len; create2_cell_body_setlen_handshake_data(cell->create2, cell_in->create_cell.handshake_len); memcpy(create2_cell_body_getarray_handshake_data(cell->create2), cell_in->create_cell.onionskin, cell_in->create_cell.handshake_len); ssize_t len_encoded = extend2_cell_body_encode( payload_out, RELAY_PAYLOAD_SIZE, cell); extend2_cell_body_free(cell); if (len_encoded < 0 || len_encoded > UINT16_MAX) return -1; *len_out = (uint16_t) len_encoded; } break; default: return -1; } return 0; } /** Format the EXTENDED{,2} cell in cell_in, storing its relay payload * in payload_out, the number of bytes used in *len_out, and the * relay command in *command_out. The payload_out must have * RELAY_PAYLOAD_SIZE bytes available. Return 0 on success, -1 on failure. */ int extended_cell_format(uint8_t *command_out, uint16_t *len_out, uint8_t *payload_out, const extended_cell_t *cell_in) { uint8_t *p; if (check_extended_cell(cell_in) < 0) return -1; p = payload_out; memset(p, 0, RELAY_PAYLOAD_SIZE); switch (cell_in->cell_type) { case RELAY_COMMAND_EXTENDED: { *command_out = RELAY_COMMAND_EXTENDED; *len_out = TAP_ONIONSKIN_REPLY_LEN; memcpy(payload_out, cell_in->created_cell.reply, TAP_ONIONSKIN_REPLY_LEN); } break; case RELAY_COMMAND_EXTENDED2: { *command_out = RELAY_COMMAND_EXTENDED2; *len_out = 2 + cell_in->created_cell.handshake_len; set_uint16(payload_out, htons(cell_in->created_cell.handshake_len)); if (2+cell_in->created_cell.handshake_len > RELAY_PAYLOAD_SIZE) return -1; memcpy(payload_out+2, cell_in->created_cell.reply, cell_in->created_cell.handshake_len); } break; default: return -1; } return 0; }