/* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2021, 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 line, return its * string. Return NULL if the line was not * well-formed. * * If transport is set, return NULL if the line is not * referring to transport. * * 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 transport, check * the configuration file to see if the user has explicitly asked for * it to listen on a specific port. Return a 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 line, return a smartlist * with the options. Return NULL if the line was not well-formed. * * If transport is set, return NULL if the line is not * referring to transport. * * 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 transport, 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, old_options 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; }