aboutsummaryrefslogtreecommitdiff
path: root/src/or/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/config.c')
-rw-r--r--src/or/config.c632
1 files changed, 539 insertions, 93 deletions
diff --git a/src/or/config.c b/src/or/config.c
index 4e08f3c3a5..37b42e891a 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -10,7 +10,6 @@
**/
#define CONFIG_PRIVATE
-
#include "or.h"
#include "addressmap.h"
#include "channel.h"
@@ -40,11 +39,13 @@
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
+#include "sandbox.h"
#include "util.h"
#include "routerlist.h"
#include "routerset.h"
#include "statefile.h"
#include "transports.h"
+#include "ext_orport.h"
#ifdef _WIN32
#include <shlobj.h>
#endif
@@ -230,6 +231,7 @@ static config_var_t option_vars_[] = {
V(ExitPolicyRejectPrivate, BOOL, "1"),
V(ExitPortStatistics, BOOL, "0"),
V(ExtendAllowPrivateAddresses, BOOL, "0"),
+ VPORT(ExtORPort, LINELIST, NULL),
V(ExtraInfoStatistics, BOOL, "1"),
V(FallbackDir, LINELIST, NULL),
@@ -281,6 +283,7 @@ static config_var_t option_vars_[] = {
V(IPv6Exit, BOOL, "0"),
VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL),
V(ServerTransportListenAddr, LINELIST, NULL),
+ V(ServerTransportOptions, LINELIST, NULL),
V(Socks4Proxy, STRING, NULL),
V(Socks5Proxy, STRING, NULL),
V(Socks5ProxyUsername, STRING, NULL),
@@ -370,6 +373,7 @@ static config_var_t option_vars_[] = {
V(RunAsDaemon, BOOL, "0"),
// V(RunTesting, BOOL, "0"),
OBSOLETE("RunTesting"), // currently unused
+ V(Sandbox, BOOL, "0"),
V(SafeLogging, STRING, "1"),
V(SafeSocks, BOOL, "0"),
V(ServerDNSAllowBrokenConfig, BOOL, "1"),
@@ -405,7 +409,7 @@ static config_var_t option_vars_[] = {
V(UseEntryGuards, BOOL, "1"),
V(UseEntryGuardsAsDirGuards, BOOL, "1"),
V(UseMicrodescriptors, AUTOBOOL, "auto"),
- V(UseNTorHandshake, AUTOBOOL, "auto"),
+ V(UseNTorHandshake, AUTOBOOL, "1"),
V(User, STRING, NULL),
V(UserspaceIOCPBuffers, BOOL, "0"),
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
@@ -414,6 +418,7 @@ static config_var_t option_vars_[] = {
V(TestingV3AuthInitialVotingInterval, INTERVAL, "30 minutes"),
V(TestingV3AuthInitialVoteDelay, INTERVAL, "5 minutes"),
V(TestingV3AuthInitialDistDelay, INTERVAL, "5 minutes"),
+ V(TestingV3AuthVotingStartOffset, INTERVAL, "0"),
V(V3AuthVotingInterval, INTERVAL, "1 hour"),
V(V3AuthVoteDelay, INTERVAL, "5 minutes"),
V(V3AuthDistDelay, INTERVAL, "5 minutes"),
@@ -434,6 +439,23 @@ static config_var_t option_vars_[] = {
VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL),
V(MinUptimeHidServDirectoryV2, INTERVAL, "25 hours"),
V(VoteOnHidServDirectoriesV2, BOOL, "1"),
+ V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, "
+ "300, 900, 2147483647"),
+ V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 60, 300, 600, "
+ "2147483647"),
+ V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, "
+ "300, 600, 1800, 1800, 1800, 1800, "
+ "1800, 3600, 7200"),
+ V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, "
+ "300, 600, 1800, 3600, 3600, 3600, "
+ "10800, 21600, 43200"),
+ V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"),
+ V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"),
+ V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"),
+ V(TestingConsensusMaxDownloadTries, UINT, "8"),
+ V(TestingDescriptorMaxDownloadTries, UINT, "8"),
+ V(TestingMicrodescMaxDownloadTries, UINT, "8"),
+ V(TestingCertMaxDownloadTries, UINT, "8"),
VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "0"),
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
@@ -459,9 +481,25 @@ static const config_var_t testing_tor_network_defaults[] = {
V(TestingV3AuthInitialVotingInterval, INTERVAL, "5 minutes"),
V(TestingV3AuthInitialVoteDelay, INTERVAL, "20 seconds"),
V(TestingV3AuthInitialDistDelay, INTERVAL, "20 seconds"),
+ V(TestingV3AuthVotingStartOffset, INTERVAL, "0"),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"),
V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"),
V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
+ V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 5, 10, 15, "
+ "20, 30, 60"),
+ V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, 15, 20, "
+ "30, 60"),
+ V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
+ "15, 20, 30, 60"),
+ V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
+ "15, 20, 30, 60"),
+ V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"),
+ V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"),
+ V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"),
+ V(TestingConsensusMaxDownloadTries, UINT, "80"),
+ V(TestingDescriptorMaxDownloadTries, UINT, "80"),
+ V(TestingMicrodescMaxDownloadTries, UINT, "80"),
+ V(TestingCertMaxDownloadTries, UINT, "80"),
VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"),
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
@@ -474,9 +512,6 @@ static const config_var_t testing_tor_network_defaults[] = {
#ifdef _WIN32
static char *get_windows_conf_root(void);
#endif
-static int options_validate(or_options_t *old_options,
- or_options_t *options,
- int from_setconf, char **msg);
static int options_act_reversible(const or_options_t *old_options, char **msg);
static int options_act(const or_options_t *old_options);
static int options_transition_allowed(const or_options_t *old,
@@ -486,9 +521,8 @@ static int options_transition_affects_workers(
const or_options_t *old_options, const or_options_t *new_options);
static int options_transition_affects_descriptor(
const or_options_t *old_options, const or_options_t *new_options);
-static int check_nickname_list(const char *lst, const char *name, char **msg);
+static int check_nickname_list(char **lst, const char *name, char **msg);
-static int parse_bridge_line(const char *line, int validate_only);
static int parse_client_transport_line(const char *line, int validate_only);
static int parse_server_transport_line(const char *line, int validate_only);
@@ -521,7 +555,7 @@ static void config_maybe_load_geoip_files_(const or_options_t *options,
#define OR_OPTIONS_MAGIC 9090909
/** Configuration format for or_options_t. */
-static config_format_t options_format = {
+STATIC config_format_t options_format = {
sizeof(or_options_t),
OR_OPTIONS_MAGIC,
STRUCT_OFFSET(or_options_t, magic_),
@@ -677,7 +711,7 @@ get_short_version(void)
/** Release additional memory allocated in options
*/
-static void
+STATIC void
or_options_free(or_options_t *options)
{
if (!options)
@@ -971,6 +1005,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
int set_conn_limit = 0;
int r = -1;
int logs_marked = 0;
+ int old_min_log_level = get_min_log_level();
/* Daemonize _first_, since we only want to open most of this stuff in
* the subprocess. Libevent bases can't be reliably inherited across
@@ -1109,6 +1144,8 @@ options_act_reversible(const or_options_t *old_options, char **msg)
goto rollback;
}
+ sandbox_set_debugging_fd(get_err_logging_fd());
+
commit:
r = 0;
if (logs_marked) {
@@ -1119,6 +1156,13 @@ options_act_reversible(const or_options_t *old_options, char **msg)
control_adjust_event_log_severity();
tor_free(severity);
}
+ if (get_min_log_level() >= LOG_INFO &&
+ get_min_log_level() != old_min_log_level) {
+ log_warn(LD_GENERAL, "Your log may contain sensitive information - you're "
+ "logging above \"notice\". Please log safely. Don't log unless "
+ "it serves an important reason. Overwrite the log afterwards.");
+ }
+
SMARTLIST_FOREACH(replaced_listeners, connection_t *, conn,
{
log_notice(LD_NET, "Closing old %s on %s:%d",
@@ -1301,14 +1345,23 @@ options_act(const or_options_t *old_options)
}
#endif
+ if (options->SafeLogging_ != SAFELOG_SCRUB_ALL &&
+ (!old_options || old_options->SafeLogging_ != options->SafeLogging_)) {
+ log_warn(LD_GENERAL, "Your log may contain sensitive information - you "
+ "disabled SafeLogging. Please log safely. Don't log unless it "
+ "serves an important reason. Overwrite the log afterwards.");
+ }
+
if (options->Bridges) {
mark_bridge_list();
for (cl = options->Bridges; cl; cl = cl->next) {
- if (parse_bridge_line(cl->value, 0)<0) {
+ bridge_line_t *bridge_line = parse_bridge_line(cl->value);
+ if (!bridge_line) {
log_warn(LD_BUG,
"Previously validated Bridge line could not be added!");
return -1;
}
+ bridge_add_from_config(bridge_line);
}
sweep_bridge_list();
}
@@ -1422,8 +1475,14 @@ options_act(const or_options_t *old_options)
return -1;
}
- if (init_cookie_authentication(options->CookieAuthentication) < 0) {
- log_warn(LD_CONFIG,"Error creating cookie authentication file.");
+ if (init_control_cookie_authentication(options->CookieAuthentication) < 0) {
+ log_warn(LD_CONFIG,"Error creating control cookie authentication file.");
+ return -1;
+ }
+
+ /* If we have an ExtORPort, initialize its auth cookie. */
+ if (init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) {
+ log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file.");
return -1;
}
@@ -1851,7 +1910,8 @@ options_trial_assign(config_line_t *list, int use_defaults,
return r;
}
- if (options_validate(get_options_mutable(), trial_options, 1, msg) < 0) {
+ if (options_validate(get_options_mutable(), trial_options,
+ global_default_options, 1, msg) < 0) {
config_free(&options_format, trial_options);
return SETOPT_ERR_PARSE; /*XXX make this a separate return value. */
}
@@ -2285,10 +2345,11 @@ compute_publishserverdescriptor(or_options_t *options)
* */
#define RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT (10)
-/** Return 0 if every setting in <b>options</b> is reasonable, and a
- * permissible transition from <b>old_options</b>. Else return -1.
- * Should have no side effects, except for normalizing the contents of
- * <b>options</b>.
+/** Return 0 if every setting in <b>options</b> is reasonable, is a
+ * permissible transition from <b>old_options</b>, and none of the
+ * testing-only settings differ from <b>default_options</b> unless in
+ * testing mode. Else return -1. Should have no side effects, except for
+ * normalizing the contents of <b>options</b>.
*
* On error, tor_strdup an error explanation into *<b>msg</b>.
*
@@ -2297,9 +2358,9 @@ compute_publishserverdescriptor(or_options_t *options)
* Log line should stay empty. If it's 0, then give us a default log
* if there are no logs defined.
*/
-static int
+STATIC int
options_validate(or_options_t *old_options, or_options_t *options,
- int from_setconf, char **msg)
+ or_options_t *default_options, int from_setconf, char **msg)
{
int i;
config_line_t *cl;
@@ -2972,14 +3033,14 @@ options_validate(or_options_t *old_options, or_options_t *options,
size_t len;
len = strlen(options->Socks5ProxyUsername);
- if (len < 1 || len > 255)
+ if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE)
REJECT("Socks5ProxyUsername must be between 1 and 255 characters.");
if (!options->Socks5ProxyPassword)
REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
len = strlen(options->Socks5ProxyPassword);
- if (len < 1 || len > 255)
+ if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE)
REJECT("Socks5ProxyPassword must be between 1 and 255 characters.");
} else if (options->Socks5ProxyPassword)
REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
@@ -3039,7 +3100,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
"You should also make sure you aren't listing this bridge's "
"fingerprint in any other MyFamily.");
}
- if (check_nickname_list(options->MyFamily, "MyFamily", msg))
+ if (check_nickname_list(&options->MyFamily, "MyFamily", msg))
return -1;
for (cl = options->NodeFamilies; cl; cl = cl->next) {
routerset_t *rs = routerset_new();
@@ -3063,8 +3124,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("If you set UseBridges, you must set TunnelDirConns.");
for (cl = options->Bridges; cl; cl = cl->next) {
- if (parse_bridge_line(cl->value, 1)<0)
- REJECT("Bridge line did not parse. See logs for details.");
+ bridge_line_t *bridge_line = parse_bridge_line(cl->value);
+ if (!bridge_line)
+ REJECT("Bridge line did not parse. See logs for details.");
+ bridge_line_free(bridge_line);
}
for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
@@ -3100,6 +3163,19 @@ options_validate(or_options_t *old_options, or_options_t *options,
"ServerTransportListenAddr line will be ignored.");
}
+ 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);
+ }
+
if (options->ConstrainedSockets) {
/* If the user wants to constrain socket buffer use, make sure the desired
* limit is between MIN|MAX_TCPSOCK_BUFFER in k increments. */
@@ -3192,35 +3268,46 @@ options_validate(or_options_t *old_options, or_options_t *options,
"ignore you.");
}
- /*XXXX checking for defaults manually like this is a bit fragile.*/
-
- /* Keep changes to hard-coded values synchronous to man page and default
- * values table. */
- if (options->TestingV3AuthInitialVotingInterval != 30*60 &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
- REJECT("TestingV3AuthInitialVotingInterval may only be changed in testing "
- "Tor networks!");
- } else if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL) {
+#define CHECK_DEFAULT(arg) \
+ STMT_BEGIN \
+ if (!options->TestingTorNetwork && \
+ !options->UsingTestNetworkDefaults_ && \
+ !config_is_same(&options_format,options, \
+ default_options,#arg)) { \
+ REJECT(#arg " may only be changed in testing Tor " \
+ "networks!"); \
+ } STMT_END
+ CHECK_DEFAULT(TestingV3AuthInitialVotingInterval);
+ CHECK_DEFAULT(TestingV3AuthInitialVoteDelay);
+ CHECK_DEFAULT(TestingV3AuthInitialDistDelay);
+ CHECK_DEFAULT(TestingV3AuthVotingStartOffset);
+ CHECK_DEFAULT(TestingAuthDirTimeToLearnReachability);
+ CHECK_DEFAULT(TestingEstimatedDescriptorPropagationTime);
+ CHECK_DEFAULT(TestingServerDownloadSchedule);
+ CHECK_DEFAULT(TestingClientDownloadSchedule);
+ CHECK_DEFAULT(TestingServerConsensusDownloadSchedule);
+ CHECK_DEFAULT(TestingClientConsensusDownloadSchedule);
+ CHECK_DEFAULT(TestingBridgeDownloadSchedule);
+ CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest);
+ CHECK_DEFAULT(TestingDirConnectionMaxStall);
+ CHECK_DEFAULT(TestingConsensusMaxDownloadTries);
+ CHECK_DEFAULT(TestingDescriptorMaxDownloadTries);
+ CHECK_DEFAULT(TestingMicrodescMaxDownloadTries);
+ CHECK_DEFAULT(TestingCertMaxDownloadTries);
+#undef CHECK_DEFAULT
+
+ if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL) {
REJECT("TestingV3AuthInitialVotingInterval is insanely low.");
} else if (((30*60) % options->TestingV3AuthInitialVotingInterval) != 0) {
REJECT("TestingV3AuthInitialVotingInterval does not divide evenly into "
"30 minutes.");
}
- if (options->TestingV3AuthInitialVoteDelay != 5*60 &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
-
- REJECT("TestingV3AuthInitialVoteDelay may only be changed in testing "
- "Tor networks!");
- } else if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS) {
+ if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS) {
REJECT("TestingV3AuthInitialVoteDelay is way too low.");
}
- if (options->TestingV3AuthInitialDistDelay != 5*60 &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
- REJECT("TestingV3AuthInitialDistDelay may only be changed in testing "
- "Tor networks!");
- } else if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS) {
+ if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS) {
REJECT("TestingV3AuthInitialDistDelay is way too low.");
}
@@ -3231,26 +3318,61 @@ options_validate(or_options_t *old_options, or_options_t *options,
"must be less than half TestingV3AuthInitialVotingInterval");
}
- if (options->TestingAuthDirTimeToLearnReachability != 30*60 &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
- REJECT("TestingAuthDirTimeToLearnReachability may only be changed in "
- "testing Tor networks!");
- } else if (options->TestingAuthDirTimeToLearnReachability < 0) {
+ if (options->TestingV3AuthVotingStartOffset >
+ MIN(options->TestingV3AuthInitialVotingInterval,
+ options->V3AuthVotingInterval)) {
+ REJECT("TestingV3AuthVotingStartOffset is higher than the voting "
+ "interval.");
+ }
+
+ if (options->TestingAuthDirTimeToLearnReachability < 0) {
REJECT("TestingAuthDirTimeToLearnReachability must be non-negative.");
} else if (options->TestingAuthDirTimeToLearnReachability > 2*60*60) {
COMPLAIN("TestingAuthDirTimeToLearnReachability is insanely high.");
}
- if (options->TestingEstimatedDescriptorPropagationTime != 10*60 &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
- REJECT("TestingEstimatedDescriptorPropagationTime may only be changed in "
- "testing Tor networks!");
- } else if (options->TestingEstimatedDescriptorPropagationTime < 0) {
+ if (options->TestingEstimatedDescriptorPropagationTime < 0) {
REJECT("TestingEstimatedDescriptorPropagationTime must be non-negative.");
} else if (options->TestingEstimatedDescriptorPropagationTime > 60*60) {
COMPLAIN("TestingEstimatedDescriptorPropagationTime is insanely high.");
}
+ if (options->TestingClientMaxIntervalWithoutRequest < 1) {
+ REJECT("TestingClientMaxIntervalWithoutRequest is way too low.");
+ } else if (options->TestingClientMaxIntervalWithoutRequest > 3600) {
+ COMPLAIN("TestingClientMaxIntervalWithoutRequest is insanely high.");
+ }
+
+ if (options->TestingDirConnectionMaxStall < 5) {
+ REJECT("TestingDirConnectionMaxStall is way too low.");
+ } else if (options->TestingDirConnectionMaxStall > 3600) {
+ COMPLAIN("TestingDirConnectionMaxStall is insanely high.");
+ }
+
+ if (options->TestingConsensusMaxDownloadTries < 2) {
+ REJECT("TestingConsensusMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingConsensusMaxDownloadTries > 800) {
+ COMPLAIN("TestingConsensusMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingDescriptorMaxDownloadTries < 2) {
+ REJECT("TestingDescriptorMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingDescriptorMaxDownloadTries > 800) {
+ COMPLAIN("TestingDescriptorMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingMicrodescMaxDownloadTries < 2) {
+ REJECT("TestingMicrodescMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingMicrodescMaxDownloadTries > 800) {
+ COMPLAIN("TestingMicrodescMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingCertMaxDownloadTries < 2) {
+ REJECT("TestingCertMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingCertMaxDownloadTries > 800) {
+ COMPLAIN("TestingCertMaxDownloadTries is insanely high.");
+ }
+
if (options->TestingTorNetwork) {
log_warn(LD_CONFIG, "TestingTorNetwork is set. This will make your node "
"almost unusable in the public Tor network, and is "
@@ -3509,31 +3631,63 @@ get_default_conf_file(int defaults_file)
}
/** Verify whether lst is a string containing valid-looking comma-separated
- * nicknames, or NULL. Return 0 on success. Warn and return -1 on failure.
+ * nicknames, or NULL. Will normalise <b>lst</b> to prefix '$' to any nickname
+ * or fingerprint that needs it. Return 0 on success.
+ * Warn and return -1 on failure.
*/
static int
-check_nickname_list(const char *lst, const char *name, char **msg)
+check_nickname_list(char **lst, const char *name, char **msg)
{
int r = 0;
smartlist_t *sl;
+ int changes = 0;
- if (!lst)
+ if (!*lst)
return 0;
sl = smartlist_new();
- smartlist_split_string(sl, lst, ",",
+ smartlist_split_string(sl, *lst, ",",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0);
- SMARTLIST_FOREACH(sl, const char *, s,
+ SMARTLIST_FOREACH_BEGIN(sl, char *, s)
{
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, swap the current
+ // version with a prepended one
+ tor_free(s);
+ SMARTLIST_REPLACE_CURRENT(sl, s, prepended);
+ changes = 1;
+ continue;
+ }
+
+ // Still not valid, free and fallback to error message
+ tor_free(prepended);
+ }
+
tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name);
r = -1;
break;
}
- });
+ }
+ SMARTLIST_FOREACH_END(s);
+
+ // Replace the caller's nickname list with a fixed one
+ if (changes && r == 0) {
+ char *newNicknames = smartlist_join_strings(sl, ", ", 0, NULL);
+ tor_free(*lst);
+ *lst = newNicknames;
+ }
+
SMARTLIST_FOREACH(sl, char *, s, tor_free(s));
smartlist_free(sl);
+
return r;
}
@@ -3870,7 +4024,8 @@ options_init_from_string(const char *cf_defaults, const char *cf,
}
/* Validate newoptions */
- if (options_validate(oldoptions, newoptions, 0, msg) < 0) {
+ if (options_validate(oldoptions, newoptions, newdefaultoptions,
+ 0, msg) < 0) {
err = SETOPT_ERR_PARSE; /*XXX make this a separate return value.*/
goto err;
}
@@ -4127,21 +4282,72 @@ options_init_logs(or_options_t *options, int validate_only)
return ok?0:-1;
}
+/** Given a smartlist of SOCKS arguments to be passed to a transport
+ * proxy in <b>args</b>, validate them and return -1 if they are
+ * corrupted. Return 0 if they seem OK. */
+static int
+validate_transport_socks_arguments(const smartlist_t *args)
+{
+ char *socks_string = NULL;
+ size_t socks_string_len;
+
+ tor_assert(args);
+ tor_assert(smartlist_len(args) > 0);
+
+ SMARTLIST_FOREACH_BEGIN(args, const char *, s) {
+ if (!string_is_key_value(LOG_WARN, s)) { /* items should be k=v items */
+ log_warn(LD_CONFIG, "'%s' is not a k=v item.", s);
+ return -1;
+ }
+ } SMARTLIST_FOREACH_END(s);
+
+ socks_string = pt_stringify_socks_args(args);
+ if (!socks_string)
+ return -1;
+
+ socks_string_len = strlen(socks_string);
+ tor_free(socks_string);
+
+ if (socks_string_len > MAX_SOCKS5_AUTH_SIZE_TOTAL) {
+ log_warn(LD_CONFIG, "SOCKS arguments can't be more than %u bytes (%lu).",
+ MAX_SOCKS5_AUTH_SIZE_TOTAL,
+ (unsigned long) socks_string_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Deallocate a bridge_line_t structure. */
+/* private */ void
+bridge_line_free(bridge_line_t *bridge_line)
+{
+ if (!bridge_line)
+ return;
+
+ if (bridge_line->socks_args) {
+ SMARTLIST_FOREACH(bridge_line->socks_args, char*, s, tor_free(s));
+ smartlist_free(bridge_line->socks_args);
+ }
+ tor_free(bridge_line->transport_name);
+ tor_free(bridge_line);
+}
+
/** Read the contents of a Bridge line from <b>line</b>. Return 0
* if the line is well-formed, and -1 if it isn't. If
* <b>validate_only</b> is 0, and the line is well-formed, then add
- * the bridge described in the line to our internal bridge list. */
-static int
-parse_bridge_line(const char *line, int validate_only)
+ * the bridge described in the line to our internal bridge list.
+ *
+ * Bridge line format:
+ * Bridge [transport] IP:PORT [id-fingerprint] [k=v] [k=v] ...
+ */
+/* private */ bridge_line_t *
+parse_bridge_line(const char *line)
{
smartlist_t *items = NULL;
- int r;
char *addrport=NULL, *fingerprint=NULL;
- char *transport_name=NULL;
- char *field1=NULL;
- tor_addr_t addr;
- uint16_t port = 0;
- char digest[DIGEST_LEN];
+ char *field=NULL;
+ bridge_line_t *bridge_line = tor_malloc_zero(sizeof(bridge_line_t));
items = smartlist_new();
smartlist_split_string(items, line, NULL,
@@ -4151,68 +4357,102 @@ parse_bridge_line(const char *line, int validate_only)
goto err;
}
- /* field1 is either a transport name or addrport */
- field1 = smartlist_get(items, 0);
+ /* first field is either a transport name or addrport */
+ field = smartlist_get(items, 0);
smartlist_del_keeporder(items, 0);
- if (!(strstr(field1, ".") || strstr(field1, ":"))) {
- /* new-style bridge line */
- transport_name = field1;
+ if (string_is_C_identifier(field)) {
+ /* It's a transport name. */
+ bridge_line->transport_name = field;
if (smartlist_len(items) < 1) {
log_warn(LD_CONFIG, "Too few items to Bridge line.");
goto err;
}
- addrport = smartlist_get(items, 0);
+ addrport = smartlist_get(items, 0); /* Next field is addrport then. */
smartlist_del_keeporder(items, 0);
} else {
- addrport = field1;
+ addrport = field;
}
- if (tor_addr_port_lookup(addrport, &addr, &port)<0) {
+ /* Parse addrport. */
+ if (tor_addr_port_lookup(addrport,
+ &bridge_line->addr, &bridge_line->port)<0) {
log_warn(LD_CONFIG, "Error parsing Bridge address '%s'", addrport);
goto err;
}
- if (!port) {
+ if (!bridge_line->port) {
log_info(LD_CONFIG,
"Bridge address '%s' has no port; using default port 443.",
addrport);
- port = 443;
+ bridge_line->port = 443;
}
+ /* If transports are enabled, next field could be a fingerprint or a
+ socks argument. If transports are disabled, next field must be
+ a fingerprint. */
if (smartlist_len(items)) {
- fingerprint = smartlist_join_strings(items, "", 0, NULL);
+ if (bridge_line->transport_name) { /* transports enabled: */
+ field = smartlist_get(items, 0);
+ smartlist_del_keeporder(items, 0);
+
+ /* If it's a key=value pair, then it's a SOCKS argument for the
+ transport proxy... */
+ if (string_is_key_value(LOG_DEBUG, field)) {
+ bridge_line->socks_args = smartlist_new();
+ smartlist_add(bridge_line->socks_args, field);
+ } else { /* ...otherwise, it's the bridge fingerprint. */
+ fingerprint = field;
+ }
+
+ } else { /* transports disabled: */
+ fingerprint = smartlist_join_strings(items, "", 0, NULL);
+ }
+ }
+
+ /* Handle fingerprint, if it was provided. */
+ if (fingerprint) {
if (strlen(fingerprint) != HEX_DIGEST_LEN) {
log_warn(LD_CONFIG, "Key digest for Bridge is wrong length.");
goto err;
}
- if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) {
+ if (base16_decode(bridge_line->digest, DIGEST_LEN,
+ fingerprint, HEX_DIGEST_LEN)<0) {
log_warn(LD_CONFIG, "Unable to decode Bridge key digest.");
goto err;
}
}
- if (!validate_only) {
- log_debug(LD_DIR, "Bridge at %s (transport: %s) (%s)",
- fmt_addrport(&addr, port),
- transport_name ? transport_name : "no transport",
- fingerprint ? fingerprint : "no key listed");
- bridge_add_from_config(&addr, port,
- fingerprint ? digest : NULL, transport_name);
+ /* If we are using transports, any remaining items in the smartlist
+ should be k=v values. */
+ if (bridge_line->transport_name && smartlist_len(items)) {
+ if (!bridge_line->socks_args)
+ bridge_line->socks_args = smartlist_new();
+
+ /* append remaining items of 'items' to 'socks_args' */
+ smartlist_add_all(bridge_line->socks_args, items);
+ smartlist_clear(items);
+
+ tor_assert(smartlist_len(bridge_line->socks_args) > 0);
+ }
+
+ if (bridge_line->socks_args) {
+ if (validate_transport_socks_arguments(bridge_line->socks_args) < 0)
+ goto err;
}
- r = 0;
goto done;
err:
- r = -1;
+ bridge_line_free(bridge_line);
+ bridge_line = NULL;
done:
SMARTLIST_FOREACH(items, char*, s, tor_free(s));
smartlist_free(items);
tor_free(addrport);
- tor_free(transport_name);
tor_free(fingerprint);
- return r;
+
+ return bridge_line;
}
/** Read the contents of a ClientTransportPlugin line from
@@ -4402,6 +4642,63 @@ get_bindaddr_from_transport_listen_line(const char *line,const char *transport)
return addrport;
}
+/** 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. */
+smartlist_t *
+get_options_from_transport_options_line(const char *line,const char *transport)
+{
+ smartlist_t *items = smartlist_new();
+ smartlist_t *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(options, tor_strdup(option));
+ log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option));
+ } SMARTLIST_FOREACH_END(option);
+
+ goto done;
+
+ err:
+ SMARTLIST_FOREACH(options, char*, s, tor_free(s));
+ smartlist_free(options);
+ options = NULL;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+
+ return options;
+}
+
/** 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
@@ -4422,6 +4719,26 @@ get_transport_bindaddr_from_config(const char *transport)
return NULL;
}
+/** 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 *
+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;
+}
+
/** Read the contents of a ServerTransportPlugin line from
* <b>line</b>. Return 0 if the line is well-formed, and -1 if it
* isn't.
@@ -4826,6 +5143,27 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname,
} SMARTLIST_FOREACH_END(port);
}
+/** Warn for every Extended ORPort port in <b>ports</b> that is on a
+ * publicly routable address. */
+static void
+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 port_cfg_t in <b>ports</b>, warn any controller port there
* is listening on any non-loopback address. If <b>forbid</b> is true,
* then emit a stronger warning and remove the port from the list.
@@ -4926,6 +5264,7 @@ parse_port_config(smartlist_t *out,
smartlist_t *elts;
int retval = -1;
const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER);
+ const unsigned is_ext_orport = (listener_type == CONN_TYPE_EXT_OR_LISTENER);
const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS;
const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS;
const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
@@ -5003,6 +5342,8 @@ parse_port_config(smartlist_t *out,
if (warn_nonlocal && out) {
if (is_control)
warn_nonlocal_controller_ports(out, forbid_nonlocal);
+ else if (is_ext_orport)
+ warn_nonlocal_ext_orports(out, portname);
else
warn_nonlocal_client_ports(out, portname, listener_type);
}
@@ -5276,6 +5617,8 @@ parse_port_config(smartlist_t *out,
if (warn_nonlocal && out) {
if (is_control)
warn_nonlocal_controller_ports(out, forbid_nonlocal);
+ else if (is_ext_orport)
+ warn_nonlocal_ext_orports(out, portname);
else
warn_nonlocal_client_ports(out, portname, listener_type);
}
@@ -5422,6 +5765,14 @@ parse_ports(or_options_t *options, int validate_only,
goto err;
}
if (parse_port_config(ports,
+ options->ExtORPort_lines, NULL,
+ "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 (parse_port_config(ports,
options->DirPort_lines, options->DirListenAddress,
"Dir", CONN_TYPE_DIR_LISTENER,
"0.0.0.0", 0,
@@ -5456,6 +5807,8 @@ parse_ports(or_options_t *options, int validate_only,
!! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER);
options->DNSPort_set =
!! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER);
+ options->ExtORPort_set =
+ !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER);
if (!validate_only) {
if (configured_ports) {
@@ -5903,6 +6256,43 @@ options_get_datadir_fname2_suffix(const or_options_t *options,
return fname;
}
+/** Check wether the data directory has a private subdirectory
+ * <b>subdir</b>. If not, try to create it. Return 0 on success,
+ * -1 otherwise. */
+int
+check_or_create_data_subdir(const char *subdir)
+{
+ char *statsdir = get_datadir_fname(subdir);
+ int return_val = 0;
+
+ if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
+ log_warn(LD_HIST, "Unable to create %s/ directory!", subdir);
+ return_val = -1;
+ }
+ tor_free(statsdir);
+ return return_val;
+}
+
+/** Create a file named <b>fname</b> with contents <b>str</b> in the
+ * subdirectory <b>subdir</b> of the data directory. <b>descr</b>
+ * should be a short description of the file's content and will be
+ * used for the warning message, if it's present and the write process
+ * fails. Return 0 on success, -1 otherwise.*/
+int
+write_to_data_subdir(const char* subdir, const char* fname,
+ const char* str, const char* descr)
+{
+ char *filename = get_datadir_fname2(subdir, fname);
+ int return_val = 0;
+
+ if (write_str_to_file(filename, str, 0) < 0) {
+ log_warn(LD_HIST, "Unable to write %s to disk!", descr ? descr : fname);
+ return_val = -1;
+ }
+ tor_free(filename);
+ return return_val;
+}
+
/** Given a file name check to see whether the file exists but has not been
* modified for a very long time. If so, remove it. */
void
@@ -5992,6 +6382,7 @@ getinfo_helper_config(control_connection_t *conn,
case CONFIG_TYPE_ISOTIME: type = "Time"; break;
case CONFIG_TYPE_ROUTERSET: type = "RouterList"; break;
case CONFIG_TYPE_CSV: type = "CommaList"; break;
+ case CONFIG_TYPE_CSV_INTERVAL: type = "TimeIntervalCommaList"; break;
case CONFIG_TYPE_LINELIST: type = "LineList"; break;
case CONFIG_TYPE_LINELIST_S: type = "Dependant"; break;
case CONFIG_TYPE_LINELIST_V: type = "Virtual"; break;
@@ -6123,3 +6514,58 @@ config_maybe_load_geoip_files_(const or_options_t *options,
config_load_geoip_file_(AF_INET6, options->GeoIPv6File, "geoip6");
}
+/** Initialize cookie authentication (used so far by the ControlPort
+ * and Extended ORPort).
+ *
+ * Allocate memory and create a cookie (of length <b>cookie_len</b>)
+ * in <b>cookie_out</b>.
+ * Then write it down to <b>fname</b> and prepend it with <b>header</b>.
+ *
+ * If the whole procedure was successful, set
+ * <b>cookie_is_set_out</b> to True. */
+int
+init_cookie_authentication(const char *fname, const char *header,
+ int cookie_len,
+ uint8_t **cookie_out, int *cookie_is_set_out)
+{
+ char cookie_file_str_len = strlen(header) + cookie_len;
+ char *cookie_file_str = tor_malloc(cookie_file_str_len);
+ int retval = -1;
+
+ /* We don't want to generate a new cookie every time we call
+ * options_act(). One should be enough. */
+ if (*cookie_is_set_out) {
+ retval = 0; /* we are all set */
+ goto done;
+ }
+
+ /* If we've already set the cookie, free it before re-setting
+ it. This can happen if we previously generated a cookie, but
+ couldn't write it to a disk. */
+ if (*cookie_out)
+ tor_free(*cookie_out);
+
+ /* Generate the cookie */
+ *cookie_out = tor_malloc(cookie_len);
+ if (crypto_rand((char *)*cookie_out, cookie_len) < 0)
+ goto done;
+
+ /* Create the string that should be written on the file. */
+ memcpy(cookie_file_str, header, strlen(header));
+ memcpy(cookie_file_str+strlen(header), *cookie_out, cookie_len);
+ if (write_bytes_to_file(fname, cookie_file_str, cookie_file_str_len, 1)) {
+ log_warn(LD_FS,"Error writing auth cookie to %s.", escaped(fname));
+ goto done;
+ }
+
+ /* Success! */
+ log_info(LD_GENERAL, "Generated auth cookie file in '%s'.", escaped(fname));
+ *cookie_is_set_out = 1;
+ retval = 0;
+
+ done:
+ memwipe(cookie_file_str, 0, cookie_file_str_len);
+ tor_free(cookie_file_str);
+ return retval;
+}
+