commit ed62f0fa151f7a21a9634fbec1e95832ddbede94 Merge: 98755cbdd 3e64553f7 Author: Nick Mathewson nickm@torproject.org Date: Thu Jan 3 09:02:39 2019 -0500
Merge branch 'maint-0.3.4' into maint-0.3.5
changes/ticket28973 | 6 ++++++ src/core/or/connection_or.c | 16 +++++++++------ src/lib/tls/tortls_openssl.c | 46 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 7 deletions(-)
diff --cc src/core/or/connection_or.c index 65f4e28c9,000000000..e6f04259f mode 100644,000000..100644 --- a/src/core/or/connection_or.c +++ b/src/core/or/connection_or.c @@@ -1,3022 -1,0 +1,3026 @@@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file connection_or.c + * \brief Functions to handle OR connections, TLS handshaking, and + * cells on the network. + * + * An or_connection_t is a subtype of connection_t (as implemented in + * connection.c) that uses a TLS connection to send and receive cells on the + * Tor network. (By sending and receiving cells connection_or.c, it cooperates + * with channeltls.c to implement a the channel interface of channel.c.) + * + * Every OR connection has an underlying tortls_t object (as implemented in + * tortls.c) which it uses as its TLS stream. It is responsible for + * sending and receiving cells over that TLS. + * + * This module also implements the client side of the v3 Tor link handshake, + **/ +#include "core/or/or.h" +#include "feature/client/bridges.h" +#include "lib/container/buffers.h" +/* + * Define this so we get channel internal functions, since we're implementing + * part of a subclass (channel_tls_t). + */ +#define TOR_CHANNEL_INTERNAL_ +#define CONNECTION_OR_PRIVATE +#include "core/or/channel.h" +#include "core/or/channeltls.h" +#include "core/or/circuitbuild.h" +#include "core/or/circuitlist.h" +#include "core/or/circuitstats.h" +#include "core/or/command.h" +#include "app/config/config.h" +#include "core/mainloop/connection.h" +#include "core/or/connection_or.h" +#include "feature/control/control.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_util.h" +#include "feature/dirauth/reachability.h" +#include "feature/client/entrynodes.h" +#include "lib/geoip/geoip.h" +#include "core/mainloop/mainloop.h" +#include "trunnel/link_handshake.h" +#include "feature/nodelist/microdesc.h" +#include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/nodelist.h" +#include "core/proto/proto_cell.h" +#include "core/or/reasons.h" +#include "core/or/relay.h" +#include "feature/rend/rendcommon.h" +#include "feature/stats/rephist.h" +#include "feature/relay/router.h" +#include "feature/relay/routerkeys.h" +#include "feature/relay/routermode.h" +#include "feature/nodelist/dirlist.h" +#include "feature/nodelist/routerlist.h" +#include "feature/relay/ext_orport.h" +#include "core/or/scheduler.h" +#include "feature/nodelist/torcert.h" +#include "core/or/channelpadding.h" +#include "feature/dirauth/authmode.h" + +#include "core/or/cell_st.h" +#include "core/or/cell_queue_st.h" +#include "core/or/or_connection_st.h" +#include "core/or/or_handshake_certs_st.h" +#include "core/or/or_handshake_state_st.h" +#include "app/config/or_state_st.h" +#include "feature/nodelist/routerinfo_st.h" +#include "core/or/var_cell_st.h" +#include "lib/crypt_ops/crypto_format.h" + +#include "lib/tls/tortls.h" +#include "lib/tls/x509.h" + +static int connection_tls_finish_handshake(or_connection_t *conn); +static int connection_or_launch_v3_or_handshake(or_connection_t *conn); +static int connection_or_process_cells_from_inbuf(or_connection_t *conn); +static int connection_or_check_valid_tls_handshake(or_connection_t *conn, + int started_here, + char *digest_rcvd_out); + +static void connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn); + +static unsigned int +connection_or_is_bad_for_new_circs(or_connection_t *or_conn); +static void connection_or_mark_bad_for_new_circs(or_connection_t *or_conn); + +/* + * Call this when changing connection state, so notifications to the owning + * channel can be handled. + */ + +static void connection_or_change_state(or_connection_t *conn, uint8_t state); + +static void connection_or_check_canonicity(or_connection_t *conn, + int started_here); + +/**************************************************************/ + +/** Convert a connection_t* to an or_connection_t*; assert if the cast is + * invalid. */ +or_connection_t * +TO_OR_CONN(connection_t *c) +{ + tor_assert(c->magic == OR_CONNECTION_MAGIC); + return DOWNCAST(or_connection_t, c); +} + +/** Global map between Extended ORPort identifiers and OR + * connections. */ +static digestmap_t *orconn_ext_or_id_map = NULL; + +/** Clear clear conn->identity_digest and update other data + * structures as appropriate.*/ +void +connection_or_clear_identity(or_connection_t *conn) +{ + tor_assert(conn); + memset(conn->identity_digest, 0, DIGEST_LEN); +} + +/** Clear all identities in OR conns.*/ +void +connection_or_clear_identity_map(void) +{ + smartlist_t *conns = get_connection_array(); + SMARTLIST_FOREACH(conns, connection_t *, conn, + { + if (conn->type == CONN_TYPE_OR) { + connection_or_clear_identity(TO_OR_CONN(conn)); + } + }); +} + +/** Change conn->identity_digest to digest, and add conn into + * the appropriate digest maps. + * + * NOTE that this function only allows two kinds of transitions: from + * unset identity to set identity, and from idempotent re-settings + * of the same identity. It's not allowed to clear an identity or to + * change an identity. Return 0 on success, and -1 if the transition + * is not allowed. + **/ +static void +connection_or_set_identity_digest(or_connection_t *conn, + const char *rsa_digest, + const ed25519_public_key_t *ed_id) +{ + channel_t *chan = NULL; + tor_assert(conn); + tor_assert(rsa_digest); + + if (conn->chan) + chan = TLS_CHAN_TO_BASE(conn->chan); + + log_info(LD_HANDSHAKE, "Set identity digest for %p (%s): %s %s.", + conn, + escaped_safe_str(conn->base_.address), + hex_str(rsa_digest, DIGEST_LEN), + ed25519_fmt(ed_id)); + log_info(LD_HANDSHAKE, " (Previously: %s %s)", + hex_str(conn->identity_digest, DIGEST_LEN), + chan ? ed25519_fmt(&chan->ed25519_identity) : "<null>"); + + const int rsa_id_was_set = ! tor_digest_is_zero(conn->identity_digest); + const int ed_id_was_set = + chan && !ed25519_public_key_is_zero(&chan->ed25519_identity); + const int rsa_changed = + tor_memneq(conn->identity_digest, rsa_digest, DIGEST_LEN); + const int ed_changed = ed_id_was_set && + (!ed_id || !ed25519_pubkey_eq(ed_id, &chan->ed25519_identity)); + + tor_assert(!rsa_changed || !rsa_id_was_set); + tor_assert(!ed_changed || !ed_id_was_set); + + if (!rsa_changed && !ed_changed) + return; + + /* If the identity was set previously, remove the old mapping. */ + if (rsa_id_was_set) { + connection_or_clear_identity(conn); + if (chan) + channel_clear_identity_digest(chan); + } + + memcpy(conn->identity_digest, rsa_digest, DIGEST_LEN); + + /* If we're initializing the IDs to zero, don't add a mapping yet. */ + if (tor_digest_is_zero(rsa_digest) && + (!ed_id || ed25519_public_key_is_zero(ed_id))) + return; + + /* Deal with channels */ + if (chan) + channel_set_identity_digest(chan, rsa_digest, ed_id); +} + +/** Remove the Extended ORPort identifier of <b>conn</b> from the + * global identifier list. Also, clear the identifier from the + * connection itself. */ +void +connection_or_remove_from_ext_or_id_map(or_connection_t *conn) +{ + or_connection_t *tmp; + if (!orconn_ext_or_id_map) + return; + if (!conn->ext_or_conn_id) + return; + + tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id); + if (!tor_digest_is_zero(conn->ext_or_conn_id)) + tor_assert(tmp == conn); + + memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN); +} + +/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such + * connection is found. */ +or_connection_t * +connection_or_get_by_ext_or_id(const char *id) +{ + if (!orconn_ext_or_id_map) + return NULL; + return digestmap_get(orconn_ext_or_id_map, id); +} + +/** Deallocate the global Extended ORPort identifier list */ +void +connection_or_clear_ext_or_id_map(void) +{ + digestmap_free(orconn_ext_or_id_map, NULL); + orconn_ext_or_id_map = NULL; +} + +/** Creates an Extended ORPort identifier for <b>conn</b> and deposits + * it into the global list of identifiers. */ +void +connection_or_set_ext_or_identifier(or_connection_t *conn) +{ + char random_id[EXT_OR_CONN_ID_LEN]; + or_connection_t *tmp; + + if (!orconn_ext_or_id_map) + orconn_ext_or_id_map = digestmap_new(); + + /* Remove any previous identifiers: */ + if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id)) + connection_or_remove_from_ext_or_id_map(conn); + + do { + crypto_rand(random_id, sizeof(random_id)); + } while (digestmap_get(orconn_ext_or_id_map, random_id)); + + if (!conn->ext_or_conn_id) + conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN); + + memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN); + + tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn); + tor_assert(!tmp); +} + +/**************************************************************/ + +/** Map from a string describing what a non-open OR connection was doing when + * failed, to an intptr_t describing the count of connections that failed that + * way. Note that the count is stored _as_ the pointer. + */ +static strmap_t *broken_connection_counts; + +/** If true, do not record information in <b>broken_connection_counts</b>. */ +static int disable_broken_connection_counts = 0; + +/** Record that an OR connection failed in <b>state</b>. */ +static void +note_broken_connection(const char *state) +{ + void *ptr; + intptr_t val; + if (disable_broken_connection_counts) + return; + + if (!broken_connection_counts) + broken_connection_counts = strmap_new(); + + ptr = strmap_get(broken_connection_counts, state); + val = (intptr_t)ptr; + val++; + ptr = (void*)val; + strmap_set(broken_connection_counts, state, ptr); +} + +/** Forget all recorded states for failed connections. If + * <b>stop_recording</b> is true, don't record any more. */ +void +clear_broken_connection_map(int stop_recording) +{ + if (broken_connection_counts) + strmap_free(broken_connection_counts, NULL); + broken_connection_counts = NULL; + if (stop_recording) + disable_broken_connection_counts = 1; +} + +/** Write a detailed description the state of <b>orconn</b> into the + * <b>buflen</b>-byte buffer at <b>buf</b>. This description includes not + * only the OR-conn level state but also the TLS state. It's useful for + * diagnosing broken handshakes. */ +static void +connection_or_get_state_description(or_connection_t *orconn, + char *buf, size_t buflen) +{ + connection_t *conn = TO_CONN(orconn); + const char *conn_state; + char tls_state[256]; + + tor_assert(conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR); + + conn_state = conn_state_to_string(conn->type, conn->state); + tor_tls_get_state_description(orconn->tls, tls_state, sizeof(tls_state)); + + tor_snprintf(buf, buflen, "%s with SSL state %s", conn_state, tls_state); +} + +/** Record the current state of <b>orconn</b> as the state of a broken + * connection. */ +static void +connection_or_note_state_when_broken(or_connection_t *orconn) +{ + char buf[256]; + if (disable_broken_connection_counts) + return; + connection_or_get_state_description(orconn, buf, sizeof(buf)); + log_info(LD_HANDSHAKE,"Connection died in state '%s'", buf); + note_broken_connection(buf); +} + +/** Helper type used to sort connection states and find the most frequent. */ +typedef struct broken_state_count_t { + intptr_t count; + const char *state; +} broken_state_count_t; + +/** Helper function used to sort broken_state_count_t by frequency. */ +static int +broken_state_count_compare(const void **a_ptr, const void **b_ptr) +{ + const broken_state_count_t *a = *a_ptr, *b = *b_ptr; + if (b->count < a->count) + return -1; + else if (b->count == a->count) + return 0; + else + return 1; +} + +/** Upper limit on the number of different states to report for connection + * failure. */ +#define MAX_REASONS_TO_REPORT 10 + +/** Report a list of the top states for failed OR connections at log level + * <b>severity</b>, in log domain <b>domain</b>. */ +void +connection_or_report_broken_states(int severity, int domain) +{ + int total = 0; + smartlist_t *items; + + if (!broken_connection_counts || disable_broken_connection_counts) + return; + + items = smartlist_new(); + STRMAP_FOREACH(broken_connection_counts, state, void *, countptr) { + broken_state_count_t *c = tor_malloc(sizeof(broken_state_count_t)); + c->count = (intptr_t)countptr; + total += (int)c->count; + c->state = state; + smartlist_add(items, c); + } STRMAP_FOREACH_END; + + smartlist_sort(items, broken_state_count_compare); + + tor_log(severity, domain, "%d connections have failed%s", total, + smartlist_len(items) > MAX_REASONS_TO_REPORT ? ". Top reasons:" : ":"); + + SMARTLIST_FOREACH_BEGIN(items, const broken_state_count_t *, c) { + if (c_sl_idx > MAX_REASONS_TO_REPORT) + break; + tor_log(severity, domain, + " %d connections died in state %s", (int)c->count, c->state); + } SMARTLIST_FOREACH_END(c); + + SMARTLIST_FOREACH(items, broken_state_count_t *, c, tor_free(c)); + smartlist_free(items); +} + +/** Call this to change or_connection_t states, so the owning channel_tls_t can + * be notified. + */ + +static void +connection_or_change_state(or_connection_t *conn, uint8_t state) +{ + uint8_t old_state; + + tor_assert(conn); + + old_state = conn->base_.state; + conn->base_.state = state; + + if (conn->chan) + channel_tls_handle_state_change_on_orconn(conn->chan, conn, + old_state, state); +} + +/** Return the number of circuits using an or_connection_t; this used to + * be an or_connection_t field, but it got moved to channel_t and we + * shouldn't maintain two copies. */ + +MOCK_IMPL(int, +connection_or_get_num_circuits, (or_connection_t *conn)) +{ + tor_assert(conn); + + if (conn->chan) { + return channel_num_circuits(TLS_CHAN_TO_BASE(conn->chan)); + } else return 0; +} + +/**************************************************************/ + +/** Pack the cell_t host-order structure <b>src</b> into network-order + * in the buffer <b>dest</b>. See tor-spec.txt for details about the + * wire format. + * + * Note that this function doesn't touch <b>dst</b>->next: the caller + * should set it or clear it as appropriate. + */ +void +cell_pack(packed_cell_t *dst, const cell_t *src, int wide_circ_ids) +{ + char *dest = dst->body; + if (wide_circ_ids) { + set_uint32(dest, htonl(src->circ_id)); + dest += 4; + } else { + /* Clear the last two bytes of dest, in case we can accidentally + * send them to the network somehow. */ + memset(dest+CELL_MAX_NETWORK_SIZE-2, 0, 2); + set_uint16(dest, htons(src->circ_id)); + dest += 2; + } + set_uint8(dest, src->command); + memcpy(dest+1, src->payload, CELL_PAYLOAD_SIZE); +} + +/** Unpack the network-order buffer <b>src</b> into a host-order + * cell_t structure <b>dest</b>. + */ +static void +cell_unpack(cell_t *dest, const char *src, int wide_circ_ids) +{ + if (wide_circ_ids) { + dest->circ_id = ntohl(get_uint32(src)); + src += 4; + } else { + dest->circ_id = ntohs(get_uint16(src)); + src += 2; + } + dest->command = get_uint8(src); + memcpy(dest->payload, src+1, CELL_PAYLOAD_SIZE); +} + +/** Write the header of <b>cell</b> into the first VAR_CELL_MAX_HEADER_SIZE + * bytes of <b>hdr_out</b>. Returns number of bytes used. */ +int +var_cell_pack_header(const var_cell_t *cell, char *hdr_out, int wide_circ_ids) +{ + int r; + if (wide_circ_ids) { + set_uint32(hdr_out, htonl(cell->circ_id)); + hdr_out += 4; + r = VAR_CELL_MAX_HEADER_SIZE; + } else { + set_uint16(hdr_out, htons(cell->circ_id)); + hdr_out += 2; + r = VAR_CELL_MAX_HEADER_SIZE - 2; + } + set_uint8(hdr_out, cell->command); + set_uint16(hdr_out+1, htons(cell->payload_len)); + return r; +} + +/** Allocate and return a new var_cell_t with <b>payload_len</b> bytes of + * payload space. */ +var_cell_t * +var_cell_new(uint16_t payload_len) +{ + size_t size = offsetof(var_cell_t, payload) + payload_len; + var_cell_t *cell = tor_malloc_zero(size); + cell->payload_len = payload_len; + cell->command = 0; + cell->circ_id = 0; + return cell; +} + +/** + * Copy a var_cell_t + */ + +var_cell_t * +var_cell_copy(const var_cell_t *src) +{ + var_cell_t *copy = NULL; + size_t size = 0; + + if (src != NULL) { + size = offsetof(var_cell_t, payload) + src->payload_len; + copy = tor_malloc_zero(size); + copy->payload_len = src->payload_len; + copy->command = src->command; + copy->circ_id = src->circ_id; + memcpy(copy->payload, src->payload, copy->payload_len); + } + + return copy; +} + +/** Release all space held by <b>cell</b>. */ +void +var_cell_free_(var_cell_t *cell) +{ + tor_free(cell); +} + +/** We've received an EOF from <b>conn</b>. Mark it for close and return. */ +int +connection_or_reached_eof(or_connection_t *conn) +{ + tor_assert(conn); + + log_info(LD_OR,"OR connection reached EOF. Closing."); + connection_or_close_normally(conn, 1); + + return 0; +} + +/** Handle any new bytes that have come in on connection <b>conn</b>. + * If conn is in 'open' state, hand it to + * connection_or_process_cells_from_inbuf() + * (else do nothing). + */ +int +connection_or_process_inbuf(or_connection_t *conn) +{ + /** Don't let the inbuf of a nonopen OR connection grow beyond this many + * bytes: it's either a broken client, a non-Tor client, or a DOS + * attempt. */ +#define MAX_OR_INBUF_WHEN_NONOPEN 0 + + int ret = 0; + tor_assert(conn); + + switch (conn->base_.state) { + case OR_CONN_STATE_PROXY_HANDSHAKING: + ret = connection_read_proxy_handshake(TO_CONN(conn)); + + /* start TLS after handshake completion, or deal with error */ + if (ret == 1) { + tor_assert(TO_CONN(conn)->proxy_state == PROXY_CONNECTED); + if (connection_tls_start_handshake(conn, 0) < 0) + ret = -1; + /* Touch the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + } + if (ret < 0) { + connection_or_close_for_error(conn, 0); + } + + return ret; + case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: + case OR_CONN_STATE_OPEN: + case OR_CONN_STATE_OR_HANDSHAKING_V2: + case OR_CONN_STATE_OR_HANDSHAKING_V3: + return connection_or_process_cells_from_inbuf(conn); + default: + break; /* don't do anything */ + } + + /* This check was necessary with 0.2.2, when the TLS_SERVER_RENEGOTIATING + * check would otherwise just let data accumulate. It serves no purpose + * in 0.2.3. + * + * XXXX Remove this check once we verify that the above paragraph is + * 100% true. */ + if (buf_datalen(conn->base_.inbuf) > MAX_OR_INBUF_WHEN_NONOPEN) { + log_fn(LOG_PROTOCOL_WARN, LD_NET, "Accumulated too much data (%d bytes) " + "on nonopen OR connection %s %s:%u in state %s; closing.", + (int)buf_datalen(conn->base_.inbuf), + connection_or_nonopen_was_started_here(conn) ? "to" : "from", + conn->base_.address, conn->base_.port, + conn_state_to_string(conn->base_.type, conn->base_.state)); + connection_or_close_for_error(conn, 0); + ret = -1; + } + + return ret; +} + +/** Called whenever we have flushed some data on an or_conn: add more data + * from active circuits. */ +int +connection_or_flushed_some(or_connection_t *conn) +{ + size_t datalen; + + /* Update the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + + /* If we're under the low water mark, add cells until we're just over the + * high water mark. */ + datalen = connection_get_outbuf_len(TO_CONN(conn)); + if (datalen < OR_CONN_LOWWATER) { + /* Let the scheduler know */ + scheduler_channel_wants_writes(TLS_CHAN_TO_BASE(conn->chan)); + } + + return 0; +} + +/** This is for channeltls.c to ask how many cells we could accept if + * they were available. */ +ssize_t +connection_or_num_cells_writeable(or_connection_t *conn) +{ + size_t datalen, cell_network_size; + ssize_t n = 0; + + tor_assert(conn); + + /* + * If we're under the high water mark, we're potentially + * writeable; note this is different from the calculation above + * used to trigger when to start writing after we've stopped. + */ + datalen = connection_get_outbuf_len(TO_CONN(conn)); + if (datalen < OR_CONN_HIGHWATER) { + cell_network_size = get_cell_network_size(conn->wide_circ_ids); + n = CEIL_DIV(OR_CONN_HIGHWATER - datalen, cell_network_size); + } + + return n; +} + +/** Connection <b>conn</b> has finished writing and has no bytes left on + * its outbuf. + * + * Otherwise it's in state "open": stop writing and return. + * + * If <b>conn</b> is broken, mark it for close and return -1, else + * return 0. + */ +int +connection_or_finished_flushing(or_connection_t *conn) +{ + tor_assert(conn); + assert_connection_ok(TO_CONN(conn),0); + + switch (conn->base_.state) { + case OR_CONN_STATE_PROXY_HANDSHAKING: + case OR_CONN_STATE_OPEN: + case OR_CONN_STATE_OR_HANDSHAKING_V2: + case OR_CONN_STATE_OR_HANDSHAKING_V3: + break; + default: + log_err(LD_BUG,"Called in unexpected state %d.", conn->base_.state); + tor_fragile_assert(); + return -1; + } + + /* Update the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + + return 0; +} + +/** Connected handler for OR connections: begin the TLS handshake. + */ +int +connection_or_finished_connecting(or_connection_t *or_conn) +{ + const int proxy_type = or_conn->proxy_type; + connection_t *conn; + + tor_assert(or_conn); + conn = TO_CONN(or_conn); + tor_assert(conn->state == OR_CONN_STATE_CONNECTING); + + log_debug(LD_HANDSHAKE,"OR connect() to router at %s:%u finished.", + conn->address,conn->port); + control_event_bootstrap(BOOTSTRAP_STATUS_HANDSHAKE, 0); + control_event_boot_first_orconn(); + + if (proxy_type != PROXY_NONE) { + /* start proxy handshake */ + if (connection_proxy_connect(conn, proxy_type) < 0) { + connection_or_close_for_error(or_conn, 0); + return -1; + } + + connection_start_reading(conn); + connection_or_change_state(or_conn, OR_CONN_STATE_PROXY_HANDSHAKING); + return 0; + } + + if (connection_tls_start_handshake(or_conn, 0) < 0) { + /* TLS handshaking error of some kind. */ + connection_or_close_for_error(or_conn, 0); + return -1; + } + return 0; +} + +/** Called when we're about to finally unlink and free an OR connection: + * perform necessary accounting and cleanup */ +void +connection_or_about_to_close(or_connection_t *or_conn) +{ + connection_t *conn = TO_CONN(or_conn); + + /* Tell the controlling channel we're closed */ + if (or_conn->chan) { + channel_closed(TLS_CHAN_TO_BASE(or_conn->chan)); + /* + * NULL this out because the channel might hang around a little + * longer before channel_run_cleanup() gets it. + */ + or_conn->chan->conn = NULL; + or_conn->chan = NULL; + } + + /* Remember why we're closing this connection. */ + if (conn->state != OR_CONN_STATE_OPEN) { + /* now mark things down as needed */ + if (connection_or_nonopen_was_started_here(or_conn)) { + const or_options_t *options = get_options(); + connection_or_note_state_when_broken(or_conn); + /* Tell the new guard API about the channel failure */ + entry_guard_chan_failed(TLS_CHAN_TO_BASE(or_conn->chan)); + if (conn->state >= OR_CONN_STATE_TLS_HANDSHAKING) { + int reason = tls_error_to_orconn_end_reason(or_conn->tls_error); + control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED, + reason); + if (!authdir_mode_tests_reachability(options)) + control_event_bootstrap_prob_or( + orconn_end_reason_to_control_string(reason), + reason, or_conn); + } + } + } else if (conn->hold_open_until_flushed) { + /* We only set hold_open_until_flushed when we're intentionally + * closing a connection. */ + control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED, + tls_error_to_orconn_end_reason(or_conn->tls_error)); + } else if (!tor_digest_is_zero(or_conn->identity_digest)) { + control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED, + tls_error_to_orconn_end_reason(or_conn->tls_error)); + } +} + +/** Return 1 if identity digest <b>id_digest</b> is known to be a + * currently or recently running relay. Otherwise return 0. */ +int +connection_or_digest_is_known_relay(const char *id_digest) +{ + if (router_get_consensus_status_by_id(id_digest)) + return 1; /* It's in the consensus: "yes" */ + if (router_get_by_id_digest(id_digest)) + return 1; /* Not in the consensus, but we have a descriptor for + * it. Probably it was in a recent consensus. "Yes". */ + return 0; +} + +/** Set the per-conn read and write limits for <b>conn</b>. If it's a known + * relay, we will rely on the global read and write buckets, so give it + * per-conn limits that are big enough they'll never matter. But if it's + * not a known relay, first check if we set PerConnBwRate/Burst, then + * check if the consensus sets them, else default to 'big enough'. + * + * If <b>reset</b> is true, set the bucket to be full. Otherwise, just + * clip the bucket if it happens to be <em>too</em> full. + */ +static void +connection_or_update_token_buckets_helper(or_connection_t *conn, int reset, + const or_options_t *options) +{ + int rate, burst; /* per-connection rate limiting params */ + if (connection_or_digest_is_known_relay(conn->identity_digest)) { + /* It's in the consensus, or we have a descriptor for it meaning it + * was probably in a recent consensus. It's a recognized relay: + * give it full bandwidth. */ + rate = (int)options->BandwidthRate; + burst = (int)options->BandwidthBurst; + } else { + /* Not a recognized relay. Squeeze it down based on the suggested + * bandwidth parameters in the consensus, but allow local config + * options to override. */ + rate = options->PerConnBWRate ? (int)options->PerConnBWRate : + networkstatus_get_param(NULL, "perconnbwrate", + (int)options->BandwidthRate, 1, INT32_MAX); + burst = options->PerConnBWBurst ? (int)options->PerConnBWBurst : + networkstatus_get_param(NULL, "perconnbwburst", + (int)options->BandwidthBurst, 1, INT32_MAX); + } + + token_bucket_rw_adjust(&conn->bucket, rate, burst); + if (reset) { + token_bucket_rw_reset(&conn->bucket, monotime_coarse_get_stamp()); + } +} + +/** Either our set of relays or our per-conn rate limits have changed. + * Go through all the OR connections and update their token buckets to make + * sure they don't exceed their maximum values. */ +void +connection_or_update_token_buckets(smartlist_t *conns, + const or_options_t *options) +{ + SMARTLIST_FOREACH(conns, connection_t *, conn, + { + if (connection_speaks_cells(conn)) + connection_or_update_token_buckets_helper(TO_OR_CONN(conn), 0, options); + }); +} + +/* Mark <b>or_conn</b> as canonical if <b>is_canonical</b> is set, and + * non-canonical otherwise. Adjust idle_timeout accordingly. + */ +void +connection_or_set_canonical(or_connection_t *or_conn, + int is_canonical) +{ + if (bool_eq(is_canonical, or_conn->is_canonical) && + or_conn->idle_timeout != 0) { + /* Don't recalculate an existing idle_timeout unless the canonical + * status changed. */ + return; + } + + or_conn->is_canonical = !! is_canonical; /* force to a 1-bit boolean */ + or_conn->idle_timeout = channelpadding_get_channel_idle_timeout( + TLS_CHAN_TO_BASE(or_conn->chan), is_canonical); + + log_info(LD_CIRC, + "Channel %"PRIu64 " chose an idle timeout of %d.", + or_conn->chan ? + (TLS_CHAN_TO_BASE(or_conn->chan)->global_identifier):0, + or_conn->idle_timeout); +} + +/** If we don't necessarily know the router we're connecting to, but we + * have an addr/port/id_digest, then fill in as much as we can. Start + * by checking to see if this describes a router we know. + * <b>started_here</b> is 1 if we are the initiator of <b>conn</b> and + * 0 if it's an incoming connection. */ +void +connection_or_init_conn_from_address(or_connection_t *conn, + const tor_addr_t *addr, uint16_t port, + const char *id_digest, + const ed25519_public_key_t *ed_id, + int started_here) +{ + log_debug(LD_HANDSHAKE, "init conn from address %s: %s, %s (%d)", + fmt_addr(addr), + hex_str((const char*)id_digest, DIGEST_LEN), + ed25519_fmt(ed_id), + started_here); + + connection_or_set_identity_digest(conn, id_digest, ed_id); + connection_or_update_token_buckets_helper(conn, 1, get_options()); + + conn->base_.port = port; + tor_addr_copy(&conn->base_.addr, addr); + tor_addr_copy(&conn->real_addr, addr); + + connection_or_check_canonicity(conn, started_here); +} + +/** Check whether the identity of <b>conn</b> matches a known node. If it + * does, check whether the address of conn matches the expected address, and + * update the connection's is_canonical flag, nickname, and address fields as + * appropriate. */ +static void +connection_or_check_canonicity(or_connection_t *conn, int started_here) +{ + const char *id_digest = conn->identity_digest; + const ed25519_public_key_t *ed_id = NULL; + const tor_addr_t *addr = &conn->real_addr; + if (conn->chan) + ed_id = & TLS_CHAN_TO_BASE(conn->chan)->ed25519_identity; + + const node_t *r = node_get_by_id(id_digest); + if (r && + node_supports_ed25519_link_authentication(r, 1) && + ! node_ed25519_id_matches(r, ed_id)) { + /* If this node is capable of proving an ed25519 ID, + * we can't call this a canonical connection unless both IDs match. */ + r = NULL; + } + + if (r) { + tor_addr_port_t node_ap; + node_get_pref_orport(r, &node_ap); + /* XXXX proposal 186 is making this more complex. For now, a conn + is canonical when it uses the _preferred_ address. */ + if (tor_addr_eq(&conn->base_.addr, &node_ap.addr)) + connection_or_set_canonical(conn, 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! */ + /* XXXX arma: this is stupid, and it's the reason we need real_addr + * to track is_canonical properly. What requires it? */ + /* XXXX <arma> i believe the reason we did this, originally, is because + * we wanted to log what OR a connection was to, and if we logged the + * right IP address and port 56244, that wouldn't be as helpful. now we + * log the "right" port too, so we know if it's moria1 or moria2. + */ + tor_addr_copy(&conn->base_.addr, &node_ap.addr); + conn->base_.port = node_ap.port; + } + tor_free(conn->nickname); + conn->nickname = tor_strdup(node_get_nickname(r)); + tor_free(conn->base_.address); + conn->base_.address = tor_addr_to_str_dup(&node_ap.addr); + } else { + tor_free(conn->nickname); + conn->nickname = tor_malloc(HEX_DIGEST_LEN+2); + conn->nickname[0] = '$'; + base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1, + conn->identity_digest, DIGEST_LEN); + + tor_free(conn->base_.address); + conn->base_.address = tor_addr_to_str_dup(addr); + } + + /* + * We have to tell channeltls.c to update the channel marks (local, in + * particular), since we may have changed the address. + */ + + if (conn->chan) { + channel_tls_update_marks(conn); + } +} + +/** These just pass all the is_bad_for_new_circs manipulation on to + * channel_t */ + +static unsigned int +connection_or_is_bad_for_new_circs(or_connection_t *or_conn) +{ + tor_assert(or_conn); + + if (or_conn->chan) + return channel_is_bad_for_new_circs(TLS_CHAN_TO_BASE(or_conn->chan)); + else return 0; +} + +static void +connection_or_mark_bad_for_new_circs(or_connection_t *or_conn) +{ + tor_assert(or_conn); + + if (or_conn->chan) + channel_mark_bad_for_new_circs(TLS_CHAN_TO_BASE(or_conn->chan)); +} + +/** How old do we let a connection to an OR get before deciding it's + * too old for new circuits? */ +#define TIME_BEFORE_OR_CONN_IS_TOO_OLD (60*60*24*7) + +/** Expire an or_connection if it is too old. Helper for + * connection_or_group_set_badness_ and fast path for + * channel_rsa_id_group_set_badness. + * + * Returns 1 if the connection was already expired, else 0. + */ +int +connection_or_single_set_badness_(time_t now, + or_connection_t *or_conn, + int force) +{ + /* XXXX this function should also be about channels? */ + if (or_conn->base_.marked_for_close || + connection_or_is_bad_for_new_circs(or_conn)) + return 1; + + if (force || + or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD + < now) { + log_info(LD_OR, + "Marking OR conn to %s:%d as too old for new circuits " + "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", + or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + (int)(now - or_conn->base_.timestamp_created)); + connection_or_mark_bad_for_new_circs(or_conn); + } + + return 0; +} + +/** Given a list of all the or_connections with a given + * identity, set elements of that list as is_bad_for_new_circs as + * appropriate. Helper for connection_or_set_bad_connections(). + * + * Specifically, we set the is_bad_for_new_circs flag on: + * - all connections if <b>force</b> is true. + * - all connections that are too old. + * - all open non-canonical connections for which a canonical connection + * exists to the same router. + * - all open canonical connections for which a 'better' canonical + * connection exists to the same router. + * - all open non-canonical connections for which a 'better' non-canonical + * connection exists to the same router at the same address. + * + * See channel_is_better() in channel.c for our idea of what makes one OR + * connection better than another. + */ +void +connection_or_group_set_badness_(smartlist_t *group, int force) +{ + /* XXXX this function should be entirely about channels, not OR + * XXXX connections. */ + + or_connection_t *best = NULL; + int n_old = 0, n_inprogress = 0, n_canonical = 0, n_other = 0; + time_t now = time(NULL); + + /* Pass 1: expire everything that's old, and see what the status of + * everything else is. */ + SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) { + if (connection_or_single_set_badness_(now, or_conn, force)) + continue; + + if (connection_or_is_bad_for_new_circs(or_conn)) { + ++n_old; + } else if (or_conn->base_.state != OR_CONN_STATE_OPEN) { + ++n_inprogress; + } else if (or_conn->is_canonical) { + ++n_canonical; + } else { + ++n_other; + } + } SMARTLIST_FOREACH_END(or_conn); + + /* Pass 2: We know how about how good the best connection is. + * expire everything that's worse, and find the very best if we can. */ + SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) { + if (or_conn->base_.marked_for_close || + connection_or_is_bad_for_new_circs(or_conn)) + continue; /* This one doesn't need to be marked bad. */ + if (or_conn->base_.state != OR_CONN_STATE_OPEN) + continue; /* Don't mark anything bad until we have seen what happens + * when the connection finishes. */ + if (n_canonical && !or_conn->is_canonical) { + /* We have at least one open canonical connection to this router, + * and this one is open but not canonical. Mark it bad. */ + log_info(LD_OR, + "Marking OR conn to %s:%d as unsuitable for new circuits: " + "(fd "TOR_SOCKET_T_FORMAT", %d secs old). It is not " + "canonical, and we have another connection to that OR that is.", + or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + (int)(now - or_conn->base_.timestamp_created)); + connection_or_mark_bad_for_new_circs(or_conn); + continue; + } + + if (!best || + channel_is_better(TLS_CHAN_TO_BASE(or_conn->chan), + TLS_CHAN_TO_BASE(best->chan))) { + best = or_conn; + } + } SMARTLIST_FOREACH_END(or_conn); + + if (!best) + return; + + /* Pass 3: One connection to OR is best. If it's canonical, mark as bad + * every other open connection. If it's non-canonical, mark as bad + * every other open connection to the same address. + * + * XXXX This isn't optimal; if we have connections to an OR at multiple + * addresses, we'd like to pick the best _for each address_, and mark as + * bad every open connection that isn't best for its address. But this + * can only occur in cases where the other OR is old (so we have no + * canonical connection to it), or where all the connections to the OR are + * at noncanonical addresses and we have no good direct connection (which + * means we aren't at risk of attaching circuits to it anyway). As + * 0.1.2.x dies out, the first case will go away, and the second one is + * "mostly harmless", so a fix can wait until somebody is bored. + */ + SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) { + if (or_conn->base_.marked_for_close || + connection_or_is_bad_for_new_circs(or_conn) || + or_conn->base_.state != OR_CONN_STATE_OPEN) + continue; + if (or_conn != best && + channel_is_better(TLS_CHAN_TO_BASE(best->chan), + TLS_CHAN_TO_BASE(or_conn->chan))) { + /* This isn't the best conn, _and_ the best conn is better than it */ + if (best->is_canonical) { + log_info(LD_OR, + "Marking OR conn to %s:%d as unsuitable for new circuits: " + "(fd "TOR_SOCKET_T_FORMAT", %d secs old). " + "We have a better canonical one " + "(fd "TOR_SOCKET_T_FORMAT"; %d secs old).", + or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + (int)(now - or_conn->base_.timestamp_created), + best->base_.s, (int)(now - best->base_.timestamp_created)); + connection_or_mark_bad_for_new_circs(or_conn); + } else if (!tor_addr_compare(&or_conn->real_addr, + &best->real_addr, CMP_EXACT)) { + log_info(LD_OR, + "Marking OR conn to %s:%d as unsuitable for new circuits: " + "(fd "TOR_SOCKET_T_FORMAT", %d secs old). We have a better " + "one with the " + "same address (fd "TOR_SOCKET_T_FORMAT"; %d secs old).", + or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + (int)(now - or_conn->base_.timestamp_created), + best->base_.s, (int)(now - best->base_.timestamp_created)); + connection_or_mark_bad_for_new_circs(or_conn); + } + } + } SMARTLIST_FOREACH_END(or_conn); +} + +/* Lifetime of a connection failure. After that, we'll retry. This is in + * seconds. */ +#define OR_CONNECT_FAILURE_LIFETIME 60 +/* The interval to use with when to clean up the failure cache. */ +#define OR_CONNECT_FAILURE_CLEANUP_INTERVAL 60 + +/* When is the next time we have to cleanup the failure map. We keep this + * because we clean it opportunistically. */ +static time_t or_connect_failure_map_next_cleanup_ts = 0; + +/* OR connection failure entry data structure. It is kept in the connection + * failure map defined below and indexed by OR identity digest, address and + * port. + * + * We need to identify a connection failure with these three values because we + * want to avoid to wrongfully blacklist a relay if someone is trying to + * extend to a known identity digest but with the wrong IP/port. For instance, + * it can happen if a relay changed its port but the client still has an old + * descriptor with the old port. We want to stop connecting to that + * IP/port/identity all together, not only the relay identity. */ +typedef struct or_connect_failure_entry_t { + HT_ENTRY(or_connect_failure_entry_t) node; + /* Identity digest of the connection where it is connecting to. */ + uint8_t identity_digest[DIGEST_LEN]; + /* This is the connection address from the base connection_t. After the + * connection is checked for canonicity, the base address should represent + * what we know instead of where we are connecting to. This is what we need + * so we can correlate known relays within the consensus. */ + tor_addr_t addr; + uint16_t port; + /* Last time we were unable to connect. */ + time_t last_failed_connect_ts; +} or_connect_failure_entry_t; + +/* Map where we keep connection failure entries. They are indexed by addr, + * port and identity digest. */ +static HT_HEAD(or_connect_failure_ht, or_connect_failure_entry_t) + or_connect_failures_map = HT_INITIALIZER(); + +/* Helper: Hashtable equal function. Return 1 if equal else 0. */ +static int +or_connect_failure_ht_eq(const or_connect_failure_entry_t *a, + const or_connect_failure_entry_t *b) +{ + return fast_memeq(a->identity_digest, b->identity_digest, DIGEST_LEN) && + tor_addr_eq(&a->addr, &b->addr) && + a->port == b->port; +} + +/* Helper: Return the hash for the hashtable of the given entry. For this + * table, it is a combination of address, port and identity digest. */ +static unsigned int +or_connect_failure_ht_hash(const or_connect_failure_entry_t *entry) +{ + size_t offset = 0, addr_size; + const void *addr_ptr; + /* Largest size is IPv6 and IPv4 is smaller so it is fine. */ + uint8_t data[16 + sizeof(uint16_t) + DIGEST_LEN]; + + /* Get the right address bytes depending on the family. */ + switch (tor_addr_family(&entry->addr)) { + case AF_INET: + addr_size = 4; + addr_ptr = &entry->addr.addr.in_addr.s_addr; + break; + case AF_INET6: + addr_size = 16; + addr_ptr = &entry->addr.addr.in6_addr.s6_addr; + break; + default: + tor_assert_nonfatal_unreached(); + return 0; + } + + memcpy(data, addr_ptr, addr_size); + offset += addr_size; + memcpy(data + offset, entry->identity_digest, DIGEST_LEN); + offset += DIGEST_LEN; + set_uint16(data + offset, entry->port); + offset += sizeof(uint16_t); + + return (unsigned int) siphash24g(data, offset); +} + +HT_PROTOTYPE(or_connect_failure_ht, or_connect_failure_entry_t, node, + or_connect_failure_ht_hash, or_connect_failure_ht_eq) + +HT_GENERATE2(or_connect_failure_ht, or_connect_failure_entry_t, node, + or_connect_failure_ht_hash, or_connect_failure_ht_eq, + 0.6, tor_reallocarray_, tor_free_) + +/* Initialize a given connect failure entry with the given identity_digest, + * addr and port. All field are optional except ocf. */ +static void +or_connect_failure_init(const char *identity_digest, const tor_addr_t *addr, + uint16_t port, or_connect_failure_entry_t *ocf) +{ + tor_assert(ocf); + if (identity_digest) { + memcpy(ocf->identity_digest, identity_digest, + sizeof(ocf->identity_digest)); + } + if (addr) { + tor_addr_copy(&ocf->addr, addr); + } + ocf->port = port; +} + +/* Return a newly allocated connection failure entry. It is initialized with + * the given or_conn data. This can't fail. */ +static or_connect_failure_entry_t * +or_connect_failure_new(const or_connection_t *or_conn) +{ + or_connect_failure_entry_t *ocf = tor_malloc_zero(sizeof(*ocf)); + or_connect_failure_init(or_conn->identity_digest, &or_conn->real_addr, + TO_CONN(or_conn)->port, ocf); + return ocf; +} + +/* Return a connection failure entry matching the given or_conn. NULL is + * returned if not found. */ +static or_connect_failure_entry_t * +or_connect_failure_find(const or_connection_t *or_conn) +{ + or_connect_failure_entry_t lookup; + tor_assert(or_conn); + or_connect_failure_init(or_conn->identity_digest, &TO_CONN(or_conn)->addr, + TO_CONN(or_conn)->port, &lookup); + return HT_FIND(or_connect_failure_ht, &or_connect_failures_map, &lookup); +} + +/* Note down in the connection failure cache that a failure occurred on the + * given or_conn. */ +STATIC void +note_or_connect_failed(const or_connection_t *or_conn) +{ + or_connect_failure_entry_t *ocf = NULL; + + tor_assert(or_conn); + + ocf = or_connect_failure_find(or_conn); + if (ocf == NULL) { + ocf = or_connect_failure_new(or_conn); + HT_INSERT(or_connect_failure_ht, &or_connect_failures_map, ocf); + } + ocf->last_failed_connect_ts = approx_time(); +} + +/* Cleanup the connection failure cache and remove all entries below the + * given cutoff. */ +static void +or_connect_failure_map_cleanup(time_t cutoff) +{ + or_connect_failure_entry_t **ptr, **next, *entry; + + for (ptr = HT_START(or_connect_failure_ht, &or_connect_failures_map); + ptr != NULL; ptr = next) { + entry = *ptr; + if (entry->last_failed_connect_ts <= cutoff) { + next = HT_NEXT_RMV(or_connect_failure_ht, &or_connect_failures_map, ptr); + tor_free(entry); + } else { + next = HT_NEXT(or_connect_failure_ht, &or_connect_failures_map, ptr); + } + } +} + +/* Return true iff the given OR connection can connect to its destination that + * is the triplet identity_digest, address and port. + * + * The or_conn MUST have gone through connection_or_check_canonicity() so the + * base address is properly set to what we know or doesn't know. */ +STATIC int +should_connect_to_relay(const or_connection_t *or_conn) +{ + time_t now, cutoff; + time_t connect_failed_since_ts = 0; + or_connect_failure_entry_t *ocf; + + tor_assert(or_conn); + + now = approx_time(); + cutoff = now - OR_CONNECT_FAILURE_LIFETIME; + + /* Opportunistically try to cleanup the failure cache. We do that at regular + * interval so it doesn't grow too big. */ + if (or_connect_failure_map_next_cleanup_ts <= now) { + or_connect_failure_map_cleanup(cutoff); + or_connect_failure_map_next_cleanup_ts = + now + OR_CONNECT_FAILURE_CLEANUP_INTERVAL; + } + + /* Look if we have failed previously to the same destination as this + * OR connection. */ + ocf = or_connect_failure_find(or_conn); + if (ocf) { + connect_failed_since_ts = ocf->last_failed_connect_ts; + } + /* If we do have an unable to connect timestamp and it is below cutoff, we + * can connect. Or we have never failed before so let it connect. */ + if (connect_failed_since_ts > cutoff) { + goto no_connect; + } + + /* Ok we can connect! */ + return 1; + no_connect: + return 0; +} + +/** <b>conn</b> is in the 'connecting' state, and it failed to complete + * a TCP connection. Send notifications appropriately. + * + * <b>reason</b> specifies the or_conn_end_reason for the failure; + * <b>msg</b> specifies the strerror-style error message. + */ +void +connection_or_connect_failed(or_connection_t *conn, + int reason, const char *msg) +{ + control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, reason); + if (!authdir_mode_tests_reachability(get_options())) + control_event_bootstrap_prob_or(msg, reason, conn); + note_or_connect_failed(conn); +} + +/** <b>conn</b> got an error in connection_handle_read_impl() or + * connection_handle_write_impl() and is going to die soon. + * + * <b>reason</b> specifies the or_conn_end_reason for the failure; + * <b>msg</b> specifies the strerror-style error message. + */ +void +connection_or_notify_error(or_connection_t *conn, + int reason, const char *msg) +{ + channel_t *chan; + + tor_assert(conn); + + /* If we're connecting, call connect_failed() too */ + if (TO_CONN(conn)->state == OR_CONN_STATE_CONNECTING) + connection_or_connect_failed(conn, reason, msg); + + /* Tell the controlling channel if we have one */ + if (conn->chan) { + chan = TLS_CHAN_TO_BASE(conn->chan); + /* Don't transition if we're already in closing, closed or error */ + if (!CHANNEL_CONDEMNED(chan)) { + channel_close_for_error(chan); + } + } + + /* No need to mark for error because connection.c is about to do that */ +} + +/** Launch a new OR connection to <b>addr</b>:<b>port</b> and expect to + * handshake with an OR with identity digest <b>id_digest</b>. Optionally, + * pass in a pointer to a channel using this connection. + * + * If <b>id_digest</b> is me, do nothing. If we're already connected to it, + * return that connection. If the connect() is in progress, set the + * new conn's state to 'connecting' and return it. If connect() succeeds, + * call connection_tls_start_handshake() on it. + * + * This function is called from router_retry_connections(), for + * ORs connecting to ORs, and circuit_establish_circuit(), for + * OPs connecting to ORs. + * + * Return the launched conn, or NULL if it failed. + */ + +MOCK_IMPL(or_connection_t *, +connection_or_connect, (const tor_addr_t *_addr, uint16_t port, + const char *id_digest, + const ed25519_public_key_t *ed_id, + channel_tls_t *chan)) +{ + or_connection_t *conn; + const or_options_t *options = get_options(); + int socket_error = 0; + tor_addr_t addr; + + int r; + tor_addr_t proxy_addr; + uint16_t proxy_port; + int proxy_type; + + tor_assert(_addr); + tor_assert(id_digest); + tor_addr_copy(&addr, _addr); + + if (server_mode(options) && router_digest_is_me(id_digest)) { + log_info(LD_PROTOCOL,"Client asked me to connect to myself. Refusing."); + return NULL; + } + if (server_mode(options) && router_ed25519_id_is_me(ed_id)) { + log_info(LD_PROTOCOL,"Client asked me to connect to myself by Ed25519 " + "identity. Refusing."); + return NULL; + } + + conn = or_connection_new(CONN_TYPE_OR, tor_addr_family(&addr)); + + /* + * Set up conn so it's got all the data we need to remember for channels + * + * This stuff needs to happen before connection_or_init_conn_from_address() + * so connection_or_set_identity_digest() and such know where to look to + * keep the channel up to date. + */ + conn->chan = chan; + chan->conn = conn; + connection_or_init_conn_from_address(conn, &addr, port, id_digest, ed_id, 1); + + /* We have a proper OR connection setup, now check if we can connect to it + * that is we haven't had a failure earlier. This is to avoid to try to + * constantly connect to relays that we think are not reachable. */ + if (!should_connect_to_relay(conn)) { + log_info(LD_GENERAL, "Can't connect to identity %s at %s:%u because we " + "failed earlier. Refusing.", + hex_str(id_digest, DIGEST_LEN), fmt_addr(&TO_CONN(conn)->addr), + TO_CONN(conn)->port); + connection_free_(TO_CONN(conn)); + return NULL; + } + + connection_or_change_state(conn, OR_CONN_STATE_CONNECTING); + control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0); + + conn->is_outgoing = 1; + + /* If we are using a proxy server, find it and use it. */ + r = get_proxy_addrport(&proxy_addr, &proxy_port, &proxy_type, TO_CONN(conn)); + if (r == 0) { + conn->proxy_type = proxy_type; + if (proxy_type != PROXY_NONE) { + tor_addr_copy(&addr, &proxy_addr); + port = proxy_port; + conn->base_.proxy_state = PROXY_INFANT; + } + } else { + /* get_proxy_addrport() might fail if we have a Bridge line that + references a transport, but no ClientTransportPlugin lines + defining its transport proxy. If this is the case, let's try to + output a useful log message to the user. */ + const char *transport_name = + find_transport_name_by_bridge_addrport(&TO_CONN(conn)->addr, + TO_CONN(conn)->port); + + if (transport_name) { + log_warn(LD_GENERAL, "We were supposed to connect to bridge '%s' " + "using pluggable transport '%s', but we can't find a pluggable " + "transport proxy supporting '%s'. This can happen if you " + "haven't provided a ClientTransportPlugin line, or if " + "your pluggable transport proxy stopped running.", + fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port), + transport_name, transport_name); + + control_event_bootstrap_prob_or( + "Can't connect to bridge", + END_OR_CONN_REASON_PT_MISSING, + conn); + + } else { + log_warn(LD_GENERAL, "Tried to connect to '%s' through a proxy, but " + "the proxy address could not be found.", + fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port)); + } + + connection_free_(TO_CONN(conn)); + return NULL; + } + + switch (connection_connect(TO_CONN(conn), conn->base_.address, + &addr, port, &socket_error)) { + case -1: + /* We failed to establish a connection probably because of a local + * error. No need to blame the guard in this case. Notify the networking + * system of this failure. */ + connection_or_connect_failed(conn, + errno_to_orconn_end_reason(socket_error), + tor_socket_strerror(socket_error)); + connection_free_(TO_CONN(conn)); + return NULL; + case 0: + connection_watch_events(TO_CONN(conn), READ_EVENT | WRITE_EVENT); + /* writable indicates finish, readable indicates broken link, + error indicates broken link on windows */ + return conn; + /* case 1: fall through */ + } + + if (connection_or_finished_connecting(conn) < 0) { + /* already marked for close */ + return NULL; + } + return conn; +} + +/** Mark orconn for close and transition the associated channel, if any, to + * the closing state. + * + * It's safe to call this and connection_or_close_for_error() any time, and + * channel layer will treat it as a connection closing for reasons outside + * its control, like the remote end closing it. It can also be a local + * reason that's specific to connection_t/or_connection_t rather than + * the channel mechanism, such as expiration of old connections in + * run_connection_housekeeping(). If you want to close a channel_t + * from somewhere that logically works in terms of generic channels + * rather than connections, use channel_mark_for_close(); see also + * the comment on that function in channel.c. + */ + +void +connection_or_close_normally(or_connection_t *orconn, int flush) +{ + channel_t *chan = NULL; + + tor_assert(orconn); + if (flush) connection_mark_and_flush_internal(TO_CONN(orconn)); + else connection_mark_for_close_internal(TO_CONN(orconn)); + if (orconn->chan) { + chan = TLS_CHAN_TO_BASE(orconn->chan); + /* Don't transition if we're already in closing, closed or error */ + if (!CHANNEL_CONDEMNED(chan)) { + channel_close_from_lower_layer(chan); + } + } +} + +/** Mark orconn for close and transition the associated channel, if any, to + * the error state. + */ + +MOCK_IMPL(void, +connection_or_close_for_error,(or_connection_t *orconn, int flush)) +{ + channel_t *chan = NULL; + + tor_assert(orconn); + if (flush) connection_mark_and_flush_internal(TO_CONN(orconn)); + else connection_mark_for_close_internal(TO_CONN(orconn)); + if (orconn->chan) { + chan = TLS_CHAN_TO_BASE(orconn->chan); + /* Don't transition if we're already in closing, closed or error */ + if (!CHANNEL_CONDEMNED(chan)) { + channel_close_for_error(chan); + } + } +} + +/** Begin the tls handshake with <b>conn</b>. <b>receiving</b> is 0 if + * we initiated the connection, else it's 1. + * + * Assign a new tls object to conn->tls, begin reading on <b>conn</b>, and + * pass <b>conn</b> to connection_tls_continue_handshake(). + * + * Return -1 if <b>conn</b> is broken, else return 0. + */ +MOCK_IMPL(int, +connection_tls_start_handshake,(or_connection_t *conn, int receiving)) +{ + channel_listener_t *chan_listener; + channel_t *chan; + + /* Incoming connections will need a new channel passed to the + * channel_tls_listener */ + if (receiving) { + /* It shouldn't already be set */ + tor_assert(!(conn->chan)); + chan_listener = channel_tls_get_listener(); + if (!chan_listener) { + chan_listener = channel_tls_start_listener(); + command_setup_listener(chan_listener); + } + chan = channel_tls_handle_incoming(conn); + channel_listener_queue_incoming(chan_listener, chan); + } + + connection_or_change_state(conn, OR_CONN_STATE_TLS_HANDSHAKING); + tor_assert(!conn->tls); + conn->tls = tor_tls_new(conn->base_.s, receiving); + if (!conn->tls) { + log_warn(LD_BUG,"tor_tls_new failed. Closing."); + return -1; + } + tor_tls_set_logged_address(conn->tls, // XXX client and relay? + escaped_safe_str(conn->base_.address)); + + connection_start_reading(TO_CONN(conn)); + log_debug(LD_HANDSHAKE,"starting TLS handshake on fd "TOR_SOCKET_T_FORMAT, + conn->base_.s); + + if (connection_tls_continue_handshake(conn) < 0) + return -1; + + return 0; +} + +/** Block all future attempts to renegotiate on 'conn' */ +void +connection_or_block_renegotiation(or_connection_t *conn) +{ + tor_tls_t *tls = conn->tls; + if (!tls) + return; + tor_tls_set_renegotiate_callback(tls, NULL, NULL); + tor_tls_block_renegotiation(tls); +} + +/** Invoked on the server side from inside tor_tls_read() when the server + * gets a successful TLS renegotiation from the client. */ +static void +connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn) +{ + or_connection_t *conn = _conn; + (void)tls; + + /* Don't invoke this again. */ + connection_or_block_renegotiation(conn); + + if (connection_tls_finish_handshake(conn) < 0) { + /* XXXX_TLS double-check that it's ok to do this from inside read. */ + /* XXXX_TLS double-check that this verifies certificates. */ + connection_or_close_for_error(conn, 0); + } +} + +/** Move forward with the tls handshake. If it finishes, hand + * <b>conn</b> to connection_tls_finish_handshake(). + * + * Return -1 if <b>conn</b> is broken, else return 0. + */ +int +connection_tls_continue_handshake(or_connection_t *conn) +{ + int result; + check_no_tls_errors(); + + tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING); + // log_notice(LD_OR, "Continue handshake with %p", conn->tls); + result = tor_tls_handshake(conn->tls); + // log_notice(LD_OR, "Result: %d", result); + + switch (result) { + CASE_TOR_TLS_ERROR_ANY: + log_info(LD_OR,"tls error [%s]. breaking connection.", + tor_tls_err_to_string(result)); + return -1; + case TOR_TLS_DONE: + if (! tor_tls_used_v1_handshake(conn->tls)) { + if (!tor_tls_is_server(conn->tls)) { + tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING); + return connection_or_launch_v3_or_handshake(conn); + } else { + /* v2/v3 handshake, but we are not a client. */ + log_debug(LD_OR, "Done with initial SSL handshake (server-side). " + "Expecting renegotiation or VERSIONS cell"); + tor_tls_set_renegotiate_callback(conn->tls, + connection_or_tls_renegotiated_cb, + conn); + connection_or_change_state(conn, + OR_CONN_STATE_TLS_SERVER_RENEGOTIATING); + connection_stop_writing(TO_CONN(conn)); + connection_start_reading(TO_CONN(conn)); + return 0; + } + } + tor_assert(tor_tls_is_server(conn->tls)); + return connection_tls_finish_handshake(conn); + case TOR_TLS_WANTWRITE: + connection_start_writing(TO_CONN(conn)); + log_debug(LD_OR,"wanted write"); + return 0; + case TOR_TLS_WANTREAD: /* handshaking conns are *always* reading */ + log_debug(LD_OR,"wanted read"); + return 0; + case TOR_TLS_CLOSE: + log_info(LD_OR,"tls closed. breaking connection."); + return -1; + } + return 0; +} + +/** Return 1 if we initiated this connection, or 0 if it started + * out as an incoming connection. + */ +int +connection_or_nonopen_was_started_here(or_connection_t *conn) +{ + tor_assert(conn->base_.type == CONN_TYPE_OR || + conn->base_.type == CONN_TYPE_EXT_OR); + if (!conn->tls) + return 1; /* it's still in proxy states or something */ + if (conn->handshake_state) + return conn->handshake_state->started_here; + return !tor_tls_is_server(conn->tls); +} + +/** <b>Conn</b> just completed its handshake. Return 0 if all is well, and + * return -1 if they are lying, broken, or otherwise something is wrong. + * + * If we initiated this connection (<b>started_here</b> is true), make sure + * the other side sent a correctly formed certificate. If I initiated the + * connection, make sure it's the right relay by checking the certificate. + * + * Otherwise (if we _didn't_ initiate this connection), it's okay for + * the certificate to be weird or absent. + * + * If we return 0, and the certificate is as expected, write a hash of the + * identity key into <b>digest_rcvd_out</b>, which must have DIGEST_LEN + * space in it. + * If the certificate is invalid or missing on an incoming connection, + * we return 0 and set <b>digest_rcvd_out</b> to DIGEST_LEN NUL bytes. + * (If we return -1, the contents of this buffer are undefined.) + * + * As side effects, + * 1) Set conn->circ_id_type according to tor-spec.txt. + * 2) If we're an authdirserver and we initiated the connection: drop all + * descriptors that claim to be on that IP/port but that aren't + * this relay; and note that this relay is reachable. + * 3) If this is a bridge and we didn't configure its identity + * fingerprint, remember the keyid we just learned. + */ +static int +connection_or_check_valid_tls_handshake(or_connection_t *conn, + int started_here, + char *digest_rcvd_out) +{ + crypto_pk_t *identity_rcvd=NULL; + const or_options_t *options = get_options(); + int severity = server_mode(options) ? LOG_PROTOCOL_WARN : LOG_WARN; + const char *safe_address = + started_here ? conn->base_.address : + safe_str_client(conn->base_.address); + const char *conn_type = started_here ? "outgoing" : "incoming"; + int has_cert = 0; + + check_no_tls_errors(); + has_cert = tor_tls_peer_has_cert(conn->tls); + if (started_here && !has_cert) { + log_info(LD_HANDSHAKE,"Tried connecting to router at %s:%d, but it didn't " + "send a cert! Closing.", + safe_address, conn->base_.port); + return -1; + } else if (!has_cert) { + log_debug(LD_HANDSHAKE,"Got incoming connection with no certificate. " + "That's ok."); + } + check_no_tls_errors(); + + if (has_cert) { + int v = tor_tls_verify(started_here?severity:LOG_INFO, + conn->tls, &identity_rcvd); + if (started_here && v<0) { + log_fn(severity,LD_HANDSHAKE,"Tried connecting to router at %s:%d: It" + " has a cert but it's invalid. Closing.", + safe_address, conn->base_.port); + return -1; + } else if (v<0) { + log_info(LD_HANDSHAKE,"Incoming connection gave us an invalid cert " + "chain; ignoring."); + } else { + log_debug(LD_HANDSHAKE, + "The certificate seems to be valid on %s connection " + "with %s:%d", conn_type, safe_address, conn->base_.port); + } + check_no_tls_errors(); + } + + if (identity_rcvd) { + if (crypto_pk_get_digest(identity_rcvd, digest_rcvd_out) < 0) { + crypto_pk_free(identity_rcvd); + return -1; + } + } else { + memset(digest_rcvd_out, 0, DIGEST_LEN); + } + + tor_assert(conn->chan); + channel_set_circid_type(TLS_CHAN_TO_BASE(conn->chan), identity_rcvd, 1); + + crypto_pk_free(identity_rcvd); + + if (started_here) { + /* A TLS handshake can't teach us an Ed25519 ID, so we set it to NULL + * here. */ + log_debug(LD_HANDSHAKE, "Calling client_learned_peer_id from " + "check_valid_tls_handshake"); + return connection_or_client_learned_peer_id(conn, + (const uint8_t*)digest_rcvd_out, + NULL); + } + + return 0; +} + +/** Called when we (as a connection initiator) have definitively, + * authenticatedly, learned that ID of the Tor instance on the other + * side of <b>conn</b> is <b>rsa_peer_id</b> and optionally <b>ed_peer_id</b>. + * For v1 and v2 handshakes, + * this is right after we get a certificate chain in a TLS handshake + * or renegotiation. For v3+ handshakes, this is right after we get a + * certificate chain in a CERTS cell. + * + * If we did not know the ID before, record the one we got. + * + * If we wanted an ID, but we didn't get the one we expected, log a message + * and return -1. + * On relays: + * - log a protocol warning whenever the fingerprints don't match; + * On clients: + * - if a relay's fingerprint doesn't match, log a warning; + * - if we don't have updated relay fingerprints from a recent consensus, and + * a fallback directory mirror's hard-coded fingerprint has changed, log an + * info explaining that we will try another fallback. + * + * If we're testing reachability, remember what we learned. + * + * Return 0 on success, -1 on failure. + */ +int +connection_or_client_learned_peer_id(or_connection_t *conn, + const uint8_t *rsa_peer_id, + const ed25519_public_key_t *ed_peer_id) +{ + const or_options_t *options = get_options(); + channel_tls_t *chan_tls = conn->chan; + channel_t *chan = channel_tls_to_base(chan_tls); + int changed_identity = 0; + tor_assert(chan); + + const int expected_rsa_key = + ! tor_digest_is_zero(conn->identity_digest); + const int expected_ed_key = + ! ed25519_public_key_is_zero(&chan->ed25519_identity); + + log_info(LD_HANDSHAKE, "learned peer id for %p (%s): %s, %s", + conn, + safe_str_client(conn->base_.address), + hex_str((const char*)rsa_peer_id, DIGEST_LEN), + ed25519_fmt(ed_peer_id)); + + if (! expected_rsa_key && ! expected_ed_key) { + log_info(LD_HANDSHAKE, "(we had no ID in mind when we made this " + "connection."); + connection_or_set_identity_digest(conn, + (const char*)rsa_peer_id, ed_peer_id); + tor_free(conn->nickname); + conn->nickname = tor_malloc(HEX_DIGEST_LEN+2); + conn->nickname[0] = '$'; + base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1, + conn->identity_digest, DIGEST_LEN); + log_info(LD_HANDSHAKE, "Connected to router %s at %s:%d without knowing " + "its key. Hoping for the best.", + conn->nickname, conn->base_.address, conn->base_.port); + /* if it's a bridge and we didn't know its identity fingerprint, now + * we do -- remember it for future attempts. */ + learned_router_identity(&conn->base_.addr, conn->base_.port, + (const char*)rsa_peer_id, ed_peer_id); + changed_identity = 1; + } + + const int rsa_mismatch = expected_rsa_key && + tor_memneq(rsa_peer_id, conn->identity_digest, DIGEST_LEN); + /* It only counts as an ed25519 mismatch if we wanted an ed25519 identity + * and didn't get it. It's okay if we get one that we didn't ask for. */ + const int ed25519_mismatch = + expected_ed_key && + (ed_peer_id == NULL || + ! ed25519_pubkey_eq(&chan->ed25519_identity, ed_peer_id)); + + if (rsa_mismatch || ed25519_mismatch) { + /* I was aiming for a particular digest. I didn't get it! */ + char seen_rsa[HEX_DIGEST_LEN+1]; + char expected_rsa[HEX_DIGEST_LEN+1]; + char seen_ed[ED25519_BASE64_LEN+1]; + char expected_ed[ED25519_BASE64_LEN+1]; + base16_encode(seen_rsa, sizeof(seen_rsa), + (const char*)rsa_peer_id, DIGEST_LEN); + base16_encode(expected_rsa, sizeof(expected_rsa), conn->identity_digest, + DIGEST_LEN); + if (ed_peer_id) { + ed25519_public_to_base64(seen_ed, ed_peer_id); + } else { + strlcpy(seen_ed, "no ed25519 key", sizeof(seen_ed)); + } + if (! ed25519_public_key_is_zero(&chan->ed25519_identity)) { + ed25519_public_to_base64(expected_ed, &chan->ed25519_identity); + } else { + strlcpy(expected_ed, "no ed25519 key", sizeof(expected_ed)); + } + const int using_hardcoded_fingerprints = + !networkstatus_get_reasonably_live_consensus(time(NULL), + usable_consensus_flavor()); + const int is_fallback_fingerprint = router_digest_is_fallback_dir( + conn->identity_digest); + const int is_authority_fingerprint = router_digest_is_trusted_dir( + conn->identity_digest); + const int non_anonymous_mode = rend_non_anonymous_mode_enabled(options); + int severity; + const char *extra_log = ""; + + /* Relays and Single Onion Services make direct connections using + * untrusted authentication keys. */ + if (server_mode(options) || non_anonymous_mode) { + severity = LOG_PROTOCOL_WARN; + } else { + if (using_hardcoded_fingerprints) { + /* We need to do the checks in this order, because the list of + * fallbacks includes the list of authorities */ + if (is_authority_fingerprint) { + severity = LOG_WARN; + } else if (is_fallback_fingerprint) { + /* we expect a small number of fallbacks to change from their + * hard-coded fingerprints over the life of a release */ + severity = LOG_INFO; + extra_log = " Tor will try a different fallback."; + } else { + /* it's a bridge, it's either a misconfiguration, or unexpected */ + severity = LOG_WARN; + } + } else { + /* a relay has changed its fingerprint from the one in the consensus */ + severity = LOG_WARN; + } + } + + log_fn(severity, LD_HANDSHAKE, + "Tried connecting to router at %s:%d, but RSA + ed25519 identity " + "keys were not as expected: wanted %s + %s but got %s + %s.%s", + conn->base_.address, conn->base_.port, + expected_rsa, expected_ed, seen_rsa, seen_ed, extra_log); + + /* Tell the new guard API about the channel failure */ + entry_guard_chan_failed(TLS_CHAN_TO_BASE(conn->chan)); + control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, + END_OR_CONN_REASON_OR_IDENTITY); + if (!authdir_mode_tests_reachability(options)) + control_event_bootstrap_prob_or( + "Unexpected identity in router certificate", + END_OR_CONN_REASON_OR_IDENTITY, + conn); + return -1; + } + + if (!expected_ed_key && ed_peer_id) { + log_info(LD_HANDSHAKE, "(We had no Ed25519 ID in mind when we made this " + "connection.)"); + connection_or_set_identity_digest(conn, + (const char*)rsa_peer_id, ed_peer_id); + changed_identity = 1; + } + + if (changed_identity) { + /* If we learned an identity for this connection, then we might have + * just discovered it to be canonical. */ + connection_or_check_canonicity(conn, conn->handshake_state->started_here); + } + + if (authdir_mode_tests_reachability(options)) { + dirserv_orconn_tls_done(&conn->base_.addr, conn->base_.port, + (const char*)rsa_peer_id, ed_peer_id); + } + + return 0; +} + +/** Return when we last used this channel for client activity (origin + * circuits). This is called from connection.c, since client_used is now one + * of the timestamps in channel_t */ + +time_t +connection_or_client_used(or_connection_t *conn) +{ + tor_assert(conn); + + if (conn->chan) { + return channel_when_last_client(TLS_CHAN_TO_BASE(conn->chan)); + } else return 0; +} + +/** The v1/v2 TLS handshake is finished. + * + * Make sure we are happy with the peer we just handshaked with. + * + * If they initiated the connection, make sure they're not already connected, + * then initialize conn from the information in router. + * + * If all is successful, call circuit_n_conn_done() to handle events + * that have been pending on the <tls handshake completion. Also set the + * directory to be dirty (only matters if I'm an authdirserver). + * + * If this is a v2 TLS handshake, send a versions cell. + */ +static int +connection_tls_finish_handshake(or_connection_t *conn) +{ + char digest_rcvd[DIGEST_LEN]; + int started_here = connection_or_nonopen_was_started_here(conn); + + tor_assert(!started_here); + + log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done, using " + "ciphersuite %s. verifying.", + started_here?"outgoing":"incoming", + conn, + safe_str_client(conn->base_.address), + tor_tls_get_ciphersuite_name(conn->tls)); + + if (connection_or_check_valid_tls_handshake(conn, started_here, + digest_rcvd) < 0) + return -1; + + circuit_build_times_network_is_live(get_circuit_build_times_mutable()); + + if (tor_tls_used_v1_handshake(conn->tls)) { + conn->link_proto = 1; + connection_or_init_conn_from_address(conn, &conn->base_.addr, + conn->base_.port, digest_rcvd, + NULL, 0); + tor_tls_block_renegotiation(conn->tls); + rep_hist_note_negotiated_link_proto(1, started_here); + return connection_or_set_state_open(conn); + } else { + connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V2); + if (connection_init_or_handshake_state(conn, started_here) < 0) + return -1; + connection_or_init_conn_from_address(conn, &conn->base_.addr, + conn->base_.port, digest_rcvd, + NULL, 0); + return connection_or_send_versions(conn, 0); + } +} + +/** + * Called as client when initial TLS handshake is done, and we notice + * that we got a v3-handshake signalling certificate from the server. + * Set up structures, do bookkeeping, and send the versions cell. + * Return 0 on success and -1 on failure. + */ +static int +connection_or_launch_v3_or_handshake(or_connection_t *conn) +{ + tor_assert(connection_or_nonopen_was_started_here(conn)); + + circuit_build_times_network_is_live(get_circuit_build_times_mutable()); + + connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V3); + if (connection_init_or_handshake_state(conn, 1) < 0) + return -1; + + return connection_or_send_versions(conn, 1); +} + +/** Allocate a new connection handshake state for the connection + * <b>conn</b>. Return 0 on success, -1 on failure. */ +int +connection_init_or_handshake_state(or_connection_t *conn, int started_here) +{ + or_handshake_state_t *s; + if (conn->handshake_state) { + log_warn(LD_BUG, "Duplicate call to connection_init_or_handshake_state!"); + return 0; + } + s = conn->handshake_state = tor_malloc_zero(sizeof(or_handshake_state_t)); + s->started_here = started_here ? 1 : 0; + s->digest_sent_data = 1; + s->digest_received_data = 1; + if (! started_here && get_current_link_cert_cert()) { + s->own_link_cert = tor_cert_dup(get_current_link_cert_cert()); + } + s->certs = or_handshake_certs_new(); + s->certs->started_here = s->started_here; + return 0; +} + +/** Free all storage held by <b>state</b>. */ +void +or_handshake_state_free_(or_handshake_state_t *state) +{ + if (!state) + return; + crypto_digest_free(state->digest_sent); + crypto_digest_free(state->digest_received); + or_handshake_certs_free(state->certs); + tor_cert_free(state->own_link_cert); + memwipe(state, 0xBE, sizeof(or_handshake_state_t)); + tor_free(state); +} + +/** + * Remember that <b>cell</b> has been transmitted (if <b>incoming</b> is + * false) or received (if <b>incoming</b> is true) during a V3 handshake using + * <b>state</b>. + * + * (We don't record the cell, but we keep a digest of everything sent or + * received during the v3 handshake, and the client signs it in an + * authenticate cell.) + */ +void +or_handshake_state_record_cell(or_connection_t *conn, + or_handshake_state_t *state, + const cell_t *cell, + int incoming) +{ + size_t cell_network_size = get_cell_network_size(conn->wide_circ_ids); + crypto_digest_t *d, **dptr; + packed_cell_t packed; + if (incoming) { + if (!state->digest_received_data) + return; + } else { + if (!state->digest_sent_data) + return; + } + if (!incoming) { + log_warn(LD_BUG, "We shouldn't be sending any non-variable-length cells " + "while making a handshake digest. But we think we are sending " + "one with type %d.", (int)cell->command); + } + dptr = incoming ? &state->digest_received : &state->digest_sent; + if (! *dptr) + *dptr = crypto_digest256_new(DIGEST_SHA256); + + d = *dptr; + /* Re-packing like this is a little inefficient, but we don't have to do + this very often at all. */ + cell_pack(&packed, cell, conn->wide_circ_ids); + crypto_digest_add_bytes(d, packed.body, cell_network_size); + memwipe(&packed, 0, sizeof(packed)); +} + +/** Remember that a variable-length <b>cell</b> has been transmitted (if + * <b>incoming</b> is false) or received (if <b>incoming</b> is true) during a + * V3 handshake using <b>state</b>. + * + * (We don't record the cell, but we keep a digest of everything sent or + * received during the v3 handshake, and the client signs it in an + * authenticate cell.) + */ +void +or_handshake_state_record_var_cell(or_connection_t *conn, + or_handshake_state_t *state, + const var_cell_t *cell, + int incoming) +{ + crypto_digest_t *d, **dptr; + int n; + char buf[VAR_CELL_MAX_HEADER_SIZE]; + if (incoming) { + if (!state->digest_received_data) + return; + } else { + if (!state->digest_sent_data) + return; + } + dptr = incoming ? &state->digest_received : &state->digest_sent; + if (! *dptr) + *dptr = crypto_digest256_new(DIGEST_SHA256); + + d = *dptr; + + n = var_cell_pack_header(cell, buf, conn->wide_circ_ids); + crypto_digest_add_bytes(d, buf, n); + crypto_digest_add_bytes(d, (const char *)cell->payload, cell->payload_len); + + memwipe(buf, 0, sizeof(buf)); +} + +/** Set <b>conn</b>'s state to OR_CONN_STATE_OPEN, and tell other subsystems + * as appropriate. Called when we are done with all TLS and OR handshaking. + */ +int +connection_or_set_state_open(or_connection_t *conn) +{ + connection_or_change_state(conn, OR_CONN_STATE_OPEN); + control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED, 0); + + /* Link protocol 3 appeared in Tor 0.2.3.6-alpha, so any connection + * that uses an earlier link protocol should not be treated as a relay. */ + if (conn->link_proto < 3) { + channel_mark_client(TLS_CHAN_TO_BASE(conn->chan)); + } + + or_handshake_state_free(conn->handshake_state); + conn->handshake_state = NULL; + connection_start_reading(TO_CONN(conn)); + + return 0; +} + +/** Pack <b>cell</b> into wire-format, and write it onto <b>conn</b>'s outbuf. + * For cells that use or affect a circuit, this should only be called by + * connection_or_flush_from_first_active_circuit(). + */ +void +connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn) +{ + packed_cell_t networkcell; + size_t cell_network_size = get_cell_network_size(conn->wide_circ_ids); + + tor_assert(cell); + tor_assert(conn); + + cell_pack(&networkcell, cell, conn->wide_circ_ids); + + rep_hist_padding_count_write(PADDING_TYPE_TOTAL); + if (cell->command == CELL_PADDING) + rep_hist_padding_count_write(PADDING_TYPE_CELL); + + connection_buf_add(networkcell.body, cell_network_size, TO_CONN(conn)); + + /* Touch the channel's active timestamp if there is one */ + if (conn->chan) { + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + + if (TLS_CHAN_TO_BASE(conn->chan)->currently_padding) { + rep_hist_padding_count_write(PADDING_TYPE_ENABLED_TOTAL); + if (cell->command == CELL_PADDING) + rep_hist_padding_count_write(PADDING_TYPE_ENABLED_CELL); + } + } + + if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) + or_handshake_state_record_cell(conn, conn->handshake_state, cell, 0); +} + +/** Pack a variable-length <b>cell</b> into wire-format, and write it onto + * <b>conn</b>'s outbuf. Right now, this <em>DOES NOT</em> support cells that + * affect a circuit. + */ +MOCK_IMPL(void, +connection_or_write_var_cell_to_buf,(const var_cell_t *cell, + or_connection_t *conn)) +{ + int n; + char hdr[VAR_CELL_MAX_HEADER_SIZE]; + tor_assert(cell); + tor_assert(conn); + n = var_cell_pack_header(cell, hdr, conn->wide_circ_ids); + connection_buf_add(hdr, n, TO_CONN(conn)); + connection_buf_add((char*)cell->payload, + cell->payload_len, TO_CONN(conn)); + if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) + or_handshake_state_record_var_cell(conn, conn->handshake_state, cell, 0); + + /* Touch the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); +} + +/** See whether there's a variable-length cell waiting on <b>or_conn</b>'s + * inbuf. Return values as for fetch_var_cell_from_buf(). */ +static int +connection_fetch_var_cell_from_buf(or_connection_t *or_conn, var_cell_t **out) +{ + connection_t *conn = TO_CONN(or_conn); + return fetch_var_cell_from_buf(conn->inbuf, out, or_conn->link_proto); +} + +/** Process cells from <b>conn</b>'s inbuf. + * + * Loop: while inbuf contains a cell, pull it off the inbuf, unpack it, + * and hand it to command_process_cell(). + * + * Always return 0. + */ +static int +connection_or_process_cells_from_inbuf(or_connection_t *conn) +{ + var_cell_t *var_cell; + + /* + * Note on memory management for incoming cells: below the channel layer, + * we shouldn't need to consider its internal queueing/copying logic. It + * is safe to pass cells to it on the stack or on the heap, but in the + * latter case we must be sure we free them later. + * + * The incoming cell queue code in channel.c will (in the common case) + * decide it can pass them to the upper layer immediately, in which case + * those functions may run directly on the cell pointers we pass here, or + * it may decide to queue them, in which case it will allocate its own + * buffer and copy the cell. + */ + + while (1) { + log_debug(LD_OR, + TOR_SOCKET_T_FORMAT": starting, inbuf_datalen %d " + "(%d pending in tls object).", + conn->base_.s,(int)connection_get_inbuf_len(TO_CONN(conn)), + tor_tls_get_pending_bytes(conn->tls)); + if (connection_fetch_var_cell_from_buf(conn, &var_cell)) { + if (!var_cell) + return 0; /* not yet. */ + + /* Touch the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + + circuit_build_times_network_is_live(get_circuit_build_times_mutable()); + channel_tls_handle_var_cell(var_cell, conn); + var_cell_free(var_cell); + } else { + const int wide_circ_ids = conn->wide_circ_ids; + size_t cell_network_size = get_cell_network_size(conn->wide_circ_ids); + char buf[CELL_MAX_NETWORK_SIZE]; + cell_t cell; + if (connection_get_inbuf_len(TO_CONN(conn)) + < cell_network_size) /* whole response available? */ + return 0; /* not yet */ + + /* Touch the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + + circuit_build_times_network_is_live(get_circuit_build_times_mutable()); + connection_buf_get_bytes(buf, cell_network_size, TO_CONN(conn)); + + /* retrieve cell info from buf (create the host-order struct from the + * network-order string) */ + cell_unpack(&cell, buf, wide_circ_ids); + + channel_tls_handle_cell(&cell, conn); + } + } +} + +/** Array of recognized link protocol versions. */ +static const uint16_t or_protocol_versions[] = { 1, 2, 3, 4, 5 }; +/** Number of versions in <b>or_protocol_versions</b>. */ +static const int n_or_protocol_versions = + (int)( sizeof(or_protocol_versions)/sizeof(uint16_t) ); + +/** Return true iff <b>v</b> is a link protocol version that this Tor + * implementation believes it can support. */ +int +is_or_protocol_version_known(uint16_t v) +{ + int i; + for (i = 0; i < n_or_protocol_versions; ++i) { + if (or_protocol_versions[i] == v) + return 1; + } + return 0; +} + +/** Send a VERSIONS cell on <b>conn</b>, telling the other host about the + * link protocol versions that this Tor can support. + * + * If <b>v3_plus</b>, this is part of a V3 protocol handshake, so only + * allow protocol version v3 or later. If not <b>v3_plus</b>, this is + * not part of a v3 protocol handshake, so don't allow protocol v3 or + * later. + **/ +int +connection_or_send_versions(or_connection_t *conn, int v3_plus) +{ + var_cell_t *cell; + int i; + int n_versions = 0; + const int min_version = v3_plus ? 3 : 0; + const int max_version = v3_plus ? UINT16_MAX : 2; + tor_assert(conn->handshake_state && + !conn->handshake_state->sent_versions_at); + cell = var_cell_new(n_or_protocol_versions * 2); + cell->command = CELL_VERSIONS; + for (i = 0; i < n_or_protocol_versions; ++i) { + uint16_t v = or_protocol_versions[i]; + if (v < min_version || v > max_version) + continue; + set_uint16(cell->payload+(2*n_versions), htons(v)); + ++n_versions; + } + cell->payload_len = n_versions * 2; + + connection_or_write_var_cell_to_buf(cell, conn); + conn->handshake_state->sent_versions_at = time(NULL); + + var_cell_free(cell); + return 0; +} + +/** Send a NETINFO cell on <b>conn</b>, telling the other server what we know + * about their address, our address, and the current time. */ +MOCK_IMPL(int, +connection_or_send_netinfo,(or_connection_t *conn)) +{ + cell_t cell; + time_t now = time(NULL); + const routerinfo_t *me; + int len; + uint8_t *out; + + tor_assert(conn->handshake_state); + + if (conn->handshake_state->sent_netinfo) { + log_warn(LD_BUG, "Attempted to send an extra netinfo cell on a connection " + "where we already sent one."); + return 0; + } + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_NETINFO; + + /* Timestamp, if we're a relay. */ + if (public_server_mode(get_options()) || ! conn->is_outgoing) + set_uint32(cell.payload, htonl((uint32_t)now)); + + /* Their address. */ + out = cell.payload + 4; + /* We use &conn->real_addr below, unless it hasn't yet been set. If it + * hasn't yet been set, we know that base_.addr hasn't been tampered with + * yet either. */ + len = append_address_to_payload(out, !tor_addr_is_null(&conn->real_addr) + ? &conn->real_addr : &conn->base_.addr); + if (len<0) + return -1; + out += len; + + /* My address -- only include it if I'm a public relay, or if I'm a + * bridge and this is an incoming connection. If I'm a bridge and this + * is an outgoing connection, act like a normal client and omit it. */ + if ((public_server_mode(get_options()) || !conn->is_outgoing) && + (me = router_get_my_routerinfo())) { + tor_addr_t my_addr; + *out++ = 1 + !tor_addr_is_null(&me->ipv6_addr); + + tor_addr_from_ipv4h(&my_addr, me->addr); + len = append_address_to_payload(out, &my_addr); + if (len < 0) + return -1; + out += len; + + if (!tor_addr_is_null(&me->ipv6_addr)) { + len = append_address_to_payload(out, &me->ipv6_addr); + if (len < 0) + return -1; + } + } else { + *out = 0; + } + + conn->handshake_state->digest_sent_data = 0; + conn->handshake_state->sent_netinfo = 1; + connection_or_write_cell_to_buf(&cell, conn); + + return 0; +} + +/** Helper used to add an encoded certs to a cert cell */ +static void +add_certs_cell_cert_helper(certs_cell_t *certs_cell, + uint8_t cert_type, + const uint8_t *cert_encoded, + size_t cert_len) +{ + tor_assert(cert_len <= UINT16_MAX); + certs_cell_cert_t *ccc = certs_cell_cert_new(); + ccc->cert_type = cert_type; + ccc->cert_len = cert_len; + certs_cell_cert_setlen_body(ccc, cert_len); + memcpy(certs_cell_cert_getarray_body(ccc), cert_encoded, cert_len); + + certs_cell_add_certs(certs_cell, ccc); +} + +/** Add an encoded X509 cert (stored as <b>cert_len</b> bytes at + * <b>cert_encoded</b>) to the trunnel certs_cell_t object that we are + * building in <b>certs_cell</b>. Set its type field to <b>cert_type</b>. + * (If <b>cert</b> is NULL, take no action.) */ +static void +add_x509_cert(certs_cell_t *certs_cell, + uint8_t cert_type, + const tor_x509_cert_t *cert) +{ + if (NULL == cert) + return; + + const uint8_t *cert_encoded = NULL; + size_t cert_len; + tor_x509_cert_get_der(cert, &cert_encoded, &cert_len); + + add_certs_cell_cert_helper(certs_cell, cert_type, cert_encoded, cert_len); +} + +/** Add an Ed25519 cert from <b>cert</b> to the trunnel certs_cell_t object + * that we are building in <b>certs_cell</b>. Set its type field to + * <b>cert_type</b>. (If <b>cert</b> is NULL, take no action.) */ +static void +add_ed25519_cert(certs_cell_t *certs_cell, + uint8_t cert_type, + const tor_cert_t *cert) +{ + if (NULL == cert) + return; + + add_certs_cell_cert_helper(certs_cell, cert_type, + cert->encoded, cert->encoded_len); +} + +#ifdef TOR_UNIT_TESTS +int certs_cell_ed25519_disabled_for_testing = 0; +#else +#define certs_cell_ed25519_disabled_for_testing 0 +#endif + +/** Send a CERTS cell on the connection <b>conn</b>. Return 0 on success, -1 + * on failure. */ +int +connection_or_send_certs_cell(or_connection_t *conn) +{ + const tor_x509_cert_t *global_link_cert = NULL, *id_cert = NULL; + tor_x509_cert_t *own_link_cert = NULL; + var_cell_t *cell; + + certs_cell_t *certs_cell = NULL; + + tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); + + if (! conn->handshake_state) + return -1; + + const int conn_in_server_mode = ! conn->handshake_state->started_here; + + /* Get the encoded values of the X509 certificates */ + if (tor_tls_get_my_certs(conn_in_server_mode, + &global_link_cert, &id_cert) < 0) + return -1; + + if (conn_in_server_mode) { + own_link_cert = tor_tls_get_own_cert(conn->tls); + } + tor_assert(id_cert); + + certs_cell = certs_cell_new(); + + /* Start adding certs. First the link cert or auth1024 cert. */ + if (conn_in_server_mode) { + tor_assert_nonfatal(own_link_cert); + add_x509_cert(certs_cell, + OR_CERT_TYPE_TLS_LINK, own_link_cert); + } else { + tor_assert(global_link_cert); + add_x509_cert(certs_cell, + OR_CERT_TYPE_AUTH_1024, global_link_cert); + } + + /* Next the RSA->RSA ID cert */ + add_x509_cert(certs_cell, + OR_CERT_TYPE_ID_1024, id_cert); + + /* Next the Ed25519 certs */ + add_ed25519_cert(certs_cell, + CERTTYPE_ED_ID_SIGN, + get_master_signing_key_cert()); + if (conn_in_server_mode) { + tor_assert_nonfatal(conn->handshake_state->own_link_cert || + certs_cell_ed25519_disabled_for_testing); + add_ed25519_cert(certs_cell, + CERTTYPE_ED_SIGN_LINK, + conn->handshake_state->own_link_cert); + } else { + add_ed25519_cert(certs_cell, + CERTTYPE_ED_SIGN_AUTH, + get_current_auth_key_cert()); + } + + /* And finally the crosscert. */ + { + const uint8_t *crosscert=NULL; + size_t crosscert_len; + get_master_rsa_crosscert(&crosscert, &crosscert_len); + if (crosscert) { + add_certs_cell_cert_helper(certs_cell, + CERTTYPE_RSA1024_ID_EDID, + crosscert, crosscert_len); + } + } + + /* We've added all the certs; make the cell. */ + certs_cell->n_certs = certs_cell_getlen_certs(certs_cell); + + ssize_t alloc_len = certs_cell_encoded_len(certs_cell); + tor_assert(alloc_len >= 0 && alloc_len <= UINT16_MAX); + cell = var_cell_new(alloc_len); + cell->command = CELL_CERTS; + ssize_t enc_len = certs_cell_encode(cell->payload, alloc_len, certs_cell); + tor_assert(enc_len > 0 && enc_len <= alloc_len); + cell->payload_len = enc_len; + + connection_or_write_var_cell_to_buf(cell, conn); + var_cell_free(cell); + certs_cell_free(certs_cell); + tor_x509_cert_free(own_link_cert); + + return 0; +} + +#ifdef TOR_UNIT_TESTS +int testing__connection_or_pretend_TLSSECRET_is_supported = 0; +#else +#define testing__connection_or_pretend_TLSSECRET_is_supported 0 +#endif + +/** Return true iff <b>challenge_type</b> is an AUTHCHALLENGE type that + * we can send and receive. */ +int +authchallenge_type_is_supported(uint16_t challenge_type) +{ + switch (challenge_type) { + case AUTHTYPE_RSA_SHA256_TLSSECRET: +#ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS + return 1; +#else + return testing__connection_or_pretend_TLSSECRET_is_supported; +#endif + case AUTHTYPE_ED25519_SHA256_RFC5705: + return 1; + case AUTHTYPE_RSA_SHA256_RFC5705: + default: + return 0; + } +} + +/** Return true iff <b>challenge_type_a</b> is one that we would rather + * use than <b>challenge_type_b</b>. */ +int +authchallenge_type_is_better(uint16_t challenge_type_a, + uint16_t challenge_type_b) +{ + /* Any supported type is better than an unsupported one; + * all unsupported types are equally bad. */ + if (!authchallenge_type_is_supported(challenge_type_a)) + return 0; + if (!authchallenge_type_is_supported(challenge_type_b)) + return 1; + /* It happens that types are superior in numerically ascending order. + * If that ever changes, this must change too. */ + return (challenge_type_a > challenge_type_b); +} + +/** Send an AUTH_CHALLENGE cell on the connection <b>conn</b>. Return 0 + * on success, -1 on failure. */ +int +connection_or_send_auth_challenge_cell(or_connection_t *conn) +{ + var_cell_t *cell = NULL; + int r = -1; + tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); + + if (! conn->handshake_state) + return -1; + + auth_challenge_cell_t *ac = auth_challenge_cell_new(); + + tor_assert(sizeof(ac->challenge) == 32); + crypto_rand((char*)ac->challenge, sizeof(ac->challenge)); + + if (authchallenge_type_is_supported(AUTHTYPE_RSA_SHA256_TLSSECRET)) + auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET); + /* Disabled, because everything that supports this method also supports + * the much-superior ED25519_SHA256_RFC5705 */ + /* auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_RFC5705); */ + if (authchallenge_type_is_supported(AUTHTYPE_ED25519_SHA256_RFC5705)) + auth_challenge_cell_add_methods(ac, AUTHTYPE_ED25519_SHA256_RFC5705); + auth_challenge_cell_set_n_methods(ac, + auth_challenge_cell_getlen_methods(ac)); + + cell = var_cell_new(auth_challenge_cell_encoded_len(ac)); + ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len, + ac); + if (len != cell->payload_len) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Encoded auth challenge cell length not as expected"); + goto done; + /* LCOV_EXCL_STOP */ + } + cell->command = CELL_AUTH_CHALLENGE; + + connection_or_write_var_cell_to_buf(cell, conn); + r = 0; + + done: + var_cell_free(cell); + auth_challenge_cell_free(ac); + + return r; +} + +/** Compute the main body of an AUTHENTICATE cell that a client can use + * to authenticate itself on a v3 handshake for <b>conn</b>. Return it + * in a var_cell_t. + * + * If <b>server</b> is true, only calculate the first + * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's + * determined by the rest of the handshake, and which match the provided value + * exactly. + * + * If <b>server</b> is false and <b>signing_key</b> is NULL, calculate the + * first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything + * that should be signed), but don't actually sign it. + * + * If <b>server</b> is false and <b>signing_key</b> is provided, calculate the + * entire authenticator, signed with <b>signing_key</b>. + * + * Return the length of the cell body on success, and -1 on failure. + */ +var_cell_t * +connection_or_compute_authenticate_cell_body(or_connection_t *conn, + const int authtype, + crypto_pk_t *signing_key, + const ed25519_keypair_t *ed_signing_key, + int server) +{ + auth1_t *auth = NULL; + auth_ctx_t *ctx = auth_ctx_new(); + var_cell_t *result = NULL; + int old_tlssecrets_algorithm = 0; + const char *authtype_str = NULL; + + int is_ed = 0; + + /* assert state is reasonable XXXX */ + switch (authtype) { + case AUTHTYPE_RSA_SHA256_TLSSECRET: + authtype_str = "AUTH0001"; + old_tlssecrets_algorithm = 1; + break; + case AUTHTYPE_RSA_SHA256_RFC5705: + authtype_str = "AUTH0002"; + break; + case AUTHTYPE_ED25519_SHA256_RFC5705: + authtype_str = "AUTH0003"; + is_ed = 1; + break; + default: + tor_assert(0); + break; + } + + auth = auth1_new(); + ctx->is_ed = is_ed; + + /* Type: 8 bytes. */ + memcpy(auth1_getarray_type(auth), authtype_str, 8); + + { + const tor_x509_cert_t *id_cert=NULL; + const common_digests_t *my_digests, *their_digests; + const uint8_t *my_id, *their_id, *client_id, *server_id; + if (tor_tls_get_my_certs(server, NULL, &id_cert)) + goto err; + my_digests = tor_x509_cert_get_id_digests(id_cert); + their_digests = + tor_x509_cert_get_id_digests(conn->handshake_state->certs->id_cert); + tor_assert(my_digests); + tor_assert(their_digests); + my_id = (uint8_t*)my_digests->d[DIGEST_SHA256]; + their_id = (uint8_t*)their_digests->d[DIGEST_SHA256]; + + client_id = server ? their_id : my_id; + server_id = server ? my_id : their_id; + + /* Client ID digest: 32 octets. */ + memcpy(auth->cid, client_id, 32); + + /* Server ID digest: 32 octets. */ + memcpy(auth->sid, server_id, 32); + } + + if (is_ed) { + const ed25519_public_key_t *my_ed_id, *their_ed_id; + if (!conn->handshake_state->certs->ed_id_sign) { + log_warn(LD_OR, "Ed authenticate without Ed ID cert from peer."); + goto err; + } + my_ed_id = get_master_identity_key(); + their_ed_id = &conn->handshake_state->certs->ed_id_sign->signing_key; + + const uint8_t *cid_ed = (server ? their_ed_id : my_ed_id)->pubkey; + const uint8_t *sid_ed = (server ? my_ed_id : their_ed_id)->pubkey; + + memcpy(auth->u1_cid_ed, cid_ed, ED25519_PUBKEY_LEN); + memcpy(auth->u1_sid_ed, sid_ed, ED25519_PUBKEY_LEN); + } + + { + crypto_digest_t *server_d, *client_d; + if (server) { + server_d = conn->handshake_state->digest_sent; + client_d = conn->handshake_state->digest_received; + } else { + client_d = conn->handshake_state->digest_sent; + server_d = conn->handshake_state->digest_received; + } + + /* Server log digest : 32 octets */ + crypto_digest_get_digest(server_d, (char*)auth->slog, 32); + + /* Client log digest : 32 octets */ + crypto_digest_get_digest(client_d, (char*)auth->clog, 32); + } + + { + /* Digest of cert used on TLS link : 32 octets. */ + tor_x509_cert_t *cert = NULL; + if (server) { + cert = tor_tls_get_own_cert(conn->tls); + } else { + cert = tor_tls_get_peer_cert(conn->tls); + } + if (!cert) { + log_warn(LD_OR, "Unable to find cert when making %s data.", + authtype_str); + goto err; + } + + memcpy(auth->scert, + tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); + + tor_x509_cert_free(cert); + } + + /* HMAC of clientrandom and serverrandom using master key : 32 octets */ + if (old_tlssecrets_algorithm) { + if (tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets) < 0) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, "Somebody asked us for an older TLS " + "authentication method (AUTHTYPE_RSA_SHA256_TLSSECRET) " + "which we don't support."); + } + } else { + char label[128]; + tor_snprintf(label, sizeof(label), + "EXPORTER FOR TOR TLS CLIENT BINDING %s", authtype_str); - tor_tls_export_key_material(conn->tls, auth->tlssecrets, - auth->cid, sizeof(auth->cid), - label); ++ int r = tor_tls_export_key_material(conn->tls, auth->tlssecrets, ++ auth->cid, sizeof(auth->cid), ++ label); ++ if (r < 0) { ++ if (r != -2) ++ log_warn(LD_BUG, "TLS key export failed for unknown reason."); ++ // If r == -2, this was openssl bug 7712. ++ goto err; ++ } + } + + /* 8 octets were reserved for the current time, but we're trying to get out + * of the habit of sending time around willynilly. Fortunately, nothing + * checks it. That's followed by 16 bytes of nonce. */ + crypto_rand((char*)auth->rand, 24); + + ssize_t maxlen = auth1_encoded_len(auth, ctx); + if (ed_signing_key && is_ed) { + maxlen += ED25519_SIG_LEN; + } else if (signing_key && !is_ed) { + maxlen += crypto_pk_keysize(signing_key); + } + + const int AUTH_CELL_HEADER_LEN = 4; /* 2 bytes of type, 2 bytes of length */ + result = var_cell_new(AUTH_CELL_HEADER_LEN + maxlen); + uint8_t *const out = result->payload + AUTH_CELL_HEADER_LEN; + const size_t outlen = maxlen; + ssize_t len; + + result->command = CELL_AUTHENTICATE; + set_uint16(result->payload, htons(authtype)); + + if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to encode signed part of AUTH1 data."); + goto err; + /* LCOV_EXCL_STOP */ + } + + if (server) { + auth1_t *tmp = NULL; + ssize_t len2 = auth1_parse(&tmp, out, len, ctx); + if (!tmp) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to parse signed part of AUTH1 data that " + "we just encoded"); + goto err; + /* LCOV_EXCL_STOP */ + } + result->payload_len = (tmp->end_of_signed - result->payload); + + auth1_free(tmp); + if (len2 != len) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Mismatched length when re-parsing AUTH1 data."); + goto err; + /* LCOV_EXCL_STOP */ + } + goto done; + } + + if (ed_signing_key && is_ed) { + ed25519_signature_t sig; + if (ed25519_sign(&sig, out, len, ed_signing_key) < 0) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to sign ed25519 authentication data"); + goto err; + /* LCOV_EXCL_STOP */ + } + auth1_setlen_sig(auth, ED25519_SIG_LEN); + memcpy(auth1_getarray_sig(auth), sig.sig, ED25519_SIG_LEN); + + } else if (signing_key && !is_ed) { + auth1_setlen_sig(auth, crypto_pk_keysize(signing_key)); + + char d[32]; + crypto_digest256(d, (char*)out, len, DIGEST_SHA256); + int siglen = crypto_pk_private_sign(signing_key, + (char*)auth1_getarray_sig(auth), + auth1_getlen_sig(auth), + d, 32); + if (siglen < 0) { + log_warn(LD_OR, "Unable to sign AUTH1 data."); + goto err; + } + + auth1_setlen_sig(auth, siglen); + } + + len = auth1_encode(out, outlen, auth, ctx); + if (len < 0) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to encode signed AUTH1 data."); + goto err; + /* LCOV_EXCL_STOP */ + } + tor_assert(len + AUTH_CELL_HEADER_LEN <= result->payload_len); + result->payload_len = len + AUTH_CELL_HEADER_LEN; + set_uint16(result->payload+2, htons(len)); + + goto done; + + err: + var_cell_free(result); + result = NULL; + done: + auth1_free(auth); + auth_ctx_free(ctx); + return result; +} + +/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on + * success, -1 on failure */ +MOCK_IMPL(int, +connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype)) +{ + var_cell_t *cell; + crypto_pk_t *pk = tor_tls_get_my_client_auth_key(); + /* XXXX make sure we're actually supposed to send this! */ + + if (!pk) { + log_warn(LD_BUG, "Can't compute authenticate cell: no client auth key"); + return -1; + } + if (! authchallenge_type_is_supported(authtype)) { + log_warn(LD_BUG, "Tried to send authenticate cell with unknown " + "authentication type %d", authtype); + return -1; + } + + cell = connection_or_compute_authenticate_cell_body(conn, + authtype, + pk, + get_current_auth_keypair(), + 0 /* not server */); + if (! cell) { - /* LCOV_EXCL_START */ - log_warn(LD_BUG, "Unable to compute authenticate cell!"); ++ log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unable to compute authenticate cell!"); + return -1; - /* LCOV_EXCL_STOP */ + } + connection_or_write_var_cell_to_buf(cell, conn); + var_cell_free(cell); + + return 0; +} diff --cc src/lib/tls/tortls_openssl.c index 63f6259a6,000000000..99371cfc4 mode 100644,000000..100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@@ -1,1751 -1,0 +1,1795 @@@ +/* Copyright (c) 2003, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file tortls.c + * \brief Wrapper functions to present a consistent interface to + * TLS, SSL, and X.509 functions from OpenSSL. + **/ + +/* (Unlike other tor functions, these + * are prefixed with tor_ in order to avoid conflicting with OpenSSL + * functions and variables.) + */ + +#include "orconfig.h" + +#define TORTLS_PRIVATE +#define TORTLS_OPENSSL_PRIVATE +#define TOR_X509_PRIVATE + +#ifdef _WIN32 + /* We need to include these here, or else the dtls1.h header will include + * <winsock.h> and mess things up, in at least some openssl versions. */ + #include <winsock2.h> + #include <ws2tcpip.h> +#endif + +#include "lib/crypt_ops/crypto_cipher.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_dh.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/compat_openssl.h" +#include "lib/tls/x509.h" +#include "lib/tls/x509_internal.h" + +/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in + * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ +DISABLE_GCC_WARNING(redundant-decls) + +#include <openssl/opensslv.h> + +#ifdef OPENSSL_NO_EC +#error "We require OpenSSL with ECC support" +#endif + +#include <openssl/ssl.h> +#include <openssl/ssl3.h> +#include <openssl/err.h> +#include <openssl/tls1.h> +#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/rsa.h> + +ENABLE_GCC_WARNING(redundant-decls) + +#include "lib/tls/tortls.h" +#include "lib/tls/tortls_st.h" +#include "lib/tls/tortls_internal.h" +#include "lib/log/log.h" +#include "lib/log/util_bug.h" +#include "lib/container/smartlist.h" +#include "lib/string/compat_string.h" +#include "lib/string/printf.h" +#include "lib/net/socket.h" +#include "lib/intmath/cmp.h" +#include "lib/ctime/di_ops.h" +#include "lib/encoding/time_fmt.h" + +#include <stdlib.h> +#include <string.h> + +#include "lib/arch/bytes.h" + +/* Copied from or.h */ +#define LEGAL_NICKNAME_CHARACTERS \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +#define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer") + +#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') +/* This is a version of OpenSSL before 1.0.0f. It does not have + * the CVE-2011-4576 fix, and as such it can't use RELEASE_BUFFERS and + * SSL3 safely at the same time. + */ +#define DISABLE_SSL3_HANDSHAKE +#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') */ + +/* We redefine these so that we can run correctly even if the vendor gives us + * a version of OpenSSL that does not match its header files. (Apple: I am + * looking at you.) + */ +#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION +#define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x00040000L +#endif +#ifndef SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION +#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010 +#endif + ++/** Set to true iff openssl bug 7712 has been detected. */ ++static int openssl_bug_7712_is_present = 0; ++ +/** Return values for tor_tls_classify_client_ciphers. + * + * @{ + */ +/** An error occurred when examining the client ciphers */ +#define CIPHERS_ERR -1 +/** The client cipher list indicates that a v1 handshake was in use. */ +#define CIPHERS_V1 1 +/** The client cipher list indicates that the client is using the v2 or the + * v3 handshake, but that it is (probably!) lying about what ciphers it + * supports */ +#define CIPHERS_V2 2 +/** The client cipher list indicates that the client is using the v2 or the + * v3 handshake, and that it is telling the truth about what ciphers it + * supports */ +#define CIPHERS_UNRESTRICTED 3 +/** @} */ + +/** The ex_data index in which we store a pointer to an SSL object's + * corresponding tor_tls_t object. */ +STATIC int tor_tls_object_ex_data_index = -1; + +/** Helper: Allocate tor_tls_object_ex_data_index. */ +void +tor_tls_allocate_tor_tls_object_ex_data_index(void) +{ + if (tor_tls_object_ex_data_index == -1) { + tor_tls_object_ex_data_index = + SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + tor_assert(tor_tls_object_ex_data_index != -1); + } +} + +/** Helper: given a SSL* pointer, return the tor_tls_t object using that + * pointer. */ +tor_tls_t * +tor_tls_get_by_ssl(const SSL *ssl) +{ + tor_tls_t *result = SSL_get_ex_data(ssl, tor_tls_object_ex_data_index); + if (result) + tor_assert(result->magic == TOR_TLS_MAGIC); + return result; +} + +/** True iff tor_tls_init() has been called. */ +static int tls_library_is_initialized = 0; + +/* Module-internal error codes. */ +#define TOR_TLS_SYSCALL_ (MIN_TOR_TLS_ERROR_VAL_ - 2) +#define TOR_TLS_ZERORETURN_ (MIN_TOR_TLS_ERROR_VAL_ - 1) + +/** Write a description of the current state of <b>tls</b> into the + * <b>sz</b>-byte buffer at <b>buf</b>. */ +void +tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz) +{ + const char *ssl_state; + const char *tortls_state; + + if (PREDICT_UNLIKELY(!tls || !tls->ssl)) { + strlcpy(buf, "(No SSL object)", sz); + return; + } + + ssl_state = SSL_state_string_long(tls->ssl); + switch (tls->state) { +#define CASE(st) case TOR_TLS_ST_##st: tortls_state = " in "#st ; break + CASE(HANDSHAKE); + CASE(OPEN); + CASE(GOTCLOSE); + CASE(SENTCLOSE); + CASE(CLOSED); + CASE(RENEGOTIATE); +#undef CASE + case TOR_TLS_ST_BUFFEREVENT: + tortls_state = ""; + break; + default: + tortls_state = " in unknown TLS state"; + break; + } + + tor_snprintf(buf, sz, "%s%s", ssl_state, tortls_state); +} + +/** Log a single error <b>err</b> as returned by ERR_get_error(), which was + * received while performing an operation <b>doing</b> on <b>tls</b>. Log + * the message at <b>severity</b>, in log domain <b>domain</b>. */ +void +tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, + int severity, int domain, const char *doing) +{ + const char *state = NULL, *addr; + const char *msg, *lib, *func; + + state = (tls && tls->ssl)?SSL_state_string_long(tls->ssl):"---"; + + addr = tls ? tls->address : NULL; + + /* Some errors are known-benign, meaning they are the fault of the other + * side of the connection. The caller doesn't know this, so override the + * priority for those cases. */ + switch (ERR_GET_REASON(err)) { + case SSL_R_HTTP_REQUEST: + case SSL_R_HTTPS_PROXY_REQUEST: + case SSL_R_RECORD_LENGTH_MISMATCH: +#ifndef OPENSSL_1_1_API + case SSL_R_RECORD_TOO_LARGE: +#endif + case SSL_R_UNKNOWN_PROTOCOL: + case SSL_R_UNSUPPORTED_PROTOCOL: + severity = LOG_INFO; + break; + default: + break; + } + + msg = (const char*)ERR_reason_error_string(err); + lib = (const char*)ERR_lib_error_string(err); + func = (const char*)ERR_func_error_string(err); + if (!msg) msg = "(null)"; + if (!lib) lib = "(null)"; + if (!func) func = "(null)"; + if (doing) { + tor_log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)", + doing, addr?" with ":"", addr?addr:"", + msg, lib, func, state); + } else { + tor_log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)", + addr?" with ":"", addr?addr:"", + msg, lib, func, state); + } +} + +/** Log all pending tls errors at level <b>severity</b> in log domain + * <b>domain</b>. Use <b>doing</b> to describe our current activities. + */ +void +tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) +{ + unsigned long err; + + while ((err = ERR_get_error()) != 0) { + tor_tls_log_one_error(tls, err, severity, domain, doing); + } +} + +#define CATCH_SYSCALL 1 +#define CATCH_ZERO 2 + +/** Given a TLS object and the result of an SSL_* call, use + * SSL_get_error to determine whether an error has occurred, and if so + * which one. Return one of TOR_TLS_{DONE|WANTREAD|WANTWRITE|ERROR}. + * If extra&CATCH_SYSCALL is true, return TOR_TLS_SYSCALL_ instead of + * reporting syscall errors. If extra&CATCH_ZERO is true, return + * TOR_TLS_ZERORETURN_ instead of reporting zero-return errors. + * + * If an error has occurred, log it at level <b>severity</b> and describe the + * current action as <b>doing</b>. + */ +int +tor_tls_get_error(tor_tls_t *tls, int r, int extra, + const char *doing, int severity, int domain) +{ + int err = SSL_get_error(tls->ssl, r); + int tor_error = TOR_TLS_ERROR_MISC; + switch (err) { + case SSL_ERROR_NONE: + return TOR_TLS_DONE; + case SSL_ERROR_WANT_READ: + return TOR_TLS_WANTREAD; + case SSL_ERROR_WANT_WRITE: + return TOR_TLS_WANTWRITE; + case SSL_ERROR_SYSCALL: + if (extra&CATCH_SYSCALL) + return TOR_TLS_SYSCALL_; + if (r == 0) { + tor_log(severity, LD_NET, "TLS error: unexpected close while %s (%s)", + doing, SSL_state_string_long(tls->ssl)); + tor_error = TOR_TLS_ERROR_IO; + } else { + int e = tor_socket_errno(tls->socket); + tor_log(severity, LD_NET, + "TLS error: <syscall error while %s> (errno=%d: %s; state=%s)", + doing, e, tor_socket_strerror(e), + SSL_state_string_long(tls->ssl)); + tor_error = tor_errno_to_tls_error(e); + } + tls_log_errors(tls, severity, domain, doing); + return tor_error; + case SSL_ERROR_ZERO_RETURN: + if (extra&CATCH_ZERO) + return TOR_TLS_ZERORETURN_; + tor_log(severity, LD_NET, "TLS connection closed while %s in state %s", + doing, SSL_state_string_long(tls->ssl)); + tls_log_errors(tls, severity, domain, doing); + return TOR_TLS_CLOSE; + default: + tls_log_errors(tls, severity, domain, doing); + return TOR_TLS_ERROR_MISC; + } +} + +/** Initialize OpenSSL, unless it has already been initialized. + */ +void +tor_tls_init(void) +{ + check_no_tls_errors(); + + if (!tls_library_is_initialized) { +#ifdef OPENSSL_1_1_API + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); +#else + SSL_library_init(); + SSL_load_error_strings(); +#endif + +#if (SIZEOF_VOID_P >= 8 && \ + OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1)) + long version = OpenSSL_version_num(); + + /* LCOV_EXCL_START : we can't test these lines on the same machine */ + if (version >= OPENSSL_V_SERIES(1,0,1)) { + /* Warn if we could *almost* be running with much faster ECDH. + If we're built for a 64-bit target, using OpenSSL 1.0.1, but we + don't have one of the built-in __uint128-based speedups, we are + just one build operation away from an accelerated handshake. + + (We could be looking at OPENSSL_NO_EC_NISTP_64_GCC_128 instead of + doing this test, but that gives compile-time options, not runtime + behavior.) + */ + EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + const EC_GROUP *g = key ? EC_KEY_get0_group(key) : NULL; + const EC_METHOD *m = g ? EC_GROUP_method_of(g) : NULL; + const int warn = (m == EC_GFp_simple_method() || + m == EC_GFp_mont_method() || + m == EC_GFp_nist_method()); + EC_KEY_free(key); + + if (warn) + log_notice(LD_GENERAL, "We were built to run on a 64-bit CPU, with " + "OpenSSL 1.0.1 or later, but with a version of OpenSSL " + "that apparently lacks accelerated support for the NIST " + "P-224 and P-256 groups. Building openssl with such " + "support (using the enable-ec_nistp_64_gcc_128 option " + "when configuring it) would make ECDH much faster."); + } + /* LCOV_EXCL_STOP */ +#endif /* (SIZEOF_VOID_P >= 8 && ... */ + + tor_tls_allocate_tor_tls_object_ex_data_index(); + + tls_library_is_initialized = 1; + } +} + +/** We need to give OpenSSL a callback to verify certificates. This is + * it: We always accept peer certs and complete the handshake. We + * don't validate them until later. + */ +int +always_accept_verify_cb(int preverify_ok, + X509_STORE_CTX *x509_ctx) +{ + (void) preverify_ok; + (void) x509_ctx; + return 1; +} + +/** List of ciphers that servers should select from when the client might be + * claiming extra unsupported ciphers in order to avoid fingerprinting. */ +static const char SERVER_CIPHER_LIST[] = +#ifdef TLS1_3_TXT_AES_128_GCM_SHA256 + /* This one can never actually get selected, since if the client lists it, + * we will assume that the client is honest, and not use this list. + * Nonetheless we list it if it's available, so that the server doesn't + * conclude that it has no valid ciphers if it's running with TLS1.3. + */ + TLS1_3_TXT_AES_128_GCM_SHA256 ":" +#endif + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA; + +/** List of ciphers that servers should select from when we actually have + * our choice of what cipher to use. */ +static const char UNRESTRICTED_SERVER_CIPHER_LIST[] = + /* Here are the TLS 1.3 ciphers we like, in the order we prefer. */ +#ifdef TLS1_3_TXT_AES_256_GCM_SHA384 + TLS1_3_TXT_AES_256_GCM_SHA384 ":" +#endif +#ifdef TLS1_3_TXT_CHACHA20_POLY1305_SHA256 + TLS1_3_TXT_CHACHA20_POLY1305_SHA256 ":" +#endif +#ifdef TLS1_3_TXT_AES_128_GCM_SHA256 + TLS1_3_TXT_AES_128_GCM_SHA256 ":" +#endif +#ifdef TLS1_3_TXT_AES_128_CCM_SHA256 + TLS1_3_TXT_AES_128_CCM_SHA256 ":" +#endif + + /* This list is autogenerated with the gen_server_ciphers.py script; + * don't hand-edit it. */ +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 + TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 + TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA + TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA + TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 + TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 + TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_CCM + TLS1_TXT_DHE_RSA_WITH_AES_256_CCM ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_CCM + TLS1_TXT_DHE_RSA_WITH_AES_128_CCM ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 ":" +#endif + /* Required */ + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" + /* Required */ + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" +#ifdef TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 + TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305 + TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305 +#endif + ; + +/* Note: to set up your own private testing network with link crypto + * disabled, set your Tors' cipher list to + * (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate + * with any of the "real" Tors, though. */ + +#define CIPHER(id, name) name ":" +#define XCIPHER(id, name) +/** List of ciphers that clients should advertise, omitting items that + * our OpenSSL doesn't know about. */ +static const char CLIENT_CIPHER_LIST[] = +#include "ciphers.inc" + /* Tell it not to use SSLv2 ciphers, so that it can select an SSLv3 version + * of any cipher we say. */ + "!SSLv2" + ; +#undef CIPHER +#undef XCIPHER + +/** Return true iff the other side of <b>tls</b> has authenticated to us, and + * the key certified in <b>cert</b> is the same as the key they used to do it. + */ +MOCK_IMPL(int, +tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert)) +{ + tor_x509_cert_t *peer = tor_tls_get_peer_cert((tor_tls_t *)tls); + if (!peer) + return 0; + + X509 *peercert = peer->cert; + EVP_PKEY *link_key = NULL, *cert_key = NULL; + int result; + + link_key = X509_get_pubkey(peercert); + cert_key = X509_get_pubkey(cert->cert); + + result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1; + + tor_x509_cert_free(peer); + if (link_key) + EVP_PKEY_free(link_key); + if (cert_key) + EVP_PKEY_free(cert_key); + + return result; +} + +void +tor_tls_context_impl_free_(struct ssl_ctx_st *ctx) +{ + if (!ctx) + return; + SSL_CTX_free(ctx); +} + +/** The group we should use for ecdhe when none was selected. */ +#define NID_tor_default_ecdhe_group NID_X9_62_prime256v1 + +/** Create a new TLS context for use with Tor TLS handshakes. + * <b>identity</b> should be set to the identity key used to sign the + * certificate. + */ +tor_tls_context_t * +tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, + unsigned flags, int is_client) +{ + EVP_PKEY *pkey = NULL; + tor_tls_context_t *result = NULL; + + tor_tls_init(); + + result = tor_malloc_zero(sizeof(tor_tls_context_t)); + result->refcnt = 1; + + if (! is_client) { + if (tor_tls_context_init_certificates(result, identity, key_lifetime, + flags) < 0) { + goto error; + } + } + +#if 0 + /* Tell OpenSSL to only use TLS1. This may have subtly different results + * from SSLv23_method() with SSLv2 and SSLv3 disabled, so we need to do some + * investigation before we consider adjusting it. It should be compatible + * with existing Tors. */ + if (!(result->ctx = SSL_CTX_new(TLSv1_method()))) + goto error; +#endif /* 0 */ + + /* Tell OpenSSL to use TLS 1.0 or later but not SSL2 or SSL3. */ +#ifdef HAVE_TLS_METHOD + if (!(result->ctx = SSL_CTX_new(TLS_method()))) + goto error; +#else + if (!(result->ctx = SSL_CTX_new(SSLv23_method()))) + goto error; +#endif /* defined(HAVE_TLS_METHOD) */ + +#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL + /* Level 1 re-enables RSA1024 and DH1024 for compatibility with old tors */ + SSL_CTX_set_security_level(result->ctx, 1); +#endif + + SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3); + + /* Prefer the server's ordering of ciphers: the client's ordering has + * historically been chosen for fingerprinting resistance. */ + SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + + /* Disable TLS tickets if they're supported. We never want to use them; + * using them can make our perfect forward secrecy a little worse, *and* + * create an opportunity to fingerprint us (since it's unusual to use them + * with TLS sessions turned off). + * + * In 0.2.4, clients advertise support for them though, to avoid a TLS + * distinguishability vector. This can give us worse PFS, though, if we + * get a server that doesn't set SSL_OP_NO_TICKET. With luck, there will + * be few such servers by the time 0.2.4 is more stable. + */ +#ifdef SSL_OP_NO_TICKET + if (! is_client) { + SSL_CTX_set_options(result->ctx, SSL_OP_NO_TICKET); + } +#endif + + SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE); + +#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + SSL_CTX_set_options(result->ctx, + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); +#endif + /* Yes, we know what we are doing here. No, we do not treat a renegotiation + * as authenticating any earlier-received data. + */ + { + SSL_CTX_set_options(result->ctx, + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); + } + + /* Don't actually allow compression; it uses RAM and time, it makes TLS + * vulnerable to CRIME-style attacks, and most of the data we transmit over + * TLS is encrypted (and therefore uncompressible) anyway. */ +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(result->ctx, SSL_OP_NO_COMPRESSION); +#endif +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) +#ifndef OPENSSL_NO_COMP + if (result->ctx->comp_methods) + result->ctx->comp_methods = NULL; +#endif +#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) */ + +#ifdef SSL_MODE_RELEASE_BUFFERS + SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS); +#endif + if (! is_client) { + if (result->my_link_cert && + !SSL_CTX_use_certificate(result->ctx, + result->my_link_cert->cert)) { + goto error; + } + if (result->my_id_cert) { + X509_STORE *s = SSL_CTX_get_cert_store(result->ctx); + tor_assert(s); + X509_STORE_add_cert(s, result->my_id_cert->cert); + } + } + SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); + if (!is_client) { + tor_assert(result->link_key); + if (!(pkey = crypto_pk_get_openssl_evp_pkey_(result->link_key,1))) + goto error; + if (!SSL_CTX_use_PrivateKey(result->ctx, pkey)) + goto error; + EVP_PKEY_free(pkey); + pkey = NULL; + if (!SSL_CTX_check_private_key(result->ctx)) + goto error; + } + + { + DH *dh = crypto_dh_new_openssl_tls(); + tor_assert(dh); + SSL_CTX_set_tmp_dh(result->ctx, dh); + DH_free(dh); + } +/* We check for this function in two ways, since it might be either a symbol + * or a macro. */ +#if defined(SSL_CTX_set1_groups_list) || defined(HAVE_SSL_CTX_SET1_GROUPS_LIST) + { + const char *list; + if (flags & TOR_TLS_CTX_USE_ECDHE_P224) + list = "P-224:P-256"; + else if (flags & TOR_TLS_CTX_USE_ECDHE_P256) + list = "P-256:P-224"; + else + list = "P-256:P-224"; + int r = (int) SSL_CTX_set1_groups_list(result->ctx, list); + if (r < 0) + goto error; + } +#else + if (! is_client) { + int nid; + EC_KEY *ec_key; + if (flags & TOR_TLS_CTX_USE_ECDHE_P224) + nid = NID_secp224r1; + else if (flags & TOR_TLS_CTX_USE_ECDHE_P256) + nid = NID_X9_62_prime256v1; + else + nid = NID_tor_default_ecdhe_group; + /* Use P-256 for ECDHE. */ + ec_key = EC_KEY_new_by_curve_name(nid); + if (ec_key != NULL) /*XXXX Handle errors? */ + SSL_CTX_set_tmp_ecdh(result->ctx, ec_key); + EC_KEY_free(ec_key); + } +#endif + SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER, + always_accept_verify_cb); + /* let us realloc bufs that we're writing from */ + SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + return result; + + error: + tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context"); + if (pkey) + EVP_PKEY_free(pkey); + tor_tls_context_decref(result); + return NULL; +} + +/** Invoked when a TLS state changes: log the change at severity 'debug' */ +void +tor_tls_debug_state_callback(const SSL *ssl, int type, int val) +{ + /* LCOV_EXCL_START since this depends on whether debug is captured or not */ + log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", + ssl, SSL_state_string_long(ssl), type, val); + /* LCOV_EXCL_STOP */ +} + +/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */ +const char * +tor_tls_get_ciphersuite_name(tor_tls_t *tls) +{ + return SSL_get_cipher(tls->ssl); +} + +/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to + * 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers + * that it claims to support. We'll prune this list to remove the ciphers + * *we* don't recognize. */ +STATIC uint16_t v2_cipher_list[] = { + 0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */ + 0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */ + 0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */ + 0x0038, /* TLS1_TXT_DHE_DSS_WITH_AES_256_SHA */ + 0xc00f, /* TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA */ + 0xc005, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA */ + 0x0035, /* TLS1_TXT_RSA_WITH_AES_256_SHA */ + 0xc007, /* TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA */ + 0xc009, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA */ + 0xc011, /* TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA */ + 0xc013, /* TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA */ + 0x0033, /* TLS1_TXT_DHE_RSA_WITH_AES_128_SHA */ + 0x0032, /* TLS1_TXT_DHE_DSS_WITH_AES_128_SHA */ + 0xc00c, /* TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA */ + 0xc00e, /* TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA */ + 0xc002, /* TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA */ + 0xc004, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA */ + 0x0004, /* SSL3_TXT_RSA_RC4_128_MD5 */ + 0x0005, /* SSL3_TXT_RSA_RC4_128_SHA */ + 0x002f, /* TLS1_TXT_RSA_WITH_AES_128_SHA */ + 0xc008, /* TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA */ + 0xc012, /* TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA */ + 0x0016, /* SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA */ + 0x0013, /* SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA */ + 0xc00d, /* TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA */ + 0xc003, /* TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA */ + 0xfeff, /* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA */ + 0x000a, /* SSL3_TXT_RSA_DES_192_CBC3_SHA */ + 0 +}; +/** Have we removed the unrecognized ciphers from v2_cipher_list yet? */ +static int v2_cipher_list_pruned = 0; + +/** Return 0 if <b>m</b> does not support the cipher with ID <b>cipher</b>; + * return 1 if it does support it, or if we have no way to tell. */ +int +find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher) +{ + const SSL_CIPHER *c; +#ifdef HAVE_SSL_CIPHER_FIND + (void) m; + { + unsigned char cipherid[3]; + tor_assert(ssl); + set_uint16(cipherid, tor_htons(cipher)); + cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting + * with a two-byte 'cipherid', it may look for a v2 + * cipher with the appropriate 3 bytes. */ + c = SSL_CIPHER_find((SSL*)ssl, cipherid); + if (c) + tor_assert((SSL_CIPHER_get_id(c) & 0xffff) == cipher); + return c != NULL; + } +#else /* !(defined(HAVE_SSL_CIPHER_FIND)) */ + +# if defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) + if (m && m->get_cipher_by_char) { + unsigned char cipherid[3]; + set_uint16(cipherid, tor_htons(cipher)); + cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting + * with a two-byte 'cipherid', it may look for a v2 + * cipher with the appropriate 3 bytes. */ + c = m->get_cipher_by_char(cipherid); + if (c) + tor_assert((c->id & 0xffff) == cipher); + return c != NULL; + } +#endif /* defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) */ +# ifndef OPENSSL_1_1_API + if (m && m->get_cipher && m->num_ciphers) { + /* It would seem that some of the "let's-clean-up-openssl" forks have + * removed the get_cipher_by_char function. Okay, so now you get a + * quadratic search. + */ + int i; + for (i = 0; i < m->num_ciphers(); ++i) { + c = m->get_cipher(i); + if (c && (c->id & 0xffff) == cipher) { + return 1; + } + } + return 0; + } +#endif /* !defined(OPENSSL_1_1_API) */ + (void) ssl; + (void) m; + (void) cipher; + return 1; /* No way to search */ +#endif /* defined(HAVE_SSL_CIPHER_FIND) */ +} + +/** Remove from v2_cipher_list every cipher that we don't support, so that + * comparing v2_cipher_list to a client's cipher list will give a sensible + * result. */ +static void +prune_v2_cipher_list(const SSL *ssl) +{ + uint16_t *inp, *outp; +#ifdef HAVE_TLS_METHOD + const SSL_METHOD *m = TLS_method(); +#else + const SSL_METHOD *m = SSLv23_method(); +#endif + + inp = outp = v2_cipher_list; + while (*inp) { + if (find_cipher_by_id(ssl, m, *inp)) { + *outp++ = *inp++; + } else { + inp++; + } + } + *outp = 0; + + v2_cipher_list_pruned = 1; +} + +/** Examine the client cipher list in <b>ssl</b>, and determine what kind of + * client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2, + * CIPHERS_UNRESTRICTED. + **/ +int +tor_tls_classify_client_ciphers(const SSL *ssl, + STACK_OF(SSL_CIPHER) *peer_ciphers) +{ + int i, res; + tor_tls_t *tor_tls; + if (PREDICT_UNLIKELY(!v2_cipher_list_pruned)) + prune_v2_cipher_list(ssl); + + tor_tls = tor_tls_get_by_ssl(ssl); + if (tor_tls && tor_tls->client_cipher_list_type) + return tor_tls->client_cipher_list_type; + + /* If we reached this point, we just got a client hello. See if there is + * a cipher list. */ + if (!peer_ciphers) { + log_info(LD_NET, "No ciphers on session"); + res = CIPHERS_ERR; + goto done; + } + /* Now we need to see if there are any ciphers whose presence means we're + * dealing with an updated Tor. */ + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); + const char *ciphername = SSL_CIPHER_get_name(cipher); + if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) && + strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) && + strcmp(ciphername, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) && + strcmp(ciphername, "(NONE)")) { + log_debug(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername); + // return 1; + goto v2_or_higher; + } + } + res = CIPHERS_V1; + goto done; + v2_or_higher: + { + const uint16_t *v2_cipher = v2_cipher_list; + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); + uint16_t id = SSL_CIPHER_get_id(cipher) & 0xffff; + if (id == 0x00ff) /* extended renegotiation indicator. */ + continue; + if (!id || id != *v2_cipher) { + res = CIPHERS_UNRESTRICTED; + goto dump_ciphers; + } + ++v2_cipher; + } + if (*v2_cipher != 0) { + res = CIPHERS_UNRESTRICTED; + goto dump_ciphers; + } + res = CIPHERS_V2; + } + + dump_ciphers: + { + smartlist_t *elts = smartlist_new(); + char *s; + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); + const char *ciphername = SSL_CIPHER_get_name(cipher); + smartlist_add(elts, (char*)ciphername); + } + s = smartlist_join_strings(elts, ":", 0, NULL); + log_debug(LD_NET, "Got a %s V2/V3 cipher list from %s. It is: '%s'", + (res == CIPHERS_V2) ? "fictitious" : "real", ADDR(tor_tls), s); + tor_free(s); + smartlist_free(elts); + } + done: + if (tor_tls) + return tor_tls->client_cipher_list_type = res; + + return res; +} + +/** Return true iff the cipher list suggested by the client for <b>ssl</b> is + * a list that indicates that the client knows how to do the v2 TLS connection + * handshake. */ +int +tor_tls_client_is_using_v2_ciphers(const SSL *ssl) +{ + STACK_OF(SSL_CIPHER) *ciphers; +#ifdef HAVE_SSL_GET_CLIENT_CIPHERS + ciphers = SSL_get_client_ciphers(ssl); +#else + SSL_SESSION *session; + if (!(session = SSL_get_session((SSL *)ssl))) { + log_info(LD_NET, "No session on TLS?"); + return CIPHERS_ERR; + } + ciphers = session->ciphers; +#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */ + + return tor_tls_classify_client_ciphers(ssl, ciphers) >= CIPHERS_V2; +} + +/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection + * changes state. We use this: + * <ul><li>To alter the state of the handshake partway through, so we + * do not send or request extra certificates in v2 handshakes.</li> + * <li>To detect renegotiation</li></ul> + */ +void +tor_tls_server_info_callback(const SSL *ssl, int type, int val) +{ + tor_tls_t *tls; + (void) val; + + IF_BUG_ONCE(ssl == NULL) { + return; // LCOV_EXCL_LINE + } + + tor_tls_debug_state_callback(ssl, type, val); + + if (type != SSL_CB_ACCEPT_LOOP) + return; + + OSSL_HANDSHAKE_STATE ssl_state = SSL_get_state(ssl); + if (! STATE_IS_SW_SERVER_HELLO(ssl_state)) + return; + tls = tor_tls_get_by_ssl(ssl); + if (tls) { + /* Check whether we're watching for renegotiates. If so, this is one! */ + if (tls->negotiated_callback) + tls->got_renegotiate = 1; + } else { + log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); + return; + } + + /* Now check the cipher list. */ + if (tor_tls_client_is_using_v2_ciphers(ssl)) { + if (tls->wasV2Handshake) + return; /* We already turned this stuff off for the first handshake; + * This is a renegotiation. */ + + /* Yes, we're casting away the const from ssl. This is very naughty of us. + * Let's hope openssl doesn't notice! */ + + /* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */ + SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN); + /* Don't send a hello request. */ + SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL); + + if (tls) { + tls->wasV2Handshake = 1; + } else { + /* LCOV_EXCL_START this line is not reachable */ + log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); + /* LCOV_EXCL_STOP */ + } + } +} + +/** Callback to get invoked on a server after we've read the list of ciphers + * the client supports, but before we pick our own ciphersuite. + * + * We can't abuse an info_cb for this, since by the time one of the + * client_hello info_cbs is called, we've already picked which ciphersuite to + * use. + * + * Technically, this function is an abuse of this callback, since the point of + * a session_secret_cb is to try to set up and/or verify a shared-secret for + * authentication on the fly. But as long as we return 0, we won't actually be + * setting up a shared secret, and all will be fine. + */ +int +tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher, + void *arg) +{ + (void) secret; + (void) secret_len; + (void) peer_ciphers; + (void) cipher; + (void) arg; + + if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) == + CIPHERS_UNRESTRICTED) { + SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST); + } + + SSL_set_session_secret_cb(ssl, NULL, NULL); + + return 0; +} +static void +tor_tls_setup_session_secret_cb(tor_tls_t *tls) +{ + SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL); +} + +/** Create a new TLS object from a file descriptor, and a flag to + * determine whether it is functioning as a server. + */ +tor_tls_t * +tor_tls_new(tor_socket_t sock, int isServer) +{ + BIO *bio = NULL; + tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t)); + tor_tls_context_t *context = tor_tls_context_get(isServer); + result->magic = TOR_TLS_MAGIC; + + check_no_tls_errors(); + tor_assert(context); /* make sure somebody made it first */ + if (!(result->ssl = SSL_new(context->ctx))) { + tls_log_errors(NULL, LOG_WARN, LD_NET, "creating SSL object"); + tor_free(result); + goto err; + } + +#ifdef SSL_set_tlsext_host_name + /* Browsers use the TLS hostname extension, so we should too. */ + if (!isServer) { + char *fake_hostname = crypto_random_hostname(4,25, "www.",".com"); + SSL_set_tlsext_host_name(result->ssl, fake_hostname); + tor_free(fake_hostname); + } +#endif /* defined(SSL_set_tlsext_host_name) */ + ++#ifdef SSL_CTRL_SET_MAX_PROTO_VERSION ++ if (openssl_bug_7712_is_present) { ++ /* We can't actually use TLS 1.3 until this bug is fixed. */ ++ SSL_set_max_proto_version(result->ssl, TLS1_2_VERSION); ++ } ++#endif ++ + if (!SSL_set_cipher_list(result->ssl, + isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) { + tls_log_errors(NULL, LOG_WARN, LD_NET, "setting ciphers"); +#ifdef SSL_set_tlsext_host_name + SSL_set_tlsext_host_name(result->ssl, NULL); +#endif + SSL_free(result->ssl); + tor_free(result); + goto err; + } + result->socket = sock; + bio = BIO_new_socket(sock, BIO_CLOSE); + if (! bio) { + tls_log_errors(NULL, LOG_WARN, LD_NET, "opening BIO"); +#ifdef SSL_set_tlsext_host_name + SSL_set_tlsext_host_name(result->ssl, NULL); +#endif + SSL_free(result->ssl); + tor_free(result); + goto err; + } + { + int set_worked = + SSL_set_ex_data(result->ssl, tor_tls_object_ex_data_index, result); + if (!set_worked) { + log_warn(LD_BUG, + "Couldn't set the tls for an SSL*; connection will fail"); + } + } + SSL_set_bio(result->ssl, bio, bio); + tor_tls_context_incref(context); + result->context = context; + result->state = TOR_TLS_ST_HANDSHAKE; + result->isServer = isServer; + result->wantwrite_n = 0; + result->last_write_count = (unsigned long) BIO_number_written(bio); + result->last_read_count = (unsigned long) BIO_number_read(bio); + if (result->last_write_count || result->last_read_count) { + log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu", + result->last_read_count, result->last_write_count); + } + if (isServer) { + SSL_set_info_callback(result->ssl, tor_tls_server_info_callback); + } else { + SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback); + } + + if (isServer) + tor_tls_setup_session_secret_cb(result); + + goto done; + err: + result = NULL; + done: + /* Not expected to get called. */ + tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object"); + return result; +} + +/** Set <b>cb</b> to be called with argument <b>arg</b> whenever <b>tls</b> + * next gets a client-side renegotiate in the middle of a read. Do not + * invoke this function until <em>after</em> initial handshaking is done! + */ +void +tor_tls_set_renegotiate_callback(tor_tls_t *tls, + void (*cb)(tor_tls_t *, void *arg), + void *arg) +{ + tls->negotiated_callback = cb; + tls->callback_arg = arg; + tls->got_renegotiate = 0; + if (cb) { + SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback); + } else { + SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback); + } +} + +/** If this version of openssl requires it, turn on renegotiation on + * <b>tls</b>. + */ +void +tor_tls_unblock_renegotiation(tor_tls_t *tls) +{ + /* Yes, we know what we are doing here. No, we do not treat a renegotiation + * as authenticating any earlier-received data. */ + SSL_set_options(tls->ssl, + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); +} + +/** If this version of openssl supports it, turn off renegotiation on + * <b>tls</b>. (Our protocol never requires this for security, but it's nice + * to use belt-and-suspenders here.) + */ +void +tor_tls_block_renegotiation(tor_tls_t *tls) +{ +#ifdef SUPPORT_UNSAFE_RENEGOTIATION_FLAG + tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; +#else + (void) tls; +#endif +} + +/** Assert that the flags that allow legacy renegotiation are still set */ +void +tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) +{ +#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && \ + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION != 0 + long options = SSL_get_options(tls->ssl); + tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); +#else + (void) tls; +#endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */ +} + +/** + * Tell the TLS library that the underlying socket for <b>tls</b> has been + * closed, and the library should not attempt to free that socket itself. + */ +void +tor_tls_release_socket(tor_tls_t *tls) +{ + if (! tls) + return; + + BIO *rbio, *wbio; + rbio = SSL_get_rbio(tls->ssl); + wbio = SSL_get_wbio(tls->ssl); + + if (rbio) { + (void) BIO_set_close(rbio, BIO_NOCLOSE); + } + if (wbio && wbio != rbio) { + (void) BIO_set_close(wbio, BIO_NOCLOSE); + } +} + +void +tor_tls_impl_free_(tor_tls_impl_t *ssl) +{ + if (!ssl) + return; + +#ifdef SSL_set_tlsext_host_name + SSL_set_tlsext_host_name(ssl, NULL); +#endif + SSL_free(ssl); +} + +/** Underlying function for TLS reading. Reads up to <b>len</b> + * characters from <b>tls</b> into <b>cp</b>. On success, returns the + * number of characters read. On failure, returns TOR_TLS_ERROR, + * TOR_TLS_CLOSE, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE. + */ +MOCK_IMPL(int, +tor_tls_read,(tor_tls_t *tls, char *cp, size_t len)) +{ + int r, err; + tor_assert(tls); + tor_assert(tls->ssl); + tor_assert(tls->state == TOR_TLS_ST_OPEN); + tor_assert(len<INT_MAX); + r = SSL_read(tls->ssl, cp, (int)len); + if (r > 0) { + if (tls->got_renegotiate) { + /* Renegotiation happened! */ + log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls)); + if (tls->negotiated_callback) + tls->negotiated_callback(tls, tls->callback_arg); + tls->got_renegotiate = 0; + } + return r; + } + err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET); + if (err == TOR_TLS_ZERORETURN_ || err == TOR_TLS_CLOSE) { + log_debug(LD_NET,"read returned r=%d; TLS is closed",r); + tls->state = TOR_TLS_ST_CLOSED; + return TOR_TLS_CLOSE; + } else { + tor_assert(err != TOR_TLS_DONE); + log_debug(LD_NET,"read returned r=%d, err=%d",r,err); + return err; + } +} + +/** Total number of bytes that we've used TLS to send. Used to track TLS + * overhead. */ +STATIC uint64_t total_bytes_written_over_tls = 0; +/** Total number of bytes that TLS has put on the network for us. Used to + * track TLS overhead. */ +STATIC uint64_t total_bytes_written_by_tls = 0; + +/** Underlying function for TLS writing. Write up to <b>n</b> + * characters from <b>cp</b> onto <b>tls</b>. On success, returns the + * number of characters written. On failure, returns TOR_TLS_ERROR, + * TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE. + */ +int +tor_tls_write(tor_tls_t *tls, const char *cp, size_t n) +{ + int r, err; + tor_assert(tls); + tor_assert(tls->ssl); + tor_assert(tls->state == TOR_TLS_ST_OPEN); + tor_assert(n < INT_MAX); + if (n == 0) + return 0; + if (tls->wantwrite_n) { + /* if WANTWRITE last time, we must use the _same_ n as before */ + tor_assert(n >= tls->wantwrite_n); + log_debug(LD_NET,"resuming pending-write, (%d to flush, reusing %d)", + (int)n, (int)tls->wantwrite_n); + n = tls->wantwrite_n; + tls->wantwrite_n = 0; + } + r = SSL_write(tls->ssl, cp, (int)n); + err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO, LD_NET); + if (err == TOR_TLS_DONE) { + total_bytes_written_over_tls += r; + return r; + } + if (err == TOR_TLS_WANTWRITE || err == TOR_TLS_WANTREAD) { + tls->wantwrite_n = n; + } + return err; +} + +/** Perform initial handshake on <b>tls</b>. When finished, returns + * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, + * or TOR_TLS_WANTWRITE. + */ +int +tor_tls_handshake(tor_tls_t *tls) +{ + int r; + tor_assert(tls); + tor_assert(tls->ssl); + tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE); + + check_no_tls_errors(); + + OSSL_HANDSHAKE_STATE oldstate = SSL_get_state(tls->ssl); + + if (tls->isServer) { + log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls, + SSL_state_string_long(tls->ssl)); + r = SSL_accept(tls->ssl); + } else { + log_debug(LD_HANDSHAKE, "About to call SSL_connect on %p (%s)", tls, + SSL_state_string_long(tls->ssl)); + r = SSL_connect(tls->ssl); + } + + OSSL_HANDSHAKE_STATE newstate = SSL_get_state(tls->ssl); + + if (oldstate != newstate) + log_debug(LD_HANDSHAKE, "After call, %p was in state %s", + tls, SSL_state_string_long(tls->ssl)); + /* We need to call this here and not earlier, since OpenSSL has a penchant + * for clearing its flags when you say accept or connect. */ + tor_tls_unblock_renegotiation(tls); + r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO, LD_HANDSHAKE); + if (ERR_peek_error() != 0) { + tls_log_errors(tls, tls->isServer ? LOG_INFO : LOG_WARN, LD_HANDSHAKE, + "handshaking"); + return TOR_TLS_ERROR_MISC; + } + if (r == TOR_TLS_DONE) { + tls->state = TOR_TLS_ST_OPEN; + return tor_tls_finish_handshake(tls); + } + return r; +} + +/** Perform the final part of the initial TLS handshake on <b>tls</b>. This + * should be called for the first handshake only: it determines whether the v1 + * or the v2 handshake was used, and adjusts things for the renegotiation + * handshake as appropriate. + * + * tor_tls_handshake() calls this on its own; you only need to call this if + * bufferevent is doing the handshake for you. + */ +int +tor_tls_finish_handshake(tor_tls_t *tls) +{ + int r = TOR_TLS_DONE; + check_no_tls_errors(); + if (tls->isServer) { + SSL_set_info_callback(tls->ssl, NULL); + SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb); + SSL_clear_mode(tls->ssl, SSL_MODE_NO_AUTO_CHAIN); + if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) { + /* This check is redundant, but back when we did it in the callback, + * we might have not been able to look up the tor_tls_t if the code + * was buggy. Fixing that. */ + if (!tls->wasV2Handshake) { + log_warn(LD_BUG, "For some reason, wasV2Handshake didn't" + " get set. Fixing that."); + } + tls->wasV2Handshake = 1; + log_debug(LD_HANDSHAKE, "Completed V2 TLS handshake with client; waiting" + " for renegotiation."); + } else { + tls->wasV2Handshake = 0; + } + } else { + /* Client-side */ + tls->wasV2Handshake = 1; + /* XXXX this can move, probably? -NM */ + if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) { + tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers"); + r = TOR_TLS_ERROR_MISC; + } + } + tls_log_errors(NULL, LOG_WARN, LD_NET, "finishing the handshake"); + return r; +} + +/** Return true iff this TLS connection is authenticated. + */ +int +tor_tls_peer_has_cert(tor_tls_t *tls) +{ + X509 *cert; + cert = SSL_get_peer_certificate(tls->ssl); + tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate"); + if (!cert) + return 0; + X509_free(cert); + return 1; +} + +/** Return a newly allocated copy of the peer certificate, or NULL if there + * isn't one. */ +MOCK_IMPL(tor_x509_cert_t *, +tor_tls_get_peer_cert,(tor_tls_t *tls)) +{ + X509 *cert; + cert = SSL_get_peer_certificate(tls->ssl); + tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate"); + if (!cert) + return NULL; + return tor_x509_cert_new(cert); +} + +/** Return a newly allocated copy of the cerficate we used on the connection, + * or NULL if somehow we didn't use one. */ +MOCK_IMPL(tor_x509_cert_t *, +tor_tls_get_own_cert,(tor_tls_t *tls)) +{ + X509 *cert = SSL_get_certificate(tls->ssl); + tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, + "getting own-connection certificate"); + if (!cert) + return NULL; + /* Fun inconsistency: SSL_get_peer_certificate increments the reference + * count, but SSL_get_certificate does not. */ + X509 *duplicate = X509_dup(cert); + if (BUG(duplicate == NULL)) + return NULL; + return tor_x509_cert_new(duplicate); +} + +/** Helper function: try to extract a link certificate and an identity + * certificate from <b>tls</b>, and store them in *<b>cert_out</b> and + * *<b>id_cert_out</b> respectively. Log all messages at level + * <b>severity</b>. + * + * Note that a reference is added both of the returned certificates. */ +MOCK_IMPL(void, +try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, + X509 **cert_out, X509 **id_cert_out)) +{ + X509 *cert = NULL, *id_cert = NULL; + STACK_OF(X509) *chain = NULL; + int num_in_chain, i; + *cert_out = *id_cert_out = NULL; + if (!(cert = SSL_get_peer_certificate(tls->ssl))) + return; + *cert_out = cert; + if (!(chain = SSL_get_peer_cert_chain(tls->ssl))) + return; + num_in_chain = sk_X509_num(chain); + /* 1 means we're receiving (server-side), and it's just the id_cert. + * 2 means we're connecting (client-side), and it's both the link + * cert and the id_cert. + */ + if (num_in_chain < 1) { + log_fn(severity,LD_PROTOCOL, + "Unexpected number of certificates in chain (%d)", + num_in_chain); + return; + } + for (i=0; i<num_in_chain; ++i) { + id_cert = sk_X509_value(chain, i); + if (X509_cmp(id_cert, cert) != 0) + break; + } + *id_cert_out = id_cert ? X509_dup(id_cert) : NULL; +} + +/** Return the number of bytes available for reading from <b>tls</b>. + */ +int +tor_tls_get_pending_bytes(tor_tls_t *tls) +{ + tor_assert(tls); + return SSL_pending(tls->ssl); +} + +/** If <b>tls</b> requires that the next write be of a particular size, + * return that size. Otherwise, return 0. */ +size_t +tor_tls_get_forced_write_size(tor_tls_t *tls) +{ + return tls->wantwrite_n; +} + +/** Sets n_read and n_written to the number of bytes read and written, + * respectively, on the raw socket used by <b>tls</b> since the last time this + * function was called on <b>tls</b>. */ +void +tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written) +{ + BIO *wbio, *tmpbio; + unsigned long r, w; + r = (unsigned long) BIO_number_read(SSL_get_rbio(tls->ssl)); + /* We want the number of bytes actually for real written. Unfortunately, + * sometimes OpenSSL replaces the wbio on tls->ssl with a buffering bio, + * which makes the answer turn out wrong. Let's cope with that. Note + * that this approach will fail if we ever replace tls->ssl's BIOs with + * buffering bios for reasons of our own. As an alternative, we could + * save the original BIO for tls->ssl in the tor_tls_t structure, but + * that would be tempting fate. */ + wbio = SSL_get_wbio(tls->ssl); +#if OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) + /* BIO structure is opaque as of OpenSSL 1.1.0-pre5-dev. Again, not + * supposed to use this form of the version macro, but the OpenSSL developers + * introduced major API changes in the pre-release stage. + */ + if (BIO_method_type(wbio) == BIO_TYPE_BUFFER && + (tmpbio = BIO_next(wbio)) != NULL) + wbio = tmpbio; +#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5)) */ + if (wbio->method == BIO_f_buffer() && (tmpbio = BIO_next(wbio)) != NULL) + wbio = tmpbio; +#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) */ + w = (unsigned long) BIO_number_written(wbio); + + /* We are ok with letting these unsigned ints go "negative" here: + * If we wrapped around, this should still give us the right answer, unless + * we wrapped around by more than ULONG_MAX since the last time we called + * this function. + */ + *n_read = (size_t)(r - tls->last_read_count); + *n_written = (size_t)(w - tls->last_write_count); + if (*n_read > INT_MAX || *n_written > INT_MAX) { + log_warn(LD_BUG, "Preposterously large value in tor_tls_get_n_raw_bytes. " + "r=%lu, last_read=%lu, w=%lu, last_written=%lu", + r, tls->last_read_count, w, tls->last_write_count); + } + total_bytes_written_by_tls += *n_written; + tls->last_read_count = r; + tls->last_write_count = w; +} + +/** Return a ratio of the bytes that TLS has sent to the bytes that we've told + * it to send. Used to track whether our TLS records are getting too tiny. */ +MOCK_IMPL(double, +tls_get_write_overhead_ratio,(void)) +{ + if (total_bytes_written_over_tls == 0) + return 1.0; + + return ((double)total_bytes_written_by_tls) / + ((double)total_bytes_written_over_tls); +} + +/** Implement check_no_tls_errors: If there are any pending OpenSSL + * errors, log an error message. */ +void +check_no_tls_errors_(const char *fname, int line) +{ + if (ERR_peek_error() == 0) + return; + log_warn(LD_CRYPTO, "Unhandled OpenSSL errors found at %s:%d: ", + tor_fix_source_file(fname), line); + tls_log_errors(NULL, LOG_WARN, LD_NET, NULL); +} + +/** Return true iff the initial TLS connection at <b>tls</b> did not use a v2 + * TLS handshake. Output is undefined if the handshake isn't finished. */ +int +tor_tls_used_v1_handshake(tor_tls_t *tls) +{ + return ! tls->wasV2Handshake; +} + +/** Return true iff the server TLS connection <b>tls</b> got the renegotiation + * request it was waiting for. */ +int +tor_tls_server_got_renegotiate(tor_tls_t *tls) +{ + return tls->got_renegotiate; +} + +#ifndef HAVE_SSL_GET_CLIENT_RANDOM +static size_t +SSL_get_client_random(SSL *s, uint8_t *out, size_t len) +{ + if (len == 0) + return SSL3_RANDOM_SIZE; + tor_assert(len == SSL3_RANDOM_SIZE); + tor_assert(s->s3); + memcpy(out, s->s3->client_random, len); + return len; +} +#endif /* !defined(HAVE_SSL_GET_CLIENT_RANDOM) */ + +#ifndef HAVE_SSL_GET_SERVER_RANDOM +static size_t +SSL_get_server_random(SSL *s, uint8_t *out, size_t len) +{ + if (len == 0) + return SSL3_RANDOM_SIZE; + tor_assert(len == SSL3_RANDOM_SIZE); + tor_assert(s->s3); + memcpy(out, s->s3->server_random, len); + return len; +} +#endif /* !defined(HAVE_SSL_GET_SERVER_RANDOM) */ + +#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY +size_t +SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len) +{ + tor_assert(s); + if (len == 0) + return s->master_key_length; + tor_assert(len == (size_t)s->master_key_length); + tor_assert(out); + memcpy(out, s->master_key, len); + return len; +} +#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */ + +/** Set the DIGEST256_LEN buffer at <b>secrets_out</b> to the value used in + * the v3 handshake to prove that the client knows the TLS secrets for the + * connection <b>tls</b>. Return 0 on success, -1 on failure. + */ +MOCK_IMPL(int, +tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out)) +{ +#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification" + uint8_t buf[128]; + size_t len; + tor_assert(tls); + + SSL *const ssl = tls->ssl; + SSL_SESSION *const session = SSL_get_session(ssl); + + tor_assert(ssl); + tor_assert(session); + + const size_t server_random_len = SSL_get_server_random(ssl, NULL, 0); + const size_t client_random_len = SSL_get_client_random(ssl, NULL, 0); + const size_t master_key_len = SSL_SESSION_get_master_key(session, NULL, 0); + + tor_assert(server_random_len); + tor_assert(client_random_len); + tor_assert(master_key_len); + + len = client_random_len + server_random_len + strlen(TLSSECRET_MAGIC) + 1; + tor_assert(len <= sizeof(buf)); + + { + size_t r = SSL_get_client_random(ssl, buf, client_random_len); + tor_assert(r == client_random_len); + } + + { + size_t r = SSL_get_server_random(ssl, + buf+client_random_len, + server_random_len); + tor_assert(r == server_random_len); + } + + uint8_t *master_key = tor_malloc_zero(master_key_len); + { + size_t r = SSL_SESSION_get_master_key(session, master_key, master_key_len); + tor_assert(r == master_key_len); + } + + uint8_t *nextbuf = buf + client_random_len + server_random_len; + memcpy(nextbuf, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1); + + /* + The value is an HMAC, using the TLS master key as the HMAC key, of + client_random | server_random | TLSSECRET_MAGIC + */ + crypto_hmac_sha256((char*)secrets_out, + (char*)master_key, + master_key_len, + (char*)buf, len); + memwipe(buf, 0, sizeof(buf)); + memwipe(master_key, 0, master_key_len); + tor_free(master_key); + + return 0; +} + +/** Using the RFC5705 key material exporting construction, and the + * provided <b>context</b> (<b>context_len</b> bytes long) and + * <b>label</b> (a NUL-terminated string), compute a 32-byte secret in + * <b>secrets_out</b> that only the parties to this TLS session can - * compute. Return 0 on success and -1 on failure. ++ * compute. Return 0 on success; -1 on failure; and -2 on failure ++ * caused by OpenSSL bug 7712. + */ +MOCK_IMPL(int, +tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out, + const uint8_t *context, + size_t context_len, + const char *label)) +{ + tor_assert(tls); + tor_assert(tls->ssl); + + int r = SSL_export_keying_material(tls->ssl, + secrets_out, DIGEST256_LEN, + label, strlen(label), + context, context_len, 1); ++ ++ if (r != 1) { ++ int severity = openssl_bug_7712_is_present ? LOG_WARN : LOG_DEBUG; ++ tls_log_errors(tls, severity, LD_NET, "exporting keying material"); ++ } ++ ++#ifdef TLS1_3_VERSION ++ if (r != 1 && ++ strlen(label) > 12 && ++ SSL_version(tls->ssl) >= TLS1_3_VERSION) { ++ ++ if (! openssl_bug_7712_is_present) { ++ /* We might have run into OpenSSL issue 7712, which caused OpenSSL ++ * 1.1.1a to not handle long labels. Let's test to see if we have. ++ */ ++ r = SSL_export_keying_material(tls->ssl, secrets_out, DIGEST256_LEN, ++ "short", 5, context, context_len, 1); ++ if (r == 1) { ++ /* A short label succeeds, but a long label fails. This was openssl ++ * issue 7712. */ ++ openssl_bug_7712_is_present = 1; ++ log_warn(LD_GENERAL, "Detected OpenSSL bug 7712: disabling TLS 1.3 on " ++ "future connections. A fix is expected to appear in OpenSSL " ++ "1.1.1b."); ++ } ++ } ++ if (openssl_bug_7712_is_present) ++ return -2; ++ else ++ return -1; ++ } ++#endif ++ + return (r == 1) ? 0 : -1; +} + +/** Examine the amount of memory used and available for buffers in <b>tls</b>. + * Set *<b>rbuf_capacity</b> to the amount of storage allocated for the read + * buffer and *<b>rbuf_bytes</b> to the amount actually used. + * Set *<b>wbuf_capacity</b> to the amount of storage allocated for the write + * buffer and *<b>wbuf_bytes</b> to the amount actually used. + * + * Return 0 on success, -1 on failure.*/ +int +tor_tls_get_buffer_sizes(tor_tls_t *tls, + size_t *rbuf_capacity, size_t *rbuf_bytes, + size_t *wbuf_capacity, size_t *wbuf_bytes) +{ +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) + (void)tls; + (void)rbuf_capacity; + (void)rbuf_bytes; + (void)wbuf_capacity; + (void)wbuf_bytes; + + return -1; +#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)) */ + if (tls->ssl->s3->rbuf.buf) + *rbuf_capacity = tls->ssl->s3->rbuf.len; + else + *rbuf_capacity = 0; + if (tls->ssl->s3->wbuf.buf) + *wbuf_capacity = tls->ssl->s3->wbuf.len; + else + *wbuf_capacity = 0; + *rbuf_bytes = tls->ssl->s3->rbuf.left; + *wbuf_bytes = tls->ssl->s3->wbuf.left; + return 0; +#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */ +} + +/** Check whether the ECC group requested is supported by the current OpenSSL + * library instance. Return 1 if the group is supported, and 0 if not. + */ +int +evaluate_ecgroup_for_tls(const char *ecgroup) +{ + EC_KEY *ec_key; + int nid; + int ret; + + if (!ecgroup) + nid = NID_tor_default_ecdhe_group; + else if (!strcasecmp(ecgroup, "P256")) + nid = NID_X9_62_prime256v1; + else if (!strcasecmp(ecgroup, "P224")) + nid = NID_secp224r1; + else + return 0; + + ec_key = EC_KEY_new_by_curve_name(nid); + ret = (ec_key != NULL); + EC_KEY_free(ec_key); + + return ret; +}
tor-commits@lists.torproject.org