diff options
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/fuzz/fuzz_address.c | 26 | ||||
-rw-r--r-- | src/test/fuzz/fuzz_addressPTR.c | 32 | ||||
-rw-r--r-- | src/test/fuzz/include.am | 64 | ||||
-rw-r--r-- | src/test/include.am | 25 | ||||
-rwxr-xr-x | src/test/ntor_v3_ref.py | 308 | ||||
-rw-r--r-- | src/test/test.c | 5 | ||||
-rw-r--r-- | src/test/test.h | 2 | ||||
-rw-r--r-- | src/test/test_address.c | 37 | ||||
-rw-r--r-- | src/test/test_address_set.c | 13 | ||||
-rw-r--r-- | src/test/test_channeltls.c | 3 | ||||
-rw-r--r-- | src/test/test_circuitbuild.c | 2 | ||||
-rw-r--r-- | src/test/test_circuitpadding.c | 6 | ||||
-rw-r--r-- | src/test/test_dir.c | 4 | ||||
-rw-r--r-- | src/test/test_entrynodes.c | 40 | ||||
-rw-r--r-- | src/test/test_hs_control.c | 2 | ||||
-rw-r--r-- | src/test/test_hs_ob.c | 1 | ||||
-rw-r--r-- | src/test/test_ntor_v3.c | 172 | ||||
-rw-r--r-- | src/test/test_process_descs.c | 14 | ||||
-rw-r--r-- | src/test/test_protover.c | 18 | ||||
-rw-r--r-- | src/test/test_relay.c | 4 | ||||
-rwxr-xr-x | src/test/test_rust.sh | 28 | ||||
-rw-r--r-- | src/test/test_sandbox.c | 348 | ||||
-rw-r--r-- | src/test/test_util.c | 37 |
23 files changed, 1100 insertions, 91 deletions
diff --git a/src/test/fuzz/fuzz_address.c b/src/test/fuzz/fuzz_address.c new file mode 100644 index 0000000000..6dccd65e9d --- /dev/null +++ b/src/test/fuzz/fuzz_address.c @@ -0,0 +1,26 @@ +#include "lib/net/address.h" +#include "lib/malloc/malloc.h" + +#include "test/fuzz/fuzzing.h" + +int +fuzz_init(void) +{ + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + tor_addr_t addr; + char *fuzzing_data = tor_memdup_nulterm(data, sz); + tor_addr_parse(&addr, fuzzing_data); + tor_free(fuzzing_data); + return 0; +} diff --git a/src/test/fuzz/fuzz_addressPTR.c b/src/test/fuzz/fuzz_addressPTR.c new file mode 100644 index 0000000000..b503d53666 --- /dev/null +++ b/src/test/fuzz/fuzz_addressPTR.c @@ -0,0 +1,32 @@ +#include "lib/net/address.h" +#include "lib/net/socket.h" +#include "lib/cc/ctassert.h" +#include "lib/container/smartlist.h" +#include "lib/ctime/di_ops.h" +#include "lib/log/log.h" +#include "lib/log/escape.h" +#include "lib/malloc/malloc.h" +#include "lib/net/address.h" +#include "test/fuzz/fuzzing.h" + +int +fuzz_init(void) +{ + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + tor_addr_t addr_result; + char *fuzzing_data = tor_memdup_nulterm(data, sz); + tor_addr_parse_PTR_name(&addr_result, fuzzing_data, AF_UNSPEC, 1); + tor_free(fuzzing_data); + return 0; +} diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am index 4943f7fffd..9fece7d004 100644 --- a/src/test/fuzz/include.am +++ b/src/test/fuzz/include.am @@ -8,7 +8,6 @@ FUZZING_LDFLAG = \ @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@ FUZZING_LIBS = \ src/test/libtor-testing.a \ - $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \ @@ -23,17 +22,36 @@ oss-fuzz-prereqs: \ noinst_HEADERS += \ src/test/fuzz/fuzzing.h -LIBFUZZER = -lFuzzer LIBFUZZER_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ LIBFUZZER_CFLAGS = $(FUZZING_CFLAGS) -LIBFUZZER_LDFLAG = $(FUZZING_LDFLAG) -LIBFUZZER_LIBS = $(FUZZING_LIBS) $(LIBFUZZER) -lstdc++ +LIBFUZZER_LDFLAG = $(FUZZING_LDFLAG) -fsanitize=fuzzer +LIBFUZZER_LIBS = $(FUZZING_LIBS) -lstdc++ LIBOSS_FUZZ_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ LIBOSS_FUZZ_CFLAGS = $(FUZZING_CFLAGS) # ===== AFL fuzzers if UNITTESTS_ENABLED +src_test_fuzz_fuzz_address_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_address.c +src_test_fuzz_fuzz_address_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_address_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_address_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_address_LDADD = $(FUZZING_LIBS) +endif + +if UNITTESTS_ENABLED +src_test_fuzz_fuzz_addressPTR_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_addressPTR.c +src_test_fuzz_fuzz_addressPTR_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_addressPTR_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_addressPTR_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_addressPTR_LDADD = $(FUZZING_LIBS) +endif + +if UNITTESTS_ENABLED src_test_fuzz_fuzz_consensus_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_consensus.c @@ -177,6 +195,8 @@ endif if UNITTESTS_ENABLED FUZZERS = \ + src/test/fuzz/fuzz-address \ + src/test/fuzz/fuzz-addressPTR \ src/test/fuzz/fuzz-consensus \ src/test/fuzz/fuzz-descriptor \ src/test/fuzz/fuzz-diff \ @@ -197,6 +217,24 @@ endif if LIBFUZZER_ENABLED if UNITTESTS_ENABLED +src_test_fuzz_lf_fuzz_address_SOURCES = \ + $(src_test_fuzz_fuzz_address_SOURCES) +src_test_fuzz_lf_fuzz_address_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_address_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_address_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_address_LDADD = $(LIBFUZZER_LIBS) +endif + +if UNITTESTS_ENABLED +src_test_fuzz_lf_fuzz_addressPTR_SOURCES = \ + $(src_test_fuzz_fuzz_addressPTR_SOURCES) +src_test_fuzz_lf_fuzz_addressPTR_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_addressPTR_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_addressPTR_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_addressPTR_LDADD = $(LIBFUZZER_LIBS) +endif + +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_consensus_SOURCES = \ $(src_test_fuzz_fuzz_consensus_SOURCES) src_test_fuzz_lf_fuzz_consensus_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) @@ -324,6 +362,8 @@ src_test_fuzz_lf_fuzz_vrs_LDADD = $(LIBFUZZER_LIBS) endif LIBFUZZER_FUZZERS = \ + src/test/fuzz/lf-fuzz-address \ + src/test/fuzz/lf-fuzz-addressPTR \ src/test/fuzz/lf-fuzz-consensus \ src/test/fuzz/lf-fuzz-descriptor \ src/test/fuzz/lf-fuzz-diff \ @@ -347,6 +387,20 @@ endif if OSS_FUZZ_ENABLED if UNITTESTS_ENABLED +src_test_fuzz_liboss_fuzz_address_a_SOURCES = \ + $(src_test_fuzz_fuzz_address_SOURCES) +src_test_fuzz_liboss_fuzz_address_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_address_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif + +if UNITTESTS_ENABLED +src_test_fuzz_liboss_fuzz_addressPTR_a_SOURCES = \ + $(src_test_fuzz_fuzz_addressPTR_SOURCES) +src_test_fuzz_liboss_fuzz_addressPTR_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_addressPTR_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif + +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_consensus_a_SOURCES = \ $(src_test_fuzz_fuzz_consensus_SOURCES) src_test_fuzz_liboss_fuzz_consensus_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) @@ -445,6 +499,8 @@ src_test_fuzz_liboss_fuzz_vrs_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) endif OSS_FUZZ_FUZZERS = \ + src/test/fuzz/liboss-fuzz-address.a \ + src/test/fuzz/liboss-fuzz-addressPTR.a \ src/test/fuzz/liboss-fuzz-consensus.a \ src/test/fuzz/liboss-fuzz-descriptor.a \ src/test/fuzz/liboss-fuzz-diff.a \ diff --git a/src/test/include.am b/src/test/include.am index b5e121d1ad..2765cf27d0 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -8,11 +8,7 @@ TESTS_ENVIRONMENT = \ export abs_top_builddir="$(abs_top_builddir)"; \ export builddir="$(builddir)"; \ export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; \ - export CARGO="$(CARGO)"; \ - export EXTRA_CARGO_OPTIONS="$(EXTRA_CARGO_OPTIONS)"; \ - export CARGO_ONLINE="$(CARGO_ONLINE)"; \ - export CCLD="$(CCLD)"; \ - export RUSTFLAGS="-C linker=`echo '$(CC)' | cut -d' ' -f 1` $(RUST_LINKER_OPTIONS)"; + export CCLD="$(CCLD)"; TESTSCRIPTS = \ src/test/fuzz_static_testcases.sh \ @@ -35,11 +31,6 @@ TESTSCRIPTS = \ src/test/unittest_part7.sh \ src/test/unittest_part8.sh -if USE_RUST -TESTSCRIPTS += \ - src/test/test_rust.sh -endif - if USEPYTHON TESTSCRIPTS += \ src/test/test_ntor.sh \ @@ -204,6 +195,7 @@ src_test_test_SOURCES += \ src/test/test_namemap.c \ src/test/test_netinfo.c \ src/test/test_nodelist.c \ + src/test/test_ntor_v3.c \ src/test/test_oom.c \ src/test/test_oos.c \ src/test/test_options.c \ @@ -230,6 +222,7 @@ src_test_test_SOURCES += \ src/test/test_routerkeys.c \ src/test/test_routerlist.c \ src/test/test_routerset.c \ + src/test/test_sandbox.c \ src/test/test_scheduler.c \ src/test/test_sendme.c \ src/test/test_shared_random.c \ @@ -304,7 +297,6 @@ src_test_test_switch_id_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_test_test_switch_id_LDFLAGS = @TOR_LDFLAGS_zlib@ src_test_test_switch_id_LDADD = \ $(TOR_UTIL_TESTING_LIBS) \ - $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_USERENV@ \ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@ @@ -312,7 +304,6 @@ src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \ @TOR_LDFLAGS_libevent@ src_test_test_LDADD = \ src/test/libtor-testing.a \ - $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ @@ -341,7 +332,6 @@ src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \ @TOR_LDFLAGS_libevent@ src_test_bench_LDADD = \ libtor.a \ - $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ @@ -351,7 +341,6 @@ src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \ @TOR_LDFLAGS_libevent@ src_test_test_workqueue_LDADD = \ src/test/libtor-testing.a \ - $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ @@ -363,7 +352,6 @@ src_test_test_timers_LDADD = \ src/lib/libtor-evloop-testing.a \ $(TOR_CRYPTO_TESTING_LIBS) \ $(TOR_UTIL_TESTING_LIBS) \ - $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ @@ -399,7 +387,6 @@ src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) src_test_test_ntor_cl_LDADD = \ libtor.a \ - $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ @TOR_LZMA_LIBS@ @TOR_TRACE_LIBS@ @@ -422,7 +409,6 @@ noinst_PROGRAMS += src/test/test-bt-cl src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c src_test_test_bt_cl_LDADD = \ $(TOR_UTIL_TESTING_LIBS) \ - $(rust_ldadd) \ @TOR_LIB_MATH@ \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @TOR_TRACE_LIBS@ @@ -444,13 +430,11 @@ EXTRA_DIST += \ src/test/test_include.py \ src/test/zero_length_keys.sh \ scripts/maint/run_check_subsystem_order.sh \ - src/test/rust_supp.txt \ src/test/test_keygen.sh \ src/test/test_key_expiration.sh \ src/test/test_zero_length_keys.sh \ src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh \ src/test/test-network.sh \ - src/test/test_rust.sh \ src/test/test_switch_id.sh \ src/test/test_workqueue_cancel.sh \ src/test/test_workqueue_efd.sh \ @@ -468,6 +452,3 @@ EXTRA_DIST += \ src/test/unittest_part6.sh \ src/test/unittest_part7.sh \ src/test/unittest_part8.sh - -test-rust: - $(TESTS_ENVIRONMENT) "$(abs_top_srcdir)/src/test/test_rust.sh" diff --git a/src/test/ntor_v3_ref.py b/src/test/ntor_v3_ref.py new file mode 100755 index 0000000000..28bc077105 --- /dev/null +++ b/src/test/ntor_v3_ref.py @@ -0,0 +1,308 @@ +#!/usr/bin/python + +import binascii +import hashlib +import os +import struct + +import donna25519 +from Crypto.Cipher import AES +from Crypto.Util import Counter + +# Define basic wrappers. + +DIGEST_LEN = 32 +ENC_KEY_LEN = 32 +PUB_KEY_LEN = 32 +SEC_KEY_LEN = 32 +IDENTITY_LEN = 32 + +def sha3_256(s): + d = hashlib.sha3_256(s).digest() + assert len(d) == DIGEST_LEN + return d + +def shake_256(s): + # Note: In reality, you wouldn't want to generate more bytes than needed. + MAX_KEY_BYTES = 1024 + return hashlib.shake_256(s).digest(MAX_KEY_BYTES) + +def curve25519(pk, sk): + assert len(pk) == PUB_KEY_LEN + assert len(sk) == SEC_KEY_LEN + private = donna25519.PrivateKey.load(sk) + public = donna25519.PublicKey(pk) + return private.do_exchange(public) + +def keygen(): + private = donna25519.PrivateKey() + public = private.get_public() + return (private.private, public.public) + +def aes256_ctr(k, s): + assert len(k) == ENC_KEY_LEN + cipher = AES.new(k, AES.MODE_CTR, counter=Counter.new(128, initial_value=0)) + return cipher.encrypt(s) + +# Byte-oriented helper. We use this for decoding keystreams and messages. + +class ByteSeq: + def __init__(self, data): + self.data = data + + def take(self, n): + assert n <= len(self.data) + result = self.data[:n] + self.data = self.data[n:] + return result + + def exhausted(self): + return len(self.data) == 0 + + def remaining(self): + return len(self.data) + +# Low-level functions + +MAC_KEY_LEN = 32 +MAC_LEN = DIGEST_LEN + +hash_func = sha3_256 + +def encapsulate(s): + """encapsulate `s` with a length prefix. + + We use this whenever we need to avoid message ambiguities in + cryptographic inputs. + """ + assert len(s) <= 0xffffffff + header = b"\0\0\0\0" + struct.pack("!L", len(s)) + assert len(header) == 8 + return header + s + +def h(s, tweak): + return hash_func(encapsulate(tweak) + s) + +def mac(s, key, tweak): + return hash_func(encapsulate(tweak) + encapsulate(key) + s) + +def kdf(s, tweak): + data = shake_256(encapsulate(tweak) + s) + return ByteSeq(data) + +def enc(s, k): + return aes256_ctr(k, s) + +# Tweaked wrappers + +PROTOID = b"ntor3-curve25519-sha3_256-1" +T_KDF_PHASE1 = PROTOID + b":kdf_phase1" +T_MAC_PHASE1 = PROTOID + b":msg_mac" +T_KDF_FINAL = PROTOID + b":kdf_final" +T_KEY_SEED = PROTOID + b":key_seed" +T_VERIFY = PROTOID + b":verify" +T_AUTH = PROTOID + b":auth_final" + +def kdf_phase1(s): + return kdf(s, T_KDF_PHASE1) + +def kdf_final(s): + return kdf(s, T_KDF_FINAL) + +def mac_phase1(s, key): + return mac(s, key, T_MAC_PHASE1) + +def h_key_seed(s): + return h(s, T_KEY_SEED) + +def h_verify(s): + return h(s, T_VERIFY) + +def h_auth(s): + return h(s, T_AUTH) + +# Handshake. + +def client_phase1(msg, verification, B, ID): + assert len(B) == PUB_KEY_LEN + assert len(ID) == IDENTITY_LEN + + (x,X) = keygen() + p(["x", "X"], locals()) + p(["msg", "verification"], locals()) + Bx = curve25519(B, x) + secret_input_phase1 = Bx + ID + X + B + PROTOID + encapsulate(verification) + + phase1_keys = kdf_phase1(secret_input_phase1) + enc_key = phase1_keys.take(ENC_KEY_LEN) + mac_key = phase1_keys.take(MAC_KEY_LEN) + p(["enc_key", "mac_key"], locals()) + + msg_0 = ID + B + X + enc(msg, enc_key) + mac = mac_phase1(msg_0, mac_key) + p(["mac"], locals()) + + client_handshake = msg_0 + mac + state = dict(x=x, X=X, B=B, ID=ID, Bx=Bx, mac=mac, verification=verification) + + p(["client_handshake"], locals()) + + return (client_handshake, state) + +# server. + +class Reject(Exception): + pass + +def server_part1(cmsg, verification, b, B, ID): + assert len(B) == PUB_KEY_LEN + assert len(ID) == IDENTITY_LEN + assert len(b) == SEC_KEY_LEN + + if len(cmsg) < (IDENTITY_LEN + PUB_KEY_LEN * 2 + MAC_LEN): + raise Reject() + + mac_covered_portion = cmsg[0:-MAC_LEN] + cmsg = ByteSeq(cmsg) + cmsg_id = cmsg.take(IDENTITY_LEN) + cmsg_B = cmsg.take(PUB_KEY_LEN) + cmsg_X = cmsg.take(PUB_KEY_LEN) + cmsg_msg = cmsg.take(cmsg.remaining() - MAC_LEN) + cmsg_mac = cmsg.take(MAC_LEN) + + assert cmsg.exhausted() + + # XXXX for real purposes, you would use constant-time checks here + if cmsg_id != ID or cmsg_B != B: + raise Reject() + + Xb = curve25519(cmsg_X, b) + secret_input_phase1 = Xb + ID + cmsg_X + B + PROTOID + encapsulate(verification) + + phase1_keys = kdf_phase1(secret_input_phase1) + enc_key = phase1_keys.take(ENC_KEY_LEN) + mac_key = phase1_keys.take(MAC_KEY_LEN) + + mac_received = mac_phase1(mac_covered_portion, mac_key) + if mac_received != cmsg_mac: + raise Reject() + + client_msg = enc(cmsg_msg, enc_key) + state = dict( + b=b, + B=B, + X=cmsg_X, + mac_received=mac_received, + Xb=Xb, + ID=ID, + verification=verification) + + return (client_msg, state) + +def server_part2(state, server_msg): + X = state['X'] + Xb = state['Xb'] + B = state['B'] + b = state['b'] + ID = state['ID'] + mac_received = state['mac_received'] + verification = state['verification'] + + p(["server_msg"], locals()) + + (y,Y) = keygen() + p(["y", "Y"], locals()) + Xy = curve25519(X, y) + + secret_input = Xy + Xb + ID + B + X + Y + PROTOID + encapsulate(verification) + key_seed = h_key_seed(secret_input) + verify = h_verify(secret_input) + p(["key_seed", "verify"], locals()) + + keys = kdf_final(key_seed) + server_enc_key = keys.take(ENC_KEY_LEN) + p(["server_enc_key"], locals()) + + smsg_msg = enc(server_msg, server_enc_key) + + auth_input = verify + ID + B + Y + X + mac_received + encapsulate(smsg_msg) + PROTOID + b"Server" + + auth = h_auth(auth_input) + server_handshake = Y + auth + smsg_msg + p(["auth", "server_handshake"], locals()) + + return (server_handshake, keys) + +def client_phase2(state, smsg): + x = state['x'] + X = state['X'] + B = state['B'] + ID = state['ID'] + Bx = state['Bx'] + mac_sent = state['mac'] + verification = state['verification'] + + if len(smsg) < PUB_KEY_LEN + DIGEST_LEN: + raise Reject() + + smsg = ByteSeq(smsg) + Y = smsg.take(PUB_KEY_LEN) + auth_received = smsg.take(DIGEST_LEN) + server_msg = smsg.take(smsg.remaining()) + + Yx = curve25519(Y,x) + + secret_input = Yx + Bx + ID + B + X + Y + PROTOID + encapsulate(verification) + key_seed = h_key_seed(secret_input) + verify = h_verify(secret_input) + + auth_input = verify + ID + B + Y + X + mac_sent + encapsulate(server_msg) + PROTOID + b"Server" + + auth = h_auth(auth_input) + if auth != auth_received: + raise Reject() + + keys = kdf_final(key_seed) + enc_key = keys.take(ENC_KEY_LEN) + + server_msg_decrypted = enc(server_msg, enc_key) + + return (keys, server_msg_decrypted) + +def p(varnames, localvars): + for v in varnames: + label = v + val = localvars[label] + print('{} = "{}"'.format(label, binascii.b2a_hex(val).decode("ascii"))) + +def test(): + (b,B) = keygen() + ID = os.urandom(IDENTITY_LEN) + + p(["b", "B", "ID"], locals()) + + print("# ============") + (c_handshake, c_state) = client_phase1(b"hello world", b"xyzzy", B, ID) + + print("# ============") + + (c_msg_got, s_state) = server_part1(c_handshake, b"xyzzy", b, B, ID) + + #print(repr(c_msg_got)) + + (s_handshake, s_keys) = server_part2(s_state, b"Hola Mundo") + + print("# ============") + + (c_keys, s_msg_got) = client_phase2(c_state, s_handshake) + + #print(repr(s_msg_got)) + + c_keys_256 = c_keys.take(256) + p(["c_keys_256"], locals()) + + assert (c_keys_256 == s_keys.take(256)) + + +if __name__ == '__main__': + test() diff --git a/src/test/test.c b/src/test/test.c index 9543a24376..c38d78da30 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -52,6 +52,7 @@ #include "core/crypto/onion_fast.h" #include "core/crypto/onion_tap.h" #include "core/or/policies.h" +#include "lib/sandbox/sandbox.h" #include "app/config/statefile.h" #include "lib/crypt_ops/crypto_curve25519.h" #include "feature/nodelist/networkstatus.h" @@ -707,6 +708,7 @@ struct testgroup_t testgroups[] = { { "netinfo/", netinfo_tests }, { "nodelist/", nodelist_tests }, { "oom/", oom_tests }, + { "onion-handshake/ntor-v3/", ntor_v3_tests }, { "oos/", oos_tests }, { "options/", options_tests }, { "options/act/", options_act_tests }, @@ -731,6 +733,9 @@ struct testgroup_t testgroups[] = { { "routerkeys/", routerkeys_tests }, { "routerlist/", routerlist_tests }, { "routerset/" , routerset_tests }, +#ifdef USE_LIBSECCOMP + { "sandbox/" , sandbox_tests }, +#endif { "scheduler/", scheduler_tests }, { "sendme/", sendme_tests }, { "shared-random/", sr_tests }, diff --git a/src/test/test.h b/src/test/test.h index 700aa70a4b..e17bce427c 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -155,6 +155,7 @@ extern struct testcase_t microdesc_tests[]; extern struct testcase_t namemap_tests[]; extern struct testcase_t netinfo_tests[]; extern struct testcase_t nodelist_tests[]; +extern struct testcase_t ntor_v3_tests[]; extern struct testcase_t oom_tests[]; extern struct testcase_t oos_tests[]; extern struct testcase_t options_tests[]; @@ -183,6 +184,7 @@ extern struct testcase_t router_tests[]; extern struct testcase_t routerkeys_tests[]; extern struct testcase_t routerlist_tests[]; extern struct testcase_t routerset_tests[]; +extern struct testcase_t sandbox_tests[]; extern struct testcase_t scheduler_tests[]; extern struct testcase_t sendme_tests[]; extern struct testcase_t socks_tests[]; diff --git a/src/test/test_address.c b/src/test/test_address.c index 9c1415419c..015ca0807c 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -1326,6 +1326,42 @@ test_address_dirserv_router_addr_private(void *opt_dir_allow_private) UNMOCK(get_options); } +static void +test_address_parse_port_range(void *arg) +{ + int ret; + uint16_t min_out = 0; + uint16_t max_out = 0; + + (void) arg; + + /* Invalid. */ + ret = parse_port_range("0x00", &min_out, &max_out); + tt_int_op(ret, OP_EQ, -1); + ret = parse_port_range("0x01", &min_out, &max_out); + tt_int_op(ret, OP_EQ, -1); + ret = parse_port_range("1817161", &min_out, &max_out); + tt_int_op(ret, OP_EQ, -1); + ret = parse_port_range("65536", &min_out, &max_out); + tt_int_op(ret, OP_EQ, -1); + ret = parse_port_range("1-65536", &min_out, &max_out); + tt_int_op(ret, OP_EQ, -1); + + /* Valid. */ + ret = parse_port_range("65535", &min_out, &max_out); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(min_out, OP_EQ, 65535); + tt_int_op(max_out, OP_EQ, 65535); + + ret = parse_port_range("1-65535", &min_out, &max_out); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(min_out, OP_EQ, 1); + tt_int_op(max_out, OP_EQ, 65535); + + done: + ; +} + #define ADDRESS_TEST(name, flags) \ { #name, test_address_ ## name, flags, NULL, NULL } #define ADDRESS_TEST_STR_ARG(name, flags, str_arg) \ @@ -1364,5 +1400,6 @@ struct testcase_t address_tests[] = { ADDRESS_TEST(tor_node_in_same_network_family, 0), ADDRESS_TEST(dirserv_router_addr_private, 0), ADDRESS_TEST_STR_ARG(dirserv_router_addr_private, 0, "allow_private"), + ADDRESS_TEST(parse_port_range, 0), END_OF_TESTCASES }; diff --git a/src/test/test_address_set.c b/src/test/test_address_set.c index 1aacc58c4f..6860906791 100644 --- a/src/test/test_address_set.c +++ b/src/test/test_address_set.c @@ -17,6 +17,7 @@ #include "feature/nodelist/routerstatus_st.h" #include "test/test.h" +#include "test/rng_test_helpers.h" static networkstatus_t *dummy_ns = NULL; static networkstatus_t * @@ -62,6 +63,10 @@ test_contains(void *arg) tor_addr_parse(&addr_v4, "42.42.42.42"); uint32_t ipv4h = tor_addr_to_ipv4h(&addr_v4); + /* Use our deterministic RNG since the address set uses a bloom filter + * internally. */ + testing_enable_deterministic_rng(); + /* Make it very big so the chance of failing the contain test will be * extremely rare. */ set = address_set_new(1024); @@ -89,6 +94,8 @@ test_contains(void *arg) done: address_set_free(set); + + testing_disable_deterministic_rng(); } static void @@ -108,6 +115,10 @@ test_nodelist(void *arg) MOCK(dirlist_add_trusted_dir_addresses, mock_dirlist_add_trusted_dir_addresses); + /* Use our deterministic RNG since the address set, used for + * nodelist_probably_contains_address() uses a bloom filter internally. */ + testing_enable_deterministic_rng(); + dummy_ns = tor_malloc_zero(sizeof(*dummy_ns)); dummy_ns->flavor = FLAV_MICRODESC; dummy_ns->routerstatus_list = smartlist_new(); @@ -179,6 +190,8 @@ test_nodelist(void *arg) UNMOCK(networkstatus_get_latest_consensus_by_flavor); UNMOCK(get_estimated_address_per_node); UNMOCK(dirlist_add_trusted_dir_addresses); + + testing_disable_deterministic_rng(); } /** Test that the no-reentry exit filter works as intended */ diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c index 5219c86097..ca7fee2c53 100644 --- a/src/test/test_channeltls.c +++ b/src/test/test_channeltls.c @@ -20,6 +20,7 @@ #include "lib/tls/tortls.h" #include "core/or/or_connection_st.h" +#include "core/or/congestion_control_common.h" /* Test suite stuff */ #include "test/test.h" @@ -155,7 +156,7 @@ test_channeltls_num_bytes_queued(void *arg) * - 2 cells. */ n = ch->num_cells_writeable(ch); - tt_int_op(n, OP_EQ, CEIL_DIV(OR_CONN_HIGHWATER, 512) - 2); + tt_int_op(n, OP_EQ, CEIL_DIV(or_conn_highwatermark(), 512) - 2); UNMOCK(buf_datalen); tlschan_buf_datalen_mock_target = NULL; tlschan_buf_datalen_mock_size = 0; diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 873391a84f..0a5c3530bd 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -113,7 +113,7 @@ test_new_route_len_safe_exit(void *arg) /* hidden service connecting to introduction point */ r = new_route_len(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, &dummy_ei, &dummy_nodes); - tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + tt_int_op(DEFAULT_ROUTE_LEN+1, OP_EQ, r); /* router testing its own reachability */ r = new_route_len(CIRCUIT_PURPOSE_TESTING, &dummy_ei, &dummy_nodes); diff --git a/src/test/test_circuitpadding.c b/src/test/test_circuitpadding.c index 86baf54f40..6ced3f4111 100644 --- a/src/test/test_circuitpadding.c +++ b/src/test/test_circuitpadding.c @@ -1367,7 +1367,7 @@ test_circuitpadding_wronghop(void *arg) tt_ptr_op(client_side->padding_info[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL); - tt_int_op(n_relay_cells, OP_EQ, 3); + tt_int_op(n_relay_cells, OP_EQ, 2); tt_int_op(n_client_cells, OP_EQ, 2); /* 6. Sending negotiated command to relay does nothing */ @@ -1396,11 +1396,9 @@ test_circuitpadding_wronghop(void *arg) /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); - tt_int_op(n_relay_cells, OP_EQ, 3); - tt_int_op(n_client_cells, OP_EQ, 2); /* verify no echo was sent */ - tt_int_op(n_relay_cells, OP_EQ, 3); + tt_int_op(n_relay_cells, OP_EQ, 2); tt_int_op(n_client_cells, OP_EQ, 2); /* Finish circuit */ diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 9624a9fdc4..0d2d6800ba 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -2135,8 +2135,8 @@ test_dir_measured_bw_kb(void *arg) /* Test that a line with vote=0 will fail too, so that it is ignored. */ "node_id=$557365204145532d32353620696e73746561642e bw=1024 vote=0\n", /* Test that a line with vote=0 will fail even if unmeasured=0. */ - "node_id=$557365204145532d32353620696e73746561642e bw=1024 vote=0 " - "unmeasured=0\n", + ("node_id=$557365204145532d32353620696e73746561642e bw=1024 vote=0 " + "unmeasured=0\n"), "end" }; diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index c94b5d6a23..118b66dfa7 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -92,6 +92,12 @@ bfn_mock_node_get_by_id(const char *id) return NULL; } +static int +mock_router_have_minimum_dir_info(void) +{ + return 1; +} + /* Helper function to free a test node. */ static void test_node_free(node_t *n) @@ -3087,6 +3093,38 @@ test_entry_guard_vanguard_path_selection(void *arg) circuit_free_(circ); } +static void +test_entry_guard_layer2_guards(void *arg) +{ + (void) arg; + MOCK(router_have_minimum_dir_info, mock_router_have_minimum_dir_info); + + /* First check the enable/disable switch */ + get_options_mutable()->VanguardsLiteEnabled = 0; + tt_int_op(vanguards_lite_is_enabled(), OP_EQ, 0); + + get_options_mutable()->VanguardsLiteEnabled = 1; + tt_int_op(vanguards_lite_is_enabled(), OP_EQ, 1); + + get_options_mutable()->VanguardsLiteEnabled = -1; + tt_int_op(vanguards_lite_is_enabled(), OP_EQ, 1); + + /* OK now let's move to actual testing */ + + /* Remove restrictions to route around Big Fake Network restrictions */ + get_options_mutable()->EnforceDistinctSubnets = 0; + + /* Create the L2 guardset */ + maintain_layer2_guards(); + + const routerset_t *l2_guards = get_layer2_guards(); + tt_assert(l2_guards); + tt_int_op(routerset_len(l2_guards), OP_EQ, 4); + + done: + UNMOCK(router_have_minimum_dir_info); +} + static const struct testcase_setup_t big_fake_network = { big_fake_network_setup, big_fake_network_cleanup }; @@ -3152,6 +3190,8 @@ struct testcase_t entrynodes_tests[] = { BFN_TEST(manage_primary), BFN_TEST(correct_cascading_order), + BFN_TEST(layer2_guards), + EN_TEST_FORK(guard_preferred), BFN_TEST(select_for_circuit_no_confirmed), diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c index b036c5eada..c32803b380 100644 --- a/src/test/test_hs_control.c +++ b/src/test/test_hs_control.c @@ -798,7 +798,7 @@ test_hs_control_add_onion_helper_add_service(void *arg) hs_service_ht *global_map; hs_port_config_t *portcfg; smartlist_t *portcfgs; - char *address_out_good, *address_out_bad; + char *address_out_good = NULL, *address_out_bad = NULL; hs_service_t *service_good = NULL; hs_service_t *service_bad = NULL; diff --git a/src/test/test_hs_ob.c b/src/test/test_hs_ob.c index 3485655c2e..2f69bf31e0 100644 --- a/src/test/test_hs_ob.c +++ b/src/test/test_hs_ob.c @@ -174,6 +174,7 @@ test_get_subcredentials(void *arg) hs_subcredential_t *subcreds = NULL; (void) arg; + memset(&config, 0, sizeof(config)); MOCK(networkstatus_get_live_consensus, mock_networkstatus_get_live_consensus); diff --git a/src/test/test_ntor_v3.c b/src/test/test_ntor_v3.c new file mode 100644 index 0000000000..096ac6668f --- /dev/null +++ b/src/test/test_ntor_v3.c @@ -0,0 +1,172 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2021, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#define ONION_NTOR_V3_PRIVATE +#include "core/or/or.h" +#include "test/test.h" +#include "lib/crypt_ops/crypto_curve25519.h" +#include "lib/crypt_ops/crypto_ed25519.h" +#include "core/crypto/onion_ntor_v3.h" + +#define unhex(arry, s) \ + { tt_int_op(sizeof(arry), OP_EQ, \ + base16_decode((char*)arry, sizeof(arry), s, strlen(s))); \ + } + +static void +test_ntor3_testvecs(void *arg) +{ + (void)arg; + char *mem_op_hex_tmp = NULL; // temp val to make test_memeq_hex work. + + ntor3_server_handshake_state_t *relay_state = NULL; + uint8_t *onion_skin = NULL; + size_t onion_skin_len; + ntor3_handshake_state_t *client_state = NULL; + uint8_t *cm = NULL, *sm = NULL; + size_t cm_len, sm_len; + di_digest256_map_t *private_keys = NULL; + uint8_t *server_handshake = NULL; + size_t server_handshake_len; + + // Test vectors from python implementation, confirmed with rust + // implementation. + curve25519_keypair_t relay_keypair_b; + curve25519_keypair_t client_keypair_x; + curve25519_keypair_t relay_keypair_y; + ed25519_public_key_t relay_id; + + unhex(relay_keypair_b.seckey.secret_key, + "4051daa5921cfa2a1c27b08451324919538e79e788a81b38cbed097a5dff454a"); + unhex(relay_keypair_b.pubkey.public_key, + "f8307a2bc1870b00b828bb74dbb8fd88e632a6375ab3bcd1ae706aaa8b6cdd1d"); + unhex(relay_id.pubkey, + "9fad2af287ef942632833d21f946c6260c33fae6172b60006e86e4a6911753a2"); + unhex(client_keypair_x.seckey.secret_key, + "b825a3719147bcbe5fb1d0b0fcb9c09e51948048e2e3283d2ab7b45b5ef38b49"); + unhex(client_keypair_x.pubkey.public_key, + "252fe9ae91264c91d4ecb8501f79d0387e34ad8ca0f7c995184f7d11d5da4f46"); + unhex(relay_keypair_y.seckey.secret_key, + "4865a5b7689dafd978f529291c7171bc159be076b92186405d13220b80e2a053"); + unhex(relay_keypair_y.pubkey.public_key, + "4bf4814326fdab45ad5184f5518bd7fae25dc59374062698201a50a22954246d"); + + uint8_t client_message[11]; + uint8_t verification[5]; + unhex(client_message, "68656c6c6f20776f726c64"); + unhex(verification, "78797a7a79"); + + // ========= Client handshake 1. + + onion_skin_ntor3_create_nokeygen( + &client_keypair_x, + &relay_id, + &relay_keypair_b.pubkey, + verification, + sizeof(verification), + client_message, + sizeof(client_message), + &client_state, + &onion_skin, + &onion_skin_len); + + const char expect_client_handshake[] = "9fad2af287ef942632833d21f946c6260c" + "33fae6172b60006e86e4a6911753a2f8307a2bc1870b00b828bb74dbb8fd88e632a6375" + "ab3bcd1ae706aaa8b6cdd1d252fe9ae91264c91d4ecb8501f79d0387e34ad8ca0f7c995" + "184f7d11d5da4f463bebd9151fd3b47c180abc9e044d53565f04d82bbb3bebed3d06cea" + "65db8be9c72b68cd461942088502f67"; + + tt_int_op(onion_skin_len, OP_EQ, strlen(expect_client_handshake)/2); + test_memeq_hex(onion_skin, expect_client_handshake); + + // ========= Relay handshake. + + dimap_add_entry(&private_keys, + relay_keypair_b.pubkey.public_key, + &relay_keypair_b); + + int r = onion_skin_ntor3_server_handshake_part1( + private_keys, + &client_keypair_x, + &relay_id, + onion_skin, + onion_skin_len, + verification, + sizeof(verification), + &cm, + &cm_len, + &relay_state); + tt_int_op(r, OP_EQ, 0); + tt_int_op(cm_len, OP_EQ, sizeof(client_message)); + tt_mem_op(cm, OP_EQ, client_message, cm_len); + + uint8_t server_message[10]; + unhex(server_message, "486f6c61204d756e646f"); + + uint8_t server_keys[256]; + onion_skin_ntor3_server_handshake_part2_nokeygen( + &relay_keypair_y, + relay_state, + verification, + sizeof(verification), + server_message, + sizeof(server_message), + &server_handshake, + &server_handshake_len, + server_keys, + sizeof(server_keys)); + + const char expect_server_handshake[] = "4bf4814326fdab45ad5184f5518bd7fae25" + "dc59374062698201a50a22954246d2fc5f8773ca824542bc6cf6f57c7c29bbf4e5476461" + "ab130c5b18ab0a91276651202c3e1e87c0d32054c"; + tt_int_op(server_handshake_len, OP_EQ, strlen(expect_server_handshake)/2); + test_memeq_hex(server_handshake, expect_server_handshake); + + uint8_t expect_keys[256]; + unhex(expect_keys, "9c19b631fd94ed86a817e01f6c80b0743a43f5faebd39cfaa8b00f" + "a8bcc65c3bfeaa403d91acbd68a821bf6ee8504602b094a254392a07737d5662768" + "c7a9fb1b2814bb34780eaee6e867c773e28c212ead563e98a1cd5d5b4576f5ee61c" + "59bde025ff2851bb19b721421694f263818e3531e43a9e4e3e2c661e2ad547d8984" + "caa28ebecd3e4525452299be26b9185a20a90ce1eac20a91f2832d731b54502b097" + "49b5a2a2949292f8cfcbeffb790c7790ed935a9d251e7e336148ea83b063a5618fc" + "ff674a44581585fd22077ca0e52c59a24347a38d1a1ceebddbf238541f226b8f88d" + "0fb9c07a1bcd2ea764bbbb5dacdaf5312a14c0b9e4f06309b0333b4a"); + tt_mem_op(server_keys, OP_EQ, expect_keys, 256); + + // ===== Client handshake 2 + + uint8_t client_keys[256]; + r = onion_ntor3_client_handshake( + client_state, + server_handshake, + server_handshake_len, + verification, + sizeof(verification), + client_keys, + sizeof(client_keys), + &sm, + &sm_len); + + tt_int_op(r, OP_EQ, 0); + tt_int_op(sm_len, OP_EQ, sizeof(server_message)); + tt_mem_op(sm, OP_EQ, server_message, sizeof(server_message)); + tt_mem_op(client_keys, OP_EQ, server_keys, 256); + + done: + tor_free(onion_skin); + tor_free(server_handshake); + tor_free(mem_op_hex_tmp); + ntor3_handshake_state_free(client_state); + ntor3_server_handshake_state_free(relay_state); + tor_free(cm); + tor_free(sm); + dimap_free(private_keys, NULL); +} + +struct testcase_t ntor_v3_tests[] = { + { "testvecs", test_ntor3_testvecs, 0, NULL, NULL, }, + END_OF_TESTCASES, +}; diff --git a/src/test/test_process_descs.c b/src/test/test_process_descs.c index fa2657f6c2..5503fc69ee 100644 --- a/src/test/test_process_descs.c +++ b/src/test/test_process_descs.c @@ -39,12 +39,20 @@ test_process_descs_versions(void *arg) { "Tor 0.4.1.1-alpha", true }, { "Tor 0.4.1.4-rc", true }, { "Tor 0.4.1.5", true }, + { "Tor 0.4.2.1-alpha", true }, + { "Tor 0.4.2.4-rc", true }, + { "Tor 0.4.2.5", true }, + { "Tor 0.4.3.0-alpha-dev", true }, + { "Tor 0.4.3.8", true }, + { "Tor 0.4.4.9", true }, + { "Tor 0.4.5.5-rc", true }, // new enough to be supported { "Tor 0.3.5.7", false }, { "Tor 0.3.5.8", false }, - { "Tor 0.4.2.1-alpha", false }, - { "Tor 0.4.2.4-rc", false }, - { "Tor 0.4.3.0-alpha-dev", false }, + { "Tor 0.4.5.6", false }, + { "Tor 0.4.6.0-alpha-dev", false }, + { "Tor 0.4.6.5", false }, + { "Tor 0.4.7.0-alpha-dev", false }, // Very far in the future { "Tor 100.100.1.5", false }, }; diff --git a/src/test/test_protover.c b/src/test/test_protover.c index 2f77db185f..205b39a9c7 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -23,13 +23,6 @@ static void test_protover_parse(void *arg) { (void) arg; -#ifdef HAVE_RUST - /** This test is disabled on rust builds, because it only exists to test - * internal C functions. */ - tt_skip(); - done: - ; -#else /* !defined(HAVE_RUST) */ char *re_encoded = NULL; const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16"; @@ -64,18 +57,12 @@ test_protover_parse(void *arg) SMARTLIST_FOREACH(elts, proto_entry_t *, ent, proto_entry_free(ent)); smartlist_free(elts); tor_free(re_encoded); -#endif /* defined(HAVE_RUST) */ } static void test_protover_parse_fail(void *arg) { (void)arg; -#ifdef HAVE_RUST - /** This test is disabled on rust builds, because it only exists to test - * internal C functions. */ - tt_skip(); -#else smartlist_t *elts; /* random junk */ @@ -108,7 +95,6 @@ test_protover_parse_fail(void *arg) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); tt_ptr_op(elts, OP_EQ, NULL); -#endif /* defined(HAVE_RUST) */ done: ; } @@ -265,7 +251,7 @@ test_protover_all_supported(void *arg) #endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* Protocol name too long */ -#if !defined(HAVE_RUST) && !defined(ALL_BUGS_ARE_FATAL) +#if !defined(ALL_BUGS_ARE_FATAL) tor_capture_bugs_(1); tt_assert(protover_all_supported( "DoSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" @@ -273,7 +259,7 @@ test_protover_all_supported(void *arg) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaa=1-65536", &msg)); tor_end_capture_bugs_(); -#endif /* !defined(HAVE_RUST) && !defined(ALL_BUGS_ARE_FATAL) */ +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ done: tor_end_capture_bugs_(); diff --git a/src/test/test_relay.c b/src/test/test_relay.c index 7338340e25..dbedc021e4 100644 --- a/src/test/test_relay.c +++ b/src/test/test_relay.c @@ -98,7 +98,7 @@ test_relay_close_circuit(void *arg) tt_int_op(new_count, OP_EQ, old_count + 1); /* Ensure our write totals are 0 */ - tt_u64_op(find_largest_max(write_array), OP_EQ, 0); + tt_u64_op(find_largest_max(write_array, 86400), OP_EQ, 0); /* Mark the circuit for close */ circuit_mark_for_close(TO_CIRCUIT(orcirc), 0); @@ -107,7 +107,7 @@ test_relay_close_circuit(void *arg) advance_obs(write_array); commit_max(write_array); /* Check for two cells plus overhead */ - tt_u64_op(find_largest_max(write_array), OP_EQ, + tt_u64_op(find_largest_max(write_array, 86400), OP_EQ, 2*(get_cell_network_size(nchan->wide_circ_ids) +TLS_PER_CELL_OVERHEAD)); diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh deleted file mode 100755 index 804d2ada36..0000000000 --- a/src/test/test_rust.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -# Test all Rust crates - -set -e - -export LSAN_OPTIONS=suppressions=${abs_top_srcdir:-../../..}/src/test/rust_supp.txt - -# When testing Cargo we pass a number of very specific linker flags down -# through Cargo. We do not, however, want these flags to affect things like -# build scripts, only the tests that we're compiling. To ensure this happens -# we unconditionally pass `--target` into Cargo, ensuring that `RUSTFLAGS` in -# the environment won't make their way into build scripts. -rustc_host=$(rustc -vV | grep host | sed 's/host: //') - -for cargo_toml_dir in "${abs_top_srcdir:-../../..}"/src/rust/*; do - if [ -e "${cargo_toml_dir}/Cargo.toml" ]; then - # shellcheck disable=SC2086 - cd "${abs_top_builddir:-../../..}/src/rust" && \ - CARGO_TARGET_DIR="${abs_top_builddir:-../../..}/src/rust/target" \ - "${CARGO:-cargo}" test "${CARGO_ONLINE-'--frozen'}" \ - --features "test_linking_hack" \ - --target "$rustc_host" \ - ${EXTRA_CARGO_OPTIONS} \ - --manifest-path "${cargo_toml_dir}/Cargo.toml" || exitcode=1 - fi -done - -exit $exitcode diff --git a/src/test/test_sandbox.c b/src/test/test_sandbox.c new file mode 100644 index 0000000000..e5064c58ec --- /dev/null +++ b/src/test/test_sandbox.c @@ -0,0 +1,348 @@ +/* Copyright (c) 2021, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef _LARGEFILE64_SOURCE +/** + * Temporarily required for O_LARGEFILE flag. Needs to be removed + * with the libevent fix. + */ +#define _LARGEFILE64_SOURCE +#endif /* !defined(_LARGEFILE64_SOURCE) */ + +#include "orconfig.h" + +#include "lib/sandbox/sandbox.h" + +#ifdef USE_LIBSECCOMP + +#include <dirent.h> +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "core/or/or.h" + +#include "test/test.h" +#include "test/log_test_helpers.h" + +typedef struct { + sandbox_cfg_t *cfg; + + char *file_ops_allowed; + char *file_ops_blocked; + + char *file_rename_target_allowed; + + char *dir_ops_allowed; + char *dir_ops_blocked; +} sandbox_data_t; + +/* All tests are skipped when coverage support is enabled (see further below) + * as the sandbox interferes with the use of gcov. Prevent a compiler warning + * by omitting these definitions in that case. */ +#ifndef ENABLE_COVERAGE +static void * +setup_sandbox(const struct testcase_t *testcase) +{ + sandbox_data_t *data = tor_malloc_zero(sizeof(*data)); + + (void)testcase; + + /* Establish common file and directory names within the test suite's + * temporary directory. */ + data->file_ops_allowed = tor_strdup(get_fname("file_ops_allowed")); + data->file_ops_blocked = tor_strdup(get_fname("file_ops_blocked")); + + data->file_rename_target_allowed = + tor_strdup(get_fname("file_rename_target_allowed")); + + data->dir_ops_allowed = tor_strdup(get_fname("dir_ops_allowed")); + data->dir_ops_blocked = tor_strdup(get_fname("dir_ops_blocked")); + + /* Create the corresponding filesystem objects. */ + creat(data->file_ops_allowed, S_IRWXU); + creat(data->file_ops_blocked, S_IRWXU); + mkdir(data->dir_ops_allowed, S_IRWXU); + mkdir(data->dir_ops_blocked, S_IRWXU); + + /* Create the sandbox configuration. */ + data->cfg = sandbox_cfg_new(); + + sandbox_cfg_allow_open_filename(&data->cfg, + tor_strdup(data->file_ops_allowed)); + sandbox_cfg_allow_open_filename(&data->cfg, + tor_strdup(data->dir_ops_allowed)); + + sandbox_cfg_allow_chmod_filename(&data->cfg, + tor_strdup(data->file_ops_allowed)); + sandbox_cfg_allow_chmod_filename(&data->cfg, + tor_strdup(data->dir_ops_allowed)); + sandbox_cfg_allow_chown_filename(&data->cfg, + tor_strdup(data->file_ops_allowed)); + sandbox_cfg_allow_chown_filename(&data->cfg, + tor_strdup(data->dir_ops_allowed)); + + sandbox_cfg_allow_rename(&data->cfg, tor_strdup(data->file_ops_allowed), + tor_strdup(data->file_rename_target_allowed)); + + sandbox_cfg_allow_openat_filename(&data->cfg, + tor_strdup(data->dir_ops_allowed)); + + sandbox_cfg_allow_opendir_dirname(&data->cfg, + tor_strdup(data->dir_ops_allowed)); + + sandbox_cfg_allow_stat_filename(&data->cfg, + tor_strdup(data->file_ops_allowed)); + sandbox_cfg_allow_stat_filename(&data->cfg, + tor_strdup(data->dir_ops_allowed)); + + /* Activate the sandbox, which will remain in effect until the process + * terminates. */ + sandbox_init(data->cfg); + + return data; +} + +static int +cleanup_sandbox(const struct testcase_t *testcase, void *data_) +{ + sandbox_data_t *data = data_; + + (void)testcase; + + tor_free(data->dir_ops_blocked); + tor_free(data->dir_ops_allowed); + tor_free(data->file_rename_target_allowed); + tor_free(data->file_ops_blocked); + tor_free(data->file_ops_allowed); + + tor_free(data); + + return 1; +} + +static const struct testcase_setup_t sandboxed_testcase_setup = { + .setup_fn = setup_sandbox, + .cleanup_fn = cleanup_sandbox +}; +#endif /* !defined(ENABLE_COVERAGE) */ + +static void +test_sandbox_is_active(void *ignored) +{ + (void)ignored; + + tt_assert(!sandbox_is_active()); + + sandbox_init(sandbox_cfg_new()); + tt_assert(sandbox_is_active()); + + done: + (void)0; +} + +static void +test_sandbox_open_filename(void *arg) +{ + sandbox_data_t *data = arg; + int fd, errsv; + + fd = open(sandbox_intern_string(data->file_ops_allowed), O_RDONLY); + if (fd == -1) + tt_abort_perror("open"); + close(fd); + + /* It might be nice to use sandbox_intern_string() in the line below as well + * (and likewise in the test cases that follow) but this would require + * capturing the warning message it logs, and the mechanism for doing so + * relies on system calls that are normally blocked by the sandbox and may + * vary across architectures. */ + fd = open(data->file_ops_blocked, O_RDONLY); + errsv = errno; + tt_int_op(fd, OP_EQ, -1); + tt_int_op(errsv, OP_EQ, EPERM); + + done: + if (fd >= 0) + close(fd); +} + +static void +test_sandbox_chmod_filename(void *arg) +{ + sandbox_data_t *data = arg; + int rc, errsv; + + if (chmod(sandbox_intern_string(data->file_ops_allowed), + S_IRUSR | S_IWUSR) != 0) + tt_abort_perror("chmod"); + + rc = chmod(data->file_ops_blocked, S_IRUSR | S_IWUSR); + errsv = errno; + tt_int_op(rc, OP_EQ, -1); + tt_int_op(errsv, OP_EQ, EPERM); + + done: + (void)0; +} + +static void +test_sandbox_chown_filename(void *arg) +{ + sandbox_data_t *data = arg; + int rc, errsv; + + if (chown(sandbox_intern_string(data->file_ops_allowed), -1, -1) != 0) + tt_abort_perror("chown"); + + rc = chown(data->file_ops_blocked, -1, -1); + errsv = errno; + tt_int_op(rc, OP_EQ, -1); + tt_int_op(errsv, OP_EQ, EPERM); + + done: + (void)0; +} + +static void +test_sandbox_rename_filename(void *arg) +{ + sandbox_data_t *data = arg; + const char *fname_old = sandbox_intern_string(data->file_ops_allowed), + *fname_new = sandbox_intern_string(data->file_rename_target_allowed); + int rc, errsv; + + if (rename(fname_old, fname_new) != 0) + tt_abort_perror("rename"); + + rc = rename(fname_new, fname_old); + errsv = errno; + tt_int_op(rc, OP_EQ, -1); + tt_int_op(errsv, OP_EQ, EPERM); + + done: + (void)0; +} + +static void +test_sandbox_openat_filename(void *arg) +{ + sandbox_data_t *data = arg; + int flags = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY | O_CLOEXEC; + int fd, errsv; + + fd = openat(AT_FDCWD, sandbox_intern_string(data->dir_ops_allowed), flags); + if (fd < 0) + tt_abort_perror("openat"); + close(fd); + + fd = openat(AT_FDCWD, data->dir_ops_blocked, flags); + errsv = errno; + tt_int_op(fd, OP_EQ, -1); + tt_int_op(errsv, OP_EQ, EPERM); + + done: + if (fd >= 0) + close(fd); +} + +static void +test_sandbox_opendir_dirname(void *arg) +{ + sandbox_data_t *data = arg; + DIR *dir; + int errsv; + + dir = opendir(sandbox_intern_string(data->dir_ops_allowed)); + if (dir == NULL) + tt_abort_perror("opendir"); + closedir(dir); + + dir = opendir(data->dir_ops_blocked); + errsv = errno; + tt_ptr_op(dir, OP_EQ, NULL); + tt_int_op(errsv, OP_EQ, EPERM); + + done: + if (dir) + closedir(dir); +} + +static void +test_sandbox_stat_filename(void *arg) +{ + sandbox_data_t *data = arg; + struct stat st; + + if (stat(sandbox_intern_string(data->file_ops_allowed), &st) != 0) + tt_abort_perror("stat"); + + int rc = stat(data->file_ops_blocked, &st); + int errsv = errno; + tt_int_op(rc, OP_EQ, -1); + tt_int_op(errsv, OP_EQ, EPERM); + + done: + (void)0; +} + +#define SANDBOX_TEST_SKIPPED(name) \ + { #name, test_sandbox_ ## name, TT_SKIP, NULL, NULL } + +/* Skip all tests when coverage support is enabled, as the sandbox interferes + * with gcov and prevents it from producing any results. */ +#ifdef ENABLE_COVERAGE +#define SANDBOX_TEST(name, flags) SANDBOX_TEST_SKIPPED(name) +#define SANDBOX_TEST_IN_SANDBOX(name) SANDBOX_TEST_SKIPPED(name) +#else +#define SANDBOX_TEST(name, flags) \ + { #name, test_sandbox_ ## name, flags, NULL, NULL } +#define SANDBOX_TEST_IN_SANDBOX(name) \ + { #name, test_sandbox_ ## name, TT_FORK, &sandboxed_testcase_setup, NULL } +#endif /* defined(ENABLE_COVERAGE) */ + +struct testcase_t sandbox_tests[] = { + SANDBOX_TEST(is_active, TT_FORK), + +/* When Tor is built with fragile compiler-hardening the sandbox is unable to + * filter requests to open files or directories (on systems where glibc uses + * the "open" system call to provide this functionality), as doing so would + * interfere with the address sanitizer as it retrieves information about the + * running process via the filesystem. Skip these tests in that case as the + * corresponding functions are likely to have no effect and this will cause the + * tests to fail. */ +#ifdef ENABLE_FRAGILE_HARDENING + SANDBOX_TEST_SKIPPED(open_filename), + SANDBOX_TEST_SKIPPED(opendir_dirname), +#else + SANDBOX_TEST_IN_SANDBOX(open_filename), + SANDBOX_TEST_IN_SANDBOX(opendir_dirname), +#endif /* defined(ENABLE_FRAGILE_HARDENING) */ + + SANDBOX_TEST_IN_SANDBOX(openat_filename), + SANDBOX_TEST_IN_SANDBOX(chmod_filename), + SANDBOX_TEST_IN_SANDBOX(chown_filename), + SANDBOX_TEST_IN_SANDBOX(rename_filename), + +/* Currently the sandbox is unable to filter stat() calls on systems where + * glibc implements this function using the legacy "stat" system call, or where + * glibc version 2.33 or later is in use and the newer "newfstatat" syscall is + * available. + * + * Skip testing sandbox_cfg_allow_stat_filename() if it seems the likely the + * function will have no effect and the test will therefore not succeed. */ +#if !defined(__NR_newfstatat) && (!defined(__NR_stat) || defined(__NR_stat64)) + SANDBOX_TEST_IN_SANDBOX(stat_filename), +#else + SANDBOX_TEST_SKIPPED(stat_filename), +#endif + END_OF_TESTCASES +}; + +#endif /* defined(USE_SECCOMP) */ diff --git a/src/test/test_util.c b/src/test/test_util.c index f10aed71ac..aa38d0fc5d 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -7,6 +7,7 @@ #define COMPAT_TIME_PRIVATE #define UTIL_MALLOC_PRIVATE #define PROCESS_WIN32_PRIVATE +#define TIME_FMT_PRIVATE #include "lib/testsupport/testsupport.h" #include "core/or/or.h" #include "lib/buf/buffers.h" @@ -111,7 +112,7 @@ static time_t tor_timegm_wrapper(const struct tm *tm) { time_t t; - if (tor_timegm(tm, &t) < 0) + if (tor_timegm_impl(tm, &t) < 0) return -1; return t; } @@ -804,7 +805,7 @@ test_util_time(void *arg) #if SIZEOF_TIME_T == 4 setup_full_capture_of_logs(LOG_WARN); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); - expect_single_log_msg_containing("Result does not fit in tor_timegm"); + //expect_single_log_msg_containing("Result does not fit in tor_timegm"); teardown_capture_of_logs(); #elif SIZEOF_TIME_T == 8 t_res = 2178252895UL; @@ -818,17 +819,16 @@ test_util_time(void *arg) /* The below tests will all cause a BUG message, so we capture, suppress, * and detect. */ #define CAPTURE() do { \ + teardown_capture_of_logs(); \ setup_full_capture_of_logs(LOG_WARN); \ } while (0) #define CHECK_TIMEGM_WARNING(msg) do { \ expect_single_log_msg_containing(msg); \ - teardown_capture_of_logs(); \ } while (0) #define CHECK_POSSIBLE_EINVAL() do { \ if (mock_saved_log_n_entries()) { \ expect_single_log_msg_containing("Invalid argument"); \ } \ - teardown_capture_of_logs(); \ } while (0) #define CHECK_TIMEGM_ARG_OUT_OF_RANGE(msg) \ @@ -1217,7 +1217,7 @@ test_util_time(void *arg) t_res = 0; CAPTURE(); i = parse_rfc1123_time(timestr, &t_res); - CHECK_TIMEGM_WARNING("does not fit in tor_timegm"); + // CHECK_TIMEGM_WARNING("does not fit in tor_timegm"); tt_int_op(-1,OP_EQ, i); #elif SIZEOF_TIME_T == 8 tt_str_op("Wed, 17 Feb 2038 06:13:20 GMT",OP_EQ, timestr); @@ -1296,7 +1296,7 @@ test_util_time(void *arg) CAPTURE(); i = parse_iso_time("2038-02-17 06:13:20", &t_res); tt_int_op(-1,OP_EQ, i); - CHECK_TIMEGM_WARNING("does not fit in tor_timegm"); + //CHECK_TIMEGM_WARNING("does not fit in tor_timegm"); #elif SIZEOF_TIME_T == 8 i = parse_iso_time("2038-02-17 06:13:20", &t_res); tt_int_op(0,OP_EQ, i); @@ -1479,7 +1479,7 @@ test_util_parse_http_time(void *arg) setup_full_capture_of_logs(LOG_WARN); tt_int_op(0,OP_EQ,parse_http_time("Wed, 17 Feb 2038 06:13:20 GMT", &a_time)); tt_int_op((time_t)-1,OP_EQ, tor_timegm(&a_time)); - expect_single_log_msg_containing("does not fit in tor_timegm"); + //expect_single_log_msg_containing("does not fit in tor_timegm"); teardown_capture_of_logs(); #elif SIZEOF_TIME_T == 8 tt_int_op(0,OP_EQ,parse_http_time("Wed, 17 Feb 2038 06:13:20 GMT", &a_time)); @@ -1502,6 +1502,28 @@ test_util_parse_http_time(void *arg) } static void +test_util_timegm_real(void *arg) +{ + (void)arg; + /* Get the real timegm again! We're not testing our impl; we want the + * one that will actually get called. */ +#undef tor_timegm + + /* Now check: is timegm the real inverse of gmtime? */ + time_t now = time(NULL), time2=0; + struct tm tm, *p; + p = tor_gmtime_r(&now, &tm); + tt_ptr_op(p, OP_NE, NULL); + + int r = tor_timegm(&tm, &time2); + tt_int_op(r, OP_EQ, 0); + tt_i64_op((int64_t) now, OP_EQ, (int64_t) time2); + + done: + ; +} + +static void test_util_config_line(void *arg) { char buf[1024]; @@ -7036,6 +7058,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(monotonic_time_ratchet, TT_FORK), UTIL_TEST(monotonic_time_zero, 0), UTIL_TEST(monotonic_time_add_msec, 0), + UTIL_TEST(timegm_real, 0), UTIL_TEST(htonll, 0), UTIL_TEST(get_unquoted_path, 0), UTIL_TEST(map_anon, 0), |