diff options
79 files changed, 1561 insertions, 527 deletions
@@ -1,6 +1,107 @@ Changes in version 0.3.0.1-alpha - 2016-??-?? +Changes in version 0.2.8.11 - 2016-12-08 + Tor 0.2.8.11 backports fixes for additional portability issues that + could prevent Tor from building correctly on OSX Sierra, or with + OpenSSL 1.1. Affected users should upgrade; others can safely stay + with 0.2.8.10. + + o Minor bugfixes (portability): + - Avoid compilation errors when building on OSX Sierra. Sierra began + to support the getentropy() and clock_gettime() APIs, but created + a few problems in doing so. Tor 0.2.9 has a more thorough set of + workarounds; in 0.2.8, we are just using the /dev/urandom and mach + monotonic time interfaces. Fixes bug 20865. Bugfix + on 0.2.8.1-alpha. + + o Minor bugfixes (portability, backport from 0.2.9.5-alpha): + - Fix compilation with OpenSSL 1.1 and less commonly-used CPU + architectures. Closes ticket 20588. + + +Changes in version 0.2.8.10 - 2016-12-02 + Tor 0.2.8.10 backports a fix for a bug that would sometimes make clients + unusable after they left standby mode. It also backports fixes for + a few portability issues and a small but problematic memory leak. + + o Major bugfixes (client reliability, backport from 0.2.9.5-alpha): + - When Tor leaves standby because of a new application request, open + circuits as needed to serve that request. Previously, we would + potentially wait a very long time. Fixes part of bug 19969; bugfix + on 0.2.8.1-alpha. + + o Major bugfixes (client performance, backport from 0.2.9.5-alpha): + - Clients now respond to new application stream requests immediately + when they arrive, rather than waiting up to one second before + starting to handle them. Fixes part of bug 19969; bugfix + on 0.2.8.1-alpha. + + o Minor bugfixes (portability, backport from 0.2.9.6-rc): + - Work around a bug in the OSX 10.12 SDK that would prevent us from + successfully targeting earlier versions of OSX. Resolves + ticket 20235. + + o Minor bugfixes (portability, backport from 0.2.9.5-alpha): + - Fix implicit conversion warnings under OpenSSL 1.1. Fixes bug + 20551; bugfix on 0.2.1.1-alpha. + + o Minor bugfixes (relay, backport from 0.2.9.5-alpha): + - Work around a memory leak in OpenSSL 1.1 when encoding public + keys. Fixes bug 20553; bugfix on 0.0.2pre8. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 3 2016 Maxmind GeoLite2 + Country database. + +Changes in version 0.2.9.6-rc - 2016-12-02 + Tor 0.2.9.6-rc fixes a few remaining bugs found in the previous alpha + version. We hope that it will be ready to become stable soon, and we + encourage everyone to test this release. If no showstopper bugs are + found here, the next 0.2.9 release will be stable. + + o Major bugfixes (relay, resolver, logging): + - For relays that don't know their own address, avoid attempting a + local hostname resolve for each descriptor we download. This + will cut down on the number of "Success: chose address 'x.x.x.x'" + log lines, and also avoid confusing clock jumps if the resolver + is slow. Fixes bugs 20423 and 20610; bugfix on 0.2.8.1-alpha. + + o Minor bugfixes (client, fascistfirewall): + - Avoid spurious warnings when ReachableAddresses or FascistFirewall + is set. Fixes bug 20306; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (hidden services): + - Stop ignoring the anonymity status of saved keys for hidden + services and single onion services when first starting tor. + Instead, refuse to start tor if any hidden service key has been + used in a different hidden service anonymity mode. Fixes bug + 20638; bugfix on 17178 in 0.2.9.3-alpha; reported by ahf. + + o Minor bugfixes (portability): + - Work around a bug in the OSX 10.12 SDK that would prevent us from + successfully targeting earlier versions of OSX. Resolves + ticket 20235. + - Run correctly when built on Windows build environments that + require _vcsprintf(). Fixes bug 20560; bugfix on 0.2.2.11-alpha. + + o Minor bugfixes (single onion services, Tor2web): + - Stop complaining about long-term one-hop circuits deliberately + created by single onion services and Tor2web. These log messages + are intended to diagnose issue 8387, which relates to circuits + hanging around forever for no reason. Fixes bug 20613; bugfix on + 0.2.9.1-alpha. Reported by "pastly". + + o Minor bugfixes (unit tests): + - Stop spurious failures in the local interface address discovery + unit tests. Fixes bug 20634; bugfix on 0.2.8.1-alpha; patch by + Neel Chauhan. + + o Documentation: + - Correct the minimum bandwidth value in torrc.sample, and queue a + corresponding change for torrc.minimal. Closes ticket 20085. + + Changes in version 0.2.9.5-alpha - 2016-11-08 Tor 0.2.9.5-alpha fixes numerous bugs discovered in the previous alpha version. We believe one or two probably remain, and we encourage @@ -319,8 +420,9 @@ Changes in version 0.2.9.3-alpha - 2016-09-23 OpenSSL 0.9.7 or later since 2009. Closes ticket 19998. o Minor feature (fallback directories): - - Remove broken entries from the hard-coded fallback directory list. - Closes ticket 20190; patch by teor. + - Remove 8 fallbacks that are no longer suitable, leaving 81 of the + 100 fallbacks originally introduced in Tor 0.2.8.2-alpha in March + 2016. Closes ticket 20190; patch by teor. o Minor features (geoip, also in 0.2.8.8): - Update geoip and geoip6 to the September 6 2016 Maxmind GeoLite2 @@ -498,8 +600,9 @@ Changes in version 0.2.8.8 - 2016-09-23 this one. o Minor feature (fallback directories): - - Remove broken fallbacks from the hard-coded fallback directory - list. Closes ticket 20190; patch by teor. + - Remove 8 fallbacks that are no longer suitable, leaving 81 of the + 100 fallbacks originally introduced in Tor 0.2.8.2-alpha in March + 2016. Closes ticket 20190; patch by teor. o Minor features (geoip): - Update geoip and geoip6 to the September 6 2016 Maxmind GeoLite2 @@ -953,8 +1056,9 @@ Changes in version 0.2.8.6 - 2016-08-02 is signed. Fixes bug 19682; bugfix on 0.2.8.1-alpha. o Minor bugfixes (fallback directories): - - Remove a fallback that was on the hardcoded list, then opted-out. - Fixes bug 19782; update to fallback list from 0.2.8.2-alpha. + - Remove 1 fallback that was on the hardcoded list, then opted-out, + leaving 89 of the 100 fallbacks originally introduced in Tor + 0.2.8.2-alpha in March 2016. Closes ticket 19782; patch by teor. o Minor bugfixes (Linux seccomp2 sandbox): - Allow more syscalls when running with "Sandbox 1" enabled: @@ -1026,8 +1130,9 @@ Changes in version 0.2.8.5-rc - 2016-07-07 - Update fallback whitelist and blacklist based on relay operator emails. Blacklist unsuitable (non-working, over-volatile) fallbacks. Resolves ticket 19071. Patch by teor. - - Update hard-coded fallback list to remove unsuitable fallbacks. - Resolves ticket 19071. Patch by teor. + - Remove 10 unsuitable fallbacks, leaving 90 of the 100 fallbacks + originally introduced in Tor 0.2.8.2-alpha in March 2016. Closes + ticket 19071; patch by teor. Changes in version 0.2.8.4-rc - 2016-06-15 @@ -1137,10 +1242,12 @@ Changes in version 0.2.8.3-alpha - 2016-05-26 - Give each fallback the same weight for client selection; restrict fallbacks to one per operator; report fallback directory detail changes when rebuilding list; add new fallback directory mirrors - to the whitelist; update fallback directories based on the latest - OnionOO data; and any other minor simplifications and fixes. - Closes tasks 17158, 17905, 18749, bug 18689, and fixes part of bug - 18812 on 0.2.8.1-alpha; patch by "teor". + to the whitelist; and many other minor simplifications and fixes. + Closes tasks 17905, 18749, bug 18689, and fixes part of bug 18812 on + 0.2.8.1-alpha; patch by "teor". + - Replace the 21 fallbacks generated in January 2016 and included in + Tor 0.2.8.1-alpha, with a list of 100 fallbacks generated in March + 2016. Closes task 17158; patch by "teor". o Minor features (geoip): - Update geoip and geoip6 to the May 4 2016 Maxmind GeoLite2 @@ -1584,10 +1691,11 @@ Changes in version 0.2.8.1-alpha - 2016-02-04 should reduces failures due to fallback churn. Implements ticket 4483. Patch by "teor". Implements IPv4 portions of proposal 210 by "mikeperry" and "teor". - - Include a trial list of default fallback directories, based on an - opt-in survey of suitable relays. Doing this should make clients - bootstrap more quickly and reliably, and reduce the load on the - directory authorities. Closes ticket 15775. Patch by "teor". + - Include a trial list of 21 default fallback directories, generated + in January 2016, based on an opt-in survey of suitable relays. + Doing this should make clients bootstrap more quickly and reliably, + and reduce the load on the directory authorities. Closes ticket + 15775. Patch by "teor". Candidates identified using an OnionOO script by "weasel", "teor", "gsathya", and "karsten". - Previously only relays that explicitly opened a directory port diff --git a/ReleaseNotes b/ReleaseNotes index af61a4d739..97db5af763 100644 --- a/ReleaseNotes +++ b/ReleaseNotes @@ -2,6 +2,61 @@ This document summarizes new features and bugfixes in each stable release of Tor. If you want to see more detailed descriptions of the changes in each development snapshot, see the ChangeLog file. + +Changes in version 0.2.8.11 - 2016-12-08 + Tor 0.2.8.11 backports fixes for additional portability issues that + could prevent Tor from building correctly on OSX Sierra, or with + OpenSSL 1.1. Affected users should upgrade; others can safely stay + with 0.2.8.10. + + o Minor bugfixes (portability): + - Avoid compilation errors when building on OSX Sierra. Sierra began + to support the getentropy() and clock_gettime() APIs, but created + a few problems in doing so. Tor 0.2.9 has a more thorough set of + workarounds; in 0.2.8, we are just using the /dev/urandom and mach + monotonic time interfaces. Fixes bug 20865. Bugfix + on 0.2.8.1-alpha. + + o Minor bugfixes (portability, backport from 0.2.9.5-alpha): + - Fix compilation with OpenSSL 1.1 and less commonly-used CPU + architectures. Closes ticket 20588. + + +Changes in version 0.2.8.10 - 2016-12-02 + Tor 0.2.8.10 backports a fix for a bug that would sometimes make clients + unusable after they left standby mode. It also backports fixes for + a few portability issues and a small but problematic memory leak. + + o Major bugfixes (client reliability, backport from 0.2.9.5-alpha): + - When Tor leaves standby because of a new application request, open + circuits as needed to serve that request. Previously, we would + potentially wait a very long time. Fixes part of bug 19969; bugfix + on 0.2.8.1-alpha. + + o Major bugfixes (client performance, backport from 0.2.9.5-alpha): + - Clients now respond to new application stream requests immediately + when they arrive, rather than waiting up to one second before + starting to handle them. Fixes part of bug 19969; bugfix + on 0.2.8.1-alpha. + + o Minor bugfixes (portability, backport from 0.2.9.6-rc): + - Work around a bug in the OSX 10.12 SDK that would prevent us from + successfully targeting earlier versions of OSX. Resolves + ticket 20235. + + o Minor bugfixes (portability, backport from 0.2.9.5-alpha): + - Fix implicit conversion warnings under OpenSSL 1.1. Fixes bug + 20551; bugfix on 0.2.1.1-alpha. + + o Minor bugfixes (relay, backport from 0.2.9.5-alpha): + - Work around a memory leak in OpenSSL 1.1 when encoding public + keys. Fixes bug 20553; bugfix on 0.0.2pre8. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 3 2016 Maxmind GeoLite2 + Country database. + + Changes in version 0.2.8.9 - 2016-10-17 Tor 0.2.8.9 backports a fix for a security hole in previous versions of Tor that would allow a remote attacker to crash a Tor client, diff --git a/changes/17070 b/changes/17070 new file mode 100644 index 0000000000..ffe616f38d --- /dev/null +++ b/changes/17070 @@ -0,0 +1,4 @@ + o Documentation (SOCKS connections): + - Clarify that when `ClientRejectInternalAddresses` is enabled (which is the + default), multicast DNS hostnames for machines on the local network (of + the form *.local) are also rejected. Closes ticket 17070. diff --git a/changes/19974 b/changes/19974 new file mode 100644 index 0000000000..5496143ddf --- /dev/null +++ b/changes/19974 @@ -0,0 +1,5 @@ + o Minor bugfixes (unit tests): + - Fix tolerances in unit tests for monotonic time comparisons between + nanoseconds and microseconds. Previously, we accepted a 10 us + difference only, which is not realistic on every platform's + clock_gettime(). Fixes bug 19974; bugfix on 0.2.9.1-alpha. diff --git a/changes/20492 b/changes/20492 new file mode 100644 index 0000000000..fdcd4d0b4b --- /dev/null +++ b/changes/20492 @@ -0,0 +1,4 @@ + o Minor bugfix (build): + - The current Git revision when building from a local repository is now + detected correctly when using git worktrees. Fixes bug 20492; bugfix on + 0.2.3.9-alpha. diff --git a/changes/20853 b/changes/20853 new file mode 100644 index 0000000000..84d95c41e0 --- /dev/null +++ b/changes/20853 @@ -0,0 +1,4 @@ + o Minor bugfix (hidden services): + - Change ephemeral service checks in `rendservice.c` to use the new + `rend_service_is_ephemeral` helper function. Fixes bug 20853; bugfix on + bug 20526; not in any released version of Tor. diff --git a/changes/bug19960 b/changes/bug19960 new file mode 100644 index 0000000000..5d655859a6 --- /dev/null +++ b/changes/bug19960 @@ -0,0 +1,4 @@ + o Minor bugfixes (netbsd, unit tests): + - Stop expecting NetBSD unit tests to report success for ipfw; + on NetBSD, it's only pf that's supported. + Part of a fix for bug 19960; bugfix on 0.2.9.5-alpha. diff --git a/changes/bug20085 b/changes/bug20085 deleted file mode 100644 index fd10e7eeeb..0000000000 --- a/changes/bug20085 +++ /dev/null @@ -1,4 +0,0 @@ - o Documentation: - - Correct the minimum bandwidth value in torrc.sample, and queue a - corresponding change for torrc.minimal. Closes ticket 20085. - diff --git a/changes/bug20306_029 b/changes/bug20306_029 deleted file mode 100644 index ada2676b2b..0000000000 --- a/changes/bug20306_029 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (fascistfirewall): - - Avoid spurious warnings when ReachableAddresses or FascistFirewall - is set. Fixes bug 20306; bugfix on 0.2.8.2-alpha. - diff --git a/changes/bug20530 b/changes/bug20530 new file mode 100644 index 0000000000..c21d5fbd34 --- /dev/null +++ b/changes/bug20530 @@ -0,0 +1,4 @@ + o Minor Fixes (Windows): + - Check for getpagesize before using it to mmap files. This fixes + compilation in some MinGW environments. Fixes bug 20530; bugfix on + commit bf72878 in tor-0.1.2.1-alpha, reported by "ice". diff --git a/changes/bug20559 b/changes/bug20559 new file mode 100644 index 0000000000..f117162dde --- /dev/null +++ b/changes/bug20559 @@ -0,0 +1,4 @@ + o Minor bugfixes (hidden services): + - Stop ignoring misconfigured hidden services. Instead, refuse to start + tor until the misconfigurations have been corrected. + Fixes bug 20559; bugfix on multiple commits in 0.2.7.1-alpha and earlier. diff --git a/changes/bug20560 b/changes/bug20560 deleted file mode 100644 index 43d605b296..0000000000 --- a/changes/bug20560 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (portability): - - Run correctly when built on Windows build environments that require - _vcsprintf(). Fixes bug 20560; bugfix on 0.2.2.11-alpha. - diff --git a/changes/bug20597 b/changes/bug20597 deleted file mode 100644 index f199b63933..0000000000 --- a/changes/bug20597 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (test networks, exponential backoff): - - When using exponential backoff in test networks, use a lower exponent, - so the delays do not vary as much. This helps test networks bootstrap - consistently. Fixes bug 20597; bugfix on 20499; not in any released - version of tor. diff --git a/changes/bug20613 b/changes/bug20613 deleted file mode 100644 index 19bb61f4e0..0000000000 --- a/changes/bug20613 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (single onion services, Tor2web): - - Stop logging long-term one-hop circuits deliberately created by single - onion services and Tor2web. These log messages are intended to diagnose - issue 8387, which relates to circuits hanging around forever for no - reason. - Fixes bug 20613; bugfix on 0.2.9.1-alpha. Reported by "pastly". diff --git a/changes/bug20646 b/changes/bug20646 new file mode 100644 index 0000000000..42e319ffcb --- /dev/null +++ b/changes/bug20646 @@ -0,0 +1,5 @@ + o Minor bugfix (util) + - When finishing writing a file to disk, if we were about to replace the + file with the temporary file created before and we fail to replace it, + remove the temporary file so it doesn't stay on disk. Closes #20646; + bugfix on tor-0.2.0.7-alpha; Patch by fk. diff --git a/changes/bug20667 b/changes/bug20667 new file mode 100644 index 0000000000..93b293a4e6 --- /dev/null +++ b/changes/bug20667 @@ -0,0 +1,5 @@ + o Minor bugfixes (directory downloads): + - Download all consensus flavors, descriptors, and authority certificates + when FetchUselessDescriptors is set, regardless of whether tor is a + directory cache or not. + Fixes bug 20667; bugfix on all recent tor versions. diff --git a/changes/bug20710_025 b/changes/bug20710_025 new file mode 100644 index 0000000000..12bd07536c --- /dev/null +++ b/changes/bug20710_025 @@ -0,0 +1,4 @@ + o Minor bugfixes (memory leak, use-after-free, linux seccomp2 sandbox): + - Fix a memory leak and use-after-free error when removing entries + from the sandbox's getaddrinfo() cache. Fixes bug 20710; bugfix on + 0.2.5.5-alpha. Patch from "cypherpunks". diff --git a/changes/bug20716 b/changes/bug20716 new file mode 100644 index 0000000000..37fd6feecf --- /dev/null +++ b/changes/bug20716 @@ -0,0 +1,3 @@ + o Minor bugfixes (client, memory leak): + - Fix a small memory leak when receiving AF_UNIX connections on + a SocksPort. Fixes bug 20716; bugfix on 0.2.6.3-alpha. diff --git a/changes/bug20810 b/changes/bug20810 new file mode 100644 index 0000000000..5420a73175 --- /dev/null +++ b/changes/bug20810 @@ -0,0 +1,4 @@ + o Minor bugfixes (relay) + - When computing old Tor protocol line version in protover, we were + looking at 0.2.7.5 twice instead of a specific case for 0.2.9.1-alpha. + Bugfix on tor-0.2.9.4-alpha. diff --git a/changes/bug20839 b/changes/bug20839 new file mode 100644 index 0000000000..c290097d80 --- /dev/null +++ b/changes/bug20839 @@ -0,0 +1,5 @@ + o Minor bugfixes (descriptors): + - Correctly recognise downloaded full descriptors as valid, even when + using microdescriptors as circuits. This affects clients with + FetchUselessDescriptors set, and may affect directory authorities. + Fixes bug 20839; bugfix on commit 6083276 in 0.2.3.2-alpha. diff --git a/changes/bug20860 b/changes/bug20860 new file mode 100644 index 0000000000..81b0dd8fc0 --- /dev/null +++ b/changes/bug20860 @@ -0,0 +1,4 @@ + o Minor bugfixes (hidden services): + - Stop ignoring duplicate hidden services when validating: this could + lead to a crash when those services were created. + Fixes bug 20860; bugfix on 20559; not in any released version of tor. diff --git a/changes/bug20864 b/changes/bug20864 new file mode 100644 index 0000000000..7b8c70fad6 --- /dev/null +++ b/changes/bug20864 @@ -0,0 +1,4 @@ + o Minor bugfixes (unit tests, hidden services): + - Remove a double-free in the single onion service unit test. Stop + ignoring a return value. Make future changes less error-prone. + Fixes bug 20864; bugfix on 0.2.9.6-rc. diff --git a/changes/bug20875 b/changes/bug20875 new file mode 100644 index 0000000000..6bba2cbc12 --- /dev/null +++ b/changes/bug20875 @@ -0,0 +1,4 @@ + o Minor bugfixes (download scheduling) + - Resolve a "bug" warning when considering a download schedule whose + delay had approached INT_MAX. Fixes 20875; bugfix on 0.2.9.5-alpha. + diff --git a/changes/ticket18873 b/changes/ticket18873 new file mode 100644 index 0000000000..f1d9bef6d2 --- /dev/null +++ b/changes/ticket18873 @@ -0,0 +1,7 @@ + o Code simplification and refactoring: + - Extracted dummy_origin_circuit_new so it can be used by other test + functions. + - Refactor circuit_predict_and_launch_new for readability and testability. + - Added unit tests for extracted functions. + - Extracted magic numbers in circuituse.c into defined variables. + - Refactor circuit_is_available_for_use to remove unnecessary check diff --git a/changes/ticket20717 b/changes/ticket20717 new file mode 100644 index 0000000000..c896f8ad98 --- /dev/null +++ b/changes/ticket20717 @@ -0,0 +1,4 @@ + o Code simplification and refactoring: + - Refactors the hashing API to return negative values for errors as is done + as a standard throughout the codebase. + - Refactors calling functions to expect negative values for errors. diff --git a/configure.ac b/configure.ac index 27d7577a5a..2f556271f9 100644 --- a/configure.ac +++ b/configure.ac @@ -379,14 +379,12 @@ AC_CHECK_FUNCS( accept4 \ backtrace \ backtrace_symbols_fd \ - clock_gettime \ eventfd \ explicit_bzero \ timingsafe_memcmp \ flock \ ftime \ getaddrinfo \ - getentropy \ getifaddrs \ getpass \ getrlimit \ @@ -424,6 +422,34 @@ AC_CHECK_FUNCS( _vscprintf ) +# Apple messed up when they added two functions functions in Sierra: they +# forgot to decorate them with appropriate AVAILABLE_MAC_OS_VERSION +# checks. So we should only probe for those functions if we are sure that we +# are not targetting OSX 10.11 or earlier. +AC_MSG_CHECKING([for a pre-Sierra OSX build target]) +AC_TRY_COMPILE([ +#ifdef __APPLE__ +# include <AvailabilityMacros.h> +# ifndef MAC_OS_VERSION_10_12 +# define MAC_OS_VERSION_10_12 101200 +# endif +# if defined(MAC_OS_X_VERSION_MIN_REQUIRED) +# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_10_12 +# error "Running on Mac OSX 10.11 or earlier" +# endif +# endif +#endif +], [], + [on_macos_pre_10_12=no ; AC_MSG_RESULT([no])], + [on_macos_pre_10_12=yes; AC_MSG_RESULT([yes])]) + +if test "$on_macos_pre_10_12" = "no"; then + AC_CHECK_FUNCS( + clock_gettime \ + getentropy \ + ) +fi + if test "$bwin32" != "true"; then AC_CHECK_HEADERS(pthread.h) AC_CHECK_FUNCS(pthread_create) @@ -1410,6 +1436,14 @@ AC_CHECK_DECLS([mlockall], , , [ #include <sys/mman.h> #endif]) +# Some MinGW environments don't have getpagesize in unistd.h. We don't use +# AC_CHECK_FUNCS(getpagesize), because other environments rename getpagesize +# using macros +AC_CHECK_DECLS([getpagesize], , , [ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif]) + # Allow user to specify an alternate syslog facility AC_ARG_WITH(syslog-facility, AS_HELP_STRING(--with-syslog-facility=LOG, [syslog facility to use (default=LOG_DAEMON)]), diff --git a/doc/HACKING/WritingTests.md b/doc/HACKING/WritingTests.md index de80bbdef2..4dae41e922 100644 --- a/doc/HACKING/WritingTests.md +++ b/doc/HACKING/WritingTests.md @@ -48,7 +48,7 @@ isolation, you just run `./src/test/test-memwipe`. To run tests within the unit test programs, you can specify the name of the test. The string ".." can be used as a wildcard at the end of the test name. For example, to run all the cell format tests, enter -`./src/test/test cellfmt/..`. To run +`./src/test/test cellfmt/..`. Many tests that need to mess with global state run in forked subprocesses in order to keep from contaminating one another. But when debugging a failing test, diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 9f6f95830a..9b8a0f00bf 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -504,11 +504,13 @@ GENERAL OPTIONS (Default: 1) [[FetchUselessDescriptors]] **FetchUselessDescriptors** **0**|**1**:: - If set to 1, Tor will fetch every non-obsolete descriptor from the - authorities that it hears about. Otherwise, it will avoid fetching useless - descriptors, for example for routers that are not running. This option is - useful if you're using the contributed "exitlist" script to enumerate Tor - nodes that exit to certain addresses. (Default: 0) + If set to 1, Tor will fetch every consensus flavor, descriptor, and + certificate that it hears about. Otherwise, it will avoid fetching useless + descriptors: flavors that it is not using to build circuits, and authority + certificates it does not trust. This option is useful if you're using a + tor client with an external parser that uses a full consensus. + This option fetches all documents, **DirCache** fetches and serves + all documents. (Default: 0) [[HTTPProxy]] **HTTPProxy** __host__[:__port__]:: Tor will make all its directory requests through this host:port (or host:80 @@ -1419,7 +1421,8 @@ The following options are useful only for clients (that is, if If true, Tor does not try to fulfill requests to connect to an internal address (like 127.0.0.1 or 192.168.0.1) __unless a exit node is specifically requested__ (for example, via a .exit hostname, or a - controller request). (Default: 1) + controller request). If true, multicast DNS hostnames for machines on the + local network (of the form *.local) are also rejected. (Default: 1) [[DownloadExtraInfo]] **DownloadExtraInfo** **0**|**1**:: If true, Tor downloads and caches "extra-info" documents. These documents @@ -2441,8 +2444,9 @@ The following options are used to configure a hidden service. servers with different IP addresses. HiddenServiceSingleHopMode requires HiddenServiceNonAnonymousMode to be set - to 1. Since a Single Onion is non-anonymous, you can not to run an - anonymous SOCKSPort on the same tor instance as a Single Onion service. + to 1. Since a Single Onion service is non-anonymous, you can not configure + a SOCKSPort on a tor instance that is running in + **HiddenServiceSingleHopMode**. (Default: 0) [[HiddenServiceNonAnonymousMode]] **HiddenServiceNonAnonymousMode** **0**|**1**:: diff --git a/src/common/address.c b/src/common/address.c index 773e688554..fa6630ef92 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -2100,7 +2100,8 @@ get_interface_address,(int severity, uint32_t *addr)) } /** Return true if we can tell that <b>name</b> is a canonical name for the - * loopback address. */ + * loopback address. Return true also for *.local hostnames, which are + * multicast DNS names for hosts on the local network. */ int tor_addr_hostname_is_local(const char *name) { diff --git a/src/common/compat.c b/src/common/compat.c index 8d6a491c42..ebf05f59e1 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -204,7 +204,15 @@ tor_rename(const char *path_old, const char *path_new) sandbox_intern_string(path_new)); } -#if defined(HAVE_SYS_MMAN_H) || defined(RUNNING_DOXYGEN) +/* Some MinGW builds have sys/mman.h, but not the corresponding symbols. + * Other configs rename the symbols using macros (including getpagesize). + * So check for sys/mman.h and unistd.h, and a getpagesize declaration. */ +#if (defined(HAVE_SYS_MMAN_H) && defined(HAVE_UNISTD_H) && \ + defined(HAVE_DECL_GETPAGESIZE)) +#define COMPAT_HAS_MMAN_AND_PAGESIZE +#endif + +#if defined(COMPAT_HAS_MMAN_AND_PAGESIZE) || defined(RUNNING_DOXYGEN) /** Try to create a memory mapping for <b>filename</b> and return it. On * failure, return NULL. Sets errno properly, using ERANGE to mean * "empty file". */ diff --git a/src/common/crypto.c b/src/common/crypto.c index fff516cc8e..be42d36af6 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1506,7 +1506,7 @@ crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out) if (crypto_pk_get_digest(pk, digest)) { return -1; } - if (crypto_digest(hashed_digest, digest, DIGEST_LEN)) { + if (crypto_digest(hashed_digest, digest, DIGEST_LEN) < 0) { return -1; } base16_encode(fp_out, FINGERPRINT_LEN + 1, hashed_digest, DIGEST_LEN); @@ -1700,19 +1700,21 @@ crypto_cipher_decrypt_with_iv(const char *key, /** Compute the SHA1 digest of the <b>len</b> bytes on data stored in * <b>m</b>. Write the DIGEST_LEN byte result into <b>digest</b>. - * Return 0 on success, 1 on failure. + * Return 0 on success, -1 on failure. */ int crypto_digest(char *digest, const char *m, size_t len) { tor_assert(m); tor_assert(digest); - return (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL); + if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL) + return -1; + return 0; } /** Compute a 256-bit digest of <b>len</b> bytes in data stored in <b>m</b>, * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN256-byte result - * into <b>digest</b>. Return 0 on success, 1 on failure. */ + * into <b>digest</b>. Return 0 on success, -1 on failure. */ int crypto_digest256(char *digest, const char *m, size_t len, digest_algorithm_t algorithm) @@ -1720,16 +1722,22 @@ crypto_digest256(char *digest, const char *m, size_t len, tor_assert(m); tor_assert(digest); tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256); + + int ret = 0; if (algorithm == DIGEST_SHA256) - return (SHA256((const uint8_t*)m,len,(uint8_t*)digest) == NULL); + ret = (SHA256((const uint8_t*)m,len,(uint8_t*)digest) != NULL); else - return (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len) - == -1); + ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len) + > -1); + + if (!ret) + return -1; + return 0; } /** Compute a 512-bit digest of <b>len</b> bytes in data stored in <b>m</b>, * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN512-byte result - * into <b>digest</b>. Return 0 on success, 1 on failure. */ + * into <b>digest</b>. Return 0 on success, -1 on failure. */ int crypto_digest512(char *digest, const char *m, size_t len, digest_algorithm_t algorithm) @@ -1737,12 +1745,18 @@ crypto_digest512(char *digest, const char *m, size_t len, tor_assert(m); tor_assert(digest); tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512); + + int ret = 0; if (algorithm == DIGEST_SHA512) - return (SHA512((const unsigned char*)m,len,(unsigned char*)digest) - == NULL); + ret = (SHA512((const unsigned char*)m,len,(unsigned char*)digest) + != NULL); else - return (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len) - == -1); + ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len) + > -1); + + if (!ret) + return -1; + return 0; } /** Set the common_digests_t in <b>ds_out</b> to contain every digest on the @@ -2628,7 +2642,7 @@ crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len, for (cp = key_out, i=0; cp < key_out+key_out_len; ++i, cp += DIGEST_LEN) { tmp[key_in_len] = i; - if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1)) + if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1) < 0) goto exit; memcpy(cp, digest, MIN(DIGEST_LEN, key_out_len-(cp-key_out))); } diff --git a/src/common/sandbox.c b/src/common/sandbox.c index 24ba8a2997..ebc843e130 100644 --- a/src/common/sandbox.c +++ b/src/common/sandbox.c @@ -1579,13 +1579,14 @@ sandbox_add_addrinfo(const char *name) void sandbox_free_getaddrinfo_cache(void) { - cached_getaddrinfo_item_t **next, **item; + cached_getaddrinfo_item_t **next, **item, *this; for (item = HT_START(getaddrinfo_cache, &getaddrinfo_cache); item; item = next) { + this = *item; next = HT_NEXT_RMV(getaddrinfo_cache, &getaddrinfo_cache, item); - cached_getaddrinfo_item_free(*item); + cached_getaddrinfo_item_free(this); } HT_CLEAR(getaddrinfo_cache, &getaddrinfo_cache); diff --git a/src/common/timers.c b/src/common/timers.c index 41b2008ac4..e1ad47b15b 100644 --- a/src/common/timers.c +++ b/src/common/timers.c @@ -255,6 +255,20 @@ timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg) } /** + * Set *<b>cb_out</b> (if provided) to this timer's callback function, + * and *<b>arg_out</b> (if provided) to this timer's callback argument. + */ +void +timer_get_cb(const tor_timer_t *t, + timer_cb_fn_t *cb_out, void **arg_out) +{ + if (cb_out) + *cb_out = t->callback.cb; + if (arg_out) + *arg_out = t->callback.arg; +} + +/** * Schedule the timer t to fire at the current time plus a delay of * <b>delay</b> microseconds. All times are relative to monotime_get(). */ diff --git a/src/common/timers.h b/src/common/timers.h index 5f918f8e15..c5246a3335 100644 --- a/src/common/timers.h +++ b/src/common/timers.h @@ -13,6 +13,8 @@ typedef void (*timer_cb_fn_t)(tor_timer_t *, void *, const struct monotime_t *); tor_timer_t *timer_new(timer_cb_fn_t cb, void *arg); void timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg); +void timer_get_cb(const tor_timer_t *t, + timer_cb_fn_t *cb_out, void **arg_out); void timer_schedule(tor_timer_t *t, const struct timeval *delay); void timer_disable(tor_timer_t *t); void timer_free(tor_timer_t *t); diff --git a/src/common/util.c b/src/common/util.c index cb5f12821e..417aa89433 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2270,10 +2270,14 @@ check_private_dir,(const char *dirname, cpd_check_t check, * permissions on the directory will be checked again below.*/ fd = open(sandbox_intern_string(dirname), O_NOFOLLOW); - if (fd == -1) + if (fd == -1) { + log_warn(LD_FS, "Could not reopen recently created directory %s: %s", + dirname, + strerror(errno)); return -1; - else + } else { close(fd); + } } else if (!(check & CPD_CHECK)) { log_warn(LD_FS, "Directory %s does not exist.", dirname); @@ -2601,6 +2605,14 @@ finish_writing_to_file_impl(open_file_t *file_data, int abort_write) if (file_data->rename_on_close) { tor_assert(file_data->tempname && file_data->filename); + if (!abort_write) { + tor_assert(strcmp(file_data->filename, file_data->tempname)); + if (replace_file(file_data->tempname, file_data->filename)) { + log_warn(LD_FS, "Error replacing \"%s\": %s", file_data->filename, + strerror(errno)); + abort_write = r = -1; + } + } if (abort_write) { int res = unlink(file_data->tempname); if (res != 0) { @@ -2609,13 +2621,6 @@ finish_writing_to_file_impl(open_file_t *file_data, int abort_write) file_data->tempname, strerror(errno)); r = -1; } - } else { - tor_assert(strcmp(file_data->filename, file_data->tempname)); - if (replace_file(file_data->tempname, file_data->filename)) { - log_warn(LD_FS, "Error replacing \"%s\": %s", file_data->filename, - strerror(errno)); - r = -1; - } } } diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 96bd472dba..f60a8bfa89 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -1537,9 +1537,9 @@ circuit_get_unhandled_ports(time_t now) * If we're returning 0, set need_uptime and need_capacity to * indicate any requirements that the unhandled ports have. */ -int -circuit_all_predicted_ports_handled(time_t now, int *need_uptime, - int *need_capacity) +MOCK_IMPL(int, +circuit_all_predicted_ports_handled, (time_t now, int *need_uptime, + int *need_capacity)) { int i, enough; uint16_t *port; diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index f71c116eda..54d14bbc7f 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -40,8 +40,9 @@ int onionskin_answer(or_circuit_t *circ, const struct created_cell_t *created_cell, const char *keys, const uint8_t *rend_circ_nonce); -int circuit_all_predicted_ports_handled(time_t now, int *need_uptime, - int *need_capacity); +MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now, + int *need_uptime, + int *need_capacity)); int circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info); int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info); diff --git a/src/or/circuituse.c b/src/or/circuituse.c index eaecfd9dc0..04c5af92e8 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1022,8 +1022,117 @@ circuit_stream_is_being_handled(entry_connection_t *conn, /** Don't keep more than this many unused open circuits around. */ #define MAX_UNUSED_OPEN_CIRCUITS 14 -/** Figure out how many circuits we have open that are clean. Make - * sure it's enough for all the upcoming behaviors we predict we'll have. +/* Return true if a circuit is available for use, meaning that it is open, + * clean, usable for new multi-hop connections, and a general purpose origin + * circuit. + * Accept any kind of circuit, return false if the above conditions are not + * met. */ +STATIC int +circuit_is_available_for_use(const circuit_t *circ) +{ + const origin_circuit_t *origin_circ; + cpath_build_state_t *build_state; + + if (!CIRCUIT_IS_ORIGIN(circ)) + return 0; /* We first filter out only origin circuits before doing the + following checks. */ + if (circ->marked_for_close) + return 0; /* Don't mess with marked circs */ + if (circ->timestamp_dirty) + return 0; /* Only count clean circs */ + if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL) + return 0; /* We only pay attention to general purpose circuits. + General purpose circuits are always origin circuits. */ + + origin_circ = CONST_TO_ORIGIN_CIRCUIT(circ); + if (origin_circ->unusable_for_new_conns) + return 0; + + build_state = origin_circ->build_state; + if (build_state->onehop_tunnel) + return 0; + + return 1; +} + +/* Return true if we need any more exit circuits. + * needs_uptime and needs_capacity are set only if we need more exit circuits. + * Check if we know of a port that's been requested recently and no circuit + * is currently available that can handle it. */ +STATIC int +needs_exit_circuits(time_t now, int *needs_uptime, int *needs_capacity) +{ + return (!circuit_all_predicted_ports_handled(now, needs_uptime, + needs_capacity) && + router_have_consensus_path() == CONSENSUS_PATH_EXIT); +} + +/* Hidden services need at least this many internal circuits */ +#define SUFFICIENT_UPTIME_INTERNAL_HS_SERVERS 3 + +/* Return true if we need any more hidden service server circuits. + * HS servers only need an internal circuit. */ +STATIC int +needs_hs_server_circuits(int num_uptime_internal) +{ + return (num_rend_services() && + num_uptime_internal < SUFFICIENT_UPTIME_INTERNAL_HS_SERVERS && + router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN); +} + +/* We need at least this many internal circuits for hidden service clients */ +#define SUFFICIENT_INTERNAL_HS_CLIENTS 3 + +/* We need at least this much uptime for internal circuits for hidden service + * clients */ +#define SUFFICIENT_UPTIME_INTERNAL_HS_CLIENTS 2 + +/* Return true if we need any more hidden service client circuits. + * HS clients only need an internal circuit. */ +STATIC int +needs_hs_client_circuits(time_t now, int *needs_uptime, int *needs_capacity, + int num_internal, int num_uptime_internal) +{ + int used_internal_recently = rep_hist_get_predicted_internal(now, + needs_uptime, + needs_capacity); + int requires_uptime = num_uptime_internal < + SUFFICIENT_UPTIME_INTERNAL_HS_CLIENTS && + needs_uptime; + + return (used_internal_recently && + (requires_uptime || num_internal < SUFFICIENT_INTERNAL_HS_CLIENTS) && + router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN); +} + +/* The minimum number of open slots we should keep in order to preemptively + * build circuits. */ +#define CBT_MIN_REMAINING_PREEMPTIVE_CIRCUITS 2 + +/* Check to see if we need more circuits to have a good build timeout. However, + * leave a couple slots open so that we can still build circuits preemptively + * as needed. */ +#define CBT_MAX_UNUSED_OPEN_CIRCUITS (MAX_UNUSED_OPEN_CIRCUITS - \ + CBT_MIN_REMAINING_PREEMPTIVE_CIRCUITS) + +/* Return true if we need more circuits for a good build timeout. + * XXXX make the assumption that build timeout streams should be + * created whenever we can build internal circuits. */ +STATIC int +needs_circuits_for_build(int num) +{ + if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) { + if (num < CBT_MAX_UNUSED_OPEN_CIRCUITS && + !circuit_build_times_disabled() && + circuit_build_times_needs_circuits_now(get_circuit_build_times())) { + return 1; + } + } + return 0; +} + +/** Determine how many circuits we have open that are clean, + * Make sure it's enough for all the upcoming behaviors we predict we'll have. * But put an upper bound on the total number of circuits. */ static void @@ -1035,25 +1144,14 @@ circuit_predict_and_launch_new(void) time_t now = time(NULL); int flags = 0; - /* First, count how many of each type of circuit we have already. */ + /* Count how many of each type of circuit we currently have. */ SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) { - cpath_build_state_t *build_state; - origin_circuit_t *origin_circ; - if (!CIRCUIT_IS_ORIGIN(circ)) - continue; - if (circ->marked_for_close) - continue; /* don't mess with marked circs */ - if (circ->timestamp_dirty) - continue; /* only count clean circs */ - if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL) - continue; /* only pay attention to general-purpose circs */ - origin_circ = TO_ORIGIN_CIRCUIT(circ); - if (origin_circ->unusable_for_new_conns) - continue; - build_state = origin_circ->build_state; - if (build_state->onehop_tunnel) + if (!circuit_is_available_for_use(circ)) continue; + num++; + + cpath_build_state_t *build_state = TO_ORIGIN_CIRCUIT(circ)->build_state; if (build_state->is_internal) num_internal++; if (build_state->need_uptime && build_state->is_internal) @@ -1063,19 +1161,14 @@ circuit_predict_and_launch_new(void) /* If that's enough, then stop now. */ if (num >= MAX_UNUSED_OPEN_CIRCUITS) - return; /* we already have many, making more probably will hurt */ - - /* Second, see if we need any more exit circuits. */ - /* check if we know of a port that's been requested recently - * and no circuit is currently available that can handle it. - * Exits (obviously) require an exit circuit. */ - if (!circuit_all_predicted_ports_handled(now, &port_needs_uptime, - &port_needs_capacity) - && router_have_consensus_path() == CONSENSUS_PATH_EXIT) { + return; + + if (needs_exit_circuits(now, &port_needs_uptime, &port_needs_capacity)) { if (port_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME; if (port_needs_capacity) flags |= CIRCLAUNCH_NEED_CAPACITY; + log_info(LD_CIRC, "Have %d clean circs (%d internal), need another exit circ.", num, num_internal); @@ -1083,12 +1176,10 @@ circuit_predict_and_launch_new(void) return; } - /* Third, see if we need any more hidden service (server) circuits. - * HS servers only need an internal circuit. */ - if (num_rend_services() && num_uptime_internal < 3 - && router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) { + if (needs_hs_server_circuits(num_uptime_internal)) { flags = (CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL); + log_info(LD_CIRC, "Have %d clean circs (%d internal), need another internal " "circ for my hidden service.", @@ -1097,18 +1188,16 @@ circuit_predict_and_launch_new(void) return; } - /* Fourth, see if we need any more hidden service (client) circuits. - * HS clients only need an internal circuit. */ - if (rep_hist_get_predicted_internal(now, &hidserv_needs_uptime, - &hidserv_needs_capacity) && - ((num_uptime_internal<2 && hidserv_needs_uptime) || - num_internal<3) - && router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) { + if (needs_hs_client_circuits(now, &hidserv_needs_uptime, + &hidserv_needs_capacity, + num_internal, num_uptime_internal)) + { if (hidserv_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME; if (hidserv_needs_capacity) flags |= CIRCLAUNCH_NEED_CAPACITY; flags |= CIRCLAUNCH_IS_INTERNAL; + log_info(LD_CIRC, "Have %d clean circs (%d uptime-internal, %d internal), need" " another hidden service circ.", @@ -1117,26 +1206,17 @@ circuit_predict_and_launch_new(void) return; } - /* Finally, check to see if we still need more circuits to learn - * a good build timeout. But if we're close to our max number we - * want, don't do another -- we want to leave a few slots open so - * we can still build circuits preemptively as needed. - * XXXX make the assumption that build timeout streams should be - * created whenever we can build internal circuits. */ - if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) { - if (num < MAX_UNUSED_OPEN_CIRCUITS-2 && - ! circuit_build_times_disabled() && - circuit_build_times_needs_circuits_now(get_circuit_build_times())) { - flags = CIRCLAUNCH_NEED_CAPACITY; - /* if there are no exits in the consensus, make timeout - * circuits internal */ - if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL) - flags |= CIRCLAUNCH_IS_INTERNAL; + if (needs_circuits_for_build(num)) { + flags = CIRCLAUNCH_NEED_CAPACITY; + /* if there are no exits in the consensus, make timeout + * circuits internal */ + if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL) + flags |= CIRCLAUNCH_IS_INTERNAL; + log_info(LD_CIRC, "Have %d clean circs need another buildtime test circ.", num); circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags); return; - } } } diff --git a/src/or/circuituse.h b/src/or/circuituse.h index 5973978c45..d484be1986 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -59,5 +59,25 @@ int hostname_in_track_host_exits(const or_options_t *options, const char *address); void mark_circuit_unusable_for_new_conns(origin_circuit_t *circ); +#ifdef TOR_UNIT_TESTS +/* Used only by circuituse.c and test_circuituse.c */ + +STATIC int circuit_is_available_for_use(const circuit_t *circ); + +STATIC int needs_exit_circuits(time_t now, + int *port_needs_uptime, + int *port_needs_capacity); +STATIC int needs_hs_server_circuits(int num_uptime_internal); + +STATIC int needs_hs_client_circuits(time_t now, + int *needs_uptime, + int *needs_capacity, + int num_internal, + int num_uptime_internal); + +STATIC int needs_circuits_for_build(int num); + +#endif + #endif diff --git a/src/or/config.c b/src/or/config.c index d100af812c..6948bdbdb4 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1780,25 +1780,6 @@ options_act(const or_options_t *old_options) monitor_owning_controller_process(options->OwningControllerProcess); - /* We must create new keys after we poison the directories, because our - * poisoning code checks for existing keys, and refuses to modify their - * directories. */ - - /* If we use non-anonymous single onion services, make sure we poison any - new hidden service directories, so that we never accidentally launch the - non-anonymous hidden services thinking they are anonymous. */ - if (running_tor && rend_service_non_anonymous_mode_enabled(options)) { - if (options->RendConfigLines && !num_rend_services()) { - log_warn(LD_BUG,"Error: hidden services configured, but not parsed."); - return -1; - } - if (rend_service_poison_new_single_onion_dirs(NULL) < 0) { - log_warn(LD_GENERAL,"Failed to mark new hidden services as non-anonymous" - "."); - return -1; - } - } - /* reload keys as needed for rendezvous services. */ if (rend_service_load_all_keys(NULL)<0) { log_warn(LD_GENERAL,"Error loading rendezvous service keys"); @@ -2942,21 +2923,6 @@ options_validate_single_onion(or_options_t *options, char **msg) options->UseEntryGuards = 0; } - /* Check if existing hidden service keys were created in a different - * single onion service mode, and refuse to launch if they - * have. We'll poison new keys in options_act() just before we create them. - */ - if (rend_service_list_verify_single_onion_poison(NULL, options) < 0) { - log_warn(LD_GENERAL, "We are configured with " - "HiddenServiceNonAnonymousMode %d, but one or more hidden " - "service keys were created in %s mode. This is not allowed.", - rend_service_non_anonymous_mode_enabled(options) ? 1 : 0, - rend_service_non_anonymous_mode_enabled(options) ? - "an anonymous" : "a non-anonymous" - ); - return -1; - } - return 0; } @@ -3089,7 +3055,7 @@ options_validate(or_options_t *old_options, or_options_t *options, } else if (!strcasecmp(options->TransProxyType, "ipfw")) { #ifndef KERNEL_MAY_SUPPORT_IPFW /* Earlier versions of OS X have ipfw */ - REJECT("ipfw is a FreeBSD-specific" + REJECT("ipfw is a FreeBSD-specific " "and OS X/Darwin-specific feature."); #else options->TransProxyType_parsed = TPT_IPFW; diff --git a/src/or/connection.c b/src/or/connection.c index f80602155b..ac3408a72e 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1595,16 +1595,19 @@ connection_handle_listener_read(connection_t *conn, int new_type) /* remember the remote address */ tor_addr_copy(&newconn->addr, &addr); - newconn->port = port; - newconn->address = tor_addr_to_str_dup(&addr); + if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { + newconn->port = 0; + newconn->address = tor_strdup(conn->address); + } else { + newconn->port = port; + newconn->address = tor_addr_to_str_dup(&addr); + } if (new_type == CONN_TYPE_AP && conn->socket_family != AF_UNIX) { log_info(LD_NET, "New SOCKS connection opened from %s.", fmt_and_decorate_addr(&addr)); } if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { - newconn->port = 0; - newconn->address = tor_strdup(conn->address); log_info(LD_NET, "New SOCKS AF_UNIX connection opened"); } if (new_type == CONN_TYPE_CONTROL) { diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 875c911f01..3874d52c23 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1627,11 +1627,9 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, } tor_assert(!automap); rep_hist_note_used_resolve(now); /* help predict this next time */ - } + } else if (socks->command == SOCKS_COMMAND_CONNECT) { + /* Now see if this is a connect request that we can reject immediately */ - /* Now see if this is a connect request that we can reject immediately */ - if (socks->command == SOCKS_COMMAND_CONNECT) { - /* Special handling for attempts to connect */ tor_assert(!automap); /* Don't allow connections to port 0. */ if (socks->port == 0) { @@ -1771,7 +1769,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, rep_hist_note_used_resolve(now); /* help predict this next time */ /* no extra processing needed */ } else { - /* We should only be doing CONNECT or RESOLVE! */ + /* We should only be doing CONNECT, RESOLVE, or RESOLVE_PTR! */ tor_fragile_assert(); } diff --git a/src/or/control.c b/src/or/control.c index 96cc41bc4b..a22113174a 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1705,7 +1705,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, *answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS"); } else if (!strcmp(question, "address")) { uint32_t addr; - if (router_pick_published_address(get_options(), &addr) < 0) { + if (router_pick_published_address(get_options(), &addr, 0) < 0) { *errmsg = "Address unknown"; return -1; } @@ -2029,7 +2029,7 @@ getinfo_helper_dir(control_connection_t *control_conn, } else if (!strcmpstart(question, "dir/status/")) { *answer = tor_strdup(""); } else if (!strcmp(question, "dir/status-vote/current/consensus")) { /* v3 */ - if (directory_caches_dir_info(get_options())) { + if (we_want_to_fetch_flavor(get_options(), FLAV_NS)) { const cached_dir_t *consensus = dirserv_get_consensus("ns"); if (consensus) *answer = tor_strdup(consensus->dir); diff --git a/src/or/directory.c b/src/or/directory.c index 65ddd7d583..f0affbd395 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3988,10 +3988,12 @@ STATIC int next_random_exponential_delay(int delay, int max_delay) { /* Check preconditions */ + if (BUG(max_delay < 0)) + max_delay = 0; if (BUG(delay > max_delay)) delay = max_delay; - if (BUG(delay == INT_MAX)) - delay -= 1; /* prevent overflow */ + if (delay == INT_MAX) + return INT_MAX; /* prevent overflow */ if (BUG(delay < 0)) delay = 0; diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 0b896a2845..399d5ea955 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1033,7 +1033,8 @@ directory_fetches_from_authorities(const or_options_t *options) return 1; if (options->BridgeRelay == 1) return 0; - if (server_mode(options) && router_pick_published_address(options, &addr)<0) + if (server_mode(options) && + router_pick_published_address(options, &addr, 1) < 0) return 1; /* we don't know our IP address; ask an authority. */ refuseunknown = ! router_my_exit_policy_is_reject_star() && should_refuse_unknown_exits(options); @@ -1068,8 +1069,10 @@ directory_fetches_dir_info_later(const or_options_t *options) return options->UseBridges != 0; } -/** Return true iff we want to fetch and keep certificates for authorities +/** Return true iff we want to serve certificates for authorities * that we don't acknowledge as authorities ourself. + * Use we_want_to_fetch_unknown_auth_certs to check if we want to fetch + * and keep these certificates. */ int directory_caches_unknown_auth_certs(const or_options_t *options) @@ -1077,11 +1080,14 @@ directory_caches_unknown_auth_certs(const or_options_t *options) return dir_server_mode(options) || options->BridgeRelay; } -/** Return 1 if we want to keep descriptors, networkstatuses, etc around. +/** Return 1 if we want to fetch and serve descriptors, networkstatuses, etc * Else return 0. * Check options->DirPort_set and directory_permits_begindir_requests() * to see if we are willing to serve these directory documents to others via * the DirPort and begindir-over-ORPort, respectively. + * + * To check if we should fetch documents, use we_want_to_fetch_flavor and + * we_want_to_fetch_unknown_auth_certs instead of this function. */ int directory_caches_dir_info(const or_options_t *options) diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c index 1517ccb12e..37aa1d745e 100644 --- a/src/or/hs_descriptor.c +++ b/src/or/hs_descriptor.c @@ -15,6 +15,7 @@ #include "ed25519_cert.h" /* Trunnel interface. */ #include "parsecommon.h" #include "rendcache.h" +#include "torcert.h" /* tor_cert_encode_ed22519() */ /* Constant string value used for the descriptor format. */ #define str_hs_desc "hs-descriptor" @@ -135,45 +136,6 @@ desc_encrypted_data_free_contents(hs_desc_encrypted_data_t *desc) /* === ENCODING === */ -/* Encode the ed25519 certificate <b>cert</b> and put the newly allocated - * string in <b>cert_str_out</b>. Return 0 on success else a negative value. */ -STATIC int -encode_cert(const tor_cert_t *cert, char **cert_str_out) -{ - int ret = -1; - char *ed_cert_b64 = NULL; - size_t ed_cert_b64_len; - - tor_assert(cert); - tor_assert(cert_str_out); - - /* Get the encoded size and add the NUL byte. */ - ed_cert_b64_len = base64_encode_size(cert->encoded_len, - BASE64_ENCODE_MULTILINE) + 1; - ed_cert_b64 = tor_malloc_zero(ed_cert_b64_len); - - /* Base64 encode the encoded certificate. */ - if (base64_encode(ed_cert_b64, ed_cert_b64_len, - (const char *) cert->encoded, cert->encoded_len, - BASE64_ENCODE_MULTILINE) < 0) { - log_err(LD_BUG, "Couldn't base64-encode descriptor signing key cert!"); - goto err; - } - - /* Put everything together in a NUL terminated string. */ - tor_asprintf(cert_str_out, - "-----BEGIN ED25519 CERT-----\n" - "%s" - "-----END ED25519 CERT-----", - ed_cert_b64); - /* Success! */ - ret = 0; - - err: - tor_free(ed_cert_b64); - return ret; -} - /* Encode the given link specifier objects into a newly allocated string. * This can't fail so caller can always assume a valid string being * returned. */ @@ -327,7 +289,7 @@ encode_enc_key(const ed25519_keypair_t *sig_key, if (!cross_cert) { goto err; } - ret = encode_cert(cross_cert, &encoded_cert); + ret = tor_cert_encode_ed22519(cross_cert, &encoded_cert); tor_cert_free(cross_cert); if (ret) { goto err; @@ -375,7 +337,7 @@ encode_intro_point(const ed25519_keypair_t *sig_key, /* Authentication key encoding. */ { char *encoded_cert; - if (encode_cert(ip->auth_key_cert, &encoded_cert) < 0) { + if (tor_cert_encode_ed22519(ip->auth_key_cert, &encoded_cert) < 0) { goto err; } smartlist_add_asprintf(lines, "%s\n%s", str_ip_auth_key, encoded_cert); @@ -769,7 +731,7 @@ desc_encode_v3(const hs_descriptor_t *desc, char **encoded_out) "(%d)", (int) desc->plaintext_data.signing_key_cert->cert_type); goto err; } - if (encode_cert(desc->plaintext_data.signing_key_cert, + if (tor_cert_encode_ed22519(desc->plaintext_data.signing_key_cert, &encoded_cert) < 0) { /* The function will print error logs. */ goto err; @@ -1394,15 +1356,10 @@ decode_intro_points(const hs_descriptor_t *desc, retval = 0; err: - if (chunked_desc) { - SMARTLIST_FOREACH(chunked_desc, char *, a, tor_free(a)); - smartlist_free(chunked_desc); - } - if (intro_points) { - SMARTLIST_FOREACH(intro_points, char *, a, tor_free(a)); - smartlist_free(intro_points); - } - + SMARTLIST_FOREACH(chunked_desc, char *, a, tor_free(a)); + smartlist_free(chunked_desc); + SMARTLIST_FOREACH(intro_points, char *, a, tor_free(a)); + smartlist_free(intro_points); return retval; } /* Return 1 iff the given base64 encoded signature in b64_sig from the encoded diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h index 895bed2485..083d353860 100644 --- a/src/or/hs_descriptor.h +++ b/src/or/hs_descriptor.h @@ -216,7 +216,6 @@ size_t hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data); #ifdef HS_DESCRIPTOR_PRIVATE /* Encoding. */ -STATIC int encode_cert(const tor_cert_t *cert, char **cert_str_out); STATIC char *encode_link_specifiers(const smartlist_t *specs); STATIC size_t build_plaintext_padding(const char *plaintext, size_t plaintext_len, diff --git a/src/or/include.am b/src/or/include.am index 10f8b85bdf..99912a9947 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -206,7 +206,7 @@ noinst_HEADERS+= $(ORHEADERS) micro-revision.i micro-revision.i: FORCE $(AM_V_at)rm -f micro-revision.tmp; \ - if test -d "$(top_srcdir)/.git" && \ + if test -r "$(top_srcdir)/.git" && \ test -x "`which git 2>&1;true`"; then \ HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \ echo \"$$HASH\" > micro-revision.tmp; \ diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 316ce48387..bfb36413ce 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -814,8 +814,11 @@ networkstatus_nickname_is_unnamed(const char *nickname) #define NONAUTHORITY_NS_CACHE_INTERVAL (60*60) /** Return true iff, given the options listed in <b>options</b>, <b>flavor</b> - * is the flavor of a consensus networkstatus that we would like to fetch. */ -static int + * is the flavor of a consensus networkstatus that we would like to fetch. + * + * For certificate fetches, use we_want_to_fetch_unknown_auth_certs, and + * for serving fetched documents, use directory_caches_dir_info. */ +int we_want_to_fetch_flavor(const or_options_t *options, int flavor) { if (flavor < 0 || flavor > N_CONSENSUS_FLAVORS) { @@ -837,6 +840,29 @@ we_want_to_fetch_flavor(const or_options_t *options, int flavor) return flavor == usable_consensus_flavor(); } +/** Return true iff, given the options listed in <b>options</b>, we would like + * to fetch and store unknown authority certificates. + * + * For consensus and descriptor fetches, use we_want_to_fetch_flavor, and + * for serving fetched certificates, use directory_caches_unknown_auth_certs. + */ +int +we_want_to_fetch_unknown_auth_certs(const or_options_t *options) +{ + if (authdir_mode_v3(options) || + directory_caches_unknown_auth_certs((options))) { + /* We want to serve all certs to others, regardless if we would use + * them ourselves. */ + return 1; + } + if (options->FetchUselessDescriptors) { + /* Unknown certificates are definitely useless. */ + return 1; + } + /* Otherwise, don't fetch unknown certificates. */ + return 0; +} + /** How long will we hang onto a possibly live consensus for which we're * fetching certs before we check whether there is a better one? */ #define DELAY_WHILE_FETCHING_CERTS (20*60) @@ -1728,9 +1754,9 @@ networkstatus_set_current_consensus(const char *consensus, } if (flav != usable_consensus_flavor() && - !directory_caches_dir_info(options)) { - /* This consensus is totally boring to us: we won't use it, and we won't - * serve it. Drop it. */ + !we_want_to_fetch_flavor(options, flav)) { + /* This consensus is totally boring to us: we won't use it, we didn't want + * it, and we won't serve it. Drop it. */ goto done; } @@ -1932,7 +1958,7 @@ networkstatus_set_current_consensus(const char *consensus, download_status_failed(&consensus_dl_status[flav], 0); } - if (directory_caches_dir_info(options)) { + if (we_want_to_fetch_flavor(options, flav)) { dirserv_set_cached_consensus_networkstatus(consensus, flavor, &c->digests, @@ -2381,9 +2407,9 @@ int client_would_use_router(const routerstatus_t *rs, time_t now, const or_options_t *options) { - if (!rs->is_flagged_running && !options->FetchUselessDescriptors) { + if (!rs->is_flagged_running) { /* If we had this router descriptor, we wouldn't even bother using it. - * But, if we want to have a complete list, fetch it anyway. */ + * (Fetching and storing depends on by we_want_to_fetch_flavor().) */ return 0; } if (rs->published_on + options->TestingEstimatedDescriptorPropagationTime diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index 71f36b69ed..454356e0bb 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -66,6 +66,8 @@ const routerstatus_t *router_get_consensus_status_by_nickname( int warn_if_unnamed); const char *networkstatus_get_router_digest_by_nickname(const char *nickname); int networkstatus_nickname_is_unnamed(const char *nickname); +int we_want_to_fetch_flavor(const or_options_t *options, int flavor); +int we_want_to_fetch_unknown_auth_certs(const or_options_t *options); void networkstatus_consensus_download_failed(int status_code, const char *flavname); void update_consensus_networkstatus_fetch_time(time_t now); diff --git a/src/or/nodelist.c b/src/or/nodelist.c index d8d2dbbfd4..6117b86d65 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -1639,8 +1639,8 @@ router_have_minimum_dir_info(void) * this can cause router_have_consensus_path() to be set to * CONSENSUS_PATH_EXIT, even if there are no nodes with accept exit policies. */ -consensus_path_type_t -router_have_consensus_path(void) +MOCK_IMPL(consensus_path_type_t, +router_have_consensus_path, (void)) { return have_consensus_path; } diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 31fdc64663..8456d21c6c 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -123,7 +123,8 @@ typedef enum { * create exit and internal paths, circuits, streams, ... */ CONSENSUS_PATH_EXIT = 1 } consensus_path_type_t; -consensus_path_type_t router_have_consensus_path(void); + +MOCK_DECL(consensus_path_type_t, router_have_consensus_path, (void)); void router_dir_info_changed(void); const char *get_dir_info_status_string(void); diff --git a/src/or/protover.c b/src/or/protover.c index 5972e61be7..ceaf2d5ccf 100644 --- a/src/or/protover.c +++ b/src/or/protover.c @@ -697,7 +697,7 @@ protover_compute_for_old_tor(const char *version) if (tor_version_as_new_as(version, FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS)) { return ""; - } else if (tor_version_as_new_as(version, "0.2.7.5")) { + } else if (tor_version_as_new_as(version, "0.2.9.1-alpha")) { /* 0.2.9.1-alpha HSRend=2 */ return "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 " "Link=1-4 LinkAuth=1 " diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 0819d8a713..545fba1449 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -76,6 +76,13 @@ static ssize_t rend_service_parse_intro_for_v3( static int rend_service_check_private_dir(const or_options_t *options, const rend_service_t *s, int create); +static int rend_service_check_private_dir_impl(const or_options_t *options, + const rend_service_t *s, + int create); +static const smartlist_t* rend_get_service_list( + const smartlist_t* substitute_service_list); +static smartlist_t* rend_get_service_list_mutable( + smartlist_t* substitute_service_list); /** Represents the mapping from a virtual port of a rendezvous service to * a real port on some IP. @@ -121,8 +128,44 @@ static const char *hostname_fname = "hostname"; static const char *client_keys_fname = "client_keys"; static const char *sos_poison_fname = "onion_service_non_anonymous"; +/** A list of rend_service_t's for services run on this OP. + */ +static smartlist_t *rend_service_list = NULL; + +/* Like rend_get_service_list_mutable, but returns a read-only list. */ +static const smartlist_t* +rend_get_service_list(const smartlist_t* substitute_service_list) +{ + /* It is safe to cast away the const here, because + * rend_get_service_list_mutable does not actually modify the list */ + return rend_get_service_list_mutable((smartlist_t*)substitute_service_list); +} + +/* Return a mutable list of hidden services. + * If substitute_service_list is not NULL, return it. + * Otherwise, check if the global rend_service_list is non-NULL, and if so, + * return it. + * Otherwise, log a BUG message and return NULL. + * */ +static smartlist_t* +rend_get_service_list_mutable(smartlist_t* substitute_service_list) +{ + if (substitute_service_list) { + return substitute_service_list; + } + + /* If no special service list is provided, then just use the global one. */ + + if (BUG(!rend_service_list)) { + /* No global HS list, which is a programmer error. */ + return NULL; + } + + return rend_service_list; +} + /** Tells if onion service <b>s</b> is ephemeral. -*/ + */ static unsigned int rend_service_is_ephemeral(const struct rend_service_t *s) { @@ -137,10 +180,6 @@ rend_service_escaped_dir(const struct rend_service_t *s) return rend_service_is_ephemeral(s) ? "[EPHEMERAL]" : escaped(s->directory); } -/** A list of rend_service_t's for services run on this OP. - */ -static smartlist_t *rend_service_list = NULL; - /** Return the number of rendezvous services we have configured. */ int num_rend_services(void) @@ -225,21 +264,32 @@ rend_service_free_all(void) rend_service_list = NULL; } -/** Validate <b>service</b> and add it to rend_service_list if possible. +/** Validate <b>service</b> and add it to <b>service_list</b>, or to + * the global rend_service_list if <b>service_list</b> is NULL. * Return 0 on success. On failure, free <b>service</b> and return -1. + * Takes ownership of <b>service</b>. */ static int -rend_add_service(rend_service_t *service) +rend_add_service(smartlist_t *service_list, rend_service_t *service) { int i; rend_service_port_config_t *p; + tor_assert(service); + + smartlist_t *s_list = rend_get_service_list_mutable(service_list); + /* We must have a service list, even if it's a temporary one, so we can + * check for duplicate services */ + if (BUG(!s_list)) { + return -1; + } + service->intro_nodes = smartlist_new(); service->expiring_nodes = smartlist_new(); if (service->max_streams_per_circuit < 0) { log_warn(LD_CONFIG, "Hidden service (%s) configured with negative max " - "streams per circuit; ignoring.", + "streams per circuit.", rend_service_escaped_dir(service)); rend_service_free(service); return -1; @@ -248,24 +298,24 @@ rend_add_service(rend_service_t *service) if (service->max_streams_close_circuit < 0 || service->max_streams_close_circuit > 1) { log_warn(LD_CONFIG, "Hidden service (%s) configured with invalid " - "max streams handling; ignoring.", + "max streams handling.", rend_service_escaped_dir(service)); rend_service_free(service); return -1; } if (service->auth_type != REND_NO_AUTH && - smartlist_len(service->clients) == 0) { + (!service->clients || + smartlist_len(service->clients) == 0)) { log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no " - "clients; ignoring.", + "clients.", rend_service_escaped_dir(service)); rend_service_free(service); return -1; } - if (!smartlist_len(service->ports)) { - log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured; " - "ignoring.", + if (!service->ports || !smartlist_len(service->ports)) { + log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured.", rend_service_escaped_dir(service)); rend_service_free(service); return -1; @@ -286,20 +336,20 @@ rend_add_service(rend_service_t *service) * lock file. But this is enough to detect a simple mistake that * at least one person has actually made. */ + tor_assert(s_list); if (!rend_service_is_ephemeral(service)) { /* Skip dupe for ephemeral services. */ - SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr, + SMARTLIST_FOREACH(s_list, rend_service_t*, ptr, dupe = dupe || !strcmp(ptr->directory, service->directory)); if (dupe) { log_warn(LD_REND, "Another hidden service is already configured for " - "directory %s, ignoring.", + "directory %s.", rend_service_escaped_dir(service)); rend_service_free(service); return -1; } } - smartlist_add(rend_service_list, service); log_debug(LD_REND,"Configuring service with directory %s", rend_service_escaped_dir(service)); for (i = 0; i < smartlist_len(service->ports); ++i) { @@ -315,14 +365,19 @@ rend_add_service(rend_service_t *service) "Service maps port %d to socket at \"%s\"", p->virtual_port, p->unix_addr); #else - log_debug(LD_REND, - "Service maps port %d to an AF_UNIX socket, but we " - "have no AF_UNIX support on this platform. This is " - "probably a bug.", - p->virtual_port); + log_warn(LD_BUG, + "Service maps port %d to an AF_UNIX socket, but we " + "have no AF_UNIX support on this platform. This is " + "probably a bug.", + p->virtual_port); + rend_service_free(service); + return -1; #endif /* defined(HAVE_SYS_UN_H) */ } } + /* The service passed all the checks */ + tor_assert(s_list); + smartlist_add(s_list, service); return 0; } /* NOTREACHED */ @@ -344,9 +399,9 @@ rend_service_port_config_new(const char *socket_path) return conf; } -/** Parses a real-port to virtual-port mapping separated by the provided - * separator and returns a new rend_service_port_config_t, or NULL and an - * optional error string on failure. +/** Parses a virtual-port to real-port/socket mapping separated by + * the provided separator and returns a new rend_service_port_config_t, + * or NULL and an optional error string on failure. * * The format is: VirtualPort SEP (IP|RealPort|IP:RealPort|'socket':path)? * @@ -371,14 +426,12 @@ rend_service_parse_port_config(const char *string, const char *sep, smartlist_split_string(sl, string, sep, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); if (smartlist_len(sl) < 1 || BUG(smartlist_len(sl) > 2)) { - if (err_msg_out) - err_msg = tor_strdup("Bad syntax in hidden service port configuration."); + err_msg = tor_strdup("Bad syntax in hidden service port configuration."); goto err; } virtport = (int)tor_parse_long(smartlist_get(sl,0), 10, 1, 65535, NULL,NULL); if (!virtport) { - if (err_msg_out) - tor_asprintf(&err_msg, "Missing or invalid port %s in hidden service " + tor_asprintf(&err_msg, "Missing or invalid port %s in hidden service " "port configuration", escaped(smartlist_get(sl,0))); goto err; @@ -406,10 +459,8 @@ rend_service_parse_port_config(const char *string, const char *sep, } else if (strchr(addrport, ':') || strchr(addrport, '.')) { /* else try it as an IP:port pair if it has a : or . in it */ if (tor_addr_port_lookup(addrport, &addr, &p)<0) { - if (err_msg_out) - err_msg = tor_strdup("Unparseable address in hidden service port " - "configuration."); - + err_msg = tor_strdup("Unparseable address in hidden service port " + "configuration."); goto err; } realport = p?p:virtport; @@ -417,11 +468,9 @@ rend_service_parse_port_config(const char *string, const char *sep, /* No addr:port, no addr -- must be port. */ realport = (int)tor_parse_long(addrport, 10, 1, 65535, NULL, NULL); if (!realport) { - if (err_msg_out) - tor_asprintf(&err_msg, "Unparseable or out-of-range port %s in " - "hidden service port configuration.", - escaped(addrport)); - + tor_asprintf(&err_msg, "Unparseable or out-of-range port %s in " + "hidden service port configuration.", + escaped(addrport)); goto err; } tor_addr_from_ipv4h(&addr, 0x7F000001u); /* Default to 127.0.0.1 */ @@ -440,7 +489,11 @@ rend_service_parse_port_config(const char *string, const char *sep, err: tor_free(addrport); - if (err_msg_out) *err_msg_out = err_msg; + if (err_msg_out != NULL) { + *err_msg_out = err_msg; + } else { + tor_free(err_msg); + } SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); @@ -454,6 +507,41 @@ rend_service_port_config_free(rend_service_port_config_t *p) tor_free(p); } +/* Check the directory for <b>service</b>, and add the service to + * <b>service_list</b>, or to the global list if <b>service_list</b> is NULL. + * Only add the service to the list if <b>validate_only</b> is false. + * If <b>validate_only</b> is true, free the service. + * If <b>service</b> is NULL, ignore it, and return 0. + * Returns 0 on success, and -1 on failure. + * Takes ownership of <b>service</b>, either freeing it, or adding it to the + * global service list. + */ +STATIC int +rend_service_check_dir_and_add(smartlist_t *service_list, + const or_options_t *options, + rend_service_t *service, + int validate_only) +{ + if (!service) { + /* It is ok for a service to be NULL, this means there are no services */ + return 0; + } + + if (rend_service_check_private_dir(options, service, !validate_only) + < 0) { + rend_service_free(service); + return -1; + } + + smartlist_t *s_list = rend_get_service_list_mutable(service_list); + /* We must have a service list, even if it's a temporary one, so we can + * check for duplicate services */ + if (BUG(!s_list)) { + return -1; + } + return rend_add_service(s_list, service); +} + /** Set up rend_service_list, based on the values of HiddenServiceDir and * HiddenServicePort in <b>options</b>. Return 0 on success and -1 on * failure. (If <b>validate_only</b> is set, parse, warn and return as @@ -466,25 +554,21 @@ rend_config_services(const or_options_t *options, int validate_only) rend_service_t *service = NULL; rend_service_port_config_t *portcfg; smartlist_t *old_service_list = NULL; + smartlist_t *temp_service_list = NULL; int ok = 0; - if (!validate_only) { - old_service_list = rend_service_list; - rend_service_list = smartlist_new(); - } + /* Use a temporary service list, so that we can check the new services' + * consistency with each other */ + temp_service_list = smartlist_new(); for (line = options->RendConfigLines; line; line = line->next) { if (!strcasecmp(line->key, "HiddenServiceDir")) { - if (service) { /* register the one we just finished parsing */ - if (rend_service_check_private_dir(options, service, 0) < 0) { - rend_service_free(service); + /* register the service we just finished parsing + * this code registers every service except the last one parsed, + * which is registered below the loop */ + if (rend_service_check_dir_and_add(temp_service_list, options, service, + validate_only) < 0) { return -1; - } - - if (validate_only) - rend_service_free(service); - else - rend_add_service(service); } service = tor_malloc_zero(sizeof(rend_service_t)); service->directory = tor_strdup(line->value); @@ -695,19 +779,30 @@ rend_config_services(const or_options_t *options, int validate_only) } } } - if (service) { - if (rend_service_check_private_dir(options, service, 0) < 0) { - rend_service_free(service); - return -1; - } + /* register the final service after we have finished parsing all services + * this code only registers the last service, other services are registered + * within the loop. It is ok for this service to be NULL, it is ignored. */ + if (rend_service_check_dir_and_add(temp_service_list, options, service, + validate_only) < 0) { + return -1; + } - if (validate_only) { - rend_service_free(service); - } else { - rend_add_service(service); - } + /* Free the newly added services if validating */ + if (validate_only) { + SMARTLIST_FOREACH(temp_service_list, rend_service_t *, ptr, + rend_service_free(ptr)); + smartlist_free(temp_service_list); + temp_service_list = NULL; + return 0; } + /* Otherwise, use the newly added services as the new service list + * Since we have now replaced the global service list, from this point on we + * must succeed, or die trying. */ + old_service_list = rend_service_list; + rend_service_list = temp_service_list; + temp_service_list = NULL; + /* If this is a reload and there were hidden services configured before, * keep the introduction points that are still needed and close the * other ones. */ @@ -729,7 +824,7 @@ rend_config_services(const or_options_t *options, int validate_only) * will NOT have their intro point closed. */ SMARTLIST_FOREACH(old_service_list, rend_service_t *, old, { - if (!old->directory) { + if (rend_service_is_ephemeral(old)) { SMARTLIST_DEL_CURRENT(old_service_list, old); smartlist_add(surviving_services, old); smartlist_add(rend_service_list, old); @@ -741,15 +836,20 @@ rend_config_services(const or_options_t *options, int validate_only) * probably ok? */ SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, new) { SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) { - if (new->directory && old->directory && - !strcmp(old->directory, new->directory)) { - smartlist_add_all(new->intro_nodes, old->intro_nodes); - smartlist_clear(old->intro_nodes); - smartlist_add_all(new->expiring_nodes, old->expiring_nodes); - smartlist_clear(old->expiring_nodes); - smartlist_add(surviving_services, old); - break; + if (BUG(rend_service_is_ephemeral(new)) || + BUG(rend_service_is_ephemeral(old))) { + continue; + } + if (BUG(!new->directory) || BUG(!old->directory) || + strcmp(old->directory, new->directory)) { + continue; } + smartlist_add_all(new->intro_nodes, old->intro_nodes); + smartlist_clear(old->intro_nodes); + smartlist_add_all(new->expiring_nodes, old->expiring_nodes); + smartlist_clear(old->expiring_nodes); + smartlist_add(surviving_services, old); + break; } SMARTLIST_FOREACH_END(old); } SMARTLIST_FOREACH_END(new); @@ -861,7 +961,7 @@ rend_service_add_ephemeral(crypto_pk_t *pk, } /* Initialize the service. */ - if (rend_add_service(s)) { + if (rend_add_service(NULL, s)) { return RSAE_INTERNAL; } *service_id_out = tor_strdup(s->service_id); @@ -1011,6 +1111,11 @@ service_is_single_onion_poisoned(const rend_service_t *service) char *poison_fname = NULL; file_status_t fstatus; + /* Passing a NULL service is a bug */ + if (BUG(!service)) { + return 0; + } + if (rend_service_is_ephemeral(service)) { return 0; } @@ -1044,58 +1149,69 @@ rend_service_private_key_exists(const rend_service_t *service) return private_key_status == FN_FILE; } -/** Check the single onion service poison state of all existing hidden service - * directories: - * - If each service is poisoned, and we are in Single Onion Mode, +/** Check the single onion service poison state of the directory for s: + * - If the service is poisoned, and we are in Single Onion Mode, * return 0, - * - If each service is not poisoned, and we are not in Single Onion Mode, + * - If the service is not poisoned, and we are not in Single Onion Mode, * return 0, - * - Otherwise, the poison state is invalid, and a service that was created in - * one mode is being used in the other, return -1. - * Hidden service directories without keys are not checked for consistency. - * When their keys are created, they will be poisoned (if needed). - * If a <b>service_list</b> is provided, treat it - * as the list of hidden services (used in unittests). */ -int -rend_service_list_verify_single_onion_poison(const smartlist_t *service_list, - const or_options_t *options) + * - Otherwise, the poison state is invalid: the service was created in one + * mode, and is being used in the other, return -1. + * Hidden service directories without keys are always considered consistent. + * They will be poisoned after their directory is created (if needed). */ +STATIC int +rend_service_verify_single_onion_poison(const rend_service_t* s, + const or_options_t* options) { - const smartlist_t *s_list; - /* If no special service list is provided, then just use the global one. */ - if (!service_list) { - if (!rend_service_list) { /* No global HS list. Nothing to see here. */ - return 0; - } + /* Passing a NULL service is a bug */ + if (BUG(!s)) { + return -1; + } - s_list = rend_service_list; - } else { - s_list = service_list; + /* Ephemeral services are checked at ADD_ONION time */ + if (BUG(rend_service_is_ephemeral(s))) { + return -1; } - int consistent = 1; - SMARTLIST_FOREACH_BEGIN(s_list, const rend_service_t *, s) { - if (service_is_single_onion_poisoned(s) != - rend_service_non_anonymous_mode_enabled(options) && - rend_service_private_key_exists(s)) { - consistent = 0; - } - } SMARTLIST_FOREACH_END(s); + /* Service is expected to have a directory */ + if (BUG(!s->directory)) { + return -1; + } - return consistent ? 0 : -1; + /* Services without keys are always ok - their keys will only ever be used + * in the current mode */ + if (!rend_service_private_key_exists(s)) { + return 0; + } + + /* The key has been used before in a different mode */ + if (service_is_single_onion_poisoned(s) != + rend_service_non_anonymous_mode_enabled(options)) { + return -1; + } + + /* The key exists and is consistent with the current mode */ + return 0; } -/*** Helper for rend_service_poison_new_single_onion_dirs(). Add a file to - * this hidden service directory that marks it as a single onion service. - * Tor must be in single onion mode before calling this function. +/*** Helper for rend_service_poison_new_single_onion_dir(). Add a file to + * the hidden service directory for s that marks it as a single onion service. + * Tor must be in single onion mode before calling this function, and the + * service directory must already have been created. * Returns 0 when a directory is successfully poisoned, or if it is already * poisoned. Returns -1 on a failure to read the directory or write the poison * file, or if there is an existing private key file in the directory. (The * service should have been poisoned when the key was created.) */ static int -poison_new_single_onion_hidden_service_dir(const rend_service_t *service) +poison_new_single_onion_hidden_service_dir_impl(const rend_service_t *service, + const or_options_t* options) { + /* Passing a NULL service is a bug */ + if (BUG(!service)) { + return -1; + } + /* We must only poison directories if we're in Single Onion mode */ - tor_assert(rend_service_non_anonymous_mode_enabled(get_options())); + tor_assert(rend_service_non_anonymous_mode_enabled(options)); int fd; int retval = -1; @@ -1113,8 +1229,8 @@ poison_new_single_onion_hidden_service_dir(const rend_service_t *service) return -1; } - /* Make sure the directory exists */ - if (rend_service_check_private_dir(get_options(), service, 1) < 0) + /* Make sure the directory was created before calling this function. */ + if (BUG(rend_service_check_private_dir_impl(options, service, 0) < 0)) return -1; poison_fname = rend_service_sos_poison_path(service); @@ -1151,44 +1267,39 @@ poison_new_single_onion_hidden_service_dir(const rend_service_t *service) return retval; } -/** We just got launched in Single Onion Mode. That's a non-anoymous - * mode for hidden services; hence we should mark all new hidden service - * directories appropriately so that they are never launched as - * location-private hidden services again. (New directories don't have private - * key files.) - * If a <b>service_list</b> is provided, treat it as the list of hidden - * services (used in unittests). +/** We just got launched in Single Onion Mode. That's a non-anonymous mode for + * hidden services. If s is new, we should mark its hidden service + * directory appropriately so that it is never launched as a location-private + * hidden service. (New directories don't have private key files.) * Return 0 on success, -1 on fail. */ -int -rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list) +STATIC int +rend_service_poison_new_single_onion_dir(const rend_service_t *s, + const or_options_t* options) { + /* Passing a NULL service is a bug */ + if (BUG(!s)) { + return -1; + } + /* We must only poison directories if we're in Single Onion mode */ - tor_assert(rend_service_non_anonymous_mode_enabled(get_options())); + tor_assert(rend_service_non_anonymous_mode_enabled(options)); - const smartlist_t *s_list; - /* If no special service list is provided, then just use the global one. */ - if (!service_list) { - if (!rend_service_list) { /* No global HS list. Nothing to see here. */ - return 0; - } + /* Ephemeral services aren't allowed in non-anonymous mode */ + if (BUG(rend_service_is_ephemeral(s))) { + return -1; + } - s_list = rend_service_list; - } else { - s_list = service_list; + /* Service is expected to have a directory */ + if (BUG(!s->directory)) { + return -1; } - SMARTLIST_FOREACH_BEGIN(s_list, const rend_service_t *, s) { - if (!rend_service_private_key_exists(s)) { - if (poison_new_single_onion_hidden_service_dir(s) < 0) { - return -1; - } + if (!rend_service_private_key_exists(s)) { + if (poison_new_single_onion_hidden_service_dir_impl(s, options) + < 0) { + return -1; } - } SMARTLIST_FOREACH_END(s); - - /* The keys for these services are linked to the server IP address */ - log_notice(LD_REND, "The configured onion service directories have been " - "used in single onion mode. They can not be used for anonymous " - "hidden services."); + } return 0; } @@ -1202,13 +1313,10 @@ rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list) int rend_service_load_all_keys(const smartlist_t *service_list) { - const smartlist_t *s_list; - /* If no special service list is provided, then just use the global one. */ - if (!service_list) { - tor_assert(rend_service_list); - s_list = rend_service_list; - } else { - s_list = service_list; + /* Use service_list for unit tests */ + const smartlist_t *s_list = rend_get_service_list(service_list); + if (BUG(!s_list)) { + return -1; } SMARTLIST_FOREACH_BEGIN(s_list, rend_service_t *, s) { @@ -1272,6 +1380,32 @@ rend_service_derive_key_digests(struct rend_service_t *s) return 0; } +/* Implements the directory check from rend_service_check_private_dir, + * without doing the single onion poison checks. */ +static int +rend_service_check_private_dir_impl(const or_options_t *options, + const rend_service_t *s, + int create) +{ + cpd_check_t check_opts = CPD_NONE; + if (create) { + check_opts |= CPD_CREATE; + } else { + check_opts |= CPD_CHECK_MODE_ONLY; + check_opts |= CPD_CHECK; + } + if (s->dir_group_readable) { + check_opts |= CPD_GROUP_READ; + } + /* Check/create directory */ + if (check_private_dir(s->directory, check_opts, options->User) < 0) { + log_warn(LD_REND, "Checking service directory %s failed.", s->directory); + return -1; + } + + return 0; +} + /** Make sure that the directory for <b>s</b> is private, using the config in * <b>options</b>. * If <b>create</b> is true: @@ -1286,20 +1420,58 @@ rend_service_check_private_dir(const or_options_t *options, const rend_service_t *s, int create) { - cpd_check_t check_opts = CPD_NONE; - if (create) { - check_opts |= CPD_CREATE; - } else { - check_opts |= CPD_CHECK_MODE_ONLY; - check_opts |= CPD_CHECK; - } - if (s->dir_group_readable) { - check_opts |= CPD_GROUP_READ; + /* Passing a NULL service is a bug */ + if (BUG(!s)) { + return -1; } + /* Check/create directory */ - if (check_private_dir(s->directory, check_opts, options->User) < 0) { + if (rend_service_check_private_dir_impl(options, s, create) < 0) { + return -1; + } + + /* Check if the hidden service key exists, and was created in a different + * single onion service mode, and refuse to launch if it has. + * This is safe to call even when create is false, as it ignores missing + * keys and directories: they are always valid. + */ + if (rend_service_verify_single_onion_poison(s, options) < 0) { + /* We can't use s->service_id here, as the key may not have been loaded */ + log_warn(LD_GENERAL, "We are configured with " + "HiddenServiceNonAnonymousMode %d, but the hidden " + "service key in directory %s was created in %s mode. " + "This is not allowed.", + rend_service_non_anonymous_mode_enabled(options) ? 1 : 0, + rend_service_escaped_dir(s), + rend_service_non_anonymous_mode_enabled(options) ? + "an anonymous" : "a non-anonymous" + ); return -1; } + + /* Poison new single onion directories immediately after they are created, + * so that we never accidentally launch non-anonymous hidden services + * thinking they are anonymous. Any keys created later will end up with the + * correct poisoning state. + */ + if (create && rend_service_non_anonymous_mode_enabled(options)) { + static int logged_warning = 0; + + if (rend_service_poison_new_single_onion_dir(s, options) < 0) { + log_warn(LD_GENERAL,"Failed to mark new hidden services as non-anonymous" + "."); + return -1; + } + + if (!logged_warning) { + /* The keys for these services are linked to the server IP address */ + log_notice(LD_REND, "The configured onion service directories have been " + "used in single onion mode. They can not be used for " + "anonymous hidden services."); + logged_warning = 1; + } + } + return 0; } @@ -1312,7 +1484,9 @@ rend_service_load_keys(rend_service_t *s) char *fname = NULL; char buf[128]; - if (rend_service_check_private_dir(get_options(), s, 1) < 0) + /* Make sure the directory was created and single onion poisoning was + * checked before calling this function */ + if (BUG(rend_service_check_private_dir(get_options(), s, 0) < 0)) goto err; /* Load key */ @@ -3086,7 +3260,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) len += 2; memcpy(auth, circuit->cpath->prev->rend_circ_nonce, DIGEST_LEN); memcpy(auth+DIGEST_LEN, "INTRODUCE", 9); - if (crypto_digest(buf+len, auth, DIGEST_LEN+9)) + if (crypto_digest(buf+len, auth, DIGEST_LEN+9) < 0) goto err; len += 20; note_crypto_pk_op(REND_SERVER); diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 630191e8b7..3b185672f6 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -119,7 +119,16 @@ typedef struct rend_service_t { STATIC void rend_service_free(rend_service_t *service); STATIC char *rend_service_sos_poison_path(const rend_service_t *service); - +STATIC int rend_service_check_dir_and_add(smartlist_t *service_list, + const or_options_t *options, + rend_service_t *service, + int validate_only); +STATIC int rend_service_verify_single_onion_poison( + const rend_service_t *s, + const or_options_t *options); +STATIC int rend_service_poison_new_single_onion_dir( + const rend_service_t *s, + const or_options_t* options); #endif int num_rend_services(void); @@ -165,11 +174,6 @@ void rend_service_port_config_free(rend_service_port_config_t *p); void rend_authorized_client_free(rend_authorized_client_t *client); -int rend_service_list_verify_single_onion_poison( - const smartlist_t *service_list, - const or_options_t *options); -int rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list); - /** Return value from rend_service_add_ephemeral. */ typedef enum { RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */ diff --git a/src/or/router.c b/src/or/router.c index bc0eb3a34a..917caaa1f5 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1968,23 +1968,34 @@ static int router_guess_address_from_dir_headers(uint32_t *guess); /** Make a current best guess at our address, either because * it's configured in torrc, or because we've learned it from * dirserver headers. Place the answer in *<b>addr</b> and return - * 0 on success, else return -1 if we have no guess. */ + * 0 on success, else return -1 if we have no guess. + * + * If <b>cache_only</b> is true, just return any cached answers, and + * don't try to get any new answers. + */ MOCK_IMPL(int, -router_pick_published_address,(const or_options_t *options, uint32_t *addr)) +router_pick_published_address,(const or_options_t *options, uint32_t *addr, + int cache_only)) { + /* First, check the cached output from resolve_my_address(). */ *addr = get_last_resolved_addr(); - if (!*addr && - resolve_my_address(LOG_INFO, options, addr, NULL, NULL) < 0) { - log_info(LD_CONFIG, "Could not determine our address locally. " - "Checking if directory headers provide any hints."); - if (router_guess_address_from_dir_headers(addr) < 0) { - log_info(LD_CONFIG, "No hints from directory headers either. " - "Will try again later."); - return -1; + if (*addr) + return 0; + + /* Second, consider doing a resolve attempt right here. */ + if (!cache_only) { + if (resolve_my_address(LOG_INFO, options, addr, NULL, NULL) >= 0) { + log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr)); + return 0; } } - log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr)); - return 0; + + /* Third, check the cached output from router_new_address_suggestion(). */ + if (router_guess_address_from_dir_headers(addr) >= 0) + return 0; + + /* We have no useful cached answers. Return failure. */ + return -1; } /* Like router_check_descriptor_address_consistency, but specifically for the @@ -2081,7 +2092,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) int hibernating = we_are_hibernating(); const or_options_t *options = get_options(); - if (router_pick_published_address(options, &addr) < 0) { + if (router_pick_published_address(options, &addr, 0) < 0) { log_warn(LD_CONFIG, "Don't know my address while generating descriptor"); return -1; } @@ -2330,7 +2341,7 @@ router_rebuild_descriptor(int force) if (desc_clean_since && !force) return 0; - if (router_pick_published_address(options, &addr) < 0 || + if (router_pick_published_address(options, &addr, 0) < 0 || router_get_advertised_or_port(options) == 0) { /* Stop trying to rebuild our descriptor every second. We'll * learn that it's time to try again when ip_address_changed() diff --git a/src/or/router.h b/src/or/router.h index 73bfea1faa..c30a0301b7 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -91,7 +91,8 @@ const uint8_t *router_get_my_id_digest(void); int router_extrainfo_digest_is_me(const char *digest); int router_is_me(const routerinfo_t *router); MOCK_DECL(int,router_pick_published_address,(const or_options_t *options, - uint32_t *addr)); + uint32_t *addr, + int cache_only)); int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e); int router_rebuild_descriptor(int force); char *router_dump_router_to_string(routerinfo_t *router, diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index ab78dbe61e..51802b15e5 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -742,8 +742,12 @@ load_ed_keys(const or_options_t *options, time_t now) if (need_new_signing_key) { log_notice(LD_OR, "It looks like I need to generate and sign a new " - "medium-term signing key, because %s. To do that, I need to " - "load%s the permanent master identity key.", + "medium-term signing key, because %s. To do that, I " + "need to load%s the permanent master identity key. " + "If the master identity key was not moved or encrypted " + "with a passphrase, this will be done automatically and " + "no further action is required. Otherwise, provide the " + "necessary data using 'tor --keygen' to do it manually.", (NULL == use_signing) ? "I don't have one" : EXPIRES_SOON(check_signing_cert, 0) ? "the one I have is expired" : "you asked me to make one with --keygen", @@ -751,15 +755,19 @@ load_ed_keys(const or_options_t *options, time_t now) } else if (want_new_signing_key && !offline_master) { log_notice(LD_OR, "It looks like I should try to generate and sign a " "new medium-term signing key, because the one I have is " - "going to expire soon. To do that, I'm going to have to try to " - "load the permanent master identity key."); + "going to expire soon. To do that, I'm going to have to " + "try to load the permanent master identity key. " + "If the master identity key was not moved or encrypted " + "with a passphrase, this will be done automatically and " + "no further action is required. Otherwise, provide the " + "necessary data using 'tor --keygen' to do it manually."); } else if (want_new_signing_key) { log_notice(LD_OR, "It looks like I should try to generate and sign a " "new medium-term signing key, because the one I have is " "going to expire soon. But OfflineMasterKey is set, so I " - "won't try to load a permanent master identity key is set. " - "You will need to use 'tor --keygen' make a new signing key " - "and certificate."); + "won't try to load a permanent master identity key. You " + "will need to use 'tor --keygen' to make a new signing " + "key and certificate."); } { diff --git a/src/or/routerlist.c b/src/or/routerlist.c index c99d22ed41..9bcca76b63 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -586,7 +586,7 @@ trusted_dirs_load_certs_from_string(const char *contents, int source, "signing key %s", from_store ? "cached" : "downloaded", ds->nickname, hex_str(cert->signing_key_digest,DIGEST_LEN)); } else { - int adding = directory_caches_unknown_auth_certs(get_options()); + int adding = we_want_to_fetch_unknown_auth_certs(get_options()); log_info(LD_DIR, "%s %s certificate for unrecognized directory " "authority with signing key %s", adding ? "Adding" : "Not adding", @@ -1012,7 +1012,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now, char *resource = NULL; cert_list_t *cl; const or_options_t *options = get_options(); - const int cache = directory_caches_unknown_auth_certs(options); + const int keep_unknown = we_want_to_fetch_unknown_auth_certs(options); fp_pair_t *fp_tmp = NULL; char id_digest_str[2*DIGEST_LEN+1]; char sk_digest_str[2*DIGEST_LEN+1]; @@ -1084,9 +1084,10 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now, if (!smartlist_len(voter->sigs)) continue; /* This authority never signed this consensus, so don't * go looking for a cert with key digest 0000000000. */ - if (!cache && + if (!keep_unknown && !trusteddirserver_get_by_v3_auth_digest(voter->identity_digest)) - continue; /* We are not a cache, and we don't know this authority.*/ + continue; /* We don't want unknown certs, and we don't know this + * authority.*/ /* * If we don't know *any* cert for this authority, and a download by ID @@ -3895,7 +3896,7 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg, router_describe(router)); *msg = "Router descriptor is not referenced by any network-status."; - /* Only journal this desc if we'll be serving it. */ + /* Only journal this desc if we want to keep old descriptors */ if (!from_cache && should_cache_old_descriptors()) signed_desc_append_to_journal(&router->cache_info, &routerlist->desc_store); @@ -4525,13 +4526,14 @@ router_load_extrainfo_from_string(const char *s, const char *eos, smartlist_free(extrainfo_list); } -/** Return true iff any networkstatus includes a descriptor whose digest - * is that of <b>desc</b>. */ +/** Return true iff the latest ns-flavored consensus includes a descriptor + * whose digest is that of <b>desc</b>. */ static int signed_desc_digest_is_recognized(signed_descriptor_t *desc) { const routerstatus_t *rs; - networkstatus_t *consensus = networkstatus_get_latest_consensus(); + networkstatus_t *consensus = networkstatus_get_latest_consensus_by_flavor( + FLAV_NS); if (consensus) { rs = networkstatus_vote_find_entry(consensus, desc->identity_digest); @@ -5154,7 +5156,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, ++n_would_reject; continue; /* We would throw it out immediately. */ } - if (!directory_caches_dir_info(options) && + if (!we_want_to_fetch_flavor(options, consensus->flavor) && !client_would_use_router(rs, now, options)) { ++n_wouldnt_use; continue; /* We would never use it ourself. */ diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 2cfd3fc58a..38ceb942a9 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -715,7 +715,7 @@ dump_desc_populate_one_file, (const char *dirname, const char *f)) * filename. */ if (crypto_digest256((char *)content_digest, desc, (size_t) st.st_size, - DIGEST_SHA256) != 0) { + DIGEST_SHA256) < 0) { /* Weird, but okay */ log_info(LD_DIR, "Unable to hash content of %s from unparseable descriptors " @@ -879,7 +879,7 @@ dump_desc(const char *desc, const char *type) /* Get the hash for logging purposes anyway */ len = strlen(desc); if (crypto_digest256((char *)digest_sha256, desc, len, - DIGEST_SHA256) != 0) { + DIGEST_SHA256) < 0) { log_info(LD_DIR, "Unable to parse descriptor of type %s, and unable to even hash" " it!", type); @@ -4536,12 +4536,12 @@ router_get_hash_impl(const char *s, size_t s_len, char *digest, return -1; if (alg == DIGEST_SHA1) { - if (crypto_digest(digest, start, end-start)) { + if (crypto_digest(digest, start, end-start) < 0) { log_warn(LD_BUG,"couldn't compute digest"); return -1; } } else { - if (crypto_digest256(digest, start, end-start, alg)) { + if (crypto_digest256(digest, start, end-start, alg) < 0) { log_warn(LD_BUG,"couldn't compute digest"); return -1; } diff --git a/src/or/shared_random.c b/src/or/shared_random.c index 5f6b03f1ba..0eb93382ca 100644 --- a/src/or/shared_random.c +++ b/src/or/shared_random.c @@ -192,7 +192,7 @@ verify_commit_and_reveal(const sr_commit_t *commit) /* Use the invariant length since the encoded reveal variable has an * extra byte for the NUL terminated byte. */ if (crypto_digest256(received_hashed_reveal, commit->encoded_reveal, - SR_REVEAL_BASE64_LEN, commit->alg)) { + SR_REVEAL_BASE64_LEN, commit->alg) < 0) { /* Unable to digest the reveal blob, this is unlikely. */ goto invalid; } @@ -932,7 +932,7 @@ sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert) /* The invariant length is used here since the encoded reveal variable * has an extra byte added for the NULL terminated byte. */ if (crypto_digest256(commit->hashed_reveal, commit->encoded_reveal, - SR_REVEAL_BASE64_LEN, commit->alg)) { + SR_REVEAL_BASE64_LEN, commit->alg) < 0) { goto error; } @@ -1012,7 +1012,7 @@ sr_compute_srv(void) SMARTLIST_FOREACH(chunks, char *, s, tor_free(s)); smartlist_free(chunks); if (crypto_digest256(hashed_reveals, reveals, strlen(reveals), - SR_DIGEST_ALG)) { + SR_DIGEST_ALG) < 0) { goto end; } current_srv = generate_srv(hashed_reveals, reveal_num, diff --git a/src/or/torcert.c b/src/or/torcert.c index 852def9ef6..c58f3da2d3 100644 --- a/src/or/torcert.c +++ b/src/or/torcert.c @@ -156,11 +156,12 @@ tor_cert_parse(const uint8_t *encoded, const size_t len) cert->encoded_len = len; memcpy(cert->signed_key.pubkey, parsed->certified_key, 32); - const int64_t valid_until_64 = ((int64_t)parsed->exp_field) * 3600; + int64_t valid_until_64 = ((int64_t)parsed->exp_field) * 3600; +#if SIZEOF_TIME_T < SIZEOF_INT64_T if (valid_until_64 > TIME_MAX) - cert->valid_until = TIME_MAX - 1; - else - cert->valid_until = (time_t) valid_until_64; + valid_until_64 = TIME_MAX - 1; +#endif + cert->valid_until = (time_t) valid_until_64; cert->cert_type = parsed->cert_type; for (unsigned i = 0; i < ed25519_cert_getlen_ext(parsed); ++i) { @@ -647,3 +648,44 @@ or_handshake_certs_check_both(int severity, } } +/* === ENCODING === */ + +/* Encode the ed25519 certificate <b>cert</b> and put the newly allocated + * string in <b>cert_str_out</b>. Return 0 on success else a negative value. */ +int +tor_cert_encode_ed22519(const tor_cert_t *cert, char **cert_str_out) +{ + int ret = -1; + char *ed_cert_b64 = NULL; + size_t ed_cert_b64_len; + + tor_assert(cert); + tor_assert(cert_str_out); + + /* Get the encoded size and add the NUL byte. */ + ed_cert_b64_len = base64_encode_size(cert->encoded_len, + BASE64_ENCODE_MULTILINE) + 1; + ed_cert_b64 = tor_malloc_zero(ed_cert_b64_len); + + /* Base64 encode the encoded certificate. */ + if (base64_encode(ed_cert_b64, ed_cert_b64_len, + (const char *) cert->encoded, cert->encoded_len, + BASE64_ENCODE_MULTILINE) < 0) { + log_err(LD_BUG, "Couldn't base64-encode ed22519 cert!"); + goto err; + } + + /* Put everything together in a NUL terminated string. */ + tor_asprintf(cert_str_out, + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----", + ed_cert_b64); + /* Success! */ + ret = 0; + + err: + tor_free(ed_cert_b64); + return ret; +} + diff --git a/src/or/torcert.h b/src/or/torcert.h index 4bd816f4a4..090f6b5811 100644 --- a/src/or/torcert.h +++ b/src/or/torcert.h @@ -98,5 +98,7 @@ void or_handshake_certs_check_both(int severity, const ed25519_public_key_t **ed_id_out, const common_digests_t **rsa_id_out); +int tor_cert_encode_ed22519(const tor_cert_t *cert, char **cert_str_out); + #endif diff --git a/src/or/transports.c b/src/or/transports.c index 614b28c168..f755882c16 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -1611,7 +1611,7 @@ pt_get_extra_info_descriptor_string(void) uint32_t external_ip_address = 0; if (tor_addr_is_null(&t->addr) && router_pick_published_address(get_options(), - &external_ip_address) >= 0) { + &external_ip_address, 0) >= 0) { tor_addr_t addr; tor_addr_from_ipv4h(&addr, external_ip_address); addrport = fmt_addrport(&addr, t->port); diff --git a/src/test/include.am b/src/test/include.am index f6e43148f0..8d0fc2ff6b 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -80,6 +80,7 @@ src_test_test_SOURCES = \ src/test/test_checkdir.c \ src/test/test_circuitlist.c \ src/test/test_circuitmux.c \ + src/test/test_circuituse.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ src/test/test_connection.c \ diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h index 4c020c7ec3..922c68b42f 100644 --- a/src/test/log_test_helpers.h +++ b/src/test/log_test_helpers.h @@ -49,7 +49,22 @@ void mock_dump_saved_logs(void); #define expect_log_msg_containing_either(str1, str2) \ assert_log_predicate(mock_saved_log_has_message_containing(str1) || \ mock_saved_log_has_message_containing(str2), \ - "expected log to contain " # str1 " or " # str2); + "expected log to contain " # str1 " or " # str2); + +#define expect_log_msg_containing_either3(str1, str2, str3) \ + assert_log_predicate(mock_saved_log_has_message_containing(str1) || \ + mock_saved_log_has_message_containing(str2) || \ + mock_saved_log_has_message_containing(str3), \ + "expected log to contain " # str1 " or " # str2 \ + " or " # str3); + +#define expect_log_msg_containing_either4(str1, str2, str3, str4) \ + assert_log_predicate(mock_saved_log_has_message_containing(str1) || \ + mock_saved_log_has_message_containing(str2) || \ + mock_saved_log_has_message_containing(str3) || \ + mock_saved_log_has_message_containing(str4), \ + "expected log to contain " # str1 " or " # str2 \ + " or " # str3 " or " # str4); #define expect_single_log_msg(str) \ do { \ diff --git a/src/test/test.c b/src/test/test.c index b02ecb922d..750d8b00e4 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1190,6 +1190,7 @@ struct testgroup_t testgroups[] = { { "checkdir/", checkdir_tests }, { "circuitlist/", circuitlist_tests }, { "circuitmux/", circuitmux_tests }, + { "circuituse/", circuituse_tests }, { "compat/libevent/", compat_libevent_tests }, { "config/", config_tests }, { "connection/", connection_tests }, diff --git a/src/test/test.h b/src/test/test.h index 49b4499594..2fa73592ef 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -185,6 +185,7 @@ extern struct testcase_t channeltls_tests[]; extern struct testcase_t checkdir_tests[]; extern struct testcase_t circuitlist_tests[]; extern struct testcase_t circuitmux_tests[]; +extern struct testcase_t circuituse_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; extern struct testcase_t connection_tests[]; diff --git a/src/test/test_address.c b/src/test/test_address.c index e52779cb64..0d142ad483 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -808,8 +808,13 @@ test_address_get_if_addrs6_list_internal(void *arg) results = get_interface_address6_list(LOG_ERR, AF_INET6, 1); tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1); if (smartlist_len(mock_saved_logs()) == 1) { - expect_log_msg_containing_either("connect() failed", - "unable to create socket"); + expect_log_msg_containing_either4("connect() failed", + "unable to create socket", + "Address that we determined via UDP " + "socket magic is unsuitable for public " + "comms.", + "getsockname() to determine interface " + "failed"); } teardown_capture_of_logs(); @@ -846,8 +851,13 @@ test_address_get_if_addrs6_list_no_internal(void *arg) results = get_interface_address6_list(LOG_ERR, AF_INET6, 0); tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1); if (smartlist_len(mock_saved_logs()) == 1) { - expect_log_msg_containing_either("connect() failed", - "unable to create socket"); + expect_log_msg_containing_either4("connect() failed", + "unable to create socket", + "Address that we determined via UDP " + "socket magic is unsuitable for public " + "comms.", + "getsockname() to determine interface " + "failed"); } teardown_capture_of_logs(); diff --git a/src/test/test_circuituse.c b/src/test/test_circuituse.c new file mode 100644 index 0000000000..edbc9f6391 --- /dev/null +++ b/src/test/test_circuituse.c @@ -0,0 +1,302 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "test.h" +#include "test_helpers.h" +#include "config.h" +#include "circuitlist.h" +#include "circuituse.h" +#include "circuitbuild.h" +#include "nodelist.h" + +static void +test_circuit_is_available_for_use_ret_false_when_marked_for_close(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->marked_for_close = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_when_timestamp_dirty(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->timestamp_dirty = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_general_purpose(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_general_origin(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_origin_purpose(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_OR; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_unusable_for_new_conns(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + mark_circuit_unusable_for_new_conns(TO_ORIGIN_CIRCUIT(circ)); + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_returns_false_for_onehop_tunnel(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + oc->build_state = tor_malloc(sizeof(cpath_build_state_t)); + oc->build_state->onehop_tunnel = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_returns_true_for_clean_circuit(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + oc->build_state = tor_malloc(sizeof(cpath_build_state_t)); + oc->build_state->onehop_tunnel = 0; + + tt_int_op(1, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static int +mock_circuit_all_predicted_ports_handled(time_t now, + int *need_uptime, + int *need_capacity) +{ + (void)now; + + if (need_uptime && need_capacity) + return 0; + return 1; +} + +static consensus_path_type_t +mock_router_have_unknown_consensus_path(void) +{ + return CONSENSUS_PATH_UNKNOWN; +} + +static consensus_path_type_t +mock_router_have_exit_consensus_path(void) +{ + return CONSENSUS_PATH_EXIT; +} + +static void +test_needs_exit_circuits_ret_false_for_predicted_ports_and_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 0; + + time_t now = time(NULL); + tt_int_op(0, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); +} + +static void +test_needs_exit_circuits_ret_false_for_non_exit_consensus_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 1; + MOCK(router_have_consensus_path, mock_router_have_unknown_consensus_path); + + time_t now = time(NULL); + tt_int_op(0, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_exit_circuits_ret_true_for_predicted_ports_and_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 1; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + + time_t now = time(NULL); + tt_int_op(1, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_circuits_for_build_ret_false_consensus_path_unknown(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_unknown_consensus_path); + tt_int_op(0, ==, needs_circuits_for_build(0)); + done: ; +} + +static void +test_needs_circuits_for_build_ret_false_if_num_less_than_max(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + tt_int_op(0, ==, needs_circuits_for_build(13)); + done: + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_circuits_for_build_returns_true_when_more_are_needed(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + tt_int_op(1, ==, needs_circuits_for_build(0)); + done: + UNMOCK(router_have_consensus_path); +} + +struct testcase_t circuituse_tests[] = { + { "marked", + test_circuit_is_available_for_use_ret_false_when_marked_for_close, + TT_FORK, NULL, NULL + }, + { "timestamp", + test_circuit_is_available_for_use_ret_false_when_timestamp_dirty, + TT_FORK, NULL, NULL + }, + { "non_general", + test_circuit_is_available_for_use_ret_false_for_non_general_purpose, + TT_FORK, NULL, NULL + }, + { "non_general", + test_circuit_is_available_for_use_ret_false_for_non_general_origin, + TT_FORK, NULL, NULL + }, + { "origin", + test_circuit_is_available_for_use_ret_false_for_non_origin_purpose, + TT_FORK, NULL, NULL + }, + { "clean", + test_circuit_is_available_for_use_ret_false_unusable_for_new_conns, + TT_FORK, NULL, NULL + }, + { "onehop", + test_circuit_is_available_for_use_returns_false_for_onehop_tunnel, + TT_FORK, NULL, NULL + }, + { "clean_circ", + test_circuit_is_available_for_use_returns_true_for_clean_circuit, + TT_FORK, NULL, NULL + }, + { "exit_f", + test_needs_exit_circuits_ret_false_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "exit_t", + test_needs_exit_circuits_ret_true_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "non_exit", + test_needs_exit_circuits_ret_false_for_non_exit_consensus_path, + TT_FORK, NULL, NULL + }, + { "true", + test_needs_exit_circuits_ret_true_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "consensus_path_unknown", + test_needs_circuits_for_build_ret_false_consensus_path_unknown, + TT_FORK, NULL, NULL + }, + { "less_than_max", + test_needs_circuits_for_build_ret_false_if_num_less_than_max, + TT_FORK, NULL, NULL + }, + { "more_needed", + test_needs_circuits_for_build_returns_true_when_more_are_needed, + TT_FORK, NULL, NULL + }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_config.c b/src/test/test_config.c index 384bff410f..2fc37b0bb8 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -3480,10 +3480,12 @@ test_config_default_dir_servers(void *arg) static int mock_router_pick_published_address_result = 0; static int -mock_router_pick_published_address(const or_options_t *options, uint32_t *addr) +mock_router_pick_published_address(const or_options_t *options, + uint32_t *addr, int cache_only) { (void)options; (void)addr; + (void)cache_only; return mock_router_pick_published_address_result; } diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 6f83ceff00..fa0a174813 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -3501,10 +3501,15 @@ test_dir_purpose_needs_anonymity_returns_true_by_default(void *arg) (void)arg; tor_capture_bugs_(1); + setup_full_capture_of_logs(LOG_WARN); tt_int_op(1, ==, purpose_needs_anonymity(0, 0, NULL)); tt_int_op(1, ==, smartlist_len(tor_get_captured_bug_log_())); + expect_single_log_msg_containing("Called with dir_purpose=0"); + tor_end_capture_bugs_(); - done: ; + done: + tor_end_capture_bugs_(); + teardown_capture_of_logs(); } static void diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index 130ec43a3a..132af39776 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -10,6 +10,7 @@ #include "orconfig.h" #include "or.h" +#include "relay.h" #include "routerlist.h" #include "nodelist.h" #include "buffers.h" @@ -23,6 +24,8 @@ DISABLE_GCC_WARNING(overlength-strings) * at large. */ #endif #include "test_descriptors.inc" +#include "or.h" +#include "circuitlist.h" #ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS ENABLE_GCC_WARNING(overlength-strings) #endif @@ -105,3 +108,23 @@ connection_write_to_buf_mock(const char *string, size_t len, write_to_buf(string, len, conn->outbuf); } +/* Set up a fake origin circuit with the specified number of cells, + * Return a pointer to the newly-created dummy circuit */ +circuit_t * +dummy_origin_circuit_new(int n_cells) +{ + origin_circuit_t *circ = origin_circuit_new(); + int i; + cell_t cell; + + for (i=0; i < n_cells; ++i) { + crypto_rand((void*)&cell, sizeof(cell)); + cell_queue_append_packed_copy(TO_CIRCUIT(circ), + &TO_CIRCUIT(circ)->n_chan_cells, + 1, &cell, 1, 0); + } + + TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + return TO_CIRCUIT(circ); +} + diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index b77a459256..ba93b100d5 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -6,6 +6,8 @@ const char *get_yesterday_date_str(void); +circuit_t * dummy_origin_circuit_new(int num_cells); + /* Number of descriptors contained in test_descriptors.txt. */ #define HELPER_NUMBER_OF_DESCRIPTORS 8 diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 318c549762..6fadeeead2 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -550,17 +550,18 @@ test_single_onion_poisoning(void *arg) rend_service_t *service_2 = tor_malloc_zero(sizeof(rend_service_t)); char *dir2 = tor_strdup(get_fname_rnd("test_hs_dir2")); smartlist_t *services = smartlist_new(); + char *poison_path = NULL; - /* No services, no problem! */ + /* No services, no service to verify, no problem! */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_config_services(mock_options, 1); tt_assert(ret == 0); /* Either way, no problem. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_config_services(mock_options, 1); tt_assert(ret == 0); /* Create the data directory, and, if the correct bit in arg is set, @@ -578,44 +579,88 @@ test_single_onion_poisoning(void *arg) tt_assert(ret == 0); } - service_1->directory = dir1; - service_2->directory = dir2; + service_1->directory = tor_strdup(dir1); + service_2->directory = tor_strdup(dir2); + /* The services own the directory pointers now */ dir1 = dir2 = NULL; - smartlist_add(services, service_1); + /* Add port to service 1 */ + service_1->ports = smartlist_new(); + service_2->ports = smartlist_new(); + char *err_msg = NULL; + rend_service_port_config_t *port1 = rend_service_parse_port_config("80", " ", + &err_msg); + tt_assert(port1); + tt_assert(!err_msg); + smartlist_add(service_1->ports, port1); + + rend_service_port_config_t *port2 = rend_service_parse_port_config("90", " ", + &err_msg); + /* Add port to service 2 */ + tt_assert(port2); + tt_assert(!err_msg); + smartlist_add(service_2->ports, port2); + + /* No services, a service to verify, no problem! */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Either way, no problem. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Add the first service */ + ret = rend_service_check_dir_and_add(services, mock_options, service_1, 0); + tt_assert(ret == 0); /* But don't add the second service yet. */ /* Service directories, but no previous keys, no problem! */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Either way, no problem. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Poison! Poison! Poison! * This can only be done in HiddenServiceSingleHopMode. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_poison_new_single_onion_dirs(services); + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); tt_assert(ret == 0); /* Poisoning twice is a no-op. */ - ret = rend_service_poison_new_single_onion_dirs(services); + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); tt_assert(ret == 0); /* Poisoned service directories, but no previous keys, no problem! */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Either way, no problem. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Now add some keys, and we'll have a problem. */ @@ -625,95 +670,121 @@ test_single_onion_poisoning(void *arg) /* Poisoned service directories with previous keys are not allowed. */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); /* But they are allowed if we're in non-anonymous mode. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Re-poisoning directories with existing keys is a no-op, because * directories with existing keys are ignored. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_poison_new_single_onion_dirs(services); + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); tt_assert(ret == 0); /* And it keeps the poison. */ - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Now add the second service: it has no key and no poison file */ - smartlist_add(services, service_2); + ret = rend_service_check_dir_and_add(services, mock_options, service_2, 0); + tt_assert(ret == 0); /* A new service, and an existing poisoned service. Not ok. */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); /* But ok to add in non-anonymous mode. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Now remove the poisoning from the first service, and we have the opposite * problem. */ - char *poison_path = rend_service_sos_poison_path(service_1); + poison_path = rend_service_sos_poison_path(service_1); + tt_assert(poison_path); ret = unlink(poison_path); - tor_free(poison_path); tt_assert(ret == 0); /* Unpoisoned service directories with previous keys are ok, as are empty * directories. */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* But the existing unpoisoned key is not ok in non-anonymous mode, even if * there is an empty service. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); /* Poisoning directories with existing keys is a no-op, because directories * with existing keys are ignored. But the new directory should poison. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_poison_new_single_onion_dirs(services); + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_poison_new_single_onion_dir(service_2, mock_options); tt_assert(ret == 0); /* And the old directory remains unpoisoned. */ - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); /* And the new directory should be ignored, because it has no key. */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Re-poisoning directories without existing keys is a no-op. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_poison_new_single_onion_dirs(services); + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_poison_new_single_onion_dir(service_2, mock_options); tt_assert(ret == 0); /* And the old directory remains unpoisoned. */ - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); done: /* The test harness deletes the directories at exit */ + tor_free(poison_path); + tor_free(dir1); + tor_free(dir2); + smartlist_free(services); rend_service_free(service_1); rend_service_free(service_2); - smartlist_free(services); UNMOCK(get_options); tor_free(mock_options->DataDirectory); - tor_free(dir1); - tor_free(dir2); } struct testcase_t hs_tests[] = { diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c index 8af5cabca3..9749c3b096 100644 --- a/src/test/test_hs_descriptor.c +++ b/src/test/test_hs_descriptor.c @@ -254,7 +254,7 @@ test_cert_encoding(void *arg) tt_assert(cert); /* Test the certificate encoding function. */ - ret = encode_cert(cert, &encoded); + ret = tor_cert_encode_ed22519(cert, &encoded); tt_int_op(ret, ==, 0); /* Validated the certificate string. */ diff --git a/src/test/test_oom.c b/src/test/test_oom.c index 6102af01f5..0f97972032 100644 --- a/src/test/test_oom.c +++ b/src/test/test_oom.c @@ -15,6 +15,7 @@ #include "config.h" #include "relay.h" #include "test.h" +#include "test_helpers.h" /* small replacement mock for circuit_mark_for_close_ to avoid doing all * the other bookkeeping that comes with marking circuits. */ @@ -58,24 +59,6 @@ dummy_or_circuit_new(int n_p_cells, int n_n_cells) return TO_CIRCUIT(circ); } -static circuit_t * -dummy_origin_circuit_new(int n_cells) -{ - origin_circuit_t *circ = origin_circuit_new(); - int i; - cell_t cell; - - for (i=0; i < n_cells; ++i) { - crypto_rand((void*)&cell, sizeof(cell)); - cell_queue_append_packed_copy(TO_CIRCUIT(circ), - &TO_CIRCUIT(circ)->n_chan_cells, - 1, &cell, 1, 0); - } - - TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - return TO_CIRCUIT(circ); -} - static void add_bytes_to_buf(buf_t *buf, size_t n_bytes) { diff --git a/src/test/test_options.c b/src/test/test_options.c index 0eada98cb2..e85e11805b 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -1050,7 +1050,7 @@ test_options_validate__transproxy(void *ignored) tt_int_op(ret, OP_EQ, -1); #ifndef KERNEL_MAY_SUPPORT_IPFW - tt_str_op(msg, OP_EQ, "ipfw is a FreeBSD-specificand OS X/Darwin-specific " + tt_str_op(msg, OP_EQ, "ipfw is a FreeBSD-specific and OS X/Darwin-specific " "feature."); #else tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_IPFW); @@ -1071,26 +1071,38 @@ test_options_validate__transproxy(void *ignored) free_options_test_data(tdata); tdata = NULL; -#if defined(linux) +#if defined(__linux__) tdata = get_options_test_data("TransProxyType tproxy\n" "TransPort 127.0.0.1:123\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); - tt_assert(!msg); -#endif -#if defined(__FreeBSD_kernel__) || defined( DARWIN ) || defined(__NetBSD__) + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } +#elif defined(KERNEL_MAY_SUPPORT_IPFW) tdata = get_options_test_data("TransProxyType ipfw\n" "TransPort 127.0.0.1:123\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); - tt_assert(!msg); -#endif -#if defined(__OpenBSD__) + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } +#elif defined(__OpenBSD__) tdata = get_options_test_data("TransProxyType pf-divert\n" "TransPort 127.0.0.1:123\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); - tt_assert(!msg); + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } +#elif defined(__NetBSD__) + tdata = get_options_test_data("TransProxyType default\n" + "TransPort 127.0.0.1:123\n"); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, -1); + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } #endif // Assert that a test has run for some TransProxyType diff --git a/src/test/test_util.c b/src/test/test_util.c index b74f658146..7e5a44cb59 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -5531,9 +5531,9 @@ test_util_monotonic_time(void *arg) tt_u64_op(msecc1, OP_GE, nsecc1 / 1000000); tt_u64_op(usecc1, OP_GE, nsecc1 / 1000); tt_u64_op(msec1, OP_LE, nsec1 / 1000000 + 1); - tt_u64_op(usec1, OP_LE, nsec1 / 1000 +10); + tt_u64_op(usec1, OP_LE, nsec1 / 1000 + 1000); tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 1); - tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 10); + tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 1000); done: ; |