diff options
Diffstat (limited to 'src/feature')
127 files changed, 4236 insertions, 489 deletions
diff --git a/src/feature/api/feature_api.md b/src/feature/api/feature_api.md new file mode 100644 index 0000000000..3065c000aa --- /dev/null +++ b/src/feature/api/feature_api.md @@ -0,0 +1,2 @@ +@dir /feature/api +@brief feature/api: In-process interface to starting/stopping Tor. diff --git a/src/feature/api/tor_api_internal.h b/src/feature/api/tor_api_internal.h index 60e0f3aa59..115d33d58e 100644 --- a/src/feature/api/tor_api_internal.h +++ b/src/feature/api/tor_api_internal.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file tor_api_internal.h + * @brief Internal declarations for in-process Tor API. + **/ + #ifndef TOR_API_INTERNAL_H #define TOR_API_INTERNAL_H diff --git a/src/feature/client/addressmap.h b/src/feature/client/addressmap.h index 9179aef1d0..eb9742c50b 100644 --- a/src/feature/client/addressmap.h +++ b/src/feature/client/addressmap.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file addressmap.h + * @brief Header for addressmap.c + **/ + #ifndef TOR_ADDRESSMAP_H #define TOR_ADDRESSMAP_H @@ -62,4 +67,3 @@ STATIC void get_random_virtual_addr(const virtual_addr_conf_t *conf, #endif /* defined(ADDRESSMAP_PRIVATE) */ #endif /* !defined(TOR_ADDRESSMAP_H) */ - diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c index 4e61899ecd..8af8f14bb0 100644 --- a/src/feature/client/entrynodes.c +++ b/src/feature/client/entrynodes.c @@ -114,7 +114,7 @@ #include "core/or/or.h" #include "app/config/config.h" -#include "lib/confmgt/confparse.h" +#include "lib/confmgt/confmgt.h" #include "app/config/statefile.h" #include "core/mainloop/connection.h" #include "core/mainloop/mainloop.h" diff --git a/src/feature/client/entrynodes.h b/src/feature/client/entrynodes.h index 4e5eb4e960..f2978bc483 100644 --- a/src/feature/client/entrynodes.h +++ b/src/feature/client/entrynodes.h @@ -15,7 +15,7 @@ #include "lib/container/handles.h" /* Forward declare for guard_selection_t; entrynodes.c has the real struct */ -typedef struct guard_selection_s guard_selection_t; +typedef struct guard_selection_t guard_selection_t; /* Forward declare for entry_guard_t; the real declaration is private. */ typedef struct entry_guard_t entry_guard_t; @@ -28,7 +28,7 @@ typedef struct circuit_guard_state_t circuit_guard_state_t; private. */ typedef struct entry_guard_restriction_t entry_guard_restriction_t; -/* Information about a guard's pathbias status. +/** Information about a guard's pathbias status. * These fields are used in circpathbias.c to try to detect entry * nodes that are failing circuits at a suspicious frequency. */ @@ -210,7 +210,7 @@ typedef enum guard_selection_type_t { * See the module documentation for entrynodes.c for more information * about guard selection algorithms. */ -struct guard_selection_s { +struct guard_selection_t { /** * The name for this guard-selection object. (Must not contain spaces). */ diff --git a/src/feature/client/feature_client.md b/src/feature/client/feature_client.md new file mode 100644 index 0000000000..dd4bf78ec8 --- /dev/null +++ b/src/feature/client/feature_client.md @@ -0,0 +1,5 @@ +@dir /feature/client +@brief feature/client: Client-specific code + +(There is also a bunch of client-specific code in other modules.) + diff --git a/src/feature/client/proxymode.c b/src/feature/client/proxymode.c index 3b5fba5cda..0682205336 100644 --- a/src/feature/client/proxymode.c +++ b/src/feature/client/proxymode.c @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file proxymode.c + * @brief Determine whether we are trying to be a proxy. + **/ + #include "core/or/or.h" #include "app/config/config.h" diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c index 3f731ac7d4..6537a4b2da 100644 --- a/src/feature/client/transports.c +++ b/src/feature/client/transports.c @@ -97,6 +97,8 @@ #include "core/or/circuitbuild.h" #include "feature/client/transports.h" #include "feature/relay/router.h" +/* 31851: split the server transport code out of the client module */ +#include "feature/relay/transport_config.h" #include "app/config/statefile.h" #include "core/or/connection_or.h" #include "feature/relay/ext_orport.h" @@ -1279,7 +1281,7 @@ get_transport_options_for_server_proxy(const managed_proxy_t *mp) string. */ SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, transport) { smartlist_t *options_tmp_sl = NULL; - options_tmp_sl = get_options_for_server_transport(transport); + options_tmp_sl = pt_get_options_for_server_transport(transport); if (!options_tmp_sl) continue; diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c index 68179beef0..656ddf5ca1 100644 --- a/src/feature/control/control_cmd.c +++ b/src/feature/control/control_cmd.c @@ -13,7 +13,7 @@ #include "core/or/or.h" #include "app/config/config.h" -#include "lib/confmgt/confparse.h" +#include "lib/confmgt/confmgt.h" #include "app/main/main.h" #include "core/mainloop/connection.h" #include "core/or/circuitbuild.h" @@ -26,6 +26,7 @@ #include "feature/control/control.h" #include "feature/control/control_auth.h" #include "feature/control/control_cmd.h" +#include "feature/control/control_hs.h" #include "feature/control/control_events.h" #include "feature/control/control_getinfo.h" #include "feature/control/control_proto.h" @@ -590,7 +591,7 @@ control_setconf_helper(control_connection_t *conn, const unsigned flags = CAL_CLEAR_FIRST | (use_defaults ? CAL_USE_DEFAULTS : 0); - // We need a copy here, since confparse.c wants to canonicalize cases. + // We need a copy here, since confmgt.c wants to canonicalize cases. config_line_t *lines = config_lines_dup(args->kwargs); opt_err = options_trial_assign(lines, flags, &errstring); @@ -1970,6 +1971,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, decoded_key->v2 = pk; *hs_version = HS_VERSION_TWO; } else if (!strcasecmp(key_type_ed25519_v3, key_type)) { + /* parsing of private ed25519 key */ /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */ ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob, @@ -2317,6 +2319,9 @@ static const control_cmd_def_t CONTROL_COMMANDS[] = MULTLINE(hspost, 0), ONE_LINE(add_onion, CMD_FL_WIPE), ONE_LINE(del_onion, CMD_FL_WIPE), + ONE_LINE(onion_client_auth_add, CMD_FL_WIPE), + ONE_LINE(onion_client_auth_remove, 0), + ONE_LINE(onion_client_auth_view, 0), }; /** diff --git a/src/feature/control/control_connection_st.h b/src/feature/control/control_connection_st.h index c9164f03b3..8ecce5ee69 100644 --- a/src/feature/control/control_connection_st.h +++ b/src/feature/control/control_connection_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file control_connection_st.h + * @brief Controller connection structure. + **/ + #ifndef CONTROL_CONNECTION_ST_H #define CONTROL_CONNECTION_ST_H diff --git a/src/feature/control/control_events.c b/src/feature/control/control_events.c index 427edbdf3f..f88bcfdb9f 100644 --- a/src/feature/control/control_events.c +++ b/src/feature/control/control_events.c @@ -317,7 +317,7 @@ control_per_second_events(void) /** Represents an event that's queued to be sent to one or more * controllers. */ -typedef struct queued_event_s { +typedef struct queued_event_t { uint16_t event; char *msg; } queued_event_t; @@ -1217,7 +1217,7 @@ control_event_circuit_cell_stats(void) static int next_measurement_idx = 0; /* number of entries set in n_measurements */ static int n_measurements = 0; -static struct cached_bw_event_s { +static struct cached_bw_event_t { uint32_t n_read; uint32_t n_written; } cached_bw_events[N_BW_EVENTS_TO_CACHE]; @@ -1256,7 +1256,7 @@ get_bw_samples(void) for (i = 0; i < n_measurements; ++i) { tor_assert(0 <= idx && idx < N_BW_EVENTS_TO_CACHE); - const struct cached_bw_event_s *bwe = &cached_bw_events[idx]; + const struct cached_bw_event_t *bwe = &cached_bw_events[idx]; smartlist_add_asprintf(elements, "%u,%u", (unsigned)bwe->n_read, diff --git a/src/feature/control/control_hs.c b/src/feature/control/control_hs.c new file mode 100644 index 0000000000..4c1d16a8c8 --- /dev/null +++ b/src/feature/control/control_hs.c @@ -0,0 +1,342 @@ +/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file control_hs.c + * + * \brief Implement commands for Tor's control-socket interface that are + * related to onion services. + **/ + +#include "core/or/or.h" +#include "feature/control/control_cmd.h" +#include "feature/control/control_hs.h" +#include "feature/control/control_proto.h" +#include "feature/hs/hs_client.h" +#include "lib/encoding/confline.h" + +#include "feature/control/control_cmd_args_st.h" + +/** Parse the 'KeyType ":" PrivateKey' from <b>client_privkey_str</b> and store + * it into <b>privkey</b>. Use <b>conn</b> to output any errors if needed. + * + * Return 0 if all went well, -1 otherwise. */ +static int +parse_private_key_from_control_port(const char *client_privkey_str, + curve25519_secret_key_t *privkey, + control_connection_t *conn) +{ + int retval = -1; + smartlist_t *key_args = smartlist_new(); + + tor_assert(privkey); + + smartlist_split_string(key_args, client_privkey_str, ":", + SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(key_args) != 2) { + control_printf_endreply(conn, 512, "Invalid key type/blob"); + goto err; + } + + const char *key_type = smartlist_get(key_args, 0); + const char *key_blob = smartlist_get(key_args, 1); + + if (strcasecmp(key_type, "x25519")) { + control_printf_endreply(conn, 552, + "Unrecognized key type \"%s\"", key_type); + goto err; + } + + if (base64_decode((char*)privkey->secret_key, sizeof(privkey->secret_key), + key_blob, + strlen(key_blob)) != sizeof(privkey->secret_key)) { + control_printf_endreply(conn, 512, "Failed to decode x25519 private key"); + goto err; + } + + retval = 0; + + err: + SMARTLIST_FOREACH(key_args, char *, c, tor_free(c)); + smartlist_free(key_args); + return retval; +} + +/** Syntax details for ONION_CLIENT_AUTH_ADD */ +const control_cmd_syntax_t onion_client_auth_add_syntax = { + .max_args = 2, + .accept_keywords = true, +}; + +/** Called when we get an ONION_CLIENT_AUTH_ADD command; parse the body, and + * register the new client-side client auth credentials: + * "ONION_CLIENT_AUTH_ADD" SP HSAddress + * SP KeyType ":" PrivateKeyBlob + * [SP "ClientName=" Nickname] + * [SP "Type=" TYPE] CRLF + */ +int +handle_control_onion_client_auth_add(control_connection_t *conn, + const control_cmd_args_t *args) +{ + int retval = -1; + smartlist_t *flags = smartlist_new(); + hs_client_service_authorization_t *creds = NULL; + + tor_assert(args); + + int argc = smartlist_len(args->args); + /* We need at least 'HSAddress' and 'PrivateKeyBlob' */ + if (argc < 2) { + control_printf_endreply(conn, 512, + "Incomplete ONION_CLIENT_AUTH_ADD command"); + goto err; + } + + creds = tor_malloc_zero(sizeof(hs_client_service_authorization_t)); + + const char *hsaddress = smartlist_get(args->args, 0); + if (!hs_address_is_valid(hsaddress)) { + control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress); + goto err; + } + strlcpy(creds->onion_address, hsaddress, sizeof(creds->onion_address)); + + /* Parse the client private key */ + const char *client_privkey = smartlist_get(args->args, 1); + if (parse_private_key_from_control_port(client_privkey, + &creds->enc_seckey, conn) < 0) { + goto err; + } + + /* Now let's parse the remaining arguments (variable size) */ + for (const config_line_t *line = args->kwargs; line; line = line->next) { + if (!strcasecmp(line->key, "ClientName")) { + if (strlen(line->value) > HS_CLIENT_AUTH_MAX_NICKNAME_LENGTH) { + control_write_endreply(conn, 512, "Too big 'ClientName' argument"); + goto err; + } + creds->nickname = tor_strdup(line->value); + + } else if (!strcasecmpstart(line->key, "Flags")) { + smartlist_split_string(flags, line->value, ",", SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(flags) < 1) { + control_write_endreply(conn, 512, "Invalid 'Flags' argument"); + goto err; + } + SMARTLIST_FOREACH_BEGIN(flags, const char *, flag) { + if (!strcasecmp(flag, "Permanent")) { + creds->flags |= CLIENT_AUTH_FLAG_IS_PERMANENT; + } else { + control_printf_endreply(conn, 512, "Invalid 'Flags' argument: %s", + escaped(flag)); + goto err; + } + } SMARTLIST_FOREACH_END(flag); + } + } + + hs_client_register_auth_status_t register_status; + /* Register the credential (register func takes ownership of cred.) */ + register_status = hs_client_register_auth_credentials(creds); + switch (register_status) { + case REGISTER_FAIL_BAD_ADDRESS: + /* It's a bug because the service addr has already been validated above */ + control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"", hsaddress); + break; + case REGISTER_SUCCESS_ALREADY_EXISTS: + control_printf_endreply(conn, 251,"Client for onion existed and replaced"); + break; + case REGISTER_SUCCESS_AND_DECRYPTED: + control_printf_endreply(conn, 252,"Registered client and decrypted desc"); + break; + case REGISTER_SUCCESS: + control_printf_endreply(conn, 250, "OK"); + break; + default: + tor_assert_nonfatal_unreached(); + } + + retval = 0; + goto done; + + err: + client_service_authorization_free(creds); + + done: + SMARTLIST_FOREACH(flags, char *, s, tor_free(s)); + smartlist_free(flags); + return retval; +} + +/** Syntax details for ONION_CLIENT_AUTH_REMOVE */ +const control_cmd_syntax_t onion_client_auth_remove_syntax = { + .max_args = 1, + .accept_keywords = true, +}; + +/** Called when we get an ONION_CLIENT_AUTH_REMOVE command; parse the body, and + * register the new client-side client auth credentials. + * "ONION_CLIENT_AUTH_REMOVE" SP HSAddress + */ +int +handle_control_onion_client_auth_remove(control_connection_t *conn, + const control_cmd_args_t *args) +{ + int retval = -1; + + tor_assert(args); + + int argc = smartlist_len(args->args); + if (argc < 1) { + control_printf_endreply(conn, 512, + "Incomplete ONION_CLIENT_AUTH_REMOVE command"); + goto err; + } + + const char *hsaddress = smartlist_get(args->args, 0); + if (!hs_address_is_valid(hsaddress)) { + control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress); + goto err; + } + + hs_client_removal_auth_status_t removal_status; + removal_status = hs_client_remove_auth_credentials(hsaddress); + switch (removal_status) { + case REMOVAL_BAD_ADDRESS: + /* It's a bug because the service addr has already been validated above */ + control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress); + break; + case REMOVAL_SUCCESS_NOT_FOUND: + control_printf_endreply(conn, 251, "No credentials for \"%s\"",hsaddress); + break; + case REMOVAL_SUCCESS: + control_printf_endreply(conn, 250, "OK"); + break; + default: + tor_assert_nonfatal_unreached(); + } + + retval = 0; + + err: + return retval; +} + +/** Helper: Return a newly allocated string with the encoding of client + * authorization credentials */ +static char * +encode_client_auth_cred_for_control_port( + hs_client_service_authorization_t *cred) +{ + smartlist_t *control_line = smartlist_new(); + char x25519_b64[128]; + char *msg_str = NULL; + + tor_assert(cred); + + if (base64_encode(x25519_b64, sizeof(x25519_b64), + (char *)cred->enc_seckey.secret_key, + sizeof(cred->enc_seckey.secret_key), 0) < 0) { + tor_assert_nonfatal_unreached(); + goto err; + } + + smartlist_add_asprintf(control_line, "CLIENT x25519:%s", x25519_b64); + + if (cred->nickname) { /* nickname is optional */ + smartlist_add_asprintf(control_line, " ClientName=%s", cred->nickname); + } + + if (cred->flags) { /* flags are also optional */ + if (cred->flags & CLIENT_AUTH_FLAG_IS_PERMANENT) { + smartlist_add_asprintf(control_line, " Flags=Permanent"); + } + } + + /* Join all the components into a single string */ + msg_str = smartlist_join_strings(control_line, "", 0, NULL); + + err: + SMARTLIST_FOREACH(control_line, char *, cp, tor_free(cp)); + smartlist_free(control_line); + + return msg_str; +} + +/** Syntax details for ONION_CLIENT_AUTH_VIEW */ +const control_cmd_syntax_t onion_client_auth_view_syntax = { + .max_args = 1, + .accept_keywords = true, +}; + +/** Called when we get an ONION_CLIENT_AUTH_VIEW command; parse the body, and + * register the new client-side client auth credentials. + * "ONION_CLIENT_AUTH_VIEW" [SP HSAddress] CRLF + */ +int +handle_control_onion_client_auth_view(control_connection_t *conn, + const control_cmd_args_t *args) +{ + int retval = -1; + const char *hsaddress = NULL; + /* We are gonna put all the credential strings into a smartlist, and sort it + before printing, so that we can get a guaranteed order of printing. */ + smartlist_t *creds_str_list = smartlist_new(); + + tor_assert(args); + + int argc = smartlist_len(args->args); + if (argc >= 1) { + hsaddress = smartlist_get(args->args, 0); + if (!hs_address_is_valid(hsaddress)) { + control_printf_endreply(conn, 512, "Invalid v3 addr \"%s\"", hsaddress); + goto err; + } + } + + if (hsaddress) { + control_printf_midreply(conn, 250, "ONION_CLIENT_AUTH_VIEW %s", hsaddress); + } else { + control_printf_midreply(conn, 250, "ONION_CLIENT_AUTH_VIEW"); + } + + /* Create an iterator out of the digest256map */ + digest256map_t *client_auths = get_hs_client_auths_map(); + digest256map_iter_t *itr = digest256map_iter_init(client_auths); + while (!digest256map_iter_done(itr)) { + const uint8_t *service_pubkey; + void *valp; + digest256map_iter_get(itr, &service_pubkey, &valp); + tor_assert(valp); + hs_client_service_authorization_t *cred = valp; + + /* If a specific HS address was requested, only print creds for that one */ + if (hsaddress && strcmp(cred->onion_address, hsaddress)) { + itr = digest256map_iter_next(client_auths, itr); + continue; + } + + char *encoding_str = encode_client_auth_cred_for_control_port(cred); + tor_assert_nonfatal(encoding_str); + smartlist_add(creds_str_list, encoding_str); + + itr = digest256map_iter_next(client_auths, itr); + } + + /* We got everything: Now sort the strings and print them */ + smartlist_sort_strings(creds_str_list); + SMARTLIST_FOREACH_BEGIN(creds_str_list, char *, c) { + control_printf_midreply(conn, 250, "%s", c); + } SMARTLIST_FOREACH_END(c); + + send_control_done(conn); + + retval = 0; + + err: + SMARTLIST_FOREACH(creds_str_list, char *, cp, tor_free(cp)); + smartlist_free(creds_str_list); + return retval; +} diff --git a/src/feature/control/control_hs.h b/src/feature/control/control_hs.h new file mode 100644 index 0000000000..35ac1b22d4 --- /dev/null +++ b/src/feature/control/control_hs.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file control_hs.c + * + * \brief Header file for control_hs.c. + **/ + +#ifndef TOR_CONTROL_HS_H +#define TOR_CONTROL_HS_H + +struct control_cmd_syntax_t; + +extern const struct control_cmd_syntax_t onion_client_auth_add_syntax; +extern const struct control_cmd_syntax_t onion_client_auth_remove_syntax; +extern const struct control_cmd_syntax_t onion_client_auth_view_syntax; + +int +handle_control_onion_client_auth_add(control_connection_t *conn, + const control_cmd_args_t *args); + +int +handle_control_onion_client_auth_remove(control_connection_t *conn, + const control_cmd_args_t *args); + +int +handle_control_onion_client_auth_view(control_connection_t *conn, + const control_cmd_args_t *args); + +#endif + diff --git a/src/feature/control/feature_control.md b/src/feature/control/feature_control.md new file mode 100644 index 0000000000..9f1681ea91 --- /dev/null +++ b/src/feature/control/feature_control.md @@ -0,0 +1,8 @@ +@dir /feature/control +@brief feature/control: Controller API. + +The Controller API is a text-based protocol that another program (or another +thread, if you're running Tor in-process) can use to configure and control +Tor while it is running. The current protocol is documented in +[control-spec.txt](https://gitweb.torproject.org/torspec.git/tree/control-spec.txt). + diff --git a/src/feature/control/fmt_serverstatus.c b/src/feature/control/fmt_serverstatus.c index 33c5ba1336..92db70758f 100644 --- a/src/feature/control/fmt_serverstatus.c +++ b/src/feature/control/fmt_serverstatus.c @@ -3,6 +3,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file fmt_serverstatus.c + * @brief Format relay info for a controller. + **/ + #include "core/or/or.h" #include "feature/control/fmt_serverstatus.h" diff --git a/src/feature/control/getinfo_geoip.c b/src/feature/control/getinfo_geoip.c index d188725fa3..4636ede039 100644 --- a/src/feature/control/getinfo_geoip.c +++ b/src/feature/control/getinfo_geoip.c @@ -1,3 +1,12 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file getinfo_geoip.c + * @brief GEOIP-related contoller GETINFO commands. + **/ #include "core/or/or.h" #include "core/mainloop/connection.h" diff --git a/src/feature/control/getinfo_geoip.h b/src/feature/control/getinfo_geoip.h index 94759d0d18..f6bc86cb53 100644 --- a/src/feature/control/getinfo_geoip.h +++ b/src/feature/control/getinfo_geoip.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file getinfo_geoip.h + * @brief Header for getinfo_geoip.c + **/ + #ifndef TOR_GETINFO_GEOIP_H #define TOR_GETINFO_GEOIP_H diff --git a/src/feature/dirauth/bridgeauth.c b/src/feature/dirauth/bridgeauth.c index 4aaefc7a6d..56782011c2 100644 --- a/src/feature/dirauth/bridgeauth.c +++ b/src/feature/dirauth/bridgeauth.c @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file bridgeauth.c + * @brief Bridge authority code + **/ + #include "core/or/or.h" #include "feature/dirauth/bridgeauth.h" #include "feature/dirauth/voteflags.h" diff --git a/src/feature/dirauth/bridgeauth.h b/src/feature/dirauth/bridgeauth.h index 4905e9c3ee..76676e8db5 100644 --- a/src/feature/dirauth/bridgeauth.h +++ b/src/feature/dirauth/bridgeauth.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file bridgeauth.h + * @brief Header for bridgeauth.c + **/ + #ifndef TOR_DIRAUTH_BRIDGEAUTH_H #define TOR_DIRAUTH_BRIDGEAUTH_H diff --git a/src/feature/dirauth/bwauth.c b/src/feature/dirauth/bwauth.c index e60c8b86bd..b1cde79628 100644 --- a/src/feature/dirauth/bwauth.c +++ b/src/feature/dirauth/bwauth.c @@ -56,7 +56,7 @@ dirserv_get_last_n_measured_bws(void) } /** Measured bandwidth cache entry */ -typedef struct mbw_cache_entry_s { +typedef struct mbw_cache_entry_t { long mbw_kb; time_t as_of; } mbw_cache_entry_t; diff --git a/src/feature/dirauth/dirauth_config.c b/src/feature/dirauth/dirauth_config.c new file mode 100644 index 0000000000..552f851461 --- /dev/null +++ b/src/feature/dirauth/dirauth_config.c @@ -0,0 +1,440 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_config.c + * @brief Code to interpret the user's configuration of Tor's directory + * authority module. + **/ + +#include "orconfig.h" +#include "feature/dirauth/dirauth_config.h" + +#include "lib/encoding/confline.h" +#include "lib/confmgt/confmgt.h" + +/* Required for dirinfo_type_t in or_options_t */ +#include "core/or/or.h" +#include "app/config/config.h" + +#include "feature/dircommon/voting_schedule.h" +#include "feature/stats/rephist.h" + +#include "feature/dirauth/authmode.h" +#include "feature/dirauth/bwauth.h" +#include "feature/dirauth/dirauth_periodic.h" +#include "feature/dirauth/dirvote.h" +#include "feature/dirauth/guardfraction.h" + +/* Copied from config.c, we will refactor later in 29211. */ +#define REJECT(arg) \ + STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END +#if defined(__GNUC__) && __GNUC__ <= 3 +#define COMPLAIN(args...) \ + STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END +#else +#define COMPLAIN(args, ...) \ + STMT_BEGIN log_warn(LD_CONFIG, args, ##__VA_ARGS__); STMT_END +#endif /* defined(__GNUC__) && __GNUC__ <= 3 */ + +#define YES_IF_CHANGED_INT(opt) \ + if (!CFG_EQ_INT(old_options, new_options, opt)) return 1; + +/** + * Legacy validation/normalization function for the dirauth mode options in + * options. Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_dirauth_mode(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (!authdir_mode(options)) + return 0; + + /* confirm that our address isn't broken, so we can complain now */ + uint32_t tmp; + if (resolve_my_address(LOG_WARN, options, &tmp, NULL, NULL) < 0) + REJECT("Failed to resolve/guess local address. See logs for details."); + + if (!options->ContactInfo && !options->TestingTorNetwork) + REJECT("Authoritative directory servers must set ContactInfo"); + if (!options->RecommendedClientVersions) + options->RecommendedClientVersions = + config_lines_dup(options->RecommendedVersions); + if (!options->RecommendedServerVersions) + options->RecommendedServerVersions = + config_lines_dup(options->RecommendedVersions); + if (options->VersioningAuthoritativeDir && + (!options->RecommendedClientVersions || + !options->RecommendedServerVersions)) + REJECT("Versioning authoritative dir servers must set " + "Recommended*Versions."); + + char *t; + /* Call these functions to produce warnings only. */ + t = format_recommended_version_list(options->RecommendedClientVersions, 1); + tor_free(t); + t = format_recommended_version_list(options->RecommendedServerVersions, 1); + tor_free(t); + + if (options->UseEntryGuards) { + log_info(LD_CONFIG, "Authoritative directory servers can't set " + "UseEntryGuards. Disabling."); + options->UseEntryGuards = 0; + } + if (!options->DownloadExtraInfo && authdir_mode_v3(options)) { + log_info(LD_CONFIG, "Authoritative directories always try to download " + "extra-info documents. Setting DownloadExtraInfo."); + options->DownloadExtraInfo = 1; + } + if (!(options->BridgeAuthoritativeDir || + options->V3AuthoritativeDir)) + REJECT("AuthoritativeDir is set, but none of " + "(Bridge/V3)AuthoritativeDir is set."); + + /* If we have a v3bandwidthsfile and it's broken, complain on startup */ + if (options->V3BandwidthsFile && !old_options) { + dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL, NULL, + NULL); + } + /* same for guardfraction file */ + if (options->GuardfractionFile && !old_options) { + dirserv_read_guardfraction_file(options->GuardfractionFile, NULL); + } + + if (!options->DirPort_set) + REJECT("Running as authoritative directory, but no DirPort set."); + + if (!options->ORPort_set) + REJECT("Running as authoritative directory, but no ORPort set."); + + if (options->ClientOnly) + REJECT("Running as authoritative directory, but ClientOnly also set."); + + if (options->MinUptimeHidServDirectoryV2 < 0) { + log_warn(LD_CONFIG, "MinUptimeHidServDirectoryV2 option must be at " + "least 0 seconds. Changing to 0."); + options->MinUptimeHidServDirectoryV2 = 0; + } + + return 0; +} + +/** + * Legacy validation/normalization function for the dirauth bandwidth options + * in options. Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_dirauth_bandwidth(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (!authdir_mode(options)) + return 0; + + if (config_ensure_bandwidth_cap(&options->AuthDirFastGuarantee, + "AuthDirFastGuarantee", msg) < 0) + return -1; + if (config_ensure_bandwidth_cap(&options->AuthDirGuardBWGuarantee, + "AuthDirGuardBWGuarantee", msg) < 0) + return -1; + + return 0; +} + +/** + * Legacy validation/normalization function for the dirauth schedule options + * in options. Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_dirauth_schedule(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (!authdir_mode_v3(options)) + return 0; + + if (options->V3AuthVoteDelay + options->V3AuthDistDelay >= + options->V3AuthVotingInterval/2) { + REJECT("V3AuthVoteDelay plus V3AuthDistDelay must be less than half " + "V3AuthVotingInterval"); + } + + if (options->V3AuthVoteDelay < MIN_VOTE_SECONDS) { + if (options->TestingTorNetwork) { + if (options->V3AuthVoteDelay < MIN_VOTE_SECONDS_TESTING) { + REJECT("V3AuthVoteDelay is way too low."); + } else { + COMPLAIN("V3AuthVoteDelay is very low. " + "This may lead to failure to vote for a consensus."); + } + } else { + REJECT("V3AuthVoteDelay is way too low."); + } + } + + if (options->V3AuthDistDelay < MIN_DIST_SECONDS) { + if (options->TestingTorNetwork) { + if (options->V3AuthDistDelay < MIN_DIST_SECONDS_TESTING) { + REJECT("V3AuthDistDelay is way too low."); + } else { + COMPLAIN("V3AuthDistDelay is very low. " + "This may lead to missing votes in a consensus."); + } + } else { + REJECT("V3AuthDistDelay is way too low."); + } + } + + if (options->V3AuthNIntervalsValid < 2) + REJECT("V3AuthNIntervalsValid must be at least 2."); + + if (options->V3AuthVotingInterval < MIN_VOTE_INTERVAL) { + if (options->TestingTorNetwork) { + if (options->V3AuthVotingInterval < MIN_VOTE_INTERVAL_TESTING) { + /* Unreachable, covered by earlier checks */ + REJECT("V3AuthVotingInterval is insanely low."); /* LCOV_EXCL_LINE */ + } else { + COMPLAIN("V3AuthVotingInterval is very low. " + "This may lead to failure to synchronise for a consensus."); + } + } else { + REJECT("V3AuthVotingInterval is insanely low."); + } + } else if (options->V3AuthVotingInterval > 24*60*60) { + REJECT("V3AuthVotingInterval is insanely high."); + } else if (((24*60*60) % options->V3AuthVotingInterval) != 0) { + COMPLAIN("V3AuthVotingInterval does not divide evenly into 24 hours."); + } + + return 0; +} + +/** + * Legacy validation/normalization function for the dirauth testing options + * in options. Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_dirauth_testing(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (!authdir_mode(options)) + return 0; + + if (options->TestingAuthDirTimeToLearnReachability < 0) { + REJECT("TestingAuthDirTimeToLearnReachability must be non-negative."); + } else if (options->TestingAuthDirTimeToLearnReachability > 2*60*60) { + COMPLAIN("TestingAuthDirTimeToLearnReachability is insanely high."); + } + + if (!authdir_mode_v3(options)) + return 0; + + if (options->TestingV3AuthInitialVotingInterval + < MIN_VOTE_INTERVAL_TESTING_INITIAL) { + REJECT("TestingV3AuthInitialVotingInterval is insanely low."); + } else if (((30*60) % options->TestingV3AuthInitialVotingInterval) != 0) { + REJECT("TestingV3AuthInitialVotingInterval does not divide evenly into " + "30 minutes."); + } + + if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS_TESTING) { + REJECT("TestingV3AuthInitialVoteDelay is way too low."); + } + + if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS_TESTING) { + REJECT("TestingV3AuthInitialDistDelay is way too low."); + } + + if (options->TestingV3AuthInitialVoteDelay + + options->TestingV3AuthInitialDistDelay >= + options->TestingV3AuthInitialVotingInterval) { + REJECT("TestingV3AuthInitialVoteDelay plus TestingV3AuthInitialDistDelay " + "must be less than TestingV3AuthInitialVotingInterval"); + } + + if (options->TestingV3AuthVotingStartOffset > + MIN(options->TestingV3AuthInitialVotingInterval, + options->V3AuthVotingInterval)) { + REJECT("TestingV3AuthVotingStartOffset is higher than the voting " + "interval."); + } else if (options->TestingV3AuthVotingStartOffset < 0) { + REJECT("TestingV3AuthVotingStartOffset must be non-negative."); + } + + return 0; +} + +/** + * Return true if changing the configuration from <b>old</b> to <b>new</b> + * affects the timing of the voting subsystem + */ +static int +options_transition_affects_dirauth_timing(const or_options_t *old_options, + const or_options_t *new_options) +{ + tor_assert(old_options); + tor_assert(new_options); + + if (authdir_mode_v3(old_options) != authdir_mode_v3(new_options)) + return 1; + if (! authdir_mode_v3(new_options)) + return 0; + + YES_IF_CHANGED_INT(V3AuthVotingInterval); + YES_IF_CHANGED_INT(V3AuthVoteDelay); + YES_IF_CHANGED_INT(V3AuthDistDelay); + YES_IF_CHANGED_INT(TestingV3AuthInitialVotingInterval); + YES_IF_CHANGED_INT(TestingV3AuthInitialVoteDelay); + YES_IF_CHANGED_INT(TestingV3AuthInitialDistDelay); + YES_IF_CHANGED_INT(TestingV3AuthVotingStartOffset); + + return 0; +} + +/** Fetch the active option list, and take dirauth actions based on it. All of + * the things we do should survive being done repeatedly. If present, + * <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_dirauth(const or_options_t *old_options) +{ + const or_options_t *options = get_options(); + + /* We may need to reschedule some dirauth stuff if our status changed. */ + if (old_options) { + if (options_transition_affects_dirauth_timing(old_options, options)) { + voting_schedule_recalculate_timing(options, time(NULL)); + reschedule_dirvote(options); + } + } + + return 0; +} + +/** Fetch the active option list, and take dirauth mtbf actions based on it. + * All of the things we do should survive being done repeatedly. If present, + * <b>old_options</b> contains the previous value of the options. + * + * Must be called immediately after a successful or_state_load(). + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_dirauth_mtbf(const or_options_t *old_options) +{ + (void)old_options; + + const or_options_t *options = get_options(); + int running_tor = options->command == CMD_RUN_TOR; + + if (!authdir_mode(options)) + return 0; + + /* Load dirauth state */ + if (running_tor) { + rep_hist_load_mtbf_data(time(NULL)); + } + + return 0; +} + +/** Fetch the active option list, and take dirauth statistics actions based + * on it. All of the things we do should survive being done repeatedly. If + * present, <b>old_options</b> contains the previous value of the options. + * + * Sets <b>*print_notice_out</b> if we enabled stats, and need to print + * a stats log using options_act_relay_stats_msg(). + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_dirauth_stats(const or_options_t *old_options, + bool *print_notice_out) +{ + if (BUG(!print_notice_out)) + return -1; + + const or_options_t *options = get_options(); + + if (authdir_mode_bridge(options)) { + time_t now = time(NULL); + int print_notice = 0; + + if (!old_options || !authdir_mode_bridge(old_options)) { + rep_hist_desc_stats_init(now); + print_notice = 1; + } + if (print_notice) + *print_notice_out = 1; + } + + /* If we used to have statistics enabled but we just disabled them, + stop gathering them. */ + if (old_options && authdir_mode_bridge(old_options) && + !authdir_mode_bridge(options)) + rep_hist_desc_stats_term(); + + return 0; +} diff --git a/src/feature/dirauth/dirauth_config.h b/src/feature/dirauth/dirauth_config.h new file mode 100644 index 0000000000..b494ca685e --- /dev/null +++ b/src/feature/dirauth/dirauth_config.h @@ -0,0 +1,87 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_config.h + * @brief Header for feature/dirauth/dirauth_config.c + **/ + +#ifndef TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H +#define TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H + +struct or_options_t; + +#ifdef HAVE_MODULE_DIRAUTH + +#include "lib/cc/torint.h" + +int options_validate_dirauth_mode(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_dirauth_bandwidth(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_dirauth_schedule(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_dirauth_testing(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_act_dirauth(const struct or_options_t *old_options); +int options_act_dirauth_mtbf(const struct or_options_t *old_options); +int options_act_dirauth_stats(const struct or_options_t *old_options, + bool *print_notice_out); + +#else /* !defined(HAVE_MODULE_DIRAUTH) */ + +/** When tor is compiled with the dirauth module disabled, it can't be + * configured as a directory authority. + * + * Returns -1 and sets msg to a newly allocated string, if AuthoritativeDir + * is set in options. Otherwise returns 0. */ +static inline int +options_validate_dirauth_mode(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg) +{ + (void)old_options; + + /* Only check the primary option for now, #29211 will disable more + * options. */ + if (options->AuthoritativeDir) { + /* REJECT() this configuration */ + *msg = tor_strdup("This tor was built with dirauth mode disabled. " + "It can not be configured with AuthoritativeDir 1."); + return -1; + } + + return 0; +} + +#define options_validate_dirauth_bandwidth(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_validate_dirauth_schedule(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_validate_dirauth_testing(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_validate_dirauth_testing(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) + +#define options_act_dirauth(old_options) \ + (((void)(old_options)),0) +#define options_act_dirauth_mtbf(old_options) \ + (((void)(old_options)),0) + +#define options_act_dirauth_stats(old_options, print_notice_out) \ + (((void)(old_options)),((void)(print_notice_out)),0) + +#endif /* defined(HAVE_MODULE_DIRAUTH) */ + +#endif /* !defined(TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H) */ diff --git a/src/feature/dirauth/dirauth_periodic.c b/src/feature/dirauth/dirauth_periodic.c index fc26358290..96194b4677 100644 --- a/src/feature/dirauth/dirauth_periodic.c +++ b/src/feature/dirauth/dirauth_periodic.c @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file dirauth_periodic.c + * @brief Peridoic events for directory authorities. + **/ + #include "core/or/or.h" #include "app/config/or_options_st.h" diff --git a/src/feature/dirauth/dirauth_periodic.h b/src/feature/dirauth/dirauth_periodic.h index 866fbd35de..de4a799d37 100644 --- a/src/feature/dirauth/dirauth_periodic.h +++ b/src/feature/dirauth/dirauth_periodic.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file dirauth_periodic.h + * @brief Header for dirauth_periodic.c + **/ + #ifndef DIRVOTE_PERIODIC_H #define DIRVOTE_PERIODIC_H diff --git a/src/feature/dirauth/dirauth_sys.c b/src/feature/dirauth/dirauth_sys.c index e38d391300..090e9129f2 100644 --- a/src/feature/dirauth/dirauth_sys.c +++ b/src/feature/dirauth/dirauth_sys.c @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file dirauth_sys.c + * @brief Directory authority subsystem declarations + **/ + #include "core/or/or.h" #include "feature/dirauth/bwauth.h" diff --git a/src/feature/dirauth/dirauth_sys.h b/src/feature/dirauth/dirauth_sys.h index 4e9b6a2ab4..86c8d8ba3e 100644 --- a/src/feature/dirauth/dirauth_sys.h +++ b/src/feature/dirauth/dirauth_sys.h @@ -4,9 +4,18 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file dirauth_sys.h + * @brief Header for dirauth_sys.c + **/ + #ifndef DIRAUTH_SYS_H #define DIRAUTH_SYS_H +#ifdef HAVE_MODULE_DIRAUTH + extern const struct subsys_fns_t sys_dirauth; +#endif + #endif /* !defined(DIRAUTH_SYS_H) */ diff --git a/src/feature/dirauth/dircollate.c b/src/feature/dirauth/dircollate.c index 7992e3a85f..733afbd279 100644 --- a/src/feature/dirauth/dircollate.c +++ b/src/feature/dirauth/dircollate.c @@ -32,8 +32,8 @@ static void dircollator_collate_by_ed25519(dircollator_t *dc); /** Hashtable entry mapping a pair of digests (actually an ed25519 key and an * RSA SHA1 digest) to an array of vote_routerstatus_t. */ -typedef struct ddmap_entry_s { - HT_ENTRY(ddmap_entry_s) node; +typedef struct ddmap_entry_t { + HT_ENTRY(ddmap_entry_t) node; /** A SHA1-RSA1024 identity digest and Ed25519 identity key, * concatenated. (If there is no ed25519 identity key, there is no * entry in this table.) */ @@ -89,9 +89,9 @@ ddmap_entry_set_digests(ddmap_entry_t *ent, memcpy(ent->d + DIGEST_LEN, ed25519, DIGEST256_LEN); } -HT_PROTOTYPE(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash, +HT_PROTOTYPE(double_digest_map, ddmap_entry_t, node, ddmap_entry_hash, ddmap_entry_eq) -HT_GENERATE2(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash, +HT_GENERATE2(double_digest_map, ddmap_entry_t, node, ddmap_entry_hash, ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_) /** Helper: add a single vote_routerstatus_t <b>vrs</b> to the collator diff --git a/src/feature/dirauth/dircollate.h b/src/feature/dirauth/dircollate.h index 754a094817..46ea3c3c68 100644 --- a/src/feature/dirauth/dircollate.h +++ b/src/feature/dirauth/dircollate.h @@ -15,7 +15,7 @@ #include "lib/testsupport/testsupport.h" #include "core/or/or.h" -typedef struct dircollator_s dircollator_t; +typedef struct dircollator_t dircollator_t; dircollator_t *dircollator_new(int n_votes, int n_authorities); void dircollator_free_(dircollator_t *obj); @@ -30,11 +30,11 @@ vote_routerstatus_t **dircollator_get_votes_for_router(dircollator_t *dc, int idx); #ifdef DIRCOLLATE_PRIVATE -struct ddmap_entry_s; -typedef HT_HEAD(double_digest_map, ddmap_entry_s) double_digest_map_t; +struct ddmap_entry_t; +typedef HT_HEAD(double_digest_map, ddmap_entry_t) double_digest_map_t; /** A dircollator keeps track of all the routerstatus entries in a * set of networkstatus votes, and matches them by an appropriate rule. */ -struct dircollator_s { +struct dircollator_t { /** True iff we have run the collation algorithm. */ int is_collated; /** The total number of votes that we received. */ diff --git a/src/feature/dirauth/feature_dirauth.md b/src/feature/dirauth/feature_dirauth.md new file mode 100644 index 0000000000..b152b94894 --- /dev/null +++ b/src/feature/dirauth/feature_dirauth.md @@ -0,0 +1,9 @@ +@dir /feature/dirauth +@brief feature/dirauth: Directory authority implementation. + +This module handles running Tor as a directory authority. + +The directory protocol is specified in +[dir-spec.txt](https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt). + + diff --git a/src/feature/dirauth/keypin.h b/src/feature/dirauth/keypin.h index 1de84f6d4a..d77f6fc5f3 100644 --- a/src/feature/dirauth/keypin.h +++ b/src/feature/dirauth/keypin.h @@ -1,6 +1,11 @@ /* Copyright (c) 2014-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file keypin.h + * @brief Header for keypin.c + **/ + #ifndef TOR_KEYPIN_H #define TOR_KEYPIN_H diff --git a/src/feature/dirauth/ns_detached_signatures_st.h b/src/feature/dirauth/ns_detached_signatures_st.h index 61d20b7525..7c50cda40a 100644 --- a/src/feature/dirauth/ns_detached_signatures_st.h +++ b/src/feature/dirauth/ns_detached_signatures_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file ns_detached_signatures_st.h + * @brief Detached consensus signatures structure. + **/ + #ifndef NS_DETACHED_SIGNATURES_ST_H #define NS_DETACHED_SIGNATURES_ST_H @@ -19,4 +24,3 @@ struct ns_detached_signatures_t { }; #endif /* !defined(NS_DETACHED_SIGNATURES_ST_H) */ - diff --git a/src/feature/dirauth/reachability.h b/src/feature/dirauth/reachability.h index 46d0e7ee2e..d100908de0 100644 --- a/src/feature/dirauth/reachability.h +++ b/src/feature/dirauth/reachability.h @@ -24,10 +24,10 @@ #define REACHABILITY_TEST_CYCLE_PERIOD \ (REACHABILITY_TEST_INTERVAL*REACHABILITY_MODULO_PER_TEST) +#ifdef HAVE_MODULE_DIRAUTH void dirserv_single_reachability_test(time_t now, routerinfo_t *router); void dirserv_test_reachability(time_t now); -#ifdef HAVE_MODULE_DIRAUTH int dirserv_should_launch_reachability_test(const routerinfo_t *ri, const routerinfo_t *ri_old); void dirserv_orconn_tls_done(const tor_addr_t *addr, @@ -35,25 +35,16 @@ void dirserv_orconn_tls_done(const tor_addr_t *addr, const char *digest_rcvd, const struct ed25519_public_key_t *ed_id_rcvd); #else /* !defined(HAVE_MODULE_DIRAUTH) */ -static inline int -dirserv_should_launch_reachability_test(const routerinfo_t *ri, - const routerinfo_t *ri_old) -{ - (void)ri; - (void)ri_old; - return 0; -} -static inline void -dirserv_orconn_tls_done(const tor_addr_t *addr, - uint16_t or_port, - const char *digest_rcvd, - const struct ed25519_public_key_t *ed_id_rcvd) -{ - (void)addr; - (void)or_port; - (void)digest_rcvd; - (void)ed_id_rcvd; -} +#define dirserv_single_reachability_test(now, router) \ + (((void)(now)),((void)(router))) +#define dirserv_test_reachability(now) \ + (((void)(now))) + +#define dirserv_should_launch_reachability_test(ri, ri_old) \ + (((void)(ri)),((void)(ri_old)),0) +#define dirserv_orconn_tls_done(addr, or_port, digest_rcvd, ed_id_rcvd) \ + (((void)(addr)),((void)(or_port)),((void)(digest_rcvd)), \ + ((void)(ed_id_rcvd))) #endif /* defined(HAVE_MODULE_DIRAUTH) */ #endif /* !defined(TOR_REACHABILITY_H) */ diff --git a/src/feature/dirauth/shared_random.c b/src/feature/dirauth/shared_random.c index 2b9dc0c383..ebc595e517 100644 --- a/src/feature/dirauth/shared_random.c +++ b/src/feature/dirauth/shared_random.c @@ -90,7 +90,7 @@ #include "core/or/or.h" #include "feature/dirauth/shared_random.h" #include "app/config/config.h" -#include "lib/confmgt/confparse.h" +#include "lib/confmgt/confmgt.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" #include "feature/nodelist/networkstatus.h" diff --git a/src/feature/dirauth/shared_random_state.c b/src/feature/dirauth/shared_random_state.c index e548eb4028..bf4302f168 100644 --- a/src/feature/dirauth/shared_random_state.c +++ b/src/feature/dirauth/shared_random_state.c @@ -12,7 +12,7 @@ #include "core/or/or.h" #include "app/config/config.h" -#include "lib/confmgt/confparse.h" +#include "lib/confmgt/confmgt.h" #include "lib/crypt_ops/crypto_util.h" #include "feature/dirauth/dirvote.h" #include "feature/nodelist/networkstatus.h" @@ -92,7 +92,6 @@ static const config_format_t state_format = { }, .vars = state_vars, .extra = &state_extra_var, - .config_suite_offset = -1, }; /** Global configuration manager for the shared-random state file */ diff --git a/src/feature/dirauth/shared_random_state.h b/src/feature/dirauth/shared_random_state.h index f6bcddd088..74792967a7 100644 --- a/src/feature/dirauth/shared_random_state.h +++ b/src/feature/dirauth/shared_random_state.h @@ -1,6 +1,11 @@ /* Copyright (c) 2016-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file shared_random_state.h + * @brief Header for shared_random_state.c + **/ + #ifndef TOR_SHARED_RANDOM_STATE_H #define TOR_SHARED_RANDOM_STATE_H diff --git a/src/feature/dirauth/vote_microdesc_hash_st.h b/src/feature/dirauth/vote_microdesc_hash_st.h index 7869f92b4f..24c7443b36 100644 --- a/src/feature/dirauth/vote_microdesc_hash_st.h +++ b/src/feature/dirauth/vote_microdesc_hash_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file vote_microdesc_hash_st.h + * @brief Microdescriptor-hash voting strcture. + **/ + #ifndef VOTE_MICRODESC_HASH_ST_H #define VOTE_MICRODESC_HASH_ST_H @@ -19,4 +24,3 @@ struct vote_microdesc_hash_t { }; #endif /* !defined(VOTE_MICRODESC_HASH_ST_H) */ - diff --git a/src/feature/dircache/cached_dir_st.h b/src/feature/dircache/cached_dir_st.h index a28802f905..e086f5b11d 100644 --- a/src/feature/dircache/cached_dir_st.h +++ b/src/feature/dircache/cached_dir_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file cached_dir_st.h + * @brief Cached large directory object structure. + **/ + #ifndef CACHED_DIR_ST_H #define CACHED_DIR_ST_H @@ -22,4 +27,3 @@ struct cached_dir_t { }; #endif /* !defined(CACHED_DIR_ST_H) */ - diff --git a/src/feature/dircache/conscache.c b/src/feature/dircache/conscache.c index 2ec9981c03..dde5f35df0 100644 --- a/src/feature/dircache/conscache.c +++ b/src/feature/dircache/conscache.c @@ -1,6 +1,11 @@ /* Copyright (c) 2017-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file conscache.c + * @brief Consensus and diff on-disk cache. + **/ + #include "core/or/or.h" #include "app/config/config.h" @@ -133,7 +138,7 @@ consensus_cache_may_overallocate(consensus_cache_t *cache) */ int consensus_cache_register_with_sandbox(consensus_cache_t *cache, - struct sandbox_cfg_elem **cfg) + struct sandbox_cfg_elem_t **cfg) { #ifdef MUST_UNMAP_TO_UNLINK /* Our Linux sandbox doesn't support huge file lists like the one that would @@ -246,7 +251,7 @@ consensus_cache_find_first(consensus_cache_t *cache, } /** - * Given a <b>cache</b>, add every entry to <b>out<b> for which + * Given a <b>cache</b>, add every entry to <b>out</b> for which * <b>key</b>=<b>value</b>. If <b>key</b> is NULL, add every entry. * * Do not add any entry that has been marked for removal. diff --git a/src/feature/dircache/conscache.h b/src/feature/dircache/conscache.h index d848e57617..5e0489f3eb 100644 --- a/src/feature/dircache/conscache.h +++ b/src/feature/dircache/conscache.h @@ -1,6 +1,11 @@ /* Copyright (c) 2017-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file conscache.h + * @brief Header for conscache.c + **/ + #ifndef TOR_CONSCACHE_H #define TOR_CONSCACHE_H @@ -18,10 +23,10 @@ consensus_cache_t *consensus_cache_open(const char *subdir, int max_entries); void consensus_cache_free_(consensus_cache_t *cache); #define consensus_cache_free(cache) \ FREE_AND_NULL(consensus_cache_t, consensus_cache_free_, (cache)) -struct sandbox_cfg_elem; +struct sandbox_cfg_elem_t; int consensus_cache_may_overallocate(consensus_cache_t *cache); int consensus_cache_register_with_sandbox(consensus_cache_t *cache, - struct sandbox_cfg_elem **cfg); + struct sandbox_cfg_elem_t **cfg); void consensus_cache_unmap_lazy(consensus_cache_t *cache, time_t cutoff); void consensus_cache_delete_pending(consensus_cache_t *cache, int force); diff --git a/src/feature/dircache/consdiffmgr.c b/src/feature/dircache/consdiffmgr.c index 058ff1f500..556376b020 100644 --- a/src/feature/dircache/consdiffmgr.c +++ b/src/feature/dircache/consdiffmgr.c @@ -844,7 +844,7 @@ consdiffmgr_configure(const consdiff_cfg_t *cfg) * operations that the consensus diff manager will need. */ int -consdiffmgr_register_with_sandbox(struct sandbox_cfg_elem **cfg) +consdiffmgr_register_with_sandbox(struct sandbox_cfg_elem_t **cfg) { return consensus_cache_register_with_sandbox(cdm_cache_get(), cfg); } diff --git a/src/feature/dircache/consdiffmgr.h b/src/feature/dircache/consdiffmgr.h index b1b3323b6c..f72dd5b282 100644 --- a/src/feature/dircache/consdiffmgr.h +++ b/src/feature/dircache/consdiffmgr.h @@ -1,6 +1,11 @@ /* Copyright (c) 2017-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file consdiffmgr.h + * @brief Header for consdiffmgr.c + **/ + #ifndef TOR_CONSDIFFMGR_H #define TOR_CONSDIFFMGR_H @@ -55,8 +60,8 @@ void consdiffmgr_rescan(void); int consdiffmgr_cleanup(void); void consdiffmgr_enable_background_compression(void); void consdiffmgr_configure(const consdiff_cfg_t *cfg); -struct sandbox_cfg_elem; -int consdiffmgr_register_with_sandbox(struct sandbox_cfg_elem **cfg); +struct sandbox_cfg_elem_t; +int consdiffmgr_register_with_sandbox(struct sandbox_cfg_elem_t **cfg); void consdiffmgr_free_all(void); int consdiffmgr_validate(void); diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index d4d0ad9939..940dc3d14a 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -3,6 +3,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file dircache.c + * @brief Cache directories and serve them to clients. + **/ + #define DIRCACHE_PRIVATE #include "core/or/or.h" @@ -23,6 +28,7 @@ #include "feature/nodelist/authcert.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/routerlist.h" +#include "feature/relay/relay_config.h" #include "feature/relay/routermode.h" #include "feature/rend/rendcache.h" #include "feature/stats/geoip_stats.h" @@ -328,7 +334,7 @@ typedef struct get_handler_args_t { * an arguments structure, and must return 0 on success or -1 if we should * close the connection. **/ -typedef struct url_table_ent_s { +typedef struct url_table_ent_t { const char *string; int is_prefix; int (*handler)(dir_connection_t *conn, const get_handler_args_t *args); @@ -473,7 +479,7 @@ static int handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args) { (void) args; /* unused */ - const char *frontpage = get_dirportfrontpage(); + const char *frontpage = relay_get_dirportfrontpage(); if (frontpage) { size_t dlen; @@ -560,7 +566,7 @@ parse_one_diff_hash(uint8_t *digest, const char *hex, const char *location, } /** If there is an X-Or-Diff-From-Consensus header included in <b>headers</b>, - * set <b>digest_out<b> to a new smartlist containing every 256-bit + * set <b>digest_out</b> to a new smartlist containing every 256-bit * hex-encoded digest listed in that header and return 0. Otherwise return * -1. */ static int @@ -1379,7 +1385,7 @@ handle_get_hs_descriptor_v2(dir_connection_t *conn, return 0; } -/** Helper function for GET /tor/hs/3/<z>. Only for version 3. +/** Helper function for GET `/tor/hs/3/...`. Only for version 3. */ STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn, diff --git a/src/feature/dircache/feature_dircache.md b/src/feature/dircache/feature_dircache.md new file mode 100644 index 0000000000..97734f2a34 --- /dev/null +++ b/src/feature/dircache/feature_dircache.md @@ -0,0 +1,6 @@ +@dir /feature/dircache +@brief feature/dircache: Run as a directory cache server + +This module handles the directory caching functionality that all relays may +provide, for serving cached directory objects to objects. + diff --git a/src/feature/dirclient/dir_server_st.h b/src/feature/dirclient/dir_server_st.h index 8e35532435..69c3856185 100644 --- a/src/feature/dirclient/dir_server_st.h +++ b/src/feature/dirclient/dir_server_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file dir_server_st.h + * @brief Trusted/fallback directory server structure. + **/ + #ifndef DIR_SERVER_ST_H #define DIR_SERVER_ST_H diff --git a/src/feature/dirclient/dirclient.c b/src/feature/dirclient/dirclient.c index 1ea50fd350..8c1130f651 100644 --- a/src/feature/dirclient/dirclient.c +++ b/src/feature/dirclient/dirclient.c @@ -3,6 +3,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file dirclient.c + * @brief Download directory information + **/ + #define DIRCLIENT_PRIVATE #include "core/or/or.h" @@ -2728,62 +2733,7 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, log_info(LD_REND,"Received v3 hsdesc (body size %d, status %d (%s))", (int)body_len, status_code, escaped(reason)); - switch (status_code) { - case 200: - /* We got something: Try storing it in the cache. */ - if (hs_cache_store_as_client(body, &conn->hs_ident->identity_pk) < 0) { - log_info(LD_REND, "Failed to store hidden service descriptor"); - /* Fire control port FAILED event. */ - hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, - "BAD_DESC"); - hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, - NULL); - } else { - log_info(LD_REND, "Stored hidden service descriptor successfully."); - TO_CONN(conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC; - hs_client_desc_has_arrived(conn->hs_ident); - /* Fire control port RECEIVED event. */ - hs_control_desc_event_received(conn->hs_ident, conn->identity_digest); - hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, - body); - } - break; - case 404: - /* Not there. We'll retry when connection_about_to_close_connection() - * tries to clean this conn up. */ - log_info(LD_REND, "Fetching hidden service v3 descriptor not found: " - "Retrying at another directory."); - /* Fire control port FAILED event. */ - hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, - "NOT_FOUND"); - hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, - NULL); - break; - case 400: - log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " - "http status 400 (%s). Dirserver didn't like our " - "query? Retrying at another directory.", - escaped(reason)); - /* Fire control port FAILED event. */ - hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, - "QUERY_REJECTED"); - hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, - NULL); - break; - default: - log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " - "http status %d (%s) response unexpected from HSDir server " - "'%s:%d'. Retrying at another directory.", - status_code, escaped(reason), TO_CONN(conn)->address, - TO_CONN(conn)->port); - /* Fire control port FAILED event. */ - hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, - "UNEXPECTED"); - hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, - NULL); - break; - } - + hs_client_dir_fetch_done(conn, reason, body, status_code); return 0; } diff --git a/src/feature/dirclient/dlstatus.c b/src/feature/dirclient/dlstatus.c index 0842a2c676..c6fdbebed9 100644 --- a/src/feature/dirclient/dlstatus.c +++ b/src/feature/dirclient/dlstatus.c @@ -3,6 +3,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file dlstatus.c + * @brief Track status and retry schedule of a downloadable object. + **/ + #define DLSTATUS_PRIVATE #include "core/or/or.h" diff --git a/src/feature/dirclient/download_status_st.h b/src/feature/dirclient/download_status_st.h index 39a5ad2860..29d0dd5d5f 100644 --- a/src/feature/dirclient/download_status_st.h +++ b/src/feature/dirclient/download_status_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file download_status_st.h + * @brief Directory download status/schedule structure. + **/ + #ifndef DOWNLOAD_STATUS_ST_H #define DOWNLOAD_STATUS_ST_H @@ -62,4 +67,3 @@ struct download_status_t { }; #endif /* !defined(DOWNLOAD_STATUS_ST_H) */ - diff --git a/src/feature/dirclient/feature_dirclient.md b/src/feature/dirclient/feature_dirclient.md new file mode 100644 index 0000000000..5c7ee964d3 --- /dev/null +++ b/src/feature/dirclient/feature_dirclient.md @@ -0,0 +1,7 @@ +@dir /feature/dirclient +@brief feature/dirclient: Directory client implementation. + +The code here is used by all Tor instances that need to download directory +information. Currently, that is all of them, since even authorities need to +launch downloads to learn about relays that other authorities have listed. + diff --git a/src/feature/dircommon/consdiff.c b/src/feature/dircommon/consdiff.c index fbfa9e0c0a..dd8e986b51 100644 --- a/src/feature/dircommon/consdiff.c +++ b/src/feature/dircommon/consdiff.c @@ -1050,7 +1050,7 @@ consdiff_gen_diff(const smartlist_t *cons1, if (smartlist_len(cons2) == smartlist_len(ed_cons2)) { SMARTLIST_FOREACH_BEGIN(cons2, const cdline_t *, line1) { const cdline_t *line2 = smartlist_get(ed_cons2, line1_sl_idx); - if (! lines_eq(line1, line2) ) { + if (!lines_eq(line1, line2)) { cons2_eq = 0; break; } diff --git a/src/feature/dircommon/consdiff.h b/src/feature/dircommon/consdiff.h index b63fcb2cc6..0e8c4b4d8e 100644 --- a/src/feature/dircommon/consdiff.h +++ b/src/feature/dircommon/consdiff.h @@ -2,6 +2,11 @@ * Copyright (c) 2014-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file consdiff.h + * @brief Header for consdiff.c + **/ + #ifndef TOR_CONSDIFF_H #define TOR_CONSDIFF_H diff --git a/src/feature/dircommon/dir_connection_st.h b/src/feature/dircommon/dir_connection_st.h index a858560c29..ba978e142a 100644 --- a/src/feature/dircommon/dir_connection_st.h +++ b/src/feature/dircommon/dir_connection_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file dir_connection_st.h + * @brief Client/server directory connection structure. + **/ + #ifndef DIR_CONNECTION_ST_H #define DIR_CONNECTION_ST_H diff --git a/src/feature/dircommon/feature_dircommon.md b/src/feature/dircommon/feature_dircommon.md new file mode 100644 index 0000000000..359049ecd8 --- /dev/null +++ b/src/feature/dircommon/feature_dircommon.md @@ -0,0 +1,7 @@ +@dir /feature/dircommon +@brief feature/dircommon: Directory client and server shared code + +This module has the code that directory clients (anybody who download +information about relays) and directory servers (anybody who serves such +information) share in common. + diff --git a/src/feature/dircommon/fp_pair.c b/src/feature/dircommon/fp_pair.c index 284600df77..2a21ff85ad 100644 --- a/src/feature/dircommon/fp_pair.c +++ b/src/feature/dircommon/fp_pair.c @@ -22,14 +22,14 @@ /* Define fp_pair_map_t structures */ -struct fp_pair_map_entry_s { - HT_ENTRY(fp_pair_map_entry_s) node; +struct fp_pair_map_entry_t { + HT_ENTRY(fp_pair_map_entry_t) node; void *val; fp_pair_t key; }; -struct fp_pair_map_s { - HT_HEAD(fp_pair_map_impl, fp_pair_map_entry_s) head; +struct fp_pair_map_t { + HT_HEAD(fp_pair_map_impl, fp_pair_map_entry_t) head; }; /* @@ -56,9 +56,9 @@ fp_pair_map_entry_hash(const fp_pair_map_entry_t *a) * Hash table functions for fp_pair_map_t */ -HT_PROTOTYPE(fp_pair_map_impl, fp_pair_map_entry_s, node, +HT_PROTOTYPE(fp_pair_map_impl, fp_pair_map_entry_t, node, fp_pair_map_entry_hash, fp_pair_map_entries_eq) -HT_GENERATE2(fp_pair_map_impl, fp_pair_map_entry_s, node, +HT_GENERATE2(fp_pair_map_impl, fp_pair_map_entry_t, node, fp_pair_map_entry_hash, fp_pair_map_entries_eq, 0.6, tor_reallocarray_, tor_free_) diff --git a/src/feature/dircommon/fp_pair.h b/src/feature/dircommon/fp_pair.h index 5041583e88..9f06a8c86b 100644 --- a/src/feature/dircommon/fp_pair.h +++ b/src/feature/dircommon/fp_pair.h @@ -19,8 +19,8 @@ typedef struct { * Declare fp_pair_map_t functions and structs */ -typedef struct fp_pair_map_entry_s fp_pair_map_entry_t; -typedef struct fp_pair_map_s fp_pair_map_t; +typedef struct fp_pair_map_entry_t fp_pair_map_entry_t; +typedef struct fp_pair_map_t fp_pair_map_t; typedef fp_pair_map_entry_t *fp_pair_map_iter_t; fp_pair_map_t * fp_pair_map_new(void); diff --git a/src/feature/dircommon/vote_timing_st.h b/src/feature/dircommon/vote_timing_st.h index 814a325314..352a69b2dd 100644 --- a/src/feature/dircommon/vote_timing_st.h +++ b/src/feature/dircommon/vote_timing_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file vote_timing_st.h + * @brief Directory voting schedule structure. + **/ + #ifndef VOTE_TIMING_ST_H #define VOTE_TIMING_ST_H @@ -21,4 +26,3 @@ struct vote_timing_t { }; #endif /* !defined(VOTE_TIMING_ST_H) */ - diff --git a/src/feature/dirparse/authcert_parse.c b/src/feature/dirparse/authcert_parse.c index d22293e281..b18e1159f3 100644 --- a/src/feature/dirparse/authcert_parse.c +++ b/src/feature/dirparse/authcert_parse.c @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file authcert_parse.c + * @brief Authority certificate parsing. + **/ + #include "core/or/or.h" #include "feature/dirparse/authcert_parse.h" #include "feature/dirparse/parsecommon.h" diff --git a/src/feature/dirparse/feature_dirparse.md b/src/feature/dirparse/feature_dirparse.md new file mode 100644 index 0000000000..e4b34668ba --- /dev/null +++ b/src/feature/dirparse/feature_dirparse.md @@ -0,0 +1,8 @@ +@dir /feature/dirparse +@brief feature/dirparse: Parsing Tor directory objects + +We define a number of "directory objects" in +[dir-spec.txt](https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt), +all of them using a common line-oriented meta-format. This module is used by +other parts of Tor to parse them. + diff --git a/src/feature/dirparse/unparseable.c b/src/feature/dirparse/unparseable.c index 941b5a1f6d..3b96df9e30 100644 --- a/src/feature/dirparse/unparseable.c +++ b/src/feature/dirparse/unparseable.c @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file unparseable.c + * @brief Dump unparseable objects to disk. + **/ + #define UNPARSEABLE_PRIVATE #include "core/or/or.h" diff --git a/src/feature/feature.md b/src/feature/feature.md new file mode 100644 index 0000000000..acc3487e55 --- /dev/null +++ b/src/feature/feature.md @@ -0,0 +1,7 @@ +@dir /feature +@brief feature: domain-specific modules + +The "feature" directory has modules that Tor uses only for a particular +role or service, such as maintaining/using an onion service, operating as a +relay or a client, or being a directory authority. + diff --git a/src/feature/hibernate/feature_hibernate.md b/src/feature/hibernate/feature_hibernate.md new file mode 100644 index 0000000000..0eb5ffea0d --- /dev/null +++ b/src/feature/hibernate/feature_hibernate.md @@ -0,0 +1,14 @@ +@dir /feature/hibernate +@brief feature/hibernate: Bandwidth accounting and hibernation (!) + +This module implements two features that are only somewhat related, and +should probably be separated in the future. One feature is bandwidth +accounting (making sure we use no more than so many gigabytes in a day) and +hibernation (avoiding network activity while we have used up all/most of our +configured gigabytes). The other feature is clean shutdown, where we stop +accepting new connections for a while and give the old ones time to close. + +The two features are related only in the sense that "soft hibernation" (being +almost out of ) is very close to the "shutting down" state. But it would be +better in the long run to make the two completely separate. + diff --git a/src/feature/hs/feature_hs.md b/src/feature/hs/feature_hs.md new file mode 100644 index 0000000000..299d07e014 --- /dev/null +++ b/src/feature/hs/feature_hs.md @@ -0,0 +1,8 @@ +@dir /feature/hs +@brief feature/hs: v3 (current) onion service protocol + +This directory implements the v3 onion service protocol, +as specified in +[rend-spec-v3.txt](https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt). + + diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c index 395839fce0..9cbef2fa41 100644 --- a/src/feature/hs/hs_cache.c +++ b/src/feature/hs/hs_cache.c @@ -395,8 +395,10 @@ lookup_v3_desc_as_client(const uint8_t *key) * hs_cache_client_descriptor_t object. In case of error, return NULL. */ static hs_cache_client_descriptor_t * cache_client_desc_new(const char *desc_str, - const ed25519_public_key_t *service_identity_pk) + const ed25519_public_key_t *service_identity_pk, + hs_desc_decode_status_t *decode_status_out) { + hs_desc_decode_status_t ret; hs_descriptor_t *desc = NULL; hs_cache_client_descriptor_t *client_desc = NULL; @@ -404,10 +406,24 @@ cache_client_desc_new(const char *desc_str, tor_assert(service_identity_pk); /* Decode the descriptor we just fetched. */ - if (hs_client_decode_descriptor(desc_str, service_identity_pk, &desc) < 0) { + ret = hs_client_decode_descriptor(desc_str, service_identity_pk, &desc); + if (ret != HS_DESC_DECODE_OK && + ret != HS_DESC_DECODE_NEED_CLIENT_AUTH && + ret != HS_DESC_DECODE_BAD_CLIENT_AUTH) { + /* In the case of a missing or bad client authorization, we'll keep the + * descriptor in the cache because those credentials can arrive later. */ goto end; } - tor_assert(desc); + /* Make sure we do have a descriptor if decoding was successful. */ + if (ret == HS_DESC_DECODE_OK) { + tor_assert(desc); + } else { + if (BUG(desc != NULL)) { + /* We are not suppose to have a descriptor if the decoding code is not + * indicating success. Just in case, bail early to recover. */ + goto end; + } + } /* All is good: make a cache object for this descriptor */ client_desc = tor_malloc_zero(sizeof(hs_cache_client_descriptor_t)); @@ -420,6 +436,9 @@ cache_client_desc_new(const char *desc_str, client_desc->encoded_desc = tor_strdup(desc_str); end: + if (decode_status_out) { + *decode_status_out = ret; + } return client_desc; } @@ -635,9 +654,19 @@ cache_store_as_client(hs_cache_client_descriptor_t *client_desc) tor_assert(client_desc); /* Check if we already have a descriptor from this HS in cache. If we do, - * check if this descriptor is newer than the cached one */ + * check if this descriptor is newer than the cached one only if we have a + * decoded descriptor. We do keep non-decoded descriptor that requires + * client authorization. */ cache_entry = lookup_v3_desc_as_client(client_desc->key.pubkey); if (cache_entry != NULL) { + /* Signalling an undecrypted descriptor. We'll always replace the one we + * have with the new one just fetched. */ + if (cache_entry->desc == NULL) { + remove_v3_desc_as_client(cache_entry); + cache_client_desc_free(cache_entry); + goto store; + } + /* If we have an entry in our cache that has a revision counter greater * than the one we just fetched, discard the one we fetched. */ if (cache_entry->desc->plaintext_data.revision_counter > @@ -657,6 +686,7 @@ cache_store_as_client(hs_cache_client_descriptor_t *client_desc) cache_client_desc_free(cache_entry); } + store: /* Store descriptor in cache */ store_v3_desc_as_client(client_desc); @@ -752,7 +782,9 @@ hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key) } /** Public API: Given the HS ed25519 identity public key in <b>key</b>, return - * its HS descriptor if it's stored in our cache, or NULL if not. */ + * its HS descriptor if it's stored in our cache, or NULL if not or if the + * descriptor was never decrypted. The later can happen if we are waiting for + * client authorization to be added. */ const hs_descriptor_t * hs_cache_lookup_as_client(const ed25519_public_key_t *key) { @@ -761,27 +793,41 @@ hs_cache_lookup_as_client(const ed25519_public_key_t *key) tor_assert(key); cached_desc = lookup_v3_desc_as_client(key->pubkey); - if (cached_desc) { - tor_assert(cached_desc->desc); + if (cached_desc && cached_desc->desc) { return cached_desc->desc; } return NULL; } -/** Public API: Given an encoded descriptor, store it in the client HS - * cache. Return -1 on error, 0 on success .*/ -int +/** Public API: Given an encoded descriptor, store it in the client HS cache. + * Return a decode status which changes how we handle the SOCKS connection + * depending on its value: + * + * HS_DESC_DECODE_OK: Returned on success. Descriptor was properly decoded + * and is now stored. + * + * HS_DESC_DECODE_NEED_CLIENT_AUTH: Client authorization is needed but the + * descriptor was still stored. + * + * HS_DESC_DECODE_BAD_CLIENT_AUTH: Client authorization for this descriptor + * was not usable but the descriptor was + * still stored. + * + * Any other codes means indicate where the error occured and the descriptor + * was not stored. */ +hs_desc_decode_status_t hs_cache_store_as_client(const char *desc_str, const ed25519_public_key_t *identity_pk) { + hs_desc_decode_status_t ret; hs_cache_client_descriptor_t *client_desc = NULL; tor_assert(desc_str); tor_assert(identity_pk); /* Create client cache descriptor object */ - client_desc = cache_client_desc_new(desc_str, identity_pk); + client_desc = cache_client_desc_new(desc_str, identity_pk, &ret); if (!client_desc) { log_warn(LD_GENERAL, "HSDesc parsing failed!"); log_debug(LD_GENERAL, "Failed to parse HSDesc: %s.", escaped(desc_str)); @@ -790,14 +836,15 @@ hs_cache_store_as_client(const char *desc_str, /* Push it to the cache */ if (cache_store_as_client(client_desc) < 0) { + ret = HS_DESC_DECODE_GENERIC_ERROR; goto err; } - return 0; + return ret; err: cache_client_desc_free(client_desc); - return -1; + return ret; } /** Clean all client caches using the current time now. */ @@ -895,6 +942,38 @@ hs_cache_client_intro_state_purge(void) "cache purged."); } +/* This is called when new client authorization was added to the global state. + * It attemps to decode the descriptor of the given service identity key. + * + * Return true if decoding was successful else false. */ +bool +hs_cache_client_new_auth_parse(const ed25519_public_key_t *service_pk) +{ + bool ret = false; + hs_cache_client_descriptor_t *cached_desc = NULL; + + tor_assert(service_pk); + + if (!hs_cache_v3_client) { + return false; + } + + cached_desc = lookup_v3_desc_as_client(service_pk->pubkey); + if (cached_desc == NULL || cached_desc->desc != NULL) { + /* No entry for that service or the descriptor is already decoded. */ + goto end; + } + + /* Attempt a decode. If we are successful, inform the caller. */ + if (hs_client_decode_descriptor(cached_desc->encoded_desc, service_pk, + &cached_desc->desc) == HS_DESC_DECODE_OK) { + ret = true; + } + + end: + return ret; +} + /**************** Generics *********************************/ /** Do a round of OOM cleanup on all directory caches. Return the amount of diff --git a/src/feature/hs/hs_cache.h b/src/feature/hs/hs_cache.h index 5df7e54fc0..a56e2cc6c6 100644 --- a/src/feature/hs/hs_cache.h +++ b/src/feature/hs/hs_cache.h @@ -83,8 +83,8 @@ const hs_descriptor_t * hs_cache_lookup_as_client(const struct ed25519_public_key_t *key); const char * hs_cache_lookup_encoded_as_client(const struct ed25519_public_key_t *key); -int hs_cache_store_as_client(const char *desc_str, - const struct ed25519_public_key_t *identity_pk); +hs_desc_decode_status_t hs_cache_store_as_client(const char *desc_str, + const struct ed25519_public_key_t *identity_pk); void hs_cache_clean_as_client(time_t now); void hs_cache_purge_as_client(void); @@ -99,6 +99,8 @@ const hs_cache_intro_state_t *hs_cache_client_intro_state_find( void hs_cache_client_intro_state_clean(time_t now); void hs_cache_client_intro_state_purge(void); +bool hs_cache_client_new_auth_parse(const ed25519_public_key_t *service_pk); + #ifdef HS_CACHE_PRIVATE #include "lib/crypt_ops/crypto_ed25519.h" @@ -112,8 +114,10 @@ typedef struct hs_cache_client_descriptor_t { * using the next blinded key of the service. */ time_t expiration_ts; - /** The cached descriptor, this object is the owner. It can't be NULL. A - * cache object without a valid descriptor is not possible. */ + /** The cached decoded descriptor, this object is the owner. This can be + * NULL if the descriptor couldn't be decoded due to missing or bad client + * authorization. It can be decoded later from the encoded_desc object if + * the proper client authorization is given tor. */ hs_descriptor_t *desc; /** Encoded descriptor in string form. Can't be NULL. */ diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c index 5e213b5aba..9d21acf42f 100644 --- a/src/feature/hs/hs_circuit.c +++ b/src/feature/hs/hs_circuit.c @@ -40,7 +40,7 @@ #include "feature/nodelist/node_st.h" #include "core/or/origin_circuit_st.h" -/* A circuit is about to become an e2e rendezvous circuit. Check +/** A circuit is about to become an e2e rendezvous circuit. Check * <b>circ_purpose</b> and ensure that it's properly set. Return true iff * circuit purpose is properly set, otherwise return false. */ static int @@ -67,7 +67,7 @@ circuit_purpose_is_correct_for_rend(unsigned int circ_purpose, return 1; } -/* Create and return a crypt path for the final hop of a v3 prop224 rendezvous +/** Create and return a crypt path for the final hop of a v3 prop224 rendezvous * circuit. Initialize the crypt path crypto using the output material from the * ntor key exchange at <b>ntor_key_seed</b>. * @@ -101,7 +101,7 @@ create_rend_cpath(const uint8_t *ntor_key_seed, size_t seed_len, return cpath; } -/* We are a v2 legacy HS client: Create and return a crypt path for the hidden +/** We are a v2 legacy HS client: Create and return a crypt path for the hidden * service on the other side of the rendezvous circuit <b>circ</b>. Initialize * the crypt path crypto using the body of the RENDEZVOUS1 cell at * <b>rend_cell_body</b> (which must be at least DH1024_KEY_LEN+DIGEST_LEN @@ -152,7 +152,7 @@ create_rend_cpath_legacy(origin_circuit_t *circ, const uint8_t *rend_cell_body) return hop; } -/* Append the final <b>hop</b> to the cpath of the rend <b>circ</b>, and mark +/** Append the final <b>hop</b> to the cpath of the rend <b>circ</b>, and mark * <b>circ</b> ready for use to transfer HS relay cells. */ static void finalize_rend_circuit(origin_circuit_t *circ, crypt_path_t *hop, @@ -193,7 +193,7 @@ finalize_rend_circuit(origin_circuit_t *circ, crypt_path_t *hop, } } -/* For a given circuit and a service introduction point object, register the +/** For a given circuit and a service introduction point object, register the * intro circuit to the circuitmap. This supports legacy intro point. */ static void register_intro_circ(const hs_service_intro_point_t *ip, @@ -211,7 +211,7 @@ register_intro_circ(const hs_service_intro_point_t *ip, } } -/* Return the number of opened introduction circuit for the given circuit that +/** Return the number of opened introduction circuit for the given circuit that * is matching its identity key. */ static unsigned int count_opened_desc_intro_point_circuits(const hs_service_t *service, @@ -243,7 +243,7 @@ count_opened_desc_intro_point_circuits(const hs_service_t *service, return count; } -/* From a given service, rendezvous cookie and handshake info, create a +/** From a given service, rendezvous cookie and handshake info, create a * rendezvous point circuit identifier. This can't fail. */ STATIC hs_ident_circuit_t * create_rp_circuit_identifier(const hs_service_t *service, @@ -282,7 +282,7 @@ create_rp_circuit_identifier(const hs_service_t *service, return ident; } -/* From a given service and service intro point, create an introduction point +/** From a given service and service intro point, create an introduction point * circuit identifier. This can't fail. */ static hs_ident_circuit_t * create_intro_circuit_identifier(const hs_service_t *service, @@ -299,7 +299,7 @@ create_intro_circuit_identifier(const hs_service_t *service, return ident; } -/* For a given introduction point and an introduction circuit, send the +/** For a given introduction point and an introduction circuit, send the * ESTABLISH_INTRO cell. The service object is used for logging. This can fail * and if so, the circuit is closed and the intro point object is flagged * that the circuit is not established anymore which is important for the @@ -349,7 +349,7 @@ send_establish_intro(const hs_service_t *service, memwipe(payload, 0, sizeof(payload)); } -/* Return a string constant describing the anonymity of service. */ +/** Return a string constant describing the anonymity of service. */ static const char * get_service_anonymity_string(const hs_service_t *service) { @@ -360,7 +360,7 @@ get_service_anonymity_string(const hs_service_t *service) } } -/* For a given service, the ntor onion key and a rendezvous cookie, launch a +/** For a given service, the ntor onion key and a rendezvous cookie, launch a * circuit to the rendezvous point specified by the link specifiers. On * success, a circuit identifier is attached to the circuit with the needed * data. This function will try to open a circuit for a maximum value of @@ -469,7 +469,7 @@ launch_rendezvous_point_circuit(const hs_service_t *service, extend_info_free(info); } -/* Return true iff the given service rendezvous circuit circ is allowed for a +/** Return true iff the given service rendezvous circuit circ is allowed for a * relaunch to the rendezvous point. */ static int can_relaunch_service_rendezvous_point(const origin_circuit_t *circ) @@ -516,7 +516,7 @@ can_relaunch_service_rendezvous_point(const origin_circuit_t *circ) return 0; } -/* Retry the rendezvous point of circ by launching a new circuit to it. */ +/** Retry the rendezvous point of circ by launching a new circuit to it. */ static void retry_service_rendezvous_point(const origin_circuit_t *circ) { @@ -565,7 +565,7 @@ retry_service_rendezvous_point(const origin_circuit_t *circ) return; } -/* Using the given descriptor intro point ip, the node of the +/** Using the given descriptor intro point ip, the node of the * rendezvous point rp_node and the service's subcredential, populate the * already allocated intro1_data object with the needed key material and link * specifiers. @@ -622,7 +622,7 @@ setup_introduce1_data(const hs_desc_intro_point_t *ip, /* Public API */ /* ========== */ -/* Return an introduction point circuit matching the given intro point object. +/** Return an introduction point circuit matching the given intro point object. * NULL is returned is no such circuit can be found. */ origin_circuit_t * hs_circ_service_get_intro_circ(const hs_service_intro_point_t *ip) @@ -637,7 +637,28 @@ hs_circ_service_get_intro_circ(const hs_service_intro_point_t *ip) } } -/* Called when we fail building a rendezvous circuit at some point other than +/** Return an introduction point established circuit matching the given intro + * point object. The circuit purpose has to be CIRCUIT_PURPOSE_S_INTRO. NULL + * is returned is no such circuit can be found. */ +origin_circuit_t * +hs_circ_service_get_established_intro_circ(const hs_service_intro_point_t *ip) +{ + origin_circuit_t *circ; + + tor_assert(ip); + + if (ip->base.is_only_legacy) { + circ = hs_circuitmap_get_intro_circ_v2_service_side(ip->legacy_key_digest); + } else { + circ = hs_circuitmap_get_intro_circ_v3_service_side( + &ip->auth_key_kp.pubkey); + } + + /* Only return circuit if it is established. */ + return (TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO) ? circ : NULL; +} + +/** Called when we fail building a rendezvous circuit at some point other than * the last hop: launches a new circuit to the same rendezvous point. This * supports legacy service. * @@ -677,7 +698,7 @@ hs_circ_retry_service_rendezvous_point(origin_circuit_t *circ) return; } -/* For a given service and a service intro point, launch a circuit to the +/** For a given service and a service intro point, launch a circuit to the * extend info ei. If the service is a single onion, and direct_conn is true, * a one-hop circuit will be requested. * @@ -738,7 +759,7 @@ hs_circ_launch_intro_point(hs_service_t *service, return ret; } -/* Called when a service introduction point circuit is done building. Given +/** Called when a service introduction point circuit is done building. Given * the service and intro point object, this function will send the * ESTABLISH_INTRO cell on the circuit. Return 0 on success. Return 1 if the * circuit has been repurposed to General because we already have too many @@ -807,7 +828,7 @@ hs_circ_service_intro_has_opened(hs_service_t *service, return ret; } -/* Called when a service rendezvous point circuit is done building. Given the +/** Called when a service rendezvous point circuit is done building. Given the * service and the circuit, this function will send a RENDEZVOUS1 cell on the * circuit using the information in the circuit identifier. If the cell can't * be sent, the circuit is closed. */ @@ -873,7 +894,7 @@ hs_circ_service_rp_has_opened(const hs_service_t *service, memwipe(payload, 0, sizeof(payload)); } -/* Circ has been expecting an INTRO_ESTABLISHED cell that just arrived. Handle +/** Circ has been expecting an INTRO_ESTABLISHED cell that just arrived. Handle * the INTRO_ESTABLISHED cell payload of length payload_len arriving on the * given introduction circuit circ. The service is only used for logging * purposes. Return 0 on success else a negative value. */ @@ -918,7 +939,7 @@ hs_circ_handle_intro_established(const hs_service_t *service, return ret; } -/* We just received an INTRODUCE2 cell on the established introduction circuit +/** We just received an INTRODUCE2 cell on the established introduction circuit * circ. Handle the INTRODUCE2 payload of size payload_len for the given * circuit and service. This cell is associated with the intro point object ip * and the subcredential. Return 0 on success else a negative value. */ @@ -985,7 +1006,7 @@ hs_circ_handle_introduce2(const hs_service_t *service, return ret; } -/* Circuit <b>circ</b> just finished the rend ntor key exchange. Use the key +/** Circuit <b>circ</b> just finished the rend ntor key exchange. Use the key * exchange output material at <b>ntor_key_seed</b> and setup <b>circ</b> to * serve as a rendezvous end-to-end circuit between the client and the * service. If <b>is_service_side</b> is set, then we are the hidden service @@ -1015,7 +1036,7 @@ hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ, return 0; } -/* We are a v2 legacy HS client and we just received a RENDEZVOUS1 cell +/** We are a v2 legacy HS client and we just received a RENDEZVOUS1 cell * <b>rend_cell_body</b> on <b>circ</b>. Finish up the DH key exchange and then * extend the crypt path of <b>circ</b> so that the hidden service is on the * other side. */ @@ -1040,7 +1061,7 @@ hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ, return 0; } -/* Given the introduction circuit intro_circ, the rendezvous circuit +/** Given the introduction circuit intro_circ, the rendezvous circuit * rend_circ, a descriptor intro point object ip and the service's * subcredential, send an INTRODUCE1 cell on intro_circ. * @@ -1125,7 +1146,7 @@ hs_circ_send_introduce1(origin_circuit_t *intro_circ, return ret; } -/* Send an ESTABLISH_RENDEZVOUS cell along the rendezvous circuit circ. On +/** Send an ESTABLISH_RENDEZVOUS cell along the rendezvous circuit circ. On * success, 0 is returned else -1 and the circuit is marked for close. */ int hs_circ_send_establish_rendezvous(origin_circuit_t *circ) @@ -1176,7 +1197,7 @@ hs_circ_send_establish_rendezvous(origin_circuit_t *circ) return -1; } -/* We are about to close or free this <b>circ</b>. Clean it up from any +/** We are about to close or free this <b>circ</b>. Clean it up from any * related HS data structures. This function can be called multiple times * safely for the same circuit. */ void diff --git a/src/feature/hs/hs_circuit.h b/src/feature/hs/hs_circuit.h index e168b301f1..c817f3e37a 100644 --- a/src/feature/hs/hs_circuit.h +++ b/src/feature/hs/hs_circuit.h @@ -35,6 +35,8 @@ void hs_circ_retry_service_rendezvous_point(origin_circuit_t *circ); origin_circuit_t *hs_circ_service_get_intro_circ( const hs_service_intro_point_t *ip); +origin_circuit_t *hs_circ_service_get_established_intro_circ( + const hs_service_intro_point_t *ip); /* Cell API. */ int hs_circ_handle_intro_established(const hs_service_t *service, diff --git a/src/feature/hs/hs_circuitmap.c b/src/feature/hs/hs_circuitmap.c index e34f564fb4..0df4519488 100644 --- a/src/feature/hs/hs_circuitmap.c +++ b/src/feature/hs/hs_circuitmap.c @@ -23,13 +23,13 @@ /************************** HS circuitmap code *******************************/ -/* This is the hidden service circuitmap. It's a hash table that maps +/** This is the hidden service circuitmap. It's a hash table that maps introduction and rendezvous tokens to specific circuits such that given a token it's easy to find the corresponding circuit. */ static struct hs_circuitmap_ht *the_hs_circuitmap = NULL; -/* This is a helper function used by the hash table code (HT_). It returns 1 if - * two circuits have the same HS token. */ +/** This is a helper function used by the hash table code (HT_). It returns 1 + * if two circuits have the same HS token. */ static int hs_circuits_have_same_token(const circuit_t *first_circuit, const circuit_t *second_circuit) @@ -60,8 +60,9 @@ hs_circuits_have_same_token(const circuit_t *first_circuit, first_token->token_len); } -/* This is a helper function for the hash table code (HT_). It hashes a circuit - * HS token into an unsigned int for use as a key by the hash table routines.*/ +/** This is a helper function for the hash table code (HT_). It hashes a + * circuit HS token into an unsigned int for use as a key by the hash table + * routines.*/ static inline unsigned int hs_circuit_hash_token(const circuit_t *circuit) { @@ -71,7 +72,7 @@ hs_circuit_hash_token(const circuit_t *circuit) circuit->hs_token->token_len); } -/* Register the circuitmap hash table */ +/** Register the circuitmap hash table */ HT_PROTOTYPE(hs_circuitmap_ht, // The name of the hashtable struct circuit_t, // The name of the element struct, hs_circuitmap_node, // The name of HT_ENTRY member @@ -83,7 +84,7 @@ HT_GENERATE2(hs_circuitmap_ht, circuit_t, hs_circuitmap_node, #ifdef TOR_UNIT_TESTS -/* Return the global HS circuitmap. Used by unittests. */ +/** Return the global HS circuitmap. Used by unittests. */ hs_circuitmap_ht * get_hs_circuitmap(void) { @@ -136,7 +137,7 @@ get_circuit_with_token(hs_token_t *search_token) return HT_FIND(hs_circuitmap_ht, the_hs_circuitmap, &search_circ); } -/* Helper function that registers <b>circ</b> with <b>token</b> on the HS +/** Helper function that registers <b>circ</b> with <b>token</b> on the HS circuitmap. This function steals reference of <b>token</b>. */ static void hs_circuitmap_register_impl(circuit_t *circ, hs_token_t *token) @@ -186,7 +187,7 @@ hs_circuitmap_register_circuit(circuit_t *circ, hs_circuitmap_register_impl(circ, hs_token); } -/* Helper function for hs_circuitmap_get_origin_circuit() and +/** Helper function for hs_circuitmap_get_origin_circuit() and * hs_circuitmap_get_or_circuit(). Because only circuit_t are indexed in the * circuitmap, this function returns object type so the specialized functions * using this helper can upcast it to the right type. @@ -220,7 +221,7 @@ hs_circuitmap_get_circuit_impl(hs_token_type_t type, return found_circ; } -/* Helper function: Query circuitmap for origin circuit with <b>token</b> of +/** Helper function: Query circuitmap for origin circuit with <b>token</b> of * size <b>token_len</b> and <b>type</b>. Only returns a circuit with purpose * equal to the <b>wanted_circ_purpose</b> parameter and if it is NOT marked * for close. Return NULL if no such circuit is found. */ @@ -244,7 +245,7 @@ hs_circuitmap_get_origin_circuit(hs_token_type_t type, return TO_ORIGIN_CIRCUIT(circ); } -/* Helper function: Query circuitmap for OR circuit with <b>token</b> of size +/** Helper function: Query circuitmap for OR circuit with <b>token</b> of size * <b>token_len</b> and <b>type</b>. Only returns a circuit with purpose equal * to the <b>wanted_circ_purpose</b> parameter and if it is NOT marked for * close. Return NULL if no such circuit is found. */ @@ -272,7 +273,7 @@ hs_circuitmap_get_or_circuit(hs_token_type_t type, /**** Public relay-side getters: */ -/* Public function: Return v2 and v3 introduction circuit to this relay. +/** Public function: Return v2 and v3 introduction circuit to this relay. * Always return a newly allocated list for which it is the caller's * responsability to free it. */ smartlist_t * @@ -299,7 +300,7 @@ hs_circuitmap_get_all_intro_circ_relay_side(void) return circuit_list; } -/* Public function: Return a v3 introduction circuit to this relay with +/** Public function: Return a v3 introduction circuit to this relay with * <b>auth_key</b>. Return NULL if no such circuit is found in the * circuitmap. */ or_circuit_t * @@ -311,7 +312,7 @@ hs_circuitmap_get_intro_circ_v3_relay_side( CIRCUIT_PURPOSE_INTRO_POINT); } -/* Public function: Return v2 introduction circuit to this relay with +/** Public function: Return v2 introduction circuit to this relay with * <b>digest</b>. Return NULL if no such circuit is found in the circuitmap. */ or_circuit_t * hs_circuitmap_get_intro_circ_v2_relay_side(const uint8_t *digest) @@ -321,7 +322,7 @@ hs_circuitmap_get_intro_circ_v2_relay_side(const uint8_t *digest) CIRCUIT_PURPOSE_INTRO_POINT); } -/* Public function: Return rendezvous circuit to this relay with rendezvous +/** Public function: Return rendezvous circuit to this relay with rendezvous * <b>cookie</b>. Return NULL if no such circuit is found in the circuitmap. */ or_circuit_t * hs_circuitmap_get_rend_circ_relay_side(const uint8_t *cookie) @@ -333,7 +334,7 @@ hs_circuitmap_get_rend_circ_relay_side(const uint8_t *cookie) /** Public relay-side setters: */ -/* Public function: Register rendezvous circuit with key <b>cookie</b> to the +/** Public function: Register rendezvous circuit with key <b>cookie</b> to the * circuitmap. */ void hs_circuitmap_register_rend_circ_relay_side(or_circuit_t *circ, @@ -343,7 +344,7 @@ hs_circuitmap_register_rend_circ_relay_side(or_circuit_t *circ, HS_TOKEN_REND_RELAY_SIDE, REND_TOKEN_LEN, cookie); } -/* Public function: Register v2 intro circuit with key <b>digest</b> to the +/** Public function: Register v2 intro circuit with key <b>digest</b> to the * circuitmap. */ void hs_circuitmap_register_intro_circ_v2_relay_side(or_circuit_t *circ, @@ -354,7 +355,7 @@ hs_circuitmap_register_intro_circ_v2_relay_side(or_circuit_t *circ, REND_TOKEN_LEN, digest); } -/* Public function: Register v3 intro circuit with key <b>auth_key</b> to the +/** Public function: Register v3 intro circuit with key <b>auth_key</b> to the * circuitmap. */ void hs_circuitmap_register_intro_circ_v3_relay_side(or_circuit_t *circ, @@ -367,7 +368,7 @@ hs_circuitmap_register_intro_circ_v3_relay_side(or_circuit_t *circ, /**** Public servide-side getters: */ -/* Public function: Return v3 introduction circuit with <b>auth_key</b> +/** Public function: Return v3 introduction circuit with <b>auth_key</b> * originating from this hidden service. Return NULL if no such circuit is * found in the circuitmap. */ origin_circuit_t * @@ -392,9 +393,9 @@ hs_circuitmap_get_intro_circ_v3_service_side(const return circ; } -/* Public function: Return v2 introduction circuit originating from this hidden - * service with <b>digest</b>. Return NULL if no such circuit is found in the - * circuitmap. */ +/** Public function: Return v2 introduction circuit originating from this + * hidden service with <b>digest</b>. Return NULL if no such circuit is found + * in the circuitmap. */ origin_circuit_t * hs_circuitmap_get_intro_circ_v2_service_side(const uint8_t *digest) { @@ -416,7 +417,7 @@ hs_circuitmap_get_intro_circ_v2_service_side(const uint8_t *digest) return circ; } -/* Public function: Return rendezvous circuit originating from this hidden +/** Public function: Return rendezvous circuit originating from this hidden * service with rendezvous <b>cookie</b>. Return NULL if no such circuit is * found in the circuitmap. */ origin_circuit_t * @@ -439,7 +440,7 @@ hs_circuitmap_get_rend_circ_service_side(const uint8_t *cookie) return circ; } -/* Public function: Return client-side rendezvous circuit with rendezvous +/** Public function: Return client-side rendezvous circuit with rendezvous * <b>cookie</b>. It will look for circuits with the following purposes: * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received @@ -472,7 +473,7 @@ hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie) return circ; } -/* Public function: Return client-side established rendezvous circuit with +/** Public function: Return client-side established rendezvous circuit with * rendezvous <b>cookie</b>. It will look for circuits with the following * purposes: * @@ -514,7 +515,7 @@ hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie) /**** Public servide-side setters: */ -/* Public function: Register v2 intro circuit with key <b>digest</b> to the +/** Public function: Register v2 intro circuit with key <b>digest</b> to the * circuitmap. */ void hs_circuitmap_register_intro_circ_v2_service_side(origin_circuit_t *circ, @@ -525,7 +526,7 @@ hs_circuitmap_register_intro_circ_v2_service_side(origin_circuit_t *circ, REND_TOKEN_LEN, digest); } -/* Public function: Register v3 intro circuit with key <b>auth_key</b> to the +/** Public function: Register v3 intro circuit with key <b>auth_key</b> to the * circuitmap. */ void hs_circuitmap_register_intro_circ_v3_service_side(origin_circuit_t *circ, @@ -536,7 +537,7 @@ hs_circuitmap_register_intro_circ_v3_service_side(origin_circuit_t *circ, ED25519_PUBKEY_LEN, auth_key->pubkey); } -/* Public function: Register rendezvous circuit with key <b>cookie</b> to the +/** Public function: Register rendezvous circuit with key <b>cookie</b> to the * circuitmap. */ void hs_circuitmap_register_rend_circ_service_side(origin_circuit_t *circ, @@ -547,7 +548,7 @@ hs_circuitmap_register_rend_circ_service_side(origin_circuit_t *circ, REND_TOKEN_LEN, cookie); } -/* Public function: Register rendezvous circuit with key <b>cookie</b> to the +/** Public function: Register rendezvous circuit with key <b>cookie</b> to the * client-side circuitmap. */ void hs_circuitmap_register_rend_circ_client_side(origin_circuit_t *or_circ, @@ -591,7 +592,7 @@ hs_circuitmap_remove_circuit(circuit_t *circ) circ->hs_token = NULL; } -/* Public function: Initialize the global HS circuitmap. */ +/** Public function: Initialize the global HS circuitmap. */ void hs_circuitmap_init(void) { @@ -601,7 +602,7 @@ hs_circuitmap_init(void) HT_INIT(hs_circuitmap_ht, the_hs_circuitmap); } -/* Public function: Free all memory allocated by the global HS circuitmap. */ +/** Public function: Free all memory allocated by the global HS circuitmap. */ void hs_circuitmap_free_all(void) { diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index c79bc63393..787b29b576 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -42,6 +42,7 @@ #include "core/or/entry_connection_st.h" #include "core/or/extend_info_st.h" #include "core/or/origin_circuit_st.h" +#include "core/or/socks_request_st.h" /** Client-side authorizations for hidden services; map of service identity * public key to hs_client_service_authorization_t *. */ @@ -100,7 +101,46 @@ fetch_status_should_close_socks(hs_client_fetch_status_t status) return 1; } -/** Cancel all descriptor fetches currently in progress. */ +/* Return a newly allocated list of all the entry connections that matches the + * given service identity pk. If service_identity_pk is NULL, all entry + * connections with an hs_ident are returned. + * + * Caller must free the returned list but does NOT have ownership of the + * object inside thus they have to remain untouched. */ +static smartlist_t * +find_entry_conns(const ed25519_public_key_t *service_identity_pk) +{ + time_t now = time(NULL); + smartlist_t *conns = NULL, *entry_conns = NULL; + + entry_conns = smartlist_new(); + + conns = connection_list_by_type_state(CONN_TYPE_AP, + AP_CONN_STATE_RENDDESC_WAIT); + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) { + entry_connection_t *entry_conn = TO_ENTRY_CONN(base_conn); + const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn); + + /* Only consider the entry connections that matches the service for which + * we just fetched its descriptor. */ + if (!edge_conn->hs_ident || + (service_identity_pk && + !ed25519_pubkey_eq(service_identity_pk, + &edge_conn->hs_ident->identity_pk))) { + continue; + } + assert_connection_ok(base_conn, now); + + /* Validated! Add the entry connection to the list. */ + smartlist_add(entry_conns, entry_conn); + } SMARTLIST_FOREACH_END(base_conn); + + /* We don't have ownership of the objects in this list. */ + smartlist_free(conns); + return entry_conns; +} + +/* Cancel all descriptor fetches currently in progress. */ static void cancel_descriptor_fetches(void) { @@ -230,26 +270,13 @@ close_all_socks_conns_waiting_for_desc(const ed25519_public_key_t *identity_pk, int reason) { unsigned int count = 0; - time_t now = approx_time(); - smartlist_t *conns = - connection_list_by_type_state(CONN_TYPE_AP, AP_CONN_STATE_RENDDESC_WAIT); + smartlist_t *entry_conns = find_entry_conns(identity_pk); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) { - entry_connection_t *entry_conn = TO_ENTRY_CONN(base_conn); - const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn); - - /* Only consider the entry connections that matches the service for which - * we tried to get the descriptor */ - if (!edge_conn->hs_ident || - !ed25519_pubkey_eq(identity_pk, - &edge_conn->hs_ident->identity_pk)) { - continue; - } - assert_connection_ok(base_conn, now); + SMARTLIST_FOREACH_BEGIN(entry_conns, entry_connection_t *, entry_conn) { /* Unattach the entry connection which will close for the reason. */ connection_mark_unattached_ap(entry_conn, reason); count++; - } SMARTLIST_FOREACH_END(base_conn); + } SMARTLIST_FOREACH_END(entry_conn); if (count > 0) { char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; @@ -262,7 +289,7 @@ close_all_socks_conns_waiting_for_desc(const ed25519_public_key_t *identity_pk, } /* No ownership of the object(s) in this list. */ - smartlist_free(conns); + smartlist_free(entry_conns); } /** Find all pending SOCKS connection waiting for a descriptor and retry them @@ -270,18 +297,18 @@ close_all_socks_conns_waiting_for_desc(const ed25519_public_key_t *identity_pk, STATIC void retry_all_socks_conn_waiting_for_desc(void) { - smartlist_t *conns = - connection_list_by_type_state(CONN_TYPE_AP, AP_CONN_STATE_RENDDESC_WAIT); + smartlist_t *entry_conns = find_entry_conns(NULL); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) { + SMARTLIST_FOREACH_BEGIN(entry_conns, entry_connection_t *, entry_conn) { hs_client_fetch_status_t status; - const edge_connection_t *edge_conn = - ENTRY_TO_EDGE_CONN(TO_ENTRY_CONN(base_conn)); + edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn); + connection_t *base_conn = &edge_conn->base_; /* Ignore non HS or non v3 connection. */ if (edge_conn->hs_ident == NULL) { continue; } + /* In this loop, we will possibly try to fetch a descriptor for the * pending connections because we just got more directory information. * However, the refetch process can cleanup all SOCKS request to the same @@ -315,10 +342,10 @@ retry_all_socks_conn_waiting_for_desc(void) * closed or we are still missing directory information. Leave the * connection in renddesc wait state so when we get more info, we'll be * able to try it again. */ - } SMARTLIST_FOREACH_END(base_conn); + } SMARTLIST_FOREACH_END(entry_conn); /* We don't have ownership of those objects. */ - smartlist_free(conns); + smartlist_free(entry_conns); } /** A v3 HS circuit successfully connected to the hidden service. Update the @@ -1235,6 +1262,262 @@ find_client_auth(const ed25519_public_key_t *service_identity_pk) return digest256map_get(client_auths, service_identity_pk->pubkey); } +/** This is called when a descriptor has arrived following a fetch request and + * has been stored in the client cache. The given entry connections, matching + * the service identity key, will get attached to the service circuit. */ +static void +client_desc_has_arrived(const smartlist_t *entry_conns) +{ + time_t now = time(NULL); + + tor_assert(entry_conns); + + SMARTLIST_FOREACH_BEGIN(entry_conns, entry_connection_t *, entry_conn) { + const hs_descriptor_t *desc; + edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn); + const ed25519_public_key_t *identity_pk = + &edge_conn->hs_ident->identity_pk; + + /* We were just called because we stored the descriptor for this service + * so not finding a descriptor means we have a bigger problem. */ + desc = hs_cache_lookup_as_client(identity_pk); + if (BUG(desc == NULL)) { + goto end; + } + + if (!hs_client_any_intro_points_usable(identity_pk, desc)) { + log_info(LD_REND, "Hidden service descriptor is unusable. " + "Closing streams."); + connection_mark_unattached_ap(entry_conn, + END_STREAM_REASON_RESOLVEFAILED); + /* We are unable to use the descriptor so remove the directory request + * from the cache so the next connection can try again. */ + note_connection_attempt_succeeded(edge_conn->hs_ident); + continue; + } + + log_info(LD_REND, "Descriptor has arrived. Launching circuits."); + + /* Mark connection as waiting for a circuit since we do have a usable + * descriptor now. */ + mark_conn_as_waiting_for_circuit(&edge_conn->base_, now); + } SMARTLIST_FOREACH_END(entry_conn); + + end: + return; +} + +/** This is called when a descriptor fetch was successful but the descriptor + * couldn't be decrypted due to missing or bad client authorization. */ +static void +client_desc_missing_bad_client_auth(const smartlist_t *entry_conns, + hs_desc_decode_status_t status) +{ + tor_assert(entry_conns); + + SMARTLIST_FOREACH_BEGIN(entry_conns, entry_connection_t *, entry_conn) { + socks5_reply_status_t code; + if (status == HS_DESC_DECODE_BAD_CLIENT_AUTH) { + code = SOCKS5_HS_BAD_CLIENT_AUTH; + } else if (status == HS_DESC_DECODE_NEED_CLIENT_AUTH) { + code = SOCKS5_HS_MISSING_CLIENT_AUTH; + } else { + /* We should not be called with another type of status. Recover by + * sending a generic error. */ + tor_assert_nonfatal_unreached(); + code = HS_DESC_DECODE_GENERIC_ERROR; + } + entry_conn->socks_request->socks_extended_error_code = code; + connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_MISC); + } SMARTLIST_FOREACH_END(entry_conn); +} + +/** Called when we get a 200 directory fetch status code. */ +static void +client_dir_fetch_200(dir_connection_t *dir_conn, + const smartlist_t *entry_conns, const char *body) +{ + hs_desc_decode_status_t decode_status; + + tor_assert(dir_conn); + tor_assert(entry_conns); + tor_assert(body); + + /* We got something: Try storing it in the cache. */ + decode_status = hs_cache_store_as_client(body, + &dir_conn->hs_ident->identity_pk); + switch (decode_status) { + case HS_DESC_DECODE_OK: + case HS_DESC_DECODE_NEED_CLIENT_AUTH: + case HS_DESC_DECODE_BAD_CLIENT_AUTH: + log_info(LD_REND, "Stored hidden service descriptor successfully."); + TO_CONN(dir_conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC; + if (decode_status == HS_DESC_DECODE_OK) { + client_desc_has_arrived(entry_conns); + } else { + /* This handles both client auth decode status. */ + client_desc_missing_bad_client_auth(entry_conns, decode_status); + log_info(LD_REND, "Stored hidden service descriptor requires " + "%s client authorization.", + decode_status == HS_DESC_DECODE_NEED_CLIENT_AUTH ? "missing" + : "new"); + } + /* Fire control port RECEIVED event. */ + hs_control_desc_event_received(dir_conn->hs_ident, + dir_conn->identity_digest); + hs_control_desc_event_content(dir_conn->hs_ident, + dir_conn->identity_digest, body); + break; + case HS_DESC_DECODE_ENCRYPTED_ERROR: + case HS_DESC_DECODE_SUPERENC_ERROR: + case HS_DESC_DECODE_PLAINTEXT_ERROR: + case HS_DESC_DECODE_GENERIC_ERROR: + default: + log_info(LD_REND, "Failed to store hidden service descriptor. " + "Descriptor decoding status: %d", decode_status); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(dir_conn->hs_ident, + dir_conn->identity_digest, "BAD_DESC"); + hs_control_desc_event_content(dir_conn->hs_ident, + dir_conn->identity_digest, NULL); + break; + } +} + +/** Called when we get a 404 directory fetch status code. */ +static void +client_dir_fetch_404(dir_connection_t *dir_conn, + const smartlist_t *entry_conns) +{ + tor_assert(entry_conns); + + /* Not there. We'll retry when connection_about_to_close_connection() tries + * to clean this conn up. */ + log_info(LD_REND, "Fetching hidden service v3 descriptor not found: " + "Retrying at another directory."); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(dir_conn->hs_ident, dir_conn->identity_digest, + "NOT_FOUND"); + hs_control_desc_event_content(dir_conn->hs_ident, dir_conn->identity_digest, + NULL); + + /* Flag every entry connections that the descriptor was not found. */ + SMARTLIST_FOREACH_BEGIN(entry_conns, entry_connection_t *, entry_conn) { + entry_conn->socks_request->socks_extended_error_code = + SOCKS5_HS_NOT_FOUND; + } SMARTLIST_FOREACH_END(entry_conn); +} + +/** Called when we get a 400 directory fetch status code. */ +static void +client_dir_fetch_400(dir_connection_t *dir_conn, const char *reason) +{ + tor_assert(dir_conn); + + log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " + "http status 400 (%s). Dirserver didn't like our " + "query? Retrying at another directory.", + escaped(reason)); + + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(dir_conn->hs_ident, dir_conn->identity_digest, + "QUERY_REJECTED"); + hs_control_desc_event_content(dir_conn->hs_ident, dir_conn->identity_digest, + NULL); +} + +/** Called when we get an unexpected directory fetch status code. */ +static void +client_dir_fetch_unexpected(dir_connection_t *dir_conn, const char *reason, + const int status_code) +{ + tor_assert(dir_conn); + + log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " + "http status %d (%s) response unexpected from HSDir " + "server '%s:%d'. Retrying at another directory.", + status_code, escaped(reason), TO_CONN(dir_conn)->address, + TO_CONN(dir_conn)->port); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(dir_conn->hs_ident, dir_conn->identity_digest, + "UNEXPECTED"); + hs_control_desc_event_content(dir_conn->hs_ident, dir_conn->identity_digest, + NULL); +} + +/** Register the credential <b>creds</b> as part of the client auth subsystem. + * + * Takes ownership of <b>creds</b>. + **/ +hs_client_register_auth_status_t +hs_client_register_auth_credentials(hs_client_service_authorization_t *creds) +{ + ed25519_public_key_t service_identity_pk; + hs_client_service_authorization_t *old_creds = NULL; + hs_client_register_auth_status_t retval = REGISTER_SUCCESS; + + tor_assert(creds); + + if (!client_auths) { + client_auths = digest256map_new(); + } + + if (hs_parse_address(creds->onion_address, &service_identity_pk, + NULL, NULL) < 0) { + client_service_authorization_free(creds); + return REGISTER_FAIL_BAD_ADDRESS; + } + + old_creds = digest256map_get(client_auths, service_identity_pk.pubkey); + if (old_creds) { + digest256map_remove(client_auths, service_identity_pk.pubkey); + client_service_authorization_free(old_creds); + retval = REGISTER_SUCCESS_ALREADY_EXISTS; + } + + digest256map_set(client_auths, service_identity_pk.pubkey, creds); + + /** Now that we set the new credentials, also try to decrypt any cached + * descriptors. */ + if (hs_cache_client_new_auth_parse(&service_identity_pk)) { + retval = REGISTER_SUCCESS_AND_DECRYPTED; + } + + return retval; +} + +/** Remove client auth credentials for the service <b>hs_address</b>. */ +hs_client_removal_auth_status_t +hs_client_remove_auth_credentials(const char *hsaddress) +{ + ed25519_public_key_t service_identity_pk; + + if (!client_auths) { + return REMOVAL_SUCCESS_NOT_FOUND; + } + + if (hs_parse_address(hsaddress, &service_identity_pk, NULL, NULL) < 0) { + return REMOVAL_BAD_ADDRESS; + } + + hs_client_service_authorization_t *cred = NULL; + cred = digest256map_remove(client_auths, service_identity_pk.pubkey); + /* digestmap_remove() returns the previously stored data if there were any */ + if (cred) { + client_service_authorization_free(cred); + return REMOVAL_SUCCESS; + } + + return REMOVAL_SUCCESS_NOT_FOUND; +} + +/** Get the HS client auth map. */ +digest256map_t * +get_hs_client_auths_map(void) +{ + return client_auths; +} + /* ========== */ /* Public API */ /* ========== */ @@ -1264,13 +1547,15 @@ hs_client_note_connection_attempt_succeeded(const edge_connection_t *conn) * service_identity_pk, decode the descriptor and set the desc pointer with a * newly allocated descriptor object. * - * Return 0 on success else a negative value and desc is set to NULL. */ -int + * On success, HS_DESC_DECODE_OK is returned and desc is set to the decoded + * descriptor. On error, desc is set to NULL and a decoding error status is + * returned depending on what was the issue. */ +hs_desc_decode_status_t hs_client_decode_descriptor(const char *desc_str, const ed25519_public_key_t *service_identity_pk, hs_descriptor_t **desc) { - int ret; + hs_desc_decode_status_t ret; uint8_t subcredential[DIGEST256_LEN]; ed25519_public_key_t blinded_pubkey; hs_client_service_authorization_t *client_auth = NULL; @@ -1298,7 +1583,7 @@ hs_client_decode_descriptor(const char *desc_str, ret = hs_desc_decode_descriptor(desc_str, subcredential, client_auht_sk, desc); memwipe(subcredential, 0, sizeof(subcredential)); - if (ret < 0) { + if (ret != HS_DESC_DECODE_OK) { goto err; } @@ -1311,12 +1596,13 @@ hs_client_decode_descriptor(const char *desc_str, log_warn(LD_GENERAL, "Descriptor signing key certificate signature " "doesn't validate with computed blinded key: %s", tor_cert_describe_signature_status(cert)); + ret = HS_DESC_DECODE_GENERIC_ERROR; goto err; } - return 0; + return HS_DESC_DECODE_OK; err: - return -1; + return ret; } /** Return true iff there are at least one usable intro point in the service @@ -1456,16 +1742,18 @@ hs_client_receive_rendezvous_acked(origin_circuit_t *circ, return -1; } -#define client_service_authorization_free(auth) \ - FREE_AND_NULL(hs_client_service_authorization_t, \ - client_service_authorization_free_, (auth)) - -static void +void client_service_authorization_free_(hs_client_service_authorization_t *auth) { - if (auth) { - memwipe(auth, 0, sizeof(*auth)); + if (!auth) { + return; + } + + if (auth->nickname) { + tor_free(auth->nickname); } + + memwipe(auth, 0, sizeof(*auth)); tor_free(auth); } @@ -1685,62 +1973,45 @@ hs_config_client_authorization(const or_options_t *options, return ret; } -/** This is called when a descriptor has arrived following a fetch request and - * has been stored in the client cache. Every entry connection that matches - * the service identity key in the ident will get attached to the hidden - * service circuit. */ +/** Called when a descriptor directory fetch is done. + * + * Act accordingly on all entry connections depending on the HTTP status code + * we got. In case of an error, the SOCKS error is set (if ExtendedErrors is + * set). + * + * The reason is a human readable string returned by the directory server + * which can describe the status of the request. The body is the response + * content, on 200 code it is the descriptor itself. Finally, the status_code + * is the HTTP code returned by the directory server. */ void -hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident) +hs_client_dir_fetch_done(dir_connection_t *dir_conn, const char *reason, + const char *body, const int status_code) { - time_t now = time(NULL); - smartlist_t *conns = NULL; + smartlist_t *entry_conns; - tor_assert(ident); + tor_assert(dir_conn); + tor_assert(body); - conns = connection_list_by_type_state(CONN_TYPE_AP, - AP_CONN_STATE_RENDDESC_WAIT); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) { - const hs_descriptor_t *desc; - entry_connection_t *entry_conn = TO_ENTRY_CONN(base_conn); - const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn); + /* Get all related entry connections. */ + entry_conns = find_entry_conns(&dir_conn->hs_ident->identity_pk); - /* Only consider the entry connections that matches the service for which - * we just fetched its descriptor. */ - if (!edge_conn->hs_ident || - !ed25519_pubkey_eq(&ident->identity_pk, - &edge_conn->hs_ident->identity_pk)) { - continue; - } - assert_connection_ok(base_conn, now); - - /* We were just called because we stored the descriptor for this service - * so not finding a descriptor means we have a bigger problem. */ - desc = hs_cache_lookup_as_client(&ident->identity_pk); - if (BUG(desc == NULL)) { - goto end; - } - - if (!hs_client_any_intro_points_usable(&ident->identity_pk, desc)) { - log_info(LD_REND, "Hidden service descriptor is unusable. " - "Closing streams."); - connection_mark_unattached_ap(entry_conn, - END_STREAM_REASON_RESOLVEFAILED); - /* We are unable to use the descriptor so remove the directory request - * from the cache so the next connection can try again. */ - note_connection_attempt_succeeded(edge_conn->hs_ident); - continue; - } - - log_info(LD_REND, "Descriptor has arrived. Launching circuits."); - - /* Mark connection as waiting for a circuit since we do have a usable - * descriptor now. */ - mark_conn_as_waiting_for_circuit(base_conn, now); - } SMARTLIST_FOREACH_END(base_conn); + switch (status_code) { + case 200: + client_dir_fetch_200(dir_conn, entry_conns, body); + break; + case 404: + client_dir_fetch_404(dir_conn, entry_conns); + break; + case 400: + client_dir_fetch_400(dir_conn, reason); + break; + default: + client_dir_fetch_unexpected(dir_conn, reason, status_code); + break; + } - end: /* We don't have ownership of the objects in this list. */ - smartlist_free(conns); + smartlist_free(entry_conns); } /** Return a newly allocated extend_info_t for a randomly chosen introduction @@ -1942,10 +2213,11 @@ hs_client_dir_info_changed(void) #ifdef TOR_UNIT_TESTS -STATIC digest256map_t * -get_hs_client_auths_map(void) +STATIC void +set_hs_client_auths_map(digest256map_t *map) { - return client_auths; + client_auths = map; } #endif /* defined(TOR_UNIT_TESTS) */ + diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h index 69e48ca31b..04827ea92a 100644 --- a/src/feature/hs/hs_client.h +++ b/src/feature/hs/hs_client.h @@ -31,7 +31,37 @@ typedef enum { HS_CLIENT_FETCH_PENDING = 5, } hs_client_fetch_status_t; -/** Client-side configuration of authorization for a service. */ +/* Status code of client auth credential registration */ +typedef enum { + /* We successfuly registered these credentials */ + REGISTER_SUCCESS, + /* We successfully registered these credentials, but had to replace some + * existing ones. */ + REGISTER_SUCCESS_ALREADY_EXISTS, + /* We successfuly registered these credentials, and also decrypted a cached + * descriptor. */ + REGISTER_SUCCESS_AND_DECRYPTED, + /* We failed to register these credentials, because of a bad HS address. */ + REGISTER_FAIL_BAD_ADDRESS, +} hs_client_register_auth_status_t; + +/* Status code of client auth credential removal */ +typedef enum { + /* We successfuly removed these credentials */ + REMOVAL_SUCCESS, + /* No need to remove those credentials, because they were not there. */ + REMOVAL_SUCCESS_NOT_FOUND, + /* We failed to register these credentials, because of a bad HS address. */ + REMOVAL_BAD_ADDRESS, +} hs_client_removal_auth_status_t; + +/** Flag to set when a client auth is permanent (saved on disk). */ +#define CLIENT_AUTH_FLAG_IS_PERMANENT (1<<0) + +/** Max length of a client auth nickname */ +#define HS_CLIENT_AUTH_MAX_NICKNAME_LENGTH 255 + +/** Client-side configuration of client authorization */ typedef struct hs_client_service_authorization_t { /** An curve25519 secret key used to compute decryption keys that * allow the client to decrypt the hidden service descriptor. */ @@ -39,8 +69,29 @@ typedef struct hs_client_service_authorization_t { /** An onion address that is used to connect to the onion service. */ char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1]; + + /* An optional nickname for this client */ + char *nickname; + + /* Optional flags for this client. */ + int flags; } hs_client_service_authorization_t; +hs_client_register_auth_status_t +hs_client_register_auth_credentials(hs_client_service_authorization_t *creds); + +hs_client_removal_auth_status_t +hs_client_remove_auth_credentials(const char *hsaddress); + +digest256map_t *get_hs_client_auths_map(void); + +#define client_service_authorization_free(auth) \ + FREE_AND_NULL(hs_client_service_authorization_t, \ + client_service_authorization_free_, (auth)) + +void +client_service_authorization_free_(hs_client_service_authorization_t *auth); + void hs_client_note_connection_attempt_succeeded( const edge_connection_t *conn); @@ -48,7 +99,7 @@ void hs_client_launch_v3_desc_fetch( const ed25519_public_key_t *onion_identity_pk, const smartlist_t *hsdirs); -int hs_client_decode_descriptor( +hs_desc_decode_status_t hs_client_decode_descriptor( const char *desc_str, const ed25519_public_key_t *service_identity_pk, hs_descriptor_t **desc); @@ -72,7 +123,8 @@ int hs_client_receive_rendezvous2(origin_circuit_t *circ, const uint8_t *payload, size_t payload_len); -void hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident); +void hs_client_dir_fetch_done(dir_connection_t *dir_conn, const char *reason, + const char *body, const int status_code); extend_info_t *hs_client_get_random_intro_from_edge( const edge_connection_t *edge_conn); @@ -113,7 +165,7 @@ STATIC void retry_all_socks_conn_waiting_for_desc(void); #ifdef TOR_UNIT_TESTS -STATIC digest256map_t *get_hs_client_auths_map(void); +STATIC void set_hs_client_auths_map(digest256map_t *map); #endif /* defined(TOR_UNIT_TESTS) */ diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c index cf98bea320..75f4385eea 100644 --- a/src/feature/hs/hs_common.c +++ b/src/feature/hs/hs_common.c @@ -305,9 +305,9 @@ hs_get_next_time_period_num(time_t now) return hs_get_time_period_num(now) + 1; } -/* Get the number of the _previous_ HS time period, given that the current time - * is <b>now</b>. If <b>now</b> is not set, we try to get the time from a live - * consensus. */ +/** Get the number of the _previous_ HS time period, given that the current + * time is <b>now</b>. If <b>now</b> is not set, we try to get the time from a + * live consensus. */ uint64_t hs_get_previous_time_period_num(time_t now) { @@ -725,7 +725,7 @@ build_blinded_key_param(const ed25519_public_key_t *pubkey, memwipe(nonce, 0, sizeof(nonce)); } -/* Using an ed25519 public key and version to build the checksum of an +/** Using an ed25519 public key and version to build the checksum of an * address. Put in checksum_out. Format is: * SHA3-256(".onion checksum" || PUBKEY || VERSION) * diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c index 3b6caaec6a..ed577daf7d 100644 --- a/src/feature/hs/hs_config.c +++ b/src/feature/hs/hs_config.c @@ -34,7 +34,7 @@ #include "lib/encoding/confline.h" #include "app/config/or_options_st.h" -/* Using the given list of services, stage them into our global state. Every +/** Using the given list of services, stage them into our global state. Every * service version are handled. This function can remove entries in the given * service_list. * @@ -70,7 +70,7 @@ stage_services(smartlist_t *service_list) hs_service_stage_services(service_list); } -/* Validate the given service against all service in the given list. If the +/** Validate the given service against all service in the given list. If the * service is ephemeral, this function ignores it. Services with the same * directory path aren't allowed and will return an error. If a duplicate is * found, 1 is returned else 0 if none found. */ @@ -118,7 +118,7 @@ service_is_duplicate_in_list(const smartlist_t *service_list, return ret; } -/* Helper function: Given an configuration option name, its value, a minimum +/** Helper function: Given an configuration option name, its value, a minimum * min and a maxium max, parse the value as a uint64_t. On success, ok is set * to 1 and ret is the parsed value. On error, ok is set to 0 and ret must be * ignored. This function logs both on error and success. */ @@ -173,7 +173,7 @@ helper_parse_circuit_id_protocol(const char *key, const char *value, int *ok) return ret; } -/* Return the service version by trying to learn it from the key on disk if +/** Return the service version by trying to learn it from the key on disk if * any. If nothing is found, the current service configured version is * returned. */ static int @@ -191,7 +191,7 @@ config_learn_service_version(hs_service_t *service) return version; } -/* Return true iff the given options starting at line_ for a hidden service +/** Return true iff the given options starting at line_ for a hidden service * contains at least one invalid option. Each hidden service option don't * apply to all versions so this function can find out. The line_ MUST start * right after the HiddenServiceDir line of this service. @@ -273,7 +273,7 @@ config_has_invalid_options(const config_line_t *line_, return ret; } -/* Validate service configuration. This is used when loading the configuration +/** Validate service configuration. This is used when loading the configuration * and once we've setup a service object, it's config object is passed to this * function for further validation. This does not validate service key * material. Return 0 if valid else -1 if invalid. */ @@ -304,7 +304,7 @@ config_validate_service(const hs_service_config_t *config) return -1; } -/* Configuration funcion for a version 3 service. The line_ must be pointing +/** Configuration funcion for a version 3 service. The line_ must be pointing * to the directive directly after a HiddenServiceDir. That way, when hitting * the next HiddenServiceDir line or reaching the end of the list of lines, we * know that we have to stop looking for more options. The given service @@ -423,7 +423,7 @@ config_service_v3(const config_line_t *line_, return -1; } -/* Configure a service using the given options in line_ and options. This is +/** Configure a service using the given options in line_ and options. This is * called for any service regardless of its version which means that all * directives in this function are generic to any service version. This * function will also check the validity of the service directory path. @@ -577,7 +577,7 @@ config_generic_service(const config_line_t *line_, return -1; } -/* Configure a service using the given line and options. This function will +/** Configure a service using the given line and options. This function will * call the corresponding configuration function for a specific service * version and validate the service against the other ones. On success, add * the service to the given list and return 0. On error, nothing is added to @@ -663,7 +663,7 @@ config_service(const config_line_t *line, const or_options_t *options, return -1; } -/* From a set of <b>options</b>, setup every hidden service found. Return 0 on +/** From a set of <b>options</b>, setup every hidden service found. Return 0 on * success or -1 on failure. If <b>validate_only</b> is set, parse, warn and * return as normal, but don't actually change the configured services. */ int @@ -731,7 +731,7 @@ hs_config_service_all(const or_options_t *options, int validate_only) return ret; } -/* From a set of <b>options</b>, setup every client authorization found. +/** From a set of <b>options</b>, setup every client authorization found. * Return 0 on success or -1 on failure. If <b>validate_only</b> is set, * parse, warn and return as normal, but don't actually change the * configured state. */ diff --git a/src/feature/hs/hs_control.c b/src/feature/hs/hs_control.c index abb421345c..461be3e1e4 100644 --- a/src/feature/hs/hs_control.c +++ b/src/feature/hs/hs_control.c @@ -20,7 +20,7 @@ #include "feature/nodelist/node_st.h" #include "feature/nodelist/routerstatus_st.h" -/* Send on the control port the "HS_DESC REQUESTEDÂ [...]" event. +/** Send on the control port the "HS_DESC REQUESTEDÂ [...]" event. * * The onion_pk is the onion service public key, base64_blinded_pk is the * base64 encoded blinded key for the service and hsdir_rs is the routerstatus @@ -57,7 +57,7 @@ hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk, memwipe(onion_address, 0, sizeof(onion_address)); } -/* Send on the control port the "HS_DESC FAILED [...]" event. +/** Send on the control port the "HS_DESC FAILED [...]" event. * * Using a directory connection identifier, the HSDir identity digest and a * reason for the failure. None can be NULL. */ @@ -81,7 +81,7 @@ hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident, hsdir_id_digest, reason); } -/* Send on the control port the "HS_DESC RECEIVED [...]" event. +/** Send on the control port the "HS_DESC RECEIVED [...]" event. * * Using a directory connection identifier and the HSDir identity digest. * None can be NULL. */ @@ -103,7 +103,7 @@ hs_control_desc_event_received(const hs_ident_dir_conn_t *ident, hsdir_id_digest); } -/* Send on the control port the "HS_DESC CREATED [...]" event. +/** Send on the control port the "HS_DESC CREATED [...]" event. * * Using the onion address of the descriptor's service and the blinded public * key of the descriptor as a descriptor ID. None can be NULL. */ @@ -124,7 +124,7 @@ hs_control_desc_event_created(const char *onion_address, control_event_hs_descriptor_created(onion_address, base64_blinded_pk, -1); } -/* Send on the control port the "HS_DESC UPLOAD [...]" event. +/** Send on the control port the "HS_DESC UPLOAD [...]" event. * * Using the onion address of the descriptor's service, the HSDir identity * digest, the blinded public key of the descriptor as a descriptor ID and the @@ -151,7 +151,7 @@ hs_control_desc_event_upload(const char *onion_address, DIGEST256_LEN)); } -/* Send on the control port the "HS_DESC UPLOADED [...]" event. +/** Send on the control port the "HS_DESC UPLOADED [...]" event. * * Using the directory connection identifier and the HSDir identity digest. * None can be NULL. */ @@ -169,7 +169,7 @@ hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident, control_event_hs_descriptor_uploaded(hsdir_id_digest, onion_address); } -/* Send on the control port the "HS_DESC_CONTENT [...]" event. +/** Send on the control port the "HS_DESC_CONTENT [...]" event. * * Using the directory connection identifier, the HSDir identity digest and * the body of the descriptor (as it was received from the directory). None @@ -193,7 +193,7 @@ hs_control_desc_event_content(const hs_ident_dir_conn_t *ident, hsdir_id_digest, body); } -/* Handle the "HSPOST [...]" command. The body is an encoded descriptor for +/** Handle the "HSPOST [...]" command. The body is an encoded descriptor for * the given onion_address. The descriptor will be uploaded to each directory * in hsdirs_rs. If NULL, the responsible directories for the current time * period will be selected. @@ -248,7 +248,7 @@ hs_control_hspost_command(const char *body, const char *onion_address, return ret; } -/* With a given <b>onion_identity_pk</b>, fetch its descriptor, optionally +/** With a given <b>onion_identity_pk</b>, fetch its descriptor, optionally * using the list of directory servers given in <b>hsdirs</b>, or a random * server if it is NULL. This function calls hs_client_launch_v3_desc_fetch(). */ diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c index 60f2bfb0de..65a6f94802 100644 --- a/src/feature/hs/hs_descriptor.c +++ b/src/feature/hs/hs_descriptor.c @@ -2038,7 +2038,7 @@ desc_sig_is_valid(const char *b64_sig, * unknowns but requires that all v3 token be present and valid. * * Return 0 on success else a negative value. */ -static int +static hs_desc_decode_status_t desc_decode_plaintext_v3(smartlist_t *tokens, hs_desc_plaintext_data_t *desc, const char *encoded_desc, size_t encoded_len) @@ -2128,21 +2128,19 @@ desc_decode_plaintext_v3(smartlist_t *tokens, goto err; } - return 0; - + return HS_DESC_DECODE_OK; err: - return -1; + return HS_DESC_DECODE_PLAINTEXT_ERROR; } /** Decode the version 3 superencrypted section of the given descriptor desc. - * The desc_superencrypted_out will be populated with the decoded data. - * Return 0 on success else -1. */ -static int + * The desc_superencrypted_out will be populated with the decoded data. */ +static hs_desc_decode_status_t desc_decode_superencrypted_v3(const hs_descriptor_t *desc, hs_desc_superencrypted_data_t * desc_superencrypted_out) { - int ret = -1; + int ret = HS_DESC_DECODE_SUPERENC_ERROR; char *message = NULL; size_t message_len; memarea_t *area = NULL; @@ -2228,11 +2226,11 @@ desc_decode_superencrypted_v3(const hs_descriptor_t *desc, tok->object_size); superencrypted->encrypted_blob_size = tok->object_size; - ret = 0; + ret = HS_DESC_DECODE_OK; goto done; err: - tor_assert(ret < 0); + tor_assert(ret < HS_DESC_DECODE_OK); hs_desc_superencrypted_data_free_contents(desc_superencrypted_out); done: @@ -2250,14 +2248,13 @@ desc_decode_superencrypted_v3(const hs_descriptor_t *desc, } /** Decode the version 3 encrypted section of the given descriptor desc. The - * desc_encrypted_out will be populated with the decoded data. Return 0 on - * success else -1. */ -static int + * desc_encrypted_out will be populated with the decoded data. */ +static hs_desc_decode_status_t desc_decode_encrypted_v3(const hs_descriptor_t *desc, const curve25519_secret_key_t *client_auth_sk, hs_desc_encrypted_data_t *desc_encrypted_out) { - int ret = -1; + int ret = HS_DESC_DECODE_ENCRYPTED_ERROR; char *message = NULL; size_t message_len; memarea_t *area = NULL; @@ -2280,12 +2277,14 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc, * authorization is failing. */ log_warn(LD_REND, "Client authorization for requested onion address " "is invalid. Can't decrypt the descriptor."); + ret = HS_DESC_DECODE_BAD_CLIENT_AUTH; } else { /* Inform at notice level that the onion address requested can't be * reached without client authorization most likely. */ log_notice(LD_REND, "Fail to decrypt descriptor for requested onion " "address. It is likely requiring client " "authorization."); + ret = HS_DESC_DECODE_NEED_CLIENT_AUTH; } goto err; } @@ -2343,11 +2342,11 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc, /* NOTE: Unknown fields are allowed because this function could be used to * decode other descriptor version. */ - ret = 0; + ret = HS_DESC_DECODE_OK; goto done; err: - tor_assert(ret < 0); + tor_assert(ret < HS_DESC_DECODE_OK); hs_desc_encrypted_data_free_contents(desc_encrypted_out); done: @@ -2366,7 +2365,7 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc, /** Table of encrypted decode function version specific. The function are * indexed by the version number so v3 callback is at index 3 in the array. */ -static int +static hs_desc_decode_status_t (*decode_encrypted_handlers[])( const hs_descriptor_t *desc, const curve25519_secret_key_t *client_auth_sk, @@ -2379,12 +2378,12 @@ static int /** Decode the encrypted data section of the given descriptor and store the * data in the given encrypted data object. Return 0 on success else a * negative value on error. */ -int +hs_desc_decode_status_t hs_desc_decode_encrypted(const hs_descriptor_t *desc, const curve25519_secret_key_t *client_auth_sk, hs_desc_encrypted_data_t *desc_encrypted) { - int ret; + int ret = HS_DESC_DECODE_ENCRYPTED_ERROR; uint32_t version; tor_assert(desc); @@ -2398,7 +2397,6 @@ hs_desc_decode_encrypted(const hs_descriptor_t *desc, /* Let's make sure we have a supported version as well. By correctly parsing * the plaintext, this should not fail. */ if (BUG(!hs_desc_is_supported_version(version))) { - ret = -1; goto err; } /* Extra precaution. Having no handler for the supported version should @@ -2419,7 +2417,7 @@ hs_desc_decode_encrypted(const hs_descriptor_t *desc, /** Table of superencrypted decode function version specific. The function are * indexed by the version number so v3 callback is at index 3 in the array. */ -static int +static hs_desc_decode_status_t (*decode_superencrypted_handlers[])( const hs_descriptor_t *desc, hs_desc_superencrypted_data_t *desc_superencrypted) = @@ -2429,14 +2427,13 @@ static int }; /** Decode the superencrypted data section of the given descriptor and store - * the data in the given superencrypted data object. Return 0 on success else - * a negative value on error. */ -int + * the data in the given superencrypted data object. */ +hs_desc_decode_status_t hs_desc_decode_superencrypted(const hs_descriptor_t *desc, hs_desc_superencrypted_data_t * desc_superencrypted) { - int ret; + int ret = HS_DESC_DECODE_SUPERENC_ERROR; uint32_t version; tor_assert(desc); @@ -2450,7 +2447,6 @@ hs_desc_decode_superencrypted(const hs_descriptor_t *desc, /* Let's make sure we have a supported version as well. By correctly parsing * the plaintext, this should not fail. */ if (BUG(!hs_desc_is_supported_version(version))) { - ret = -1; goto err; } /* Extra precaution. Having no handler for the supported version should @@ -2470,7 +2466,7 @@ hs_desc_decode_superencrypted(const hs_descriptor_t *desc, /** Table of plaintext decode function version specific. The function are * indexed by the version number so v3 callback is at index 3 in the array. */ -static int +static hs_desc_decode_status_t (*decode_plaintext_handlers[])( smartlist_t *tokens, hs_desc_plaintext_data_t *desc, @@ -2482,12 +2478,12 @@ static int }; /** Fully decode the given descriptor plaintext and store the data in the - * plaintext data object. Returns 0 on success else a negative value. */ -int + * plaintext data object. */ +hs_desc_decode_status_t hs_desc_decode_plaintext(const char *encoded, hs_desc_plaintext_data_t *plaintext) { - int ok = 0, ret = -1; + int ok = 0, ret = HS_DESC_DECODE_PLAINTEXT_ERROR; memarea_t *area = NULL; smartlist_t *tokens = NULL; size_t encoded_len; @@ -2537,11 +2533,11 @@ hs_desc_decode_plaintext(const char *encoded, /* Run the version specific plaintext decoder. */ ret = decode_plaintext_handlers[plaintext->version](tokens, plaintext, encoded, encoded_len); - if (ret < 0) { + if (ret != HS_DESC_DECODE_OK) { goto err; } /* Success. Descriptor has been populated with the data. */ - ret = 0; + ret = HS_DESC_DECODE_OK; err: if (tokens) { @@ -2560,13 +2556,13 @@ hs_desc_decode_plaintext(const char *encoded, * * Return 0 on success. A negative value is returned on error and desc_out is * set to NULL. */ -int +hs_desc_decode_status_t hs_desc_decode_descriptor(const char *encoded, const uint8_t *subcredential, const curve25519_secret_key_t *client_auth_sk, hs_descriptor_t **desc_out) { - int ret = -1; + hs_desc_decode_status_t ret = HS_DESC_DECODE_GENERIC_ERROR; hs_descriptor_t *desc; tor_assert(encoded); @@ -2583,17 +2579,17 @@ hs_desc_decode_descriptor(const char *encoded, memcpy(desc->subcredential, subcredential, sizeof(desc->subcredential)); ret = hs_desc_decode_plaintext(encoded, &desc->plaintext_data); - if (ret < 0) { + if (ret != HS_DESC_DECODE_OK) { goto err; } ret = hs_desc_decode_superencrypted(desc, &desc->superencrypted_data); - if (ret < 0) { + if (ret != HS_DESC_DECODE_OK) { goto err; } ret = hs_desc_decode_encrypted(desc, client_auth_sk, &desc->encrypted_data); - if (ret < 0) { + if (ret != HS_DESC_DECODE_OK) { goto err; } @@ -2672,7 +2668,8 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc, if (!descriptor_cookie) { ret = hs_desc_decode_descriptor(*encoded_out, desc->subcredential, NULL, NULL); - if (BUG(ret < 0)) { + if (BUG(ret != HS_DESC_DECODE_OK)) { + ret = -1; goto err; } } @@ -2815,7 +2812,9 @@ hs_desc_encrypted_obj_size(const hs_desc_encrypted_data_t *data) size_t hs_desc_obj_size(const hs_descriptor_t *data) { - tor_assert(data); + if (data == NULL) { + return 0; + } return (hs_desc_plaintext_obj_size(&data->plaintext_data) + hs_desc_encrypted_obj_size(&data->encrypted_data) + sizeof(data->subcredential)); diff --git a/src/feature/hs/hs_descriptor.h b/src/feature/hs/hs_descriptor.h index 731e0c5ce9..4f726f8c97 100644 --- a/src/feature/hs/hs_descriptor.h +++ b/src/feature/hs/hs_descriptor.h @@ -69,6 +69,31 @@ typedef enum { HS_DESC_AUTH_ED25519 = 1 } hs_desc_auth_type_t; +/** Error code when decoding a descriptor. */ +typedef enum { + /* The configured client authorization for the requested .onion address + * failed to decode the descriptor. */ + HS_DESC_DECODE_BAD_CLIENT_AUTH = -6, + + /* The requested .onion address requires a client authorization. */ + HS_DESC_DECODE_NEED_CLIENT_AUTH = -5, + + /* Error during decryption of the encrypted layer. */ + HS_DESC_DECODE_ENCRYPTED_ERROR = -4, + + /* Error during decryption of the super encrypted layer. */ + HS_DESC_DECODE_SUPERENC_ERROR = -3, + + /* Error while decoding the plaintext section. */ + HS_DESC_DECODE_PLAINTEXT_ERROR = -2, + + /* Generic error. */ + HS_DESC_DECODE_GENERIC_ERROR = -1, + + /* Decoding a descriptor was successful. */ + HS_DESC_DECODE_OK = 0, +} hs_desc_decode_status_t; + /** Introduction point information located in a descriptor. */ typedef struct hs_desc_intro_point_t { /** Link specifier(s) which details how to extend to the relay. This list diff --git a/src/feature/hs/hs_dos.c b/src/feature/hs/hs_dos.c index 19794e09d3..529955b0e7 100644 --- a/src/feature/hs/hs_dos.c +++ b/src/feature/hs/hs_dos.c @@ -31,20 +31,23 @@ #include "feature/hs/hs_dos.h" -/* Default value of the allowed INTRODUCE2 cell rate per second. Above that +/** Default value of the allowed INTRODUCE2 cell rate per second. Above that * value per second, the introduction is denied. */ #define HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC 25 -/* Default value of the allowed INTRODUCE2 cell burst per second. This is the +/** Default value of the allowed INTRODUCE2 cell burst per second. This is the * maximum value a token bucket has per second. We thus allow up to this value * of INTRODUCE2 cell per second but the bucket is refilled by the rate value * but never goes above that burst value. */ #define HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC 200 -/* Default value of the consensus parameter enabling or disabling the +/** Default value of the consensus parameter enabling or disabling the * introduction DoS defense. Disabled by default. */ #define HS_DOS_INTRODUCE_ENABLED_DEFAULT 0 +/** INTRODUCE2 rejected request counter. */ +static uint64_t intro2_rejected_count = 0; + /* Consensus parameters. The ESTABLISH_INTRO DoS cell extension have higher * priority than these values. If no extension is sent, these are used only by * the introduction point. */ @@ -62,7 +65,7 @@ get_intro2_enable_consensus_param(const networkstatus_t *ns) HS_DOS_INTRODUCE_ENABLED_DEFAULT, 0, 1); } -/* Return the parameter for the introduction rate per sec. */ +/** Return the parameter for the introduction rate per sec. */ STATIC uint32_t get_intro2_rate_consensus_param(const networkstatus_t *ns) { @@ -71,7 +74,7 @@ get_intro2_rate_consensus_param(const networkstatus_t *ns) 0, INT32_MAX); } -/* Return the parameter for the introduction burst per sec. */ +/** Return the parameter for the introduction burst per sec. */ STATIC uint32_t get_intro2_burst_consensus_param(const networkstatus_t *ns) { @@ -80,7 +83,7 @@ get_intro2_burst_consensus_param(const networkstatus_t *ns) 0, INT32_MAX); } -/* Go over all introduction circuit relay side and adjust their rate/burst +/** Go over all introduction circuit relay side and adjust their rate/burst * values using the global parameters. This is called right after the * consensus parameters might have changed. */ static void @@ -102,7 +105,7 @@ update_intro_circuits(void) smartlist_free(intro_circs); } -/* Set consensus parameters. */ +/** Set consensus parameters. */ static void set_consensus_parameters(const networkstatus_t *ns) { @@ -122,7 +125,7 @@ set_consensus_parameters(const networkstatus_t *ns) * Public API. */ -/* Initialize the INTRODUCE2 token bucket for the DoS defenses using the +/** Initialize the INTRODUCE2 token bucket for the DoS defenses using the * consensus/default values. We might get a cell extension that changes those * later but if we don't, the default or consensus parameters are used. */ void @@ -138,7 +141,7 @@ hs_dos_setup_default_intro2_defenses(or_circuit_t *circ) (uint32_t) approx_time()); } -/* Called when the consensus has changed. We might have new consensus +/** Called when the consensus has changed. We might have new consensus * parameters to look at. */ void hs_dos_consensus_has_changed(const networkstatus_t *ns) @@ -152,7 +155,7 @@ hs_dos_consensus_has_changed(const networkstatus_t *ns) set_consensus_parameters(ns); } -/* Return true iff an INTRODUCE2 cell can be sent on the given service +/** Return true iff an INTRODUCE2 cell can be sent on the given service * introduction circuit. */ bool hs_dos_can_send_intro2(or_circuit_t *s_intro_circ) @@ -163,12 +166,12 @@ hs_dos_can_send_intro2(or_circuit_t *s_intro_circ) * This can be set by the consensus, the ESTABLISH_INTRO cell extension or * the hardcoded values in tor code. */ if (!s_intro_circ->introduce2_dos_defense_enabled) { - return true; + goto allow; } /* Should not happen but if so, scream loudly. */ if (BUG(TO_CIRCUIT(s_intro_circ)->purpose != CIRCUIT_PURPOSE_INTRO_POINT)) { - return false; + goto disallow; } /* This is called just after we got a valid and parsed INTRODUCE1 cell. The @@ -189,10 +192,28 @@ hs_dos_can_send_intro2(or_circuit_t *s_intro_circ) } /* Finally, we can send a new INTRODUCE2 if there are still tokens. */ - return token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0; + if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) { + goto allow; + } + + /* Fallthrough is to disallow since this means the bucket has reached 0. */ + disallow: + /* Increment stats counter, we are rejecting the INTRO2 cell. */ + intro2_rejected_count++; + return false; + + allow: + return true; +} + +/** Return rolling count of rejected INTRO2. */ +uint64_t +hs_dos_get_intro2_rejected_count(void) +{ + return intro2_rejected_count; } -/* Initialize the onion service Denial of Service subsystem. */ +/** Initialize the onion service Denial of Service subsystem. */ void hs_dos_init(void) { diff --git a/src/feature/hs/hs_dos.h b/src/feature/hs/hs_dos.h index ccf4e27179..b9e39aca4e 100644 --- a/src/feature/hs/hs_dos.h +++ b/src/feature/hs/hs_dos.h @@ -24,6 +24,9 @@ void hs_dos_consensus_has_changed(const networkstatus_t *ns); bool hs_dos_can_send_intro2(or_circuit_t *s_intro_circ); void hs_dos_setup_default_intro2_defenses(or_circuit_t *circ); +/* Statistics. */ +uint64_t hs_dos_get_intro2_rejected_count(void); + #ifdef HS_DOS_PRIVATE #ifdef TOR_UNIT_TESTS diff --git a/src/feature/hs/hs_ident.c b/src/feature/hs/hs_ident.c index a00e55ec23..dd1cd2362c 100644 --- a/src/feature/hs/hs_ident.c +++ b/src/feature/hs/hs_ident.c @@ -10,7 +10,7 @@ #include "lib/crypt_ops/crypto_util.h" #include "feature/hs/hs_ident.h" -/* Return a newly allocated circuit identifier. The given public key is copied +/** Return a newly allocated circuit identifier. The given public key is copied * identity_pk into the identifier. */ hs_ident_circuit_t * hs_ident_circuit_new(const ed25519_public_key_t *identity_pk) @@ -20,7 +20,7 @@ hs_ident_circuit_new(const ed25519_public_key_t *identity_pk) return ident; } -/* Free the given circuit identifier. */ +/** Free the given circuit identifier. */ void hs_ident_circuit_free_(hs_ident_circuit_t *ident) { @@ -31,7 +31,7 @@ hs_ident_circuit_free_(hs_ident_circuit_t *ident) tor_free(ident); } -/* For a given circuit identifier src, return a newly allocated copy of it. +/** For a given circuit identifier src, return a newly allocated copy of it. * This can't fail. */ hs_ident_circuit_t * hs_ident_circuit_dup(const hs_ident_circuit_t *src) @@ -41,7 +41,7 @@ hs_ident_circuit_dup(const hs_ident_circuit_t *src) return ident; } -/* For a given directory connection identifier src, return a newly allocated +/** For a given directory connection identifier src, return a newly allocated * copy of it. This can't fail. */ hs_ident_dir_conn_t * hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src) @@ -51,7 +51,7 @@ hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src) return ident; } -/* Free the given directory connection identifier. */ +/** Free the given directory connection identifier. */ void hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident) { @@ -62,7 +62,7 @@ hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident) tor_free(ident); } -/* Initialized the allocated ident object with identity_pk and blinded_pk. +/** Initialized the allocated ident object with identity_pk and blinded_pk. * None of them can be NULL since a valid directory connection identifier must * have all fields set. */ void @@ -78,7 +78,7 @@ hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk, ed25519_pubkey_copy(&ident->blinded_pk, blinded_pk); } -/* Return a newly allocated edge connection identifier. The given public key +/** Return a newly allocated edge connection identifier. The given public key * identity_pk is copied into the identifier. */ hs_ident_edge_conn_t * hs_ident_edge_conn_new(const ed25519_public_key_t *identity_pk) @@ -88,7 +88,7 @@ hs_ident_edge_conn_new(const ed25519_public_key_t *identity_pk) return ident; } -/* Free the given edge connection identifier. */ +/** Free the given edge connection identifier. */ void hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident) { @@ -99,7 +99,7 @@ hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident) tor_free(ident); } -/* Return true if the given ident is valid for an introduction circuit. */ +/** Return true if the given ident is valid for an introduction circuit. */ int hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident) { @@ -120,4 +120,3 @@ hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident) invalid: return 0; } - diff --git a/src/feature/hs/hs_ident.h b/src/feature/hs/hs_ident.h index 82ca50f6b5..32cb373913 100644 --- a/src/feature/hs/hs_ident.h +++ b/src/feature/hs/hs_ident.h @@ -25,70 +25,71 @@ #include "feature/hs/hs_common.h" -/* Length of the rendezvous cookie that is used to connect circuits at the +/** Length of the rendezvous cookie that is used to connect circuits at the * rendezvous point. */ #define HS_REND_COOKIE_LEN DIGEST_LEN -/* Type of circuit an hs_ident_t object is associated with. */ +/** Type of circuit an hs_ident_t object is associated with. */ typedef enum { HS_IDENT_CIRCUIT_INTRO = 1, HS_IDENT_CIRCUIT_RENDEZVOUS = 2, } hs_ident_circuit_type_t; -/* Client and service side circuit identifier that is used for hidden service +/** Client and service side circuit identifier that is used for hidden service * circuit establishment. Not all fields contain data, it depends on the * circuit purpose. This is attached to an origin_circuit_t. All fields are * used by both client and service. */ typedef struct hs_ident_circuit_t { - /* (All circuit) The public key used to uniquely identify the service. It is + /** (All circuit) The public key used to uniquely identify the service. It is * the one found in the onion address. */ ed25519_public_key_t identity_pk; - /* (All circuit) Introduction point authentication key. It's also needed on + /** (All circuit) Introduction point authentication key. It's also needed on * the rendezvous circuit for the ntor handshake. It's used as the unique key * of the introduction point so it should not be shared between multiple * intro points. */ ed25519_public_key_t intro_auth_pk; - /* (Only client rendezvous circuit) Introduction point encryption public + /** (Only client rendezvous circuit) Introduction point encryption public * key. We keep it in the rendezvous identifier for the ntor handshake. */ curve25519_public_key_t intro_enc_pk; - /* (Only rendezvous circuit) Rendezvous cookie sent from the client to the + /** (Only rendezvous circuit) Rendezvous cookie sent from the client to the * service with an INTRODUCE1 cell and used by the service in an * RENDEZVOUS1 cell. */ uint8_t rendezvous_cookie[HS_REND_COOKIE_LEN]; - /* (Only service rendezvous circuit) The HANDSHAKE_INFO needed in the + /** (Only service rendezvous circuit) The HANDSHAKE_INFO needed in the * RENDEZVOUS1 cell of the service. The construction is as follows: - * SERVER_PK [32 bytes] - * AUTH_MAC [32 bytes] + * + * SERVER_PK [32 bytes] + * AUTH_MAC [32 bytes] */ uint8_t rendezvous_handshake_info[CURVE25519_PUBKEY_LEN + DIGEST256_LEN]; - /* (Only client rendezvous circuit) Client ephemeral keypair needed for the + /** (Only client rendezvous circuit) Client ephemeral keypair needed for the * e2e encryption with the service. */ curve25519_keypair_t rendezvous_client_kp; - /* (Only rendezvous circuit) The NTOR_KEY_SEED needed for key derivation for + /** (Only rendezvous circuit) The NTOR_KEY_SEED needed for key derivation for * the e2e encryption with the client on the circuit. */ uint8_t rendezvous_ntor_key_seed[DIGEST256_LEN]; - /* (Only rendezvous circuit) Number of streams associated with this + /** (Only rendezvous circuit) Number of streams associated with this * rendezvous circuit. We track this because there is a check on a maximum * value. */ uint64_t num_rdv_streams; } hs_ident_circuit_t; -/* Client and service side directory connection identifier used for a +/** Client and service side directory connection identifier used for a * directory connection to identify which service is being queried. This is * attached to a dir_connection_t. */ typedef struct hs_ident_dir_conn_t { - /* The public key used to uniquely identify the service. It is the one found + /** The public key used to uniquely identify the service. It is the one found * in the onion address. */ ed25519_public_key_t identity_pk; - /* The blinded public key used to uniquely identify the descriptor that this + /** The blinded public key used to uniquely identify the descriptor that this * directory connection identifier is for. Only used by the service-side code * to fine control descriptor uploads. */ ed25519_public_key_t blinded_pk; @@ -96,15 +97,15 @@ typedef struct hs_ident_dir_conn_t { /* XXX: Client authorization. */ } hs_ident_dir_conn_t; -/* Client and service side edge connection identifier used for an edge +/** Client and service side edge connection identifier used for an edge * connection to identify which service is being queried. This is attached to * a edge_connection_t. */ typedef struct hs_ident_edge_conn_t { - /* The public key used to uniquely identify the service. It is the one found + /** The public key used to uniquely identify the service. It is the one found * in the onion address. */ ed25519_public_key_t identity_pk; - /* The original virtual port that was used by the client to access the onion + /** The original virtual port that was used by the client to access the onion * service, regardless of the internal port forwarding that might have * happened on the service-side. */ uint16_t orig_virtual_port; @@ -139,4 +140,3 @@ void hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident); int hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident); #endif /* !defined(TOR_HS_IDENT_H) */ - diff --git a/src/feature/hs/hs_intropoint.c b/src/feature/hs/hs_intropoint.c index fe8486b1a6..c3889004f2 100644 --- a/src/feature/hs/hs_intropoint.c +++ b/src/feature/hs/hs_intropoint.c @@ -147,7 +147,7 @@ verify_establish_intro_cell(const trn_cell_establish_intro_t *cell, return 0; } -/* Send an INTRO_ESTABLISHED cell to <b>circ</b>. */ +/** Send an INTRO_ESTABLISHED cell to <b>circ</b>. */ MOCK_IMPL(int, hs_intro_send_intro_established_cell,(or_circuit_t *circ)) { @@ -182,7 +182,7 @@ hs_intro_send_intro_established_cell,(or_circuit_t *circ)) return ret; } -/* Validate the cell DoS extension parameters. Return true iff they've been +/** Validate the cell DoS extension parameters. Return true iff they've been * bound check and can be used. Else return false. See proposal 305 for * details and reasons about this validation. */ STATIC bool @@ -244,7 +244,7 @@ cell_dos_extension_parameters_are_valid(uint64_t intro2_rate_per_sec, return ret; } -/* Parse the cell DoS extension and apply defenses on the given circuit if +/** Parse the cell DoS extension and apply defenses on the given circuit if * validation passes. If the cell extension is malformed or contains unusable * values, the DoS defenses is disabled on the circuit. */ static void @@ -321,7 +321,7 @@ handle_establish_intro_cell_dos_extension( return; } -/* Parse every cell extension in the given ESTABLISH_INTRO cell. */ +/** Parse every cell extension in the given ESTABLISH_INTRO cell. */ static void handle_establish_intro_cell_extensions( const trn_cell_establish_intro_t *parsed_cell, @@ -457,7 +457,7 @@ handle_establish_intro(or_circuit_t *circ, const uint8_t *request, return retval; } -/* Return True if circuit is suitable for being an intro circuit. */ +/** Return True if circuit is suitable for being an intro circuit. */ static int circuit_is_suitable_intro_point(const or_circuit_t *circ, const char *log_cell_type_str) @@ -482,14 +482,14 @@ circuit_is_suitable_intro_point(const or_circuit_t *circ, return 1; } -/* Return True if circuit is suitable for being service-side intro circuit. */ +/** Return True if circuit is suitable for being service-side intro circuit. */ int hs_intro_circuit_is_suitable_for_establish_intro(const or_circuit_t *circ) { return circuit_is_suitable_intro_point(circ, "ESTABLISH_INTRO"); } -/* We just received an ESTABLISH_INTRO cell in <b>circ</b>. Figure out of it's +/** We just received an ESTABLISH_INTRO cell in <b>circ</b>. Figure out of it's * a legacy or a next gen cell, and pass it to the appropriate handler. */ int hs_intro_received_establish_intro(or_circuit_t *circ, const uint8_t *request, @@ -523,7 +523,7 @@ hs_intro_received_establish_intro(or_circuit_t *circ, const uint8_t *request, return -1; } -/* Send an INTRODUCE_ACK cell onto the circuit <b>circ</b> with the status +/** Send an INTRODUCE_ACK cell onto the circuit <b>circ</b> with the status * value in <b>status</b>. Depending on the status, it can be ACK or a NACK. * Return 0 on success else a negative value on error which will close the * circuit. */ @@ -567,7 +567,7 @@ send_introduce_ack_cell(or_circuit_t *circ, uint16_t status) return ret; } -/* Validate a parsed INTRODUCE1 <b>cell</b>. Return 0 if valid or else a +/** Validate a parsed INTRODUCE1 <b>cell</b>. Return 0 if valid or else a * negative value for an invalid cell that should be NACKed. */ STATIC int validate_introduce1_parsed_cell(const trn_cell_introduce1_t *cell) @@ -613,7 +613,7 @@ validate_introduce1_parsed_cell(const trn_cell_introduce1_t *cell) return -1; } -/* We just received a non legacy INTRODUCE1 cell on <b>client_circ</b> with +/** We just received a non legacy INTRODUCE1 cell on <b>client_circ</b> with * the payload in <b>request</b> of size <b>request_len</b>. Return 0 if * everything went well, or -1 if an error occurred. This function is in charge * of sending back an INTRODUCE_ACK cell and will close client_circ on error. @@ -712,7 +712,7 @@ handle_introduce1(or_circuit_t *client_circ, const uint8_t *request, return ret; } -/* Identify if the encoded cell we just received is a legacy one or not. The +/** Identify if the encoded cell we just received is a legacy one or not. The * <b>request</b> should be at least DIGEST_LEN bytes long. */ STATIC int introduce1_cell_is_legacy(const uint8_t *request) @@ -729,7 +729,7 @@ introduce1_cell_is_legacy(const uint8_t *request) return 0; } -/* Return true iff the circuit <b>circ</b> is suitable for receiving an +/** Return true iff the circuit <b>circ</b> is suitable for receiving an * INTRODUCE1 cell. */ STATIC int circuit_is_suitable_for_introduce1(const or_circuit_t *circ) @@ -760,7 +760,7 @@ circuit_is_suitable_for_introduce1(const or_circuit_t *circ) return 1; } -/* We just received an INTRODUCE1 cell on <b>circ</b>. Figure out which type +/** We just received an INTRODUCE1 cell on <b>circ</b>. Figure out which type * it is and pass it to the appropriate handler. Return 0 on success else a * negative value and the circuit is closed. */ int @@ -804,8 +804,8 @@ hs_intro_received_introduce1(or_circuit_t *circ, const uint8_t *request, return -1; } -/* Clear memory allocated by the given intropoint object ip (but don't free the - * object itself). */ +/** Clear memory allocated by the given intropoint object ip (but don't free + * the object itself). */ void hs_intropoint_clear(hs_intropoint_t *ip) { diff --git a/src/feature/hs/hs_intropoint.h b/src/feature/hs/hs_intropoint.h index 94ebf021e4..f4d7ad2637 100644 --- a/src/feature/hs/hs_intropoint.h +++ b/src/feature/hs/hs_intropoint.h @@ -12,15 +12,15 @@ #include "lib/crypt_ops/crypto_curve25519.h" #include "feature/nodelist/torcert.h" -/* Object containing introduction point common data between the service and +/** Object containing introduction point common data between the service and * the client side. */ typedef struct hs_intropoint_t { - /* Does this intro point only supports legacy ID ?. */ + /** Does this intro point only supports legacy ID ?. */ unsigned int is_only_legacy : 1; - /* Authentication key certificate from the descriptor. */ + /** Authentication key certificate from the descriptor. */ tor_cert_t *auth_key_cert; - /* A list of link specifier. */ + /** A list of link specifier. */ smartlist_t *link_specifiers; } hs_intropoint_t; @@ -64,4 +64,3 @@ STATIC bool cell_dos_extension_parameters_are_valid( #endif /* defined(HS_INTROPOINT_PRIVATE) */ #endif /* !defined(TOR_HS_INTRO_H) */ - diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c index da772fa6d2..5693cdb0f1 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -509,7 +509,7 @@ service_intro_point_new(const node_t *node) return NULL; } -/* Add the given intro point object to the given intro point map. The intro +/** Add the given intro point object to the given intro point map. The intro * point MUST have its RSA encryption key set if this is a legacy type or the * authentication key set otherwise. */ STATIC void @@ -711,7 +711,7 @@ count_desc_circuit_established(const hs_service_descriptor_t *desc) DIGEST256MAP_FOREACH(desc->intro_points.map, key, const hs_service_intro_point_t *, ip) { - count += ip->circuit_established; + count += !!hs_circ_service_get_established_intro_circ(ip); } DIGEST256MAP_FOREACH_END; return count; @@ -1076,7 +1076,7 @@ load_service_keys(hs_service_t *service) return ret; } -/* Check if the client file name is valid or not. Return 1 if valid, +/** Check if the client file name is valid or not. Return 1 if valid, * otherwise return 0. */ STATIC int client_filename_is_valid(const char *filename) @@ -1662,7 +1662,7 @@ build_desc_intro_points(const hs_service_t *service, DIGEST256MAP_FOREACH(desc->intro_points.map, key, const hs_service_intro_point_t *, ip) { - if (!ip->circuit_established) { + if (!hs_circ_service_get_established_intro_circ(ip)) { /* Ignore un-established intro points. They can linger in that list * because their circuit has not opened and they haven't been removed * yet even though we have enough intro circuits. @@ -1737,7 +1737,7 @@ build_service_desc_encrypted(const hs_service_t *service, return 0; } -/* Populate the descriptor superencrypted section from the given service +/** Populate the descriptor superencrypted section from the given service * object. This will generate a valid list of hs_desc_authorized_client_t * of clients that are authorized to use the service. Return 0 on success * else -1 on error. */ @@ -2372,10 +2372,6 @@ should_remove_intro_point(hs_service_intro_point_t *ip, time_t now) * remove it because it might simply be valid and opened at the previous * scheduled event for the last retry. */ - /* Did we established already? */ - if (ip->circuit_established) { - goto end; - } /* Do we simply have an existing circuit regardless of its state? */ if (hs_circ_service_get_intro_circ(ip)) { goto end; @@ -2641,7 +2637,7 @@ run_build_descriptor_event(time_t now) update_all_descriptors_intro_points(now); } -/* For the given service, launch any intro point circuits that could be +/** For the given service, launch any intro point circuits that could be * needed. This considers every descriptor of the service. */ static void launch_intro_point_circuits(hs_service_t *service) @@ -3328,11 +3324,6 @@ service_handle_intro_established(origin_circuit_t *circ, goto err; } - /* Flag that we have an established circuit for this intro point. This value - * is what indicates the upload scheduled event if we are ready to build the - * intro point into the descriptor and upload. */ - ip->circuit_established = 1; - log_info(LD_REND, "Successfully received an INTRO_ESTABLISHED cell " "on circuit %u for service %s", TO_CIRCUIT(circ)->n_circ_id, @@ -3727,10 +3718,6 @@ hs_service_intro_circ_has_closed(origin_circuit_t *circ) /* Can't have an intro point object without a descriptor. */ tor_assert(desc); - /* Circuit disappeared so make sure the intro point is updated. By - * keeping the object in the descriptor, we'll be able to retry. */ - ip->circuit_established = 0; - end: return; } diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h index 7d865f3754..6c929c7ff1 100644 --- a/src/feature/hs/hs_service.h +++ b/src/feature/hs/hs_service.h @@ -29,6 +29,7 @@ /** As described in the specification, service publishes their next descriptor * at a random time between those two values (in seconds). */ #define HS_SERVICE_NEXT_UPLOAD_TIME_MIN (60 * 60) +/** Maximum interval for uploading next descriptor (in seconds). */ #define HS_SERVICE_NEXT_UPLOAD_TIME_MAX (120 * 60) /** Service side introduction point. */ @@ -69,9 +70,6 @@ typedef struct hs_service_intro_point_t { * consensus. After MAX_INTRO_POINT_CIRCUIT_RETRIES, we give up on it. */ uint32_t circuit_retries; - /** Set if this intro point has an established circuit. */ - unsigned int circuit_established : 1; - /** Replay cache recording the encrypted part of an INTRODUCE2 cell that the * circuit associated with this intro point has received. This is used to * prevent replay attacks. */ @@ -319,6 +317,11 @@ void hs_service_free_all(void); /* Service new/free functions. */ hs_service_t *hs_service_new(const or_options_t *options); void hs_service_free_(hs_service_t *service); +/** + * @copydoc hs_service_free_ + * + * Additionally, set the pointer <b>s</b> to NULL. + **/ #define hs_service_free(s) FREE_AND_NULL(hs_service_t, hs_service_free_, (s)) MOCK_DECL(unsigned int, hs_service_get_num_services,(void)); diff --git a/src/feature/hs/hsdir_index_st.h b/src/feature/hs/hsdir_index_st.h index 6c86c02f47..0a0ac8ae6a 100644 --- a/src/feature/hs/hsdir_index_st.h +++ b/src/feature/hs/hsdir_index_st.h @@ -4,21 +4,26 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file hsdir_index_st.h + * @brief HS directory index structure + **/ + #ifndef HSDIR_INDEX_ST_H #define HSDIR_INDEX_ST_H -/* Hidden service directory index used in a node_t which is set once we set +/** Hidden service directory index used in a node_t which is set once we set * the consensus. */ struct hsdir_index_t { - /* HSDir index to use when fetching a descriptor. */ + /** HSDir index to use when fetching a descriptor. */ uint8_t fetch[DIGEST256_LEN]; - /* HSDir index used by services to store their first and second + /** HSDir index used by services to store their first and second * descriptor. The first descriptor is chronologically older than the second * one and uses older TP and SRV values. */ uint8_t store_first[DIGEST256_LEN]; + /** Newer index, for second descriptor. */ uint8_t store_second[DIGEST256_LEN]; }; #endif /* !defined(HSDIR_INDEX_ST_H) */ - diff --git a/src/feature/hs_common/feature_hs_common.md b/src/feature/hs_common/feature_hs_common.md new file mode 100644 index 0000000000..3a5e351a0a --- /dev/null +++ b/src/feature/hs_common/feature_hs_common.md @@ -0,0 +1,3 @@ +@dir /feature/hs_common +@brief feature/hs_common: Common to v2 (old) and v3 (current) onion services + diff --git a/src/feature/hs_common/replaycache.h b/src/feature/hs_common/replaycache.h index 01f5e600c2..812a05d260 100644 --- a/src/feature/hs_common/replaycache.h +++ b/src/feature/hs_common/replaycache.h @@ -14,16 +14,16 @@ typedef struct replaycache_t replaycache_t; #ifdef REPLAYCACHE_PRIVATE struct replaycache_t { - /* Scrub interval */ + /** Scrub interval */ time_t scrub_interval; - /* Last scrubbed */ + /** Last scrubbed */ time_t scrubbed; - /* + /** * Horizon * (don't return true on digests in the cache but older than this) */ time_t horizon; - /* + /** * Digest map: keys are digests, values are times the digest was last seen */ digest256map_t *digests_seen; @@ -34,6 +34,11 @@ struct replaycache_t { /* replaycache_t free/new */ void replaycache_free_(replaycache_t *r); +/** + * @copydoc replaycache_free_ + * + * Additionally, set the pointer <b>r</b> to NULL. + **/ #define replaycache_free(r) \ FREE_AND_NULL(replaycache_t, replaycache_free_, (r)) replaycache_t * replaycache_new(time_t horizon, time_t interval); diff --git a/src/feature/hs_common/shared_random_client.c b/src/feature/hs_common/shared_random_client.c index 5772034c6d..f5328e6e10 100644 --- a/src/feature/hs_common/shared_random_client.c +++ b/src/feature/hs_common/shared_random_client.c @@ -18,7 +18,7 @@ #include "feature/nodelist/networkstatus_st.h" -/* Convert a given srv object to a string for the control port. This doesn't +/** Convert a given srv object to a string for the control port. This doesn't * fail and the srv object MUST be valid. */ static char * srv_to_control_string(const sr_srv_t *srv) @@ -32,7 +32,7 @@ srv_to_control_string(const sr_srv_t *srv) return srv_str; } -/* Return the voting interval of the tor vote subsystem. */ +/** Return the voting interval of the tor vote subsystem. */ int get_voting_interval(void) { @@ -51,7 +51,7 @@ get_voting_interval(void) return interval; } -/* Given the current consensus, return the start time of the current round of +/** Given the current consensus, return the start time of the current round of * the SR protocol. For example, if it's 23:47:08, the current round thus * started at 23:47:00 for a voting interval of 10 seconds. * @@ -78,7 +78,7 @@ get_start_time_of_current_round(void) * Public API */ -/* Encode the given shared random value and put it in dst. Destination +/** Encode the given shared random value and put it in dst. Destination * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */ void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv) @@ -99,7 +99,7 @@ sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv) strlcpy(dst, buf, dst_len); } -/* Return the current SRV string representation for the control port. Return a +/** Return the current SRV string representation for the control port. Return a * newly allocated string on success containing the value else "" if not found * or if we don't have a valid consensus yet. */ char * @@ -115,7 +115,7 @@ sr_get_current_for_control(void) return srv_str; } -/* Return the previous SRV string representation for the control port. Return +/** Return the previous SRV string representation for the control port. Return * a newly allocated string on success containing the value else "" if not * found or if we don't have a valid consensus yet. */ char * @@ -131,7 +131,7 @@ sr_get_previous_for_control(void) return srv_str; } -/* Return current shared random value from the latest consensus. Caller can +/** Return current shared random value from the latest consensus. Caller can * NOT keep a reference to the returned pointer. Return NULL if none. */ const sr_srv_t * sr_get_current(const networkstatus_t *ns) @@ -154,7 +154,7 @@ sr_get_current(const networkstatus_t *ns) return NULL; } -/* Return previous shared random value from the latest consensus. Caller can +/** Return previous shared random value from the latest consensus. Caller can * NOT keep a reference to the returned pointer. Return NULL if none. */ const sr_srv_t * sr_get_previous(const networkstatus_t *ns) @@ -177,7 +177,7 @@ sr_get_previous(const networkstatus_t *ns) return NULL; } -/* Parse a list of arguments from a SRV value either from a vote, consensus +/** Parse a list of arguments from a SRV value either from a vote, consensus * or from our disk state and return a newly allocated srv object. NULL is * returned on error. * @@ -290,4 +290,3 @@ sr_state_get_protocol_run_duration(void) int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES; return total_protocol_rounds * get_voting_interval(); } - diff --git a/src/feature/keymgt/feature_keymgt.md b/src/feature/keymgt/feature_keymgt.md new file mode 100644 index 0000000000..1eac7cca50 --- /dev/null +++ b/src/feature/keymgt/feature_keymgt.md @@ -0,0 +1,3 @@ +@dir /feature/keymgt +@brief feature/keymgt: Store keys for relays, authorities, etc. + diff --git a/src/feature/nodelist/authority_cert_st.h b/src/feature/nodelist/authority_cert_st.h index bf9b690c24..6787487c7c 100644 --- a/src/feature/nodelist/authority_cert_st.h +++ b/src/feature/nodelist/authority_cert_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file authority_cert_st.h + * @brief Authority certificate structure. + **/ + #ifndef AUTHORITY_CERT_ST_H #define AUTHORITY_CERT_ST_H @@ -29,4 +34,3 @@ struct authority_cert_t { }; #endif /* !defined(AUTHORITY_CERT_ST_H) */ - diff --git a/src/feature/nodelist/desc_store_st.h b/src/feature/nodelist/desc_store_st.h index 4d1378cdfa..75300ecf97 100644 --- a/src/feature/nodelist/desc_store_st.h +++ b/src/feature/nodelist/desc_store_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file desc_store_st.h + * @brief Routerinfo/extrainfo storage structure. + **/ + #ifndef DESC_STORE_ST_H #define DESC_STORE_ST_H diff --git a/src/feature/nodelist/document_signature_st.h b/src/feature/nodelist/document_signature_st.h index ac2a803252..ba4581c1b8 100644 --- a/src/feature/nodelist/document_signature_st.h +++ b/src/feature/nodelist/document_signature_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file document_signature_st.h + * @brief Authority signature structure + **/ + #ifndef DOCUMENT_SIGNATURE_ST_H #define DOCUMENT_SIGNATURE_ST_H @@ -26,4 +31,3 @@ struct document_signature_t { }; #endif /* !defined(DOCUMENT_SIGNATURE_ST_H) */ - diff --git a/src/feature/nodelist/extrainfo_st.h b/src/feature/nodelist/extrainfo_st.h index 22c708f018..6d707bea05 100644 --- a/src/feature/nodelist/extrainfo_st.h +++ b/src/feature/nodelist/extrainfo_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file extrainfo_st.h + * @brief A relay's extra-info structure. + **/ + #ifndef EXTRAINFO_ST_H #define EXTRAINFO_ST_H @@ -27,4 +32,3 @@ struct extrainfo_t { }; #endif /* !defined(EXTRAINFO_ST_H) */ - diff --git a/src/feature/nodelist/feature_nodelist.md b/src/feature/nodelist/feature_nodelist.md new file mode 100644 index 0000000000..9d715308c2 --- /dev/null +++ b/src/feature/nodelist/feature_nodelist.md @@ -0,0 +1,2 @@ +@dir /feature/nodelist +@brief feature/nodelist: Download and manage a list of relays diff --git a/src/feature/nodelist/microdesc_st.h b/src/feature/nodelist/microdesc_st.h index e017c46c79..58b0630573 100644 --- a/src/feature/nodelist/microdesc_st.h +++ b/src/feature/nodelist/microdesc_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file microdesc_st.h + * @brief Microdescriptor structure + **/ + #ifndef MICRODESC_ST_H #define MICRODESC_ST_H diff --git a/src/feature/nodelist/networkstatus_sr_info_st.h b/src/feature/nodelist/networkstatus_sr_info_st.h index 420c3d61e4..1392fa6853 100644 --- a/src/feature/nodelist/networkstatus_sr_info_st.h +++ b/src/feature/nodelist/networkstatus_sr_info_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file networkstatus_sr_info_st.h + * @brief Shared-randomness structure. + **/ + #ifndef NETWORKSTATUS_SR_INFO_ST_H #define NETWORKSTATUS_SR_INFO_ST_H @@ -20,4 +25,3 @@ struct networkstatus_sr_info_t { }; #endif /* !defined(NETWORKSTATUS_SR_INFO_ST_H) */ - diff --git a/src/feature/nodelist/networkstatus_st.h b/src/feature/nodelist/networkstatus_st.h index 6e84c170d6..a23ef0c193 100644 --- a/src/feature/nodelist/networkstatus_st.h +++ b/src/feature/nodelist/networkstatus_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file networkstatus_st.h + * @brief Networkstatus consensus/vote structure. + **/ + #ifndef NETWORKSTATUS_ST_H #define NETWORKSTATUS_ST_H diff --git a/src/feature/nodelist/networkstatus_voter_info_st.h b/src/feature/nodelist/networkstatus_voter_info_st.h index 66af82a8e3..629cb52254 100644 --- a/src/feature/nodelist/networkstatus_voter_info_st.h +++ b/src/feature/nodelist/networkstatus_voter_info_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file networkstatus_voter_info_st.h + * @brief Single consensus voter structure. + **/ + #ifndef NETWORKSTATUS_VOTER_INFO_ST_H #define NETWORKSTATUS_VOTER_INFO_ST_H diff --git a/src/feature/nodelist/node_st.h b/src/feature/nodelist/node_st.h index c63a535a19..6ad10bb85a 100644 --- a/src/feature/nodelist/node_st.h +++ b/src/feature/nodelist/node_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file node_st.h + * @brief Node information structure. + **/ + #ifndef NODE_ST_H #define NODE_ST_H diff --git a/src/feature/nodelist/nodefamily_st.h b/src/feature/nodelist/nodefamily_st.h index 20390c9308..4aa00b0255 100644 --- a/src/feature/nodelist/nodefamily_st.h +++ b/src/feature/nodelist/nodefamily_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file nodefamily_st.h + * @brief Compact node-family structure + **/ + #ifndef TOR_NODEFAMILY_ST_H #define TOR_NODEFAMILY_ST_H diff --git a/src/feature/nodelist/routerinfo.c b/src/feature/nodelist/routerinfo.c index 975b503615..4e570fcbb2 100644 --- a/src/feature/nodelist/routerinfo.c +++ b/src/feature/nodelist/routerinfo.c @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file routerinfo.c + * @brief Manipulate full router descriptors. + **/ + #include "core/or/or.h" #include "feature/nodelist/nodelist.h" diff --git a/src/feature/nodelist/routerinfo_st.h b/src/feature/nodelist/routerinfo_st.h index 59fd56d0a0..16387f1005 100644 --- a/src/feature/nodelist/routerinfo_st.h +++ b/src/feature/nodelist/routerinfo_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file routerinfo_st.h + * @brief Router descriptor structure. + **/ + #ifndef ROUTERINFO_ST_H #define ROUTERINFO_ST_H diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c index 0cd7a76a9a..0709a3bbe8 100644 --- a/src/feature/nodelist/routerlist.c +++ b/src/feature/nodelist/routerlist.c @@ -1936,9 +1936,7 @@ routerlist_descriptors_added(smartlist_t *sl, int from_cache) learned_bridge_descriptor(ri, from_cache); if (ri->needs_retest_if_added) { ri->needs_retest_if_added = 0; -#ifdef HAVE_MODULE_DIRAUTH dirserv_single_reachability_test(approx_time(), ri); -#endif } } SMARTLIST_FOREACH_END(ri); } diff --git a/src/feature/nodelist/routerlist_st.h b/src/feature/nodelist/routerlist_st.h index 10b919a1bf..d3a3874983 100644 --- a/src/feature/nodelist/routerlist_st.h +++ b/src/feature/nodelist/routerlist_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file routerlist_st.h + * @brief Router descriptor list structure. + **/ + #ifndef ROUTERLIST_ST_H #define ROUTERLIST_ST_H @@ -37,4 +42,3 @@ struct routerlist_t { }; #endif /* !defined(ROUTERLIST_ST_H) */ - diff --git a/src/feature/nodelist/routerset.c b/src/feature/nodelist/routerset.c index 9a205d39b7..4f2406d10f 100644 --- a/src/feature/nodelist/routerset.c +++ b/src/feature/nodelist/routerset.c @@ -17,7 +17,7 @@ * * Routersets are typically used for user-specified restrictions, and * are created by invoking routerset_new and routerset_parse from - * config.c and confparse.c. To use a routerset, invoke one of + * config.c and confmgt.c. To use a routerset, invoke one of * routerset_contains_...() functions , or use * routerstatus_get_all_nodes() / routerstatus_subtract_nodes() to * manipulate a smartlist of node_t pointers. diff --git a/src/feature/nodelist/routerset.h b/src/feature/nodelist/routerset.h index f3bf4a1f7c..6bd97f9422 100644 --- a/src/feature/nodelist/routerset.h +++ b/src/feature/nodelist/routerset.h @@ -4,7 +4,7 @@ /* See LICENSE for licensing information */ /** - * \file routerlist.h + * \file routerset.h * \brief Header file for routerset.c **/ @@ -46,6 +46,7 @@ int routerset_len(const routerset_t *set); struct var_type_def_t; extern const struct var_type_def_t ROUTERSET_type_defn; +typedef routerset_t config_decl_ROUTERSET; #ifdef ROUTERSET_PRIVATE #include "lib/container/bitarray.h" diff --git a/src/feature/nodelist/routerstatus_st.h b/src/feature/nodelist/routerstatus_st.h index 46337c9e52..b60e0e41d0 100644 --- a/src/feature/nodelist/routerstatus_st.h +++ b/src/feature/nodelist/routerstatus_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file routerstatus_st.h + * @brief Routerstatus (consensus entry) structure + **/ + #ifndef ROUTERSTATUS_ST_H #define ROUTERSTATUS_ST_H @@ -79,4 +84,3 @@ struct routerstatus_t { }; #endif /* !defined(ROUTERSTATUS_ST_H) */ - diff --git a/src/feature/nodelist/signed_descriptor_st.h b/src/feature/nodelist/signed_descriptor_st.h index 64c28f7440..952fc702f4 100644 --- a/src/feature/nodelist/signed_descriptor_st.h +++ b/src/feature/nodelist/signed_descriptor_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file signed_descriptor_st.h + * @brief Descriptor/extrainfo signature structure + **/ + #ifndef SIGNED_DESCRIPTOR_ST_H #define SIGNED_DESCRIPTOR_ST_H @@ -58,4 +63,3 @@ struct signed_descriptor_t { }; #endif /* !defined(SIGNED_DESCRIPTOR_ST_H) */ - diff --git a/src/feature/nodelist/torcert.h b/src/feature/nodelist/torcert.h index 03d5bdca93..5a1f932392 100644 --- a/src/feature/nodelist/torcert.h +++ b/src/feature/nodelist/torcert.h @@ -1,6 +1,11 @@ /* Copyright (c) 2014-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file torcert.h + * @brief Header for torcert.c + **/ + #ifndef TORCERT_H_INCLUDED #define TORCERT_H_INCLUDED diff --git a/src/feature/nodelist/vote_routerstatus_st.h b/src/feature/nodelist/vote_routerstatus_st.h index 0d909da260..e34b5e5f69 100644 --- a/src/feature/nodelist/vote_routerstatus_st.h +++ b/src/feature/nodelist/vote_routerstatus_st.h @@ -4,6 +4,10 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file vote_routerstatus_st.h + * @brief Routerstatus (vote entry) structure + **/ #ifndef VOTE_ROUTERSTATUS_ST_H #define VOTE_ROUTERSTATUS_ST_H diff --git a/src/feature/relay/ext_orport.h b/src/feature/relay/ext_orport.h index 7313ebd03d..a981ca80c2 100644 --- a/src/feature/relay/ext_orport.h +++ b/src/feature/relay/ext_orport.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file ext_orport.h + * @brief Header for ext_orport.c + **/ + #ifndef EXT_ORPORT_H #define EXT_ORPORT_H diff --git a/src/feature/relay/feature_relay.md b/src/feature/relay/feature_relay.md new file mode 100644 index 0000000000..a7f0c2153a --- /dev/null +++ b/src/feature/relay/feature_relay.md @@ -0,0 +1,4 @@ +@dir /feature/relay +@brief feature/relay: Relay-specific code + +(There is also a bunch of relay-specific code in other modules.) diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c new file mode 100644 index 0000000000..275e0e6a68 --- /dev/null +++ b/src/feature/relay/relay_config.c @@ -0,0 +1,1440 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file relay_config.c + * @brief Code to interpret the user's configuration of Tor's relay module. + **/ + +#include "orconfig.h" +#define RELAY_CONFIG_PRIVATE +#include "feature/relay/relay_config.h" + +#include "lib/encoding/confline.h" +#include "lib/confmgt/confmgt.h" + +#include "lib/container/smartlist.h" +#include "lib/geoip/geoip.h" +#include "lib/meminfo/meminfo.h" +#include "lib/osinfo/uname.h" +#include "lib/process/setuid.h" + +/* Required for dirinfo_type_t in or_options_t */ +#include "core/or/or.h" +#include "app/config/config.h" + +#include "core/mainloop/connection.h" +#include "core/mainloop/cpuworker.h" +#include "core/mainloop/mainloop.h" +#include "core/or/circuitbuild.h" +#include "core/or/connection_or.h" +#include "core/or/port_cfg_st.h" + +#include "feature/hibernate/hibernate.h" +#include "feature/nodelist/nickname.h" +#include "feature/stats/geoip_stats.h" +#include "feature/stats/predict_ports.h" +#include "feature/stats/rephist.h" + +#include "feature/dirauth/authmode.h" + +#include "feature/dircache/consdiffmgr.h" +#include "feature/relay/dns.h" +#include "feature/relay/routermode.h" + +/** Contents of most recently read DirPortFrontPage file. */ +static char *global_dirfrontpagecontents = NULL; + +/* Copied from config.c, we will refactor later in 29211. */ +#define REJECT(arg) \ + STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END +#if defined(__GNUC__) && __GNUC__ <= 3 +#define COMPLAIN(args...) \ + STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END +#else +#define COMPLAIN(args, ...) \ + STMT_BEGIN log_warn(LD_CONFIG, args, ##__VA_ARGS__); STMT_END +#endif /* defined(__GNUC__) && __GNUC__ <= 3 */ + +/* Used in the various options_transition_affects* functions. */ +#define YES_IF_CHANGED_BOOL(opt) \ + if (!CFG_EQ_BOOL(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_INT(opt) \ + if (!CFG_EQ_INT(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_STRING(opt) \ + if (!CFG_EQ_STRING(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_LINELIST(opt) \ + if (!CFG_EQ_LINELIST(old_options, new_options, opt)) return 1; + +/** Return the contents of our frontpage string, or NULL if not configured. */ +MOCK_IMPL(const char*, +relay_get_dirportfrontpage, (void)) +{ + return global_dirfrontpagecontents; +} + +/** Release all memory and resources held by global relay configuration + * structures. + */ +void +relay_config_free_all(void) +{ + tor_free(global_dirfrontpagecontents); +} + +/** Return the bandwidthrate that we are going to report to the authorities + * based on the config options. */ +uint32_t +relay_get_effective_bwrate(const or_options_t *options) +{ + uint64_t bw = options->BandwidthRate; + if (bw > options->MaxAdvertisedBandwidth) + bw = options->MaxAdvertisedBandwidth; + if (options->RelayBandwidthRate > 0 && bw > options->RelayBandwidthRate) + bw = options->RelayBandwidthRate; + /* config_ensure_bandwidth_cap() makes sure that this cast can't overflow. */ + return (uint32_t)bw; +} + +/** Return the bandwidthburst that we are going to report to the authorities + * based on the config options. */ +uint32_t +relay_get_effective_bwburst(const or_options_t *options) +{ + uint64_t bw = options->BandwidthBurst; + if (options->RelayBandwidthBurst > 0 && bw > options->RelayBandwidthBurst) + bw = options->RelayBandwidthBurst; + /* config_ensure_bandwidth_cap() makes sure that this cast can't overflow. */ + return (uint32_t)bw; +} + +/** Warn for every Extended ORPort port in <b>ports</b> that is on a + * publicly routable address. */ +void +port_warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname) +{ + SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { + if (port->type != CONN_TYPE_EXT_OR_LISTENER) + continue; + if (port->is_unix_addr) + continue; + /* XXX maybe warn even if address is RFC1918? */ + if (!tor_addr_is_internal(&port->addr, 1)) { + log_warn(LD_CONFIG, "You specified a public address '%s' for %sPort. " + "This is not advised; this address is supposed to only be " + "exposed on localhost so that your pluggable transport " + "proxies can connect to it.", + fmt_addrport(&port->addr, port->port), portname); + } + } SMARTLIST_FOREACH_END(port); +} + +/** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal + * consistency and warn as appropriate. On Unix-based OSes, set + * *<b>n_low_ports_out</b> to the number of sub-1024 ports we will be + * binding, and warn if we may be unable to re-bind after hibernation. */ +static int +check_server_ports(const smartlist_t *ports, + const or_options_t *options, + int *n_low_ports_out) +{ + if (BUG(!ports)) + return -1; + + if (BUG(!options)) + return -1; + + if (BUG(!n_low_ports_out)) + return -1; + + int n_orport_advertised = 0; + int n_orport_advertised_ipv4 = 0; + int n_orport_listeners = 0; + int n_dirport_advertised = 0; + int n_dirport_listeners = 0; + int n_low_port = 0; + int r = 0; + + SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { + if (port->type == CONN_TYPE_DIR_LISTENER) { + if (! port->server_cfg.no_advertise) + ++n_dirport_advertised; + if (! port->server_cfg.no_listen) + ++n_dirport_listeners; + } else if (port->type == CONN_TYPE_OR_LISTENER) { + if (! port->server_cfg.no_advertise) { + ++n_orport_advertised; + if (port_binds_ipv4(port)) + ++n_orport_advertised_ipv4; + } + if (! port->server_cfg.no_listen) + ++n_orport_listeners; + } else { + continue; + } +#ifndef _WIN32 + if (!port->server_cfg.no_listen && port->port < 1024) + ++n_low_port; +#endif + } SMARTLIST_FOREACH_END(port); + + if (n_orport_advertised && !n_orport_listeners) { + log_warn(LD_CONFIG, "We are advertising an ORPort, but not actually " + "listening on one."); + r = -1; + } + if (n_orport_listeners && !n_orport_advertised) { + log_warn(LD_CONFIG, "We are listening on an ORPort, but not advertising " + "any ORPorts. This will keep us from building a %s " + "descriptor, and make us impossible to use.", + options->BridgeRelay ? "bridge" : "router"); + r = -1; + } + if (n_dirport_advertised && !n_dirport_listeners) { + log_warn(LD_CONFIG, "We are advertising a DirPort, but not actually " + "listening on one."); + r = -1; + } + if (n_dirport_advertised > 1) { + log_warn(LD_CONFIG, "Can't advertise more than one DirPort."); + r = -1; + } + if (n_orport_advertised && !n_orport_advertised_ipv4 && + !options->BridgeRelay) { + log_warn(LD_CONFIG, "Configured public relay to listen only on an IPv6 " + "address. Tor needs to listen on an IPv4 address too."); + r = -1; + } + + if (n_low_port && options->AccountingMax && + (!have_capability_support() || options->KeepBindCapabilities == 0)) { + const char *extra = ""; + if (options->KeepBindCapabilities == 0 && have_capability_support()) + extra = ", and you have disabled KeepBindCapabilities."; + log_warn(LD_CONFIG, + "You have set AccountingMax to use hibernation. You have also " + "chosen a low DirPort or OrPort%s." + "This combination can make Tor stop " + "working when it tries to re-attach the port after a period of " + "hibernation. Please choose a different port or turn off " + "hibernation unless you know this combination will work on your " + "platform.", extra); + } + + if (n_low_ports_out) + *n_low_ports_out = n_low_port; + + return r; +} + +/** Parse all relay ports from <b>options</b>. On success, add parsed ports to + * <b>ports</b>, and return 0. On failure, set *<b>msg</b> to a description + * of the problem and return -1. + **/ +int +port_parse_ports_relay(or_options_t *options, + char **msg, + smartlist_t *ports_out, + int *have_low_ports_out) +{ + int retval = -1; + smartlist_t *ports = smartlist_new(); + int n_low_ports = 0; + + if (BUG(!options)) + goto err; + + if (BUG(!msg)) + goto err; + + if (BUG(!ports_out)) + goto err; + + if (BUG(!have_low_ports_out)) + goto err; + + if (options->ClientOnly) { + retval = 0; + goto err; + } + + if (port_parse_config(ports, + options->ORPort_lines, + "OR", CONN_TYPE_OR_LISTENER, + "0.0.0.0", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid ORPort configuration"); + goto err; + } + if (port_parse_config(ports, + options->ExtORPort_lines, + "ExtOR", CONN_TYPE_EXT_OR_LISTENER, + "127.0.0.1", 0, + CL_PORT_SERVER_OPTIONS|CL_PORT_WARN_NONLOCAL) < 0) { + *msg = tor_strdup("Invalid ExtORPort configuration"); + goto err; + } + if (port_parse_config(ports, + options->DirPort_lines, + "Dir", CONN_TYPE_DIR_LISTENER, + "0.0.0.0", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid DirPort configuration"); + goto err; + } + + if (check_server_ports(ports, options, &n_low_ports) < 0) { + *msg = tor_strdup("Misconfigured server ports"); + goto err; + } + + smartlist_add_all(ports_out, ports); + smartlist_free(ports); + ports = NULL; + retval = 0; + + err: + if (*have_low_ports_out < 0) + *have_low_ports_out = (n_low_ports > 0); + if (ports) { + SMARTLIST_FOREACH(ports, port_cfg_t *, p, port_cfg_free(p)); + smartlist_free(ports); + } + return retval; +} + +/** Update the relay *Port_set values in <b>options</b> from <b>ports</b>. */ +void +port_update_port_set_relay(or_options_t *options, + const smartlist_t *ports) +{ + if (BUG(!options)) + return; + + if (BUG(!ports)) + return; + + if (options->ClientOnly) + return; + + /* Update the relay *Port_set options. The !! here is to force a boolean + * out of an integer. */ + options->ORPort_set = + !! port_count_real_listeners(ports, CONN_TYPE_OR_LISTENER, 0); + options->DirPort_set = + !! port_count_real_listeners(ports, CONN_TYPE_DIR_LISTENER, 0); + options->ExtORPort_set = + !! port_count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER, 0); +} + +/** + * Legacy validation function, which checks that the current OS is usable in + * relay mode, if options is set to a relay mode. + * + * Warns about OSes with potential issues. Always returns 0. + */ +int +options_validate_relay_os(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (!server_mode(options)) + return 0; + + const char *uname = get_uname(); + + if (!strcmpstart(uname, "Windows 95") || + !strcmpstart(uname, "Windows 98") || + !strcmpstart(uname, "Windows Me")) { + log_warn(LD_CONFIG, "Tor is running as a server, but you are " + "running %s; this probably won't work. See " + "https://www.torproject.org/docs/faq.html#BestOSForRelay " + "for details.", uname); + } + + return 0; +} + +/** + * Legacy validation/normalization function for the relay info options. + * Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_relay_info(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (options->Nickname == NULL) { + if (server_mode(options)) { + options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME); + } + } else { + if (!is_legal_nickname(options->Nickname)) { + tor_asprintf(msg, + "Nickname '%s', nicknames must be between 1 and 19 characters " + "inclusive, and must contain only the characters [a-zA-Z0-9].", + options->Nickname); + return -1; + } + } + + if (server_mode(options) && !options->ContactInfo) + log_notice(LD_CONFIG, "Your ContactInfo config option is not set. " + "Please consider setting it, so we can contact you if your server is " + "misconfigured or something else goes wrong."); + + const char *ContactInfo = options->ContactInfo; + if (ContactInfo && !string_is_utf8(ContactInfo, strlen(ContactInfo))) + REJECT("ContactInfo config option must be UTF-8."); + + return 0; +} + +/** Parse an authority type from <b>options</b>-\>PublishServerDescriptor + * and write it to <b>options</b>-\>PublishServerDescriptor_. Treat "1" + * as "v3" unless BridgeRelay is 1, in which case treat it as "bridge". + * Treat "0" as "". + * Return 0 on success or -1 if not a recognized authority type (in which + * case the value of PublishServerDescriptor_ is undefined). */ +static int +compute_publishserverdescriptor(or_options_t *options) +{ + smartlist_t *list = options->PublishServerDescriptor; + dirinfo_type_t *auth = &options->PublishServerDescriptor_; + *auth = NO_DIRINFO; + if (!list) /* empty list, answer is none */ + return 0; + SMARTLIST_FOREACH_BEGIN(list, const char *, string) { + if (!strcasecmp(string, "v1")) + log_warn(LD_CONFIG, "PublishServerDescriptor v1 has no effect, because " + "there are no v1 directory authorities anymore."); + else if (!strcmp(string, "1")) + if (options->BridgeRelay) + *auth |= BRIDGE_DIRINFO; + else + *auth |= V3_DIRINFO; + else if (!strcasecmp(string, "v2")) + log_warn(LD_CONFIG, "PublishServerDescriptor v2 has no effect, because " + "there are no v2 directory authorities anymore."); + else if (!strcasecmp(string, "v3")) + *auth |= V3_DIRINFO; + else if (!strcasecmp(string, "bridge")) + *auth |= BRIDGE_DIRINFO; + else if (!strcasecmp(string, "hidserv")) + log_warn(LD_CONFIG, + "PublishServerDescriptor hidserv is invalid. See " + "PublishHidServDescriptors."); + else if (!strcasecmp(string, "") || !strcmp(string, "0")) + /* no authority */; + else + return -1; + } SMARTLIST_FOREACH_END(string); + return 0; +} + +/** + * Validate the configured bridge distribution method from a BridgeDistribution + * config line. + * + * The input <b>bd</b>, is a string taken from the BridgeDistribution config + * line (if present). If the option wasn't set, return 0 immediately. The + * BridgeDistribution option is then validated. Currently valid, recognised + * options are: + * + * - "none" + * - "any" + * - "https" + * - "email" + * - "moat" + * - "hyphae" + * + * If the option string is unrecognised, a warning will be logged and 0 is + * returned. If the option string contains an invalid character, -1 is + * returned. + **/ +STATIC int +check_bridge_distribution_setting(const char *bd) +{ + if (bd == NULL) + return 0; + + const char *RECOGNIZED[] = { + "none", "any", "https", "email", "moat", "hyphae" + }; + unsigned i; + for (i = 0; i < ARRAY_LENGTH(RECOGNIZED); ++i) { + if (!strcmp(bd, RECOGNIZED[i])) + return 0; + } + + const char *cp = bd; + // Method = (KeywordChar | "_") + + while (TOR_ISALNUM(*cp) || *cp == '-' || *cp == '_') + ++cp; + + if (*cp == 0) { + log_warn(LD_CONFIG, "Unrecognized BridgeDistribution value %s. I'll " + "assume you know what you are doing...", escaped(bd)); + return 0; // we reached the end of the string; all is well + } else { + return -1; // we found a bad character in the string. + } +} + +/** + * Legacy validation/normalization function for the bridge relay options. + * Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_publish_server(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (compute_publishserverdescriptor(options) < 0) { + tor_asprintf(msg, "Unrecognized value in PublishServerDescriptor"); + return -1; + } + + if ((options->BridgeRelay + || options->PublishServerDescriptor_ & BRIDGE_DIRINFO) + && (options->PublishServerDescriptor_ & V3_DIRINFO)) { + REJECT("Bridges are not supposed to publish router descriptors to the " + "directory authorities. Please correct your " + "PublishServerDescriptor line."); + } + + if (options->BridgeDistribution) { + if (!options->BridgeRelay) { + REJECT("You set BridgeDistribution, but you didn't set BridgeRelay!"); + } + if (check_bridge_distribution_setting(options->BridgeDistribution) < 0) { + REJECT("Invalid BridgeDistribution value."); + } + } + + if (options->PublishServerDescriptor) + SMARTLIST_FOREACH(options->PublishServerDescriptor, const char *, pubdes, { + if (!strcmp(pubdes, "1") || !strcmp(pubdes, "0")) + if (smartlist_len(options->PublishServerDescriptor) > 1) { + COMPLAIN("You have passed a list of multiple arguments to the " + "PublishServerDescriptor option that includes 0 or 1. " + "0 or 1 should only be used as the sole argument. " + "This configuration will be rejected in a future release."); + break; + } + }); + + return 0; +} + +/** + * Legacy validation/normalization function for the relay padding options. + * Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_relay_padding(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (!server_mode(options)) + return 0; + + if (options->ConnectionPadding != -1) { + REJECT("Relays must use 'auto' for the ConnectionPadding setting."); + } + + if (options->ReducedConnectionPadding != 0) { + REJECT("Relays cannot set ReducedConnectionPadding. "); + } + + if (options->CircuitPadding == 0) { + REJECT("Relays cannot set CircuitPadding to 0. "); + } + + if (options->ReducedCircuitPadding == 1) { + REJECT("Relays cannot set ReducedCircuitPadding. "); + } + + return 0; +} + +/** + * Legacy validation/normalization function for the relay bandwidth options. + * Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_relay_bandwidth(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + /* 31851: the tests expect us to validate bandwidths, even when we are not + * in relay mode. */ + if (config_ensure_bandwidth_cap(&options->MaxAdvertisedBandwidth, + "MaxAdvertisedBandwidth", msg) < 0) + return -1; + if (config_ensure_bandwidth_cap(&options->RelayBandwidthRate, + "RelayBandwidthRate", msg) < 0) + return -1; + if (config_ensure_bandwidth_cap(&options->RelayBandwidthBurst, + "RelayBandwidthBurst", msg) < 0) + return -1; + if (config_ensure_bandwidth_cap(&options->PerConnBWRate, + "PerConnBWRate", msg) < 0) + return -1; + if (config_ensure_bandwidth_cap(&options->PerConnBWBurst, + "PerConnBWBurst", msg) < 0) + return -1; + + if (options->RelayBandwidthRate && !options->RelayBandwidthBurst) + options->RelayBandwidthBurst = options->RelayBandwidthRate; + if (options->RelayBandwidthBurst && !options->RelayBandwidthRate) + options->RelayBandwidthRate = options->RelayBandwidthBurst; + + if (server_mode(options)) { + const unsigned required_min_bw = + public_server_mode(options) ? + RELAY_REQUIRED_MIN_BANDWIDTH : BRIDGE_REQUIRED_MIN_BANDWIDTH; + const char * const optbridge = + public_server_mode(options) ? "" : "bridge "; + if (options->BandwidthRate < required_min_bw) { + tor_asprintf(msg, + "BandwidthRate is set to %d bytes/second. " + "For %sservers, it must be at least %u.", + (int)options->BandwidthRate, optbridge, + required_min_bw); + return -1; + } else if (options->MaxAdvertisedBandwidth < + required_min_bw/2) { + tor_asprintf(msg, + "MaxAdvertisedBandwidth is set to %d bytes/second. " + "For %sservers, it must be at least %u.", + (int)options->MaxAdvertisedBandwidth, optbridge, + required_min_bw/2); + return -1; + } + if (options->RelayBandwidthRate && + options->RelayBandwidthRate < required_min_bw) { + tor_asprintf(msg, + "RelayBandwidthRate is set to %d bytes/second. " + "For %sservers, it must be at least %u.", + (int)options->RelayBandwidthRate, optbridge, + required_min_bw); + return -1; + } + } + + /* 31851: the tests expect us to validate bandwidths, even when we are not + * in relay mode. */ + if (options->RelayBandwidthRate > options->RelayBandwidthBurst) + REJECT("RelayBandwidthBurst must be at least equal " + "to RelayBandwidthRate."); + + /* if they set relaybandwidth* really high but left bandwidth* + * at the default, raise the defaults. */ + if (options->RelayBandwidthRate > options->BandwidthRate) + options->BandwidthRate = options->RelayBandwidthRate; + if (options->RelayBandwidthBurst > options->BandwidthBurst) + options->BandwidthBurst = options->RelayBandwidthBurst; + + return 0; +} + +/** + * Legacy validation/normalization function for the relay bandwidth accounting + * options. Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_relay_accounting(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + /* 31851: the tests expect us to validate accounting, even when we are not + * in relay mode. */ + if (accounting_parse_options(options, 1)<0) + REJECT("Failed to parse accounting options. See logs for details."); + + if (options->AccountingMax) { + if (options->RendConfigLines && server_mode(options)) { + log_warn(LD_CONFIG, "Using accounting with a hidden service and an " + "ORPort is risky: your hidden service(s) and your public " + "address will all turn off at the same time, which may alert " + "observers that they are being run by the same party."); + } else if (config_count_key(options->RendConfigLines, + "HiddenServiceDir") > 1) { + log_warn(LD_CONFIG, "Using accounting with multiple hidden services is " + "risky: they will all turn off at the same time, which may " + "alert observers that they are being run by the same party."); + } + } + + options->AccountingRule = ACCT_MAX; + if (options->AccountingRule_option) { + if (!strcmp(options->AccountingRule_option, "sum")) + options->AccountingRule = ACCT_SUM; + else if (!strcmp(options->AccountingRule_option, "max")) + options->AccountingRule = ACCT_MAX; + else if (!strcmp(options->AccountingRule_option, "in")) + options->AccountingRule = ACCT_IN; + else if (!strcmp(options->AccountingRule_option, "out")) + options->AccountingRule = ACCT_OUT; + else + REJECT("AccountingRule must be 'sum', 'max', 'in', or 'out'"); + } + + return 0; +} + +/** Verify whether lst is a list of strings containing valid-looking + * comma-separated nicknames, or NULL. Will normalise <b>lst</b> to prefix '$' + * to any nickname or fingerprint that needs it. Also splits comma-separated + * list elements into multiple elements. Return 0 on success. + * Warn and return -1 on failure. + */ +static int +normalize_nickname_list(config_line_t **normalized_out, + const config_line_t *lst, const char *name, + char **msg) +{ + if (!lst) + return 0; + + config_line_t *new_nicknames = NULL; + config_line_t **new_nicknames_next = &new_nicknames; + + const config_line_t *cl; + for (cl = lst; cl; cl = cl->next) { + const char *line = cl->value; + if (!line) + continue; + + int valid_line = 1; + smartlist_t *sl = smartlist_new(); + smartlist_split_string(sl, line, ",", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0); + SMARTLIST_FOREACH_BEGIN(sl, char *, s) + { + char *normalized = NULL; + if (!is_legal_nickname_or_hexdigest(s)) { + // check if first char is dollar + if (s[0] != '$') { + // Try again but with a dollar symbol prepended + char *prepended; + tor_asprintf(&prepended, "$%s", s); + + if (is_legal_nickname_or_hexdigest(prepended)) { + // The nickname is valid when it's prepended, set it as the + // normalized version + normalized = prepended; + } else { + // Still not valid, free and fallback to error message + tor_free(prepended); + } + } + + if (!normalized) { + tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name); + valid_line = 0; + break; + } + } else { + normalized = tor_strdup(s); + } + + config_line_t *next = tor_malloc_zero(sizeof(*next)); + next->key = tor_strdup(cl->key); + next->value = normalized; + next->next = NULL; + + *new_nicknames_next = next; + new_nicknames_next = &next->next; + } SMARTLIST_FOREACH_END(s); + + SMARTLIST_FOREACH(sl, char *, s, tor_free(s)); + smartlist_free(sl); + + if (!valid_line) { + config_free_lines(new_nicknames); + return -1; + } + } + + *normalized_out = new_nicknames; + + return 0; +} + +#define ONE_MEGABYTE (UINT64_C(1) << 20) + +/* If we have less than 300 MB suggest disabling dircache */ +#define DIRCACHE_MIN_MEM_MB 300 +#define DIRCACHE_MIN_MEM_BYTES (DIRCACHE_MIN_MEM_MB*ONE_MEGABYTE) +#define STRINGIFY(val) #val + +/** Create a warning message for emitting if we are a dircache but may not have + * enough system memory, or if we are not a dircache but probably should be. + * Return -1 when a message is returned in *msg*, else return 0. */ +STATIC int +have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem, + char **msg) +{ + *msg = NULL; + /* XXX We should possibly be looking at MaxMemInQueues here + * unconditionally. Or we should believe total_mem unconditionally. */ + if (total_mem == 0) { + if (get_total_system_memory(&total_mem) < 0) { + total_mem = options->MaxMemInQueues >= SIZE_MAX ? + SIZE_MAX : (size_t)options->MaxMemInQueues; + } + } + if (options->DirCache) { + if (total_mem < DIRCACHE_MIN_MEM_BYTES) { + if (options->BridgeRelay) { + tor_asprintf(msg, "Running a Bridge with less than %d MB of memory " + "is not recommended.", DIRCACHE_MIN_MEM_MB); + } else { + tor_asprintf(msg, "Being a directory cache (default) with less than " + "%d MB of memory is not recommended and may consume " + "most of the available resources. Consider disabling " + "this functionality by setting the DirCache option " + "to 0.", DIRCACHE_MIN_MEM_MB); + } + } + } else { + if (total_mem >= DIRCACHE_MIN_MEM_BYTES) { + *msg = tor_strdup("DirCache is disabled and we are configured as a " + "relay. We will not become a Guard."); + } + } + return *msg == NULL ? 0 : -1; +} +#undef STRINGIFY + +/** + * Legacy validation/normalization function for the relay mode options. + * Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_relay_mode(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (server_mode(options) && options->RendConfigLines) + log_warn(LD_CONFIG, + "Tor is currently configured as a relay and a hidden service. " + "That's not very secure: you should probably run your hidden service " + "in a separate Tor process, at least -- see " + "https://trac.torproject.org/8742"); + + if (options->BridgeRelay && options->DirPort_set) { + log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling " + "DirPort"); + config_free_lines(options->DirPort_lines); + options->DirPort_lines = NULL; + options->DirPort_set = 0; + } + + if (options->DirPort_set && !options->DirCache) { + REJECT("DirPort configured but DirCache disabled. DirPort requires " + "DirCache."); + } + + if (options->BridgeRelay && !options->DirCache) { + REJECT("We're a bridge but DirCache is disabled. BridgeRelay requires " + "DirCache."); + } + + if (options->BridgeRelay == 1 && ! options->ORPort_set) + REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid " + "combination."); + + if (server_mode(options)) { + char *dircache_msg = NULL; + if (have_enough_mem_for_dircache(options, 0, &dircache_msg)) { + log_warn(LD_CONFIG, "%s", dircache_msg); + tor_free(dircache_msg); + } + } + + if (options->MyFamily_lines && options->BridgeRelay) { + log_warn(LD_CONFIG, "Listing a family for a bridge relay is not " + "supported: it can reveal bridge fingerprints to censors. " + "You should also make sure you aren't listing this bridge's " + "fingerprint in any other MyFamily."); + } + if (options->MyFamily_lines && !options->ContactInfo) { + log_warn(LD_CONFIG, "MyFamily is set but ContactInfo is not configured. " + "ContactInfo should always be set when MyFamily option is too."); + } + if (normalize_nickname_list(&options->MyFamily, + options->MyFamily_lines, "MyFamily", msg)) + return -1; + + if (options->ConstrainedSockets) { + if (options->DirPort_set) { + /* Providing cached directory entries while system TCP buffers are scarce + * will exacerbate the socket errors. Suggest that this be disabled. */ + COMPLAIN("You have requested constrained socket buffers while also " + "serving directory entries via DirPort. It is strongly " + "suggested that you disable serving directory requests when " + "system TCP buffer resources are scarce."); + } + } + + return 0; +} + +/** + * Legacy validation/normalization function for the relay testing options + * in options. Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_relay_testing(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2) + REJECT("SigningKeyLifetime is too short."); + if (options->TestingLinkCertLifetime < options->TestingAuthKeySlop*2) + REJECT("LinkCertLifetime is too short."); + if (options->TestingAuthKeyLifetime < options->TestingLinkKeySlop*2) + REJECT("TestingAuthKeyLifetime is too short."); + + return 0; +} + +/** Return 1 if any change from <b>old_options</b> to <b>new_options</b> + * will require us to rotate the CPU and DNS workers; else return 0. */ +static int +options_transition_affects_workers(const or_options_t *old_options, + const or_options_t *new_options) +{ + YES_IF_CHANGED_STRING(DataDirectory); + YES_IF_CHANGED_INT(NumCPUs); + YES_IF_CHANGED_LINELIST(ORPort_lines); + YES_IF_CHANGED_BOOL(ServerDNSSearchDomains); + YES_IF_CHANGED_BOOL(SafeLogging_); + YES_IF_CHANGED_BOOL(ClientOnly); + YES_IF_CHANGED_BOOL(LogMessageDomains); + YES_IF_CHANGED_LINELIST(Logs); + + if (server_mode(old_options) != server_mode(new_options) || + public_server_mode(old_options) != public_server_mode(new_options) || + dir_server_mode(old_options) != dir_server_mode(new_options)) + return 1; + + /* Nothing that changed matters. */ + return 0; +} + +/** Return 1 if any change from <b>old_options</b> to <b>new_options</b> + * will require us to generate a new descriptor; else return 0. */ +static int +options_transition_affects_descriptor(const or_options_t *old_options, + const or_options_t *new_options) +{ + /* XXX We can be smarter here. If your DirPort isn't being + * published and you just turned it off, no need to republish. Etc. */ + + YES_IF_CHANGED_STRING(DataDirectory); + YES_IF_CHANGED_STRING(Nickname); + YES_IF_CHANGED_STRING(Address); + YES_IF_CHANGED_LINELIST(ExitPolicy); + YES_IF_CHANGED_BOOL(ExitRelay); + YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate); + YES_IF_CHANGED_BOOL(ExitPolicyRejectLocalInterfaces); + YES_IF_CHANGED_BOOL(IPv6Exit); + YES_IF_CHANGED_LINELIST(ORPort_lines); + YES_IF_CHANGED_LINELIST(DirPort_lines); + YES_IF_CHANGED_LINELIST(DirPort_lines); + YES_IF_CHANGED_BOOL(ClientOnly); + YES_IF_CHANGED_BOOL(DisableNetwork); + YES_IF_CHANGED_BOOL(PublishServerDescriptor_); + YES_IF_CHANGED_STRING(ContactInfo); + YES_IF_CHANGED_STRING(BridgeDistribution); + YES_IF_CHANGED_LINELIST(MyFamily); + YES_IF_CHANGED_STRING(AccountingStart); + YES_IF_CHANGED_INT(AccountingMax); + YES_IF_CHANGED_INT(AccountingRule); + YES_IF_CHANGED_BOOL(DirCache); + YES_IF_CHANGED_BOOL(AssumeReachable); + + if (relay_get_effective_bwrate(old_options) != + relay_get_effective_bwrate(new_options) || + relay_get_effective_bwburst(old_options) != + relay_get_effective_bwburst(new_options) || + public_server_mode(old_options) != public_server_mode(new_options)) + return 1; + + return 0; +} + +/** Fetch the active option list, and take relay actions based on it. All of + * the things we do should survive being done repeatedly. If present, + * <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_relay(const or_options_t *old_options) +{ + const or_options_t *options = get_options(); + + const int transition_affects_workers = + old_options && options_transition_affects_workers(old_options, options); + + /* We want to reinit keys as needed before we do much of anything else: + keys are important, and other things can depend on them. */ + if (transition_affects_workers || + (authdir_mode_v3(options) && (!old_options || + !authdir_mode_v3(old_options)))) { + if (init_keys() < 0) { + log_warn(LD_BUG,"Error initializing keys; exiting"); + return -1; + } + } + + if (server_mode(options)) { + static int cdm_initialized = 0; + if (cdm_initialized == 0) { + cdm_initialized = 1; + consdiffmgr_configure(NULL); + consdiffmgr_validate(); + } + } + + /* Check for transitions that need action. */ + if (old_options) { + if (transition_affects_workers) { + log_info(LD_GENERAL, + "Worker-related options changed. Rotating workers."); + const int server_mode_turned_on = + server_mode(options) && !server_mode(old_options); + const int dir_server_mode_turned_on = + dir_server_mode(options) && !dir_server_mode(old_options); + + if (server_mode_turned_on || dir_server_mode_turned_on) { + cpu_init(); + } + + if (server_mode_turned_on) { + ip_address_changed(0); + if (have_completed_a_circuit() || !any_predicted_circuits(time(NULL))) + inform_testing_reachability(); + } + cpuworkers_rotate_keyinfo(); + } + } + + return 0; +} + +/** Fetch the active option list, and take relay accounting actions based on + * it. All of the things we do should survive being done repeatedly. If + * present, <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_relay_accounting(const or_options_t *old_options) +{ + (void)old_options; + + const or_options_t *options = get_options(); + + /* Set up accounting */ + if (accounting_parse_options(options, 0)<0) { + // LCOV_EXCL_START + log_warn(LD_BUG,"Error in previously validated accounting options"); + return -1; + // LCOV_EXCL_STOP + } + if (accounting_is_enabled(options)) + configure_accounting(time(NULL)); + + return 0; +} + +/** Fetch the active option list, and take relay bandwidth actions based on + * it. All of the things we do should survive being done repeatedly. If + * present, <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_relay_bandwidth(const or_options_t *old_options) +{ + const or_options_t *options = get_options(); + + /* Check for transitions that need action. */ + if (old_options) { + if (options->PerConnBWRate != old_options->PerConnBWRate || + options->PerConnBWBurst != old_options->PerConnBWBurst) + connection_or_update_token_buckets(get_connection_array(), options); + + if (options->RelayBandwidthRate != old_options->RelayBandwidthRate || + options->RelayBandwidthBurst != old_options->RelayBandwidthBurst) + connection_bucket_adjust(options); + } + + return 0; +} + +/** Fetch the active option list, and take bridge statistics actions based on + * it. All of the things we do should survive being done repeatedly. If + * present, <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_bridge_stats(const or_options_t *old_options) +{ + const or_options_t *options = get_options(); + +/* How long should we delay counting bridge stats after becoming a bridge? + * We use this so we don't count clients who used our bridge thinking it is + * a relay. If you change this, don't forget to change the log message + * below. It's 4 hours (the time it takes to stop being used by clients) + * plus some extra time for clock skew. */ +#define RELAY_BRIDGE_STATS_DELAY (6 * 60 * 60) + + /* Check for transitions that need action. */ + if (old_options) { + if (! bool_eq(options->BridgeRelay, old_options->BridgeRelay)) { + int was_relay = 0; + if (options->BridgeRelay) { + time_t int_start = time(NULL); + if (config_lines_eq(old_options->ORPort_lines,options->ORPort_lines)) { + int_start += RELAY_BRIDGE_STATS_DELAY; + was_relay = 1; + } + geoip_bridge_stats_init(int_start); + log_info(LD_CONFIG, "We are acting as a bridge now. Starting new " + "GeoIP stats interval%s.", was_relay ? " in 6 " + "hours from now" : ""); + } else { + geoip_bridge_stats_term(); + log_info(LD_GENERAL, "We are no longer acting as a bridge. " + "Forgetting GeoIP stats."); + } + } + } + + return 0; +} + +/** Fetch the active option list, and take relay statistics actions based on + * it. All of the things we do should survive being done repeatedly. If + * present, <b>old_options</b> contains the previous value of the options. + * + * Sets <b>*print_notice_out</b> if we enabled stats, and need to print + * a stats log using options_act_relay_stats_msg(). + * + * If loading the GeoIP file failed, sets DirReqStatistics and + * EntryStatistics to 0. This breaks the normalization/act ordering + * introduced in 29211. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_relay_stats(const or_options_t *old_options, + bool *print_notice_out) +{ + if (BUG(!print_notice_out)) + return -1; + + or_options_t *options = get_options_mutable(); + + if (options->CellStatistics || options->DirReqStatistics || + options->EntryStatistics || options->ExitPortStatistics || + options->ConnDirectionStatistics || + options->HiddenServiceStatistics) { + time_t now = time(NULL); + int print_notice = 0; + + if ((!old_options || !old_options->CellStatistics) && + options->CellStatistics) { + rep_hist_buffer_stats_init(now); + print_notice = 1; + } + if ((!old_options || !old_options->DirReqStatistics) && + options->DirReqStatistics) { + if (geoip_is_loaded(AF_INET)) { + geoip_dirreq_stats_init(now); + print_notice = 1; + } else { + /* disable statistics collection since we have no geoip file */ + /* 29211: refactor to avoid the normalisation/act inversion */ + options->DirReqStatistics = 0; + if (options->ORPort_set) + log_notice(LD_CONFIG, "Configured to measure directory request " + "statistics, but no GeoIP database found. " + "Please specify a GeoIP database using the " + "GeoIPFile option."); + } + } + if ((!old_options || !old_options->EntryStatistics) && + options->EntryStatistics && !should_record_bridge_info(options)) { + /* If we get here, we've started recording bridge info when we didn't + * do so before. Note that "should_record_bridge_info()" will + * always be false at this point, because of the earlier block + * that cleared EntryStatistics when public_server_mode() was false. + * We're leaving it in as defensive programming. */ + if (geoip_is_loaded(AF_INET) || geoip_is_loaded(AF_INET6)) { + geoip_entry_stats_init(now); + print_notice = 1; + } else { + options->EntryStatistics = 0; + log_notice(LD_CONFIG, "Configured to measure entry node " + "statistics, but no GeoIP database found. " + "Please specify a GeoIP database using the " + "GeoIPFile option."); + } + } + if ((!old_options || !old_options->ExitPortStatistics) && + options->ExitPortStatistics) { + rep_hist_exit_stats_init(now); + print_notice = 1; + } + if ((!old_options || !old_options->ConnDirectionStatistics) && + options->ConnDirectionStatistics) { + rep_hist_conn_stats_init(now); + } + if ((!old_options || !old_options->HiddenServiceStatistics) && + options->HiddenServiceStatistics) { + log_info(LD_CONFIG, "Configured to measure hidden service statistics."); + rep_hist_hs_stats_init(now); + } + if (print_notice) + *print_notice_out = 1; + } + + /* If we used to have statistics enabled but we just disabled them, + stop gathering them. */ + if (old_options && old_options->CellStatistics && + !options->CellStatistics) + rep_hist_buffer_stats_term(); + if (old_options && old_options->DirReqStatistics && + !options->DirReqStatistics) + geoip_dirreq_stats_term(); + if (old_options && old_options->EntryStatistics && + !options->EntryStatistics) + geoip_entry_stats_term(); + if (old_options && old_options->HiddenServiceStatistics && + !options->HiddenServiceStatistics) + rep_hist_hs_stats_term(); + if (old_options && old_options->ExitPortStatistics && + !options->ExitPortStatistics) + rep_hist_exit_stats_term(); + if (old_options && old_options->ConnDirectionStatistics && + !options->ConnDirectionStatistics) + rep_hist_conn_stats_term(); + + return 0; +} + +/** Print a notice about relay/dirauth stats being enabled. */ +void +options_act_relay_stats_msg(void) +{ + log_notice(LD_CONFIG, "Configured to measure statistics. Look for " + "the *-stats files that will first be written to the " + "data directory in 24 hours from now."); +} + +/** Fetch the active option list, and take relay descriptor actions based on + * it. All of the things we do should survive being done repeatedly. If + * present, <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_relay_desc(const or_options_t *old_options) +{ + const or_options_t *options = get_options(); + + /* Since our options changed, we might need to regenerate and upload our + * server descriptor. + */ + if (!old_options || + options_transition_affects_descriptor(old_options, options)) + mark_my_descriptor_dirty("config change"); + + return 0; +} + +/** Fetch the active option list, and take relay DoS actions based on + * it. All of the things we do should survive being done repeatedly. If + * present, <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_relay_dos(const or_options_t *old_options) +{ + const or_options_t *options = get_options(); + + /* DoS mitigation subsystem only applies to public relay. */ + if (public_server_mode(options)) { + /* If we are configured as a relay, initialize the subsystem. Even on HUP, + * this is safe to call as it will load data from the current options + * or/and the consensus. */ + dos_init(); + } else if (old_options && public_server_mode(old_options)) { + /* Going from relay to non relay, clean it up. */ + dos_free_all(); + } + + return 0; +} + +/** Fetch the active option list, and take dirport actions based on + * it. All of the things we do should survive being done repeatedly. If + * present, <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_relay_dir(const or_options_t *old_options) +{ + (void)old_options; + + const or_options_t *options = get_options(); + + if (!public_server_mode(options)) + return 0; + + /* Load the webpage we're going to serve every time someone asks for '/' on + our DirPort. */ + tor_free(global_dirfrontpagecontents); + if (options->DirPortFrontPage) { + global_dirfrontpagecontents = + read_file_to_str(options->DirPortFrontPage, 0, NULL); + if (!global_dirfrontpagecontents) { + log_warn(LD_CONFIG, + "DirPortFrontPage file '%s' not found. Continuing anyway.", + options->DirPortFrontPage); + } + } + + return 0; +} diff --git a/src/feature/relay/relay_config.h b/src/feature/relay/relay_config.h new file mode 100644 index 0000000000..57e6ddf94b --- /dev/null +++ b/src/feature/relay/relay_config.h @@ -0,0 +1,185 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file relay_config.h + * @brief Header for feature/relay/relay_config.c + **/ + +#ifndef TOR_FEATURE_RELAY_RELAY_CONFIG_H +#define TOR_FEATURE_RELAY_RELAY_CONFIG_H + +struct or_options_t; + +#ifdef HAVE_MODULE_RELAY + +#include "lib/cc/torint.h" +#include "lib/testsupport/testsupport.h" + +struct smartlist_t; + +int options_validate_relay_mode(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +MOCK_DECL(const char*, relay_get_dirportfrontpage, (void)); +void relay_config_free_all(void); + +uint32_t relay_get_effective_bwrate(const struct or_options_t *options); +uint32_t relay_get_effective_bwburst(const struct or_options_t *options); + +void port_warn_nonlocal_ext_orports(const struct smartlist_t *ports, + const char *portname); + +int port_parse_ports_relay(struct or_options_t *options, + char **msg, + struct smartlist_t *ports_out, + int *have_low_ports_out); +void port_update_port_set_relay(struct or_options_t *options, + const struct smartlist_t *ports); + +int options_validate_relay_os(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_relay_info(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_publish_server(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_relay_padding(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_relay_bandwidth(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_relay_accounting(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_relay_testing(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_act_relay(const struct or_options_t *old_options); +int options_act_relay_accounting(const struct or_options_t *old_options); +int options_act_relay_bandwidth(const struct or_options_t *old_options); +int options_act_bridge_stats(const struct or_options_t *old_options); + +int options_act_relay_stats(const struct or_options_t *old_options, + bool *print_notice_out); +void options_act_relay_stats_msg(void); + +int options_act_relay_desc(const struct or_options_t *old_options); +int options_act_relay_dos(const struct or_options_t *old_options); +int options_act_relay_dir(const struct or_options_t *old_options); + +#ifdef RELAY_CONFIG_PRIVATE + +STATIC int check_bridge_distribution_setting(const char *bd); +STATIC int have_enough_mem_for_dircache(const struct or_options_t *options, + size_t total_mem, char **msg); + +#endif /* defined(RELAY_CONFIG_PRIVATE) */ + +#else /* !defined(HAVE_MODULE_RELAY) */ + +#include "lib/cc/compat_compiler.h" + +/** When tor is compiled with the relay module disabled, it can't be + * configured as a relay or bridge. + * + * Always sets ClientOnly to 1. + * + * Returns -1 and sets msg to a newly allocated string, if ORPort, DirPort, + * DirCache, or BridgeRelay are set in options. Otherwise returns 0. */ +static inline int +options_validate_relay_mode(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg) +{ + (void)old_options; + + /* Only check the primary options for now, #29211 will disable more + * options. These ORPort and DirPort checks are too strict, and will + * reject valid configs that disable ports, like "ORPort 0". */ + if (options->DirCache || + options->BridgeRelay || + options->ORPort_lines || + options->DirPort_lines) { + /* REJECT() this configuration */ + *msg = tor_strdup("This tor was built with relay mode disabled. " + "It can not be configured with an ORPort, a DirPort, " + "DirCache 1, or BridgeRelay 1."); + return -1; + } + + return 0; +} + +#define relay_get_dirportfrontpage() \ + (NULL) +#define relay_config_free_all() \ + STMT_BEGIN STMT_END + +#define relay_get_effective_bwrate(options) \ + (((void)(options)),0) +#define relay_get_effective_bwburst(options) \ + (((void)(options)),0) + +#define port_warn_nonlocal_ext_orports(ports, portname) \ + (((void)(ports)),((void)(portname))) + +#define port_parse_ports_relay(options, msg, ports_out, have_low_ports_out) \ + (((void)(options)),((void)(msg)),((void)(ports_out)), \ + ((void)(have_low_ports_out)),0) +#define port_update_port_set_relay(options, ports) \ + (((void)(options)),((void)(ports))) + +#define options_validate_relay_os(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_validate_relay_info(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_validate_publish_server(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_validate_relay_padding(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_validate_relay_bandwidth(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_validate_relay_accounting(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_validate_relay_testing(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) + +#define options_act_relay(old_options) \ + (((void)(old_options)),0) +#define options_act_relay_accounting(old_options) \ + (((void)(old_options)),0) +#define options_act_relay_bandwidth(old_options) \ + (((void)(old_options)),0) +#define options_act_bridge_stats(old_options) \ + (((void)(old_options)),0) + +#define options_act_relay_stats(old_options, print_notice_out) \ + (((void)(old_options)),((void)(print_notice_out)),0) +#define options_act_relay_stats_msg() \ + STMT_BEGIN STMT_END + +#define options_act_relay_desc(old_options) \ + (((void)(old_options)),0) +#define options_act_relay_dos(old_options) \ + (((void)(old_options)),0) +#define options_act_relay_dir(old_options) \ + (((void)(old_options)),0) + +#endif /* defined(HAVE_MODULE_RELAY) */ + +#endif /* !defined(TOR_FEATURE_RELAY_RELAY_CONFIG_H) */ diff --git a/src/feature/relay/relay_periodic.h b/src/feature/relay/relay_periodic.h index b6ea83c749..84bc8a9780 100644 --- a/src/feature/relay/relay_periodic.h +++ b/src/feature/relay/relay_periodic.h @@ -12,7 +12,20 @@ #ifndef TOR_FEATURE_RELAY_RELAY_PERIODIC_H #define TOR_FEATURE_RELAY_RELAY_PERIODIC_H +#ifdef HAVE_MODULE_RELAY + void relay_register_periodic_events(void); void reschedule_descriptor_update_check(void); +#else /* !defined(HAVE_MODULE_RELAY) */ + +#include "lib/cc/compat_compiler.h" + +#define relay_register_periodic_events() \ + STMT_NIL +#define reschedule_descriptor_update_check() \ + STMT_NIL + +#endif /* defined(HAVE_MODULE_RELAY) */ + #endif /* !defined(TOR_FEATURE_RELAY_RELAY_PERIODIC_H) */ diff --git a/src/feature/relay/relay_sys.h b/src/feature/relay/relay_sys.h index 32e21d90d8..aa387369b5 100644 --- a/src/feature/relay/relay_sys.h +++ b/src/feature/relay/relay_sys.h @@ -12,6 +12,10 @@ #ifndef TOR_FEATURE_RELAY_RELAY_SYS_H #define TOR_FEATURE_RELAY_RELAY_SYS_H +#ifdef HAVE_MODULE_RELAY + extern const struct subsys_fns_t sys_relay; +#endif + #endif /* !defined(TOR_FEATURE_RELAY_RELAY_SYS_H) */ diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 5c79010934..410ed8c2f3 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -35,6 +35,7 @@ #include "feature/nodelist/routerlist.h" #include "feature/nodelist/torcert.h" #include "feature/relay/dns.h" +#include "feature/relay/relay_config.h" #include "feature/relay/router.h" #include "feature/relay/routerkeys.h" #include "feature/relay/routermode.h" @@ -886,15 +887,6 @@ init_keys_common(void) if (!key_lock) key_lock = tor_mutex_new(); - /* There are a couple of paths that put us here before we've asked - * openssl to initialize itself. */ - if (crypto_global_init(get_options()->HardwareAccel, - get_options()->AccelName, - get_options()->AccelDir)) { - log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting."); - return -1; - } - return 0; } @@ -1222,7 +1214,7 @@ router_should_be_dirserver(const or_options_t *options, int dir_port) * much larger effect on output than input so there is no reason to turn it * off if using AccountingRule in. */ int interval_length = accounting_get_interval_length(); - uint32_t effective_bw = get_effective_bwrate(options); + uint32_t effective_bw = relay_get_effective_bwrate(options); uint64_t acc_bytes; if (!interval_length) { log_warn(LD_BUG, "An accounting interval is not allowed to be zero " @@ -2041,10 +2033,10 @@ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out)) ri->protocol_list = tor_strdup(protover_get_supported_protocols()); /* compute ri->bandwidthrate as the min of various options */ - ri->bandwidthrate = get_effective_bwrate(options); + ri->bandwidthrate = relay_get_effective_bwrate(options); /* and compute ri->bandwidthburst similarly */ - ri->bandwidthburst = get_effective_bwburst(options); + ri->bandwidthburst = relay_get_effective_bwburst(options); /* Report bandwidth, unless we're hibernating or shutting down */ ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess(); diff --git a/src/feature/relay/routerkeys.h b/src/feature/relay/routerkeys.h index cde07b52c3..d2860718b2 100644 --- a/src/feature/relay/routerkeys.h +++ b/src/feature/relay/routerkeys.h @@ -1,6 +1,11 @@ /* Copyright (c) 2014-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file routerkeys.h + * @brief Header for routerkeys.c + **/ + #ifndef TOR_ROUTERKEYS_H #define TOR_ROUTERKEYS_H diff --git a/src/feature/relay/routermode.c b/src/feature/relay/routermode.c index 3613841b1e..92bcfaf8fa 100644 --- a/src/feature/relay/routermode.c +++ b/src/feature/relay/routermode.c @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file routermode.c + * @brief Check if we're running as a relay/cache. + **/ + #include "core/or/or.h" #include "app/config/config.h" diff --git a/src/feature/relay/transport_config.c b/src/feature/relay/transport_config.c new file mode 100644 index 0000000000..9d6be4bafd --- /dev/null +++ b/src/feature/relay/transport_config.c @@ -0,0 +1,307 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file transport_config.c + * @brief Code to interpret the user's configuration of Tor's server + * pluggable transports. + **/ + +#include "orconfig.h" +#define RELAY_TRANSPORT_CONFIG_PRIVATE +#include "feature/relay/transport_config.h" + +#include "lib/encoding/confline.h" +#include "lib/encoding/keyval.h" + +#include "lib/container/smartlist.h" + +/* Required for dirinfo_type_t in or_options_t */ +#include "core/or/or.h" +#include "app/config/config.h" + +#include "feature/relay/ext_orport.h" +#include "feature/relay/routermode.h" + +/* Copied from config.c, we will refactor later in 29211. */ +#define REJECT(arg) \ + STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END + +/** Given a ServerTransportListenAddr <b>line</b>, return its + * <address:port> string. Return NULL if the line was not + * well-formed. + * + * If <b>transport</b> is set, return NULL if the line is not + * referring to <b>transport</b>. + * + * The returned string is allocated on the heap and it's the + * responsibility of the caller to free it. */ +static char * +get_bindaddr_from_transport_listen_line(const char *line, + const char *transport) +{ + smartlist_t *items = NULL; + const char *parsed_transport = NULL; + char *addrport = NULL; + tor_addr_t addr; + uint16_t port = 0; + + items = smartlist_new(); + smartlist_split_string(items, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + + if (smartlist_len(items) < 2) { + log_warn(LD_CONFIG,"Too few arguments on ServerTransportListenAddr line."); + goto err; + } + + parsed_transport = smartlist_get(items, 0); + addrport = tor_strdup(smartlist_get(items, 1)); + + /* If 'transport' is given, check if it matches the one on the line */ + if (transport && strcmp(transport, parsed_transport)) + goto err; + + /* Validate addrport */ + if (tor_addr_port_parse(LOG_WARN, addrport, &addr, &port, -1)<0) { + log_warn(LD_CONFIG, "Error parsing ServerTransportListenAddr " + "address '%s'", addrport); + goto err; + } + + goto done; + + err: + tor_free(addrport); + addrport = NULL; + + done: + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + + return addrport; +} + +/** Given the name of a pluggable transport in <b>transport</b>, check + * the configuration file to see if the user has explicitly asked for + * it to listen on a specific port. Return a <address:port> string if + * so, otherwise NULL. */ +char * +pt_get_bindaddr_from_config(const char *transport) +{ + config_line_t *cl; + const or_options_t *options = get_options(); + + for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) { + char *bindaddr = + get_bindaddr_from_transport_listen_line(cl->value, transport); + if (bindaddr) + return bindaddr; + } + + return NULL; +} + +/** Given a ServerTransportOptions <b>line</b>, return a smartlist + * with the options. Return NULL if the line was not well-formed. + * + * If <b>transport</b> is set, return NULL if the line is not + * referring to <b>transport</b>. + * + * The returned smartlist and its strings are allocated on the heap + * and it's the responsibility of the caller to free it. */ +STATIC smartlist_t * +get_options_from_transport_options_line(const char *line, + const char *transport) +{ + smartlist_t *items = smartlist_new(); + smartlist_t *pt_options = smartlist_new(); + const char *parsed_transport = NULL; + + smartlist_split_string(items, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + + if (smartlist_len(items) < 2) { + log_warn(LD_CONFIG,"Too few arguments on ServerTransportOptions line."); + goto err; + } + + parsed_transport = smartlist_get(items, 0); + /* If 'transport' is given, check if it matches the one on the line */ + if (transport && strcmp(transport, parsed_transport)) + goto err; + + SMARTLIST_FOREACH_BEGIN(items, const char *, option) { + if (option_sl_idx == 0) /* skip the transport field (first field)*/ + continue; + + /* validate that it's a k=v value */ + if (!string_is_key_value(LOG_WARN, option)) { + log_warn(LD_CONFIG, "%s is not a k=v value.", escaped(option)); + goto err; + } + + /* add it to the options smartlist */ + smartlist_add_strdup(pt_options, option); + log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option)); + } SMARTLIST_FOREACH_END(option); + + goto done; + + err: + SMARTLIST_FOREACH(pt_options, char*, s, tor_free(s)); + smartlist_free(pt_options); + pt_options = NULL; + + done: + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + + return pt_options; +} + +/** Given the name of a pluggable transport in <b>transport</b>, check + * the configuration file to see if the user has asked us to pass any + * parameters to the pluggable transport. Return a smartlist + * containing the parameters, otherwise NULL. */ +smartlist_t * +pt_get_options_for_server_transport(const char *transport) +{ + config_line_t *cl; + const or_options_t *options = get_options(); + + for (cl = options->ServerTransportOptions; cl; cl = cl->next) { + smartlist_t *options_sl = + get_options_from_transport_options_line(cl->value, transport); + if (options_sl) + return options_sl; + } + + return NULL; +} + +/** + * Legacy validation/normalization function for the server transport options. + * Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_server_transport(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + config_line_t *cl; + + if (options->ServerTransportPlugin && !server_mode(options)) { + log_notice(LD_GENERAL, "Tor is not configured as a relay but you specified" + " a ServerTransportPlugin line (%s). The ServerTransportPlugin " + "line will be ignored.", + escaped(options->ServerTransportPlugin->value)); + } + + if (options->ServerTransportListenAddr && !options->ServerTransportPlugin) { + log_notice(LD_GENERAL, "You need at least a single managed-proxy to " + "specify a transport listen address. The " + "ServerTransportListenAddr line will be ignored."); + } + + for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { + if (pt_parse_transport_line(options, cl->value, 1, 1) < 0) + REJECT("Invalid server transport line. See logs for details."); + } + + for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) { + /** If get_bindaddr_from_transport_listen_line() fails with + 'transport' being NULL, it means that something went wrong + while parsing the ServerTransportListenAddr line. */ + char *bindaddr = get_bindaddr_from_transport_listen_line(cl->value, NULL); + if (!bindaddr) + REJECT("ServerTransportListenAddr did not parse. See logs for details."); + tor_free(bindaddr); + } + + for (cl = options->ServerTransportOptions; cl; cl = cl->next) { + /** If get_options_from_transport_options_line() fails with + 'transport' being NULL, it means that something went wrong + while parsing the ServerTransportOptions line. */ + smartlist_t *options_sl = + get_options_from_transport_options_line(cl->value, NULL); + if (!options_sl) + REJECT("ServerTransportOptions did not parse. See logs for details."); + + SMARTLIST_FOREACH(options_sl, char *, cp, tor_free(cp)); + smartlist_free(options_sl); + } + + return 0; +} + +/** Fetch the active option list, and take server pluggable transport actions + * based on it. All of the things we do should survive being done repeatedly. + * If present, <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_server_transport(const or_options_t *old_options) +{ + (void)old_options; + + config_line_t *cl; + const or_options_t *options = get_options(); + int running_tor = options->command == CMD_RUN_TOR; + + /* If we are a bridge with a pluggable transport proxy but no + Extended ORPort, inform the user that they are missing out. */ + if (options->ServerTransportPlugin && + !options->ExtORPort_lines) { + log_notice(LD_CONFIG, "We use pluggable transports but the Extended " + "ORPort is disabled. Tor and your pluggable transports proxy " + "communicate with each other via the Extended ORPort so it " + "is suggested you enable it: it will also allow your Bridge " + "to collect statistics about its clients that use pluggable " + "transports. Please enable it using the ExtORPort torrc option " + "(e.g. set 'ExtORPort auto')."); + } + + /* If we have an ExtORPort, initialize its auth cookie. */ + if (running_tor && + init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) { + log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file."); + return -1; + } + + if (!options->DisableNetwork) { + if (options->ServerTransportPlugin) { + for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { + if (pt_parse_transport_line(options, cl->value, 0, 1) < 0) { + // LCOV_EXCL_START + log_warn(LD_BUG, + "Previously validated ServerTransportPlugin line " + "could not be added!"); + return -1; + // LCOV_EXCL_STOP + } + } + } + } + + return 0; +} diff --git a/src/feature/relay/transport_config.h b/src/feature/relay/transport_config.h new file mode 100644 index 0000000000..38f804b4be --- /dev/null +++ b/src/feature/relay/transport_config.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file transport_config.h + * @brief Header for feature/relay/transport_config.c + **/ + +#ifndef TOR_FEATURE_RELAY_TRANSPORT_CONFIG_H +#define TOR_FEATURE_RELAY_TRANSPORT_CONFIG_H + +#ifdef HAVE_MODULE_RELAY + +#include "lib/testsupport/testsupport.h" + +struct or_options_t; +struct smartlist_t; + +int options_validate_server_transport(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +char *pt_get_bindaddr_from_config(const char *transport); +struct smartlist_t *pt_get_options_for_server_transport(const char *transport); + +int options_act_server_transport(const struct or_options_t *old_options); + +#ifdef RELAY_TRANSPORT_CONFIG_PRIVATE + +STATIC struct smartlist_t *get_options_from_transport_options_line( + const char *line, + const char *transport); + +#endif /* defined(RELAY_TRANSPORT_CONFIG_PRIVATE) */ + +#else /* !defined(HAVE_MODULE_RELAY) */ + +/** When tor is compiled with the relay module disabled, it can't be + * configured with server pluggable transports. + * + * Returns -1 and sets msg to a newly allocated string, if ExtORPort, + * ServerTransportPlugin, ServerTransportListenAddr, or + * ServerTransportOptions are set in options. Otherwise returns 0. */ +static inline int +options_validate_server_transport(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg) +{ + (void)old_options; + + /* These ExtORPort checks are too strict, and will reject valid configs + * that disable ports, like "ExtORPort 0". */ + if (options->ServerTransportPlugin || + options->ServerTransportListenAddr || + options->ServerTransportOptions || + options->ExtORPort_lines) { + /* REJECT() this configuration */ + *msg = tor_strdup("This tor was built with relay mode disabled. " + "It can not be configured with an ExtORPort, " + "a ServerTransportPlugin, a ServerTransportListenAddr, " + "or ServerTransportOptions."); + return -1; + } + + return 0; +} + +#define pt_get_bindaddr_from_config(transport) \ + (((void)(transport)),NULL) + +/* 31851: called from client/transports.c, but only from server code */ +#define pt_get_options_for_server_transport(transport) \ + (((void)(transport)),NULL) + +#define options_validate_server_transport(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_act_server_transport(old_options) \ + (((void)(old_options)),0) + +#endif /* defined(HAVE_MODULE_RELAY) */ + +#endif /* !defined(TOR_FEATURE_RELAY_TRANSPORT_CONFIG_H) */ diff --git a/src/feature/rend/feature_rend.md b/src/feature/rend/feature_rend.md new file mode 100644 index 0000000000..bfd8ae3dbc --- /dev/null +++ b/src/feature/rend/feature_rend.md @@ -0,0 +1,7 @@ +@dir /feature/rend +@brief feature/rend: version 2 (old) hidden services + +This directory implements the v2 onion service protocol, +as specified in +[rend-spec-v2.txt](https://gitweb.torproject.org/torspec.git/tree/rend-spec-v2.txt). + diff --git a/src/feature/rend/rend_authorized_client_st.h b/src/feature/rend/rend_authorized_client_st.h index 51a1798fcb..0819f2134a 100644 --- a/src/feature/rend/rend_authorized_client_st.h +++ b/src/feature/rend/rend_authorized_client_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file rend_authorized_client_st.h + * @brief Hidden-service authorized client structure. + **/ + #ifndef REND_AUTHORIZED_CLIENT_ST_H #define REND_AUTHORIZED_CLIENT_ST_H @@ -15,4 +20,3 @@ struct rend_authorized_client_t { }; #endif /* !defined(REND_AUTHORIZED_CLIENT_ST_H) */ - diff --git a/src/feature/rend/rend_encoded_v2_service_descriptor_st.h b/src/feature/rend/rend_encoded_v2_service_descriptor_st.h index bd8a60f0d9..5eec600fd1 100644 --- a/src/feature/rend/rend_encoded_v2_service_descriptor_st.h +++ b/src/feature/rend/rend_encoded_v2_service_descriptor_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file rend_encoded_v2_service_descriptor_st.h + * @brief Encoded v2 HS descriptor structure. + **/ + #ifndef REND_ENCODED_V2_SERVICE_DESCRIPTOR_ST_H #define REND_ENCODED_V2_SERVICE_DESCRIPTOR_ST_H @@ -14,4 +19,3 @@ struct rend_encoded_v2_service_descriptor_t { }; #endif /* !defined(REND_ENCODED_V2_SERVICE_DESCRIPTOR_ST_H) */ - diff --git a/src/feature/rend/rend_intro_point_st.h b/src/feature/rend/rend_intro_point_st.h index 4882b62752..9acf155708 100644 --- a/src/feature/rend/rend_intro_point_st.h +++ b/src/feature/rend/rend_intro_point_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file rend_intro_point_st.h + * @brief v2 hidden service introduction point structure. + **/ + #ifndef REND_INTRO_POINT_ST_H #define REND_INTRO_POINT_ST_H diff --git a/src/feature/rend/rend_service_descriptor_st.h b/src/feature/rend/rend_service_descriptor_st.h index ff7627ce96..bdfe87dcae 100644 --- a/src/feature/rend/rend_service_descriptor_st.h +++ b/src/feature/rend/rend_service_descriptor_st.h @@ -4,6 +4,11 @@ * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file rend_service_descriptor_st.h + * @brief Parsed v2 HS descriptor structure. + **/ + #ifndef REND_SERVICE_DESCRIPTOR_ST_H #define REND_SERVICE_DESCRIPTOR_ST_H @@ -31,4 +36,3 @@ struct rend_service_descriptor_t { }; #endif /* !defined(REND_SERVICE_DESCRIPTOR_ST_H) */ - diff --git a/src/feature/rend/rendcache.c b/src/feature/rend/rendcache.c index c3f86d8c82..2d8de2a80d 100644 --- a/src/feature/rend/rendcache.c +++ b/src/feature/rend/rendcache.c @@ -228,6 +228,17 @@ rend_cache_entry_free_void(void *p) rend_cache_entry_free_(p); } +/** Check if a failure cache entry exists for the given intro point. */ +bool +rend_cache_intro_failure_exists(const char *service_id, + const uint8_t *intro_identity) +{ + tor_assert(service_id); + tor_assert(intro_identity); + + return cache_failure_intro_lookup(intro_identity, service_id, NULL); +} + /** Free all storage held by the service descriptor cache. */ void rend_cache_free_all(void) diff --git a/src/feature/rend/rendcache.h b/src/feature/rend/rendcache.h index aec97eabb8..c83f36d189 100644 --- a/src/feature/rend/rendcache.h +++ b/src/feature/rend/rendcache.h @@ -80,6 +80,8 @@ int rend_cache_store_v2_desc_as_client(const char *desc, rend_cache_entry_t **entry); size_t rend_cache_get_total_allocation(void); +bool rend_cache_intro_failure_exists(const char *service_id, + const uint8_t *intro_identity); void rend_cache_intro_failure_note(rend_intro_point_failure_t failure, const uint8_t *identity, const char *service_id); diff --git a/src/feature/rend/rendclient.c b/src/feature/rend/rendclient.c index 2540066dfc..6f4ee8b8c1 100644 --- a/src/feature/rend/rendclient.c +++ b/src/feature/rend/rendclient.c @@ -1048,18 +1048,30 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry, const or_options_t *options = get_options(); smartlist_t *usable_nodes; int n_excluded = 0; + char service_id[REND_SERVICE_ID_LEN_BASE32 + 1]; /* We'll keep a separate list of the usable nodes. If this becomes empty, * no nodes are usable. */ usable_nodes = smartlist_new(); smartlist_add_all(usable_nodes, entry->parsed->intro_nodes); + /* Get service ID so we can use it to query the failure cache. If we fail to + * parse it, this cache entry is no good. */ + if (BUG(rend_get_service_id(entry->parsed->pk, service_id) < 0)) { + smartlist_free(usable_nodes); + return NULL; + } + /* Remove the intro points that have timed out during this HS * connection attempt from our list of usable nodes. */ - SMARTLIST_FOREACH(usable_nodes, rend_intro_point_t *, ip, - if (ip->timed_out) { - SMARTLIST_DEL_CURRENT(usable_nodes, ip); - }); + SMARTLIST_FOREACH_BEGIN(usable_nodes, const rend_intro_point_t *, ip) { + bool failed_intro = + rend_cache_intro_failure_exists(service_id, + (const uint8_t *) ip->extend_info->identity_digest); + if (ip->timed_out || failed_intro) { + SMARTLIST_DEL_CURRENT(usable_nodes, ip); + }; + } SMARTLIST_FOREACH_END(ip); again: if (smartlist_len(usable_nodes) == 0) { diff --git a/src/feature/stats/feature_stats.md b/src/feature/stats/feature_stats.md new file mode 100644 index 0000000000..d205fe5571 --- /dev/null +++ b/src/feature/stats/feature_stats.md @@ -0,0 +1,10 @@ +@dir /feature/stats +@brief feature/stats: Relay statistics. Also, port prediction. + +This module collects anonymized relay statistics in order to publish them in +relays' routerinfo and extrainfo documents. + +Additionally, it contains predict_ports.c, which remembers which ports we've +visited recently as a client, so we can make sure we have open circuits that +support them. + |