diff options
344 files changed, 8362 insertions, 3385 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index 7e08602fe3..fefc7d05e7 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,7 +18,7 @@ environment: - target: i686-w64-mingw32 compiler_path: mingw32 mingw_prefix: mingw-w64-i686 - hardening: + hardening: --enable-all-bugs-are-fatal - target: x86_64-w64-mingw32 compiler_path: mingw64 mingw_prefix: mingw-w64-x86_64 @@ -36,7 +36,7 @@ matrix: target: i686-w64-mingw32 compiler_path: mingw32 mingw_prefix: mingw-w64-i686 - hardening: + hardening: --enable-all-bugs-are-fatal - image: Visual Studio 2015 target: x86_64-w64-mingw32 compiler_path: mingw64 @@ -61,6 +61,9 @@ install: # unprefixed packages are from MSYS2, which is like Cygwin. Avoid them. # # Use pacman --debug to show package downloads and install locations + # + # All installed library dlls must be copied to the test and app + # directories, before running tor's tests. (See below.) #> Execute-Command "C:\msys64\usr\bin\pacman" -Sy --verbose --needed --noconfirm ${env:mingw_prefix}-libevent ${env:mingw_prefix}-openssl ${env:mingw_prefix}-pkg-config ${env:mingw_prefix}-xz ${env:mingw_prefix}-zstd ; @@ -95,9 +98,18 @@ test_script: $buildpath = @("C:\msys64\${env:compiler_path}\bin") + $oldpath $env:Path = $buildpath -join ';' Set-Location "${env:build}" - Copy-Item "C:/msys64/${env:compiler_path}/bin/libssp-0.dll" -Destination "${env:build}/src/test" - Copy-Item "C:/msys64/${env:compiler_path}/bin/zlib1.dll" -Destination "${env:build}/src/test" - Execute-Bash "VERBOSE=1 make -k -j2 check" + <# Some compiler dlls must be copied to the test and app + # directories, before running tor's tests. + #> + Copy-Item "C:/msys64/${env:compiler_path}/bin/libssp-0.dll","C:/msys64/${env:compiler_path}/bin/zlib1.dll" -Destination "${env:build}/src/test" + Copy-Item "C:/msys64/${env:compiler_path}/bin/libssp-0.dll","C:/msys64/${env:compiler_path}/bin/zlib1.dll" -Destination "${env:build}/src/app" + <# All installed library dlls must be copied to the test and app + # directories, before running tor's tests. + # (See install command above.) + #> + Copy-Item "C:/${env:compiler_path}/bin/libcrypto*.dll","C:/${env:compiler_path}/bin/libssl*.dll","C:/${env:compiler_path}/bin/liblzma*.dll","C:/${env:compiler_path}/bin/libevent*.dll","C:/${env:compiler_path}/bin/libzstd*.dll" -Destination "${env:build}/src/test" + Copy-Item "C:/${env:compiler_path}/bin/libcrypto*.dll","C:/${env:compiler_path}/bin/libssl*.dll","C:/${env:compiler_path}/bin/liblzma*.dll","C:/${env:compiler_path}/bin/libevent*.dll","C:/${env:compiler_path}/bin/libzstd*.dll" -Destination "${env:build}/src/app" + Execute-Bash "VERBOSE=1 TOR_SKIP_TESTCASES=crypto/openssl_version make -k -j2 check" } on_finish: diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..7be73e0612 --- /dev/null +++ b/.clang-format @@ -0,0 +1,163 @@ +# DO NOT COMMIT OR MERGE CODE THAT IS RUN THROUGH THIS TOOL YET. +# +# WE ARE STILL DISCUSSING OUR DESIRED STYLE AND ITERATING ON IT. +# (12 Feb 2020) + +--- +Language: Cpp +# Out of all supported styles, LLVM seems closest to our own. +BasedOnStyle: LLVM + +################ +# +# Deviations from LLVM's style. +# +################ + +# We prefer an indentation width of 4 columns; LLVM likes 2. +## OVERRIDE FOR COMPARISON +IndentWidth: 2 + +## OVERRIDE FOR COMPARISON +## for now i'm not sorting includes, since that makes every file get touched. +SortIncludes: false + +# We prefer 79; llvm likes 80. +ColumnLimit: 79 + +# Where do we want to put backslashes on multiline macros? Our choices are +# "as far left as possible", "as far right as possible", and "make no changes." +# LLVM defaults to right, but we don't dig that. +AlignEscapedNewlines: Left + +# When we see a bunch of things in a row with comments after them, should we +# try to align those comments? Doing so makes some of our code pretty ugly. +AlignTrailingComments: false + +# We use a function declaration style much closer to BSD KNF than to LLVM's. +# We say: +# int foo(int x); +# int +# foo(int x) +# { +# ... +# } +# whereas llvm prefers: +# int foo(int x); +# int foo(int x) { +# ... +# } +# or even: +# int foo(int x) { ... } +# +BreakBeforeBraces: Custom +BraceWrapping: + AfterFunction: true +AllowShortFunctionsOnASingleLine: None +AlwaysBreakAfterReturnType: AllDefinitions + +# We don't like blocks to start with an empty line. +# +KeepEmptyLinesAtTheStartOfBlocks: false + +################ +# +# Tor-specific magic +# +################ + +# +# These comments are magical, and should not be changed. +# +CommentPragmas: 'LCOV_EXCL|COVERITY' + +# +# Remove duplicate empty lines. +# +MaxEmptyLinesToKeep: 1 + +# +# Indent preprocessor directives, for clarity. +# +IndentPPDirectives: AfterHash + +# +# These introduce an iteration, and work a bit like a for loop. +# +# Note that we can NOT include ones that don't work like "for". For example, +# if the body is an argument to the macro, we can't list it here. +# +ForEachMacros: + - MAP_FOREACH + - MAP_FOREACH_MODIFY + - TOR_SIMPLEQ_FOREACH + - TOR_SIMPLEQ_FOREACH_SAFE + - TOR_SLIST_FOREACH + - TOR_SLIST_FOREACH_SAFE + - TOR_LIST_FOREACH + - TOR_LIST_FOREACH_SAFE + - TOR_TAILQ_FOREACH + - TOR_TAILQ_FOREACH_SAFE + - TOR_TAILQ_FOREACH_REVERSE + - TOR_TAILQ_FOREACH_REVERSE_SAFE + - TOR_CIRCLEQ_FOREACH + - TOR_CIRCLEQ_FOREACH_SAFE + - TOR_CIRCLEQ_FOREACH_REVERSE + - TOR_CIRCLEQ_FOREACH_REVERSE_SAFE + - HT_FOREACH + - SMARTLIST_FOREACH_BEGIN + - DIGESTMAP_FOREACH + - DIGESTMAP_FOREACH_MODIFY + - DIGEST256MAP_FOREACH + - DIGEST256MAP_FOREACH_MODIFY + - SDMAP_FOREACH + - RIMAP_FOREACH + - EIMAP_FOREACH + +# +# Omitting: +# +# - SMARTLIST_FOREACH, since the body of the loop is an argument. + +# +# This explains how to sort our headers. +# +# This is more complex than it truly should be, but I've edited this till +# compilation still mostly passes. +# +# I'm disabling this, however, since it's a distraction from the other +# formatting issues. See SortIncludes above. +# +IncludeCategories: + - Regex: '^"orconfig.h' + Priority: -30 + - Regex: '^"ext/' + Priority: -18 + - Regex: '^"lib/' + Priority: -10 + - Regex: '^"core/or/or.h' + Priority: -5 + - Regex: '^"core/' + Priority: 5 + - Regex: '^"feature/' + Priority: 10 + - Regex: '^"app/' + Priority: 20 + +# +# These macros should always cause indentation, as though they were { and }. +# +# Do NOT put macros here unless you want an extra level of indentation between +# them whenever they appear. +# +MacroBlockBegin: "^STMT_BEGIN|TT_STMT_BEGIN$" +MacroBlockEnd: "^STMT_END|TT_STMT_END$" + +# +# These macros are interpreted as types. +# (Not supported in my clang-format) +# +# TypenameMacros: +# - "STACK_OF" + +... diff --git a/.gitignore b/.gitignore index 77610b3193..ea6b68ec5f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.swo # C stuff *.o +*.a *.obj # Diff droppings *.orig @@ -77,7 +78,6 @@ uptime-*.json /coverage_html/ /callgraph/ - # /contrib/ /contrib/dist/tor.sh /contrib/dist/torctl @@ -137,9 +137,6 @@ uptime-*.json /src/Makefile /src/Makefile.in -# /src/trace -/src/trace/libor-trace.a - # /src/config/ /src/config/Makefile /src/config/Makefile.in @@ -149,87 +146,10 @@ uptime-*.json /src/config/torrc.minimal # /src/ext/ -/src/ext/ed25519/ref10/libed25519_ref10.a /src/ext/ed25519/ref10/libed25519_ref10.lib -/src/ext/ed25519/donna/libed25519_donna.a /src/ext/ed25519/donna/libed25519_donna.lib -/src/ext/keccak-tiny/libkeccak-tiny.a /src/ext/keccak-tiny/libkeccak-tiny.lib -# /src/lib -/src/lib/libcurve25519_donna.a -/src/lib/libtor-buf.a -/src/lib/libtor-buf-testing.a -/src/lib/libtor-compress.a -/src/lib/libtor-compress-testing.a -/src/lib/libtor-confmgt.a -/src/lib/libtor-confmgt-testing.a -/src/lib/libtor-container.a -/src/lib/libtor-container-testing.a -/src/lib/libtor-crypt-ops.a -/src/lib/libtor-crypt-ops-testing.a -/src/lib/libtor-ctime.a -/src/lib/libtor-ctime-testing.a -/src/lib/libtor-dispatch.a -/src/lib/libtor-dispatch-testing.a -/src/lib/libtor-encoding.a -/src/lib/libtor-encoding-testing.a -/src/lib/libtor-evloop.a -/src/lib/libtor-evloop-testing.a -/src/lib/libtor-err.a -/src/lib/libtor-err-testing.a -/src/lib/libtor-fdio.a -/src/lib/libtor-fdio-testing.a -/src/lib/libtor-fs.a -/src/lib/libtor-fs-testing.a -/src/lib/libtor-geoip.a -/src/lib/libtor-geoip-testing.a -/src/lib/libtor-intmath.a -/src/lib/libtor-intmath-testing.a -/src/lib/libtor-lock.a -/src/lib/libtor-lock-testing.a -/src/lib/libtor-log.a -/src/lib/libtor-log-testing.a -/src/lib/libtor-malloc.a -/src/lib/libtor-malloc-testing.a -/src/lib/libtor-math.a -/src/lib/libtor-math-testing.a -/src/lib/libtor-memarea.a -/src/lib/libtor-memarea-testing.a -/src/lib/libtor-meminfo.a -/src/lib/libtor-meminfo-testing.a -/src/lib/libtor-net.a -/src/lib/libtor-net-testing.a -/src/lib/libtor-osinfo.a -/src/lib/libtor-osinfo-testing.a -/src/lib/libtor-process.a -/src/lib/libtor-process-testing.a -/src/lib/libtor-pubsub.a -/src/lib/libtor-pubsub-testing.a -/src/lib/libtor-sandbox.a -/src/lib/libtor-sandbox-testing.a -/src/lib/libtor-string.a -/src/lib/libtor-string-testing.a -/src/lib/libtor-smartlist-core.a -/src/lib/libtor-smartlist-core-testing.a -/src/lib/libtor-term.a -/src/lib/libtor-term-testing.a -/src/lib/libtor-thread.a -/src/lib/libtor-thread-testing.a -/src/lib/libtor-time.a -/src/lib/libtor-time-testing.a -/src/lib/libtor-tls.a -/src/lib/libtor-tls-testing.a -/src/lib/libtor-trace.a -/src/lib/libtor-version.a -/src/lib/libtor-version-testing.a -/src/lib/libtor-wallclock.a -/src/lib/libtor-wallclock-testing.a - -# /src/tor -/src/core/libtor-app.a -/src/core/libtor-app-testing.a - # /src/app /src/app/tor /src/app/tor.exe @@ -275,7 +195,6 @@ uptime-*.json /src/test/fuzz/lf-fuzz-* # /src/tools/ -/src/tools/libtorrunner.a /src/tools/tor-checkkey /src/tools/tor-resolve /src/tools/tor-cov-resolve @@ -291,10 +210,6 @@ uptime-*.json /src/tools/Makefile /src/tools/Makefile.in -# /src/trunnel/ -/src/trunnel/libor-trunnel-testing.a -/src/trunnel/libor-trunnel.a - # /src/win32/ /src/win32/Makefile /src/win32/Makefile.in diff --git a/.travis.yml b/.travis.yml index af5052682f..01343e65d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ env: - MAKEFLAGS="-k -j 2" ## We turn on hardening by default ## Also known as --enable-fragile-hardening in 0.3.0.3-alpha and later - - HARDENING_OPTIONS="--enable-expensive-hardening" + - HARDENING_OPTIONS="--enable-all-bugs-are-fatal --enable-expensive-hardening" ## We turn off asciidoc by default, because it's slow - ASCIIDOC_OPTIONS="--disable-asciidoc" ## Our default rust version is the minimum supported version @@ -34,6 +34,8 @@ env: ## Turn off tor's sandbox in chutney, until we fix sandbox errors that are ## triggered by Ubuntu Xenial and Bionic. See #32722. - CHUTNEY_TOR_SANDBOX="0" + ## The default target for chutney jobs + - CHUTNEY_MAKE="test-network-all" matrix: ## This matrix entry is required, but it doesn't actually create any jobs - @@ -41,34 +43,49 @@ env: matrix: ## include creates builds with gcc, linux, unless we override those defaults include: - ## We run basic tests on macOS + ## We run chutney on macOS, because macOS Travis has IPv6 + ## But we only run the IPv6 chutney tests, to speed up the job + - env: CHUTNEY_MAKE="test-network-ipv6" CHUTNEY="yes" CHUTNEY_ALLOW_FAILURES="2" SKIP_MAKE_CHECK="yes" + os: osx + + ## We also run basic tests on macOS - compiler: clang os: osx ## Turn off some newer features, turn on clang's -Wtypedef-redefinition - env: C_DIALECT_OPTIONS="-std=gnu99" - ## We check NSS - ## Use -std=gnu99 to turn off some newer features, and maybe turn on some - ## extra gcc warnings? - - env: NSS_OPTIONS="--enable-nss" C_DIALECT_OPTIONS="-std=gnu99" + ## Also, disable ALL_BUGS_ARE_FATAL macro. + env: C_DIALECT_OPTIONS="-std=gnu99" HARDENING_OPTIONS="--enable-expensive-hardening" + ## We run chutney on Linux, because it's faster than chutney on macOS ## Chutney is a fast job, clang is slower on Linux, so we do Chutney clang - env: CHUTNEY="yes" CHUTNEY_ALLOW_FAILURES="2" SKIP_MAKE_CHECK="yes" compiler: clang + ## We check asciidoc with distcheck, to make sure we remove doc products - ## We use Linux clang, because there are no other Linux clang jobs - env: DISTCHECK="yes" ASCIIDOC_OPTIONS="" SKIP_MAKE_CHECK="yes" - compiler: clang - ## We include a single coverage build with the best options for coverage - - env: COVERAGE_OPTIONS="--enable-coverage" HARDENING_OPTIONS="" TOR_TEST_RNG_SEED="636f766572616765" + + ## We check disable module relay + - env: MODULES_OPTIONS="--disable-module-relay" HARDENING_OPTIONS="--enable-expensive-hardening" + ## We check disable module dirauth + - env: MODULES_OPTIONS="--disable-module-dirauth" HARDENING_OPTIONS="--enable-expensive-hardening" + ## We run rust on Linux, because it's faster than rust on macOS ## We check rust offline - env: RUST_OPTIONS="--enable-rust" TOR_RUST_DEPENDENCIES=true - ## We check asciidoc with distcheck, to make sure we remove doc products - - env: DISTCHECK="yes" ASCIIDOC_OPTIONS="" SKIP_MAKE_CHECK="yes" - ## We check disable module relay - - env: MODULES_OPTIONS="--disable-module-relay" - ## We check disable module dirauth - - env: MODULES_OPTIONS="--disable-module-dirauth" + + ## We check NSS + ## Use -std=gnu99 to turn off some newer features, and maybe turn on some + ## extra gcc warnings? + - env: NSS_OPTIONS="--enable-nss" C_DIALECT_OPTIONS="-std=gnu99" HARDENING_OPTIONS="--enable-expensive-hardening" + + ## We include a single coverage build with the best options for coverage + - env: COVERAGE_OPTIONS="--enable-coverage" HARDENING_OPTIONS="" TOR_TEST_RNG_SEED="636f766572616765" + + ## We clone our stem repo and run `make test-stem` + - env: TEST_STEM="yes" SKIP_MAKE_CHECK="yes" + + ## We run `make doxygen` without `make check`. + - env: SKIP_MAKE_CHECK="yes" DOXYGEN="yes" + ## macOS builds are very slow, and we have a limited number of ## concurrent macOS jobs. We're not actively developing Rust, so it is ## the lowest priority. @@ -76,13 +93,6 @@ matrix: #- env: RUST_VERSION="nightly" RUST_OPTIONS="--enable-rust --enable-cargo-online-mode" # compiler: clang # os: osx - ## We run chutney on macOS, because macOS Travis has IPv6 - - env: CHUTNEY="yes" CHUTNEY_ALLOW_FAILURES="2" SKIP_MAKE_CHECK="yes" - os: osx - ## We clone our stem repo and run `make test-stem` - - env: TEST_STEM="yes" SKIP_MAKE_CHECK="yes" - ## We run `make doxygen` without `make check`. - - env: SKIP_MAKE_CHECK="yes" DOXYGEN="yes" ## Allow the build to report success (with non-required sub-builds ## continuing to run) if all required sub-builds have succeeded. @@ -94,11 +104,17 @@ matrix: ## macOS rust and chutney are very slow, so we let the build finish before ## they are done. We'd like to fast finish, but still eventually show ## any failures in the build status. But Travis doesn't have that ability. - - env: RUST_VERSION="nightly" RUST_OPTIONS="--enable-rust --enable-cargo-online-mode" - compiler: clang - os: osx - - env: CHUTNEY="yes" CHUTNEY_ALLOW_FAILURES="2" SKIP_MAKE_CHECK="yes" - os: osx + + ## Since this job is disabled, there's not much point having an exception + ## for it + #- env: RUST_VERSION="nightly" RUST_OPTIONS="--enable-rust --enable-cargo-online-mode" + # compiler: clang + # os: osx + + ## Since we're actively developing IPv6, we want to require the IPv6 + ## chutney tests + #- env: CHUTNEY_MAKE="test-network-ipv6" CHUTNEY="yes" CHUTNEY_ALLOW_FAILURES="2" SKIP_MAKE_CHECK="yes" + # os: osx ## (Linux only) Use a recent Linux image (Ubuntu Bionic) dist: bionic @@ -226,11 +242,11 @@ script: - ./autogen.sh - CONFIGURE_FLAGS="$ASCIIDOC_OPTIONS $COVERAGE_OPTIONS $HARDENING_OPTIONS $MODULES_OPTIONS $NSS_OPTIONS $OPENSSL_OPTIONS $RUST_OPTIONS --enable-fatal-warnings --disable-silent-rules" - echo "Configure flags are $CONFIGURE_FLAGS CC=\"$CC $C_DIALECT_OPTIONS\"" - - ./configure $CONFIGURE_FLAGS CC="$CC $C_DIALECT_OPTIONS" + - ./configure $CONFIGURE_FLAGS CC="$CC $C_DIALECT_OPTIONS"; ## We run `make check` because that's what https://jenkins.torproject.org does. - if [[ "$SKIP_MAKE_CHECK" == "" ]]; then make check; fi - if [[ "$DISTCHECK" != "" ]]; then make distcheck DISTCHECK_CONFIGURE_FLAGS="$CONFIGURE_FLAGS"; fi - - if [[ "$CHUTNEY" != "" ]]; then make test-network-all; fi + - if [[ "$CHUTNEY" != "" ]]; then make "$CHUTNEY_MAKE"; fi ## Diagnostic for bug 29437: kill stem if it hangs for 9.5 minutes ## Travis will kill the job after 10 minutes with no output - if [[ "$TEST_STEM" != "" ]]; then make src/app/tor; timelimit -p -t 540 -s USR1 -T 30 -S ABRT python3 "$STEM_SOURCE_DIR"/run_tests.py --tor src/app/tor --integ --test control.controller --test control.base_controller --test process --log TRACE --log-file stem.log; fi @@ -247,7 +263,7 @@ after_failure: ## `make distcheck` puts it somewhere different. - if [[ "$DISTCHECK" != "" ]]; then make show-distdir-testlog || echo "make failed"; fi - if [[ "$DISTCHECK" != "" ]]; then make show-distdir-core || echo "make failed"; fi - - if [[ "$CHUTNEY" != "" ]]; then ls test_network_log || echo "ls failed"; cat test_network_log/* || echo "cat failed"; fi + - if [[ "$CHUTNEY" != "" ]]; then "$CHUTNEY_PATH/tools/diagnostics.sh" || echo "diagnostics failed"; ls test_network_log || echo "ls failed"; cat test_network_log/* || echo "cat failed"; fi - if [[ "$TEST_STEM" != "" ]]; then tail -1000 "$STEM_SOURCE_DIR"/test/data/tor_log || echo "tail failed"; fi - if [[ "$TEST_STEM" != "" ]]; then grep -v "SocketClosed" stem.log | tail -1000 || echo "grep | tail failed"; fi @@ -1,3 +1,343 @@ +Changes in version 0.4.3.3-alpha - 2020-03-18 + Tor 0.4.3.3-alpha fixes several bugs in previous releases, including + TROVE-2020-002, a major denial-of-service vulnerability that affected + all released Tor instances since 0.2.1.5-alpha. Using this + vulnerability, an attacker could cause Tor instances to consume a huge + amount of CPU, disrupting their operations for several seconds or + minutes. This attack could be launched by anybody against a relay, or + by a directory cache against any client that had connected to it. The + attacker could launch this attack as much as they wanted, thereby + disrupting service or creating patterns that could aid in traffic + analysis. This issue was found by OSS-Fuzz, and is also tracked + as CVE-2020-10592. + + We do not have reason to believe that this attack is currently being + exploited in the wild, but nonetheless we advise everyone to upgrade + as soon as packages are available. + + o Major bugfixes (security, denial-of-service): + - Fix a denial-of-service bug that could be used by anyone to + consume a bunch of CPU on any Tor relay or authority, or by + directories to consume a bunch of CPU on clients or hidden + services. Because of the potential for CPU consumption to + introduce observable timing patterns, we are treating this as a + high-severity security issue. Fixes bug 33119; bugfix on + 0.2.1.5-alpha. Found by OSS-Fuzz. We are also tracking this issue + as TROVE-2020-002 and CVE-2020-10592. + + o Major bugfixes (circuit padding, memory leak): + - Avoid a remotely triggered memory leak in the case that a circuit + padding machine is somehow negotiated twice on the same circuit. + Fixes bug 33619; bugfix on 0.4.0.1-alpha. Found by Tobias Pulls. + This is also tracked as TROVE-2020-004 and CVE-2020-10593. + + o Major bugfixes (directory authority): + - Directory authorities will now send a 503 (not enough bandwidth) + code to clients when under bandwidth pressure. Known relays and + other authorities will always be answered regardless of the + bandwidth situation. Fixes bug 33029; bugfix on 0.1.2.5-alpha. + + o Minor features (diagnostic): + - Improve assertions and add some memory-poisoning code to try to + track down possible causes of a rare crash (32564) in the EWMA + code. Closes ticket 33290. + + o Minor features (directory authorities): + - Directory authorities now reject descriptors from relays running + Tor versions from the 0.2.9 and 0.4.0 series. The 0.3.5 series is + still allowed. Resolves ticket 32672. Patch by Neel Chauhan. + + o Minor features (usability): + - Include more information when failing to parse a configuration + value. This should make it easier to tell what's going wrong when + a configuration file doesn't parse. Closes ticket 33460. + + o Minor bugfix (relay, configuration): + - Warn if the ContactInfo field is not set, and tell the relay + operator that not having a ContactInfo field set might cause their + relay to get rejected in the future. Fixes bug 33361; bugfix + on 0.1.1.10-alpha. + + o Minor bugfixes (coding best practices checks): + - Allow the "practracker" script to read unicode files when using + Python 2. We made the script use unicode literals in 0.4.3.1-alpha, + but didn't change the codec for opening files. Fixes bug 33374; + bugfix on 0.4.3.1-alpha. + + o Minor bugfixes (continuous integration): + - Remove the buggy and unused mirroring job. Fixes bug 33213; bugfix + on 0.3.2.2-alpha. + + o Minor bugfixes (onion service v3, client): + - Remove a BUG() warning that would cause a stack trace if an onion + service descriptor was freed while we were waiting for a + rendezvous circuit to complete. Fixes bug 28992; bugfix + on 0.3.2.1-alpha. + + o Minor bugfixes (onion services v3): + - Fix an assertion failure that could result from a corrupted + ADD_ONION control port command. Found by Saibato. Fixes bug 33137; + bugfix on 0.3.3.1-alpha. This issue is also tracked + as TROVE-2020-003. + + o Documentation (manpage): + - Alphabetize the Server and Directory server sections of the tor + manpage. Also split Statistics options into their own section of + the manpage. Closes ticket 33188. Work by Swati Thacker as part of + Google Season of Docs. + - Document the __OwningControllerProcess torrc option and specify + its polling interval. Resolves issue 32971. + + o Testing (Travis CI): + - Remove a redundant distcheck job. Closes ticket 33194. + - Sort the Travis jobs in order of speed: putting the slowest jobs + first takes full advantage of Travis job concurrency. Closes + ticket 33194. + - Stop allowing the Chutney IPv6 Travis job to fail. This job was + previously configured to fast_finish (which requires + allow_failure), to speed up the build. Closes ticket 33195. + - When a Travis chutney job fails, use chutney's new "diagnostics.sh" + tool to produce detailed diagnostic output. Closes ticket 32792. + + +Changes in version 0.4.2.7 - 2020-03-18 + This is the third stable release in the 0.4.2.x series. It backports + numerous fixes from later releases, including a fix for TROVE-2020- + 002, a major denial-of-service vulnerability that affected all + released Tor instances since 0.2.1.5-alpha. Using this vulnerability, + an attacker could cause Tor instances to consume a huge amount of CPU, + disrupting their operations for several seconds or minutes. This + attack could be launched by anybody against a relay, or by a directory + cache against any client that had connected to it. The attacker could + launch this attack as much as they wanted, thereby disrupting service + or creating patterns that could aid in traffic analysis. This issue + was found by OSS-Fuzz, and is also tracked as CVE-2020-10592. + + We do not have reason to believe that this attack is currently being + exploited in the wild, but nonetheless we advise everyone to upgrade + as soon as packages are available. + + o Major bugfixes (security, denial-of-service, backport from 0.4.3.3-alpha): + - Fix a denial-of-service bug that could be used by anyone to + consume a bunch of CPU on any Tor relay or authority, or by + directories to consume a bunch of CPU on clients or hidden + services. Because of the potential for CPU consumption to + introduce observable timing patterns, we are treating this as a + high-severity security issue. Fixes bug 33119; bugfix on + 0.2.1.5-alpha. Found by OSS-Fuzz. We are also tracking this issue + as TROVE-2020-002 and CVE-2020-10592. + + o Major bugfixes (circuit padding, memory leak, backport from 0.4.3.3-alpha): + - Avoid a remotely triggered memory leak in the case that a circuit + padding machine is somehow negotiated twice on the same circuit. + Fixes bug 33619; bugfix on 0.4.0.1-alpha. Found by Tobias Pulls. + This is also tracked as TROVE-2020-004 and CVE-2020-10593. + + o Major bugfixes (directory authority, backport from 0.4.3.3-alpha): + - Directory authorities will now send a 503 (not enough bandwidth) + code to clients when under bandwidth pressure. Known relays and + other authorities will always be answered regardless of the + bandwidth situation. Fixes bug 33029; bugfix on 0.1.2.5-alpha. + + o Minor features (continuous integration, backport from 0.4.3.2-alpha): + - Stop allowing failures on the Travis CI stem tests job. It looks + like all the stem hangs we were seeing before are now fixed. + Closes ticket 33075. + + o Minor bugfixes (bridges, backport from 0.4.3.1-alpha): + - Lowercase the configured value of BridgeDistribution before adding + it to the descriptor. Fixes bug 32753; bugfix on 0.3.2.3-alpha. + + o Minor bugfixes (logging, backport from 0.4.3.2-alpha): + - If we encounter a bug when flushing a buffer to a TLS connection, + only log the bug once per invocation of the Tor process. + Previously we would log with every occurrence, which could cause + us to run out of disk space. Fixes bug 33093; bugfix + on 0.3.2.2-alpha. + + o Minor bugfixes (onion services v3, backport from 0.4.3.3-alpha): + - Fix an assertion failure that could result from a corrupted + ADD_ONION control port command. Found by Saibato. Fixes bug 33137; + bugfix on 0.3.3.1-alpha. This issue is also tracked + as TROVE-2020-003. + + o Minor bugfixes (rust, build, backport from 0.4.3.2-alpha): + - Fix a syntax warning given by newer versions of Rust that was + creating problems for our continuous integration. Fixes bug 33212; + bugfix on 0.3.5.1-alpha. + + o Testing (Travis CI, backport from 0.4.3.3-alpha): + - Remove a redundant distcheck job. Closes ticket 33194. + - Sort the Travis jobs in order of speed: putting the slowest jobs + first takes full advantage of Travis job concurrency. Closes + ticket 33194. + - Stop allowing the Chutney IPv6 Travis job to fail. This job was + previously configured to fast_finish (which requires + allow_failure), to speed up the build. Closes ticket 33195. + - When a Travis chutney job fails, use chutney's new "diagnostics.sh" + tool to produce detailed diagnostic output. Closes ticket 32792. + + +Changes in version 0.4.1.9 - 2020-03-18 + Tor 0.4.1.9 backports important fixes from later Tor releases, + including a fix for TROVE-2020-002, a major denial-of-service + vulnerability that affected all released Tor instances since + 0.2.1.5-alpha. Using this vulnerability, an attacker could cause Tor + instances to consume a huge amount of CPU, disrupting their operations + for several seconds or minutes. This attack could be launched by + anybody against a relay, or by a directory cache against any client + that had connected to it. The attacker could launch this attack as + much as they wanted, thereby disrupting service or creating patterns + that could aid in traffic analysis. This issue was found by OSS-Fuzz, + and is also tracked as CVE-2020-10592. + + We do not have reason to believe that this attack is currently being + exploited in the wild, but nonetheless we advise everyone to upgrade + as soon as packages are available. + + o Major bugfixes (security, denial-of-service, backport from 0.4.3.3-alpha): + - Fix a denial-of-service bug that could be used by anyone to + consume a bunch of CPU on any Tor relay or authority, or by + directories to consume a bunch of CPU on clients or hidden + services. Because of the potential for CPU consumption to + introduce observable timing patterns, we are treating this as a + high-severity security issue. Fixes bug 33119; bugfix on + 0.2.1.5-alpha. Found by OSS-Fuzz. We are also tracking this issue + as TROVE-2020-002 and CVE-2020-10592. + + o Major bugfixes (circuit padding, memory leak, backport from 0.4.3.3-alpha): + - Avoid a remotely triggered memory leak in the case that a circuit + padding machine is somehow negotiated twice on the same circuit. + Fixes bug 33619; bugfix on 0.4.0.1-alpha. Found by Tobias Pulls. + This is also tracked as TROVE-2020-004 and CVE-2020-10593. + + o Minor bugfixes (bridges, backport from 0.4.3.1-alpha): + - Lowercase the configured value of BridgeDistribution before adding + it to the descriptor. Fixes bug 32753; bugfix on 0.3.2.3-alpha. + + o Minor bugfixes (logging, backport from 0.4.3.2-alpha): + - If we encounter a bug when flushing a buffer to a TLS connection, + only log the bug once per invocation of the Tor process. + Previously we would log with every occurrence, which could cause + us to run out of disk space. Fixes bug 33093; bugfix + on 0.3.2.2-alpha. + + o Minor bugfixes (onion services v3, backport from 0.4.3.3-alpha): + - Fix an assertion failure that could result from a corrupted + ADD_ONION control port command. Found by Saibato. Fixes bug 33137; + bugfix on 0.3.3.1-alpha. This issue is also tracked + as TROVE-2020-003. + + o Minor bugfixes (rust, build, backport from 0.4.3.2-alpha): + - Fix a syntax warning given by newer versions of Rust that was + creating problems for our continuous integration. Fixes bug 33212; + bugfix on 0.3.5.1-alpha. + + o Testing (Travis CI, backport from 0.4.3.3-alpha): + - Remove a redundant distcheck job. Closes ticket 33194. + - Sort the Travis jobs in order of speed: putting the slowest jobs + first takes full advantage of Travis job concurrency. Closes + ticket 33194. + - Stop allowing the Chutney IPv6 Travis job to fail. This job was + previously configured to fast_finish (which requires + allow_failure), to speed up the build. Closes ticket 33195. + - When a Travis chutney job fails, use chutney's new "diagnostics.sh" + tool to produce detailed diagnostic output. Closes ticket 32792. + + +Changes in version 0.3.5.10 - 2020-03-18 + Tor 0.3.5.10 backports many fixes from later Tor releases, including a + fix for TROVE-2020-002, a major denial-of-service vulnerability that + affected all released Tor instances since 0.2.1.5-alpha. Using this + vulnerability, an attacker could cause Tor instances to consume a huge + amount of CPU, disrupting their operations for several seconds or + minutes. This attack could be launched by anybody against a relay, or + by a directory cache against any client that had connected to it. The + attacker could launch this attack as much as they wanted, thereby + disrupting service or creating patterns that could aid in traffic + analysis. This issue was found by OSS-Fuzz, and is also tracked + as CVE-2020-10592. + + We do not have reason to believe that this attack is currently being + exploited in the wild, but nonetheless we advise everyone to upgrade + as soon as packages are available. + + o Major bugfixes (security, denial-of-service, backport from 0.4.3.3-alpha): + - Fix a denial-of-service bug that could be used by anyone to + consume a bunch of CPU on any Tor relay or authority, or by + directories to consume a bunch of CPU on clients or hidden + services. Because of the potential for CPU consumption to + introduce observable timing patterns, we are treating this as a + high-severity security issue. Fixes bug 33119; bugfix on + 0.2.1.5-alpha. Found by OSS-Fuzz. We are also tracking this issue + as TROVE-2020-002 and CVE-2020-10592. + + o Major bugfixes (linux seccomp sandbox, backport from 0.4.3.1-alpha): + - Correct how we use libseccomp. Particularly, stop assuming that + rules are applied in a particular order or that more rules are + processed after the first match. Neither is the case! In + libseccomp <2.4.0 this lead to some rules having no effect. + libseccomp 2.4.0 changed how rules are generated, leading to a + different ordering, which in turn led to a fatal crash during + startup. Fixes bug 29819; bugfix on 0.2.5.1-alpha. Patch by + Peter Gerber. + + o Minor features (continuous integration, backport from 0.4.3.2-alpha): + - Stop allowing failures on the Travis CI stem tests job. It looks + like all the stem hangs we were seeing before are now fixed. + Closes ticket 33075. + + o Minor bugfixes (bridges, backport from 0.4.3.1-alpha): + - Lowercase the configured value of BridgeDistribution before adding + it to the descriptor. Fixes bug 32753; bugfix on 0.3.2.3-alpha. + + o Minor bugfixes (crash, backport from 0.4.2.4-rc): + - When running Tor with an option like --verify-config or + --dump-config that does not start the event loop, avoid crashing + if we try to exit early because of an error. Fixes bug 32407; + bugfix on 0.3.3.1-alpha. + + o Minor bugfixes (logging, backport from 0.4.3.2-alpha): + - If we encounter a bug when flushing a buffer to a TLS connection, + only log the bug once per invocation of the Tor process. + Previously we would log with every occurrence, which could cause + us to run out of disk space. Fixes bug 33093; bugfix + on 0.3.2.2-alpha. + + o Minor bugfixes (onion services v3, backport from 0.4.3.3-alpha): + - Fix an assertion failure that could result from a corrupted + ADD_ONION control port command. Found by Saibato. Fixes bug 33137; + bugfix on 0.3.3.1-alpha. This issue is also tracked + as TROVE-2020-003. + + o Minor bugfixes (rust, build, backport from 0.4.3.2-alpha): + - Fix a syntax warning given by newer versions of Rust that was + creating problems for our continuous integration. Fixes bug 33212; + bugfix on 0.3.5.1-alpha. + + o Testing (backport from 0.4.3.1-alpha): + - Re-enable the Travis CI macOS Chutney build, but don't let it + prevent the Travis job from finishing. (The Travis macOS jobs are + slow, so we don't want to have it delay the whole CI process.) + Closes ticket 32629. + - Turn off Tor's Sandbox in Chutney jobs, and run those jobs on + Ubuntu Bionic. Turning off the Sandbox is a work-around, until we + fix the sandbox errors in 32722. Closes ticket 32240. + + o Testing (continuous integration, backport from 0.4.3.1-alpha): + - Use zstd in our Travis Linux builds. Closes ticket 32242. + + o Testing (Travis CI, backport from 0.4.3.3-alpha): + - Remove a redundant distcheck job. Closes ticket 33194. + - Sort the Travis jobs in order of speed: putting the slowest jobs + first takes full advantage of Travis job concurrency. Closes + ticket 33194. + - Stop allowing the Chutney IPv6 Travis job to fail. This job was + previously configured to fast_finish (which requires + - When a Travis chutney job fails, use chutney's new "diagnostics.sh" + tool to produce detailed diagnostic output. Closes ticket 32792. + + Changes in version 0.4.3.2-alpha - 2020-02-10 This is the second stable alpha release in the Tor 0.4.3.x series. It fixes several bugs present in the previous alpha release. Anybody diff --git a/Makefile.am b/Makefile.am index ac61a990fc..1a1fa9b658 100644 --- a/Makefile.am +++ b/Makefile.am @@ -70,6 +70,7 @@ TOR_UTIL_LIBS = \ src/lib/libtor-wallclock.a \ src/lib/libtor-err.a \ src/lib/libtor-version.a \ + src/lib/libtor-llharden.a \ src/lib/libtor-intmath.a \ src/lib/libtor-ctime.a @@ -104,6 +105,7 @@ TOR_UTIL_TESTING_LIBS = \ src/lib/libtor-wallclock-testing.a \ src/lib/libtor-err-testing.a \ src/lib/libtor-version-testing.a \ + src/lib/libtor-llharden-testing.a \ src/lib/libtor-intmath.a \ src/lib/libtor-ctime-testing.a endif @@ -275,60 +277,186 @@ check-local: \ shellcheck \ check-cocci +# test-network requires a copy of Chutney in $CHUTNEY_PATH. +# Chutney can be cloned from https://git.torproject.org/chutney.git . +.PHONY: need-chutney-path need-chutney-path: @if test ! -d "$$CHUTNEY_PATH"; then \ echo '$$CHUTNEY_PATH was not set.'; \ - if test -d $(top_srcdir)/../chutney -a -x $(top_srcdir)/../chutney/chutney; then \ - echo "Assuming test-network.sh will find" $(top_srcdir)/../chutney; \ + if test -d "$(top_srcdir)/../chutney" && \ + test -x "$(top_srcdir)/../chutney/chutney"; then \ + echo "Assuming test-network.sh will find" \ + "$(top_srcdir)/../chutney"; \ else \ echo; \ - echo "To run these tests, git clone https://git.torproject.org/chutney.git ; export CHUTNEY_PATH=\`pwd\`/chutney"; \ + echo "To run these tests," \ + "git clone" \ + "https://git.torproject.org/chutney.git ;" \ + "export CHUTNEY_PATH=\`pwd\`/chutney"; \ exit 1; \ fi \ fi -# Note that test-network requires a copy of Chutney in $CHUTNEY_PATH. -# Chutney can be cloned from https://git.torproject.org/chutney.git . -test-network: need-chutney-path $(TESTING_TOR_BINARY) src/tools/tor-gencert - $(top_srcdir)/src/test/test-network.sh $(TEST_NETWORK_FLAGS) +# Run some basic tests using automake's test-driver +.PHONY: test-network +# Hide directory path logs from submakes using $(MAKE) -s +test-network: + @$(MAKE) -s test-network-mkdir + @$(MAKE) -s test-network-clean + @$(MAKE) -s test-network-run \ + ipv4_flavors="$(TEST_CHUTNEY_FLAVOR_QUICK)" \ + ipv6_flavors="$(TEST_CHUTNEY_FLAVOR_QUICK_IPV6)" + @$(MAKE) -s test-network-results # Run all available tests using automake's test-driver +.PHONY: test-network-all +# Hide directory path logs from submakes using $(MAKE) -s +test-network-all: + @$(MAKE) -s test-network-mkdir + @$(MAKE) -s test-network-clean + @$(MAKE) -s test-network-run \ + ipv4_flavors="$(TEST_CHUTNEY_FLAVORS)" \ + mixed_flavors="$(TEST_CHUTNEY_FLAVORS_MIXED)" \ + ipv6_flavors="$(TEST_CHUTNEY_FLAVORS_IPV6)" \ + ipv6_mixed_flavors="$(TEST_CHUTNEY_FLAVORS_IPV6_MIXED)" + @$(MAKE) -s test-network-results + +# Run IPv4 and mixed tests using automake's test-driver +.PHONY: test-network-ipv4 +# Hide directory path logs from submakes using $(MAKE) -s +test-network-ipv4: + @$(MAKE) -s test-network-mkdir + @$(MAKE) -s test-network-clean + @$(MAKE) -s test-network-run \ + ipv4_flavors="$(TEST_CHUTNEY_FLAVORS)" \ + mixed_flavors="$(TEST_CHUTNEY_FLAVORS_MIXED)" + @$(MAKE) -s test-network-results + +# Run IPv6 tests using automake's test-driver +.PHONY: test-network-ipv6 +# Hide directory path logs from submakes using $(MAKE) -s +test-network-ipv6: + @$(MAKE) -s test-network-mkdir + @$(MAKE) -s test-network-clean + @$(MAKE) -s test-network-run \ + ipv6_flavors="$(TEST_CHUTNEY_FLAVORS_IPV6)" \ + ipv6_mixed_flavors="$(TEST_CHUTNEY_FLAVORS_IPV6_MIXED)" + @$(MAKE) -s test-network-results + +# Make the test network log directory, if it does not exist +.PHONY: test-network-mkdir +test-network-mkdir: + @mkdir -p "$(TEST_NETWORK_ALL_LOG_DIR)" + +# Clean the test network log directory +.PHONY: test-network-clean +# We need to remove all matching files, so we can't quote the glob part of the +# rm arguments +test-network-clean: + rm -f "$(TEST_NETWORK_ALL_LOG_DIR)"/*.log \ + "$(TEST_NETWORK_ALL_LOG_DIR)"/*.trs + +# Run tests using automake's test-driver +# When checking if a set of test can be run, log the type of test, and the +# list of tests that will be run (or skipped). +# +# Run the IPv4 tests in $(ipv4_flavors), unconditionally +# - tor relays and directory authorities require IPv4. +# Run the IPv6 tests in $(ipv6_flavors), if IPv6 is available # - only run IPv6 tests if we can ping6 or ping -6 ::1 (localhost) # we try the syntax for BSD ping6, Linux ping6, and Linux ping -6, # because they're incompatible # - some IPv6 tests may fail without an IPv6 DNS server # (see #16971 and #17011) +# Run the mixed tests in $(mixed_flavors), if a tor-stable binary is available # - only run mixed tests if we have a tor-stable binary -# - show tor warnings on the console after each network run -# (otherwise, warnings go to the logs, and people don't see them unless -# there is a network failure) -test-network-all: need-chutney-path test-driver $(TESTING_TOR_BINARY) src/tools/tor-gencert - mkdir -p $(TEST_NETWORK_ALL_LOG_DIR) - rm -f $(TEST_NETWORK_ALL_LOG_DIR)/*.log $(TEST_NETWORK_ALL_LOG_DIR)/*.trs - @flavors="$(TEST_CHUTNEY_FLAVORS)"; \ - if ping6 -q -c 1 -o ::1 >/dev/null 2>&1 || ping6 -q -c 1 -W 1 ::1 >/dev/null 2>&1 || ping -6 -c 1 -W 1 ::1 >/dev/null 2>&1; then \ - echo "ping6 ::1 or ping ::1 succeeded, running IPv6 flavors: $(TEST_CHUTNEY_FLAVORS_IPV6)."; \ - flavors="$$flavors $(TEST_CHUTNEY_FLAVORS_IPV6)"; \ - else \ - echo "ping6 ::1 and ping ::1 failed, skipping IPv6 flavors: $(TEST_CHUTNEY_FLAVORS_IPV6)."; \ - skip_flavors="$$skip_flavors $(TEST_CHUTNEY_FLAVORS_IPV6)"; \ +# Run the IPv6 mixed tests in $(ipv6_mixed_flavors), if IPv6 and mixed are run +# - see above for details about IPv6 and mixed +.PHONY: test-network-run +# We need the word splitting in the "for" lines, so we can't quote +# $(skip_flavors) or $(flavors) +test-network-run: need-chutney-path test-driver $(TESTING_TOR_BINARY) src/tools/tor-gencert + @flavors=""; \ + skip_flavors=""; \ + if test -n "$(ipv4_flavors)"; then \ + echo "Running IPv4 flavors: $(ipv4_flavors)."; \ + flavors="$$flavors $(ipv4_flavors)"; \ fi; \ - if command -v tor-stable >/dev/null 2>&1; then \ - echo "tor-stable found, running mixed flavors: $(TEST_CHUTNEY_FLAVORS_MIXED)."; \ - flavors="$$flavors $(TEST_CHUTNEY_FLAVORS_MIXED)"; \ - else \ - echo "tor-stable not found, skipping mixed flavors: $(TEST_CHUTNEY_FLAVORS_MIXED)."; \ - skip_flavors="$$skip_flavors $(TEST_CHUTNEY_FLAVORS_MIXED)"; \ + test_network_ipv6=false; \ + if test -n "$(ipv6_flavors)" || \ + test -n "$(ipv6_mixed_flavors)"; then \ + if ping6 -q -c 1 -o ::1 >/dev/null 2>&1 || \ + ping6 -q -c 1 -W 1 ::1 >/dev/null 2>&1 || \ + ping -6 -c 1 -W 1 ::1 >/dev/null 2>&1; then \ + test_network_ipv6=true; \ + fi; \ + fi; \ + if test -n "$(ipv6_flavors)"; then \ + if test "$$test_network_ipv6" = "true"; then \ + echo "ping6 ::1 or ping ::1 succeeded, running IPv6" \ + "flavors: $(ipv6_flavors)."; \ + flavors="$$flavors $(ipv6_flavors)"; \ + else \ + echo "ping6 ::1 and ping ::1 failed, skipping IPv6" \ + "flavors: $(ipv6_flavors)."; \ + skip_flavors="$$skip_flavors $(ipv6_flavors)"; \ + fi; \ + fi; \ + test_network_mixed=false; \ + if test -n "$(mixed_flavors)" || \ + test -n "$(ipv6_mixed_flavors)"; then \ + if command -v tor-stable >/dev/null 2>&1; then \ + test_network_mixed=true; \ + fi; \ + fi; \ + if test -n "$(mixed_flavors)"; then \ + if test "$$test_network_mixed" = "true"; then \ + echo "tor-stable found, running mixed flavors:" \ + "$(mixed_flavors)."; \ + flavors="$$flavors $(mixed_flavors)"; \ + else \ + echo "tor-stable not found, skipping mixed flavors:" \ + "$(mixed_flavors)."; \ + skip_flavors="$$skip_flavors $(mixed_flavors)"; \ + fi; \ + fi; \ + if test -n "$(ipv6_mixed_flavors)"; then \ + if test "$$test_network_ipv6" = "true" && \ + test "$$test_network_mixed" = "true"; then \ + echo "Running IPv6 mixed flavors:" \ + "$(ipv6_mixed_flavors)."; \ + flavors="$$flavors $(ipv6_mixed_flavors)"; \ + else \ + echo "Skipping IPv6 mixed flavors:" \ + "$(ipv6_mixed_flavors)."; \ + skip_flavors="$$skip_flavors $(ipv6_mixed_flavors)"; \ + fi; \ fi; \ for f in $$skip_flavors; do \ echo "SKIP: $$f"; \ done; \ for f in $$flavors; do \ - $(SHELL) $(top_srcdir)/test-driver --test-name $$f --log-file $(TEST_NETWORK_ALL_LOG_DIR)/$$f.log --trs-file $(TEST_NETWORK_ALL_LOG_DIR)/$$f.trs $(TEST_NETWORK_ALL_DRIVER_FLAGS) $(top_srcdir)/src/test/test-network.sh --flavor $$f $(TEST_NETWORK_FLAGS); \ - $(top_srcdir)/src/test/test-network.sh $(TEST_NETWORK_SHOW_WARNINGS_FOR_LAST_RUN_FLAGS); \ - done; \ - echo "Log and result files are available in $(TEST_NETWORK_ALL_LOG_DIR)."; \ - ! grep -q FAIL $(TEST_NETWORK_ALL_LOG_DIR)/*.trs + $(SHELL) "$(top_srcdir)/test-driver" --test-name "$$f" \ + --log-file "$(TEST_NETWORK_ALL_LOG_DIR)/$$f.log" \ + --trs-file "$(TEST_NETWORK_ALL_LOG_DIR)/$$f.trs" \ + $(TEST_NETWORK_ALL_DRIVER_FLAGS) \ + "$(top_srcdir)/src/test/test-network.sh" \ + --flavor "$$f" $(TEST_NETWORK_FLAGS); \ + "$(top_srcdir)/src/test/test-network.sh" \ + $(TEST_NETWORK_SHOW_WARNINGS_FOR_LAST_RUN_FLAGS); \ + done + +# Print the results from automake's test-driver +# - show tor warnings on the console after each network run +# (otherwise, warnings go to the logs, and people don't see them unless +# there is a network failure) +.PHONY: test-network-results +# We need to grep all matching files, so we can't quote the glob part of the +# grep arguments +test-network-results: + @echo \ + "Log and result files are available in $(TEST_NETWORK_ALL_LOG_DIR)." + @! grep -q FAIL "$(TEST_NETWORK_ALL_LOG_DIR)"/*.trs need-stem-path: @if test ! -d "$$STEM_SOURCE_DIR"; then \ @@ -24,9 +24,8 @@ Making applications work with Tor: Frequently Asked Questions: https://www.torproject.org/docs/faq.html +Release timeline: + https://trac.torproject.org/projects/tor/wiki/org/teams/NetworkTeam/CoreTorReleases To get started working on Tor development: See the doc/HACKING directory. - -Release timeline: - https://trac.torproject.org/projects/tor/wiki/org/teams/NetworkTeam/CoreTorReleases diff --git a/ReleaseNotes b/ReleaseNotes index 7d981e2c42..a9adbfe342 100644 --- a/ReleaseNotes +++ b/ReleaseNotes @@ -2,6 +2,346 @@ This document summarizes new features and bugfixes in each stable release of Tor. If you want to see more detailed descriptions of the changes in each development snapshot, see the ChangeLog file. +Changes in version 0.4.3.3-alpha - 2020-03-18 + Tor 0.4.3.3-alpha fixes several bugs in previous releases, including + TROVE-2020-002, a major denial-of-service vulnerability that affected + all released Tor instances since 0.2.1.5-alpha. Using this + vulnerability, an attacker could cause Tor instances to consume a huge + amount of CPU, disrupting their operations for several seconds or + minutes. This attack could be launched by anybody against a relay, or + by a directory cache against any client that had connected to it. The + attacker could launch this attack as much as they wanted, thereby + disrupting service or creating patterns that could aid in traffic + analysis. This issue was found by OSS-Fuzz, and is also tracked + as CVE-2020-10592. + + We do not have reason to believe that this attack is currently being + exploited in the wild, but nonetheless we advise everyone to upgrade + as soon as packages are available. + + o Major bugfixes (security, denial-of-service): + - Fix a denial-of-service bug that could be used by anyone to + consume a bunch of CPU on any Tor relay or authority, or by + directories to consume a bunch of CPU on clients or hidden + services. Because of the potential for CPU consumption to + introduce observable timing patterns, we are treating this as a + high-severity security issue. Fixes bug 33119; bugfix on + 0.2.1.5-alpha. Found by OSS-Fuzz. We are also tracking this issue + as TROVE-2020-002 and CVE-2020-10592. + + o Major bugfixes (circuit padding, memory leak): + - Avoid a remotely triggered memory leak in the case that a circuit + padding machine is somehow negotiated twice on the same circuit. + Fixes bug 33619; bugfix on 0.4.0.1-alpha. Found by Tobias Pulls. + This is also tracked as TROVE-2020-004 and CVE-2020-10593. + + o Major bugfixes (directory authority): + - Directory authorities will now send a 503 (not enough bandwidth) + code to clients when under bandwidth pressure. Known relays and + other authorities will always be answered regardless of the + bandwidth situation. Fixes bug 33029; bugfix on 0.1.2.5-alpha. + + o Minor features (diagnostic): + - Improve assertions and add some memory-poisoning code to try to + track down possible causes of a rare crash (32564) in the EWMA + code. Closes ticket 33290. + + o Minor features (directory authorities): + - Directory authorities now reject descriptors from relays running + Tor versions from the 0.2.9 and 0.4.0 series. The 0.3.5 series is + still allowed. Resolves ticket 32672. Patch by Neel Chauhan. + + o Minor features (usability): + - Include more information when failing to parse a configuration + value. This should make it easier to tell what's going wrong when + a configuration file doesn't parse. Closes ticket 33460. + + o Minor bugfix (relay, configuration): + - Warn if the ContactInfo field is not set, and tell the relay + operator that not having a ContactInfo field set might cause their + relay to get rejected in the future. Fixes bug 33361; bugfix + on 0.1.1.10-alpha. + + o Minor bugfixes (coding best practices checks): + - Allow the "practracker" script to read unicode files when using + Python 2. We made the script use unicode literals in 0.4.3.1-alpha, + but didn't change the codec for opening files. Fixes bug 33374; + bugfix on 0.4.3.1-alpha. + + o Minor bugfixes (continuous integration): + - Remove the buggy and unused mirroring job. Fixes bug 33213; bugfix + on 0.3.2.2-alpha. + + o Minor bugfixes (onion service v3, client): + - Remove a BUG() warning that would cause a stack trace if an onion + service descriptor was freed while we were waiting for a + rendezvous circuit to complete. Fixes bug 28992; bugfix + on 0.3.2.1-alpha. + + o Minor bugfixes (onion services v3): + - Fix an assertion failure that could result from a corrupted + ADD_ONION control port command. Found by Saibato. Fixes bug 33137; + bugfix on 0.3.3.1-alpha. This issue is also tracked + as TROVE-2020-003. + + o Documentation (manpage): + - Alphabetize the Server and Directory server sections of the tor + manpage. Also split Statistics options into their own section of + the manpage. Closes ticket 33188. Work by Swati Thacker as part of + Google Season of Docs. + - Document the __OwningControllerProcess torrc option and specify + its polling interval. Resolves issue 32971. + + o Testing (Travis CI): + - Remove a redundant distcheck job. Closes ticket 33194. + - Sort the Travis jobs in order of speed: putting the slowest jobs + first takes full advantage of Travis job concurrency. Closes + ticket 33194. + - Stop allowing the Chutney IPv6 Travis job to fail. This job was + previously configured to fast_finish (which requires + allow_failure), to speed up the build. Closes ticket 33195. + - When a Travis chutney job fails, use chutney's new "diagnostics.sh" + tool to produce detailed diagnostic output. Closes ticket 32792. + + +Changes in version 0.4.2.7 - 2020-03-18 + This is the third stable release in the 0.4.2.x series. It backports + numerous fixes from later releases, including a fix for TROVE-2020- + 002, a major denial-of-service vulnerability that affected all + released Tor instances since 0.2.1.5-alpha. Using this vulnerability, + an attacker could cause Tor instances to consume a huge amount of CPU, + disrupting their operations for several seconds or minutes. This + attack could be launched by anybody against a relay, or by a directory + cache against any client that had connected to it. The attacker could + launch this attack as much as they wanted, thereby disrupting service + or creating patterns that could aid in traffic analysis. This issue + was found by OSS-Fuzz, and is also tracked as CVE-2020-10592. + + We do not have reason to believe that this attack is currently being + exploited in the wild, but nonetheless we advise everyone to upgrade + as soon as packages are available. + + o Major bugfixes (security, denial-of-service, backport from 0.4.3.3-alpha): + - Fix a denial-of-service bug that could be used by anyone to + consume a bunch of CPU on any Tor relay or authority, or by + directories to consume a bunch of CPU on clients or hidden + services. Because of the potential for CPU consumption to + introduce observable timing patterns, we are treating this as a + high-severity security issue. Fixes bug 33119; bugfix on + 0.2.1.5-alpha. Found by OSS-Fuzz. We are also tracking this issue + as TROVE-2020-002 and CVE-2020-10592. + + o Major bugfixes (circuit padding, memory leak, backport from 0.4.3.3-alpha): + - Avoid a remotely triggered memory leak in the case that a circuit + padding machine is somehow negotiated twice on the same circuit. + Fixes bug 33619; bugfix on 0.4.0.1-alpha. Found by Tobias Pulls. + This is also tracked as TROVE-2020-004 and CVE-2020-10593. + + o Major bugfixes (directory authority, backport from 0.4.3.3-alpha): + - Directory authorities will now send a 503 (not enough bandwidth) + code to clients when under bandwidth pressure. Known relays and + other authorities will always be answered regardless of the + bandwidth situation. Fixes bug 33029; bugfix on 0.1.2.5-alpha. + + o Minor features (continuous integration, backport from 0.4.3.2-alpha): + - Stop allowing failures on the Travis CI stem tests job. It looks + like all the stem hangs we were seeing before are now fixed. + Closes ticket 33075. + + o Minor bugfixes (bridges, backport from 0.4.3.1-alpha): + - Lowercase the configured value of BridgeDistribution before adding + it to the descriptor. Fixes bug 32753; bugfix on 0.3.2.3-alpha. + + o Minor bugfixes (logging, backport from 0.4.3.2-alpha): + - If we encounter a bug when flushing a buffer to a TLS connection, + only log the bug once per invocation of the Tor process. + Previously we would log with every occurrence, which could cause + us to run out of disk space. Fixes bug 33093; bugfix + on 0.3.2.2-alpha. + + o Minor bugfixes (onion services v3, backport from 0.4.3.3-alpha): + - Fix an assertion failure that could result from a corrupted + ADD_ONION control port command. Found by Saibato. Fixes bug 33137; + bugfix on 0.3.3.1-alpha. This issue is also tracked + as TROVE-2020-003. + + o Minor bugfixes (rust, build, backport from 0.4.3.2-alpha): + - Fix a syntax warning given by newer versions of Rust that was + creating problems for our continuous integration. Fixes bug 33212; + bugfix on 0.3.5.1-alpha. + + o Testing (Travis CI, backport from 0.4.3.3-alpha): + - Remove a redundant distcheck job. Closes ticket 33194. + - Sort the Travis jobs in order of speed: putting the slowest jobs + first takes full advantage of Travis job concurrency. Closes + ticket 33194. + - Stop allowing the Chutney IPv6 Travis job to fail. This job was + previously configured to fast_finish (which requires + allow_failure), to speed up the build. Closes ticket 33195. + - When a Travis chutney job fails, use chutney's new "diagnostics.sh" + tool to produce detailed diagnostic output. Closes ticket 32792. + + +Changes in version 0.4.1.9 - 2020-03-18 + Tor 0.4.1.9 backports important fixes from later Tor releases, + including a fix for TROVE-2020-002, a major denial-of-service + vulnerability that affected all released Tor instances since + 0.2.1.5-alpha. Using this vulnerability, an attacker could cause Tor + instances to consume a huge amount of CPU, disrupting their operations + for several seconds or minutes. This attack could be launched by + anybody against a relay, or by a directory cache against any client + that had connected to it. The attacker could launch this attack as + much as they wanted, thereby disrupting service or creating patterns + that could aid in traffic analysis. This issue was found by OSS-Fuzz, + and is also tracked as CVE-2020-10592. + + We do not have reason to believe that this attack is currently being + exploited in the wild, but nonetheless we advise everyone to upgrade + as soon as packages are available. + + o Major bugfixes (security, denial-of-service, backport from 0.4.3.3-alpha): + - Fix a denial-of-service bug that could be used by anyone to + consume a bunch of CPU on any Tor relay or authority, or by + directories to consume a bunch of CPU on clients or hidden + services. Because of the potential for CPU consumption to + introduce observable timing patterns, we are treating this as a + high-severity security issue. Fixes bug 33119; bugfix on + 0.2.1.5-alpha. Found by OSS-Fuzz. We are also tracking this issue + as TROVE-2020-002 and CVE-2020-10592. + + o Major bugfixes (circuit padding, memory leak, backport from 0.4.3.3-alpha): + - Avoid a remotely triggered memory leak in the case that a circuit + padding machine is somehow negotiated twice on the same circuit. + Fixes bug 33619; bugfix on 0.4.0.1-alpha. Found by Tobias Pulls. + This is also tracked as TROVE-2020-004 and CVE-2020-10593. + + o Minor bugfixes (bridges, backport from 0.4.3.1-alpha): + - Lowercase the configured value of BridgeDistribution before adding + it to the descriptor. Fixes bug 32753; bugfix on 0.3.2.3-alpha. + + o Minor bugfixes (logging, backport from 0.4.3.2-alpha): + - If we encounter a bug when flushing a buffer to a TLS connection, + only log the bug once per invocation of the Tor process. + Previously we would log with every occurrence, which could cause + us to run out of disk space. Fixes bug 33093; bugfix + on 0.3.2.2-alpha. + + o Minor bugfixes (onion services v3, backport from 0.4.3.3-alpha): + - Fix an assertion failure that could result from a corrupted + ADD_ONION control port command. Found by Saibato. Fixes bug 33137; + bugfix on 0.3.3.1-alpha. This issue is also tracked + as TROVE-2020-003. + + o Minor bugfixes (rust, build, backport from 0.4.3.2-alpha): + - Fix a syntax warning given by newer versions of Rust that was + creating problems for our continuous integration. Fixes bug 33212; + bugfix on 0.3.5.1-alpha. + + o Testing (Travis CI, backport from 0.4.3.3-alpha): + - Remove a redundant distcheck job. Closes ticket 33194. + - Sort the Travis jobs in order of speed: putting the slowest jobs + first takes full advantage of Travis job concurrency. Closes + ticket 33194. + - Stop allowing the Chutney IPv6 Travis job to fail. This job was + previously configured to fast_finish (which requires + allow_failure), to speed up the build. Closes ticket 33195. + - When a Travis chutney job fails, use chutney's new "diagnostics.sh" + tool to produce detailed diagnostic output. Closes ticket 32792. + + +Changes in version 0.3.5.10 - 2020-03-18 + Tor 0.3.5.10 backports many fixes from later Tor releases, including a + fix for TROVE-2020-002, a major denial-of-service vulnerability that + affected all released Tor instances since 0.2.1.5-alpha. Using this + vulnerability, an attacker could cause Tor instances to consume a huge + amount of CPU, disrupting their operations for several seconds or + minutes. This attack could be launched by anybody against a relay, or + by a directory cache against any client that had connected to it. The + attacker could launch this attack as much as they wanted, thereby + disrupting service or creating patterns that could aid in traffic + analysis. This issue was found by OSS-Fuzz, and is also tracked + as CVE-2020-10592. + + We do not have reason to believe that this attack is currently being + exploited in the wild, but nonetheless we advise everyone to upgrade + as soon as packages are available. + + o Major bugfixes (security, denial-of-service, backport from 0.4.3.3-alpha): + - Fix a denial-of-service bug that could be used by anyone to + consume a bunch of CPU on any Tor relay or authority, or by + directories to consume a bunch of CPU on clients or hidden + services. Because of the potential for CPU consumption to + introduce observable timing patterns, we are treating this as a + high-severity security issue. Fixes bug 33119; bugfix on + 0.2.1.5-alpha. Found by OSS-Fuzz. We are also tracking this issue + as TROVE-2020-002 and CVE-2020-10592. + + o Major bugfixes (linux seccomp sandbox, backport from 0.4.3.1-alpha): + - Correct how we use libseccomp. Particularly, stop assuming that + rules are applied in a particular order or that more rules are + processed after the first match. Neither is the case! In + libseccomp <2.4.0 this lead to some rules having no effect. + libseccomp 2.4.0 changed how rules are generated, leading to a + different ordering, which in turn led to a fatal crash during + startup. Fixes bug 29819; bugfix on 0.2.5.1-alpha. Patch by + Peter Gerber. + + o Minor features (continuous integration, backport from 0.4.3.2-alpha): + - Stop allowing failures on the Travis CI stem tests job. It looks + like all the stem hangs we were seeing before are now fixed. + Closes ticket 33075. + + o Minor bugfixes (bridges, backport from 0.4.3.1-alpha): + - Lowercase the configured value of BridgeDistribution before adding + it to the descriptor. Fixes bug 32753; bugfix on 0.3.2.3-alpha. + + o Minor bugfixes (crash, backport from 0.4.2.4-rc): + - When running Tor with an option like --verify-config or + --dump-config that does not start the event loop, avoid crashing + if we try to exit early because of an error. Fixes bug 32407; + bugfix on 0.3.3.1-alpha. + + o Minor bugfixes (logging, backport from 0.4.3.2-alpha): + - If we encounter a bug when flushing a buffer to a TLS connection, + only log the bug once per invocation of the Tor process. + Previously we would log with every occurrence, which could cause + us to run out of disk space. Fixes bug 33093; bugfix + on 0.3.2.2-alpha. + + o Minor bugfixes (onion services v3, backport from 0.4.3.3-alpha): + - Fix an assertion failure that could result from a corrupted + ADD_ONION control port command. Found by Saibato. Fixes bug 33137; + bugfix on 0.3.3.1-alpha. This issue is also tracked + as TROVE-2020-003. + + o Minor bugfixes (rust, build, backport from 0.4.3.2-alpha): + - Fix a syntax warning given by newer versions of Rust that was + creating problems for our continuous integration. Fixes bug 33212; + bugfix on 0.3.5.1-alpha. + + o Testing (backport from 0.4.3.1-alpha): + - Re-enable the Travis CI macOS Chutney build, but don't let it + prevent the Travis job from finishing. (The Travis macOS jobs are + slow, so we don't want to have it delay the whole CI process.) + Closes ticket 32629. + - Turn off Tor's Sandbox in Chutney jobs, and run those jobs on + Ubuntu Bionic. Turning off the Sandbox is a work-around, until we + fix the sandbox errors in 32722. Closes ticket 32240. + + o Testing (continuous integration, backport from 0.4.3.1-alpha): + - Use zstd in our Travis Linux builds. Closes ticket 32242. + + o Testing (Travis CI, backport from 0.4.3.3-alpha): + - Remove a redundant distcheck job. Closes ticket 33194. + - Sort the Travis jobs in order of speed: putting the slowest jobs + first takes full advantage of Travis job concurrency. Closes + ticket 33194. + - Stop allowing the Chutney IPv6 Travis job to fail. This job was + previously configured to fast_finish (which requires + - When a Travis chutney job fails, use chutney's new "diagnostics.sh" + tool to produce detailed diagnostic output. Closes ticket 32792. + + Changes in version 0.4.2.6 - 2020-01-30 This is the second stable release in the 0.4.2.x series. It backports several bugfixes from 0.4.3.1-alpha, including some that had affected diff --git a/changes/bug31669 b/changes/bug31669 new file mode 100644 index 0000000000..8079c98f62 --- /dev/null +++ b/changes/bug31669 @@ -0,0 +1,4 @@ + o Minor bugfixes (onion services v3): + - Relax severity of a log message that can appear naturally when decoding + onion service descriptors as a relay. Also add some diagnostics to debug + any future bugs in that area. Fixes bug 31669; bugfix on 0.3.0.1-alpha.
\ No newline at end of file diff --git a/changes/bug32588 b/changes/bug32588 new file mode 100644 index 0000000000..f31f2ce1ad --- /dev/null +++ b/changes/bug32588 @@ -0,0 +1,4 @@ + o Minor bugfixes (relays): + - Stop advertising incorrect IPv6 ORPorts in relay and bridge descriptors, + when the IPv6 port was configured as "auto". + Fixes bug 32588; bugfix on 0.2.3.9-alpha diff --git a/changes/bug32709 b/changes/bug32709 new file mode 100644 index 0000000000..d00b112be6 --- /dev/null +++ b/changes/bug32709 @@ -0,0 +1,4 @@ + o Major features (v3 onion services): + - Allow v3 onion services to act as OnionBalance backend instances using + the HiddenServiceOnionBalanceInstance torrc option. Closes ticket 32709. + diff --git a/changes/bug33032 b/changes/bug33032 new file mode 100644 index 0000000000..0c665f25df --- /dev/null +++ b/changes/bug33032 @@ -0,0 +1,6 @@ + o Minor bugfixes (key portability): + - When reading PEM-encoded key data, tolerate CRLF line-endings even if + we are not running on Windows. Previously, non-Windows hosts + would reject these line-endings in certain positions, making + certain key files hard to move from one host to another. + Fixes bug 33032; bugfix on 0.3.5.1-alpha. diff --git a/changes/bug33087 b/changes/bug33087 new file mode 100644 index 0000000000..ab6df58cc6 --- /dev/null +++ b/changes/bug33087 @@ -0,0 +1,7 @@ + o Minor bugfixes (logging): + - Stop closing stderr and stdout during shutdown. Closing these file + descriptors can hide sanitiser logs. + Fixes bug 33087; bugfix on 0.4.1.6. + - Flush stderr, stdout, and file logs during shutdown, if supported by the + OS. This change helps make sure that any final logs are recorded. + Fixes bug 33087; bugfix on 0.4.1.6. diff --git a/changes/bug33531 b/changes/bug33531 new file mode 100644 index 0000000000..c4284c55c9 --- /dev/null +++ b/changes/bug33531 @@ -0,0 +1,3 @@ + o Minor bugfixes (logs): + - Remove surprising empty line in info-level log about circuit build + timeout. Fixes bug 33531; bugfix on 0.3.3.1-alpha. diff --git a/changes/bug33608 b/changes/bug33608 new file mode 100644 index 0000000000..0e82a8eec9 --- /dev/null +++ b/changes/bug33608 @@ -0,0 +1,5 @@ + o Minor bugfixes (client IPv6): + - Stop forcing all non-SOCKSPorts to prefer IPv6 exit connections. Instead, + prefer IPv6 connections by default, but allow users to change their + configs using the "NoPreferIPv6" port flag. + Fixes bug 33608; bugfix on 0.4.3.1-alpha. diff --git a/changes/bug33668 b/changes/bug33668 new file mode 100644 index 0000000000..2b0830d6d8 --- /dev/null +++ b/changes/bug33668 @@ -0,0 +1,4 @@ + o Minor bugfixes (--disable-module-relay): + - Fix an assertion failure when Tor is build without the relay module, + and then invoked with the "User" option. Fixes bug 33668; bugfix on + 0.4.3.1-alpha. diff --git a/changes/bug33673 b/changes/bug33673 new file mode 100644 index 0000000000..37c00f2e6e --- /dev/null +++ b/changes/bug33673 @@ -0,0 +1,6 @@ + o Testing: + - In our Appveyor Windows CI, copy required DLLs to test and app, before + running tor's tests. This ensures that tor.exe and test*.exe use the + correct version of each DLL. This fix is not required, but we hope it + will avoid DLL search issues in future. + Fixes bug 33673; bugfix on 0.3.4.2-alpha. diff --git a/changes/bug33674 b/changes/bug33674 new file mode 100644 index 0000000000..bcc3fcab03 --- /dev/null +++ b/changes/bug33674 @@ -0,0 +1,4 @@ + o Minor bugfixes (--disable-module-relay,--disable-module-dirauth): + - Set some output arguments in the relay and dirauth module stubs, to + guard against future stub argument handling bugs like 33668. + Fixes bug 33674; bugfix on 0.4.3.1-alpha. diff --git a/changes/bug4631 b/changes/bug4631 new file mode 100644 index 0000000000..be3dd2b43e --- /dev/null +++ b/changes/bug4631 @@ -0,0 +1,6 @@ + o Minor bugfixes (directory authorities): + - Directory authorities reject votes that arrive too late. In particular, + once an authority has started fetching missing votes, it no longer + accepts new votes posted by other authorities. This change helps prevent + a consensus split, where only some authorities have the late vote. + Fixes bug 4631; bugfix on 0.2.0.5-alpha. diff --git a/changes/doc33417 b/changes/doc33417 new file mode 100644 index 0000000000..0fc868fc65 --- /dev/null +++ b/changes/doc33417 @@ -0,0 +1,3 @@ + o Documentation (manpage): + - Refrain from mentioning nicknames in manpage section for MyFamily torrc + option. Resolves issue 33417. diff --git a/changes/ticket28208 b/changes/ticket28208 new file mode 100644 index 0000000000..8818aad1d5 --- /dev/null +++ b/changes/ticket28208 @@ -0,0 +1,4 @@ + o Testing: + - Use the "bridges+hs-v23" chutney network flavor in "make test-network". + This test requires a recent version of chutney (mid-February 2020). + Closes ticket 28208. diff --git a/changes/ticket32143 b/changes/ticket32143 new file mode 100644 index 0000000000..7f8a809ba5 --- /dev/null +++ b/changes/ticket32143 @@ -0,0 +1,4 @@ + o Minor features (continuous integration): + - Run unit-test and integration test (Stem, Chutney) jobs with + ALL_BUGS_ARE_FATAL macro being enabled on Travis and Appveyor. + Resolves ticket 32143. diff --git a/changes/ticket32720 b/changes/ticket32720 new file mode 100644 index 0000000000..87c540b7ff --- /dev/null +++ b/changes/ticket32720 @@ -0,0 +1,4 @@ + o Minor features (directory): + - Remember the number of bytes we have downloaded for each directory + purpose while bootstrapping, and while fully bootstrapped. Log this + information as part of the heartbeat message. Closes ticket 32720. diff --git a/changes/ticket32792 b/changes/ticket32792 new file mode 100644 index 0000000000..553cf0ca81 --- /dev/null +++ b/changes/ticket32792 @@ -0,0 +1,3 @@ + o Testing: + - When a Travis chutney job fails, use chutney's new "diagnostics.sh" tool + to produce detailed diagnostic output. Closes ticket 32792. diff --git a/changes/ticket32905 b/changes/ticket32905 new file mode 100644 index 0000000000..6f420ec693 --- /dev/null +++ b/changes/ticket32905 @@ -0,0 +1,6 @@ + o Removed features: + - Remove the ClientAutoIPv6ORPort option. This option attempted + to randomly choose between IPv4 and IPv6 for client connections, + and isn't a true implementation of Happy Eyeballs. Often, this + option failed on IPv4-only or IPv6-only connections. Closes + ticket 32905. Patch by Neel Chauhan. diff --git a/changes/ticket32994 b/changes/ticket32994 new file mode 100644 index 0000000000..43a32afa78 --- /dev/null +++ b/changes/ticket32994 @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Rewrite port_parse_config() to use the default port flags from + port_cfg_new(). Closes ticket 32994. Patch by MrSquanchee. diff --git a/changes/ticket33014 b/changes/ticket33014 new file mode 100644 index 0000000000..885051d9cf --- /dev/null +++ b/changes/ticket33014 @@ -0,0 +1,3 @@ + o Code simplification and refactoring (onion service): + - Refactor configuration parsing to use the new config subsystem code. + Closes ticket 33014. diff --git a/changes/ticket33188 b/changes/ticket33188 deleted file mode 100644 index 7bec15b99b..0000000000 --- a/changes/ticket33188 +++ /dev/null @@ -1,5 +0,0 @@ - o Documentation (manpage): - - Alphabetize the Server and Directory server sections of the tor - manpage. Also split Statistics options into their own section - of the manpage. Closes ticket 33188. Work by Swati Thacker as - part of Google Season of Docs. diff --git a/changes/ticket33192 b/changes/ticket33192 new file mode 100644 index 0000000000..97f976226b --- /dev/null +++ b/changes/ticket33192 @@ -0,0 +1,5 @@ + o Minor feature (python): + - Stop assuming that /usr/bin/python exists. Instead of using a + hardcoded path in scripts that still use Python 2, use /usr/bin/env, + similarly to the scripts that use Python 3. Fixes bug 33192; bugfix + on 0.4.2.
\ No newline at end of file diff --git a/changes/ticket33194 b/changes/ticket33194 new file mode 100644 index 0000000000..b87e55348e --- /dev/null +++ b/changes/ticket33194 @@ -0,0 +1,4 @@ + o Testing: + - Remove a redundant distcheck job. Closes ticket 33194. + - Sort the Travis jobs in order of speed. Putting the slowest jobs first + takes full advantage of Travis job concurrency. Closes ticket 33194. diff --git a/changes/ticket33195 b/changes/ticket33195 new file mode 100644 index 0000000000..11abd4816e --- /dev/null +++ b/changes/ticket33195 @@ -0,0 +1,4 @@ + o Testing: + - Stop allowing the Chutney IPv6 Travis job to fail. This job was + previously configured to fast_finish (which requires allow_failure), to + speed up the build. Closes ticket 33195. diff --git a/changes/ticket33213 b/changes/ticket33213 deleted file mode 100644 index f1da591bd1..0000000000 --- a/changes/ticket33213 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (continuous integration): - - Remove the buggy and unused mirroring job. Fixes bug 33213; - bugfix on 0.3.2.2-alpha. diff --git a/changes/ticket33275 b/changes/ticket33275 new file mode 100644 index 0000000000..bff3a7a3ad --- /dev/null +++ b/changes/ticket33275 @@ -0,0 +1,5 @@ + o Documentation (manpage): + - Alphabetize the Denial of Service Mitigation Options, Directory + Authority Server Options, Hidden Service Options, and Testing + Network Options sections of the tor(1) manpage. Closes ticket + 33275. Work by Swati Thacker as part of Google Season of Docs. diff --git a/changes/ticket33280 b/changes/ticket33280 new file mode 100644 index 0000000000..b90c3086ea --- /dev/null +++ b/changes/ticket33280 @@ -0,0 +1,4 @@ + o Testing: + - Add test-network-ipv4 and test-network-ipv6 jobs to the Makefile. + These jobs run the IPv4-only and dual-stack chutney flavours from + test-network-all. Closes ticket 33280. diff --git a/changes/ticket33290 b/changes/ticket33290 deleted file mode 100644 index 882764020e..0000000000 --- a/changes/ticket33290 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (diagnostic): - - Improve assertions and add some memory-poisoning code to try to track - down possible causes of a rare crash (32564) in the EWMA code. - Closes ticket 33290. diff --git a/changes/ticket33300 b/changes/ticket33300 new file mode 100644 index 0000000000..9b0bdce372 --- /dev/null +++ b/changes/ticket33300 @@ -0,0 +1,3 @@ + o Testing: + - Add a basic IPv6 test to "make test-network". This test only runs when + the local machine has an IPv6 stack. Closes ticket 33300. diff --git a/changes/ticket33303 b/changes/ticket33303 new file mode 100644 index 0000000000..b7ac7b5067 --- /dev/null +++ b/changes/ticket33303 @@ -0,0 +1,4 @@ + o Testing: + - Run the test-network-ipv6 Makefile target in the Travis CI IPv6 chutney + job. This job runs on macOS, so it's a bit slow. + Closes ticket 33303. diff --git a/changes/ticket33316 b/changes/ticket33316 new file mode 100644 index 0000000000..25b0444078 --- /dev/null +++ b/changes/ticket33316 @@ -0,0 +1,15 @@ + o Minor bugfixes (initialization): + - Initialize the subsystems in our code in an order more closely + corresponding to their dependencies, so that every system is + initialized before the ones that (theoretically) depend on it. + Fixes bug 33316; bugfix on 0.4.0.1-alpha. + + o Minor features (tests): + - Initialize all subsystems at the beginning of our unit test harness, + to avoid crashes due to uninitialized subsystems. + Follow-up from ticket 33316. + + o Code simplification and refactoring: + - Merge the orconn and ocirc events into the "core" subsystem, which + manages or connections and origin circuits. Previously they + were isolated in subsystems of their own. diff --git a/changes/ticket33334 b/changes/ticket33334 new file mode 100644 index 0000000000..ada3cb284c --- /dev/null +++ b/changes/ticket33334 @@ -0,0 +1,5 @@ + o Testing: + - Test v3 onion services to tor's mixed IPv4 chutney network. And add a + mixed IPv6 chutney network. These networks are used in the + test-network-all, test-network-ipv4, and test-network-ipv6 make targets. + Closes ticket 33334. diff --git a/changes/ticket33339 b/changes/ticket33339 new file mode 100644 index 0000000000..75ccb3546f --- /dev/null +++ b/changes/ticket33339 @@ -0,0 +1,3 @@ + o Minor feature (developer tools): + - Add a script to help check the alphabetical ordering of option + names in a manpage. Closes ticket 33339. diff --git a/changes/ticket33349 b/changes/ticket33349 new file mode 100644 index 0000000000..0458a72c8d --- /dev/null +++ b/changes/ticket33349 @@ -0,0 +1,4 @@ + o Code simplification and refactoring: + - Updated comments in 'scheduler.c' to reflect old code changes, + and simplified the scheduler channel state change code. Closes + ticket 33349. diff --git a/changes/ticket33366 b/changes/ticket33366 new file mode 100644 index 0000000000..1310c493c2 --- /dev/null +++ b/changes/ticket33366 @@ -0,0 +1,3 @@ + o Minor features (compilation size): + - Most Server-side DNS code is now disabled when building without + support for relay mode. Closes ticket 33366. diff --git a/changes/ticket33368 b/changes/ticket33368 new file mode 100644 index 0000000000..ecc6f66f4e --- /dev/null +++ b/changes/ticket33368 @@ -0,0 +1,3 @@ + o Minor features (client-only compilation): + - Disable more code related to the ext_orport protocol when compiling + without support for relay mode. Closes ticket 33368. diff --git a/changes/ticket33369 b/changes/ticket33369 new file mode 100644 index 0000000000..c55335c5b7 --- /dev/null +++ b/changes/ticket33369 @@ -0,0 +1,4 @@ + o Documentation (manpage): + - Add cross reference links and a table of contents to the HTML + tor manpage. Closes ticket 33369. Work by Swati Thacker as + part of Google Season of Docs. diff --git a/changes/ticket33370 b/changes/ticket33370 new file mode 100644 index 0000000000..41e03357f0 --- /dev/null +++ b/changes/ticket33370 @@ -0,0 +1,3 @@ + o Minor features (client-only compilation): + - Disable more of our self-testing code when support for relay mode is + disabled. Closes ticket 33370. diff --git a/changes/ticket33400 b/changes/ticket33400 new file mode 100644 index 0000000000..7603890765 --- /dev/null +++ b/changes/ticket33400 @@ -0,0 +1,3 @@ + o Minor feature (onion service v3): + - Log at INFO level why the service can not upload its descriptor(s). Closes + ticket 33400; bugfix on 0.3.2.1-alpha. diff --git a/changes/ticket33436 b/changes/ticket33436 new file mode 100644 index 0000000000..69b5545c6d --- /dev/null +++ b/changes/ticket33436 @@ -0,0 +1,4 @@ + o Minor features (directory authority, shared random): + - Refactor more authority-only parts of the shared-random scheduling code + to reside in the dirauth module, and to be disabled when compiling with + --disable-module-dirauth. Closes ticket 33436. diff --git a/changes/ticket33451 b/changes/ticket33451 new file mode 100644 index 0000000000..74dd6d1ad8 --- /dev/null +++ b/changes/ticket33451 @@ -0,0 +1,3 @@ + o Minor features (developer tools): + - Add a script ("git-install-tools.sh") to install git hooks and helper + scripts. Closes ticket 33451. diff --git a/changes/ticket33458 b/changes/ticket33458 new file mode 100644 index 0000000000..885c6dc505 --- /dev/null +++ b/changes/ticket33458 @@ -0,0 +1,4 @@ + o Minor bugfix (onion service v3): + - When cleaning the client descriptor cache, an attempt at closing circuits + for a non decrypted descriptor (lacking client authorization) lead to an + assert(). Fixes bug 33458; bugfix on 0.4.2.1-alpha. diff --git a/changes/ticket33491 b/changes/ticket33491 new file mode 100644 index 0000000000..595ea863ea --- /dev/null +++ b/changes/ticket33491 @@ -0,0 +1,6 @@ + o Major bugfixes (DoS defenses, bridges, pluggable transport): + - DoS subsystem was not given the transport name of the client connection + when tor is a bridge and thus failing to find the GeoIP cache entry for + that client address. This resulted in failing to apply DoS defenses on + bridges with a pluggable transport. Fixes bug 33491; bugfix on + 0.3.3.2-alpha. diff --git a/changes/ticket33642 b/changes/ticket33642 new file mode 100644 index 0000000000..b81edf7613 --- /dev/null +++ b/changes/ticket33642 @@ -0,0 +1,4 @@ + o Minor features (developer tooling): + - Refrain from listing all .a files that are generated by Tor build in + .gitignore. Add a single wildcard *.a entry that covers all of them for + present and future. Closes ticket 33642. diff --git a/changes/ticket33643 b/changes/ticket33643 new file mode 100644 index 0000000000..7fddab74eb --- /dev/null +++ b/changes/ticket33643 @@ -0,0 +1,5 @@ + o Minor features (testing): + - The unit tests now support a "TOR_SKIP_TESTCASES" environment variable + to specify a list of space-separated test cases that should not be + executed. We will use this to disable certain tests that are failing on + Appveyor because of mismatched OpenSSL libraries. Part of ticket 33643. diff --git a/changes/ticket33643_part2 b/changes/ticket33643_part2 new file mode 100644 index 0000000000..28193d2af5 --- /dev/null +++ b/changes/ticket33643_part2 @@ -0,0 +1,3 @@ + o Testing (CI): + - On appveyor, skip the crypto/openssl_version test, which is failing + because of a mismatched library installation. Fix for 33643. diff --git a/changes/ticket33646 b/changes/ticket33646 new file mode 100644 index 0000000000..751c5d5bf2 --- /dev/null +++ b/changes/ticket33646 @@ -0,0 +1,4 @@ + o Minor bugfixes (build system): + - Correctly output the enabled module in the configure summary. Before that, + the list shown was just plain wrong. Fixes bug 33646; bugfix on + 0.4.3.2-alpha. diff --git a/configure.ac b/configure.ac index aa511a3911..7e2645a11e 100644 --- a/configure.ac +++ b/configure.ac @@ -228,6 +228,13 @@ if test "x$enable_expensive_hardening" = "xyes" || test "x$enable_fragile_harden AC_DEFINE(DEBUG_SMARTLIST, 1, [Enable smartlist debugging]) fi +AC_ARG_ENABLE(all-bugs-are-fatal, + AS_HELP_STRING(--enable-all-bugs-are-fatal, [force all soft asserts in Tor codebase (tor_assert_nonfatal(), BUG(), etc.) to act as hard asserts (tor_assert() and equivalents); makes Tor fragile; only recommended for dev builds])) + +if test "x$enable_all_bugs_are_fatal" = "xyes"; then + AC_DEFINE(ALL_BUGS_ARE_FATAL, 1, [All assert failures are fatal]) +fi + dnl Linker hardening options dnl Currently these options are ELF specific - you can't use this with MacOSX AC_ARG_ENABLE(linker-hardening, @@ -649,6 +656,7 @@ AC_CHECK_FUNCS( explicit_bzero \ timingsafe_memcmp \ flock \ + fsync \ ftime \ get_current_dir_name \ getaddrinfo \ @@ -2665,7 +2673,7 @@ PPRINT_SUBTITLE([Modules]) m4_foreach_w([mname], MODULES, [ - test "xenable_module_mname" != "xno" && value=1 || value=0 + AM_COND_IF(m4_join([], [BUILD_MODULE_], m4_toupper([]mname[])), value=1, value=0) PPRINT_PROP_BOOL([mname (--disable-module-mname)], $value) ] ) diff --git a/contrib/client-tools/tor-resolve.py b/contrib/client-tools/tor-resolve.py index 3562193715..85e7d2d8b4 100755 --- a/contrib/client-tools/tor-resolve.py +++ b/contrib/client-tools/tor-resolve.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Future imports for Python 2.7, mandatory in 3.0 from __future__ import division diff --git a/doc/HACKING/CircuitPaddingQuickStart.md b/doc/HACKING/CircuitPaddingQuickStart.md index 167ff9f292..2780b5c6ea 100644 --- a/doc/HACKING/CircuitPaddingQuickStart.md +++ b/doc/HACKING/CircuitPaddingQuickStart.md @@ -47,6 +47,7 @@ If you want to install on your localsystem, run `make install`. For our case we just want the tor binary at `src/app/tor`. ## Use tor in TB and at a relay + Download and install a fresh Tor Browser (TB) from torproject.org. Make sure it works. From the command line, relative to the folder created when you extracted TB, run `./Browser/start-tor-browser --verbose` to get some basic log output. @@ -75,6 +76,7 @@ relay. Start TB, visit a website, and manually confirm that the middle is used by looking at the circuit display. ## Add a bare-bones APE padding machine + Now the fun part. We have several resources at our disposal (mind that links might be broken in the future, just search for the headings): - The official [Circuit Padding Developer @@ -192,6 +194,7 @@ register our machines: We run `make` to get a new `tor` binary and copy it to our local TB. ## Run the machine + To be able to view circuit info events in the console as we launch TB, we add `Log [circ]info notice stdout` to `torrc` of TB. diff --git a/doc/HACKING/CodeStructure.md b/doc/HACKING/CodeStructure.md index fffafcaed1..d387018f9b 100644 --- a/doc/HACKING/CodeStructure.md +++ b/doc/HACKING/CodeStructure.md @@ -1,3 +1,4 @@ +# Code Structure TODO: revise this to talk about how things are, rather than how things have changed. diff --git a/doc/HACKING/CodingStandards.md b/doc/HACKING/CodingStandards.md index 7999724166..a181759d60 100644 --- a/doc/HACKING/CodingStandards.md +++ b/doc/HACKING/CodingStandards.md @@ -1,5 +1,4 @@ -Coding conventions for Tor -========================== +# Coding conventions for Tor tl;dr: @@ -10,8 +9,7 @@ tl;dr: - Run `make distcheck` if you have made changes to build system components - Add a file in `changes` for your branch. -Patch checklist ---------------- +## Patch checklist If possible, send your patch as one of these (in descending order of preference) @@ -34,7 +32,7 @@ Did you remember... If you are submitting a major patch or new feature, or want to in the future... - - Set up Chutney and Stem, see HACKING/WritingTests.md + - Set up Chutney and Stem, see `doc/HACKING/WritingTests.md` - Run `make test-full` to test against all unit and integration tests. If you have changed build system components: @@ -42,9 +40,7 @@ If you have changed build system components: - For example, if you have changed Makefiles, autoconf files, or anything else that affects the build system. - -License issues -============== +## License issues Tor is distributed under the license terms in the LICENSE -- in brief, the "3-clause BSD license". If you send us code to @@ -58,9 +54,7 @@ Some compatible licenses include: - 2-clause BSD - CC0 Public Domain Dedication - -How we use Git branches -======================= +## How we use Git branches Each main development series (like 0.2.1, 0.2.2, etc) has its main work applied to a single branch. At most one series can be the development series @@ -91,9 +85,7 @@ conflicts in the ChangeLog when it comes time to merge your branch into Tor. Best advice: don't try to keep an independent branch forked for more than 6 months and expect it to merge cleanly. Try to merge pieces early and often. - -How we log changes -================== +## How we log changes When you do a commit that needs a ChangeLog entry, add a new file to the `changes` toplevel subdirectory. It should have the format of a @@ -199,8 +191,7 @@ Why use changes files instead of entries in the ChangeLog? * Having every single commit touch the ChangeLog file tended to create zillions of merge conflicts. -Whitespace and C conformance ----------------------------- +## Whitespace and C conformance Invoke `make check-spaces` from time to time, so it can tell you about deviations from our C whitespace style. Generally, we use: @@ -231,8 +222,7 @@ you're using gcc, you should invoke the configure script with the option `--enable-fatal-warnings`. This will tell the compiler to make all warnings into errors. -Functions to use; functions not to use --------------------------------------- +## Functions to use; functions not to use We have some wrapper functions like `tor_malloc`, `tor_free`, `tor_strdup`, and `tor_gettimeofday;` use them instead of their generic equivalents. (They @@ -250,7 +240,6 @@ available containers in `src/lib/containers/*.h`. You should probably familiarize yourself with these modules before you write too much code, or else you'll wind up reinventing the wheel. - We don't use `strcat` or `strcpy` or `sprintf` of any of those notoriously broken old C functions. We also avoid `strncat` and `strncpy`. Use `strlcat`, `strlcpy`, or `tor_snprintf/tor_asprintf` instead. @@ -281,9 +270,7 @@ version prefixed with `tor_` instead: strtok_r, memmem, memstr, asprintf, localtime_r, gmtime_r, inet_aton, inet_ntop, inet_pton, getpass, ntohll, htonll. (This list is incomplete.) - -What code can use what other code? ----------------------------------- +## What code can use what other code? We're trying to simplify Tor's structure over time. In the long run, we want Tor to be structured as a set of modules with *no circular dependencies*. @@ -300,8 +287,7 @@ included except those specifically permitted by the `.may_include` file. When editing one of these files, please make sure that you are not introducing any cycles into Tor's dependency graph. -Floating point math is hard ---------------------------- +## Floating point math is hard Floating point arithmetic as typically implemented by computers is very counterintuitive. Failure to adequately analyze floating point @@ -360,8 +346,7 @@ For more detailed (and math-intensive) background, see [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). -Other C conventions -------------------- +## Other C conventions The `a ? b : c` trinary operator only goes inside other expressions; don't use it as a replacement for if. (You can ignore this inside macro @@ -370,8 +355,7 @@ definitions when necessary.) Assignment operators shouldn't nest inside other expressions. (You can ignore this inside macro definitions when necessary.) -Binary data and wire formats ----------------------------- +## Binary data and wire formats Use pointer to `char` when representing NUL-terminated string. To represent arbitrary binary data, use pointer to `uint8_t`. (Many older Tor APIs ignore @@ -390,8 +374,7 @@ for more information about trunnel. For information on adding new trunnel code to Tor, see src/trunnel/README -Calling and naming conventions ------------------------------- +## Calling and naming conventions Whenever possible, functions should return -1 on error and 0 on success. @@ -414,17 +397,15 @@ probably time to create an enum. If you find that you are passing three or more flags to a function, it's probably time to create a flags argument that takes a bitfield. -What To Optimize ----------------- +## What To Optimize Don't optimize anything if it's not in the critical path. Right now, the critical path seems to be AES, logging, and the network itself. Feel free to do your own profiling to determine otherwise. -Log conventions ---------------- +## Log conventions -`https://www.torproject.org/docs/faq#LogLevel` +[FAQ - Log Levels](https://www.torproject.org/docs/faq#LogLevel) No error or warning messages should be expected during normal OR or OP operation. @@ -438,8 +419,7 @@ end-users that they aren't expected to understand the message (perhaps with a string like "internal error"). Option (A) is to be preferred to option (B). -Assertions In Tor ------------------ +## Assertions In Tor Assertions should be used for bug-detection only. Don't use assertions to detect bad user inputs, network errors, resource exhaustion, or similar @@ -458,8 +438,7 @@ macro, as in: if (BUG(ptr == NULL)) return -1; -Allocator conventions ---------------------- +## Allocator conventions By convention, any tor type with a name like `abc_t` should be allocated by a function named `abc_new()`. This function should never return @@ -500,8 +479,7 @@ to use it as a function callback), define it with a name like When deallocating, don't say e.g. `if (x) tor_free(x)`. The convention is to have deallocators do nothing when NULL pointer is passed. -Doxygen comment conventions ---------------------------- +## Doxygen comment conventions Say what functions do as a series of one or more imperative sentences, as though you were telling somebody how to be the function. In other words, DO diff --git a/doc/HACKING/CodingStandardsRust.md b/doc/HACKING/CodingStandardsRust.md index b570e10dc7..97026c9b7c 100644 --- a/doc/HACKING/CodingStandardsRust.md +++ b/doc/HACKING/CodingStandardsRust.md @@ -1,39 +1,36 @@ +# Rust Coding Standards - Rust Coding Standards -======================= - -You MUST follow the standards laid out in `.../doc/HACKING/CodingStandards.md`, +You MUST follow the standards laid out in `doc/HACKING/CodingStandards.md`, where applicable. - Module/Crate Declarations ---------------------------- +## Module/Crate Declarations Each Tor C module which is being rewritten MUST be in its own crate. -See the structure of `.../src/rust` for examples. +See the structure of `src/rust` for examples. In your crate, you MUST use `lib.rs` ONLY for pulling in external crates (e.g. `extern crate libc;`) and exporting public objects from other Rust modules (e.g. `pub use mymodule::foo;`). For example, if -you create a crate in `.../src/rust/yourcrate`, your Rust code should -live in `.../src/rust/yourcrate/yourcode.rs` and the public interface -to it should be exported in `.../src/rust/yourcrate/lib.rs`. +you create a crate in `src/rust/yourcrate`, your Rust code should +live in `src/rust/yourcrate/yourcode.rs` and the public interface +to it should be exported in `src/rust/yourcrate/lib.rs`. If your code is to be called from Tor C code, you MUST define a safe `ffi.rs`. See the "Safety" section further down for more details. For example, in a hypothetical `tor_addition` Rust module: -In `.../src/rust/tor_addition/addition.rs`: +In `src/rust/tor_addition/addition.rs`: pub fn get_sum(a: i32, b: i32) -> i32 { a + b } -In `.../src/rust/tor_addition/lib.rs`: +In `src/rust/tor_addition/lib.rs`: pub use addition::*; -In `.../src/rust/tor_addition/ffi.rs`: +In `src/rust/tor_addition/ffi.rs`: #[no_mangle] pub extern "C" fn tor_get_sum(a: c_int, b: c_int) -> c_int { @@ -42,7 +39,7 @@ In `.../src/rust/tor_addition/ffi.rs`: If your Rust code must call out to parts of Tor's C code, you must declare the functions you are calling in the `external` crate, located -at `.../src/rust/external`. +at `src/rust/external`. <!-- XXX get better examples of how to declare these externs, when/how they --> <!-- XXX are unsafe, what they are expected to do —isis --> @@ -54,8 +51,7 @@ If you have any external modules as dependencies (e.g. `extern crate libc;`), you MUST declare them in your crate's `lib.rs` and NOT in any other module. - Dependencies and versions ---------------------------- +## Dependencies and versions In general, we use modules from only the Rust standard library whenever possible. We will review including external crates on a @@ -81,8 +77,7 @@ Currently, Tor requires that you use the latest stable Rust version. At some point in the future, we will freeze on a given stable Rust version, to ensure backward compatibility with stable distributions that ship it. - Updating/Adding Dependencies ------------------------------- +## Updating/Adding Dependencies To add/remove/update dependencies, first add your dependencies, exactly specifying their versions, into the appropriate *crate-level* @@ -101,14 +96,13 @@ Next, run `/scripts/maint/updateRustDependencies.sh`. Then, go into `src/ext/rust` and commit the changes to the `tor-rust-dependencies` repo. - Documentation ---------------- +## Documentation You MUST include `#![deny(missing_docs)]` in your crate. For function/method comments, you SHOULD include a one-sentence, "first person" description of function behaviour (see requirements for documentation as -described in `.../src/HACKING/CodingStandards.md`), then an `# Inputs` section +described in `src/HACKING/CodingStandards.md`), then an `# Inputs` section for inputs or initialisation values, a `# Returns` section for return values/types, a `# Warning` section containing warnings for unsafe behaviours or panics that could happen. For publicly accessible @@ -118,14 +112,12 @@ types/constants/objects/functions/methods, you SHOULD also include an You MUST document your module with _module docstring_ comments, i.e. `//!` at the beginning of each line. - Style -------- +## Style You SHOULD consider breaking up large literal numbers with `_` when it makes it more human readable to do so, e.g. `let x: u64 = 100_000_000_000`. - Testing ---------- +## Testing All code MUST be unittested and integration tested. @@ -134,7 +126,7 @@ describing how the function/object is expected to be used. Integration tests SHOULD go into a `tests/` directory inside your crate. Unittests SHOULD go into their own module inside the module -they are testing, e.g. in `.../src/rust/tor_addition/addition.rs` you +they are testing, e.g. in `src/rust/tor_addition/addition.rs` you should put: #[cfg(test)] @@ -148,8 +140,7 @@ should put: } } - Benchmarking --------------- +## Benchmarking The external `test` crate can be used for most benchmarking. However, using this crate requires nightly Rust. Since we may want to switch to a more @@ -173,7 +164,7 @@ for basic benchmarks, is only used when running benchmarks via `cargo bench --features bench`. Finally, to write your benchmark code, in -`.../src/rust/tor_addition/addition.rs` you SHOULD put: +`src/rust/tor_addition/addition.rs` you SHOULD put: #[cfg(all(test, features = "bench"))] mod bench { @@ -186,23 +177,20 @@ Finally, to write your benchmark code, in } } - Fuzzing ---------- +## Fuzzing If you wish to fuzz parts of your code, please see the -[`cargo fuzz`](https://github.com/rust-fuzz/cargo-fuzz) crate, which uses +[cargo fuzz](https://github.com/rust-fuzz/cargo-fuzz) crate, which uses [libfuzzer-sys](https://github.com/rust-fuzz/libfuzzer-sys). - Whitespace & Formatting -------------------------- +## Whitespace & Formatting You MUST run `rustfmt` (https://github.com/rust-lang-nursery/rustfmt) on your code before your code will be merged. You can install rustfmt by doing `cargo install rustfmt-nightly` and then run it with `cargo fmt`. - Safety --------- +## Safety You SHOULD read [the nomicon](https://doc.rust-lang.org/nomicon/) before writing Rust FFI code. It is *highly advised* that you read and write normal Rust code diff --git a/doc/HACKING/Fuzzing.md b/doc/HACKING/Fuzzing.md index c2db7e9853..41853a8a23 100644 --- a/doc/HACKING/Fuzzing.md +++ b/doc/HACKING/Fuzzing.md @@ -11,7 +11,6 @@ To run the fuzzing test cases in a deterministic fashion, use: This won't actually fuzz Tor! It will just run all the fuzz binaries on our existing set of testcases for the fuzzer. - ## Different kinds of fuzzing Right now we support three different kinds of fuzzer. @@ -51,7 +50,6 @@ But the fuzzing harness should crash if tor fails an assertion, triggers a bug, or accesses memory it shouldn't. This helps fuzzing frameworks detect "interesting" cases. - ## Guided Fuzzing with AFL There is no HTTPS, hash, or signature for American Fuzzy Lop's source code, so diff --git a/doc/HACKING/GettingStarted.md b/doc/HACKING/GettingStarted.md index 0c42404634..c2ca74d960 100644 --- a/doc/HACKING/GettingStarted.md +++ b/doc/HACKING/GettingStarted.md @@ -1,23 +1,19 @@ - -Getting started in Tor development -================================== +# Getting started in Tor development Congratulations! You've found this file, and you're reading it! This means that you might be interested in getting started in developing Tor. -(This guide is just about Tor itself--the small network program at the +(_This guide is just about Tor itself--the small network program at the heart of the Tor network--and not about all the other programs in the -whole Tor ecosystem.) - +whole Tor ecosystem._) If you are looking for a more bare-bones, less user-friendly information -dump of important information, you might like reading the "torguts" -documents linked to below. You should probably read it before you write +dump of important information, you might like reading the +[doxygen output](https://src-ref.docs.torproject.org/tor/index.html). +You probably should skim some of the topic headings there before you write your first patch. - -Required background -------------------- +## Required background First, I'm going to assume that you can build Tor from source, and that you know enough of the C language to read and write it. (See the README @@ -26,22 +22,20 @@ and any high-quality guide to C for information on programming.) I'm also going to assume that you know a little bit about how to use Git, or that you're able to follow one of the several excellent guides -at http://git-scm.org to learn. +at [git-scm](http://git-scm.org) to learn. -Most Tor developers develop using some Unix-based system, such as Linux, -BSD, or OSX. It's okay to develop on Windows if you want, but you're +Most Tor developers develop using some Unix-based system, such as GNU/Linux, +BSD, or macOS. It's okay to develop on Windows if you want, but you're going to have a more difficult time. - -Getting your first patch into Tor ---------------------------------- +## Getting your first patch into Tor Once you've reached this point, here's what you need to know. 1. Get the source. We keep our source under version control in Git. To get the latest - version, run + version, run: git clone https://git.torproject.org/git/tor @@ -49,20 +43,18 @@ Once you've reached this point, here's what you need to know. going to fix a bug that appears in a stable version, check out the appropriate "maint" branch, as in: - git checkout maint-0.2.7 - - 2. Find your way around the source + git checkout maint-0.4.3 - Our overall code structure is explained in the "torguts" documents, - currently at + 2. Find your way around the source. - git clone https://git.torproject.org/user/nickm/torguts.git + Our overall code structure is explained in our + [source documentation](https://src-ref.docs.torproject.org/tor/index.html). Find a part of the code that looks interesting to you, and start looking around it to see how it fits together! We do some unusual things in our codebase. Our testing-related - practices and kludges are explained in doc/WritingTests.txt. + practices and kludges are explained in `doc/HACKING/WritingTests.md`. If you see something that doesn't make sense, we love to get questions! @@ -74,10 +66,10 @@ Once you've reached this point, here's what you need to know. Many people have gotten started by looking for an area where they personally felt Tor was underperforming, and investigating ways to - fix it. If you're looking for ideas, you can head to our bug - tracker at trac.torproject.org and look for tickets that have - received the "easy" tag: these are ones that developers think would - be pretty simple for a new person to work on. For a bigger + fix it. If you're looking for ideas, you can head to + [trac](https://trac.torproject.org) our bug tracking tool and look for + tickets that have received the "easy" tag: these are ones that developers + think would be pretty simple for a new person to work on. For a bigger challenge, you might want to look for tickets with the "lorax" keyword: these are tickets that the developers think might be a good idea to build, but which we have no time to work on any time @@ -96,7 +88,7 @@ Once you've reached this point, here's what you need to know. 4. Meet the developers! - We discuss stuff on the tor-dev mailing list and on the #tor-dev + We discuss stuff on the tor-dev mailing list and on the `#tor-dev` IRC channel on OFTC. We're generally friendly and approachable, and we like to talk about how Tor fits together. If we have ideas about how something should be implemented, we'll be happy to share @@ -113,8 +105,8 @@ Once you've reached this point, here's what you need to know. protocols, there needs to be a written design proposal before it can be merged. (We use this process to manage changes in the protocols.) To write one, see the instructions at - https://gitweb.torproject.org/torspec.git/tree/proposals/001-process.txt - . If you'd like help writing a proposal, just ask! We're happy to + [the Tor proposal process](https://gitweb.torproject.org/torspec.git/plain/proposals/001-process.txt). + If you'd like help writing a proposal, just ask! We're happy to help out with good ideas. You might also like to look around the rest of that directory, to @@ -125,7 +117,7 @@ Once you've reached this point, here's what you need to know. As you write your code, you'll probably want it to fit in with the standards of the rest of the Tor codebase so it will be easy for us to review and merge. You can learn our coding standards in - doc/HACKING. + `doc/HACKING` directory. If your patch is large and/or is divided into multiple logical components, remember to divide it into a series of Git commits. A @@ -137,16 +129,16 @@ Once you've reached this point, here's what you need to know. ensure that it runs correctly. Also, all code should actually be _run_ by somebody, to make sure it works. - See doc/WritingTests.txt for more information on how we test things + See `doc/HACKING/WritingTests.md` for more information on how we test things in Tor. If you'd like any help writing tests, just ask! We're glad to help out. 8. Submitting your patch We review patches through tickets on our bugtracker at - trac.torproject.org. You can either upload your patches there, or + [trac](https://trac.torproject.org). You can either upload your patches there, or put them at a public git repository somewhere we can fetch them - (like github or bitbucket) and then paste a link on the appropriate + (like gitlab, github or bitbucket) and then paste a link on the appropriate trac ticket. Once your patches are available, write a short explanation of what @@ -163,17 +155,17 @@ Once you've reached this point, here's what you need to know. When your patch is reviewed, one of these things will happen: - * The reviewer will say "looks good to me" and your + * The reviewer will say "_looks good to me_" and your patch will get merged right into Tor. [Assuming we're not in the middle of a code-freeze window. If the codebase is frozen, your patch will go into the next release series.] - * OR the reviewer will say "looks good, just needs some small - changes!" And then the reviewer will make those changes, + * OR the reviewer will say "_looks good, just needs some small + changes!_" And then the reviewer will make those changes, and merge the modified patch into Tor. - * OR the reviewer will say "Here are some questions and - comments," followed by a bunch of stuff that the reviewer + * OR the reviewer will say "_Here are some questions and + comments,_" followed by a bunch of stuff that the reviewer thinks should change in your code, or questions that the reviewer has. diff --git a/doc/HACKING/GettingStartedRust.md b/doc/HACKING/GettingStartedRust.md index aa29c097da..247ea5c695 100644 --- a/doc/HACKING/GettingStartedRust.md +++ b/doc/HACKING/GettingStartedRust.md @@ -1,12 +1,9 @@ +# Hacking on Rust in Tor - Hacking on Rust in Tor -======================== - - Getting Started ------------------ +## Getting Started Please read or review our documentation on Rust coding standards -(`.../doc/HACKING/CodingStandardsRust.md`) before doing anything. +(`doc/HACKING/CodingStandardsRust.md`) before doing anything. Please also read [the Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). We @@ -23,8 +20,7 @@ Please be patient with the other people who are working on getting more Rust code into Tor, because they are graciously donating their free time to contribute to this effort. - Resources for learning Rust ------------------------------ +## Resources for learning Rust **Beginning resources** @@ -49,8 +45,7 @@ is For learning more about FFI and Rust, see Jake Goulding's [Rust FFI Omnibus](http://jakegoulding.com/rust-ffi-omnibus/). - Compiling Tor with Rust enabled ---------------------------------- +## Compiling Tor with Rust enabled You will need to run the `configure` script with the `--enable-rust` flag to explicitly build with Rust. Additionally, you will need to @@ -79,7 +74,7 @@ you are in the top level of the repository) configure tor with: TOR_RUST_DEPENDENCIES='path_to_dependencies_directory' ./configure --enable-rust -(Note that TOR_RUST_DEPENDENCIES must be the full path to the directory; it +(Note that `TOR_RUST_DEPENDENCIES` must be the full path to the directory; it cannot be relative.) Assuming you used the above `git submodule` commands and you're in the @@ -87,9 +82,7 @@ topmost directory of the repository, this would be: TOR_RUST_DEPENDENCIES=`pwd`/src/ext/rust/crates ./configure --enable-rust - - Identifying which modules to rewrite -====================================== +## Identifying which modules to rewrite The places in the Tor codebase that are good candidates for porting to Rust are: @@ -117,12 +110,11 @@ interconnected your target module is. The output will tell you each module name, along with a set of every module that the module calls. Modules which call fewer other modules are better targets. - Writing your Rust module -========================== +## Writing your Rust module Strive to change the C API as little as possible. -We are currently targetting Rust stable. (See CodingStandardsRust.md for more +We are currently targetting Rust stable. (See `CodingStandardsRust.md` for more details.) It is on our TODO list to try to cultivate good @@ -134,19 +126,17 @@ If parts of your Rust code needs to stay in sync with C code (such as handling enums across the FFI boundary), annonotate these places in a comment structured as follows: - /// C_RUST_COUPLED: <path_to_file> `<name_of_c_object>` + `/// C_RUST_COUPLED: <path_to_file> <name_of_c_object>` -Where <name_of_c_object> can be an enum, struct, constant, etc. Then, +Where `<name_of_c_object>` can be an enum, struct, constant, etc. Then, do the same in the C code, to note that rust will need to be changed when the C does. - - Adding your Rust module to Tor's build system ------------------------------------------------ +## Adding your Rust module to Tor's build system 0. Your translation of the C module should live in its own crate(s) - in the `.../tor/src/rust/` directory. -1. Add your crate to `.../tor/src/rust/Cargo.toml`, in the + in the `src/rust/` directory. +1. Add your crate to `src/rust/Cargo.toml`, in the `[workspace.members]` section. 2. Add your crate's files to src/rust/include.am @@ -156,12 +146,11 @@ dependency of other Rust modules): `src/rust/tor_util/Cargo.toml` and include it in `src/rust/tor_rust/lib.rs` - How to test your Rust code ----------------------------- +## How to test your Rust code Everything should be tested full stop. Even non-public functionality. -Be sure to edit `.../tor/src/test/test_rust.sh` to add the name of your +Be sure to edit `src/test/test_rust.sh` to add the name of your crate to the `crates` variable! This will ensure that `cargo test` is run on your crate. @@ -177,7 +166,6 @@ Tor's integration tests should also pass: make test-stem - Submitting a patch -===================== +## Submitting a patch -Please follow the instructions in `.../doc/HACKING/GettingStarted.md`. +Please follow the instructions in `doc/HACKING/GettingStarted.md`. diff --git a/doc/HACKING/HelpfulTools.md b/doc/HACKING/HelpfulTools.md index ae892c34a2..15bd153318 100644 --- a/doc/HACKING/HelpfulTools.md +++ b/doc/HACKING/HelpfulTools.md @@ -1,11 +1,10 @@ -Useful tools -============ +# Useful tools These aren't strictly necessary for hacking on Tor, but they can help track down bugs. -Travis/Appveyor CI ------------------- +## Travis/Appveyor CI + It's CI. Looks like this: @@ -29,8 +28,7 @@ for your fork to build commits outside of PRs too: Builds should show up on the web at travis-ci.com and on IRC at #tor-ci on OFTC. If they don't, ask #tor-dev (also on OFTC). -Jenkins -------- +## Jenkins It's CI/builders. Looks like this: https://jenkins.torproject.org @@ -43,8 +41,7 @@ Builds Linux and Windows cross-compilation. Runs Linux tests. Builds should show up on the web at jenkins.torproject.org and on IRC at #tor-bots on OFTC. If they don't, ask #tor-dev (also on OFTC). -Valgrind --------- +## Valgrind valgrind --leak-check=yes --error-limit=no --show-reachable=yes src/app/tor @@ -52,16 +49,14 @@ Valgrind pass `--undef-value-errors=no` to valgrind, or rebuild your openssl with `-DPURIFY`.) -Coverity --------- +## Coverity Nick regularly runs the coverity static analyzer on the Tor codebase. The preprocessor define `__COVERITY__` is used to work around instances where coverity picks up behavior that we wish to permit. -clang Static Analyzer ---------------------- +## clang Static Analyzer The clang static analyzer can be run on the Tor codebase using Xcode (WIP) or a command-line build. @@ -69,8 +64,7 @@ or a command-line build. The preprocessor define `__clang_analyzer__` is used to work around instances where clang picks up behavior that we wish to permit. -clang Runtime Sanitizers ------------------------- +## clang Runtime Sanitizers To build the Tor codebase with the clang Address and Undefined Behavior sanitizers, see the file `contrib/clang/sanitize_blacklist.txt`. @@ -78,8 +72,7 @@ sanitizers, see the file `contrib/clang/sanitize_blacklist.txt`. Preprocessor workarounds for instances where clang picks up behavior that we wish to permit are also documented in the blacklist file. -Running lcov for unit test coverage ------------------------------------ +## Running lcov for unit test coverage Lcov is a utility that generates pretty HTML reports of test code coverage. To generate such a report: @@ -96,8 +89,7 @@ output directory, use `make coverage-html HTML_COVER_DIR=./funky_new_cov_dir`. Coverage diffs using lcov are not currently implemented, but are being investigated (as of July 2014). -Running the unit tests ----------------------- +## Running the unit tests To quickly run all the tests distributed with Tor: @@ -123,8 +115,7 @@ working connection to the internet: make test-full-online -Running gcov for unit test coverage ------------------------------------ +## Running gcov for unit test coverage ./configure --enable-coverage make @@ -164,8 +155,7 @@ lines? If you run ./scripts/test/cov-exclude, it marks excluded unreached lines with 'x', and excluded reached lines with '!!!'. -Running integration tests -------------------------- +## Running integration tests We have the beginnings of a set of scripts to run integration tests using Chutney. To try them, set CHUTNEY_PATH to your chutney source directory, and @@ -174,14 +164,12 @@ run `make test-network`. We also have scripts to run integration tests using Stem. To try them, set `STEM_SOURCE_DIR` to your Stem source directory, and run `test-stem`. -Profiling Tor -------------- +## Profiling Tor Ongoing notes about Tor profiling can be found at https://pad.riseup.net/p/profiling-tor -Profiling Tor with oprofile ---------------------------- +## Profiling Tor with oprofile The oprofile tool runs (on Linux only!) to tell you what functions Tor is spending its CPU time in, so we can identify performance bottlenecks. @@ -206,8 +194,7 @@ Here are some basic instructions * `opreport -l that_dir/*` - Profit -Profiling Tor with perf ------------------------ +## Profiling Tor with perf This works with a running Tor, and requires root. @@ -236,8 +223,7 @@ This works with a running Tor, and requires root. report -g > <FILENAME>.out`. Then you can compress that and put it somewhere public. -Profiling Tor with gperftools aka Google-performance-tools ----------------------------------------------------------- +## Profiling Tor with gperftools aka Google-performance-tools This should work on nearly any unixy system. It doesn't seem to be compatible with RunAsDaemon though. @@ -255,8 +241,7 @@ performance! See the gperftools manual for more info, but basically: 3. Run `pprof src/app/tor /tmp/profile` to start the REPL. -Generating and analyzing a callgraph ------------------------------------- +## Generating and analyzing a callgraph 0. Build Tor on linux or mac, ideally with -O0 or -fno-inline. @@ -266,8 +251,7 @@ Generating and analyzing a callgraph Note that currently the callgraph generator can't detect calls that pass through function pointers. -Getting emacs to edit Tor source properly ------------------------------------------ +## Getting emacs to edit Tor source properly Nick likes to put the following snippet in his .emacs file: @@ -315,8 +299,7 @@ If you use emacs for editing Tor and nothing else, you could always just say: There is probably a better way to do this. No, we are probably not going to clutter the files with emacs stuff. -Building a tag file (code index) --------------------------------- +## Building a tag file (code index) Many functions in tor use `MOCK_IMPL` wrappers for unit tests. Your tag-building program must be told how to handle this syntax. @@ -340,8 +323,7 @@ instead: A vim-compatible tag file will be generated by default. If you use emacs, add the `-e` flag to generate an emacs-compatible tag file. -Doxygen -------- +## Doxygen We use the 'doxygen' utility to generate documentation from our source code. Here's how to use it: @@ -396,8 +378,7 @@ source code. Here's how to use it: 6. See the Doxygen manual for more information; this summary just scratches the surface. -Style and best-practices checking --------------------------------- +## Style and best-practices checking We use scripts to check for various problems in the formatting and style of our source code. The "check-spaces" test detects a bunch of violations diff --git a/doc/HACKING/HowToReview.md b/doc/HACKING/HowToReview.md index 2325e70175..7815e76632 100644 --- a/doc/HACKING/HowToReview.md +++ b/doc/HACKING/HowToReview.md @@ -1,5 +1,4 @@ -How to review a patch -===================== +# How to review a patch Some folks have said that they'd like to review patches more often, but they don't know how. @@ -9,9 +8,7 @@ So, here are a bunch of things to check for when reviewing a patch! Note that if you can't do every one of these, that doesn't mean you can't do a good review! Just make it clear what you checked for and what you didn't. - -Top-level smell-checks ----------------------- +## Top-level smell-checks (Difficulty: easy) @@ -37,10 +34,9 @@ Top-level smell-checks - If this changes anything in the code, is there a "changes" file? -Let's look at the code! ------------------------ +## Let's look at the code! -- Does the code conform to CodingStandards.txt? +- Does the code conform to `CodingStandards.md`? - Does the code leak memory? @@ -60,8 +56,7 @@ Let's look at the code! - Is there duplicated code that could be turned into a function? -Let's look at the documentation! --------------------------------- +## Let's look at the documentation! - Does the documentation conform to CodingStandards.txt? @@ -70,8 +65,7 @@ Let's look at the documentation! - Can you predict what the function will do from its documentation? -Let's think about security! ---------------------------- +## Let's think about security! - If there are any arrays, buffers, are you 100% sure that they cannot overflow? diff --git a/doc/HACKING/Module.md b/doc/HACKING/Module.md index 781bb978f2..f8a9773d47 100644 --- a/doc/HACKING/Module.md +++ b/doc/HACKING/Module.md @@ -1,9 +1,9 @@ -# Modules in Tor # +# Modules in Tor This document describes the build system and coding standards when writing a module in Tor. -## What is a module? ## +## What is a module? In the context of the tor code base, a module is a subsystem that we can selectively enable or disable, at `configure` time. @@ -27,7 +27,7 @@ because you would actually want to run one without the other.) To disable a module, pass `--disable-module-{dirauth,relay}` at configure time. All modules are currently enabled by default. -## Build System ## +## Build System The changes to the build system are pretty straightforward. @@ -55,7 +55,7 @@ Finally, your module will automatically be included in the `TOR_MODULES_ALL_ENABLED` variable which is used to build the unit tests. They always build everything in order to test everything. -## Coding ## +## Coding As mentioned above, a module should be isolated in its own directories, suffixed with the name of the module, in `src/*/`. diff --git a/doc/HACKING/README.1st.md b/doc/HACKING/README.1st.md index 8299fe634a..2278a61d6c 100644 --- a/doc/HACKING/README.1st.md +++ b/doc/HACKING/README.1st.md @@ -1,17 +1,18 @@ +# README.1st -In this directory ------------------ +## In this directory This directory has helpful information about what you need to know to hack on Tor! -First, read `GettingStarted.md` to learn how to get a start in Tor -development. +First, read `GettingStarted.md` and `GettingStartedRust.md` +to learn how to get a start in Tor development. -If you've decided to write a patch, `CodingStandards.txt` will give -you a bunch of information about how we structure our code. +If you've decided to write a patch, `CodingStandards.md` and +`CodingStandardsRust.md` will give you a bunch of information +about how we structure our code. -It's important to get code right! Reading `WritingTests.md` will +It's important to get the code right! Reading `WritingTests.md` will tell you how to write and run tests in the Tor codebase. There are a bunch of other programs we use to help maintain and @@ -21,42 +22,26 @@ with Tor. If it's your job to put out Tor releases, see `ReleasingTor.md` so that you don't miss any steps! - ------------------------ +## Additional Information For full information on how Tor is supposed to work, look at the files in -`https://gitweb.torproject.org/torspec.git/tree`. +[Tor specification](https://gitweb.torproject.org/torspec.git/tree). For an explanation of how to change Tor's design to work differently, look at -`https://gitweb.torproject.org/torspec.git/blob_plain/HEAD:/proposals/001-process.txt`. +[the Tor proposal process](https://gitweb.torproject.org/torspec.git/plain/proposals/001-process.txt). For the latest version of the code, get a copy of git, and git clone https://git.torproject.org/git/tor +## Stay in touch + We talk about Tor on the `tor-talk` mailing list. Design proposals and discussion belong on the `tor-dev` mailing list. We hang around on -irc.oftc.net, with general discussion happening on #tor and development +irc.oftc.net, with general discussion happening on `#tor` and development happening on `#tor-dev`. The other files in this `HACKING` directory may also be useful as you get started working with Tor. Happy hacking! - - ------------------------ - -XXXXX also describe - -doc/HACKING/WritingTests.md - -torguts.git - -torspec.git - -The design paper - -freehaven.net/anonbib - -XXXX describe these and add links. diff --git a/doc/HACKING/ReleasingTor.md b/doc/HACKING/ReleasingTor.md index 0f453ca2aa..2464d8afb4 100644 --- a/doc/HACKING/ReleasingTor.md +++ b/doc/HACKING/ReleasingTor.md @@ -1,11 +1,9 @@ - -Putting out a new release -------------------------- +# Putting out a new release Here are the steps that the maintainer should take when putting out a new Tor release: -### 0. Preliminaries +## 0. Preliminaries 1. Get at least two of weasel/arma/Sebastian to put the new version number in their approved versions list. Give them a few @@ -18,7 +16,7 @@ new Tor release: date of a TB that contains it. See note below in "commit, upload, announce". -### I. Make sure it works +## I. Make sure it works 1. Make sure that CI passes: have a look at Travis (https://travis-ci.org/torproject/tor/branches), Appveyor @@ -51,9 +49,7 @@ new Tor release: libevent and openssl, so using valgrind will sometimes find extra memory leaks.) - -### II. Write a changelog - +## II. Write a changelog 1a. (Alpha release variant) @@ -138,8 +134,7 @@ new Tor release: to start sorting and condensing entries. (Generally, we don't edit the text of existing entries, though.) - -### III. Making the source release. +## III. Making the source release. 1. In `maint-0.?.x`, bump the version number in `configure.ac` and run `make update-versions` to update version numbers in other @@ -165,7 +160,7 @@ new Tor release: If it is not, you'll need to poke Roger, Weasel, and Sebastian again: see item 0.1 at the start of this document. -### IV. Commit, upload, announce +## IV. Commit, upload, announce 1. Sign the tarball, then sign and push the git tag: @@ -215,7 +210,6 @@ new Tor release: Include a link to the changelog. - 4. Add the version number to Trac. To do this, go to Trac, log in, select "Admin" near the top of the screen, then select "Versions" from the menu on the left. At the right, there will be an "Add version" @@ -241,7 +235,7 @@ new Tor release: For templates to use when announcing, see: https://trac.torproject.org/projects/tor/wiki/org/teams/NetworkTeam/AnnouncementTemplates -### V. Aftermath and cleanup +## V. Aftermath and cleanup 1. If it's a stable release, bump the version number in the `maint-x.y.z` branch to "newversion-dev", and do a `merge -s ours` diff --git a/doc/HACKING/Tracing.md b/doc/HACKING/Tracing.md index 24fa761310..e1e97abe6d 100644 --- a/doc/HACKING/Tracing.md +++ b/doc/HACKING/Tracing.md @@ -1,16 +1,16 @@ -# Tracing # +# Tracing This document describes how the event tracing subsystem works in tor so developers can add events to the code base but also hook them to an event tracing framework. -## Basics ### +## Basics Event tracing is separated in two concepts, trace events and a tracer. The tracing subsystem can be found in `src/trace`. The `events.h` header file is the main file that maps the different tracers to trace events. -### Events ### +### Events A trace event is basically a function from which we can pass any data that we want to collect. In addition, we specify a context for the event such as @@ -39,7 +39,7 @@ How `argc` is collected or used has nothing to do with the instrumentation the trace events and collection framework (tracer) are decoupled. You _can_ have trace events without a tracer. -### Tracer ### +### Tracer In `src/trace/events.h`, we map the `tor_trace()` function to the right tracer. A tracer support is only enabled at compile time. For instance, the @@ -47,7 +47,7 @@ file `src/trace/debug.h` contains the mapping of the generic tracing function `tor_trace()` to the `log_debug()` function. More specialized function can be mapped depending on the tracepoint. -## Build System ## +## Build System This section describes how it is integrated into the build system of tor. @@ -66,7 +66,7 @@ configure option: --enable-tracing-debug -## Instrument Tor ## +## Instrument Tor This is pretty easy. Let's say you want to add a trace event in `src/feature/rend/rendcache.c`, you only have to add this include statement: diff --git a/doc/HACKING/WritingTests.md b/doc/HACKING/WritingTests.md index 05de8e0be8..d212020525 100644 --- a/doc/HACKING/WritingTests.md +++ b/doc/HACKING/WritingTests.md @@ -1,6 +1,4 @@ - -Writing tests for Tor: an incomplete guide -========================================== +# Writing tests for Tor: an incomplete guide Tor uses a variety of testing frameworks and methodologies to try to keep from introducing bugs. The major ones are: @@ -19,8 +17,7 @@ keep from introducing bugs. The major ones are: 5. The Shadow network simulator. -How to run these tests ----------------------- +## How to run these tests ### The easy version @@ -64,7 +61,7 @@ The former are those that should finish in a few seconds; the latter tend to take more time, and may include CPU-intensive operations, deliberate delays, and stuff like that. -### Finding test coverage +## Finding test coverage Test coverage is a measurement of which lines your tests actually visit. @@ -112,7 +109,7 @@ To count new or modified uncovered lines in D2, you can run: ./scripts/test/cov-diff ${D1} ${D2}" | grep '^+ *\#' | wc -l -### Marking lines as unreachable by tests +## Marking lines as unreachable by tests You can mark a specific line as unreachable by using the special string LCOV_EXCL_LINE. You can mark a range of lines as unreachable @@ -126,9 +123,7 @@ unreached lines with 'x', and excluded reached lines with '!!!'. Note: you should never do this unless the line is meant to 100% unreachable by actual code. - -What kinds of test should I write? ----------------------------------- +## What kinds of test should I write? Integration testing and unit testing are complementary: it's probably a good idea to make sure that your code is hit by both if you can. @@ -143,8 +138,7 @@ If your code adds new externally visible functionality to Tor, it would be great to have a test for that functionality. That's where integration tests more usually come in. -Unit and regression tests: Does this function do what it's supposed to? ------------------------------------------------------------------------ +## Unit and regression tests: Does this function do what it's supposed to? Most of Tor's unit tests are made using the "tinytest" testing framework. You can see a guide to using it in the tinytest manual at @@ -165,7 +159,7 @@ If you have created a new test file, you will need to: I use the term "unit test" and "regression tests" very sloppily here. -### A simple example +## A simple example Here's an example of a test function for a simple function in util.c: @@ -207,7 +201,7 @@ Finally, remember that by convention, all `*_free()` functions that Tor defines are defined to accept NULL harmlessly. Thus, you don't need to say `if (contents)` in the cleanup block. -### Exposing static functions for testing +## Exposing static functions for testing Sometimes you need to test a function, but you don't want to expose it outside its usual module. @@ -228,7 +222,7 @@ For example, `crypto_curve25519.h` contains: The `crypto_curve25519.c` file and the `test_crypto.c` file both define `CRYPTO_CURVE25519_PRIVATE`, so they can see this declaration. -### STOP! Does this test really test? +## STOP! Does this test really test? When writing tests, it's not enough to just generate coverage on all the lines of the code that you're testing: It's important to make sure that @@ -269,8 +263,7 @@ it's supposed to do, and fail otherwise. Try to design your tests so that they check for the code's intended and documented functionality as much as possible. - -### Mock functions for testing in isolation +## Mock functions for testing in isolation Often we want to test that a function works right, but the function to be tested depends on other functions whose behavior is hard to observe, @@ -311,7 +304,7 @@ And later, you can restore the original function with: For more information, see the definitions of this mocking logic in `testsupport.h`. -### Okay but what should my tests actually do? +## Okay but what should my tests actually do? We talk above about "test coverage" -- making sure that your tests visit every line of code, or every branch of code. But visiting the code isn't @@ -382,8 +375,7 @@ Based on the implementation, we now see three more edge cases to test: * Removing an element from the end of the list * Removing an element from a position other than the end of the list. - -### What should my tests NOT do? +## What should my tests NOT do? Tests shouldn't require a network connection. @@ -401,8 +393,7 @@ When possible, tests should not be over-fit to the implementation. That is, the test should verify that the documented behavior is implemented, but should not break if other permissible behavior is later implemented. - -### Advanced techniques: Namespaces +## Advanced techniques: Namespaces Sometimes, when you're doing a lot of mocking at once, it's convenient to isolate your identifiers within a single namespace. If this were C++, we'd @@ -414,9 +405,7 @@ them, you define `NS_MODULE` to a prefix to be used for your identifiers, and then use other macros in place of identifier names. See `src/test/test.h` for more documentation. - -Integration tests: Calling Tor from the outside ------------------------------------------------ +## Integration tests: Calling Tor from the outside Some tests need to invoke Tor from the outside, and shouldn't run from the same process as the Tor test program. Reasons for doing this might include: @@ -436,8 +425,7 @@ wrapped, add a new shell script to `TESTS`, and the new program to makefile (eg `${PYTHON}` for a python interpreter), then make sure that the makefile exports them. -Writing integration tests with Stem ------------------------------------ +## Writing integration tests with Stem The 'stem' library includes extensive tests for the Tor controller protocol. You can run stem tests from tor with `make test-stem`, or see @@ -483,8 +471,7 @@ you notice any strange behaviour that seems totally unreasonable. Check out the `test_exit_policy()` function in abovementioned file to see the final implementation for this test. -System testing with Chutney ---------------------------- +## System testing with Chutney The 'chutney' program configures and launches a set of Tor relays, authorities, and clients on your local host. It has a `test network` diff --git a/doc/HACKING/android/Simpleperf.md b/doc/HACKING/android/Simpleperf.md index 25f39a3d23..c7e63a7c86 100644 --- a/doc/HACKING/android/Simpleperf.md +++ b/doc/HACKING/android/Simpleperf.md @@ -95,4 +95,3 @@ was spend on the call. Start Tor the normal way via Orbot and collect the logs from your computer using $ adb logcat - diff --git a/doc/tor.1.txt b/doc/tor.1.txt index db4dd2755a..5b55b39d47 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -9,12 +9,12 @@ // attribute to make it easier to write names containing double underscores :dbl_: __ = TOR(1) +:toc: == NAME tor - The second-generation onion router - == SYNOPSIS **tor** [__OPTION__ __value__]... @@ -158,7 +158,7 @@ The following options in this section are only recognized on the When generating a master key, you may want to use **`--DataDirectory`** to control where the keys and certificates will be stored, and **`--SigningKeyLifetime`** to control their - lifetimes. See the server options section to learn more about the + lifetimes. See <<server-options,SERVER OPTIONS>> to learn more about the behavior of these options. You must have write access to the specified DataDirectory. + @@ -427,7 +427,7 @@ forward slash (/) in the configuration file and on the command line. [[CookieAuthFile]] **CookieAuthFile** __Path__:: If set, this option overrides the default location and file name - for Tor's cookie file. (See CookieAuthentication above.) + for Tor's cookie file. (See <<CookieAuthentication,CookieAuthentication>>.) [[CookieAuthFileGroupReadable]] **CookieAuthFileGroupReadable** **0**|**1**:: If this option is set to 0, don't allow the filesystem group to read the @@ -561,7 +561,7 @@ forward slash (/) in the configuration file and on the command line. + By default, the directory authorities are also FallbackDirs. Specifying a FallbackDir replaces Tor's default hard-coded FallbackDirs (if any). - (See the **DirAuthority** entry for an explanation of each flag.) + (See <<DirAuthority,DirAuthority>> for an explanation of each flag.) [[FetchDirInfoEarly]] **FetchDirInfoEarly** **0**|**1**:: If set to 1, Tor will always fetch directory information like other @@ -760,6 +760,11 @@ forward slash (/) in the configuration file and on the command line. This setting will be ignored for connections to the loopback addresses (127.0.0.0/8 and ::1). +[[OwningControllerProcess]] **{dbl_}OwningControllerProcess** __PID__:: + Make Tor instance periodically check for presence of a controller process + with given PID and terminate itself if this process is no longer alive. + Polling interval is 15 seconds. + [[PerConnBWBurst]] **PerConnBWBurst** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: If this option is set manually, or via the "perconnbwburst" consensus field, Tor will use it for separate rate limiting for each connection @@ -859,7 +864,7 @@ forward slash (/) in the configuration file and on the command line. **KIST**: Kernel-Informed Socket Transport. Tor will use TCP information from the kernel to make informed decisions regarding how much data to send and when to send it. KIST also handles traffic in batches (see - KISTSchedRunInterval) in order to improve traffic prioritization decisions. + <<KISTSchedRunInterval,KISTSchedRunInterval>>) in order to improve traffic prioritization decisions. As implemented, KIST will only work on Linux kernel version 2.6.39 or higher. + + @@ -1018,13 +1023,6 @@ The following options are useful only for clients (that is, if via the UI to mobile users for use where bandwidth may be expensive. (Default: 0) -[[ClientAutoIPv6ORPort]] **ClientAutoIPv6ORPort** **0**|**1**:: - If this option is set to 1, Tor clients randomly prefer a node's IPv4 or - IPv6 ORPort. The random preference is set every time a node is loaded - from a new consensus or bridge config. When this option is set to 1, - **ClientPreferIPv6ORPort** is ignored. (Default: 0) (DEPRECATED: This - option is unreliable if a connection isn't reliably dual-stack.) - [[ClientBootstrapConsensusAuthorityDownloadInitialDelay]] **ClientBootstrapConsensusAuthorityDownloadInitialDelay** __N__:: Initial delay in seconds for when clients should download consensuses from authorities if they are bootstrapping (that is, they don't have a usable, reasonably @@ -1140,7 +1138,7 @@ The following options are useful only for clients (that is, if doesn't handle arbitrary DNS request types. Set the port to "auto" to have Tor pick a port for you. This directive can be specified multiple times to bind to multiple - addresses/ports. See SocksPort for an explanation of isolation + addresses/ports. See <<SocksPort,SocksPort>> for an explanation of isolation flags. (Default: 0) [[DownloadExtraInfo]] **DownloadExtraInfo** **0**|**1**:: @@ -1156,7 +1154,7 @@ The following options are useful only for clients (that is, if [[FascistFirewall]] **FascistFirewall** **0**|**1**:: If 1, Tor will only create outgoing connections to ORs running on ports - that your firewall allows (defaults to 80 and 443; see **FirewallPorts**). + that your firewall allows (defaults to 80 and 443; see <<FirewallPorts,FirewallPorts>>). This will allow you to run Tor as a client behind a firewall with restrictive policies, but will not allow you to run as a server behind such a firewall. If you prefer more fine-grained control, use @@ -1185,7 +1183,7 @@ The following options are useful only for clients (that is, if specified multiple times to bind to multiple addresses/ports. If multiple entries of this option are present in your configuration file, Tor will perform stream isolation between listeners by default. See - SOCKSPort for an explanation of isolation flags. (Default: 0) + <<SocksPort,SocksPort>> for an explanation of isolation flags. (Default: 0) [[LongLivedPorts]] **LongLivedPorts** __PORTS__:: A list of ports for services that tend to have long-running connections @@ -1270,7 +1268,7 @@ The following options are useful only for clients (that is, if specified multiple times to bind to multiple addresses/ports. If multiple entries of this option are present in your configuration file, Tor will perform stream isolation between listeners by default. See - SocksPort for an explanation of isolation flags. + + <<SocksPort,SocksPort>> for an explanation of isolation flags. + + This option is only for people who cannot use TransPort. (Default: 0) @@ -1381,7 +1379,7 @@ The following options are useful only for clients (that is, if + The separation between **ReachableORAddresses** and **ReachableDirAddresses** is only interesting when you are connecting - through proxies (see **HTTPProxy** and **HTTPSProxy**). Most proxies limit + through proxies (see <<HTTPProxy,HTTPProxy>> and <<HTTPSProxy,HTTPSProxy>>). Most proxies limit TLS connections (which Tor uses to connect to Onion Routers) to port 443, and some limit HTTP GET requests (which Tor uses for fetching directory information) to port 80. @@ -1397,7 +1395,7 @@ The following options are useful only for clients (that is, if [[TestSocks]] **TestSocks** **0**|**1**:: When this option is enabled, Tor will make a notice-level log entry for each connection to the Socks port indicating whether the request used a - safe socks protocol or an unsafe one (see above entry on SafeSocks). This + safe socks protocol or an unsafe one (see <<SafeSocks,SafeSocks>>). This helps to determine whether an application using Tor is possibly leaking DNS requests. (Default: 0) @@ -1627,7 +1625,7 @@ The following options are useful only for clients (that is, if specified multiple times to bind to multiple addresses/ports. If multiple entries of this option are present in your configuration file, Tor will perform stream isolation between listeners by default. See - SOCKSPort for an explanation of isolation flags. + + <<SocksPort,SocksPort>> for an explanation of isolation flags. + + TransPort requires OS support for transparent proxies, such as BSDs' pf or Linux's IPTables. If you're planning to use Tor as a transparent proxy for @@ -1842,7 +1840,7 @@ different from other Tor clients: + The ExcludeNodes option overrides this option: any node listed in both EntryNodes and ExcludeNodes is treated as excluded. See - the **ExcludeNodes** option for more information on how to specify nodes. + <<ExcludeNodes,ExcludeNodes>> for more information on how to specify nodes. [[ExcludeNodes]] **ExcludeNodes** __node__,__node__,__...__:: A list of identity fingerprints, country codes, and address @@ -1866,7 +1864,7 @@ different from other Tor clients: + Country codes are case-insensitive. The code "\{??}" refers to nodes whose country can't be identified. No country code, including \{??}, works if - no GeoIPFile can be loaded. See also the GeoIPExcludeUnknown option below. + no GeoIPFile can be loaded. See also the <<GeoIPExcludeUnknown,GeoIPExcludeUnknown>> option below. // Out of order because it logically belongs after the ExcludeNodes option [[ExcludeExitNodes]] **ExcludeExitNodes** __node__,__node__,__...__:: @@ -1875,14 +1873,14 @@ different from other Tor clients: node that delivers traffic for you *outside* the Tor network. Note that any node listed in ExcludeNodes is automatically considered to be part of this list too. See - the **ExcludeNodes** option for more information on how to specify - nodes. See also the caveats on the "ExitNodes" option below. + <<ExcludeNodes,ExcludeNodes>> for more information on how to specify + nodes. See also the caveats on the <<ExitNodes,ExitNodes>> option below. [[ExitNodes]] **ExitNodes** __node__,__node__,__...__:: A list of identity fingerprints, country codes, and address patterns of nodes to use as exit node---that is, a node that delivers traffic for you *outside* the Tor network. See - the **ExcludeNodes** option for more information on how to specify nodes. + + <<ExcludeNodes,ExcludeNodes>> for more information on how to specify nodes. + + Note that if you list too few nodes here, or if you exclude too many exit nodes with ExcludeExitNodes, you can degrade functionality. For example, @@ -1894,7 +1892,7 @@ different from other Tor clients: used to connect to hidden services, those that do directory fetches, those used for relay reachability self-tests, and so on) that end at a non-exit node. To - keep a node from being used entirely, see ExcludeNodes and StrictNodes. + + keep a node from being used entirely, see <<ExcludeNodes,ExcludeNodes>> and <<StrictNodes,StrictNodes>>. + + The ExcludeNodes option overrides this option: any node listed in both ExitNodes and ExcludeNodes is treated as excluded. + @@ -2038,7 +2036,7 @@ different from other Tor clients: + The ExcludeNodes option overrides this option: any node listed in both MiddleNodes and ExcludeNodes is treated as excluded. See - the **ExcludeNodes** option for more information on how to specify nodes. + the <<ExcludeNodes,ExcludeNodes>> for more information on how to specify nodes. [[NodeFamily]] **NodeFamily** __node__,__node__,__...__:: The Tor servers, defined by their identity fingerprints, @@ -2047,7 +2045,7 @@ different from other Tor clients: when a server doesn't list the family itself (with MyFamily). This option can be used multiple times; each instance defines a separate family. In addition to nodes, you can also list IP address and ranges and country - codes in {curly braces}. See the **ExcludeNodes** option for more + codes in {curly braces}. See <<ExcludeNodes,ExcludeNodes>> for more information on how to specify nodes. [[StrictNodes]] **StrictNodes** **0**|**1**:: @@ -2063,6 +2061,7 @@ different from other Tor clients: fulfill a .exit request, upload directory information, or download directory information. (Default: 0) +[[server-options]] == SERVER OPTIONS // These options are in alphabetical order, with exceptions as noted. @@ -2073,7 +2072,7 @@ is non-zero): [[AccountingMax]] **AccountingMax** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: Limits the max number of bytes sent and received within a set time period - using a given calculation rule (see: AccountingStart, AccountingRule). + using a given calculation rule (see <<AccountingStart,AccountingStart>> and <<AccountingRule,AccountingRule>>). Useful if you need to stay under a specific bandwidth. By default, the number used for calculation is the max of either the bytes sent or received. For example, with AccountingMax set to 1 TByte, a server @@ -2148,7 +2147,8 @@ is non-zero): GeoIP data, Tor keeps a per-country count of how many client addresses have contacted it so that it can help the bridge authority guess which countries have blocked access to it. If ExtraInfoStatistics is - enabled, it will be published as part of extra-info document. (Default: 1) + enabled, it will be published as part of the extra-info document. + (Default: 1) //Out of order because it logically belongs after BridgeRelay. [[BridgeDistribution]] **BridgeDistribution** __string__:: @@ -2219,7 +2219,7 @@ is non-zero): may also allow connections to your own computer that are addressed to its public (external) IP address. See RFC 1918 and RFC 3330 for more details about internal and reserved IP address space. See - ExitPolicyRejectLocalInterfaces if you want to block every address on the + <<ExitPolicyRejectLocalInterfaces,ExitPolicyRejectLocalInterfaces>> if you want to block every address on the relay, even those that aren't advertised in the descriptor. + + This directive can be specified multiple times so you don't have to put it @@ -2264,7 +2264,7 @@ is non-zero): bind addresses of any port options, such as ControlPort or DNSPort, and any public IPv4 and IPv6 addresses on any interface on the relay. (If IPv6Exit is not set, all IPv6 addresses will be rejected anyway.) - See above entry on ExitPolicy. + See above entry on <<ExitPolicy,ExitPolicy>>. This option is off by default, because it lists all public relay IP addresses in the ExitPolicy, even those relay operators might prefer not to disclose. @@ -2273,7 +2273,7 @@ is non-zero): [[ExitPolicyRejectPrivate]] **ExitPolicyRejectPrivate** **0**|**1**:: Reject all private (local) networks, along with the relay's advertised public IPv4 and IPv6 addresses, at the beginning of your exit policy. - See above entry on ExitPolicy. + See above entry on <<ExitPolicy,ExitPolicy>>. (Default: 1) [[ExitRelay]] **ExitRelay** **0**|**1**|**auto**:: @@ -2362,14 +2362,11 @@ is non-zero): list itself, but it won't hurt if it does.) Do not list any bridge relay as it would compromise its concealment. + + - When listing a node, it's better to list it by fingerprint than by - nickname: fingerprints are more reliable. + - + If you run more than one relay, the MyFamily option on each relay **must** list all other relays, as described above. + + Note: do not use MyFamily when configuring your Tor instance as a - brigde. + bridge. [[Nickname]] **Nickname** __name__:: Set the server's nickname to \'name'. Nicknames must be between 1 and 19 @@ -2617,7 +2614,7 @@ types of statistics that Tor relays collect and publish: circuit) and writes them into disk every 24 hours. Onion router operators may use the statistics for performance monitoring. If ExtraInfoStatistics is enabled, it will published as part of - extra-info document. (Default: 0) + the extra-info document. (Default: 0) [[ConnDirectionStatistics]] **ConnDirectionStatistics** **0**|**1**:: Relays only. @@ -2625,7 +2622,7 @@ types of statistics that Tor relays collect and publish: traffic it passes between itself and other relays to disk every 24 hours. Enables relay operators to monitor how much their relay is being used as middle node in the circuit. If ExtraInfoStatistics is - enabled, it will be published as part of extra-info document. + enabled, it will be published as part of the extra-info document. (Default: 0) [[DirReqStatistics]] **DirReqStatistics** **0**|**1**:: @@ -2635,7 +2632,7 @@ types of statistics that Tor relays collect and publish: hours. Enables relay and bridge operators to monitor how much their server is being used by clients to learn about Tor network. If ExtraInfoStatistics is enabled, it will published as part of - extra-info document. (Default: 1) + the extra-info document. (Default: 1) [[EntryStatistics]] **EntryStatistics** **0**|**1**:: Relays only. @@ -2644,7 +2641,7 @@ types of statistics that Tor relays collect and publish: operators to monitor how much inbound traffic that originates from Tor clients passes through their server to go further down the Tor network. If ExtraInfoStatistics is enabled, it will be published - as part of extra-info document. (Default: 0) + as part of the extra-info document. (Default: 0) [[ExitPortStatistics]] **ExitPortStatistics** **0**|**1**:: Exit relays only. @@ -2652,7 +2649,7 @@ types of statistics that Tor relays collect and publish: relayed bytes and opened stream per exit port to disk every 24 hours. Enables exit relay operators to measure and monitor amounts of traffic that leaves Tor network through their exit node. If ExtraInfoStatistics - is enabled, it will be published as part of extra-info document. + is enabled, it will be published as part of the extra-info document. (Default: 0) [[ExtraInfoStatistics]] **ExtraInfoStatistics** **0**|**1**:: @@ -2660,7 +2657,7 @@ types of statistics that Tor relays collect and publish: its extra-info documents that it uploads to the directory authorities. Disabling this option also removes bandwidth usage statistics, and GeoIPFile and GeoIPv6File hashes from the extra-info file. Bridge - ServerTransportPlugin lines are always includes in the extra-info file, + ServerTransportPlugin lines are always included in the extra-info file, because they are required by BridgeDB. (Default: 1) @@ -2668,9 +2665,9 @@ types of statistics that Tor relays collect and publish: Relays only. When this option is enabled, a Tor relay writes obfuscated statistics on its role as hidden-service directory, introduction - point, or rendezvous point to disk every 24 hours. If - ExtraInfoStatistics is also enabled, these statistics are further - published to the directory authorities. (Default: 1) + point, or rendezvous point to disk every 24 hours. If ExtraInfoStatistics + is enabled, it will be published as part of the extra-info document. + (Default: 1) [[PaddingStatistics]] **PaddingStatistics** **0**|**1**:: Relays and bridges only. @@ -2679,12 +2676,12 @@ types of statistics that Tor relays collect and publish: These statistics are rounded, and omitted if traffic is low. This information is important for load balancing decisions related to padding. If ExtraInfoStatistics is enabled, it will be published - as a part of extra-info document. (Default: 1) + as a part of the extra-info document. (Default: 1) == DIRECTORY SERVER OPTIONS The following options are useful only for directory servers. (Relays with -enough bandwidth automatically become directory servers; see DirCache for +enough bandwidth automatically become directory servers; see <<DirCache,DirCache>> for details.) [[DirCache]] **DirCache** **0**|**1**:: @@ -2740,9 +2737,11 @@ and are as follows: + 2. If a single client IP address (v4 or v6) makes circuits too quickly (default values are more than 3 per second, with an allowed burst of 90, - see DoSCircuitCreationRate and DoSCircuitCreationBurst) while also having + see <<DoSCircuitCreationRate,DoSCircuitCreationRate>> and + <<DoSCircuitCreationBurst,DoSCircuitCreationBurst>>) while also having too many connections open (default is 3, see - DoSCircuitCreationMinConnections), tor will refuse any new circuit (CREATE + <<DoSCircuitCreationMinConnections,DoSCircuitCreationMinConnections>>), + tor will refuse any new circuit (CREATE cells) for the next while (random value between 1 and 2 hours). + 3. If a client asks to establish a rendezvous point to you directly (ex: @@ -2764,32 +2763,17 @@ your log at NOTICE level which looks like: The following options are useful only for a public relay. They control the Denial of Service mitigation subsystem described above. +//Out of order because it logically belongs before the other DoSCircuitCreation options. [[DoSCircuitCreationEnabled]] **DoSCircuitCreationEnabled** **0**|**1**|**auto**:: Enable circuit creation DoS mitigation. If set to 1 (enabled), tor will cache client IPs along with statistics in order to detect circuit DoS attacks. If an address is positively identified, tor will activate - defenses against the address. See the DoSCircuitCreationDefenseType option - for more details. This is a client to relay detection only. "auto" means + defenses against the address. See <<DoSCircuitCreationDefenseType,DoSCircuitCreationDefenseType>> + option for more details. This is a client to relay detection only. "auto" means use the consensus parameter. If not defined in the consensus, the value is 0. (Default: auto) -[[DoSCircuitCreationMinConnections]] **DoSCircuitCreationMinConnections** __NUM__:: - - Minimum threshold of concurrent connections before a client address can be - flagged as executing a circuit creation DoS. In other words, once a client - address reaches the circuit rate and has a minimum of NUM concurrent - connections, a detection is positive. "0" means use the consensus - parameter. If not defined in the consensus, the value is 3. - (Default: 0) - -[[DoSCircuitCreationRate]] **DoSCircuitCreationRate** __NUM__:: - - The allowed circuit creation rate per second applied per client IP - address. If this option is 0, it obeys a consensus parameter. If not - defined in the consensus, the value is 3. - (Default: 0) - [[DoSCircuitCreationBurst]] **DoSCircuitCreationBurst** __NUM__:: The allowed circuit creation burst per client IP address. If the circuit @@ -2798,6 +2782,14 @@ Denial of Service mitigation subsystem described above. consensus, the value is 90. (Default: 0) +[[DoSCircuitCreationDefenseTimePeriod]] **DoSCircuitCreationDefenseTimePeriod** __N__ **seconds**|**minutes**|**hours**:: + + The base time period in seconds that the DoS defense is activated for. The + actual value is selected randomly for each activation from N+1 to 3/2 * N. + "0" means use the consensus parameter. If not defined in the consensus, + the value is 3600 seconds (1 hour). + (Default: 0) + [[DoSCircuitCreationDefenseType]] **DoSCircuitCreationDefenseType** __NUM__:: This is the type of defense applied to a detected client address. The @@ -2810,14 +2802,23 @@ Denial of Service mitigation subsystem described above. "0" means use the consensus parameter. If not defined in the consensus, the value is 2. (Default: 0) -[[DoSCircuitCreationDefenseTimePeriod]] **DoSCircuitCreationDefenseTimePeriod** __N__ **seconds**|**minutes**|**hours**:: +[[DoSCircuitCreationMinConnections]] **DoSCircuitCreationMinConnections** __NUM__:: - The base time period in seconds that the DoS defense is activated for. The - actual value is selected randomly for each activation from N+1 to 3/2 * N. - "0" means use the consensus parameter. If not defined in the consensus, - the value is 3600 seconds (1 hour). + Minimum threshold of concurrent connections before a client address can be + flagged as executing a circuit creation DoS. In other words, once a client + address reaches the circuit rate and has a minimum of NUM concurrent + connections, a detection is positive. "0" means use the consensus + parameter. If not defined in the consensus, the value is 3. + (Default: 0) + +[[DoSCircuitCreationRate]] **DoSCircuitCreationRate** __NUM__:: + + The allowed circuit creation rate per second applied per client IP + address. If this option is 0, it obeys a consensus parameter. If not + defined in the consensus, the value is 3. (Default: 0) +//out of order because it logically belongs before the other DoSConnection options. [[DoSConnectionEnabled]] **DoSConnectionEnabled** **0**|**1**|**auto**:: Enable the connection DoS mitigation. If set to 1 (enabled), for client @@ -2826,14 +2827,6 @@ Denial of Service mitigation subsystem described above. consensus parameter. If not defined in the consensus, the value is 0. (Default: auto) -[[DoSConnectionMaxConcurrentCount]] **DoSConnectionMaxConcurrentCount** __NUM__:: - - The maximum threshold of concurrent connection from a client IP address. - Above this limit, a defense selected by DoSConnectionDefenseType is - applied. "0" means use the consensus parameter. If not defined in the - consensus, the value is 100. - (Default: 0) - [[DoSConnectionDefenseType]] **DoSConnectionDefenseType** __NUM__:: This is the type of defense applied to a detected client address for the @@ -2846,6 +2839,14 @@ Denial of Service mitigation subsystem described above. "0" means use the consensus parameter. If not defined in the consensus, the value is 2. (Default: 0) +[[DoSConnectionMaxConcurrentCount]] **DoSConnectionMaxConcurrentCount** __NUM__:: + + The maximum threshold of concurrent connection from a client IP address. + Above this limit, a defense selected by DoSConnectionDefenseType is + applied. "0" means use the consensus parameter. If not defined in the + consensus, the value is 100. + (Default: 0) + [[DoSRefuseSingleHopClientRendezvous]] **DoSRefuseSingleHopClientRendezvous** **0**|**1**|**auto**:: Refuse establishment of rendezvous points for single hop clients. In other @@ -2862,6 +2863,7 @@ control how Tor behaves as a directory authority. You should not need to adjust any of them if you're running a regular relay or exit server on the public Tor network. +// Out of order because it logically belongs first in this section [[AuthoritativeDirectory]] **AuthoritativeDirectory** **0**|**1**:: When this option is set to 1, Tor operates as an authoritative directory server. Instead of caching the directory, it generates its own list of @@ -2869,59 +2871,18 @@ on the public Tor network. already have you listed as a trusted directory, you probably do not want to set this option. -[[V3AuthoritativeDirectory]] **V3AuthoritativeDirectory** **0**|**1**:: - When this option is set in addition to **AuthoritativeDirectory**, Tor - generates version 3 network statuses and serves descriptors, etc as - described in dir-spec.txt file of https://spec.torproject.org/[torspec] - (for Tor clients and servers running at least 0.2.0.x). - -[[VersioningAuthoritativeDirectory]] **VersioningAuthoritativeDirectory** **0**|**1**:: - When this option is set to 1, Tor adds information on which versions of - Tor are still believed safe for use to the published directory. Each - version 1 authority is automatically a versioning authority; version 2 - authorities provide this service optionally. See **RecommendedVersions**, - **RecommendedClientVersions**, and **RecommendedServerVersions**. - -[[RecommendedVersions]] **RecommendedVersions** __STRING__:: - STRING is a comma-separated list of Tor versions currently believed to be - safe. The list is included in each directory, and nodes which pull down the - directory learn whether they need to upgrade. This option can appear - multiple times: the values from multiple lines are spliced together. When - this is set then **VersioningAuthoritativeDirectory** should be set too. - -[[RecommendedClientVersions]] **RecommendedClientVersions** __STRING__:: - STRING is a comma-separated list of Tor versions currently believed to be - safe for clients to use. This information is included in version 2 - directories. If this is not set then the value of **RecommendedVersions** - is used. When this is set then **VersioningAuthoritativeDirectory** should - be set too. - +//Out of order because it belongs with the AuthoritativeDirectory option. [[BridgeAuthoritativeDir]] **BridgeAuthoritativeDir** **0**|**1**:: When this option is set in addition to **AuthoritativeDirectory**, Tor accepts and serves server descriptors, but it caches and serves the main networkstatus documents rather than generating its own. (Default: 0) -[[MinUptimeHidServDirectoryV2]] **MinUptimeHidServDirectoryV2** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**:: - Minimum uptime of a v2 hidden service directory to be accepted as such by - authoritative directories. (Default: 25 hours) - -[[RecommendedServerVersions]] **RecommendedServerVersions** __STRING__:: - STRING is a comma-separated list of Tor versions currently believed to be - safe for servers to use. This information is included in version 2 - directories. If this is not set then the value of **RecommendedVersions** - is used. When this is set then **VersioningAuthoritativeDirectory** should - be set too. - -[[ConsensusParams]] **ConsensusParams** __STRING__:: - STRING is a space-separated list of key=value pairs that Tor will include - in the "params" line of its networkstatus vote. - -[[DirAllowPrivateAddresses]] **DirAllowPrivateAddresses** **0**|**1**:: - If set to 1, Tor will accept server descriptors with arbitrary "Address" - elements. Otherwise, if the address is not an IP address or is a private IP - address, it will reject the server descriptor. Additionally, Tor - will allow exit policies for private networks to fulfill Exit flag - requirements. (Default: 0) +//Out of order because it belongs with the AuthoritativeDirectory option. +[[V3AuthoritativeDirectory]] **V3AuthoritativeDirectory** **0**|**1**:: + When this option is set in addition to **AuthoritativeDirectory**, Tor + generates version 3 network statuses and serves descriptors, etc as + described in dir-spec.txt file of https://spec.torproject.org/[torspec] + (for Tor clients and servers running at least 0.2.0.x). [[AuthDirBadExit]] **AuthDirBadExit** __AddressPattern...__:: Authoritative directories only. A set of address patterns for servers that @@ -2932,26 +2893,46 @@ on the public Tor network. is the same as for exit policies, except that you don't need to say "accept" or "reject", and ports are not needed.) -[[AuthDirInvalid]] **AuthDirInvalid** __AddressPattern...__:: - Authoritative directories only. A set of address patterns for servers that - will never be listed as "valid" in any network status document that this - authority publishes. +[[AuthDirFastGuarantee]] **AuthDirFastGuarantee** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: + Authoritative directories only. If non-zero, always vote the + Fast flag for any relay advertising this amount of capacity or + more. (Default: 100 KBytes) -[[AuthDirReject]] **AuthDirReject** __AddressPattern__...:: - Authoritative directories only. A set of address patterns for servers that - will never be listed at all in any network status document that this - authority publishes, or accepted as an OR address in any descriptor - submitted for publication by this authority. +[[AuthDirGuardBWGuarantee]] **AuthDirGuardBWGuarantee** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: + Authoritative directories only. If non-zero, this advertised capacity + or more is always sufficient to satisfy the bandwidth requirement + for the Guard flag. (Default: 2 MBytes) -[[AuthDirBadExitCCs]] **AuthDirBadExitCCs** __CC__,... + +[[AuthDirHasIPv6Connectivity]] **AuthDirHasIPv6Connectivity** **0**|**1**:: + Authoritative directories only. When set to 0, OR ports with an + IPv6 address are not included in the authority's votes. When set to 1, + IPv6 OR ports are tested for reachability like IPv4 OR ports. If the + reachability test succeeds, the authority votes for the IPv6 ORPort, and + votes Running for the relay. If the reachability test fails, the authority + does not vote for the IPv6 ORPort, and does not vote Running (Default: 0) + ++ + The content of the consensus depends on the number of voting authorities + that set AuthDirHasIPv6Connectivity: -[[AuthDirInvalidCCs]] **AuthDirInvalidCCs** __CC__,... + + If no authorities set AuthDirHasIPv6Connectivity 1, there will be no + IPv6 ORPorts in the consensus. -[[AuthDirRejectCCs]] **AuthDirRejectCCs** __CC__,...:: - Authoritative directories only. These options contain a comma-separated - list of country codes such that any server in one of those country codes - will be marked as a bad exit/invalid for use, or rejected - entirely. + If a minority of authorities set AuthDirHasIPv6Connectivity 1, + unreachable IPv6 ORPorts will be removed from the consensus. But the + majority of IPv4-only authorities will still vote the relay as Running. + Reachable IPv6 ORPort lines will be included in the consensus + + If a majority of voting authorities set AuthDirHasIPv6Connectivity 1, + relays with unreachable IPv6 ORPorts will not be listed as Running. + Reachable IPv6 ORPort lines will be included in the consensus + (To ensure that any valid majority will vote relays with unreachable + IPv6 ORPorts not Running, 75% of authorities must set + AuthDirHasIPv6Connectivity 1.) + +[[AuthDirInvalid]] **AuthDirInvalid** __AddressPattern...__:: + Authoritative directories only. A set of address patterns for servers that + will never be listed as "valid" in any network status document that this + authority publishes. [[AuthDirListBadExits]] **AuthDirListBadExits** **0**|**1**:: Authoritative directories only. If set to 1, this directory has some @@ -2964,16 +2945,6 @@ on the public Tor network. list as acceptable on a single IP address. Set this to "0" for "no limit". (Default: 2) -[[AuthDirFastGuarantee]] **AuthDirFastGuarantee** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: - Authoritative directories only. If non-zero, always vote the - Fast flag for any relay advertising this amount of capacity or - more. (Default: 100 KBytes) - -[[AuthDirGuardBWGuarantee]] **AuthDirGuardBWGuarantee** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: - Authoritative directories only. If non-zero, this advertised capacity - or more is always sufficient to satisfy the bandwidth requirement - for the Guard flag. (Default: 2 MBytes) - [[AuthDirPinKeys]] **AuthDirPinKeys** **0**|**1**:: Authoritative directories only. If non-zero, do not allow any relay to publish a descriptor if any other relay has reserved its <Ed25519,RSA> @@ -2981,6 +2952,31 @@ on the public Tor network. in a journal if it is new, or if it differs from the most recently accepted pinning for one of the keys it contains. (Default: 1) +[[AuthDirReject]] **AuthDirReject** __AddressPattern__...:: + Authoritative directories only. A set of address patterns for servers that + will never be listed at all in any network status document that this + authority publishes, or accepted as an OR address in any descriptor + submitted for publication by this authority. + +//Out of order because it logically belongs with the other CCs options. +[[AuthDirBadExitCCs]] **AuthDirBadExitCCs** __CC__,... + + +//Out of order because it logically belongs with the other CCs options. +[[AuthDirInvalidCCs]] **AuthDirInvalidCCs** __CC__,... + + + +[[AuthDirRejectRequestsUnderLoad]] **AuthDirRejectRequestsUnderLoad** **0**|**1**:: + If set, the directory authority will start rejecting directory requests + from non relay connections by sending a 503 error code if it is under + bandwidth pressure (reaching the configured limit if any). Relays will + always tried to be answered even if this is on. (Default: 1) + +[[AuthDirRejectCCs]] **AuthDirRejectCCs** __CC__,...:: + Authoritative directories only. These options contain a comma-separated + list of country codes such that any server in one of those country codes + will be marked as a bad exit/invalid for use, or rejected + entirely. + [[AuthDirSharedRandomness]] **AuthDirSharedRandomness** **0**|**1**:: Authoritative directories only. Switch for the shared random protocol. If zero, the authority won't participate in the protocol. If non-zero @@ -3002,17 +2998,52 @@ on the public Tor network. and their target user audience can periodically fetch the list of available community bridges to stay up-to-date. (Default: not set) -[[V3AuthVotingInterval]] **V3AuthVotingInterval** __N__ **minutes**|**hours**:: - V3 authoritative directories only. Configures the server's preferred voting - interval. Note that voting will __actually__ happen at an interval chosen - by consensus from all the authorities' preferred intervals. This time - SHOULD divide evenly into a day. (Default: 1 hour) +[[ConsensusParams]] **ConsensusParams** __STRING__:: + STRING is a space-separated list of key=value pairs that Tor will include + in the "params" line of its networkstatus vote. -[[V3AuthVoteDelay]] **V3AuthVoteDelay** __N__ **minutes**|**hours**:: - V3 authoritative directories only. Configures the server's preferred delay - between publishing its vote and assuming it has all the votes from all the - other authorities. Note that the actual time used is not the server's - preferred time, but the consensus of all preferences. (Default: 5 minutes) +[[DirAllowPrivateAddresses]] **DirAllowPrivateAddresses** **0**|**1**:: + If set to 1, Tor will accept server descriptors with arbitrary "Address" + elements. Otherwise, if the address is not an IP address or is a private IP + address, it will reject the server descriptor. Additionally, Tor + will allow exit policies for private networks to fulfill Exit flag + requirements. (Default: 0) + +[[GuardfractionFile]] **GuardfractionFile** __FILENAME__:: + V3 authoritative directories only. Configures the location of the + guardfraction file which contains information about how long relays + have been guards. (Default: unset) + +[[MinMeasuredBWsForAuthToIgnoreAdvertised]] **MinMeasuredBWsForAuthToIgnoreAdvertised** __N__:: + A total value, in abstract bandwidth units, describing how much + measured total bandwidth an authority should have observed on the network + before it will treat advertised bandwidths as wholly + unreliable. (Default: 500) + +[[MinUptimeHidServDirectoryV2]] **MinUptimeHidServDirectoryV2** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**:: + Minimum uptime of a v2 hidden service directory to be accepted as such by + authoritative directories. (Default: 25 hours) + +[[RecommendedClientVersions]] **RecommendedClientVersions** __STRING__:: + STRING is a comma-separated list of Tor versions currently believed to be + safe for clients to use. This information is included in version 2 + directories. If this is not set then the value of **RecommendedVersions** + is used. When this is set then **VersioningAuthoritativeDirectory** should + be set too. + +[[RecommendedServerVersions]] **RecommendedServerVersions** __STRING__:: + STRING is a comma-separated list of Tor versions currently believed to be + safe for servers to use. This information is included in version 2 + directories. If this is not set then the value of **RecommendedVersions** + is used. When this is set then **VersioningAuthoritativeDirectory** should + be set too. + +[[RecommendedVersions]] **RecommendedVersions** __STRING__:: + STRING is a comma-separated list of Tor versions currently believed to be + safe. The list is included in each directory, and nodes which pull down the + directory learn whether they need to upgrade. This option can appear + multiple times: the values from multiple lines are spliced together. When + this is set then **VersioningAuthoritativeDirectory** should be set too. [[V3AuthDistDelay]] **V3AuthDistDelay** __N__ **minutes**|**hours**:: V3 authoritative directories only. Configures the server's preferred delay @@ -3029,55 +3060,38 @@ on the public Tor network. server's preferred number, but the consensus of all preferences. Must be at least 2. (Default: 3) -[[V3BandwidthsFile]] **V3BandwidthsFile** __FILENAME__:: - V3 authoritative directories only. Configures the location of the - bandwidth-authority generated file storing information on relays' measured - bandwidth capacities. To avoid inconsistent reads, bandwidth data should - be written to temporary file, then renamed to the configured filename. - (Default: unset) - -[[GuardfractionFile]] **GuardfractionFile** __FILENAME__:: - V3 authoritative directories only. Configures the location of the - guardfraction file which contains information about how long relays - have been guards. (Default: unset) - [[V3AuthUseLegacyKey]] **V3AuthUseLegacyKey** **0**|**1**:: If set, the directory authority will sign consensuses not only with its own signing key, but also with a "legacy" key and certificate with a different identity. This feature is used to migrate directory authority keys in the event of a compromise. (Default: 0) -[[AuthDirHasIPv6Connectivity]] **AuthDirHasIPv6Connectivity** **0**|**1**:: - Authoritative directories only. When set to 0, OR ports with an - IPv6 address are not included in the authority's votes. When set to 1, - IPv6 OR ports are tested for reachability like IPv4 OR ports. If the - reachability test succeeds, the authority votes for the IPv6 ORPort, and - votes Running for the relay. If the reachability test fails, the authority - does not vote for the IPv6 ORPort, and does not vote Running (Default: 0) + -+ - The content of the consensus depends on the number of voting authorities - that set AuthDirHasIPv6Connectivity: - - If no authorities set AuthDirHasIPv6Connectivity 1, there will be no - IPv6 ORPorts in the consensus. +[[V3AuthVoteDelay]] **V3AuthVoteDelay** __N__ **minutes**|**hours**:: + V3 authoritative directories only. Configures the server's preferred delay + between publishing its vote and assuming it has all the votes from all the + other authorities. Note that the actual time used is not the server's + preferred time, but the consensus of all preferences. (Default: 5 + minutes) - If a minority of authorities set AuthDirHasIPv6Connectivity 1, - unreachable IPv6 ORPorts will be removed from the consensus. But the - majority of IPv4-only authorities will still vote the relay as Running. - Reachable IPv6 ORPort lines will be included in the consensus +[[V3AuthVotingInterval]] **V3AuthVotingInterval** __N__ **minutes**|**hours**:: + V3 authoritative directories only. Configures the server's preferred voting + interval. Note that voting will __actually__ happen at an interval chosen + by consensus from all the authorities' preferred intervals. This time + SHOULD divide evenly into a day. (Default: 1 hour) - If a majority of voting authorities set AuthDirHasIPv6Connectivity 1, - relays with unreachable IPv6 ORPorts will not be listed as Running. - Reachable IPv6 ORPort lines will be included in the consensus - (To ensure that any valid majority will vote relays with unreachable - IPv6 ORPorts not Running, 75% of authorities must set - AuthDirHasIPv6Connectivity 1.) +[[V3BandwidthsFile]] **V3BandwidthsFile** __FILENAME__:: + V3 authoritative directories only. Configures the location of the + bandwidth-authority generated file storing information on relays' measured + bandwidth capacities. To avoid inconsistent reads, bandwidth data should + be written to temporary file, then renamed to the configured filename. + (Default: unset) -[[MinMeasuredBWsForAuthToIgnoreAdvertised]] **MinMeasuredBWsForAuthToIgnoreAdvertised** __N__:: - A total value, in abstract bandwidth units, describing how much - measured total bandwidth an authority should have observed on the network - before it will treat advertised bandwidths as wholly - unreliable. (Default: 500) +[[VersioningAuthoritativeDirectory]] **VersioningAuthoritativeDirectory** **0**|**1**:: + When this option is set to 1, Tor adds information on which versions of + Tor are still believed safe for use to the published directory. Each + version 1 authority is automatically a versioning authority; version 2 + authorities provide this service optionally. See <<RecommendedVersions,RecommendedVersions>>, + <<RecommendedClientVersions,RecommendedClientVersions>>, and <<RecommendedServerVersions,RecommendedServerVersions>>. == HIDDEN SERVICE OPTIONS @@ -3089,6 +3103,26 @@ The next section describes the per service options that can only be set **PER SERVICE OPTIONS:** +[[HiddenServiceAllowUnknownPorts]] **HiddenServiceAllowUnknownPorts** **0**|**1**:: + If set to 1, then connections to unrecognized ports do not cause the + current hidden service to close rendezvous circuits. (Setting this to 0 is + not an authorization mechanism; it is instead meant to be a mild + inconvenience to port-scanners.) (Default: 0) + +[[HiddenServiceAuthorizeClient]] **HiddenServiceAuthorizeClient** __auth-type__ __client-name__,__client-name__,__...__:: + If configured, the v2 hidden service is accessible for authorized clients + only. The auth-type can either be \'basic' for a general-purpose + authorization protocol or \'stealth' for a less scalable protocol that also + hides service activity from unauthorized clients. Only clients that are + listed here are authorized to access the hidden service. Valid client names + are 1 to 16 characters long and only use characters in A-Za-z0-9+-_ (no + spaces). If this option is set, the hidden service is not accessible for + clients without authorization any more. Generated authorization data can be + found in the hostname file. Clients need to put this authorization data in + their configuration file using **HidServAuth**. This option is only for v2 + services; v3 services configure client authentication in a subdirectory of + HiddenServiceDir instead (see <<client-authorization,CLIENT AUTHORIZATION>>). + [[HiddenServiceDir]] **HiddenServiceDir** __DIRECTORY__:: Store data files for a hidden service in DIRECTORY. Every hidden service must have a separate directory. You may use this option multiple times to @@ -3101,41 +3135,35 @@ The next section describes the per service options that can only be set rely on this behavior; it is not guaranteed to remain the same in future versions.) -[[HiddenServicePort]] **HiddenServicePort** __VIRTPORT__ [__TARGET__]:: - Configure a virtual port VIRTPORT for a hidden service. You may use this - option multiple times; each time applies to the service using the most - recent HiddenServiceDir. By default, this option maps the virtual port to - the same port on 127.0.0.1 over TCP. You may override the target port, - address, or both by specifying a target of addr, port, addr:port, or - **unix:**__path__. (You can specify an IPv6 target as [addr]:port. Unix - paths may be quoted, and may use standard C escapes.) - You may also have multiple lines with the same VIRTPORT: when a user - connects to that VIRTPORT, one of the TARGETs from those lines will be - chosen at random. Note that address-port pairs have to be comma-separated. +[[HiddenServiceDirGroupReadable]] **HiddenServiceDirGroupReadable** **0**|**1**:: + If this option is set to 1, allow the filesystem group to read the + hidden service directory and hostname file. If the option is set to 0, + only owner is able to read the hidden service directory. (Default: 0) + Has no effect on Windows. -[[HiddenServiceVersion]] **HiddenServiceVersion** **2**|**3**:: - A list of rendezvous service descriptor versions to publish for the hidden - service. Currently, versions 2 and 3 are supported. (Default: 3) +[[HiddenServiceEnableIntroDoSDefense]] **HiddenServiceEnableIntroDoSDefense** **0**|**1**:: + Enable DoS defense at the intropoint level. When this is enabled, the + rate and burst parameter (see below) will be sent to the intro point which + will then use them to apply rate limiting for introduction request to this + service. + + + The introduction point honors the consensus parameters except if this is + specifically set by the service operator using this option. The service + never looks at the consensus parameters in order to enable or disable this + defense. (Default: 0) -[[HiddenServiceAuthorizeClient]] **HiddenServiceAuthorizeClient** __auth-type__ __client-name__,__client-name__,__...__:: - If configured, the v2 hidden service is accessible for authorized clients - only. The auth-type can either be \'basic' for a general-purpose - authorization protocol or \'stealth' for a less scalable protocol that also - hides service activity from unauthorized clients. Only clients that are - listed here are authorized to access the hidden service. Valid client names - are 1 to 16 characters long and only use characters in A-Za-z0-9+-_ (no - spaces). If this option is set, the hidden service is not accessible for - clients without authorization any more. Generated authorization data can be - found in the hostname file. Clients need to put this authorization data in - their configuration file using **HidServAuth**. This option is only for v2 - services; v3 services configure client authentication in a subdirectory of - HiddenServiceDir instead (see the **Client Authorization** section). +//Out of order because it logically belongs after HiddenServiceEnableIntroDoSDefense. +[[HiddenServiceEnableIntroDoSBurstPerSec]] **HiddenServiceEnableIntroDoSBurstPerSec** __NUM__:: + The allowed client introduction burst per second at the introduction + point. If this option is 0, it is considered infinite and thus if + **HiddenServiceEnableIntroDoSDefense** is set, it then effectively + disables the defenses. (Default: 200) -[[HiddenServiceAllowUnknownPorts]] **HiddenServiceAllowUnknownPorts** **0**|**1**:: - If set to 1, then connections to unrecognized ports do not cause the - current hidden service to close rendezvous circuits. (Setting this to 0 is - not an authorization mechanism; it is instead meant to be a mild - inconvenience to port-scanners.) (Default: 0) +[[HiddenServiceEnableIntroDoSRatePerSec]] **HiddenServiceEnableIntroDoSRatePerSec** __NUM__:: + The allowed client introduction rate per second at the introduction + point. If this option is 0, it is considered infinite and thus if + **HiddenServiceEnableIntroDoSDefense** is set, it then effectively + disables the defenses. (Default: 25) [[HiddenServiceExportCircuitID]] **HiddenServiceExportCircuitID** __protocol__:: The onion service will use the given protocol to expose the global circuit @@ -3164,6 +3192,19 @@ The next section describes the per service options that can only be set The HAProxy version 1 protocol is described in detail at https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt +[[HiddenServiceOnionBalanceInstance]] **HiddenServiceOnionBalanceInstance** **0**|**1**:: + + If set to 1, this onion service becomes an OnionBalance instance and will + accept client connections destined to an OnionBalance frontend. In this + case, Tor expects to find a file named "ob_config" inside the + **HiddenServiceDir** directory with content: + + + MasterOnionAddress <frontend_onion_address> + + + where <frontend_onion_address> is the onion address of the OnionBalance + frontend (e.g. wrxdvcaqpuzakbfww5sxs6r2uybczwijzfn2ezy2osaj7iox7kl7nhad.onion). + + [[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__:: The maximum number of simultaneous streams (connections) per rendezvous circuit. The maximum value allowed is 65535. (Setting this to 0 will allow @@ -3174,6 +3215,26 @@ The next section describes the per service options that can only be set offending rendezvous circuit to be torn down, as opposed to stream creation requests that exceed the limit being silently ignored. (Default: 0) +[[HiddenServiceNumIntroductionPoints]] **HiddenServiceNumIntroductionPoints** __NUM__:: + Number of introduction points the hidden service will have. You can't + have more than 10 for v2 service and 20 for v3. (Default: 3) + +[[HiddenServicePort]] **HiddenServicePort** __VIRTPORT__ [__TARGET__]:: + Configure a virtual port VIRTPORT for a hidden service. You may use this + option multiple times; each time applies to the service using the most + recent HiddenServiceDir. By default, this option maps the virtual port to + the same port on 127.0.0.1 over TCP. You may override the target port, + address, or both by specifying a target of addr, port, addr:port, or + **unix:**__path__. (You can specify an IPv6 target as [addr]:port. Unix + paths may be quoted, and may use standard C escapes.) + You may also have multiple lines with the same VIRTPORT: when a user + connects to that VIRTPORT, one of the TARGETs from those lines will be + chosen at random. Note that address-port pairs have to be comma-separated. + +[[HiddenServiceVersion]] **HiddenServiceVersion** **2**|**3**:: + A list of rendezvous service descriptor versions to publish for the hidden + service. Currently, versions 2 and 3 are supported. (Default: 3) + [[RendPostPeriod]] **RendPostPeriod** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**:: Every time the specified period elapses, Tor uploads any rendezvous service descriptors to the directory servers. This information is also @@ -3181,48 +3242,10 @@ The next section describes the per service options that can only be set maximum is 3.5 days. This option is only for v2 services. (Default: 1 hour) -[[HiddenServiceDirGroupReadable]] **HiddenServiceDirGroupReadable** **0**|**1**:: - If this option is set to 1, allow the filesystem group to read the - hidden service directory and hostname file. If the option is set to 0, - only owner is able to read the hidden service directory. (Default: 0) - Has no effect on Windows. - -[[HiddenServiceNumIntroductionPoints]] **HiddenServiceNumIntroductionPoints** __NUM__:: - Number of introduction points the hidden service will have. You can't - have more than 10 for v2 service and 20 for v3. (Default: 3) - -[[HiddenServiceEnableIntroDoSDefense]] **HiddenServiceEnableIntroDoSDefense** **0**|**1**:: - Enable DoS defense at the intropoint level. When this is enabled, the - rate and burst parameter (see below) will be sent to the intro point which - will then use them to apply rate limiting for introduction request to this - service. - + - The introduction point honors the consensus parameters except if this is - specifically set by the service operator using this option. The service - never looks at the consensus parameters in order to enable or disable this - defense. (Default: 0) - -[[HiddenServiceEnableIntroDoSRatePerSec]] **HiddenServiceEnableIntroDoSRatePerSec** __NUM__:: - The allowed client introduction rate per second at the introduction - point. If this option is 0, it is considered infinite and thus if - **HiddenServiceEnableIntroDoSDefense** is set, it then effectively - disables the defenses. (Default: 25) - -[[HiddenServiceEnableIntroDoSBurstPerSec]] **HiddenServiceEnableIntroDoSBurstPerSec** __NUM__:: - The allowed client introduction burst per second at the introduction - point. If this option is 0, it is considered infinite and thus if - **HiddenServiceEnableIntroDoSDefense** is set, it then effectively - disables the defenses. (Default: 200) **PER INSTANCE OPTIONS:** -[[PublishHidServDescriptors]] **PublishHidServDescriptors** **0**|**1**:: - If set to 0, Tor will run any hidden services you configure, but it won't - advertise them to the rendezvous directory. This option is only useful if - you're using a Tor controller that handles hidserv publishing for you. - (Default: 1) - [[HiddenServiceSingleHopMode]] **HiddenServiceSingleHopMode** **0**|**1**:: **Experimental - Non Anonymous** Hidden Services on a tor instance in HiddenServiceSingleHopMode make one-hop (direct) circuits between the onion @@ -3249,6 +3272,7 @@ The next section describes the per service options that can only be set **HiddenServiceSingleHopMode**. Can not be changed while tor is running. (Default: 0) +//Out of order because it belongs after HiddenServiceSingleHopMode. [[HiddenServiceNonAnonymousMode]] **HiddenServiceNonAnonymousMode** **0**|**1**:: Makes hidden services non-anonymous on this tor instance. Allows the non-anonymous HiddenServiceSingleHopMode. Enables direct connections in the @@ -3257,7 +3281,14 @@ The next section describes the per service options that can only be set including setting SOCKSPort to "0". Can not be changed while tor is running. (Default: 0) -== Client Authorization +[[PublishHidServDescriptors]] **PublishHidServDescriptors** **0**|**1**:: + If set to 0, Tor will run any hidden services you configure, but it won't + advertise them to the rendezvous directory. This option is only useful if + you're using a Tor controller that handles hidserv publishing for you. + (Default: 1) + +[[client-authorization]] +== CLIENT AUTHORIZATION (Version 3 only) @@ -3302,6 +3333,7 @@ For more information, please see https://2019.www.torproject.org/docs/tor-onion- The following options are used for running a testing Tor network. +//Out of order because it logically belongs first in this section. [[TestingTorNetwork]] **TestingTorNetwork** **0**|**1**:: If set to 1, Tor adjusts default values of the configuration options below, so that it is easier to set up a testing Tor network. May only be set if @@ -3342,59 +3374,35 @@ The following options are used for running a testing Tor network. TestingEnableConnBwEvent 1 TestingEnableCellStatsEvent 1 -[[TestingV3AuthInitialVotingInterval]] **TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**:: - Like V3AuthVotingInterval, but for initial voting interval before the first - consensus has been created. Changing this requires that - **TestingTorNetwork** is set. (Default: 30 minutes) - -[[TestingV3AuthInitialVoteDelay]] **TestingV3AuthInitialVoteDelay** __N__ **minutes**|**hours**:: - Like V3AuthVoteDelay, but for initial voting interval before - the first consensus has been created. Changing this requires that - **TestingTorNetwork** is set. (Default: 5 minutes) - -[[TestingV3AuthInitialDistDelay]] **TestingV3AuthInitialDistDelay** __N__ **minutes**|**hours**:: - Like V3AuthDistDelay, but for initial voting interval before - the first consensus has been created. Changing this requires that - **TestingTorNetwork** is set. (Default: 5 minutes) - -[[TestingV3AuthVotingStartOffset]] **TestingV3AuthVotingStartOffset** __N__ **seconds**|**minutes**|**hours**:: - Directory authorities offset voting start time by this much. - Changing this requires that **TestingTorNetwork** is set. (Default: 0) - [[TestingAuthDirTimeToLearnReachability]] **TestingAuthDirTimeToLearnReachability** __N__ **minutes**|**hours**:: After starting as an authority, do not make claims about whether routers are Running until this much time has passed. Changing this requires that **TestingTorNetwork** is set. (Default: 30 minutes) -[[TestingMinFastFlagThreshold]] **TestingMinFastFlagThreshold** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: - Minimum value for the Fast flag. Overrides the ordinary minimum taken - from the consensus when TestingTorNetwork is set. (Default: 0.) - -[[TestingServerDownloadInitialDelay]] **TestingServerDownloadInitialDelay** __N__:: - Initial delay in seconds for when servers should download things in general. Changing this - requires that **TestingTorNetwork** is set. (Default: 0) - -[[TestingClientDownloadInitialDelay]] **TestingClientDownloadInitialDelay** __N__:: - Initial delay in seconds for when clients should download things in general. Changing this - requires that **TestingTorNetwork** is set. (Default: 0) +[[TestingAuthKeyLifetime]] **TestingAuthKeyLifetime** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**|**months**:: + Overrides the default lifetime for a signing Ed25519 TLS Link authentication + key. + (Default: 2 days) -[[TestingServerConsensusDownloadInitialDelay]] **TestingServerConsensusDownloadInitialDelay** __N__:: - Initial delay in seconds for when servers should download consensuses. Changing this - requires that **TestingTorNetwork** is set. (Default: 0) +[[TestingAuthKeySlop]] **TestingAuthKeySlop** __N__ **seconds**|**minutes**|**hours** + -[[TestingClientConsensusDownloadInitialDelay]] **TestingClientConsensusDownloadInitialDelay** __N__:: - Initial delay in seconds for when clients should download consensuses. Changing this - requires that **TestingTorNetwork** is set. (Default: 0) +[[TestingBridgeBootstrapDownloadInitialDelay]] **TestingBridgeBootstrapDownloadInitialDelay** __N__:: + Initial delay in seconds for when clients should download each bridge descriptor when they + have just started, or when they can not contact any of their bridges. + Changing this requires that **TestingTorNetwork** is set. (Default: 0) [[TestingBridgeDownloadInitialDelay]] **TestingBridgeDownloadInitialDelay** __N__:: Initial delay in seconds for when clients should download each bridge descriptor when they know that one or more of their configured bridges are running. Changing this requires that **TestingTorNetwork** is set. (Default: 10800) -[[TestingBridgeBootstrapDownloadInitialDelay]] **TestingBridgeBootstrapDownloadInitialDelay** __N__:: - Initial delay in seconds for when clients should download each bridge descriptor when they - have just started, or when they can not contact any of their bridges. - Changing this requires that **TestingTorNetwork** is set. (Default: 0) +[[TestingClientConsensusDownloadInitialDelay]] **TestingClientConsensusDownloadInitialDelay** __N__:: + Initial delay in seconds for when clients should download consensuses. Changing this + requires that **TestingTorNetwork** is set. (Default: 0) + +[[TestingClientDownloadInitialDelay]] **TestingClientDownloadInitialDelay** __N__:: + Initial delay in seconds for when clients should download things in general. Changing this + requires that **TestingTorNetwork** is set. (Default: 0) [[TestingClientMaxIntervalWithoutRequest]] **TestingClientMaxIntervalWithoutRequest** __N__ **seconds**|**minutes**:: When directory clients have only a few descriptors to request, they batch @@ -3402,19 +3410,14 @@ The following options are used for running a testing Tor network. Changing this requires that **TestingTorNetwork** is set. (Default: 10 minutes) -[[TestingDirConnectionMaxStall]] **TestingDirConnectionMaxStall** __N__ **seconds**|**minutes**:: - Let a directory connection stall this long before expiring it. - Changing this requires that **TestingTorNetwork** is set. (Default: - 5 minutes) - [[TestingDirAuthVoteExit]] **TestingDirAuthVoteExit** __node__,__node__,__...__:: A list of identity fingerprints, country codes, and address patterns of nodes to vote Exit for regardless of their - uptime, bandwidth, or exit policy. See the **ExcludeNodes** - option for more information on how to specify nodes. + + uptime, bandwidth, or exit policy. See <<ExcludeNodes,ExcludeNodes>> + for more information on how to specify nodes. + + In order for this option to have any effect, **TestingTorNetwork** - has to be set. See the **ExcludeNodes** option for more + has to be set. See <<ExcludeNodes,ExcludeNodes>> for more information on how to specify nodes. [[TestingDirAuthVoteExitIsStrict]] **TestingDirAuthVoteExitIsStrict** **0**|**1** :: @@ -3428,7 +3431,7 @@ The following options are used for running a testing Tor network. [[TestingDirAuthVoteGuard]] **TestingDirAuthVoteGuard** __node__,__node__,__...__:: A list of identity fingerprints and country codes and address patterns of nodes to vote Guard for regardless of their - uptime and bandwidth. See the **ExcludeNodes** option for more + uptime and bandwidth. See <<ExcludeNodes,ExcludeNodes>> for more information on how to specify nodes. + + In order for this option to have any effect, **TestingTorNetwork** @@ -3444,7 +3447,7 @@ The following options are used for running a testing Tor network. [[TestingDirAuthVoteHSDir]] **TestingDirAuthVoteHSDir** __node__,__node__,__...__:: A list of identity fingerprints and country codes and address patterns of nodes to vote HSDir for regardless of their - uptime and DirPort. See the **ExcludeNodes** option for more + uptime and DirPort. See <<ExcludeNodes,ExcludeNodes>> for more information on how to specify nodes. + + In order for this option to have any effect, **TestingTorNetwork** @@ -3457,40 +3460,70 @@ The following options are used for running a testing Tor network. In order for this option to have any effect, **TestingTorNetwork** has to be set. -[[TestingEnableConnBwEvent]] **TestingEnableConnBwEvent** **0**|**1**:: - If this option is set, then Tor controllers may register for CONN_BW - events. Changing this requires that **TestingTorNetwork** is set. - (Default: 0) +[[TestingDirConnectionMaxStall]] **TestingDirConnectionMaxStall** __N__ **seconds**|**minutes**:: + Let a directory connection stall this long before expiring it. + Changing this requires that **TestingTorNetwork** is set. (Default: + 5 minutes) [[TestingEnableCellStatsEvent]] **TestingEnableCellStatsEvent** **0**|**1**:: If this option is set, then Tor controllers may register for CELL_STATS events. Changing this requires that **TestingTorNetwork** is set. (Default: 0) -[[TestingMinExitFlagThreshold]] **TestingMinExitFlagThreshold** __N__ **KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: - Sets a lower-bound for assigning an exit flag when running as an - authority on a testing network. Overrides the usual default lower bound - of 4 KBytes. (Default: 0) +[[TestingEnableConnBwEvent]] **TestingEnableConnBwEvent** **0**|**1**:: + If this option is set, then Tor controllers may register for CONN_BW + events. Changing this requires that **TestingTorNetwork** is set. + (Default: 0) [[TestingLinkCertLifetime]] **TestingLinkCertLifetime** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**|**months**:: Overrides the default lifetime for the certificates used to authenticate our X509 link cert with our ed25519 signing key. (Default: 2 days) -[[TestingAuthKeyLifetime]] **TestingAuthKeyLifetime** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**|**months**:: - Overrides the default lifetime for a signing Ed25519 TLS Link authentication - key. - (Default: 2 days) - [[TestingLinkKeySlop]] **TestingLinkKeySlop** __N__ **seconds**|**minutes**|**hours** + -[[TestingAuthKeySlop]] **TestingAuthKeySlop** __N__ **seconds**|**minutes**|**hours** + +[[TestingMinExitFlagThreshold]] **TestingMinExitFlagThreshold** __N__ **KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: + Sets a lower-bound for assigning an exit flag when running as an + authority on a testing network. Overrides the usual default lower bound + of 4 KBytes. (Default: 0) + +[[TestingMinFastFlagThreshold]] **TestingMinFastFlagThreshold** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: + Minimum value for the Fast flag. Overrides the ordinary minimum taken + from the consensus when TestingTorNetwork is set. (Default: 0.) + +[[TestingServerConsensusDownloadInitialDelay]] **TestingServerConsensusDownloadInitialDelay** __N__:: + Initial delay in seconds for when servers should download consensuses. Changing this + requires that **TestingTorNetwork** is set. (Default: 0) + +[[TestingServerDownloadInitialDelay]] **TestingServerDownloadInitialDelay** __N__:: + Initial delay in seconds for when servers should download things in general. Changing this + requires that **TestingTorNetwork** is set. (Default: 0) [[TestingSigningKeySlop]] **TestingSigningKeySlop** __N__ **seconds**|**minutes**|**hours**:: How early before the official expiration of a an Ed25519 signing key do we replace it and issue a new key? (Default: 3 hours for link and auth; 1 day for signing.) +[[TestingV3AuthInitialDistDelay]] **TestingV3AuthInitialDistDelay** __N__ **minutes**|**hours**:: + Like V3AuthDistDelay, but for initial voting interval before + the first consensus has been created. Changing this requires that + **TestingTorNetwork** is set. (Default: 5 minutes) + +[[TestingV3AuthInitialVoteDelay]] **TestingV3AuthInitialVoteDelay** __N__ **minutes**|**hours**:: + Like V3AuthVoteDelay, but for initial voting interval before + the first consensus has been created. Changing this requires that + **TestingTorNetwork** is set. (Default: 5 minutes) + +[[TestingV3AuthInitialVotingInterval]] **TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**:: + Like V3AuthVotingInterval, but for initial voting interval before the first + consensus has been created. Changing this requires that + **TestingTorNetwork** is set. (Default: 30 minutes) + +[[TestingV3AuthVotingStartOffset]] **TestingV3AuthVotingStartOffset** __N__ **seconds**|**minutes**|**hours**:: + Directory authorities offset voting start time by this much. + Changing this requires that **TestingTorNetwork** is set. (Default: 0) + + == NON-PERSISTENT OPTIONS These options are not saved to the torrc file by the "SAVECONF" controller @@ -3566,8 +3599,8 @@ __CacheDirectory__/**`cached-extrainfo`** and **`cached-extrainfo.new`**:: Similar to **cached-descriptors**, but holds optionally-downloaded "extra-info" documents. Relays use these documents to send inessential information about statistics, bandwidth history, and network health to the - authorities. They aren't fetched by default. See the DownloadExtraInfo - option for more information. + authorities. They aren't fetched by default. See <<DownloadExtraInfo,DownloadExtraInfo>> + for more information. __CacheDirectory__/**`cached-microdescs`** and **`cached-microdescs.new`**:: These files hold downloaded microdescriptors. Lines beginning with @@ -3634,11 +3667,11 @@ __KeyDirectory__/**`authority_signing_key`**:: __KeyDirectory__/**`legacy_certificate`**:: As authority_certificate; used only when `V3AuthUseLegacyKey` is set. See - documentation for V3AuthUseLegacyKey. + documentation for <<V3AuthUseLegacyKey,V3AuthUseLegacyKey>>. __KeyDirectory__/**`legacy_signing_key`**:: As authority_signing_key: used only when `V3AuthUseLegacyKey` is set. See - documentation for V3AuthUseLegacyKey. + documentation for <<V3AuthUseLegacyKey,V3AuthUseLegacyKey>>. __KeyDirectory__/**`secret_id_key`**:: A relay's RSA1024 permanent identity key, including private and public @@ -3682,14 +3715,15 @@ __DataDirectory__/**`hashed-fingerprint`**:: identity key. (That is, the hash of the hash of the identity key.) __DataDirectory__/**`approved-routers`**:: - Only used by authoritative directory servers. This file lists the status - and a fingerprint/pubkey. Each line lists a status and a fingerprint - separated by whitespace. See your **fingerprint** file in the - __DataDirectory__ for an example fingerprint line. If the status is - **!reject** then descriptors from the given identity (fingerprint/pubkey) - are rejected by this server. If it is **!invalid** then descriptors are - accepted but marked in the directory as not valid, that is, not - recommended. + Only used by authoritative directory servers. Each line lists a status and + an identity, separated by whitespace. Identities can be hex-encoded RSA + fingerprints, or base-64 encoded ed25519 public keys. See the + **fingerprint** file in a tor relay's __DataDirectory__ for an example + fingerprint line. If the status is **!reject**, then descriptors from the + given identity are rejected by this server. If it is **!invalid** then + descriptors are accepted, but marked in the directory as not valid, that + is, not recommended. In either case, the corresponding relays are not + included in the consensus. __DataDirectory__/**`v3-status-votes`**:: Only for v3 authoritative directory servers. This file contains status diff --git a/scripts/codegen/fuzzing_include_am.py b/scripts/codegen/fuzzing_include_am.py index aa3ba49a73..ae50563074 100755 --- a/scripts/codegen/fuzzing_include_am.py +++ b/scripts/codegen/fuzzing_include_am.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Future imports for Python 2.7, mandatory in 3.0 from __future__ import division diff --git a/scripts/codegen/gen_server_ciphers.py b/scripts/codegen/gen_server_ciphers.py index 3b77952243..8c88e54a13 100755 --- a/scripts/codegen/gen_server_ciphers.py +++ b/scripts/codegen/gen_server_ciphers.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright 2014-2019, The Tor Project, Inc # See LICENSE for licensing information diff --git a/scripts/codegen/get_mozilla_ciphers.py b/scripts/codegen/get_mozilla_ciphers.py index 165105736a..ff01dd8719 100755 --- a/scripts/codegen/get_mozilla_ciphers.py +++ b/scripts/codegen/get_mozilla_ciphers.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # coding=utf-8 # Copyright 2011-2019, The Tor Project, Inc # original version by Arturo Filastò diff --git a/scripts/codegen/makedesc.py b/scripts/codegen/makedesc.py index af926a6438..48d1d31a02 100644 --- a/scripts/codegen/makedesc.py +++ b/scripts/codegen/makedesc.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright 2014-2019, The Tor Project, Inc. # See LICENSE for license information diff --git a/scripts/git/git-install-tools.sh b/scripts/git/git-install-tools.sh new file mode 100755 index 0000000000..ef8623a018 --- /dev/null +++ b/scripts/git/git-install-tools.sh @@ -0,0 +1,189 @@ +#!/usr/bin/env bash + +SCRIPT_NAME=$(basename "$0") +SCRIPTS_DIR=$(dirname "$0") + +TOOL_NAMES=(push-all pull-all merge-forward list-tor-branches) + +function usage() +{ + echo "$SCRIPT_NAME [-h] [-n] [-v] [-f] <all|hooks|tools|aliases>" + echo + echo " flags:" + echo " -h: show this help text" + echo " -n: dry-run" + echo " -v: verbose mode" + echo " -f: force-install even if \$TOR_DEVTOOLS_DIR looks fishy" + echo + echo " modes:" + echo " hooks: install git hooks in this repository." + echo " tools: install scripts in \$TOR_DEVTOOLS_DIR" + echo " aliases: set up global git aliases for git tools in \$TOR_DEVTOOLS_DIR" + echo " all: all of the above." +} + +INSTALL_HOOKS=0 +INSTALL_TOOLS=0 +INSTALL_ALIASES=0 + +DRY_RUN=0 +VERBOSE=0 +FORCE=0 + +while getopts "hnfv" opt; do + case "$opt" in + h) usage + exit 0 + ;; + n) DRY_RUN=1 + ;; + v) VERBOSE=1 + ;; + f) FORCE=1 + ;; + *) echo + usage + exit 1 + ;; + esac +done + +for item in "${@:$OPTIND}"; do + case "$item" in + hooks) INSTALL_HOOKS=1 + ;; + tools) INSTALL_TOOLS=1 + ;; + aliases) INSTALL_ALIASES=1 + ;; + all) INSTALL_HOOKS=1 + INSTALL_TOOLS=1 + INSTALL_ALIASES=1 + ;; + *) echo "Unrecognized mode '$item'" + usage + exit 1 + ;; + esac +done + +if [[ $VERBOSE = 1 ]]; then + function note() + { + echo "$@" + } +else + function note() + { + true + } +fi + +function fail() +{ + echo "$@" 1>&2 + exit 1 +} + +if [[ $INSTALL_HOOKS = 0 && $INSTALL_TOOLS = 0 && $INSTALL_ALIASES = 0 ]]; then + echo "Nothing to do. Try $SCRIPT_NAME -h for a list of commands." + exit 0 +fi + +if [[ $INSTALL_TOOLS = 1 || $INSTALL_ALIASES = 1 ]]; then + if [[ -z "$TOR_DEVTOOLS_DIR" ]] ; then + fail "\$TOR_DEVTOOLS_DIR was not set." + fi + note "Checking whether \$TOR_DEVTOOLS_DIR ($TOR_DEVTOOLS_DIR) is a git repo..." + GITDIR=$(cd "$TOR_DEVTOOLS_DIR" && git rev-parse --git-dir 2>/dev/null) + note "GITDIR is $GITDIR" + if [[ -n "$GITDIR" ]] ; then + cat <<EOF +You have asked me to install to \$TOR_DEVTOOLS_DIR ($TOR_DEVTOOLS_DIR). +That is inside a git repository, so you might not want to install there: +depending on what you pull or push, you might find yourself giving somebody +else write access to your scripts. I think you should just use ~/bin or +something. +EOF + + echo + if [[ "$FORCE" = 1 ]] ; then + echo "I will install anyway, since you said '-f'." + else + echo "I will not install. You can tell me -f if you are really sure." + exit 1 + fi + else + note "It was not." + fi +fi + +if [[ ! -d "$SCRIPTS_DIR" || ! -e "$SCRIPTS_DIR/git-push-all.sh" ]]; then + fail "Couldn't find scripts in '$SCRIPTS_DIR'" +fi + +if [[ $DRY_RUN = 1 ]]; then + echo "** DRY RUN **" + RUN="echo >>" +else + RUN= +fi + +set -e + +# ====================================================================== +if [[ $INSTALL_HOOKS = 1 ]]; then + HOOKS_DIR=$(git rev-parse --git-path hooks) + + note "Looking for hooks directory" + + if [[ -z "$HOOKS_DIR" || ! -d "$HOOKS_DIR" ]]; then + fail "Couldn't find git hooks directory." + fi + + note "Found hooks directory in $HOOKS_DIR" + + note "Installing hooks" + for fn in "$SCRIPTS_DIR"/*.git-hook; do + name=$(basename "$fn") + $RUN install --backup "$fn" "${HOOKS_DIR}/${name%.git-hook}" + done +fi + + +# ====================================================================== +if [[ $INSTALL_TOOLS = 1 ]]; then + note "Installing tools." + note "Looking for \$TOR_DEVTOOLS_DIR ($TOR_DEVTOOLS_DIR)" + + if [[ ! -d "$TOR_DEVTOOLS_DIR" ]]; then + note "Creating directory" + $RUN mkdir -p "$TOR_DEVTOOLS_DIR" + fi + + note "Copying scripts" + for tool in "${TOOL_NAMES[@]}"; do + $RUN install --backup "${SCRIPTS_DIR}/git-${tool}.sh" "${TOR_DEVTOOLS_DIR}/" + done +fi + +# ====================================================================== +if [[ $INSTALL_ALIASES = 1 ]]; then + note "Installing aliases." + note "Looking for \$TOR_DEVTOOLS_DIR ($TOR_DEVTOOLS_DIR)" + + note "Checking for ${TOR_DEVTOOLS_DIR}/git-push-all.sh" + if [[ ! -x "${TOR_DEVTOOLS_DIR}/git-push-all.sh" ]]; then + if [[ $DRY_RUN = 0 ]]; then + fail "Could not find scripts in \$TOR_DEVTOOLS_DIR" + fi + fi + + note "Setting aliases" + for tool in "${TOOL_NAMES[@]}"; do + $RUN git config --global "alias.$tool" \!"${TOR_DEVTOOLS_DIR}/git-${tool}.sh" + done + +fi + +note Done. diff --git a/scripts/git/git-list-tor-branches.sh b/scripts/git/git-list-tor-branches.sh new file mode 100755 index 0000000000..d6b30f064f --- /dev/null +++ b/scripts/git/git-list-tor-branches.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash + +# Script to be used by other git scripts, and provide a single place +# that lists our supported branches. To change which branches are +# supported, look at the end of the file that says 'edit here'. + +SCRIPT_NAME=$(basename "$0") + +function usage() +{ + echo "$SCRIPT_NAME [-h] [-l|-s|-b|-m] [-R]" + echo + echo " arguments:" + echo " -h: show this help text" + echo + echo " -l: list the active tor branches (default)" + echo " -s: list the suffixes to be used with the active tor branches" + echo " -b: write bash code setting WORKTREE to an array of ( branch path ) arrays" + echo " -m: write bash code setting WORKTREE to an array of" + echo " ( branch parent path suffix parent_suffix ) arrays" + echo + echo " -R: omit release branches." +} + +# list : just a list of branch names. +# branch_path : For git-setup-dirs.sh and git-pull-all.sh +# suffix: write a list of suffixes. +# merge: branch, upstream, path, suffix, upstream suffix. +mode="list" +skip_release_branches="no" + +while getopts "hblmsR" opt ; do + case "$opt" in + h) usage + exit 0 + ;; + b) mode="branch_path" + ;; + l) mode="list" + ;; + s) mode="suffix" + ;; + m) mode="merge" + ;; + R) skip_release_branches="yes" + ;; + *) echo "Unknown option" + exit 1 + ;; + esac +done + +all_branch_vars=() + +prev_maint_branch="" +prev_maint_suffix="" + +branch() { + # The name of the branch. (Supplied by caller) Ex: maint-0.4.3 + brname="$1" + + # The name of the branch with no dots. Ex: maint-043 + brname_nodots="${brname//./}" + # The name of the branch with no dots, and _ instead of -. Ex: maint_043 + brname_nodots_uscore="${brname_nodots//-/_}" + # Name to use for a variable to represent the branch. Ex: MAINT_043 + varname="${brname_nodots_uscore^^}" + + is_maint="no" + + # suffix: a suffix to place at the end of branches we generate with respect + # to this branch. Ex: _043 + + # location: where the branch can be found. + + if [[ "$brname" == "master" ]]; then + suffix="_master" + location="\$GIT_PATH/\$TOR_MASTER_NAME" + elif [[ "$brname" =~ ^maint- ]]; then + suffix="_${brname_nodots#maint-}" + location="\$GIT_PATH/\$TOR_WKT_NAME/$brname" + is_maint="yes" + elif [[ "$brname" =~ ^release- ]]; then + suffix="_r${brname_nodots#release-}" + location="\$GIT_PATH/\$TOR_WKT_NAME/$brname" + + if [[ "$skip_release_branches" = "yes" ]]; then + return + fi + else + echo "Unrecognized branch type '${brname}'" >&2 + exit 1 + fi + + all_branch_vars+=("$varname") + + # Now emit the per-branch information + if [[ "$mode" == "branch_path" ]]; then + echo "${varname}=( \"$brname\" \"$location\" )" + elif [[ "$mode" == "merge" ]]; then + echo "${varname}=( \"$brname\" \"$prev_maint_branch\" \"$location\" \"$suffix\" \"$prev_maint_suffix\" )" + elif [[ "$mode" == "list" ]]; then + echo "$brname" + elif [[ "$mode" == "suffix" ]]; then + echo "$suffix" + else + echo "unknown mode $mode" >&2 + exit 1 + fi + + if [[ "$is_maint" == "yes" ]]; then + prev_maint_branch="$brname" + prev_maint_suffix="$suffix" + fi +} + +finish() { + if [[ "$mode" == branch_path ]] || [[ "$mode" == merge ]]; then + echo "WORKTREE=(" + for v in "${all_branch_vars[@]}"; do + echo " ${v}[@]" + done + echo ")" + elif [[ "$mode" == list ]] || [[ "$mode" == suffix ]]; then + # nothing to do + : + else + echo "unknown mode $mode" >&2 + exit 1 + fi +} + +# ============================== +# EDIT HERE +# ============================== +# List of all branches. These must be in order, from oldest to newest, with +# maint before release. + +branch maint-0.3.5 +branch release-0.3.5 + +branch maint-0.4.1 +branch release-0.4.1 + +branch maint-0.4.2 +branch release-0.4.2 + +branch maint-0.4.3 +branch release-0.4.3 + +branch master + +finish diff --git a/scripts/git/git-merge-forward.sh b/scripts/git/git-merge-forward.sh index 247c605436..7c72f8478d 100755 --- a/scripts/git/git-merge-forward.sh +++ b/scripts/git/git-merge-forward.sh @@ -91,41 +91,11 @@ TOR_WKT_NAME=${TOR_WKT_NAME:-"tor-wkt"} # But it's the earliest maint branch, so we don't merge forward into it. # Since we don't merge forward into it, the second and fifth items must be # blank (""). -MAINT_035_TB=( "maint-0.3.5" "" "$GIT_PATH/$TOR_WKT_NAME/maint-0.3.5" \ - "_035" "") -# Used in maint/release merge and test branch modes -MAINT_041=( "maint-0.4.1" "maint-0.3.5" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.1" \ - "_041" "_035") -MAINT_042=( "maint-0.4.2" "maint-0.4.1" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.2" \ - "_042" "_041") -MAINT_043=( "maint-0.4.3" "maint-0.4.2" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.3" \ - "_043" "_042") -MAINT_MASTER=( "master" "maint-0.4.3" "$GIT_PATH/$TOR_MASTER_NAME" \ - "_master" "_043") - -RELEASE_035=( "release-0.3.5" "maint-0.3.5" "$GIT_PATH/$TOR_WKT_NAME/release-0.3.5" ) -RELEASE_041=( "release-0.4.1" "maint-0.4.1" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.1" ) -RELEASE_042=( "release-0.4.2" "maint-0.4.2" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.2" ) -RELEASE_043=( "release-0.4.3" "maint-0.4.3" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.3" ) - -# The master branch path has to be the main repository thus contains the + # origin that will be used to fetch the updates. All the worktrees are created # from that repository. ORIGIN_PATH="$GIT_PATH/$TOR_MASTER_NAME" -# SC2034 -- shellcheck thinks that these are unused. We know better. -ACTUALLY_THESE_ARE_USED=<<EOF -${MAINT_035_TB[0]} -${MAINT_041[0]} -${MAINT_042[0]} -${MAINT_043[0]} -${MAINT_MASTER[0]} -${RELEASE_035[0]} -${RELEASE_041[0]} -${RELEASE_042[0]} -${RELEASE_043[0]} -EOF - ####################### # Argument processing # ####################### @@ -170,49 +140,16 @@ done # Git worktrees to manage # ########################### +set -e if [ -z "$TEST_BRANCH_PREFIX" ]; then - # maint/release merge mode - # - # List of all worktrees to merge forward into. All defined above. - # Ordering is important. Always the maint-* branch BEFORE the release-*. - WORKTREE=( - # We don't merge forward into MAINT_035_TB[@], because it's the earliest - # maint branch - RELEASE_035[@] - - MAINT_041[@] - RELEASE_041[@] - - MAINT_042[@] - RELEASE_042[@] - - MAINT_043[@] - RELEASE_043[@] - - MAINT_MASTER[@] - ) - + eval "$(git-list-tor-branches.sh -m)" + # Remove first element: we don't merge forward into it. + WORKTREE=( "${WORKTREE[@]:1}" ) else - - # Test branch mode: base test branches on maint branches only - # - # List of all worktrees to create test branches from. All defined above. - # Ordering is important. All maint-* branches, including the earliest one. - WORKTREE=( - # We want a test branch based on the earliest maint branch - MAINT_035_TB[@] - - MAINT_041[@] - - MAINT_042[@] - - MAINT_043[@] - - MAINT_MASTER[@] - ) - + eval "$(git-list-tor-branches.sh -m -R)" fi +set +e COUNT=${#WORKTREE[@]} diff --git a/scripts/git/git-pull-all.sh b/scripts/git/git-pull-all.sh index eb3e1c8881..7f82eda296 100755 --- a/scripts/git/git-pull-all.sh +++ b/scripts/git/git-pull-all.sh @@ -2,7 +2,7 @@ SCRIPT_NAME=$(basename "$0") -function usage() +usage() { echo "$SCRIPT_NAME [-h] [-n]" echo @@ -47,66 +47,15 @@ TOR_WKT_NAME=${TOR_WKT_NAME:-"tor-wkt"} # Git branches to manage # ########################## -# Configuration of the branches that need pulling. The values are in order: -# (1) Branch name to pull (update). -# (2) Full path of the git worktree. -# -# As an example: -# $ cd <PATH/TO/WORKTREE> (3) -# $ git checkout maint-0.3.5 (1) -# $ git pull -# -# First set of arrays are the maint-* branch and then the release-* branch. -# New arrays need to be in the WORKTREE= array else they aren't considered. -MAINT_035=( "maint-0.3.5" "$GIT_PATH/$TOR_WKT_NAME/maint-0.3.5" ) -MAINT_041=( "maint-0.4.1" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.1" ) -MAINT_042=( "maint-0.4.2" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.2" ) -MAINT_043=( "maint-0.4.3" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.3" ) -MAINT_MASTER=( "master" "$GIT_PATH/$TOR_MASTER_NAME" ) - -RELEASE_035=( "release-0.3.5" "$GIT_PATH/$TOR_WKT_NAME/release-0.3.5" ) -RELEASE_041=( "release-0.4.1" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.1" ) -RELEASE_042=( "release-0.4.2" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.2" ) -RELEASE_043=( "release-0.4.3" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.3" ) +set -e +eval "$(git-list-tor-branches.sh -b)" +set +e # The master branch path has to be the main repository thus contains the # origin that will be used to fetch the updates. All the worktrees are created # from that repository. ORIGIN_PATH="$GIT_PATH/$TOR_MASTER_NAME" -# SC2034 -- shellcheck thinks that these are unused. We know better. -ACTUALLY_THESE_ARE_USED=<<EOF -${MAINT_035[0]} -${MAINT_041[0]} -${MAINT_042[0]} -${MAINT_043[0]} -${MAINT_MASTER[0]} -${RELEASE_035[0]} -${RELEASE_041[0]} -${RELEASE_042[0]} -${RELEASE_043[0]} -EOF - -########################### -# Git worktrees to manage # -########################### - -# List of all worktrees to pull. All defined above. Ordering is not important. -WORKTREE=( - MAINT_035[@] - RELEASE_035[@] - - MAINT_041[@] - RELEASE_041[@] - - MAINT_042[@] - RELEASE_042[@] - - MAINT_043[@] - RELEASE_043[@] - - MAINT_MASTER[@] -) COUNT=${#WORKTREE[@]} ####################### diff --git a/scripts/git/git-push-all.sh b/scripts/git/git-push-all.sh index cb7bb5269b..558ea8d01c 100755 --- a/scripts/git/git-push-all.sh +++ b/scripts/git/git-push-all.sh @@ -168,63 +168,42 @@ echo "Calling $GIT_PUSH" "$@" "<branches>" # Git upstream remote branches # ################################ +set -e DEFAULT_UPSTREAM_BRANCHES= if [ "$DEFAULT_UPSTREAM_REMOTE" != "$UPSTREAM_REMOTE" ]; then - DEFAULT_UPSTREAM_BRANCHES=$(echo \ - "$DEFAULT_UPSTREAM_REMOTE"/master \ - "$DEFAULT_UPSTREAM_REMOTE"/{release,maint}-0.4.3 \ - "$DEFAULT_UPSTREAM_REMOTE"/{release,maint}-0.4.2 \ - "$DEFAULT_UPSTREAM_REMOTE"/{release,maint}-0.4.1 \ - "$DEFAULT_UPSTREAM_REMOTE"/{release,maint}-0.3.5 \ - ) + for br in $(git-list-tor-branches.sh -l); do + DEFAULT_UPSTREAM_BRANCHES="${DEFAULT_UPSTREAM_BRANCHES} ${DEFAULT_UPSTREAM_REMOTE}/${br}" + done fi -UPSTREAM_BRANCHES=$(echo \ - "$UPSTREAM_REMOTE"/master \ - "$UPSTREAM_REMOTE"/{release,maint}-0.4.3 \ - "$UPSTREAM_REMOTE"/{release,maint}-0.4.2 \ - "$UPSTREAM_REMOTE"/{release,maint}-0.4.1 \ - "$UPSTREAM_REMOTE"/{release,maint}-0.3.5 \ - ) +UPSTREAM_BRANCHES= +for br in $(git-list-tor-branches.sh -l); do + UPSTREAM_BRANCHES="${UPSTREAM_BRANCHES} ${UPSTREAM_REMOTE}/${br}" +done ######################## # Git branches to push # ######################## -PUSH_BRANCHES=$(echo \ - master \ - {release,maint}-0.4.3 \ - {release,maint}-0.4.2 \ - {release,maint}-0.4.1 \ - {release,maint}-0.3.5 \ - ) - if [ -z "$TEST_BRANCH_PREFIX" ]; then # maint/release push mode: push all branches. # # List of branches to push. Ordering is not important. - PUSH_BRANCHES=$(echo \ - master \ - {release,maint}-0.4.3 \ - {release,maint}-0.4.2 \ - {release,maint}-0.4.1 \ - {release,maint}-0.3.5 \ - ) + PUSH_BRANCHES="$(git-list-tor-branches.sh -l)" else # Test branch push mode: push test branches, based on each maint branch. # # List of branches to push. Ordering is not important. - PUSH_BRANCHES=" \ - ${TEST_BRANCH_PREFIX}_master \ - ${TEST_BRANCH_PREFIX}_043 \ - ${TEST_BRANCH_PREFIX}_042 \ - ${TEST_BRANCH_PREFIX}_041 \ - ${TEST_BRANCH_PREFIX}_035 \ - " + PUSH_BRANCHES="" + for suffix in $(git-list-tor-branches.sh -s -R); do + PUSH_BRANCHES="${PUSH_BRANCHES} ${TEST_BRANCH_PREFIX}${suffix}" + done fi +set +e + ############### # Entry point # ############### diff --git a/scripts/git/git-setup-dirs.sh b/scripts/git/git-setup-dirs.sh index 2d16cc1d66..1f61eb8b83 100755 --- a/scripts/git/git-setup-dirs.sh +++ b/scripts/git/git-setup-dirs.sh @@ -90,41 +90,15 @@ GITHUB_PUSH=${TOR_GITHUB_PUSH:-"No_Pushing_To_GitHub"} # The branches and worktrees need to be modified when there is a new branch, # and when an old branch is no longer supported. -# Configuration of the branches that needs merging. The values are in order: -# (0) current maint/release branch name -# (1) Full path of the git worktree -# -# First set of arrays are the maint-* branch and then the release-* branch. -# New arrays need to be in the WORKTREE= array else they aren't considered. -MAINT_035=( "maint-0.3.5" "$GIT_PATH/$TOR_WKT_NAME/maint-0.3.5" ) -MAINT_041=( "maint-0.4.1" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.1" ) -MAINT_042=( "maint-0.4.2" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.2" ) -MAINT_043=( "maint-0.4.3" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.3" ) -MAINT_MASTER=( "master" "$GIT_PATH/$TOR_MASTER_NAME" ) - -RELEASE_035=( "release-0.3.5" "$GIT_PATH/$TOR_WKT_NAME/release-0.3.5" ) -RELEASE_041=( "release-0.4.1" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.1" ) -RELEASE_042=( "release-0.4.2" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.2" ) -RELEASE_043=( "release-0.4.3" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.3" ) +set -e +eval "$(git-list-tor-branches.sh -b)" +set +e # The master branch path has to be the main repository thus contains the # origin that will be used to fetch the updates. All the worktrees are created # from that repository. ORIGIN_PATH="$GIT_PATH/$TOR_MASTER_NAME" -# SC2034 -- shellcheck thinks that these are unused. We know better. -ACTUALLY_THESE_ARE_USED=<<EOF -${MAINT_035[0]} -${MAINT_041[0]} -${MAINT_042[0]} -${MAINT_043[0]} -${MAINT_MASTER[0]} -${RELEASE_035[0]} -${RELEASE_041[0]} -${RELEASE_042[0]} -${RELEASE_043[0]} -EOF - ####################### # Argument processing # ####################### @@ -161,22 +135,6 @@ done # Git worktrees to manage # ########################### -WORKTREE=( - MAINT_035[@] - RELEASE_035[@] - - MAINT_041[@] - RELEASE_041[@] - - MAINT_042[@] - RELEASE_042[@] - - MAINT_043[@] - RELEASE_043[@] - - MAINT_MASTER[@] -) - COUNT=${#WORKTREE[@]} ############# diff --git a/scripts/maint/annotate_ifdef_directives.py b/scripts/maint/annotate_ifdef_directives.py index cd70b55c8c..9ca090d595 100755 --- a/scripts/maint/annotate_ifdef_directives.py +++ b/scripts/maint/annotate_ifdef_directives.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2017-2019, The Tor Project, Inc. # See LICENSE for licensing information diff --git a/scripts/maint/checkIncludes.py b/scripts/maint/checkIncludes.py index 2ca46347f0..ae0ccb9e12 100755 --- a/scripts/maint/checkIncludes.py +++ b/scripts/maint/checkIncludes.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright 2018 The Tor Project, Inc. See LICENSE file for licensing info. # This file is no longer here; see practracker/includes.py for this diff --git a/scripts/maint/checkManpageAlpha.py b/scripts/maint/checkManpageAlpha.py new file mode 100755 index 0000000000..70421c2fd1 --- /dev/null +++ b/scripts/maint/checkManpageAlpha.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +import difflib +import re +import sys + +# Assume we only use the "== Section Name" section title syntax +sectionheader_re = re.compile(r'^==+\s(.*)\s*$') + +# Assume we only use the "[[ItemName]]" anchor syntax +anchor_re = re.compile(r'^\[\[([^]]+)\]\]') + +class Reader(object): + def __init__(self): + self.d = {} + # Initial state is to gather section headers + self.getline = self._getsec + self.section = None + + def _getsec(self, line): + """Read a section header + + Prepare to gather anchors from subsequent lines. Don't change + state if the line isn't a section header. + """ + m = sectionheader_re.match(line) + if not m: + return + self.anchors = anchors = [] + self.d[m.group(1)] = anchors + self.getline = self._getanchor + + def _getanchor(self, line): + """Read an anchor for an item definition + + Append the anchor names to the list of items in the current + section. + """ + m = anchor_re.match(line) + if not m: + return self._getsec(line) + self.anchors.append(m.group(1)) + + def diffsort(self, key): + """Unified diff of unsorted and sorted item lists + """ + # Append newlines because difflib works better with them + a = [s + '\n' for s in self.d[key]] + b = sorted(a, key=str.lower) + return difflib.unified_diff(a, b, fromfile=key+' unsorted', + tofile=key+' sorted') + +def main(): + """Diff unsorted and sorted lists of option names in a manpage + + Use the file named by the first argument, or standard input if + there is none. + """ + try: + fname = sys.argv[1] + f = open(fname, 'r') + except IndexError: + f = sys.stdin + + reader = Reader() + for line in f: + reader.getline(line) + for key in sorted(reader.d.keys(), key=str.lower): + sys.stdout.writelines(reader.diffsort(key)) + +if __name__ == '__main__': + main() diff --git a/scripts/maint/checkSpace.pl b/scripts/maint/checkSpace.pl index f4e6f733c8..857ce6f6f1 100755 --- a/scripts/maint/checkSpace.pl +++ b/scripts/maint/checkSpace.pl @@ -23,6 +23,25 @@ if ($ARGV[0] =~ /^-/) { $C = ($lang eq '-C'); } +# hashmap of things where we allow spaces between them and (. +our %allow_space_after= map {$_, 1} qw{ + if while for switch return int unsigned elsif WINAPI + void __attribute__ op size_t double uint64_t + bool ssize_t + workqueue_reply_t hs_desc_decode_status_t + PRStatus + SMARTLIST_FOREACH_BEGIN SMARTLIST_FOREACH_END + HT_FOREACH + DIGESTMAP_FOREACH_MODIFY DIGESTMAP_FOREACH + DIGEST256MAP_FOREACH_MODIFY DIGEST256MAP_FOREACH + STRMAP_FOREACH_MODIFY STRMAP_FOREACH + SDMAP_FOREACH EIMAP_FOREACH RIMAP_FOREACH + MAP_FOREACH_MODIFY MAP_FOREACH + TOR_SIMPLEQ_FOREACH TOR_SIMPLEQ_FOREACH_SAFE + TOR_LIST_FOREACH TOR_LIST_FOREACH_SAFE + TOR_SLIST_FOREACH TOR_SLIST_FOREACH_SAFE +}; + our %basenames = (); our %guardnames = (); @@ -58,9 +77,9 @@ for my $fn (@ARGV) { } ## Warn about labels that don't have a space in front of them # (We indent every label at least one space) - if (/^[a-zA-Z_][a-zA-Z_0-9]*:/) { - msg "nosplabel:$fn:$.\n"; - } + #if (/^[a-zA-Z_][a-zA-Z_0-9]*:/) { + # msg "nosplabel:$fn:$.\n"; + #} ## Warn about trailing whitespace. # (We don't allow whitespace at the end of the line; make your # editor highlight it for you so you can stop adding it in.) @@ -111,7 +130,7 @@ for my $fn (@ARGV) { ## Terminals are still 80 columns wide in my world. I refuse to ## accept double-line lines. # (Don't make lines wider than 80 characters, including newline.) - if (/^.{80}/) { + if (/^.{80}/ and not /LCOV_EXCL/) { msg "Wide:$fn:$.\n"; } ### Juju to skip over comments and strings, since the tests @@ -128,12 +147,12 @@ for my $fn (@ARGV) { if ($isheader) { if ($seenguard == 0) { - if (/ifndef\s+(\S+)/) { + if (/^\s*\#\s*ifndef\s+(\S+)/) { ++$seenguard; $guardname = $1; } } elsif ($seenguard == 1) { - if (/^\#define (\S+)/) { + if (/^\s*\#\s*define (\S+)/) { ++$seenguard; if ($1 ne $guardname) { msg "GUARD:$fn:$.: Header guard macro mismatch.\n"; @@ -156,9 +175,8 @@ for my $fn (@ARGV) { # msg "//:$fn:$.\n"; s!//.*!!; } - ## Warn about unquoted braces preceded by non-space. - # (No character except a space should come before a {) - if (/([^\s'])\{/) { + ## Warn about unquoted braces preceded by unexpected character. + if (/([^\s'\)\(\{])\{/) { msg "$1\{:$fn:$.\n"; } ## Warn about double semi-colons at the end of a line. @@ -178,12 +196,7 @@ for my $fn (@ARGV) { # (Don't put a space between the name of a function and its # arguments.) if (/(\w+)\s\(([A-Z]*)/) { - if ($1 ne "if" and $1 ne "while" and $1 ne "for" and - $1 ne "switch" and $1 ne "return" and $1 ne "int" and - $1 ne "elsif" and $1 ne "WINAPI" and $2 ne "WINAPI" and - $1 ne "void" and $1 ne "__attribute__" and $1 ne "op" and - $1 ne "size_t" and $1 ne "double" and $1 ne "uint64_t" and - $1 ne "workqueue_reply_t" and $1 ne "bool") { + if (! $allow_space_after{$1} && $2 ne 'WINAPI') { msg "fn ():$fn:$.\n"; } } @@ -194,8 +207,8 @@ for my $fn (@ARGV) { if ($in_func_head || ($fn !~ /\.h$/ && /^[a-zA-Z0-9_]/ && ! /^(?:const |static )*(?:typedef|struct|union)[^\(]*$/ && - ! /= *\{$/ && ! /;$/)) { - if (/.\{$/){ + ! /= *\{$/ && ! /;$/) && ! /^[a-zA-Z0-9_]+\s*:/) { + if (/[^,\s]\s*\{$/){ msg "fn() {:$fn:$.\n"; $in_func_head = 0; } elsif (/^\S[^\(]* +\**[a-zA-Z0-9_]+\(/) { diff --git a/scripts/maint/checkspace_tests/expected.txt b/scripts/maint/checkspace_tests/expected.txt index 935b750ef9..38595ed373 100644 --- a/scripts/maint/checkspace_tests/expected.txt +++ b/scripts/maint/checkspace_tests/expected.txt @@ -5,7 +5,6 @@ tp fn():./dubious.c:15 Wide:./dubious.c:17 TAB:./dubious.c:24 - nosplabel:./dubious.c:26 CR:./dubious.c:30 Space@EOL:./dubious.c:32 non-K&R {:./dubious.c:39 diff --git a/scripts/maint/clang-format.sh b/scripts/maint/clang-format.sh new file mode 100755 index 0000000000..59832117b4 --- /dev/null +++ b/scripts/maint/clang-format.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# Copyright 2020, The Tor Project, Inc. +# See LICENSE for licensing information. + +# +# DO NOT COMMIT OR MERGE CODE THAT IS RUN THROUGH THIS TOOL YET. +# +# WE ARE STILL DISCUSSING OUR DESIRED STYLE AND ITERATING ON IT. +# (12 Feb 2020) +# + +# This script runs "clang-format" and "codetool" in sequence over each of +# our source files, and replaces the original file only if it has changed. +# +# We can't just use clang-format -i, since we also want to use codetool to +# reformat a few things back to how we want them, and we want avoid changing +# the mtime on files that didn't actually change. + +set -e + +cd "$(dirname "$0")/../../src/" + +# Shellcheck complains that a for loop over find's output is unreliable, +# since there might be special characters in the output. But we happen +# to know that none of our C files have special characters or spaces in +# their names, so this is safe. +# +# shellcheck disable=SC2044 +for fname in $(find lib core feature app test tools -name '[^.]*.[ch]'); do + tmpfname="${fname}.clang_fmt.tmp" + rm -f "${tmpfname}" + clang-format --style=file "${fname}" > "${tmpfname}" + ../scripts/maint/codetool.py "${tmpfname}" + if cmp "${fname}" "${tmpfname}" >/dev/null 2>&1; then + echo "No change in ${fname}" + rm -f "${tmpfname}" + else + echo "Change in ${fname}" + mv "${tmpfname}" "${fname}" + fi +done diff --git a/scripts/maint/codetool.py b/scripts/maint/codetool.py new file mode 100755 index 0000000000..725712c0cc --- /dev/null +++ b/scripts/maint/codetool.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020, The Tor Project, Inc. +# See LICENSE for licensing information. + +# +# DO NOT COMMIT OR MERGE CODE THAT IS RUN THROUGH THIS TOOL YET. +# +# WE ARE STILL DISCUSSING OUR DESIRED STYLE AND ITERATING ON IT, +# ALONG WITH THE TOOLS THAT ACHIEVE IT. +# (12 Feb 2020) +# + +""" + This program uses a set of plugable filters to inspect and transform + our C code. +""" + +import os +import re +import sys + +class Filter: + """A Filter transforms a string containing a C program.""" + def __init__(self): + pass + + def transform(self, s): + return s + +class CompoundFilt(Filter): + """A CompoundFilt runs another set of filters, in sequence.""" + def __init__(self, items=()): + super().__init__() + self._filters = list(items) + + def add(self, filt): + self._filters.append(filt) + return self + + def transform(self, s): + for f in self._filters: + s = f.transform(s) + + return s + +class SplitError(Exception): + """Exception: raised if split_comments() can't understand a C file.""" + pass + +def split_comments(s): + r"""Iterate over the C code in 's', and yield a sequence of (code, + comment) pairs. Each pair will contain either a nonempty piece + of code, a nonempty comment, or both. + + >>> list(split_comments("hello // world\n")) + [('hello ', '// world'), ('\n', '')] + + >>> list(split_comments("a /* b cd */ efg // hi")) + [('a ', '/* b cd */'), (' efg ', '// hi')] + """ + + # Matches a block of code without any comments. + PAT_CODE = re.compile(r'''^(?: [^/"']+ | + "(?:[^\\"]+|\\.)*" | + '(?:[^\\']+|\\.)*' | + /[^/*] + )*''', re.VERBOSE|re.DOTALL) + + # Matches a C99 "//" comment. + PAT_C99_COMMENT = re.compile(r'^//.*$', re.MULTILINE) + + # Matches a C "/* */" comment. + PAT_C_COMMENT = re.compile(r'^/\*(?:[^*]|\*+[^*/])*\*+/', re.DOTALL) + + while True: + # Find some non-comment code at the start of the string. + m = PAT_CODE.match(s) + + # If we found some code here, save it and advance the string. + # Otherwise set 'code' to "". + if m: + code = m.group(0) + s = s[m.end():] + else: + code = "" + + # Now we have a comment, or the end of the string. Find out which + # one, and how long it is. + if s.startswith("//"): + m = PAT_C99_COMMENT.match(s) + else: + m = PAT_C_COMMENT.match(s) + + # If we got a comment, save it and advance the string. Otherwise + # set 'comment' to "". + if m: + comment = m.group(0) + s = s[m.end():] + else: + comment = "" + + # If we found no code and no comment, we should be at the end of + # the string... + if code == "" and comment == "": + if s: + # But in case we *aren't* at the end of the string, raise + # an error. + raise SplitError() + # ... all is well, we're done scanning the code. + return + + yield (code, comment) + +class IgnoreCommentsFilt(Filter): + """Wrapper: applies another filter to C code only, excluding comments. + """ + def __init__(self, filt): + super().__init__() + self._filt = filt + + def transform(self, s): + result = [] + for code, comment in split_comments(s): + result.append(self._filt.transform(code)) + result.append(comment) + return "".join(result) + + +class RegexFilt(Filter): + """A regex filter applies a regular expression to some C code.""" + def __init__(self, pat, replacement, flags=0): + super().__init__() + self._pat = re.compile(pat, flags) + self._replacement = replacement + + def transform(self, s): + s, _ = self._pat.subn(self._replacement, s) + return s + +def revise(fname, filt): + """Run 'filt' on the contents of the file in 'fname'. If any + changes are made, then replace the file with its new contents. + Otherwise, leave the file alone. + """ + contents = open(fname, 'r').read() + result = filt.transform(contents) + if result == contents: + return + + tmpname = "{}_codetool_tmp".format(fname) + try: + with open(tmpname, 'w') as f: + f.write(result) + os.rename(tmpname, fname) + except: + os.unlink(tmpname) + raise + +############################## +# Filtering rules. +############################## + +# Make sure that there is a newline after the first comma in a MOCK_IMPL() +BREAK_MOCK_IMPL = RegexFilt( + r'^MOCK_IMPL\(([^,]+),\s*(\S+)', + r'MOCK_IMPL(\1,\n\2', + re.MULTILINE) + +# Make sure there is no newline between } and a loop iteration terminator. +RESTORE_SMARTLIST_END = RegexFilt( + r'}\s*(SMARTLIST|DIGESTMAP|DIGEST256MAP|STRMAP|MAP)_FOREACH_END\s*\(', + r'} \1_FOREACH_END (', + re.MULTILINE) + +F = CompoundFilt() +F.add(IgnoreCommentsFilt(CompoundFilt([ + RESTORE_SMARTLIST_END, + BREAK_MOCK_IMPL]))) + +if __name__ == '__main__': + for fname in sys.argv[1:]: + revise(fname, F) diff --git a/scripts/maint/format_changelog.py b/scripts/maint/format_changelog.py index 32085c3602..7cf55a0d96 100755 --- a/scripts/maint/format_changelog.py +++ b/scripts/maint/format_changelog.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2014-2019, The Tor Project, Inc. # See LICENSE for licensing information # diff --git a/scripts/maint/lintChanges.py b/scripts/maint/lintChanges.py index 88a865a572..cf7b09fcc3 100755 --- a/scripts/maint/lintChanges.py +++ b/scripts/maint/lintChanges.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Future imports for Python 2.7, mandatory in 3.0 from __future__ import division @@ -25,7 +25,12 @@ KNOWN_GROUPS = set([ "Code simplification and refactoring", "Removed features", "Deprecated features", - "Directory authority changes"]) + "Directory authority changes", + + # These aren't preferred, but sortChanges knows how to clean them up. + "Code simplifications and refactoring", + "Code simplification and refactorings", + "Code simplifications and refactorings"]) NEEDS_SUBCATEGORIES = set([ "Minor bugfix", diff --git a/scripts/maint/locatemissingdoxygen.py b/scripts/maint/locatemissingdoxygen.py index 7733977359..a2844346d6 100755 --- a/scripts/maint/locatemissingdoxygen.py +++ b/scripts/maint/locatemissingdoxygen.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python """ This script parses the stderr output of doxygen and looks for undocumented diff --git a/scripts/maint/practracker/exceptions.txt b/scripts/maint/practracker/exceptions.txt index 70e6a55199..f41a8f4c81 100644 --- a/scripts/maint/practracker/exceptions.txt +++ b/scripts/maint/practracker/exceptions.txt @@ -33,23 +33,22 @@ # # Remember: It is better to fix the problem than to add a new exception! -problem file-size /src/app/config/config.c 7400 +problem file-size /src/app/config/config.c 7527 problem include-count /src/app/config/config.c 80 -problem function-size /src/app/config/config.c:options_act_reversible() 298 problem function-size /src/app/config/config.c:options_act() 381 -problem function-size /src/app/config/config.c:resolve_my_address() 190 -problem function-size /src/app/config/config.c:options_validate_cb() 780 +problem function-size /src/app/config/config.c:resolve_my_address() 191 +problem function-size /src/app/config/config.c:options_validate_cb() 794 problem function-size /src/app/config/config.c:options_init_from_torrc() 188 problem function-size /src/app/config/config.c:options_init_from_string() 103 problem function-size /src/app/config/config.c:options_init_logs() 125 problem function-size /src/app/config/config.c:parse_bridge_line() 104 -problem function-size /src/app/config/config.c:pt_parse_transport_line() 189 +problem function-size /src/app/config/config.c:pt_parse_transport_line() 190 problem function-size /src/app/config/config.c:parse_dir_authority_line() 150 problem function-size /src/app/config/config.c:parse_dir_fallback_line() 101 problem function-size /src/app/config/config.c:port_parse_config() 450 problem function-size /src/app/config/config.c:parse_ports() 132 -problem file-size /src/app/config/or_options_st.h 1115 -problem include-count /src/app/main/main.c 69 +problem file-size /src/app/config/or_options_st.h 1050 +problem include-count /src/app/main/main.c 68 problem function-size /src/app/main/main.c:dumpstats() 102 problem function-size /src/app/main/main.c:tor_init() 101 problem function-size /src/app/main/main.c:sandbox_init_filter() 291 @@ -61,22 +60,21 @@ problem dependency-violation /src/core/crypto/onion_crypto.c 5 problem dependency-violation /src/core/crypto/onion_fast.c 1 problem dependency-violation /src/core/crypto/onion_tap.c 3 problem dependency-violation /src/core/crypto/relay_crypto.c 9 -problem file-size /src/core/mainloop/connection.c 5577 -problem include-count /src/core/mainloop/connection.c 62 -problem function-size /src/core/mainloop/connection.c:connection_free_minimal() 185 +problem file-size /src/core/mainloop/connection.c 5680 +problem include-count /src/core/mainloop/connection.c 65 +problem function-size /src/core/mainloop/connection.c:connection_free_minimal() 181 problem function-size /src/core/mainloop/connection.c:connection_listener_new() 324 problem function-size /src/core/mainloop/connection.c:connection_handle_listener_read() 161 -problem function-size /src/core/mainloop/connection.c:connection_proxy_connect() 148 problem function-size /src/core/mainloop/connection.c:connection_read_proxy_handshake() 153 problem function-size /src/core/mainloop/connection.c:retry_listener_ports() 112 problem function-size /src/core/mainloop/connection.c:connection_handle_read_impl() 111 problem function-size /src/core/mainloop/connection.c:connection_buf_read_from_socket() 180 problem function-size /src/core/mainloop/connection.c:connection_handle_write_impl() 241 problem function-size /src/core/mainloop/connection.c:assert_connection_ok() 143 -problem dependency-violation /src/core/mainloop/connection.c 44 +problem dependency-violation /src/core/mainloop/connection.c 47 problem dependency-violation /src/core/mainloop/cpuworker.c 12 problem include-count /src/core/mainloop/mainloop.c 64 -problem function-size /src/core/mainloop/mainloop.c:conn_close_if_marked() 108 +problem function-size /src/core/mainloop/mainloop.c:conn_close_if_marked() 107 problem function-size /src/core/mainloop/mainloop.c:run_connection_housekeeping() 123 problem dependency-violation /src/core/mainloop/mainloop.c 50 problem dependency-violation /src/core/mainloop/mainloop_pubsub.c 1 @@ -85,9 +83,9 @@ problem dependency-violation /src/core/mainloop/netstatus.c 4 problem dependency-violation /src/core/mainloop/periodic.c 2 problem dependency-violation /src/core/or/address_set.c 1 problem dependency-violation /src/core/or/cell_queue_st.h 1 -problem file-size /src/core/or/channel.c 3487 +problem file-size /src/core/or/channel.c 3464 problem dependency-violation /src/core/or/channel.c 9 -problem file-size /src/core/or/channel.h 781 +problem file-size /src/core/or/channel.h 775 problem dependency-violation /src/core/or/channel.h 1 problem dependency-violation /src/core/or/channelpadding.c 6 problem function-size /src/core/or/channeltls.c:channel_tls_handle_var_cell() 160 @@ -95,7 +93,7 @@ problem function-size /src/core/or/channeltls.c:channel_tls_process_versions_cel problem function-size /src/core/or/channeltls.c:channel_tls_process_netinfo_cell() 214 problem function-size /src/core/or/channeltls.c:channel_tls_process_certs_cell() 246 problem function-size /src/core/or/channeltls.c:channel_tls_process_authenticate_cell() 202 -problem dependency-violation /src/core/or/channeltls.c 10 +problem dependency-violation /src/core/or/channeltls.c 11 problem include-count /src/core/or/circuitbuild.c 54 problem function-size /src/core/or/circuitbuild.c:get_unique_circ_id_by_chan() 128 problem function-size /src/core/or/circuitbuild.c:circuit_extend() 147 @@ -105,14 +103,13 @@ problem include-count /src/core/or/circuitlist.c 55 problem function-size /src/core/or/circuitlist.c:HT_PROTOTYPE() 109 problem function-size /src/core/or/circuitlist.c:circuit_free_() 146 problem function-size /src/core/or/circuitlist.c:circuit_find_to_cannibalize() 101 -problem function-size /src/core/or/circuitlist.c:circuit_about_to_free() 120 problem function-size /src/core/or/circuitlist.c:circuits_handle_oom() 117 problem dependency-violation /src/core/or/circuitlist.c 19 problem dependency-violation /src/core/or/circuitlist.h 1 problem function-size /src/core/or/circuitmux.c:circuitmux_set_policy() 109 problem function-size /src/core/or/circuitmux.c:circuitmux_attach_circuit() 113 problem dependency-violation /src/core/or/circuitmux_ewma.c 2 -problem file-size /src/core/or/circuitpadding.c 3098 +problem file-size /src/core/or/circuitpadding.c 3101 problem function-size /src/core/or/circuitpadding.c:circpad_machine_schedule_padding() 113 problem dependency-violation /src/core/or/circuitpadding.c 6 problem file-size /src/core/or/circuitpadding.h 813 @@ -121,9 +118,9 @@ problem function-size /src/core/or/circuitpadding_machines.c:circpad_machine_cli problem dependency-violation /src/core/or/circuitpadding_machines.c 1 problem function-size /src/core/or/circuitstats.c:circuit_build_times_parse_state() 123 problem dependency-violation /src/core/or/circuitstats.c 11 -problem file-size /src/core/or/circuituse.c 3162 +problem file-size /src/core/or/circuituse.c 3195 problem function-size /src/core/or/circuituse.c:circuit_is_acceptable() 128 -problem function-size /src/core/or/circuituse.c:circuit_expire_building() 394 +problem function-size /src/core/or/circuituse.c:circuit_expire_building() 389 problem function-size /src/core/or/circuituse.c:circuit_log_ancient_one_hop_circuits() 126 problem function-size /src/core/or/circuituse.c:circuit_build_failed() 149 problem function-size /src/core/or/circuituse.c:circuit_launch_by_extend_info() 108 @@ -133,7 +130,7 @@ problem dependency-violation /src/core/or/circuituse.c 24 problem function-size /src/core/or/command.c:command_process_create_cell() 156 problem function-size /src/core/or/command.c:command_process_relay_cell() 132 problem dependency-violation /src/core/or/command.c 8 -problem file-size /src/core/or/connection_edge.c 4640 +problem file-size /src/core/or/connection_edge.c 4655 problem include-count /src/core/or/connection_edge.c 65 problem function-size /src/core/or/connection_edge.c:connection_ap_expire_beginning() 117 problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_rewrite() 193 @@ -145,19 +142,16 @@ problem function-size /src/core/or/connection_edge.c:connection_exit_begin_conn( problem function-size /src/core/or/connection_edge.c:connection_exit_connect() 102 problem dependency-violation /src/core/or/connection_edge.c 27 problem dependency-violation /src/core/or/connection_edge.h 1 -problem file-size /src/core/or/connection_or.c 3122 -problem include-count /src/core/or/connection_or.c 51 problem function-size /src/core/or/connection_or.c:connection_or_group_set_badness_() 105 problem function-size /src/core/or/connection_or.c:connection_or_client_learned_peer_id() 142 -problem function-size /src/core/or/connection_or.c:connection_or_compute_authenticate_cell_body() 231 -problem dependency-violation /src/core/or/connection_or.c 20 +problem dependency-violation /src/core/or/connection_or.c 21 problem dependency-violation /src/core/or/dos.c 6 problem dependency-violation /src/core/or/onion.c 2 problem file-size /src/core/or/or.h 1107 -problem include-count /src/core/or/or.h 49 +problem include-count /src/core/or/or.h 48 problem dependency-violation /src/core/or/or.h 1 problem dependency-violation /src/core/or/or_periodic.c 1 -problem file-size /src/core/or/policies.c 3249 +problem file-size /src/core/or/policies.c 3182 problem function-size /src/core/or/policies.c:policy_summarize() 107 problem dependency-violation /src/core/or/policies.c 14 problem function-size /src/core/or/protover.c:protover_all_supported() 117 @@ -177,7 +171,7 @@ problem dependency-violation /src/core/or/scheduler_kist.c 2 problem function-size /src/core/or/scheduler_vanilla.c:vanilla_scheduler_run() 109 problem dependency-violation /src/core/or/scheduler_vanilla.c 1 problem dependency-violation /src/core/or/sendme.c 2 -problem dependency-violation /src/core/or/status.c 12 +problem dependency-violation /src/core/or/status.c 13 problem function-size /src/core/or/versions.c:tor_version_parse() 104 problem dependency-violation /src/core/proto/proto_cell.c 3 problem dependency-violation /src/core/proto/proto_control0.c 1 @@ -186,53 +180,53 @@ problem dependency-violation /src/core/proto/proto_http.c 1 problem function-size /src/core/proto/proto_socks.c:parse_socks_client() 110 problem dependency-violation /src/core/proto/proto_socks.c 8 problem function-size /src/feature/client/addressmap.c:addressmap_rewrite() 109 -problem function-size /src/feature/client/bridges.c:rewrite_node_address_for_bridge() 126 +problem function-size /src/feature/client/bridges.c:rewrite_node_address_for_bridge() 125 problem function-size /src/feature/client/circpathbias.c:pathbias_measure_close_rate() 108 problem function-size /src/feature/client/dnsserv.c:evdns_server_callback() 153 -problem file-size /src/feature/client/entrynodes.c 3825 +problem file-size /src/feature/client/entrynodes.c 3827 problem function-size /src/feature/client/entrynodes.c:entry_guards_upgrade_waiting_circuits() 155 problem function-size /src/feature/client/entrynodes.c:entry_guard_parse_from_state() 246 problem file-size /src/feature/client/entrynodes.h 639 problem function-size /src/feature/client/transports.c:handle_proxy_line() 108 problem function-size /src/feature/client/transports.c:parse_method_line_helper() 110 -problem function-size /src/feature/client/transports.c:create_managed_proxy_environment() 109 +problem function-size /src/feature/client/transports.c:create_managed_proxy_environment() 111 problem function-size /src/feature/control/control.c:connection_control_process_inbuf() 113 problem function-size /src/feature/control/control_auth.c:handle_control_authenticate() 186 problem function-size /src/feature/control/control_cmd.c:handle_control_extendcircuit() 150 problem function-size /src/feature/control/control_cmd.c:handle_control_add_onion() 256 problem function-size /src/feature/control/control_cmd.c:add_onion_helper_keyarg() 118 problem function-size /src/feature/control/control_events.c:control_event_stream_status() 124 -problem include-count /src/feature/control/control_getinfo.c 54 +problem include-count /src/feature/control/control_getinfo.c 56 problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_misc() 108 problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_dir() 297 problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_events() 234 problem function-size /src/feature/dirauth/bwauth.c:dirserv_read_measured_bandwidths() 121 -problem file-size /src/feature/dirauth/dirvote.c 4700 -problem include-count /src/feature/dirauth/dirvote.c 53 -problem function-size /src/feature/dirauth/dirvote.c:format_networkstatus_vote() 231 +problem file-size /src/feature/dirauth/dirvote.c 4734 +problem include-count /src/feature/dirauth/dirvote.c 55 +problem function-size /src/feature/dirauth/dirvote.c:format_networkstatus_vote() 230 problem function-size /src/feature/dirauth/dirvote.c:networkstatus_compute_bw_weights_v10() 233 -problem function-size /src/feature/dirauth/dirvote.c:networkstatus_compute_consensus() 956 +problem function-size /src/feature/dirauth/dirvote.c:networkstatus_compute_consensus() 952 problem function-size /src/feature/dirauth/dirvote.c:networkstatus_add_detached_signatures() 119 -problem function-size /src/feature/dirauth/dirvote.c:dirvote_add_vote() 162 +problem function-size /src/feature/dirauth/dirvote.c:dirvote_add_vote() 161 problem function-size /src/feature/dirauth/dirvote.c:dirvote_compute_consensuses() 164 -problem function-size /src/feature/dirauth/dirvote.c:dirserv_generate_networkstatus_vote_obj() 283 +problem function-size /src/feature/dirauth/dirvote.c:dirserv_generate_networkstatus_vote_obj() 281 problem function-size /src/feature/dirauth/dsigs_parse.c:networkstatus_parse_detached_signatures() 196 problem function-size /src/feature/dirauth/guardfraction.c:dirserv_read_guardfraction_file_from_str() 109 problem function-size /src/feature/dirauth/process_descs.c:dirserv_add_descriptor() 125 problem function-size /src/feature/dirauth/shared_random.c:should_keep_commit() 109 -problem function-size /src/feature/dirauth/voteflags.c:dirserv_compute_performance_thresholds() 172 +problem function-size /src/feature/dirauth/voteflags.c:dirserv_compute_performance_thresholds() 175 problem function-size /src/feature/dircache/consdiffmgr.c:consdiffmgr_cleanup() 115 problem function-size /src/feature/dircache/consdiffmgr.c:consdiffmgr_rescan_flavor_() 111 problem function-size /src/feature/dircache/consdiffmgr.c:consensus_diff_worker_threadfn() 132 problem function-size /src/feature/dircache/dircache.c:handle_get_current_consensus() 165 problem function-size /src/feature/dircache/dircache.c:directory_handle_command_post() 124 -problem file-size /src/feature/dirclient/dirclient.c 3165 -problem include-count /src/feature/dirclient/dirclient.c 51 +problem file-size /src/feature/dirclient/dirclient.c 3204 +problem include-count /src/feature/dirclient/dirclient.c 54 problem function-size /src/feature/dirclient/dirclient.c:directory_get_from_dirserver() 126 problem function-size /src/feature/dirclient/dirclient.c:directory_initiate_request() 201 problem function-size /src/feature/dirclient/dirclient.c:directory_send_command() 239 problem function-size /src/feature/dirclient/dirclient.c:dir_client_decompress_response_body() 111 -problem function-size /src/feature/dirclient/dirclient.c:connection_dir_client_reached_eof() 189 +problem function-size /src/feature/dirclient/dirclient.c:connection_dir_client_reached_eof() 199 problem function-size /src/feature/dirclient/dirclient.c:handle_response_fetch_consensus() 104 problem function-size /src/feature/dircommon/consdiff.c:gen_ed_diff() 203 problem function-size /src/feature/dircommon/consdiff.c:apply_ed_diff() 158 @@ -241,50 +235,49 @@ problem function-size /src/feature/dirparse/ns_parse.c:routerstatus_parse_entry_ problem function-size /src/feature/dirparse/ns_parse.c:networkstatus_verify_bw_weights() 389 problem function-size /src/feature/dirparse/ns_parse.c:networkstatus_parse_vote_from_string() 635 problem function-size /src/feature/dirparse/parsecommon.c:tokenize_string() 101 -problem function-size /src/feature/dirparse/parsecommon.c:get_next_token() 158 +problem function-size /src/feature/dirparse/parsecommon.c:get_next_token() 165 problem function-size /src/feature/dirparse/routerparse.c:router_parse_entry_from_string() 554 problem function-size /src/feature/dirparse/routerparse.c:extrainfo_parse_entry_from_string() 208 problem function-size /src/feature/hibernate/hibernate.c:accounting_parse_options() 109 problem function-size /src/feature/hs/hs_cell.c:hs_cell_build_establish_intro() 115 -problem function-size /src/feature/hs/hs_cell.c:hs_cell_parse_introduce2() 152 -problem function-size /src/feature/hs/hs_client.c:send_introduce1() 103 -problem function-size /src/feature/hs/hs_client.c:hs_config_client_authorization() 107 +problem function-size /src/feature/hs/hs_cell.c:hs_cell_parse_introduce2() 134 +problem function-size /src/feature/hs/hs_client.c:send_introduce1() 108 problem function-size /src/feature/hs/hs_common.c:hs_get_responsible_hsdirs() 102 -problem function-size /src/feature/hs/hs_config.c:config_service_v3() 107 +problem function-size /src/feature/hs/hs_config.c:config_service_v3() 128 problem function-size /src/feature/hs/hs_config.c:config_generic_service() 138 -problem function-size /src/feature/hs/hs_descriptor.c:desc_encode_v3() 101 problem function-size /src/feature/hs/hs_descriptor.c:decrypt_desc_layer() 111 problem function-size /src/feature/hs/hs_descriptor.c:decode_introduction_point() 122 problem function-size /src/feature/hs/hs_descriptor.c:desc_decode_superencrypted_v3() 107 problem function-size /src/feature/hs/hs_descriptor.c:desc_decode_encrypted_v3() 109 -problem file-size /src/feature/hs/hs_service.c 4172 +problem file-size /src/feature/hs/hs_service.c 4247 problem function-size /src/feature/keymgt/loadkey.c:ed_key_init_from_file() 326 problem function-size /src/feature/nodelist/authcert.c:trusted_dirs_load_certs_from_string() 123 problem function-size /src/feature/nodelist/authcert.c:authority_certs_fetch_missing() 295 -problem function-size /src/feature/nodelist/fmt_routerstatus.c:routerstatus_format_entry() 162 +problem function-size /src/feature/nodelist/fmt_routerstatus.c:routerstatus_format_entry() 158 problem function-size /src/feature/nodelist/microdesc.c:microdesc_cache_rebuild() 134 -problem include-count /src/feature/nodelist/networkstatus.c 63 +problem include-count /src/feature/nodelist/networkstatus.c 65 problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_check_consensus_signature() 175 problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_set_current_consensus() 289 problem function-size /src/feature/nodelist/node_select.c:router_pick_directory_server_impl() 122 -problem function-size /src/feature/nodelist/node_select.c:compute_weighted_bandwidths() 203 +problem function-size /src/feature/nodelist/node_select.c:compute_weighted_bandwidths() 204 problem function-size /src/feature/nodelist/node_select.c:router_pick_trusteddirserver_impl() 112 problem function-size /src/feature/nodelist/nodelist.c:compute_frac_paths_available() 190 -problem file-size /src/feature/nodelist/routerlist.c 3239 +problem file-size /src/feature/nodelist/routerlist.c 3247 problem function-size /src/feature/nodelist/routerlist.c:router_rebuild_store() 148 problem function-size /src/feature/nodelist/routerlist.c:router_add_to_routerlist() 168 problem function-size /src/feature/nodelist/routerlist.c:routerlist_remove_old_routers() 121 -problem function-size /src/feature/nodelist/routerlist.c:update_consensus_router_descriptor_downloads() 135 +problem function-size /src/feature/nodelist/routerlist.c:update_consensus_router_descriptor_downloads() 142 problem function-size /src/feature/nodelist/routerlist.c:update_extrainfo_downloads() 103 problem function-size /src/feature/relay/dns.c:dns_resolve_impl() 131 problem function-size /src/feature/relay/dns.c:configure_nameservers() 161 problem function-size /src/feature/relay/dns.c:evdns_callback() 108 -problem file-size /src/feature/relay/router.c 3520 +problem function-size /src/feature/relay/relay_handshake.c:connection_or_compute_authenticate_cell_body() 231 +problem file-size /src/feature/relay/router.c 3600 problem include-count /src/feature/relay/router.c 57 -problem function-size /src/feature/relay/router.c:init_keys() 252 +problem function-size /src/feature/relay/router.c:init_keys() 254 problem function-size /src/feature/relay/router.c:get_my_declared_family() 114 -problem function-size /src/feature/relay/router.c:router_build_fresh_unsigned_routerinfo() 136 -problem function-size /src/feature/relay/router.c:router_dump_router_to_string() 367 +problem function-size /src/feature/relay/router.c:router_build_fresh_unsigned_routerinfo() 113 +problem function-size /src/feature/relay/router.c:router_dump_router_to_string() 372 problem function-size /src/feature/relay/routerkeys.c:load_ed_keys() 294 problem function-size /src/feature/rend/rendcache.c:rend_cache_store_v2_desc_as_client() 190 problem function-size /src/feature/rend/rendclient.c:rend_client_send_introduction() 219 @@ -319,7 +312,6 @@ problem function-size /src/lib/net/address.c:tor_addr_parse_mask_ports() 194 problem function-size /src/lib/net/address.c:tor_addr_compare_masked() 110 problem function-size /src/lib/net/inaddr.c:tor_inet_pton() 107 problem function-size /src/lib/net/socketpair.c:tor_ersatz_socketpair() 102 -problem function-size /src/lib/osinfo/uname.c:get_uname() 116 problem function-size /src/lib/process/process_unix.c:process_unix_exec() 213 problem function-size /src/lib/process/process_win32.c:process_win32_exec() 151 problem function-size /src/lib/process/process_win32.c:process_win32_create_pipe() 109 diff --git a/scripts/maint/practracker/includes.py b/scripts/maint/practracker/includes.py index fe0f32e253..e9b02c35b0 100755 --- a/scripts/maint/practracker/includes.py +++ b/scripts/maint/practracker/includes.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright 2018 The Tor Project, Inc. See LICENSE file for licensing info. """This script looks through all the directories for files matching *.c or diff --git a/scripts/maint/practracker/metrics.py b/scripts/maint/practracker/metrics.py index ae88b84f31..300a4501a9 100644 --- a/scripts/maint/practracker/metrics.py +++ b/scripts/maint/practracker/metrics.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Implementation of various source code metrics. # These are currently ad-hoc string operations and regexps. diff --git a/scripts/maint/practracker/practracker.py b/scripts/maint/practracker/practracker.py index 6149fb79cb..76ffd64cfb 100755 --- a/scripts/maint/practracker/practracker.py +++ b/scripts/maint/practracker/practracker.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python """ Best-practices tracker for Tor source code. @@ -24,7 +24,7 @@ from __future__ import division from __future__ import print_function from __future__ import unicode_literals -import os, sys +import codecs, os, sys import metrics import util @@ -63,12 +63,8 @@ TOR_TOPDIR = None ####################################################### -if sys.version_info[0] <= 2: - def open_file(fname): - return open(fname, 'r') -else: - def open_file(fname): - return open(fname, 'r', encoding='utf-8') +def open_file(fname): + return codecs.open(fname, 'r', encoding='utf-8') def consider_file_size(fname, f): """Consider the size of 'f' and yield an FileSizeItem for it. diff --git a/scripts/maint/practracker/practracker_tests.py b/scripts/maint/practracker/practracker_tests.py index 8d0418880c..e03c9e05ae 100755 --- a/scripts/maint/practracker/practracker_tests.py +++ b/scripts/maint/practracker/practracker_tests.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python """Some simple tests for practracker metrics""" diff --git a/scripts/maint/rectify_include_paths.py b/scripts/maint/rectify_include_paths.py index 111cf816ce..6c7b252535 100755 --- a/scripts/maint/rectify_include_paths.py +++ b/scripts/maint/rectify_include_paths.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Future imports for Python 2.7, mandatory in 3.0 from __future__ import division @@ -29,6 +29,12 @@ def get_include_map(): exclude(["ext", "win32"], dirnames) for fname in fnames: + # Avoid editor temporary files + if fname.startswith("."): + continue + if fname.startswith("#"): + continue + if fname.endswith(".h"): if fname in includes: warn("Multiple headers named %s"%fname) @@ -63,6 +69,12 @@ for dirpath,dirnames,fnames in os.walk("src"): exclude(["trunnel"], dirnames) for fname in fnames: + # Avoid editor temporary files + if fname.startswith("."): + continue + if fname.startswith("#"): + continue + if fname.endswith(".c") or fname.endswith(".h"): fname = os.path.join(dirpath, fname) tmpfile = fname+".tmp" diff --git a/scripts/maint/redox.py b/scripts/maint/redox.py index 3ad3e3f1b8..12b02c8a44 100755 --- a/scripts/maint/redox.py +++ b/scripts/maint/redox.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # Copyright (c) 2008-2019, The Tor Project, Inc. # See LICENSE for licensing information. diff --git a/scripts/maint/rename_c_identifier.py b/scripts/maint/rename_c_identifier.py index 6e0c1d8cf1..77802e10f3 100755 --- a/scripts/maint/rename_c_identifier.py +++ b/scripts/maint/rename_c_identifier.py @@ -44,7 +44,8 @@ def is_c_file(fn): False """ fn = os.path.split(fn)[1] - if fn.startswith("."): + # Avoid editor temporary files + if fn.startswith(".") or fn.startswith("#"): return False ext = os.path.splitext(fn)[1] return ext in {".c", ".h", ".i", ".inc"} diff --git a/scripts/maint/sortChanges.py b/scripts/maint/sortChanges.py index 2e049b1e53..5f6324e387 100755 --- a/scripts/maint/sortChanges.py +++ b/scripts/maint/sortChanges.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2014-2019, The Tor Project, Inc. # See LICENSE for licensing information diff --git a/src/app/config/config.c b/src/app/config/config.c index bbf984ad08..4becae4756 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -182,7 +182,7 @@ static const char unix_q_socket_prefix[] = "unix:\""; * *DowloadInitialDelay . */ #ifndef COCCI #define DOWNLOAD_SCHEDULE(name) \ - { #name "DownloadSchedule", #name "DownloadInitialDelay", 0, 1 } + { (#name "DownloadSchedule"), (#name "DownloadInitialDelay"), 0, 1 } #else #define DOWNLOAD_SCHEDULE(name) { NULL, NULL, 0, 1 } #endif /* !defined(COCCI) */ @@ -366,7 +366,7 @@ static const config_var_t option_vars_[] = { #endif /* defined(HAVE_MODULE_RELAY) || defined(TOR_UNIT_TESTS) */ V(ClientPreferIPv6ORPort, AUTOBOOL, "auto"), V(ClientPreferIPv6DirPort, AUTOBOOL, "auto"), - V(ClientAutoIPv6ORPort, BOOL, "0"), + OBSOLETE("ClientAutoIPv6ORPort"), V(ClientRejectInternalAddresses, BOOL, "1"), V(ClientTransportPlugin, LINELIST, NULL), V(ClientUseIPv6, BOOL, "0"), @@ -510,6 +510,8 @@ static const config_var_t option_vars_[] = { LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceEnableIntroDoSBurstPerSec", LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceOnionBalanceInstance", + LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"), V(HidServAuth, LINELIST, NULL), V(ClientOnionAuthDir, FILENAME, NULL), @@ -823,9 +825,6 @@ static char *get_windows_conf_root(void); static int options_check_transition_cb(const void *old, const void *new, char **msg); -static int parse_ports(or_options_t *options, int validate_only, - char **msg_out, int *n_ports_out, - int *world_writable_control_socket); static int validate_data_directories(or_options_t *options); static int write_configuration_file(const char *fname, const or_options_t *options); @@ -2906,7 +2905,8 @@ resolve_my_address(int warn_severity, const or_options_t *options, /* make sure we're ok with publishing an internal IP */ if (using_default_dir_authorities(options)) { /* if they are using the default authorities, disallow internal IPs - * always. */ + * always. For IPv6 ORPorts, this check is done in + * router_get_advertised_ipv6_or_ap(). See #33681. */ log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private IP address '%s'. " "Tor servers that use the default DirAuthorities must have " @@ -5878,18 +5878,28 @@ parse_dir_fallback_line(const char *line, return r; } -/** Allocate and return a new port_cfg_t with reasonable defaults. */ +/** Allocate and return a new port_cfg_t with reasonable defaults. + * + * <b>namelen</b> is the length of the unix socket name + * (typically the filesystem path), not including the trailing NUL. + * It should be 0 for ports that are not zunix sockets. */ port_cfg_t * 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); + + /* entry_cfg flags */ cfg->entry_cfg.ipv4_traffic = 1; cfg->entry_cfg.ipv6_traffic = 1; cfg->entry_cfg.prefer_ipv6 = 1; cfg->entry_cfg.dns_request = 1; cfg->entry_cfg.onion_traffic = 1; cfg->entry_cfg.prefer_ipv6_virtaddr = 1; + cfg->entry_cfg.session_group = SESSION_GROUP_UNSET; + cfg->entry_cfg.isolation_flags = ISO_DEFAULT; + + /* Other flags default to 0 due to tor_malloc_zero */ return cfg; } @@ -5998,7 +6008,7 @@ port_cfg_line_extract_addrport(const char *line, size_t sz; *is_unix_out = 1; *addrport_out = NULL; - line += strlen(unix_socket_prefix); /*No q: Keep the quote */ + line += strlen(unix_socket_prefix); /* No 'unix:', but keep the quote */ *rest_out = unescape_string(line, addrport_out, &sz); if (!*rest_out || (*addrport_out && sz != strlen(*addrport_out))) { tor_free(*addrport_out); @@ -6093,11 +6103,12 @@ port_parse_config(smartlist_t *out, const unsigned is_unix_socket = flags & CL_PORT_IS_UNIXSOCKET; int got_zero_port=0, got_nonzero_port=0; char *unix_socket_path = NULL; + port_cfg_t *cfg = NULL; /* 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); + cfg = port_cfg_new(is_unix_socket ? strlen(defaultaddr) : 0); cfg->type = listener_type; if (is_unix_socket) { tor_addr_make_unspec(&cfg->addr); @@ -6107,8 +6118,6 @@ port_parse_config(smartlist_t *out, cfg->port = defaultport; tor_addr_parse(&cfg->addr, defaultaddr); } - cfg->entry_cfg.session_group = SESSION_GROUP_UNSET; - cfg->entry_cfg.isolation_flags = ISO_DEFAULT; smartlist_add(out, cfg); } return 0; @@ -6122,28 +6131,12 @@ port_parse_config(smartlist_t *out, for (; ports; ports = ports->next) { tor_addr_t addr; tor_addr_make_unspec(&addr); - - int port; - int sessiongroup = SESSION_GROUP_UNSET; - unsigned isolation = ISO_DEFAULT; - int prefer_no_auth = 0; - int socks_iso_keep_alive = 0; - + int port, ok, + has_used_unix_socket_only_option = 0, + is_unix_tagged_addr = 0; uint16_t ptmp=0; - int ok; - /* 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 = 1, prefer_ipv6 = 1, dns_request = 1, - onion_traffic = 1, - 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, - has_used_unix_socket_only_option = 0, extended_errors = 0; - - int is_unix_tagged_addr = 0; const char *rest_of_line = NULL; + if (port_cfg_line_extract_addrport(ports->value, &addrport, &is_unix_tagged_addr, &rest_of_line)<0) { log_warn(LD_CONFIG, "Invalid %sPort line with unparsable address", @@ -6219,17 +6212,20 @@ port_parse_config(smartlist_t *out, } } + /* Default port_cfg_t object initialization */ + cfg = port_cfg_new(unix_socket_path ? strlen(unix_socket_path) : 0); + if (unix_socket_path && default_to_group_writable) - group_writable = 1; + cfg->is_group_writable = 1; /* Now parse the rest of the options, if any. */ if (use_server_options) { /* This is a server port; parse advertising options */ SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { if (!strcasecmp(elt, "NoAdvertise")) { - no_advertise = 1; + cfg->server_cfg.no_advertise = 1; } else if (!strcasecmp(elt, "NoListen")) { - no_listen = 1; + cfg->server_cfg.no_listen = 1; #if 0 /* not implemented yet. */ } else if (!strcasecmp(elt, "AllAddrs")) { @@ -6237,33 +6233,36 @@ port_parse_config(smartlist_t *out, all_addrs = 1; #endif /* 0 */ } else if (!strcasecmp(elt, "IPv4Only")) { - bind_ipv4_only = 1; + cfg->server_cfg.bind_ipv4_only = 1; } else if (!strcasecmp(elt, "IPv6Only")) { - bind_ipv6_only = 1; + cfg->server_cfg.bind_ipv6_only = 1; } else { log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", portname, escaped(elt)); } } SMARTLIST_FOREACH_END(elt); - if (no_advertise && no_listen) { + if (cfg->server_cfg.no_advertise && cfg->server_cfg.no_listen) { log_warn(LD_CONFIG, "Tried to set both NoListen and NoAdvertise " "on %sPort line '%s'", portname, escaped(ports->value)); goto err; } - if (bind_ipv4_only && bind_ipv6_only) { + if (cfg->server_cfg.bind_ipv4_only && + cfg->server_cfg.bind_ipv6_only) { log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only " "on %sPort line '%s'", portname, escaped(ports->value)); goto err; } - if (bind_ipv4_only && tor_addr_family(&addr) != AF_INET) { + if (cfg->server_cfg.bind_ipv4_only && + tor_addr_family(&addr) != AF_INET) { log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4", portname); goto err; } - if (bind_ipv6_only && tor_addr_family(&addr) != AF_INET6) { + if (cfg->server_cfg.bind_ipv6_only && + tor_addr_family(&addr) != AF_INET6) { log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", portname); goto err; @@ -6282,12 +6281,12 @@ port_parse_config(smartlist_t *out, portname, escaped(elt)); goto err; } - if (sessiongroup >= 0) { + if (cfg->entry_cfg.session_group >= 0) { log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort", portname); goto err; } - sessiongroup = group; + cfg->entry_cfg.session_group = group; continue; } @@ -6297,15 +6296,15 @@ port_parse_config(smartlist_t *out, } if (!strcasecmp(elt, "GroupWritable")) { - group_writable = !no; + cfg->is_group_writable = !no; has_used_unix_socket_only_option = 1; continue; } else if (!strcasecmp(elt, "WorldWritable")) { - world_writable = !no; + cfg->is_world_writable = !no; has_used_unix_socket_only_option = 1; continue; } else if (!strcasecmp(elt, "RelaxDirModeCheck")) { - relax_dirmode_check = !no; + cfg->relax_dirmode_check = !no; has_used_unix_socket_only_option = 1; continue; } @@ -6318,19 +6317,19 @@ port_parse_config(smartlist_t *out, if (takes_hostnames) { if (!strcasecmp(elt, "IPv4Traffic")) { - ipv4_traffic = ! no; + cfg->entry_cfg.ipv4_traffic = ! no; continue; } else if (!strcasecmp(elt, "IPv6Traffic")) { - ipv6_traffic = ! no; + cfg->entry_cfg.ipv6_traffic = ! no; continue; } else if (!strcasecmp(elt, "PreferIPv6")) { - prefer_ipv6 = ! no; + cfg->entry_cfg.prefer_ipv6 = ! no; continue; } else if (!strcasecmp(elt, "DNSRequest")) { - dns_request = ! no; + cfg->entry_cfg.dns_request = ! no; continue; } else if (!strcasecmp(elt, "OnionTraffic")) { - onion_traffic = ! no; + cfg->entry_cfg.onion_traffic = ! no; continue; } else if (!strcasecmp(elt, "OnionTrafficOnly")) { /* Only connect to .onion addresses. Equivalent to @@ -6341,46 +6340,50 @@ port_parse_config(smartlist_t *out, "DNSRequest, IPv4Traffic, and/or IPv6Traffic instead.", portname, escaped(elt)); } else { - ipv4_traffic = ipv6_traffic = dns_request = 0; + cfg->entry_cfg.ipv4_traffic = 0; + cfg->entry_cfg.ipv6_traffic = 0; + cfg->entry_cfg.dns_request = 0; } continue; } } if (!strcasecmp(elt, "CacheIPv4DNS")) { warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha - cache_ipv4 = ! no; + cfg->entry_cfg.cache_ipv4_answers = ! no; continue; } else if (!strcasecmp(elt, "CacheIPv6DNS")) { warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha - cache_ipv6 = ! no; + cfg->entry_cfg.cache_ipv6_answers = ! no; continue; } else if (!strcasecmp(elt, "CacheDNS")) { warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha - cache_ipv4 = cache_ipv6 = ! no; + cfg->entry_cfg.cache_ipv4_answers = ! no; + cfg->entry_cfg.cache_ipv6_answers = ! no; continue; } else if (!strcasecmp(elt, "UseIPv4Cache")) { warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha - use_cached_ipv4 = ! no; + cfg->entry_cfg.use_cached_ipv4_answers = ! no; continue; } else if (!strcasecmp(elt, "UseIPv6Cache")) { warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha - use_cached_ipv6 = ! no; + cfg->entry_cfg.use_cached_ipv6_answers = ! no; continue; } else if (!strcasecmp(elt, "UseDNSCache")) { warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha - use_cached_ipv4 = use_cached_ipv6 = ! no; + cfg->entry_cfg.use_cached_ipv4_answers = ! no; + cfg->entry_cfg.use_cached_ipv6_answers = ! no; continue; } else if (!strcasecmp(elt, "PreferIPv6Automap")) { - prefer_ipv6_automap = ! no; + cfg->entry_cfg.prefer_ipv6_virtaddr = ! no; continue; } else if (!strcasecmp(elt, "PreferSOCKSNoAuth")) { - prefer_no_auth = ! no; + cfg->entry_cfg.socks_prefer_no_auth = ! no; continue; } else if (!strcasecmp(elt, "KeepAliveIsolateSOCKSAuth")) { - socks_iso_keep_alive = ! no; + cfg->entry_cfg.socks_iso_keep_alive = ! no; continue; } else if (!strcasecmp(elt, "ExtendedErrors")) { - extended_errors = ! no; + cfg->entry_cfg.extended_socks5_codes = ! no; continue; } @@ -6403,9 +6406,9 @@ port_parse_config(smartlist_t *out, } if (no) { - isolation &= ~isoflag; + cfg->entry_cfg.isolation_flags &= ~isoflag; } else { - isolation |= isoflag; + cfg->entry_cfg.isolation_flags |= isoflag; } } SMARTLIST_FOREACH_END(elt); } @@ -6415,51 +6418,51 @@ port_parse_config(smartlist_t *out, else got_zero_port = 1; - if (dns_request == 0 && listener_type == CONN_TYPE_AP_DNS_LISTENER) { + if (cfg->entry_cfg.dns_request == 0 && + listener_type == CONN_TYPE_AP_DNS_LISTENER) { log_warn(LD_CONFIG, "You have a %sPort entry with DNS disabled; that " "won't work.", portname); goto err; } - - if (ipv4_traffic == 0 && ipv6_traffic == 0 && onion_traffic == 0 - && listener_type != CONN_TYPE_AP_DNS_LISTENER) { + if (cfg->entry_cfg.ipv4_traffic == 0 && + cfg->entry_cfg.ipv6_traffic == 0 && + cfg->entry_cfg.onion_traffic == 0 && + listener_type != CONN_TYPE_AP_DNS_LISTENER) { log_warn(LD_CONFIG, "You have a %sPort entry with all of IPv4 and " "IPv6 and .onion disabled; that won't work.", portname); goto err; } - - if (dns_request == 1 && ipv4_traffic == 0 && ipv6_traffic == 0 - && listener_type != CONN_TYPE_AP_DNS_LISTENER) { + if (cfg->entry_cfg.dns_request == 1 && + cfg->entry_cfg.ipv4_traffic == 0 && + cfg->entry_cfg.ipv6_traffic == 0 && + listener_type != CONN_TYPE_AP_DNS_LISTENER) { log_warn(LD_CONFIG, "You have a %sPort entry with DNSRequest enabled, " "but IPv4 and IPv6 disabled; DNS-based sites won't work.", portname); goto err; } - if ( has_used_unix_socket_only_option && ! unix_socket_path) { log_warn(LD_CONFIG, "You have a %sPort entry with GroupWritable, " "WorldWritable, or RelaxDirModeCheck, but it is not a " "unix socket.", portname); goto err; } - - if (!(isolation & ISO_SOCKSAUTH) && socks_iso_keep_alive) { + if (!(cfg->entry_cfg.isolation_flags & ISO_SOCKSAUTH) && + cfg->entry_cfg.socks_iso_keep_alive) { log_warn(LD_CONFIG, "You have a %sPort entry with both " "NoIsolateSOCKSAuth and KeepAliveIsolateSOCKSAuth set.", portname); goto err; } - - if (unix_socket_path && (isolation & ISO_CLIENTADDR)) { + if (unix_socket_path && + (cfg->entry_cfg.isolation_flags & ISO_CLIENTADDR)) { /* `IsolateClientAddr` is nonsensical in the context of AF_LOCAL. * just silently remove the isolation flag. */ - isolation &= ~ISO_CLIENTADDR; + cfg->entry_cfg.isolation_flags &= ~ISO_CLIENTADDR; } - if (out && port) { size_t namelen = unix_socket_path ? strlen(unix_socket_path) : 0; - port_cfg_t *cfg = port_cfg_new(namelen); if (unix_socket_path) { tor_addr_make_unspec(&cfg->addr); memcpy(cfg->unix_addr, unix_socket_path, namelen + 1); @@ -6470,33 +6473,13 @@ port_parse_config(smartlist_t *out, cfg->port = port; } cfg->type = listener_type; - cfg->is_world_writable = world_writable; - cfg->is_group_writable = group_writable; - cfg->relax_dirmode_check = relax_dirmode_check; - cfg->entry_cfg.isolation_flags = isolation; - cfg->entry_cfg.session_group = sessiongroup; - cfg->server_cfg.no_advertise = no_advertise; - cfg->server_cfg.no_listen = no_listen; - cfg->server_cfg.all_addrs = all_addrs; - cfg->server_cfg.bind_ipv4_only = bind_ipv4_only; - cfg->server_cfg.bind_ipv6_only = bind_ipv6_only; - cfg->entry_cfg.ipv4_traffic = ipv4_traffic; - cfg->entry_cfg.ipv6_traffic = ipv6_traffic; - cfg->entry_cfg.prefer_ipv6 = prefer_ipv6; - cfg->entry_cfg.dns_request = dns_request; - cfg->entry_cfg.onion_traffic = onion_traffic; - cfg->entry_cfg.cache_ipv4_answers = cache_ipv4; - cfg->entry_cfg.cache_ipv6_answers = cache_ipv6; - cfg->entry_cfg.use_cached_ipv4_answers = use_cached_ipv4; - cfg->entry_cfg.use_cached_ipv6_answers = use_cached_ipv6; - cfg->entry_cfg.prefer_ipv6_virtaddr = prefer_ipv6_automap; - cfg->entry_cfg.socks_prefer_no_auth = prefer_no_auth; - if (! (isolation & ISO_SOCKSAUTH)) + if (! (cfg->entry_cfg.isolation_flags & ISO_SOCKSAUTH)) cfg->entry_cfg.socks_prefer_no_auth = 1; - cfg->entry_cfg.socks_iso_keep_alive = socks_iso_keep_alive; - cfg->entry_cfg.extended_socks5_codes = extended_errors; - smartlist_add(out, cfg); + /* out owns cfg now, don't re-use or free it */ + cfg = NULL; + } else { + tor_free(cfg); } SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_clear(elts); @@ -6522,10 +6505,22 @@ port_parse_config(smartlist_t *out, retval = 0; err: + /* There are two ways we can error out: + * 1. part way through the loop: cfg needs to be freed; + * 2. ending the loop normally: cfg is always NULL. + * In this case, cfg has either been: + * - added to out, then set to NULL, or + * - freed and set to NULL (because out is NULL, or port is 0). + */ + tor_free(cfg); + + /* Free the other variables from the loop. + * elts is always non-NULL here, but it may or may not be empty. */ SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_free(elts); tor_free(unix_socket_path); tor_free(addrport); + return retval; } @@ -6557,7 +6552,7 @@ port_count_real_listeners(const smartlist_t *ports, int listenertype, * If <b>validate_only</b> is false, set configured_client_ports to the * new list of ports parsed from <b>options</b>. **/ -static int +STATIC int parse_ports(or_options_t *options, int validate_only, char **msg, int *n_ports_out, int *world_writable_control_socket) diff --git a/src/app/config/config.h b/src/app/config/config.h index 04d877e9da..311e27917a 100644 --- a/src/app/config/config.h +++ b/src/app/config/config.h @@ -319,6 +319,10 @@ int options_validate(const or_options_t *old_options, char **msg); #endif +STATIC int parse_ports(or_options_t *options, int validate_only, + char **msg, int *n_ports_out, + int *world_writable_control_socket); + #endif /* defined(CONFIG_PRIVATE) */ #endif /* !defined(TOR_CONFIG_H) */ diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index 35ba15a9e2..bf58205f89 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -662,9 +662,6 @@ struct or_options_t { * accessing this value directly. */ int ClientPreferIPv6DirPort; - /** If true, prefer an IPv4 or IPv6 OR port at random. */ - int ClientAutoIPv6ORPort; - /** The length of time that we think a consensus should be fresh. */ int V3AuthVotingInterval; /** The length of time we think it will take to distribute votes. */ diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c index d9667733cc..dcc55f1898 100644 --- a/src/app/config/statefile.c +++ b/src/app/config/statefile.c @@ -78,6 +78,7 @@ DUMMY_TYPECHECK_INSTANCE(or_state_t); VAR(#member, conftype, member, initvalue) /** Array of "state" variables saved to the ~/.tor/state file. */ +// clang-format off static const config_var_t state_vars_[] = { /* Remember to document these in state-contents.txt ! */ @@ -134,6 +135,7 @@ static const config_var_t state_vars_[] = { END_OF_CONFIG_VARS }; +// clang-format on #undef VAR #undef V diff --git a/src/app/main/shutdown.c b/src/app/main/shutdown.c index 27d92609eb..aac15246b9 100644 --- a/src/app/main/shutdown.c +++ b/src/app/main/shutdown.c @@ -75,7 +75,8 @@ tor_cleanup(void) /* Remove Extended ORPort cookie authentication file */ { char *cookie_fname = get_ext_or_auth_cookie_file_name(); - tor_remove_file(cookie_fname); + if (cookie_fname) + tor_remove_file(cookie_fname); tor_free(cookie_fname); } if (accounting_is_enabled(options)) diff --git a/src/app/main/subsystem_list.c b/src/app/main/subsystem_list.c index b4439cdc7b..e32083537f 100644 --- a/src/app/main/subsystem_list.c +++ b/src/app/main/subsystem_list.c @@ -14,9 +14,7 @@ #include "lib/cc/torint.h" #include "core/mainloop/mainloop_sys.h" -#include "core/or/ocirc_event_sys.h" #include "core/or/or_sys.h" -#include "core/or/orconn_event_sys.h" #include "feature/control/btrack_sys.h" #include "lib/compress/compress_sys.h" #include "lib/crypt_ops/crypto_sys.h" @@ -24,7 +22,7 @@ #include "lib/log/log_sys.h" #include "lib/net/network_sys.h" #include "lib/process/process_sys.h" -#include "lib/process/winprocess_sys.h" +#include "lib/llharden/winprocess_sys.h" #include "lib/thread/thread_sys.h" #include "lib/time/time_sys.h" #include "lib/tls/tortls_sys.h" @@ -46,28 +44,26 @@ const subsys_fns_t *tor_subsystems[] = { &sys_torerr, &sys_wallclock, - &sys_threads, &sys_logging, + &sys_threads, &sys_time, - &sys_network, - &sys_compress, &sys_crypto, + &sys_compress, + &sys_network, &sys_tortls, - &sys_process, - - &sys_orconn_event, - &sys_ocirc_event, - &sys_btrack, &sys_evloop, + &sys_process, &sys_mainloop, &sys_or, &sys_relay, + &sys_btrack, + &sys_dirauth, }; diff --git a/src/core/crypto/hs_ntor.c b/src/core/crypto/hs_ntor.c index 2bd4c32446..07bcdc566c 100644 --- a/src/core/crypto/hs_ntor.c +++ b/src/core/crypto/hs_ntor.c @@ -170,7 +170,7 @@ get_rendezvous1_key_material(const uint8_t *rend_secret_hs_input, * necessary key material, and return 0. */ static void get_introduce1_key_material(const uint8_t *secret_input, - const uint8_t *subcredential, + const hs_subcredential_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) { uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN]; @@ -181,7 +181,7 @@ get_introduce1_key_material(const uint8_t *secret_input, /* Let's build info */ ptr = info_blob; APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND)); - APPEND(ptr, subcredential, DIGEST256_LEN); + APPEND(ptr, subcredential->subcred, SUBCRED_LEN); tor_assert(ptr == info_blob + sizeof(info_blob)); /* Let's build the input to the KDF */ @@ -317,7 +317,7 @@ hs_ntor_client_get_introduce1_keys( const ed25519_public_key_t *intro_auth_pubkey, const curve25519_public_key_t *intro_enc_pubkey, const curve25519_keypair_t *client_ephemeral_enc_keypair, - const uint8_t *subcredential, + const hs_subcredential_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) { int bad = 0; @@ -450,9 +450,31 @@ hs_ntor_service_get_introduce1_keys( const ed25519_public_key_t *intro_auth_pubkey, const curve25519_keypair_t *intro_enc_keypair, const curve25519_public_key_t *client_ephemeral_enc_pubkey, - const uint8_t *subcredential, + const hs_subcredential_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) { + return hs_ntor_service_get_introduce1_keys_multi( + intro_auth_pubkey, + intro_enc_keypair, + client_ephemeral_enc_pubkey, + 1, + subcredential, + hs_ntor_intro_cell_keys_out); +} + +/** + * As hs_ntor_service_get_introduce1_keys(), but take multiple subcredentials + * as input, and yield multiple sets of keys as output. + **/ +int +hs_ntor_service_get_introduce1_keys_multi( + const struct ed25519_public_key_t *intro_auth_pubkey, + const struct curve25519_keypair_t *intro_enc_keypair, + const struct curve25519_public_key_t *client_ephemeral_enc_pubkey, + size_t n_subcredentials, + const hs_subcredential_t *subcredentials, + hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) +{ int bad = 0; uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN]; uint8_t dh_result[CURVE25519_OUTPUT_LEN]; @@ -460,7 +482,8 @@ hs_ntor_service_get_introduce1_keys( tor_assert(intro_auth_pubkey); tor_assert(intro_enc_keypair); tor_assert(client_ephemeral_enc_pubkey); - tor_assert(subcredential); + tor_assert(n_subcredentials >= 1); + tor_assert(subcredentials); tor_assert(hs_ntor_intro_cell_keys_out); /* Compute EXP(X, b) */ @@ -476,13 +499,16 @@ hs_ntor_service_get_introduce1_keys( secret_input); bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN); - /* Get ENC_KEY and MAC_KEY! */ - get_introduce1_key_material(secret_input, subcredential, - hs_ntor_intro_cell_keys_out); + for (unsigned i = 0; i < n_subcredentials; ++i) { + /* Get ENC_KEY and MAC_KEY! */ + get_introduce1_key_material(secret_input, &subcredentials[i], + &hs_ntor_intro_cell_keys_out[i]); + } memwipe(secret_input, 0, sizeof(secret_input)); if (bad) { - memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t)); + memwipe(hs_ntor_intro_cell_keys_out, 0, + sizeof(hs_ntor_intro_cell_keys_t) * n_subcredentials); } return bad ? -1 : 0; diff --git a/src/core/crypto/hs_ntor.h b/src/core/crypto/hs_ntor.h index 2bce5686cd..9a975dd83f 100644 --- a/src/core/crypto/hs_ntor.h +++ b/src/core/crypto/hs_ntor.h @@ -35,11 +35,20 @@ typedef struct hs_ntor_rend_cell_keys_t { uint8_t ntor_key_seed[DIGEST256_LEN]; } hs_ntor_rend_cell_keys_t; +#define SUBCRED_LEN DIGEST256_LEN + +/** + * A 'subcredential' used to prove knowledge of a hidden service. + **/ +typedef struct hs_subcredential_t { + uint8_t subcred[SUBCRED_LEN]; +} hs_subcredential_t; + int hs_ntor_client_get_introduce1_keys( const struct ed25519_public_key_t *intro_auth_pubkey, const struct curve25519_public_key_t *intro_enc_pubkey, const struct curve25519_keypair_t *client_ephemeral_enc_keypair, - const uint8_t *subcredential, + const hs_subcredential_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out); int hs_ntor_client_get_rendezvous1_keys( @@ -49,11 +58,19 @@ int hs_ntor_client_get_rendezvous1_keys( const struct curve25519_public_key_t *service_ephemeral_rend_pubkey, hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out); +int hs_ntor_service_get_introduce1_keys_multi( + const struct ed25519_public_key_t *intro_auth_pubkey, + const struct curve25519_keypair_t *intro_enc_keypair, + const struct curve25519_public_key_t *client_ephemeral_enc_pubkey, + size_t n_subcredentials, + const hs_subcredential_t *subcredentials, + hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out); + int hs_ntor_service_get_introduce1_keys( const struct ed25519_public_key_t *intro_auth_pubkey, const struct curve25519_keypair_t *intro_enc_keypair, const struct curve25519_public_key_t *client_ephemeral_enc_pubkey, - const uint8_t *subcredential, + const hs_subcredential_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out); int hs_ntor_service_get_rendezvous1_keys( diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index 48f31922d7..9602978458 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -91,6 +91,7 @@ #include "feature/control/control.h" #include "feature/control/control_events.h" #include "feature/dirauth/authmode.h" +#include "feature/dirauth/dirauth_config.h" #include "feature/dircache/dirserv.h" #include "feature/dircommon/directory.h" #include "feature/hibernate/hibernate.h" @@ -1513,10 +1514,11 @@ connection_listener_new(const struct sockaddr *listensockaddr, } } + /* Force IPv4 and IPv6 traffic on for non-SOCKSPorts. + * Forcing options on isn't a good idea, see #32994 and #33607. */ if (type != CONN_TYPE_AP_LISTENER) { lis_conn->entry_cfg.ipv4_traffic = 1; lis_conn->entry_cfg.ipv6_traffic = 1; - lis_conn->entry_cfg.prefer_ipv6 = 1; } if (connection_add(conn) < 0) { /* no space, forget it */ @@ -3137,7 +3139,7 @@ connection_mark_all_noncontrol_connections(void) * uses pluggable transports, since we should then limit it even if it * comes from an internal IP address. */ static int -connection_is_rate_limited(connection_t *conn) +connection_is_rate_limited(const connection_t *conn) { const or_options_t *options = get_options(); if (conn->linked) @@ -3272,14 +3274,14 @@ connection_bucket_write_limit(connection_t *conn, time_t now) global_bucket_val, conn_bucket); } -/** Return 1 if the global write buckets are low enough that we +/** Return true iff the global write buckets are low enough that we * shouldn't send <b>attempt</b> bytes of low-priority directory stuff - * out to <b>conn</b>. Else return 0. - - * Priority was 1 for v1 requests (directories and running-routers), - * and 2 for v2 requests and later (statuses and descriptors). + * out to <b>conn</b>. + * + * If we are a directory authority, always answer dir requests thus true is + * always returned. * - * There are a lot of parameters we could use here: + * Note: There are a lot of parameters we could use here: * - global_relayed_write_bucket. Low is bad. * - global_write_bucket. Low is bad. * - bandwidthrate. Low is bad. @@ -3291,39 +3293,40 @@ connection_bucket_write_limit(connection_t *conn, time_t now) * mean is "total directory bytes added to outbufs recently", but * that's harder to quantify and harder to keep track of. */ -int -global_write_bucket_low(connection_t *conn, size_t attempt, int priority) +bool +connection_dir_is_global_write_low(const connection_t *conn, size_t attempt) { size_t smaller_bucket = MIN(token_bucket_rw_get_write(&global_bucket), token_bucket_rw_get_write(&global_relayed_bucket)); - if (authdir_mode(get_options()) && priority>1) - return 0; /* there's always room to answer v2 if we're an auth dir */ + + /* Special case for authorities (directory only). */ + if (authdir_mode_v3(get_options())) { + /* Are we configured to possibly reject requests under load? */ + if (!dirauth_should_reject_requests_under_load()) { + /* Answer request no matter what. */ + return false; + } + /* Always answer requests from a known relay which includes the other + * authorities. The following looks up the addresses for relays that we + * have their descriptor _and_ any configured trusted directories. */ + if (nodelist_probably_contains_address(&conn->addr)) { + return false; + } + } if (!connection_is_rate_limited(conn)) - return 0; /* local conns don't get limited */ + return false; /* local conns don't get limited */ if (smaller_bucket < attempt) - return 1; /* not enough space no matter the priority */ + return true; /* not enough space. */ { const time_t diff = approx_time() - write_buckets_last_empty_at; if (diff <= 1) - return 1; /* we're already hitting our limits, no more please */ + return true; /* we're already hitting our limits, no more please */ } - - if (priority == 1) { /* old-style v1 query */ - /* Could we handle *two* of these requests within the next two seconds? */ - const or_options_t *options = get_options(); - size_t can_write = (size_t) (smaller_bucket - + 2*(options->RelayBandwidthRate ? options->RelayBandwidthRate : - options->BandwidthRate)); - if (can_write < 2*attempt) - return 1; - } else { /* v2 query */ - /* no further constraints yet */ - } - return 0; + return false; } /** When did we last tell the accounting subsystem about transmitted diff --git a/src/core/mainloop/connection.h b/src/core/mainloop/connection.h index 0ab601d86f..bcd3d590a5 100644 --- a/src/core/mainloop/connection.h +++ b/src/core/mainloop/connection.h @@ -219,8 +219,8 @@ void connection_mark_all_noncontrol_listeners(void); void connection_mark_all_noncontrol_connections(void); ssize_t connection_bucket_write_limit(struct connection_t *conn, time_t now); -int global_write_bucket_low(struct connection_t *conn, - size_t attempt, int priority); +bool connection_dir_is_global_write_low(const struct connection_t *conn, + size_t attempt); void connection_bucket_init(void); void connection_bucket_adjust(const struct or_options_t *options); void connection_bucket_refill_all(time_t now, diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c index 260de181e5..e4e17f6b76 100644 --- a/src/core/mainloop/mainloop.c +++ b/src/core/mainloop/mainloop.c @@ -966,7 +966,6 @@ conn_close_if_marked(int i) return 0; /* nothing to see here, move along */ now = time(NULL); assert_connection_ok(conn, now); - /* assert_all_pending_dns_resolves_ok(); */ log_debug(LD_NET,"Cleaning up connection (fd "TOR_SOCKET_T_FORMAT").", conn->s); diff --git a/src/core/or/addr_policy_st.h b/src/core/or/addr_policy_st.h index 5a2b7f6fb3..08d16ee616 100644 --- a/src/core/or/addr_policy_st.h +++ b/src/core/or/addr_policy_st.h @@ -38,7 +38,7 @@ struct addr_policy_t { /** Base address to accept or reject. * * Note that wildcards are treated - * differntly depending on address family. An AF_UNSPEC address means + * differently depending on address family. An AF_UNSPEC address means * "All addresses, IPv4 or IPv6." An AF_INET address with maskbits==0 means * "All IPv4 addresses" and an AF_INET6 address with maskbits == 0 means * "All IPv6 addresses". diff --git a/src/core/or/channel.c b/src/core/or/channel.c index d52dc14a32..160ab587f5 100644 --- a/src/core/or/channel.c +++ b/src/core/or/channel.c @@ -119,10 +119,10 @@ channel_id_eq(const channel_t *a, const channel_t *b) return a->global_identifier == b->global_identifier; } HT_PROTOTYPE(channel_gid_map, channel_t, gidmap_node, - channel_id_hash, channel_id_eq) + channel_id_hash, channel_id_eq); HT_GENERATE2(channel_gid_map, channel_t, gidmap_node, channel_id_hash, channel_id_eq, - 0.6, tor_reallocarray_, tor_free_) + 0.6, tor_reallocarray_, tor_free_); HANDLE_IMPL(channel, channel_t,) @@ -160,9 +160,9 @@ channel_idmap_eq(const channel_idmap_entry_t *a, } HT_PROTOTYPE(channel_idmap, channel_idmap_entry_t, node, channel_idmap_hash, - channel_idmap_eq) + channel_idmap_eq); HT_GENERATE2(channel_idmap, channel_idmap_entry_t, node, channel_idmap_hash, - channel_idmap_eq, 0.5, tor_reallocarray_, tor_free_) + channel_idmap_eq, 0.5, tor_reallocarray_, tor_free_); /* Functions to maintain the digest map */ static void channel_remove_from_digest_map(channel_t *chan); @@ -1859,7 +1859,7 @@ channel_do_open_actions(channel_t *chan) tor_free(transport_name); /* Notify the DoS subsystem of a new client. */ if (tlschan && tlschan->conn) { - dos_new_client_conn(tlschan->conn); + dos_new_client_conn(tlschan->conn, transport_name); } } /* Otherwise the underlying transport can't tell us this, so skip it */ diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c index 7974da4832..2a35237d30 100644 --- a/src/core/or/channeltls.c +++ b/src/core/or/channeltls.c @@ -47,6 +47,7 @@ #include "app/config/config.h" #include "core/mainloop/connection.h" #include "core/or/connection_or.h" +#include "feature/relay/relay_handshake.h" #include "feature/control/control.h" #include "feature/client/entrynodes.h" #include "trunnel/link_handshake.h" diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c index 03ed2c7d29..003b91af8d 100644 --- a/src/core/or/circuitbuild.c +++ b/src/core/or/circuitbuild.c @@ -2819,8 +2819,8 @@ extend_info_dup(extend_info_t *info) * If there is no chosen exit, or if we don't know the node_t for * the chosen exit, return NULL. */ -const node_t * -build_state_get_exit_node(cpath_build_state_t *state) +MOCK_IMPL(const node_t *, +build_state_get_exit_node,(cpath_build_state_t *state)) { if (!state || !state->chosen_exit) return NULL; diff --git a/src/core/or/circuitbuild.h b/src/core/or/circuitbuild.h index f5a3439064..48592dd346 100644 --- a/src/core/or/circuitbuild.h +++ b/src/core/or/circuitbuild.h @@ -66,7 +66,8 @@ int circuit_can_use_tap(const origin_circuit_t *circ); int circuit_has_usable_onion_key(const origin_circuit_t *circ); int extend_info_has_preferred_onion_key(const extend_info_t* ei); const uint8_t *build_state_get_exit_rsa_id(cpath_build_state_t *state); -const node_t *build_state_get_exit_node(cpath_build_state_t *state); +MOCK_DECL(const node_t *, + build_state_get_exit_node,(cpath_build_state_t *state)); const char *build_state_get_exit_nickname(cpath_build_state_t *state); struct circuit_guard_state_t; diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c index 6a712926a3..ca174c442c 100644 --- a/src/core/or/circuitlist.c +++ b/src/core/or/circuitlist.c @@ -215,10 +215,10 @@ chan_circid_entry_hash_(chan_circid_circuit_map_t *a) static HT_HEAD(chan_circid_map, chan_circid_circuit_map_t) chan_circid_map = HT_INITIALIZER(); HT_PROTOTYPE(chan_circid_map, chan_circid_circuit_map_t, node, - chan_circid_entry_hash_, chan_circid_entries_eq_) + chan_circid_entry_hash_, chan_circid_entries_eq_); HT_GENERATE2(chan_circid_map, chan_circid_circuit_map_t, node, chan_circid_entry_hash_, chan_circid_entries_eq_, 0.6, - tor_reallocarray_, tor_free_) + tor_reallocarray_, tor_free_); /** The most recently returned entry from circuit_get_by_circid_chan; * used to improve performance when many cells arrive in a row from the diff --git a/src/core/or/circuitmux.c b/src/core/or/circuitmux.c index da95e93657..b770e40bf2 100644 --- a/src/core/or/circuitmux.c +++ b/src/core/or/circuitmux.c @@ -176,10 +176,10 @@ chanid_circid_entry_hash(chanid_circid_muxinfo_t *a) /* Emit a bunch of hash table stuff */ HT_PROTOTYPE(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node, - chanid_circid_entry_hash, chanid_circid_entries_eq) + chanid_circid_entry_hash, chanid_circid_entries_eq); HT_GENERATE2(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node, chanid_circid_entry_hash, chanid_circid_entries_eq, 0.6, - tor_reallocarray_, tor_free_) + tor_reallocarray_, tor_free_); /* * Circuitmux alloc/free functions diff --git a/src/core/or/circuitmux_ewma.c b/src/core/or/circuitmux_ewma.c index b50f33528f..0dcd22e8a7 100644 --- a/src/core/or/circuitmux_ewma.c +++ b/src/core/or/circuitmux_ewma.c @@ -423,7 +423,7 @@ ewma_cmp_cmux(circuitmux_t *cmux_1, circuitmux_policy_data_t *pol_data_1, /* Pick whichever one has the better best circuit */ return compare_cell_ewma_counts(ce1, ce2); } else { - if (ce1 != NULL ) { + if (ce1 != NULL) { /* We only have a circuit on cmux_1, so prefer it */ return -1; } else if (ce2 != NULL) { @@ -609,7 +609,7 @@ cmux_ewma_set_options(const or_options_t *options, /* convert halflife into halflife-per-tick. */ halflife /= EWMA_TICK_LEN; /* compute per-tick scale factor. */ - ewma_scale_factor = exp( LOG_ONEHALF / halflife ); + ewma_scale_factor = exp(LOG_ONEHALF / halflife); log_info(LD_OR, "Enabled cell_ewma algorithm because of value in %s; " "scale factor is %f per %d seconds", diff --git a/src/core/or/circuitpadding.c b/src/core/or/circuitpadding.c index 3853e9fdc4..43f4a31624 100644 --- a/src/core/or/circuitpadding.c +++ b/src/core/or/circuitpadding.c @@ -2446,9 +2446,12 @@ circpad_setup_machine_on_circ(circuit_t *on_circ, return; } - tor_assert_nonfatal(on_circ->padding_machine[machine->machine_index] - == NULL); - tor_assert_nonfatal(on_circ->padding_info[machine->machine_index] == NULL); + IF_BUG_ONCE(on_circ->padding_machine[machine->machine_index] != NULL) { + return; + } + IF_BUG_ONCE(on_circ->padding_info[machine->machine_index] != NULL) { + return; + } /* Log message */ if (CIRCUIT_IS_ORIGIN(on_circ)) { diff --git a/src/core/or/circuituse.c b/src/core/or/circuituse.c index 5d10cacc71..066d5d437a 100644 --- a/src/core/or/circuituse.c +++ b/src/core/or/circuituse.c @@ -732,7 +732,7 @@ circuit_expire_building(void) circuit_build_times_enough_to_compute(get_circuit_build_times())) { log_info(LD_CIRC, - "Deciding to count the timeout for circuit %"PRIu32"\n", + "Deciding to count the timeout for circuit %"PRIu32, TO_ORIGIN_CIRCUIT(victim)->global_identifier); /* Circuits are allowed to last longer for measurement. diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index aeb9ec6460..23c6e230cb 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -432,6 +432,21 @@ warn_if_hs_unreachable(const edge_connection_t *conn, uint8_t reason) } } +/** Given a TTL (in seconds) from a DNS response or from a relay, determine + * what TTL clients and relays should actually use for caching it. */ +uint32_t +clip_dns_ttl(uint32_t ttl) +{ + /* This logic is a defense against "DefectTor" DNS-based traffic + * confirmation attacks, as in https://nymity.ch/tor-dns/tor-dns.pdf . + * We only give two values: a "low" value and a "high" value. + */ + if (ttl < MIN_DNS_TTL) + return MIN_DNS_TTL; + else + return MAX_DNS_TTL; +} + /** Send a relay end cell from stream <b>conn</b> down conn's circuit, and * remember that we've done so. If this is not a client connection, set the * relay end cell's reason for closing as <b>reason</b>. @@ -480,7 +495,7 @@ connection_edge_end(edge_connection_t *conn, uint8_t reason) memcpy(payload+1, tor_addr_to_in6_addr8(&conn->base_.addr), 16); addrlen = 16; } - set_uint32(payload+1+addrlen, htonl(dns_clip_ttl(conn->address_ttl))); + set_uint32(payload+1+addrlen, htonl(clip_dns_ttl(conn->address_ttl))); payload_len += 4+addrlen; } @@ -845,7 +860,7 @@ connected_cell_format_payload(uint8_t *payload_out, return -1; } - set_uint32(payload_out + connected_payload_len, htonl(dns_clip_ttl(ttl))); + set_uint32(payload_out + connected_payload_len, htonl(clip_dns_ttl(ttl))); connected_payload_len += 4; tor_assert(connected_payload_len <= MAX_CONNECTED_CELL_PAYLOAD_LEN); diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h index 11cb252935..8c06af5664 100644 --- a/src/core/or/connection_edge.h +++ b/src/core/or/connection_edge.h @@ -182,6 +182,21 @@ void connection_ap_warn_and_unmark_if_pending_circ( entry_connection_t *entry_conn, const char *where); +/** Lowest value for DNS ttl that a server should give or a client should + * believe. */ +#define MIN_DNS_TTL (5*60) +/** Highest value for DNS ttl that a server should give or a client should + * believe. */ +#define MAX_DNS_TTL (60*60) +/** How long do we keep DNS cache entries before purging them (regardless of + * their TTL)? */ +#define MAX_DNS_ENTRY_AGE (3*60*60) +/** How long do we cache/tell clients to cache DNS records when no TTL is + * known? */ +#define DEFAULT_DNS_TTL (30*60) + +uint32_t clip_dns_ttl(uint32_t ttl); + int connection_half_edge_is_valid_data(const smartlist_t *half_conns, streamid_t stream_id); int connection_half_edge_is_valid_sendme(const smartlist_t *half_conns, diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c index 76bfbf0b30..6b11f33232 100644 --- a/src/core/or/connection_or.c +++ b/src/core/or/connection_or.c @@ -39,14 +39,13 @@ #include "app/config/config.h" #include "core/mainloop/connection.h" #include "core/or/connection_or.h" +#include "feature/relay/relay_handshake.h" #include "feature/control/control_events.h" -#include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" #include "feature/dirauth/reachability.h" #include "feature/client/entrynodes.h" #include "lib/geoip/geoip.h" #include "core/mainloop/mainloop.h" -#include "trunnel/link_handshake.h" #include "trunnel/netinfo.h" #include "feature/nodelist/microdesc.h" #include "feature/nodelist/networkstatus.h" @@ -78,7 +77,6 @@ #include "lib/crypt_ops/crypto_format.h" #include "lib/tls/tortls.h" -#include "lib/tls/x509.h" #include "core/or/orconn_event.h" @@ -109,10 +107,6 @@ TO_OR_CONN(connection_t *c) return DOWNCAST(or_connection_t, c); } -/** Global map between Extended ORPort identifiers and OR - * connections. */ -static digestmap_t *orconn_ext_or_id_map = NULL; - /** Clear clear conn->identity_digest and update other data * structures as appropriate.*/ void @@ -198,71 +192,6 @@ connection_or_set_identity_digest(or_connection_t *conn, channel_set_identity_digest(chan, rsa_digest, ed_id); } -/** Remove the Extended ORPort identifier of <b>conn</b> from the - * global identifier list. Also, clear the identifier from the - * connection itself. */ -void -connection_or_remove_from_ext_or_id_map(or_connection_t *conn) -{ - or_connection_t *tmp; - if (!orconn_ext_or_id_map) - return; - if (!conn->ext_or_conn_id) - return; - - tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id); - if (!tor_digest_is_zero(conn->ext_or_conn_id)) - tor_assert(tmp == conn); - - memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN); -} - -/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such - * connection is found. */ -or_connection_t * -connection_or_get_by_ext_or_id(const char *id) -{ - if (!orconn_ext_or_id_map) - return NULL; - return digestmap_get(orconn_ext_or_id_map, id); -} - -/** Deallocate the global Extended ORPort identifier list */ -void -connection_or_clear_ext_or_id_map(void) -{ - digestmap_free(orconn_ext_or_id_map, NULL); - orconn_ext_or_id_map = NULL; -} - -/** Creates an Extended ORPort identifier for <b>conn</b> and deposits - * it into the global list of identifiers. */ -void -connection_or_set_ext_or_identifier(or_connection_t *conn) -{ - char random_id[EXT_OR_CONN_ID_LEN]; - or_connection_t *tmp; - - if (!orconn_ext_or_id_map) - orconn_ext_or_id_map = digestmap_new(); - - /* Remove any previous identifiers: */ - if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id)) - connection_or_remove_from_ext_or_id_map(conn); - - do { - crypto_rand(random_id, sizeof(random_id)); - } while (digestmap_get(orconn_ext_or_id_map, random_id)); - - if (!conn->ext_or_conn_id) - conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN); - - memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN); - - tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn); - tor_assert(!tmp); -} - /**************************************************************/ /** Map from a string describing what a non-open OR connection was doing when @@ -1283,11 +1212,11 @@ or_connect_failure_ht_hash(const or_connect_failure_entry_t *entry) } HT_PROTOTYPE(or_connect_failure_ht, or_connect_failure_entry_t, node, - or_connect_failure_ht_hash, or_connect_failure_ht_eq) + or_connect_failure_ht_hash, or_connect_failure_ht_eq); HT_GENERATE2(or_connect_failure_ht, or_connect_failure_entry_t, node, or_connect_failure_ht_hash, or_connect_failure_ht_eq, - 0.6, tor_reallocarray_, tor_free_) + 0.6, tor_reallocarray_, tor_free_); /* Initialize a given connect failure entry with the given identity_digest, * addr and port. All field are optional except ocf. */ @@ -2596,533 +2525,3 @@ connection_or_send_netinfo,(or_connection_t *conn)) return r; } - -/** Helper used to add an encoded certs to a cert cell */ -static void -add_certs_cell_cert_helper(certs_cell_t *certs_cell, - uint8_t cert_type, - const uint8_t *cert_encoded, - size_t cert_len) -{ - tor_assert(cert_len <= UINT16_MAX); - certs_cell_cert_t *ccc = certs_cell_cert_new(); - ccc->cert_type = cert_type; - ccc->cert_len = cert_len; - certs_cell_cert_setlen_body(ccc, cert_len); - memcpy(certs_cell_cert_getarray_body(ccc), cert_encoded, cert_len); - - certs_cell_add_certs(certs_cell, ccc); -} - -/** Add an encoded X509 cert (stored as <b>cert_len</b> bytes at - * <b>cert_encoded</b>) to the trunnel certs_cell_t object that we are - * building in <b>certs_cell</b>. Set its type field to <b>cert_type</b>. - * (If <b>cert</b> is NULL, take no action.) */ -static void -add_x509_cert(certs_cell_t *certs_cell, - uint8_t cert_type, - const tor_x509_cert_t *cert) -{ - if (NULL == cert) - return; - - const uint8_t *cert_encoded = NULL; - size_t cert_len; - tor_x509_cert_get_der(cert, &cert_encoded, &cert_len); - - add_certs_cell_cert_helper(certs_cell, cert_type, cert_encoded, cert_len); -} - -/** Add an Ed25519 cert from <b>cert</b> to the trunnel certs_cell_t object - * that we are building in <b>certs_cell</b>. Set its type field to - * <b>cert_type</b>. (If <b>cert</b> is NULL, take no action.) */ -static void -add_ed25519_cert(certs_cell_t *certs_cell, - uint8_t cert_type, - const tor_cert_t *cert) -{ - if (NULL == cert) - return; - - add_certs_cell_cert_helper(certs_cell, cert_type, - cert->encoded, cert->encoded_len); -} - -#ifdef TOR_UNIT_TESTS -int certs_cell_ed25519_disabled_for_testing = 0; -#else -#define certs_cell_ed25519_disabled_for_testing 0 -#endif - -/** Send a CERTS cell on the connection <b>conn</b>. Return 0 on success, -1 - * on failure. */ -int -connection_or_send_certs_cell(or_connection_t *conn) -{ - const tor_x509_cert_t *global_link_cert = NULL, *id_cert = NULL; - tor_x509_cert_t *own_link_cert = NULL; - var_cell_t *cell; - - certs_cell_t *certs_cell = NULL; - - tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); - - if (! conn->handshake_state) - return -1; - - const int conn_in_server_mode = ! conn->handshake_state->started_here; - - /* Get the encoded values of the X509 certificates */ - if (tor_tls_get_my_certs(conn_in_server_mode, - &global_link_cert, &id_cert) < 0) - return -1; - - if (conn_in_server_mode) { - own_link_cert = tor_tls_get_own_cert(conn->tls); - } - tor_assert(id_cert); - - certs_cell = certs_cell_new(); - - /* Start adding certs. First the link cert or auth1024 cert. */ - if (conn_in_server_mode) { - tor_assert_nonfatal(own_link_cert); - add_x509_cert(certs_cell, - OR_CERT_TYPE_TLS_LINK, own_link_cert); - } else { - tor_assert(global_link_cert); - add_x509_cert(certs_cell, - OR_CERT_TYPE_AUTH_1024, global_link_cert); - } - - /* Next the RSA->RSA ID cert */ - add_x509_cert(certs_cell, - OR_CERT_TYPE_ID_1024, id_cert); - - /* Next the Ed25519 certs */ - add_ed25519_cert(certs_cell, - CERTTYPE_ED_ID_SIGN, - get_master_signing_key_cert()); - if (conn_in_server_mode) { - tor_assert_nonfatal(conn->handshake_state->own_link_cert || - certs_cell_ed25519_disabled_for_testing); - add_ed25519_cert(certs_cell, - CERTTYPE_ED_SIGN_LINK, - conn->handshake_state->own_link_cert); - } else { - add_ed25519_cert(certs_cell, - CERTTYPE_ED_SIGN_AUTH, - get_current_auth_key_cert()); - } - - /* And finally the crosscert. */ - { - const uint8_t *crosscert=NULL; - size_t crosscert_len; - get_master_rsa_crosscert(&crosscert, &crosscert_len); - if (crosscert) { - add_certs_cell_cert_helper(certs_cell, - CERTTYPE_RSA1024_ID_EDID, - crosscert, crosscert_len); - } - } - - /* We've added all the certs; make the cell. */ - certs_cell->n_certs = certs_cell_getlen_certs(certs_cell); - - ssize_t alloc_len = certs_cell_encoded_len(certs_cell); - tor_assert(alloc_len >= 0 && alloc_len <= UINT16_MAX); - cell = var_cell_new(alloc_len); - cell->command = CELL_CERTS; - ssize_t enc_len = certs_cell_encode(cell->payload, alloc_len, certs_cell); - tor_assert(enc_len > 0 && enc_len <= alloc_len); - cell->payload_len = enc_len; - - connection_or_write_var_cell_to_buf(cell, conn); - var_cell_free(cell); - certs_cell_free(certs_cell); - tor_x509_cert_free(own_link_cert); - - return 0; -} - -#ifdef TOR_UNIT_TESTS -int testing__connection_or_pretend_TLSSECRET_is_supported = 0; -#else -#define testing__connection_or_pretend_TLSSECRET_is_supported 0 -#endif - -/** Return true iff <b>challenge_type</b> is an AUTHCHALLENGE type that - * we can send and receive. */ -int -authchallenge_type_is_supported(uint16_t challenge_type) -{ - switch (challenge_type) { - case AUTHTYPE_RSA_SHA256_TLSSECRET: -#ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS - return 1; -#else - return testing__connection_or_pretend_TLSSECRET_is_supported; -#endif - case AUTHTYPE_ED25519_SHA256_RFC5705: - return 1; - case AUTHTYPE_RSA_SHA256_RFC5705: - default: - return 0; - } -} - -/** Return true iff <b>challenge_type_a</b> is one that we would rather - * use than <b>challenge_type_b</b>. */ -int -authchallenge_type_is_better(uint16_t challenge_type_a, - uint16_t challenge_type_b) -{ - /* Any supported type is better than an unsupported one; - * all unsupported types are equally bad. */ - if (!authchallenge_type_is_supported(challenge_type_a)) - return 0; - if (!authchallenge_type_is_supported(challenge_type_b)) - return 1; - /* It happens that types are superior in numerically ascending order. - * If that ever changes, this must change too. */ - return (challenge_type_a > challenge_type_b); -} - -/** Send an AUTH_CHALLENGE cell on the connection <b>conn</b>. Return 0 - * on success, -1 on failure. */ -int -connection_or_send_auth_challenge_cell(or_connection_t *conn) -{ - var_cell_t *cell = NULL; - int r = -1; - tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); - - if (! conn->handshake_state) - return -1; - - auth_challenge_cell_t *ac = auth_challenge_cell_new(); - - tor_assert(sizeof(ac->challenge) == 32); - crypto_rand((char*)ac->challenge, sizeof(ac->challenge)); - - if (authchallenge_type_is_supported(AUTHTYPE_RSA_SHA256_TLSSECRET)) - auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET); - /* Disabled, because everything that supports this method also supports - * the much-superior ED25519_SHA256_RFC5705 */ - /* auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_RFC5705); */ - if (authchallenge_type_is_supported(AUTHTYPE_ED25519_SHA256_RFC5705)) - auth_challenge_cell_add_methods(ac, AUTHTYPE_ED25519_SHA256_RFC5705); - auth_challenge_cell_set_n_methods(ac, - auth_challenge_cell_getlen_methods(ac)); - - cell = var_cell_new(auth_challenge_cell_encoded_len(ac)); - ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len, - ac); - if (len != cell->payload_len) { - /* LCOV_EXCL_START */ - log_warn(LD_BUG, "Encoded auth challenge cell length not as expected"); - goto done; - /* LCOV_EXCL_STOP */ - } - cell->command = CELL_AUTH_CHALLENGE; - - connection_or_write_var_cell_to_buf(cell, conn); - r = 0; - - done: - var_cell_free(cell); - auth_challenge_cell_free(ac); - - return r; -} - -/** Compute the main body of an AUTHENTICATE cell that a client can use - * to authenticate itself on a v3 handshake for <b>conn</b>. Return it - * in a var_cell_t. - * - * If <b>server</b> is true, only calculate the first - * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's - * determined by the rest of the handshake, and which match the provided value - * exactly. - * - * If <b>server</b> is false and <b>signing_key</b> is NULL, calculate the - * first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything - * that should be signed), but don't actually sign it. - * - * If <b>server</b> is false and <b>signing_key</b> is provided, calculate the - * entire authenticator, signed with <b>signing_key</b>. - * - * Return the length of the cell body on success, and -1 on failure. - */ -var_cell_t * -connection_or_compute_authenticate_cell_body(or_connection_t *conn, - const int authtype, - crypto_pk_t *signing_key, - const ed25519_keypair_t *ed_signing_key, - int server) -{ - auth1_t *auth = NULL; - auth_ctx_t *ctx = auth_ctx_new(); - var_cell_t *result = NULL; - int old_tlssecrets_algorithm = 0; - const char *authtype_str = NULL; - - int is_ed = 0; - - /* assert state is reasonable XXXX */ - switch (authtype) { - case AUTHTYPE_RSA_SHA256_TLSSECRET: - authtype_str = "AUTH0001"; - old_tlssecrets_algorithm = 1; - break; - case AUTHTYPE_RSA_SHA256_RFC5705: - authtype_str = "AUTH0002"; - break; - case AUTHTYPE_ED25519_SHA256_RFC5705: - authtype_str = "AUTH0003"; - is_ed = 1; - break; - default: - tor_assert(0); - break; - } - - auth = auth1_new(); - ctx->is_ed = is_ed; - - /* Type: 8 bytes. */ - memcpy(auth1_getarray_type(auth), authtype_str, 8); - - { - const tor_x509_cert_t *id_cert=NULL; - const common_digests_t *my_digests, *their_digests; - const uint8_t *my_id, *their_id, *client_id, *server_id; - if (tor_tls_get_my_certs(server, NULL, &id_cert)) - goto err; - my_digests = tor_x509_cert_get_id_digests(id_cert); - their_digests = - tor_x509_cert_get_id_digests(conn->handshake_state->certs->id_cert); - tor_assert(my_digests); - tor_assert(their_digests); - my_id = (uint8_t*)my_digests->d[DIGEST_SHA256]; - their_id = (uint8_t*)their_digests->d[DIGEST_SHA256]; - - client_id = server ? their_id : my_id; - server_id = server ? my_id : their_id; - - /* Client ID digest: 32 octets. */ - memcpy(auth->cid, client_id, 32); - - /* Server ID digest: 32 octets. */ - memcpy(auth->sid, server_id, 32); - } - - if (is_ed) { - const ed25519_public_key_t *my_ed_id, *their_ed_id; - if (!conn->handshake_state->certs->ed_id_sign) { - log_warn(LD_OR, "Ed authenticate without Ed ID cert from peer."); - goto err; - } - my_ed_id = get_master_identity_key(); - their_ed_id = &conn->handshake_state->certs->ed_id_sign->signing_key; - - const uint8_t *cid_ed = (server ? their_ed_id : my_ed_id)->pubkey; - const uint8_t *sid_ed = (server ? my_ed_id : their_ed_id)->pubkey; - - memcpy(auth->u1_cid_ed, cid_ed, ED25519_PUBKEY_LEN); - memcpy(auth->u1_sid_ed, sid_ed, ED25519_PUBKEY_LEN); - } - - { - crypto_digest_t *server_d, *client_d; - if (server) { - server_d = conn->handshake_state->digest_sent; - client_d = conn->handshake_state->digest_received; - } else { - client_d = conn->handshake_state->digest_sent; - server_d = conn->handshake_state->digest_received; - } - - /* Server log digest : 32 octets */ - crypto_digest_get_digest(server_d, (char*)auth->slog, 32); - - /* Client log digest : 32 octets */ - crypto_digest_get_digest(client_d, (char*)auth->clog, 32); - } - - { - /* Digest of cert used on TLS link : 32 octets. */ - tor_x509_cert_t *cert = NULL; - if (server) { - cert = tor_tls_get_own_cert(conn->tls); - } else { - cert = tor_tls_get_peer_cert(conn->tls); - } - if (!cert) { - log_warn(LD_OR, "Unable to find cert when making %s data.", - authtype_str); - goto err; - } - - memcpy(auth->scert, - tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); - - tor_x509_cert_free(cert); - } - - /* HMAC of clientrandom and serverrandom using master key : 32 octets */ - if (old_tlssecrets_algorithm) { - if (tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets) < 0) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, "Somebody asked us for an older TLS " - "authentication method (AUTHTYPE_RSA_SHA256_TLSSECRET) " - "which we don't support."); - } - } else { - char label[128]; - tor_snprintf(label, sizeof(label), - "EXPORTER FOR TOR TLS CLIENT BINDING %s", authtype_str); - int r = tor_tls_export_key_material(conn->tls, auth->tlssecrets, - auth->cid, sizeof(auth->cid), - label); - if (r < 0) { - if (r != -2) - log_warn(LD_BUG, "TLS key export failed for unknown reason."); - // If r == -2, this was openssl bug 7712. - goto err; - } - } - - /* 8 octets were reserved for the current time, but we're trying to get out - * of the habit of sending time around willynilly. Fortunately, nothing - * checks it. That's followed by 16 bytes of nonce. */ - crypto_rand((char*)auth->rand, 24); - - ssize_t maxlen = auth1_encoded_len(auth, ctx); - if (ed_signing_key && is_ed) { - maxlen += ED25519_SIG_LEN; - } else if (signing_key && !is_ed) { - maxlen += crypto_pk_keysize(signing_key); - } - - const int AUTH_CELL_HEADER_LEN = 4; /* 2 bytes of type, 2 bytes of length */ - result = var_cell_new(AUTH_CELL_HEADER_LEN + maxlen); - uint8_t *const out = result->payload + AUTH_CELL_HEADER_LEN; - const size_t outlen = maxlen; - ssize_t len; - - result->command = CELL_AUTHENTICATE; - set_uint16(result->payload, htons(authtype)); - - if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) { - /* LCOV_EXCL_START */ - log_warn(LD_BUG, "Unable to encode signed part of AUTH1 data."); - goto err; - /* LCOV_EXCL_STOP */ - } - - if (server) { - auth1_t *tmp = NULL; - ssize_t len2 = auth1_parse(&tmp, out, len, ctx); - if (!tmp) { - /* LCOV_EXCL_START */ - log_warn(LD_BUG, "Unable to parse signed part of AUTH1 data that " - "we just encoded"); - goto err; - /* LCOV_EXCL_STOP */ - } - result->payload_len = (tmp->end_of_signed - result->payload); - - auth1_free(tmp); - if (len2 != len) { - /* LCOV_EXCL_START */ - log_warn(LD_BUG, "Mismatched length when re-parsing AUTH1 data."); - goto err; - /* LCOV_EXCL_STOP */ - } - goto done; - } - - if (ed_signing_key && is_ed) { - ed25519_signature_t sig; - if (ed25519_sign(&sig, out, len, ed_signing_key) < 0) { - /* LCOV_EXCL_START */ - log_warn(LD_BUG, "Unable to sign ed25519 authentication data"); - goto err; - /* LCOV_EXCL_STOP */ - } - auth1_setlen_sig(auth, ED25519_SIG_LEN); - memcpy(auth1_getarray_sig(auth), sig.sig, ED25519_SIG_LEN); - - } else if (signing_key && !is_ed) { - auth1_setlen_sig(auth, crypto_pk_keysize(signing_key)); - - char d[32]; - crypto_digest256(d, (char*)out, len, DIGEST_SHA256); - int siglen = crypto_pk_private_sign(signing_key, - (char*)auth1_getarray_sig(auth), - auth1_getlen_sig(auth), - d, 32); - if (siglen < 0) { - log_warn(LD_OR, "Unable to sign AUTH1 data."); - goto err; - } - - auth1_setlen_sig(auth, siglen); - } - - len = auth1_encode(out, outlen, auth, ctx); - if (len < 0) { - /* LCOV_EXCL_START */ - log_warn(LD_BUG, "Unable to encode signed AUTH1 data."); - goto err; - /* LCOV_EXCL_STOP */ - } - tor_assert(len + AUTH_CELL_HEADER_LEN <= result->payload_len); - result->payload_len = len + AUTH_CELL_HEADER_LEN; - set_uint16(result->payload+2, htons(len)); - - goto done; - - err: - var_cell_free(result); - result = NULL; - done: - auth1_free(auth); - auth_ctx_free(ctx); - return result; -} - -/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on - * success, -1 on failure */ -MOCK_IMPL(int, -connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype)) -{ - var_cell_t *cell; - crypto_pk_t *pk = tor_tls_get_my_client_auth_key(); - /* XXXX make sure we're actually supposed to send this! */ - - if (!pk) { - log_warn(LD_BUG, "Can't compute authenticate cell: no client auth key"); - return -1; - } - if (! authchallenge_type_is_supported(authtype)) { - log_warn(LD_BUG, "Tried to send authenticate cell with unknown " - "authentication type %d", authtype); - return -1; - } - - cell = connection_or_compute_authenticate_cell_body(conn, - authtype, - pk, - get_current_auth_keypair(), - 0 /* not server */); - if (! cell) { - log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unable to compute authenticate cell!"); - return -1; - } - connection_or_write_var_cell_to_buf(cell, conn); - var_cell_free(cell); - - return 0; -} diff --git a/src/core/or/connection_or.h b/src/core/or/connection_or.h index 90b21ad77b..02bc87a864 100644 --- a/src/core/or/connection_or.h +++ b/src/core/or/connection_or.h @@ -97,19 +97,6 @@ MOCK_DECL(void,connection_or_write_var_cell_to_buf,(const var_cell_t *cell, or_connection_t *conn)); int connection_or_send_versions(or_connection_t *conn, int v3_plus); MOCK_DECL(int,connection_or_send_netinfo,(or_connection_t *conn)); -int connection_or_send_certs_cell(or_connection_t *conn); -int connection_or_send_auth_challenge_cell(or_connection_t *conn); -int authchallenge_type_is_supported(uint16_t challenge_type); -int authchallenge_type_is_better(uint16_t challenge_type_a, - uint16_t challenge_type_b); -var_cell_t *connection_or_compute_authenticate_cell_body( - or_connection_t *conn, - const int authtype, - crypto_pk_t *signing_key, - const struct ed25519_keypair_t *ed_signing_key, - int server); -MOCK_DECL(int,connection_or_send_authenticate_cell, - (or_connection_t *conn, int type)); int is_or_protocol_version_known(uint16_t version); @@ -144,7 +131,6 @@ MOCK_DECL(STATIC void,connection_or_change_state, #endif /* defined(CONNECTION_OR_PRIVATE) */ #ifdef TOR_UNIT_TESTS -extern int certs_cell_ed25519_disabled_for_testing; extern int testing__connection_or_pretend_TLSSECRET_is_supported; #endif diff --git a/src/core/or/dos.c b/src/core/or/dos.c index be64b6286e..5f99280030 100644 --- a/src/core/or/dos.c +++ b/src/core/or/dos.c @@ -680,7 +680,7 @@ dos_log_heartbeat(void) /* Called when a new client connection has been established on the given * address. */ void -dos_new_client_conn(or_connection_t *or_conn) +dos_new_client_conn(or_connection_t *or_conn, const char *transport_name) { clientmap_entry_t *entry; @@ -701,7 +701,7 @@ dos_new_client_conn(or_connection_t *or_conn) } /* We are only interested in client connection from the geoip cache. */ - entry = geoip_lookup_client(&or_conn->real_addr, NULL, + entry = geoip_lookup_client(&or_conn->real_addr, transport_name, GEOIP_CLIENT_CONNECT); if (BUG(entry == NULL)) { /* Should never happen because we note down the address in the geoip diff --git a/src/core/or/dos.h b/src/core/or/dos.h index b7b1d3f635..b3eca058b8 100644 --- a/src/core/or/dos.h +++ b/src/core/or/dos.h @@ -53,7 +53,8 @@ int dos_enabled(void); void dos_log_heartbeat(void); void dos_geoip_entry_about_to_free(const struct clientmap_entry_t *geoip_ent); -void dos_new_client_conn(or_connection_t *or_conn); +void dos_new_client_conn(or_connection_t *or_conn, + const char *transport_name); void dos_close_client_conn(const or_connection_t *or_conn); int dos_should_refuse_single_hop_client(void); diff --git a/src/core/or/include.am b/src/core/or/include.am index 4dd251d2e4..3626e76bed 100644 --- a/src/core/or/include.am +++ b/src/core/or/include.am @@ -74,13 +74,11 @@ noinst_HEADERS += \ src/core/or/or_periodic.h \ src/core/or/or_sys.h \ src/core/or/orconn_event.h \ - src/core/or/orconn_event_sys.h \ src/core/or/or_circuit_st.h \ src/core/or/or_connection_st.h \ src/core/or/or_handshake_certs_st.h \ src/core/or/or_handshake_state_st.h \ src/core/or/ocirc_event.h \ - src/core/or/ocirc_event_sys.h \ src/core/or/origin_circuit_st.h \ src/core/or/policies.h \ src/core/or/port_cfg_st.h \ diff --git a/src/core/or/ocirc_event.c b/src/core/or/ocirc_event.c index 66992a0b5f..fa16459175 100644 --- a/src/core/or/ocirc_event.c +++ b/src/core/or/ocirc_event.c @@ -22,7 +22,7 @@ #include "core/or/cpath_build_state_st.h" #include "core/or/ocirc_event.h" -#include "core/or/ocirc_event_sys.h" +#include "core/or/or_sys.h" #include "core/or/origin_circuit_st.h" #include "lib/subsys/subsys.h" @@ -84,7 +84,7 @@ static dispatch_typefns_t ocirc_cevent_fns = { .fmt_fn = ocirc_cevent_fmt, }; -static int +int ocirc_add_pubsub(struct pubsub_connector_t *connector) { if (DISPATCH_REGISTER_TYPE(connector, ocirc_state, ô_state_fns)) @@ -119,10 +119,3 @@ ocirc_cevent_publish(ocirc_cevent_msg_t *msg) { PUBLISH(ocirc_cevent, msg); } - -const subsys_fns_t sys_ocirc_event = { - .name = "ocirc_event", - .supported = true, - .level = -32, - .add_pubsub = ocirc_add_pubsub, -}; diff --git a/src/core/or/ocirc_event_sys.h b/src/core/or/ocirc_event_sys.h deleted file mode 100644 index abb89b04a0..0000000000 --- a/src/core/or/ocirc_event_sys.h +++ /dev/null @@ -1,13 +0,0 @@ -/* Copyright (c) 2007-2020, The Tor Project, Inc. */ - -/** - * \file ocirc_event_sys.h - * \brief Declare subsystem object for the origin circuit event module. - **/ - -#ifndef TOR_OCIRC_EVENT_SYS_H -#define TOR_OCIRC_EVENT_SYS_H - -extern const struct subsys_fns_t sys_ocirc_event; - -#endif /* !defined(TOR_OCIRC_EVENT_SYS_H) */ diff --git a/src/core/or/or_sys.c b/src/core/or/or_sys.c index 394b7945e1..126f5448cf 100644 --- a/src/core/or/or_sys.c +++ b/src/core/or/or_sys.c @@ -34,10 +34,22 @@ subsys_or_shutdown(void) policies_free_all(); } +static int +subsys_or_add_pubsub(struct pubsub_connector_t *connector) +{ + int rv = 0; + if (orconn_add_pubsub(connector) < 0) + rv = -1; + if (ocirc_add_pubsub(connector) < 0) + rv = -1; + return rv; +} + const struct subsys_fns_t sys_or = { .name = "or", .supported = true, .level = 20, .initialize = subsys_or_initialize, .shutdown = subsys_or_shutdown, + .add_pubsub = subsys_or_add_pubsub, }; diff --git a/src/core/or/or_sys.h b/src/core/or/or_sys.h index 3ae09f7b52..7ee56c8682 100644 --- a/src/core/or/or_sys.h +++ b/src/core/or/or_sys.h @@ -14,4 +14,8 @@ extern const struct subsys_fns_t sys_or; +struct pubsub_connector_t; +int ocirc_add_pubsub(struct pubsub_connector_t *connector); +int orconn_add_pubsub(struct pubsub_connector_t *connector); + #endif /* !defined(TOR_CORE_OR_OR_SYS_H) */ diff --git a/src/core/or/orconn_event.c b/src/core/or/orconn_event.c index d0a06aa040..c30e2dd22f 100644 --- a/src/core/or/orconn_event.c +++ b/src/core/or/orconn_event.c @@ -22,7 +22,7 @@ #define ORCONN_EVENT_PRIVATE #include "core/or/orconn_event.h" -#include "core/or/orconn_event_sys.h" +#include "core/or/or_sys.h" DECLARE_PUBLISH(orconn_state); DECLARE_PUBLISH(orconn_status); @@ -65,7 +65,7 @@ static dispatch_typefns_t orconn_status_fns = { .fmt_fn = orconn_status_fmt, }; -static int +int orconn_add_pubsub(struct pubsub_connector_t *connector) { if (DISPATCH_REGISTER_TYPE(connector, orconn_state, &orconn_state_fns)) @@ -90,10 +90,3 @@ orconn_status_publish(orconn_status_msg_t *msg) { PUBLISH(orconn_status, msg); } - -const subsys_fns_t sys_orconn_event = { - .name = "orconn_event", - .supported = true, - .level = -33, - .add_pubsub = orconn_add_pubsub, -}; diff --git a/src/core/or/orconn_event_sys.h b/src/core/or/orconn_event_sys.h deleted file mode 100644 index 02f0b8116b..0000000000 --- a/src/core/or/orconn_event_sys.h +++ /dev/null @@ -1,12 +0,0 @@ -/* Copyright (c) 2007-2020, The Tor Project, Inc. */ - -/** - * \file orconn_event_sys.h - * \brief Declare subsystem object for the OR connection event module. - **/ -#ifndef TOR_ORCONN_EVENT_SYS_H -#define TOR_ORCONN_EVENT_SYS_H - -extern const struct subsys_fns_t sys_orconn_event; - -#endif /* !defined(TOR_ORCONN_EVENT_SYS_H) */ diff --git a/src/core/or/policies.c b/src/core/or/policies.c index a82995fe12..dd4feaadfc 100644 --- a/src/core/or/policies.c +++ b/src/core/or/policies.c @@ -463,8 +463,7 @@ fascist_firewall_use_ipv6(const or_options_t *options) * ClientPreferIPv6DirPort is deprecated, but check it anyway. */ return (options->ClientUseIPv6 == 1 || options->ClientUseIPv4 == 0 || options->ClientPreferIPv6ORPort == 1 || - options->ClientPreferIPv6DirPort == 1 || options->UseBridges == 1 || - options->ClientAutoIPv6ORPort == 1); + options->ClientPreferIPv6DirPort == 1 || options->UseBridges == 1); } /** Do we prefer to connect to IPv6, ignoring ClientPreferIPv6ORPort and @@ -491,15 +490,6 @@ fascist_firewall_prefer_ipv6_impl(const or_options_t *options) return -1; } -/* Choose whether we prefer IPv4 or IPv6 by randomly choosing an address - * family. Return 0 for IPv4, and 1 for IPv6. */ -MOCK_IMPL(int, -fascist_firewall_rand_prefer_ipv6_addr, (void)) -{ - /* TODO: Check for failures, and infer our preference based on this. */ - return crypto_rand_int(2); -} - /** Do we prefer to connect to IPv6 ORPorts? * Use node_ipv6_or_preferred() whenever possible: it supports bridge client * per-node IPv6 preferences. @@ -514,10 +504,7 @@ fascist_firewall_prefer_ipv6_orport(const or_options_t *options) } /* We can use both IPv4 and IPv6 - which do we prefer? */ - if (options->ClientAutoIPv6ORPort == 1) { - /* If ClientAutoIPv6ORPort is 1, we prefer IPv4 or IPv6 at random. */ - return fascist_firewall_rand_prefer_ipv6_addr(); - } else if (options->ClientPreferIPv6ORPort == 1) { + if (options->ClientPreferIPv6ORPort == 1) { return 1; } @@ -1405,9 +1392,9 @@ policy_hash(const policy_map_ent_t *ent) } HT_PROTOTYPE(policy_map, policy_map_ent_t, node, policy_hash, - policy_eq) + policy_eq); HT_GENERATE2(policy_map, policy_map_ent_t, node, policy_hash, - policy_eq, 0.6, tor_reallocarray_, tor_free_) + policy_eq, 0.6, tor_reallocarray_, tor_free_); /** Given a pointer to an addr_policy_t, return a copy of the pointer to the * "canonical" copy of that addr_policy_t; the canonical copy is a single diff --git a/src/core/or/policies.h b/src/core/or/policies.h index b9477b2db1..72a37d62b0 100644 --- a/src/core/or/policies.h +++ b/src/core/or/policies.h @@ -70,7 +70,6 @@ typedef struct short_policy_t { int firewall_is_fascist_or(void); int firewall_is_fascist_dir(void); int fascist_firewall_use_ipv6(const or_options_t *options); -MOCK_DECL(int, fascist_firewall_rand_prefer_ipv6_addr, (void)); int fascist_firewall_prefer_ipv6_orport(const or_options_t *options); int fascist_firewall_prefer_ipv6_dirport(const or_options_t *options); diff --git a/src/core/or/scheduler.c b/src/core/or/scheduler.c index cd9aa54642..6633ccfe1f 100644 --- a/src/core/or/scheduler.c +++ b/src/core/or/scheduler.c @@ -502,7 +502,12 @@ scheduler_free_all(void) the_scheduler = NULL; } -/** Mark a channel as no longer ready to accept writes. */ +/** Mark a channel as no longer ready to accept writes. + * + * Possible state changes: + * - SCHED_CHAN_PENDING -> SCHED_CHAN_WAITING_TO_WRITE + * - SCHED_CHAN_WAITING_FOR_CELLS -> SCHED_CHAN_IDLE + */ MOCK_IMPL(void, scheduler_channel_doesnt_want_writes,(channel_t *chan)) { @@ -513,31 +518,32 @@ scheduler_channel_doesnt_want_writes,(channel_t *chan)) return; } - /* If it's already in pending, we can put it in waiting_to_write */ if (chan->scheduler_state == SCHED_CHAN_PENDING) { /* - * It's in channels_pending, so it shouldn't be in any of - * the other lists. It can't write any more, so it goes to - * channels_waiting_to_write. + * It has cells but no longer can write, so it becomes + * SCHED_CHAN_WAITING_TO_WRITE. It's in channels_pending, so we + * should remove it from the list. */ smartlist_pqueue_remove(channels_pending, scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), chan); scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); - } else { + } else if (chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS) { /* - * It's not in pending, so it can't become waiting_to_write; it's - * either not in any of the lists (nothing to do) or it's already in - * waiting_for_cells (remove it, can't write any more). + * It does not have cells and no longer can write, so it becomes + * SCHED_CHAN_IDLE. */ - if (chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS) { - scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); - } + scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); } } -/** Mark a channel as having waiting cells. */ +/** Mark a channel as having waiting cells. + * + * Possible state changes: + * - SCHED_CHAN_WAITING_FOR_CELLS -> SCHED_CHAN_PENDING + * - SCHED_CHAN_IDLE -> SCHED_CHAN_WAITING_TO_WRITE + */ MOCK_IMPL(void, scheduler_channel_has_waiting_cells,(channel_t *chan)) { @@ -548,12 +554,11 @@ scheduler_channel_has_waiting_cells,(channel_t *chan)) return; } - /* First, check if it's also writeable */ if (chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS) { /* - * It's in channels_waiting_for_cells, so it shouldn't be in any of - * the other lists. It has waiting cells now, so it goes to - * channels_pending. + * It is able to write and now has cells, so it becomes + * SCHED_CHAN_PENDING. It must be added to the channels_pending + * list. */ scheduler_set_channel_state(chan, SCHED_CHAN_PENDING); if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) { @@ -565,16 +570,12 @@ scheduler_channel_has_waiting_cells,(channel_t *chan)) /* If we made a channel pending, we potentially have scheduling work to * do. */ the_scheduler->schedule(); - } else { + } else if (chan->scheduler_state == SCHED_CHAN_IDLE) { /* - * It's not in waiting_for_cells, so it can't become pending; it's - * either not in any of the lists (we add it to waiting_to_write) - * or it's already in waiting_to_write or pending (we do nothing) + * It is not able to write but now has cells, so it becomes + * SCHED_CHAN_WAITING_TO_WRITE. */ - if (!(chan->scheduler_state == SCHED_CHAN_WAITING_TO_WRITE || - chan->scheduler_state == SCHED_CHAN_PENDING)) { - scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); - } + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); } } @@ -662,8 +663,12 @@ scheduler_release_channel,(channel_t *chan)) scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); } -/** Mark a channel as ready to accept writes */ - +/** Mark a channel as ready to accept writes. + * Possible state changes: + * + * - SCHED_CHAN_WAITING_TO_WRITE -> SCHED_CHAN_PENDING + * - SCHED_CHAN_IDLE -> SCHED_CHAN_WAITING_FOR_CELLS + */ void scheduler_channel_wants_writes(channel_t *chan) { @@ -674,10 +679,11 @@ scheduler_channel_wants_writes(channel_t *chan) return; } - /* If it's already in waiting_to_write, we can put it in pending */ if (chan->scheduler_state == SCHED_CHAN_WAITING_TO_WRITE) { /* - * It can write now, so it goes to channels_pending. + * It has cells and can now write, so it becomes + * SCHED_CHAN_PENDING. It must be added to the channels_pending + * list. */ scheduler_set_channel_state(chan, SCHED_CHAN_PENDING); if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) { @@ -688,15 +694,12 @@ scheduler_channel_wants_writes(channel_t *chan) } /* We just made a channel pending, we have scheduling work to do. */ the_scheduler->schedule(); - } else { + } else if (chan->scheduler_state == SCHED_CHAN_IDLE) { /* - * It's not in SCHED_CHAN_WAITING_TO_WRITE, so it can't become pending; - * it's either idle and goes to WAITING_FOR_CELLS, or it's a no-op. + * It does not have cells but can now write, so it becomes + * SCHED_CHAN_WAITING_FOR_CELLS. */ - if (!(chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS || - chan->scheduler_state == SCHED_CHAN_PENDING)) { - scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); - } + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } } diff --git a/src/core/or/scheduler_kist.c b/src/core/or/scheduler_kist.c index e56942be09..c73d768f88 100644 --- a/src/core/or/scheduler_kist.c +++ b/src/core/or/scheduler_kist.c @@ -56,9 +56,9 @@ typedef HT_HEAD(socket_table_s, socket_table_ent_t) socket_table_t; static socket_table_t socket_table = HT_INITIALIZER(); HT_PROTOTYPE(socket_table_s, socket_table_ent_t, node, socket_table_ent_hash, - socket_table_ent_eq) + socket_table_ent_eq); HT_GENERATE2(socket_table_s, socket_table_ent_t, node, socket_table_ent_hash, - socket_table_ent_eq, 0.6, tor_reallocarray, tor_free_) + socket_table_ent_eq, 0.6, tor_reallocarray, tor_free_); /* outbuf_table hash table stuff. The outbuf_table keeps track of which * channels have data sitting in their outbuf so the kist scheduler can force @@ -83,9 +83,9 @@ outbuf_table_ent_eq(const outbuf_table_ent_t *a, const outbuf_table_ent_t *b) } HT_PROTOTYPE(outbuf_table_s, outbuf_table_ent_t, node, outbuf_table_ent_hash, - outbuf_table_ent_eq) + outbuf_table_ent_eq); HT_GENERATE2(outbuf_table_s, outbuf_table_ent_t, node, outbuf_table_ent_hash, - outbuf_table_ent_eq, 0.6, tor_reallocarray, tor_free_) + outbuf_table_ent_eq, 0.6, tor_reallocarray, tor_free_); /***************************************************************************** * Other internal data @@ -463,6 +463,13 @@ MOCK_IMPL(void, channel_write_to_kernel, (channel_t *chan)) log_debug(LD_SCHED, "Writing %lu bytes to kernel for chan %" PRIu64, (unsigned long)channel_outbuf_length(chan), chan->global_identifier); + /* Note that 'connection_handle_write()' may change the scheduler state of + * the channel during the scheduling loop with + * 'connection_or_flushed_some()' -> 'scheduler_channel_wants_writes()'. + * This side-effect will only occur if the channel is currently in the + * 'SCHED_CHAN_WAITING_TO_WRITE' or 'SCHED_CHAN_IDLE' states, which KIST + * rarely uses, so it should be fine unless KIST begins using these states + * in the future. */ connection_handle_write(TO_CONN(BASE_CHAN_TO_TLS(chan)->conn), 0); } diff --git a/src/core/or/sendme.h b/src/core/or/sendme.h index d5b2f8c93f..05d37ec3bb 100644 --- a/src/core/or/sendme.h +++ b/src/core/or/sendme.h @@ -48,7 +48,7 @@ void sendme_record_sending_cell_digest(circuit_t *circ, crypt_path_t *cpath); #define SENDME_MAX_SUPPORTED_VERSION 1 /* The cell version constants for when emitting a cell. */ -#define SENDME_EMIT_MIN_VERSION_DEFAULT 0 +#define SENDME_EMIT_MIN_VERSION_DEFAULT 1 #define SENDME_EMIT_MIN_VERSION_MIN 0 #define SENDME_EMIT_MIN_VERSION_MAX UINT8_MAX diff --git a/src/core/or/status.c b/src/core/or/status.c index f9f603f3b7..ed8448883c 100644 --- a/src/core/or/status.c +++ b/src/core/or/status.c @@ -17,6 +17,7 @@ #include "core/or/or.h" #include "core/or/circuituse.h" #include "app/config/config.h" +#include "feature/dirclient/dirclient.h" #include "core/or/status.h" #include "feature/nodelist/nodelist.h" #include "core/or/relay.h" @@ -146,6 +147,8 @@ log_heartbeat(time_t now) uptime, count_circuits(), bw_sent, bw_rcvd, hibernating?" We are currently hibernating.":""); + dirclient_dump_total_dls(); + if (server_mode(options) && accounting_is_enabled(options) && !hibernating) { log_accounting(now, options); } diff --git a/src/core/proto/proto_ext_or.h b/src/core/proto/proto_ext_or.h index daac3e3eb7..3408599fb7 100644 --- a/src/core/proto/proto_ext_or.h +++ b/src/core/proto/proto_ext_or.h @@ -24,4 +24,11 @@ struct ext_or_cmd_t { int fetch_ext_or_command_from_buf(struct buf_t *buf, struct ext_or_cmd_t **out); +ext_or_cmd_t *ext_or_cmd_new(uint16_t len); + +#define ext_or_cmd_free(cmd) \ + FREE_AND_NULL(ext_or_cmd_t, ext_or_cmd_free_, (cmd)) + +void ext_or_cmd_free_(ext_or_cmd_t *cmd); + #endif /* !defined(TOR_PROTO_EXT_OR_H) */ diff --git a/src/ext/ht.h b/src/ext/ht.h index 9d4add1936..4bfce36903 100644 --- a/src/ext/ht.h +++ b/src/ext/ht.h @@ -232,6 +232,10 @@ ht_string_hash(const char *s) #define HT_ASSERT_(x) (void)0 #endif +/* Macro put at the end of the end of a macro definition so that it + * consumes the following semicolon at file scope. Used only inside ht.h. */ +#define HT_EAT_SEMICOLON__ struct ht_semicolon_eater + #define HT_PROTOTYPE(name, type, field, hashfn, eqfn) \ int name##_HT_GROW(struct name *ht, unsigned min_capacity); \ void name##_HT_CLEAR(struct name *ht); \ @@ -413,7 +417,8 @@ ht_string_hash(const char *s) } \ return NULL; \ } \ - } + } \ + HT_EAT_SEMICOLON__ #define HT_GENERATE2(name, type, field, hashfn, eqfn, load, reallocarrayfn, \ freefn) \ @@ -538,7 +543,8 @@ ht_string_hash(const char *s) if (n != head->hth_n_entries) \ return 6; \ return 0; \ - } + } \ + HT_EAT_SEMICOLON__ #define HT_GENERATE(name, type, field, hashfn, eqfn, load, mallocfn, \ reallocfn, freefn) \ diff --git a/src/feature/client/addressmap.c b/src/feature/client/addressmap.c index 1a6958d38c..cc97166f36 100644 --- a/src/feature/client/addressmap.c +++ b/src/feature/client/addressmap.c @@ -23,7 +23,6 @@ #include "app/config/config.h" #include "core/or/connection_edge.h" #include "feature/control/control_events.h" -#include "feature/relay/dns.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerset.h" @@ -689,7 +688,7 @@ client_dns_set_addressmap_impl(entry_connection_t *for_conn, if (ttl<0) ttl = DEFAULT_DNS_TTL; else - ttl = dns_clip_ttl(ttl); + ttl = clip_dns_ttl(ttl); if (exitname) { /* XXXX fails to ever get attempts to get an exit address of diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c index 2b52a1173d..66b04f3bc2 100644 --- a/src/feature/client/bridges.c +++ b/src/feature/client/bridges.c @@ -844,8 +844,7 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) } } - if (options->ClientPreferIPv6ORPort == -1 || - options->ClientAutoIPv6ORPort == 0) { + if (options->ClientPreferIPv6ORPort == -1) { /* Mark which address to use based on which bridge_t we got. */ node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 && !tor_addr_is_null(&node->ri->ipv6_addr)); diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c index 8962f65006..2843558e93 100644 --- a/src/feature/client/entrynodes.c +++ b/src/feature/client/entrynodes.c @@ -1974,10 +1974,12 @@ get_retry_schedule(time_t failing_since, time_t now, const struct { time_t maximum; int primary_delay; int nonprimary_delay; } delays[] = { + // clang-format off { SIX_HOURS, 10*60, 1*60*60 }, { FOUR_DAYS, 90*60, 4*60*60 }, { SEVEN_DAYS, 4*60*60, 18*60*60 }, { TIME_MAX, 9*60*60, 36*60*60 } + // clang-format on }; unsigned i; diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c index a8ea9781a4..55069bb60a 100644 --- a/src/feature/client/transports.c +++ b/src/feature/client/transports.c @@ -1420,8 +1420,10 @@ create_managed_proxy_environment(const managed_proxy_t *mp) smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=%s", ext_or_addrport_tmp); } - smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s", - cookie_file_loc); + if (cookie_file_loc) { + smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s", + cookie_file_loc); + } tor_free(ext_or_addrport_tmp); tor_free(cookie_file_loc); diff --git a/src/feature/control/btrack.c b/src/feature/control/btrack.c index 874150ee13..3595af0fcc 100644 --- a/src/feature/control/btrack.c +++ b/src/feature/control/btrack.c @@ -57,7 +57,7 @@ btrack_add_pubsub(pubsub_connector_t *connector) const subsys_fns_t sys_btrack = { .name = "btrack", .supported = true, - .level = -30, + .level = 55, .initialize = btrack_init, .shutdown = btrack_fini, .add_pubsub = btrack_add_pubsub, diff --git a/src/feature/control/btrack_orconn_maps.c b/src/feature/control/btrack_orconn_maps.c index 0ef54237a8..a60dffb8c4 100644 --- a/src/feature/control/btrack_orconn_maps.c +++ b/src/feature/control/btrack_orconn_maps.c @@ -47,17 +47,18 @@ bto_chan_eq_(bt_orconn_t *a, bt_orconn_t *b) } HT_HEAD(bto_gid_ht, bt_orconn_t); -HT_PROTOTYPE(bto_gid_ht, bt_orconn_t, node, bto_gid_hash_, bto_gid_eq_) +HT_PROTOTYPE(bto_gid_ht, bt_orconn_t, node, bto_gid_hash_, bto_gid_eq_); HT_GENERATE2(bto_gid_ht, bt_orconn_t, node, bto_gid_hash_, bto_gid_eq_, 0.6, - tor_reallocarray_, tor_free_) + tor_reallocarray_, tor_free_); static struct bto_gid_ht *bto_gid_map; HT_HEAD(bto_chan_ht, bt_orconn_t); -HT_PROTOTYPE(bto_chan_ht, bt_orconn_t, chan_node, bto_chan_hash_, bto_chan_eq_) +HT_PROTOTYPE(bto_chan_ht, bt_orconn_t, chan_node, bto_chan_hash_, + bto_chan_eq_); HT_GENERATE2(bto_chan_ht, bt_orconn_t, chan_node, bto_chan_hash_, bto_chan_eq_, 0.6, - tor_reallocarray_, tor_free_) + tor_reallocarray_, tor_free_); static struct bto_chan_ht *bto_chan_map; /** Clear the GID hash map, freeing any bt_orconn_t objects that become diff --git a/src/feature/control/control_bootstrap.c b/src/feature/control/control_bootstrap.c index 2e78fad690..fee7612ba2 100644 --- a/src/feature/control/control_bootstrap.c +++ b/src/feature/control/control_bootstrap.c @@ -171,6 +171,12 @@ control_event_bootstrap_core(int loglevel, bootstrap_status_t status, control_event_client_status(LOG_NOTICE, "%s", buf); } +int +control_get_bootstrap_percent(void) +{ + return bootstrap_percent; +} + /** Called when Tor has made progress at bootstrapping its directory * information and initial circuits. * diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c index c2d23243e5..cdefef97e1 100644 --- a/src/feature/control/control_cmd.c +++ b/src/feature/control/control_cmd.c @@ -2272,7 +2272,7 @@ typedef struct control_cmd_def_t { **/ #define ONE_LINE(name, flags) \ { \ - #name, \ + (#name), \ handle_control_ ##name, \ flags, \ &name##_syntax, \ @@ -2283,7 +2283,7 @@ typedef struct control_cmd_def_t { * flags. **/ #define MULTLINE(name, flags) \ - { "+"#name, \ + { ("+"#name), \ handle_control_ ##name, \ flags, \ &name##_syntax \ diff --git a/src/feature/control/control_events.h b/src/feature/control/control_events.h index 74bbc0047d..4a5492b510 100644 --- a/src/feature/control/control_events.h +++ b/src/feature/control/control_events.h @@ -12,6 +12,7 @@ #ifndef TOR_CONTROL_EVENTS_H #define TOR_CONTROL_EVENTS_H +#include "lib/cc/ctassert.h" #include "core/or/ocirc_event.h" #include "core/or/orconn_event.h" @@ -164,6 +165,7 @@ int control_event_buildtimeout_set(buildtimeout_set_event_t type, int control_event_signal(uintptr_t signal); void control_event_bootstrap(bootstrap_status_t status, int progress); +int control_get_bootstrap_percent(void); MOCK_DECL(void, control_event_bootstrap_prob_or,(const char *warn, int reason, or_connection_t *or_conn)); @@ -287,10 +289,7 @@ typedef uint64_t event_mask_t; /* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a * different structure, as it can only handle a maximum left shift of 1<<63. */ - -#if EVENT_MAX_ >= EVENT_CAPACITY_ -#error control_connection_t.event_mask has an event greater than its capacity -#endif +CTASSERT(EVENT_MAX_ < EVENT_CAPACITY_); #define EVENT_MASK_(e) (((uint64_t)1)<<(e)) diff --git a/src/feature/dirauth/dirauth_config.c b/src/feature/dirauth/dirauth_config.c index 3aeeab3b31..38d2a8bc5a 100644 --- a/src/feature/dirauth/dirauth_config.c +++ b/src/feature/dirauth/dirauth_config.c @@ -21,12 +21,13 @@ #include "core/or/or.h" #include "app/config/config.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/stats/rephist.h" #include "feature/dirauth/authmode.h" #include "feature/dirauth/bwauth.h" #include "feature/dirauth/dirauth_periodic.h" +#include "feature/dirauth/dirauth_sys.h" #include "feature/dirauth/dirvote.h" #include "feature/dirauth/guardfraction.h" #include "feature/dirauth/dirauth_options_st.h" @@ -45,6 +46,14 @@ #define YES_IF_CHANGED_INT(opt) \ if (!CFG_EQ_INT(old_options, new_options, opt)) return 1; +/** Return true iff we are configured to reject request under load for non + * relay connections. */ +bool +dirauth_should_reject_requests_under_load(void) +{ + return !!dirauth_get_options()->AuthDirRejectRequestsUnderLoad; +} + /** * Legacy validation/normalization function for the dirauth mode options in * options. Uses old_options as the previous options. @@ -296,7 +305,7 @@ options_act_dirauth(const or_options_t *old_options) /* We may need to reschedule some dirauth stuff if our status changed. */ if (old_options) { if (options_transition_affects_dirauth_timing(old_options, options)) { - voting_schedule_recalculate_timing(options, time(NULL)); + dirauth_sched_recalculate_timing(options, time(NULL)); reschedule_dirvote(options); } } diff --git a/src/feature/dirauth/dirauth_config.h b/src/feature/dirauth/dirauth_config.h index 2ebafd917e..9042ff8779 100644 --- a/src/feature/dirauth/dirauth_config.h +++ b/src/feature/dirauth/dirauth_config.h @@ -35,6 +35,8 @@ int options_act_dirauth_mtbf(const struct or_options_t *old_options); int options_act_dirauth_stats(const struct or_options_t *old_options, bool *print_notice_out); +bool dirauth_should_reject_requests_under_load(void); + extern const struct config_format_t dirauth_options_fmt; #else /* !defined(HAVE_MODULE_DIRAUTH) */ @@ -67,16 +69,22 @@ options_validate_dirauth_mode(const struct or_options_t *old_options, (((void)(old_options)),((void)(options)),((void)(msg)),0) #define options_validate_dirauth_testing(old_options, options, msg) \ (((void)(old_options)),((void)(options)),((void)(msg)),0) -#define options_validate_dirauth_testing(old_options, options, msg) \ - (((void)(old_options)),((void)(options)),((void)(msg)),0) #define options_act_dirauth(old_options) \ (((void)(old_options)),0) #define options_act_dirauth_mtbf(old_options) \ (((void)(old_options)),0) -#define options_act_dirauth_stats(old_options, print_notice_out) \ - (((void)(old_options)),((void)(print_notice_out)),0) +static inline int +options_act_dirauth_stats(const struct or_options_t *old_options, + bool *print_notice_out) +{ + (void)old_options; + *print_notice_out = 0; + return 0; +} + +#define dirauth_should_reject_requests_under_load() (false) #endif /* defined(HAVE_MODULE_DIRAUTH) */ diff --git a/src/feature/dirauth/dirauth_options.inc b/src/feature/dirauth/dirauth_options.inc index 5939010fe7..21f4996c39 100644 --- a/src/feature/dirauth/dirauth_options.inc +++ b/src/feature/dirauth/dirauth_options.inc @@ -95,4 +95,11 @@ CONF_VAR(TestingMinFastFlagThreshold, MEMUNIT, 0, "0") * versions? */ CONF_VAR(VersioningAuthoritativeDirectory, BOOL, 0, "0") +/** Boolean: Under bandwidth pressure, if set to 1, the authority will always + * answer directory requests from relays but will start sending 503 error code + * for the other connections. If set to 0, all connections are considered the + * same and the authority will try to answer them all regardless of bandwidth + * pressure or not. */ +CONF_VAR(AuthDirRejectRequestsUnderLoad, BOOL, 0, "1") + END_CONF_STRUCT(dirauth_options_t) diff --git a/src/feature/dirauth/dircollate.c b/src/feature/dirauth/dircollate.c index b35cb021ff..2657f53853 100644 --- a/src/feature/dirauth/dircollate.c +++ b/src/feature/dirauth/dircollate.c @@ -90,9 +90,9 @@ ddmap_entry_set_digests(ddmap_entry_t *ent, } HT_PROTOTYPE(double_digest_map, ddmap_entry_t, node, ddmap_entry_hash, - ddmap_entry_eq) + ddmap_entry_eq); HT_GENERATE2(double_digest_map, ddmap_entry_t, node, ddmap_entry_hash, - ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_) + ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_); /** Helper: add a single vote_routerstatus_t <b>vrs</b> to the collator * <b>dc</b>, indexing it by its RSA key digest, and by the 2-tuple of its RSA @@ -324,4 +324,3 @@ dircollator_get_votes_for_router(dircollator_t *dc, int idx) return digestmap_get(dc->by_collated_rsa_sha1, smartlist_get(dc->all_rsa_sha1_lst, idx)); } - diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index 4e0e19dc91..7eb2b720a6 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -36,7 +36,7 @@ #include "feature/stats/rephist.h" #include "feature/client/entrynodes.h" /* needed for guardfraction methods */ #include "feature/nodelist/torcert.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/dirauth/dirvote.h" #include "feature/dirauth/authmode.h" @@ -886,7 +886,7 @@ dirvote_get_intermediate_param_value(const smartlist_t *param_list, int ok; value = (int32_t) tor_parse_long(integer_str, 10, INT32_MIN, INT32_MAX, &ok, NULL); - if (BUG(! ok)) + if (BUG(!ok)) return default_val; ++n_found; } @@ -2527,9 +2527,12 @@ compute_consensus_package_lines(smartlist_t *votes) * any new signatures in <b>src_voter_list</b> that should be added to * <b>target</b>. (A signature should be added if we have no signature for that * voter in <b>target</b> yet, or if we have no verifiable signature and the - * new signature is verifiable.) Return the number of signatures added or - * changed, or -1 if the document signed by <b>sigs</b> isn't the same - * document as <b>target</b>. */ + * new signature is verifiable.) + * + * Return the number of signatures added or changed, or -1 if the document + * signatures are invalid. Sets *<b>msg_out</b> to a string constant + * describing the signature status. + */ STATIC int networkstatus_add_detached_signatures(networkstatus_t *target, ns_detached_signatures_t *sigs, @@ -2850,7 +2853,7 @@ dirvote_act(const or_options_t *options, time_t now) "Mine is %s.", keys, hex_str(c->cache_info.identity_digest, DIGEST_LEN)); tor_free(keys); - voting_schedule_recalculate_timing(options, now); + dirauth_sched_recalculate_timing(options, now); } #define IF_TIME_FOR_NEXT_ACTION(when_field, done_field) \ @@ -2896,7 +2899,7 @@ dirvote_act(const or_options_t *options, time_t now) networkstatus_get_latest_consensus_by_flavor(FLAV_NS)); /* XXXX We will want to try again later if we haven't got enough * signatures yet. Implement this if it turns out to ever happen. */ - voting_schedule_recalculate_timing(options, now); + dirauth_sched_recalculate_timing(options, now); return voting_schedule.voting_starts; } ENDIF @@ -2963,7 +2966,7 @@ dirvote_perform_vote(void) if (!contents) return -1; - pending_vote = dirvote_add_vote(contents, &msg, &status); + pending_vote = dirvote_add_vote(contents, 0, &msg, &status); tor_free(contents); if (!pending_vote) { log_warn(LD_DIR, "Couldn't store my own vote! (I told myself, '%s'.)", @@ -3119,13 +3122,45 @@ list_v3_auth_ids(void) return keys; } +/* Check the voter information <b>vi</b>, and assert that at least one + * signature is good. Asserts on failure. */ +static void +assert_any_sig_good(const networkstatus_voter_info_t *vi) +{ + int any_sig_good = 0; + SMARTLIST_FOREACH(vi->sigs, document_signature_t *, sig, + if (sig->good_signature) + any_sig_good = 1); + tor_assert(any_sig_good); +} + +/* Add <b>cert</b> to our list of known authority certificates. */ +static void +add_new_cert_if_needed(const struct authority_cert_t *cert) +{ + tor_assert(cert); + if (!authority_cert_get_by_digests(cert->cache_info.identity_digest, + cert->signing_key_digest)) { + /* Hey, it's a new cert! */ + trusted_dirs_load_certs_from_string( + cert->cache_info.signed_descriptor_body, + TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/, + NULL); + if (!authority_cert_get_by_digests(cert->cache_info.identity_digest, + cert->signing_key_digest)) { + log_warn(LD_BUG, "We added a cert, but still couldn't find it."); + } + } +} + /** Called when we have received a networkstatus vote in <b>vote_body</b>. * Parse and validate it, and on success store it as a pending vote (which we * then return). Return NULL on failure. Sets *<b>msg_out</b> and * *<b>status_out</b> to an HTTP response and status code. (V3 authority * only) */ pending_vote_t * -dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) +dirvote_add_vote(const char *vote_body, time_t time_posted, + const char **msg_out, int *status_out) { networkstatus_t *vote; networkstatus_voter_info_t *vi; @@ -3156,13 +3191,7 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) } tor_assert(smartlist_len(vote->voters) == 1); vi = get_voter(vote); - { - int any_sig_good = 0; - SMARTLIST_FOREACH(vi->sigs, document_signature_t *, sig, - if (sig->good_signature) - any_sig_good = 1); - tor_assert(any_sig_good); - } + assert_any_sig_good(vi); ds = trusteddirserver_get_by_v3_auth_digest(vi->identity_digest); if (!ds) { char *keys = list_v3_auth_ids(); @@ -3175,19 +3204,7 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) *msg_out = "Vote not from a recognized v3 authority"; goto err; } - tor_assert(vote->cert); - if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest, - vote->cert->signing_key_digest)) { - /* Hey, it's a new cert! */ - trusted_dirs_load_certs_from_string( - vote->cert->cache_info.signed_descriptor_body, - TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/, - NULL); - if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest, - vote->cert->signing_key_digest)) { - log_warn(LD_BUG, "We added a cert, but still couldn't find it."); - } - } + add_new_cert_if_needed(vote->cert); /* Is it for the right period? */ if (vote->valid_after != voting_schedule.interval_starts) { @@ -3200,6 +3217,23 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) goto err; } + /* Check if we received it, as a post, after the cutoff when we + * start asking other dir auths for it. If we do, the best plan + * is to discard it, because using it greatly increases the chances + * of a split vote for this round (some dir auths got it in time, + * some didn't). */ + if (time_posted && time_posted > voting_schedule.fetch_missing_votes) { + char tbuf1[ISO_TIME_LEN+1], tbuf2[ISO_TIME_LEN+1]; + format_iso_time(tbuf1, time_posted); + format_iso_time(tbuf2, voting_schedule.fetch_missing_votes); + log_warn(LD_DIR, "Rejecting posted vote from %s received at %s; " + "our cutoff for received votes is %s. Check your clock, " + "CPU load, and network load. Also check the authority that " + "posted the vote.", vi->address, tbuf1, tbuf2); + *msg_out = "Posted vote received too late, would be dangerous to count it"; + goto err; + } + /* Fetch any new router descriptors we just learned about */ update_consensus_router_descriptor_downloads(time(NULL), 1, vote); @@ -3564,6 +3598,14 @@ dirvote_add_signatures_to_pending_consensus( return r; } +/** Helper: we just got the <b>detached_signatures_body</b> sent to us as + * signatures on the currently pending consensus. Add them to the pending + * consensus (if we have one). + * + * Set *<b>msg</b> to a string constant describing the status, regardless of + * success or failure. + * + * Return negative on failure, nonnegative on success. */ static int dirvote_add_signatures_to_all_pending_consensuses( const char *detached_signatures_body, @@ -3626,7 +3668,12 @@ dirvote_add_signatures_to_all_pending_consensuses( /** Helper: we just got the <b>detached_signatures_body</b> sent to us as * signatures on the currently pending consensus. Add them to the pending * consensus (if we have one); otherwise queue them until we have a - * consensus. Return negative on failure, nonnegative on success. */ + * consensus. + * + * Set *<b>msg</b> to a string constant describing the status, regardless of + * success or failure. + * + * Return negative on failure, nonnegative on success. */ int dirvote_add_signatures(const char *detached_signatures_body, const char *source, @@ -4597,7 +4644,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, else last_consensus_interval = options->TestingV3AuthInitialVotingInterval; v3_out->valid_after = - voting_schedule_get_start_of_next_interval(now, + voting_sched_get_start_of_interval_after(now, (int)last_consensus_interval, options->TestingV3AuthVotingStartOffset); format_iso_time(tbuf, v3_out->valid_after); diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h index 305094aa49..fa7b1da4ab 100644 --- a/src/feature/dirauth/dirvote.h +++ b/src/feature/dirauth/dirvote.h @@ -94,6 +94,7 @@ void dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, /* Storing signatures and votes functions */ struct pending_vote_t * dirvote_add_vote(const char *vote_body, + time_t time_posted, const char **msg_out, int *status_out); int dirvote_add_signatures(const char *detached_signatures_body, @@ -142,9 +143,13 @@ dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, } static inline struct pending_vote_t * -dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) +dirvote_add_vote(const char *vote_body, + time_t time_posted, + const char **msg_out, + int *status_out) { (void) vote_body; + (void) time_posted; /* If the dirauth module is disabled, this should NEVER be called else we * failed to safeguard the dirauth module. */ tor_assert_nonfatal_unreached(); @@ -162,7 +167,7 @@ dirvote_add_signatures(const char *detached_signatures_body, { (void) detached_signatures_body; (void) source; - (void) msg_out; + *msg_out = "No directory authority support"; /* If the dirauth module is disabled, this should NEVER be called else we * failed to safeguard the dirauth module. */ tor_assert_nonfatal_unreached(); diff --git a/src/feature/dirauth/guardfraction.c b/src/feature/dirauth/guardfraction.c index 40189ce494..b84f804f5f 100644 --- a/src/feature/dirauth/guardfraction.c +++ b/src/feature/dirauth/guardfraction.c @@ -188,7 +188,7 @@ guardfraction_file_parse_inputs_line(const char *inputs_line, * * guardfraction-file-version 1 * written-at <date and time> - * n-inputs <number of consesuses parsed> <number of days considered> + * n-inputs <number of consensuses parsed> <number of days considered> * * guard-seen <fpr 1> <guardfraction percentage> <consensus appearances> * guard-seen <fpr 2> <guardfraction percentage> <consensus appearances> diff --git a/src/feature/dirauth/include.am b/src/feature/dirauth/include.am index 2ef629ae35..e26f120d4e 100644 --- a/src/feature/dirauth/include.am +++ b/src/feature/dirauth/include.am @@ -19,7 +19,8 @@ MODULE_DIRAUTH_SOURCES = \ src/feature/dirauth/recommend_pkg.c \ src/feature/dirauth/shared_random.c \ src/feature/dirauth/shared_random_state.c \ - src/feature/dirauth/voteflags.c + src/feature/dirauth/voteflags.c \ + src/feature/dirauth/voting_schedule.c # ADD_C_FILE: INSERT HEADERS HERE. noinst_HEADERS += \ @@ -43,7 +44,8 @@ noinst_HEADERS += \ src/feature/dirauth/shared_random.h \ src/feature/dirauth/shared_random_state.h \ src/feature/dirauth/vote_microdesc_hash_st.h \ - src/feature/dirauth/voteflags.h + src/feature/dirauth/voteflags.h \ + src/feature/dirauth/voting_schedule.h if BUILD_MODULE_DIRAUTH LIBTOR_APP_A_SOURCES += $(MODULE_DIRAUTH_SOURCES) diff --git a/src/feature/dirauth/keypin.c b/src/feature/dirauth/keypin.c index 6f6cfc01f1..98584a7d42 100644 --- a/src/feature/dirauth/keypin.c +++ b/src/feature/dirauth/keypin.c @@ -118,14 +118,14 @@ return (unsigned) siphash24g(a->ed25519_key, sizeof(a->ed25519_key)); } HT_PROTOTYPE(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa, - keypin_ents_eq_rsa) + keypin_ents_eq_rsa); HT_GENERATE2(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa, - keypin_ents_eq_rsa, 0.6, tor_reallocarray, tor_free_) + keypin_ents_eq_rsa, 0.6, tor_reallocarray, tor_free_); HT_PROTOTYPE(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed, - keypin_ents_eq_ed) + keypin_ents_eq_ed); HT_GENERATE2(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed, - keypin_ents_eq_ed, 0.6, tor_reallocarray, tor_free_) + keypin_ents_eq_ed, 0.6, tor_reallocarray, tor_free_); /** * Check whether we already have an entry in the key pinning table for a diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c index baf8f8c217..5025d0ae39 100644 --- a/src/feature/dirauth/process_descs.c +++ b/src/feature/dirauth/process_descs.c @@ -285,7 +285,7 @@ dirserv_load_fingerprint_file(void) * Return the appropriate router status. * * If the status is 'RTR_REJECT' and <b>msg</b> is provided, set - * *<b>msg</b> to an explanation of why. */ + * *<b>msg</b> to a string constant explaining why. */ uint32_t dirserv_router_get_status(const routerinfo_t *router, const char **msg, int severity) @@ -399,22 +399,21 @@ dirserv_rejects_tor_version(const char *platform, static const char please_upgrade_string[] = "Tor version is insecure or unsupported. Please upgrade!"; - /* Versions before Tor 0.2.9 are unsupported. Versions between 0.2.9.0 and - * 0.2.9.4 suffer from bug #20499, where relays don't keep their consensus - * up to date */ - if (!tor_version_as_new_as(platform,"0.2.9.5-alpha")) { + /* Versions before Tor 0.3.5 are unsupported. + * + * Also, reject unstable versions of 0.3.5, since (as of this writing) + * they are almost none of the network. */ + if (!tor_version_as_new_as(platform,"0.3.5.7")) { if (msg) *msg = please_upgrade_string; return true; } - /* Series between Tor 0.3.0 and 0.3.4 inclusive are unsupported, and some - * have bug #27841, which makes them broken as intro points. Reject them. - * - * Also reject unstable versions of 0.3.5, since (as of this writing) - * they are almost none of the network. */ - if (tor_version_as_new_as(platform,"0.3.0.0-alpha-dev") && - !tor_version_as_new_as(platform,"0.3.5.7")) { + /* Series between Tor 0.3.6 and 0.4.1.4-rc inclusive are unsupported. + * Reject them. 0.3.6.0-alpha-dev only existed for a short time, before + * it was renamed to 0.4.0.0-alpha-dev. */ + if (tor_version_as_new_as(platform,"0.3.6.0-alpha-dev") && + !tor_version_as_new_as(platform,"0.4.1.5")) { if (msg) { *msg = please_upgrade_string; } @@ -564,7 +563,8 @@ dirserv_router_has_valid_address(routerinfo_t *ri) /** Check whether we, as a directory server, want to accept <b>ri</b>. If so, * set its is_valid,running fields and return 0. Otherwise, return -1. * - * If the router is rejected, set *<b>msg</b> to an explanation of why. + * If the router is rejected, set *<b>msg</b> to a string constant explining + * why. * * If <b>complain</b> then explain at log-level 'notice' why we refused * a descriptor; else explain at log-level 'info'. @@ -730,7 +730,8 @@ dirserv_add_multiple_descriptors(const char *desc, size_t desclen, * That means the caller must not access <b>ri</b> after this function * returns, since it might have been freed. * - * Return the status of the operation. + * Return the status of the operation, and set *<b>msg</b> to a string + * constant describing the status. * * This function is only called when fresh descriptors are posted, not when * we re-load the cache. diff --git a/src/feature/dirauth/process_descs.h b/src/feature/dirauth/process_descs.h index 9c13692778..1461ab697d 100644 --- a/src/feature/dirauth/process_descs.h +++ b/src/feature/dirauth/process_descs.h @@ -98,7 +98,7 @@ dirserv_add_multiple_descriptors(const char *desc, size_t desclen, (void)desclen; (void)purpose; (void)source; - (void)msg; + *msg = "No directory authority support"; return (enum was_router_added_t)0; } static inline enum was_router_added_t @@ -107,8 +107,8 @@ dirserv_add_descriptor(routerinfo_t *ri, const char *source) { (void)ri; - (void)msg; (void)source; + *msg = "No directory authority support"; return (enum was_router_added_t)0; } static inline int @@ -125,9 +125,9 @@ authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, int *valid_out) { (void)ri; - (void)msg; (void)complain; - (void)valid_out; + *msg = "No directory authority support"; + *valid_out = 0; return 0; } static inline int @@ -143,8 +143,9 @@ dirserv_router_get_status(const routerinfo_t *router, int severity) { (void)router; - (void)msg; (void)severity; + if (msg) + *msg = "No directory authority support"; return 0; } static inline void diff --git a/src/feature/dirauth/shared_random.c b/src/feature/dirauth/shared_random.c index 48e2147ea6..fd55008242 100644 --- a/src/feature/dirauth/shared_random.c +++ b/src/feature/dirauth/shared_random.c @@ -99,7 +99,7 @@ #include "feature/nodelist/dirlist.h" #include "feature/hs_common/shared_random_client.h" #include "feature/dirauth/shared_random_state.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/dirauth/dirvote.h" #include "feature/dirauth/authmode.h" @@ -1261,7 +1261,7 @@ sr_act_post_consensus(const networkstatus_t *consensus) } /* Prepare our state so that it's ready for the next voting period. */ - sr_state_update(voting_schedule_get_next_valid_after_time()); + sr_state_update(dirauth_sched_get_next_valid_after_time()); } /** Initialize shared random subsystem. This MUST be called early in the boot diff --git a/src/feature/dirauth/shared_random_state.c b/src/feature/dirauth/shared_random_state.c index 1792d540c6..598d781557 100644 --- a/src/feature/dirauth/shared_random_state.c +++ b/src/feature/dirauth/shared_random_state.c @@ -20,7 +20,7 @@ #include "feature/dirauth/shared_random.h" #include "feature/hs_common/shared_random_client.h" #include "feature/dirauth/shared_random_state.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "lib/encoding/confline.h" #include "lib/version/torversion.h" @@ -60,6 +60,7 @@ DUMMY_TYPECHECK_INSTANCE(sr_disk_state_t); #define SR_DISK_STATE_MAGIC 0x98AB1254 /** Array of variables that are saved to disk as a persistent state. */ +// clang-format off static const config_var_t state_vars[] = { V(Version, POSINT, "0"), V(TorVersion, STRING, NULL), @@ -73,6 +74,7 @@ static const config_var_t state_vars[] = { VAR("SharedRandCurrentValue", LINELIST_S, SharedRandValues, NULL), END_OF_CONFIG_VARS }; +// clang-format on /** "Extra" variable in the state that receives lines we can't parse. This * lets us preserve options from versions of Tor newer than us. */ @@ -139,7 +141,7 @@ get_state_valid_until_time(time_t now) voting_interval = get_voting_interval(); /* Find the time the current round started. */ - beginning_of_current_round = get_start_time_of_current_round(); + beginning_of_current_round = dirauth_sched_get_cur_valid_after_time(); /* Find how many rounds are left till the end of the protocol run */ current_round = (now / voting_interval) % total_rounds; @@ -1330,7 +1332,7 @@ sr_state_init(int save_to_disk, int read_from_disk) /* We have a state in memory, let's make sure it's updated for the current * and next voting round. */ { - time_t valid_after = voting_schedule_get_next_valid_after_time(); + time_t valid_after = dirauth_sched_get_next_valid_after_time(); sr_state_update(valid_after); } return 0; diff --git a/src/feature/dircommon/voting_schedule.c b/src/feature/dirauth/voting_schedule.c index 389f7f6b5d..efc4a0b316 100644 --- a/src/feature/dircommon/voting_schedule.c +++ b/src/feature/dirauth/voting_schedule.c @@ -3,12 +3,11 @@ /** * \file voting_schedule.c - * \brief This file contains functions that are from the directory authority - * subsystem related to voting specifically but used by many part of - * tor. The full feature is built as part of the dirauth module. + * \brief Compute information about our voting schedule as a directory + * authority. **/ -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "core/or/or.h" #include "app/config/config.h" @@ -20,55 +19,11 @@ * Vote scheduling * ===== */ -/** Return the start of the next interval of size <b>interval</b> (in - * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always - * starts a fresh interval, and if the last interval of a day would be - * truncated to less than half its size, it is rolled into the - * previous interval. */ -time_t -voting_schedule_get_start_of_next_interval(time_t now, int interval, - int offset) -{ - struct tm tm; - time_t midnight_today=0; - time_t midnight_tomorrow; - time_t next; - - tor_gmtime_r(&now, &tm); - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - - if (tor_timegm(&tm, &midnight_today) < 0) { - // LCOV_EXCL_START - log_warn(LD_BUG, "Ran into an invalid time when trying to find midnight."); - // LCOV_EXCL_STOP - } - midnight_tomorrow = midnight_today + (24*60*60); - - next = midnight_today + ((now-midnight_today)/interval + 1)*interval; - - /* Intervals never cross midnight. */ - if (next > midnight_tomorrow) - next = midnight_tomorrow; - - /* If the interval would only last half as long as it's supposed to, then - * skip over to the next day. */ - if (next + interval/2 > midnight_tomorrow) - next = midnight_tomorrow; - - next += offset; - if (next - interval > now) - next -= interval; - - return next; -} - /* Populate and return a new voting_schedule_t that can be used to schedule * voting. The object is allocated on the heap and it's the responsibility of * the caller to free it. Can't fail. */ static voting_schedule_t * -get_voting_schedule(const or_options_t *options, time_t now, int severity) +create_voting_schedule(const or_options_t *options, time_t now, int severity) { int interval, vote_delay, dist_delay; time_t start; @@ -95,14 +50,15 @@ get_voting_schedule(const or_options_t *options, time_t now, int severity) } tor_assert(interval > 0); + new_voting_schedule->interval = interval; if (vote_delay + dist_delay > interval/2) vote_delay = dist_delay = interval / 4; start = new_voting_schedule->interval_starts = - voting_schedule_get_start_of_next_interval(now,interval, + voting_sched_get_start_of_interval_after(now,interval, options->TestingV3AuthVotingStartOffset); - end = voting_schedule_get_start_of_next_interval(start+1, interval, + end = voting_sched_get_start_of_interval_after(start+1, interval, options->TestingV3AuthVotingStartOffset); tor_assert(end > start); @@ -139,9 +95,13 @@ voting_schedule_free_(voting_schedule_t *voting_schedule_to_free) voting_schedule_t voting_schedule; -/* Using the time <b>now</b>, return the next voting valid-after time. */ -time_t -voting_schedule_get_next_valid_after_time(void) +/** + * Return the current voting schedule, recreating it if necessary. + * + * Dirauth only. + **/ +static const voting_schedule_t * +dirauth_get_voting_schedule(void) { time_t now = approx_time(); bool need_to_recalculate_voting_schedule = false; @@ -167,27 +127,62 @@ voting_schedule_get_next_valid_after_time(void) done: if (need_to_recalculate_voting_schedule) { - voting_schedule_recalculate_timing(get_options(), approx_time()); + dirauth_sched_recalculate_timing(get_options(), approx_time()); voting_schedule.created_on_demand = 1; } - return voting_schedule.interval_starts; + return &voting_schedule; +} + +/** Return the next voting valid-after time. + * + * Dirauth only. */ +time_t +dirauth_sched_get_next_valid_after_time(void) +{ + return dirauth_get_voting_schedule()->interval_starts; +} + +/** + * Return our best idea of what the valid-after time for the _current_ + * consensus, whether we have one or not. + * + * Dirauth only. + **/ +time_t +dirauth_sched_get_cur_valid_after_time(void) +{ + const voting_schedule_t *sched = dirauth_get_voting_schedule(); + time_t next_start = sched->interval_starts; + int interval = sched->interval; + int offset = get_options()->TestingV3AuthVotingStartOffset; + return voting_sched_get_start_of_interval_after(next_start - interval - 1, + interval, + offset); +} + +/** Return the voting interval that we are configured to use. + * + * Dirauth only. */ +int +dirauth_sched_get_configured_interval(void) +{ + return get_options()->V3AuthVotingInterval; } /** Set voting_schedule to hold the timing for the next vote we should be * doing. All type of tor do that because HS subsystem needs the timing as * well to function properly. */ void -voting_schedule_recalculate_timing(const or_options_t *options, time_t now) +dirauth_sched_recalculate_timing(const or_options_t *options, time_t now) { voting_schedule_t *new_voting_schedule; /* get the new voting schedule */ - new_voting_schedule = get_voting_schedule(options, now, LOG_INFO); + new_voting_schedule = create_voting_schedule(options, now, LOG_INFO); tor_assert(new_voting_schedule); /* Fill in the global static struct now */ memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule)); voting_schedule_free(new_voting_schedule); } - diff --git a/src/feature/dircommon/voting_schedule.h b/src/feature/dirauth/voting_schedule.h index e4c6210087..9e2ac29c75 100644 --- a/src/feature/dircommon/voting_schedule.h +++ b/src/feature/dirauth/voting_schedule.h @@ -11,6 +11,8 @@ #include "core/or/or.h" +#ifdef HAVE_MODULE_DIRAUTH + /** Scheduling information for a voting interval. */ typedef struct { /** When do we generate and distribute our vote for this interval? */ @@ -26,6 +28,9 @@ typedef struct { /** When do we publish the consensus? */ time_t interval_starts; + /** Our computed dirauth interval */ + int interval; + /** True iff we have generated and distributed our vote. */ int have_voted; /** True iff we've requested missing votes. */ @@ -53,12 +58,36 @@ typedef struct { extern voting_schedule_t voting_schedule; -void voting_schedule_recalculate_timing(const or_options_t *options, +void dirauth_sched_recalculate_timing(const or_options_t *options, time_t now); -time_t voting_schedule_get_start_of_next_interval(time_t now, - int interval, - int offset); -time_t voting_schedule_get_next_valid_after_time(void); +time_t dirauth_sched_get_next_valid_after_time(void); +time_t dirauth_sched_get_cur_valid_after_time(void); +int dirauth_sched_get_configured_interval(void); + +#else /* !defined(HAVE_MODULE_DIRAUTH) */ + +#define dirauth_sched_recalculate_timing(opt,now) \ + ((void)(opt), (void)(now)) + +static inline time_t +dirauth_sched_get_next_valid_after_time(void) +{ + tor_assert_unreached(); + return 0; +} +static inline time_t +dirauth_sched_get_cur_valid_after_time(void) +{ + tor_assert_unreached(); + return 0; +} +static inline int +dirauth_sched_get_configured_interval(void) +{ + tor_assert_unreached(); + return 1; +} +#endif /* defined(HAVE_MODULE_DIRAUTH) */ #endif /* !defined(TOR_VOTING_SCHEDULE_H) */ diff --git a/src/feature/dircache/conscache.c b/src/feature/dircache/conscache.c index ceba410a5f..d9aaccddc1 100644 --- a/src/feature/dircache/conscache.c +++ b/src/feature/dircache/conscache.c @@ -132,6 +132,15 @@ consensus_cache_may_overallocate(consensus_cache_t *cache) #endif } +// HACK: GCC on Appveyor hates that we may assert before returning. Work around +// the error. +#ifdef _WIN32 +#ifndef COCCI +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn" +#endif +#endif + /** * Tell the sandbox (if any) configured by <b>cfg</b> to allow the * operations that <b>cache</b> will need. @@ -156,6 +165,12 @@ consensus_cache_register_with_sandbox(consensus_cache_t *cache, return storage_dir_register_with_sandbox(cache->dir, cfg); } +#ifdef _WIN32 +#ifndef COCCI +#pragma GCC diagnostic pop +#endif +#endif + /** * Helper: clear all entries from <b>cache</b> (but do not delete * any that aren't marked for removal diff --git a/src/feature/dircache/consdiffmgr.c b/src/feature/dircache/consdiffmgr.c index 8445b8f986..10590cd6d2 100644 --- a/src/feature/dircache/consdiffmgr.c +++ b/src/feature/dircache/consdiffmgr.c @@ -218,9 +218,9 @@ cdm_diff_eq(const cdm_diff_t *diff1, const cdm_diff_t *diff2) diff1->compress_method == diff2->compress_method; } -HT_PROTOTYPE(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq) +HT_PROTOTYPE(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq); HT_GENERATE2(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq, - 0.6, tor_reallocarray, tor_free_) + 0.6, tor_reallocarray, tor_free_); #define cdm_diff_free(diff) \ FREE_AND_NULL(cdm_diff_t, cdm_diff_free_, (diff)) diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index ef7054001e..4f7f209207 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -957,7 +957,7 @@ handle_get_current_consensus(dir_connection_t *conn, goto done; } - if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) { + if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) { log_debug(LD_DIRSERV, "Client asked for network status lists, but we've been " "writing too many bytes lately. Sending 503 Dir busy."); @@ -1066,7 +1066,7 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args) } }); - if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) { + if (connection_dir_is_global_write_low(TO_CONN(conn), estimated_len)) { write_short_http_response(conn, 503, "Directory busy, try again later"); goto vote_done; } @@ -1125,7 +1125,7 @@ handle_get_microdesc(dir_connection_t *conn, const get_handler_args_t *args) write_short_http_response(conn, 404, "Not found"); goto done; } - if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) { + if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) { log_info(LD_DIRSERV, "Client asked for server descriptors, but we've been " "writing too many bytes lately. Sending 503 Dir busy."); @@ -1223,7 +1223,7 @@ handle_get_descriptor(dir_connection_t *conn, const get_handler_args_t *args) msg = "Not found"; write_short_http_response(conn, 404, msg); } else { - if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) { + if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) { log_info(LD_DIRSERV, "Client asked for server descriptors, but we've been " "writing too many bytes lately. Sending 503 Dir busy."); @@ -1319,9 +1319,8 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args) SMARTLIST_FOREACH(certs, authority_cert_t *, c, len += c->cache_info.signed_descriptor_len); - if (global_write_bucket_low(TO_CONN(conn), - compress_method != NO_METHOD ? len/2 : len, - 2)) { + if (connection_dir_is_global_write_low(TO_CONN(conn), + compress_method != NO_METHOD ? len/2 : len)) { write_short_http_response(conn, 503, "Directory busy, try again later"); goto keys_done; } @@ -1696,7 +1695,7 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, !strcmp(url,"/tor/post/vote")) { /* v3 networkstatus vote */ const char *msg = "OK"; int status; - if (dirvote_add_vote(body, &msg, &status)) { + if (dirvote_add_vote(body, approx_time(), &msg, &status)) { write_short_http_response(conn, status, "Vote stored"); } else { tor_assert(msg); diff --git a/src/feature/dirclient/dirclient.c b/src/feature/dirclient/dirclient.c index 1b6eed12f0..2072dddadd 100644 --- a/src/feature/dirclient/dirclient.c +++ b/src/feature/dirclient/dirclient.c @@ -21,6 +21,7 @@ #include "feature/client/entrynodes.h" #include "feature/control/control_events.h" #include "feature/dirauth/authmode.h" +#include "feature/dirclient/dirclient.h" #include "feature/dirauth/dirvote.h" #include "feature/dirauth/shared_random.h" #include "feature/dircache/dirserv.h" @@ -51,6 +52,7 @@ #include "feature/rend/rendservice.h" #include "feature/stats/predict_ports.h" +#include "lib/cc/ctassert.h" #include "lib/compress/compress.h" #include "lib/crypt_ops/crypto_format.h" #include "lib/crypt_ops/crypto_util.h" @@ -1443,9 +1445,7 @@ compare_strs_(const void **a, const void **b) } #define CONDITIONAL_CONSENSUS_FPR_LEN 3 -#if (CONDITIONAL_CONSENSUS_FPR_LEN > DIGEST_LEN) -#error "conditional consensus fingerprint length is larger than digest length" -#endif +CTASSERT(CONDITIONAL_CONSENSUS_FPR_LEN <= DIGEST_LEN); /** Return the URL we should use for a consensus download. * @@ -1964,6 +1964,44 @@ dir_client_decompress_response_body(char **bodyp, size_t *bodylenp, return rv; } +/** + * Total number of bytes downloaded of each directory purpose, when + * bootstrapped, and when not bootstrapped. + * + * (For example, the number of bytes downloaded of purpose p while + * not fully bootstrapped is total_dl[p][false].) + **/ +static uint64_t total_dl[DIR_PURPOSE_MAX_][2]; + +/** + * Heartbeat: dump a summary of how many bytes of which purpose we've + * downloaded, when bootstrapping and when not bootstrapping. + **/ +void +dirclient_dump_total_dls(void) +{ + const or_options_t *options = get_options(); + for (int bootstrapped = 0; bootstrapped < 2; ++bootstrapped) { + bool first_time = true; + for (int i=0; i < DIR_PURPOSE_MAX_; ++i) { + uint64_t n = total_dl[i][bootstrapped]; + if (n == 0) + continue; + if (options->SafeLogging_ != SAFELOG_SCRUB_NONE && + purpose_needs_anonymity(i, ROUTER_PURPOSE_GENERAL, NULL)) + continue; + if (first_time) { + log_notice(LD_NET, + "While %sbootstrapping, fetched this many bytes: ", + bootstrapped?"not ":""); + first_time = false; + } + log_notice(LD_NET, " %"PRIu64" (%s)", + n, dir_conn_purpose_to_string(i)); + } + } +} + /** We are a client, and we've finished reading the server's * response. Parse it and act appropriately. * @@ -1997,6 +2035,16 @@ connection_dir_client_reached_eof(dir_connection_t *conn) received_bytes = connection_get_inbuf_len(TO_CONN(conn)); + log_debug(LD_DIR, "Downloaded %"TOR_PRIuSZ" bytes on connection of purpose " + "%s; bootstrap %d%%", + received_bytes, + dir_conn_purpose_to_string(conn->base_.purpose), + control_get_bootstrap_percent()); + { + bool bootstrapped = control_get_bootstrap_percent() == 100; + total_dl[conn->base_.purpose][bootstrapped] += received_bytes; + } + switch (connection_fetch_from_buf_http(TO_CONN(conn), &headers, MAX_HEADERS_SIZE, &body, &body_len, MAX_DIR_DL_SIZE, @@ -2364,7 +2412,7 @@ handle_response_fetch_status_vote(dir_connection_t *conn, conn->base_.port, conn->requested_resource); return -1; } - dirvote_add_vote(body, &msg, &st); + dirvote_add_vote(body, 0, &msg, &st); if (st > 299) { log_warn(LD_DIR, "Error adding retrieved vote: %s", msg); } else { diff --git a/src/feature/dirclient/dirclient.h b/src/feature/dirclient/dirclient.h index 08209721bb..096b197526 100644 --- a/src/feature/dirclient/dirclient.h +++ b/src/feature/dirclient/dirclient.h @@ -14,6 +14,8 @@ #include "feature/hs/hs_ident.h" +void dirclient_dump_total_dls(void); + int directories_have_accepted_server_descriptor(void); void directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, dirinfo_type_t type, const char *payload, diff --git a/src/feature/dircommon/fp_pair.c b/src/feature/dircommon/fp_pair.c index 8b55896ba8..87e1c253bd 100644 --- a/src/feature/dircommon/fp_pair.c +++ b/src/feature/dircommon/fp_pair.c @@ -57,10 +57,10 @@ fp_pair_map_entry_hash(const fp_pair_map_entry_t *a) */ HT_PROTOTYPE(fp_pair_map_impl, fp_pair_map_entry_t, node, - fp_pair_map_entry_hash, fp_pair_map_entries_eq) + fp_pair_map_entry_hash, fp_pair_map_entries_eq); HT_GENERATE2(fp_pair_map_impl, fp_pair_map_entry_t, node, fp_pair_map_entry_hash, fp_pair_map_entries_eq, - 0.6, tor_reallocarray_, tor_free_) + 0.6, tor_reallocarray_, tor_free_); /** Constructor to create a new empty map from fp_pair_t to void * */ @@ -312,4 +312,3 @@ fp_pair_map_assert_ok(const fp_pair_map_t *map) { tor_assert(!fp_pair_map_impl_HT_REP_IS_BAD_(&(map->head))); } - diff --git a/src/feature/dircommon/include.am b/src/feature/dircommon/include.am index f0f0323d12..87850ce183 100644 --- a/src/feature/dircommon/include.am +++ b/src/feature/dircommon/include.am @@ -3,8 +3,7 @@ LIBTOR_APP_A_SOURCES += \ src/feature/dircommon/consdiff.c \ src/feature/dircommon/directory.c \ - src/feature/dircommon/fp_pair.c \ - src/feature/dircommon/voting_schedule.c + src/feature/dircommon/fp_pair.c # ADD_C_FILE: INSERT HEADERS HERE. noinst_HEADERS += \ @@ -12,5 +11,4 @@ noinst_HEADERS += \ src/feature/dircommon/dir_connection_st.h \ src/feature/dircommon/directory.h \ src/feature/dircommon/fp_pair.h \ - src/feature/dircommon/vote_timing_st.h \ - src/feature/dircommon/voting_schedule.h + src/feature/dircommon/vote_timing_st.h diff --git a/src/feature/dirparse/authcert_members.h b/src/feature/dirparse/authcert_members.h index c6755bb629..53eab175d6 100644 --- a/src/feature/dirparse/authcert_members.h +++ b/src/feature/dirparse/authcert_members.h @@ -14,6 +14,7 @@ #ifndef TOR_AUTHCERT_MEMBERS_H #define TOR_AUTHCERT_MEMBERS_H +// clang-format off #define AUTHCERT_MEMBERS \ T1("dir-key-certificate-version", K_DIR_KEY_CERTIFICATE_VERSION, \ GE(1), NO_OBJ ), \ @@ -25,5 +26,6 @@ T1("dir-key-certification", K_DIR_KEY_CERTIFICATION,\ NO_ARGS, NEED_OBJ),\ T01("dir-address", K_DIR_ADDRESS, GE(1), NO_OBJ) +// clang-format on #endif /* !defined(TOR_AUTHCERT_MEMBERS_H) */ diff --git a/src/feature/dirparse/authcert_parse.c b/src/feature/dirparse/authcert_parse.c index 3d42119b94..deb45c12de 100644 --- a/src/feature/dirparse/authcert_parse.c +++ b/src/feature/dirparse/authcert_parse.c @@ -21,11 +21,13 @@ #include "feature/dirparse/authcert_members.h" /** List of tokens recognized in V3 authority certificates. */ +// clang-format off static token_rule_t dir_key_certificate_table[] = { AUTHCERT_MEMBERS, T1("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ), END_OF_TABLE }; +// clang-format on /** Parse a key certificate from <b>s</b>; point <b>end-of-string</b> to * the first character after the certificate. */ diff --git a/src/feature/dirparse/microdesc_parse.c b/src/feature/dirparse/microdesc_parse.c index c2eabeb404..9231080aaa 100644 --- a/src/feature/dirparse/microdesc_parse.c +++ b/src/feature/dirparse/microdesc_parse.c @@ -28,6 +28,7 @@ #include "feature/nodelist/microdesc_st.h" /** List of tokens recognized in microdescriptors */ +// clang-format off static token_rule_t microdesc_token_table[] = { T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024), T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ), @@ -39,6 +40,7 @@ static token_rule_t microdesc_token_table[] = { A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ ), END_OF_TABLE }; +// clang-format on /** Assuming that s starts with a microdesc, return the start of the * *NEXT* one. Return NULL on "not found." */ diff --git a/src/feature/dirparse/ns_parse.c b/src/feature/dirparse/ns_parse.c index 4d9b6e6e73..ac9325a608 100644 --- a/src/feature/dirparse/ns_parse.c +++ b/src/feature/dirparse/ns_parse.c @@ -43,6 +43,7 @@ /** List of tokens recognized in the body part of v3 networkstatus * documents. */ +// clang-format off static token_rule_t rtrstatus_token_table[] = { T01("p", K_P, CONCAT_ARGS, NO_OBJ ), T1( "r", K_R, GE(7), NO_OBJ ), @@ -56,8 +57,10 @@ static token_rule_t rtrstatus_token_table[] = { T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), END_OF_TABLE }; +// clang-format on /** List of tokens recognized in V3 networkstatus votes. */ +// clang-format off static token_rule_t networkstatus_token_table[] = { T1_START("network-status-version", K_NETWORK_STATUS_VERSION, GE(1), NO_OBJ ), @@ -98,8 +101,10 @@ static token_rule_t networkstatus_token_table[] = { END_OF_TABLE }; +// clang-format on /** List of tokens recognized in V3 networkstatus consensuses. */ +// clang-format off static token_rule_t networkstatus_consensus_token_table[] = { T1_START("network-status-version", K_NETWORK_STATUS_VERSION, GE(1), NO_OBJ ), @@ -136,14 +141,17 @@ static token_rule_t networkstatus_consensus_token_table[] = { END_OF_TABLE }; +// clang-format on /** List of tokens recognized in the footer of v1 directory footers. */ +// clang-format off static token_rule_t networkstatus_vote_footer_token_table[] = { T01("directory-footer", K_DIRECTORY_FOOTER, NO_ARGS, NO_OBJ ), T01("bandwidth-weights", K_BW_WEIGHTS, ARGS, NO_OBJ ), T( "directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ), END_OF_TABLE }; +// clang-format on /** Try to find the start and end of the signed portion of a networkstatus * document in <b>s</b>. On success, set <b>start_out</b> to the first diff --git a/src/feature/dirparse/parsecommon.c b/src/feature/dirparse/parsecommon.c index 75ef70d4ee..0c63cd4846 100644 --- a/src/feature/dirparse/parsecommon.c +++ b/src/feature/dirparse/parsecommon.c @@ -403,12 +403,19 @@ get_next_token(memarea_t *area, } if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a public key */ + if (o_syn != NEED_KEY && o_syn != NEED_KEY_1024 && o_syn != OBJ_OK) { + RET_ERR("Unexpected public key."); + } tok->key = crypto_pk_asn1_decode(tok->object_body, tok->object_size); if (! tok->key) RET_ERR("Couldn't parse public key."); } else if (!strcmp(tok->object_type, "RSA PRIVATE KEY")) { /* private key */ + if (o_syn != NEED_SKEY_1024 && o_syn != OBJ_OK) { + RET_ERR("Unexpected private key."); + } tok->key = crypto_pk_asn1_decode_private(tok->object_body, - tok->object_size); + tok->object_size, + 1024); if (! tok->key) RET_ERR("Couldn't parse private key."); } diff --git a/src/feature/dirparse/routerparse.c b/src/feature/dirparse/routerparse.c index f476beec66..8828a0f97a 100644 --- a/src/feature/dirparse/routerparse.c +++ b/src/feature/dirparse/routerparse.c @@ -81,6 +81,7 @@ /****************************************************************************/ /** List of tokens recognized in router descriptors */ +// clang-format off const token_rule_t routerdesc_token_table[] = { T0N("reject", K_REJECT, ARGS, NO_OBJ ), T0N("accept", K_ACCEPT, ARGS, NO_OBJ ), @@ -123,8 +124,10 @@ const token_rule_t routerdesc_token_table[] = { END_OF_TABLE }; +// clang-format on /** List of tokens recognized in extra-info documents. */ +// clang-format off static token_rule_t extrainfo_token_table[] = { T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ), T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ), @@ -162,6 +165,7 @@ static token_rule_t extrainfo_token_table[] = { END_OF_TABLE }; +// clang-format on #undef T diff --git a/src/feature/hs/.may_include b/src/feature/hs/.may_include index 424c745c12..11c5ffbb14 100644 --- a/src/feature/hs/.may_include +++ b/src/feature/hs/.may_include @@ -1 +1,2 @@ *.h +*.inc diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c index 9cf408ca3e..44cd2505fd 100644 --- a/src/feature/hs/hs_cache.c +++ b/src/feature/hs/hs_cache.c @@ -27,6 +27,21 @@ static int cached_client_descriptor_has_expired(time_t now, const hs_cache_client_descriptor_t *cached_desc); +/** Helper function: Return true iff the cache entry has a decrypted + * descriptor. + * + * A NULL desc object in the entry means that we were not able to decrypt the + * descriptor because we are likely lacking client authorization. It is still + * a valid entry but some operations can't be done without the decrypted + * descriptor thus this function MUST be used to safe guard access to the + * decrypted desc object. */ +static inline bool +entry_has_decrypted_descriptor(const hs_cache_client_descriptor_t *entry) +{ + tor_assert(entry); + return (entry->desc != NULL); +} + /********************** Directory HS cache ******************/ /** Directory descriptor cache. Map indexed by blinded key. */ @@ -341,8 +356,23 @@ static digest256map_t *hs_cache_client_intro_state; static size_t cache_get_client_entry_size(const hs_cache_client_descriptor_t *entry) { - return sizeof(*entry) + - strlen(entry->encoded_desc) + hs_desc_obj_size(entry->desc); + size_t size = 0; + + if (entry == NULL) { + goto end; + } + size += sizeof(*entry); + + if (entry->encoded_desc) { + size += strlen(entry->encoded_desc); + } + + if (entry_has_decrypted_descriptor(entry)) { + size += hs_desc_obj_size(entry->desc); + } + + end: + return size; } /** Remove a given descriptor from our cache. */ @@ -659,14 +689,20 @@ cache_store_as_client(hs_cache_client_descriptor_t *client_desc) * client authorization. */ cache_entry = lookup_v3_desc_as_client(client_desc->key.pubkey); if (cache_entry != NULL) { - /* Signalling an undecrypted descriptor. We'll always replace the one we - * have with the new one just fetched. */ - if (cache_entry->desc == NULL) { + /* If the current or the new cache entry don't have a decrypted descriptor + * (missing client authorization), we always replace the current one with + * the new one. Reason is that we can't inspect the revision counter + * within the plaintext data so we blindly replace. */ + if (!entry_has_decrypted_descriptor(cache_entry) || + !entry_has_decrypted_descriptor(client_desc)) { remove_v3_desc_as_client(cache_entry); cache_client_desc_free(cache_entry); goto store; } + /* From this point on, we know that the decrypted descriptor is in the + * current entry and new object thus safe to access. */ + /* If we have an entry in our cache that has a revision counter greater * than the one we just fetched, discard the one we fetched. */ if (cache_entry->desc->plaintext_data.revision_counter > @@ -740,11 +776,15 @@ cache_clean_v3_as_client(time_t now) MAP_DEL_CURRENT(key); entry_size = cache_get_client_entry_size(entry); bytes_removed += entry_size; + /* We just removed an old descriptor. We need to close all intro circuits - * so we don't have leftovers that can be selected while lacking a - * descriptor. We leave the rendezvous circuits opened because they could - * be in use. */ - hs_client_close_intro_circuits_from_desc(entry->desc); + * if the descriptor is decrypted so we don't have leftovers that can be + * selected while lacking a descriptor. Circuits are selected by intro + * authentication key thus we need the descriptor. We leave the rendezvous + * circuits opened because they could be in use. */ + if (entry_has_decrypted_descriptor(entry)) { + hs_client_close_intro_circuits_from_desc(entry->desc); + } /* Entry is not in the cache anymore, destroy it. */ cache_client_desc_free(entry); /* Update our OOM. We didn't use the remove() function because we are in @@ -793,7 +833,7 @@ hs_cache_lookup_as_client(const ed25519_public_key_t *key) tor_assert(key); cached_desc = lookup_v3_desc_as_client(key->pubkey); - if (cached_desc && cached_desc->desc) { + if (cached_desc && entry_has_decrypted_descriptor(cached_desc)) { return cached_desc->desc; } @@ -866,7 +906,7 @@ hs_cache_remove_as_client(const ed25519_public_key_t *key) /* If we have a decrypted/decoded descriptor, attempt to close its * introduction circuit(s). We shouldn't have circuit(s) without a * descriptor else it will lead to a failure. */ - if (cached_desc->desc) { + if (entry_has_decrypted_descriptor(cached_desc)) { hs_client_close_intro_circuits_from_desc(cached_desc->desc); } /* Remove and free. */ @@ -995,7 +1035,7 @@ hs_cache_client_new_auth_parse(const ed25519_public_key_t *service_pk) } cached_desc = lookup_v3_desc_as_client(service_pk->pubkey); - if (cached_desc == NULL || cached_desc->desc != NULL) { + if (cached_desc == NULL || entry_has_decrypted_descriptor(cached_desc)) { /* No entry for that service or the descriptor is already decoded. */ goto end; } diff --git a/src/feature/hs/hs_cell.c b/src/feature/hs/hs_cell.c index 52bd663200..fc9f4a2654 100644 --- a/src/feature/hs/hs_cell.c +++ b/src/feature/hs/hs_cell.c @@ -13,6 +13,7 @@ #include "feature/hs_common/replaycache.h" #include "feature/hs/hs_cell.h" +#include "feature/hs/hs_ob.h" #include "core/crypto/hs_ntor.h" #include "core/or/origin_circuit_st.h" @@ -67,14 +68,17 @@ compute_introduce_mac(const uint8_t *encoded_cell, size_t encoded_cell_len, memwipe(mac_msg, 0, sizeof(mac_msg)); } -/** From a set of keys, subcredential and the ENCRYPTED section of an - * INTRODUCE2 cell, return a newly allocated intro cell keys structure. - * Finally, the client public key is copied in client_pk. On error, return - * NULL. */ +/** + * From a set of keys, a list of subcredentials, and the ENCRYPTED section of + * an INTRODUCE2 cell, return an array of newly allocated intro cell keys + * structures. Finally, the client public key is copied in client_pk. On + * error, return NULL. + **/ static hs_ntor_intro_cell_keys_t * get_introduce2_key_material(const ed25519_public_key_t *auth_key, const curve25519_keypair_t *enc_key, - const uint8_t *subcredential, + size_t n_subcredentials, + const hs_subcredential_t *subcredentials, const uint8_t *encrypted_section, curve25519_public_key_t *client_pk) { @@ -82,17 +86,19 @@ get_introduce2_key_material(const ed25519_public_key_t *auth_key, tor_assert(auth_key); tor_assert(enc_key); - tor_assert(subcredential); + tor_assert(n_subcredentials > 0); + tor_assert(subcredentials); tor_assert(encrypted_section); tor_assert(client_pk); - keys = tor_malloc_zero(sizeof(*keys)); + keys = tor_calloc(n_subcredentials, sizeof(hs_ntor_intro_cell_keys_t)); /* First bytes of the ENCRYPTED section are the client public key. */ memcpy(client_pk->public_key, encrypted_section, CURVE25519_PUBKEY_LEN); - if (hs_ntor_service_get_introduce1_keys(auth_key, enc_key, client_pk, - subcredential, keys) < 0) { + if (hs_ntor_service_get_introduce1_keys_multi(auth_key, enc_key, client_pk, + n_subcredentials, + subcredentials, keys) < 0) { /* Don't rely on the caller to wipe this on error. */ memwipe(client_pk, 0, sizeof(curve25519_public_key_t)); tor_free(keys); @@ -747,6 +753,74 @@ hs_cell_parse_intro_established(const uint8_t *payload, size_t payload_len) return ret; } +/** For the encrypted INTRO2 cell in <b>encrypted_section</b>, use the crypto + * material in <b>data</b> to compute the right ntor keys. Also validate the + * INTRO2 MAC to ensure that the keys are the right ones. + * + * Return NULL on failure to either produce the key material or on MAC + * validation. Else return a newly allocated intro keys object. */ +static hs_ntor_intro_cell_keys_t * +get_introduce2_keys_and_verify_mac(hs_cell_introduce2_data_t *data, + const uint8_t *encrypted_section, + size_t encrypted_section_len) +{ + hs_ntor_intro_cell_keys_t *intro_keys = NULL; + hs_ntor_intro_cell_keys_t *intro_keys_result = NULL; + + /* Build the key material out of the key material found in the cell. */ + intro_keys = get_introduce2_key_material(data->auth_pk, data->enc_kp, + data->n_subcredentials, + data->subcredentials, + encrypted_section, + &data->client_pk); + if (intro_keys == NULL) { + log_info(LD_REND, "Invalid INTRODUCE2 encrypted data. Unable to " + "compute key material"); + return NULL; + } + + /* Make sure we are not about to underflow. */ + if (BUG(encrypted_section_len < DIGEST256_LEN)) { + return NULL; + } + + /* Validate MAC from the cell and our computed key material. The MAC field + * in the cell is at the end of the encrypted section. */ + intro_keys_result = tor_malloc_zero(sizeof(*intro_keys_result)); + for (unsigned i = 0; i < data->n_subcredentials; ++i) { + uint8_t mac[DIGEST256_LEN]; + + /* The MAC field is at the very end of the ENCRYPTED section. */ + size_t mac_offset = encrypted_section_len - sizeof(mac); + /* Compute the MAC. Use the entire encoded payload with a length up to the + * ENCRYPTED section. */ + compute_introduce_mac(data->payload, + data->payload_len - encrypted_section_len, + encrypted_section, encrypted_section_len, + intro_keys[i].mac_key, + sizeof(intro_keys[i].mac_key), + mac, sizeof(mac)); + /* Time-invariant conditional copy: if the MAC is what we expected, then + * set intro_keys_result to intro_keys[i]. Otherwise, don't: but don't + * leak which one it was! */ + bool equal = tor_memeq(mac, encrypted_section + mac_offset, sizeof(mac)); + memcpy_if_true_timei(equal, intro_keys_result, &intro_keys[i], + sizeof(*intro_keys_result)); + } + + /* We no longer need intro_keys. */ + memwipe(intro_keys, 0, + sizeof(hs_ntor_intro_cell_keys_t) * data->n_subcredentials); + tor_free(intro_keys); + + if (safe_mem_is_zero(intro_keys_result, sizeof(*intro_keys_result))) { + log_info(LD_REND, "Invalid MAC validation for INTRODUCE2 cell"); + tor_free(intro_keys_result); /* sets intro_keys_result to NULL */ + } + + return intro_keys_result; +} + /** Parse the INTRODUCE2 cell using data which contains everything we need to * do so and contains the destination buffers of information we extract and * compute from the cell. Return 0 on success else a negative value. The @@ -795,47 +869,29 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data, /* Check our replay cache for this introduction point. */ if (replaycache_add_test_and_elapsed(data->replay_cache, encrypted_section, encrypted_section_len, &elapsed)) { - log_warn(LD_REND, "Possible replay detected! An INTRODUCE2 cell with the" + log_warn(LD_REND, "Possible replay detected! An INTRODUCE2 cell with the " "same ENCRYPTED section was seen %ld seconds ago. " "Dropping cell.", (long int) elapsed); goto done; } - /* Build the key material out of the key material found in the cell. */ - intro_keys = get_introduce2_key_material(data->auth_pk, data->enc_kp, - data->subcredential, - encrypted_section, - &data->client_pk); - if (intro_keys == NULL) { - log_info(LD_REND, "Invalid INTRODUCE2 encrypted data. Unable to " - "compute key material on circuit %u for service %s", - TO_CIRCUIT(circ)->n_circ_id, + /* First bytes of the ENCRYPTED section are the client public key (they are + * guaranteed to exist because of the length check above). We are gonna use + * the client public key to compute the ntor keys and decrypt the payload: + */ + memcpy(&data->client_pk.public_key, encrypted_section, + CURVE25519_PUBKEY_LEN); + + /* Get the right INTRODUCE2 ntor keys and verify the cell MAC */ + intro_keys = get_introduce2_keys_and_verify_mac(data, encrypted_section, + encrypted_section_len); + if (!intro_keys) { + log_warn(LD_REND, "Could not get valid INTRO2 keys on circuit %u " + "for service %s", TO_CIRCUIT(circ)->n_circ_id, safe_str_client(service->onion_address)); goto done; } - /* Validate MAC from the cell and our computed key material. The MAC field - * in the cell is at the end of the encrypted section. */ - { - uint8_t mac[DIGEST256_LEN]; - /* The MAC field is at the very end of the ENCRYPTED section. */ - size_t mac_offset = encrypted_section_len - sizeof(mac); - /* Compute the MAC. Use the entire encoded payload with a length up to the - * ENCRYPTED section. */ - compute_introduce_mac(data->payload, - data->payload_len - encrypted_section_len, - encrypted_section, encrypted_section_len, - intro_keys->mac_key, sizeof(intro_keys->mac_key), - mac, sizeof(mac)); - if (tor_memcmp(mac, encrypted_section + mac_offset, sizeof(mac))) { - log_info(LD_REND, "Invalid MAC validation for INTRODUCE2 cell on " - "circuit %u for service %s", - TO_CIRCUIT(circ)->n_circ_id, - safe_str_client(service->onion_address)); - goto done; - } - } - { /* The ENCRYPTED_DATA section starts just after the CLIENT_PK. */ const uint8_t *encrypted_data = diff --git a/src/feature/hs/hs_cell.h b/src/feature/hs/hs_cell.h index 80f37057d2..2b28c44c50 100644 --- a/src/feature/hs/hs_cell.h +++ b/src/feature/hs/hs_cell.h @@ -16,6 +16,8 @@ * 3.2.2 of the specification). Below this value, the cell must be padded. */ #define HS_CELL_INTRODUCE1_MIN_SIZE 246 +struct hs_subcredential_t; + /** This data structure contains data that we need to build an INTRODUCE1 cell * used by the INTRODUCE1 build function. */ typedef struct hs_cell_introduce1_data_t { @@ -29,7 +31,7 @@ typedef struct hs_cell_introduce1_data_t { /** Introduction point encryption public key. */ const curve25519_public_key_t *enc_pk; /** Subcredentials of the service. */ - const uint8_t *subcredential; + const struct hs_subcredential_t *subcredential; /** Onion public key for the ntor handshake. */ const curve25519_public_key_t *onion_pk; /** Rendezvous cookie. */ @@ -55,9 +57,14 @@ typedef struct hs_cell_introduce2_data_t { owned by the introduction point object through which we received the INTRO2 cell*/ const curve25519_keypair_t *enc_kp; - /** Subcredentials of the service. Pointer owned by the descriptor that owns - the introduction point through which we received the INTRO2 cell. */ - const uint8_t *subcredential; + /** + * Length of the subcredentials array below. + **/ + size_t n_subcredentials; + /** Array of <b>n_subcredentials</b> subcredentials for the service. Pointer + * owned by the descriptor that owns the introduction point through which we + * received the INTRO2 cell. */ + const struct hs_subcredential_t *subcredentials; /** Payload of the received encoded cell. */ const uint8_t *payload; /** Size of the payload of the received encoded cell. */ diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c index 90805a98b7..dc13c7045e 100644 --- a/src/feature/hs/hs_circuit.c +++ b/src/feature/hs/hs_circuit.c @@ -19,6 +19,7 @@ #include "feature/client/circpathbias.h" #include "feature/hs/hs_cell.h" #include "feature/hs/hs_circuit.h" +#include "feature/hs/hs_ob.h" #include "feature/hs/hs_circuitmap.h" #include "feature/hs/hs_client.h" #include "feature/hs/hs_ident.h" @@ -367,10 +368,10 @@ get_service_anonymity_string(const hs_service_t *service) * success, a circuit identifier is attached to the circuit with the needed * data. This function will try to open a circuit for a maximum value of * MAX_REND_FAILURES then it will give up. */ -static void -launch_rendezvous_point_circuit(const hs_service_t *service, - const hs_service_intro_point_t *ip, - const hs_cell_introduce2_data_t *data) +MOCK_IMPL(STATIC void, +launch_rendezvous_point_circuit,(const hs_service_t *service, + const hs_service_intro_point_t *ip, + const hs_cell_introduce2_data_t *data)) { int circ_needs_uptime; time_t now = time(NULL); @@ -578,7 +579,7 @@ retry_service_rendezvous_point(const origin_circuit_t *circ) static int setup_introduce1_data(const hs_desc_intro_point_t *ip, const node_t *rp_node, - const uint8_t *subcredential, + const hs_subcredential_t *subcredential, hs_cell_introduce1_data_t *intro1_data) { int ret = -1; @@ -958,6 +959,42 @@ hs_circ_handle_intro_established(const hs_service_t *service, return ret; } +/** + * Go into <b>data</b> and add the right subcredential to be able to handle + * this incoming cell. + * + * <b>desc_subcred</b> is the subcredential of the descriptor that corresponds + * to the intro point that received this intro request. This subcredential + * should be used if we are not an onionbalance instance. + * + * Return 0 if everything went well, or -1 in case of internal error. + */ +static int +get_subcredential_for_handling_intro2_cell(const hs_service_t *service, + hs_cell_introduce2_data_t *data, + const hs_subcredential_t *desc_subcred) +{ + /* Handle the simple case first: We are not an onionbalance instance and we + * should just use the regular descriptor subcredential */ + if (!hs_ob_service_is_instance(service)) { + data->n_subcredentials = 1; + data->subcredentials = desc_subcred; + return 0; + } + + /* This should not happen since we should have made onionbalance + * subcredentials when we created our descriptors. */ + if (BUG(!service->state.ob_subcreds)) { + return -1; + } + + /* We are an onionbalance instance: */ + data->n_subcredentials = service->state.n_ob_subcreds; + data->subcredentials = service->state.ob_subcreds; + + return 0; +} + /** We just received an INTRODUCE2 cell on the established introduction circuit * circ. Handle the INTRODUCE2 payload of size payload_len for the given * circuit and service. This cell is associated with the intro point object ip @@ -966,7 +1003,7 @@ int hs_circ_handle_introduce2(const hs_service_t *service, const origin_circuit_t *circ, hs_service_intro_point_t *ip, - const uint8_t *subcredential, + const hs_subcredential_t *subcredential, const uint8_t *payload, size_t payload_len) { int ret = -1; @@ -983,12 +1020,16 @@ hs_circ_handle_introduce2(const hs_service_t *service, * parsed, decrypted and key material computed correctly. */ data.auth_pk = &ip->auth_key_kp.pubkey; data.enc_kp = &ip->enc_key_kp; - data.subcredential = subcredential; data.payload = payload; data.payload_len = payload_len; data.link_specifiers = smartlist_new(); data.replay_cache = ip->replay_cache; + if (get_subcredential_for_handling_intro2_cell(service, + &data, subcredential)) { + goto done; + } + if (hs_cell_parse_introduce2(&data, circ, service) < 0) { goto done; } @@ -1092,7 +1133,7 @@ int hs_circ_send_introduce1(origin_circuit_t *intro_circ, origin_circuit_t *rend_circ, const hs_desc_intro_point_t *ip, - const uint8_t *subcredential) + const hs_subcredential_t *subcredential) { int ret = -1; ssize_t payload_len; diff --git a/src/feature/hs/hs_circuit.h b/src/feature/hs/hs_circuit.h index 92231369c6..22e936e685 100644 --- a/src/feature/hs/hs_circuit.h +++ b/src/feature/hs/hs_circuit.h @@ -46,15 +46,16 @@ int hs_circ_handle_intro_established(const hs_service_t *service, origin_circuit_t *circ, const uint8_t *payload, size_t payload_len); +struct hs_subcredential_t; int hs_circ_handle_introduce2(const hs_service_t *service, const origin_circuit_t *circ, hs_service_intro_point_t *ip, - const uint8_t *subcredential, + const struct hs_subcredential_t *subcredential, const uint8_t *payload, size_t payload_len); int hs_circ_send_introduce1(origin_circuit_t *intro_circ, origin_circuit_t *rend_circ, const hs_desc_intro_point_t *ip, - const uint8_t *subcredential); + const struct hs_subcredential_t *subcredential); int hs_circ_send_establish_rendezvous(origin_circuit_t *circ); /* e2e circuit API. */ @@ -78,6 +79,12 @@ create_rp_circuit_identifier(const hs_service_t *service, const curve25519_public_key_t *server_pk, const struct hs_ntor_rend_cell_keys_t *keys); +struct hs_cell_introduce2_data_t; +MOCK_DECL(STATIC void, +launch_rendezvous_point_circuit,(const hs_service_t *service, + const hs_service_intro_point_t *ip, + const struct hs_cell_introduce2_data_t *data)); + #endif /* defined(HS_CIRCUIT_PRIVATE) */ #endif /* !defined(TOR_HS_CIRCUIT_H) */ diff --git a/src/feature/hs/hs_circuitmap.c b/src/feature/hs/hs_circuitmap.c index 2343d729dd..466a02de39 100644 --- a/src/feature/hs/hs_circuitmap.c +++ b/src/feature/hs/hs_circuitmap.c @@ -76,11 +76,11 @@ hs_circuit_hash_token(const circuit_t *circuit) HT_PROTOTYPE(hs_circuitmap_ht, // The name of the hashtable struct circuit_t, // The name of the element struct, hs_circuitmap_node, // The name of HT_ENTRY member - hs_circuit_hash_token, hs_circuits_have_same_token) + hs_circuit_hash_token, hs_circuits_have_same_token); HT_GENERATE2(hs_circuitmap_ht, circuit_t, hs_circuitmap_node, hs_circuit_hash_token, hs_circuits_have_same_token, - 0.6, tor_reallocarray, tor_free_) + 0.6, tor_reallocarray, tor_free_); #ifdef TOR_UNIT_TESTS diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index 222261e604..cc1b01d2ef 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -646,7 +646,7 @@ send_introduce1(origin_circuit_t *intro_circ, /* Send the INTRODUCE1 cell. */ if (hs_circ_send_introduce1(intro_circ, rend_circ, ip, - desc->subcredential) < 0) { + &desc->subcredential) < 0) { if (TO_CIRCUIT(intro_circ)->marked_for_close) { /* If the introduction circuit was closed, we were unable to send the * cell for some reasons. In any case, the intro circuit has to be @@ -1845,10 +1845,10 @@ hs_client_decode_descriptor(const char *desc_str, hs_descriptor_t **desc) { hs_desc_decode_status_t ret; - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; ed25519_public_key_t blinded_pubkey; hs_client_service_authorization_t *client_auth = NULL; - curve25519_secret_key_t *client_auht_sk = NULL; + curve25519_secret_key_t *client_auth_sk = NULL; tor_assert(desc_str); tor_assert(service_identity_pk); @@ -1857,7 +1857,7 @@ hs_client_decode_descriptor(const char *desc_str, /* Check if we have a client authorization for this service in the map. */ client_auth = find_client_auth(service_identity_pk); if (client_auth) { - client_auht_sk = &client_auth->enc_seckey; + client_auth_sk = &client_auth->enc_seckey; } /* Create subcredential for this HS so that we can decrypt */ @@ -1865,13 +1865,13 @@ hs_client_decode_descriptor(const char *desc_str, uint64_t current_time_period = hs_get_time_period_num(0); hs_build_blinded_pubkey(service_identity_pk, NULL, 0, current_time_period, &blinded_pubkey); - hs_get_subcredential(service_identity_pk, &blinded_pubkey, subcredential); + hs_get_subcredential(service_identity_pk, &blinded_pubkey, &subcredential); } /* Parse descriptor */ - ret = hs_desc_decode_descriptor(desc_str, subcredential, - client_auht_sk, desc); - memwipe(subcredential, 0, sizeof(subcredential)); + ret = hs_desc_decode_descriptor(desc_str, &subcredential, + client_auth_sk, desc); + memwipe(&subcredential, 0, sizeof(subcredential)); if (ret != HS_DESC_DECODE_OK) { goto err; } @@ -2486,4 +2486,3 @@ set_hs_client_auths_map(digest256map_t *map) } #endif /* defined(TOR_UNIT_TESTS) */ - diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c index f8b031cc26..4639cdb68a 100644 --- a/src/feature/hs/hs_common.c +++ b/src/feature/hs/hs_common.c @@ -22,6 +22,7 @@ #include "feature/hs/hs_client.h" #include "feature/hs/hs_common.h" #include "feature/hs/hs_dos.h" +#include "feature/hs/hs_ob.h" #include "feature/hs/hs_ident.h" #include "feature/hs/hs_service.h" #include "feature/hs_common/shared_random_client.h" @@ -808,12 +809,12 @@ hs_parse_address_impl(const char *address, ed25519_public_key_t *key_out, } /** Using the given identity public key and a blinded public key, compute the - * subcredential and put it in subcred_out (must be of size DIGEST256_LEN). + * subcredential and put it in subcred_out. * This can't fail. */ void hs_get_subcredential(const ed25519_public_key_t *identity_pk, const ed25519_public_key_t *blinded_pk, - uint8_t *subcred_out) + hs_subcredential_t *subcred_out) { uint8_t credential[DIGEST256_LEN]; crypto_digest_t *digest; @@ -841,7 +842,8 @@ hs_get_subcredential(const ed25519_public_key_t *identity_pk, sizeof(credential)); crypto_digest_add_bytes(digest, (const char *) blinded_pk->pubkey, ED25519_PUBKEY_LEN); - crypto_digest_get_digest(digest, (char *) subcred_out, DIGEST256_LEN); + crypto_digest_get_digest(digest, (char *) subcred_out->subcred, + SUBCRED_LEN); crypto_digest_free(digest); memwipe(credential, 0, sizeof(credential)); @@ -909,30 +911,35 @@ hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn) * case the caller would want only one field. checksum_out MUST at least be 2 * bytes long. * - * Return 0 if parsing went well; return -1 in case of error. */ + * Return 0 if parsing went well; return -1 in case of error and if errmsg is + * non NULL, a human readable string message is set. */ int -hs_parse_address(const char *address, ed25519_public_key_t *key_out, - uint8_t *checksum_out, uint8_t *version_out) +hs_parse_address_no_log(const char *address, ed25519_public_key_t *key_out, + uint8_t *checksum_out, uint8_t *version_out, + const char **errmsg) { char decoded[HS_SERVICE_ADDR_LEN]; tor_assert(address); + if (errmsg) { + *errmsg = NULL; + } + /* Obvious length check. */ if (strlen(address) != HS_SERVICE_ADDR_LEN_BASE32) { - log_warn(LD_REND, "Service address %s has an invalid length. " - "Expected %lu but got %lu.", - escaped_safe_str(address), - (unsigned long) HS_SERVICE_ADDR_LEN_BASE32, - (unsigned long) strlen(address)); + if (errmsg) { + *errmsg = "Invalid length"; + } goto invalid; } /* Decode address so we can extract needed fields. */ if (base32_decode(decoded, sizeof(decoded), address, strlen(address)) != sizeof(decoded)) { - log_warn(LD_REND, "Service address %s can't be decoded.", - escaped_safe_str(address)); + if (errmsg) { + *errmsg = "Unable to base32 decode"; + } goto invalid; } @@ -944,6 +951,22 @@ hs_parse_address(const char *address, ed25519_public_key_t *key_out, return -1; } +/** Same has hs_parse_address_no_log() but emits a log warning on parsing + * failure. */ +int +hs_parse_address(const char *address, ed25519_public_key_t *key_out, + uint8_t *checksum_out, uint8_t *version_out) +{ + const char *errmsg = NULL; + int ret = hs_parse_address_no_log(address, key_out, checksum_out, + version_out, &errmsg); + if (ret < 0) { + log_warn(LD_REND, "Service address %s failed to be parsed: %s", + escaped_safe_str(address), errmsg); + } + return ret; +} + /** Validate a given onion address. The length, the base32 decoding, and * checksum are validated. Return 1 if valid else 0. */ int @@ -1807,6 +1830,7 @@ hs_free_all(void) hs_service_free_all(); hs_cache_free_all(); hs_client_free_all(); + hs_ob_free_all(); } /** For the given origin circuit circ, decrement the number of rendezvous diff --git a/src/feature/hs/hs_common.h b/src/feature/hs/hs_common.h index 8f743d4d37..997b7298a6 100644 --- a/src/feature/hs/hs_common.h +++ b/src/feature/hs/hs_common.h @@ -179,6 +179,10 @@ void hs_build_address(const struct ed25519_public_key_t *key, uint8_t version, int hs_address_is_valid(const char *address); int hs_parse_address(const char *address, struct ed25519_public_key_t *key_out, uint8_t *checksum_out, uint8_t *version_out); +int hs_parse_address_no_log(const char *address, + struct ed25519_public_key_t *key_out, + uint8_t *checksum_out, uint8_t *version_out, + const char **errmsg); void hs_build_blinded_pubkey(const struct ed25519_public_key_t *pubkey, const uint8_t *secret, size_t secret_len, @@ -210,9 +214,10 @@ const uint8_t *rend_data_get_pk_digest(const rend_data_t *rend_data, routerstatus_t *pick_hsdir(const char *desc_id, const char *desc_id_base32); +struct hs_subcredential_t; void hs_get_subcredential(const struct ed25519_public_key_t *identity_pk, const struct ed25519_public_key_t *blinded_pk, - uint8_t *subcred_out); + struct hs_subcredential_t *subcred_out); uint64_t hs_get_previous_time_period_num(time_t now); uint64_t hs_get_time_period_num(time_t now); diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c index 64656b1935..0dad8dd6d8 100644 --- a/src/feature/hs/hs_config.c +++ b/src/feature/hs/hs_config.c @@ -26,12 +26,68 @@ #include "feature/hs/hs_common.h" #include "feature/hs/hs_config.h" #include "feature/hs/hs_client.h" +#include "feature/hs/hs_ob.h" #include "feature/hs/hs_service.h" #include "feature/rend/rendclient.h" #include "feature/rend/rendservice.h" #include "lib/encoding/confline.h" +#include "lib/conf/confdecl.h" +#include "lib/confmgt/confmgt.h" + +#include "feature/hs/hs_opts_st.h" #include "app/config/or_options_st.h" +/* Declare the table mapping hs options to hs_opts_t */ +#define CONF_CONTEXT TABLE +#include "feature/hs/hs_options.inc" +#undef CONF_CONTEXT + +/** Magic number for hs_opts_t. */ +#define HS_OPTS_MAGIC 0x6f6e796e + +static const config_format_t hs_opts_fmt = { + .size = sizeof(hs_opts_t), + .magic = { "hs_opts_t", + HS_OPTS_MAGIC, + offsetof(hs_opts_t, magic) }, + .vars = hs_opts_t_vars, +}; + +/** Global configuration manager to handle HS sections*/ +static config_mgr_t *hs_opts_mgr = NULL; + +/** + * Return a configuration manager for the hs_opts_t configuration type. + **/ +static const config_mgr_t * +get_hs_opts_mgr(void) +{ + if (PREDICT_UNLIKELY(hs_opts_mgr == NULL)) { + hs_opts_mgr = config_mgr_new(&hs_opts_fmt); + config_mgr_freeze(hs_opts_mgr); + } + return hs_opts_mgr; +} + +/** + * Allocate, initialize, and return a new hs_opts_t. + **/ +static hs_opts_t * +hs_opts_new(void) +{ + const config_mgr_t *mgr = get_hs_opts_mgr(); + hs_opts_t *r = config_new(mgr); + tor_assert(r); + config_init(mgr, r); + return r; +} + +/** + * Free an hs_opts_t. + **/ +#define hs_opts_free(opts) \ + config_free(get_hs_opts_mgr(), (opts)) + /** Using the given list of services, stage them into our global state. Every * service version are handled. This function can remove entries in the given * service_list. @@ -116,33 +172,27 @@ service_is_duplicate_in_list(const smartlist_t *service_list, return ret; } -/** Helper function: Given an configuration option name, its value, a minimum - * min and a maxium max, parse the value as a uint64_t. On success, ok is set - * to 1 and ret is the parsed value. On error, ok is set to 0 and ret must be - * ignored. This function logs both on error and success. */ -static uint64_t -helper_parse_uint64(const char *opt, const char *value, uint64_t min, - uint64_t max, int *ok) +/** Check whether an integer <b>i</b> is out of bounds (not between <b>low</b> + * and <b>high</b> incusive). If it is, then log a warning about the option + * <b>name</b>, and return true. Otherwise return false. */ +static bool +check_value_oob(int i, const char *name, int low, int high) { - uint64_t ret = 0; - - tor_assert(opt); - tor_assert(value); - tor_assert(ok); - - *ok = 0; - ret = tor_parse_uint64(value, 10, min, max, ok, NULL); - if (!*ok) { - log_warn(LD_CONFIG, "%s must be between %" PRIu64 " and %"PRIu64 - ", not %s.", - opt, min, max, value); - goto err; + if (i < low || i > high) { + log_warn(LD_CONFIG, "%s must be between %d and %d, not %d.", + name, low, high, i); + return true; } - log_info(LD_CONFIG, "%s was parsed to %" PRIu64, opt, ret); - err: - return ret; + return false; } +/** + * Helper: check whether the integer value called <b>name</b> in <b>opts</b> + * is out-of-bounds. + **/ +#define CHECK_OOB(opts, name, low, high) \ + check_value_oob((opts)->name, #name, (low), (high)) + /** Helper function: Given a configuration option and its value, parse the * value as a hs_circuit_id_protocol_t. On success, ok is set to 1 and ret is * the parse value. On error, ok is set to 0 and the "none" @@ -189,6 +239,12 @@ config_learn_service_version(hs_service_t *service) return version; } +/** + * Header key indicating the start of a new hidden service configuration + * block. + **/ +static const char SECTION_HEADER[] = "HiddenServiceDir"; + /** Return true iff the given options starting at line_ for a hidden service * contains at least one invalid option. Each hidden service option don't * apply to all versions so this function can find out. The line_ MUST start @@ -219,6 +275,7 @@ config_has_invalid_options(const config_line_t *line_, "HiddenServiceEnableIntroDoSDefense", "HiddenServiceEnableIntroDoSRatePerSec", "HiddenServiceEnableIntroDoSBurstPerSec", + "HiddenServiceOnionBalanceInstance", NULL /* End marker. */ }; @@ -242,8 +299,11 @@ config_has_invalid_options(const config_line_t *line_, for (int i = 0; optlist[i]; i++) { const char *opt = optlist[i]; for (line = line_; line; line = line->next) { - if (!strcasecmp(line->key, "HiddenServiceDir")) { - /* We just hit the next hidden service, stop right now. */ + if (!strcasecmp(line->key, SECTION_HEADER)) { + /* We just hit the next hidden service, stop right now. + * (This shouldn't be possible, now that we have partitioned the list + * into sections.) */ + tor_assert_nonfatal_unreached(); goto end; } if (!strcasecmp(line->key, opt)) { @@ -302,105 +362,68 @@ config_validate_service(const hs_service_config_t *config) return -1; } -/** Configuration funcion for a version 3 service. The line_ must be pointing - * to the directive directly after a HiddenServiceDir. That way, when hitting - * the next HiddenServiceDir line or reaching the end of the list of lines, we - * know that we have to stop looking for more options. The given service +/** Configuration funcion for a version 3 service. The given service * object must be already allocated and passed through * config_generic_service() prior to calling this function. * * Return 0 on success else a negative value. */ static int -config_service_v3(const config_line_t *line_, +config_service_v3(const hs_opts_t *hs_opts, hs_service_config_t *config) { - int have_num_ip = 0; - bool export_circuit_id = false; /* just to detect duplicate options */ - bool dos_enabled = false, dos_rate_per_sec = false; - bool dos_burst_per_sec = false; - const char *dup_opt_seen = NULL; - const config_line_t *line; - tor_assert(config); + tor_assert(hs_opts); - for (line = line_; line; line = line->next) { - int ok = 0; - if (!strcasecmp(line->key, "HiddenServiceDir")) { - /* We just hit the next hidden service, stop right now. */ - break; - } - /* Number of introduction points. */ - if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) { - config->num_intro_points = - (unsigned int) helper_parse_uint64(line->key, line->value, - NUM_INTRO_POINTS_DEFAULT, - HS_CONFIG_V3_MAX_INTRO_POINTS, - &ok); - if (!ok || have_num_ip) { - if (have_num_ip) - dup_opt_seen = line->key; - goto err; - } - have_num_ip = 1; - continue; - } - if (!strcasecmp(line->key, "HiddenServiceExportCircuitID")) { - config->circuit_id_protocol = - helper_parse_circuit_id_protocol(line->key, line->value, &ok); - if (!ok || export_circuit_id) { - if (export_circuit_id) { - dup_opt_seen = line->key; - } - goto err; - } - export_circuit_id = true; - continue; - } - if (!strcasecmp(line->key, "HiddenServiceEnableIntroDoSDefense")) { - config->has_dos_defense_enabled = - (unsigned int) helper_parse_uint64(line->key, line->value, - HS_CONFIG_V3_DOS_DEFENSE_DEFAULT, - 1, &ok); - if (!ok || dos_enabled) { - if (dos_enabled) { - dup_opt_seen = line->key; - } - goto err; - } - dos_enabled = true; - continue; - } - if (!strcasecmp(line->key, "HiddenServiceEnableIntroDoSRatePerSec")) { - config->intro_dos_rate_per_sec = - (unsigned int) helper_parse_uint64(line->key, line->value, - HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN, - HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MAX, &ok); - if (!ok || dos_rate_per_sec) { - if (dos_rate_per_sec) { - dup_opt_seen = line->key; - } - goto err; - } - dos_rate_per_sec = true; - log_info(LD_REND, "Service INTRO2 DoS defenses rate set to: %" PRIu32, - config->intro_dos_rate_per_sec); - continue; + /* Number of introduction points. */ + if (CHECK_OOB(hs_opts, HiddenServiceNumIntroductionPoints, + NUM_INTRO_POINTS_DEFAULT, + HS_CONFIG_V3_MAX_INTRO_POINTS)) { + goto err; + } + config->num_intro_points = hs_opts->HiddenServiceNumIntroductionPoints; + + /* Circuit ID export setting. */ + if (hs_opts->HiddenServiceExportCircuitID) { + int ok; + config->circuit_id_protocol = + helper_parse_circuit_id_protocol("HiddenServcieExportCircuitID", + hs_opts->HiddenServiceExportCircuitID, + &ok); + if (!ok) { + goto err; } - if (!strcasecmp(line->key, "HiddenServiceEnableIntroDoSBurstPerSec")) { - config->intro_dos_burst_per_sec = - (unsigned int) helper_parse_uint64(line->key, line->value, - HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN, - HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MAX, &ok); - if (!ok || dos_burst_per_sec) { - if (dos_burst_per_sec) { - dup_opt_seen = line->key; - } - goto err; - } - dos_burst_per_sec = true; - log_info(LD_REND, "Service INTRO2 DoS defenses burst set to: %" PRIu32, - config->intro_dos_burst_per_sec); - continue; + } + + /* Is the DoS defense enabled? */ + config->has_dos_defense_enabled = + hs_opts->HiddenServiceEnableIntroDoSDefense; + + /* Rate for DoS defense */ + if (CHECK_OOB(hs_opts, HiddenServiceEnableIntroDoSRatePerSec, + HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN, + HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MAX)) { + goto err; + } + config->intro_dos_rate_per_sec = + hs_opts->HiddenServiceEnableIntroDoSRatePerSec; + log_info(LD_REND, "Service INTRO2 DoS defenses rate set to: %" PRIu32, + config->intro_dos_rate_per_sec); + + if (CHECK_OOB(hs_opts, HiddenServiceEnableIntroDoSBurstPerSec, + HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN, + HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MAX)) { + goto err; + } + config->intro_dos_burst_per_sec = + hs_opts->HiddenServiceEnableIntroDoSBurstPerSec; + log_info(LD_REND, "Service INTRO2 DoS defenses burst set to: %" PRIu32, + config->intro_dos_burst_per_sec); + + /* Is this an onionbalance instance? */ + if (hs_opts->HiddenServiceOnionBalanceInstance) { + /* Option is enabled, parse config file. */ + if (! hs_ob_parse_config_file(config)) { + goto err; } } @@ -415,13 +438,10 @@ config_service_v3(const config_line_t *line_, return 0; err: - if (dup_opt_seen) { - log_warn(LD_CONFIG, "Duplicate directive %s.", dup_opt_seen); - } return -1; } -/** Configure a service using the given options in line_ and options. This is +/** Configure a service using the given options in hs_opts and options. This is * called for any service regardless of its version which means that all * directives in this function are generic to any service version. This * function will also check the validity of the service directory path. @@ -433,133 +453,73 @@ config_service_v3(const config_line_t *line_, * * Return 0 on success else -1. */ static int -config_generic_service(const config_line_t *line_, +config_generic_service(const hs_opts_t *hs_opts, const or_options_t *options, hs_service_t *service) { - int dir_seen = 0; - const config_line_t *line; hs_service_config_t *config; - /* If this is set, we've seen a duplicate of this option. Keep the string - * so we can log the directive. */ - const char *dup_opt_seen = NULL; - /* These variables will tell us if we ever have duplicate. */ - int have_version = 0, have_allow_unknown_ports = 0; - int have_dir_group_read = 0, have_max_streams = 0; - int have_max_streams_close = 0; - - tor_assert(line_); + + tor_assert(hs_opts); tor_assert(options); tor_assert(service); /* Makes thing easier. */ config = &service->config; - /* The first line starts with HiddenServiceDir so we consider what's next is - * the configuration of the service. */ - for (line = line_; line ; line = line->next) { - int ok = 0; - - /* This indicate that we have a new service to configure. */ - if (!strcasecmp(line->key, "HiddenServiceDir")) { - /* This function only configures one service at a time so if we've - * already seen one, stop right now. */ - if (dir_seen) { - break; + /* Directory where the service's keys are stored. */ + tor_assert(hs_opts->HiddenServiceDir); + config->directory_path = tor_strdup(hs_opts->HiddenServiceDir); + log_info(LD_CONFIG, "%s=%s. Configuring...", + SECTION_HEADER, escaped(config->directory_path)); + + /* Protocol version for the service. */ + if (hs_opts->HiddenServiceVersion == -1) { + /* No value was set; stay with the default. */ + } else if (CHECK_OOB(hs_opts, HiddenServiceVersion, + HS_VERSION_MIN, HS_VERSION_MAX)) { + goto err; + } else { + config->hs_version_explicitly_set = 1; + config->version = hs_opts->HiddenServiceVersion; + } + + /* Virtual port. */ + for (const config_line_t *portline = hs_opts->HiddenServicePort; + portline; portline = portline->next) { + char *err_msg = NULL; + /* XXX: Can we rename this? */ + rend_service_port_config_t *portcfg = + rend_service_parse_port_config(portline->value, " ", &err_msg); + if (!portcfg) { + if (err_msg) { + log_warn(LD_CONFIG, "%s", err_msg); } - /* Ok, we've seen one and we are about to configure it. */ - dir_seen = 1; - config->directory_path = tor_strdup(line->value); - log_info(LD_CONFIG, "HiddenServiceDir=%s. Configuring...", - escaped(config->directory_path)); - continue; - } - if (BUG(!dir_seen)) { + tor_free(err_msg); goto err; } - /* Version of the service. */ - if (!strcasecmp(line->key, "HiddenServiceVersion")) { - service->config.version = - (uint32_t) helper_parse_uint64(line->key, line->value, HS_VERSION_MIN, - HS_VERSION_MAX, &ok); - if (!ok || have_version) { - if (have_version) - dup_opt_seen = line->key; - goto err; - } - have_version = service->config.hs_version_explicitly_set = 1; - continue; - } - /* Virtual port. */ - if (!strcasecmp(line->key, "HiddenServicePort")) { - char *err_msg = NULL; - /* XXX: Can we rename this? */ - rend_service_port_config_t *portcfg = - rend_service_parse_port_config(line->value, " ", &err_msg); - if (!portcfg) { - if (err_msg) { - log_warn(LD_CONFIG, "%s", err_msg); - } - tor_free(err_msg); - goto err; - } - tor_assert(!err_msg); - smartlist_add(config->ports, portcfg); - log_info(LD_CONFIG, "HiddenServicePort=%s for %s", - line->value, escaped(config->directory_path)); - continue; - } - /* Do we allow unknown ports. */ - if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) { - config->allow_unknown_ports = - (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok); - if (!ok || have_allow_unknown_ports) { - if (have_allow_unknown_ports) - dup_opt_seen = line->key; - goto err; - } - have_allow_unknown_ports = 1; - continue; - } - /* Directory group readable. */ - if (!strcasecmp(line->key, "HiddenServiceDirGroupReadable")) { - config->dir_group_readable = - (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok); - if (!ok || have_dir_group_read) { - if (have_dir_group_read) - dup_opt_seen = line->key; - goto err; - } - have_dir_group_read = 1; - continue; - } - /* Maximum streams per circuit. */ - if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) { - config->max_streams_per_rdv_circuit = - helper_parse_uint64(line->key, line->value, 0, - HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT, &ok); - if (!ok || have_max_streams) { - if (have_max_streams) - dup_opt_seen = line->key; - goto err; - } - have_max_streams = 1; - continue; - } - /* Maximum amount of streams before we close the circuit. */ - if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) { - config->max_streams_close_circuit = - (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok); - if (!ok || have_max_streams_close) { - if (have_max_streams_close) - dup_opt_seen = line->key; - goto err; - } - have_max_streams_close = 1; - continue; - } + tor_assert(!err_msg); + smartlist_add(config->ports, portcfg); + log_info(LD_CONFIG, "HiddenServicePort=%s for %s", + portline->value, escaped(config->directory_path)); } + /* Do we allow unknown ports? */ + config->allow_unknown_ports = hs_opts->HiddenServiceAllowUnknownPorts; + + /* Directory group readable. */ + config->dir_group_readable = hs_opts->HiddenServiceDirGroupReadable; + + /* Maximum streams per circuit. */ + if (CHECK_OOB(hs_opts, HiddenServiceMaxStreams, + 0, HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT)) { + goto err; + } + config->max_streams_per_rdv_circuit = hs_opts->HiddenServiceMaxStreams; + + /* Maximum amount of streams before we close the circuit. */ + config->max_streams_close_circuit = + hs_opts->HiddenServiceMaxStreamsCloseCircuit; + /* Check if we are configured in non anonymous mode meaning every service * becomes a single onion service. */ if (rend_service_non_anonymous_mode_enabled(options)) { @@ -569,9 +529,6 @@ config_generic_service(const config_line_t *line_, /* Success */ return 0; err: - if (dup_opt_seen) { - log_warn(LD_CONFIG, "Duplicate directive %s.", dup_opt_seen); - } return -1; } @@ -581,11 +538,13 @@ config_generic_service(const config_line_t *line_, * the service to the given list and return 0. On error, nothing is added to * the list and a negative value is returned. */ static int -config_service(const config_line_t *line, const or_options_t *options, +config_service(config_line_t *line, const or_options_t *options, smartlist_t *service_list) { int ret; hs_service_t *service = NULL; + hs_opts_t *hs_opts = NULL; + char *msg = NULL; tor_assert(line); tor_assert(options); @@ -594,9 +553,25 @@ config_service(const config_line_t *line, const or_options_t *options, /* We have a new hidden service. */ service = hs_service_new(options); + /* Try to validate and parse the configuration lines into 'hs_opts' */ + hs_opts = hs_opts_new(); + ret = config_assign(get_hs_opts_mgr(), hs_opts, line, 0, &msg); + if (ret < 0) { + log_warn(LD_REND, "Can't parse configuration for onion service: %s", msg); + goto err; + } + tor_assert_nonfatal(msg == NULL); + validation_status_t vs = config_validate(get_hs_opts_mgr(), NULL, + hs_opts, &msg); + if (vs < 0) { + log_warn(LD_REND, "Bad configuration for onion service: %s", msg); + goto err; + } + tor_assert_nonfatal(msg == NULL); + /* We'll configure that service as a generic one and then pass it to a * specific function according to the configured version number. */ - if (config_generic_service(line, options, service) < 0) { + if (config_generic_service(hs_opts, options, service) < 0) { goto err; } @@ -631,10 +606,10 @@ config_service(const config_line_t *line, const or_options_t *options, * directory line, the function knows that it has to stop parsing. */ switch (service->config.version) { case HS_VERSION_TWO: - ret = rend_config_service(line->next, options, &service->config); + ret = rend_config_service(hs_opts, options, &service->config); break; case HS_VERSION_THREE: - ret = config_service_v3(line->next, &service->config); + ret = config_service_v3(hs_opts, &service->config); break; default: /* We do validate before if we support the parsed version. */ @@ -653,11 +628,14 @@ config_service(const config_line_t *line, const or_options_t *options, /* Passes, add it to the given list. */ smartlist_add(service_list, service); + hs_opts_free(hs_opts); return 0; err: hs_service_free(service); + hs_opts_free(hs_opts); + tor_free(msg); return -1; } @@ -667,8 +645,8 @@ config_service(const config_line_t *line, const or_options_t *options, int hs_config_service_all(const or_options_t *options, int validate_only) { - int dir_option_seen = 0, ret = -1; - const config_line_t *line; + int ret = -1; + config_line_t *remaining = NULL; smartlist_t *new_service_list = NULL; tor_assert(options); @@ -677,23 +655,24 @@ hs_config_service_all(const or_options_t *options, int validate_only) * validation and staging for >= v3. */ new_service_list = smartlist_new(); - for (line = options->RendConfigLines; line; line = line->next) { - /* Ignore all directives that aren't the start of a service. */ - if (strcasecmp(line->key, "HiddenServiceDir")) { - if (!dir_option_seen) { - log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive", - line->key); - goto err; - } - continue; - } - /* Flag that we've seen a directory directive and we'll use it to make - * sure that the torrc options ordering is actually valid. */ - dir_option_seen = 1; + /* We need to start with a HiddenServiceDir line */ + if (options->RendConfigLines && + strcasecmp(options->RendConfigLines->key, SECTION_HEADER)) { + log_warn(LD_CONFIG, "%s with no preceding %s directive", + options->RendConfigLines->key, SECTION_HEADER); + goto err; + } + + remaining = config_lines_dup(options->RendConfigLines); + while (remaining) { + config_line_t *section = remaining; + remaining = config_lines_partition(section, SECTION_HEADER); /* Try to configure this service now. On success, it will be added to the * list and validated against the service in that same list. */ - if (config_service(line, options, new_service_list) < 0) { + int rv = config_service(section, options, new_service_list); + config_free_lines(section); + if (rv < 0) { goto err; } } @@ -753,3 +732,12 @@ hs_config_client_auth_all(const or_options_t *options, int validate_only) done: return ret; } + +/** + * Free all resources held by the hs_config.c module. + **/ +void +hs_config_free_all(void) +{ + config_mgr_free(hs_opts_mgr); +} diff --git a/src/feature/hs/hs_config.h b/src/feature/hs/hs_config.h index 5694cf1e9b..c60b4fbb5d 100644 --- a/src/feature/hs/hs_config.h +++ b/src/feature/hs/hs_config.h @@ -30,5 +30,6 @@ int hs_config_service_all(const or_options_t *options, int validate_only); int hs_config_client_auth_all(const or_options_t *options, int validate_only); -#endif /* !defined(TOR_HS_CONFIG_H) */ +void hs_config_free_all(void); +#endif /* !defined(TOR_HS_CONFIG_H) */ diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c index 65d6c7a581..a39a4a09c9 100644 --- a/src/feature/hs/hs_descriptor.c +++ b/src/feature/hs/hs_descriptor.c @@ -56,6 +56,7 @@ #define HS_DESCRIPTOR_PRIVATE #include "core/or/or.h" +#include "app/config/config.h" #include "trunnel/ed25519_cert.h" /* Trunnel interface. */ #include "feature/hs/hs_descriptor.h" #include "core/or/circuitbuild.h" @@ -211,7 +212,7 @@ build_secret_input(const hs_descriptor_t *desc, memcpy(secret_input, secret_data, secret_data_len); offset += secret_data_len; /* Copy subcredential. */ - memcpy(secret_input + offset, desc->subcredential, DIGEST256_LEN); + memcpy(secret_input + offset, desc->subcredential.subcred, DIGEST256_LEN); offset += DIGEST256_LEN; /* Copy revision counter value. */ set_uint64(secret_input + offset, @@ -1018,10 +1019,6 @@ desc_encode_v3(const hs_descriptor_t *desc, tor_assert(encoded_out); tor_assert(desc->plaintext_data.version == 3); - if (BUG(desc->subcredential == NULL)) { - goto err; - } - /* Build the non-encrypted values. */ { char *encoded_cert; @@ -1287,11 +1284,20 @@ cert_is_valid(tor_cert_t *cert, uint8_t type, const char *log_obj_type) log_warn(LD_REND, "Signing key is NOT included for %s.", log_obj_type); goto err; } + /* The following will not only check if the signature matches but also the * expiration date and overall validity. */ if (tor_cert_checksig(cert, &cert->signing_key, approx_time()) < 0) { - log_warn(LD_REND, "Invalid signature for %s: %s", log_obj_type, - tor_cert_describe_signature_status(cert)); + if (cert->cert_expired) { + char expiration_str[ISO_TIME_LEN+1]; + format_iso_time(expiration_str, cert->valid_until); + log_fn(LOG_PROTOCOL_WARN, LD_REND, "Invalid signature for %s: %s (%s)", + log_obj_type, tor_cert_describe_signature_status(cert), + expiration_str); + } else { + log_warn(LD_REND, "Invalid signature for %s: %s", + log_obj_type, tor_cert_describe_signature_status(cert)); + } goto err; } @@ -1366,8 +1372,7 @@ encrypted_data_length_is_valid(size_t len) * and return the buffer's length. The caller should wipe and free its content * once done with it. This function can't fail. */ static size_t -build_descriptor_cookie_keys(const uint8_t *subcredential, - size_t subcredential_len, +build_descriptor_cookie_keys(const hs_subcredential_t *subcredential, const curve25519_secret_key_t *sk, const curve25519_public_key_t *pk, uint8_t **keys_out) @@ -1389,7 +1394,7 @@ build_descriptor_cookie_keys(const uint8_t *subcredential, /* Calculate KEYS = KDF(subcredential | SECRET_SEED, 40) */ xof = crypto_xof_new(); - crypto_xof_add_bytes(xof, subcredential, subcredential_len); + crypto_xof_add_bytes(xof, subcredential->subcred, SUBCRED_LEN); crypto_xof_add_bytes(xof, secret_seed, sizeof(secret_seed)); crypto_xof_squeeze_bytes(xof, keystream, keystream_len); crypto_xof_free(xof); @@ -1426,11 +1431,12 @@ decrypt_descriptor_cookie(const hs_descriptor_t *desc, sizeof(desc->superencrypted_data.auth_ephemeral_pubkey))); tor_assert(!fast_mem_is_zero((char *) client_auth_sk, sizeof(*client_auth_sk))); - tor_assert(!fast_mem_is_zero((char *) desc->subcredential, DIGEST256_LEN)); + tor_assert(!fast_mem_is_zero((char *) desc->subcredential.subcred, + DIGEST256_LEN)); /* Get the KEYS component to derive the CLIENT-ID and COOKIE-KEY. */ keystream_length = - build_descriptor_cookie_keys(desc->subcredential, DIGEST256_LEN, + build_descriptor_cookie_keys(&desc->subcredential, client_auth_sk, &desc->superencrypted_data.auth_ephemeral_pubkey, &keystream); @@ -2558,7 +2564,7 @@ hs_desc_decode_plaintext(const char *encoded, * set to NULL. */ hs_desc_decode_status_t hs_desc_decode_descriptor(const char *encoded, - const uint8_t *subcredential, + const hs_subcredential_t *subcredential, const curve25519_secret_key_t *client_auth_sk, hs_descriptor_t **desc_out) { @@ -2576,7 +2582,7 @@ hs_desc_decode_descriptor(const char *encoded, goto err; } - memcpy(desc->subcredential, subcredential, sizeof(desc->subcredential)); + memcpy(&desc->subcredential, subcredential, sizeof(desc->subcredential)); ret = hs_desc_decode_plaintext(encoded, &desc->plaintext_data); if (ret != HS_DESC_DECODE_OK) { @@ -2666,7 +2672,7 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc, * symmetric only if the client auth is disabled. That is, the descriptor * cookie will be NULL. */ if (!descriptor_cookie) { - ret = hs_desc_decode_descriptor(*encoded_out, desc->subcredential, + ret = hs_desc_decode_descriptor(*encoded_out, &desc->subcredential, NULL, NULL); if (BUG(ret != HS_DESC_DECODE_OK)) { ret = -1; @@ -2870,7 +2876,7 @@ hs_desc_build_fake_authorized_client(void) * key, and descriptor cookie, build the auth client so we can then encode the * descriptor for publication. client_out must be already allocated. */ void -hs_desc_build_authorized_client(const uint8_t *subcredential, +hs_desc_build_authorized_client(const hs_subcredential_t *subcredential, const curve25519_public_key_t *client_auth_pk, const curve25519_secret_key_t * auth_ephemeral_sk, @@ -2898,7 +2904,7 @@ hs_desc_build_authorized_client(const uint8_t *subcredential, /* Get the KEYS part so we can derive the CLIENT-ID and COOKIE-KEY. */ keystream_length = - build_descriptor_cookie_keys(subcredential, DIGEST256_LEN, + build_descriptor_cookie_keys(subcredential, auth_ephemeral_sk, client_auth_pk, &keystream); tor_assert(keystream_length > 0); diff --git a/src/feature/hs/hs_descriptor.h b/src/feature/hs/hs_descriptor.h index 639dd31c8f..08daa904b6 100644 --- a/src/feature/hs/hs_descriptor.h +++ b/src/feature/hs/hs_descriptor.h @@ -14,6 +14,7 @@ #include "core/or/or.h" #include "trunnel/ed25519_cert.h" /* needed for trunnel */ #include "feature/nodelist/torcert.h" +#include "core/crypto/hs_ntor.h" /* for hs_subcredential_t */ /* Trunnel */ struct link_specifier_t; @@ -238,7 +239,7 @@ typedef struct hs_descriptor_t { /** Subcredentials of a service, used by the client and service to decrypt * the encrypted data. */ - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; } hs_descriptor_t; /** Return true iff the given descriptor format version is supported. */ @@ -277,7 +278,7 @@ MOCK_DECL(int, char **encoded_out)); int hs_desc_decode_descriptor(const char *encoded, - const uint8_t *subcredential, + const hs_subcredential_t *subcredential, const curve25519_secret_key_t *client_auth_sk, hs_descriptor_t **desc_out); int hs_desc_decode_plaintext(const char *encoded, @@ -302,7 +303,7 @@ void hs_desc_authorized_client_free_(hs_desc_authorized_client_t *client); hs_desc_authorized_client_t *hs_desc_build_fake_authorized_client(void); -void hs_desc_build_authorized_client(const uint8_t *subcredential, +void hs_desc_build_authorized_client(const hs_subcredential_t *subcredential, const curve25519_public_key_t * client_auth_pk, const curve25519_secret_key_t * diff --git a/src/feature/hs/hs_ob.c b/src/feature/hs/hs_ob.c new file mode 100644 index 0000000000..9499c28d20 --- /dev/null +++ b/src/feature/hs/hs_ob.c @@ -0,0 +1,408 @@ +/* Copyright (c) 2017-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_ob.c + * \brief Implement Onion Balance specific code. + **/ + +#define HS_OB_PRIVATE + +#include "feature/hs/hs_service.h" + +#include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/networkstatus_st.h" + +#include "lib/confmgt/confmgt.h" +#include "lib/encoding/confline.h" + +#include "feature/hs/hs_ob.h" + +/* Options config magic number. */ +#define OB_OPTIONS_MAGIC 0x631DE7EA + +/* Helper macros. */ +#define VAR(varname, conftype, member, initvalue) \ + CONFIG_VAR_ETYPE(ob_options_t, varname, conftype, member, 0, initvalue) +#define V(member,conftype,initvalue) \ + VAR(#member, conftype, member, initvalue) + +/* Dummy instance of ob_options_t, used for type-checking its members with + * CONF_CHECK_VAR_TYPE. */ +DUMMY_TYPECHECK_INSTANCE(ob_options_t); + +/* Array of variables for the config file options. */ +static const config_var_t config_vars[] = { + V(MasterOnionAddress, LINELIST, NULL), + + END_OF_CONFIG_VARS +}; + +/* "Extra" variable in the state that receives lines we can't parse. This + * lets us preserve options from versions of Tor newer than us. */ +static const struct_member_t config_extra_vars = { + .name = "__extra", + .type = CONFIG_TYPE_LINELIST, + .offset = offsetof(ob_options_t, ExtraLines), +}; + +/* Configuration format of ob_options_t. */ +static const config_format_t config_format = { + .size = sizeof(ob_options_t), + .magic = { + "ob_options_t", + OB_OPTIONS_MAGIC, + offsetof(ob_options_t, magic_), + }, + .vars = config_vars, + .extra = &config_extra_vars, +}; + +/* Global configuration manager for the config file. */ +static config_mgr_t *config_options_mgr = NULL; + +/* Return the configuration manager for the config file. */ +static const config_mgr_t * +get_config_options_mgr(void) +{ + if (PREDICT_UNLIKELY(config_options_mgr == NULL)) { + config_options_mgr = config_mgr_new(&config_format); + config_mgr_freeze(config_options_mgr); + } + return config_options_mgr; +} + +#define ob_option_free(val) \ + FREE_AND_NULL(ob_options_t, ob_option_free_, (val)) + +/** Helper: Free a config options object. */ +static void +ob_option_free_(ob_options_t *opts) +{ + if (opts == NULL) { + return; + } + config_free(get_config_options_mgr(), opts); +} + +/** Return an allocated config options object. */ +static ob_options_t * +ob_option_new(void) +{ + ob_options_t *opts = config_new(get_config_options_mgr()); + config_init(get_config_options_mgr(), opts); + return opts; +} + +/** Helper function: From the configuration line value which is an onion + * address with the ".onion" extension, find the public key and put it in + * pkey_out. + * + * On success, true is returned. Else, false and pkey is untouched. */ +static bool +get_onion_public_key(const char *value, ed25519_public_key_t *pkey_out) +{ + char address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + + tor_assert(value); + tor_assert(pkey_out); + + if (strcmpend(value, ".onion")) { + /* Not a .onion extension, bad format. */ + return false; + } + + /* Length validation. The -1 is because sizeof() counts the NUL byte. */ + if (strlen(value) > + (HS_SERVICE_ADDR_LEN_BASE32 + sizeof(".onion") - 1)) { + /* Too long, bad format. */ + return false; + } + + /* We don't want the .onion so we add 2 because size - 1 is copied with + * strlcpy() in order to accomodate the NUL byte and sizeof() counts the NUL + * byte so we need to remove them from the equation. */ + strlcpy(address, value, strlen(value) - sizeof(".onion") + 2); + + if (hs_parse_address_no_log(address, pkey_out, NULL, NULL, NULL) < 0) { + return false; + } + + /* Success. */ + return true; +} + +/** Parse the given ob options in opts and set the service config object + * accordingly. + * + * Return 1 on success else 0. */ +static int +ob_option_parse(hs_service_config_t *config, const ob_options_t *opts) +{ + int ret = 0; + config_line_t *line; + + tor_assert(config); + tor_assert(opts); + + for (line = opts->MasterOnionAddress; line; line = line->next) { + /* Allocate config list if need be. */ + if (!config->ob_master_pubkeys) { + config->ob_master_pubkeys = smartlist_new(); + } + ed25519_public_key_t *pubkey = tor_malloc_zero(sizeof(*pubkey)); + + if (!get_onion_public_key(line->value, pubkey)) { + log_warn(LD_REND, "OnionBalance: MasterOnionAddress %s is invalid", + line->value); + tor_free(pubkey); + goto end; + } + smartlist_add(config->ob_master_pubkeys, pubkey); + log_notice(LD_REND, "OnionBalance: MasterOnionAddress %s registered", + line->value); + } + /* Success. */ + ret = 1; + + end: + /* No keys added, we free the list since no list means no onion balance + * support for this tor instance. */ + if (smartlist_len(config->ob_master_pubkeys) == 0) { + smartlist_free(config->ob_master_pubkeys); + } + return ret; +} + +/** For the given master public key and time period, compute the subcredential + * and put them into subcredential. The subcredential parameter needs to be at + * least DIGEST256_LEN in size. */ +static void +build_subcredential(const ed25519_public_key_t *pkey, uint64_t tp, + hs_subcredential_t *subcredential) +{ + ed25519_public_key_t blinded_pubkey; + + tor_assert(pkey); + tor_assert(subcredential); + + hs_build_blinded_pubkey(pkey, NULL, 0, tp, &blinded_pubkey); + hs_get_subcredential(pkey, &blinded_pubkey, subcredential); +} + +/* + * Public API. + */ + +/** Return true iff the given service is configured as an onion balance + * instance. To satisfy that condition, there must at least be one master + * ed25519 public key configured. */ +bool +hs_ob_service_is_instance(const hs_service_t *service) +{ + if (BUG(service == NULL)) { + return false; + } + + /* No list, we are not an instance. */ + if (!service->config.ob_master_pubkeys) { + return false; + } + + return smartlist_len(service->config.ob_master_pubkeys) > 0; +} + +/** Read and parse the config file at fname on disk. The service config object + * is populated with the options if any. + * + * Return 1 on success else 0. This is to follow the "ok" convention in + * hs_config.c. */ +int +hs_ob_parse_config_file(hs_service_config_t *config) +{ + static const char *fname = "ob_config"; + int ret = 0; + char *content = NULL, *errmsg = NULL, *config_file_path = NULL; + ob_options_t *options = NULL; + config_line_t *lines = NULL; + + tor_assert(config); + + /* Read file from disk. */ + config_file_path = hs_path_from_filename(config->directory_path, fname); + content = read_file_to_str(config_file_path, 0, NULL); + if (!content) { + log_warn(LD_FS, "OnionBalance: Unable to read config file %s", + escaped(config_file_path)); + goto end; + } + + /* Parse lines. */ + if (config_get_lines(content, &lines, 0) < 0) { + goto end; + } + + options = ob_option_new(); + config_assign(get_config_options_mgr(), options, lines, 0, &errmsg); + if (errmsg) { + log_warn(LD_REND, "OnionBalance: Unable to parse config file: %s", + errmsg); + tor_free(errmsg); + goto end; + } + + /* Parse the options and set the service config object with the details. */ + ret = ob_option_parse(config, options); + + end: + config_free_lines(lines); + ob_option_free(options); + tor_free(content); + tor_free(config_file_path); + return ret; +} + +/** Compute all possible subcredentials for every onion master key in the given + * service config object. subcredentials_out is allocated and set as an + * continous array containing all possible values. + * + * On success, return the number of subcredential put in the array which will + * correspond to an arry of size: n * DIGEST256_LEN where DIGEST256_LEN is the + * length of a single subcredential. + * + * If the given configuration object has no OB master keys configured, 0 is + * returned and subcredentials_out is set to NULL. + * + * Otherwise, this can't fail. */ +STATIC size_t +compute_subcredentials(const hs_service_t *service, + hs_subcredential_t **subcredentials_out) +{ + unsigned int num_pkeys, idx = 0; + hs_subcredential_t *subcreds = NULL; + const int steps[3] = {0, -1, 1}; + const unsigned int num_steps = ARRAY_LENGTH(steps); + const uint64_t tp = hs_get_time_period_num(0); + + tor_assert(service); + tor_assert(subcredentials_out); + /* Our caller has checked these too */ + tor_assert(service->desc_current); + tor_assert(service->desc_next); + + /* Make sure we are an OB instance, or bail out. */ + num_pkeys = smartlist_len(service->config.ob_master_pubkeys); + if (!num_pkeys) { + *subcredentials_out = NULL; + return 0; + } + + /* Time to build all the subcredentials for each time period: two for each + * instance descriptor plus three for the onionbalance frontend service: the + * previous one (-1), the current one (0) and the next one (1) for each + * configured key in order to accomodate client and service consensus skew. + * + * If the client consensus after_time is at 23:00 but the service one is at + * 01:00, the client will be using the previous time period where the + * service will think it is the client next time period. Thus why we have + * to try them all. + * + * The normal use case works because the service gets the descriptor object + * that corresponds to the intro point's request, and because each + * descriptor corresponds to a specific subcredential, we get the right + * subcredential out of it, and use that to do the decryption. + * + * As a slight optimization, statistically, the current time period (0) will + * be the one to work first so we'll put them first in the array to maximize + * our chance of success. */ + + /* We use a flat array, not a smartlist_t, in order to minimize memory + * allocation. + * + * Size of array is: length of a single subcredential multiplied by the + * number of time period we need to compute and finally multiplied by the + * total number of keys we are about to process. In other words, for each + * key, we allocate 3 subcredential slots. Then in the end we also add two + * subcredentials for this instance's active descriptors. */ + subcreds = + tor_calloc((num_steps * num_pkeys) + 2, sizeof(hs_subcredential_t)); + + /* For each master pubkey we add 3 subcredentials: */ + for (unsigned int i = 0; i < num_steps; i++) { + SMARTLIST_FOREACH_BEGIN(service->config.ob_master_pubkeys, + const ed25519_public_key_t *, pkey) { + build_subcredential(pkey, tp + steps[i], &subcreds[idx]); + idx++; + } SMARTLIST_FOREACH_END(pkey); + } + + /* And then in the end we add the two subcredentials of the current active + * instance descriptors */ + memcpy(&subcreds[idx++], &service->desc_current->desc->subcredential, + sizeof(hs_subcredential_t)); + memcpy(&subcreds[idx++], &service->desc_next->desc->subcredential, + sizeof(hs_subcredential_t)); + + log_info(LD_REND, "Refreshing %u onionbalance keys (TP #%d).", + idx, (int)tp); + + *subcredentials_out = subcreds; + return idx; +} + +/** + * If we are an Onionbalance instance, refresh our keys. + * + * If we are not an Onionbalance instance or we are not ready to do so, this + * is a NOP. + * + * This function is called everytime we build a new descriptor. That's because + * we want our Onionbalance keys to always use up-to-date subcredentials both + * for the instance (ourselves) and for the onionbalance frontend. + */ +void +hs_ob_refresh_keys(hs_service_t *service) +{ + hs_subcredential_t *ob_subcreds = NULL; + size_t num_subcreds; + + tor_assert(service); + + /* Don't do any of this if we are not configured as an OB instance */ + if (!hs_ob_service_is_instance(service)) { + return; + } + + /* We need both service descriptors created to make onionbalance keys. + * + * That's because we fetch our own (the instance's) subcredentials from our + * own descriptors which should always include the latest subcredentials that + * clients would use. + * + * This function is called with each descriptor build, so we will be + * eventually be called when both descriptors are created. */ + if (!service->desc_current || !service->desc_next) { + return; + } + + /* Get a new set of subcreds */ + num_subcreds = compute_subcredentials(service, &ob_subcreds); + if (BUG(!num_subcreds)) { + return; + } + + /* Delete old subcredentials if any */ + if (service->state.ob_subcreds) { + tor_free(service->state.ob_subcreds); + } + + service->state.ob_subcreds = ob_subcreds; + service->state.n_ob_subcreds = num_subcreds; +} + +/** Free any memory allocated by the onionblance subsystem. */ +void +hs_ob_free_all(void) +{ + config_mgr_free(config_options_mgr); +} diff --git a/src/feature/hs/hs_ob.h b/src/feature/hs/hs_ob.h new file mode 100644 index 0000000000..d6e6e73a84 --- /dev/null +++ b/src/feature/hs/hs_ob.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_ob.h + * \brief Header file for the specific code for onion balance. + **/ + +#ifndef TOR_HS_OB_H +#define TOR_HS_OB_H + +#include "feature/hs/hs_service.h" + +bool hs_ob_service_is_instance(const hs_service_t *service); + +int hs_ob_parse_config_file(hs_service_config_t *config); + +struct hs_subcredential_t; + +void hs_ob_free_all(void); + +void hs_ob_refresh_keys(hs_service_t *service); + +#ifdef HS_OB_PRIVATE + +STATIC size_t compute_subcredentials(const hs_service_t *service, + struct hs_subcredential_t **subcredentials); + +typedef struct ob_options_t { + /** Magic number to identify the structure in memory. */ + uint32_t magic_; + /** Master Onion Address(es). */ + struct config_line_t *MasterOnionAddress; + /** Extra Lines for configuration we might not know. */ + struct config_line_t *ExtraLines; +} ob_options_t; + +#endif /* defined(HS_OB_PRIVATE) */ + +#endif /* !defined(TOR_HS_OB_H) */ diff --git a/src/feature/hs/hs_options.inc b/src/feature/hs/hs_options.inc new file mode 100644 index 0000000000..1a1444fd05 --- /dev/null +++ b/src/feature/hs/hs_options.inc @@ -0,0 +1,36 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_options.inc + * @brief Declare configuration options for a single hidden service. + * + * Note that this options file behaves differently from most, since it + * is not used directly by the options manager. Instead, it is applied to + * a group of hidden service options starting with a HiddenServiceDir and + * extending up to the next HiddenServiceDir. + **/ + +/** Holds configuration for a single hidden service. */ +BEGIN_CONF_STRUCT(hs_opts_t) + +CONF_VAR(HiddenServiceDir, FILENAME, 0, NULL) +CONF_VAR(HiddenServiceDirGroupReadable, BOOL, 0, "0") +CONF_VAR(HiddenServicePort, LINELIST, 0, NULL) +// "-1" means "auto" here. +CONF_VAR(HiddenServiceVersion, INT, 0, "-1") +CONF_VAR(HiddenServiceAuthorizeClient, STRING, 0, NULL) +CONF_VAR(HiddenServiceAllowUnknownPorts, BOOL, 0, "0") +CONF_VAR(HiddenServiceMaxStreams, POSINT, 0, "0") +CONF_VAR(HiddenServiceMaxStreamsCloseCircuit, BOOL, 0, "0") +CONF_VAR(HiddenServiceNumIntroductionPoints, POSINT, 0, "3") +CONF_VAR(HiddenServiceExportCircuitID, STRING, 0, NULL) +CONF_VAR(HiddenServiceEnableIntroDoSDefense, BOOL, 0, "0") +CONF_VAR(HiddenServiceEnableIntroDoSRatePerSec, POSINT, 0, "25") +CONF_VAR(HiddenServiceEnableIntroDoSBurstPerSec, POSINT, 0, "200") +CONF_VAR(HiddenServiceOnionBalanceInstance, BOOL, 0, "0") + +END_CONF_STRUCT(hs_opts_t) diff --git a/src/feature/hs/hs_opts_st.h b/src/feature/hs/hs_opts_st.h new file mode 100644 index 0000000000..f6ee0f3cfa --- /dev/null +++ b/src/feature/hs/hs_opts_st.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_options_st.h + * @brief Structure hs_opts_t to hold options for a single hidden service. + **/ + +#ifndef TOR_FEATURE_HS_HS_OPTS_ST_H +#define TOR_FEATURE_HS_HS_OPTS_ST_H + +#include "lib/conf/confdecl.h" +#define CONF_CONTEXT STRUCT +#include "feature/hs/hs_options.inc" +#undef CONF_CONTEXT + +/** + * An hs_opts_t holds the parsed options for a single HS configuration + * section. + * + * This name ends with 'opts' instead of 'options' to signal that it is not + * handled directly by the or_options_t configuration manager, but that + * first we partition the "HiddenService*" options by section. + **/ +typedef struct hs_opts_t hs_opts_t; + +#endif diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c index 81b37eab40..53e33f80ea 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -41,6 +41,7 @@ #include "feature/hs/hs_intropoint.h" #include "feature/hs/hs_service.h" #include "feature/hs/hs_stats.h" +#include "feature/hs/hs_ob.h" #include "feature/dircommon/dir_connection_st.h" #include "core/or/edge_connection_st.h" @@ -151,11 +152,11 @@ HT_PROTOTYPE(hs_service_ht, /* Name of hashtable. */ hs_service_t, /* Object contained in the map. */ hs_service_node, /* The name of the HT_ENTRY member. */ hs_service_ht_hash, /* Hashing function. */ - hs_service_ht_eq) /* Compare function for objects. */ + hs_service_ht_eq); /* Compare function for objects. */ HT_GENERATE2(hs_service_ht, hs_service_t, hs_service_node, hs_service_ht_hash, hs_service_ht_eq, - 0.6, tor_reallocarray, tor_free_) + 0.6, tor_reallocarray, tor_free_); /** Query the given service map with a public key and return a service object * if found else NULL. It is also possible to set a directory path in the @@ -267,6 +268,11 @@ service_clear_config(hs_service_config_t *config) service_authorized_client_free(p)); smartlist_free(config->clients); } + if (config->ob_master_pubkeys) { + SMARTLIST_FOREACH(config->ob_master_pubkeys, ed25519_public_key_t *, k, + tor_free(k)); + smartlist_free(config->ob_master_pubkeys); + } memset(config, 0, sizeof(*config)); } @@ -701,8 +707,8 @@ get_extend_info_from_intro_point(const hs_service_intro_point_t *ip, /** Return the number of introduction points that are established for the * given descriptor. */ -static unsigned int -count_desc_circuit_established(const hs_service_descriptor_t *desc) +MOCK_IMPL(STATIC unsigned int, +count_desc_circuit_established, (const hs_service_descriptor_t *desc)) { unsigned int count = 0; @@ -884,10 +890,18 @@ move_hs_state(hs_service_t *src_service, hs_service_t *dst_service) if (dst->replay_cache_rend_cookie != NULL) { replaycache_free(dst->replay_cache_rend_cookie); } + dst->replay_cache_rend_cookie = src->replay_cache_rend_cookie; + src->replay_cache_rend_cookie = NULL; /* steal pointer reference */ + dst->next_rotation_time = src->next_rotation_time; - src->replay_cache_rend_cookie = NULL; /* steal pointer reference */ + if (src->ob_subcreds) { + dst->ob_subcreds = src->ob_subcreds; + dst->n_ob_subcreds = src->n_ob_subcreds; + + src->ob_subcreds = NULL; /* steal pointer reference */ + } } /** Register services that are in the staging list. Once this function returns, @@ -1764,7 +1778,8 @@ build_service_desc_superencrypted(const hs_service_t *service, sizeof(curve25519_public_key_t)); /* Test that subcred is not zero because we might use it below */ - if (BUG(fast_mem_is_zero((char*)desc->desc->subcredential, DIGEST256_LEN))) { + if (BUG(fast_mem_is_zero((char*)desc->desc->subcredential.subcred, + DIGEST256_LEN))) { return -1; } @@ -1781,7 +1796,7 @@ build_service_desc_superencrypted(const hs_service_t *service, /* Prepare the client for descriptor and then add to the list in the * superencrypted part of the descriptor */ - hs_desc_build_authorized_client(desc->desc->subcredential, + hs_desc_build_authorized_client(&desc->desc->subcredential, &client->client_pk, &desc->auth_ephemeral_kp.seckey, desc->descriptor_cookie, desc_client); @@ -1837,7 +1852,7 @@ build_service_desc_plaintext(const hs_service_t *service, /* Set the subcredential. */ hs_get_subcredential(&service->keys.identity_pk, &desc->blinded_kp.pubkey, - desc->desc->subcredential); + &desc->desc->subcredential); plaintext = &desc->desc->plaintext_data; @@ -1980,9 +1995,15 @@ build_service_descriptor(hs_service_t *service, uint64_t time_period_num, /* Assign newly built descriptor to the next slot. */ *desc_out = desc; + /* Fire a CREATED control port event. */ hs_control_desc_event_created(service->onion_address, &desc->blinded_kp.pubkey); + + /* If we are an onionbalance instance, we refresh our keys when we rotate + * descriptors. */ + hs_ob_refresh_keys(service); + return; err: @@ -3042,13 +3063,85 @@ service_desc_hsdirs_changed(const hs_service_t *service, return should_reupload; } +/** These are all the reasons why a descriptor upload can't occur. We use + * those to log the reason properly with the right rate limiting and for the + * right descriptor. */ +typedef enum { + LOG_DESC_UPLOAD_REASON_MISSING_IPS = 0, + LOG_DESC_UPLOAD_REASON_IP_NOT_ESTABLISHED = 1, + LOG_DESC_UPLOAD_REASON_NOT_TIME = 2, + LOG_DESC_UPLOAD_REASON_NO_LIVE_CONSENSUS = 3, + LOG_DESC_UPLOAD_REASON_NO_DIRINFO = 4, +} log_desc_upload_reason_t; + +/** Maximum number of reasons. This is used to allocate the static array of + * all rate limiting objects. */ +#define LOG_DESC_UPLOAD_REASON_MAX LOG_DESC_UPLOAD_REASON_NO_DIRINFO + +/** Log the reason why we can't upload the given descriptor for the given + * service. This takes a message string (allocated by the caller) and a + * reason. + * + * Depending on the reason and descriptor, different rate limit applies. This + * is done because this function will basically be called every second. Each + * descriptor for each reason uses its own log rate limit object in order to + * avoid message suppression for different reasons and descriptors. */ +static void +log_cant_upload_desc(const hs_service_t *service, + const hs_service_descriptor_t *desc, const char *msg, + const log_desc_upload_reason_t reason) +{ + /* Writing the log every minute shouldn't be too annoying for log rate limit + * since this can be emitted every second for each descriptor. + * + * However, for one specific case, we increase it to 10 minutes because it + * is hit constantly, as an expected behavior, which is the reason + * indicating that it is not the time to upload. */ + static ratelim_t limits[2][LOG_DESC_UPLOAD_REASON_MAX + 1] = + { { RATELIM_INIT(60), RATELIM_INIT(60), RATELIM_INIT(60 * 10), + RATELIM_INIT(60), RATELIM_INIT(60) }, + { RATELIM_INIT(60), RATELIM_INIT(60), RATELIM_INIT(60 * 10), + RATELIM_INIT(60), RATELIM_INIT(60) }, + }; + bool is_next_desc = false; + unsigned int rlim_pos = 0; + ratelim_t *rlim = NULL; + + tor_assert(service); + tor_assert(desc); + tor_assert(msg); + + /* Make sure the reason value is valid. It should never happen because we + * control that value in the code flow but will be apparent during + * development if a reason is added but LOG_DESC_UPLOAD_REASON_NUM_ is not + * updated. */ + if (BUG(reason > LOG_DESC_UPLOAD_REASON_MAX || reason < 0)) { + return; + } + + /* Ease our life. Flag that tells us if the descriptor is the next one. */ + is_next_desc = (service->desc_next == desc); + + /* Current descriptor is the first element in the ratelimit object array. + * The next descriptor is the second element. */ + rlim_pos = (is_next_desc ? 1 : 0); + /* Get the ratelimit object for the reason _and_ right descriptor. */ + rlim = &limits[rlim_pos][reason]; + + log_fn_ratelim(rlim, LOG_INFO, LD_REND, + "Service %s can't upload its %s descriptor: %s", + safe_str_client(service->onion_address), + (is_next_desc) ? "next" : "current", msg); +} + /** Return 1 if the given descriptor from the given service can be uploaded * else return 0 if it can not. */ static int should_service_upload_descriptor(const hs_service_t *service, const hs_service_descriptor_t *desc, time_t now) { - unsigned int num_intro_points; + char *msg = NULL; + unsigned int num_intro_points, count_ip_established; tor_assert(service); tor_assert(desc); @@ -3068,34 +3161,54 @@ should_service_upload_descriptor(const hs_service_t *service, * upload descriptor in this case. We need at least one for the service to * be reachable. */ if (desc->missing_intro_points && num_intro_points == 0) { + msg = tor_strdup("Missing intro points"); + log_cant_upload_desc(service, desc, msg, + LOG_DESC_UPLOAD_REASON_MISSING_IPS); goto cannot; } /* Check if all our introduction circuit have been established for all the * intro points we have selected. */ - if (count_desc_circuit_established(desc) != num_intro_points) { + count_ip_established = count_desc_circuit_established(desc); + if (count_ip_established != num_intro_points) { + tor_asprintf(&msg, "Intro circuits aren't yet all established (%d/%d).", + count_ip_established, num_intro_points); + log_cant_upload_desc(service, desc, msg, + LOG_DESC_UPLOAD_REASON_IP_NOT_ESTABLISHED); goto cannot; } /* Is it the right time to upload? */ if (desc->next_upload_time > now) { + tor_asprintf(&msg, "Next upload time is %ld, it is now %ld.", + (long int) desc->next_upload_time, (long int) now); + log_cant_upload_desc(service, desc, msg, + LOG_DESC_UPLOAD_REASON_NOT_TIME); goto cannot; } /* Don't upload desc if we don't have a live consensus */ if (!networkstatus_get_live_consensus(now)) { + msg = tor_strdup("No live consensus"); + log_cant_upload_desc(service, desc, msg, + LOG_DESC_UPLOAD_REASON_NO_LIVE_CONSENSUS); goto cannot; } /* Do we know enough router descriptors to have adequate vision of the HSDir hash ring? */ if (!router_have_minimum_dir_info()) { + msg = tor_strdup("Not enough directory information"); + log_cant_upload_desc(service, desc, msg, + LOG_DESC_UPLOAD_REASON_NO_DIRINFO); goto cannot; } /* Can upload! */ return 1; + cannot: + tor_free(msg); return 0; } @@ -3369,7 +3482,7 @@ service_handle_introduce2(origin_circuit_t *circ, const uint8_t *payload, /* The following will parse, decode and launch the rendezvous point circuit. * Both current and legacy cells are handled. */ - if (hs_circ_handle_introduce2(service, circ, ip, desc->desc->subcredential, + if (hs_circ_handle_introduce2(service, circ, ip, &desc->desc->subcredential, payload, payload_len) < 0) { goto err; } @@ -3556,6 +3669,12 @@ hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports, goto err; } + if (ed25519_validate_pubkey(&service->keys.identity_pk) < 0) { + log_warn(LD_CONFIG, "Bad ed25519 private key was provided"); + ret = RSAE_BADPRIVKEY; + goto err; + } + /* Make sure we have at least one port. */ if (smartlist_len(service->config.ports) == 0) { log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified " @@ -4042,6 +4161,11 @@ hs_service_free_(hs_service_t *service) replaycache_free(service->state.replay_cache_rend_cookie); } + /* Free onionbalance subcredentials (if any) */ + if (service->state.ob_subcreds) { + tor_free(service->state.ob_subcreds); + } + /* Wipe service keys. */ memwipe(&service->keys.identity_sk, 0, sizeof(service->keys.identity_sk)); @@ -4094,6 +4218,7 @@ hs_service_free_all(void) { rend_service_free_all(); service_free_all(); + hs_config_free_all(); } #ifdef TOR_UNIT_TESTS diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h index 8809411e01..0f6a2c2358 100644 --- a/src/feature/hs/hs_service.h +++ b/src/feature/hs/hs_service.h @@ -248,10 +248,14 @@ typedef struct hs_service_config_t { /** Does this service export the circuit ID of its clients? */ hs_circuit_id_protocol_t circuit_id_protocol; - /* DoS defenses. For the ESTABLISH_INTRO cell extension. */ + /** DoS defenses. For the ESTABLISH_INTRO cell extension. */ unsigned int has_dos_defense_enabled : 1; uint32_t intro_dos_rate_per_sec; uint32_t intro_dos_burst_per_sec; + + /** If set, contains the Onion Balance master ed25519 public key (taken from + * an .onion addresses) that this tor instance serves as backend. */ + smartlist_t *ob_master_pubkeys; } hs_service_config_t; /** Service state. */ @@ -275,6 +279,14 @@ typedef struct hs_service_state_t { /** When is the next time we should rotate our descriptors. This is has to be * done at the start time of the next SRV protocol run. */ time_t next_rotation_time; + + /* If this is an onionbalance instance, this is an array of subcredentials + * that should be used when decrypting an INTRO2 cell. If this is not an + * onionbalance instance, this is NULL. + * See [ONIONBALANCE] section in rend-spec-v3.txt for more details . */ + hs_subcredential_t *ob_subcreds; + /* Number of OB subcredentials */ + size_t n_ob_subcreds; } hs_service_state_t; /** Representation of a service running on this tor instance. */ @@ -300,9 +312,6 @@ typedef struct hs_service_t { hs_service_descriptor_t *desc_current; /** Next descriptor. */ hs_service_descriptor_t *desc_next; - - /* XXX: Credential (client auth.) #20700. */ - } hs_service_t; /** For the service global hash map, we define a specific type for it which @@ -375,6 +384,9 @@ STATIC hs_service_t *get_first_service(void); STATIC hs_service_intro_point_t *service_intro_point_find_by_ident( const hs_service_t *service, const hs_ident_circuit_t *ident); + +MOCK_DECL(STATIC unsigned int, count_desc_circuit_established, + (const hs_service_descriptor_t *desc)); #endif /* defined(TOR_UNIT_TESTS) */ /* Service accessors. */ diff --git a/src/feature/hs/include.am b/src/feature/hs/include.am index 5e69607e59..af1dc65585 100644 --- a/src/feature/hs/include.am +++ b/src/feature/hs/include.am @@ -13,6 +13,7 @@ LIBTOR_APP_A_SOURCES += \ src/feature/hs/hs_dos.c \ src/feature/hs/hs_ident.c \ src/feature/hs/hs_intropoint.c \ + src/feature/hs/hs_ob.c \ src/feature/hs/hs_service.c \ src/feature/hs/hs_stats.c @@ -30,6 +31,9 @@ noinst_HEADERS += \ src/feature/hs/hs_dos.h \ src/feature/hs/hs_ident.h \ src/feature/hs/hs_intropoint.h \ + src/feature/hs/hs_ob.h \ + src/feature/hs/hs_opts_st.h \ + src/feature/hs/hs_options.inc \ src/feature/hs/hs_service.h \ src/feature/hs/hs_stats.h \ src/feature/hs/hsdir_index_st.h diff --git a/src/feature/hs_common/shared_random_client.c b/src/feature/hs_common/shared_random_client.c index a46666ab50..3f46321be4 100644 --- a/src/feature/hs_common/shared_random_client.c +++ b/src/feature/hs_common/shared_random_client.c @@ -11,7 +11,8 @@ #include "feature/hs_common/shared_random_client.h" #include "app/config/config.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/authmode.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/nodelist/networkstatus.h" #include "lib/encoding/binascii.h" @@ -31,6 +32,24 @@ srv_to_control_string(const sr_srv_t *srv) return srv_str; } +/** + * If we have no consensus and we are not an authority, assume that this is + * the voting interval. We should never actually use this: only authorities + * should be trying to figure out the schedule when they don't have a + * consensus. + **/ +#define DEFAULT_NETWORK_VOTING_INTERVAL (3600) + +/* This is an unpleasing workaround for tests. Our unit tests assume that we + * are scheduling all of our shared random stuff as if we were a directory + * authority, but they do not always set V3AuthoritativeDir. + */ +#ifdef TOR_UNIT_TESTS +#define ASSUME_AUTHORITY_SCHEDULING 1 +#else +#define ASSUME_AUTHORITY_SCHEDULING 0 +#endif + /** Return the voting interval of the tor vote subsystem. */ int get_voting_interval(void) @@ -39,40 +58,27 @@ get_voting_interval(void) networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL)); if (consensus) { + /* Ideally we have a live consensus and we can just use that. */ + interval = (int)(consensus->fresh_until - consensus->valid_after); + } else if (authdir_mode(get_options()) || ASSUME_AUTHORITY_SCHEDULING) { + /* If we don't have a live consensus and we're an authority, + * we should believe our own view of what the schedule ought to be. */ + interval = dirauth_sched_get_configured_interval(); + } else if ((consensus = networkstatus_get_latest_consensus())) { + /* If we're a client, then maybe a latest consensus is good enough? + * It's better than falling back to the non-consensus case. */ interval = (int)(consensus->fresh_until - consensus->valid_after); } else { - /* Same for both a testing and real network. We voluntarily ignore the - * InitialVotingInterval since it complexifies things and it doesn't - * affect the SR protocol. */ - interval = get_options()->V3AuthVotingInterval; + /* We should never be reaching this point, since a client should never + * call this code unless they have some kind of a consensus. All we can + * do is hope that this network is using the default voting interval. */ + tor_assert_nonfatal_unreached_once(); + interval = DEFAULT_NETWORK_VOTING_INTERVAL; } tor_assert(interval > 0); return interval; } -/** Given the current consensus, return the start time of the current round of - * the SR protocol. For example, if it's 23:47:08, the current round thus - * started at 23:47:00 for a voting interval of 10 seconds. - * - * This function uses the consensus voting schedule to derive its results, - * instead of the actual consensus we are currently using, so it should be used - * for voting purposes. */ -time_t -get_start_time_of_current_round(void) -{ - const or_options_t *options = get_options(); - int voting_interval = get_voting_interval(); - /* First, get the start time of the next round */ - time_t next_start = voting_schedule_get_next_valid_after_time(); - /* Now roll back next_start by a voting interval to find the start time of - the current round. */ - time_t curr_start = voting_schedule_get_start_of_next_interval( - next_start - voting_interval - 1, - voting_interval, - options->TestingV3AuthVotingStartOffset); - return curr_start; -} - /* * Public API */ @@ -237,13 +243,27 @@ sr_state_get_start_time_of_current_protocol_run(void) time_t beginning_of_curr_round; /* This function is not used for voting purposes, so if we have a live - consensus, use its valid-after as the beginning of the current round, - otherwise resort to the voting schedule which should always exist. */ + consensus, use its valid-after as the beginning of the current round. + If we have no consensus but we're an authority, use our own + schedule. Otherwise, try using our view of the voting interval + to figure out when the current round _should_ be starting. + */ networkstatus_t *ns = networkstatus_get_live_consensus(approx_time()); if (ns) { beginning_of_curr_round = ns->valid_after; + } else if (authdir_mode(get_options()) || ASSUME_AUTHORITY_SCHEDULING) { + beginning_of_curr_round = dirauth_sched_get_cur_valid_after_time(); } else { - beginning_of_curr_round = get_start_time_of_current_round(); + /* voting_interval comes from get_voting_interval(), so if we're in + * this case as a client, we already tried to get the voting interval + * from the latest_consensus and gave a bug warning if we couldn't. + * + * We wouldn't want to look at the latest consensus's valid_after time, + * since that would be out of date. */ + beginning_of_curr_round = voting_sched_get_start_of_interval_after( + approx_time() - voting_interval, + voting_interval, + 0); } /* Get current SR protocol round */ diff --git a/src/feature/hs_common/shared_random_client.h b/src/feature/hs_common/shared_random_client.h index 3031a2bb9a..37a086d590 100644 --- a/src/feature/hs_common/shared_random_client.h +++ b/src/feature/hs_common/shared_random_client.h @@ -38,11 +38,9 @@ time_t sr_state_get_start_time_of_current_protocol_run(void); time_t sr_state_get_start_time_of_previous_protocol_run(void); unsigned int sr_state_get_phase_duration(void); unsigned int sr_state_get_protocol_run_duration(void); -time_t get_start_time_of_current_round(void); #ifdef TOR_UNIT_TESTS #endif /* TOR_UNIT_TESTS */ #endif /* !defined(TOR_SHARED_RANDOM_CLIENT_H) */ - diff --git a/src/feature/nodelist/authcert.c b/src/feature/nodelist/authcert.c index 7bdfabaeab..9c7525b6e2 100644 --- a/src/feature/nodelist/authcert.c +++ b/src/feature/nodelist/authcert.c @@ -46,7 +46,7 @@ #include "feature/nodelist/networkstatus_voter_info_st.h" #include "feature/nodelist/node_st.h" -DECLARE_TYPED_DIGESTMAP_FNS(dsmap_, digest_ds_map_t, download_status_t) +DECLARE_TYPED_DIGESTMAP_FNS(dsmap, digest_ds_map_t, download_status_t) #define DSMAP_FOREACH(map, keyvar, valvar) \ DIGESTMAP_FOREACH(dsmap_to_digestmap(map), keyvar, download_status_t *, \ valvar) diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c index 06f4f5482b..ad3af0a143 100644 --- a/src/feature/nodelist/dirlist.c +++ b/src/feature/nodelist/dirlist.c @@ -49,6 +49,37 @@ static smartlist_t *trusted_dir_servers = NULL; * and all fallback directory servers. */ static smartlist_t *fallback_dir_servers = NULL; +/** Helper: From a given trusted directory entry, add the v4 or/and v6 address + * to the nodelist address set. */ +static void +add_trusted_dir_to_nodelist_addr_set(const dir_server_t *dir) +{ + tor_assert(dir); + tor_assert(dir->is_authority); + + /* Add IPv4 and then IPv6 if applicable. */ + nodelist_add_addr4_to_address_set(dir->addr); + if (!tor_addr_is_null(&dir->ipv6_addr)) { + nodelist_add_addr6_to_address_set(&dir->ipv6_addr); + } +} + +/** Go over the trusted directory server list and add their address(es) to the + * nodelist address set. This is called everytime a new consensus is set. */ +MOCK_IMPL(void, +dirlist_add_trusted_dir_addresses, (void)) +{ + if (!trusted_dir_servers) { + return; + } + + SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, const dir_server_t *, ent) { + if (ent->is_authority) { + add_trusted_dir_to_nodelist_addr_set(ent); + } + } SMARTLIST_FOREACH_END(ent); +} + /** Return the number of directory authorities whose type matches some bit set * in <b>type</b> */ int diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h index 6bfc2e1b01..9201e76a9c 100644 --- a/src/feature/nodelist/dirlist.h +++ b/src/feature/nodelist/dirlist.h @@ -44,4 +44,6 @@ void dir_server_add(dir_server_t *ent); void clear_dir_servers(void); void dirlist_free_all(void); +MOCK_DECL(void, dirlist_add_trusted_dir_addresses, (void)); + #endif /* !defined(TOR_DIRLIST_H) */ diff --git a/src/feature/nodelist/microdesc.c b/src/feature/nodelist/microdesc.c index d32a4ea61e..cf7732b8dc 100644 --- a/src/feature/nodelist/microdesc.c +++ b/src/feature/nodelist/microdesc.c @@ -90,10 +90,10 @@ microdesc_eq_(microdesc_t *a, microdesc_t *b) } HT_PROTOTYPE(microdesc_map, microdesc_t, node, - microdesc_hash_, microdesc_eq_) + microdesc_hash_, microdesc_eq_); HT_GENERATE2(microdesc_map, microdesc_t, node, microdesc_hash_, microdesc_eq_, 0.6, - tor_reallocarray_, tor_free_) + tor_reallocarray_, tor_free_); /************************* md fetch fail cache *****************************/ diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c index cc4b8e1c34..e07d58c91c 100644 --- a/src/feature/nodelist/networkstatus.c +++ b/src/feature/nodelist/networkstatus.c @@ -66,7 +66,7 @@ #include "feature/dirclient/dirclient_modes.h" #include "feature/dirclient/dlstatus.h" #include "feature/dircommon/directory.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/dirparse/ns_parse.h" #include "feature/hibernate/hibernate.h" #include "feature/hs/hs_dos.h" @@ -2120,7 +2120,7 @@ networkstatus_set_current_consensus(const char *consensus, * the first thing we need to do is recalculate the voting schedule static * object so we can use the timings in there needed by some subsystems * such as hidden service and shared random. */ - voting_schedule_recalculate_timing(options, now); + dirauth_sched_recalculate_timing(options, now); reschedule_dirvote(options); nodelist_set_consensus(c); @@ -2167,7 +2167,7 @@ networkstatus_set_current_consensus(const char *consensus, warn_early_consensus(c, flavor, now); - /* We got a new consesus. Reset our md fetch fail cache */ + /* We got a new consensus. Reset our md fetch fail cache */ microdesc_reset_outdated_dirservers_list(); router_dir_info_changed(); @@ -2765,3 +2765,47 @@ networkstatus_free_all(void) } } } + +/** Return the start of the next interval of size <b>interval</b> (in + * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always + * starts a fresh interval, and if the last interval of a day would be + * truncated to less than half its size, it is rolled into the + * previous interval. */ +time_t +voting_sched_get_start_of_interval_after(time_t now, int interval, + int offset) +{ + struct tm tm; + time_t midnight_today=0; + time_t midnight_tomorrow; + time_t next; + + tor_gmtime_r(&now, &tm); + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + + if (tor_timegm(&tm, &midnight_today) < 0) { + // LCOV_EXCL_START + log_warn(LD_BUG, "Ran into an invalid time when trying to find midnight."); + // LCOV_EXCL_STOP + } + midnight_tomorrow = midnight_today + (24*60*60); + + next = midnight_today + ((now-midnight_today)/interval + 1)*interval; + + /* Intervals never cross midnight. */ + if (next > midnight_tomorrow) + next = midnight_tomorrow; + + /* If the interval would only last half as long as it's supposed to, then + * skip over to the next day. */ + if (next + interval/2 > midnight_tomorrow) + next = midnight_tomorrow; + + next += offset; + if (next - interval > now) + next -= interval; + + return next; +} diff --git a/src/feature/nodelist/networkstatus.h b/src/feature/nodelist/networkstatus.h index 5e8c8a9e57..ce050aeadc 100644 --- a/src/feature/nodelist/networkstatus.h +++ b/src/feature/nodelist/networkstatus.h @@ -153,6 +153,9 @@ void vote_routerstatus_free_(vote_routerstatus_t *rs); void set_routerstatus_from_routerinfo(routerstatus_t *rs, const node_t *node, const routerinfo_t *ri); +time_t voting_sched_get_start_of_interval_after(time_t now, + int interval, + int offset); #ifdef NETWORKSTATUS_PRIVATE #ifdef TOR_UNIT_TESTS diff --git a/src/feature/nodelist/nodefamily.c b/src/feature/nodelist/nodefamily.c index 7ae8620749..feaa3730dc 100644 --- a/src/feature/nodelist/nodefamily.c +++ b/src/feature/nodelist/nodefamily.c @@ -69,9 +69,9 @@ static HT_HEAD(nodefamily_map, nodefamily_t) the_node_families = HT_INITIALIZER(); HT_PROTOTYPE(nodefamily_map, nodefamily_t, ht_ent, nodefamily_hash, - nodefamily_eq) + nodefamily_eq); HT_GENERATE2(nodefamily_map, nodefamily_t, ht_ent, nodefamily_hash, - node_family_eq, 0.6, tor_reallocarray_, tor_free_) + node_family_eq, 0.6, tor_reallocarray_, tor_free_); /** * Parse the family declaration in <b>s</b>, returning the canonical diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c index 94ff08826f..fcf27b1a3a 100644 --- a/src/feature/nodelist/nodelist.c +++ b/src/feature/nodelist/nodelist.c @@ -153,9 +153,9 @@ node_id_eq(const node_t *node1, const node_t *node2) return tor_memeq(node1->identity, node2->identity, DIGEST_LEN); } -HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq) +HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq); HT_GENERATE2(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq, - 0.6, tor_reallocarray_, tor_free_) + 0.6, tor_reallocarray_, tor_free_); static inline unsigned int node_ed_id_hash(const node_t *node) @@ -170,9 +170,9 @@ node_ed_id_eq(const node_t *node1, const node_t *node2) } HT_PROTOTYPE(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash, - node_ed_id_eq) + node_ed_id_eq); HT_GENERATE2(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash, - node_ed_id_eq, 0.6, tor_reallocarray_, tor_free_) + node_ed_id_eq, 0.6, tor_reallocarray_, tor_free_); /** The global nodelist. */ static nodelist_t *the_nodelist=NULL; @@ -455,22 +455,43 @@ node_add_to_address_set(const node_t *node) if (node->rs) { if (node->rs->addr) - address_set_add_ipv4h(the_nodelist->node_addrs, node->rs->addr); + nodelist_add_addr4_to_address_set(node->rs->addr); if (!tor_addr_is_null(&node->rs->ipv6_addr)) - address_set_add(the_nodelist->node_addrs, &node->rs->ipv6_addr); + nodelist_add_addr6_to_address_set(&node->rs->ipv6_addr); } if (node->ri) { if (node->ri->addr) - address_set_add_ipv4h(the_nodelist->node_addrs, node->ri->addr); + nodelist_add_addr4_to_address_set(node->ri->addr); if (!tor_addr_is_null(&node->ri->ipv6_addr)) - address_set_add(the_nodelist->node_addrs, &node->ri->ipv6_addr); + nodelist_add_addr6_to_address_set(&node->ri->ipv6_addr); } if (node->md) { if (!tor_addr_is_null(&node->md->ipv6_addr)) - address_set_add(the_nodelist->node_addrs, &node->md->ipv6_addr); + nodelist_add_addr6_to_address_set(&node->md->ipv6_addr); } } +/** Add the given v4 address into the nodelist address set. */ +void +nodelist_add_addr4_to_address_set(const uint32_t addr) +{ + if (!the_nodelist || !the_nodelist->node_addrs || addr == 0) { + return; + } + address_set_add_ipv4h(the_nodelist->node_addrs, addr); +} + +/** Add the given v6 address into the nodelist address set. */ +void +nodelist_add_addr6_to_address_set(const tor_addr_t *addr) +{ + if (BUG(!addr) || tor_addr_is_null(addr) || tor_addr_is_v4(addr) || + !the_nodelist || !the_nodelist->node_addrs) { + return; + } + address_set_add(the_nodelist->node_addrs, addr); +} + /** Return true if <b>addr</b> is the address of some node in the nodelist. * If not, probably return false. */ int @@ -612,9 +633,12 @@ nodelist_set_consensus(networkstatus_t *ns) SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node, node->rs = NULL); - /* Conservatively estimate that every node will have 2 addresses. */ - const int estimated_addresses = smartlist_len(ns->routerstatus_list) * - get_estimated_address_per_node(); + /* Conservatively estimate that every node will have 2 addresses (v4 and + * v6). Then we add the number of configured trusted authorities we have. */ + int estimated_addresses = smartlist_len(ns->routerstatus_list) * + get_estimated_address_per_node(); + estimated_addresses += (get_n_authorities(V3_DIRINFO & BRIDGE_DIRINFO) * + get_estimated_address_per_node()); address_set_free(the_nodelist->node_addrs); the_nodelist->node_addrs = address_set_new(estimated_addresses); @@ -665,6 +689,9 @@ nodelist_set_consensus(networkstatus_t *ns) SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { node_add_to_address_set(node); } SMARTLIST_FOREACH_END(node); + /* Then, add all trusted configured directories. Some might not be in the + * consensus so make sure we know them. */ + dirlist_add_trusted_dir_addresses(); if (! authdir) { SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { @@ -1213,8 +1240,8 @@ node_get_rsa_id_digest(const node_t *node) * If node is NULL, returns an empty smartlist. * * The smartlist must be freed using link_specifier_smartlist_free(). */ -smartlist_t * -node_get_link_specifier_smartlist(const node_t *node, bool direct_conn) +MOCK_IMPL(smartlist_t *, +node_get_link_specifier_smartlist,(const node_t *node, bool direct_conn)) { link_specifier_t *ls; tor_addr_port_t ap; diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h index 9742e3dff2..6e854ec879 100644 --- a/src/feature/nodelist/nodelist.h +++ b/src/feature/nodelist/nodelist.h @@ -35,6 +35,8 @@ node_t *nodelist_add_microdesc(microdesc_t *md); void nodelist_set_consensus(networkstatus_t *ns); void nodelist_ensure_freshness(networkstatus_t *ns); int nodelist_probably_contains_address(const tor_addr_t *addr); +void nodelist_add_addr4_to_address_set(const uint32_t addr); +void nodelist_add_addr6_to_address_set(const tor_addr_t *addr); void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md); void nodelist_remove_routerinfo(routerinfo_t *ri); @@ -78,8 +80,8 @@ int node_supports_ed25519_hs_intro(const node_t *node); int node_supports_v3_rendezvous_point(const node_t *node); int node_supports_establish_intro_dos_extension(const node_t *node); const uint8_t *node_get_rsa_id_digest(const node_t *node); -smartlist_t *node_get_link_specifier_smartlist(const node_t *node, - bool direct_conn); +MOCK_DECL(smartlist_t *,node_get_link_specifier_smartlist,(const node_t *node, + bool direct_conn)); void link_specifier_smartlist_free_(smartlist_t *ls_list); #define link_specifier_smartlist_free(ls_list) \ FREE_AND_NULL(smartlist_t, link_specifier_smartlist_free_, (ls_list)) diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c index 42ce6f4c4e..80c1aa6893 100644 --- a/src/feature/nodelist/routerlist.c +++ b/src/feature/nodelist/routerlist.c @@ -117,9 +117,9 @@ /* Typed wrappers for different digestmap types; used to avoid type * confusion. */ -DECLARE_TYPED_DIGESTMAP_FNS(sdmap_, digest_sd_map_t, signed_descriptor_t) -DECLARE_TYPED_DIGESTMAP_FNS(rimap_, digest_ri_map_t, routerinfo_t) -DECLARE_TYPED_DIGESTMAP_FNS(eimap_, digest_ei_map_t, extrainfo_t) +DECLARE_TYPED_DIGESTMAP_FNS(sdmap, digest_sd_map_t, signed_descriptor_t) +DECLARE_TYPED_DIGESTMAP_FNS(rimap, digest_ri_map_t, routerinfo_t) +DECLARE_TYPED_DIGESTMAP_FNS(eimap, digest_ei_map_t, extrainfo_t) #define SDMAP_FOREACH(map, keyvar, valvar) \ DIGESTMAP_FOREACH(sdmap_to_digestmap(map), keyvar, signed_descriptor_t *, \ valvar) @@ -2922,7 +2922,7 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2) (r1->bandwidthburst != r2->bandwidthburst)) return 0; - /* Did more than 12 hours pass? */ + /* Has enough time passed between the publication times? */ if (r1->cache_info.published_on + ROUTER_MAX_COSMETIC_TIME_DIFFERENCE < r2->cache_info.published_on) return 0; diff --git a/src/feature/relay/dns.c b/src/feature/relay/dns.c index da0cbb1df4..5f868a9020 100644 --- a/src/feature/relay/dns.c +++ b/src/feature/relay/dns.c @@ -146,9 +146,9 @@ cached_resolve_hash(cached_resolve_t *a) } HT_PROTOTYPE(cache_map, cached_resolve_t, node, cached_resolve_hash, - cached_resolves_eq) + cached_resolves_eq); HT_GENERATE2(cache_map, cached_resolve_t, node, cached_resolve_hash, - cached_resolves_eq, 0.6, tor_reallocarray_, tor_free_) + cached_resolves_eq, 0.6, tor_reallocarray_, tor_free_); /** Initialize the DNS cache. */ static void @@ -268,22 +268,6 @@ has_dns_init_failed(void) return nameserver_config_failed; } -/** Helper: Given a TTL from a DNS response, determine what TTL to give the - * OP that asked us to resolve it, and how long to cache that record - * ourselves. */ -uint32_t -dns_clip_ttl(uint32_t ttl) -{ - /* This logic is a defense against "DefectTor" DNS-based traffic - * confirmation attacks, as in https://nymity.ch/tor-dns/tor-dns.pdf . - * We only give two values: a "low" value and a "high" value. - */ - if (ttl < MIN_DNS_TTL_AT_EXIT) - return MIN_DNS_TTL_AT_EXIT; - else - return MAX_DNS_TTL_AT_EXIT; -} - /** Helper: free storage held by an entry in the DNS cache. */ static void free_cached_resolve_(cached_resolve_t *r) @@ -521,7 +505,7 @@ send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type, uint32_t ttl; buf[0] = answer_type; - ttl = dns_clip_ttl(conn->address_ttl); + ttl = clip_dns_ttl(conn->address_ttl); switch (answer_type) { @@ -593,7 +577,7 @@ send_resolved_hostname_cell,(edge_connection_t *conn, size_t namelen = strlen(hostname); tor_assert(namelen < 256); - ttl = dns_clip_ttl(conn->address_ttl); + ttl = clip_dns_ttl(conn->address_ttl); buf[0] = RESOLVED_TYPE_HOSTNAME; buf[1] = (uint8_t)namelen; @@ -987,25 +971,6 @@ assert_connection_edge_not_dns_pending(edge_connection_t *conn) #endif /* 1 */ } -/** Log an error and abort if any connection waiting for a DNS resolve is - * corrupted. */ -void -assert_all_pending_dns_resolves_ok(void) -{ - pending_connection_t *pend; - cached_resolve_t **resolve; - - HT_FOREACH(resolve, cache_map, &cache_root) { - for (pend = (*resolve)->pending_connections; - pend; - pend = pend->next) { - assert_connection_ok(TO_CONN(pend->conn), 0); - tor_assert(!SOCKET_OK(pend->conn->base_.s)); - tor_assert(!connection_in_array(TO_CONN(pend->conn))); - } - } -} - /** Remove <b>conn</b> from the list of connections waiting for conn-\>address. */ void @@ -1063,7 +1028,7 @@ connection_dns_remove(edge_connection_t *conn) * the resolve for <b>address</b> itself, and remove any cached results for * <b>address</b> from the cache. */ -MOCK_IMPL(void, +MOCK_IMPL(STATIC void, dns_cancel_pending_resolve,(const char *address)) { pending_connection_t *pend; @@ -1338,7 +1303,7 @@ make_pending_resolve_cached(cached_resolve_t *resolve) resolve->ttl_hostname < ttl) ttl = resolve->ttl_hostname; - set_expiry(new_resolve, time(NULL) + dns_clip_ttl(ttl)); + set_expiry(new_resolve, time(NULL) + clip_dns_ttl(ttl)); } assert_cache_ok(); @@ -2051,12 +2016,12 @@ dns_launch_correctness_checks(void) /* Wait a while before launching requests for test addresses, so we can * get the results from checking for wildcarding. */ - if (! launch_event) + if (!launch_event) launch_event = tor_evtimer_new(tor_libevent_get_base(), launch_test_addresses, NULL); timeout.tv_sec = 30; timeout.tv_usec = 0; - if (evtimer_add(launch_event, &timeout)<0) { + if (evtimer_add(launch_event, &timeout) < 0) { log_warn(LD_BUG, "Couldn't add timer for checking for dns hijacking"); } } @@ -2188,7 +2153,7 @@ dns_cache_handle_oom(time_t now, size_t min_remove_bytes) total_bytes_removed += bytes_removed; /* Increase time_inc by a reasonable fraction. */ - time_inc += (MAX_DNS_TTL_AT_EXIT / 4); + time_inc += (MAX_DNS_TTL / 4); } while (total_bytes_removed < min_remove_bytes); return total_bytes_removed; diff --git a/src/feature/relay/dns.h b/src/feature/relay/dns.h index 2b1da8d126..120b75bf8d 100644 --- a/src/feature/relay/dns.h +++ b/src/feature/relay/dns.h @@ -12,29 +12,14 @@ #ifndef TOR_DNS_H #define TOR_DNS_H -/** Lowest value for DNS ttl that a server will give. */ -#define MIN_DNS_TTL_AT_EXIT (5*60) -/** Highest value for DNS ttl that a server will give. */ -#define MAX_DNS_TTL_AT_EXIT (60*60) - -/** How long do we keep DNS cache entries before purging them (regardless of - * their TTL)? */ -#define MAX_DNS_ENTRY_AGE (3*60*60) -/** How long do we cache/tell clients to cache DNS records when no TTL is - * known? */ -#define DEFAULT_DNS_TTL (30*60) +#ifdef HAVE_MODULE_RELAY int dns_init(void); int has_dns_init_failed(void); -void dns_free_all(void); -uint32_t dns_clip_ttl(uint32_t ttl); int dns_reset(void); void connection_dns_remove(edge_connection_t *conn); void assert_connection_edge_not_dns_pending(edge_connection_t *conn); -void assert_all_pending_dns_resolves_ok(void); -MOCK_DECL(void,dns_cancel_pending_resolve,(const char *question)); int dns_resolve(edge_connection_t *exitconn); -void dns_launch_correctness_checks(void); int dns_seems_to_be_broken(void); int dns_seems_to_be_broken_for_ipv6(void); void dns_reset_correctness_checks(void); @@ -42,6 +27,48 @@ size_t dns_cache_total_allocation(void); void dump_dns_mem_usage(int severity); size_t dns_cache_handle_oom(time_t now, size_t min_remove_bytes); +/* These functions are only used within the feature/relay module, and don't + * need stubs. */ +void dns_free_all(void); +void dns_launch_correctness_checks(void); + +#else /* !defined(HAVE_MODULE_RELAY) */ + +#define dns_init() (0) +#define dns_seems_to_be_broken() (0) +#define has_dns_init_failed() (0) +#define dns_cache_total_allocation() (0) + +#define dns_reset_correctness_checks() STMT_NIL + +#define assert_connection_edge_not_dns_pending(conn) \ + ((void)(conn)) +#define dump_dns_mem_usage(severity)\ + ((void)(severity)) +#define dns_cache_handle_oom(now, bytes) \ + ((void)(now), (void)(bytes), 0) + +#define connection_dns_remove(conn) \ + STMT_BEGIN \ + (void)(conn); \ + tor_assert_nonfatal_unreached(); \ + STMT_END + +static inline int +dns_reset(void) +{ + return 0; +} +static inline int +dns_resolve(edge_connection_t *exitconn) +{ + (void)exitconn; + tor_assert_nonfatal_unreached(); + return -1; +} + +#endif /* defined(HAVE_MODULE_RELAY) */ + #ifdef DNS_PRIVATE #include "feature/relay/dns_structs.h" @@ -50,6 +77,7 @@ size_t number_of_configured_nameservers(void); tor_addr_t *configured_nameserver_address(const size_t idx); #endif +MOCK_DECL(STATIC void,dns_cancel_pending_resolve,(const char *question)); MOCK_DECL(STATIC int,dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve,or_circuit_t *oncirc, char **hostname_out, int *made_connection_pending_out, cached_resolve_t **resolve_out)); @@ -74,4 +102,3 @@ launch_resolve,(cached_resolve_t *resolve)); #endif /* defined(DNS_PRIVATE) */ #endif /* !defined(TOR_DNS_H) */ - diff --git a/src/feature/relay/ext_orport.c b/src/feature/relay/ext_orport.c index ce4e043dd7..cff5f42cc7 100644 --- a/src/feature/relay/ext_orport.c +++ b/src/feature/relay/ext_orport.c @@ -602,7 +602,7 @@ connection_ext_or_process_inbuf(or_connection_t *or_conn) command->body, command->len) < 0) goto err; } else { - log_notice(LD_NET,"Got Extended ORPort command we don't regognize (%u).", + log_notice(LD_NET,"Got Extended ORPort command we don't recognize (%u).", command->cmd); } @@ -652,6 +652,77 @@ connection_ext_or_start_auth(or_connection_t *or_conn) return 0; } +/** Global map between Extended ORPort identifiers and OR + * connections. */ +static digestmap_t *orconn_ext_or_id_map = NULL; + +/** Remove the Extended ORPort identifier of <b>conn</b> from the + * global identifier list. Also, clear the identifier from the + * connection itself. */ +void +connection_or_remove_from_ext_or_id_map(or_connection_t *conn) +{ + or_connection_t *tmp; + if (!orconn_ext_or_id_map) + return; + if (!conn->ext_or_conn_id) + return; + + tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id); + if (!tor_digest_is_zero(conn->ext_or_conn_id)) + tor_assert(tmp == conn); + + memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN); +} + +#ifdef TOR_UNIT_TESTS +/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such + * connection is found. */ +or_connection_t * +connection_or_get_by_ext_or_id(const char *id) +{ + if (!orconn_ext_or_id_map) + return NULL; + return digestmap_get(orconn_ext_or_id_map, id); +} +#endif /* defined(TOR_UNIT_TESTS) */ + +/** Deallocate the global Extended ORPort identifier list */ +void +connection_or_clear_ext_or_id_map(void) +{ + digestmap_free(orconn_ext_or_id_map, NULL); + orconn_ext_or_id_map = NULL; +} + +/** Creates an Extended ORPort identifier for <b>conn</b> and deposits + * it into the global list of identifiers. */ +void +connection_or_set_ext_or_identifier(or_connection_t *conn) +{ + char random_id[EXT_OR_CONN_ID_LEN]; + or_connection_t *tmp; + + if (!orconn_ext_or_id_map) + orconn_ext_or_id_map = digestmap_new(); + + /* Remove any previous identifiers: */ + if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id)) + connection_or_remove_from_ext_or_id_map(conn); + + do { + crypto_rand(random_id, sizeof(random_id)); + } while (digestmap_get(orconn_ext_or_id_map, random_id)); + + if (!conn->ext_or_conn_id) + conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN); + + memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN); + + tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn); + tor_assert(!tmp); +} + /** Free any leftover allocated memory of the ext_orport.c subsystem. */ void ext_orport_free_all(void) diff --git a/src/feature/relay/ext_orport.h b/src/feature/relay/ext_orport.h index dbe89fce18..416c358397 100644 --- a/src/feature/relay/ext_orport.h +++ b/src/feature/relay/ext_orport.h @@ -31,26 +31,56 @@ #define EXT_OR_CONN_STATE_FLUSHING 5 #define EXT_OR_CONN_STATE_MAX_ 5 -int connection_ext_or_start_auth(or_connection_t *or_conn); - -ext_or_cmd_t *ext_or_cmd_new(uint16_t len); +#ifdef HAVE_MODULE_RELAY -#define ext_or_cmd_free(cmd) \ - FREE_AND_NULL(ext_or_cmd_t, ext_or_cmd_free_, (cmd)) +int connection_ext_or_start_auth(or_connection_t *or_conn); -void ext_or_cmd_free_(ext_or_cmd_t *cmd); void connection_or_set_ext_or_identifier(or_connection_t *conn); void connection_or_remove_from_ext_or_id_map(or_connection_t *conn); void connection_or_clear_ext_or_id_map(void); -or_connection_t *connection_or_get_by_ext_or_id(const char *id); - int connection_ext_or_finished_flushing(or_connection_t *conn); int connection_ext_or_process_inbuf(or_connection_t *or_conn); +char *get_ext_or_auth_cookie_file_name(void); +/* (No stub needed for these: they are only called within feature/relay.) */ int init_ext_or_cookie_authentication(int is_enabled); -char *get_ext_or_auth_cookie_file_name(void); void ext_orport_free_all(void); +#else /* !defined(HAVE_MODULE_RELAY) */ + +static inline int +connection_ext_or_start_auth(or_connection_t *conn) +{ + (void)conn; + tor_assert_nonfatal_unreached(); + return -1; +} +static inline int +connection_ext_or_finished_flushing(or_connection_t *conn) +{ + (void)conn; + tor_assert_nonfatal_unreached(); + return -1; +} +static inline int +connection_ext_or_process_inbuf(or_connection_t *conn) +{ + (void)conn; + tor_assert_nonfatal_unreached(); + return -1; +} +#define connection_or_set_ext_or_identifier(conn) \ + ((void)(conn)) +#define connection_or_remove_from_ext_or_id_map(conn) \ + ((void)(conn)) +#define connection_or_clear_ext_or_id_map() \ + STMT_NIL + +#define get_ext_or_auth_cookie_file_name() \ + (NULL) + +#endif /* defined(HAVE_MODULE_RELAY) */ + #ifdef EXT_ORPORT_PRIVATE STATIC int connection_write_ext_or_command(connection_t *conn, uint16_t command, @@ -60,9 +90,11 @@ STATIC int handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len, char **client_hash_out, char **reply_out, size_t *reply_len_out); + #ifdef TOR_UNIT_TESTS extern uint8_t *ext_or_auth_cookie; extern int ext_or_auth_cookie_is_set; +or_connection_t *connection_or_get_by_ext_or_id(const char *id); #endif #endif /* defined(EXT_ORPORT_PRIVATE) */ diff --git a/src/feature/relay/include.am b/src/feature/relay/include.am index a4c025ae12..813ddb8fb1 100644 --- a/src/feature/relay/include.am +++ b/src/feature/relay/include.am @@ -1,21 +1,22 @@ # Legacy shared relay code: migrate to the relay module over time LIBTOR_APP_A_SOURCES += \ - src/feature/relay/dns.c \ - src/feature/relay/ext_orport.c \ src/feature/relay/onion_queue.c \ - src/feature/relay/router.c \ - src/feature/relay/routerkeys.c \ - src/feature/relay/selftest.c + src/feature/relay/router.c # The Relay module. # ADD_C_FILE: INSERT SOURCES HERE. MODULE_RELAY_SOURCES = \ + src/feature/relay/dns.c \ + src/feature/relay/ext_orport.c \ src/feature/relay/routermode.c \ src/feature/relay/relay_config.c \ + src/feature/relay/relay_handshake.c \ src/feature/relay/relay_periodic.c \ src/feature/relay/relay_sys.c \ + src/feature/relay/routerkeys.c \ + src/feature/relay/selftest.c \ src/feature/relay/transport_config.c # ADD_C_FILE: INSERT HEADERS HERE. @@ -25,6 +26,7 @@ noinst_HEADERS += \ src/feature/relay/ext_orport.h \ src/feature/relay/onion_queue.h \ src/feature/relay/relay_config.h \ + src/feature/relay/relay_handshake.h \ src/feature/relay/relay_periodic.h \ src/feature/relay/relay_sys.h \ src/feature/relay/router.h \ diff --git a/src/feature/relay/onion_queue.c b/src/feature/relay/onion_queue.c index ce2d41b7e1..3cbaa65d28 100644 --- a/src/feature/relay/onion_queue.c +++ b/src/feature/relay/onion_queue.c @@ -49,10 +49,12 @@ typedef struct onion_queue_t { /** 5 seconds on the onion queue til we just send back a destroy */ #define ONIONQUEUE_WAIT_CUTOFF 5 +TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t); +typedef struct onion_queue_head_t onion_queue_head_t; + /** Array of queues of circuits waiting for CPU workers. An element is NULL * if that queue is empty.*/ -static TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t) - ol_list[MAX_ONION_HANDSHAKE_TYPE+1] = +static onion_queue_head_t ol_list[MAX_ONION_HANDSHAKE_TYPE+1] = { TOR_TAILQ_HEAD_INITIALIZER(ol_list[0]), /* tap */ TOR_TAILQ_HEAD_INITIALIZER(ol_list[1]), /* fast */ TOR_TAILQ_HEAD_INITIALIZER(ol_list[2]), /* ntor */ diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c index 8d20e97eb6..3e9961f47e 100644 --- a/src/feature/relay/relay_config.c +++ b/src/feature/relay/relay_config.c @@ -231,8 +231,8 @@ check_server_ports(const smartlist_t *ports, } /** Parse all relay ports from <b>options</b>. On success, add parsed ports to - * <b>ports</b>, and return 0. On failure, set *<b>msg</b> to a description - * of the problem and return -1. + * <b>ports</b>, and return 0. On failure, set *<b>msg</b> to a newly + * allocated string describing the problem, and return -1. **/ int port_parse_ports_relay(or_options_t *options, @@ -334,7 +334,8 @@ port_update_port_set_relay(or_options_t *options, * Legacy validation function, which checks that the current OS is usable in * relay mode, if options is set to a relay mode. * - * Warns about OSes with potential issues. Always returns 0. + * Warns about OSes with potential issues. Does not set *<b>msg</b>. + * Always returns 0. */ int options_validate_relay_os(const or_options_t *old_options, @@ -400,10 +401,14 @@ options_validate_relay_info(const or_options_t *old_options, } } - if (server_mode(options) && !options->ContactInfo) - log_notice(LD_CONFIG, "Your ContactInfo config option is not set. " - "Please consider setting it, so we can contact you if your server is " - "misconfigured or something else goes wrong."); + if (server_mode(options) && !options->ContactInfo) { + log_warn(LD_CONFIG, + "Your ContactInfo config option is not set. Please strongly " + "consider setting it, so we can contact you if your relay is " + "misconfigured, end-of-life, or something else goes wrong. " + "It is also possible that your relay might get rejected from " + "the network due to a missing valid contact address."); + } const char *ContactInfo = options->ContactInfo; if (ContactInfo && !string_is_utf8(ContactInfo, strlen(ContactInfo))) diff --git a/src/feature/relay/relay_config.h b/src/feature/relay/relay_config.h index 7a05561c26..c70c322d88 100644 --- a/src/feature/relay/relay_config.h +++ b/src/feature/relay/relay_config.h @@ -125,6 +125,20 @@ options_validate_relay_mode(const struct or_options_t *old_options, return 0; } +static inline int +port_parse_ports_relay(or_options_t *options, + char **msg, + smartlist_t *ports_out, + int *have_low_ports_out) +{ + (void)options; + (void)msg; + (void)ports_out; + if (*have_low_ports_out < 0) + *have_low_ports_out = 0; + return 0; +} + #define relay_get_dirportfrontpage() \ (NULL) #define relay_config_free_all() \ @@ -138,9 +152,6 @@ options_validate_relay_mode(const struct or_options_t *old_options, #define port_warn_nonlocal_ext_orports(ports, portname) \ (((void)(ports)),((void)(portname))) -#define port_parse_ports_relay(options, msg, ports_out, have_low_ports_out) \ - (((void)(options)),((void)(msg)),((void)(ports_out)), \ - ((void)(have_low_ports_out)),0) #define port_update_port_set_relay(options, ports) \ (((void)(options)),((void)(ports))) diff --git a/src/feature/relay/relay_handshake.c b/src/feature/relay/relay_handshake.c new file mode 100644 index 0000000000..030dc94956 --- /dev/null +++ b/src/feature/relay/relay_handshake.c @@ -0,0 +1,565 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file relay_handshake.c + * @brief Functions to implement the relay-only parts of our + * connection handshake. + * + * Some parts of our TLS link handshake are only done by relays (including + * bridges). Specifically, only relays need to send CERTS cells; only + * relays need to send or receive AUTHCHALLENGE cells, and only relays need to + * send or receive AUTHENTICATE cells. + **/ + +#include "orconfig.h" +#include "core/or/or.h" +#include "feature/relay/relay_handshake.h" + +#include "app/config/config.h" +#include "core/or/connection_or.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "trunnel/link_handshake.h" +#include "feature/relay/routerkeys.h" +#include "feature/nodelist/torcert.h" + +#include "core/or/or_connection_st.h" +#include "core/or/or_handshake_certs_st.h" +#include "core/or/or_handshake_state_st.h" +#include "core/or/var_cell_st.h" + +#include "lib/tls/tortls.h" +#include "lib/tls/x509.h" + +/** Helper used to add an encoded certs to a cert cell */ +static void +add_certs_cell_cert_helper(certs_cell_t *certs_cell, + uint8_t cert_type, + const uint8_t *cert_encoded, + size_t cert_len) +{ + tor_assert(cert_len <= UINT16_MAX); + certs_cell_cert_t *ccc = certs_cell_cert_new(); + ccc->cert_type = cert_type; + ccc->cert_len = cert_len; + certs_cell_cert_setlen_body(ccc, cert_len); + memcpy(certs_cell_cert_getarray_body(ccc), cert_encoded, cert_len); + + certs_cell_add_certs(certs_cell, ccc); +} + +/** Add an encoded X509 cert (stored as <b>cert_len</b> bytes at + * <b>cert_encoded</b>) to the trunnel certs_cell_t object that we are + * building in <b>certs_cell</b>. Set its type field to <b>cert_type</b>. + * (If <b>cert</b> is NULL, take no action.) */ +static void +add_x509_cert(certs_cell_t *certs_cell, + uint8_t cert_type, + const tor_x509_cert_t *cert) +{ + if (NULL == cert) + return; + + const uint8_t *cert_encoded = NULL; + size_t cert_len; + tor_x509_cert_get_der(cert, &cert_encoded, &cert_len); + + add_certs_cell_cert_helper(certs_cell, cert_type, cert_encoded, cert_len); +} + +/** Add an Ed25519 cert from <b>cert</b> to the trunnel certs_cell_t object + * that we are building in <b>certs_cell</b>. Set its type field to + * <b>cert_type</b>. (If <b>cert</b> is NULL, take no action.) */ +static void +add_ed25519_cert(certs_cell_t *certs_cell, + uint8_t cert_type, + const tor_cert_t *cert) +{ + if (NULL == cert) + return; + + add_certs_cell_cert_helper(certs_cell, cert_type, + cert->encoded, cert->encoded_len); +} + +#ifdef TOR_UNIT_TESTS +int certs_cell_ed25519_disabled_for_testing = 0; +#else +#define certs_cell_ed25519_disabled_for_testing 0 +#endif + +/** Send a CERTS cell on the connection <b>conn</b>. Return 0 on success, -1 + * on failure. */ +int +connection_or_send_certs_cell(or_connection_t *conn) +{ + const tor_x509_cert_t *global_link_cert = NULL, *id_cert = NULL; + tor_x509_cert_t *own_link_cert = NULL; + var_cell_t *cell; + + certs_cell_t *certs_cell = NULL; + + tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); + + if (! conn->handshake_state) + return -1; + + const int conn_in_server_mode = ! conn->handshake_state->started_here; + + /* Get the encoded values of the X509 certificates */ + if (tor_tls_get_my_certs(conn_in_server_mode, + &global_link_cert, &id_cert) < 0) + return -1; + + if (conn_in_server_mode) { + own_link_cert = tor_tls_get_own_cert(conn->tls); + } + tor_assert(id_cert); + + certs_cell = certs_cell_new(); + + /* Start adding certs. First the link cert or auth1024 cert. */ + if (conn_in_server_mode) { + tor_assert_nonfatal(own_link_cert); + add_x509_cert(certs_cell, + OR_CERT_TYPE_TLS_LINK, own_link_cert); + } else { + tor_assert(global_link_cert); + add_x509_cert(certs_cell, + OR_CERT_TYPE_AUTH_1024, global_link_cert); + } + + /* Next the RSA->RSA ID cert */ + add_x509_cert(certs_cell, + OR_CERT_TYPE_ID_1024, id_cert); + + /* Next the Ed25519 certs */ + add_ed25519_cert(certs_cell, + CERTTYPE_ED_ID_SIGN, + get_master_signing_key_cert()); + if (conn_in_server_mode) { + tor_assert_nonfatal(conn->handshake_state->own_link_cert || + certs_cell_ed25519_disabled_for_testing); + add_ed25519_cert(certs_cell, + CERTTYPE_ED_SIGN_LINK, + conn->handshake_state->own_link_cert); + } else { + add_ed25519_cert(certs_cell, + CERTTYPE_ED_SIGN_AUTH, + get_current_auth_key_cert()); + } + + /* And finally the crosscert. */ + { + const uint8_t *crosscert=NULL; + size_t crosscert_len; + get_master_rsa_crosscert(&crosscert, &crosscert_len); + if (crosscert) { + add_certs_cell_cert_helper(certs_cell, + CERTTYPE_RSA1024_ID_EDID, + crosscert, crosscert_len); + } + } + + /* We've added all the certs; make the cell. */ + certs_cell->n_certs = certs_cell_getlen_certs(certs_cell); + + ssize_t alloc_len = certs_cell_encoded_len(certs_cell); + tor_assert(alloc_len >= 0 && alloc_len <= UINT16_MAX); + cell = var_cell_new(alloc_len); + cell->command = CELL_CERTS; + ssize_t enc_len = certs_cell_encode(cell->payload, alloc_len, certs_cell); + tor_assert(enc_len > 0 && enc_len <= alloc_len); + cell->payload_len = enc_len; + + connection_or_write_var_cell_to_buf(cell, conn); + var_cell_free(cell); + certs_cell_free(certs_cell); + tor_x509_cert_free(own_link_cert); + + return 0; +} + +#ifdef TOR_UNIT_TESTS +int testing__connection_or_pretend_TLSSECRET_is_supported = 0; +#else +#define testing__connection_or_pretend_TLSSECRET_is_supported 0 +#endif + +/** Return true iff <b>challenge_type</b> is an AUTHCHALLENGE type that + * we can send and receive. */ +int +authchallenge_type_is_supported(uint16_t challenge_type) +{ + switch (challenge_type) { + case AUTHTYPE_RSA_SHA256_TLSSECRET: +#ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS + return 1; +#else + return testing__connection_or_pretend_TLSSECRET_is_supported; +#endif + case AUTHTYPE_ED25519_SHA256_RFC5705: + return 1; + case AUTHTYPE_RSA_SHA256_RFC5705: + default: + return 0; + } +} + +/** Return true iff <b>challenge_type_a</b> is one that we would rather + * use than <b>challenge_type_b</b>. */ +int +authchallenge_type_is_better(uint16_t challenge_type_a, + uint16_t challenge_type_b) +{ + /* Any supported type is better than an unsupported one; + * all unsupported types are equally bad. */ + if (!authchallenge_type_is_supported(challenge_type_a)) + return 0; + if (!authchallenge_type_is_supported(challenge_type_b)) + return 1; + /* It happens that types are superior in numerically ascending order. + * If that ever changes, this must change too. */ + return (challenge_type_a > challenge_type_b); +} + +/** Send an AUTH_CHALLENGE cell on the connection <b>conn</b>. Return 0 + * on success, -1 on failure. */ +int +connection_or_send_auth_challenge_cell(or_connection_t *conn) +{ + var_cell_t *cell = NULL; + int r = -1; + tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); + + if (! conn->handshake_state) + return -1; + + auth_challenge_cell_t *ac = auth_challenge_cell_new(); + + tor_assert(sizeof(ac->challenge) == 32); + crypto_rand((char*)ac->challenge, sizeof(ac->challenge)); + + if (authchallenge_type_is_supported(AUTHTYPE_RSA_SHA256_TLSSECRET)) + auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET); + /* Disabled, because everything that supports this method also supports + * the much-superior ED25519_SHA256_RFC5705 */ + /* auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_RFC5705); */ + if (authchallenge_type_is_supported(AUTHTYPE_ED25519_SHA256_RFC5705)) + auth_challenge_cell_add_methods(ac, AUTHTYPE_ED25519_SHA256_RFC5705); + auth_challenge_cell_set_n_methods(ac, + auth_challenge_cell_getlen_methods(ac)); + + cell = var_cell_new(auth_challenge_cell_encoded_len(ac)); + ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len, + ac); + if (len != cell->payload_len) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Encoded auth challenge cell length not as expected"); + goto done; + /* LCOV_EXCL_STOP */ + } + cell->command = CELL_AUTH_CHALLENGE; + + connection_or_write_var_cell_to_buf(cell, conn); + r = 0; + + done: + var_cell_free(cell); + auth_challenge_cell_free(ac); + + return r; +} + +/** Compute the main body of an AUTHENTICATE cell that a client can use + * to authenticate itself on a v3 handshake for <b>conn</b>. Return it + * in a var_cell_t. + * + * If <b>server</b> is true, only calculate the first + * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's + * determined by the rest of the handshake, and which match the provided value + * exactly. + * + * If <b>server</b> is false and <b>signing_key</b> is NULL, calculate the + * first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything + * that should be signed), but don't actually sign it. + * + * If <b>server</b> is false and <b>signing_key</b> is provided, calculate the + * entire authenticator, signed with <b>signing_key</b>. + * + * Return the length of the cell body on success, and -1 on failure. + */ +var_cell_t * +connection_or_compute_authenticate_cell_body(or_connection_t *conn, + const int authtype, + crypto_pk_t *signing_key, + const ed25519_keypair_t *ed_signing_key, + int server) +{ + auth1_t *auth = NULL; + auth_ctx_t *ctx = auth_ctx_new(); + var_cell_t *result = NULL; + int old_tlssecrets_algorithm = 0; + const char *authtype_str = NULL; + + int is_ed = 0; + + /* assert state is reasonable XXXX */ + switch (authtype) { + case AUTHTYPE_RSA_SHA256_TLSSECRET: + authtype_str = "AUTH0001"; + old_tlssecrets_algorithm = 1; + break; + case AUTHTYPE_RSA_SHA256_RFC5705: + authtype_str = "AUTH0002"; + break; + case AUTHTYPE_ED25519_SHA256_RFC5705: + authtype_str = "AUTH0003"; + is_ed = 1; + break; + default: + tor_assert(0); + break; + } + + auth = auth1_new(); + ctx->is_ed = is_ed; + + /* Type: 8 bytes. */ + memcpy(auth1_getarray_type(auth), authtype_str, 8); + + { + const tor_x509_cert_t *id_cert=NULL; + const common_digests_t *my_digests, *their_digests; + const uint8_t *my_id, *their_id, *client_id, *server_id; + if (tor_tls_get_my_certs(server, NULL, &id_cert)) + goto err; + my_digests = tor_x509_cert_get_id_digests(id_cert); + their_digests = + tor_x509_cert_get_id_digests(conn->handshake_state->certs->id_cert); + tor_assert(my_digests); + tor_assert(their_digests); + my_id = (uint8_t*)my_digests->d[DIGEST_SHA256]; + their_id = (uint8_t*)their_digests->d[DIGEST_SHA256]; + + client_id = server ? their_id : my_id; + server_id = server ? my_id : their_id; + + /* Client ID digest: 32 octets. */ + memcpy(auth->cid, client_id, 32); + + /* Server ID digest: 32 octets. */ + memcpy(auth->sid, server_id, 32); + } + + if (is_ed) { + const ed25519_public_key_t *my_ed_id, *their_ed_id; + if (!conn->handshake_state->certs->ed_id_sign) { + log_warn(LD_OR, "Ed authenticate without Ed ID cert from peer."); + goto err; + } + my_ed_id = get_master_identity_key(); + their_ed_id = &conn->handshake_state->certs->ed_id_sign->signing_key; + + const uint8_t *cid_ed = (server ? their_ed_id : my_ed_id)->pubkey; + const uint8_t *sid_ed = (server ? my_ed_id : their_ed_id)->pubkey; + + memcpy(auth->u1_cid_ed, cid_ed, ED25519_PUBKEY_LEN); + memcpy(auth->u1_sid_ed, sid_ed, ED25519_PUBKEY_LEN); + } + + { + crypto_digest_t *server_d, *client_d; + if (server) { + server_d = conn->handshake_state->digest_sent; + client_d = conn->handshake_state->digest_received; + } else { + client_d = conn->handshake_state->digest_sent; + server_d = conn->handshake_state->digest_received; + } + + /* Server log digest : 32 octets */ + crypto_digest_get_digest(server_d, (char*)auth->slog, 32); + + /* Client log digest : 32 octets */ + crypto_digest_get_digest(client_d, (char*)auth->clog, 32); + } + + { + /* Digest of cert used on TLS link : 32 octets. */ + tor_x509_cert_t *cert = NULL; + if (server) { + cert = tor_tls_get_own_cert(conn->tls); + } else { + cert = tor_tls_get_peer_cert(conn->tls); + } + if (!cert) { + log_warn(LD_OR, "Unable to find cert when making %s data.", + authtype_str); + goto err; + } + + memcpy(auth->scert, + tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); + + tor_x509_cert_free(cert); + } + + /* HMAC of clientrandom and serverrandom using master key : 32 octets */ + if (old_tlssecrets_algorithm) { + if (tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets) < 0) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, "Somebody asked us for an older TLS " + "authentication method (AUTHTYPE_RSA_SHA256_TLSSECRET) " + "which we don't support."); + } + } else { + char label[128]; + tor_snprintf(label, sizeof(label), + "EXPORTER FOR TOR TLS CLIENT BINDING %s", authtype_str); + int r = tor_tls_export_key_material(conn->tls, auth->tlssecrets, + auth->cid, sizeof(auth->cid), + label); + if (r < 0) { + if (r != -2) + log_warn(LD_BUG, "TLS key export failed for unknown reason."); + // If r == -2, this was openssl bug 7712. + goto err; + } + } + + /* 8 octets were reserved for the current time, but we're trying to get out + * of the habit of sending time around willynilly. Fortunately, nothing + * checks it. That's followed by 16 bytes of nonce. */ + crypto_rand((char*)auth->rand, 24); + + ssize_t maxlen = auth1_encoded_len(auth, ctx); + if (ed_signing_key && is_ed) { + maxlen += ED25519_SIG_LEN; + } else if (signing_key && !is_ed) { + maxlen += crypto_pk_keysize(signing_key); + } + + const int AUTH_CELL_HEADER_LEN = 4; /* 2 bytes of type, 2 bytes of length */ + result = var_cell_new(AUTH_CELL_HEADER_LEN + maxlen); + uint8_t *const out = result->payload + AUTH_CELL_HEADER_LEN; + const size_t outlen = maxlen; + ssize_t len; + + result->command = CELL_AUTHENTICATE; + set_uint16(result->payload, htons(authtype)); + + if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to encode signed part of AUTH1 data."); + goto err; + /* LCOV_EXCL_STOP */ + } + + if (server) { + auth1_t *tmp = NULL; + ssize_t len2 = auth1_parse(&tmp, out, len, ctx); + if (!tmp) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to parse signed part of AUTH1 data that " + "we just encoded"); + goto err; + /* LCOV_EXCL_STOP */ + } + result->payload_len = (tmp->end_of_signed - result->payload); + + auth1_free(tmp); + if (len2 != len) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Mismatched length when re-parsing AUTH1 data."); + goto err; + /* LCOV_EXCL_STOP */ + } + goto done; + } + + if (ed_signing_key && is_ed) { + ed25519_signature_t sig; + if (ed25519_sign(&sig, out, len, ed_signing_key) < 0) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to sign ed25519 authentication data"); + goto err; + /* LCOV_EXCL_STOP */ + } + auth1_setlen_sig(auth, ED25519_SIG_LEN); + memcpy(auth1_getarray_sig(auth), sig.sig, ED25519_SIG_LEN); + + } else if (signing_key && !is_ed) { + auth1_setlen_sig(auth, crypto_pk_keysize(signing_key)); + + char d[32]; + crypto_digest256(d, (char*)out, len, DIGEST_SHA256); + int siglen = crypto_pk_private_sign(signing_key, + (char*)auth1_getarray_sig(auth), + auth1_getlen_sig(auth), + d, 32); + if (siglen < 0) { + log_warn(LD_OR, "Unable to sign AUTH1 data."); + goto err; + } + + auth1_setlen_sig(auth, siglen); + } + + len = auth1_encode(out, outlen, auth, ctx); + if (len < 0) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to encode signed AUTH1 data."); + goto err; + /* LCOV_EXCL_STOP */ + } + tor_assert(len + AUTH_CELL_HEADER_LEN <= result->payload_len); + result->payload_len = len + AUTH_CELL_HEADER_LEN; + set_uint16(result->payload+2, htons(len)); + + goto done; + + err: + var_cell_free(result); + result = NULL; + done: + auth1_free(auth); + auth_ctx_free(ctx); + return result; +} + +/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on + * success, -1 on failure */ +MOCK_IMPL(int, +connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype)) +{ + var_cell_t *cell; + crypto_pk_t *pk = tor_tls_get_my_client_auth_key(); + /* XXXX make sure we're actually supposed to send this! */ + + if (!pk) { + log_warn(LD_BUG, "Can't compute authenticate cell: no client auth key"); + return -1; + } + if (! authchallenge_type_is_supported(authtype)) { + log_warn(LD_BUG, "Tried to send authenticate cell with unknown " + "authentication type %d", authtype); + return -1; + } + + cell = connection_or_compute_authenticate_cell_body(conn, + authtype, + pk, + get_current_auth_keypair(), + 0 /* not server */); + if (! cell) { + log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unable to compute authenticate cell!"); + return -1; + } + connection_or_write_var_cell_to_buf(cell, conn); + var_cell_free(cell); + + return 0; +} diff --git a/src/feature/relay/relay_handshake.h b/src/feature/relay/relay_handshake.h new file mode 100644 index 0000000000..99a658cbcc --- /dev/null +++ b/src/feature/relay/relay_handshake.h @@ -0,0 +1,90 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file relay_handshake.h + * @brief Header for feature/relay/relay_handshake.c + **/ + +#ifndef TOR_CORE_OR_RELAY_HANDSHAKE_H +#define TOR_CORE_OR_RELAY_HANDSHAKE_H + +#ifdef HAVE_MODULE_RELAY +struct ed25519_keypair_t; + +int connection_or_send_certs_cell(or_connection_t *conn); +int connection_or_send_auth_challenge_cell(or_connection_t *conn); + +var_cell_t *connection_or_compute_authenticate_cell_body( + or_connection_t *conn, + const int authtype, + crypto_pk_t *signing_key, + const struct ed25519_keypair_t *ed_signing_key, + int server); + +int authchallenge_type_is_supported(uint16_t challenge_type); +int authchallenge_type_is_better(uint16_t challenge_type_a, + uint16_t challenge_type_b); + +MOCK_DECL(int,connection_or_send_authenticate_cell, + (or_connection_t *conn, int type)); + +#ifdef TOR_UNIT_TESTS +extern int certs_cell_ed25519_disabled_for_testing; +#endif +#else /* !defined(HAVE_MODULE_RELAY) */ + +static inline int +connection_or_send_certs_cell(or_connection_t *conn) +{ + (void)conn; + tor_assert_nonfatal_unreached(); + return -1; +} +static inline int +connection_or_send_auth_challenge_cell(or_connection_t *conn) +{ + (void)conn; + tor_assert_nonfatal_unreached(); + return -1; +} + +static inline var_cell_t * +connection_or_compute_authenticate_cell_body( + or_connection_t *conn, + const int authtype, + crypto_pk_t *signing_key, + const struct ed25519_keypair_t *ed_signing_key, + int server) +{ + (void)conn; + (void)authtype; + (void)signing_key; + (void)ed_signing_key; + (void)server; + tor_assert_nonfatal_unreached(); + return NULL; +} + +#define authchallenge_type_is_supported(t) (0) +#define authchallenge_type_is_better(a, b) (0) + +static inline int +connection_or_send_authenticate_cell(or_connection_t *conn, int type) +{ + (void)conn; + (void)type; + tor_assert_nonfatal_unreached(); + return -1; +} + +#ifdef TOR_UNIT_TESTS +extern int certs_cell_ed25519_disabled_for_testing; +#endif + +#endif /* defined(HAVE_MODULE_RELAY) */ + +#endif /* !defined(TOR_CORE_OR_RELAY_HANDSHAKE_H) */ diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index e24e499971..9b62bb385c 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -748,8 +748,8 @@ v3_authority_check_key_expiry(void) } /** Get the lifetime of an onion key in days. This value is defined by the - * network consesus parameter "onion-key-rotation-days". Always returns a value - * between <b>MIN_ONION_KEY_LIFETIME_DAYS</b> and + * network consensus parameter "onion-key-rotation-days". Always returns a + * value between <b>MIN_ONION_KEY_LIFETIME_DAYS</b> and * <b>MAX_ONION_KEY_LIFETIME_DAYS</b>. */ static int @@ -763,7 +763,7 @@ get_onion_key_rotation_days_(void) } /** Get the current lifetime of an onion key in seconds. This value is defined - * by the network consesus parameter "onion-key-rotation-days", but the value + * by the network consensus parameter "onion-key-rotation-days", but the value * is converted to seconds. */ int @@ -773,7 +773,7 @@ get_onion_key_lifetime(void) } /** Get the grace period of an onion key in seconds. This value is defined by - * the network consesus parameter "onion-key-grace-period-days", but the value + * the network consensus parameter "onion-key-grace-period-days", but the value * is converted to seconds. */ int @@ -1446,6 +1446,50 @@ router_get_advertised_or_port_by_af(const or_options_t *options, return port; } +/** As router_get_advertised_or_port(), but returns the IPv6 address and + * port in ipv6_ap_out, which must not be NULL. Returns a null address and + * zero port, if no ORPort is found. */ +void +router_get_advertised_ipv6_or_ap(const or_options_t *options, + tor_addr_port_t *ipv6_ap_out) +{ + /* Bug in calling function, we can't return a sensible result, and it + * shouldn't use the NULL pointer once we return. */ + tor_assert(ipv6_ap_out); + + /* If there is no valid IPv6 ORPort, return a null address and port. */ + tor_addr_make_null(&ipv6_ap_out->addr, AF_INET6); + ipv6_ap_out->port = 0; + + const tor_addr_t *addr = get_first_advertised_addr_by_type_af( + CONN_TYPE_OR_LISTENER, + AF_INET6); + const uint16_t port = router_get_advertised_or_port_by_af( + options, + AF_INET6); + + if (!addr || port == 0) { + log_info(LD_CONFIG, "There is no advertised IPv6 ORPort."); + return; + } + + /* If the relay is configured using the default authorities, disallow + * internal IPs. Otherwise, allow them. For IPv4 ORPorts and DirPorts, + * this check is done in resolve_my_address(). See #33681. */ + const int default_auth = using_default_dir_authorities(options); + if (tor_addr_is_internal(addr, 0) && default_auth) { + log_warn(LD_CONFIG, + "Unable to use configured IPv6 ORPort \"%s\" in a " + "descriptor. Skipping it. " + "Try specifying a globally reachable address explicitly.", + fmt_addrport(addr, port)); + return; + } + + tor_addr_copy(&ipv6_ap_out->addr, addr); + ipv6_ap_out->port = port; +} + /** Return the port that we should advertise as our DirPort; * this is one of three possibilities: * The one that is passed as <b>dirport</b> if the DirPort option is 0, or @@ -1990,34 +2034,11 @@ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out)) sizeof(curve25519_public_key_t)); /* For now, at most one IPv6 or-address is being advertised. */ - { - const port_cfg_t *ipv6_orport = NULL; - SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) { - if (p->type == CONN_TYPE_OR_LISTENER && - ! p->server_cfg.no_advertise && - ! p->server_cfg.bind_ipv4_only && - tor_addr_family(&p->addr) == AF_INET6) { - /* Like IPv4, if the relay is configured using the default - * authorities, disallow internal IPs. Otherwise, allow them. */ - const int default_auth = using_default_dir_authorities(options); - if (! tor_addr_is_internal(&p->addr, 0) || ! default_auth) { - ipv6_orport = p; - break; - } else { - char addrbuf[TOR_ADDR_BUF_LEN]; - log_warn(LD_CONFIG, - "Unable to use configured IPv6 address \"%s\" in a " - "descriptor. Skipping it. " - "Try specifying a globally reachable address explicitly.", - tor_addr_to_str(addrbuf, &p->addr, sizeof(addrbuf), 1)); - } - } - } SMARTLIST_FOREACH_END(p); - if (ipv6_orport) { - tor_addr_copy(&ri->ipv6_addr, &ipv6_orport->addr); - ri->ipv6_orport = ipv6_orport->port; - } - } + tor_addr_port_t ipv6_orport; + router_get_advertised_ipv6_or_ap(options, &ipv6_orport); + /* If there is no valud IPv6 ORPort, the address and port are null. */ + tor_addr_copy(&ri->ipv6_addr, &ipv6_orport.addr); + ri->ipv6_orport = ipv6_orport.port; ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key()); if (BUG(crypto_pk_get_digest(ri->identity_pkey, diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h index 782609d8ab..d1b4ce5f8f 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -66,6 +66,8 @@ int init_keys_client(void); uint16_t router_get_active_listener_port_by_type_af(int listener_type, sa_family_t family); uint16_t router_get_advertised_or_port(const or_options_t *options); +void router_get_advertised_ipv6_or_ap(const or_options_t *options, + tor_addr_port_t *ipv6_ap_out); uint16_t router_get_advertised_or_port_by_af(const or_options_t *options, sa_family_t family); uint16_t router_get_advertised_dir_port(const or_options_t *options, @@ -117,7 +119,6 @@ const char *routerinfo_err_to_string(int err); int routerinfo_err_is_transient(int err); void router_reset_warnings(void); -void router_reset_reachability(void); void router_free_all(void); #ifdef ROUTER_PRIVATE diff --git a/src/feature/relay/routerkeys.h b/src/feature/relay/routerkeys.h index c2475f195f..1fb5d724e9 100644 --- a/src/feature/relay/routerkeys.h +++ b/src/feature/relay/routerkeys.h @@ -11,6 +11,8 @@ #include "lib/crypt_ops/crypto_ed25519.h" +#ifdef HAVE_MODULE_RELAY + const ed25519_public_key_t *get_master_identity_key(void); MOCK_DECL(const ed25519_keypair_t *, get_master_signing_keypair,(void)); MOCK_DECL(const struct tor_cert_st *, get_master_signing_key_cert,(void)); @@ -24,6 +26,7 @@ void get_master_rsa_crosscert(const uint8_t **cert_out, int router_ed25519_id_is_me(const ed25519_public_key_t *id); +/* These are only used by router.c */ struct tor_cert_st *make_ntor_onion_key_crosscert( const curve25519_keypair_t *onion_key, const ed25519_public_key_t *master_id_key, @@ -42,6 +45,85 @@ int generate_ed_link_cert(const or_options_t *options, time_t now, int force); void routerkeys_free_all(void); +#else /* !defined(HAVE_MODULE_RELAY) */ + +#define router_ed25519_id_is_me(id) \ + ((void)(id), 0) + +static inline void * +relay_key_is_unavailable_(void) +{ + tor_assert_nonfatal_unreached(); + return NULL; +} +#define relay_key_is_unavailable(type) \ + ((type)(relay_key_is_unavailable_())) + +// Many of these can be removed once relay_handshake.c is relay-only. +#define get_current_auth_keypair() \ + relay_key_is_unavailable(const ed25519_keypair_t *) +#define get_master_signing_keypair() \ + relay_key_is_unavailable(const ed25519_keypair_t *) +#define get_current_link_cert_cert() \ + relay_key_is_unavailable(const struct tor_cert_st *) +#define get_current_auth_key_cert() \ + relay_key_is_unavailable(const struct tor_cert_st *) +#define get_master_signing_key_cert() \ + relay_key_is_unavailable(const struct tor_cert_st *) +#define get_master_rsa_crosscert(cert_out, size_out) \ + STMT_BEGIN \ + tor_assert_nonfatal_unreached(); \ + *(cert_out) = NULL; \ + *(size_out) = 0; \ + STMT_END +#define get_master_identity_key() \ + relay_key_is_unavailable(const ed25519_public_key_t *) + +#define generate_ed_link_cert(options, now, force) \ + ((void)(options), (void)(now), (void)(force), 0) +#define should_make_new_ed_keys(options, now) \ + ((void)(options), (void)(now), 0) + +// These can get removed once router.c becomes relay-only. +static inline struct tor_cert_st * +make_ntor_onion_key_crosscert(const curve25519_keypair_t *onion_key, + const ed25519_public_key_t *master_id_key, + time_t now, time_t lifetime, + int *sign_out) +{ + (void)onion_key; + (void)master_id_key; + (void)now; + (void)lifetime; + *sign_out = 0; + tor_assert_nonfatal_unreached(); + return NULL; +} +static inline uint8_t * +make_tap_onion_key_crosscert(const crypto_pk_t *onion_key, + const ed25519_public_key_t *master_id_key, + const crypto_pk_t *rsa_id_key, + int *len_out) +{ + (void)onion_key; + (void)master_id_key; + (void)rsa_id_key; + *len_out = 0; + tor_assert_nonfatal_unreached(); + return NULL; +} + +/* This calls is used outside of relay mode, but only to implement + * CMD_KEY_EXPIRATION */ +#define log_cert_expiration() \ + (puts("Not available: Tor has been compiled without relay support"), 0) +/* This calls is used outside of relay mode, but only to implement + * CMD_KEYGEN. */ +#define load_ed_keys(x,y) \ + (puts("Not available: Tor has been compiled without relay support"), 0) + +#endif /* defined(HAVE_MODULE_RELAY) */ + #ifdef TOR_UNIT_TESTS const ed25519_keypair_t *get_master_identity_keypair(void); void init_mock_ed_keys(const crypto_pk_t *rsa_identity_key); diff --git a/src/feature/relay/selftest.h b/src/feature/relay/selftest.h index 94f305f203..f3dd698bb7 100644 --- a/src/feature/relay/selftest.h +++ b/src/feature/relay/selftest.h @@ -12,6 +12,7 @@ #ifndef TOR_SELFTEST_H #define TOR_SELFTEST_H +#ifdef HAVE_MODULE_RELAY struct or_options_t; int check_whether_orport_reachable(const struct or_options_t *options); int check_whether_dirport_reachable(const struct or_options_t *options); @@ -20,5 +21,37 @@ void router_do_reachability_checks(int test_or, int test_dir); void router_orport_found_reachable(void); void router_dirport_found_reachable(void); void router_perform_bandwidth_test(int num_circs, time_t now); +void router_reset_reachability(void); + +#else /* !defined(HAVE_MODULE_RELAY) */ + +#define check_whether_orport_reachable(opts) \ + ((void)(opts), 0) +#define check_whether_dirport_reachable(opts) \ + ((void)(opts), 0) + +#define router_orport_found_reachable() \ + STMT_NIL +#define router_dirport_found_reachable() \ + STMT_NIL +#define router_reset_reachability() \ + STMT_NIL + +static inline void +router_do_reachability_checks(int test_or, int test_dir) +{ + (void)test_or; + (void)test_dir; + tor_assert_nonfatal_unreached(); +} +static inline void +router_perform_bandwidth_test(int num_circs, time_t now) +{ + (void)num_circs; + (void)now; + tor_assert_nonfatal_unreached(); +} + +#endif /* defined(HAVE_MODULE_RELAY) */ #endif /* !defined(TOR_SELFTEST_H) */ diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c index 182e935fa1..10a3403166 100644 --- a/src/feature/rend/rendservice.c +++ b/src/feature/rend/rendservice.c @@ -49,6 +49,7 @@ #include "core/or/crypt_path_reference_st.h" #include "core/or/edge_connection_st.h" #include "core/or/extend_info_st.h" +#include "feature/hs/hs_opts_st.h" #include "feature/nodelist/networkstatus_st.h" #include "core/or/origin_circuit_st.h" #include "feature/rend/rend_authorized_client_st.h" @@ -714,22 +715,20 @@ service_config_shadow_copy(rend_service_t *service, config->ports = NULL; } -/* Parse the hidden service configuration starting at <b>line_</b> using the +/* Parse the hidden service configuration from <b>hs_opts</b> using the * already configured generic service configuration in <b>config</b>. This * function will translate the config object to a rend_service_t and add it to * the temporary list if valid. If <b>validate_only</b> is set, parse, warn * and return as normal but don't actually add the service to the list. */ int -rend_config_service(const config_line_t *line_, +rend_config_service(const hs_opts_t *hs_opts, const or_options_t *options, hs_service_config_t *config) { - const config_line_t *line; rend_service_t *service = NULL; - /* line_ can be NULL which would mean that the service configuration only - * have one line that is the directory directive. */ tor_assert(options); + tor_assert(hs_opts); tor_assert(config); /* Use the staging service list so that we can check then do the pruning @@ -746,126 +745,109 @@ rend_config_service(const config_line_t *line_, * options, we'll copy over the useful data to the rend_service_t object. */ service_config_shadow_copy(service, config); - for (line = line_; line; line = line->next) { - if (!strcasecmp(line->key, "HiddenServiceDir")) { - /* We just hit the next hidden service, stop right now. */ - break; + /* Number of introduction points. */ + if (hs_opts->HiddenServiceNumIntroductionPoints > NUM_INTRO_POINTS_MAX) { + log_warn(LD_CONFIG, "HiddenServiceNumIntroductionPoints must be " + "between 0 and %d, not %d.", + NUM_INTRO_POINTS_MAX, + hs_opts->HiddenServiceNumIntroductionPoints); + goto err; + } + service->n_intro_points_wanted = hs_opts->HiddenServiceNumIntroductionPoints; + log_info(LD_CONFIG, "HiddenServiceNumIntroductionPoints=%d for %s", + service->n_intro_points_wanted, escaped(service->directory)); + + /* Client authorization */ + if (hs_opts->HiddenServiceAuthorizeClient) { + /* Parse auth type and comma-separated list of client names and add a + * rend_authorized_client_t for each client to the service's list + * of authorized clients. */ + smartlist_t *type_names_split, *clients; + const char *authname; + type_names_split = smartlist_new(); + smartlist_split_string(type_names_split, + hs_opts->HiddenServiceAuthorizeClient, " ", 0, 2); + if (smartlist_len(type_names_split) < 1) { + log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This " + "should have been prevented when parsing the " + "configuration."); + smartlist_free(type_names_split); + goto err; } - /* Number of introduction points. */ - if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) { - int ok = 0; - /* Those are specific defaults for version 2. */ - service->n_intro_points_wanted = - (unsigned int) tor_parse_long(line->value, 10, - 0, NUM_INTRO_POINTS_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, - "HiddenServiceNumIntroductionPoints " - "should be between %d and %d, not %s", - 0, NUM_INTRO_POINTS_MAX, line->value); - goto err; - } - log_info(LD_CONFIG, "HiddenServiceNumIntroductionPoints=%d for %s", - service->n_intro_points_wanted, escaped(service->directory)); - continue; + authname = smartlist_get(type_names_split, 0); + if (!strcasecmp(authname, "basic")) { + service->auth_type = REND_BASIC_AUTH; + } else if (!strcasecmp(authname, "stealth")) { + service->auth_type = REND_STEALTH_AUTH; + } else { + log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " + "unrecognized auth-type '%s'. Only 'basic' or 'stealth' " + "are recognized.", + (char *) smartlist_get(type_names_split, 0)); + SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); + smartlist_free(type_names_split); + goto err; } - if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) { - /* Parse auth type and comma-separated list of client names and add a - * rend_authorized_client_t for each client to the service's list - * of authorized clients. */ - smartlist_t *type_names_split, *clients; - const char *authname; - if (service->auth_type != REND_NO_AUTH) { - log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient " - "lines for a single service."); - goto err; - } - type_names_split = smartlist_new(); - smartlist_split_string(type_names_split, line->value, " ", 0, 2); - if (smartlist_len(type_names_split) < 1) { - log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This " - "should have been prevented when parsing the " - "configuration."); - smartlist_free(type_names_split); - goto err; - } - authname = smartlist_get(type_names_split, 0); - if (!strcasecmp(authname, "basic")) { - service->auth_type = REND_BASIC_AUTH; - } else if (!strcasecmp(authname, "stealth")) { - service->auth_type = REND_STEALTH_AUTH; - } else { - log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " - "unrecognized auth-type '%s'. Only 'basic' or 'stealth' " - "are recognized.", - (char *) smartlist_get(type_names_split, 0)); - SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); - smartlist_free(type_names_split); - goto err; - } - service->clients = smartlist_new(); - if (smartlist_len(type_names_split) < 2) { - log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " - "auth-type '%s', but no client names.", - service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth"); - SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); - smartlist_free(type_names_split); - continue; - } - clients = smartlist_new(); - smartlist_split_string(clients, smartlist_get(type_names_split, 1), - ",", SPLIT_SKIP_SPACE, 0); + service->clients = smartlist_new(); + if (smartlist_len(type_names_split) < 2) { + log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " + "auth-type '%s', but no client names.", + service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth"); SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); smartlist_free(type_names_split); - /* Remove duplicate client names. */ - { - int num_clients = smartlist_len(clients); - smartlist_sort_strings(clients); - smartlist_uniq_strings(clients); - if (smartlist_len(clients) < num_clients) { - log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d " - "duplicate client name(s); removing.", - num_clients - smartlist_len(clients)); - } - } - SMARTLIST_FOREACH_BEGIN(clients, const char *, client_name) - { - rend_authorized_client_t *client; - if (!rend_valid_client_name(client_name)) { - log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an " - "illegal client name: '%s'. Names must be " - "between 1 and %d characters and contain " - "only [A-Za-z0-9+_-].", - client_name, REND_CLIENTNAME_MAX_LEN); - SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp)); - smartlist_free(clients); - goto err; - } - client = tor_malloc_zero(sizeof(rend_authorized_client_t)); - client->client_name = tor_strdup(client_name); - smartlist_add(service->clients, client); - log_debug(LD_REND, "Adding client name '%s'", client_name); + goto err; + } + clients = smartlist_new(); + smartlist_split_string(clients, smartlist_get(type_names_split, 1), + ",", SPLIT_SKIP_SPACE, 0); + SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); + smartlist_free(type_names_split); + /* Remove duplicate client names. */ + { + int num_clients = smartlist_len(clients); + smartlist_sort_strings(clients); + smartlist_uniq_strings(clients); + if (smartlist_len(clients) < num_clients) { + log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d " + "duplicate client name(s); removing.", + num_clients - smartlist_len(clients)); } - SMARTLIST_FOREACH_END(client_name); - SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp)); - smartlist_free(clients); - /* Ensure maximum number of clients. */ - if ((service->auth_type == REND_BASIC_AUTH && - smartlist_len(service->clients) > 512) || - (service->auth_type == REND_STEALTH_AUTH && - smartlist_len(service->clients) > 16)) { - log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d " - "client authorization entries, but only a " - "maximum of %d entries is allowed for " - "authorization type '%s'.", - smartlist_len(service->clients), - service->auth_type == REND_BASIC_AUTH ? 512 : 16, - service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth"); + } + SMARTLIST_FOREACH_BEGIN(clients, const char *, client_name) { + rend_authorized_client_t *client; + if (!rend_valid_client_name(client_name)) { + log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an " + "illegal client name: '%s'. Names must be " + "between 1 and %d characters and contain " + "only [A-Za-z0-9+_-].", + client_name, REND_CLIENTNAME_MAX_LEN); + SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp)); + smartlist_free(clients); goto err; } - continue; + client = tor_malloc_zero(sizeof(rend_authorized_client_t)); + client->client_name = tor_strdup(client_name); + smartlist_add(service->clients, client); + log_debug(LD_REND, "Adding client name '%s'", client_name); + } SMARTLIST_FOREACH_END(client_name); + SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp)); + smartlist_free(clients); + /* Ensure maximum number of clients. */ + if ((service->auth_type == REND_BASIC_AUTH && + smartlist_len(service->clients) > 512) || + (service->auth_type == REND_STEALTH_AUTH && + smartlist_len(service->clients) > 16)) { + log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d " + "client authorization entries, but only a " + "maximum of %d entries is allowed for " + "authorization type '%s'.", + smartlist_len(service->clients), + service->auth_type == REND_BASIC_AUTH ? 512 : 16, + service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth"); + goto err; } } + /* Validate the service just parsed. */ if (rend_validate_service(rend_service_staging_list, service) < 0) { /* Service is in the staging list so don't try to free it. */ diff --git a/src/feature/rend/rendservice.h b/src/feature/rend/rendservice.h index 8202c4fcd3..012afc0f9f 100644 --- a/src/feature/rend/rendservice.h +++ b/src/feature/rend/rendservice.h @@ -139,7 +139,8 @@ STATIC void rend_service_prune_list_impl_(void); #endif /* defined(RENDSERVICE_PRIVATE) */ int rend_num_services(void); -int rend_config_service(const struct config_line_t *line_, +struct hs_opts_t; +int rend_config_service(const struct hs_opts_t *hs_opts, const or_options_t *options, hs_service_config_t *config); void rend_service_prune_list(void); diff --git a/src/feature/stats/geoip_stats.c b/src/feature/stats/geoip_stats.c index 3228b18973..f9a2f19d2e 100644 --- a/src/feature/stats/geoip_stats.c +++ b/src/feature/stats/geoip_stats.c @@ -146,9 +146,9 @@ clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b) } HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash, - clientmap_entries_eq) + clientmap_entries_eq); HT_GENERATE2(clientmap, clientmap_entry_t, node, clientmap_entry_hash, - clientmap_entries_eq, 0.6, tor_reallocarray_, tor_free_) + clientmap_entries_eq, 0.6, tor_reallocarray_, tor_free_); #define clientmap_entry_free(ent) \ FREE_AND_NULL(clientmap_entry_t, clientmap_entry_free_, ent) @@ -484,9 +484,9 @@ dirreq_map_ent_hash(const dirreq_map_entry_t *entry) } HT_PROTOTYPE(dirreqmap, dirreq_map_entry_t, node, dirreq_map_ent_hash, - dirreq_map_ent_eq) + dirreq_map_ent_eq); HT_GENERATE2(dirreqmap, dirreq_map_entry_t, node, dirreq_map_ent_hash, - dirreq_map_ent_eq, 0.6, tor_reallocarray_, tor_free_) + dirreq_map_ent_eq, 0.6, tor_reallocarray_, tor_free_); /** Helper: Put <b>entry</b> into map of directory requests using * <b>type</b> and <b>dirreq_id</b> as key parts. If there is diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c index d229c755b4..71e2e00086 100644 --- a/src/feature/stats/rephist.c +++ b/src/feature/stats/rephist.c @@ -2285,9 +2285,9 @@ bidi_map_ent_hash(const bidi_map_entry_t *entry) } HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, - bidi_map_ent_eq) + bidi_map_ent_eq); HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, - bidi_map_ent_eq, 0.6, tor_reallocarray_, tor_free_) + bidi_map_ent_eq, 0.6, tor_reallocarray_, tor_free_); /* DOCDOC bidi_map_free */ static void diff --git a/src/include.am b/src/include.am index f5f868d23f..657f6e823a 100644 --- a/src/include.am +++ b/src/include.am @@ -19,6 +19,7 @@ include src/lib/fs/include.am include src/lib/geoip/include.am include src/lib/include.libdonna.am include src/lib/intmath/include.am +include src/lib/llharden/include.am include src/lib/lock/include.am include src/lib/log/include.am include src/lib/math/include.am diff --git a/src/lib/cc/compat_compiler.h b/src/lib/cc/compat_compiler.h index 1bb8c54a0c..e4b0ea61ca 100644 --- a/src/lib/cc/compat_compiler.h +++ b/src/lib/cc/compat_compiler.h @@ -25,11 +25,11 @@ #endif /* defined(__has_feature) */ #ifndef NULL_REP_IS_ZERO_BYTES -#error "It seems your platform does not represent NULL as zero. We can't cope." +#error "Your platform does not represent NULL as zero. We can't cope." #endif #ifndef DOUBLE_0_REP_IS_ZERO_BYTES -#error "It seems your platform does not represent 0.0 as zeros. We can't cope." +#error "Your platform does not represent 0.0 as zeros. We can't cope." #endif #if 'a'!=97 || 'z'!=122 || 'A'!=65 || ' '!=32 diff --git a/src/lib/cc/torint.h b/src/lib/cc/torint.h index cef1482bdc..af7a90431c 100644 --- a/src/lib/cc/torint.h +++ b/src/lib/cc/torint.h @@ -49,7 +49,7 @@ typedef int32_t ssize_t; * aren't 2's complement, and you don't define LONG_MAX, then you're so * bizarre that I want nothing to do with you. */ #ifndef USING_TWOS_COMPLEMENT -#error "Seems that your platform doesn't use 2's complement arithmetic. Argh." +#error "Your platform doesn't use 2's complement arithmetic." #endif #ifndef TIME_MAX @@ -126,12 +126,11 @@ typedef int32_t ssize_t; #define SIZE_T_CEILING ((size_t)(SSIZE_MAX-16)) #if SIZEOF_INT > SIZEOF_VOID_P -#error "sizeof(int) > sizeof(void *) - Tor cannot be built on this platform!" +#error "sizeof(int) > sizeof(void *) - Can't build Tor here." #endif #if SIZEOF_UNSIGNED_INT > SIZEOF_VOID_P -#error "sizeof(unsigned int) > sizeof(void *) - Tor cannot be built on this \ -platform!" +#error "sizeof(unsigned int) > sizeof(void *) - Can't build Tor here." #endif #endif /* !defined(TOR_TORINT_H) */ diff --git a/src/lib/compress/compress.c b/src/lib/compress/compress.c index c62d7d5d2a..84e9601920 100644 --- a/src/lib/compress/compress.c +++ b/src/lib/compress/compress.c @@ -695,6 +695,6 @@ subsys_compress_initialize(void) const subsys_fns_t sys_compress = { .name = "compress", .supported = true, - .level = -70, + .level = -55, .initialize = subsys_compress_initialize, }; diff --git a/src/lib/conf/conftypes.h b/src/lib/conf/conftypes.h index ebc2736aaa..081ebf397f 100644 --- a/src/lib/conf/conftypes.h +++ b/src/lib/conf/conftypes.h @@ -260,6 +260,7 @@ typedef struct config_deprecation_t { const char *why_deprecated; } config_deprecation_t; +#ifndef COCCI /** * Handy macro for declaring "In the config file or on the command line, you * can abbreviate <b>tok</b>s as <b>tok</b>". Used inside an array of @@ -268,7 +269,8 @@ typedef struct config_deprecation_t { * For example, to declare "NumCpu" as an abbreviation for "NumCPUs", * you can say PLURAL(NumCpu). **/ -#define PLURAL(tok) { #tok, #tok "s", 0, 0 } +#define PLURAL(tok) { (#tok), (#tok "s"), 0, 0 } +#endif /* !defined(COCCI) */ /** * Validation function: verify whether a configuation object is well-formed diff --git a/src/lib/confmgt/type_defs.c b/src/lib/confmgt/type_defs.c index 3f66cba61a..d9e5e1e4c2 100644 --- a/src/lib/confmgt/type_defs.c +++ b/src/lib/confmgt/type_defs.c @@ -121,8 +121,9 @@ int_parse(void *target, const char *value, char **errmsg, const void *params) int ok=0; *p = (int)tor_parse_long(value, 10, pp->minval, pp->maxval, &ok, NULL); if (!ok) { - tor_asprintf(errmsg, "Integer %s is malformed or out of bounds.", - value); + tor_asprintf(errmsg, "Integer %s is malformed or out of bounds. " + "Allowed values are between %d and %d.", + value, pp->minval, pp->maxval); return -1; } return 0; @@ -228,11 +229,17 @@ units_parse_u64(void *target, const char *value, char **errmsg, tor_assert(table); uint64_t *v = (uint64_t*)target; int ok=1; - *v = config_parse_units(value, table, &ok); + char *msg = NULL; + *v = config_parse_units(value, table, &ok, &msg); if (!ok) { - *errmsg = tor_strdup("Provided value is malformed or out of bounds."); + tor_asprintf(errmsg, "Provided value is malformed or out of bounds: %s", + msg); + tor_free(msg); return -1; } + if (BUG(msg)) { + tor_free(msg); + } return 0; } @@ -244,11 +251,17 @@ units_parse_int(void *target, const char *value, char **errmsg, tor_assert(table); int *v = (int*)target; int ok=1; - uint64_t u64 = config_parse_units(value, table, &ok); + char *msg = NULL; + uint64_t u64 = config_parse_units(value, table, &ok, &msg); if (!ok) { - *errmsg = tor_strdup("Provided value is malformed or out of bounds."); + tor_asprintf(errmsg, "Provided value is malformed or out of bounds: %s", + msg); + tor_free(msg); return -1; } + if (BUG(msg)) { + tor_free(msg); + } if (u64 > INT_MAX) { tor_asprintf(errmsg, "Provided value %s is too large", value); return -1; @@ -348,11 +361,17 @@ typedef struct enumeration_table_t { int value; } enumeration_table_t; +typedef struct enumeration_params_t { + const char *allowed_val_string; + const enumeration_table_t *table; +} enumeration_params_t; + static int enum_parse(void *target, const char *value, char **errmsg, - const void *params) + const void *params_) { - const enumeration_table_t *table = params; + const enumeration_params_t *params = params_; + const enumeration_table_t *table = params->table; int *p = (int *)target; for (; table->name; ++table) { if (!strcasecmp(value, table->name)) { @@ -360,15 +379,17 @@ enum_parse(void *target, const char *value, char **errmsg, return 0; } } - tor_asprintf(errmsg, "Unrecognized value %s.", value); + tor_asprintf(errmsg, "Unrecognized value %s. %s", + value, params->allowed_val_string); return -1; } static char * -enum_encode(const void *value, const void *params) +enum_encode(const void *value, const void *params_) { int v = *(const int*)value; - const enumeration_table_t *table = params; + const enumeration_params_t *params = params_; + const enumeration_table_t *table = params->table; for (; table->name; ++table) { if (v == table->value) return tor_strdup(table->name); @@ -377,19 +398,21 @@ enum_encode(const void *value, const void *params) } static void -enum_clear(void *value, const void *params) +enum_clear(void *value, const void *params_) { int *p = (int*)value; - const enumeration_table_t *table = params; + const enumeration_params_t *params = params_; + const enumeration_table_t *table = params->table; tor_assert(table->name); *p = table->value; } static bool -enum_ok(const void *value, const void *params) +enum_ok(const void *value, const void *params_) { int v = *(const int*)value; - const enumeration_table_t *table = params; + const enumeration_params_t *params = params_; + const enumeration_table_t *table = params->table; for (; table->name; ++table) { if (v == table->value) return true; @@ -403,6 +426,11 @@ static const enumeration_table_t enum_table_bool[] = { { NULL, 0 }, }; +static const enumeration_params_t enum_params_bool = { + "Allowed values are 0 and 1.", + enum_table_bool +}; + static const enumeration_table_t enum_table_autobool[] = { { "0", 0 }, { "1", 1 }, @@ -410,6 +438,11 @@ static const enumeration_table_t enum_table_autobool[] = { { NULL, 0 }, }; +static const enumeration_params_t enum_params_autobool = { + "Allowed values are 0, 1, and auto.", + enum_table_autobool +}; + static const var_type_fns_t enum_fns = { .parse = enum_parse, .encode = enum_encode, @@ -740,10 +773,10 @@ const var_type_def_t DOUBLE_type_defn = { .name="Float", .fns=&double_fns, }; const var_type_def_t BOOL_type_defn = { .name="Boolean", .fns=&enum_fns, - .params=&enum_table_bool }; + .params=&enum_params_bool }; const var_type_def_t AUTOBOOL_type_defn = { .name="Boolean+Auto", .fns=&enum_fns, - .params=&enum_table_autobool }; + .params=&enum_params_autobool }; const var_type_def_t ISOTIME_type_defn = { .name="Time", .fns=&time_fns, }; const var_type_def_t CSV_type_defn = { diff --git a/src/lib/confmgt/typedvar.c b/src/lib/confmgt/typedvar.c index 3a188db953..1955302cdc 100644 --- a/src/lib/confmgt/typedvar.c +++ b/src/lib/confmgt/typedvar.c @@ -24,6 +24,7 @@ #include "lib/log/log.h" #include "lib/log/util_bug.h" #include "lib/malloc/malloc.h" +#include "lib/string/printf.h" #include "lib/string/util_string.h" #include "lib/confmgt/var_type_def_st.h" @@ -75,7 +76,15 @@ typed_var_kvassign(void *target, const config_line_t *line, return def->fns->kv_parse(target, line, errmsg, def->params); } - return typed_var_assign(target, line->value, errmsg, def); + int rv = typed_var_assign(target, line->value, errmsg, def); + if (rv < 0 && *errmsg != NULL) { + /* typed_var_assign() didn't know the line's keyword, but we do. + * Let's add it to the error message. */ + char *oldmsg = *errmsg; + tor_asprintf(errmsg, "Could not parse %s: %s", line->key, oldmsg); + tor_free(oldmsg); + } + return rv; } /** diff --git a/src/lib/confmgt/unitparse.c b/src/lib/confmgt/unitparse.c index 52c0eabb69..99716e8d9d 100644 --- a/src/lib/confmgt/unitparse.c +++ b/src/lib/confmgt/unitparse.c @@ -13,7 +13,9 @@ #include "lib/confmgt/unitparse.h" #include "lib/log/log.h" #include "lib/log/util_bug.h" +#include "lib/malloc/malloc.h" #include "lib/string/parse_int.h" +#include "lib/string/printf.h" #include "lib/string/util_string.h" #include "lib/intmath/muldiv.h" @@ -21,6 +23,7 @@ /** Table to map the names of memory units to the number of bytes they * contain. */ +// clang-format off const struct unit_table_t memory_units[] = { { "", 1 }, { "b", 1<< 0 }, @@ -65,9 +68,11 @@ const struct unit_table_t memory_units[] = { { "tbit", UINT64_C(1)<<37 }, { NULL, 0 }, }; +// clang-format on /** Table to map the names of time units to the number of seconds they * contain. */ +// clang-format off const struct unit_table_t time_units[] = { { "", 1 }, { "second", 1 }, @@ -84,9 +89,11 @@ const struct unit_table_t time_units[] = { { "months", 2629728, }, { NULL, 0 }, }; +// clang-format on /** Table to map the names of time units to the number of milliseconds * they contain. */ +// clang-format off const struct unit_table_t time_msec_units[] = { { "", 1 }, { "msec", 1 }, @@ -104,29 +111,37 @@ const struct unit_table_t time_msec_units[] = { { "weeks", 7*24*60*60*1000 }, { NULL, 0 }, }; +// clang-format on /** Parse a string <b>val</b> containing a number, zero or more * spaces, and an optional unit string. If the unit appears in the * table <b>u</b>, then multiply the number by the unit multiplier. * On success, set *<b>ok</b> to 1 and return this product. * Otherwise, set *<b>ok</b> to 0. - * Warns user when overflow or a negative value is detected. + * + * If an error (like overflow or a negative value is detected), put an error + * message in *<b>errmsg_out</b> if that pointer is non-NULL, and otherwise + * log a warning. */ uint64_t -config_parse_units(const char *val, const unit_table_t *u, int *ok) +config_parse_units(const char *val, const unit_table_t *u, int *ok, + char **errmsg_out) { uint64_t v = 0; double d = 0; int use_float = 0; char *cp; + char *errmsg = NULL; tor_assert(ok); v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp); if (!*ok || (cp && *cp == '.')) { d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp); - if (!*ok) + if (!*ok) { + tor_asprintf(&errmsg, "Unable to parse %s as a number", val); goto done; + } use_float = 1; } @@ -148,8 +163,8 @@ config_parse_units(const char *val, const unit_table_t *u, int *ok) d = u->multiplier * d; if (d < 0) { - log_warn(LD_CONFIG, "Got a negative value while parsing %s %s", - val, u->unit); + tor_asprintf(&errmsg, "Got a negative value while parsing %s %s", + val, u->unit); *ok = 0; goto done; } @@ -157,8 +172,8 @@ config_parse_units(const char *val, const unit_table_t *u, int *ok) // Some compilers may warn about casting a double to an unsigned type // because they don't know if d is >= 0 if (d >= 0 && (d > (double)INT64_MAX || (uint64_t)d > INT64_MAX)) { - log_warn(LD_CONFIG, "Overflow detected while parsing %s %s", - val, u->unit); + tor_asprintf(&errmsg, "Overflow while parsing %s %s", + val, u->unit); *ok = 0; goto done; } @@ -168,8 +183,8 @@ config_parse_units(const char *val, const unit_table_t *u, int *ok) v = tor_mul_u64_nowrap(v, u->multiplier); if (v > INT64_MAX) { - log_warn(LD_CONFIG, "Overflow detected while parsing %s %s", - val, u->unit); + tor_asprintf(&errmsg, "Overflow while parsing %s %s", + val, u->unit); *ok = 0; goto done; } @@ -179,10 +194,20 @@ config_parse_units(const char *val, const unit_table_t *u, int *ok) goto done; } } - log_warn(LD_CONFIG, "Unknown unit '%s'.", cp); + tor_asprintf(&errmsg, "Unknown unit in %s", val); *ok = 0; done: + if (errmsg) { + tor_assert_nonfatal(!*ok); + if (errmsg_out) { + *errmsg_out = errmsg; + } else { + log_warn(LD_CONFIG, "%s", errmsg); + tor_free(errmsg); + } + } + if (*ok) return v; else @@ -196,7 +221,7 @@ config_parse_units(const char *val, const unit_table_t *u, int *ok) uint64_t config_parse_memunit(const char *s, int *ok) { - uint64_t u = config_parse_units(s, memory_units, ok); + uint64_t u = config_parse_units(s, memory_units, ok, NULL); return u; } @@ -208,7 +233,7 @@ int config_parse_msec_interval(const char *s, int *ok) { uint64_t r; - r = config_parse_units(s, time_msec_units, ok); + r = config_parse_units(s, time_msec_units, ok, NULL); if (r > INT_MAX) { log_warn(LD_CONFIG, "Msec interval '%s' is too long", s); *ok = 0; @@ -225,7 +250,7 @@ int config_parse_interval(const char *s, int *ok) { uint64_t r; - r = config_parse_units(s, time_units, ok); + r = config_parse_units(s, time_units, ok, NULL); if (r > INT_MAX) { log_warn(LD_CONFIG, "Interval '%s' is too long", s); *ok = 0; diff --git a/src/lib/confmgt/unitparse.h b/src/lib/confmgt/unitparse.h index 3f4f039a0e..047e11b424 100644 --- a/src/lib/confmgt/unitparse.h +++ b/src/lib/confmgt/unitparse.h @@ -25,7 +25,8 @@ extern const unit_table_t memory_units[]; extern const unit_table_t time_units[]; extern const struct unit_table_t time_msec_units[]; -uint64_t config_parse_units(const char *val, const unit_table_t *u, int *ok); +uint64_t config_parse_units(const char *val, const unit_table_t *u, int *ok, + char **errmsg_out); uint64_t config_parse_memunit(const char *s, int *ok); int config_parse_msec_interval(const char *s, int *ok); diff --git a/src/lib/container/map.c b/src/lib/container/map.c index c3fb0b5c8a..7db84313ea 100644 --- a/src/lib/container/map.c +++ b/src/lib/container/map.c @@ -85,21 +85,21 @@ digest256map_entry_hash(const digest256map_entry_t *a) } HT_PROTOTYPE(strmap_impl, strmap_entry_t, node, strmap_entry_hash, - strmap_entries_eq) + strmap_entries_eq); HT_GENERATE2(strmap_impl, strmap_entry_t, node, strmap_entry_hash, - strmap_entries_eq, 0.6, tor_reallocarray_, tor_free_) + strmap_entries_eq, 0.6, tor_reallocarray_, tor_free_); HT_PROTOTYPE(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash, - digestmap_entries_eq) + digestmap_entries_eq); HT_GENERATE2(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash, - digestmap_entries_eq, 0.6, tor_reallocarray_, tor_free_) + digestmap_entries_eq, 0.6, tor_reallocarray_, tor_free_); HT_PROTOTYPE(digest256map_impl, digest256map_entry_t, node, digest256map_entry_hash, - digest256map_entries_eq) + digest256map_entries_eq); HT_GENERATE2(digest256map_impl, digest256map_entry_t, node, digest256map_entry_hash, - digest256map_entries_eq, 0.6, tor_reallocarray_, tor_free_) + digest256map_entries_eq, 0.6, tor_reallocarray_, tor_free_); #define strmap_entry_free(ent) \ FREE_AND_NULL(strmap_entry_t, strmap_entry_free_, (ent)) diff --git a/src/lib/container/map.h b/src/lib/container/map.h index 989ecfad80..dbc1967247 100644 --- a/src/lib/container/map.h +++ b/src/lib/container/map.h @@ -19,29 +19,29 @@ #define DECLARE_MAP_FNS(mapname_t, keytype, prefix) \ typedef struct mapname_t mapname_t; \ - typedef struct prefix##entry_t *prefix##iter_t; \ - MOCK_DECL(mapname_t*, prefix##new, (void)); \ - void* prefix##set(mapname_t *map, keytype key, void *val); \ - void* prefix##get(const mapname_t *map, keytype key); \ - void* prefix##remove(mapname_t *map, keytype key); \ - MOCK_DECL(void, prefix##free_, (mapname_t *map, void (*free_val)(void*))); \ - int prefix##isempty(const mapname_t *map); \ - int prefix##size(const mapname_t *map); \ - prefix##iter_t *prefix##iter_init(mapname_t *map); \ - prefix##iter_t *prefix##iter_next(mapname_t *map, prefix##iter_t *iter); \ - prefix##iter_t *prefix##iter_next_rmv(mapname_t *map, \ - prefix##iter_t *iter); \ - void prefix##iter_get(prefix##iter_t *iter, keytype *keyp, void **valp); \ - int prefix##iter_done(prefix##iter_t *iter); \ - void prefix##assert_ok(const mapname_t *map) + typedef struct prefix##_entry_t *prefix##_iter_t; \ + MOCK_DECL(mapname_t*, prefix##_new, (void)); \ + void* prefix##_set(mapname_t *map, keytype key, void *val); \ + void* prefix##_get(const mapname_t *map, keytype key); \ + void* prefix##_remove(mapname_t *map, keytype key); \ + MOCK_DECL(void, prefix##_free_, (mapname_t *map, void (*free_val)(void*))); \ + int prefix##_isempty(const mapname_t *map); \ + int prefix##_size(const mapname_t *map); \ + prefix##_iter_t *prefix##_iter_init(mapname_t *map); \ + prefix##_iter_t *prefix##_iter_next(mapname_t *map, prefix##_iter_t *iter); \ + prefix##_iter_t *prefix##_iter_next_rmv(mapname_t *map, \ + prefix##_iter_t *iter); \ + void prefix##_iter_get(prefix##_iter_t *iter, keytype *keyp, void **valp); \ + int prefix##_iter_done(prefix##_iter_t *iter); \ + void prefix##_assert_ok(const mapname_t *map) /* Map from const char * to void *. Implemented with a hash table. */ -DECLARE_MAP_FNS(strmap_t, const char *, strmap_); +DECLARE_MAP_FNS(strmap_t, const char *, strmap); /* Map from const char[DIGEST_LEN] to void *. Implemented with a hash table. */ -DECLARE_MAP_FNS(digestmap_t, const char *, digestmap_); +DECLARE_MAP_FNS(digestmap_t, const char *, digestmap); /* Map from const uint8_t[DIGEST256_LEN] to void *. Implemented with a hash * table. */ -DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_); +DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map); #define MAP_FREE_AND_NULL(mapname_t, map, fn) \ do { \ @@ -56,12 +56,12 @@ DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_); #undef DECLARE_MAP_FNS /** Iterates over the key-value pairs in a map <b>map</b> in order. - * <b>prefix</b> is as for DECLARE_MAP_FNS (i.e., strmap_ or digestmap_). + * <b>prefix</b> is as for DECLARE_MAP_FNS (i.e., strmap or digestmap). * The map's keys and values are of type keytype and valtype respectively; * each iteration assigns them to keyvar and valvar. * * Example use: - * MAP_FOREACH(digestmap_, m, const char *, k, routerinfo_t *, r) { + * MAP_FOREACH(digestmap, m, const char *, k, routerinfo_t *, r) { * // use k and r * } MAP_FOREACH_END. */ @@ -81,21 +81,21 @@ DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_); */ #define MAP_FOREACH(prefix, map, keytype, keyvar, valtype, valvar) \ STMT_BEGIN \ - prefix##iter_t *keyvar##_iter; \ - for (keyvar##_iter = prefix##iter_init(map); \ - !prefix##iter_done(keyvar##_iter); \ - keyvar##_iter = prefix##iter_next(map, keyvar##_iter)) { \ + prefix##_iter_t *keyvar##_iter; \ + for (keyvar##_iter = prefix##_iter_init(map); \ + !prefix##_iter_done(keyvar##_iter); \ + keyvar##_iter = prefix##_iter_next(map, keyvar##_iter)) { \ keytype keyvar; \ void *valvar##_voidp; \ valtype valvar; \ - prefix##iter_get(keyvar##_iter, &keyvar, &valvar##_voidp); \ + prefix##_iter_get(keyvar##_iter, &keyvar, &valvar##_voidp); \ valvar = valvar##_voidp; /** As MAP_FOREACH, except allows members to be removed from the map * during the iteration via MAP_DEL_CURRENT. Example use: * * Example use: - * MAP_FOREACH(digestmap_, m, const char *, k, routerinfo_t *, r) { + * MAP_FOREACH(digestmap, m, const char *, k, routerinfo_t *, r) { * if (is_very_old(r)) * MAP_DEL_CURRENT(k); * } MAP_FOREACH_END. @@ -121,18 +121,18 @@ DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_); */ #define MAP_FOREACH_MODIFY(prefix, map, keytype, keyvar, valtype, valvar) \ STMT_BEGIN \ - prefix##iter_t *keyvar##_iter; \ + prefix##_iter_t *keyvar##_iter; \ int keyvar##_del=0; \ - for (keyvar##_iter = prefix##iter_init(map); \ - !prefix##iter_done(keyvar##_iter); \ + for (keyvar##_iter = prefix##_iter_init(map); \ + !prefix##_iter_done(keyvar##_iter); \ keyvar##_iter = keyvar##_del ? \ - prefix##iter_next_rmv(map, keyvar##_iter) : \ - prefix##iter_next(map, keyvar##_iter)) { \ + prefix##_iter_next_rmv(map, keyvar##_iter) : \ + prefix##_iter_next(map, keyvar##_iter)) { \ keytype keyvar; \ void *valvar##_voidp; \ valtype valvar; \ keyvar##_del=0; \ - prefix##iter_get(keyvar##_iter, &keyvar, &valvar##_voidp); \ + prefix##_iter_get(keyvar##_iter, &keyvar, &valvar##_voidp); \ valvar = valvar##_voidp; /** Used with MAP_FOREACH_MODIFY to remove the currently-iterated-upon @@ -152,7 +152,7 @@ DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_); * } DIGESTMAP_FOREACH_END. */ #define DIGESTMAP_FOREACH(map, keyvar, valtype, valvar) \ - MAP_FOREACH(digestmap_, map, const char *, keyvar, valtype, valvar) + MAP_FOREACH(digestmap, map, const char *, keyvar, valtype, valvar) /** As MAP_FOREACH_MODIFY, but does not require declaration of prefix or * keytype. @@ -163,21 +163,21 @@ DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_); * } DIGESTMAP_FOREACH_END. */ #define DIGESTMAP_FOREACH_MODIFY(map, keyvar, valtype, valvar) \ - MAP_FOREACH_MODIFY(digestmap_, map, const char *, keyvar, valtype, valvar) + MAP_FOREACH_MODIFY(digestmap, map, const char *, keyvar, valtype, valvar) /** Used to end a DIGESTMAP_FOREACH() block. */ #define DIGESTMAP_FOREACH_END MAP_FOREACH_END #define DIGEST256MAP_FOREACH(map, keyvar, valtype, valvar) \ - MAP_FOREACH(digest256map_, map, const uint8_t *, keyvar, valtype, valvar) + MAP_FOREACH(digest256map, map, const uint8_t *, keyvar, valtype, valvar) #define DIGEST256MAP_FOREACH_MODIFY(map, keyvar, valtype, valvar) \ - MAP_FOREACH_MODIFY(digest256map_, map, const uint8_t *, \ + MAP_FOREACH_MODIFY(digest256map, map, const uint8_t *, \ keyvar, valtype, valvar) #define DIGEST256MAP_FOREACH_END MAP_FOREACH_END #define STRMAP_FOREACH(map, keyvar, valtype, valvar) \ - MAP_FOREACH(strmap_, map, const char *, keyvar, valtype, valvar) + MAP_FOREACH(strmap, map, const char *, keyvar, valtype, valvar) #define STRMAP_FOREACH_MODIFY(map, keyvar, valtype, valvar) \ - MAP_FOREACH_MODIFY(strmap_, map, const char *, keyvar, valtype, valvar) + MAP_FOREACH_MODIFY(strmap, map, const char *, keyvar, valtype, valvar) #define STRMAP_FOREACH_END MAP_FOREACH_END void* strmap_set_lc(strmap_t *map, const char *key, void *val); @@ -186,66 +186,66 @@ void* strmap_remove_lc(strmap_t *map, const char *key); #define DECLARE_TYPED_DIGESTMAP_FNS(prefix, mapname_t, valtype) \ typedef struct mapname_t mapname_t; \ - typedef struct prefix##iter_t *prefix##iter_t; \ + typedef struct prefix##_iter_t *prefix##_iter_t; \ ATTR_UNUSED static inline mapname_t* \ - prefix##new(void) \ + prefix##_new(void) \ { \ return (mapname_t*)digestmap_new(); \ } \ ATTR_UNUSED static inline digestmap_t* \ - prefix##to_digestmap(mapname_t *map) \ + prefix##_to_digestmap(mapname_t *map) \ { \ return (digestmap_t*)map; \ } \ ATTR_UNUSED static inline valtype* \ - prefix##get(mapname_t *map, const char *key) \ + prefix##_get(mapname_t *map, const char *key) \ { \ return (valtype*)digestmap_get((digestmap_t*)map, key); \ } \ ATTR_UNUSED static inline valtype* \ - prefix##set(mapname_t *map, const char *key, valtype *val) \ + prefix##_set(mapname_t *map, const char *key, valtype *val) \ { \ return (valtype*)digestmap_set((digestmap_t*)map, key, val); \ } \ ATTR_UNUSED static inline valtype* \ - prefix##remove(mapname_t *map, const char *key) \ + prefix##_remove(mapname_t *map, const char *key) \ { \ return (valtype*)digestmap_remove((digestmap_t*)map, key); \ } \ ATTR_UNUSED static inline void \ - prefix##f##ree_(mapname_t *map, void (*free_val)(void*)) \ + prefix##_f##ree_(mapname_t *map, void (*free_val)(void*)) \ { \ digestmap_free_((digestmap_t*)map, free_val); \ } \ ATTR_UNUSED static inline int \ - prefix##isempty(mapname_t *map) \ + prefix##_isempty(mapname_t *map) \ { \ return digestmap_isempty((digestmap_t*)map); \ } \ ATTR_UNUSED static inline int \ - prefix##size(mapname_t *map) \ + prefix##_size(mapname_t *map) \ { \ return digestmap_size((digestmap_t*)map); \ } \ ATTR_UNUSED static inline \ - prefix##iter_t *prefix##iter_init(mapname_t *map) \ + prefix##_iter_t *prefix##_iter_init(mapname_t *map) \ { \ - return (prefix##iter_t*) digestmap_iter_init((digestmap_t*)map); \ + return (prefix##_iter_t*) digestmap_iter_init((digestmap_t*)map); \ } \ ATTR_UNUSED static inline \ - prefix##iter_t *prefix##iter_next(mapname_t *map, prefix##iter_t *iter) \ + prefix##_iter_t *prefix##_iter_next(mapname_t *map, prefix##_iter_t *iter) \ { \ - return (prefix##iter_t*) digestmap_iter_next( \ + return (prefix##_iter_t*) digestmap_iter_next( \ (digestmap_t*)map, (digestmap_iter_t*)iter); \ } \ - ATTR_UNUSED static inline prefix##iter_t* \ - prefix##iter_next_rmv(mapname_t *map, prefix##iter_t *iter) \ + ATTR_UNUSED static inline prefix##_iter_t* \ + prefix##_iter_next_rmv(mapname_t *map, prefix##_iter_t *iter) \ { \ - return (prefix##iter_t*) digestmap_iter_next_rmv( \ + return (prefix##_iter_t*) digestmap_iter_next_rmv( \ (digestmap_t*)map, (digestmap_iter_t*)iter); \ } \ ATTR_UNUSED static inline void \ - prefix##iter_get(prefix##iter_t *iter, \ + prefix##_iter_get(prefix##_iter_t *iter, \ const char **keyp, \ valtype **valp) \ { \ @@ -254,7 +254,7 @@ void* strmap_remove_lc(strmap_t *map, const char *key); *valp = v; \ } \ ATTR_UNUSED static inline int \ - prefix##iter_done(prefix##iter_t *iter) \ + prefix##_iter_done(prefix##_iter_t *iter) \ { \ return digestmap_iter_done((digestmap_iter_t*)iter); \ } diff --git a/src/lib/container/namemap.c b/src/lib/container/namemap.c index 28695ee3a1..e286cad947 100644 --- a/src/lib/container/namemap.c +++ b/src/lib/container/namemap.c @@ -35,9 +35,9 @@ mapped_name_hash(const mapped_name_t *a) } HT_PROTOTYPE(namemap_ht, mapped_name_t, node, mapped_name_hash, - mapped_name_eq) + mapped_name_eq); HT_GENERATE2(namemap_ht, mapped_name_t, node, mapped_name_hash, - mapped_name_eq, 0.6, tor_reallocarray_, tor_free_) + mapped_name_eq, 0.6, tor_reallocarray_, tor_free_); /** Set up an uninitialized <b>map</b>. */ void diff --git a/src/lib/crypt_ops/crypto_ed25519.c b/src/lib/crypt_ops/crypto_ed25519.c index bd97f76284..f242c7011e 100644 --- a/src/lib/crypt_ops/crypto_ed25519.c +++ b/src/lib/crypt_ops/crypto_ed25519.c @@ -795,7 +795,7 @@ ed25519_point_is_identity_element(const uint8_t *point) int ed25519_validate_pubkey(const ed25519_public_key_t *pubkey) { - uint8_t result[32] = {9}; + uint8_t result[32] = {0}; /* First check that we were not given the identity element */ if (ed25519_point_is_identity_element(pubkey->pubkey)) { diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c index 4b33ed98e5..195e4bbaf9 100644 --- a/src/lib/crypt_ops/crypto_rsa.c +++ b/src/lib/crypt_ops/crypto_rsa.c @@ -490,7 +490,7 @@ crypto_pk_write_private_key_to_string(crypto_pk_t *env, static int crypto_pk_read_from_string_generic(crypto_pk_t *env, const char *src, size_t len, int severity, - bool private_key) + bool private_key, int max_bits) { if (len == (size_t)-1) // "-1" indicates "use the length of the string." len = strlen(src); @@ -510,7 +510,7 @@ crypto_pk_read_from_string_generic(crypto_pk_t *env, const char *src, } crypto_pk_t *pk = private_key - ? crypto_pk_asn1_decode_private((const char*)buf, n) + ? crypto_pk_asn1_decode_private((const char*)buf, n, max_bits) : crypto_pk_asn1_decode((const char*)buf, n); if (! pk) { log_fn(severity, LD_CRYPTO, @@ -539,7 +539,8 @@ int crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, size_t len) { - return crypto_pk_read_from_string_generic(env, src, len, LOG_INFO, false); + return crypto_pk_read_from_string_generic(env, src, len, LOG_INFO, false, + -1); } /** Read a PEM-encoded private key from the <b>len</b>-byte string <b>src</b> @@ -550,7 +551,21 @@ int crypto_pk_read_private_key_from_string(crypto_pk_t *env, const char *src, ssize_t len) { - return crypto_pk_read_from_string_generic(env, src, len, LOG_INFO, true); + return crypto_pk_read_from_string_generic(env, src, len, LOG_INFO, true, + -1); +} + +/** + * As crypto_pk_read_private_key_from_string(), but reject any key + * with a modulus longer than 1024 bits before doing any expensive + * validation on it. + */ +int +crypto_pk_read_private_key1024_from_string(crypto_pk_t *env, + const char *src, ssize_t len) +{ + return crypto_pk_read_from_string_generic(env, src, len, LOG_INFO, true, + 1024); } /** If a file is longer than this, we won't try to decode its private key */ @@ -578,7 +593,7 @@ crypto_pk_read_private_key_from_filename(crypto_pk_t *env, } int rv = crypto_pk_read_from_string_generic(env, buf, (ssize_t)st.st_size, - LOG_WARN, true); + LOG_WARN, true, -1); if (rv < 0) { log_warn(LD_CRYPTO, "Unable to decode private key from file %s", escaped(keyfile)); @@ -662,7 +677,7 @@ crypto_pk_base64_decode_private(const char *str, size_t len) goto out; } - pk = crypto_pk_asn1_decode_private(der, der_len); + pk = crypto_pk_asn1_decode_private(der, der_len, -1); out: memwipe(der, 0, len+1); diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index 38934e7f32..ab2e9db80d 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -61,6 +61,8 @@ int crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, size_t len); int crypto_pk_read_private_key_from_string(crypto_pk_t *env, const char *s, ssize_t len); +int crypto_pk_read_private_key1024_from_string(crypto_pk_t *env, + const char *src, ssize_t len); int crypto_pk_write_private_key_to_filename(crypto_pk_t *env, const char *fname); @@ -95,7 +97,8 @@ int crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len); crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len); int crypto_pk_asn1_encode_private(const crypto_pk_t *pk, char *dest, size_t dest_len); -crypto_pk_t *crypto_pk_asn1_decode_private(const char *str, size_t len); +crypto_pk_t *crypto_pk_asn1_decode_private(const char *str, size_t len, + int max_bits); int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space); int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out); void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c index 0a5041aad4..66f325e868 100644 --- a/src/lib/crypt_ops/crypto_rsa_nss.c +++ b/src/lib/crypt_ops/crypto_rsa_nss.c @@ -679,9 +679,12 @@ crypto_pk_asn1_encode_private(const crypto_pk_t *pk, /** Given a buffer containing the DER representation of the * private key <b>str</b>, decode and return the result on success, or NULL * on failure. + * + * If <b>max_bits</b> is nonnegative, reject any key longer than max_bits + * without performing any expensive validation on it. */ crypto_pk_t * -crypto_pk_asn1_decode_private(const char *str, size_t len) +crypto_pk_asn1_decode_private(const char *str, size_t len, int max_bits) { tor_assert(str); tor_assert(len < INT_MAX); @@ -731,6 +734,15 @@ crypto_pk_asn1_decode_private(const char *str, size_t len) output = NULL; } + if (output) { + const int bits = SECKEY_PublicKeyStrengthInBits(output->pubkey); + if (max_bits >= 0 && bits > max_bits) { + log_info(LD_CRYPTO, "Private key longer than expected."); + crypto_pk_free(output); + output = NULL; + } + } + if (slot) PK11_FreeSlot(slot); diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c index e5025108ae..d54db43b92 100644 --- a/src/lib/crypt_ops/crypto_rsa_openssl.c +++ b/src/lib/crypt_ops/crypto_rsa_openssl.c @@ -33,6 +33,7 @@ ENABLE_GCC_WARNING("-Wredundant-decls") #include "lib/encoding/binascii.h" #include <string.h> +#include <stdbool.h> /** Declaration for crypto_pk_t structure. */ struct crypto_pk_t @@ -564,11 +565,71 @@ crypto_pk_asn1_encode_private(const crypto_pk_t *pk, char *dest, return len; } +/** Check whether any component of a private key is too large in a way that + * seems likely to make verification too expensive. Return true if it's too + * long, and false otherwise. */ +static bool +rsa_private_key_too_long(RSA *rsa, int max_bits) +{ + const BIGNUM *n, *e, *p, *q, *d, *dmp1, *dmq1, *iqmp; +#ifdef OPENSSL_1_1_API + +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,1) + n = RSA_get0_n(rsa); + e = RSA_get0_e(rsa); + p = RSA_get0_p(rsa); + q = RSA_get0_q(rsa); + d = RSA_get0_d(rsa); + dmp1 = RSA_get0_dmp1(rsa); + dmq1 = RSA_get0_dmq1(rsa); + iqmp = RSA_get0_iqmp(rsa); +#else + /* The accessors above did not exist in openssl 1.1.0. */ + p = q = dmp1 = dmq1 = iqmp = NULL; + RSA_get0_key(rsa, &n, &e, &d); +#endif + + if (RSA_bits(rsa) > max_bits) + return true; +#else + n = rsa->n; + e = rsa->e; + p = rsa->p; + q = rsa->q; + d = rsa->d; + dmp1 = rsa->dmp1; + dmq1 = rsa->dmq1; + iqmp = rsa->iqmp; +#endif + + if (n && BN_num_bits(n) > max_bits) + return true; + if (e && BN_num_bits(e) > max_bits) + return true; + if (p && BN_num_bits(p) > max_bits) + return true; + if (q && BN_num_bits(q) > max_bits) + return true; + if (d && BN_num_bits(d) > max_bits) + return true; + if (dmp1 && BN_num_bits(dmp1) > max_bits) + return true; + if (dmq1 && BN_num_bits(dmq1) > max_bits) + return true; + if (iqmp && BN_num_bits(iqmp) > max_bits) + return true; + + return false; +} + /** Decode an ASN.1-encoded private key from <b>str</b>; return the result on * success and NULL on failure. + * + * If <b>max_bits</b> is nonnegative, reject any key longer than max_bits + * without performing any expensive validation on it. */ crypto_pk_t * -crypto_pk_asn1_decode_private(const char *str, size_t len) +crypto_pk_asn1_decode_private(const char *str, size_t len, int max_bits) { RSA *rsa; unsigned char *buf; @@ -578,7 +639,12 @@ crypto_pk_asn1_decode_private(const char *str, size_t len) rsa = d2i_RSAPrivateKey(NULL, &cp, len); tor_free(buf); if (!rsa) { - crypto_openssl_log_errors(LOG_WARN,"decoding public key"); + crypto_openssl_log_errors(LOG_WARN,"decoding private key"); + return NULL; + } + if (max_bits >= 0 && rsa_private_key_too_long(rsa, max_bits)) { + log_info(LD_CRYPTO, "Private key longer than expected."); + RSA_free(rsa); return NULL; } crypto_pk_t *result = crypto_new_pk_from_openssl_rsa_(rsa); diff --git a/src/lib/ctime/di_ops.c b/src/lib/ctime/di_ops.c index 7448a9973e..d57d286990 100644 --- a/src/lib/ctime/di_ops.c +++ b/src/lib/ctime/di_ops.c @@ -72,10 +72,10 @@ tor_memcmp(const void *a, const void *b, size_t len) * actually implementation-defined in standard C. So how do we * get away with assuming it? Easy. We check.) */ #if ((-60 >> 8) != -1) -#error "According to cpp, right-shift doesn't perform sign-extension." +#error "cpp says right-shift doesn't perform sign-extension." #endif #ifndef RSHIFT_DOES_SIGN_EXTEND -#error "According to configure, right-shift doesn't perform sign-extension." +#error "configure says right-shift doesn't perform sign-extension." #endif /* If v1 == v2, equal_p is ~0, so this will leave retval @@ -279,3 +279,30 @@ select_array_member_cumulative_timei(const uint64_t *entries, int n_entries, return i_chosen; } + +/** + * If <b>s</b> is true, then copy <b>n</b> bytes from <b>src</b> to + * <b>dest</b>. Otherwise leave <b>dest</b> alone. + * + * This function behaves the same as + * + * if (s) + * memcpy(dest, src, n); + * + * except that it tries to run in the same amount of time whether <b>s</b> is + * true or not. + **/ +void +memcpy_if_true_timei(bool s, void *dest, const void *src, size_t n) +{ + // If s is true, mask will be ~0. If s is false, mask will be 0. + const char mask = (char) -(signed char)s; + + char *destp = dest; + const char *srcp = src; + for (size_t i = 0; i < n; ++i) { + *destp = (*destp & ~mask) | (*srcp & mask); + ++destp; + ++srcp; + } +} diff --git a/src/lib/ctime/di_ops.h b/src/lib/ctime/di_ops.h index 4ff8f03165..9fe2884ecc 100644 --- a/src/lib/ctime/di_ops.h +++ b/src/lib/ctime/di_ops.h @@ -73,4 +73,6 @@ int select_array_member_cumulative_timei(const uint64_t *entries, int n_entries, uint64_t total, uint64_t rand_val); +void memcpy_if_true_timei(bool s, void *dest, const void *src, size_t n); + #endif /* !defined(TOR_DI_OPS_H) */ diff --git a/src/lib/encoding/confline.c b/src/lib/encoding/confline.c index ff8bacba3c..eb1a4e30f0 100644 --- a/src/lib/encoding/confline.c +++ b/src/lib/encoding/confline.c @@ -253,6 +253,35 @@ config_lines_dup_and_filter(const config_line_t *inp, return result; } +/** + * Given a linelist <b>inp</b> beginning with the key <b>header</b>, find the + * next line with that key, and remove that instance and all following lines + * from the list. Return the lines that were removed. Operate + * case-insensitively. + * + * For example, if the header is "H", and <b>inp</b> contains "H, A, B, H, C, + * H, D", this function will alter <b>inp</b> to contain only "H, A, B", and + * return the elements "H, C, H, D" as a separate list. + **/ +config_line_t * +config_lines_partition(config_line_t *inp, const char *header) +{ + if (BUG(inp == NULL)) + return NULL; + if (BUG(strcasecmp(inp->key, header))) + return NULL; + + /* Advance ptr until it points to the link to the next segment of this + list. */ + config_line_t **ptr = &inp->next; + while (*ptr && strcasecmp((*ptr)->key, header)) { + ptr = &(*ptr)->next; + } + config_line_t *remainder = *ptr; + *ptr = NULL; + return remainder; +} + /** Return true iff a and b contain identical keys and values in identical * order. */ int diff --git a/src/lib/encoding/confline.h b/src/lib/encoding/confline.h index cd343e0e99..ce0d6c6e17 100644 --- a/src/lib/encoding/confline.h +++ b/src/lib/encoding/confline.h @@ -50,6 +50,7 @@ const config_line_t *config_line_find(const config_line_t *lines, const char *key); const config_line_t *config_line_find_case(const config_line_t *lines, const char *key); +config_line_t *config_lines_partition(config_line_t *inp, const char *header); int config_lines_eq(const config_line_t *a, const config_line_t *b); int config_count_key(const config_line_t *a, const char *key); void config_free_lines_(config_line_t *front); diff --git a/src/lib/encoding/pem.c b/src/lib/encoding/pem.c index c48f1016ae..6c9f10e085 100644 --- a/src/lib/encoding/pem.c +++ b/src/lib/encoding/pem.c @@ -85,13 +85,19 @@ pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen, src = eat_whitespace_eos(src, eos); char *tag = NULL; - tor_asprintf(&tag, "-----BEGIN %s-----\n", objtype); + tor_asprintf(&tag, "-----BEGIN %s-----", objtype); if ((size_t)(eos-src) < strlen(tag) || fast_memneq(src, tag, strlen(tag))) { tor_free(tag); return -1; } src += strlen(tag); tor_free(tag); + /* At this point we insist on spaces (including CR), then an LF. */ + src = eat_whitespace_eos_no_nl(src, eos); + if (src == eos || *src != '\n') { + /* Extra junk at end of line: this isn't valid. */ + return -1; + } // NOTE lack of trailing \n. We do not enforce its presence. tor_asprintf(&tag, "\n-----END %s-----", objtype); diff --git a/src/lib/err/torerr.c b/src/lib/err/torerr.c index 92ef80e56a..2de75c0be4 100644 --- a/src/lib/err/torerr.c +++ b/src/lib/err/torerr.c @@ -151,29 +151,27 @@ tor_log_reset_sigsafe_err_fds(void) } /** - * Close the list of fds that get errors from inside a signal handler or + * Flush the list of fds that get errors from inside a signal handler or * other emergency condition. These fds are shared with the logging code: - * closing them flushes the log buffers, and prevents any further logging. + * flushing them also flushes the log buffers. * - * This function closes stderr, so it should only be called immediately before - * process shutdown. + * This function is safe to call during signal handlers. */ void -tor_log_close_sigsafe_err_fds(void) +tor_log_flush_sigsafe_err_fds(void) { + /* If we don't have fsync() in unistd.h, we can't flush the logs. */ +#ifdef HAVE_FSYNC int n_fds, i; const int *fds = NULL; n_fds = tor_log_get_sigsafe_err_fds(&fds); for (i = 0; i < n_fds; ++i) { - /* tor_log_close_sigsafe_err_fds_on_error() is called on error and on - * shutdown, so we can't log or take any useful action if close() - * fails. */ - (void)close(fds[i]); + /* This function is called on error and on shutdown, so we don't log, or + * take any other action, if fsync() fails. */ + (void)fsync(fds[i]); } - - /* Don't even try logging, we've closed all the log fds. */ - tor_log_set_sigsafe_err_fds(NULL, 0); +#endif /* defined(HAVE_FSYNC) */ } /** @@ -217,13 +215,13 @@ tor_raw_assertion_failed_msg_(const char *file, int line, const char *expr, /** * Call the abort() function to kill the current process with a fatal - * error. But first, close the raw error file descriptors, so error messages + * error. But first, flush the raw error file descriptors, so error messages * are written before process termination. **/ void tor_raw_abort_(void) { - tor_log_close_sigsafe_err_fds(); + tor_log_flush_sigsafe_err_fds(); abort(); } diff --git a/src/lib/err/torerr.h b/src/lib/err/torerr.h index f5b36eef9c..ce1b049c47 100644 --- a/src/lib/err/torerr.h +++ b/src/lib/err/torerr.h @@ -40,7 +40,7 @@ void tor_log_err_sigsafe(const char *m, ...); int tor_log_get_sigsafe_err_fds(const int **out); void tor_log_set_sigsafe_err_fds(const int *fds, int n); void tor_log_reset_sigsafe_err_fds(void); -void tor_log_close_sigsafe_err_fds(void); +void tor_log_flush_sigsafe_err_fds(void); void tor_log_sigsafe_err_set_granularity(int ms); void tor_raw_abort_(void) ATTR_NORETURN; diff --git a/src/lib/err/torerr_sys.c b/src/lib/err/torerr_sys.c index aa49ba36bd..46fc853550 100644 --- a/src/lib/err/torerr_sys.c +++ b/src/lib/err/torerr_sys.c @@ -27,11 +27,9 @@ subsys_torerr_initialize(void) static void subsys_torerr_shutdown(void) { - /* Stop handling signals with backtraces, then close the logs. */ + /* Stop handling signals with backtraces, then flush the logs. */ clean_up_backtrace_handler(); - /* We can't log any log messages after this point: we've closed all the log - * fds, including stdio. */ - tor_log_close_sigsafe_err_fds(); + tor_log_flush_sigsafe_err_fds(); } const subsys_fns_t sys_torerr = { diff --git a/src/lib/evloop/procmon.c b/src/lib/evloop/procmon.c index 3276cb1808..718c7d4777 100644 --- a/src/lib/evloop/procmon.c +++ b/src/lib/evloop/procmon.c @@ -165,8 +165,8 @@ tor_validate_process_specifier(const char *process_spec, return parse_process_specifier(process_spec, &ppspec, msg); } -/* DOCDOC poll_interval_tv */ -static const struct timeval poll_interval_tv = {15, 0}; +/* We check this often for presence of owning controller process. */ +static const struct timeval poll_interval_tv = {15, 0}; // 15 seconds. /** Create a process-termination monitor for the process specifier * given in <b>process_spec</b>. Return a newly allocated diff --git a/src/lib/llharden/.may_include b/src/lib/llharden/.may_include new file mode 100644 index 0000000000..038237dadf --- /dev/null +++ b/src/lib/llharden/.may_include @@ -0,0 +1,3 @@ +lib/llharden/*.h +lib/subsys/*.h +orconfig.h diff --git a/src/lib/llharden/include.am b/src/lib/llharden/include.am new file mode 100644 index 0000000000..0a4788c7dc --- /dev/null +++ b/src/lib/llharden/include.am @@ -0,0 +1,19 @@ + +noinst_LIBRARIES += src/lib/libtor-llharden.a + +if UNITTESTS_ENABLED +noinst_LIBRARIES += src/lib/libtor-llharden-testing.a +endif + +# ADD_C_FILE: INSERT SOURCES HERE. +src_lib_libtor_llharden_a_SOURCES = \ + src/lib/llharden/winprocess_sys.c + +src_lib_libtor_llharden_testing_a_SOURCES = \ + $(src_lib_libtor_llharden_a_SOURCES) +src_lib_libtor_llharden_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_lib_libtor_llharden_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + +# ADD_C_FILE: INSERT HEADERS HERE. +noinst_HEADERS += \ + src/lib/llharden/winprocess_sys.h diff --git a/src/lib/llharden/lib_llharden.md b/src/lib/llharden/lib_llharden.md new file mode 100644 index 0000000000..69e9af5327 --- /dev/null +++ b/src/lib/llharden/lib_llharden.md @@ -0,0 +1,6 @@ +@dir /lib/llharden +@brief lib/llharden: low-level unconditional process hardening + +This module contains process hardening code that we want to run before any +other code, including configuration. It needs to be self-contained, since +nothing else will be initialized at this point. diff --git a/src/lib/process/winprocess_sys.c b/src/lib/llharden/winprocess_sys.c index e43a77e467..a5f22c182b 100644 --- a/src/lib/process/winprocess_sys.c +++ b/src/lib/llharden/winprocess_sys.c @@ -8,7 +8,7 @@ #include "orconfig.h" #include "lib/subsys/subsys.h" -#include "lib/process/winprocess_sys.h" +#include "lib/llharden/winprocess_sys.h" #include <stdbool.h> #include <stddef.h> diff --git a/src/lib/process/winprocess_sys.h b/src/lib/llharden/winprocess_sys.h index bece1b3da9..bece1b3da9 100644 --- a/src/lib/process/winprocess_sys.h +++ b/src/lib/llharden/winprocess_sys.h diff --git a/src/lib/log/log.c b/src/lib/log/log.c index 75f8f79da2..9ee87c0668 100644 --- a/src/lib/log/log.c +++ b/src/lib/log/log.c @@ -523,7 +523,7 @@ logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len, * pass them, and some very old ones do not detect overflow so well. * Regrettably, they call their maximum line length MAXLINE. */ #if MAXLINE < 64 -#warning "MAXLINE is a very low number; it might not be from syslog.h." +#warning "MAXLINE is very low; it might not be from syslog.h." #endif char *m = msg_after_prefix; if (msg_len >= MAXLINE) @@ -665,24 +665,15 @@ tor_log_update_sigsafe_err_fds(void) const logfile_t *lf; int found_real_stderr = 0; - /* log_fds and err_fds contain matching entries: log_fds are the fds used by - * the log module, and err_fds are the fds used by the err module. - * For stdio logs, the log_fd and err_fd values are identical, - * and the err module closes the fd on shutdown. - * For file logs, the err_fd is a dup() of the log_fd, - * and the log and err modules both close their respective fds on shutdown. - * (Once all fds representing a file are closed, the underlying file is - * closed.) - */ - int log_fds[TOR_SIGSAFE_LOG_MAX_FDS]; - int err_fds[TOR_SIGSAFE_LOG_MAX_FDS]; + /* The fds are the file descriptors of tor's stdout, stderr, and file + * logs. The log and err modules flush these fds during their shutdowns. */ + int fds[TOR_SIGSAFE_LOG_MAX_FDS]; int n_fds; LOCK_LOGS(); /* Reserve the first one for stderr. This is safe because when we daemonize, - * we dup2 /dev/null to stderr. - * For stderr, log_fds and err_fds are the same. */ - log_fds[0] = err_fds[0] = STDERR_FILENO; + * we dup2 /dev/null to stderr. */ + fds[0] = STDERR_FILENO; n_fds = 1; for (lf = logfiles; lf; lf = lf->next) { @@ -697,21 +688,11 @@ tor_log_update_sigsafe_err_fds(void) (LD_BUG|LD_GENERAL)) { if (lf->fd == STDERR_FILENO) found_real_stderr = 1; - /* Avoid duplicates by checking the log module fd against log_fds */ - if (int_array_contains(log_fds, n_fds, lf->fd)) + /* Avoid duplicates by checking the log module fd against fds */ + if (int_array_contains(fds, n_fds, lf->fd)) continue; - /* Update log_fds using the log module's fd */ - log_fds[n_fds] = lf->fd; - if (lf->needs_close) { - /* File log fds are duplicated, because close_log() closes the log - * module's fd, and tor_log_close_sigsafe_err_fds() closes the err - * module's fd. Both refer to the same file. */ - err_fds[n_fds] = dup(lf->fd); - } else { - /* stdio log fds are not closed by the log module. - * tor_log_close_sigsafe_err_fds() closes stdio logs. */ - err_fds[n_fds] = lf->fd; - } + /* Update fds using the log module's fd */ + fds[n_fds] = lf->fd; n_fds++; if (n_fds == TOR_SIGSAFE_LOG_MAX_FDS) break; @@ -719,20 +700,19 @@ tor_log_update_sigsafe_err_fds(void) } if (!found_real_stderr && - int_array_contains(log_fds, n_fds, STDOUT_FILENO)) { + int_array_contains(fds, n_fds, STDOUT_FILENO)) { /* Don't use a virtual stderr when we're also logging to stdout. * If we reached max_fds logs, we'll now have (max_fds - 1) logs. * That's ok, max_fds is large enough that most tor instances don't exceed * it. */ raw_assert(n_fds >= 2); /* Don't tor_assert inside log fns */ --n_fds; - log_fds[0] = log_fds[n_fds]; - err_fds[0] = err_fds[n_fds]; + fds[0] = fds[n_fds]; } UNLOCK_LOGS(); - tor_log_set_sigsafe_err_fds(err_fds, n_fds); + tor_log_set_sigsafe_err_fds(fds, n_fds); } /** Add to <b>out</b> a copy of every currently configured log file name. Used @@ -841,16 +821,16 @@ logs_free_all(void) * log mutex. */ } -/** Close signal-safe log files. - * Closing the log files makes the process and OS flush log buffers. +/** Flush the signal-safe log files. * - * This function is safe to call from a signal handler. It should only be - * called when shutting down the log or err modules. It is currenly called - * by the err module, when terminating the process on an abnormal condition. + * This function is safe to call from a signal handler. It is currenly called + * by the BUG() macros, when terminating the process on an abnormal condition. */ void -logs_close_sigsafe(void) +logs_flush_sigsafe(void) { + /* If we don't have fsync() in unistd.h, we can't flush the logs. */ +#ifdef HAVE_FSYNC logfile_t *victim, *next; /* We can't LOCK_LOGS() in a signal handler, because it may call * signal-unsafe functions. And we can't deallocate memory, either. */ @@ -860,9 +840,11 @@ logs_close_sigsafe(void) victim = next; next = next->next; if (victim->needs_close) { - close_log_sigsafe(victim); + /* We can't do anything useful if the flush fails. */ + (void)fsync(victim->fd); } } +#endif /* defined(HAVE_FSYNC) */ } /** Remove and free the log entry <b>victim</b> from the linked-list diff --git a/src/lib/log/log.h b/src/lib/log/log.h index cb588635d7..aafbf9be2f 100644 --- a/src/lib/log/log.h +++ b/src/lib/log/log.h @@ -186,7 +186,7 @@ void logs_set_domain_logging(int enabled); int get_min_log_level(void); void switch_logs_debug(void); void logs_free_all(void); -void logs_close_sigsafe(void); +void logs_flush_sigsafe(void); void add_default_log(int min_severity); void close_temp_logs(void); void rollback_log_changes(void); diff --git a/src/lib/log/util_bug.c b/src/lib/log/util_bug.c index de44d30e4e..d698ddd8a6 100644 --- a/src/lib/log/util_bug.c +++ b/src/lib/log/util_bug.c @@ -71,7 +71,6 @@ tor_set_failed_assertion_callback(void (*fn)(void)) /** Helper for tor_assert: report the assertion failure. */ void -CHECK_PRINTF(5, 6) tor_assertion_failed_(const char *fname, unsigned int line, const char *func, const char *expr, const char *fmt, ...) @@ -104,7 +103,6 @@ tor_assertion_failed_(const char *fname, unsigned int line, /** Helper for tor_assert_nonfatal: report the assertion failure. */ void -CHECK_PRINTF(6, 7) tor_bug_occurred_(const char *fname, unsigned int line, const char *func, const char *expr, int once, const char *fmt, ...) @@ -172,7 +170,7 @@ tor_bug_occurred_(const char *fname, unsigned int line, void tor_abort_(void) { - logs_close_sigsafe(); + logs_flush_sigsafe(); tor_raw_abort_(); } diff --git a/src/lib/malloc/map_anon.c b/src/lib/malloc/map_anon.c index 1926b61f07..628966012a 100644 --- a/src/lib/malloc/map_anon.c +++ b/src/lib/malloc/map_anon.c @@ -78,7 +78,7 @@ #endif /* defined(HAVE_MINHERIT) || ... */ #if defined(HAVE_MINHERIT) && !defined(FLAG_ZERO) && !defined(FLAG_NOINHERIT) -#warning "minherit() is defined, but we couldn't find the right flag for it." +#warning "minherit() is defined, but FLAG_ZERO/NOINHERIT are not." #warning "This is probably a bug in Tor's support for this platform." #endif diff --git a/src/lib/math/prob_distr.c b/src/lib/math/prob_distr.c index 548d256023..31d485120e 100644 --- a/src/lib/math/prob_distr.c +++ b/src/lib/math/prob_distr.c @@ -1284,15 +1284,16 @@ sample_genpareto_locscale(uint32_t s, double p0, double mu, double sigma, /** * Deterministically sample from the geometric distribution with * per-trial success probability p. - * + **/ +// clang-format off +/* * XXX Quantify the error (KL divergence?) of this * ceiling-of-exponential sampler from a true geometric distribution, * which we could get by rejection sampling. Relevant papers: * * John F. Monahan, `Accuracy in Random Number Generation', * Mathematics of Computation 45(172), October 1984, pp. 559--568. -*https://pdfs.semanticscholar.org/aca6/74b96da1df77b2224e8cfc5dd6d61a471632.pdf - * +https://pdfs.semanticscholar.org/aca6/74b96da1df77b2224e8cfc5dd6d61a471632.pdf * Karl Bringmann and Tobias Friedrich, `Exact and Efficient * Generation of Geometric Random Variates and Random Graphs', in * Proceedings of the 40th International Colloaquium on Automata, @@ -1301,6 +1302,7 @@ sample_genpareto_locscale(uint32_t s, double p0, double mu, double sigma, * https://doi.org/10.1007/978-3-642-39206-1_23 * https://people.mpi-inf.mpg.de/~kbringma/paper/2013ICALP-1.pdf */ +// clang-format on static double sample_geometric(uint32_t s, double p0, double p) { diff --git a/src/lib/memarea/memarea.c b/src/lib/memarea/memarea.c index d677c364a4..4d26c20eeb 100644 --- a/src/lib/memarea/memarea.c +++ b/src/lib/memarea/memarea.c @@ -39,7 +39,7 @@ #elif MEMAREA_ALIGN == 8 #define MEMAREA_ALIGN_MASK ((uintptr_t)7) #else -#error "void* is neither 4 nor 8 bytes long. I don't know how to align stuff." +#error "void* is neither 4 nor 8 bytes long." #endif /* MEMAREA_ALIGN == 4 || ... */ #if defined(__GNUC__) && defined(FLEXIBLE_ARRAY_MEMBER) diff --git a/src/lib/net/address.c b/src/lib/net/address.c index ac4350b8fe..e968d8a7f7 100644 --- a/src/lib/net/address.c +++ b/src/lib/net/address.c @@ -97,7 +97,7 @@ * work correctly. Bail out here if we've found a platform where AF_UNSPEC * isn't 0. */ #if AF_UNSPEC != 0 -#error We rely on AF_UNSPEC being 0. Let us know about your platform, please! +#error "We rely on AF_UNSPEC being 0. Yours isn't. Please tell us more!" #endif CTASSERT(AF_UNSPEC == 0); diff --git a/src/lib/net/address.h b/src/lib/net/address.h index 815fb02283..4984494939 100644 --- a/src/lib/net/address.h +++ b/src/lib/net/address.h @@ -177,7 +177,7 @@ tor_addr_to_mapped_ipv4h(const tor_addr_t *a) } /** Return the address family of <b>a</b>. Possible values are: - * AF_INET6, AF_INET, AF_UNSPEC. */ + * AF_INET6, AF_INET, AF_UNSPEC, AF_UNIX. */ static inline sa_family_t tor_addr_family(const tor_addr_t *a) { diff --git a/src/lib/net/network_sys.c b/src/lib/net/network_sys.c index 012fc56bba..f0421385b7 100644 --- a/src/lib/net/network_sys.c +++ b/src/lib/net/network_sys.c @@ -39,7 +39,7 @@ const subsys_fns_t sys_network = { .name = "network", /* Network depends on logging, and a lot of other modules depend on network. */ - .level = -80, + .level = -55, .supported = true, .initialize = subsys_network_initialize, .shutdown = subsys_network_shutdown, diff --git a/src/lib/net/resolve.c b/src/lib/net/resolve.c index df079d5db3..68a8c01ef4 100644 --- a/src/lib/net/resolve.c +++ b/src/lib/net/resolve.c @@ -372,11 +372,11 @@ static HT_HEAD(getaddrinfo_cache, cached_getaddrinfo_item_t) HT_PROTOTYPE(getaddrinfo_cache, cached_getaddrinfo_item_t, node, cached_getaddrinfo_item_hash, - cached_getaddrinfo_items_eq) + cached_getaddrinfo_items_eq); HT_GENERATE2(getaddrinfo_cache, cached_getaddrinfo_item_t, node, cached_getaddrinfo_item_hash, cached_getaddrinfo_items_eq, - 0.6, tor_reallocarray_, tor_free_) + 0.6, tor_reallocarray_, tor_free_); /** If true, don't try to cache getaddrinfo results. */ static int sandbox_getaddrinfo_cache_disabled = 0; diff --git a/src/lib/osinfo/uname.c b/src/lib/osinfo/uname.c index ac99726f51..f7f5ede307 100644 --- a/src/lib/osinfo/uname.c +++ b/src/lib/osinfo/uname.c @@ -27,6 +27,40 @@ static char uname_result[256]; /** True iff uname_result is set. */ static int uname_result_is_set = 0; +#ifdef _WIN32 +/** Table to map claimed windows versions into human-readable windows + * versions. */ +static struct { + unsigned major; + unsigned minor; + const char *client_version; + const char *server_version; +} win_version_table[] = { + /* This table must be sorted in descending order. + * Sources: + * https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions + * https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ + * ns-winnt-_osversioninfoexa#remarks + */ + /* Windows Server 2019 is indistinguishable from Windows Server 2016 + * using GetVersionEx(). + { 10, 0, NULL, "Windows Server 2019" }, */ + // clang-format off + { 10, 0, "Windows 10", "Windows Server 2016" }, + { 6, 3, "Windows 8.1", "Windows Server 2012 R2" }, + { 6, 2, "Windows 8", "Windows Server 2012" }, + { 6, 1, "Windows 7", "Windows Server 2008 R2" }, + { 6, 0, "Windows Vista", "Windows Server 2008" }, + { 5, 2, "Windows XP Professional", "Windows Server 2003" }, + /* Windows XP did not have a server version, but we need something here */ + { 5, 1, "Windows XP", "Windows XP Server" }, + { 5, 0, "Windows 2000 Professional", "Windows 2000 Server" }, + /* Earlier versions are not supported by GetVersionEx(). */ + { 0, 0, NULL, NULL } + // clang-format on +}; +#endif /* defined(_WIN32) */ + /** Return a pointer to a description of our platform. */ MOCK_IMPL(const char *, @@ -49,31 +83,6 @@ get_uname,(void)) int is_client = 0; int is_server = 0; const char *plat = NULL; - static struct { - unsigned major; unsigned minor; - const char *client_version; const char *server_version; - } win_version_table[] = { - /* This table must be sorted in descending order. - * Sources: - * https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions - * https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ - * ns-winnt-_osversioninfoexa#remarks - */ - /* Windows Server 2019 is indistinguishable from Windows Server 2016 - * using GetVersionEx(). - { 10, 0, NULL, "Windows Server 2019" }, */ - { 10, 0, "Windows 10", "Windows Server 2016" }, - { 6, 3, "Windows 8.1", "Windows Server 2012 R2" }, - { 6, 2, "Windows 8", "Windows Server 2012" }, - { 6, 1, "Windows 7", "Windows Server 2008 R2" }, - { 6, 0, "Windows Vista", "Windows Server 2008" }, - { 5, 2, "Windows XP Professional", "Windows Server 2003" }, - /* Windows XP did not have a server version, but we need something here */ - { 5, 1, "Windows XP", "Windows XP Server" }, - { 5, 0, "Windows 2000 Professional", "Windows 2000 Server" }, - /* Earlier versions are not supported by GetVersionEx(). */ - { 0, 0, NULL, NULL } - }; memset(&info, 0, sizeof(info)); info.dwOSVersionInfoSize = sizeof(info); if (! GetVersionEx((LPOSVERSIONINFO)&info)) { diff --git a/src/lib/process/include.am b/src/lib/process/include.am index af5f99617b..18876b3f54 100644 --- a/src/lib/process/include.am +++ b/src/lib/process/include.am @@ -16,8 +16,7 @@ src_lib_libtor_process_a_SOURCES = \ src/lib/process/process_win32.c \ src/lib/process/restrict.c \ src/lib/process/setuid.c \ - src/lib/process/waitpid.c \ - src/lib/process/winprocess_sys.c + src/lib/process/waitpid.c src_lib_libtor_process_testing_a_SOURCES = \ $(src_lib_libtor_process_a_SOURCES) @@ -35,5 +34,4 @@ noinst_HEADERS += \ src/lib/process/process_win32.h \ src/lib/process/restrict.h \ src/lib/process/setuid.h \ - src/lib/process/waitpid.h \ - src/lib/process/winprocess_sys.h + src/lib/process/waitpid.h diff --git a/src/lib/process/process_sys.c b/src/lib/process/process_sys.c index 283064cbfe..015ffadead 100644 --- a/src/lib/process/process_sys.c +++ b/src/lib/process/process_sys.c @@ -26,7 +26,7 @@ subsys_process_shutdown(void) const subsys_fns_t sys_process = { .name = "process", - .level = -35, + .level = -18, .supported = true, .initialize = subsys_process_initialize, .shutdown = subsys_process_shutdown diff --git a/src/lib/process/waitpid.c b/src/lib/process/waitpid.c index 89ffe9fcfe..33798f65f0 100644 --- a/src/lib/process/waitpid.c +++ b/src/lib/process/waitpid.c @@ -58,9 +58,9 @@ process_map_entries_eq_(const waitpid_callback_t *a, static HT_HEAD(process_map, waitpid_callback_t) process_map = HT_INITIALIZER(); HT_PROTOTYPE(process_map, waitpid_callback_t, node, process_map_entry_hash_, - process_map_entries_eq_) + process_map_entries_eq_); HT_GENERATE2(process_map, waitpid_callback_t, node, process_map_entry_hash_, - process_map_entries_eq_, 0.6, tor_reallocarray_, tor_free_) + process_map_entries_eq_, 0.6, tor_reallocarray_, tor_free_); /** * Begin monitoring the child pid <b>pid</b> to see if we get a SIGCHLD for diff --git a/src/lib/thread/compat_threads.c b/src/lib/thread/compat_threads.c index d56e8a3f76..21125bddad 100644 --- a/src/lib/thread/compat_threads.c +++ b/src/lib/thread/compat_threads.c @@ -130,8 +130,6 @@ subsys_threads_initialize(void) const subsys_fns_t sys_threads = { .name = "threads", .supported = true, - /* Threads is used by logging, which is a diagnostic feature, we want it to - * init right after low-level error handling and approx time. */ - .level = -95, + .level = -89, .initialize = subsys_threads_initialize, }; diff --git a/src/test/conf_examples/large_1/expected b/src/test/conf_examples/large_1/expected index 5866f5823e..99a12ffc84 100644 --- a/src/test/conf_examples/large_1/expected +++ b/src/test/conf_examples/large_1/expected @@ -15,7 +15,6 @@ CellStatistics 1 CircuitBuildTimeout 200 CircuitsAvailableTimeout 10 CircuitStreamTimeout 20 -ClientAutoIPv6ORPort 1 ClientOnly 1 ClientPreferIPv6DirPort 1 ClientPreferIPv6ORPort 1 diff --git a/src/test/conf_examples/large_1/expected_no_dirauth b/src/test/conf_examples/large_1/expected_no_dirauth index 17c11f85fc..26a33bdc7c 100644 --- a/src/test/conf_examples/large_1/expected_no_dirauth +++ b/src/test/conf_examples/large_1/expected_no_dirauth @@ -15,7 +15,6 @@ CellStatistics 1 CircuitBuildTimeout 200 CircuitsAvailableTimeout 10 CircuitStreamTimeout 20 -ClientAutoIPv6ORPort 1 ClientOnly 1 ClientPreferIPv6DirPort 1 ClientPreferIPv6ORPort 1 diff --git a/src/test/conf_examples/large_1/torrc b/src/test/conf_examples/large_1/torrc index e99acd9fb7..20ddf00e16 100644 --- a/src/test/conf_examples/large_1/torrc +++ b/src/test/conf_examples/large_1/torrc @@ -16,7 +16,6 @@ CircuitBuildTimeout 200 CircuitPadding 1 CircuitsAvailableTimeout 10 CircuitStreamTimeout 20 -ClientAutoIPv6ORPort 1 ClientOnly 1 ClientPreferIPv6DirPort 1 ClientPreferIPv6ORPort 1 diff --git a/src/test/conf_examples/relay_30/error_no_dirauth_relay b/src/test/conf_examples/relay_30/error_no_dirauth_relay new file mode 100644 index 0000000000..9f9c0fd8f3 --- /dev/null +++ b/src/test/conf_examples/relay_30/error_no_dirauth_relay @@ -0,0 +1 @@ +This tor was built with relay mode disabled. diff --git a/src/test/conf_examples/relay_30/expected b/src/test/conf_examples/relay_30/expected new file mode 100644 index 0000000000..3a4e9feb3f --- /dev/null +++ b/src/test/conf_examples/relay_30/expected @@ -0,0 +1,2 @@ +Nickname Unnamed +ORPort auto diff --git a/src/test/conf_examples/relay_30/expected_log b/src/test/conf_examples/relay_30/expected_log new file mode 100644 index 0000000000..d5478c1a15 --- /dev/null +++ b/src/test/conf_examples/relay_30/expected_log @@ -0,0 +1 @@ +Your ContactInfo config option is not set
\ No newline at end of file diff --git a/src/test/conf_examples/relay_30/torrc b/src/test/conf_examples/relay_30/torrc new file mode 100644 index 0000000000..bf8487fe16 --- /dev/null +++ b/src/test/conf_examples/relay_30/torrc @@ -0,0 +1,3 @@ +# Relay tests +# default (IPv4) ORPort auto +ORPort auto diff --git a/src/test/conf_examples/relay_31/error_no_dirauth_relay b/src/test/conf_examples/relay_31/error_no_dirauth_relay new file mode 100644 index 0000000000..9f9c0fd8f3 --- /dev/null +++ b/src/test/conf_examples/relay_31/error_no_dirauth_relay @@ -0,0 +1 @@ +This tor was built with relay mode disabled. diff --git a/src/test/conf_examples/relay_31/expected b/src/test/conf_examples/relay_31/expected new file mode 100644 index 0000000000..9a40cdd588 --- /dev/null +++ b/src/test/conf_examples/relay_31/expected @@ -0,0 +1,3 @@ +DirPort auto +Nickname Unnamed +ORPort auto diff --git a/src/test/conf_examples/relay_31/expected_log b/src/test/conf_examples/relay_31/expected_log new file mode 100644 index 0000000000..d5478c1a15 --- /dev/null +++ b/src/test/conf_examples/relay_31/expected_log @@ -0,0 +1 @@ +Your ContactInfo config option is not set
\ No newline at end of file diff --git a/src/test/conf_examples/relay_31/torrc b/src/test/conf_examples/relay_31/torrc new file mode 100644 index 0000000000..e662bb71b3 --- /dev/null +++ b/src/test/conf_examples/relay_31/torrc @@ -0,0 +1,4 @@ +# Relay tests +# default (IPv4) ORPort and DirPort auto +ORPort auto +DirPort auto diff --git a/src/test/conf_examples/relay_32/error_no_dirauth_relay b/src/test/conf_examples/relay_32/error_no_dirauth_relay new file mode 100644 index 0000000000..9f9c0fd8f3 --- /dev/null +++ b/src/test/conf_examples/relay_32/error_no_dirauth_relay @@ -0,0 +1 @@ +This tor was built with relay mode disabled. diff --git a/src/test/conf_examples/relay_32/expected b/src/test/conf_examples/relay_32/expected new file mode 100644 index 0000000000..14b36c8259 --- /dev/null +++ b/src/test/conf_examples/relay_32/expected @@ -0,0 +1,3 @@ +Nickname Unnamed +ORPort auto +ORPort [::1]:auto diff --git a/src/test/conf_examples/relay_32/expected_log b/src/test/conf_examples/relay_32/expected_log new file mode 100644 index 0000000000..d5478c1a15 --- /dev/null +++ b/src/test/conf_examples/relay_32/expected_log @@ -0,0 +1 @@ +Your ContactInfo config option is not set
\ No newline at end of file diff --git a/src/test/conf_examples/relay_32/torrc b/src/test/conf_examples/relay_32/torrc new file mode 100644 index 0000000000..95a66c4852 --- /dev/null +++ b/src/test/conf_examples/relay_32/torrc @@ -0,0 +1,4 @@ +# Relay tests +# default (IPv4) ORPort auto and IPv6 ORPort auto +ORPort auto +ORPort [::1]:auto diff --git a/src/test/conf_examples/relay_33/error_no_dirauth_relay b/src/test/conf_examples/relay_33/error_no_dirauth_relay new file mode 100644 index 0000000000..9f9c0fd8f3 --- /dev/null +++ b/src/test/conf_examples/relay_33/error_no_dirauth_relay @@ -0,0 +1 @@ +This tor was built with relay mode disabled. diff --git a/src/test/conf_examples/relay_33/expected b/src/test/conf_examples/relay_33/expected new file mode 100644 index 0000000000..22567cbe2e --- /dev/null +++ b/src/test/conf_examples/relay_33/expected @@ -0,0 +1,3 @@ +Nickname Unnamed +ORPort 127.0.0.1:auto +ORPort [::1]:auto diff --git a/src/test/conf_examples/relay_33/expected_log b/src/test/conf_examples/relay_33/expected_log new file mode 100644 index 0000000000..d5478c1a15 --- /dev/null +++ b/src/test/conf_examples/relay_33/expected_log @@ -0,0 +1 @@ +Your ContactInfo config option is not set
\ No newline at end of file diff --git a/src/test/conf_examples/relay_33/torrc b/src/test/conf_examples/relay_33/torrc new file mode 100644 index 0000000000..44d16ad31a --- /dev/null +++ b/src/test/conf_examples/relay_33/torrc @@ -0,0 +1,4 @@ +# Relay tests +# explicit IPv4 ORPort auto and IPv6 ORPort auto +ORPort 127.0.0.1:auto +ORPort [::1]:auto diff --git a/src/test/conf_examples/relay_34/error_no_dirauth_relay b/src/test/conf_examples/relay_34/error_no_dirauth_relay new file mode 100644 index 0000000000..9f9c0fd8f3 --- /dev/null +++ b/src/test/conf_examples/relay_34/error_no_dirauth_relay @@ -0,0 +1 @@ +This tor was built with relay mode disabled. diff --git a/src/test/conf_examples/relay_34/expected b/src/test/conf_examples/relay_34/expected new file mode 100644 index 0000000000..bccde684f3 --- /dev/null +++ b/src/test/conf_examples/relay_34/expected @@ -0,0 +1,4 @@ +DirPort 127.0.0.1:auto +Nickname Unnamed +ORPort 127.0.0.1:auto +ORPort [::1]:auto diff --git a/src/test/conf_examples/relay_34/expected_log b/src/test/conf_examples/relay_34/expected_log new file mode 100644 index 0000000000..d5478c1a15 --- /dev/null +++ b/src/test/conf_examples/relay_34/expected_log @@ -0,0 +1 @@ +Your ContactInfo config option is not set
\ No newline at end of file diff --git a/src/test/conf_examples/relay_34/torrc b/src/test/conf_examples/relay_34/torrc new file mode 100644 index 0000000000..01010a5c38 --- /dev/null +++ b/src/test/conf_examples/relay_34/torrc @@ -0,0 +1,5 @@ +# Relay tests +# explicit IPv4 ORPort and DirPort auto and IPv6 ORPort auto +ORPort 127.0.0.1:auto +ORPort [::1]:auto +DirPort 127.0.0.1:auto diff --git a/src/test/fuzz/fuzz_hsdescv3.c b/src/test/fuzz/fuzz_hsdescv3.c index 3955241389..8d7eab1a8d 100644 --- a/src/test/fuzz/fuzz_hsdescv3.c +++ b/src/test/fuzz/fuzz_hsdescv3.c @@ -85,12 +85,12 @@ int fuzz_main(const uint8_t *data, size_t sz) { hs_descriptor_t *desc = NULL; - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; char *fuzzing_data = tor_memdup_nulterm(data, sz); - memset(subcredential, 'A', sizeof(subcredential)); + memset(&subcredential, 'A', sizeof(subcredential)); - hs_desc_decode_descriptor(fuzzing_data, subcredential, NULL, &desc); + hs_desc_decode_descriptor(fuzzing_data, &subcredential, NULL, &desc); if (desc) { log_debug(LD_GENERAL, "Decoding okay"); hs_descriptor_free(desc); @@ -101,4 +101,3 @@ fuzz_main(const uint8_t *data, size_t sz) tor_free(fuzzing_data); return 0; } - diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c index e8b99aaac8..5116fc7169 100644 --- a/src/test/hs_test_helpers.c +++ b/src/test/hs_test_helpers.c @@ -13,9 +13,22 @@ #include "feature/hs/hs_service.h" #include "test/hs_test_helpers.h" +/** + * Create an introduction point taken straight out of an HSv3 descriptor. + * + * Use 'signing_kp' to sign the introduction point certificates. + * + * If 'intro_auth_kp' is provided use that as the introduction point + * authentication keypair, otherwise generate one on the fly. + * + * If 'intro_enc_kp' is provided use that as the introduction point encryption + * keypair, otherwise generate one on the fly. + */ hs_desc_intro_point_t * hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now, - const char *addr, int legacy) + const char *addr, int legacy, + const ed25519_keypair_t *intro_auth_kp, + const curve25519_keypair_t *intro_enc_kp) { int ret; ed25519_keypair_t auth_kp; @@ -56,8 +69,12 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now, smartlist_add(ip->link_specifiers, ls_ip); } - ret = ed25519_keypair_generate(&auth_kp, 0); - tt_int_op(ret, OP_EQ, 0); + if (intro_auth_kp) { + memcpy(&auth_kp, intro_auth_kp, sizeof(ed25519_keypair_t)); + } else { + ret = ed25519_keypair_generate(&auth_kp, 0); + tt_int_op(ret, OP_EQ, 0); + } ip->auth_key_cert = tor_cert_create(signing_kp, CERT_TYPE_AUTH_HS_IP_KEY, &auth_kp.pubkey, now, HS_DESC_CERT_LIFETIME, @@ -85,8 +102,12 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now, ed25519_keypair_t ed25519_kp; tor_cert_t *cross_cert; - ret = curve25519_keypair_generate(&curve25519_kp, 0); - tt_int_op(ret, OP_EQ, 0); + if (intro_enc_kp) { + memcpy(&curve25519_kp, intro_enc_kp, sizeof(curve25519_keypair_t)); + } else { + ret = curve25519_keypair_generate(&curve25519_kp, 0); + tt_int_op(ret, OP_EQ, 0); + } ed25519_keypair_from_curve25519_keypair(&ed25519_kp, &signbit, &curve25519_kp); cross_cert = tor_cert_create(signing_kp, CERT_TYPE_CROSS_HS_IP_KEYS, @@ -95,6 +116,8 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now, CERT_FLAG_INCLUDE_SIGNING_KEY); tt_assert(cross_cert); ip->enc_key_cert = cross_cert; + memcpy(ip->enc_key.public_key, curve25519_kp.pubkey.public_key, + CURVE25519_PUBKEY_LEN); } intro_point = ip; @@ -140,7 +163,7 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip, desc->plaintext_data.lifetime_sec = 3 * 60 * 60; hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey, - desc->subcredential); + &desc->subcredential); /* Setup superencrypted data section. */ ret = curve25519_keypair_generate(&auth_ephemeral_kp, 0); @@ -165,13 +188,17 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip, if (!no_ip) { /* Add four intro points. */ smartlist_add(desc->encrypted_data.intro_points, - hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0)); + hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0, + NULL, NULL)); smartlist_add(desc->encrypted_data.intro_points, - hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0)); + hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0, + NULL, NULL)); smartlist_add(desc->encrypted_data.intro_points, - hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1)); + hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1, + NULL, NULL)); smartlist_add(desc->encrypted_data.intro_points, - hs_helper_build_intro_point(signing_kp, now, "5.6.7.8", 1)); + hs_helper_build_intro_point(signing_kp, now, "5.6.7.8", 1, + NULL, NULL)); } descp = desc; @@ -186,7 +213,7 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip, * an HS. Used to decrypt descriptors in unittests. */ void hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp, - uint8_t *subcred_out) + hs_subcredential_t *subcred_out) { ed25519_keypair_t blinded_kp; uint64_t current_time_period = hs_get_time_period_num(approx_time()); @@ -233,7 +260,7 @@ hs_helper_build_hs_desc_with_client_auth( memcpy(&desc->superencrypted_data.auth_ephemeral_pubkey, &auth_ephemeral_kp.pubkey, sizeof(curve25519_public_key_t)); - hs_desc_build_authorized_client(desc->subcredential, client_pk, + hs_desc_build_authorized_client(&desc->subcredential, client_pk, &auth_ephemeral_kp.seckey, descriptor_cookie, desc_client); smartlist_add(desc->superencrypted_data.clients, desc_client); diff --git a/src/test/hs_test_helpers.h b/src/test/hs_test_helpers.h index a01fd45d63..23d11f2a4a 100644 --- a/src/test/hs_test_helpers.h +++ b/src/test/hs_test_helpers.h @@ -8,9 +8,11 @@ #include "feature/hs/hs_descriptor.h" /* Set of functions to help build and test descriptors. */ -hs_desc_intro_point_t *hs_helper_build_intro_point( - const ed25519_keypair_t *signing_kp, time_t now, - const char *addr, int legacy); +hs_desc_intro_point_t * +hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now, + const char *addr, int legacy, + const ed25519_keypair_t *intro_auth_kp, + const curve25519_keypair_t *intro_enc_kp); hs_descriptor_t *hs_helper_build_hs_desc_no_ip( const ed25519_keypair_t *signing_kp); hs_descriptor_t *hs_helper_build_hs_desc_with_ip( @@ -21,12 +23,11 @@ hs_descriptor_t *hs_helper_build_hs_desc_with_client_auth( const ed25519_keypair_t *signing_kp); void hs_helper_desc_equal(const hs_descriptor_t *desc1, const hs_descriptor_t *desc2); -void -hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp, - uint8_t *subcred_out); +struct hs_subcredential_t; +void hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp, + struct hs_subcredential_t *subcred_out); void hs_helper_add_client_auth(const ed25519_public_key_t *service_pk, const curve25519_secret_key_t *client_sk); #endif /* !defined(TOR_HS_TEST_HELPERS_H) */ - diff --git a/src/test/include.am b/src/test/include.am index 90e50752ce..de927836d6 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -61,13 +61,25 @@ TESTS += src/test/test src/test/test-slow src/test/test-memwipe \ $(TESTSCRIPTS) # These flavors are run using automake's test-driver and test-network.sh -TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-v2-min hs-v3-min \ - single-onion-v23 + +# run a quick test or two +# this test only uses IPv4 +TEST_CHUTNEY_FLAVOR_QUICK = bridges+hs-v23 # only run if we can ping6 ::1 (localhost) +TEST_CHUTNEY_FLAVOR_QUICK_IPV6 = single-onion-v23-ipv6-md + +# run a basic set of tests, which only use IPv4 +TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-v23-min single-onion-v23 + +# only run if we can ping ::1 (localhost) TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-v23-ipv6-md \ single-onion-v23-ipv6-md + # only run if we can find a stable (or simply another) version of tor -TEST_CHUTNEY_FLAVORS_MIXED = mixed+hs-v2 +TEST_CHUTNEY_FLAVORS_MIXED = mixed+hs-v23 + +# only run if IPv6 and mixed networks are run +TEST_CHUTNEY_FLAVORS_IPV6_MIXED = mixed+hs-v23-ipv6 ### This is a lovely feature, but it requires automake >= 1.12, and Tor ### doesn't require that yet. @@ -167,6 +179,7 @@ src_test_test_SOURCES += \ src/test/test_hs_client.c \ src/test/test_hs_intropoint.c \ src/test/test_hs_control.c \ + src/test/test_hs_ob.c \ src/test/test_handles.c \ src/test/test_hs_cache.c \ src/test/test_hs_descriptor.c \ diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h index e2ddf09466..c2d71c6bcd 100644 --- a/src/test/log_test_helpers.h +++ b/src/test/log_test_helpers.h @@ -78,7 +78,7 @@ void mock_dump_saved_logs(void); mock_saved_log_n_entries() == 1, \ ("expected log to contain exactly 1 message \"%s\"", \ str)); \ - } while (0); + } while (0) #define expect_single_log_msg_containing(str) \ do { \ @@ -86,30 +86,30 @@ void mock_dump_saved_logs(void); mock_saved_log_n_entries() == 1 , \ ("expected log to contain 1 message, containing \"%s\"",\ str)); \ - } while (0); + } while (0) #define expect_no_log_msg(str) \ assert_log_predicate(!mock_saved_log_has_message(str), \ - ("expected log to not contain \"%s\"",str)); + ("expected log to not contain \"%s\"",str)) #define expect_no_log_msg_containing(str) \ assert_log_predicate(!mock_saved_log_has_message_containing(str), \ - ("expected log to not contain \"%s\"", str)); + ("expected log to not contain \"%s\"", str)) #define expect_log_severity(severity) \ assert_log_predicate(mock_saved_log_has_severity(severity), \ - ("expected log to contain severity " # severity)); + ("expected log to contain severity " # severity)) #define expect_no_log_severity(severity) \ assert_log_predicate(!mock_saved_log_has_severity(severity), \ - ("expected log to not contain severity " # severity)); + ("expected log to not contain severity " # severity)) #define expect_log_entry() \ assert_log_predicate(mock_saved_log_has_entry(), \ - ("expected log to contain entries")); + ("expected log to contain entries")) #define expect_no_log_entry() \ assert_log_predicate(!mock_saved_log_has_entry(), \ - ("expected log to not contain entries")); + ("expected log to not contain entries")) #endif /* !defined(TOR_LOG_TEST_HELPERS_H) */ diff --git a/src/test/test.c b/src/test/test.c index 1742f1d952..4b6082ce4f 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -721,6 +721,7 @@ struct testgroup_t testgroups[] = { { "hs_dos/", hs_dos_tests }, { "hs_intropoint/", hs_intropoint_tests }, { "hs_ntor/", hs_ntor_tests }, + { "hs_ob/", hs_ob_tests }, { "hs_service/", hs_service_tests }, { "introduce/", introduce_tests }, { "keypin/", keypin_tests }, diff --git a/src/test/test.h b/src/test/test.h index 63e2faff95..18987719d0 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -141,6 +141,7 @@ extern struct testcase_t hs_descriptor[]; extern struct testcase_t hs_dos_tests[]; extern struct testcase_t hs_intropoint_tests[]; extern struct testcase_t hs_ntor_tests[]; +extern struct testcase_t hs_ob_tests[]; extern struct testcase_t hs_service_tests[]; extern struct testcase_t hs_tests[]; extern struct testcase_t introduce_tests[]; diff --git a/src/test/test_address_set.c b/src/test/test_address_set.c index 3fcf8c4d13..829ecd79e8 100644 --- a/src/test/test_address_set.c +++ b/src/test/test_address_set.c @@ -4,6 +4,7 @@ #include "core/or/or.h" #include "lib/crypt_ops/crypto_rand.h" #include "core/or/address_set.h" +#include "feature/nodelist/dirlist.h" #include "feature/nodelist/microdesc.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/nodelist.h" @@ -31,6 +32,12 @@ mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f) return dummy_ns; } +static void +mock_dirlist_add_trusted_dir_addresses(void) +{ + return; +} + /* Number of address a single node_t can have. Default to the production * value. This is to control the size of the bloom filter. */ static int addr_per_node = 2; @@ -98,6 +105,8 @@ test_nodelist(void *arg) mock_networkstatus_get_latest_consensus_by_flavor); MOCK(get_estimated_address_per_node, mock_get_estimated_address_per_node); + MOCK(dirlist_add_trusted_dir_addresses, + mock_dirlist_add_trusted_dir_addresses); dummy_ns = tor_malloc_zero(sizeof(*dummy_ns)); dummy_ns->flavor = FLAV_MICRODESC; @@ -113,7 +122,10 @@ test_nodelist(void *arg) * (the_nodelist->node_addrs) so we will fail the contain test rarely. */ addr_per_node = 1024; - /* No node no nothing. The lookups should be empty. */ + /* No node no nothing. The lookups should be empty. We've mocked the + * dirlist_add_trusted_dir_addresses in order for _no_ authorities to be + * added to the filter else it makes this test to trigger many false + * positive. */ nodelist_set_consensus(dummy_ns); /* The address set should be empty. */ @@ -167,6 +179,7 @@ test_nodelist(void *arg) UNMOCK(networkstatus_get_latest_consensus); UNMOCK(networkstatus_get_latest_consensus_by_flavor); UNMOCK(get_estimated_address_per_node); + UNMOCK(dirlist_add_trusted_dir_addresses); } struct testcase_t address_set_tests[] = { diff --git a/src/test/test_bwmgt.c b/src/test/test_bwmgt.c index 1da3796981..117783cafc 100644 --- a/src/test/test_bwmgt.c +++ b/src/test/test_bwmgt.c @@ -6,18 +6,70 @@ * \brief tests for bandwidth management / token bucket functions */ +#define CONFIG_PRIVATE +#define CONNECTION_PRIVATE +#define DIRAUTH_SYS_PRIVATE #define TOKEN_BUCKET_PRIVATE #include "core/or/or.h" -#include "test/test.h" +#include "app/config/config.h" +#include "core/mainloop/connection.h" +#include "feature/dirauth/dirauth_sys.h" +#include "feature/dircommon/directory.h" +#include "feature/nodelist/microdesc.h" +#include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/nodelist.h" +#include "feature/nodelist/routerlist.h" +#include "lib/crypt_ops/crypto_rand.h" #include "lib/evloop/token_bucket.h" +#include "test/test.h" +#include "test/test_helpers.h" + +#include "app/config/or_options_st.h" +#include "core/or/connection_st.h" +#include "feature/dirauth/dirauth_options_st.h" +#include "feature/nodelist/microdesc_st.h" +#include "feature/nodelist/networkstatus_st.h" +#include "feature/nodelist/routerinfo_st.h" +#include "feature/nodelist/routerstatus_st.h" // an imaginary time, in timestamp units. Chosen so it will roll over. static const uint32_t START_TS = UINT32_MAX-10; static const int32_t KB = 1024; static const uint32_t GB = (UINT64_C(1) << 30); +static or_options_t mock_options; + +static const or_options_t * +mock_get_options(void) +{ + return &mock_options; +} + +static networkstatus_t *dummy_ns = NULL; +static networkstatus_t * +mock_networkstatus_get_latest_consensus(void) +{ + return dummy_ns; +} + +static networkstatus_t * +mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f) +{ + tor_assert(f == FLAV_MICRODESC); + return dummy_ns; +} + +/* Number of address a single node_t can have. Default to the production + * value. This is to control the size of the bloom filter. */ +static int addr_per_node = 2; +static int +mock_get_estimated_address_per_node(void) +{ + return addr_per_node; +} + static void test_bwmgt_token_buf_init(void *arg) { @@ -220,8 +272,167 @@ test_bwmgt_token_buf_helpers(void *arg) ; } +static void +test_bwmgt_dir_conn_global_write_low(void *arg) +{ + bool ret; + int addr_family; + connection_t *conn = NULL; + routerstatus_t *rs = NULL; microdesc_t *md = NULL; routerinfo_t *ri = NULL; + tor_addr_t relay_addr; + dirauth_options_t *dirauth_opts = NULL; + + (void) arg; + + memset(&mock_options, 0, sizeof(or_options_t)); + MOCK(networkstatus_get_latest_consensus, + mock_networkstatus_get_latest_consensus); + MOCK(networkstatus_get_latest_consensus_by_flavor, + mock_networkstatus_get_latest_consensus_by_flavor); + MOCK(get_estimated_address_per_node, + mock_get_estimated_address_per_node); + + /* + * The following is rather complex but that is what it takes to add a dummy + * consensus with a valid routerlist which will populate our node address + * set that we need to lookup to test the known relay code path. + * + * We MUST do that before we MOCK(get_options) else it is another world of + * complexity. + */ + + /* This will be the address of our relay. */ + tor_addr_parse(&relay_addr, "1.2.3.4"); + + /* We'll now add a relay into our routerlist and see if we let it. */ + dummy_ns = tor_malloc_zero(sizeof(*dummy_ns)); + dummy_ns->flavor = FLAV_MICRODESC; + dummy_ns->routerstatus_list = smartlist_new(); + + md = tor_malloc_zero(sizeof(*md)); + ri = tor_malloc_zero(sizeof(*ri)); + rs = tor_malloc_zero(sizeof(*rs)); + crypto_rand(rs->identity_digest, sizeof(rs->identity_digest)); + crypto_rand(md->digest, sizeof(md->digest)); + memcpy(rs->descriptor_digest, md->digest, DIGEST256_LEN); + + /* Set IP address. */ + rs->addr = tor_addr_to_ipv4h(&relay_addr); + ri->addr = rs->addr; + /* Add the rs to the consensus becoming a node_t. */ + smartlist_add(dummy_ns->routerstatus_list, rs); + + /* Add all configured authorities (hardcoded) before we set the consensus so + * the address set exists. */ + ret = consider_adding_dir_servers(&mock_options, &mock_options); + tt_int_op(ret, OP_EQ, 0); + + /* This will make the nodelist bloom filter very large + * (the_nodelist->node_addrs) so we will fail the contain test rarely. */ + addr_per_node = 1024; + + nodelist_set_consensus(dummy_ns); + + dirauth_opts = tor_malloc_zero(sizeof(dirauth_options_t)); + dirauth_opts->AuthDirRejectRequestsUnderLoad = 0; + dirauth_set_options(dirauth_opts); + + /* Ok, now time to control which options we use. */ + MOCK(get_options, mock_get_options); + + /* Set ourselves as an authoritative dir. */ + mock_options.AuthoritativeDir = 1; + mock_options.V3AuthoritativeDir = 1; + mock_options.UseDefaultFallbackDirs = 0; + + /* This will set our global bucket to 1 byte and thus we will hit the + * banwdith limit in our test. */ + mock_options.BandwidthRate = 1; + mock_options.BandwidthBurst = 1; + + /* Else an IPv4 address screams. */ + mock_options.ClientUseIPv4 = 1; + mock_options.ClientUseIPv6 = 1; + + /* Initialize the global buckets. */ + connection_bucket_init(); + + /* The address "127.0.0.1" is set with this helper. */ + conn = test_conn_get_connection(DIR_CONN_STATE_MIN_, CONN_TYPE_DIR, + DIR_PURPOSE_MIN_); + tt_assert(conn); + + /* First try a non authority non relay IP thus a client but we are not + * configured to reject requests under load so we should get a false value + * that our limit is _not_ low. */ + addr_family = tor_addr_parse(&conn->addr, "1.1.1.1"); + tt_int_op(addr_family, OP_EQ, AF_INET); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 0); + + /* Now, we will reject requests under load so try again a non authority non + * relay IP thus a client. We should get a warning that our limit is too + * low. */ + dirauth_opts->AuthDirRejectRequestsUnderLoad = 1; + + addr_family = tor_addr_parse(&conn->addr, "1.1.1.1"); + tt_int_op(addr_family, OP_EQ, AF_INET); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 1); + + /* Now, lets try with a connection address from moria1. It should always + * pass even though our limit is too low. */ + addr_family = tor_addr_parse(&conn->addr, "128.31.0.39"); + tt_int_op(addr_family, OP_EQ, AF_INET); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 0); + + /* IPv6 testing of gabelmoo. */ + addr_family = tor_addr_parse(&conn->addr, "[2001:638:a000:4140::ffff:189]"); + tt_int_op(addr_family, OP_EQ, AF_INET6); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 0); + + /* Lets retry with a known relay address. It should pass. Possible due to + * our consensus setting above. */ + memcpy(&conn->addr, &relay_addr, sizeof(tor_addr_t)); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 0); + + /* Lets retry with a random IP that is not an authority nor a relay. */ + addr_family = tor_addr_parse(&conn->addr, "1.2.3.4"); + tt_int_op(addr_family, OP_EQ, AF_INET); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 0); + + /* Finally, just make sure it still denies an IP if we are _not_ a v3 + * directory authority. */ + mock_options.V3AuthoritativeDir = 0; + addr_family = tor_addr_parse(&conn->addr, "1.2.3.4"); + tt_int_op(addr_family, OP_EQ, AF_INET); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 1); + + /* Random IPv6 should not be allowed. */ + addr_family = tor_addr_parse(&conn->addr, "[CAFE::ACAB]"); + tt_int_op(addr_family, OP_EQ, AF_INET6); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 1); + + done: + connection_free_minimal(conn); + routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md); + smartlist_clear(dummy_ns->routerstatus_list); + networkstatus_vote_free(dummy_ns); + + UNMOCK(get_estimated_address_per_node); + UNMOCK(networkstatus_get_latest_consensus); + UNMOCK(networkstatus_get_latest_consensus_by_flavor); + UNMOCK(get_options); +} + #define BWMGT(name) \ - { #name, test_bwmgt_ ## name , 0, NULL, NULL } + { #name, test_bwmgt_ ## name , TT_FORK, NULL, NULL } struct testcase_t bwmgt_tests[] = { BWMGT(token_buf_init), @@ -229,5 +440,7 @@ struct testcase_t bwmgt_tests[] = { BWMGT(token_buf_dec), BWMGT(token_buf_refill), BWMGT(token_buf_helpers), + + BWMGT(dir_conn_global_write_low), END_OF_TESTCASES }; diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 70920c0c52..10d78abc35 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -114,6 +114,14 @@ test_new_route_len_unhandled_exit(void *arg) int r; (void)arg; +#ifdef ALL_BUGS_ARE_FATAL + /* Coverity (and maybe clang analyser) complain that the code following + * tt_skip() is unconditionally unreachable. */ +#if !defined(__COVERITY__) && !defined(__clang_analyzer__) + tt_skip(); +#endif +#endif + MOCK(count_acceptable_nodes, mock_count_acceptable_nodes); tor_capture_bugs_(1); diff --git a/src/test/test_config.c b/src/test/test_config.c index ee277104fb..9ccdbc72c0 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -4956,6 +4956,71 @@ test_config_parse_port_config__ports__server_options(void *data) } static void +test_config_get_first_advertised(void *data) +{ + (void)data; + int r, w=0, n=0; + char *msg=NULL; + or_options_t *opts = options_new(); + int port; + const tor_addr_t *addr; + + // no ports are configured? We get NULL. + port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + AF_INET); + tt_int_op(port, OP_EQ, 0); + addr = get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, + AF_INET); + tt_ptr_op(addr, OP_EQ, NULL); + + port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + AF_INET6); + tt_int_op(port, OP_EQ, 0); + addr = get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, + AF_INET6); + tt_ptr_op(addr, OP_EQ, NULL); + + config_line_append(&opts->ORPort_lines, "ORPort", "[1234::5678]:8080"); + config_line_append(&opts->ORPort_lines, "ORPort", + "1.2.3.4:9999 noadvertise"); + config_line_append(&opts->ORPort_lines, "ORPort", + "5.6.7.8:9911 nolisten"); + + r = parse_ports(opts, 0, &msg, &n, &w); + tt_assert(r == 0); + + // UNSPEC gets us nothing. + port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + AF_UNSPEC); + tt_int_op(port, OP_EQ, 0); + addr = get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, + AF_UNSPEC); + tt_ptr_op(addr, OP_EQ, NULL); + + // Try AF_INET. + port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + AF_INET); + tt_int_op(port, OP_EQ, 9911); + addr = get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, + AF_INET); + tt_ptr_op(addr, OP_NE, NULL); + tt_str_op(fmt_addrport(addr,port), OP_EQ, "5.6.7.8:9911"); + + // Try AF_INET6 + port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + AF_INET6); + tt_int_op(port, OP_EQ, 8080); + addr = get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, + AF_INET6); + tt_ptr_op(addr, OP_NE, NULL); + tt_str_op(fmt_addrport(addr,port), OP_EQ, "[1234::5678]:8080"); + + done: + or_options_free(opts); + config_free_all(); +} + +static void test_config_parse_log_severity(void *data) { int ret; @@ -6203,6 +6268,7 @@ struct testcase_t config_tests[] = { CONFIG_TEST(parse_port_config__ports__no_ports_given, 0), CONFIG_TEST(parse_port_config__ports__server_options, 0), CONFIG_TEST(parse_port_config__ports__ports_given, 0), + CONFIG_TEST(get_first_advertised, TT_FORK), CONFIG_TEST(parse_log_severity, 0), CONFIG_TEST(include_limit, 0), CONFIG_TEST(include_does_not_exist, 0), diff --git a/src/test/test_connection.c b/src/test/test_connection.c index 7ed831f7d8..fca4d884bb 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -967,9 +967,14 @@ test_failed_orconn_tracker(void *arg) #define CONNECTION_TESTCASE(name, fork, setup) \ { #name, test_conn_##name, fork, &setup, NULL } +#define STR(x) #x /* where arg is an expression (constant, variable, compound expression) */ -#define CONNECTION_TESTCASE_ARG(name, fork, setup, arg) \ - { #name "_" #arg, test_conn_##name, fork, &setup, (void *)arg } +#define CONNECTION_TESTCASE_ARG(name, fork, setup, arg) \ + { #name "_" STR(x), \ + test_conn_##name, \ + fork, \ + &setup, \ + (void *)arg } #endif /* !defined(COCCI) */ static const unsigned int PROXY_CONNECT_ARG = PROXY_CONNECT; diff --git a/src/test/test_consdiff.c b/src/test/test_consdiff.c index e4cfece9c3..242e2f7818 100644 --- a/src/test/test_consdiff.c +++ b/src/test/test_consdiff.c @@ -1030,7 +1030,7 @@ test_consdiff_apply_diff(void *arg) /* diff doesn't have enough lines. */ cons2 = consdiff_apply_diff(cons1, diff, &digests1); tt_ptr_op(NULL, OP_EQ, cons2); - expect_single_log_msg_containing("too short") + expect_single_log_msg_containing("too short"); /* first line doesn't match format-version string. */ smartlist_add_linecpy(diff, area, "foo-bar"); @@ -1038,7 +1038,7 @@ test_consdiff_apply_diff(void *arg) mock_clean_saved_logs(); cons2 = consdiff_apply_diff(cons1, diff, &digests1); tt_ptr_op(NULL, OP_EQ, cons2); - expect_single_log_msg_containing("format is not known") + expect_single_log_msg_containing("format is not known"); /* The first word of the second header line is not "hash". */ smartlist_clear(diff); @@ -1048,7 +1048,7 @@ test_consdiff_apply_diff(void *arg) mock_clean_saved_logs(); cons2 = consdiff_apply_diff(cons1, diff, &digests1); tt_ptr_op(NULL, OP_EQ, cons2); - expect_single_log_msg_containing("does not include the necessary digests") + expect_single_log_msg_containing("does not include the necessary digests"); /* Wrong number of words after "hash". */ smartlist_clear(diff); @@ -1057,7 +1057,7 @@ test_consdiff_apply_diff(void *arg) mock_clean_saved_logs(); cons2 = consdiff_apply_diff(cons1, diff, &digests1); tt_ptr_op(NULL, OP_EQ, cons2); - expect_single_log_msg_containing("does not include the necessary digests") + expect_single_log_msg_containing("does not include the necessary digests"); /* base16 digests do not have the expected length. */ smartlist_clear(diff); @@ -1067,7 +1067,7 @@ test_consdiff_apply_diff(void *arg) cons2 = consdiff_apply_diff(cons1, diff, &digests1); tt_ptr_op(NULL, OP_EQ, cons2); expect_single_log_msg_containing("includes base16-encoded digests of " - "incorrect size") + "incorrect size"); /* base16 digests contain non-base16 characters. */ smartlist_clear(diff); @@ -1078,7 +1078,7 @@ test_consdiff_apply_diff(void *arg) mock_clean_saved_logs(); cons2 = consdiff_apply_diff(cons1, diff, &digests1); tt_ptr_op(NULL, OP_EQ, cons2); - expect_single_log_msg_containing("includes malformed digests") + expect_single_log_msg_containing("includes malformed digests"); /* Invalid ed diff. * As tested in apply_ed_diff, but check that apply_diff does return NULL if @@ -1095,7 +1095,7 @@ test_consdiff_apply_diff(void *arg) cons2 = consdiff_apply_diff(cons1, diff, &digests1); tt_ptr_op(NULL, OP_EQ, cons2); expect_single_log_msg_containing("because an ed command was missing a line " - "number") + "number"); /* Base consensus doesn't match its digest as found in the diff. */ smartlist_clear(diff); diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 46e1a19ca8..0d75a212e9 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -1336,6 +1336,44 @@ test_crypto_pk_pem_encrypted(void *arg) } static void +test_crypto_pk_bad_size(void *arg) +{ + (void)arg; + crypto_pk_t *pk1 = pk_generate(0); + crypto_pk_t *pk2 = NULL; + char buf[2048]; + int n = crypto_pk_asn1_encode_private(pk1, buf, sizeof(buf)); + tt_int_op(n, OP_GT, 0); + + /* Set the max bit count smaller: we should refuse to decode the key.*/ + pk2 = crypto_pk_asn1_decode_private(buf, n, 1020); + tt_assert(! pk2); + + /* Set the max bit count one bit smaller: we should refuse to decode the + key.*/ + pk2 = crypto_pk_asn1_decode_private(buf, n, 1023); + tt_assert(! pk2); + + /* Correct size: should work. */ + pk2 = crypto_pk_asn1_decode_private(buf, n, 1024); + tt_assert(pk2); + crypto_pk_free(pk2); + + /* One bit larger: should work. */ + pk2 = crypto_pk_asn1_decode_private(buf, n, 1025); + tt_assert(pk2); + crypto_pk_free(pk2); + + /* Set the max bit count larger: it should decode fine. */ + pk2 = crypto_pk_asn1_decode_private(buf, n, 2048); + tt_assert(pk2); + + done: + crypto_pk_free(pk1); + crypto_pk_free(pk2); +} + +static void test_crypto_pk_invalid_private_key(void *arg) { (void)arg; @@ -3000,6 +3038,7 @@ struct testcase_t crypto_tests[] = { { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL }, { "pk_base64", test_crypto_pk_base64, TT_FORK, NULL, NULL }, { "pk_pem_encrypted", test_crypto_pk_pem_encrypted, TT_FORK, NULL, NULL }, + { "pk_bad_size", test_crypto_pk_bad_size, 0, NULL, NULL }, { "pk_invalid_private_key", test_crypto_pk_invalid_private_key, 0, NULL, NULL }, CRYPTO_LEGACY(digests), diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 3b2ba64d2c..bf9a04b079 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -47,7 +47,7 @@ #include "feature/dirclient/dlstatus.h" #include "feature/dircommon/directory.h" #include "feature/dircommon/fp_pair.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/hibernate/hibernate.h" #include "feature/nodelist/authcert.h" #include "feature/nodelist/dirlist.h" @@ -3005,6 +3005,7 @@ test_dir_param_voting_lookup(void *arg) tt_int_op(99, OP_EQ, dirvote_get_intermediate_param_value(lst, "abcd", 1000)); +#ifndef ALL_BUGS_ARE_FATAL /* moomin appears twice. That's a bug. */ tor_capture_bugs_(1); tt_int_op(-100, OP_EQ, @@ -3022,7 +3023,7 @@ test_dir_param_voting_lookup(void *arg) dirvote_get_intermediate_param_value(lst, "jack", -100)); tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, - "!(! ok)"); + "!(!ok)"); tor_end_capture_bugs_(); /* electricity and opa aren't integers. */ tor_capture_bugs_(1); @@ -3030,7 +3031,7 @@ test_dir_param_voting_lookup(void *arg) dirvote_get_intermediate_param_value(lst, "electricity", -100)); tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, - "!(! ok)"); + "!(!ok)"); tor_end_capture_bugs_(); tor_capture_bugs_(1); @@ -3038,8 +3039,9 @@ test_dir_param_voting_lookup(void *arg) dirvote_get_intermediate_param_value(lst, "opa", -100)); tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, - "!(! ok)"); + "!(!ok)"); tor_end_capture_bugs_(); +#endif done: SMARTLIST_FOREACH(lst, char *, cp, tor_free(cp)); @@ -3610,7 +3612,7 @@ test_a_networkstatus( sign_skey_2 = crypto_pk_new(); sign_skey_3 = crypto_pk_new(); sign_skey_leg1 = pk_generate(4); - voting_schedule_recalculate_timing(get_options(), now); + dirauth_sched_recalculate_timing(get_options(), now); sr_state_init(0, 0); tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_1, @@ -4989,6 +4991,14 @@ test_dir_purpose_needs_anonymity_returns_true_by_default(void *arg) { (void)arg; +#ifdef ALL_BUGS_ARE_FATAL + /* Coverity (and maybe clang analyser) complain that the code following + * tt_skip() is unconditionally unreachable. */ +#if !defined(__COVERITY__) && !defined(__clang_analyzer__) + tt_skip(); +#endif +#endif + tor_capture_bugs_(1); setup_full_capture_of_logs(LOG_WARN); tt_int_op(1, OP_EQ, purpose_needs_anonymity(0, 0, NULL)); diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c index 4533ad5c03..f2b4e8724b 100644 --- a/src/test/test_dir_common.c +++ b/src/test/test_dir_common.c @@ -13,7 +13,7 @@ #include "feature/dirparse/authcert_parse.h" #include "feature/dirparse/ns_parse.h" #include "test/test_dir_common.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/nodelist/authority_cert_st.h" #include "feature/nodelist/networkstatus_st.h" diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index 6293839b0d..f446bbb5eb 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -38,7 +38,7 @@ #include "feature/dircache/dirserv.h" #include "feature/dirauth/dirvote.h" #include "test/log_test_helpers.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/dircommon/dir_connection_st.h" #include "feature/dirclient/dir_server_st.h" @@ -2080,12 +2080,12 @@ test_dir_handle_get_status_vote_d(void* data) mock_options->TestingV3AuthInitialDistDelay = 1; time_t now = 1441223455 -1; - voting_schedule_recalculate_timing(mock_options, now); + dirauth_sched_recalculate_timing(mock_options, now); const char *msg_out = NULL; int status_out = 0; - struct pending_vote_t *pv = dirvote_add_vote(VOTE_BODY_V3, &msg_out, - &status_out); + struct pending_vote_t *pv = dirvote_add_vote(VOTE_BODY_V3, 0, + &msg_out, &status_out); tt_assert(pv); status_vote_current_d_test(&header, &body, &body_used); @@ -2457,10 +2457,10 @@ test_dir_handle_get_status_vote_next_authority(void* data) mock_options->TestingV3AuthInitialDistDelay = 1; time_t now = 1441223455 -1; - voting_schedule_recalculate_timing(mock_options, now); + dirauth_sched_recalculate_timing(mock_options, now); - struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out, - &status_out); + struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, 0, + &msg_out, &status_out); tt_assert(vote); MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); @@ -2617,10 +2617,10 @@ test_dir_handle_get_status_vote_current_authority(void* data) mock_options->TestingV3AuthInitialDistDelay = 1; time_t now = 1441223455; - voting_schedule_recalculate_timing(mock_options, now-1); + dirauth_sched_recalculate_timing(mock_options, now-1); - struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out, - &status_out); + struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, 0, + &msg_out, &status_out); tt_assert(vote); // move the pending vote to previous vote @@ -2658,6 +2658,183 @@ test_dir_handle_get_status_vote_current_authority(void* data) dirvote_free_all(); } +/* Test that a late vote is rejected, but an on-time vote is accepted. */ +static void +test_dir_handle_get_status_vote_too_late(void* data) +{ + dir_connection_t *conn = NULL; + char *header = NULL, *body = NULL; + const char *msg_out = NULL; + int status_out = 0; + size_t body_used = 0; + const char digest[DIGEST_LEN] = ""; + + dir_server_t *ds = NULL; + const char* mode = (const char *)data; + + clear_dir_servers(); + routerlist_free_all(); + dirvote_free_all(); + + mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, + strlen(TEST_CERTIFICATE), + NULL); + + /* create a trusted ds */ + ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest, + NULL, V3_DIRINFO, 1.0); + tt_assert(ds); + dir_server_add(ds); + + /* ds v3_identity_digest is the certificate's identity_key */ + base16_decode(ds->v3_identity_digest, DIGEST_LEN, + TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN); + + tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE, + TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL)); + + init_mock_options(); + mock_options->AuthoritativeDir = 1; + mock_options->V3AuthoritativeDir = 1; + + int base_delay = 0; + int vote_interval = 0; + int start_offset = 0; + + tt_assert(mode); + /* Set the required timings, see below for details */ + if (strcmp(mode, "min") == 0) { + /* The minimum valid test network timing */ + base_delay = 2; + vote_interval = 10; + start_offset = vote_interval - 5; + } else if (strcmp(mode, "chutney") == 0) { + /* The test network timing used by chutney */ + base_delay = 4; + vote_interval = 20; + start_offset = vote_interval - 5; + } else if (strcmp(mode, "half-public") == 0) { + /* The short consensus failure timing used in the public network */ + base_delay = 5*60; + vote_interval = 30*60; + start_offset = vote_interval - 9*60 - 5; + } else if (strcmp(mode, "public") == 0) { + /* The standard timing used in the public network */ + base_delay = 5*60; + vote_interval = 60*60; + start_offset = vote_interval - 9*60 - 5; + } + + tt_assert(base_delay > 0); + tt_assert(vote_interval > 0); + tt_assert(start_offset > 0); + + /* Skew the time to fit the fixed time in the vote */ + mock_options->TestingV3AuthVotingStartOffset = start_offset; + /* Calculate the rest of the timings */ + mock_options->TestingV3AuthInitialVotingInterval = vote_interval; + mock_options->TestingV3AuthInitialVoteDelay = base_delay; + mock_options->TestingV3AuthInitialDistDelay = base_delay; + + time_t now = 1441223455; + dirauth_sched_recalculate_timing(mock_options, now-1); + const time_t voting_starts = voting_schedule.voting_starts; + const time_t fetch_missing = voting_schedule.fetch_missing_votes; + + struct pending_vote_t *vote = NULL; + + /* Next voting interval */ + vote = dirvote_add_vote(VOTE_BODY_V3, + fetch_missing + vote_interval, + &msg_out, &status_out); + tt_assert(!vote); + tt_int_op(status_out, OP_EQ, 400); + tt_str_op(msg_out, OP_EQ, + "Posted vote received too late, would be dangerous to count it"); + + /* Just after fetch missing */ + vote = dirvote_add_vote(VOTE_BODY_V3, + fetch_missing + 1, + &msg_out, &status_out); + tt_assert(!vote); + tt_int_op(status_out, OP_EQ, 400); + tt_str_op(msg_out, OP_EQ, + "Posted vote received too late, would be dangerous to count it"); + + /* On fetch missing */ + vote = dirvote_add_vote(VOTE_BODY_V3, + fetch_missing, + &msg_out, &status_out); + tt_assert(vote); + + /* Move the pending vote to previous vote */ + dirvote_act(mock_options, now+1); + /* And reset the timing */ + dirauth_sched_recalculate_timing(mock_options, now-1); + + /* Between voting starts and fetch missing */ + vote = dirvote_add_vote(VOTE_BODY_V3, + voting_starts + 1, + &msg_out, &status_out); + tt_assert(vote); + + /* Move the pending vote to previous vote */ + dirvote_act(mock_options, now+1); + /* And reset the timing */ + dirauth_sched_recalculate_timing(mock_options, now-1); + + /* On voting starts */ + vote = dirvote_add_vote(VOTE_BODY_V3, + voting_starts, + &msg_out, &status_out); + tt_assert(vote); + + /* Move the pending vote to previous vote */ + dirvote_act(mock_options, now+1); + /* And reset the timing */ + dirauth_sched_recalculate_timing(mock_options, now-1); + + /* Just before voting starts */ + vote = dirvote_add_vote(VOTE_BODY_V3, + voting_starts - 1, + &msg_out, &status_out); + tt_assert(vote); + + /* Move the pending vote to previous vote */ + dirvote_act(mock_options, now+1); + + MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); + MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); + + conn = new_dir_conn(); + tt_int_op(0, OP_EQ, directory_handle_command_get(conn, + GET("/tor/status-vote/current/authority"), NULL, 0)); + + fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, + &body, &body_used, strlen(VOTE_BODY_V3)+1, 0); + + tt_assert(header); + tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header); + tt_assert(strstr(header, "Content-Type: text/plain\r\n")); + tt_assert(strstr(header, "Content-Encoding: identity\r\n")); + tt_assert(strstr(header, "Content-Length: 4135\r\n")); + + tt_str_op(VOTE_BODY_V3, OP_EQ, body); + + done: + UNMOCK(connection_write_to_buf_impl_); + UNMOCK(get_my_v3_authority_cert); + connection_free_minimal(TO_CONN(conn)); + tor_free(header); + tor_free(body); + authority_cert_free(mock_cert); mock_cert = NULL; + or_options_free(mock_options); mock_options = NULL; + + clear_dir_servers(); + routerlist_free_all(); + dirvote_free_all(); +} + static void test_dir_handle_get_parse_accept_encoding(void *arg) { @@ -2708,6 +2885,16 @@ test_dir_handle_get_parse_accept_encoding(void *arg) #define DIR_HANDLE_CMD(name,flags) \ { #name, test_dir_handle_get_##name, (flags), NULL, NULL } +#ifdef COCCI +/* Coccinelle doesn't like the stringification in this macro */ +#define DIR_HANDLE_CMD_ARG(name,flags,arg) \ + DIR_HANDLE_CMD(name,flags) +#else +#define DIR_HANDLE_CMD_ARG(name,flags,arg) \ + { #name "/" arg, test_dir_handle_get_##name, (flags), \ + &passthrough_setup, (void *)(arg) } +#endif /* defined(COCCI) */ + struct testcase_t dir_handle_get_tests[] = { DIR_HANDLE_CMD(not_found, 0), DIR_HANDLE_CMD(bad_request, 0), @@ -2747,6 +2934,10 @@ struct testcase_t dir_handle_get_tests[] = { DIR_HANDLE_CMD(status_vote_next_not_found, 0), DIR_HANDLE_CMD(status_vote_current_authority_not_found, 0), DIR_HANDLE_CMD(status_vote_current_authority, 0), + DIR_HANDLE_CMD_ARG(status_vote_too_late, 0, "min"), + DIR_HANDLE_CMD_ARG(status_vote_too_late, 0, "chutney"), + DIR_HANDLE_CMD_ARG(status_vote_too_late, 0, "half-public"), + DIR_HANDLE_CMD_ARG(status_vote_too_late, 0, "public"), DIR_HANDLE_CMD(status_vote_next_authority_not_found, 0), DIR_HANDLE_CMD(status_vote_next_authority, 0), DIR_HANDLE_CMD(status_vote_next_bandwidth_not_found, 0), diff --git a/src/test/test_dns.c b/src/test/test_dns.c index ec17e9e91e..299321ab64 100644 --- a/src/test/test_dns.c +++ b/src/test/test_dns.c @@ -80,11 +80,11 @@ test_dns_clip_ttl(void *arg) { (void)arg; - uint32_t ttl_mid = MIN_DNS_TTL_AT_EXIT / 2 + MAX_DNS_TTL_AT_EXIT / 2; + uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_TTL / 2; - tt_int_op(dns_clip_ttl(MIN_DNS_TTL_AT_EXIT - 1),OP_EQ,MIN_DNS_TTL_AT_EXIT); - tt_int_op(dns_clip_ttl(ttl_mid),OP_EQ,MAX_DNS_TTL_AT_EXIT); - tt_int_op(dns_clip_ttl(MAX_DNS_TTL_AT_EXIT + 1),OP_EQ,MAX_DNS_TTL_AT_EXIT); + tt_int_op(clip_dns_ttl(MIN_DNS_TTL - 1),OP_EQ,MIN_DNS_TTL); + tt_int_op(clip_dns_ttl(ttl_mid),OP_EQ,MAX_DNS_TTL); + tt_int_op(clip_dns_ttl(MAX_DNS_TTL + 1),OP_EQ,MAX_DNS_TTL); done: return; diff --git a/src/test/test_dos.c b/src/test/test_dos.c index b6a83210b7..527e5bbe7f 100644 --- a/src/test/test_dos.c +++ b/src/test/test_dos.c @@ -79,7 +79,7 @@ test_dos_conn_creation(void *arg) { /* Register many conns from this client but not enough to get it blocked */ unsigned int i; for (i = 0; i < max_concurrent_conns; i++) { - dos_new_client_conn(&or_conn); + dos_new_client_conn(&or_conn, NULL); } } @@ -88,7 +88,7 @@ test_dos_conn_creation(void *arg) dos_conn_addr_get_defense_type(addr)); /* Register another conn and check that new conns are not allowed anymore */ - dos_new_client_conn(&or_conn); + dos_new_client_conn(&or_conn, NULL); tt_int_op(DOS_CONN_DEFENSE_CLOSE, OP_EQ, dos_conn_addr_get_defense_type(addr)); @@ -98,7 +98,7 @@ test_dos_conn_creation(void *arg) dos_conn_addr_get_defense_type(addr)); /* Register another conn and see that defense measures get reactivated */ - dos_new_client_conn(&or_conn); + dos_new_client_conn(&or_conn, NULL); tt_int_op(DOS_CONN_DEFENSE_CLOSE, OP_EQ, dos_conn_addr_get_defense_type(addr)); @@ -153,7 +153,7 @@ test_dos_circuit_creation(void *arg) * circuit counting subsystem */ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, addr, NULL, now); for (i = 0; i < min_conc_conns_for_cc ; i++) { - dos_new_client_conn(&or_conn); + dos_new_client_conn(&or_conn, NULL); } /* Register new circuits for this client and conn, but not enough to get @@ -217,7 +217,7 @@ test_dos_bucket_refill(void *arg) /* Register this client */ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, addr, NULL, now); - dos_new_client_conn(&or_conn); + dos_new_client_conn(&or_conn, NULL); /* Fetch this client from the geoip cache and get its DoS structs */ clientmap_entry_t *entry = geoip_lookup_client(addr, NULL, @@ -460,11 +460,11 @@ test_known_relay(void *arg) geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &or_conn.real_addr, NULL, 0); /* Suppose we have 5 connections in rapid succession, the counter should * always be 0 because we should ignore this. */ - dos_new_client_conn(&or_conn); - dos_new_client_conn(&or_conn); - dos_new_client_conn(&or_conn); - dos_new_client_conn(&or_conn); - dos_new_client_conn(&or_conn); + dos_new_client_conn(&or_conn, NULL); + dos_new_client_conn(&or_conn, NULL); + dos_new_client_conn(&or_conn, NULL); + dos_new_client_conn(&or_conn, NULL); + dos_new_client_conn(&or_conn, NULL); entry = geoip_lookup_client(&or_conn.real_addr, NULL, GEOIP_CLIENT_CONNECT); tt_assert(entry); /* We should have a count of 0. */ @@ -474,8 +474,8 @@ test_known_relay(void *arg) * connection and see if we do get it. */ tor_addr_parse(&or_conn.real_addr, "42.42.42.43"); geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &or_conn.real_addr, NULL, 0); - dos_new_client_conn(&or_conn); - dos_new_client_conn(&or_conn); + dos_new_client_conn(&or_conn, NULL); + dos_new_client_conn(&or_conn, NULL); entry = geoip_lookup_client(&or_conn.real_addr, NULL, GEOIP_CLIENT_CONNECT); tt_assert(entry); /* We should have a count of 2. */ diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c index 8ea550b65f..f25bba3584 100644 --- a/src/test/test_hs_cache.c +++ b/src/test/test_hs_cache.c @@ -370,7 +370,7 @@ test_hsdir_revision_counter_check(void *arg) hs_descriptor_t *published_desc = NULL; char *published_desc_str = NULL; - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; char *received_desc_str = NULL; hs_descriptor_t *received_desc = NULL; @@ -407,11 +407,11 @@ test_hsdir_revision_counter_check(void *arg) const ed25519_public_key_t *blinded_key; blinded_key = &published_desc->plaintext_data.blinded_pubkey; - hs_get_subcredential(&signing_kp.pubkey, blinded_key, subcredential); + hs_get_subcredential(&signing_kp.pubkey, blinded_key, &subcredential); received_desc_str = helper_fetch_desc_from_hsdir(blinded_key); retval = hs_desc_decode_descriptor(received_desc_str, - subcredential, NULL, &received_desc); + &subcredential, NULL, &received_desc); tt_int_op(retval, OP_EQ, HS_DESC_DECODE_OK); tt_assert(received_desc); @@ -444,7 +444,7 @@ test_hsdir_revision_counter_check(void *arg) received_desc_str = helper_fetch_desc_from_hsdir(blinded_key); retval = hs_desc_decode_descriptor(received_desc_str, - subcredential, NULL, &received_desc); + &subcredential, NULL, &received_desc); tt_int_op(retval, OP_EQ, HS_DESC_DECODE_OK); tt_assert(received_desc); @@ -476,7 +476,7 @@ test_client_cache(void *arg) ed25519_keypair_t signing_kp; hs_descriptor_t *published_desc = NULL; char *published_desc_str = NULL; - uint8_t wanted_subcredential[DIGEST256_LEN]; + hs_subcredential_t wanted_subcredential; response_handler_args_t *args = NULL; dir_connection_t *conn = NULL; @@ -505,8 +505,10 @@ test_client_cache(void *arg) retval = hs_desc_encode_descriptor(published_desc, &signing_kp, NULL, &published_desc_str); tt_int_op(retval, OP_EQ, 0); - memcpy(wanted_subcredential, published_desc->subcredential, DIGEST256_LEN); - tt_assert(!fast_mem_is_zero((char*)wanted_subcredential, DIGEST256_LEN)); + memcpy(&wanted_subcredential, &published_desc->subcredential, + sizeof(hs_subcredential_t)); + tt_assert(!fast_mem_is_zero((char*)wanted_subcredential.subcred, + DIGEST256_LEN)); } /* Test handle_response_fetch_hsdesc_v3() */ @@ -540,8 +542,9 @@ test_client_cache(void *arg) const hs_descriptor_t *cached_desc = NULL; cached_desc = hs_cache_lookup_as_client(&signing_kp.pubkey); tt_assert(cached_desc); - tt_mem_op(cached_desc->subcredential, OP_EQ, wanted_subcredential, - DIGEST256_LEN); + tt_mem_op(cached_desc->subcredential.subcred, + OP_EQ, wanted_subcredential.subcred, + SUBCRED_LEN); } /* Progress time to next TP and check that desc was cleaned */ diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 5f7fe9c404..3d6422a83c 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -433,9 +433,10 @@ test_client_pick_intro(void *arg) const hs_descriptor_t *fetched_desc = hs_cache_lookup_as_client(&service_kp.pubkey); tt_assert(fetched_desc); - tt_mem_op(fetched_desc->subcredential, OP_EQ, desc->subcredential, - DIGEST256_LEN); - tt_assert(!fast_mem_is_zero((char*)fetched_desc->subcredential, + tt_mem_op(fetched_desc->subcredential.subcred, + OP_EQ, desc->subcredential.subcred, + SUBCRED_LEN); + tt_assert(!fast_mem_is_zero((char*)fetched_desc->subcredential.subcred, DIGEST256_LEN)); tor_free(encoded); } @@ -965,6 +966,7 @@ test_close_intro_circuits_new_desc(void *arg) (void) arg; hs_init(); + rend_cache_init(); /* This is needed because of the client cache expiration timestamp is based * on having a consensus. See cached_client_descriptor_has_expired(). */ @@ -989,6 +991,51 @@ test_close_intro_circuits_new_desc(void *arg) circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING; ocirc = TO_ORIGIN_CIRCUIT(circ); + /* Build a descriptor _without_ client authorization and thus not + * decryptable. Make sure the close circuit code path is not triggered. */ + { + char *desc_encoded = NULL; + uint8_t descriptor_cookie[HS_DESC_DESCRIPTOR_COOKIE_LEN]; + curve25519_keypair_t client_kp; + hs_descriptor_t *desc = NULL; + + tt_int_op(0, OP_EQ, curve25519_keypair_generate(&client_kp, 0)); + crypto_rand((char *) descriptor_cookie, sizeof(descriptor_cookie)); + + desc = hs_helper_build_hs_desc_with_client_auth(descriptor_cookie, + &client_kp.pubkey, + &service_kp); + tt_assert(desc); + ret = hs_desc_encode_descriptor(desc, &service_kp, descriptor_cookie, + &desc_encoded); + tt_int_op(ret, OP_EQ, 0); + /* Associate descriptor intro key with the dummy circuit. */ + const hs_desc_intro_point_t *ip = + smartlist_get(desc->encrypted_data.intro_points, 0); + tt_assert(ip); + ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey); + ed25519_pubkey_copy(ô->hs_ident->intro_auth_pk, + &ip->auth_key_cert->signed_key); + hs_descriptor_free(desc); + tt_assert(desc_encoded); + /* Put it in the cache. Should not be decrypted since the client + * authorization creds were not added to the global map. */ + ret = hs_cache_store_as_client(desc_encoded, &service_kp.pubkey); + tor_free(desc_encoded); + tt_int_op(ret, OP_EQ, HS_DESC_DECODE_NEED_CLIENT_AUTH); + + /* Clean cache with a future timestamp. It will trigger the clean up and + * attempt to close the circuit but only if the descriptor is decryptable. + * Cache object should be removed and circuit untouched. */ + hs_cache_clean_as_client(mock_ns.valid_after + (60 * 60 * 24)); + tt_assert(!hs_cache_lookup_as_client(&service_kp.pubkey)); + + /* Make sure the circuit still there. */ + tt_assert(circuit_get_next_intro_circ(NULL, true)); + /* Get rid of the ident, it will be replaced in the next tests. */ + hs_ident_circuit_free(ocirc->hs_ident); + } + /* Build the first descriptor and cache it. */ { char *encoded; diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index 61306778d4..4a161db334 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -32,7 +32,7 @@ #include "app/config/statefile.h" #include "core/or/circuitlist.h" #include "feature/dirauth/shared_random.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/nodelist/microdesc_st.h" #include "feature/nodelist/networkstatus_st.h" @@ -53,14 +53,14 @@ test_validate_address(void *arg) setup_full_capture_of_logs(LOG_WARN); ret = hs_address_is_valid("blah"); tt_int_op(ret, OP_EQ, 0); - expect_log_msg_containing("has an invalid length"); + expect_log_msg_containing("Invalid length"); teardown_capture_of_logs(); setup_full_capture_of_logs(LOG_WARN); ret = hs_address_is_valid( "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnadb"); tt_int_op(ret, OP_EQ, 0); - expect_log_msg_containing("has an invalid length"); + expect_log_msg_containing("Invalid length"); teardown_capture_of_logs(); /* Invalid checksum (taken from prop224) */ @@ -83,7 +83,7 @@ test_validate_address(void *arg) ret = hs_address_is_valid( "????????????????????????????????????????????????????????"); tt_int_op(ret, OP_EQ, 0); - expect_log_msg_containing("can't be decoded"); + expect_log_msg_containing("Unable to base32 decode"); teardown_capture_of_logs(); /* Valid address. */ @@ -853,7 +853,7 @@ test_time_between_tp_and_srv(void *arg) tt_int_op(ret, OP_EQ, 0); ret = parse_rfc1123_time("Sat, 26 Oct 1985 01:00:00 UTC", &ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), ns.valid_after); + dirauth_sched_recalculate_timing(get_options(), ns.valid_after); ret = hs_in_period_between_tp_and_srv(&ns, 0); tt_int_op(ret, OP_EQ, 0); @@ -861,7 +861,7 @@ test_time_between_tp_and_srv(void *arg) tt_int_op(ret, OP_EQ, 0); ret = parse_rfc1123_time("Sat, 26 Oct 1985 12:00:00 UTC", &ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), ns.valid_after); + dirauth_sched_recalculate_timing(get_options(), ns.valid_after); ret = hs_in_period_between_tp_and_srv(&ns, 0); tt_int_op(ret, OP_EQ, 0); @@ -869,7 +869,7 @@ test_time_between_tp_and_srv(void *arg) tt_int_op(ret, OP_EQ, 0); ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", &ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), ns.valid_after); + dirauth_sched_recalculate_timing(get_options(), ns.valid_after); ret = hs_in_period_between_tp_and_srv(&ns, 0); tt_int_op(ret, OP_EQ, 1); @@ -877,7 +877,7 @@ test_time_between_tp_and_srv(void *arg) tt_int_op(ret, OP_EQ, 0); ret = parse_rfc1123_time("Sat, 27 Oct 1985 00:00:00 UTC", &ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), ns.valid_after); + dirauth_sched_recalculate_timing(get_options(), ns.valid_after); ret = hs_in_period_between_tp_and_srv(&ns, 0); tt_int_op(ret, OP_EQ, 1); @@ -885,7 +885,7 @@ test_time_between_tp_and_srv(void *arg) tt_int_op(ret, OP_EQ, 0); ret = parse_rfc1123_time("Sat, 27 Oct 1985 01:00:00 UTC", &ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), ns.valid_after); + dirauth_sched_recalculate_timing(get_options(), ns.valid_after); ret = hs_in_period_between_tp_and_srv(&ns, 0); tt_int_op(ret, OP_EQ, 0); @@ -1372,7 +1372,7 @@ run_reachability_scenario(const reachability_cfg_t *cfg, int num_scenario) &mock_service_ns->valid_until); set_consensus_times(cfg->service_valid_until, &mock_service_ns->fresh_until); - voting_schedule_recalculate_timing(get_options(), + dirauth_sched_recalculate_timing(get_options(), mock_service_ns->valid_after); /* Check that service is in the right time period point */ tt_int_op(hs_in_period_between_tp_and_srv(mock_service_ns, 0), OP_EQ, @@ -1385,7 +1385,7 @@ run_reachability_scenario(const reachability_cfg_t *cfg, int num_scenario) &mock_client_ns->valid_until); set_consensus_times(cfg->client_valid_until, &mock_client_ns->fresh_until); - voting_schedule_recalculate_timing(get_options(), + dirauth_sched_recalculate_timing(get_options(), mock_client_ns->valid_after); /* Check that client is in the right time period point */ tt_int_op(hs_in_period_between_tp_and_srv(mock_client_ns, 0), OP_EQ, @@ -1608,7 +1608,7 @@ helper_set_consensus_and_system_time(networkstatus_t *ns, int position) } else { tt_assert(0); } - voting_schedule_recalculate_timing(get_options(), ns->valid_after); + dirauth_sched_recalculate_timing(get_options(), ns->valid_after); /* Set system time: pretend to be just 2 minutes before consensus expiry */ real_time = ns->valid_until - 120; diff --git a/src/test/test_hs_config.c b/src/test/test_hs_config.c index b2537b746b..dc3b598c34 100644 --- a/src/test/test_hs_config.c +++ b/src/test/test_hs_config.c @@ -62,8 +62,9 @@ test_invalid_service(void *arg) setup_full_capture_of_logs(LOG_WARN); ret = helper_config_service(conf, 1); tt_int_op(ret, OP_EQ, -1); - expect_log_msg_containing("HiddenServiceAllowUnknownPorts must be " - "between 0 and 1, not 2"); + expect_log_msg_containing("Could not parse " + "HiddenServiceAllowUnknownPorts: Unrecognized " + "value 2. Allowed values are 0 and 1."); teardown_capture_of_logs(); } @@ -76,8 +77,9 @@ test_invalid_service(void *arg) setup_full_capture_of_logs(LOG_WARN); ret = helper_config_service(conf, 1); tt_int_op(ret, OP_EQ, -1); - expect_log_msg_containing("HiddenServiceDirGroupReadable must be " - "between 0 and 1, not 2"); + expect_log_msg_containing("Could not parse " + "HiddenServiceDirGroupReadable: " + "Unrecognized value 2."); teardown_capture_of_logs(); } @@ -90,8 +92,9 @@ test_invalid_service(void *arg) setup_full_capture_of_logs(LOG_WARN); ret = helper_config_service(conf, 1); tt_int_op(ret, OP_EQ, -1); - expect_log_msg_containing("HiddenServiceMaxStreamsCloseCircuit must " - "be between 0 and 1, not 2"); + expect_log_msg_containing("Could not parse " + "HiddenServiceMaxStreamsCloseCircuit: " + "Unrecognized value 2"); teardown_capture_of_logs(); } @@ -228,8 +231,8 @@ test_invalid_service_v2(void *arg) setup_full_capture_of_logs(LOG_WARN); ret = helper_config_service(conf, validate_only); tt_int_op(ret, OP_EQ, -1); - expect_log_msg_containing("HiddenServiceNumIntroductionPoints should " - "be between 0 and 10, not 11"); + expect_log_msg_containing("HiddenServiceNumIntroductionPoints must " + "be between 0 and 10, not 11."); teardown_capture_of_logs(); } @@ -243,8 +246,9 @@ test_invalid_service_v2(void *arg) setup_full_capture_of_logs(LOG_WARN); ret = helper_config_service(conf, validate_only); tt_int_op(ret, OP_EQ, -1); - expect_log_msg_containing("HiddenServiceNumIntroductionPoints should " - "be between 0 and 10, not -1"); + expect_log_msg_containing("Could not parse " + "HiddenServiceNumIntroductionPoints: " + "Integer -1 is malformed or out of bounds."); teardown_capture_of_logs(); } @@ -532,9 +536,10 @@ test_dos_parameters(void *arg) setup_full_capture_of_logs(LOG_WARN); ret = helper_config_service(conf, 0); tt_int_op(ret, OP_EQ, -1); - expect_log_msg_containing("HiddenServiceEnableIntroDoSRatePerSec must " - "be between 0 and 2147483647, " - "not 137438953472"); + expect_log_msg_containing("Could not parse " + "HiddenServiceEnableIntroDoSRatePerSec: " + "Integer 137438953472 is malformed or out of " + "bounds."); teardown_capture_of_logs(); } @@ -551,9 +556,10 @@ test_dos_parameters(void *arg) setup_full_capture_of_logs(LOG_WARN); ret = helper_config_service(conf, 0); tt_int_op(ret, OP_EQ, -1); - expect_log_msg_containing("HiddenServiceEnableIntroDoSBurstPerSec must " - "be between 0 and 2147483647, " - "not 274877906944"); + expect_log_msg_containing("Could not parse " + "HiddenServiceEnableIntroDoSBurstPerSec: " + "Integer 274877906944 is malformed or out " + "of bounds."); teardown_capture_of_logs(); } @@ -588,8 +594,9 @@ test_dos_parameters(void *arg) setup_full_capture_of_logs(LOG_WARN); ret = helper_config_service(conf, 0); tt_int_op(ret, OP_EQ, -1); - expect_log_msg_containing("HiddenServiceEnableIntroDoSRatePerSec must be " - "between 0 and 2147483647, not -1"); + expect_log_msg_containing("Could not parse " + "HiddenServiceEnableIntroDoSRatePerSec: " + "Integer -1 is malformed or out of bounds."); teardown_capture_of_logs(); } diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c index 881479016f..9277711d2a 100644 --- a/src/test/test_hs_control.c +++ b/src/test/test_hs_control.c @@ -638,6 +638,48 @@ test_hs_control_store_permanent_creds(void *arg) hs_client_free_all(); } +/** Test that ADD_ONION properly handles an attacker passing it a bad private + * key. */ +static void +test_hs_control_add_onion_with_bad_pubkey(void *arg) +{ + (void) arg; + + MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); + + int retval; + control_connection_t conn; + char *args = NULL; + char *cp1 = NULL; + size_t sz; + + hs_init(); + + { /* Setup the control conn */ + memset(&conn, 0, sizeof(control_connection_t)); + TO_CONN(&conn)->outbuf = buf_new(); + conn.current_cmd = tor_strdup("ADD_ONION"); + } + + args = tor_strdup("ED25519-V3:AAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA " + "Port=9735,127.0.0.1 Flags=DiscardPK"); + + retval = handle_control_command(&conn, (uint32_t) strlen(args), args); + tt_int_op(retval, OP_EQ, 0); + + /* Check control port response */ + cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz); + tt_str_op(cp1, OP_EQ, "551 Failed to generate onion address\r\n"); + + done: + tor_free(args); + tor_free(cp1); + buf_free(TO_CONN(&conn)->outbuf); + tor_free(conn.current_cmd); +} + struct testcase_t hs_control_tests[] = { { "hs_desc_event", test_hs_desc_event, TT_FORK, NULL, NULL }, @@ -649,6 +691,8 @@ struct testcase_t hs_control_tests[] = { NULL, NULL }, { "hs_control_store_permanent_creds", test_hs_control_store_permanent_creds, TT_FORK, NULL, NULL }, + { "hs_control_add_onion_with_bad_pubkey", + test_hs_control_add_onion_with_bad_pubkey, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c index 43ac5490a1..782b78306c 100644 --- a/src/test/test_hs_descriptor.c +++ b/src/test/test_hs_descriptor.c @@ -14,6 +14,7 @@ #include "lib/crypt_ops/crypto_rand.h" #include "trunnel/ed25519_cert.h" #include "core/or/or.h" +#include "app/config/config.h" #include "feature/hs/hs_descriptor.h" #include "test/test.h" #include "feature/nodelist/torcert.h" @@ -37,7 +38,6 @@ test_cert_encoding(void *arg) { int ret; char *encoded = NULL; - time_t now = time(NULL); ed25519_keypair_t kp; ed25519_public_key_t signed_key; ed25519_secret_key_t secret_key; @@ -45,6 +45,10 @@ test_cert_encoding(void *arg) (void) arg; + /* Change time to 03-01-2002 23:36 UTC */ + update_approx_time(1010101010); + time_t now = approx_time(); + ret = ed25519_keypair_generate(&kp, 0); tt_int_op(ret, == , 0); ret = ed25519_secret_key_generate(&secret_key, 0); @@ -88,13 +92,31 @@ test_cert_encoding(void *arg) /* The cert did have the signing key? */ ret= ed25519_pubkey_eq(&parsed_cert->signing_key, &kp.pubkey); tt_int_op(ret, OP_EQ, 1); - tor_cert_free(parsed_cert); /* Get to the end part of the certificate. */ pos += b64_cert_len; tt_int_op(strcmpstart(pos, "-----END ED25519 CERT-----"), OP_EQ, 0); pos += strlen("-----END ED25519 CERT-----"); tt_str_op(pos, OP_EQ, ""); + + /* Check that certificate expiry works properly and emits the right log + message */ + const char *msg = "fire"; + /* Move us forward 4 hours so that the the certificate is definitely + expired */ + update_approx_time(approx_time() + 3600*4); + setup_full_capture_of_logs(LOG_PROTOCOL_WARN); + ret = cert_is_valid(parsed_cert, CERT_TYPE_SIGNING_AUTH, msg); + tt_int_op(ret, OP_EQ, 0); + /* Since the current time at the creation of the cert was "03-01-2002 + * 23:36", and the expiration date of the cert was two hours, the Tor code + * will ceiling that and make it 02:00. Make sure that the right log + * message is emitted */ + expect_log_msg_containing("Invalid signature for fire: expired" + " (2002-01-04 02:00:00)"); + teardown_capture_of_logs(); + + tor_cert_free(parsed_cert); } done: @@ -221,7 +243,7 @@ test_decode_descriptor(void *arg) hs_descriptor_t *desc = NULL; hs_descriptor_t *decoded = NULL; hs_descriptor_t *desc_no_ip = NULL; - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; (void) arg; @@ -230,10 +252,10 @@ test_decode_descriptor(void *arg) desc = hs_helper_build_hs_desc_with_ip(&signing_kp); hs_helper_get_subcred_from_identity_keypair(&signing_kp, - subcredential); + &subcredential); /* Give some bad stuff to the decoding function. */ - ret = hs_desc_decode_descriptor("hladfjlkjadf", subcredential, + ret = hs_desc_decode_descriptor("hladfjlkjadf", &subcredential, NULL, &decoded); tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR); @@ -241,7 +263,7 @@ test_decode_descriptor(void *arg) tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK); tt_assert(encoded); - ret = hs_desc_decode_descriptor(encoded, subcredential, NULL, &decoded); + ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded); tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK); tt_assert(decoded); @@ -253,7 +275,7 @@ test_decode_descriptor(void *arg) ret = ed25519_keypair_generate(&signing_kp_no_ip, 0); tt_int_op(ret, OP_EQ, 0); hs_helper_get_subcred_from_identity_keypair(&signing_kp_no_ip, - subcredential); + &subcredential); desc_no_ip = hs_helper_build_hs_desc_no_ip(&signing_kp_no_ip); tt_assert(desc_no_ip); tor_free(encoded); @@ -262,7 +284,7 @@ test_decode_descriptor(void *arg) tt_int_op(ret, OP_EQ, 0); tt_assert(encoded); hs_descriptor_free(decoded); - ret = hs_desc_decode_descriptor(encoded, subcredential, NULL, &decoded); + ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded); tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK); tt_assert(decoded); } @@ -286,14 +308,14 @@ test_decode_descriptor(void *arg) &auth_ephemeral_kp.pubkey, CURVE25519_PUBKEY_LEN); hs_helper_get_subcred_from_identity_keypair(&signing_kp, - subcredential); + &subcredential); /* Build and add the auth client to the descriptor. */ clients = desc->superencrypted_data.clients; if (!clients) { clients = smartlist_new(); } - hs_desc_build_authorized_client(subcredential, + hs_desc_build_authorized_client(&subcredential, &client_kp.pubkey, &auth_ephemeral_kp.seckey, descriptor_cookie, client); @@ -315,21 +337,21 @@ test_decode_descriptor(void *arg) /* If we do not have the client secret key, the decoding must fail. */ hs_descriptor_free(decoded); - ret = hs_desc_decode_descriptor(encoded, subcredential, + ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded); tt_int_op(ret, OP_EQ, HS_DESC_DECODE_NEED_CLIENT_AUTH); tt_assert(!decoded); /* If we have an invalid client secret key, the decoding must fail. */ hs_descriptor_free(decoded); - ret = hs_desc_decode_descriptor(encoded, subcredential, + ret = hs_desc_decode_descriptor(encoded, &subcredential, &invalid_client_kp.seckey, &decoded); tt_int_op(ret, OP_EQ, HS_DESC_DECODE_BAD_CLIENT_AUTH); tt_assert(!decoded); /* If we have the client secret key, the decoding must succeed and the * decoded descriptor must be correct. */ - ret = hs_desc_decode_descriptor(encoded, subcredential, + ret = hs_desc_decode_descriptor(encoded, &subcredential, &client_kp.seckey, &decoded); tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK); tt_assert(decoded); @@ -762,7 +784,7 @@ test_build_authorized_client(void *arg) "07d087f1d8c68393721f6e70316d3b29"; const char client_pubkey_b16[] = "8c1298fa6050e372f8598f6deca32e27b0ad457741422c2629ebb132cf7fae37"; - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; char *mem_op_hex_tmp=NULL; (void) arg; @@ -774,7 +796,7 @@ test_build_authorized_client(void *arg) tt_int_op(ret, OP_EQ, 0); curve25519_public_key_generate(&client_auth_pk, &client_auth_sk); - memset(subcredential, 42, sizeof(subcredential)); + memset(subcredential.subcred, 42, sizeof(subcredential)); desc_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t)); @@ -795,7 +817,7 @@ test_build_authorized_client(void *arg) testing_enable_prefilled_rng("\x01", 1); - hs_desc_build_authorized_client(subcredential, + hs_desc_build_authorized_client(&subcredential, &client_auth_pk, &auth_ephemeral_sk, descriptor_cookie, desc_client); diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c index 5337188427..3b6e3fd213 100644 --- a/src/test/test_hs_intropoint.c +++ b/src/test/test_hs_intropoint.c @@ -757,12 +757,15 @@ test_introduce1_validation(void *arg) cell = helper_create_introduce1_cell(); tt_assert(cell); +#ifndef ALL_BUGS_ARE_FATAL /* It should NOT be a legacy cell which will trigger a BUG(). */ memset(cell->legacy_key_id, 'a', sizeof(cell->legacy_key_id)); tor_capture_bugs_(1); ret = validate_introduce1_parsed_cell(cell); tor_end_capture_bugs_(); tt_int_op(ret, OP_EQ, -1); +#endif + /* Reset legacy ID and make sure it's correct. */ memset(cell->legacy_key_id, 0, sizeof(cell->legacy_key_id)); ret = validate_introduce1_parsed_cell(cell); diff --git a/src/test/test_hs_ntor.c b/src/test/test_hs_ntor.c index 4f98bc85dc..7867740a1a 100644 --- a/src/test/test_hs_ntor.c +++ b/src/test/test_hs_ntor.c @@ -23,7 +23,7 @@ test_hs_ntor(void *arg) { int retval; - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; ed25519_keypair_t service_intro_auth_keypair; curve25519_keypair_t service_intro_enc_keypair; @@ -42,7 +42,7 @@ test_hs_ntor(void *arg) /* Generate fake data for this unittest */ { /* Generate fake subcredential */ - memset(subcredential, 'Z', DIGEST256_LEN); + memset(subcredential.subcred, 'Z', DIGEST256_LEN); /* service */ curve25519_keypair_generate(&service_intro_enc_keypair, 0); @@ -57,7 +57,7 @@ test_hs_ntor(void *arg) hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey, &service_intro_enc_keypair.pubkey, &client_ephemeral_enc_keypair, - subcredential, + &subcredential, &client_hs_ntor_intro_cell_keys); tt_int_op(retval, OP_EQ, 0); @@ -66,7 +66,7 @@ test_hs_ntor(void *arg) hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey, &service_intro_enc_keypair, &client_ephemeral_enc_keypair.pubkey, - subcredential, + &subcredential, &service_hs_ntor_intro_cell_keys); tt_int_op(retval, OP_EQ, 0); diff --git a/src/test/test_hs_ntor_cl.c b/src/test/test_hs_ntor_cl.c index a7cebc6af4..3acd7ef0bc 100644 --- a/src/test/test_hs_ntor_cl.c +++ b/src/test/test_hs_ntor_cl.c @@ -53,7 +53,7 @@ client1(int argc, char **argv) curve25519_public_key_t intro_enc_pubkey; ed25519_public_key_t intro_auth_pubkey; curve25519_keypair_t client_ephemeral_enc_keypair; - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; /* Output */ hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys; @@ -65,7 +65,7 @@ client1(int argc, char **argv) BASE16(3, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN); BASE16(4, client_ephemeral_enc_keypair.seckey.secret_key, CURVE25519_SECKEY_LEN); - BASE16(5, subcredential, DIGEST256_LEN); + BASE16(5, subcredential.subcred, DIGEST256_LEN); /* Generate keypair */ curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey, @@ -74,7 +74,7 @@ client1(int argc, char **argv) retval = hs_ntor_client_get_introduce1_keys(&intro_auth_pubkey, &intro_enc_pubkey, &client_ephemeral_enc_keypair, - subcredential, + &subcredential, &hs_ntor_intro_cell_keys); if (retval < 0) { goto done; @@ -106,7 +106,7 @@ server1(int argc, char **argv) curve25519_keypair_t intro_enc_keypair; ed25519_public_key_t intro_auth_pubkey; curve25519_public_key_t client_ephemeral_enc_pubkey; - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; /* Output */ hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys; @@ -119,7 +119,7 @@ server1(int argc, char **argv) BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN); BASE16(3, intro_enc_keypair.seckey.secret_key, CURVE25519_SECKEY_LEN); BASE16(4, client_ephemeral_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN); - BASE16(5, subcredential, DIGEST256_LEN); + BASE16(5, subcredential.subcred, DIGEST256_LEN); /* Generate keypair */ curve25519_public_key_generate(&intro_enc_keypair.pubkey, @@ -130,7 +130,7 @@ server1(int argc, char **argv) retval = hs_ntor_service_get_introduce1_keys(&intro_auth_pubkey, &intro_enc_keypair, &client_ephemeral_enc_pubkey, - subcredential, + &subcredential, &hs_ntor_intro_cell_keys); if (retval < 0) { goto done; @@ -188,7 +188,7 @@ client2(int argc, char **argv) ed25519_public_key_t intro_auth_pubkey; curve25519_keypair_t client_ephemeral_enc_keypair; curve25519_public_key_t service_ephemeral_rend_pubkey; - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; /* Output */ hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys; @@ -201,7 +201,7 @@ client2(int argc, char **argv) CURVE25519_SECKEY_LEN); BASE16(4, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN); BASE16(5, service_ephemeral_rend_pubkey.public_key, CURVE25519_PUBKEY_LEN); - BASE16(6, subcredential, DIGEST256_LEN); + BASE16(6, subcredential.subcred, DIGEST256_LEN); /* Generate keypair */ curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey, diff --git a/src/test/test_hs_ob.c b/src/test/test_hs_ob.c new file mode 100644 index 0000000000..7f40187b5f --- /dev/null +++ b/src/test/test_hs_ob.c @@ -0,0 +1,268 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_ob.c + * \brief Test hidden service onion balance functionality. + */ + +#define CONFIG_PRIVATE +#define HS_SERVICE_PRIVATE +#define HS_OB_PRIVATE + +#include "test/test.h" +#include "test/test_helpers.h" +#include "test/log_test_helpers.h" + +#include "app/config/config.h" +#include "feature/hs/hs_config.h" +#include "feature/hs/hs_ob.h" +#include "feature/hs/hs_service.h" +#include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/networkstatus_st.h" + +static ed25519_keypair_t onion_addr_kp_1; +static char onion_addr_1[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + +static ed25519_keypair_t onion_addr_kp_2; +static char onion_addr_2[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + +static bool config_is_good = true; + +static int +helper_tor_config(const char *conf) +{ + int ret = -1; + or_options_t *options = helper_parse_options(conf); + tt_assert(options); + ret = hs_config_service_all(options, 0); + done: + or_options_free(options); + return ret; +} + +static networkstatus_t mock_ns; + +static networkstatus_t * +mock_networkstatus_get_live_consensus(time_t now) +{ + (void) now; + return &mock_ns; +} + +static char * +mock_read_file_to_str(const char *filename, int flags, struct stat *stat_out) +{ + char *ret = NULL; + + (void) flags; + (void) stat_out; + + if (!strcmp(filename, get_fname("hs3" PATH_SEPARATOR "ob_config"))) { + if (config_is_good) { + tor_asprintf(&ret, "MasterOnionAddress %s.onion\n" + "MasterOnionAddress %s.onion\n", + onion_addr_1, onion_addr_2); + } else { + tor_asprintf(&ret, "MasterOnionAddress JUNKJUNKJUNK.onion\n" + "UnknownOption BLAH\n"); + } + goto done; + } + + done: + return ret; +} + +static void +test_parse_config_file(void *arg) +{ + int ret; + char *conf = NULL; + const ed25519_public_key_t *pkey; + + (void) arg; + + hs_init(); + + MOCK(read_file_to_str, mock_read_file_to_str); + +#define fmt_conf \ + "HiddenServiceDir %s\n" \ + "HiddenServicePort 22\n" \ + "HiddenServiceOnionBalanceInstance 1\n" + tor_asprintf(&conf, fmt_conf, get_fname("hs3")); +#undef fmt_conf + + /* Build the OB frontend onion addresses. */ + ed25519_keypair_generate(&onion_addr_kp_1, 0); + hs_build_address(&onion_addr_kp_1.pubkey, HS_VERSION_THREE, onion_addr_1); + ed25519_keypair_generate(&onion_addr_kp_2, 0); + hs_build_address(&onion_addr_kp_2.pubkey, HS_VERSION_THREE, onion_addr_2); + + ret = helper_tor_config(conf); + tor_free(conf); + tt_int_op(ret, OP_EQ, 0); + + /* Load the keys for the service. After that, the v3 service should be + * registered in the global map and we'll be able to access it. */ + tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1); + hs_service_load_all_keys(); + tt_int_op(get_hs_service_map_size(), OP_EQ, 1); + const hs_service_t *s = get_first_service(); + tt_assert(s); + tt_assert(s->config.ob_master_pubkeys); + tt_assert(hs_ob_service_is_instance(s)); + tt_assert(smartlist_len(s->config.ob_master_pubkeys) == 2); + + /* Test the public keys we've added. */ + pkey = smartlist_get(s->config.ob_master_pubkeys, 0); + tt_mem_op(&onion_addr_kp_1.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN); + pkey = smartlist_get(s->config.ob_master_pubkeys, 1); + tt_mem_op(&onion_addr_kp_2.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN); + + done: + hs_free_all(); + + UNMOCK(read_file_to_str); +} + +static void +test_parse_config_file_bad(void *arg) +{ + int ret; + char *conf = NULL; + + (void) arg; + + hs_init(); + + MOCK(read_file_to_str, mock_read_file_to_str); + + /* Indicate mock_read_file_to_str() to use the bad config. */ + config_is_good = false; + +#define fmt_conf \ + "HiddenServiceDir %s\n" \ + "HiddenServicePort 22\n" \ + "HiddenServiceOnionBalanceInstance 1\n" + tor_asprintf(&conf, fmt_conf, get_fname("hs3")); +#undef fmt_conf + + setup_full_capture_of_logs(LOG_INFO); + ret = helper_tor_config(conf); + tor_free(conf); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("OnionBalance: MasterOnionAddress " + "JUNKJUNKJUNK.onion is invalid"); + expect_log_msg_containing("Found unrecognized option \'UnknownOption\'; " + "saving it."); + teardown_capture_of_logs(); + + done: + hs_free_all(); + + UNMOCK(read_file_to_str); +} + +static void +test_get_subcredentials(void *arg) +{ + int ret; + hs_service_t *service = NULL; + hs_service_config_t config; + hs_subcredential_t *subcreds = NULL; + + (void) arg; + + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus); + + /* Setup consensus with proper time so we can compute the time period. */ + ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", + &mock_ns.valid_after); + tt_int_op(ret, OP_EQ, 0); + ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", + &mock_ns.fresh_until); + tt_int_op(ret, OP_EQ, 0); + + config.ob_master_pubkeys = smartlist_new(); + tt_assert(config.ob_master_pubkeys); + + /* Set up an instance */ + service = tor_malloc_zero(sizeof(hs_service_t)); + service->config = config; + /* Setup the service descriptors */ + service->desc_current = service_descriptor_new(); + service->desc_next = service_descriptor_new(); + + /* First try to compute subcredentials but with no OB keys. Make sure that + * subcreds get NULLed. To do this check we first poison subcreds. */ + subcreds = (void*)999; + tt_ptr_op(subcreds, OP_NE, NULL); + size_t num = compute_subcredentials(service, &subcreds); + tt_ptr_op(subcreds, OP_EQ, NULL); + + /* Generate a keypair to add to the OB keys list. */ + ed25519_keypair_generate(&onion_addr_kp_1, 0); + smartlist_add(config.ob_master_pubkeys, &onion_addr_kp_1.pubkey); + + /* Set up the instance subcredentials */ + char current_subcred[SUBCRED_LEN]; + char next_subcred[SUBCRED_LEN]; + memset(current_subcred, 'C', SUBCRED_LEN); + memset(next_subcred, 'N', SUBCRED_LEN); + memcpy(service->desc_current->desc->subcredential.subcred, current_subcred, + SUBCRED_LEN); + memcpy(service->desc_next->desc->subcredential.subcred, next_subcred, + SUBCRED_LEN); + + /* See that subcreds are computed properly */ + num = compute_subcredentials(service, &subcreds); + /* 5 subcredentials: 3 for the frontend, 2 for the instance */ + tt_uint_op(num, OP_EQ, 5); + tt_ptr_op(subcreds, OP_NE, NULL); + + /* Validate the subcredentials we just got. We'll build them oursevles with + * the right time period steps and compare. */ + const uint64_t tp = hs_get_time_period_num(0); + const int steps[3] = {0, -1, 1}; + + unsigned int i; + for (i = 0; i < 3; i++) { + hs_subcredential_t subcredential; + ed25519_public_key_t blinded_pubkey; + hs_build_blinded_pubkey(&onion_addr_kp_1.pubkey, NULL, 0, tp + steps[i], + &blinded_pubkey); + hs_get_subcredential(&onion_addr_kp_1.pubkey, &blinded_pubkey, + &subcredential); + tt_mem_op(subcreds[i].subcred, OP_EQ, subcredential.subcred, + SUBCRED_LEN); + } + + tt_mem_op(subcreds[i++].subcred, OP_EQ, current_subcred, SUBCRED_LEN); + tt_mem_op(subcreds[i++].subcred, OP_EQ, next_subcred, SUBCRED_LEN); + + done: + tor_free(subcreds); + + smartlist_free(config.ob_master_pubkeys); + if (service) { + memset(&service->config, 0, sizeof(hs_service_config_t)); + hs_service_free(service); + } + + UNMOCK(networkstatus_get_live_consensus); +} + +struct testcase_t hs_ob_tests[] = { + { "parse_config_file", test_parse_config_file, TT_FORK, + NULL, NULL }, + { "parse_config_file_bad", test_parse_config_file_bad, TT_FORK, + NULL, NULL }, + + { "get_subcredentials", test_get_subcredentials, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index e33d593d94..80383baff8 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -44,13 +44,15 @@ #include "core/or/versions.h" #include "feature/dirauth/dirvote.h" #include "feature/dirauth/shared_random_state.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/hs/hs_circuit.h" #include "feature/hs/hs_circuitmap.h" #include "feature/hs/hs_client.h" #include "feature/hs/hs_common.h" #include "feature/hs/hs_config.h" #include "feature/hs/hs_ident.h" +#include "feature/hs/hs_ob.h" +#include "feature/hs/hs_cell.h" #include "feature/hs/hs_intropoint.h" #include "feature/hs/hs_service.h" #include "feature/nodelist/networkstatus.h" @@ -87,6 +89,13 @@ mock_networkstatus_get_live_consensus(time_t now) return &mock_ns; } +static networkstatus_t * +mock_networkstatus_get_live_consensus_null(time_t now) +{ + (void) now; + return NULL; +} + static or_state_t *dummy_state = NULL; /* Mock function to get fake or state (used for rev counters) */ @@ -109,6 +118,9 @@ mock_circuit_mark_for_close(circuit_t *circ, int reason, int line, return; } +static size_t relay_payload_len; +static char relay_payload[RELAY_PAYLOAD_SIZE]; + static int mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ, uint8_t relay_command, const char *payload, @@ -124,6 +136,24 @@ mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ, (void) cpath_layer; (void) filename; (void) lineno; + + memcpy(relay_payload, payload, payload_len); + relay_payload_len = payload_len; + + return 0; +} + +static unsigned int num_intro_points = 0; +static unsigned int +mock_count_desc_circuit_established(const hs_service_descriptor_t *desc) +{ + (void) desc; + return num_intro_points; +} + +static int +mock_router_have_minimum_dir_info_false(void) +{ return 0; } @@ -1160,7 +1190,7 @@ test_closing_intro_circs(void *arg) /** Test sending and receiving introduce2 cells */ static void -test_introduce2(void *arg) +test_bad_introduce2(void *arg) { int ret; int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL; @@ -1356,7 +1386,7 @@ test_rotate_descriptors(void *arg) ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", &mock_ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); + dirauth_sched_recalculate_timing(get_options(), mock_ns.valid_after); update_approx_time(mock_ns.valid_after+1); now = mock_ns.valid_after+1; @@ -1397,7 +1427,7 @@ test_rotate_descriptors(void *arg) ret = parse_rfc1123_time("Sat, 27 Oct 1985 02:00:00 UTC", &mock_ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); + dirauth_sched_recalculate_timing(get_options(), mock_ns.valid_after); update_approx_time(mock_ns.valid_after+1); now = mock_ns.valid_after+1; @@ -1465,7 +1495,7 @@ test_build_update_descriptors(void *arg) ret = parse_rfc1123_time("Sat, 26 Oct 1985 04:00:00 UTC", &mock_ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); + dirauth_sched_recalculate_timing(get_options(), mock_ns.valid_after); update_approx_time(mock_ns.valid_after+1); @@ -1696,7 +1726,7 @@ test_build_descriptors(void *arg) ret = parse_rfc1123_time("Sat, 26 Oct 1985 04:00:00 UTC", &mock_ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); + dirauth_sched_recalculate_timing(get_options(), mock_ns.valid_after); /* Generate a valid number of fake auth clients when a client authorization * is disabled. */ @@ -1797,7 +1827,7 @@ test_upload_descriptors(void *arg) ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", &mock_ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); + dirauth_sched_recalculate_timing(get_options(), mock_ns.valid_after); update_approx_time(mock_ns.valid_after+1); now = mock_ns.valid_after+1; @@ -2169,6 +2199,490 @@ test_export_client_circuit_id(void *arg) tor_free(cp2); } +static smartlist_t * +mock_node_get_link_specifier_smartlist(const node_t *node, bool direct_conn) +{ + (void) node; + (void) direct_conn; + + smartlist_t *lspecs = smartlist_new(); + link_specifier_t *ls_legacy = link_specifier_new(); + smartlist_add(lspecs, ls_legacy); + + return lspecs; +} + +static node_t *fake_node = NULL; + +static const node_t * +mock_build_state_get_exit_node(cpath_build_state_t *state) +{ + (void) state; + + if (!fake_node) { + curve25519_secret_key_t seckey; + curve25519_secret_key_generate(&seckey, 0); + + fake_node = tor_malloc_zero(sizeof(node_t)); + fake_node->ri = tor_malloc_zero(sizeof(routerinfo_t)); + fake_node->ri->onion_curve25519_pkey = + tor_malloc_zero(sizeof(curve25519_public_key_t)); + curve25519_public_key_generate(fake_node->ri->onion_curve25519_pkey, + &seckey); + } + + return fake_node; +} + +static void +mock_launch_rendezvous_point_circuit(const hs_service_t *service, + const hs_service_intro_point_t *ip, + const hs_cell_introduce2_data_t *data) +{ + (void) service; + (void) ip; + (void) data; + return; +} + +/** + * Test that INTRO2 cells are handled well by onion services in the normal + * case and also when onionbalance is enabled. + */ +static void +test_intro2_handling(void *arg) +{ + (void)arg; + + MOCK(build_state_get_exit_node, mock_build_state_get_exit_node); + MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge); + MOCK(node_get_link_specifier_smartlist, + mock_node_get_link_specifier_smartlist); + MOCK(launch_rendezvous_point_circuit, mock_launch_rendezvous_point_circuit); + + memset(relay_payload, 0, sizeof(relay_payload)); + + int retval; + time_t now = 0101010101; + update_approx_time(now); + + /** OK this is the play: + * + * In Act I, we have a standalone onion service X (without onionbalance + * enabled). We test that X can properly handle INTRO2 cells sent by a + * client Alice. + * + * In Act II, we create an onionbalance setup with frontend being Z which + * includes instances X and Y. We then setup onionbalance on X and test that + * Alice who addresses Z can communicate with X through INTRO2 cells. + * + * In Act III, we test that Alice can also communicate with X + * directly even tho onionbalance is enabled. + * + * And finally in Act IV, we check various cases where the INTRO2 cell + * should not go through because the subcredentials don't line up + * (e.g. Alice sends INTRO2 to X using Y's subcredential). + */ + + /** Let's start with some setup! Create the instances and the frontend + service, create Alice, etc: */ + + /* Create instance X */ + hs_service_t x_service; + memset(&x_service, 0, sizeof(hs_service_t)); + /* Disable onionbalance */ + x_service.config.ob_master_pubkeys = NULL; + x_service.state.replay_cache_rend_cookie = replaycache_new(0,0); + + /* Create subcredential for x: */ + ed25519_keypair_t x_identity_keypair; + hs_subcredential_t x_subcred; + ed25519_keypair_generate(&x_identity_keypair, 0); + hs_helper_get_subcred_from_identity_keypair(&x_identity_keypair, + &x_subcred); + + /* Create the x instance's intro point */ + hs_service_intro_point_t *x_ip = NULL; + { + curve25519_secret_key_t seckey; + curve25519_public_key_t pkey; + curve25519_secret_key_generate(&seckey, 0); + curve25519_public_key_generate(&pkey, &seckey); + + node_t intro_node; + memset(&intro_node, 0, sizeof(intro_node)); + routerinfo_t ri; + memset(&ri, 0, sizeof(routerinfo_t)); + ri.onion_curve25519_pkey = &pkey; + intro_node.ri = &ri; + + x_ip = service_intro_point_new(&intro_node); + } + + /* Create z frontend's subcredential */ + ed25519_keypair_t z_identity_keypair; + hs_subcredential_t z_subcred; + ed25519_keypair_generate(&z_identity_keypair, 0); + hs_helper_get_subcred_from_identity_keypair(&z_identity_keypair, + &z_subcred); + + /* Create y instance's subcredential */ + ed25519_keypair_t y_identity_keypair; + hs_subcredential_t y_subcred; + ed25519_keypair_generate(&y_identity_keypair, 0); + hs_helper_get_subcred_from_identity_keypair(&y_identity_keypair, + &y_subcred); + + /* Create Alice's intro point */ + hs_desc_intro_point_t *alice_ip; + ed25519_keypair_t signing_kp; + ed25519_keypair_generate(&signing_kp, 0); + alice_ip = hs_helper_build_intro_point(&signing_kp, now, "1.2.3.4", 0, + &x_ip->auth_key_kp, + &x_ip->enc_key_kp); + + /* Create Alice's intro and rend circuits */ + origin_circuit_t *intro_circ = origin_circuit_new(); + intro_circ->cpath = tor_malloc_zero(sizeof(crypt_path_t)); + intro_circ->cpath->prev = intro_circ->cpath; + intro_circ->hs_ident = tor_malloc_zero(sizeof(*intro_circ->hs_ident)); + origin_circuit_t rend_circ; + rend_circ.hs_ident = tor_malloc_zero(sizeof(*rend_circ.hs_ident)); + curve25519_keypair_generate(&rend_circ.hs_ident->rendezvous_client_kp, 0); + memset(rend_circ.hs_ident->rendezvous_cookie, 'r', HS_REND_COOKIE_LEN); + + /* ************************************************************ */ + + /* Act I: + * + * Where Alice connects to X without onionbalance in the picture */ + + /* Create INTRODUCE1 */ + tt_assert(fast_mem_is_zero(relay_payload, sizeof(relay_payload))); + retval = hs_circ_send_introduce1(intro_circ, &rend_circ, + alice_ip, &x_subcred); + + /* Check that the payload was written successfully */ + tt_int_op(retval, OP_EQ, 0); + tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload))); + tt_int_op(relay_payload_len, OP_NE, 0); + + /* Handle the cell */ + retval = hs_circ_handle_introduce2(&x_service, + intro_circ, x_ip, + &x_subcred, + (uint8_t*)relay_payload,relay_payload_len); + tt_int_op(retval, OP_EQ, 0); + + /* ************************************************************ */ + + /* Act II: + * + * We now create an onionbalance setup with Z being the frontend and X and Y + * being the backend instances. Make sure that Alice can talk with the + * backend instance X even tho she thinks she is talking to the frontend Z. + */ + + /* Now configure the X instance to do onionbalance with Z as the frontend */ + x_service.config.ob_master_pubkeys = smartlist_new(); + smartlist_add(x_service.config.ob_master_pubkeys, + &z_identity_keypair.pubkey); + + /* Create descriptors for x and load next descriptor with the x's + * subcredential so that it can accept connections for itself. */ + x_service.desc_current = service_descriptor_new(); + memset(x_service.desc_current->desc->subcredential.subcred, 'C',SUBCRED_LEN); + x_service.desc_next = service_descriptor_new(); + memcpy(&x_service.desc_next->desc->subcredential, &x_subcred, SUBCRED_LEN); + + /* Refresh OB keys */ + hs_ob_refresh_keys(&x_service); + + /* Create INTRODUCE1 from Alice to X through Z */ + memset(relay_payload, 0, sizeof(relay_payload)); + retval = hs_circ_send_introduce1(intro_circ, &rend_circ, + alice_ip, &z_subcred); + + /* Check that the payload was written successfully */ + tt_int_op(retval, OP_EQ, 0); + tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload))); + tt_int_op(relay_payload_len, OP_NE, 0); + + /* Deliver INTRODUCE1 to X even tho it carries Z's subcredential */ + replaycache_free(x_service.state.replay_cache_rend_cookie); + x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0); + + retval = hs_circ_handle_introduce2(&x_service, + intro_circ, x_ip, + &z_subcred, + (uint8_t*)relay_payload, relay_payload_len); + tt_int_op(retval, OP_EQ, 0); + + replaycache_free(x_ip->replay_cache); + x_ip->replay_cache = replaycache_new(0, 0); + + replaycache_free(x_service.state.replay_cache_rend_cookie); + x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0); + + /* ************************************************************ */ + + /* Act III: + * + * Now send a direct INTRODUCE cell from Alice to X using X's subcredential + * and check that it succeeds even with onionbalance enabled. + */ + + /* Refresh OB keys (just to check for memleaks) */ + hs_ob_refresh_keys(&x_service); + + /* Create INTRODUCE1 from Alice to X using X's subcred. */ + memset(relay_payload, 0, sizeof(relay_payload)); + retval = hs_circ_send_introduce1(intro_circ, &rend_circ, + alice_ip, &x_subcred); + + /* Check that the payload was written successfully */ + tt_int_op(retval, OP_EQ, 0); + tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload))); + tt_int_op(relay_payload_len, OP_NE, 0); + + /* Send INTRODUCE1 to X with X's subcredential (should succeed) */ + replaycache_free(x_service.state.replay_cache_rend_cookie); + x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0); + + retval = hs_circ_handle_introduce2(&x_service, + intro_circ, x_ip, + &x_subcred, + (uint8_t*)relay_payload, relay_payload_len); + tt_int_op(retval, OP_EQ, 0); + + /* ************************************************************ */ + + /* Act IV: + * + * Test cases where the INTRO2 cell should not be able to decode. + */ + + /* Try sending the exact same INTRODUCE2 cell again and see that the intro + * point replay cache triggers: */ + setup_full_capture_of_logs(LOG_WARN); + retval = hs_circ_handle_introduce2(&x_service, + intro_circ, x_ip, + &x_subcred, + (uint8_t*)relay_payload, relay_payload_len); + tt_int_op(retval, OP_EQ, -1); + expect_log_msg_containing("with the same ENCRYPTED section"); + teardown_capture_of_logs(); + + /* Now cleanup the intro point replay cache but not the service replay cache + and see that this one triggers this time. */ + replaycache_free(x_ip->replay_cache); + x_ip->replay_cache = replaycache_new(0, 0); + setup_full_capture_of_logs(LOG_INFO); + retval = hs_circ_handle_introduce2(&x_service, + intro_circ, x_ip, + &x_subcred, + (uint8_t*)relay_payload, relay_payload_len); + tt_int_op(retval, OP_EQ, -1); + expect_log_msg_containing("with same REND_COOKIE"); + teardown_capture_of_logs(); + + /* Now just to make sure cleanup both replay caches and make sure that the + cell gets through */ + replaycache_free(x_ip->replay_cache); + x_ip->replay_cache = replaycache_new(0, 0); + replaycache_free(x_service.state.replay_cache_rend_cookie); + x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0); + retval = hs_circ_handle_introduce2(&x_service, + intro_circ, x_ip, + &x_subcred, + (uint8_t*)relay_payload, relay_payload_len); + tt_int_op(retval, OP_EQ, 0); + + /* As a final thing, create an INTRODUCE1 cell from Alice to X using Y's + * subcred (should fail since Y is just another instance and not the frontend + * service!) */ + memset(relay_payload, 0, sizeof(relay_payload)); + retval = hs_circ_send_introduce1(intro_circ, &rend_circ, + alice_ip, &y_subcred); + tt_int_op(retval, OP_EQ, 0); + + /* Check that the payload was written successfully */ + tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload))); + tt_int_op(relay_payload_len, OP_NE, 0); + + retval = hs_circ_handle_introduce2(&x_service, + intro_circ, x_ip, + &y_subcred, + (uint8_t*)relay_payload, relay_payload_len); + tt_int_op(retval, OP_EQ, -1); + + done: + /* Start cleaning up X */ + replaycache_free(x_service.state.replay_cache_rend_cookie); + smartlist_free(x_service.config.ob_master_pubkeys); + tor_free(x_service.state.ob_subcreds); + service_descriptor_free(x_service.desc_current); + service_descriptor_free(x_service.desc_next); + service_intro_point_free(x_ip); + + /* Clean up Alice */ + hs_desc_intro_point_free(alice_ip); + tor_free(rend_circ.hs_ident); + + if (fake_node) { + tor_free(fake_node->ri->onion_curve25519_pkey); + tor_free(fake_node->ri); + tor_free(fake_node); + } + + UNMOCK(build_state_get_exit_node); + UNMOCK(relay_send_command_from_edge_); + UNMOCK(node_get_link_specifier_smartlist); + UNMOCK(launch_rendezvous_point_circuit); +} + +static void +test_cannot_upload_descriptors(void *arg) +{ + int ret; + time_t now; + hs_service_t *service; + + (void) arg; + + hs_init(); + MOCK(get_or_state, + get_or_state_replacement); + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus); + + dummy_state = or_state_new(); + + ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", + &mock_ns.valid_after); + tt_int_op(ret, OP_EQ, 0); + ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", + &mock_ns.fresh_until); + tt_int_op(ret, OP_EQ, 0); + dirauth_sched_recalculate_timing(get_options(), mock_ns.valid_after); + + update_approx_time(mock_ns.valid_after + 1); + now = mock_ns.valid_after + 1; + + /* Create a service with no descriptor. It's added to the global map. */ + service = hs_service_new(get_options()); + tt_assert(service); + service->config.version = HS_VERSION_THREE; + ed25519_secret_key_generate(&service->keys.identity_sk, 0); + ed25519_public_key_generate(&service->keys.identity_pk, + &service->keys.identity_sk); + /* Register service to global map. */ + ret = register_service(get_hs_service_map(), service); + tt_int_op(ret, OP_EQ, 0); + /* But first, build our descriptor. */ + build_all_descriptors(now); + + /* 1. Testing missing intro points reason. */ + { + digest256map_t *cur = service->desc_current->intro_points.map; + digest256map_t *tmp = digest256map_new(); + service->desc_current->intro_points.map = tmp; + service->desc_current->missing_intro_points = 1; + setup_full_capture_of_logs(LOG_INFO); + run_upload_descriptor_event(now); + digest256map_free(tmp, tor_free_); + service->desc_current->intro_points.map = cur; + expect_log_msg_containing( + "Service [scrubbed] can't upload its current descriptor: " + "Missing intro points"); + teardown_capture_of_logs(); + /* Reset. */ + service->desc_current->missing_intro_points = 0; + } + + /* 2. Testing non established intro points. */ + { + setup_full_capture_of_logs(LOG_INFO); + run_upload_descriptor_event(now); + expect_log_msg_containing( + "Service [scrubbed] can't upload its current descriptor: " + "Intro circuits aren't yet all established (0/3)."); + teardown_capture_of_logs(); + } + + /* We need to pass the established circuit tests and thus from now on, we + * MOCK this to return 3 intro points. */ + MOCK(count_desc_circuit_established, mock_count_desc_circuit_established); + num_intro_points = 3; + + /* 3. Testing non established intro points. */ + { + service->desc_current->next_upload_time = now + 1000; + setup_full_capture_of_logs(LOG_INFO); + run_upload_descriptor_event(now); + expect_log_msg_containing( + "Service [scrubbed] can't upload its current descriptor: " + "Next upload time is"); + teardown_capture_of_logs(); + /* Reset. */ + service->desc_current->next_upload_time = 0; + } + + /* 4. Testing missing live consensus. */ + { + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus_null); + setup_full_capture_of_logs(LOG_INFO); + run_upload_descriptor_event(now); + expect_log_msg_containing( + "Service [scrubbed] can't upload its current descriptor: " + "No live consensus"); + teardown_capture_of_logs(); + /* Reset. */ + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus); + } + + /* 5. Test missing minimum directory information. */ + { + MOCK(router_have_minimum_dir_info, + mock_router_have_minimum_dir_info_false); + setup_full_capture_of_logs(LOG_INFO); + run_upload_descriptor_event(now); + expect_log_msg_containing( + "Service [scrubbed] can't upload its current descriptor: " + "Not enough directory information"); + teardown_capture_of_logs(); + + /* Running it again shouldn't trigger anything due to rate limitation. */ + setup_full_capture_of_logs(LOG_INFO); + run_upload_descriptor_event(now); + expect_no_log_entry(); + teardown_capture_of_logs(); + UNMOCK(router_have_minimum_dir_info); + } + + /* Increase time and redo test (5) in order to test the rate limiting. */ + update_approx_time(mock_ns.valid_after + 61); + { + MOCK(router_have_minimum_dir_info, + mock_router_have_minimum_dir_info_false); + setup_full_capture_of_logs(LOG_INFO); + run_upload_descriptor_event(now); + expect_log_msg_containing( + "Service [scrubbed] can't upload its current descriptor: " + "Not enough directory information"); + teardown_capture_of_logs(); + UNMOCK(router_have_minimum_dir_info); + } + + done: + hs_free_all(); + UNMOCK(count_desc_circuit_established); + UNMOCK(networkstatus_get_live_consensus); + UNMOCK(get_or_state); +} + struct testcase_t hs_service_tests[] = { { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK, NULL, NULL }, @@ -2194,7 +2708,7 @@ struct testcase_t hs_service_tests[] = { NULL, NULL }, { "rdv_circuit_opened", test_rdv_circuit_opened, TT_FORK, NULL, NULL }, - { "introduce2", test_introduce2, TT_FORK, + { "bad_introduce2", test_bad_introduce2, TT_FORK, NULL, NULL }, { "service_event", test_service_event, TT_FORK, NULL, NULL }, @@ -2206,12 +2720,15 @@ struct testcase_t hs_service_tests[] = { NULL, NULL }, { "upload_descriptors", test_upload_descriptors, TT_FORK, NULL, NULL }, + { "cannot_upload_descriptors", test_cannot_upload_descriptors, TT_FORK, + NULL, NULL }, { "rendezvous1_parsing", test_rendezvous1_parsing, TT_FORK, NULL, NULL }, { "authorized_client_config_equal", test_authorized_client_config_equal, TT_FORK, NULL, NULL }, { "export_client_circuit_id", test_export_client_circuit_id, TT_FORK, NULL, NULL }, + { "intro2_handling", test_intro2_handling, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index 96542ce7ac..1566b349ed 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -18,6 +18,7 @@ #include "feature/relay/routerkeys.h" #include "core/or/scheduler.h" #include "feature/nodelist/torcert.h" +#include "feature/relay/relay_handshake.h" #include "core/or/or_connection_st.h" #include "core/or/or_handshake_certs_st.h" diff --git a/src/test/test_options.c b/src/test/test_options.c index b6a9a21501..9cd1d11d29 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -307,18 +307,10 @@ test_options_validate(void *arg) WANT_ERR("BridgeRelay 1\nDirCache 0", "We're a bridge but DirCache is disabled.", PH_VALIDATE); - // XXXX We should replace this with a more full error message once #29211 - // XXXX is done. It is truncated for now because at the current stage - // XXXX of refactoring, we can't give a full error message like before. - WANT_ERR_LOG("HeartbeatPeriod 21 snarks", - "malformed or out of bounds", LOG_WARN, - "Unknown unit 'snarks'.", - PH_ASSIGN); - // XXXX As above. - WANT_ERR_LOG("LogTimeGranularity 21 snarks", - "malformed or out of bounds", LOG_WARN, - "Unknown unit 'snarks'.", - PH_ASSIGN); + WANT_ERR("HeartbeatPeriod 21 snarks", + "Unknown unit in 21 snarks", PH_ASSIGN); + WANT_ERR("LogTimeGranularity 21 snarks", + "Unknown unit in 21 snarks", PH_ASSIGN); OK("HeartbeatPeriod 1 hour", PH_VALIDATE); OK("LogTimeGranularity 100 milliseconds", PH_VALIDATE); @@ -498,7 +490,8 @@ test_options_validate__uname_for_server(void *ignored) #endif options_test_data_t *tdata = get_options_test_data( - "ORPort 127.0.0.1:5555"); + "ORPort 127.0.0.1:5555\n" + "ContactInfo nobody@example.com"); setup_capture_of_logs(LOG_WARN); MOCK(get_uname, fixed_get_uname); @@ -644,9 +637,11 @@ test_options_validate__contactinfo(void *ignored) ret = options_validate(NULL, tdata->opt, &msg); tt_int_op(ret, OP_EQ, 0); expect_log_msg( - "Your ContactInfo config option is not" - " set. Please consider setting it, so we can contact you if your" - " server is misconfigured or something else goes wrong.\n"); + "Your ContactInfo config option is not set. Please strongly " + "consider setting it, so we can contact you if your relay is " + "misconfigured, end-of-life, or something else goes wrong. It " + "is also possible that your relay might get rejected from the " + "network due to a missing valid contact address.\n"); tor_free(msg); free_options_test_data(tdata); @@ -656,9 +651,11 @@ test_options_validate__contactinfo(void *ignored) ret = options_validate(NULL, tdata->opt, &msg); tt_int_op(ret, OP_EQ, 0); expect_no_log_msg( - "Your ContactInfo config option is not" - " set. Please consider setting it, so we can contact you if your" - " server is misconfigured or something else goes wrong.\n"); + "Your ContactInfo config option is not set. Please strongly " + "consider setting it, so we can contact you if your relay is " + "misconfigured, end-of-life, or something else goes wrong. It " + "is also possible that your relay might get rejected from the " + "network due to a missing valid contact address.\n"); tor_free(msg); done: @@ -4276,7 +4273,9 @@ test_options_trial_assign(void *arg) tt_int_op(r, OP_EQ, 0); v = options_trial_assign(lines, 0, &msg); tt_int_op(v, OP_EQ, SETOPT_ERR_PARSE); - tt_str_op(msg, OP_EQ, "Unrecognized value ambidextrous."); + tt_str_op(msg, OP_EQ, + "Could not parse UseBridges: Unrecognized value ambidextrous. " + "Allowed values are 0 and 1."); tor_free(msg); config_free_lines(lines); diff --git a/src/test/test_pem.c b/src/test/test_pem.c index 8f9f10f787..9772be124b 100644 --- a/src/test/test_pem.c +++ b/src/test/test_pem.c @@ -115,8 +115,38 @@ test_crypto_pem_decode(void *arg) ; } +static void +test_crypto_pem_decode_crlf(void *arg) +{ + (void)arg; + char crlf_version[4096]; + uint8_t buf[4096]; + + /* Convert 'expected' to a version with CRLF instead of LF. */ + const char *inp = expected; + char *outp = crlf_version; + while (*inp) { + if (*inp == '\n') { + *outp++ = '\r'; + } + *outp++ = *inp++; + } + *outp = 0; + + /* Decoding should succeed (or else we have bug 33032 again) */ + int n = pem_decode(buf, sizeof(buf), + crlf_version, strlen(crlf_version), + "WOMBAT QUOTE"); + tt_int_op(n, OP_EQ, strlen(example_pre)); + tt_mem_op(buf, OP_EQ, example_pre, n); + + done: + ; +} + struct testcase_t pem_tests[] = { { "encode", test_crypto_pem_encode, 0, NULL, NULL }, { "decode", test_crypto_pem_decode, 0, NULL, NULL }, + { "decode_crlf", test_crypto_pem_decode_crlf, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 762241249c..7949e90e9e 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -62,8 +62,8 @@ test_policy_summary_helper_family_flags(const char *policy_str, short_policy_t *short_policy = NULL; int success = 0; - line.key = (char*)"foo"; - line.value = (char *)policy_str; + line.key = (char *) "foo"; + line.value = (char *) policy_str; line.next = NULL; r = policies_parse_exit_policy(&line, &policy, @@ -2124,20 +2124,6 @@ test_policies_fascist_firewall_allows_address(void *arg) teardown_capture_of_logs(); \ STMT_END -/** Mock the preferred address function to return zero (prefer IPv4). */ -static int -mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv4(void) -{ - return 0; -} - -/** Mock the preferred address function to return one (prefer IPv6). */ -static int -mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv6(void) -{ - return 1; -} - /** Run unit tests for fascist_firewall_choose_address */ static void test_policies_fascist_firewall_choose_address(void *arg) @@ -2536,42 +2522,6 @@ test_policies_fascist_firewall_choose_address(void *arg) CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, ipv4_dir_ap); - /* Test ClientAutoIPv6ORPort and pretend we prefer IPv4. */ - memset(&mock_options, 0, sizeof(or_options_t)); - mock_options.ClientAutoIPv6ORPort = 1; - mock_options.ClientUseIPv4 = 1; - mock_options.ClientUseIPv6 = 1; - MOCK(fascist_firewall_rand_prefer_ipv6_addr, - mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv4); - /* Simulate the initialisation of fake_node.ipv6_preferred */ - fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( - &mock_options); - - CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, - ipv4_or_ap); - CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, - ipv4_or_ap); - - UNMOCK(fascist_firewall_rand_prefer_ipv6_addr); - - /* Test ClientAutoIPv6ORPort and pretend we prefer IPv6. */ - memset(&mock_options, 0, sizeof(or_options_t)); - mock_options.ClientAutoIPv6ORPort = 1; - mock_options.ClientUseIPv4 = 1; - mock_options.ClientUseIPv6 = 1; - MOCK(fascist_firewall_rand_prefer_ipv6_addr, - mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv6); - /* Simulate the initialisation of fake_node.ipv6_preferred */ - fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( - &mock_options); - - CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, - ipv6_or_ap); - CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, - ipv6_or_ap); - - UNMOCK(fascist_firewall_rand_prefer_ipv6_addr); - /* Test firewall_choose_address_ls(). To do this, we make a fake link * specifier. */ smartlist_t *lspecs = smartlist_new(), diff --git a/src/test/test_prob_distr.c b/src/test/test_prob_distr.c index c3d1c80d70..c5423ce14a 100644 --- a/src/test/test_prob_distr.c +++ b/src/test/test_prob_distr.c @@ -1223,14 +1223,16 @@ test_stochastic_weibull_impl(double lambda, double k) .k = k, }; +// clang-format off /* * XXX Consider applying a Tiku-Singh test: * * M.L. Tiku and M. Singh, `Testing the two-parameter * Weibull distribution', Communications in Statistics -- * Theory and Methods A10(9), 1981, 907--918. - *https://www.tandfonline.com/doi/pdf/10.1080/03610928108828082?needAccess=true +https://www.tandfonline.com/doi/pdf/10.1080/03610928108828082?needAccess=true */ +// clang-format on return test_psi_dist_sample(&dist.base); } diff --git a/src/test/test_process_descs.c b/src/test/test_process_descs.c index 0e9cc9d253..14865cff13 100644 --- a/src/test/test_process_descs.c +++ b/src/test/test_process_descs.c @@ -20,13 +20,10 @@ test_process_descs_versions(void *arg) { "Tor 0.1.2.3-alpha", true }, // a non-tor program: don't reject. { "Wombat 0.1.2.3-alpha", false }, - // a slightly old version: reject - { "Tor 0.2.9.4-alpha", true }, - // a slightly old version: just new enough to support. - { "Tor 0.2.9.5-alpha", false }, - // a newer 0.2.9 version: supported. - { "Tor 0.2.9.100", false }, // some unsupported versions: reject. + { "Tor 0.2.9.4-alpha", true }, + { "Tor 0.2.9.5-alpha", true }, + { "Tor 0.2.9.100", true }, { "Tor 0.3.0.0-alpha-dev", true }, { "Tor 0.3.0.2-alpha", true }, { "Tor 0.3.0.5", true }, @@ -37,11 +34,17 @@ test_process_descs_versions(void *arg) { "Tor 0.3.4.100", true }, { "Tor 0.3.5.1-alpha", true }, { "Tor 0.3.5.6-rc", true}, + { "Tor 0.4.0.1-alpha", true }, + { "Tor 0.4.0.5", true }, + { "Tor 0.4.1.1-alpha", true }, + { "Tor 0.4.1.4-rc", true }, // new enough to be supported { "Tor 0.3.5.7", false }, { "Tor 0.3.5.8", false }, - { "Tor 0.4.0.1-alpha", false }, { "Tor 0.4.1.5", false }, + { "Tor 0.4.2.1-alpha", false }, + { "Tor 0.4.2.4-rc", false }, + { "Tor 0.4.3.0-alpha-dev", false }, // Very far in the future { "Tor 100.100.1.5", false }, }; diff --git a/src/test/test_protover.c b/src/test/test_protover.c index 7d08911021..c33fbcae2c 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -317,6 +317,7 @@ test_protover_all_supported(void *arg) tt_assert(protover_all_supported("Fribble=", &msg)); tt_ptr_op(msg, OP_EQ, NULL); +#ifndef ALL_BUGS_ARE_FATAL /* If we get a completely unparseable list, protover_all_supported should * hit a fatal assertion for BUG(entries == NULL). */ tor_capture_bugs_(1); @@ -328,9 +329,10 @@ test_protover_all_supported(void *arg) tor_capture_bugs_(1); tt_assert(protover_all_supported("Sleen=1-4294967295", &msg)); tor_end_capture_bugs_(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* Protocol name too long */ -#ifndef HAVE_RUST // XXXXXX ????? +#if !defined(HAVE_RUST) && !defined(ALL_BUGS_ARE_FATAL) tor_capture_bugs_(1); tt_assert(protover_all_supported( "DoSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" @@ -338,7 +340,7 @@ test_protover_all_supported(void *arg) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaa=1-65536", &msg)); tor_end_capture_bugs_(); -#endif /* !defined(HAVE_RUST) */ +#endif /* !defined(HAVE_RUST) && !defined(ALL_BUGS_ARE_FATAL) */ done: tor_end_capture_bugs_(); diff --git a/src/test/test_pt.c b/src/test/test_pt.c index 26eaf7b7e7..893fec3674 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -579,8 +579,10 @@ test_get_pt_proxy_uri(void *arg) tor_free(uri); } +#ifndef COCCI #define PT_LEGACY(name) \ - { #name, test_pt_ ## name , 0, NULL, NULL } + { (#name), test_pt_ ## name , 0, NULL, NULL } +#endif struct testcase_t pt_tests[] = { PT_LEGACY(parsing), diff --git a/src/test/test_router.c b/src/test/test_router.c index 572ddceaa7..cf0c2b3dd1 100644 --- a/src/test/test_router.c +++ b/src/test/test_router.c @@ -8,11 +8,13 @@ **/ #define CONFIG_PRIVATE +#define CONNECTION_PRIVATE #define ROUTER_PRIVATE #include "core/or/or.h" #include "app/config/config.h" #include "core/mainloop/mainloop.h" +#include "core/mainloop/connection.h" #include "feature/hibernate/hibernate.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/networkstatus_st.h" @@ -27,6 +29,8 @@ #include "lib/crypt_ops/crypto_ed25519.h" #include "lib/encoding/confline.h" +#include "core/or/listener_connection_st.h" + /* Test suite stuff */ #include "test/test.h" #include "test/log_test_helpers.h" @@ -486,6 +490,117 @@ test_router_get_my_family(void *arg) #undef CLEAR } +static smartlist_t *fake_connection_array = NULL; +static smartlist_t * +mock_get_connection_array(void) +{ + return fake_connection_array; +} + +static void +test_router_get_advertised_or_port(void *arg) +{ + (void)arg; + int r, w=0, n=0; + char *msg=NULL; + or_options_t *opts = options_new(); + listener_connection_t *listener = NULL; + tor_addr_port_t ipv6; + + // Test one failing case of router_get_advertised_ipv6_or_ap(). + router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::]:0"); + + // And one failing case of router_get_advertised_or_port(). + tt_int_op(0, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); + tt_int_op(0, OP_EQ, router_get_advertised_or_port(opts)); + + // Set up a couple of configured ports. + config_line_append(&opts->ORPort_lines, "ORPort", "[1234::5678]:auto"); + config_line_append(&opts->ORPort_lines, "ORPort", "5.6.7.8:9999"); + r = parse_ports(opts, 0, &msg, &n, &w); + tt_assert(r == 0); + + // There are no listeners, so the "auto" case will turn up no results. + tt_int_op(0, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); + router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::]:0"); + + // This will return the matching value from the configured port. + tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); + tt_int_op(9999, OP_EQ, router_get_advertised_or_port(opts)); + + // Now set up a dummy listener. + MOCK(get_connection_array, mock_get_connection_array); + fake_connection_array = smartlist_new(); + listener = listener_connection_new(CONN_TYPE_OR_LISTENER, AF_INET6); + TO_CONN(listener)->port = 54321; + smartlist_add(fake_connection_array, TO_CONN(listener)); + + // We should get a port this time. + tt_int_op(54321, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); + + // Test one succeeding case of router_get_advertised_ipv6_or_ap(). + router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, + "[1234::5678]:54321"); + + // This will return the matching value from the configured port. + tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); + tt_int_op(9999, OP_EQ, router_get_advertised_or_port(opts)); + + done: + or_options_free(opts); + config_free_all(); + smartlist_free(fake_connection_array); + connection_free_minimal(TO_CONN(listener)); + UNMOCK(get_connection_array); +} + +static void +test_router_get_advertised_or_port_localhost(void *arg) +{ + (void)arg; + int r, w=0, n=0; + char *msg=NULL; + or_options_t *opts = options_new(); + tor_addr_port_t ipv6; + + // Set up a couple of configured ports on localhost. + config_line_append(&opts->ORPort_lines, "ORPort", "[::1]:9999"); + config_line_append(&opts->ORPort_lines, "ORPort", "127.0.0.1:8888"); + r = parse_ports(opts, 0, &msg, &n, &w); + tt_assert(r == 0); + + // We should refuse to advertise them, since we have default dirauths. + router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::]:0"); + // But the lower-level function should still report the correct value + tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); + + // The IPv4 checks are done in resolve_my_address(), which doesn't use + // ORPorts so we can't test them here. (See #33681.) Both these lower-level + // functions should still report the correct value. + tt_int_op(8888, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); + tt_int_op(8888, OP_EQ, router_get_advertised_or_port(opts)); + + // Now try with a fake authority set up. + config_line_append(&opts->DirAuthorities, "DirAuthority", + "127.0.0.1:1066 " + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + + tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); + router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::1]:9999"); + + tt_int_op(8888, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); + tt_int_op(8888, OP_EQ, router_get_advertised_or_port(opts)); + + done: + or_options_free(opts); + config_free_all(); +} + #define ROUTER_TEST(name, flags) \ { #name, test_router_ ## name, flags, NULL, NULL } @@ -494,5 +609,7 @@ struct testcase_t router_tests[] = { ROUTER_TEST(dump_router_to_string_no_bridge_distribution_method, TT_FORK), ROUTER_TEST(mark_if_too_old, TT_FORK), ROUTER_TEST(get_my_family, TT_FORK), + ROUTER_TEST(get_advertised_or_port, TT_FORK), + ROUTER_TEST(get_advertised_or_port_localhost, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index f2accb2376..148eb5cf90 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -21,7 +21,7 @@ #include "feature/nodelist/dirlist.h" #include "feature/dirparse/authcert_parse.h" #include "feature/hs_common/shared_random_client.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/dirclient/dir_server_st.h" #include "feature/nodelist/networkstatus_st.h" @@ -193,7 +193,7 @@ test_get_state_valid_until_time(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), current_time); + dirauth_sched_recalculate_timing(get_options(), current_time); valid_until_time = get_state_valid_until_time(current_time); /* Compare it with the correct result */ @@ -205,7 +205,7 @@ test_get_state_valid_until_time(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 19:22:00 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), current_time); + dirauth_sched_recalculate_timing(get_options(), current_time); valid_until_time = get_state_valid_until_time(current_time); format_iso_time(tbuf, valid_until_time); @@ -216,7 +216,7 @@ test_get_state_valid_until_time(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:00 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), current_time); + dirauth_sched_recalculate_timing(get_options(), current_time); valid_until_time = get_state_valid_until_time(current_time); format_iso_time(tbuf, valid_until_time); @@ -227,7 +227,7 @@ test_get_state_valid_until_time(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), current_time); + dirauth_sched_recalculate_timing(get_options(), current_time); valid_until_time = get_state_valid_until_time(current_time); format_iso_time(tbuf, valid_until_time); @@ -265,7 +265,7 @@ test_get_start_time_of_current_run(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), current_time); + dirauth_sched_recalculate_timing(get_options(), current_time); run_start_time = sr_state_get_start_time_of_current_protocol_run(); /* Compare it with the correct result */ @@ -277,7 +277,7 @@ test_get_start_time_of_current_run(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:59 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), current_time); + dirauth_sched_recalculate_timing(get_options(), current_time); run_start_time = sr_state_get_start_time_of_current_protocol_run(); /* Compare it with the correct result */ @@ -289,7 +289,7 @@ test_get_start_time_of_current_run(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), current_time); + dirauth_sched_recalculate_timing(get_options(), current_time); run_start_time = sr_state_get_start_time_of_current_protocol_run(); /* Compare it with the correct result */ @@ -319,7 +319,7 @@ test_get_start_time_of_current_run(void *arg) ¤t_time); tt_int_op(retval, OP_EQ, 0); update_approx_time(current_time); - voting_schedule_recalculate_timing(get_options(), current_time); + dirauth_sched_recalculate_timing(get_options(), current_time); run_start_time = sr_state_get_start_time_of_current_protocol_run(); @@ -327,7 +327,7 @@ test_get_start_time_of_current_run(void *arg) format_iso_time(tbuf, run_start_time); tt_str_op("2015-04-19 00:00:00", OP_EQ, tbuf); /* Check that voting_schedule.interval_starts is at 01:00 (see above) */ - time_t interval_starts = voting_schedule_get_next_valid_after_time(); + time_t interval_starts = dirauth_sched_get_next_valid_after_time(); format_iso_time(tbuf, interval_starts); tt_str_op("2015-04-20 01:00:00", OP_EQ, tbuf); } @@ -346,7 +346,7 @@ test_get_start_time_of_current_run(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:15:32 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - voting_schedule_recalculate_timing(get_options(), current_time); + dirauth_sched_recalculate_timing(get_options(), current_time); run_start_time = sr_state_get_start_time_of_current_protocol_run(); /* Compare it with the correct result */ @@ -378,13 +378,13 @@ test_get_start_time_functions(void *arg) tt_int_op(retval, OP_EQ, 0); time_t now = mock_consensus.valid_after; - voting_schedule_recalculate_timing(get_options(), now); + dirauth_sched_recalculate_timing(get_options(), now); time_t start_time_of_protocol_run = sr_state_get_start_time_of_current_protocol_run(); tt_assert(start_time_of_protocol_run); /* Check that the round start time of the beginning of the run, is itself */ - tt_int_op(get_start_time_of_current_round(), OP_EQ, + tt_int_op(dirauth_sched_get_cur_valid_after_time(), OP_EQ, start_time_of_protocol_run); done: diff --git a/src/test/test_util.c b/src/test/test_util.c index 0d86a5ab5d..b2ee7cd35c 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1850,6 +1850,56 @@ test_util_config_line_crlf(void *arg) tor_free(k); tor_free(v); } +static void +test_util_config_line_partition(void *arg) +{ + (void)arg; + config_line_t *lines = NULL, *orig, *rest = NULL; + + config_line_append(&lines, "Header", "X"); + config_line_append(&lines, "Item", "Y"); + config_line_append(&lines, "Thing", "Z"); + + config_line_append(&lines, "HEADER", "X2"); + + config_line_append(&lines, "header", "X3"); + config_line_append(&lines, "Item3", "Foob"); + + /* set up h2 and h3 to point to the places where we hope the headers will + be. */ + config_line_t *h2 = lines->next->next->next; + config_line_t *h3 = h2->next; + tt_str_op(h2->key, OP_EQ, "HEADER"); + tt_str_op(h3->key, OP_EQ, "header"); + + orig = lines; + rest = config_lines_partition(lines, "Header"); + tt_ptr_op(lines, OP_EQ, orig); + tt_ptr_op(rest, OP_EQ, h2); + tt_str_op(lines->next->key, OP_EQ, "Item"); + tt_str_op(lines->next->next->key, OP_EQ, "Thing"); + tt_ptr_op(lines->next->next->next, OP_EQ, NULL); + config_free_lines(lines); + + orig = lines = rest; + rest = config_lines_partition(lines, "Header"); + tt_ptr_op(lines, OP_EQ, orig); + tt_ptr_op(rest, OP_EQ, h3); + tt_ptr_op(lines->next, OP_EQ, NULL); + config_free_lines(lines); + + orig = lines = rest; + rest = config_lines_partition(lines, "Header"); + tt_ptr_op(lines, OP_EQ, orig); + tt_ptr_op(rest, OP_EQ, NULL); + tt_str_op(lines->next->key, OP_EQ, "Item3"); + tt_ptr_op(lines->next->next, OP_EQ, NULL); + + done: + config_free_lines(lines); + config_free_lines(rest); +} + #ifndef DISABLE_PWDB_TESTS static void test_util_expand_filename(void *arg) @@ -4572,6 +4622,35 @@ test_util_di_ops(void *arg) } static void +test_util_memcpy_iftrue_timei(void *arg) +{ + (void)arg; + char buf1[25]; + char buf2[25]; + char buf3[25]; + + for (int i = 0; i < 100; ++i) { + crypto_rand(buf1, sizeof(buf1)); + crypto_rand(buf2, sizeof(buf2)); + memcpy(buf3, buf1, sizeof(buf1)); + + /* We just copied buf1 into buf3. Now we're going to copy buf2 into buf2, + iff our coin flip comes up heads. */ + bool coinflip = crypto_rand_int(2) == 0; + + memcpy_if_true_timei(coinflip, buf3, buf2, sizeof(buf3)); + + if (coinflip) { + tt_mem_op(buf3, OP_EQ, buf2, sizeof(buf2)); + } else { + tt_mem_op(buf3, OP_EQ, buf1, sizeof(buf1)); + } + } + done: + ; +} + +static void test_util_di_map(void *arg) { (void)arg; @@ -6305,42 +6384,42 @@ test_util_map_anon_nofork(void *arg) #ifndef COCCI #define UTIL_LEGACY(name) \ - { #name, test_util_ ## name , 0, NULL, NULL } + { (#name), test_util_ ## name , 0, NULL, NULL } #define UTIL_TEST(name, flags) \ - { #name, test_util_ ## name, flags, NULL, NULL } + { (#name), test_util_ ## name, flags, NULL, NULL } #define COMPRESS(name, identifier) \ - { "compress/" #name, test_util_compress, 0, &compress_setup, \ + { ("compress/" #name), test_util_compress, 0, &compress_setup, \ (char*)(identifier) } #define COMPRESS_CONCAT(name, identifier) \ - { "compress_concat/" #name, test_util_decompress_concatenated, 0, \ + { ("compress_concat/" #name), test_util_decompress_concatenated, 0, \ &compress_setup, \ (char*)(identifier) } #define COMPRESS_JUNK(name, identifier) \ - { "compress_junk/" #name, test_util_decompress_junk, 0, \ + { ("compress_junk/" #name), test_util_decompress_junk, 0, \ &compress_setup, \ (char*)(identifier) } #define COMPRESS_DOS(name, identifier) \ - { "compress_dos/" #name, test_util_decompress_dos, 0, \ + { ("compress_dos/" #name), test_util_decompress_dos, 0, \ &compress_setup, \ (char*)(identifier) } -#endif /* !defined(COCCI) */ #ifdef _WIN32 #define UTIL_TEST_WIN_ONLY(n, f) UTIL_TEST(n, (f)) #else -#define UTIL_TEST_WIN_ONLY(n, f) { #n, NULL, TT_SKIP, NULL, NULL } +#define UTIL_TEST_WIN_ONLY(n, f) { (#n), NULL, TT_SKIP, NULL, NULL } #endif #ifdef DISABLE_PWDB_TESTS -#define UTIL_TEST_PWDB(n, f) { #n, NULL, TT_SKIP, NULL, NULL } +#define UTIL_TEST_PWDB(n, f) { (#n), NULL, TT_SKIP, NULL, NULL } #else #define UTIL_TEST_PWDB(n, f) UTIL_TEST(n, (f)) #endif +#endif /* !defined(COCCI) */ struct testcase_t util_tests[] = { UTIL_LEGACY(time), @@ -6350,6 +6429,7 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(config_line_comment_character), UTIL_LEGACY(config_line_escaped_content), UTIL_LEGACY(config_line_crlf), + UTIL_TEST(config_line_partition, 0), UTIL_TEST_PWDB(expand_filename, 0), UTIL_LEGACY(escape_string_socks), UTIL_LEGACY(string_is_key_value), @@ -6386,6 +6466,7 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(path_is_relative), UTIL_LEGACY(strtok), UTIL_LEGACY(di_ops), + UTIL_TEST(memcpy_iftrue_timei, 0), UTIL_TEST(di_map, 0), UTIL_TEST(round_to_next_multiple_of, 0), UTIL_TEST(laplace, 0), diff --git a/src/test/test_util_process.c b/src/test/test_util_process.c index 0e17e009f3..fc79fe9b1f 100644 --- a/src/test/test_util_process.c +++ b/src/test/test_util_process.c @@ -67,15 +67,16 @@ test_util_process_clear_waitpid_callback(void *ignored) } #endif /* !defined(_WIN32) */ +#ifndef COCCI #ifndef _WIN32 -#define TEST(name) { #name, test_util_process_##name, 0, NULL, NULL } +#define TEST(name) { (#name), test_util_process_##name, 0, NULL, NULL } #else -#define TEST(name) { #name, NULL, TT_SKIP, NULL, NULL } +#define TEST(name) { (#name), NULL, TT_SKIP, NULL, NULL } #endif +#endif /* !defined(COCCI) */ struct testcase_t util_process_tests[] = { TEST(set_waitpid_callback), TEST(clear_waitpid_callback), END_OF_TESTCASES }; - diff --git a/src/test/test_voting_schedule.c b/src/test/test_voting_schedule.c index 54d1815a77..df64b79167 100644 --- a/src/test/test_voting_schedule.c +++ b/src/test/test_voting_schedule.c @@ -4,14 +4,15 @@ #include "orconfig.h" #include "core/or/or.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" +#include "feature/nodelist/networkstatus.h" #include "test/test.h" static void test_voting_schedule_interval_start(void *arg) { -#define next_interval voting_schedule_get_start_of_next_interval +#define next_interval voting_sched_get_start_of_interval_after (void)arg; char buf[ISO_TIME_LEN+1]; @@ -61,4 +62,3 @@ struct testcase_t voting_schedule_tests[] = { VS(interval_start, 0), END_OF_TESTCASES }; - diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 655e36e336..b3337f24b0 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -273,7 +273,7 @@ main(int c, const char **v) int loglevel = LOG_ERR; int accel_crypto = 0; - subsystems_init_upto(SUBSYS_LEVEL_LIBS); + subsystems_init(); options = options_new(); @@ -358,6 +358,21 @@ main(int c, const char **v) atexit(remove_directory); + /* Look for TOR_SKIP_TESTCASES: a space-separated list of tests to skip. */ + const char *skip_tests = getenv("TOR_SKIP_TESTCASES"); + if (skip_tests) { + smartlist_t *skip = smartlist_new(); + smartlist_split_string(skip, skip_tests, NULL, + SPLIT_IGNORE_BLANK, -1); + int n = 0; + SMARTLIST_FOREACH_BEGIN(skip, char *, cp) { + n += tinytest_skip(testgroups, cp); + tor_free(cp); + } SMARTLIST_FOREACH_END(cp); + printf("Skipping %d testcases.\n", n); + smartlist_free(skip); + } + int have_failed = (tinytest_main(c, v, testgroups) != 0); free_pregenerated_keys(); |