diff options
Diffstat (limited to 'src/or/config.c')
-rw-r--r-- | src/or/config.c | 2402 |
1 files changed, 1434 insertions, 968 deletions
diff --git a/src/or/config.c b/src/or/config.c index 810f1e9a7a..53c19e3900 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1,16 +1,67 @@ + /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file config.c - * \brief Code to parse and interpret configuration files. + * \brief Code to interpret the user's configuration of Tor. + * + * This module handles torrc configuration file, including parsing it, + * combining it with torrc.defaults and the command line, allowing + * user changes to it (via editing and SIGHUP or via the control port), + * writing it back to disk (because of SAVECONF from the control port), + * and -- most importantly, acting on it. + * + * The module additionally has some tools for manipulating and + * inspecting values that are calculated as a result of the + * configured options. + * + * <h3>How to add new options</h3> + * + * To add new items to the torrc, there are a minimum of three places to edit: + * <ul> + * <li>The or_options_t structure in or.h, where the options are stored. + * <li>The option_vars_ array below in this module, which configures + * the names of the torrc options, their types, their multiplicities, + * and their mappings to fields in or_options_t. + * <li>The manual in doc/tor.1.txt, to document what the new option + * is, and how it works. + * </ul> + * + * Additionally, you might need to edit these places too: + * <ul> + * <li>options_validate() below, in case you want to reject some possible + * values of the new configuration option. + * <li>options_transition_allowed() below, in case you need to + * forbid some or all changes in the option while Tor is + * running. + * <li>options_transition_affects_workers(), in case changes in the option + * might require Tor to relaunch or reconfigure its worker threads. + * <li>options_transition_affects_descriptor(), in case changes in the + * option might require a Tor relay to build and publish a new server + * descriptor. + * <li>options_act() and/or options_act_reversible(), in case there's some + * action that needs to be taken immediately based on the option's + * value. + * </ul> + * + * <h3>Changing the value of an option</h3> + * + * Because of the SAVECONF command from the control port, it's a bad + * idea to change the value of any user-configured option in the + * or_options_t. If you want to sometimes do this anyway, we recommend + * that you create a secondary field in or_options_t; that you have the + * user option linked only to the secondary field; that you use the + * secondary field to initialize the one that Tor actually looks at; and that + * you use the one Tor looks as the one that you modify. **/ #define CONFIG_PRIVATE #include "or.h" +#include "bridges.h" #include "compat.h" #include "addressmap.h" #include "channel.h" @@ -19,18 +70,22 @@ #include "circuitmux.h" #include "circuitmux_ewma.h" #include "circuitstats.h" +#include "compress.h" #include "config.h" #include "connection.h" #include "connection_edge.h" #include "connection_or.h" +#include "consdiffmgr.h" #include "control.h" #include "confparse.h" #include "cpuworker.h" +#include "crypto_rand.h" +#include "crypto_util.h" #include "dirserv.h" -#include "dirvote.h" #include "dns.h" #include "dos.h" #include "entrynodes.h" +#include "git_revision.h" #include "geoip.h" #include "hibernate.h" #include "main.h" @@ -40,6 +95,7 @@ #include "relay.h" #include "rendclient.h" #include "rendservice.h" +#include "hs_config.h" #include "rephist.h" #include "router.h" #include "sandbox.h" @@ -50,22 +106,25 @@ #include "statefile.h" #include "transports.h" #include "ext_orport.h" -#include "torgzip.h" +#include "voting_schedule.h" #ifdef _WIN32 #include <shlobj.h> #endif #include "procmon.h" +#include "dirauth/dirvote.h" +#include "dirauth/mode.h" + #ifdef HAVE_SYSTEMD # if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) /* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse * Coverity. Here's a kludge to unconfuse it. */ # define __INCLUDE_LEVEL__ 2 -# endif +#endif /* defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) */ #include <systemd/sd-daemon.h> -#endif +#endif /* defined(HAVE_SYSTEMD) */ /* Prefix used to indicate a Unix socket in a FooPort configuration. */ static const char unix_socket_prefix[] = "unix:"; @@ -73,6 +132,11 @@ static const char unix_socket_prefix[] = "unix:"; * configuration. */ static const char unix_q_socket_prefix[] = "unix:\""; +/** macro to help with the bulk rename of *DownloadSchedule to + * *DowloadInitialDelay . */ +#define DOWNLOAD_SCHEDULE(name) \ + { #name "DownloadSchedule", #name "DownloadInitialDelay", 0, 1 } + /** A list of abbreviations and aliases to map command-line options, obsolete * option names, or alternative option names, to their current values. */ static config_abbrev_t option_abbrevs_[] = { @@ -118,24 +182,55 @@ static config_abbrev_t option_abbrevs_[] = { { "BridgeAuthoritativeDirectory", "BridgeAuthoritativeDir", 0, 0}, { "HashedControlPassword", "__HashedControlSessionPassword", 1, 0}, { "VirtualAddrNetwork", "VirtualAddrNetworkIPv4", 0, 0}, + { "SocksSocketsGroupWritable", "UnixSocksGroupWritable", 0, 1}, + { "_HSLayer2Nodes", "HSLayer2Nodes", 0, 1 }, + { "_HSLayer3Nodes", "HSLayer3Nodes", 0, 1 }, + + DOWNLOAD_SCHEDULE(ClientBootstrapConsensusAuthority), + DOWNLOAD_SCHEDULE(ClientBootstrapConsensusAuthorityOnly), + DOWNLOAD_SCHEDULE(ClientBootstrapConsensusFallback), + DOWNLOAD_SCHEDULE(TestingBridge), + DOWNLOAD_SCHEDULE(TestingBridgeBootstrap), + DOWNLOAD_SCHEDULE(TestingClient), + DOWNLOAD_SCHEDULE(TestingClientConsensus), + DOWNLOAD_SCHEDULE(TestingServer), + DOWNLOAD_SCHEDULE(TestingServerConsensus), + { NULL, NULL, 0, 0}, }; +/** dummy instance of or_options_t, used for type-checking its + * members with CONF_CHECK_VAR_TYPE. */ +DUMMY_TYPECHECK_INSTANCE(or_options_t); + /** An entry for config_vars: "The option <b>name</b> has type * CONFIG_TYPE_<b>conftype</b>, and corresponds to * or_options_t.<b>member</b>" */ #define VAR(name,conftype,member,initvalue) \ - { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_options_t, member), \ - initvalue } + { name, CONFIG_TYPE_ ## conftype, offsetof(or_options_t, member), \ + initvalue CONF_TEST_MEMBERS(or_options_t, conftype, member) } /** As VAR, but the option name and member name are the same. */ #define V(member,conftype,initvalue) \ VAR(#member, conftype, member, initvalue) /** An entry for config_vars: "The option <b>name</b> is obsolete." */ +#ifdef TOR_UNIT_TESTS +#define OBSOLETE(name) { name, CONFIG_TYPE_OBSOLETE, 0, NULL, {.INT=NULL} } +#else #define OBSOLETE(name) { name, CONFIG_TYPE_OBSOLETE, 0, NULL } +#endif -#define VPORT(member,conftype,initvalue) \ - VAR(#member, conftype, member ## _lines, initvalue) +/** + * Macro to declare *Port options. Each one comes in three entries. + * For example, most users should use "SocksPort" to configure the + * socks port, but TorBrowser wants to use __SocksPort so that it + * isn't stored by SAVECONF. The SocksPortLines virtual option is + * used to query both options from the controller. + */ +#define VPORT(member) \ + VAR(#member "Lines", LINELIST_V, member ## _lines, NULL), \ + VAR(#member, LINELIST_S, member ## _lines, NULL), \ + VAR("__" #member, LINELIST_S, member ## _lines, NULL) /** Array of configuration options. Until we disallow nonstandard * abbreviations, order is significant, since the first matching option will @@ -146,11 +241,11 @@ static config_var_t option_vars_[] = { VAR("AccountingRule", STRING, AccountingRule_option, "max"), V(AccountingStart, STRING, NULL), V(Address, STRING, NULL), - V(AllowDotExit, BOOL, "0"), - V(AllowInvalidNodes, CSV, "middle,rendezvous"), + OBSOLETE("AllowDotExit"), + OBSOLETE("AllowInvalidNodes"), V(AllowNonRFC953Hostnames, BOOL, "0"), - V(AllowSingleHopCircuits, BOOL, "0"), - V(AllowSingleHopExits, BOOL, "0"), + OBSOLETE("AllowSingleHopCircuits"), + OBSOLETE("AllowSingleHopExits"), V(AlternateBridgeAuthority, LINELIST, NULL), V(AlternateDirAuthority, LINELIST, NULL), OBSOLETE("AlternateHSAuthority"), @@ -163,14 +258,14 @@ static config_var_t option_vars_[] = { V(AuthDirInvalidCCs, CSV, ""), V(AuthDirFastGuarantee, MEMUNIT, "100 KB"), V(AuthDirGuardBWGuarantee, MEMUNIT, "2 MB"), - V(AuthDirPinKeys, BOOL, "0"), + V(AuthDirPinKeys, BOOL, "1"), V(AuthDirReject, LINELIST, NULL), V(AuthDirRejectCCs, CSV, ""), OBSOLETE("AuthDirRejectUnlisted"), OBSOLETE("AuthDirListBadDirs"), V(AuthDirListBadExits, BOOL, "0"), V(AuthDirMaxServersPerAddr, UINT, "2"), - V(AuthDirMaxServersPerAuthAddr,UINT, "5"), + OBSOLETE("AuthDirMaxServersPerAuthAddr"), V(AuthDirHasIPv6Connectivity, BOOL, "0"), VAR("AuthoritativeDirectory", BOOL, AuthoritativeDir, "0"), V(AutomapHostsOnResolve, BOOL, "0"), @@ -183,12 +278,17 @@ static config_var_t option_vars_[] = { V(BridgePassword, STRING, NULL), V(BridgeRecordUsageByCountry, BOOL, "1"), V(BridgeRelay, BOOL, "0"), + V(BridgeDistribution, STRING, NULL), + VAR("CacheDirectory", FILENAME, CacheDirectory_option, NULL), + V(CacheDirectoryGroupReadable, AUTOBOOL, "auto"), V(CellStatistics, BOOL, "0"), + V(PaddingStatistics, BOOL, "1"), V(LearnCircuitBuildTimeout, BOOL, "1"), V(CircuitBuildTimeout, INTERVAL, "0"), - V(CircuitIdleTimeout, INTERVAL, "1 hour"), + OBSOLETE("CircuitIdleTimeout"), + V(CircuitsAvailableTimeout, INTERVAL, "0"), V(CircuitStreamTimeout, INTERVAL, "0"), - V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/ + V(CircuitPriorityHalflife, DOUBLE, "-1.0"), /*negative:'Use default'*/ V(ClientDNSRejectInternalAddresses, BOOL,"1"), V(ClientOnly, BOOL, "0"), V(ClientPreferIPv6ORPort, AUTOBOOL, "auto"), @@ -203,26 +303,26 @@ static config_var_t option_vars_[] = { V(ConstrainedSockets, BOOL, "0"), V(ConstrainedSockSize, MEMUNIT, "8192"), V(ContactInfo, STRING, NULL), - V(ControlListenAddress, LINELIST, NULL), - VPORT(ControlPort, LINELIST, NULL), + OBSOLETE("ControlListenAddress"), + VPORT(ControlPort), V(ControlPortFileGroupReadable,BOOL, "0"), V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), V(ControlSocketsGroupWritable, BOOL, "0"), - V(SocksSocketsGroupWritable, BOOL, "0"), + V(UnixSocksGroupWritable, BOOL, "0"), V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), V(CookieAuthFile, STRING, NULL), V(CountPrivateBandwidth, BOOL, "0"), - V(DataDirectory, FILENAME, NULL), + VAR("DataDirectory", FILENAME, DataDirectory_option, NULL), V(DataDirectoryGroupReadable, BOOL, "0"), V(DisableOOSCheck, BOOL, "1"), V(DisableNetwork, BOOL, "0"), V(DirAllowPrivateAddresses, BOOL, "0"), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), - V(DirListenAddress, LINELIST, NULL), + OBSOLETE("DirListenAddress"), V(DirPolicy, LINELIST, NULL), - VPORT(DirPort, LINELIST, NULL), + VPORT(DirPort), V(DirPortFrontPage, FILENAME, NULL), VAR("DirReqStatistics", BOOL, DirReqStatistics_option, "1"), VAR("DirAuthority", LINELIST, DirAuthorities, NULL), @@ -240,8 +340,8 @@ static config_var_t option_vars_[] = { OBSOLETE("DisableIOCP"), OBSOLETE("DisableV2DirectoryInfo_"), OBSOLETE("DynamicDHGroups"), - VPORT(DNSPort, LINELIST, NULL), - V(DNSListenAddress, LINELIST, NULL), + VPORT(DNSPort), + OBSOLETE("DNSListenAddress"), /* DoS circuit creation options. */ V(DoSCircuitCreationEnabled, AUTOBOOL, "auto"), V(DoSCircuitCreationMinConnections, UINT, "0"), @@ -258,14 +358,14 @@ static config_var_t option_vars_[] = { V(DownloadExtraInfo, BOOL, "0"), V(TestingEnableConnBwEvent, BOOL, "0"), V(TestingEnableCellStatsEvent, BOOL, "0"), - V(TestingEnableTbEmptyEvent, BOOL, "0"), + OBSOLETE("TestingEnableTbEmptyEvent"), V(EnforceDistinctSubnets, BOOL, "1"), V(EntryNodes, ROUTERSET, NULL), V(EntryStatistics, BOOL, "0"), V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "10 minutes"), V(ExcludeNodes, ROUTERSET, NULL), V(ExcludeExitNodes, ROUTERSET, NULL), - V(ExcludeSingleHopRelays, BOOL, "1"), + OBSOLETE("ExcludeSingleHopRelays"), V(ExitNodes, ROUTERSET, NULL), V(ExitPolicy, LINELIST, NULL), V(ExitPolicyRejectPrivate, BOOL, "1"), @@ -273,17 +373,19 @@ static config_var_t option_vars_[] = { V(ExitPortStatistics, BOOL, "0"), V(ExtendAllowPrivateAddresses, BOOL, "0"), V(ExitRelay, AUTOBOOL, "auto"), - VPORT(ExtORPort, LINELIST, NULL), + VPORT(ExtORPort), V(ExtORPortCookieAuthFile, STRING, NULL), V(ExtORPortCookieAuthFileGroupReadable, BOOL, "0"), V(ExtraInfoStatistics, BOOL, "1"), + V(ExtendByEd25519ID, AUTOBOOL, "auto"), V(FallbackDir, LINELIST, NULL), + V(UseDefaultFallbackDirs, BOOL, "1"), OBSOLETE("FallbackNetworkstatusFile"), V(FascistFirewall, BOOL, "0"), V(FirewallPorts, CSV, ""), - V(FastFirstHopPK, AUTOBOOL, "auto"), + OBSOLETE("FastFirstHopPK"), V(FetchDirInfoEarly, BOOL, "0"), V(FetchDirInfoExtraEarly, BOOL, "0"), V(FetchServerDescriptors, BOOL, "1"), @@ -299,11 +401,12 @@ static config_var_t option_vars_[] = { SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip"), V(GeoIPv6File, FILENAME, SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip6"), -#endif +#endif /* defined(_WIN32) */ OBSOLETE("Group"), V(GuardLifetime, INTERVAL, "0 minutes"), V(HardwareAccel, BOOL, "0"), V(HeartbeatPeriod, INTERVAL, "6 hours"), + V(MainloopStats, BOOL, "0"), V(AccelName, STRING, NULL), V(AccelDir, FILENAME, NULL), V(HashedControlPassword, LINELIST, NULL), @@ -318,16 +421,17 @@ static config_var_t option_vars_[] = { VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceNumIntroductionPoints", LINELIST_S, RendConfigLines, NULL), - V(HiddenServiceStatistics, BOOL, "1"), + VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"), V(HidServAuth, LINELIST, NULL), - V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"), - V(CloseHSServiceRendCircuitsImmediatelyOnTimeout, BOOL, "0"), + OBSOLETE("CloseHSClientCircuitsImmediatelyOnTimeout"), + OBSOLETE("CloseHSServiceRendCircuitsImmediatelyOnTimeout"), V(HiddenServiceSingleHopMode, BOOL, "0"), V(HiddenServiceNonAnonymousMode,BOOL, "0"), V(HTTPProxy, STRING, NULL), V(HTTPProxyAuthenticator, STRING, NULL), V(HTTPSProxy, STRING, NULL), V(HTTPSProxyAuthenticator, STRING, NULL), + VPORT(HTTPTunnelPort), V(IPv6Exit, BOOL, "0"), VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL), V(ServerTransportListenAddr, LINELIST, NULL), @@ -337,6 +441,10 @@ static config_var_t option_vars_[] = { V(Socks5Proxy, STRING, NULL), V(Socks5ProxyUsername, STRING, NULL), V(Socks5ProxyPassword, STRING, NULL), + VAR("KeyDirectory", FILENAME, KeyDirectory_option, NULL), + V(KeyDirectoryGroupReadable, BOOL, "0"), + VAR("HSLayer2Nodes", ROUTERSET, HSLayer2Nodes, NULL), + VAR("HSLayer3Nodes", ROUTERSET, HSLayer3Nodes, NULL), V(KeepalivePeriod, INTERVAL, "5 minutes"), V(KeepBindCapabilities, AUTOBOOL, "auto"), VAR("Log", LINELIST, Logs, NULL), @@ -344,33 +452,39 @@ static config_var_t option_vars_[] = { V(LogTimeGranularity, MSEC_INTERVAL, "1 second"), V(TruncateLogFile, BOOL, "0"), V(SyslogIdentityTag, STRING, NULL), + V(AndroidIdentityTag, STRING, NULL), V(LongLivedPorts, CSV, "21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"), VAR("MapAddress", LINELIST, AddressMap, NULL), V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"), V(MaxCircuitDirtiness, INTERVAL, "10 minutes"), V(MaxClientCircuitsPending, UINT, "32"), + V(MaxConsensusAgeForDiffs, INTERVAL, "0 seconds"), VAR("MaxMemInQueues", MEMUNIT, MaxMemInQueues_raw, "0"), OBSOLETE("MaxOnionsPending"), V(MaxOnionQueueDelay, MSEC_INTERVAL, "1750 msec"), V(MaxUnparseableDescSizeToLog, MEMUNIT, "10 MB"), V(MinMeasuredBWsForAuthToIgnoreAdvertised, INT, "500"), - V(MyFamily, STRING, NULL), + VAR("MyFamily", LINELIST, MyFamily_lines, NULL), V(NewCircuitPeriod, INTERVAL, "30 seconds"), OBSOLETE("NamingAuthoritativeDirectory"), - V(NATDListenAddress, LINELIST, NULL), - VPORT(NATDPort, LINELIST, NULL), + OBSOLETE("NATDListenAddress"), + VPORT(NATDPort), V(Nickname, STRING, NULL), - V(PredictedPortsRelevanceTime, INTERVAL, "1 hour"), - V(WarnUnsafeSocks, BOOL, "1"), + OBSOLETE("PredictedPortsRelevanceTime"), + OBSOLETE("WarnUnsafeSocks"), VAR("NodeFamily", LINELIST, NodeFamilies, NULL), + V(NoExec, BOOL, "0"), V(NumCPUs, UINT, "0"), V(NumDirectoryGuards, UINT, "0"), V(NumEntryGuards, UINT, "0"), + V(NumPrimaryGuards, UINT, "0"), V(OfflineMasterKey, BOOL, "0"), - V(ORListenAddress, LINELIST, NULL), - VPORT(ORPort, LINELIST, NULL), + OBSOLETE("ORListenAddress"), + VPORT(ORPort), V(OutboundBindAddress, LINELIST, NULL), + V(OutboundBindAddressOR, LINELIST, NULL), + V(OutboundBindAddressExit, LINELIST, NULL), OBSOLETE("PathBiasDisableRate"), V(PathBiasCircThreshold, INT, "-1"), @@ -403,8 +517,8 @@ static config_var_t option_vars_[] = { V(TestingSigningKeySlop, INTERVAL, "1 day"), V(OptimisticData, AUTOBOOL, "auto"), - V(PortForwarding, BOOL, "0"), - V(PortForwardingHelper, FILENAME, "tor-fw-helper"), + OBSOLETE("PortForwarding"), + OBSOLETE("PortForwardingHelper"), OBSOLETE("PreferTunneledDirConns"), V(ProtocolWarnings, BOOL, "0"), V(PublishServerDescriptor, CSV, "1"), @@ -416,6 +530,8 @@ static config_var_t option_vars_[] = { V(RecommendedClientVersions, LINELIST, NULL), V(RecommendedServerVersions, LINELIST, NULL), V(RecommendedPackages, LINELIST, NULL), + V(ReducedConnectionPadding, BOOL, "0"), + V(ConnectionPadding, AUTOBOOL, "auto"), V(RefuseUnknownExits, AUTOBOOL, "auto"), V(RejectPlaintextPorts, CSV, ""), V(RelayBandwidthBurst, MEMUNIT, "0"), @@ -423,6 +539,7 @@ static config_var_t option_vars_[] = { V(RendPostPeriod, INTERVAL, "1 hour"), V(RephistTrackTime, INTERVAL, "24 hours"), V(RunAsDaemon, BOOL, "0"), + V(ReducedExitPolicy, BOOL, "0"), OBSOLETE("RunTesting"), // currently unused V(Sandbox, BOOL, "0"), V(SafeLogging, STRING, "1"), @@ -435,13 +552,16 @@ static config_var_t option_vars_[] = { V(ServerDNSSearchDomains, BOOL, "0"), V(ServerDNSTestAddresses, CSV, "www.google.com,www.mit.edu,www.yahoo.com,www.slashdot.org"), - V(SchedulerLowWaterMark__, MEMUNIT, "100 MB"), - V(SchedulerHighWaterMark__, MEMUNIT, "101 MB"), - V(SchedulerMaxFlushCells__, UINT, "1000"), + OBSOLETE("SchedulerLowWaterMark__"), + OBSOLETE("SchedulerHighWaterMark__"), + OBSOLETE("SchedulerMaxFlushCells__"), + V(KISTSchedRunInterval, MSEC_INTERVAL, "0 msec"), + V(KISTSockBufSizeFactor, DOUBLE, "1.0"), + V(Schedulers, CSV, "KIST,KISTLite,Vanilla"), V(ShutdownWaitLength, INTERVAL, "30 seconds"), - V(SocksListenAddress, LINELIST, NULL), + OBSOLETE("SocksListenAddress"), V(SocksPolicy, LINELIST, NULL), - VPORT(SocksPort, LINELIST, NULL), + VPORT(SocksPort), V(SocksTimeout, INTERVAL, "2 minutes"), V(SSLKeyLifetime, INTERVAL, "0"), OBSOLETE("StrictEntryNodes"), @@ -452,23 +572,24 @@ static config_var_t option_vars_[] = { V(TokenBucketRefillInterval, MSEC_INTERVAL, "100 msec"), V(Tor2webMode, BOOL, "0"), V(Tor2webRendezvousPoints, ROUTERSET, NULL), - V(TLSECGroup, STRING, NULL), + OBSOLETE("TLSECGroup"), V(TrackHostExits, CSV, NULL), V(TrackHostExitsExpire, INTERVAL, "30 minutes"), - V(TransListenAddress, LINELIST, NULL), - VPORT(TransPort, LINELIST, NULL), + OBSOLETE("TransListenAddress"), + VPORT(TransPort), V(TransProxyType, STRING, "default"), OBSOLETE("TunnelDirConns"), V(UpdateBridgesFromAuthority, BOOL, "0"), V(UseBridges, BOOL, "0"), VAR("UseEntryGuards", BOOL, UseEntryGuards_option, "1"), - V(UseEntryGuardsAsDirGuards, BOOL, "1"), + OBSOLETE("UseEntryGuardsAsDirGuards"), V(UseGuardFraction, AUTOBOOL, "auto"), V(UseMicrodescriptors, AUTOBOOL, "auto"), OBSOLETE("UseNTorHandshake"), V(User, STRING, NULL), OBSOLETE("UserspaceIOCPBuffers"), V(AuthDirSharedRandomness, BOOL, "1"), + V(AuthDirTestEd25519LinkKeys, BOOL, "1"), OBSOLETE("V1AuthoritativeDirectory"), OBSOLETE("V2AuthoritativeDirectory"), VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"), @@ -493,58 +614,58 @@ static config_var_t option_vars_[] = { VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"), VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"), VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"), + VAR("__DisableSignalHandlers", BOOL, DisableSignalHandlers, "0"), VAR("__LeaveStreamsUnattached",BOOL, LeaveStreamsUnattached, "0"), VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword, NULL), VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), + VAR("__OwningControllerFD",INT,OwningControllerFD, "-1"), V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"), - 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(TestingServerDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingServerConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), /* With the ClientBootstrapConsensus*Download* below: * Clients with only authorities will try: - * - 3 authorities over 10 seconds, then wait 60 minutes. + * - at least 3 authorities over 10 seconds, then exponentially backoff, + * with the next attempt 3-21 seconds later, * Clients with authorities and fallbacks will try: - * - 2 authorities and 4 fallbacks over 21 seconds, then wait 60 minutes. + * - at least 2 authorities and 4 fallbacks over 21 seconds, then + * exponentially backoff, with the next attempts 4-33 seconds later, * Clients will also retry when an application request arrives. - * After a number of failed reqests, clients retry every 3 days + 1 hour. + * After a number of failed requests, clients retry every 3 days + 1 hour. * * Clients used to try 2 authorities over 10 seconds, then wait for * 60 minutes or an application request. * * When clients have authorities and fallbacks available, they use these * schedules: (we stagger the times to avoid thundering herds) */ - V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, - "6, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */), - V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, - "0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"), + V(ClientBootstrapConsensusAuthorityDownloadInitialDelay, CSV_INTERVAL, "6"), + V(ClientBootstrapConsensusFallbackDownloadInitialDelay, CSV_INTERVAL, "0"), /* When clients only have authorities available, they use this schedule: */ - V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL, - "0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800"), + V(ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay, CSV_INTERVAL, + "0"), /* We don't want to overwhelm slow networks (or mirrors whose replies are * blocked), but we also don't want to fail if only some mirrors are * blackholed. Clients will try 3 directories simultaneously. * (Relays never use simultaneous connections.) */ V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"), - V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "1200, 900, 900, 3600"), + /* When a client has any running bridges, check each bridge occasionally, + * whether or not that bridge is actually up. */ + V(TestingBridgeDownloadInitialDelay, CSV_INTERVAL,"10800"), + /* When a client is just starting, or has no running bridges, check each + * bridge a few times quickly, and then try again later. These schedules + * are much longer than the other schedules, because we try each and every + * configured bridge with this schedule. */ + V(TestingBridgeBootstrapDownloadInitialDelay, CSV_INTERVAL, "0"), V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"), V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"), - V(TestingConsensusMaxDownloadTries, UINT, "8"), - /* Since we try connections rapidly and simultaneously, we can afford - * to give up earlier. (This protects against overloading directories.) */ - V(ClientBootstrapConsensusMaxDownloadTries, UINT, "7"), - /* We want to give up much earlier if we're only using authorities. */ - V(ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "4"), - V(TestingDescriptorMaxDownloadTries, UINT, "8"), - V(TestingMicrodescMaxDownloadTries, UINT, "8"), - V(TestingCertMaxDownloadTries, UINT, "8"), + OBSOLETE("TestingConsensusMaxDownloadTries"), + OBSOLETE("ClientBootstrapConsensusMaxDownloadTries"), + OBSOLETE("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries"), + OBSOLETE("TestingDescriptorMaxDownloadTries"), + OBSOLETE("TestingMicrodescMaxDownloadTries"), + OBSOLETE("TestingCertMaxDownloadTries"), V(TestingDirAuthVoteExit, ROUTERSET, NULL), V(TestingDirAuthVoteExitIsStrict, BOOL, "0"), V(TestingDirAuthVoteGuard, ROUTERSET, NULL), @@ -553,27 +674,21 @@ static config_var_t option_vars_[] = { V(TestingDirAuthVoteHSDirIsStrict, BOOL, "0"), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "0"), - { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } + END_OF_CONFIG_VARS }; /** Override default values with these if the user sets the TestingTorNetwork * option. */ static const config_var_t testing_tor_network_defaults[] = { - V(ServerDNSAllowBrokenConfig, BOOL, "1"), V(DirAllowPrivateAddresses, BOOL, "1"), V(EnforceDistinctSubnets, BOOL, "0"), V(AssumeReachable, BOOL, "1"), V(AuthDirMaxServersPerAddr, UINT, "0"), - V(AuthDirMaxServersPerAuthAddr,UINT, "0"), - V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, - "0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), - V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, - "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), - V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL, - "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), - V(ClientBootstrapConsensusMaxDownloadTries, UINT, "80"), - V(ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "80"), - V(ClientDNSRejectInternalAddresses, BOOL,"0"), // deprecated in 0.2.9.2-alpha + V(ClientBootstrapConsensusAuthorityDownloadInitialDelay, CSV_INTERVAL, "0"), + V(ClientBootstrapConsensusFallbackDownloadInitialDelay, CSV_INTERVAL, "0"), + V(ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay, CSV_INTERVAL, + "0"), + V(ClientDNSRejectInternalAddresses, BOOL,"0"), V(ClientRejectInternalAddresses, BOOL, "0"), V(CountPrivateBandwidth, BOOL, "1"), V(ExitPolicyRejectPrivate, BOOL, "0"), @@ -584,32 +699,23 @@ static const config_var_t testing_tor_network_defaults[] = { V(TestingV3AuthInitialVotingInterval, INTERVAL, "150 seconds"), 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(TestingServerDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingServerConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingBridgeDownloadInitialDelay, CSV_INTERVAL, "10"), + V(TestingBridgeBootstrapDownloadInitialDelay, CSV_INTERVAL, "0"), 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"), V(TestingEnableConnBwEvent, BOOL, "1"), V(TestingEnableCellStatsEvent, BOOL, "1"), - V(TestingEnableTbEmptyEvent, BOOL, "1"), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"), V(RendPostPeriod, INTERVAL, "2 minutes"), - { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } + END_OF_CONFIG_VARS }; #undef VAR @@ -617,39 +723,19 @@ static const config_var_t testing_tor_network_defaults[] = { #undef OBSOLETE static const config_deprecation_t option_deprecation_notes_[] = { - /* Deprecated since 0.2.9.2-alpha... */ - { "AllowDotExit", "Unrestricted use of the .exit notation can be used for " - "a wide variety of application-level attacks." }, - { "AllowInvalidNodes", "There is no reason to enable this option; at best " - "it will make you easier to track." }, - { "AllowSingleHopCircuits", "Almost no relays actually allow single-hop " - "exits, making this option pointless." }, - { "AllowSingleHopExits", "Turning this on will make your relay easier " - "to abuse." }, - { "ClientDNSRejectInternalAddresses", "Turning this on makes your client " - "easier to fingerprint, and may open you to esoteric attacks." }, - { "ExcludeSingleHopRelays", "Turning it on makes your client easier to " - "fingerprint." }, - { "FastFirstHopPK", "Changing this option does not make your client more " - "secure, but does make it easier to fingerprint." }, - { "CloseHSClientCircuitsImmediatelyOnTimeout", "This option makes your " - "client easier to fingerprint." }, - { "CloseHSServiceRendCircuitsImmediatelyOnTimeout", "This option makes " - "your hidden services easier to fingerprint." }, - { "WarnUnsafeSocks", "Changing this option makes it easier for you " - "to accidentally lose your anonymity by leaking DNS information" }, - { "TLSECGroup", "The default is a nice secure choice; the other option " - "is less secure." }, - { "ControlListenAddress", "Use ControlPort instead." }, - { "DirListenAddress", "Use DirPort instead, possibly with the " - "NoAdvertise sub-option" }, - { "DNSListenAddress", "Use DNSPort instead." }, - { "SocksListenAddress", "Use SocksPort instead." }, - { "TransListenAddress", "Use TransPort instead." }, - { "NATDListenAddress", "Use NATDPort instead." }, - { "ORListenAddress", "Use ORPort instead, possibly with the " - "NoAdvertise sub-option" }, - /* End of options deprecated since 0.2.9.2-alpha. */ + /* Deprecated since 0.3.2.0-alpha. */ + { "HTTPProxy", "It only applies to direct unencrypted HTTP connections " + "to your directory server, which your Tor probably wasn't using." }, + { "HTTPProxyAuthenticator", "HTTPProxy is deprecated in favor of HTTPSProxy " + "which should be used with HTTPSProxyAuthenticator." }, + /* End of options deprecated since 0.3.2.1-alpha */ + + /* Options deprecated since 0.3.2.2-alpha */ + { "ReachableDirAddresses", "It has no effect on relays, and has had no " + "effect on clients since 0.2.8." }, + { "ClientPreferIPv6DirPort", "It has no effect on relays, and has had no " + "effect on clients since 0.2.8." }, + /* End of options deprecated since 0.3.2.2-alpha. */ { NULL, NULL } }; @@ -665,7 +751,11 @@ 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(char **lst, const char *name, char **msg); +static int options_transition_affects_dirauth_timing( + const or_options_t *old_options, const or_options_t *new_options); +static int normalize_nickname_list(config_line_t **normalized_out, + const config_line_t *lst, const char *name, + char **msg); static char *get_bindaddr_from_transport_listen_line(const char *line, const char *transport); static int parse_ports(or_options_t *options, int validate_only, @@ -674,8 +764,7 @@ static int parse_ports(or_options_t *options, int validate_only, static int check_server_ports(const smartlist_t *ports, const or_options_t *options, int *num_low_ports_out); - -static int validate_data_directory(or_options_t *options); +static int validate_data_directories(or_options_t *options); static int write_configuration_file(const char *fname, const or_options_t *options); static int options_init_logs(const or_options_t *old_options, @@ -690,8 +779,8 @@ static void config_maybe_load_geoip_files_(const or_options_t *options, static int options_validate_cb(void *old_options, void *options, void *default_options, int from_setconf, char **msg); -static uint64_t compute_real_max_mem_in_queues(const uint64_t val, - int log_guess); +static void cleanup_protocol_warning_severity_level(void); +static void set_protocol_warning_severity_level(int warning_severity); /** Magic value for or_options_t. */ #define OR_OPTIONS_MAGIC 9090909 @@ -700,7 +789,7 @@ static uint64_t compute_real_max_mem_in_queues(const uint64_t val, STATIC config_format_t options_format = { sizeof(or_options_t), OR_OPTIONS_MAGIC, - STRUCT_OFFSET(or_options_t, magic_), + offsetof(or_options_t, magic_), option_abbrevs_, option_deprecation_notes_, option_vars_, @@ -720,7 +809,7 @@ static or_options_t *global_default_options = NULL; /** Name of most recently read torrc file. */ static char *torrc_fname = NULL; /** Name of the most recently read torrc-defaults file.*/ -static char *torrc_defaults_fname; +static char *torrc_defaults_fname = NULL; /** Configuration options set by command line. */ static config_line_t *global_cmdline_options = NULL; /** Non-configuration options set by the command line */ @@ -731,6 +820,11 @@ static int have_parsed_cmdline = 0; static char *global_dirfrontpagecontents = NULL; /** List of port_cfg_t for all configured ports. */ static smartlist_t *configured_ports = NULL; +/** True iff we're currently validating options, and any calls to + * get_options() are likely to be bugs. */ +static int in_option_validation = 0; +/* True iff we've initialized libevent */ +static int libevent_initialized = 0; /** Return the contents of our frontpage string, or NULL if not configured. */ MOCK_IMPL(const char*, @@ -744,6 +838,7 @@ MOCK_IMPL(or_options_t *, get_options_mutable, (void)) { tor_assert(global_options); + tor_assert_nonfatal(! in_option_validation); return global_options; } @@ -774,9 +869,13 @@ set_options(or_options_t *new_val, char **msg) return -1; } if (options_act(old_options) < 0) { /* acting on the options failed. die. */ - log_err(LD_BUG, - "Acting on config options left us in a broken state. Dying."); - exit(1); + if (! tor_event_loop_shutdown_is_pending()) { + log_err(LD_BUG, + "Acting on config options left us in a broken state. Dying."); + tor_shutdown_event_loop_and_exit(1); + } + global_options = old_options; + return -1; } /* Issues a CONF_CHANGED event to notify controller of the change. If Tor is * just starting up then the old_options will be undefined. */ @@ -802,7 +901,7 @@ set_options(or_options_t *new_val, char **msg) tor_free(line); } } else { - smartlist_add(elements, tor_strdup(options_format.vars[i].name)); + smartlist_add_strdup(elements, options_format.vars[i].name); smartlist_add(elements, NULL); } } @@ -812,14 +911,17 @@ set_options(or_options_t *new_val, char **msg) smartlist_free(elements); } - if (old_options != global_options) + if (old_options != global_options) { or_options_free(old_options); + /* If we are here it means we've successfully applied the new options and + * that the global options have been changed to the new values. We'll + * check if we need to remove or add periodic events. */ + periodic_events_on_new_options(global_options); + } return 0; } -extern const char tor_git_revision[]; /* from tor_main.c */ - /** The version of this Tor process, as parsed. */ static char *the_tor_version = NULL; /** A shorter version of this Tor process's version, for export in our router @@ -859,7 +961,7 @@ get_short_version(void) /** Release additional memory allocated in options */ STATIC void -or_options_free(or_options_t *options) +or_options_free_(or_options_t *options) { if (!options) return; @@ -870,9 +972,21 @@ or_options_free(or_options_t *options) rs, routerset_free(rs)); smartlist_free(options->NodeFamilySets); } + if (options->SchedulerTypes_) { + SMARTLIST_FOREACH(options->SchedulerTypes_, int *, i, tor_free(i)); + smartlist_free(options->SchedulerTypes_); + } + if (options->FilesOpenedByIncludes) { + SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, tor_free(f)); + smartlist_free(options->FilesOpenedByIncludes); + } + tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); + tor_free(options->KeyDirectory); tor_free(options->BridgePassword_AuthDigest_); tor_free(options->command_arg); tor_free(options->master_key_fname); + config_free_lines(options->MyFamily); config_free(&options_format, options); } @@ -905,6 +1019,11 @@ config_free_all(void) tor_free(the_short_tor_version); tor_free(the_tor_version); + + cleanup_protocol_warning_severity_level(); + + have_parsed_cmdline = 0; + libevent_initialized = 0; } /** Make <b>address</b> -- a piece of information related to our operation as @@ -964,6 +1083,52 @@ escaped_safe_str(const char *address) return escaped(address); } +/** + * The severity level that should be used for warnings of severity + * LOG_PROTOCOL_WARN. + * + * We keep this outside the options, and we use an atomic_counter_t, in case + * one thread needs to use LOG_PROTOCOL_WARN while an option transition is + * happening in the main thread. + */ +static atomic_counter_t protocol_warning_severity_level; + +/** Return the severity level that should be used for warnings of severity + * LOG_PROTOCOL_WARN. */ +int +get_protocol_warning_severity_level(void) +{ + return (int) atomic_counter_get(&protocol_warning_severity_level); +} + +/** Set the protocol warning severity level to <b>severity</b>. */ +static void +set_protocol_warning_severity_level(int warning_severity) +{ + atomic_counter_exchange(&protocol_warning_severity_level, + warning_severity); +} + +/** + * Initialize the log warning severity level for protocol warnings. Call + * only once at startup. + */ +void +init_protocol_warning_severity_level(void) +{ + atomic_counter_init(&protocol_warning_severity_level); + set_protocol_warning_severity_level(LOG_WARN); +} + +/** + * Tear down protocol_warning_severity_level. + */ +static void +cleanup_protocol_warning_severity_level(void) +{ + atomic_counter_destroy(&protocol_warning_severity_level); +} + /** List of default directory authorities */ static const char *default_authorities[] = { @@ -1129,6 +1294,69 @@ consider_adding_dir_servers(const or_options_t *options, return 0; } +/** + * Make sure that <b>directory</b> exists, with appropriate ownership and + * permissions (as modified by <b>group_readable</b>). If <b>create</b>, + * create the directory if it is missing. Return 0 on success. + * On failure, return -1 and set *<b>msg_out</b>. + */ +static int +check_and_create_data_directory(int create, + const char *directory, + int group_readable, + const char *owner, + char **msg_out) +{ + cpd_check_t cpd_opts = create ? CPD_CREATE : CPD_CHECK; + if (group_readable) + cpd_opts |= CPD_GROUP_READ; + if (check_private_dir(directory, + cpd_opts, + owner) < 0) { + tor_asprintf(msg_out, + "Couldn't %s private data directory \"%s\"", + create ? "create" : "access", + directory); + return -1; + } + +#ifndef _WIN32 + if (group_readable) { + /* Only new dirs created get new opts, also enforce group read. */ + if (chmod(directory, 0750)) { + log_warn(LD_FS,"Unable to make %s group-readable: %s", + directory, strerror(errno)); + } + } +#endif /* !defined(_WIN32) */ + + return 0; +} + +/** + * Ensure that our keys directory exists, with appropriate permissions. + * Return 0 on success, -1 on failure. + */ +int +create_keys_directory(const or_options_t *options) +{ + /* Make sure DataDirectory exists, and is private. */ + cpd_check_t cpd_opts = CPD_CREATE; + if (options->DataDirectoryGroupReadable) + cpd_opts |= CPD_GROUP_READ; + if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { + log_err(LD_OR, "Can't create/check datadirectory %s", + options->DataDirectory); + return -1; + } + + /* Check the key directory. */ + if (check_private_dir(options->KeyDirectory, CPD_CREATE, options->User)) { + return -1; + } + return 0; +} + /* Helps determine flags to pass to switch_id. */ static int have_low_ports = -1; @@ -1143,7 +1371,6 @@ options_act_reversible(const or_options_t *old_options, char **msg) { smartlist_t *new_listeners = smartlist_new(); smartlist_t *replaced_listeners = smartlist_new(); - static int libevent_initialized = 0; or_options_t *options = get_options_mutable(); int running_tor = options->command == CMD_RUN_TOR; int set_conn_limit = 0; @@ -1170,13 +1397,13 @@ options_act_reversible(const or_options_t *old_options, char **msg) "on this OS/with this build."); goto rollback; } -#else +#else /* !(!defined(HAVE_SYS_UN_H)) */ if (options->ControlSocketsGroupWritable && !options->ControlSocket) { *msg = tor_strdup("Setting ControlSocketGroupWritable without setting" "a ControlSocket makes no sense."); goto rollback; } -#endif +#endif /* !defined(HAVE_SYS_UN_H) */ if (running_tor) { int n_ports=0; @@ -1223,9 +1450,9 @@ options_act_reversible(const or_options_t *old_options, char **msg) consider_hibernation(time(NULL)); /* Launch the listeners. (We do this before we setuid, so we can bind to - * ports under 1024.) We don't want to rebind if we're hibernating. If - * networking is disabled, this will close all but the control listeners, - * but disable those. */ + * ports under 1024.) We don't want to rebind if we're hibernating or + * shutting down. If networking is disabled, this will close all but the + * control listeners, but disable those. */ if (!we_are_hibernating()) { if (retry_all_listeners(replaced_listeners, new_listeners, options->DisableNetwork) < 0) { @@ -1253,7 +1480,7 @@ options_act_reversible(const or_options_t *old_options, char **msg) goto rollback; } } -#endif +#endif /* defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) */ /* Attempt to lock all current and future memory with mlockall() only once */ if (options->DisableAllSwap) { @@ -1283,29 +1510,47 @@ options_act_reversible(const or_options_t *old_options, char **msg) } /* Ensure data directory is private; create if possible. */ - cpd_check_t cpd_opts = running_tor ? CPD_CREATE : CPD_CHECK; - if (options->DataDirectoryGroupReadable) - cpd_opts |= CPD_GROUP_READ; - if (check_private_dir(options->DataDirectory, - cpd_opts, - options->User)<0) { - tor_asprintf(msg, - "Couldn't access/create private data directory \"%s\"", - options->DataDirectory); - + /* It's okay to do this in "options_act_reversible()" even though it isn't + * actually reversible, since you can't change the DataDirectory while + * Tor is running. */ + if (check_and_create_data_directory(running_tor /* create */, + options->DataDirectory, + options->DataDirectoryGroupReadable, + options->User, + msg) < 0) { + goto done; + } + if (check_and_create_data_directory(running_tor /* create */, + options->KeyDirectory, + options->KeyDirectoryGroupReadable, + options->User, + msg) < 0) { goto done; - /* No need to roll back, since you can't change the value. */ } -#ifndef _WIN32 - if (options->DataDirectoryGroupReadable) { - /* Only new dirs created get new opts, also enforce group read. */ - if (chmod(options->DataDirectory, 0750)) { - log_warn(LD_FS,"Unable to make %s group-readable: %s", - options->DataDirectory, strerror(errno)); - } + /* We need to handle the group-readable flag for the cache directory + * specially, since the directory defaults to being the same as the + * DataDirectory. */ + int cache_dir_group_readable; + if (options->CacheDirectoryGroupReadable != -1) { + /* If the user specified a value, use their setting */ + cache_dir_group_readable = options->CacheDirectoryGroupReadable; + } else if (!strcmp(options->CacheDirectory, options->DataDirectory)) { + /* If the user left the value as "auto", and the cache is the same as the + * datadirectory, use the datadirectory setting. + */ + cache_dir_group_readable = options->DataDirectoryGroupReadable; + } else { + /* Otherwise, "auto" means "not group readable". */ + cache_dir_group_readable = 0; + } + if (check_and_create_data_directory(running_tor /* create */, + options->CacheDirectory, + cache_dir_group_readable, + options->User, + msg) < 0) { + goto done; } -#endif /* Bail out at this point if we're not going to be a client or server: * we don't run Tor itself. */ @@ -1328,6 +1573,7 @@ options_act_reversible(const or_options_t *old_options, char **msg) tor_malloc_zero(sizeof(log_severity_list_t)); close_temp_logs(); add_callback_log(severity, control_event_logmsg); + logs_set_pending_callback_callback(control_event_logmsg_pending); control_adjust_event_log_severity(); tor_free(severity); tor_log_update_sigsafe_err_fds(); @@ -1435,13 +1681,14 @@ options_act_reversible(const or_options_t *old_options, char **msg) int options_need_geoip_info(const or_options_t *options, const char **reason_out) { - int bridge_usage = - options->BridgeRelay && options->BridgeRecordUsageByCountry; + int bridge_usage = should_record_bridge_info(options); int routerset_usage = routerset_needs_geoip(options->EntryNodes) || routerset_needs_geoip(options->ExitNodes) || routerset_needs_geoip(options->ExcludeExitNodes) || routerset_needs_geoip(options->ExcludeNodes) || + routerset_needs_geoip(options->HSLayer2Nodes) || + routerset_needs_geoip(options->HSLayer3Nodes) || routerset_needs_geoip(options->Tor2webRendezvousPoints); if (routerset_usage && reason_out) { @@ -1482,19 +1729,70 @@ get_effective_bwburst(const or_options_t *options) return (uint32_t)bw; } -/** Return True if any changes from <b>old_options</b> to - * <b>new_options</b> needs us to refresh our TLS context. */ +/* 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; +#define YES_IF_CHANGED_SMARTLIST(opt) \ + if (!CFG_EQ_SMARTLIST(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_ROUTERSET(opt) \ + if (!CFG_EQ_ROUTERSET(old_options, new_options, opt)) return 1; + +/** + * Return true if changing the configuration from <b>old</b> to <b>new</b> + * affects the guard subsystem. + */ static int -options_transition_requires_fresh_tls_context(const or_options_t *old_options, - const or_options_t *new_options) +options_transition_affects_guards(const or_options_t *old_options, + const or_options_t *new_options) { + /* NOTE: Make sure this function stays in sync with + * node_passes_guard_filter */ + tor_assert(old_options); tor_assert(new_options); - if (!old_options) - return 0; + YES_IF_CHANGED_BOOL(UseEntryGuards); + YES_IF_CHANGED_BOOL(UseBridges); + YES_IF_CHANGED_BOOL(ClientUseIPv4); + YES_IF_CHANGED_BOOL(ClientUseIPv6); + YES_IF_CHANGED_BOOL(FascistFirewall); + YES_IF_CHANGED_ROUTERSET(ExcludeNodes); + YES_IF_CHANGED_ROUTERSET(EntryNodes); + YES_IF_CHANGED_SMARTLIST(FirewallPorts); + YES_IF_CHANGED_LINELIST(Bridges); + YES_IF_CHANGED_LINELIST(ReachableORAddresses); + YES_IF_CHANGED_LINELIST(ReachableDirAddresses); + + return 0; +} - if (!opt_streq(old_options->TLSECGroup, new_options->TLSECGroup)) +/** + * 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; } @@ -1517,7 +1815,12 @@ options_act(const or_options_t *old_options) char *msg=NULL; const int transition_affects_workers = old_options && options_transition_affects_workers(old_options, options); - int old_ewma_enabled; + const int transition_affects_guards = + old_options && options_transition_affects_guards(old_options, options); + + if (options->NoExec || options->Sandbox) { + tor_disable_spawning_background_processes(); + } /* disable ptrace and later, other basic debugging techniques */ { @@ -1553,8 +1856,16 @@ options_act(const or_options_t *old_options) return -1; } - if (consider_adding_dir_servers(options, old_options) < 0) + { + int warning_severity = options->ProtocolWarnings ? LOG_WARN : LOG_INFO; + set_protocol_warning_severity_level(warning_severity); + } + + if (consider_adding_dir_servers(options, old_options) < 0) { + // XXXX This should get validated earlier, and committed here, to + // XXXX lower opportunities for reaching an error case. return -1; + } if (rend_non_anonymous_mode_enabled(options)) { log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run " @@ -1563,6 +1874,7 @@ options_act(const or_options_t *old_options) #ifdef ENABLE_TOR2WEB_MODE /* LCOV_EXCL_START */ + // XXXX This should move into options_validate() if (!options->Tor2webMode) { log_err(LD_CONFIG, "This copy of Tor was compiled to run in " "'tor2web mode'. It can only be run with the Tor2webMode torrc " @@ -1570,7 +1882,8 @@ options_act(const or_options_t *old_options) return -1; } /* LCOV_EXCL_STOP */ -#else +#else /* !(defined(ENABLE_TOR2WEB_MODE)) */ + // XXXX This should move into options_validate() if (options->Tor2webMode) { log_err(LD_CONFIG, "This copy of Tor was not compiled to run in " "'tor2web mode'. It cannot be run with the Tor2webMode torrc " @@ -1578,7 +1891,7 @@ options_act(const or_options_t *old_options) "--enable-tor2web-mode option."); return -1; } -#endif +#endif /* defined(ENABLE_TOR2WEB_MODE) */ /* If we are a bridge with a pluggable transport proxy but no Extended ORPort, inform the user that they are missing out. */ @@ -1598,25 +1911,49 @@ options_act(const or_options_t *old_options) for (cl = options->Bridges; cl; cl = cl->next) { bridge_line_t *bridge_line = parse_bridge_line(cl->value); if (!bridge_line) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated Bridge line could not be added!"); return -1; + // LCOV_EXCL_STOP } bridge_add_from_config(bridge_line); } sweep_bridge_list(); } - if (running_tor && rend_config_services(options, 0)<0) { + if (running_tor && hs_config_service_all(options, 0)<0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated hidden services line could not be added!"); return -1; + // LCOV_EXCL_STOP } if (running_tor && rend_parse_service_authorization(options, 0) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated client authorization for " "hidden services could not be added!"); return -1; + // LCOV_EXCL_STOP + } + + if (running_tor && !old_options && options->OwningControllerFD != -1) { +#ifdef _WIN32 + log_warn(LD_CONFIG, "OwningControllerFD is not supported on Windows. " + "If you need it, tell the Tor developers."); + return -1; +#else + const unsigned ctrl_flags = + CC_LOCAL_FD_IS_OWNER | + CC_LOCAL_FD_IS_AUTHENTICATED; + tor_socket_t ctrl_sock = (tor_socket_t)options->OwningControllerFD; + if (control_connection_add_local_fd(ctrl_sock, ctrl_flags) < 0) { + log_warn(LD_CONFIG, "Could not add local controller connection with " + "given FD."); + return -1; + } +#endif /* defined(_WIN32) */ } /* Load state */ @@ -1639,10 +1976,12 @@ options_act(const or_options_t *old_options) if (options->ClientTransportPlugin) { for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { if (parse_transport_line(options, cl->value, 0, 0) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated ClientTransportPlugin line " "could not be added!"); return -1; + // LCOV_EXCL_STOP } } } @@ -1650,10 +1989,12 @@ options_act(const or_options_t *old_options) if (options->ServerTransportPlugin && server_mode(options)) { for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { if (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 } } } @@ -1678,6 +2019,9 @@ options_act(const or_options_t *old_options) finish_daemon(options->DataDirectory); } + /* See whether we need to enable/disable our once-a-second timer. */ + reschedule_per_second_timer(); + /* 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 || @@ -1687,19 +2031,16 @@ options_act(const or_options_t *old_options) log_warn(LD_BUG,"Error initializing keys; exiting"); return -1; } - } else if (old_options && - options_transition_requires_fresh_tls_context(old_options, - options)) { - if (router_initialize_tls_context() < 0) { - log_warn(LD_BUG,"Error initializing TLS context."); - return -1; - } } /* Write our PID to the PID file. If we do not have write permissions we - * will log a warning */ + * will log a warning and exit. */ if (options->PidFile && !sandbox_is_active()) { - write_pidfile(options->PidFile); + if (write_pidfile(options->PidFile) < 0) { + log_err(LD_CONFIG, "Unable to write PIDFile %s", + escaped(options->PidFile)); + return -1; + } } /* Register addressmap directives */ @@ -1714,6 +2055,15 @@ options_act(const or_options_t *old_options) return -1; } + if (server_mode(options)) { + static int cdm_initialized = 0; + if (cdm_initialized == 0) { + cdm_initialized = 1; + consdiffmgr_configure(NULL); + consdiffmgr_validate(); + } + } + if (init_control_cookie_authentication(options->CookieAuthentication) < 0) { log_warn(LD_CONFIG,"Error creating control cookie authentication file."); return -1; @@ -1722,35 +2072,27 @@ options_act(const or_options_t *old_options) monitor_owning_controller_process(options->OwningControllerProcess); /* reload keys as needed for rendezvous services. */ - if (rend_service_load_all_keys(NULL)<0) { + if (hs_service_load_all_keys() < 0) { log_warn(LD_GENERAL,"Error loading rendezvous service keys"); return -1; } - /* Set up scheduler thresholds */ - scheduler_set_watermarks((uint32_t)options->SchedulerLowWaterMark__, - (uint32_t)options->SchedulerHighWaterMark__, - (options->SchedulerMaxFlushCells__ > 0) ? - options->SchedulerMaxFlushCells__ : 1000); + /* Inform the scheduler subsystem that a configuration changed happened. It + * might be a change of scheduler or parameter. */ + scheduler_conf_changed(); /* Set up accounting */ if (accounting_parse_options(options, 0)<0) { - log_warn(LD_CONFIG,"Error in accounting options"); + // 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)); - old_ewma_enabled = cell_ewma_enabled(); /* Change the cell EWMA settings */ - cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus()); - /* If we just enabled ewma, set the cmux policy on all active channels */ - if (cell_ewma_enabled() && !old_ewma_enabled) { - channel_set_cmux_policy_everywhere(&ewma_policy); - } else if (!cell_ewma_enabled() && old_ewma_enabled) { - /* Turn it off everywhere */ - channel_set_cmux_policy_everywhere(NULL); - } + cmux_ewma_set_options(options, networkstatus_get_latest_consensus()); /* Update the BridgePassword's hashed version as needed. We store this as a * digest so that we can do side-channel-proof comparisons on it. @@ -1759,6 +2101,7 @@ options_act(const or_options_t *old_options) char *http_authenticator; http_authenticator = alloc_http_authenticator(options->BridgePassword); if (!http_authenticator) { + // XXXX This should get validated in options_validate(). log_warn(LD_BUG, "Unable to allocate HTTP authenticator. Not setting " "BridgePassword."); return -1; @@ -1771,9 +2114,12 @@ options_act(const or_options_t *old_options) } if (parse_outbound_addresses(options, 0, &msg) < 0) { - log_warn(LD_BUG, "Failed parsing outbound bind addresses: %s", msg); + // LCOV_EXCL_START + log_warn(LD_BUG, "Failed parsing previously validated outbound " + "bind addresses: %s", msg); tor_free(msg); return -1; + // LCOV_EXCL_STOP } config_maybe_load_geoip_files_(options, old_options); @@ -1794,6 +2140,7 @@ options_act(const or_options_t *old_options) if (old_options) { int revise_trackexithosts = 0; int revise_automap_entries = 0; + int abandon_circuits = 0; if ((options->UseEntryGuards && !old_options->UseEntryGuards) || options->UseBridges != old_options->UseBridges || (options->UseBridges && @@ -1803,6 +2150,10 @@ options_act(const or_options_t *old_options) options->ExcludeExitNodes) || !routerset_equal(old_options->EntryNodes, options->EntryNodes) || !routerset_equal(old_options->ExitNodes, options->ExitNodes) || + !routerset_equal(old_options->HSLayer2Nodes, + options->HSLayer2Nodes) || + !routerset_equal(old_options->HSLayer3Nodes, + options->HSLayer3Nodes) || !routerset_equal(old_options->Tor2webRendezvousPoints, options->Tor2webRendezvousPoints) || options->StrictNodes != old_options->StrictNodes) { @@ -1810,6 +2161,16 @@ options_act(const or_options_t *old_options) "Changed to using entry guards or bridges, or changed " "preferred or excluded node lists. " "Abandoning previous circuits."); + abandon_circuits = 1; + } + + if (transition_affects_guards) { + if (guards_update_all()) { + abandon_circuits = 1; + } + } + + if (abandon_circuits) { circuit_mark_all_unused_circs(); circuit_mark_all_dirty_circs_as_unusable(); revise_trackexithosts = 1; @@ -1840,7 +2201,7 @@ options_act(const or_options_t *old_options) addressmap_clear_invalid_automaps(options); /* How long should we delay counting bridge stats after becoming a bridge? - * We use this so we don't count people who used our bridge thinking it is + * 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. */ @@ -1868,9 +2229,16 @@ options_act(const or_options_t *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(options) && !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(); @@ -1886,11 +2254,23 @@ options_act(const or_options_t *old_options) if (options->PerConnBWRate != old_options->PerConnBWRate || options->PerConnBWBurst != old_options->PerConnBWBurst) connection_or_update_token_buckets(get_connection_array(), options); + + if (options->BandwidthRate != old_options->BandwidthRate || + options->BandwidthBurst != old_options->BandwidthBurst || + options->RelayBandwidthRate != old_options->RelayBandwidthRate || + options->RelayBandwidthBurst != old_options->RelayBandwidthBurst) + connection_bucket_adjust(options); + + if (options->MainloopStats != old_options->MainloopStats) { + reset_main_loop_counters(); + } } /* Only collect directory-request statistics on relays and bridges. */ options->DirReqStatistics = options->DirReqStatistics_option && server_mode(options); + options->HiddenServiceStatistics = + options->HiddenServiceStatistics_option && server_mode(options); if (options->CellStatistics || options->DirReqStatistics || options->EntryStatistics || options->ExitPortStatistics || @@ -1905,7 +2285,6 @@ options_act(const or_options_t *old_options) options->CellStatistics = 0; options->EntryStatistics = 0; options->ConnDirectionStatistics = 0; - options->HiddenServiceStatistics = 0; options->ExitPortStatistics = 0; } @@ -1931,6 +2310,11 @@ options_act(const or_options_t *old_options) } 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; @@ -1991,13 +2375,6 @@ options_act(const or_options_t *old_options) !options->BridgeAuthoritativeDir) rep_hist_desc_stats_term(); - /* Check if we need to parse and add the EntryNodes config option. */ - if (options->EntryNodes && - (!old_options || - !routerset_equal(old_options->EntryNodes,options->EntryNodes) || - !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes))) - entry_nodes_should_be_added(); - /* Since our options changed, we might need to regenerate and upload our * server descriptor. */ @@ -2007,8 +2384,10 @@ options_act(const or_options_t *old_options) /* We may need to reschedule some directory stuff if our status changed. */ if (old_options) { - if (authdir_mode_v3(options) && !authdir_mode_v3(old_options)) - dirvote_recalculate_timing(options, time(NULL)); + if (options_transition_affects_dirauth_timing(old_options, options)) { + voting_schedule_recalculate_timing(options, time(NULL)); + reschedule_dirvote(options); + } if (!bool_eq(directory_fetches_dir_info_early(options), directory_fetches_dir_info_early(old_options)) || !bool_eq(directory_fetches_dir_info_later(options), @@ -2065,6 +2444,7 @@ static const struct { { "--dump-config", ARGUMENT_OPTIONAL }, { "--list-fingerprint", TAKES_NO_ARGUMENT }, { "--keygen", TAKES_NO_ARGUMENT }, + { "--key-expiration", ARGUMENT_OPTIONAL }, { "--newpass", TAKES_NO_ARGUMENT }, { "--no-passphrase", TAKES_NO_ARGUMENT }, { "--passphrase-fd", ARGUMENT_NECESSARY }, @@ -2225,24 +2605,36 @@ options_trial_assign(config_line_t *list, unsigned flags, char **msg) return r; } - if (options_validate(get_options_mutable(), trial_options, + setopt_err_t rv; + or_options_t *cur_options = get_options_mutable(); + + in_option_validation = 1; + + if (options_validate(cur_options, trial_options, global_default_options, 1, msg) < 0) { or_options_free(trial_options); - return SETOPT_ERR_PARSE; /*XXX make this a separate return value. */ + rv = SETOPT_ERR_PARSE; /*XXX make this a separate return value. */ + goto done; } - if (options_transition_allowed(get_options(), trial_options, msg) < 0) { + if (options_transition_allowed(cur_options, trial_options, msg) < 0) { or_options_free(trial_options); - return SETOPT_ERR_TRANSITION; + rv = SETOPT_ERR_TRANSITION; + goto done; } + in_option_validation = 0; if (set_options(trial_options, msg)<0) { or_options_free(trial_options); - return SETOPT_ERR_SETTING; + rv = SETOPT_ERR_SETTING; + goto done; } /* we liked it. put it in place. */ - return SETOPT_OK; + rv = SETOPT_OK; + done: + in_option_validation = 0; + return rv; } /** Print a usage message for tor. */ @@ -2252,7 +2644,7 @@ print_usage(void) printf( "Copyright (c) 2001-2004, Roger Dingledine\n" "Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson\n" -"Copyright (c) 2007-2016, The Tor Project, Inc.\n\n" +"Copyright (c) 2007-2017, The Tor Project, Inc.\n\n" "tor -f <torrc> [args]\n" "See man page for options, or https://www.torproject.org/ for " "documentation.\n"); @@ -2333,8 +2725,8 @@ using_default_dir_authorities(const or_options_t *options) * Fail if one or more of the following is true: * - DNS name in <b>options-\>Address</b> cannot be resolved. * - <b>options-\>Address</b> is a local host address. - * - Attempt to getting local hostname fails. - * - Attempt to getting network interface address fails. + * - Attempt at getting local hostname fails. + * - Attempt at getting network interface address fails. * * Return 0 if all is well, or -1 if we can't find a suitable * public IP address. @@ -2714,13 +3106,13 @@ compute_publishserverdescriptor(or_options_t *options) #define MIN_REND_POST_PERIOD (10*60) #define MIN_REND_POST_PERIOD_TESTING (5) -/** Higest allowable value for PredictedPortsRelevanceTime; if this is - * too high, our selection of exits will decrease for an extended - * period of time to an uncomfortable level .*/ -#define MAX_PREDICTED_CIRCS_RELEVANCE (60*60) +/** Highest allowable value for CircuitsAvailableTimeout. + * If this is too large, client connections will stay open for too long, + * incurring extra padding overhead. */ +#define MAX_CIRCS_AVAILABLE_TIME (24*60*60) /** Highest allowable value for RendPostPeriod. */ -#define MAX_DIR_PERIOD (MIN_ONION_KEY_LIFETIME/2) +#define MAX_DIR_PERIOD ((7*24*60*60)/2) /** Lowest allowable value for MaxCircuitDirtiness; if this is too low, Tor * will generate too many circuits and potentially overload the network. */ @@ -2734,10 +3126,6 @@ compute_publishserverdescriptor(or_options_t *options) * will generate too many circuits and potentially overload the network. */ #define MIN_CIRCUIT_STREAM_TIMEOUT 10 -/** Lowest allowable value for HeartbeatPeriod; if this is too low, we might - * expose more information than we're comfortable with. */ -#define MIN_HEARTBEAT_PERIOD (30*60) - /** Lowest recommended value for CircuitBuildTimeout; if it is set too low * and LearnCircuitBuildTimeout is off, the failure rate for circuit * construction may be very high. In that case, if it is set below this @@ -2749,8 +3137,11 @@ static int options_validate_cb(void *old_options, void *options, void *default_options, int from_setconf, char **msg) { - return options_validate(old_options, options, default_options, + in_option_validation = 1; + int rv = options_validate(old_options, options, default_options, from_setconf, msg); + in_option_validation = 0; + return rv; } #define REJECT(arg) \ @@ -2761,15 +3152,17 @@ options_validate_cb(void *old_options, void *options, void *default_options, #else #define COMPLAIN(args, ...) \ STMT_BEGIN log_warn(LD_CONFIG, args, ##__VA_ARGS__); STMT_END -#endif +#endif /* defined(__GNUC__) && __GNUC__ <= 3 */ /** Log a warning message iff <b>filepath</b> is not absolute. * Warning message must contain option name <b>option</b> and * an absolute path that <b>filepath</b> will resolve to. * * In case <b>filepath</b> is absolute, do nothing. + * + * Return 1 if there were relative paths; 0 otherwise. */ -static void +static int warn_if_option_path_is_relative(const char *option, char *filepath) { @@ -2778,39 +3171,100 @@ warn_if_option_path_is_relative(const char *option, COMPLAIN("Path for %s (%s) is relative and will resolve to %s." " Is this what you wanted?", option, filepath, abs_path); tor_free(abs_path); + return 1; } + return 0; } -/** Scan <b>options</b> for occurances of relative file/directory +/** Scan <b>options</b> for occurrences of relative file/directory * path and log a warning whenever it is found. + * + * Return 1 if there were relative paths; 0 otherwise. */ -static void +static int warn_about_relative_paths(or_options_t *options) { tor_assert(options); + int n = 0; - warn_if_option_path_is_relative("CookieAuthFile", - options->CookieAuthFile); - warn_if_option_path_is_relative("ExtORPortCookieAuthFile", - options->ExtORPortCookieAuthFile); - warn_if_option_path_is_relative("DirPortFrontPage", - options->DirPortFrontPage); - warn_if_option_path_is_relative("V3BandwidthsFile", - options->V3BandwidthsFile); - warn_if_option_path_is_relative("ControlPortWriteToFile", - options->ControlPortWriteToFile); - warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile); - warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File); - warn_if_option_path_is_relative("Log",options->DebugLogFile); - warn_if_option_path_is_relative("AccelDir",options->AccelDir); - warn_if_option_path_is_relative("DataDirectory",options->DataDirectory); - warn_if_option_path_is_relative("PidFile",options->PidFile); + n += warn_if_option_path_is_relative("CookieAuthFile", + options->CookieAuthFile); + n += warn_if_option_path_is_relative("ExtORPortCookieAuthFile", + options->ExtORPortCookieAuthFile); + n += warn_if_option_path_is_relative("DirPortFrontPage", + options->DirPortFrontPage); + n += warn_if_option_path_is_relative("V3BandwidthsFile", + options->V3BandwidthsFile); + n += warn_if_option_path_is_relative("ControlPortWriteToFile", + options->ControlPortWriteToFile); + n += warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile); + n += warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File); + n += warn_if_option_path_is_relative("Log",options->DebugLogFile); + n += warn_if_option_path_is_relative("AccelDir",options->AccelDir); + n += warn_if_option_path_is_relative("DataDirectory",options->DataDirectory); + n += warn_if_option_path_is_relative("PidFile",options->PidFile); for (config_line_t *hs_line = options->RendConfigLines; hs_line; hs_line = hs_line->next) { if (!strcasecmp(hs_line->key, "HiddenServiceDir")) - warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value); + n += warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value); } + return n != 0; +} + +/* Validate options related to the scheduler. From the Schedulers list, the + * SchedulerTypes_ list is created with int values so once we select the + * scheduler, which can happen anytime at runtime, we don't have to parse + * strings and thus be quick. + * + * Return 0 on success else -1 and msg is set with an error message. */ +static int +options_validate_scheduler(or_options_t *options, char **msg) +{ + tor_assert(options); + tor_assert(msg); + + if (!options->Schedulers || smartlist_len(options->Schedulers) == 0) { + REJECT("Empty Schedulers list. Either remove the option so the defaults " + "can be used or set at least one value."); + } + /* Ok, we do have scheduler types, validate them. */ + options->SchedulerTypes_ = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(options->Schedulers, const char *, type) { + int *sched_type; + if (!strcasecmp("KISTLite", type)) { + sched_type = tor_malloc_zero(sizeof(int)); + *sched_type = SCHEDULER_KIST_LITE; + smartlist_add(options->SchedulerTypes_, sched_type); + } else if (!strcasecmp("KIST", type)) { + sched_type = tor_malloc_zero(sizeof(int)); + *sched_type = SCHEDULER_KIST; + smartlist_add(options->SchedulerTypes_, sched_type); + } else if (!strcasecmp("Vanilla", type)) { + sched_type = tor_malloc_zero(sizeof(int)); + *sched_type = SCHEDULER_VANILLA; + smartlist_add(options->SchedulerTypes_, sched_type); + } else { + tor_asprintf(msg, "Unknown type %s in option Schedulers. " + "Possible values are KIST, KISTLite and Vanilla.", + escaped(type)); + return -1; + } + } SMARTLIST_FOREACH_END(type); + + if (options->KISTSockBufSizeFactor < 0) { + REJECT("KISTSockBufSizeFactor must be at least 0"); + } + + /* Don't need to validate that the Interval is less than anything because + * zero is valid and all negative values are valid. */ + if (options->KISTSchedRunInterval > KIST_SCHED_RUN_INTERVAL_MAX) { + tor_asprintf(msg, "KISTSchedRunInterval must not be more than %d (ms)", + KIST_SCHED_RUN_INTERVAL_MAX); + return -1; + } + + return 0; } /* Validate options related to single onion services. @@ -2841,13 +3295,13 @@ options_validate_single_onion(or_options_t *options, char **msg) const int client_port_set = (options->SocksPort_set || options->TransPort_set || options->NATDPort_set || - options->DNSPort_set); + options->DNSPort_set || + options->HTTPTunnelPort_set); if (rend_service_non_anonymous_mode_enabled(options) && client_port_set && !options->Tor2webMode) { REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as " "an anonymous client. Please set Socks/Trans/NATD/DNSPort to 0, or " - "HiddenServiceNonAnonymousMode to 0, or use the non-anonymous " - "Tor2webMode."); + "revert HiddenServiceNonAnonymousMode to 0."); } /* If you run a hidden service in non-anonymous mode, the hidden service @@ -2857,12 +3311,12 @@ options_validate_single_onion(or_options_t *options, char **msg) REJECT("Non-anonymous (Tor2web) mode is incompatible with using Tor as a " "hidden service. Please remove all HiddenServiceDir lines, or use " "a version of tor compiled without --enable-tor2web-mode, or use " - " HiddenServiceNonAnonymousMode."); + "HiddenServiceNonAnonymousMode."); } if (rend_service_allow_non_anonymous_connection(options) && options->UseEntryGuards) { - /* Single Onion services only use entry guards when uploading descriptors, + /* Single Onion services only use entry guards when uploading descriptors; * all other connections are one-hop. Further, Single Onions causes the * hidden service code to do things which break the path bias * detector, and it's far easier to turn off entry guards (and @@ -2904,13 +3358,21 @@ options_validate(or_options_t *old_options, or_options_t *options, tor_assert(msg); *msg = NULL; + if (parse_ports(options, 1, msg, &n_ports, + &world_writable_control_socket) < 0) + return -1; + /* Set UseEntryGuards from the configured value, before we check it below. - * We change UseEntryGuards whenn it's incompatible with other options, + * We change UseEntryGuards when it's incompatible with other options, * but leave UseEntryGuards_option with the original value. * Always use the value of UseEntryGuards, not UseEntryGuards_option. */ options->UseEntryGuards = options->UseEntryGuards_option; - warn_about_relative_paths(options); + if (warn_about_relative_paths(options) && options->RunAsDaemon) { + REJECT("You have specified at least one relative path (see above) " + "with the RunAsDaemon option. RunAsDaemon is not compatible " + "with relative paths."); + } if (server_mode(options) && (!strcmpstart(uname, "Windows 95") || @@ -2922,19 +3384,15 @@ options_validate(or_options_t *old_options, or_options_t *options, "for details.", uname); } - if (parse_ports(options, 1, msg, &n_ports, - &world_writable_control_socket) < 0) - return -1; - if (parse_outbound_addresses(options, 1, msg) < 0) return -1; - if (validate_data_directory(options)<0) + if (validate_data_directories(options)<0) REJECT("Invalid DataDirectory"); if (options->Nickname == NULL) { if (server_mode(options)) { - options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME); + options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME); } } else { if (!is_legal_nickname(options->Nickname)) { @@ -2954,9 +3412,9 @@ options_validate(or_options_t *old_options, or_options_t *options, /* Special case on first boot if no Log options are given. */ if (!options->Logs && !options->RunAsDaemon && !from_setconf) { if (quiet_level == 0) - config_line_append(&options->Logs, "Log", "notice stdout"); + config_line_append(&options->Logs, "Log", "notice stdout"); else if (quiet_level == 1) - config_line_append(&options->Logs, "Log", "warn stdout"); + config_line_append(&options->Logs, "Log", "warn stdout"); } /* Validate the tor_log(s) */ @@ -2991,13 +3449,13 @@ options_validate(or_options_t *old_options, or_options_t *options, if (!strcasecmp(options->TransProxyType, "default")) { options->TransProxyType_parsed = TPT_DEFAULT; } else if (!strcasecmp(options->TransProxyType, "pf-divert")) { -#if !defined(__OpenBSD__) && !defined( DARWIN ) +#if !defined(OpenBSD) && !defined( DARWIN ) /* Later versions of OS X have pf */ REJECT("pf-divert is a OpenBSD-specific " "and OS X/Darwin-specific feature."); #else options->TransProxyType_parsed = TPT_PF_DIVERT; -#endif +#endif /* !defined(OpenBSD) && !defined( DARWIN ) */ } else if (!strcasecmp(options->TransProxyType, "tproxy")) { #if !defined(__linux__) REJECT("TPROXY is a Linux-specific feature."); @@ -3011,22 +3469,20 @@ options_validate(or_options_t *old_options, or_options_t *options, "and OS X/Darwin-specific feature."); #else options->TransProxyType_parsed = TPT_IPFW; -#endif +#endif /* !defined(KERNEL_MAY_SUPPORT_IPFW) */ } else { REJECT("Unrecognized value for TransProxyType"); } if (strcasecmp(options->TransProxyType, "default") && !options->TransPort_set) { - REJECT("Cannot use TransProxyType without any valid TransPort or " - "TransListenAddress."); + REJECT("Cannot use TransProxyType without any valid TransPort."); } } -#else +#else /* !(defined(USE_TRANSPARENT)) */ if (options->TransPort_set) - REJECT("TransPort and TransListenAddress are disabled " - "in this build."); -#endif + REJECT("TransPort is disabled in this build."); +#endif /* defined(USE_TRANSPARENT) */ if (options->TokenBucketRefillInterval <= 0 || options->TokenBucketRefillInterval > 1000) { @@ -3039,17 +3495,6 @@ options_validate(or_options_t *old_options, or_options_t *options, routerset_union(options->ExcludeExitNodesUnion_,options->ExcludeNodes); } - if (options->SchedulerLowWaterMark__ == 0 || - options->SchedulerLowWaterMark__ > UINT32_MAX) { - log_warn(LD_GENERAL, "Bad SchedulerLowWaterMark__ option"); - return -1; - } else if (options->SchedulerHighWaterMark__ <= - options->SchedulerLowWaterMark__ || - options->SchedulerHighWaterMark__ > UINT32_MAX) { - log_warn(LD_GENERAL, "Bad SchedulerHighWaterMark option"); - return -1; - } - if (options->NodeFamilies) { options->NodeFamilySets = smartlist_new(); for (cl = options->NodeFamilies; cl; cl = cl->next) { @@ -3062,15 +3507,6 @@ options_validate(or_options_t *old_options, or_options_t *options, } } - if (options->TLSECGroup && (strcasecmp(options->TLSECGroup, "P256") && - strcasecmp(options->TLSECGroup, "P224"))) { - COMPLAIN("Unrecognized TLSECGroup: Falling back to the default."); - tor_free(options->TLSECGroup); - } - if (!evaluate_ecgroup_for_tls(options->TLSECGroup)) { - REJECT("Unsupported TLSECGroup."); - } - if (options->ExcludeNodes && options->StrictNodes) { COMPLAIN("You have asked to exclude certain relays from all positions " "in your circuits. Expect hidden services and other Tor " @@ -3099,19 +3535,21 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("Versioning authoritative dir servers must set " "Recommended*Versions."); +#ifdef HAVE_MODULE_DIRAUTH 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); +#endif if (options->UseEntryGuards) { log_info(LD_CONFIG, "Authoritative directory servers can't set " "UseEntryGuards. Disabling."); options->UseEntryGuards = 0; } - if (!options->DownloadExtraInfo && authdir_mode_any_main(options)) { + 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; @@ -3265,23 +3703,6 @@ options_validate(or_options_t *old_options, or_options_t *options, "of the Internet, so they must not set Reachable*Addresses " "or FascistFirewall or FirewallPorts or ClientUseIPv4 0."); - /* We check if Reachable*Addresses blocks all addresses in - * parse_reachable_addresses(). */ - -#define WARN_PLEASE_USE_IPV6_LOG_MSG \ - "ClientPreferIPv6%sPort 1 is ignored unless tor is using IPv6. " \ - "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges." - - if (!fascist_firewall_use_ipv6(options) - && options->ClientPreferIPv6ORPort == 1) - log_warn(LD_CONFIG, WARN_PLEASE_USE_IPV6_LOG_MSG, "OR"); - - if (!fascist_firewall_use_ipv6(options) - && options->ClientPreferIPv6DirPort == 1) - log_warn(LD_CONFIG, WARN_PLEASE_USE_IPV6_LOG_MSG, "Dir"); - -#undef WARN_PLEASE_USE_IPV6_LOG_MSG - if (options->UseBridges && server_mode(options)) REJECT("Servers must be able to freely connect to the rest " @@ -3293,33 +3714,16 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->UseBridges && options->EntryNodes) REJECT("You cannot set both UseBridges and EntryNodes."); + /* If we have UseBridges as 1 and UseEntryGuards as 0, we end up bypassing + * the use of bridges */ + if (options->UseBridges && !options->UseEntryGuards) + REJECT("Setting UseBridges requires also setting UseEntryGuards."); + options->MaxMemInQueues = compute_real_max_mem_in_queues(options->MaxMemInQueues_raw, server_mode(options)); options->MaxMemInQueues_low_threshold = (options->MaxMemInQueues / 4) * 3; - options->AllowInvalid_ = 0; - - if (options->AllowInvalidNodes) { - SMARTLIST_FOREACH_BEGIN(options->AllowInvalidNodes, const char *, cp) { - if (!strcasecmp(cp, "entry")) - options->AllowInvalid_ |= ALLOW_INVALID_ENTRY; - else if (!strcasecmp(cp, "exit")) - options->AllowInvalid_ |= ALLOW_INVALID_EXIT; - else if (!strcasecmp(cp, "middle")) - options->AllowInvalid_ |= ALLOW_INVALID_MIDDLE; - else if (!strcasecmp(cp, "introduction")) - options->AllowInvalid_ |= ALLOW_INVALID_INTRODUCTION; - else if (!strcasecmp(cp, "rendezvous")) - options->AllowInvalid_ |= ALLOW_INVALID_RENDEZVOUS; - else { - tor_asprintf(msg, - "Unrecognized value '%s' in AllowInvalidNodes", cp); - return -1; - } - } SMARTLIST_FOREACH_END(cp); - } - if (!options->SafeLogging || !strcasecmp(options->SafeLogging, "0")) { options->SafeLogging_ = SAFELOG_SCRUB_NONE; @@ -3355,6 +3759,23 @@ options_validate(or_options_t *old_options, or_options_t *options, options->DirPort_set = 0; } + if (server_mode(options) && options->ConnectionPadding != -1) { + REJECT("Relays must use 'auto' for the ConnectionPadding setting."); + } + + if (server_mode(options) && options->ReducedConnectionPadding != 0) { + REJECT("Relays cannot set ReducedConnectionPadding. "); + } + + 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->MinUptimeHidServDirectoryV2 < 0) { log_warn(LD_CONFIG, "MinUptimeHidServDirectoryV2 option must be at " "least 0 seconds. Changing to 0."); @@ -3367,7 +3788,7 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->RendPostPeriod < min_rendpostperiod) { log_warn(LD_CONFIG, "RendPostPeriod option is too short; " "raising to %d seconds.", min_rendpostperiod); - options->RendPostPeriod = min_rendpostperiod;; + options->RendPostPeriod = min_rendpostperiod; } if (options->RendPostPeriod > MAX_DIR_PERIOD) { @@ -3376,17 +3797,17 @@ options_validate(or_options_t *old_options, or_options_t *options, options->RendPostPeriod = MAX_DIR_PERIOD; } - if (options->PredictedPortsRelevanceTime > - MAX_PREDICTED_CIRCS_RELEVANCE) { - log_warn(LD_CONFIG, "PredictedPortsRelevanceTime is too large; " - "clipping to %ds.", MAX_PREDICTED_CIRCS_RELEVANCE); - options->PredictedPortsRelevanceTime = MAX_PREDICTED_CIRCS_RELEVANCE; - } - /* Check the Single Onion Service options */ if (options_validate_single_onion(options, msg) < 0) return -1; + if (options->CircuitsAvailableTimeout > MAX_CIRCS_AVAILABLE_TIME) { + // options_t is immutable for new code (the above code is older), + // so just make the user fix the value themselves rather than + // silently keep a shadow value lower than what they asked for. + REJECT("CircuitsAvailableTimeout is too large. Max is 24 hours."); + } + #ifdef ENABLE_TOR2WEB_MODE if (options->Tor2webMode && options->UseEntryGuards) { /* tor2web mode clients do not (and should not) use entry guards @@ -3401,7 +3822,7 @@ options_validate(or_options_t *old_options, or_options_t *options, "Tor2WebMode is enabled; disabling UseEntryGuards."); options->UseEntryGuards = 0; } -#endif +#endif /* defined(ENABLE_TOR2WEB_MODE) */ if (options->Tor2webRendezvousPoints && !options->Tor2webMode) { REJECT("Tor2webRendezvousPoints cannot be set without Tor2webMode."); @@ -3421,6 +3842,11 @@ options_validate(or_options_t *old_options, or_options_t *options, "http://freehaven.net/anonbib/#hs-attack06 for details."); } + if (options->NumPrimaryGuards && options->NumEntryGuards && + options->NumEntryGuards > options->NumPrimaryGuards) { + REJECT("NumEntryGuards must not be greater than NumPrimaryGuards."); + } + if (options->EntryNodes && routerset_is_list(options->EntryNodes) && (routerset_len(options->EntryNodes) == 1) && @@ -3436,6 +3862,20 @@ options_validate(or_options_t *old_options, or_options_t *options, return -1; } + /* Inform the hidden service operator that pinning EntryNodes can possibly + * be harmful for the service anonymity. */ + if (options->EntryNodes && + routerset_is_list(options->EntryNodes) && + (options->RendConfigLines != NULL)) { + log_warn(LD_CONFIG, + "EntryNodes is set with multiple entries and at least one " + "hidden service is configured. Pinning entry nodes can possibly " + "be harmful to the service anonymity. Because of this, we " + "recommend you either don't do that or make sure you know what " + "you are doing. For more details, please look at " + "https://trac.torproject.org/projects/tor/ticket/21155."); + } + /* Single Onion Services: non-anonymous hidden services */ if (rend_service_non_anonymous_mode_enabled(options)) { log_warn(LD_CONFIG, @@ -3461,7 +3901,7 @@ options_validate(or_options_t *old_options, or_options_t *options, int severity = LOG_NOTICE; /* Be a little quieter if we've deliberately disabled * LearnCircuitBuildTimeout. */ - if (circuit_build_times_disabled()) { + if (circuit_build_times_disabled_(options, 1)) { severity = LOG_INFO; } log_fn(severity, LD_CONFIG, "You disabled LearnCircuitBuildTimeout, but " @@ -3529,11 +3969,6 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->KeepalivePeriod < 1) REJECT("KeepalivePeriod option must be positive."); - if (options->PortForwarding && options->Sandbox) { - REJECT("PortForwarding is not compatible with Sandbox; at most one can " - "be set"); - } - if (ensure_bandwidth_cap(&options->BandwidthRate, "BandwidthRate", msg) < 0) return -1; @@ -3791,13 +4226,14 @@ options_validate(or_options_t *old_options, or_options_t *options, "have it group-readable."); } - if (options->MyFamily && options->BridgeRelay) { + 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 (check_nickname_list(&options->MyFamily, "MyFamily", msg)) + if (normalize_nickname_list(&options->MyFamily, + options->MyFamily_lines, "MyFamily", msg)) return -1; for (cl = options->NodeFamilies; cl; cl = cl->next) { routerset_t *rs = routerset_new(); @@ -3970,7 +4406,7 @@ options_validate(or_options_t *old_options, or_options_t *options, COMPLAIN("V3AuthVotingInterval does not divide evenly into 24 hours."); } - if (rend_config_services(options, 1) < 0) + if (hs_config_service_all(options, 1) < 0) REJECT("Failed to configure rendezvous options. See logs for details."); /* Parse client-side authorization for hidden services. */ @@ -3994,13 +4430,6 @@ options_validate(or_options_t *old_options, or_options_t *options, "AlternateDirAuthority and AlternateBridgeAuthority configured."); } - if (options->AllowSingleHopExits && !options->DirAuthorities) { - COMPLAIN("You have set AllowSingleHopExits; now your relay will allow " - "others to make one-hop exits. However, since by default most " - "clients avoid relays that set this option, most clients will " - "ignore you."); - } - #define CHECK_DEFAULT(arg) \ STMT_BEGIN \ if (!options->TestingTorNetwork && \ @@ -4016,17 +4445,14 @@ options_validate(or_options_t *old_options, or_options_t *options, 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(TestingServerDownloadInitialDelay); + CHECK_DEFAULT(TestingClientDownloadInitialDelay); + CHECK_DEFAULT(TestingServerConsensusDownloadInitialDelay); + CHECK_DEFAULT(TestingClientConsensusDownloadInitialDelay); + CHECK_DEFAULT(TestingBridgeDownloadInitialDelay); + CHECK_DEFAULT(TestingBridgeBootstrapDownloadInitialDelay); CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest); CHECK_DEFAULT(TestingDirConnectionMaxStall); - CHECK_DEFAULT(TestingConsensusMaxDownloadTries); - CHECK_DEFAULT(TestingDescriptorMaxDownloadTries); - CHECK_DEFAULT(TestingMicrodescMaxDownloadTries); - CHECK_DEFAULT(TestingCertMaxDownloadTries); CHECK_DEFAULT(TestingAuthKeyLifetime); CHECK_DEFAULT(TestingLinkCertLifetime); CHECK_DEFAULT(TestingSigningKeySlop); @@ -4034,6 +4460,10 @@ options_validate(or_options_t *old_options, or_options_t *options, CHECK_DEFAULT(TestingLinkKeySlop); #undef CHECK_DEFAULT + if (!options->ClientDNSRejectInternalAddresses && + !(options->DirAuthorities || + (options->AlternateDirAuthority && options->AlternateBridgeAuthority))) + REJECT("ClientDNSRejectInternalAddresses used for default network."); if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2) REJECT("SigningKeyLifetime is too short."); if (options->TestingLinkCertLifetime < options->TestingAuthKeySlop*2) @@ -4097,33 +4527,6 @@ options_validate(or_options_t *old_options, or_options_t *options, COMPLAIN("TestingDirConnectionMaxStall is insanely high."); } - if (options->TestingConsensusMaxDownloadTries < 2) { - REJECT("TestingConsensusMaxDownloadTries must be greater than 2."); - } else if (options->TestingConsensusMaxDownloadTries > 800) { - COMPLAIN("TestingConsensusMaxDownloadTries is insanely high."); - } - - if (options->ClientBootstrapConsensusMaxDownloadTries < 2) { - REJECT("ClientBootstrapConsensusMaxDownloadTries must be greater " - "than 2." - ); - } else if (options->ClientBootstrapConsensusMaxDownloadTries > 800) { - COMPLAIN("ClientBootstrapConsensusMaxDownloadTries is insanely " - "high."); - } - - if (options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries - < 2) { - REJECT("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries must " - "be greater than 2." - ); - } else if ( - options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries - > 800) { - COMPLAIN("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries is " - "insanely high."); - } - if (options->ClientBootstrapConsensusMaxInProgressTries < 1) { REJECT("ClientBootstrapConsensusMaxInProgressTries must be greater " "than 0."); @@ -4133,24 +4536,6 @@ options_validate(or_options_t *old_options, or_options_t *options, "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->TestingEnableConnBwEvent && !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { REJECT("TestingEnableConnBwEvent may only be changed in testing " @@ -4163,12 +4548,6 @@ options_validate(or_options_t *old_options, or_options_t *options, "Tor networks!"); } - if (options->TestingEnableTbEmptyEvent && - !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { - REJECT("TestingEnableTbEmptyEvent may only be changed in testing " - "Tor networks!"); - } - if (options->TestingTorNetwork) { log_warn(LD_CONFIG, "TestingTorNetwork is set. This will make your node " "almost unusable in the public Tor network, and is " @@ -4197,6 +4576,10 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid " "combination."); + if (options_validate_scheduler(options, msg) < 0) { + return -1; + } + return 0; } @@ -4206,7 +4589,7 @@ options_validate(or_options_t *old_options, or_options_t *options, /* Given the value that the user has set for MaxMemInQueues, compute the * actual maximum value. We clip this value if it's too low, and autodetect * it if it's set to 0. */ -static uint64_t +STATIC uint64_t compute_real_max_mem_in_queues(const uint64_t val, int log_guess) { uint64_t result; @@ -4214,11 +4597,6 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess) if (val == 0) { #define ONE_GIGABYTE (U64_LITERAL(1) << 30) #define ONE_MEGABYTE (U64_LITERAL(1) << 20) -#if SIZEOF_VOID_P >= 8 -#define MAX_DEFAULT_MAXMEM (8*ONE_GIGABYTE) -#else -#define MAX_DEFAULT_MAXMEM (2*ONE_GIGABYTE) -#endif /* The user didn't pick a memory limit. Choose a very large one * that is still smaller than the system memory */ static int notice_sent = 0; @@ -4231,16 +4609,40 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess) #else /* (presumably) 32-bit system. Let's hope for 1 GB. */ result = ONE_GIGABYTE; -#endif +#endif /* SIZEOF_VOID_P >= 8 */ } else { - /* We detected it, so let's pick 3/4 of the total RAM as our limit. */ - const uint64_t avail = (ram / 4) * 3; + /* We detected the amount of memory available. */ + uint64_t avail = 0; + +#if SIZEOF_SIZE_T > 4 +/* On a 64-bit platform, we consider 8GB "very large". */ +#define RAM_IS_VERY_LARGE(x) ((x) >= (8 * ONE_GIGABYTE)) +#else +/* On a 32-bit platform, we can't have 8GB of ram. */ +#define RAM_IS_VERY_LARGE(x) (0) +#endif - /* Make sure it's in range from 0.25 GB to 8 GB. */ - if (avail > MAX_DEFAULT_MAXMEM) { + if (RAM_IS_VERY_LARGE(ram)) { + /* If we have 8 GB, or more, RAM available, we set the MaxMemInQueues + * to 0.4 * RAM. The idea behind this value is that the amount of RAM + * is more than enough for a single relay and should allow the relay + * operator to run two relays if they have additional bandwidth + * available. + */ + avail = (ram / 5) * 2; + } else { + /* If we have less than 8 GB of RAM available, we use the "old" default + * for MaxMemInQueues of 0.75 * RAM. + */ + avail = (ram / 4) * 3; + } + + /* Make sure it's in range from 0.25 GB to 8 GB for 64-bit and 0.25 to 2 + * GB for 32-bit. */ + if (avail > MAX_DEFAULT_MEMORY_QUEUE_SIZE) { /* If you want to use more than this much RAM, you need to configure it yourself */ - result = MAX_DEFAULT_MAXMEM; + result = MAX_DEFAULT_MEMORY_QUEUE_SIZE; } else if (avail < ONE_GIGABYTE / 4) { result = ONE_GIGABYTE / 4; } else { @@ -4266,8 +4668,8 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess) } /* If we have less than 300 MB suggest disabling dircache */ -#define DIRCACHE_MIN_MB_BANDWIDTH 300 -#define DIRCACHE_MIN_BANDWIDTH (DIRCACHE_MIN_MB_BANDWIDTH*ONE_MEGABYTE) +#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 @@ -4287,21 +4689,20 @@ have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem, } } if (options->DirCache) { - if (total_mem < DIRCACHE_MIN_BANDWIDTH) { + if (total_mem < DIRCACHE_MIN_MEM_BYTES) { if (options->BridgeRelay) { - *msg = tor_strdup("Running a Bridge with less than " - STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is " - "not recommended."); + tor_asprintf(msg, "Running a Bridge with less than %d MB of memory " + "is not recommended.", DIRCACHE_MIN_MEM_MB); } else { - *msg = tor_strdup("Being a directory cache (default) with less than " - STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " 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."); + 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_BANDWIDTH) { + if (total_mem >= DIRCACHE_MIN_MEM_BYTES) { *msg = tor_strdup("DirCache is disabled and we are configured as a " "relay. This may disqualify us from becoming a guard in the " "future."); @@ -4328,120 +4729,61 @@ options_transition_allowed(const or_options_t *old, if (!old) return 0; - if (!opt_streq(old->PidFile, new_val->PidFile)) { - *msg = tor_strdup("PidFile is not allowed to change."); - return -1; - } - - if (old->RunAsDaemon != new_val->RunAsDaemon) { - *msg = tor_strdup("While Tor is running, changing RunAsDaemon " - "is not allowed."); - return -1; - } - - if (old->Sandbox != new_val->Sandbox) { - *msg = tor_strdup("While Tor is running, changing Sandbox " - "is not allowed."); - return -1; - } - - if (strcmp(old->DataDirectory,new_val->DataDirectory)!=0) { - tor_asprintf(msg, - "While Tor is running, changing DataDirectory " - "(\"%s\"->\"%s\") is not allowed.", - old->DataDirectory, new_val->DataDirectory); - return -1; - } - - if (!opt_streq(old->User, new_val->User)) { - *msg = tor_strdup("While Tor is running, changing User is not allowed."); - return -1; - } - - if (old->KeepBindCapabilities != new_val->KeepBindCapabilities) { - *msg = tor_strdup("While Tor is running, changing KeepBindCapabilities is " - "not allowed."); - return -1; - } - - if (!opt_streq(old->SyslogIdentityTag, new_val->SyslogIdentityTag)) { - *msg = tor_strdup("While Tor is running, changing " - "SyslogIdentityTag is not allowed."); - return -1; - } - - if ((old->HardwareAccel != new_val->HardwareAccel) - || !opt_streq(old->AccelName, new_val->AccelName) - || !opt_streq(old->AccelDir, new_val->AccelDir)) { - *msg = tor_strdup("While Tor is running, changing OpenSSL hardware " - "acceleration engine is not allowed."); - return -1; - } - - if (old->TestingTorNetwork != new_val->TestingTorNetwork) { - *msg = tor_strdup("While Tor is running, changing TestingTorNetwork " - "is not allowed."); - return -1; - } - - if (old->DisableAllSwap != new_val->DisableAllSwap) { - *msg = tor_strdup("While Tor is running, changing DisableAllSwap " - "is not allowed."); - return -1; - } - - if (old->TokenBucketRefillInterval != new_val->TokenBucketRefillInterval) { - *msg = tor_strdup("While Tor is running, changing TokenBucketRefill" - "Interval is not allowed"); - return -1; - } - - if (old->HiddenServiceSingleHopMode != new_val->HiddenServiceSingleHopMode) { - *msg = tor_strdup("While Tor is running, changing " - "HiddenServiceSingleHopMode is not allowed."); - return -1; - } - - if (old->HiddenServiceNonAnonymousMode != - new_val->HiddenServiceNonAnonymousMode) { - *msg = tor_strdup("While Tor is running, changing " - "HiddenServiceNonAnonymousMode is not allowed."); - return -1; - } - - if (old->DisableDebuggerAttachment && - !new_val->DisableDebuggerAttachment) { - *msg = tor_strdup("While Tor is running, disabling " - "DisableDebuggerAttachment is not allowed."); - return -1; - } +#define BAD_CHANGE_TO(opt, how) do { \ + *msg = tor_strdup("While Tor is running"how", changing " #opt \ + " is not allowed"); \ + return -1; \ + } while (0) + +#define NO_CHANGE_BOOL(opt) \ + if (! CFG_EQ_BOOL(old, new_val, opt)) BAD_CHANGE_TO(opt,"") +#define NO_CHANGE_INT(opt) \ + if (! CFG_EQ_INT(old, new_val, opt)) BAD_CHANGE_TO(opt,"") +#define NO_CHANGE_STRING(opt) \ + if (! CFG_EQ_STRING(old, new_val, opt)) BAD_CHANGE_TO(opt,"") + + NO_CHANGE_STRING(PidFile); + NO_CHANGE_BOOL(RunAsDaemon); + NO_CHANGE_BOOL(Sandbox); + NO_CHANGE_STRING(DataDirectory); + NO_CHANGE_STRING(KeyDirectory); + NO_CHANGE_STRING(CacheDirectory); + NO_CHANGE_STRING(User); + NO_CHANGE_BOOL(KeepBindCapabilities); + NO_CHANGE_STRING(SyslogIdentityTag); + NO_CHANGE_STRING(AndroidIdentityTag); + NO_CHANGE_BOOL(HardwareAccel); + NO_CHANGE_STRING(AccelName); + NO_CHANGE_STRING(AccelDir); + NO_CHANGE_BOOL(TestingTorNetwork); + NO_CHANGE_BOOL(DisableAllSwap); + NO_CHANGE_INT(TokenBucketRefillInterval); + NO_CHANGE_BOOL(HiddenServiceSingleHopMode); + NO_CHANGE_BOOL(HiddenServiceNonAnonymousMode); + NO_CHANGE_BOOL(DisableDebuggerAttachment); + NO_CHANGE_BOOL(NoExec); + NO_CHANGE_INT(OwningControllerFD); + NO_CHANGE_BOOL(DisableSignalHandlers); if (sandbox_is_active()) { -#define SB_NOCHANGE_STR(opt) \ - do { \ - if (! opt_streq(old->opt, new_val->opt)) { \ - *msg = tor_strdup("Can't change " #opt " while Sandbox is active"); \ - return -1; \ - } \ - } while (0) +#define SB_NOCHANGE_STR(opt) \ + if (! CFG_EQ_STRING(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") +#define SB_NOCHANGE_LINELIST(opt) \ + if (! CFG_EQ_LINELIST(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") +#define SB_NOCHANGE_INT(opt) \ + if (! CFG_EQ_INT(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") SB_NOCHANGE_STR(Address); - SB_NOCHANGE_STR(PidFile); SB_NOCHANGE_STR(ServerDNSResolvConfFile); SB_NOCHANGE_STR(DirPortFrontPage); SB_NOCHANGE_STR(CookieAuthFile); SB_NOCHANGE_STR(ExtORPortCookieAuthFile); + SB_NOCHANGE_LINELIST(Logs); + SB_NOCHANGE_INT(ConnLimit); -#undef SB_NOCHANGE_STR - - if (! config_lines_eq(old->Logs, new_val->Logs)) { - *msg = tor_strdup("Can't change Logs while Sandbox is active"); - return -1; - } - if (old->ConnLimit != new_val->ConnLimit) { - *msg = tor_strdup("Can't change ConnLimit while Sandbox is active"); - return -1; - } if (server_mode(old) != server_mode(new_val)) { *msg = tor_strdup("Can't start/stop being a server while " "Sandbox is active"); @@ -4449,6 +4791,13 @@ options_transition_allowed(const or_options_t *old, } } +#undef SB_NOCHANGE_LINELIST +#undef SB_NOCHANGE_STR +#undef SB_NOCHANGE_INT +#undef BAD_CHANGE_TO +#undef NO_CHANGE_BOOL +#undef NO_CHANGE_INT +#undef NO_CHANGE_STRING return 0; } @@ -4458,21 +4807,20 @@ static int options_transition_affects_workers(const or_options_t *old_options, const or_options_t *new_options) { - if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || - old_options->NumCPUs != new_options->NumCPUs || - !config_lines_eq(old_options->ORPort_lines, new_options->ORPort_lines) || - old_options->ServerDNSSearchDomains != - new_options->ServerDNSSearchDomains || - old_options->SafeLogging_ != new_options->SafeLogging_ || - old_options->ClientOnly != new_options->ClientOnly || - server_mode(old_options) != server_mode(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) || - !config_lines_eq(old_options->Logs, new_options->Logs) || - old_options->LogMessageDomains != new_options->LogMessageDomains) + dir_server_mode(old_options) != dir_server_mode(new_options)) return 1; - /* Check whether log options match. */ - /* Nothing that changed matters. */ return 0; } @@ -4485,35 +4833,34 @@ options_transition_affects_descriptor(const or_options_t *old_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. */ - if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || - !opt_streq(old_options->Nickname,new_options->Nickname) || - !opt_streq(old_options->Address,new_options->Address) || - !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) || - old_options->ExitRelay != new_options->ExitRelay || - old_options->ExitPolicyRejectPrivate != - new_options->ExitPolicyRejectPrivate || - old_options->ExitPolicyRejectLocalInterfaces != - new_options->ExitPolicyRejectLocalInterfaces || - old_options->IPv6Exit != new_options->IPv6Exit || - !config_lines_eq(old_options->ORPort_lines, - new_options->ORPort_lines) || - !config_lines_eq(old_options->DirPort_lines, - new_options->DirPort_lines) || - old_options->ClientOnly != new_options->ClientOnly || - old_options->DisableNetwork != new_options->DisableNetwork || - old_options->PublishServerDescriptor_ != - new_options->PublishServerDescriptor_ || - get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || + + 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 (get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || get_effective_bwburst(old_options) != get_effective_bwburst(new_options) || - !opt_streq(old_options->ContactInfo, new_options->ContactInfo) || - !opt_streq(old_options->MyFamily, new_options->MyFamily) || - !opt_streq(old_options->AccountingStart, new_options->AccountingStart) || - old_options->AccountingMax != new_options->AccountingMax || - old_options->AccountingRule != new_options->AccountingRule || - public_server_mode(old_options) != public_server_mode(new_options) || - old_options->DirCache != new_options->DirCache || - old_options->AssumeReachable != new_options->AssumeReachable) + public_server_mode(old_options) != public_server_mode(new_options)) return 1; return 0; @@ -4560,7 +4907,7 @@ get_windows_conf_root(void) path[sizeof(path)-1] = '\0'; #else strlcpy(path,tpath,sizeof(path)); -#endif +#endif /* defined(UNICODE) */ /* Now we need to free the memory that the path-idl was stored in. In * typical Windows fashion, we can't just call 'free()' on it. */ @@ -4576,7 +4923,7 @@ get_windows_conf_root(void) is_set = 1; return path; } -#endif +#endif /* defined(_WIN32) */ /** Return the default location for our torrc file (if <b>defaults_file</b> is * false), or for the torrc-defaults file (if <b>defaults_file</b> is true). */ @@ -4600,30 +4947,39 @@ get_default_conf_file(int defaults_file) } #else return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc"; -#endif +#endif /* defined(DISABLE_SYSTEM_TORRC) || ... */ } -/** Verify whether lst is a string containing valid-looking comma-separated - * nicknames, or NULL. Will normalise <b>lst</b> to prefix '$' to any nickname - * or fingerprint that needs it. Return 0 on success. +/** 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 -check_nickname_list(char **lst, const char *name, char **msg) +normalize_nickname_list(config_line_t **normalized_out, + const config_line_t *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, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 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; - SMARTLIST_FOREACH_BEGIN(sl, char *, s) + 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] != '$') { @@ -4632,36 +4988,45 @@ check_nickname_list(char **lst, const char *name, char **msg) 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; + // 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); } - - // 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; + if (!normalized) { + tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name); + valid_line = 0; + break; + } + } else { + normalized = tor_strdup(s); } - } - 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; + 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; + } } - SMARTLIST_FOREACH(sl, char *, s, tor_free(s)); - smartlist_free(sl); + *normalized_out = new_nicknames; - return r; + return 0; } /** Learn config file name from command line arguments, or use the default. @@ -4733,9 +5098,9 @@ find_torrc_filename(config_line_t *cmd_arg, } else { fname = dflt ? tor_strdup(dflt) : NULL; } -#else +#else /* !(!defined(_WIN32)) */ fname = dflt ? tor_strdup(dflt) : NULL; -#endif +#endif /* !defined(_WIN32) */ } } return fname; @@ -4809,7 +5174,8 @@ load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file) /** Read a configuration file into <b>options</b>, finding the configuration * file location based on the command line. After loading the file * call options_init_from_string() to load the config. - * Return 0 if success, -1 if failure. */ + * Return 0 if success, -1 if failure, and 1 if we succeeded but should exit + * anyway. */ int options_init_from_torrc(int argc, char **argv) { @@ -4836,22 +5202,22 @@ options_init_from_torrc(int argc, char **argv) if (config_line_find(cmdline_only_options, "-h") || config_line_find(cmdline_only_options, "--help")) { print_usage(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--list-torrc-options")) { /* For validating whether we've documented everything. */ list_torrc_options(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--list-deprecated-options")) { /* For validating whether what we have deprecated really exists. */ list_deprecated_options(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--version")) { printf("Tor version %s.\n",get_version()); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--library-versions")) { @@ -4863,17 +5229,32 @@ options_init_from_torrc(int argc, char **argv) printf("OpenSSL \t\t%-15s\t\t%s\n", crypto_openssl_get_header_version_str(), crypto_openssl_get_version_str()); - printf("Zlib \t\t%-15s\t\t%s\n", - tor_zlib_get_header_version_str(), - tor_zlib_get_version_str()); + if (tor_compress_supports_method(ZLIB_METHOD)) { + printf("Zlib \t\t%-15s\t\t%s\n", + tor_compress_version_str(ZLIB_METHOD), + tor_compress_header_version_str(ZLIB_METHOD)); + } + if (tor_compress_supports_method(LZMA_METHOD)) { + printf("Liblzma \t\t%-15s\t\t%s\n", + tor_compress_version_str(LZMA_METHOD), + tor_compress_header_version_str(LZMA_METHOD)); + } + if (tor_compress_supports_method(ZSTD_METHOD)) { + printf("Libzstd \t\t%-15s\t\t%s\n", + tor_compress_version_str(ZSTD_METHOD), + tor_compress_header_version_str(ZSTD_METHOD)); + } //TODO: Hex versions? - exit(0); + return 1; } command = CMD_RUN_TOR; for (p_index = cmdline_only_options; p_index; p_index = p_index->next) { if (!strcmp(p_index->key,"--keygen")) { command = CMD_KEYGEN; + } else if (!strcmp(p_index->key, "--key-expiration")) { + command = CMD_KEY_EXPIRATION; + command_arg = p_index->value; } else if (!strcmp(p_index->key,"--list-fingerprint")) { command = CMD_LIST_FINGERPRINT; } else if (!strcmp(p_index->key, "--hash-password")) { @@ -4925,7 +5306,8 @@ options_init_from_torrc(int argc, char **argv) get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF; } else { log_err(LD_CONFIG, "--no-passphrase specified without --keygen!"); - exit(1); + retval = -1; + goto err; } } @@ -4934,7 +5316,8 @@ options_init_from_torrc(int argc, char **argv) get_options_mutable()->change_key_passphrase = 1; } else { log_err(LD_CONFIG, "--newpass specified without --keygen!"); - exit(1); + retval = -1; + goto err; } } @@ -4944,17 +5327,20 @@ options_init_from_torrc(int argc, char **argv) if (fd_line) { if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!"); - exit(1); + retval = -1; + goto err; } else if (command != CMD_KEYGEN) { log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!"); - exit(1); + retval = -1; + goto err; } else { const char *v = fd_line->value; int ok = 1; long fd = tor_parse_long(v, 10, 0, INT_MAX, &ok, NULL); if (fd < 0 || ok == 0) { log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(v)); - exit(1); + retval = -1; + goto err; } get_options_mutable()->keygen_passphrase_fd = (int)fd; get_options_mutable()->use_keygen_passphrase_fd = 1; @@ -4969,7 +5355,8 @@ options_init_from_torrc(int argc, char **argv) if (key_line) { if (command != CMD_KEYGEN) { log_err(LD_CONFIG, "--master-key without --keygen!"); - exit(1); + retval = -1; + goto err; } else { get_options_mutable()->master_key_fname = tor_strdup(key_line->value); } @@ -5005,6 +5392,7 @@ options_init_from_string(const char *cf_defaults, const char *cf, config_line_t *cl; int retval; setopt_err_t err = SETOPT_ERR_MISC; + int cf_has_include = 0; tor_assert(msg); oldoptions = global_options; /* get_options unfortunately asserts if @@ -5016,12 +5404,16 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->command = command; newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; + smartlist_t *opened_files = smartlist_new(); for (int i = 0; i < 2; ++i) { const char *body = i==0 ? cf_defaults : cf; if (!body) continue; + /* get config lines, assign them */ - retval = config_get_lines(body, &cl, 1); + retval = config_get_lines_include(body, &cl, 1, + body == cf ? &cf_has_include : NULL, + opened_files); if (retval < 0) { err = SETOPT_ERR_PARSE; goto err; @@ -5049,6 +5441,9 @@ options_init_from_string(const char *cf_defaults, const char *cf, goto err; } + newoptions->IncludeUsed = cf_has_include; + newoptions->FilesOpenedByIncludes = opened_files; + /* If this is a testing network configuration, change defaults * for a list of dependent config options, re-initialize newoptions * with the new defaults, and assign all options to it second time. */ @@ -5087,12 +5482,16 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; /* Assign all options a second time. */ + opened_files = smartlist_new(); for (int i = 0; i < 2; ++i) { const char *body = i==0 ? cf_defaults : cf; if (!body) continue; + /* get config lines, assign them */ - retval = config_get_lines(body, &cl, 1); + retval = config_get_lines_include(body, &cl, 1, + body == cf ? &cf_has_include : NULL, + opened_files); if (retval < 0) { err = SETOPT_ERR_PARSE; goto err; @@ -5115,6 +5514,10 @@ options_init_from_string(const char *cf_defaults, const char *cf, } } + newoptions->IncludeUsed = cf_has_include; + in_option_validation = 1; + newoptions->FilesOpenedByIncludes = opened_files; + /* Validate newoptions */ if (options_validate(oldoptions, newoptions, newdefaultoptions, 0, msg) < 0) { @@ -5126,17 +5529,26 @@ options_init_from_string(const char *cf_defaults, const char *cf, err = SETOPT_ERR_TRANSITION; goto err; } + in_option_validation = 0; if (set_options(newoptions, msg)) { err = SETOPT_ERR_SETTING; goto err; /* frees and replaces old options */ } + or_options_free(global_default_options); global_default_options = newdefaultoptions; return SETOPT_OK; err: + in_option_validation = 0; + if (opened_files) { + SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f)); + smartlist_free(opened_files); + } + // may have been set to opened_files, avoid double free + newoptions->FilesOpenedByIncludes = NULL; or_options_free(newoptions); or_options_free(newdefaultoptions); if (*msg) { @@ -5218,35 +5630,35 @@ addressmap_register_auto(const char *from, const char *to, int from_wildcard = 0, to_wildcard = 0; *msg = "whoops, forgot the error message"; - if (1) { - if (!strcmp(to, "*") || !strcmp(from, "*")) { - *msg = "can't remap from or to *"; - return -1; - } - /* Detect asterisks in expressions of type: '*.example.com' */ - if (!strncmp(from,"*.",2)) { - from += 2; - from_wildcard = 1; - } - if (!strncmp(to,"*.",2)) { - to += 2; - to_wildcard = 1; - } - if (to_wildcard && !from_wildcard) { - *msg = "can only use wildcard (i.e. '*.') if 'from' address " - "uses wildcard also"; - return -1; - } + if (!strcmp(to, "*") || !strcmp(from, "*")) { + *msg = "can't remap from or to *"; + return -1; + } + /* Detect asterisks in expressions of type: '*.example.com' */ + if (!strncmp(from,"*.",2)) { + from += 2; + from_wildcard = 1; + } + if (!strncmp(to,"*.",2)) { + to += 2; + to_wildcard = 1; + } - if (address_is_invalid_destination(to, 1)) { - *msg = "destination is invalid"; - return -1; - } + if (to_wildcard && !from_wildcard) { + *msg = "can only use wildcard (i.e. '*.') if 'from' address " + "uses wildcard also"; + return -1; + } - addressmap_register(from, tor_strdup(to), expires, addrmap_source, - from_wildcard, to_wildcard); + if (address_is_invalid_destination(to, 1)) { + *msg = "destination is invalid"; + return -1; } + + addressmap_register(from, tor_strdup(to), expires, addrmap_source, + from_wildcard, to_wildcard); + return 0; } @@ -5313,7 +5725,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); if (smartlist_len(elts) == 0) - smartlist_add(elts, tor_strdup("stdout")); + smartlist_add_strdup(elts, "stdout"); if (smartlist_len(elts) == 1 && (!strcasecmp(smartlist_get(elts,0), "stdout") || @@ -5332,16 +5744,29 @@ options_init_logs(const or_options_t *old_options, or_options_t *options, } goto cleanup; } - if (smartlist_len(elts) == 1 && - !strcasecmp(smartlist_get(elts,0), "syslog")) { + if (smartlist_len(elts) == 1) { + if (!strcasecmp(smartlist_get(elts,0), "syslog")) { #ifdef HAVE_SYSLOG_H - if (!validate_only) { - add_syslog_log(severity, options->SyslogIdentityTag); + if (!validate_only) { + add_syslog_log(severity, options->SyslogIdentityTag); + } +#else + log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); +#endif /* defined(HAVE_SYSLOG_H) */ + goto cleanup; } + + if (!strcasecmp(smartlist_get(elts, 0), "android")) { +#ifdef HAVE_ANDROID_LOG_H + if (!validate_only) { + add_android_log(severity, options->AndroidIdentityTag); + } #else - log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); -#endif - goto cleanup; + log_warn(LD_CONFIG, "Android logging is not supported" + " on this system. Sorry."); +#endif // HAVE_ANDROID_LOG_H. + goto cleanup; + } } if (smartlist_len(elts) == 2 && @@ -5427,7 +5852,7 @@ validate_transport_socks_arguments(const smartlist_t *args) /** Deallocate a bridge_line_t structure. */ /* private */ void -bridge_line_free(bridge_line_t *bridge_line) +bridge_line_free_(bridge_line_t *bridge_line) { if (!bridge_line) return; @@ -5657,6 +6082,15 @@ parse_transport_line(const or_options_t *options, goto err; } + if (is_managed && options->NoExec) { + log_warn(LD_CONFIG, + "Managed proxies are not compatible with NoExec mode; ignoring." + "(%sTransportPlugin line was %s)", + server ? "Server" : "Client", escaped(line)); + r = 0; + goto done; + } + if (is_managed) { /* managed */ @@ -5848,7 +6282,7 @@ get_options_from_transport_options_line(const char *line,const char *transport) } /* add it to the options smartlist */ - smartlist_add(options, tor_strdup(option)); + smartlist_add_strdup(options, option); log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option)); } SMARTLIST_FOREACH_END(option); @@ -5927,6 +6361,8 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type, dirinfo_type_t type = 0; double weight = 1.0; + memset(v3_digest, 0, sizeof(v3_digest)); + items = smartlist_new(); smartlist_split_string(items, line, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); @@ -6173,16 +6609,16 @@ port_cfg_new(size_t namelen) tor_assert(namelen <= SIZE_T_CEILING - sizeof(port_cfg_t) - 1); port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t) + namelen + 1); cfg->entry_cfg.ipv4_traffic = 1; + cfg->entry_cfg.ipv6_traffic = 1; cfg->entry_cfg.dns_request = 1; cfg->entry_cfg.onion_traffic = 1; - cfg->entry_cfg.cache_ipv4_answers = 1; cfg->entry_cfg.prefer_ipv6_virtaddr = 1; return cfg; } /** Free all storage held in <b>port</b> */ STATIC void -port_cfg_free(port_cfg_t *port) +port_cfg_free_(port_cfg_t *port) { tor_free(port); } @@ -6190,8 +6626,9 @@ port_cfg_free(port_cfg_t *port) /** Warn for every port in <b>ports</b> of type <b>listener_type</b> that is * on a publicly routable address. */ static void -warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname, - int listener_type) +warn_nonlocal_client_ports(const smartlist_t *ports, + const char *portname, + const int listener_type) { SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { if (port->type != listener_type) @@ -6348,16 +6785,60 @@ warn_client_dns_cache(const char *option, int disabling) } /** + * 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. + } +} + +/** * Parse port configuration for a single port type. * - * Read entries of the "FooPort" type from the list <b>ports</b>, and - * entries of the "FooListenAddress" type from the list - * <b>listenaddrs</b>. Two syntaxes are supported: a legacy syntax - * where FooPort is at most a single entry containing a port number and - * where FooListenAddress has any number of address:port combinations; - * and a new syntax where there are no FooListenAddress entries and - * where FooPort can have any number of entries of the format - * "[Address:][Port] IsolationOptions". + * Read entries of the "FooPort" type from the list <b>ports</b>. Syntax is + * that FooPort can have any number of entries of the format + * "[Address:][Port] IsolationOptions". * * In log messages, describe the port type as <b>portname</b>. * @@ -6371,9 +6852,6 @@ warn_client_dns_cache(const char *option, int disabling) * ports are not on a local address. If CL_PORT_FORBID_NONLOCAL is set, * this is a control port with no password set: don't even allow it. * - * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn - * if FooListenAddress is set but FooPort is 0. - * * If CL_PORT_SERVER_OPTIONS is set in <b>flags</b>, do not allow stream * isolation options in the FooPort entries; instead allow the * server-port option set. @@ -6388,7 +6866,6 @@ warn_client_dns_cache(const char *option, int disabling) STATIC int parse_port_config(smartlist_t *out, const config_line_t *ports, - const config_line_t *listenaddrs, const char *portname, int listener_type, const char *defaultaddr, @@ -6405,90 +6882,12 @@ parse_port_config(smartlist_t *out, const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL; const unsigned default_to_group_writable = flags & CL_PORT_DFLT_GROUP_WRITABLE; - const unsigned allow_spurious_listenaddr = - flags & CL_PORT_ALLOW_EXTRA_LISTENADDR; const unsigned takes_hostnames = flags & CL_PORT_TAKES_HOSTNAMES; const unsigned is_unix_socket = flags & CL_PORT_IS_UNIXSOCKET; int got_zero_port=0, got_nonzero_port=0; char *unix_socket_path = NULL; - /* FooListenAddress is deprecated; let's make it work like it used to work, - * though. */ - if (listenaddrs) { - int mainport = defaultport; - - if (ports && ports->next) { - log_warn(LD_CONFIG, "%sListenAddress can't be used when there are " - "multiple %sPort lines", portname, portname); - return -1; - } else if (ports) { - if (!strcmp(ports->value, "auto")) { - mainport = CFG_AUTO_PORT; - } else { - int ok; - mainport = (int)tor_parse_long(ports->value, 10, 0, 65535, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, "%sListenAddress can only be used with a single " - "%sPort with value \"auto\" or 1-65535 and no options set.", - portname, portname); - return -1; - } - } - } - - if (mainport == 0) { - if (allow_spurious_listenaddr) - return 1; /*DOCDOC*/ - log_warn(LD_CONFIG, "%sPort must be defined if %sListenAddress is used", - portname, portname); - return -1; - } - - if (use_server_options && out) { - /* Add a no_listen port. */ - port_cfg_t *cfg = port_cfg_new(0); - cfg->type = listener_type; - cfg->port = mainport; - tor_addr_make_unspec(&cfg->addr); /* Server ports default to 0.0.0.0 */ - cfg->server_cfg.no_listen = 1; - cfg->server_cfg.bind_ipv4_only = 1; - /* cfg->entry_cfg defaults are already set by port_cfg_new */ - smartlist_add(out, cfg); - } - - for (; listenaddrs; listenaddrs = listenaddrs->next) { - tor_addr_t addr; - uint16_t port = 0; - if (tor_addr_port_lookup(listenaddrs->value, &addr, &port) < 0) { - log_warn(LD_CONFIG, "Unable to parse %sListenAddress '%s'", - portname, listenaddrs->value); - return -1; - } - if (out) { - port_cfg_t *cfg = port_cfg_new(0); - cfg->type = listener_type; - cfg->port = port ? port : mainport; - tor_addr_copy(&cfg->addr, &addr); - cfg->entry_cfg.session_group = SESSION_GROUP_UNSET; - cfg->entry_cfg.isolation_flags = ISO_DEFAULT; - cfg->server_cfg.no_advertise = 1; - smartlist_add(out, cfg); - } - } - - 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); - } - return 0; - } /* end if (listenaddrs) */ - - /* No ListenAddress lines. If there's no FooPort, then maybe make a default - * one. */ + /* If there's no FooPort, then maybe make a default one. */ if (! ports) { if (defaultport && defaultaddr && out) { port_cfg_t *cfg = port_cfg_new(is_unix_socket ? strlen(defaultaddr) : 0); @@ -6526,9 +6925,9 @@ parse_port_config(smartlist_t *out, /* This must be kept in sync with port_cfg_new's defaults */ int no_listen = 0, no_advertise = 0, all_addrs = 0, bind_ipv4_only = 0, bind_ipv6_only = 0, - ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0, dns_request = 1, + ipv4_traffic = 1, ipv6_traffic = 1, prefer_ipv6 = 0, dns_request = 1, onion_traffic = 1, - cache_ipv4 = 1, use_cached_ipv4 = 0, + cache_ipv4 = 0, use_cached_ipv4 = 0, cache_ipv6 = 0, use_cached_ipv6 = 0, prefer_ipv6_automap = 1, world_writable = 0, group_writable = 0, relax_dirmode_check = 0, @@ -6627,7 +7026,7 @@ parse_port_config(smartlist_t *out, } else if (!strcasecmp(elt, "AllAddrs")) { all_addrs = 1; -#endif +#endif /* 0 */ } else if (!strcasecmp(elt, "IPv4Only")) { bind_ipv4_only = 1; } else if (!strcasecmp(elt, "IPv6Only")) { @@ -6889,6 +7288,7 @@ parse_port_config(smartlist_t *out, SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_clear(elts); tor_free(addrport); + tor_free(unix_socket_path); } if (warn_nonlocal && out) { @@ -6956,39 +7356,48 @@ parse_ports(or_options_t *options, int validate_only, *n_ports_out = 0; - const unsigned gw_flag = options->SocksSocketsGroupWritable ? + const unsigned gw_flag = options->UnixSocksGroupWritable ? CL_PORT_DFLT_GROUP_WRITABLE : 0; if (parse_port_config(ports, - options->SocksPort_lines, options->SocksListenAddress, + options->SocksPort_lines, "Socks", CONN_TYPE_AP_LISTENER, "127.0.0.1", 9050, - CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR| - CL_PORT_TAKES_HOSTNAMES|gw_flag) < 0) { - *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration"); + ((validate_only ? 0 : CL_PORT_WARN_NONLOCAL) + | CL_PORT_TAKES_HOSTNAMES | gw_flag)) < 0) { + *msg = tor_strdup("Invalid SocksPort configuration"); goto err; } if (parse_port_config(ports, - options->DNSPort_lines, options->DNSListenAddress, + options->DNSPort_lines, "DNS", CONN_TYPE_AP_DNS_LISTENER, "127.0.0.1", 0, CL_PORT_WARN_NONLOCAL|CL_PORT_TAKES_HOSTNAMES) < 0) { - *msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration"); + *msg = tor_strdup("Invalid DNSPort configuration"); goto err; } if (parse_port_config(ports, - options->TransPort_lines, options->TransListenAddress, + options->TransPort_lines, "Trans", CONN_TYPE_AP_TRANS_LISTENER, "127.0.0.1", 0, CL_PORT_WARN_NONLOCAL) < 0) { - *msg = tor_strdup("Invalid TransPort/TransListenAddress configuration"); + *msg = tor_strdup("Invalid TransPort configuration"); goto err; } if (parse_port_config(ports, - options->NATDPort_lines, options->NATDListenAddress, + options->NATDPort_lines, "NATD", CONN_TYPE_AP_NATD_LISTENER, "127.0.0.1", 0, CL_PORT_WARN_NONLOCAL) < 0) { - *msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration"); + *msg = tor_strdup("Invalid NatdPort configuration"); + goto err; + } + if (parse_port_config(ports, + options->HTTPTunnelPort_lines, + "HTTP Tunnel", CONN_TYPE_AP_HTTP_CONNECT_LISTENER, + "127.0.0.1", 0, + ((validate_only ? 0 : CL_PORT_WARN_NONLOCAL) + | CL_PORT_TAKES_HOSTNAMES | gw_flag)) < 0) { + *msg = tor_strdup("Invalid HTTPTunnelPort configuration"); goto err; } { @@ -7004,16 +7413,14 @@ parse_ports(or_options_t *options, int validate_only, if (parse_port_config(ports, options->ControlPort_lines, - options->ControlListenAddress, "Control", CONN_TYPE_CONTROL_LISTENER, "127.0.0.1", 0, control_port_flags) < 0) { - *msg = tor_strdup("Invalid ControlPort/ControlListenAddress " - "configuration"); + *msg = tor_strdup("Invalid ControlPort configuration"); goto err; } - if (parse_port_config(ports, options->ControlSocket, NULL, + if (parse_port_config(ports, options->ControlSocket, "ControlSocket", CONN_TYPE_CONTROL_LISTENER, NULL, 0, control_port_flags | CL_PORT_IS_UNIXSOCKET) < 0) { @@ -7023,15 +7430,15 @@ parse_ports(or_options_t *options, int validate_only, } if (! options->ClientOnly) { if (parse_port_config(ports, - options->ORPort_lines, options->ORListenAddress, + options->ORPort_lines, "OR", CONN_TYPE_OR_LISTENER, "0.0.0.0", 0, CL_PORT_SERVER_OPTIONS) < 0) { - *msg = tor_strdup("Invalid ORPort/ORListenAddress configuration"); + *msg = tor_strdup("Invalid ORPort configuration"); goto err; } if (parse_port_config(ports, - options->ExtORPort_lines, NULL, + options->ExtORPort_lines, "ExtOR", CONN_TYPE_EXT_OR_LISTENER, "127.0.0.1", 0, CL_PORT_SERVER_OPTIONS|CL_PORT_WARN_NONLOCAL) < 0) { @@ -7039,11 +7446,11 @@ parse_ports(or_options_t *options, int validate_only, goto err; } if (parse_port_config(ports, - options->DirPort_lines, options->DirListenAddress, + options->DirPort_lines, "Dir", CONN_TYPE_DIR_LISTENER, "0.0.0.0", 0, CL_PORT_SERVER_OPTIONS) < 0) { - *msg = tor_strdup("Invalid DirPort/DirListenAddress configuration"); + *msg = tor_strdup("Invalid DirPort configuration"); goto err; } } @@ -7070,6 +7477,8 @@ parse_ports(or_options_t *options, int validate_only, !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER, 1); options->NATDPort_set = !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER, 1); + options->HTTPTunnelPort_set = + !! count_real_listeners(ports, CONN_TYPE_AP_HTTP_CONNECT_LISTENER, 1); /* Use options->ControlSocket to test if a control socket is set */ options->ControlPort_set = !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER, 0); @@ -7387,60 +7796,81 @@ port_exists_by_type_addr32h_port(int listener_type, uint32_t addr_ipv4h, check_wildcard); } -/** Adjust the value of options->DataDirectory, or fill it in if it's - * absent. Return 0 on success, -1 on failure. */ -static int -normalize_data_directory(or_options_t *options) +/** Allocate and return a good value for the DataDirectory based on + * <b>val</b>, which may be NULL. Return NULL on failure. */ +static char * +get_data_directory(const char *val) { #ifdef _WIN32 - char *p; - if (options->DataDirectory) - return 0; /* all set */ - p = tor_malloc(MAX_PATH); - strlcpy(p,get_windows_conf_root(),MAX_PATH); - options->DataDirectory = p; - return 0; -#else - const char *d = options->DataDirectory; + if (val) { + return tor_strdup(val); + } else { + return tor_strdup(get_windows_conf_root()); + } +#else /* !(defined(_WIN32)) */ + const char *d = val; if (!d) d = "~/.tor"; - if (strncmp(d,"~/",2) == 0) { - char *fn = expand_filename(d); - if (!fn) { - log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d); - return -1; - } - if (!options->DataDirectory && !strcmp(fn,"/.tor")) { - /* If our homedir is /, we probably don't want to use it. */ - /* Default to LOCALSTATEDIR/tor which is probably closer to what we - * want. */ - log_warn(LD_CONFIG, - "Default DataDirectory is \"~/.tor\". This expands to " - "\"%s\", which is probably not what you want. Using " - "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR); - tor_free(fn); - fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor"); - } - tor_free(options->DataDirectory); - options->DataDirectory = fn; - } - return 0; -#endif + if (!strcmpstart(d, "~/")) { + char *fn = expand_filename(d); + if (!fn) { + log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d); + return NULL; + } + if (!val && !strcmp(fn,"/.tor")) { + /* If our homedir is /, we probably don't want to use it. */ + /* Default to LOCALSTATEDIR/tor which is probably closer to what we + * want. */ + log_warn(LD_CONFIG, + "Default DataDirectory is \"~/.tor\". This expands to " + "\"%s\", which is probably not what you want. Using " + "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR); + tor_free(fn); + fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor"); + } + return fn; + } + return tor_strdup(d); +#endif /* defined(_WIN32) */ } -/** Check and normalize the value of options->DataDirectory; return 0 if it - * is sane, -1 otherwise. */ +/** Check and normalize the values of options->{Key,Data,Cache}Directory; + * return 0 if it is sane, -1 otherwise. */ static int -validate_data_directory(or_options_t *options) +validate_data_directories(or_options_t *options) { - if (normalize_data_directory(options) < 0) + tor_free(options->DataDirectory); + options->DataDirectory = get_data_directory(options->DataDirectory_option); + if (!options->DataDirectory) return -1; - tor_assert(options->DataDirectory); if (strlen(options->DataDirectory) > (512-128)) { log_warn(LD_CONFIG, "DataDirectory is too long."); return -1; } + + tor_free(options->KeyDirectory); + if (options->KeyDirectory_option) { + options->KeyDirectory = get_data_directory(options->KeyDirectory_option); + if (!options->KeyDirectory) + return -1; + } else { + /* Default to the data directory's keys subdir */ + tor_asprintf(&options->KeyDirectory, "%s"PATH_SEPARATOR"keys", + options->DataDirectory); + } + + tor_free(options->CacheDirectory); + if (options->CacheDirectory_option) { + options->CacheDirectory = get_data_directory( + options->CacheDirectory_option); + if (!options->CacheDirectory) + return -1; + } else { + /* Default to the data directory. */ + options->CacheDirectory = tor_strdup(options->DataDirectory); + } + return 0; } @@ -7581,53 +8011,56 @@ init_libevent(const or_options_t *options) suppress_libevent_log_msg(NULL); } -/** Return a newly allocated string holding a filename relative to the data - * directory. If <b>sub1</b> is present, it is the first path component after +/** Return a newly allocated string holding a filename relative to the + * directory in <b>options</b> specified by <b>roottype</b>. + * If <b>sub1</b> is present, it is the first path component after * the data directory. If <b>sub2</b> is also present, it is the second path * component after the data directory. If <b>suffix</b> is present, it * is appended to the filename. * - * Examples: - * get_datadir_fname2_suffix("a", NULL, NULL) -> $DATADIR/a - * get_datadir_fname2_suffix("a", NULL, ".tmp") -> $DATADIR/a.tmp - * get_datadir_fname2_suffix("a", "b", ".tmp") -> $DATADIR/a/b/.tmp - * get_datadir_fname2_suffix("a", "b", NULL) -> $DATADIR/a/b - * - * Note: Consider using the get_datadir_fname* macros in or.h. + * Note: Consider using macros in config.h that wrap this function; + * you should probably never need to call it as-is. */ MOCK_IMPL(char *, -options_get_datadir_fname2_suffix,(const or_options_t *options, - const char *sub1, const char *sub2, - const char *suffix)) +options_get_dir_fname2_suffix,(const or_options_t *options, + directory_root_t roottype, + const char *sub1, const char *sub2, + const char *suffix)) { - char *fname = NULL; - size_t len; tor_assert(options); - tor_assert(options->DataDirectory); - tor_assert(sub1 || !sub2); /* If sub2 is present, sub1 must be present. */ - len = strlen(options->DataDirectory); - if (sub1) { - len += strlen(sub1)+1; - if (sub2) - len += strlen(sub2)+1; - } - if (suffix) - len += strlen(suffix); - len++; - fname = tor_malloc(len); - if (sub1) { - if (sub2) { - tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s", - options->DataDirectory, sub1, sub2); - } else { - tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s", - options->DataDirectory, sub1); - } + + const char *rootdir = NULL; + switch (roottype) { + case DIRROOT_DATADIR: + rootdir = options->DataDirectory; + break; + case DIRROOT_CACHEDIR: + rootdir = options->CacheDirectory; + break; + case DIRROOT_KEYDIR: + rootdir = options->KeyDirectory; + break; + default: + tor_assert_unreached(); + break; + } + tor_assert(rootdir); + + if (!suffix) + suffix = ""; + + char *fname = NULL; + + if (sub1 == NULL) { + tor_asprintf(&fname, "%s%s", rootdir, suffix); + tor_assert(!sub2); /* If sub2 is present, sub1 must be present. */ + } else if (sub2 == NULL) { + tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s%s", rootdir, sub1, suffix); } else { - strlcpy(fname, options->DataDirectory, len); + tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s%s", + rootdir, sub1, sub2, suffix); } - if (suffix) - strlcat(fname, suffix, len); + return fname; } @@ -7668,28 +8101,6 @@ write_to_data_subdir(const char* subdir, const char* fname, 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 -remove_file_if_very_old(const char *fname, time_t now) -{ -#define VERY_OLD_FILE_AGE (28*24*60*60) - struct stat st; - - log_debug(LD_FS, "stat()ing %s", fname); - if (stat(sandbox_intern_string(fname), &st)==0 && - st.st_mtime < now-VERY_OLD_FILE_AGE) { - char buf[ISO_TIME_LEN+1]; - format_local_iso_time(buf, st.st_mtime); - log_notice(LD_GENERAL, "Obsolete file %s hasn't been modified since %s. " - "Removing it.", fname, buf); - if (unlink(fname) != 0) { - log_warn(LD_FS, "Failed to unlink %s: %s", - fname, strerror(errno)); - } - } -} - /** Return a smartlist of ports that must be forwarded by * tor-fw-helper. The smartlist contains the ports in a string format * that is understandable by tor-fw-helper. */ @@ -7762,9 +8173,12 @@ 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; + /* This type accepts more inputs than TimeInterval, but it ignores + * everything after the first entry, so we may as well pretend + * it's a TimeInterval. */ + case CONFIG_TYPE_CSV_INTERVAL: type = "TimeInterval"; break; case CONFIG_TYPE_LINELIST: type = "LineList"; break; - case CONFIG_TYPE_LINELIST_S: type = "Dependant"; break; + case CONFIG_TYPE_LINELIST_S: type = "Dependent"; break; case CONFIG_TYPE_LINELIST_V: type = "Virtual"; break; default: case CONFIG_TYPE_OBSOLETE: @@ -7846,60 +8260,98 @@ getinfo_helper_config(control_connection_t *conn, return 0; } -/** Parse outbound bind address option lines. If <b>validate_only</b> - * is not 0 update OutboundBindAddressIPv4_ and - * OutboundBindAddressIPv6_ in <b>options</b>. On failure, set - * <b>msg</b> (if provided) to a newly allocated string containing a - * description of the problem and return -1. */ +/* Check whether an address has already been set against the options + * depending on address family and destination type. Any exsting + * value will lead to a fail, even if it is the same value. If not + * set and not only validating, copy it into this location too. + * Returns 0 on success or -1 if this address is already set. + */ static int -parse_outbound_addresses(or_options_t *options, int validate_only, char **msg) +verify_and_store_outbound_address(sa_family_t family, tor_addr_t *addr, + outbound_addr_t type, or_options_t *options, int validate_only) { - const config_line_t *lines = options->OutboundBindAddress; - int found_v4 = 0, found_v6 = 0; - + if (type>=OUTBOUND_ADDR_MAX || (family!=AF_INET && family!=AF_INET6)) { + return -1; + } + int fam_index=0; + if (family==AF_INET6) { + fam_index=1; + } + tor_addr_t *dest=&options->OutboundBindAddresses[type][fam_index]; + if (!tor_addr_is_null(dest)) { + return -1; + } if (!validate_only) { - memset(&options->OutboundBindAddressIPv4_, 0, - sizeof(options->OutboundBindAddressIPv4_)); - memset(&options->OutboundBindAddressIPv6_, 0, - sizeof(options->OutboundBindAddressIPv6_)); + tor_addr_copy(dest, addr); } + return 0; +} + +/* Parse a list of address lines for a specific destination type. + * Will store them into the options if not validate_only. If a + * problem occurs, a suitable error message is store in msg. + * Returns 0 on success or -1 if any address is already set. + */ +static int +parse_outbound_address_lines(const config_line_t *lines, outbound_addr_t type, + or_options_t *options, int validate_only, char **msg) +{ + tor_addr_t addr; + sa_family_t family; while (lines) { - tor_addr_t addr, *dst_addr = NULL; - int af = tor_addr_parse(&addr, lines->value); - switch (af) { - case AF_INET: - if (found_v4) { - if (msg) - tor_asprintf(msg, "Multiple IPv4 outbound bind addresses " - "configured: %s", lines->value); - return -1; - } - found_v4 = 1; - dst_addr = &options->OutboundBindAddressIPv4_; - break; - case AF_INET6: - if (found_v6) { - if (msg) - tor_asprintf(msg, "Multiple IPv6 outbound bind addresses " - "configured: %s", lines->value); - return -1; - } - found_v6 = 1; - dst_addr = &options->OutboundBindAddressIPv6_; - break; - default: + family = tor_addr_parse(&addr, lines->value); + if (verify_and_store_outbound_address(family, &addr, type, + options, validate_only)) { if (msg) - tor_asprintf(msg, "Outbound bind address '%s' didn't parse.", - lines->value); + tor_asprintf(msg, "Multiple%s%s outbound bind addresses " + "configured: %s", + family==AF_INET?" IPv4":(family==AF_INET6?" IPv6":""), + type==OUTBOUND_ADDR_OR?" OR": + (type==OUTBOUND_ADDR_EXIT?" exit":""), lines->value); return -1; } - if (!validate_only) - tor_addr_copy(dst_addr, &addr); lines = lines->next; } return 0; } +/** Parse outbound bind address option lines. If <b>validate_only</b> + * is not 0 update OutboundBindAddresses in <b>options</b>. + * Only one address can be set for any of these values. + * On failure, set <b>msg</b> (if provided) to a newly allocated string + * containing a description of the problem and return -1. + */ +static int +parse_outbound_addresses(or_options_t *options, int validate_only, char **msg) +{ + if (!validate_only) { + memset(&options->OutboundBindAddresses, 0, + sizeof(options->OutboundBindAddresses)); + } + + if (parse_outbound_address_lines(options->OutboundBindAddress, + OUTBOUND_ADDR_EXIT_AND_OR, options, + validate_only, msg) < 0) { + goto err; + } + + if (parse_outbound_address_lines(options->OutboundBindAddressOR, + OUTBOUND_ADDR_OR, options, validate_only, + msg) < 0) { + goto err; + } + + if (parse_outbound_address_lines(options->OutboundBindAddressExit, + OUTBOUND_ADDR_EXIT, options, validate_only, + msg) < 0) { + goto err; + } + + return 0; + err: + return -1; +} + /** Load one of the geoip files, <a>family</a> determining which * one. <a>default_fname</a> is used if on Windows and * <a>fname</a> equals "<default>". */ @@ -7919,10 +8371,10 @@ config_load_geoip_file_(sa_family_t family, } geoip_load_file(family, fname); tor_free(free_fname); -#else +#else /* !(defined(_WIN32)) */ (void)default_fname; geoip_load_file(family, fname); -#endif +#endif /* defined(_WIN32) */ } /** Load geoip files for IPv4 and IPv6 if <a>options</a> and @@ -7997,9 +8449,9 @@ init_cookie_authentication(const char *fname, const char *header, log_warn(LD_FS,"Unable to make %s group-readable.", escaped(fname)); } } -#else +#else /* !(!defined(_WIN32)) */ (void) group_readable; -#endif +#endif /* !defined(_WIN32) */ /* Success! */ log_info(LD_GENERAL, "Generated auth cookie file in '%s'.", escaped(fname)); @@ -8011,3 +8463,17 @@ init_cookie_authentication(const char *fname, const char *header, tor_free(cookie_file_str); return retval; } + +/** + * Return true if any option is set in <b>options</b> to make us behave + * as a client. + */ +int +options_any_client_port_set(const or_options_t *options) +{ + return (options->SocksPort_set || + options->TransPort_set || + options->NATDPort_set || + options->DNSPort_set || + options->HTTPTunnelPort_set); +} |