diff options
123 files changed, 4053 insertions, 1408 deletions
diff --git a/.travis.yml b/.travis.yml index af5052682f..16d2e432df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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,48 @@ 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" + ## 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 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 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 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" + + ## 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 +92,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 +103,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 @@ -230,7 +245,7 @@ script: ## 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 +262,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 diff --git a/Makefile.am b/Makefile.am index 7774995aea..1a1fa9b658 100644 --- a/Makefile.am +++ b/Makefile.am @@ -277,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 \ 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/bug33285 b/changes/bug33285 new file mode 100644 index 0000000000..a4d06a7eb8 --- /dev/null +++ b/changes/bug33285 @@ -0,0 +1,3 @@ + o Minor bugfixes (protocol versions): + - Sort tor's supported protocol version lists, as recommended by the + tor directory specification. Fixes bug 33285; bugfix on 0.4.0.1-alpha. diff --git a/changes/bug33374 b/changes/bug33374 new file mode 100644 index 0000000000..d1141faf78 --- /dev/null +++ b/changes/bug33374 @@ -0,0 +1,5 @@ + o Minor bugfixes (coding best practices checks): + - Allow the "practracker" coding best practices checking 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. 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/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/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/ticket33029 b/changes/ticket33029 new file mode 100644 index 0000000000..c32ee4ad84 --- /dev/null +++ b/changes/ticket33029 @@ -0,0 +1,5 @@ + 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. 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/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/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/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/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/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/tor.1.txt b/doc/tor.1.txt index db4dd2755a..52beb3221c 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2764,6 +2764,7 @@ 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 @@ -2774,22 +2775,6 @@ Denial of Service mitigation subsystem described above. 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 +2783,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 +2803,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 +2828,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 +2840,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 +2864,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 +2872,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 +2894,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 +2946,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 +2953,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 +2999,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 +3061,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**, + **RecommendedClientVersions**, and **RecommendedServerVersions**. == HIDDEN SERVICE OPTIONS @@ -3089,33 +3104,11 @@ The next section describes the per service options that can only be set **PER SERVICE OPTIONS:** -[[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 - specify multiple services. If DIRECTORY does not exist, Tor will create it. - Please note that you cannot add new Onion Service to already running Tor - instance if **Sandbox** is enabled. - (Note: in current versions of Tor, if DIRECTORY is a relative path, - it will be relative to the current - working directory of Tor instance, not to its DataDirectory. Do not - 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. - -[[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) +[[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 @@ -3131,11 +3124,47 @@ The next section describes the per service options that can only be set services; v3 services configure client authentication in a subdirectory of HiddenServiceDir instead (see the **Client Authorization** section). -[[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) +[[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 + specify multiple services. If DIRECTORY does not exist, Tor will create it. + Please note that you cannot add new Onion Service to already running Tor + instance if **Sandbox** is enabled. + (Note: in current versions of Tor, if DIRECTORY is a relative path, + it will be relative to the current + working directory of Tor instance, not to its DataDirectory. Do not + rely on this behavior; it is not guaranteed to remain the same in future + versions.) + +[[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. + +[[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) + +//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) + +[[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 +3193,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 +3216,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 +3243,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 +3273,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,6 +3282,13 @@ 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) +[[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 (Version 3 only) @@ -3302,6 +3334,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 +3375,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,11 +3411,6 @@ 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 @@ -3457,40 +3461,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 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/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/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..b4ef1e51aa 100644 --- a/scripts/maint/practracker/exceptions.txt +++ b/scripts/maint/practracker/exceptions.txt @@ -35,7 +35,6 @@ problem file-size /src/app/config/config.c 7400 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 @@ -48,8 +47,8 @@ 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 1053 +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 @@ -63,17 +62,16 @@ 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 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 @@ -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 @@ -105,7 +103,6 @@ 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 @@ -123,7 +120,7 @@ problem function-size /src/core/or/circuitstats.c:circuit_build_times_parse_stat problem dependency-violation /src/core/or/circuitstats.c 11 problem file-size /src/core/or/circuituse.c 3162 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 @@ -146,23 +143,22 @@ problem function-size /src/core/or/connection_edge.c:connection_exit_connect() 1 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/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 3195 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 problem dependency-violation /src/core/or/reasons.c 2 problem file-size /src/core/or/relay.c 3264 +problem dependency-violation /src/core/or/relay_handshake.c 5 problem function-size /src/core/or/relay.c:circuit_receive_relay_cell() 127 problem function-size /src/core/or/relay.c:relay_send_command_from_edge_() 109 problem function-size /src/core/or/relay.c:connection_ap_process_end_not_open() 192 @@ -207,15 +203,15 @@ problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_misc 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 file-size /src/feature/dirauth/dirvote.c 4687 problem include-count /src/feature/dirauth/dirvote.c 53 -problem function-size /src/feature/dirauth/dirvote.c:format_networkstatus_vote() 231 +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_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 @@ -226,7 +222,7 @@ problem function-size /src/feature/dircache/consdiffmgr.c:consdiffmgr_rescan_fla 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 file-size /src/feature/dirclient/dirclient.c 3156 problem include-count /src/feature/dirclient/dirclient.c 51 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 @@ -248,20 +244,19 @@ problem function-size /src/feature/hibernate/hibernate.c:accounting_parse_option 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_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 4131 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 function-size /src/feature/nodelist/networkstatus.c:networkstatus_check_consensus_signature() 175 @@ -279,6 +274,7 @@ problem function-size /src/feature/nodelist/routerlist.c:update_extrainfo_downlo 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 function-size /src/feature/relay/relay_handshake.c:connection_or_compute_authenticate_cell_body() 231 problem file-size /src/feature/relay/router.c 3520 problem include-count /src/feature/relay/router.c 57 problem function-size /src/feature/relay/router.c:init_keys() 252 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..c6c5026711 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 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/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..57aa055e73 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -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), 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/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 4a2dc21f1c..4965c2a1f4 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" @@ -3137,7 +3138,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 +3273,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 +3292,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 @@ -3345,8 +3347,17 @@ record_num_bytes_transferred_impl(connection_t *conn, rep_hist_note_dir_bytes_written(num_written, now); } + /* Linked connections and internal IPs aren't counted for statistics or + * accounting: + * - counting linked connections would double-count BEGINDIR bytes, because + * they are sent as Dir bytes on the linked connection, and OR bytes on + * the OR connection; + * - relays and clients don't connect to internal IPs, unless specifically + * configured to do so. If they are configured that way, we don't count + * internal bytes. + */ if (!connection_is_rate_limited(conn)) - return; /* local IPs are free */ + return; if (conn->type == CONN_TYPE_OR) rep_hist_note_or_conn_bytes(conn->global_identifier, num_read, 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/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/circuitmux.c b/src/core/or/circuitmux.c index 1f79ea4d91..da95e93657 100644 --- a/src/core/or/circuitmux.c +++ b/src/core/or/circuitmux.c @@ -926,7 +926,7 @@ circuitmux_detach_circuit,(circuitmux_t *cmux, circuit_t *circ)) /* Wipe and free the hash entry */ // This isn't sensitive, but we want to be sure to know if we're accessing // this accidentally. - memwipe(hashent, 0xef, sizeof(hashent)); + memwipe(hashent, 0xef, sizeof(*hashent)); tor_free(hashent); } } 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..6dd646637b 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 @@ -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/protover.c b/src/core/or/protover.c index 523c124a4f..c3f443631b 100644 --- a/src/core/or/protover.c +++ b/src/core/or/protover.c @@ -391,6 +391,7 @@ protover_get_supported_protocols(void) "Cons=1-2 " "Desc=1-2 " "DirCache=1-2 " + "FlowCtrl=1 " "HSDir=1-2 " "HSIntro=3-5 " "HSRend=1-2 " @@ -401,9 +402,8 @@ protover_get_supported_protocols(void) "LinkAuth=3 " #endif "Microdesc=1-2 " - "Relay=1-2 " "Padding=2 " - "FlowCtrl=1"; + "Relay=1-2"; } /** The protocols from protover_get_supported_protocols(), as parsed into a 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..b6f5846456 100644 --- a/src/core/or/scheduler_kist.c +++ b/src/core/or/scheduler_kist.c @@ -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/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/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/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/dirauth/dirauth_config.c b/src/feature/dirauth/dirauth_config.c index 3aeeab3b31..ca16dc8424 100644 --- a/src/feature/dirauth/dirauth_config.c +++ b/src/feature/dirauth/dirauth_config.c @@ -27,6 +27,7 @@ #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. diff --git a/src/feature/dirauth/dirauth_config.h b/src/feature/dirauth/dirauth_config.h index 2ebafd917e..1ec599717c 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) */ @@ -78,6 +80,8 @@ options_validate_dirauth_mode(const struct or_options_t *old_options, #define options_act_dirauth_stats(old_options, print_notice_out) \ (((void)(old_options)),((void)(print_notice_out)),0) +#define dirauth_should_reject_requests_under_load() (false) + #endif /* defined(HAVE_MODULE_DIRAUTH) */ #endif /* !defined(TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H) */ 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/dirvote.c b/src/feature/dirauth/dirvote.c index 9490867e82..90e82a4ee8 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -2963,7 +2963,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 +3119,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 +3188,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 +3201,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 +3214,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); @@ -4619,17 +4650,14 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, /* These are hardwired, to avoid disaster. */ v3_out->recommended_relay_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 Microdesc=1-2 Relay=2"); + tor_strdup(DIRVOTE_RECCOMEND_RELAY_PROTO); v3_out->recommended_client_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 Microdesc=1-2 Relay=2"); - v3_out->required_client_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 Microdesc=1-2 Relay=2"); + tor_strdup(DIRVOTE_RECCOMEND_CLIENT_PROTO); + v3_out->required_relay_protocols = - tor_strdup("Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=3-4 Microdesc=1 Relay=1-2"); + tor_strdup(DIRVOTE_REQUIRE_RELAY_PROTO); + v3_out->required_client_protocols = + tor_strdup(DIRVOTE_REQUIRE_CLIENT_PROTO); /* We are not allowed to vote to require anything we don't have. */ tor_assert(protover_all_supported(v3_out->required_relay_protocols, NULL)); diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h index f695e93abf..f2dbee20e5 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(); @@ -230,6 +235,36 @@ char *networkstatus_get_detached_signatures(smartlist_t *consensuses); STATIC microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method); +/** The recommended relay protocols for this authority's votes. + * Recommending a new protocol causes old tor versions to log a warning. + */ +#define DIRVOTE_RECCOMEND_RELAY_PROTO \ + "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " \ + "Link=4 Microdesc=1-2 Relay=2" +/** The recommended client protocols for this authority's votes. + * Recommending a new protocol causes old tor versions to log a warning. + */ +#define DIRVOTE_RECCOMEND_CLIENT_PROTO \ + "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " \ + "Link=4 Microdesc=1-2 Relay=2" + +/** The required relay protocols for this authority's votes. + * WARNING: Requiring a new protocol causes old tor versions to shut down. + * Requiring the wrong protocols can break the tor network. + * See Proposal 303: When and how to remove support for protocol versions. + */ +#define DIRVOTE_REQUIRE_RELAY_PROTO \ + "Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " \ + "Link=3-4 Microdesc=1 Relay=1-2" +/** The required relay protocols for this authority's votes. + * WARNING: Requiring a new protocol causes old tor versions to shut down. + * Requiring the wrong protocols can break the tor network. + * See Proposal 303: When and how to remove support for protocol versions. + */ +#define DIRVOTE_REQUIRE_CLIENT_PROTO \ + "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " \ + "Link=4 Microdesc=1-2 Relay=2" + #endif /* defined(DIRVOTE_PRIVATE) */ #endif /* !defined(TOR_DIRVOTE_H) */ 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..a216e5e27a 100644 --- a/src/feature/dirclient/dirclient.c +++ b/src/feature/dirclient/dirclient.c @@ -2364,7 +2364,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/hs/hs_cell.c b/src/feature/hs/hs_cell.c index 52bd663200..dd5fefd7e7 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 + * valication. Else 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..fdd226ba79 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->ob_subcreds)) { + return -1; + } + + /* We are an onionbalance instance: */ + data->n_subcredentials = service->n_ob_subcreds; + data->subcredentials = service->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_client.c b/src/feature/hs/hs_client.c index 222261e604..d5c1c5ca9a 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,7 +1845,7 @@ 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; @@ -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, + ret = hs_desc_decode_descriptor(desc_str, &subcredential, client_auht_sk, desc); - memwipe(subcredential, 0, sizeof(subcredential)); + 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..684f7bc975 100644 --- a/src/feature/hs/hs_config.c +++ b/src/feature/hs/hs_config.c @@ -26,6 +26,7 @@ #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" @@ -219,6 +220,7 @@ config_has_invalid_options(const config_line_t *line_, "HiddenServiceEnableIntroDoSDefense", "HiddenServiceEnableIntroDoSRatePerSec", "HiddenServiceEnableIntroDoSBurstPerSec", + "HiddenServiceOnionBalanceInstance", NULL /* End marker. */ }; @@ -317,7 +319,7 @@ config_service_v3(const config_line_t *line_, 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; + bool dos_burst_per_sec = false, ob_instance = false; const char *dup_opt_seen = NULL; const config_line_t *line; @@ -402,6 +404,27 @@ config_service_v3(const config_line_t *line_, config->intro_dos_burst_per_sec); continue; } + if (!strcasecmp(line->key, "HiddenServiceOnionBalanceInstance")) { + bool enabled = !!helper_parse_uint64(line->key, line->value, + 0, 1, &ok); + if (!ok || ob_instance) { + if (ob_instance) { + dup_opt_seen = line->key; + } + goto err; + } + ob_instance = true; + if (!enabled) { + /* Skip if this is disabled. */ + continue; + } + /* Option is enabled, parse config file. */ + ok = hs_ob_parse_config_file(config); + if (!ok) { + goto err; + } + continue; + } } /* We do not load the key material for the service at this stage. This is diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c index 65d6c7a581..c274ed7581 100644 --- a/src/feature/hs/hs_descriptor.c +++ b/src/feature/hs/hs_descriptor.c @@ -211,7 +211,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 +1018,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; @@ -1366,8 +1362,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 +1384,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 +1421,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 +2554,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 +2572,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 +2662,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 +2866,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 +2894,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..c18a789013 --- /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 "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_info(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->ob_subcreds) { + tor_free(service->ob_subcreds); + } + + service->ob_subcreds = ob_subcreds; + service->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..d5b5504be7 --- /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 "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_service.c b/src/feature/hs/hs_service.c index 81b37eab40..f0c791f21d 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" @@ -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)); } @@ -1764,7 +1770,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 +1788,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 +1844,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 +1987,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: @@ -3369,7 +3382,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; } @@ -4042,6 +4055,11 @@ hs_service_free_(hs_service_t *service) replaycache_free(service->state.replay_cache_rend_cookie); } + /* Free onionbalance subcredentials (if any) */ + if (service->ob_subcreds) { + tor_free(service->ob_subcreds); + } + /* Wipe service keys. */ memwipe(&service->keys.identity_sk, 0, sizeof(service->keys.identity_sk)); diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h index 8809411e01..94a73b2fec 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. */ @@ -301,8 +305,13 @@ typedef struct hs_service_t { /** Next descriptor. */ hs_service_descriptor_t *desc_next; - /* XXX: Credential (client auth.) #20700. */ - + /* 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_t; /** For the service global hash map, we define a specific type for it which diff --git a/src/feature/hs/include.am b/src/feature/hs/include.am index 5e69607e59..f83907c76b 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,7 @@ 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_service.h \ src/feature/hs/hs_stats.h \ src/feature/hs/hsdir_index_st.h 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/nodelist.c b/src/feature/nodelist/nodelist.c index 94ff08826f..9bee16fcaf 100644 --- a/src/feature/nodelist/nodelist.c +++ b/src/feature/nodelist/nodelist.c @@ -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/relay/dns.c b/src/feature/relay/dns.c index da0cbb1df4..99f48ab2c2 100644 --- a/src/feature/relay/dns.c +++ b/src/feature/relay/dns.c @@ -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(); @@ -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..5568dacf1a 100644 --- a/src/feature/relay/ext_orport.c +++ b/src/feature/relay/ext_orport.c @@ -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/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.h b/src/feature/relay/router.h index 782609d8ab..2e07df2e88 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -117,7 +117,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..2b5f03a2a3 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; + (void)sign_out; + 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; + (void)len_out; + 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/lib/ctime/di_ops.c b/src/lib/ctime/di_ops.c index 7448a9973e..e1ac0943fb 100644 --- a/src/lib/ctime/di_ops.c +++ b/src/lib/ctime/di_ops.c @@ -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/rust/protover/protover.rs b/src/rust/protover/protover.rs index 0ca960bd69..6d2ef33eec 100644 --- a/src/rust/protover/protover.rs +++ b/src/rust/protover/protover.rs @@ -161,30 +161,30 @@ pub(crate) fn get_supported_protocols_cstr() -> &'static CStr { "Cons=1-2 \ Desc=1-2 \ DirCache=1-2 \ + FlowCtrl=1 \ HSDir=1-2 \ HSIntro=3-4 \ HSRend=1-2 \ Link=1-5 \ LinkAuth=3 \ Microdesc=1-2 \ - Relay=1-2 \ Padding=2 \ - FlowCtrl=1" + Relay=1-2" ) } else { cstr!( "Cons=1-2 \ Desc=1-2 \ DirCache=1-2 \ + FlowCtrl=1 \ HSDir=1-2 \ HSIntro=3-4 \ HSRend=1-2 \ Link=1-5 \ LinkAuth=1,3 \ Microdesc=1-2 \ - Relay=1-2 \ Padding=2 \ - FlowCtrl=1" + Relay=1-2" ) } } 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/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_dir_handle_get.c b/src/test/test_dir_handle_get.c index 6293839b0d..cf310ebfe0 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -2084,8 +2084,8 @@ test_dir_handle_get_status_vote_d(void* data) 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); @@ -2459,8 +2459,8 @@ test_dir_handle_get_status_vote_next_authority(void* data) time_t now = 1441223455 -1; voting_schedule_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); @@ -2619,8 +2619,8 @@ test_dir_handle_get_status_vote_current_authority(void* data) time_t now = 1441223455; voting_schedule_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; + voting_schedule_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 */ + voting_schedule_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 */ + voting_schedule_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 */ + voting_schedule_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_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..2e603ec259 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); } diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index 61306778d4..20f88b637b 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -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. */ diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c index 43ac5490a1..61ccd3f919 100644 --- a/src/test/test_hs_descriptor.c +++ b/src/test/test_hs_descriptor.c @@ -221,7 +221,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 +230,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 +241,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 +253,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 +262,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 +286,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 +315,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 +762,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 +774,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 +795,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_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..1767648bbe 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -51,6 +51,8 @@ #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" @@ -109,6 +111,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 +129,10 @@ 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; } @@ -1160,7 +1169,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; @@ -2169,6 +2178,348 @@ 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.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); +} + struct testcase_t hs_service_tests[] = { { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK, NULL, NULL }, @@ -2194,7 +2545,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 }, @@ -2212,6 +2563,7 @@ struct testcase_t hs_service_tests[] = { 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_protover.c b/src/test/test_protover.c index f1d1ef0d4a..7d08911021 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -2,6 +2,7 @@ /* See LICENSE for licensing information */ #define PROTOVER_PRIVATE +#define DIRVOTE_PRIVATE #include "orconfig.h" #include "test/test.h" @@ -12,6 +13,8 @@ #include "core/or/connection_or.h" #include "lib/tls/tortls.h" +#include "feature/dirauth/dirvote.h" + static void test_protover_parse(void *arg) { @@ -634,6 +637,43 @@ test_protover_vote_roundtrip(void *args) tor_free(result); } +static void +test_protover_vote_roundtrip_ours(void *args) +{ + (void) args; + const char *examples[] = { + protover_get_supported_protocols(), + DIRVOTE_RECCOMEND_RELAY_PROTO, + DIRVOTE_RECCOMEND_CLIENT_PROTO, + DIRVOTE_REQUIRE_RELAY_PROTO, + DIRVOTE_REQUIRE_CLIENT_PROTO, + }; + unsigned u; + smartlist_t *votes = smartlist_new(); + char *result = NULL; + + for (u = 0; u < ARRAY_LENGTH(examples); ++u) { + tt_assert(examples[u]); + const char *input = examples[u]; + const char *expected_output = examples[u]; + + smartlist_add(votes, (void*)input); + result = protover_compute_vote(votes, 1); + if (expected_output != NULL) { + tt_str_op(result, OP_EQ, expected_output); + } else { + tt_str_op(result, OP_EQ, ""); + } + + smartlist_clear(votes); + tor_free(result); + } + + done: + smartlist_free(votes); + tor_free(result); +} + #define PV_TEST(name, flags) \ { #name, test_protover_ ##name, (flags), NULL, NULL } @@ -647,5 +687,6 @@ struct testcase_t protover_tests[] = { PV_TEST(supports_version, 0), PV_TEST(supported_protocols, 0), PV_TEST(vote_roundtrip, 0), + PV_TEST(vote_roundtrip_ours, 0), END_OF_TESTCASES }; diff --git a/src/test/test_util.c b/src/test/test_util.c index 0d86a5ab5d..b1e4335652 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -4572,6 +4572,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; @@ -6386,6 +6415,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), |