From 92db96d80fddca5394a24256bb6b4691f380a8ae Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 12:51:36 -0400 Subject: [PATCH 01/76] Make our crypto library symbolic in the makefiles. --- Makefile.am | 3 +++ src/app/include.am | 8 ++++---- src/test/fuzz/include.am | 4 ++-- src/test/include.am | 22 +++++++++++----------- src/tools/include.am | 8 ++++---- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/Makefile.am b/Makefile.am index 0f934cf0f2..cf268b0b7b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -127,6 +127,9 @@ TOR_INTERNAL_TESTING_LIBS = \ src/trunnel/libor-trunnel-testing.a \ src/lib/libtor-trace.a +TOR_LDFLAGS_CRYPTLIB=@TOR_LDFLAGS_openssl@ +TOR_LIBS_CRYPTLIB=@TOR_OPENSSL_LIBS@ + # All libraries used to link tor-cov include src/include.am diff --git a/src/app/include.am b/src/app/include.am index 7cf20191ab..97d53ec0fd 100644 --- a/src/app/include.am +++ b/src/app/include.am @@ -14,10 +14,10 @@ src_app_tor_SOURCES = src/app/main/tor_main.c # This seems to matter nowhere but on windows, but I assure you that it # matters a lot there, and is quite hard to debug if you forget to do it. -src_app_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ +src_app_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@ src_app_tor_LDADD = $(TOR_INTERNAL_LIBS) \ $(rust_ldadd) \ - @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @@ -26,9 +26,9 @@ if COVERAGE_ENABLED src_app_tor_cov_SOURCES = $(src_app_tor_SOURCES) src_app_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_app_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) -src_app_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ +src_app_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@ src_app_tor_cov_LDADD = $(TOR_INTERNAL_TESTING_LIBS) \ - @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ \ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am index 87dfe91675..9251e8fd5f 100644 --- a/src/test/fuzz/include.am +++ b/src/test/fuzz/include.am @@ -5,12 +5,12 @@ FUZZING_CPPFLAGS = \ FUZZING_CFLAGS = \ $(AM_CFLAGS) $(TEST_CFLAGS) FUZZING_LDFLAG = \ - @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ + @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@ FUZZING_LIBS = \ $(TOR_INTERNAL_TESTING_LIBS) \ $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ - @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \ @TOR_SYSTEMD_LIBS@ \ @TOR_LZMA_LIBS@ \ diff --git a/src/test/include.am b/src/test/include.am index a749ff1436..15ae79a521 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -225,13 +225,13 @@ src_test_test_switch_id_LDADD = \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_USERENV@ \ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ -src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ +src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \ @TOR_LDFLAGS_libevent@ src_test_test_LDADD = \ $(TOR_INTERNAL_TESTING_LIBS) \ $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ + $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @@ -248,23 +248,23 @@ src_test_test_memwipe_LDADD = $(src_test_test_LDADD) # successfully with the libraries built with them. src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS) @CFLAGS_BUGTRAP@ -src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ +src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \ @TOR_LDFLAGS_libevent@ src_test_bench_LDADD = \ $(TOR_INTERNAL_LIBS) \ $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ + $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ -src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ +src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \ @TOR_LDFLAGS_libevent@ src_test_test_workqueue_LDADD = \ $(TOR_INTERNAL_TESTING_LIBS) \ $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ + $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @@ -276,7 +276,7 @@ src_test_test_timers_LDADD = \ $(TOR_UTIL_TESTING_LIBS) \ $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ + $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ @TOR_LZMA_LIBS@ src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS) @@ -301,22 +301,22 @@ noinst_HEADERS+= \ noinst_PROGRAMS+= src/test/test-ntor-cl noinst_PROGRAMS+= src/test/test-hs-ntor-cl src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c -src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) src_test_test_ntor_cl_LDADD = \ $(TOR_INTERNAL_LIBS) \ $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ + $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ @TOR_LZMA_LIBS@ src_test_test_ntor_cl_AM_CPPFLAGS = \ $(AM_CPPFLAGS) src_test_test_hs_ntor_cl_SOURCES = src/test/test_hs_ntor_cl.c -src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) src_test_test_hs_ntor_cl_LDADD = \ $(TOR_INTERNAL_LIBS) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ src_test_test_hs_ntor_cl_AM_CPPFLAGS = \ $(AM_CPPFLAGS) diff --git a/src/tools/include.am b/src/tools/include.am index 8a2ecb23c9..7c5d3f0bc8 100644 --- a/src/tools/include.am +++ b/src/tools/include.am @@ -21,23 +21,23 @@ src_tools_tor_cov_resolve_LDADD = \ endif src_tools_tor_gencert_SOURCES = src/tools/tor-gencert.c -src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) src_tools_tor_gencert_LDADD = \ $(TOR_CRYPTO_LIBS) \ $(TOR_UTIL_LIBS) \ $(rust_ldadd) \ - @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ if COVERAGE_ENABLED src_tools_tor_cov_gencert_SOURCES = src/tools/tor-gencert.c src_tools_tor_cov_gencert_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_tools_tor_cov_gencert_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) -src_tools_tor_cov_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_tools_tor_cov_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) src_tools_tor_cov_gencert_LDADD = \ $(TOR_CRYPTO_TESTING_LIBS) \ $(TOR_UTIL_TESTING_LIBS) \ - @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ endif From 79267bad654ed39f13e6fb89a5468f23468c3169 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 12:57:30 -0400 Subject: [PATCH 02/76] Add a configure switch to build with NSS. When it is set, include the NSS headers and libraries as appropriate. Doesn't actually use them yet, though. --- Makefile.am | 5 +++++ configure.ac | 22 ++++++++++++++++++++++ src/lib/crypt_ops/include.am | 5 ++++- src/lib/include.libdonna.am | 2 +- src/lib/tls/include.am | 5 ++++- 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index cf268b0b7b..01ed8c935e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -129,6 +129,11 @@ TOR_INTERNAL_TESTING_LIBS = \ TOR_LDFLAGS_CRYPTLIB=@TOR_LDFLAGS_openssl@ TOR_LIBS_CRYPTLIB=@TOR_OPENSSL_LIBS@ +TOR_CFLAGS_CRYPTLIB= +if USE_NSS +TOR_CFLAGS_CRYPTLIB+=@NSS_CFLAGS@ +TOR_LIBS_CRYPTLIB+=@NSS_LIBS@ +endif # All libraries used to link tor-cov diff --git a/configure.ac b/configure.ac index 532476672a..2fe353d75d 100644 --- a/configure.ac +++ b/configure.ac @@ -53,6 +53,8 @@ AC_ARG_ENABLE(restart-debugging, AS_HELP_STRING(--enable-restart-debugging, [Build Tor with support for debugging in-process restart. Developers only.])) AC_ARG_ENABLE(zstd-advanced-apis, AS_HELP_STRING(--disable-zstd-advanced-apis, [Build without support for zstd's "static-only" APIs.])) +AC_ARG_ENABLE(nss, + AS_HELP_STRING(--enable-nss, [Use Mozilla's NSS TLS library. (EXPERIMENTAL)])) if test "x$enable_coverage" != "xyes" -a "x$enable_asserts_in_tests" = "xno" ; then AC_MSG_ERROR([Can't disable assertions outside of coverage build]) @@ -64,6 +66,15 @@ AM_CONDITIONAL(DISABLE_ASSERTS_IN_UNIT_TESTS, test "x$enable_asserts_in_tests" = AM_CONDITIONAL(LIBFUZZER_ENABLED, test "x$enable_libfuzzer" = "xyes") AM_CONDITIONAL(OSS_FUZZ_ENABLED, test "x$enable_oss_fuzz" = "xyes") AM_CONDITIONAL(USE_RUST, test "x$enable_rust" = "xyes") +AM_CONDITIONAL(USE_NSS, test "x$enable_nss" = "xyes") +AM_CONDITIONAL(USE_OPENSSL, true) + +if test "x$enable_nss" = "xyes"; then + AC_DEFINE(ENABLE_NSS, 1, + [Defined if we're building with NSS in addition to OpenSSL.]) +fi +AC_DEFINE(ENABLE_OPENSSL, 1, + [Defined if we're building with OpenSSL or LibreSSL]) if test "$enable_static_tor" = "yes"; then enable_static_libevent="yes"; @@ -821,6 +832,17 @@ fi LIBS="$save_LIBS" AC_SUBST(TOR_LIB_MATH) +dnl ------------------------------------------------------ +dnl Hello, NSS. You're new around here. +if test "x$enable_nss" = "xyes"; then + PKG_CHECK_MODULES(NSS, + [nss], + [have_nss=yes], + [have_nss=no; AC_MSG_ERROR([You asked for NSS but I can't find it.])]) + AC_SUBST(NSS_CFLAGS) + AC_SUBST(NSS_LIBS) +fi + dnl ------------------------------------------------------ dnl Where do you live, openssl? And how do we call you? diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 1b88b880d0..009e4c66c4 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -22,10 +22,13 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/crypto_util.c \ src/lib/crypt_ops/digestset.c +src_lib_libtor_crypt_ops_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) + src_lib_libtor_crypt_ops_testing_a_SOURCES = \ $(src_lib_libtor_crypt_ops_a_SOURCES) src_lib_libtor_crypt_ops_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) -src_lib_libtor_crypt_ops_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +src_lib_libtor_crypt_ops_testing_a_CFLAGS = \ + $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) $(TEST_CFLAGS) noinst_HEADERS += \ src/lib/crypt_ops/aes.h \ diff --git a/src/lib/include.libdonna.am b/src/lib/include.libdonna.am index 5b92dc58a0..60a3f5097e 100644 --- a/src/lib/include.libdonna.am +++ b/src/lib/include.libdonna.am @@ -1,4 +1,4 @@ -src_lib_libcurve25519_donna_a_CFLAGS= +src_lib_libcurve25519_donna_a_CFLAGS=$(TOR_CFLAGS_CRYPTO) if BUILD_CURVE25519_DONNA src_lib_libcurve25519_donna_a_SOURCES=\ diff --git a/src/lib/tls/include.am b/src/lib/tls/include.am index b3b013f4dd..9cc57ca777 100644 --- a/src/lib/tls/include.am +++ b/src/lib/tls/include.am @@ -9,10 +9,13 @@ src_lib_libtor_tls_a_SOURCES = \ src/lib/tls/buffers_tls.c \ src/lib/tls/tortls.c +src_lib_libtor_tls_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) + src_lib_libtor_tls_testing_a_SOURCES = \ $(src_lib_libtor_tls_a_SOURCES) src_lib_libtor_tls_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) -src_lib_libtor_tls_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +src_lib_libtor_tls_testing_a_CFLAGS = \ + $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) $(TEST_CFLAGS) noinst_HEADERS += \ src/lib/tls/ciphers.inc \ From 12a1ada15853f0cde2839921bf413ba24a741192 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 13:44:35 -0400 Subject: [PATCH 03/76] Move the initialization and cleanup parts of crypto.c These are now part of crypto_init.c. The openssl-only parts now live in crypto_openssl_mgt.c. I recommend reviewing this patch with -b and --color-moved. --- src/core/mainloop/main.c | 1 + src/feature/relay/router.c | 1 + src/lib/crypt_ops/crypto.c | 248 ------------------------- src/lib/crypt_ops/crypto.h | 10 - src/lib/crypt_ops/crypto_init.c | 117 ++++++++++++ src/lib/crypt_ops/crypto_init.h | 28 +++ src/lib/crypt_ops/crypto_openssl_mgt.c | 195 ++++++++++++++++++- src/lib/crypt_ops/crypto_openssl_mgt.h | 7 +- src/lib/crypt_ops/include.am | 2 + src/test/bench.c | 1 + src/test/test_crypto.c | 1 + src/test/test_workqueue.c | 1 + src/test/testing_common.c | 1 + src/tools/tor-gencert.c | 1 + 14 files changed, 353 insertions(+), 261 deletions(-) create mode 100644 src/lib/crypt_ops/crypto_init.c create mode 100644 src/lib/crypt_ops/crypto_init.h diff --git a/src/core/mainloop/main.c b/src/core/mainloop/main.c index 048397a2da..ad8c1ead67 100644 --- a/src/core/mainloop/main.c +++ b/src/core/mainloop/main.c @@ -121,6 +121,7 @@ #include "lib/evloop/compat_libevent.h" #include "lib/encoding/confline.h" #include "lib/evloop/timers.h" +#include "lib/crypt_ops/crypto_init.h" #include diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 973d3e1100..edd5ef5d56 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -55,6 +55,7 @@ #include "lib/tls/tortls.h" #include "lib/encoding/confline.h" #include "lib/crypt_ops/crypto_format.h" +#include "lib/crypt_ops/crypto_init.h" /** * \file router.c diff --git a/src/lib/crypt_ops/crypto.c b/src/lib/crypt_ops/crypto.c index 5bc2da76ab..d988473f49 100644 --- a/src/lib/crypt_ops/crypto.c +++ b/src/lib/crypt_ops/crypto.c @@ -38,7 +38,6 @@ DISABLE_GCC_WARNING(redundant-decls) #include #include -#include #include #include #include @@ -74,211 +73,6 @@ ENABLE_GCC_WARNING(redundant-decls) #include -/** Boolean: has OpenSSL's crypto been initialized? */ -static int crypto_early_initialized_ = 0; - -/** Boolean: has OpenSSL's crypto been initialized? */ -static int crypto_global_initialized_ = 0; - -#ifndef DISABLE_ENGINES -/** Log any OpenSSL engines we're using at NOTICE. */ -static void -log_engine(const char *fn, ENGINE *e) -{ - if (e) { - const char *name, *id; - name = ENGINE_get_name(e); - id = ENGINE_get_id(e); - log_notice(LD_CRYPTO, "Default OpenSSL engine for %s is %s [%s]", - fn, name?name:"?", id?id:"?"); - } else { - log_info(LD_CRYPTO, "Using default implementation for %s", fn); - } -} -#endif /* !defined(DISABLE_ENGINES) */ - -#ifndef DISABLE_ENGINES -/** Try to load an engine in a shared library via fully qualified path. - */ -static ENGINE * -try_load_engine(const char *path, const char *engine) -{ - ENGINE *e = ENGINE_by_id("dynamic"); - if (e) { - if (!ENGINE_ctrl_cmd_string(e, "ID", engine, 0) || - !ENGINE_ctrl_cmd_string(e, "DIR_LOAD", "2", 0) || - !ENGINE_ctrl_cmd_string(e, "DIR_ADD", path, 0) || - !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) { - ENGINE_free(e); - e = NULL; - } - } - return e; -} -#endif /* !defined(DISABLE_ENGINES) */ - -static int have_seeded_siphash = 0; - -/** Set up the siphash key if we haven't already done so. */ -int -crypto_init_siphash_key(void) -{ - struct sipkey key; - if (have_seeded_siphash) - return 0; - - crypto_rand((char*) &key, sizeof(key)); - siphash_set_global_key(&key); - have_seeded_siphash = 1; - return 0; -} - -/** Initialize the crypto library. Return 0 on success, -1 on failure. - */ -int -crypto_early_init(void) -{ - if (!crypto_early_initialized_) { - - crypto_early_initialized_ = 1; - -#ifdef OPENSSL_1_1_API - OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | - OPENSSL_INIT_LOAD_CRYPTO_STRINGS | - OPENSSL_INIT_ADD_ALL_CIPHERS | - OPENSSL_INIT_ADD_ALL_DIGESTS, NULL); -#else - ERR_load_crypto_strings(); - OpenSSL_add_all_algorithms(); -#endif - - setup_openssl_threading(); - - unsigned long version_num = OpenSSL_version_num(); - const char *version_str = OpenSSL_version(OPENSSL_VERSION); - if (version_num == OPENSSL_VERSION_NUMBER && - !strcmp(version_str, OPENSSL_VERSION_TEXT)) { - log_info(LD_CRYPTO, "OpenSSL version matches version from headers " - "(%lx: %s).", version_num, version_str); - } else { - log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the " - "version we're running with. If you get weird crashes, that " - "might be why. (Compiled with %lx: %s; running with %lx: %s).", - (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT, - version_num, version_str); - } - - crypto_force_rand_ssleay(); - - if (crypto_seed_rng() < 0) - return -1; - if (crypto_init_siphash_key() < 0) - return -1; - - curve25519_init(); - ed25519_init(); - } - return 0; -} - -/** Initialize the crypto library. Return 0 on success, -1 on failure. - */ -int -crypto_global_init(int useAccel, const char *accelName, const char *accelDir) -{ - if (!crypto_global_initialized_) { - if (crypto_early_init() < 0) - return -1; - - crypto_global_initialized_ = 1; - - if (useAccel > 0) { -#ifdef DISABLE_ENGINES - (void)accelName; - (void)accelDir; - log_warn(LD_CRYPTO, "No OpenSSL hardware acceleration support enabled."); -#else - ENGINE *e = NULL; - - log_info(LD_CRYPTO, "Initializing OpenSSL engine support."); - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); - - if (accelName) { - if (accelDir) { - log_info(LD_CRYPTO, "Trying to load dynamic OpenSSL engine \"%s\"" - " via path \"%s\".", accelName, accelDir); - e = try_load_engine(accelName, accelDir); - } else { - log_info(LD_CRYPTO, "Initializing dynamic OpenSSL engine \"%s\"" - " acceleration support.", accelName); - e = ENGINE_by_id(accelName); - } - if (!e) { - log_warn(LD_CRYPTO, "Unable to load dynamic OpenSSL engine \"%s\".", - accelName); - } else { - log_info(LD_CRYPTO, "Loaded dynamic OpenSSL engine \"%s\".", - accelName); - } - } - if (e) { - log_info(LD_CRYPTO, "Loaded OpenSSL hardware acceleration engine," - " setting default ciphers."); - ENGINE_set_default(e, ENGINE_METHOD_ALL); - } - /* Log, if available, the intersection of the set of algorithms - used by Tor and the set of algorithms available in the engine */ - log_engine("RSA", ENGINE_get_default_RSA()); - log_engine("DH", ENGINE_get_default_DH()); -#ifdef OPENSSL_1_1_API - log_engine("EC", ENGINE_get_default_EC()); -#else - log_engine("ECDH", ENGINE_get_default_ECDH()); - log_engine("ECDSA", ENGINE_get_default_ECDSA()); -#endif /* defined(OPENSSL_1_1_API) */ - log_engine("RAND", ENGINE_get_default_RAND()); - log_engine("RAND (which we will not use)", ENGINE_get_default_RAND()); - log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1)); - log_engine("3DES-CBC", ENGINE_get_cipher_engine(NID_des_ede3_cbc)); - log_engine("AES-128-ECB", ENGINE_get_cipher_engine(NID_aes_128_ecb)); - log_engine("AES-128-CBC", ENGINE_get_cipher_engine(NID_aes_128_cbc)); -#ifdef NID_aes_128_ctr - log_engine("AES-128-CTR", ENGINE_get_cipher_engine(NID_aes_128_ctr)); -#endif -#ifdef NID_aes_128_gcm - log_engine("AES-128-GCM", ENGINE_get_cipher_engine(NID_aes_128_gcm)); -#endif - log_engine("AES-256-CBC", ENGINE_get_cipher_engine(NID_aes_256_cbc)); -#ifdef NID_aes_256_gcm - log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm)); -#endif - -#endif /* defined(DISABLE_ENGINES) */ - } else { - log_info(LD_CRYPTO, "NOT using OpenSSL engine support."); - } - - if (crypto_force_rand_ssleay()) { - if (crypto_seed_rng() < 0) - return -1; - } - - evaluate_evp_for_aes(-1); - evaluate_ctr_for_aes(); - } - return 0; -} - -/** Free crypto resources held by this thread. */ -void -crypto_thread_cleanup(void) -{ -#ifndef NEW_THREAD_API - ERR_remove_thread_state(NULL); -#endif -} - /** Allocate and return a new symmetric cipher using the provided key and iv. * The key is bits bits long; the IV is CIPHER_IV_LEN bytes. Both * must be provided. Key length must be 128, 192, or 256 */ @@ -465,45 +259,3 @@ crypto_cipher_decrypt_with_iv(const char *key, crypto_cipher_free(cipher); return (int)(fromlen - CIPHER_IV_LEN); } - -/** @{ */ -/** Uninitialize the crypto library. Return 0 on success. Does not detect - * failure. - */ -int -crypto_global_cleanup(void) -{ -#ifndef OPENSSL_1_1_API - EVP_cleanup(); -#endif -#ifndef NEW_THREAD_API - ERR_remove_thread_state(NULL); -#endif -#ifndef OPENSSL_1_1_API - ERR_free_strings(); -#endif - - crypto_dh_free_all(); - -#ifndef DISABLE_ENGINES -#ifndef OPENSSL_1_1_API - ENGINE_cleanup(); -#endif -#endif - - CONF_modules_unload(1); -#ifndef OPENSSL_1_1_API - CRYPTO_cleanup_all_ex_data(); -#endif - - crypto_openssl_free_all(); - - crypto_early_initialized_ = 0; - crypto_global_initialized_ = 0; - have_seeded_siphash = 0; - siphash_unset_global_key(); - - return 0; -} - -/** @} */ diff --git a/src/lib/crypt_ops/crypto.h b/src/lib/crypt_ops/crypto.h index 3a0b330be6..6034c84174 100644 --- a/src/lib/crypt_ops/crypto.h +++ b/src/lib/crypt_ops/crypto.h @@ -32,16 +32,6 @@ typedef struct aes_cnt_cipher crypto_cipher_t; -/* global state */ -int crypto_init_siphash_key(void); -int crypto_early_init(void) ATTR_WUR; -int crypto_global_init(int hardwareAccel, - const char *accelName, - const char *accelPath) ATTR_WUR; - -void crypto_thread_cleanup(void); -int crypto_global_cleanup(void); - /* environment setup */ crypto_cipher_t *crypto_cipher_new(const char *key); crypto_cipher_t *crypto_cipher_new_with_bits(const char *key, int bits); diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c new file mode 100644 index 0000000000..01d5baf5be --- /dev/null +++ b/src/lib/crypt_ops/crypto_init.c @@ -0,0 +1,117 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_init.c + * + * \brief Initialize and shut down Tor's crypto library and subsystem. + **/ + +#include "orconfig.h" + +#include "lib/crypt_ops/crypto_init.h" + +#include "lib/crypt_ops/crypto_curve25519.h" +#include "lib/crypt_ops/crypto_dh.h" +#include "lib/crypt_ops/crypto_ed25519.h" +#include "lib/crypt_ops/crypto_openssl_mgt.h" +#include "lib/crypt_ops/crypto_rand.h" + +#include "siphash.h" + +/** Boolean: has OpenSSL's crypto been initialized? */ +static int crypto_early_initialized_ = 0; + +/** Boolean: has OpenSSL's crypto been initialized? */ +static int crypto_global_initialized_ = 0; + +static int have_seeded_siphash = 0; + +/** Set up the siphash key if we haven't already done so. */ +int +crypto_init_siphash_key(void) +{ + struct sipkey key; + if (have_seeded_siphash) + return 0; + + crypto_rand((char*) &key, sizeof(key)); + siphash_set_global_key(&key); + have_seeded_siphash = 1; + return 0; +} + +/** Initialize the crypto library. Return 0 on success, -1 on failure. + */ +int +crypto_early_init(void) +{ + if (!crypto_early_initialized_) { + + crypto_early_initialized_ = 1; + +#ifdef ENABLE_OPENSSL + crypto_openssl_early_init(); +#endif + + if (crypto_seed_rng() < 0) + return -1; + if (crypto_init_siphash_key() < 0) + return -1; + + curve25519_init(); + ed25519_init(); + } + return 0; +} + +/** Initialize the crypto library. Return 0 on success, -1 on failure. + */ +int +crypto_global_init(int useAccel, const char *accelName, const char *accelDir) +{ + if (!crypto_global_initialized_) { + if (crypto_early_init() < 0) + return -1; + + crypto_global_initialized_ = 1; + +#ifdef ENABLE_OPENSSL + return crypto_openssl_late_init(useAccel, accelName, accelDir); +#endif + } + return 0; +} + +/** Free crypto resources held by this thread. */ +void +crypto_thread_cleanup(void) +{ +#ifndef NEW_THREAD_API + ERR_remove_thread_state(NULL); +#endif +} + +/** + * Uninitialize the crypto library. Return 0 on success. Does not detect + * failure. + */ +int +crypto_global_cleanup(void) +{ + crypto_dh_free_all(); + +#ifdef ENABLE_OPENSSL + crypto_openssl_global_cleanup(); +#endif + + crypto_early_initialized_ = 0; + crypto_global_initialized_ = 0; + have_seeded_siphash = 0; + siphash_unset_global_key(); + + return 0; +} diff --git a/src/lib/crypt_ops/crypto_init.h b/src/lib/crypt_ops/crypto_init.h new file mode 100644 index 0000000000..e450e2d894 --- /dev/null +++ b/src/lib/crypt_ops/crypto_init.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_init.h + * + * \brief Headers for crypto_init.c + **/ + +#ifndef TOR_CRYPTO_INIT_H +#define TOR_CRYPTO_INIT_H + +#include "orconfig.h" +#include "lib/cc/compat_compiler.h" + +int crypto_init_siphash_key(void); +int crypto_early_init(void) ATTR_WUR; +int crypto_global_init(int hardwareAccel, + const char *accelName, + const char *accelPath) ATTR_WUR; + +void crypto_thread_cleanup(void); +int crypto_global_cleanup(void); + +#endif /* !defined(TOR_CRYPTO_H) */ diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.c b/src/lib/crypt_ops/crypto_openssl_mgt.c index 01de6a9d9e..b0c88bff4d 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.c +++ b/src/lib/crypt_ops/crypto_openssl_mgt.c @@ -12,8 +12,11 @@ #include "lib/crypt_ops/compat_openssl.h" #include "lib/crypt_ops/crypto_openssl_mgt.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/aes.h" #include "lib/string/util_string.h" #include "lib/lock/compat_mutex.h" +#include "lib/log/log.h" #include "lib/testsupport/testsupport.h" #include "lib/thread/threads.h" @@ -30,6 +33,7 @@ DISABLE_GCC_WARNING(redundant-decls) #include #include #include +#include ENABLE_GCC_WARNING(redundant-decls) @@ -144,7 +148,7 @@ setup_openssl_threading(void) } /** free OpenSSL variables */ -void +static void crypto_openssl_free_all(void) { tor_free(crypto_openssl_version_str); @@ -164,3 +168,192 @@ crypto_openssl_free_all(void) } #endif /* !defined(NEW_THREAD_API) */ } + +/** Perform early (pre-configuration) initialization tasks for OpenSSL. */ +void +crypto_openssl_early_init(void) +{ +#ifdef OPENSSL_1_1_API + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | + OPENSSL_INIT_LOAD_CRYPTO_STRINGS | + OPENSSL_INIT_ADD_ALL_CIPHERS | + OPENSSL_INIT_ADD_ALL_DIGESTS, NULL); +#else + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); +#endif + + setup_openssl_threading(); + + unsigned long version_num = OpenSSL_version_num(); + const char *version_str = OpenSSL_version(OPENSSL_VERSION); + if (version_num == OPENSSL_VERSION_NUMBER && + !strcmp(version_str, OPENSSL_VERSION_TEXT)) { + log_info(LD_CRYPTO, "OpenSSL version matches version from headers " + "(%lx: %s).", version_num, version_str); + } else { + log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the " + "version we're running with. If you get weird crashes, that " + "might be why. (Compiled with %lx: %s; running with %lx: %s).", + (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT, + version_num, version_str); + } + + crypto_force_rand_ssleay(); +} + +#ifndef DISABLE_ENGINES +/** Try to load an engine in a shared library via fully qualified path. + */ +static ENGINE * +try_load_engine(const char *path, const char *engine) +{ + ENGINE *e = ENGINE_by_id("dynamic"); + if (e) { + if (!ENGINE_ctrl_cmd_string(e, "ID", engine, 0) || + !ENGINE_ctrl_cmd_string(e, "DIR_LOAD", "2", 0) || + !ENGINE_ctrl_cmd_string(e, "DIR_ADD", path, 0) || + !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) { + ENGINE_free(e); + e = NULL; + } + } + return e; +} +#endif /* !defined(DISABLE_ENGINES) */ + +#ifndef DISABLE_ENGINES +/** Log any OpenSSL engines we're using at NOTICE. */ +static void +log_engine(const char *fn, ENGINE *e) +{ + if (e) { + const char *name, *id; + name = ENGINE_get_name(e); + id = ENGINE_get_id(e); + log_notice(LD_CRYPTO, "Default OpenSSL engine for %s is %s [%s]", + fn, name?name:"?", id?id:"?"); + } else { + log_info(LD_CRYPTO, "Using default implementation for %s", fn); + } +} +#endif /* !defined(DISABLE_ENGINES) */ + +/** Initialize engines for openssl (if enabled). */ +static void +crypto_openssl_init_engines(const char *accelName, + const char *accelDir) +{ +#ifdef DISABLE_ENGINES + (void)accelName; + (void)accelDir; + log_warn(LD_CRYPTO, "No OpenSSL hardware acceleration support enabled."); +#else + ENGINE *e = NULL; + + log_info(LD_CRYPTO, "Initializing OpenSSL engine support."); + ENGINE_load_builtin_engines(); + ENGINE_register_all_complete(); + + if (accelName) { + if (accelDir) { + log_info(LD_CRYPTO, "Trying to load dynamic OpenSSL engine \"%s\"" + " via path \"%s\".", accelName, accelDir); + e = try_load_engine(accelName, accelDir); + } else { + log_info(LD_CRYPTO, "Initializing dynamic OpenSSL engine \"%s\"" + " acceleration support.", accelName); + e = ENGINE_by_id(accelName); + } + if (!e) { + log_warn(LD_CRYPTO, "Unable to load dynamic OpenSSL engine \"%s\".", + accelName); + } else { + log_info(LD_CRYPTO, "Loaded dynamic OpenSSL engine \"%s\".", + accelName); + } + } + if (e) { + log_info(LD_CRYPTO, "Loaded OpenSSL hardware acceleration engine," + " setting default ciphers."); + ENGINE_set_default(e, ENGINE_METHOD_ALL); + } + /* Log, if available, the intersection of the set of algorithms + used by Tor and the set of algorithms available in the engine */ + log_engine("RSA", ENGINE_get_default_RSA()); + log_engine("DH", ENGINE_get_default_DH()); +#ifdef OPENSSL_1_1_API + log_engine("EC", ENGINE_get_default_EC()); +#else + log_engine("ECDH", ENGINE_get_default_ECDH()); + log_engine("ECDSA", ENGINE_get_default_ECDSA()); +#endif /* defined(OPENSSL_1_1_API) */ + log_engine("RAND", ENGINE_get_default_RAND()); + log_engine("RAND (which we will not use)", ENGINE_get_default_RAND()); + log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1)); + log_engine("3DES-CBC", ENGINE_get_cipher_engine(NID_des_ede3_cbc)); + log_engine("AES-128-ECB", ENGINE_get_cipher_engine(NID_aes_128_ecb)); + log_engine("AES-128-CBC", ENGINE_get_cipher_engine(NID_aes_128_cbc)); +#ifdef NID_aes_128_ctr + log_engine("AES-128-CTR", ENGINE_get_cipher_engine(NID_aes_128_ctr)); +#endif +#ifdef NID_aes_128_gcm + log_engine("AES-128-GCM", ENGINE_get_cipher_engine(NID_aes_128_gcm)); +#endif + log_engine("AES-256-CBC", ENGINE_get_cipher_engine(NID_aes_256_cbc)); +#ifdef NID_aes_256_gcm + log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm)); +#endif + +#endif /* defined(DISABLE_ENGINES) */ +} + +/** Perform late (post-init) initialization tasks for OpenSSL */ +int +crypto_openssl_late_init(int useAccel, const char *accelName, + const char *accelDir) +{ + if (useAccel > 0) { + crypto_openssl_init_engines(accelName, accelDir); + } else { + log_info(LD_CRYPTO, "NOT using OpenSSL engine support."); + } + + if (crypto_force_rand_ssleay()) { + if (crypto_seed_rng() < 0) + return -1; + } + + evaluate_evp_for_aes(-1); + evaluate_ctr_for_aes(); + + return 0; +} + +/** Clean up global resources held by openssl. */ +void +crypto_openssl_global_cleanup(void) +{ + #ifndef OPENSSL_1_1_API + EVP_cleanup(); +#endif +#ifndef NEW_THREAD_API + ERR_remove_thread_state(NULL); +#endif +#ifndef OPENSSL_1_1_API + ERR_free_strings(); +#endif + +#ifndef DISABLE_ENGINES +#ifndef OPENSSL_1_1_API + ENGINE_cleanup(); +#endif +#endif + + CONF_modules_unload(1); +#ifndef OPENSSL_1_1_API + CRYPTO_cleanup_all_ex_data(); +#endif + + crypto_openssl_free_all(); +} diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.h b/src/lib/crypt_ops/crypto_openssl_mgt.h index a2c53302e1..60583a056e 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.h +++ b/src/lib/crypt_ops/crypto_openssl_mgt.h @@ -76,7 +76,10 @@ const char * crypto_openssl_get_header_version_str(void); /* OpenSSL threading setup function */ int setup_openssl_threading(void); -/* Tor OpenSSL utility functions */ -void crypto_openssl_free_all(void); +void crypto_openssl_early_init(void); +int crypto_openssl_late_init(int useAccel, const char *accelName, + const char *accelDir); + +void crypto_openssl_global_cleanup(void); #endif /* !defined(TOR_CRYPTO_OPENSSL_H) */ diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 009e4c66c4..6bb7c929a9 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -14,6 +14,7 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/crypto_ed25519.c \ src/lib/crypt_ops/crypto_format.c \ src/lib/crypt_ops/crypto_hkdf.c \ + src/lib/crypt_ops/crypto_init.c \ src/lib/crypt_ops/crypto_openssl_mgt.c \ src/lib/crypt_ops/crypto_pwbox.c \ src/lib/crypt_ops/crypto_rand.c \ @@ -40,6 +41,7 @@ noinst_HEADERS += \ src/lib/crypt_ops/crypto_format.h \ src/lib/crypt_ops/crypto.h \ src/lib/crypt_ops/crypto_hkdf.h \ + src/lib/crypt_ops/crypto_init.h \ src/lib/crypt_ops/crypto_openssl_mgt.h \ src/lib/crypt_ops/crypto_pwbox.h \ src/lib/crypt_ops/crypto_rand.h \ diff --git a/src/test/bench.c b/src/test/bench.c index 7919a4224f..50af9e0d69 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -33,6 +33,7 @@ #include "core/or/or_circuit_st.h" #include "lib/crypt_ops/digestset.h" +#include "lib/crypt_ops/crypto_init.h" #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) static uint64_t nanostart; diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 2124e22196..17f23b96bd 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -16,6 +16,7 @@ #include "lib/crypt_ops/crypto_format.h" #include "lib/crypt_ops/crypto_hkdf.h" #include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_init.h" #include "ed25519_vectors.inc" #ifdef HAVE_SYS_STAT_H diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c index 4550bad1f0..9d48d92773 100644 --- a/src/test/test_workqueue.c +++ b/src/test/test_workqueue.c @@ -12,6 +12,7 @@ #include "lib/net/alertsock.h" #include "lib/evloop/compat_libevent.h" #include "lib/intmath/weakrng.h" +#include "lib/crypt_ops/crypto_init.h" #include diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 3880bca9c5..32d7bf7f0e 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -23,6 +23,7 @@ #include "core/mainloop/main.h" #include "lib/compress/compress.h" #include "lib/evloop/compat_libevent.h" +#include "lib/crypt_ops/crypto_init.h" #include #ifdef HAVE_FCNTL_H diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index ff924152a6..f87283097e 100644 --- a/src/tools/tor-gencert.c +++ b/src/tools/tor-gencert.c @@ -14,6 +14,7 @@ #endif #include "lib/cc/compat_compiler.h" +#include "lib/crypt_ops/crypto_init.h" /* Some versions of OpenSSL declare X509_STORE_CTX_set_verify_cb twice in * x509.h and x509_vfy.h. Suppress the GCC warning so we can build with From 8e2df9886003c12b0624c54b950090c8fc36396b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 13:51:26 -0400 Subject: [PATCH 04/76] Move crypto_add_spaces_to_fp() to crypto_rsa.c --- src/lib/crypt_ops/crypto.c | 20 -------------------- src/lib/crypt_ops/crypto.h | 2 -- src/lib/crypt_ops/crypto_rsa.c | 20 ++++++++++++++++++++ src/lib/crypt_ops/crypto_rsa.h | 1 + 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/lib/crypt_ops/crypto.c b/src/lib/crypt_ops/crypto.c index d988473f49..e4f092c80c 100644 --- a/src/lib/crypt_ops/crypto.c +++ b/src/lib/crypt_ops/crypto.c @@ -129,26 +129,6 @@ crypto_cipher_free_(crypto_cipher_t *env) aes_cipher_free(env); } -/** Copy in to the outlen-byte buffer out, adding spaces - * every four characters. */ -void -crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in) -{ - int n = 0; - char *end = out+outlen; - tor_assert(outlen < SIZE_T_CEILING); - - while (*in && outfromlen bytes from from using the cipher diff --git a/src/lib/crypt_ops/crypto.h b/src/lib/crypt_ops/crypto.h index 6034c84174..993a65fb01 100644 --- a/src/lib/crypt_ops/crypto.h +++ b/src/lib/crypt_ops/crypto.h @@ -59,6 +59,4 @@ int crypto_cipher_decrypt_with_iv(const char *key, char *to, size_t tolen, const char *from, size_t fromlen); -void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); - #endif /* !defined(TOR_CRYPTO_H) */ diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c index 5ec69d7319..f0fd6d5f4b 100644 --- a/src/lib/crypt_ops/crypto_rsa.c +++ b/src/lib/crypt_ops/crypto_rsa.c @@ -976,6 +976,26 @@ crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out) return 0; } +/** Copy in to the outlen-byte buffer out, adding spaces + * every four characters. */ +void +crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in) +{ + int n = 0; + char *end = out+outlen; + tor_assert(outlen < SIZE_T_CEILING); + + while (*in && outsig against * datalen bytes of data at data, using the public key * in env. Return 0 if sig is a correct signature for diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index 51fc974821..c70e01c3f4 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -88,6 +88,7 @@ int crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len); crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len); int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space); int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out); +void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); MOCK_DECL(int, crypto_pk_public_checksig_digest,(crypto_pk_t *env, const char *data, size_t datalen, const char *sig, size_t siglen)); From 922208bd2d63c282a189b6fdd0da29ff40c08e82 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 13:54:47 -0400 Subject: [PATCH 05/76] Extract and rename crypto_log_errors(). --- src/lib/crypt_ops/crypto_dh.c | 6 +++--- src/lib/crypt_ops/crypto_openssl_mgt.c | 22 ++++++++++++++++++++++ src/lib/crypt_ops/crypto_openssl_mgt.h | 2 ++ src/lib/crypt_ops/crypto_rsa.c | 26 +++++++++++++------------- src/lib/crypt_ops/crypto_util.c | 23 ----------------------- src/lib/crypt_ops/crypto_util.h | 9 --------- src/tools/tor-gencert.c | 11 ++++++----- 7 files changed, 46 insertions(+), 53 deletions(-) diff --git a/src/lib/crypt_ops/crypto_dh.c b/src/lib/crypt_ops/crypto_dh.c index 3e82996935..ab06fd4969 100644 --- a/src/lib/crypt_ops/crypto_dh.c +++ b/src/lib/crypt_ops/crypto_dh.c @@ -269,7 +269,7 @@ crypto_dh_new(int dh_type) /* LCOV_EXCL_START * This error condition is only reached when an allocation fails */ err: - crypto_log_errors(LOG_WARN, "creating DH object"); + crypto_openssl_log_errors(LOG_WARN, "creating DH object"); if (res->dh) DH_free(res->dh); /* frees p and g too */ tor_free(res); return NULL; @@ -309,7 +309,7 @@ crypto_dh_generate_public(crypto_dh_t *dh) if (!DH_generate_key(dh->dh)) { /* LCOV_EXCL_START * To test this we would need some way to tell openssl to break DH. */ - crypto_log_errors(LOG_WARN, "generating DH key"); + crypto_openssl_log_errors(LOG_WARN, "generating DH key"); return -1; /* LCOV_EXCL_STOP */ } @@ -472,7 +472,7 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh, error: result = -1; done: - crypto_log_errors(LOG_WARN, "completing DH handshake"); + crypto_openssl_log_errors(LOG_WARN, "completing DH handshake"); if (pubkey_bn) BN_clear_free(pubkey_bn); if (secret_tmp) { diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.c b/src/lib/crypt_ops/crypto_openssl_mgt.c index b0c88bff4d..bf5b96f9a5 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.c +++ b/src/lib/crypt_ops/crypto_openssl_mgt.c @@ -17,6 +17,7 @@ #include "lib/string/util_string.h" #include "lib/lock/compat_mutex.h" #include "lib/log/log.h" +#include "lib/log/util_bug.h" #include "lib/testsupport/testsupport.h" #include "lib/thread/threads.h" @@ -53,6 +54,27 @@ STATIC void openssl_locking_cb_(int mode, int n, const char *file, int line); STATIC void tor_set_openssl_thread_id(CRYPTO_THREADID *threadid); #endif +/** Log all pending crypto errors at level severity. Use + * doing to describe our current activities. + */ +void +crypto_openssl_log_errors(int severity, const char *doing) +{ + unsigned long err; + const char *msg, *lib, *func; + while ((err = ERR_get_error()) != 0) { + msg = (const char*)ERR_reason_error_string(err); + lib = (const char*)ERR_lib_error_string(err); + func = (const char*)ERR_func_error_string(err); + if (!msg) msg = "(null)"; + if (!lib) lib = "(null)"; + if (!func) func = "(null)"; + if (BUG(!doing)) doing = "(null)"; + tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)", + doing, msg, lib, func); + } +} + /* Returns a trimmed and human-readable version of an openssl version string * raw_version. They are usually in the form of 'OpenSSL 1.0.0b 10 * May 2012' and this will parse them into a form similar to '1.0.0b' */ diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.h b/src/lib/crypt_ops/crypto_openssl_mgt.h index 60583a056e..417bb36f01 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.h +++ b/src/lib/crypt_ops/crypto_openssl_mgt.h @@ -69,6 +69,8 @@ #define NEW_THREAD_API #endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && ... */ +void crypto_openssl_log_errors(int severity, const char *doing); + /* global openssl state */ const char * crypto_openssl_get_version_str(void); const char * crypto_openssl_get_header_version_str(void); diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c index f0fd6d5f4b..d8a9be7ec9 100644 --- a/src/lib/crypt_ops/crypto_rsa.c +++ b/src/lib/crypt_ops/crypto_rsa.c @@ -207,7 +207,7 @@ crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)) } if (!env->key) { - crypto_log_errors(LOG_WARN, "generating RSA key"); + crypto_openssl_log_errors(LOG_WARN, "generating RSA key"); return -1; } @@ -252,7 +252,7 @@ crypto_pk_read_private_key_from_string(crypto_pk_t *env, BIO_free(b); if (!env->key) { - crypto_log_errors(LOG_WARN, "Error parsing private key"); + crypto_openssl_log_errors(LOG_WARN, "Error parsing private key"); return -1; } return 0; @@ -316,7 +316,7 @@ crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest, r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL); if (!r) { - crypto_log_errors(LOG_WARN, "writing RSA key to string"); + crypto_openssl_log_errors(LOG_WARN, "writing RSA key to string"); BIO_free(b); return -1; } @@ -382,7 +382,7 @@ crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL); BIO_free(b); if (!env->key) { - crypto_log_errors(LOG_WARN, "reading public key from string"); + crypto_openssl_log_errors(LOG_WARN, "reading public key from string"); return -1; } @@ -408,7 +408,7 @@ crypto_pk_write_private_key_to_filename(crypto_pk_t *env, return -1; if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL) == 0) { - crypto_log_errors(LOG_WARN, "writing private key"); + crypto_openssl_log_errors(LOG_WARN, "writing private key"); BIO_free(bio); return -1; } @@ -434,7 +434,7 @@ crypto_pk_check_key(crypto_pk_t *env) r = RSA_check_key(env->key); if (r <= 0) - crypto_log_errors(LOG_WARN,"checking RSA key"); + crypto_openssl_log_errors(LOG_WARN,"checking RSA key"); return r; } @@ -601,7 +601,7 @@ crypto_pk_copy_full(crypto_pk_t *env) */ log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.", privatekey?"private":"public"); - crypto_log_errors(LOG_ERR, + crypto_openssl_log_errors(LOG_ERR, privatekey ? "Duplicating a private key" : "Duplicating a public key"); tor_fragile_assert(); @@ -777,7 +777,7 @@ crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, (unsigned char*)from, (unsigned char*)to, env->key, crypto_get_rsa_padding(padding)); if (r<0) { - crypto_log_errors(LOG_WARN, "performing RSA encryption"); + crypto_openssl_log_errors(LOG_WARN, "performing RSA encryption"); return -1; } return r; @@ -813,7 +813,7 @@ crypto_pk_private_decrypt(crypto_pk_t *env, char *to, env->key, crypto_get_rsa_padding(padding)); if (r<0) { - crypto_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG, + crypto_openssl_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG, "performing RSA decryption"); return -1; } @@ -844,7 +844,7 @@ crypto_pk_public_checksig,(const crypto_pk_t *env, char *to, env->key, RSA_PKCS1_PADDING); if (r<0) { - crypto_log_errors(LOG_INFO, "checking RSA signature"); + crypto_openssl_log_errors(LOG_INFO, "checking RSA signature"); return -1; } return r; @@ -876,7 +876,7 @@ crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, (unsigned char*)from, (unsigned char*)to, (RSA*)env->key, RSA_PKCS1_PADDING); if (r<0) { - crypto_log_errors(LOG_WARN, "generating RSA signature"); + crypto_openssl_log_errors(LOG_WARN, "generating RSA signature"); return -1; } return r; @@ -921,7 +921,7 @@ crypto_pk_asn1_decode(const char *str, size_t len) rsa = d2i_RSAPublicKey(NULL, &cp, len); tor_free(buf); if (!rsa) { - crypto_log_errors(LOG_WARN,"decoding public key"); + crypto_openssl_log_errors(LOG_WARN,"decoding public key"); return NULL; } return crypto_new_pk_from_rsa_(rsa); @@ -1165,7 +1165,7 @@ crypto_pk_base64_decode(const char *str, size_t len) const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */ RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len); if (!rsa) { - crypto_log_errors(LOG_WARN, "decoding private key"); + crypto_openssl_log_errors(LOG_WARN, "decoding private key"); goto out; } diff --git a/src/lib/crypt_ops/crypto_util.c b/src/lib/crypt_ops/crypto_util.c index 79988c6a91..a645321bfb 100644 --- a/src/lib/crypt_ops/crypto_util.c +++ b/src/lib/crypt_ops/crypto_util.c @@ -10,7 +10,6 @@ * \brief Common cryptographic utilities. **/ -#ifndef CRYPTO_UTIL_PRIVATE #define CRYPTO_UTIL_PRIVATE #include "lib/crypt_ops/crypto_util.h" @@ -105,25 +104,3 @@ memwipe(void *mem, uint8_t byte, size_t sz) **/ memset(mem, byte, sz); } - -/** Log all pending crypto errors at level severity. Use - * doing to describe our current activities. - */ -void -crypto_log_errors(int severity, const char *doing) -{ - unsigned long err; - const char *msg, *lib, *func; - while ((err = ERR_get_error()) != 0) { - msg = (const char*)ERR_reason_error_string(err); - lib = (const char*)ERR_lib_error_string(err); - func = (const char*)ERR_func_error_string(err); - if (!msg) msg = "(null)"; - if (!lib) lib = "(null)"; - if (!func) func = "(null)"; - if (BUG(!doing)) doing = "(null)"; - tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)", - doing, msg, lib, func); - } -} -#endif /* !defined(CRYPTO_UTIL_PRIVATE) */ diff --git a/src/lib/crypt_ops/crypto_util.h b/src/lib/crypt_ops/crypto_util.h index 3ce34e6f23..e032263225 100644 --- a/src/lib/crypt_ops/crypto_util.h +++ b/src/lib/crypt_ops/crypto_util.h @@ -18,13 +18,4 @@ /** OpenSSL-based utility functions. */ void memwipe(void *mem, uint8_t byte, size_t sz); -/** Log utility function */ -void crypto_log_errors(int severity, const char *doing); - -#ifdef CRYPTO_UTIL_PRIVATE -#ifdef TOR_UNIT_TESTS -#endif /* defined(TOR_UNIT_TESTS) */ -#endif /* defined(CRYPTO_UTIL_PRIVATE) */ - #endif /* !defined(TOR_CRYPTO_UTIL_H) */ - diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index f87283097e..7e54483b28 100644 --- a/src/tools/tor-gencert.c +++ b/src/tools/tor-gencert.c @@ -15,6 +15,7 @@ #include "lib/cc/compat_compiler.h" #include "lib/crypt_ops/crypto_init.h" +#include "lib/crypt_ops/crypto_openssl_mgt.h" /* Some versions of OpenSSL declare X509_STORE_CTX_set_verify_cb twice in * x509.h and x509_vfy.h. Suppress the GCC warning so we can build with @@ -267,7 +268,7 @@ load_identity_key(void) IDENTITY_KEY_BITS); if (!(key = generate_key(IDENTITY_KEY_BITS))) { log_err(LD_GENERAL, "Couldn't generate identity key."); - crypto_log_errors(LOG_ERR, "Generating identity key"); + crypto_openssl_log_errors(LOG_ERR, "Generating identity key"); return 1; } identity_key = EVP_PKEY_new(); @@ -289,7 +290,7 @@ load_identity_key(void) NULL, NULL)) { log_err(LD_GENERAL, "Couldn't write identity key to %s", identity_key_file); - crypto_log_errors(LOG_ERR, "Writing identity key"); + crypto_openssl_log_errors(LOG_ERR, "Writing identity key"); abort_writing_to_file(open_file); return 1; } @@ -354,7 +355,7 @@ generate_signing_key(void) SIGNING_KEY_BITS); if (!(key = generate_key(SIGNING_KEY_BITS))) { log_err(LD_GENERAL, "Couldn't generate signing key."); - crypto_log_errors(LOG_ERR, "Generating signing key"); + crypto_openssl_log_errors(LOG_ERR, "Generating signing key"); return 1; } signing_key = EVP_PKEY_new(); @@ -370,7 +371,7 @@ generate_signing_key(void) /* Write signing key with no encryption. */ if (!PEM_write_RSAPrivateKey(f, key, NULL, NULL, 0, NULL, NULL)) { - crypto_log_errors(LOG_WARN, "writing signing key"); + crypto_openssl_log_errors(LOG_WARN, "writing signing key"); abort_writing_to_file(open_file); return 1; } @@ -394,7 +395,7 @@ key_to_string(EVP_PKEY *key) b = BIO_new(BIO_s_mem()); if (!PEM_write_bio_RSAPublicKey(b, rsa)) { - crypto_log_errors(LOG_WARN, "writing public key to string"); + crypto_openssl_log_errors(LOG_WARN, "writing public key to string"); RSA_free(rsa); return NULL; } From 9010797e632065ae073cd69f6b76a2d55c06160d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 14:02:23 -0400 Subject: [PATCH 06/76] Remove most includes from crypto.c --- src/lib/crypt_ops/crypto.c | 49 -------------------------------------- 1 file changed, 49 deletions(-) diff --git a/src/lib/crypt_ops/crypto.c b/src/lib/crypt_ops/crypto.c index e4f092c80c..52ffec4ca8 100644 --- a/src/lib/crypt_ops/crypto.c +++ b/src/lib/crypt_ops/crypto.c @@ -13,63 +13,14 @@ #include "orconfig.h" -#ifdef _WIN32 -#include -#include -#include -/* Windows defines this; so does OpenSSL 0.9.8h and later. We don't actually - * use either definition. */ -#undef OCSP_RESPONSE -#endif /* defined(_WIN32) */ - -#define CRYPTO_PRIVATE -#include "lib/crypt_ops/compat_openssl.h" #include "lib/crypt_ops/crypto.h" -#include "lib/crypt_ops/crypto_curve25519.h" -#include "lib/crypt_ops/crypto_digest.h" -#include "lib/crypt_ops/crypto_dh.h" -#include "lib/crypt_ops/crypto_ed25519.h" -#include "lib/crypt_ops/crypto_format.h" #include "lib/crypt_ops/crypto_rand.h" -#include "lib/crypt_ops/crypto_rsa.h" #include "lib/crypt_ops/crypto_util.h" -DISABLE_GCC_WARNING(redundant-decls) - -#include -#include -#include -#include -#include -#include -#include - -ENABLE_GCC_WARNING(redundant-decls) - -#if __GNUC__ && GCC_VERSION >= 402 -#if GCC_VERSION >= 406 -#pragma GCC diagnostic pop -#else -#pragma GCC diagnostic warning "-Wredundant-decls" -#endif -#endif /* __GNUC__ && GCC_VERSION >= 402 */ - -#ifdef HAVE_CTYPE_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif - #include "lib/log/log.h" #include "lib/log/util_bug.h" #include "lib/cc/torint.h" #include "lib/crypt_ops/aes.h" -#include "lib/encoding/binascii.h" - -#include "keccak-tiny/keccak-tiny.h" - -#include "siphash.h" #include From f45107e7de7ff15c630dedcdd1f9bc524423838f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 14:08:22 -0400 Subject: [PATCH 07/76] Rename crypto.c to crypto_cipher.c (since that's all it still has.) --- src/core/crypto/onion_ntor.c | 2 +- src/core/crypto/relay_crypto.c | 2 +- src/core/or/or.h | 3 ++- src/feature/nodelist/parsecommon.c | 2 +- src/feature/rend/rendmid.c | 2 +- src/lib/crypt_ops/{crypto.c => crypto_cipher.c} | 8 +++----- src/lib/crypt_ops/{crypto.h => crypto_cipher.h} | 13 ++++--------- src/lib/crypt_ops/crypto_pwbox.c | 2 +- src/lib/crypt_ops/crypto_rsa.c | 2 +- src/lib/crypt_ops/crypto_rsa.h | 4 ++++ src/lib/crypt_ops/crypto_s2k.c | 2 +- src/lib/crypt_ops/include.am | 4 ++-- src/lib/tls/tortls.c | 2 +- src/test/fuzz/fuzzing_common.c | 2 +- src/test/test_hs_client.c | 2 +- src/test/test_hs_ntor_cl.c | 2 +- src/test/test_introduce.c | 2 +- src/test/test_ntor_cl.c | 2 +- src/test/test_relaycell.c | 2 +- src/test/test_routerkeys.c | 2 +- src/test/test_util_slow.c | 2 +- src/tools/tor-gencert.c | 2 +- 22 files changed, 32 insertions(+), 34 deletions(-) rename src/lib/crypt_ops/{crypto.c => crypto_cipher.c} (96%) rename src/lib/crypt_ops/{crypto.h => crypto_cipher.h} (88%) diff --git a/src/core/crypto/onion_ntor.c b/src/core/crypto/onion_ntor.c index ea7261cb53..3614e0c9b1 100644 --- a/src/core/crypto/onion_ntor.c +++ b/src/core/crypto/onion_ntor.c @@ -22,7 +22,7 @@ #define ONION_NTOR_PRIVATE -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_digest.h" #include "lib/crypt_ops/crypto_hkdf.h" #include "lib/crypt_ops/crypto_util.h" diff --git a/src/core/crypto/relay_crypto.c b/src/core/crypto/relay_crypto.c index b2388d2c45..311160a669 100644 --- a/src/core/crypto/relay_crypto.c +++ b/src/core/crypto/relay_crypto.c @@ -7,7 +7,7 @@ #include "core/or/or.h" #include "core/or/circuitlist.h" #include "app/config/config.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_util.h" #include "core/crypto/hs_ntor.h" // for HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN #include "core/or/relay.h" diff --git a/src/core/or/or.h b/src/core/or/or.h index 9aca030c74..13376366ac 100644 --- a/src/core/or/or.h +++ b/src/core/or/or.h @@ -27,7 +27,8 @@ #include "lib/cc/torint.h" #include "lib/container/map.h" #include "lib/container/smartlist.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" +#include "lib/crypt_ops/crypto_rsa.h" #include "lib/ctime/di_ops.h" #include "lib/defs/dh_sizes.h" #include "lib/encoding/binascii.h" diff --git a/src/feature/nodelist/parsecommon.c b/src/feature/nodelist/parsecommon.c index 3aaf8ac501..e05657aca0 100644 --- a/src/feature/nodelist/parsecommon.c +++ b/src/feature/nodelist/parsecommon.c @@ -14,7 +14,7 @@ #include "lib/string/util_string.h" #include "lib/string/printf.h" #include "lib/memarea/memarea.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_rsa.h" #include diff --git a/src/feature/rend/rendmid.c b/src/feature/rend/rendmid.c index 22cd6c3435..f433e7ce5b 100644 --- a/src/feature/rend/rendmid.c +++ b/src/feature/rend/rendmid.c @@ -12,7 +12,7 @@ #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "app/config/config.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "core/or/dos.h" #include "core/or/relay.h" #include "feature/rend/rendmid.h" diff --git a/src/lib/crypt_ops/crypto.c b/src/lib/crypt_ops/crypto_cipher.c similarity index 96% rename from src/lib/crypt_ops/crypto.c rename to src/lib/crypt_ops/crypto_cipher.c index 52ffec4ca8..6b762e374d 100644 --- a/src/lib/crypt_ops/crypto.c +++ b/src/lib/crypt_ops/crypto_cipher.c @@ -5,15 +5,13 @@ /* See LICENSE for licensing information */ /** - * \file crypto.c - * \brief Wrapper functions to present a consistent interface to - * public-key and symmetric cryptography operations from OpenSSL and - * other places. + * \file crypto_cipher.c + * \brief Symmetric cryptography (low-level) with AES. **/ #include "orconfig.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" diff --git a/src/lib/crypt_ops/crypto.h b/src/lib/crypt_ops/crypto_cipher.h similarity index 88% rename from src/lib/crypt_ops/crypto.h rename to src/lib/crypt_ops/crypto_cipher.h index 993a65fb01..f9444d03fc 100644 --- a/src/lib/crypt_ops/crypto.h +++ b/src/lib/crypt_ops/crypto_cipher.h @@ -5,19 +5,18 @@ /* See LICENSE for licensing information */ /** - * \file crypto.h + * \file crypto_cipher.h * - * \brief Headers for crypto.c + * \brief Headers for crypto_cipher.c **/ -#ifndef TOR_CRYPTO_H -#define TOR_CRYPTO_H +#ifndef TOR_CRYPTO_CIPHER_H +#define TOR_CRYPTO_CIPHER_H #include "orconfig.h" #include #include "lib/cc/torint.h" -#include "lib/crypt_ops/crypto_rsa.h" /** Length of our symmetric cipher's keys of 128-bit. */ #define CIPHER_KEY_LEN 16 @@ -26,10 +25,6 @@ /** Length of our symmetric cipher's keys of 256-bit. */ #define CIPHER256_KEY_LEN 32 -/** Length of encoded public key fingerprints, including space; but not - * including terminating NUL. */ -#define FINGERPRINT_LEN 49 - typedef struct aes_cnt_cipher crypto_cipher_t; /* environment setup */ diff --git a/src/lib/crypt_ops/crypto_pwbox.c b/src/lib/crypt_ops/crypto_pwbox.c index c001e295da..2377f216a0 100644 --- a/src/lib/crypt_ops/crypto_pwbox.c +++ b/src/lib/crypt_ops/crypto_pwbox.c @@ -11,7 +11,7 @@ #include #include "lib/arch/bytes.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_digest.h" #include "lib/crypt_ops/crypto_pwbox.h" #include "lib/crypt_ops/crypto_rand.h" diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c index d8a9be7ec9..ffe2bd2ceb 100644 --- a/src/lib/crypt_ops/crypto_rsa.c +++ b/src/lib/crypt_ops/crypto_rsa.c @@ -9,7 +9,7 @@ * \brief Block of functions related with RSA utilities and operations. **/ -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_digest.h" #include "lib/crypt_ops/crypto_format.h" diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index c70e01c3f4..45412d21ec 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -29,6 +29,10 @@ /** Number of bytes added for PKCS1-OAEP padding. */ #define PKCS1_OAEP_PADDING_OVERHEAD 42 +/** Length of encoded public key fingerprints, including space; but not + * including terminating NUL. */ +#define FINGERPRINT_LEN 49 + /** A public key, or a public/private key-pair. */ typedef struct crypto_pk_t crypto_pk_t; diff --git a/src/lib/crypt_ops/crypto_s2k.c b/src/lib/crypt_ops/crypto_s2k.c index ab91d92f0e..0e151f0a6c 100644 --- a/src/lib/crypt_ops/crypto_s2k.c +++ b/src/lib/crypt_ops/crypto_s2k.c @@ -12,7 +12,7 @@ #define CRYPTO_S2K_PRIVATE -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_digest.h" #include "lib/crypt_ops/crypto_hkdf.h" #include "lib/crypt_ops/crypto_rand.h" diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 6bb7c929a9..1caa3fdbc3 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -7,7 +7,7 @@ endif src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/aes.c \ - src/lib/crypt_ops/crypto.c \ + src/lib/crypt_ops/crypto_cipher.c \ src/lib/crypt_ops/crypto_curve25519.c \ src/lib/crypt_ops/crypto_dh.c \ src/lib/crypt_ops/crypto_digest.c \ @@ -39,7 +39,7 @@ noinst_HEADERS += \ src/lib/crypt_ops/crypto_digest.h \ src/lib/crypt_ops/crypto_ed25519.h \ src/lib/crypt_ops/crypto_format.h \ - src/lib/crypt_ops/crypto.h \ + src/lib/crypt_ops/crypto_cipher.h \ src/lib/crypt_ops/crypto_hkdf.h \ src/lib/crypt_ops/crypto_init.h \ src/lib/crypt_ops/crypto_openssl_mgt.h \ diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 073cae02ba..8d284dde17 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -24,7 +24,7 @@ #include #endif -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_dh.h" #include "lib/crypt_ops/crypto_util.h" diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c index 0b3483bf66..1222162bfa 100644 --- a/src/test/fuzz/fuzzing_common.c +++ b/src/test/fuzz/fuzzing_common.c @@ -7,7 +7,7 @@ #include "app/config/config.h" #include "test/fuzz/fuzzing.h" #include "lib/compress/compress.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_ed25519.h" static or_options_t *mock_options = NULL; diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 57da03ca28..7fcc1db195 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -21,7 +21,7 @@ #include "test/hs_test_helpers.h" #include "app/config/config.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_dh.h" #include "core/or/channeltls.h" #include "feature/dircache/directory.h" diff --git a/src/test/test_hs_ntor_cl.c b/src/test/test_hs_ntor_cl.c index 03e34968be..c207741794 100644 --- a/src/test/test_hs_ntor_cl.c +++ b/src/test/test_hs_ntor_cl.c @@ -14,7 +14,7 @@ #define ONION_NTOR_PRIVATE #include "core/or/or.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_ed25519.h" #include "lib/crypt_ops/crypto_format.h" diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c index cdfb70bdff..4d2d909945 100644 --- a/src/test/test_introduce.c +++ b/src/test/test_introduce.c @@ -2,7 +2,7 @@ /* See LICENSE for licensing information */ #include "orconfig.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "core/or/or.h" #include "test/test.h" diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c index 744b42c9d9..b8d3a8b426 100644 --- a/src/test/test_ntor_cl.c +++ b/src/test/test_ntor_cl.c @@ -7,7 +7,7 @@ #define ONION_NTOR_PRIVATE #include "core/or/or.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_curve25519.h" #include "core/crypto/onion_ntor.h" diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c index eb30cab0ec..a6c152f738 100644 --- a/src/test/test_relaycell.c +++ b/src/test/test_relaycell.c @@ -9,7 +9,7 @@ #include "core/mainloop/main.h" #include "app/config/config.h" #include "core/mainloop/connection.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/connection_edge.h" diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 1a1bf63ba0..82a80130e0 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -9,7 +9,7 @@ #include "app/config/config.h" #include "feature/relay/router.h" #include "feature/relay/routerkeys.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_format.h" #include "feature/nodelist/torcert.h" #include "test/test.h" diff --git a/src/test/test_util_slow.c b/src/test/test_util_slow.c index c5b24487b1..c7b3e3e2a4 100644 --- a/src/test/test_util_slow.c +++ b/src/test/test_util_slow.c @@ -6,7 +6,7 @@ #include "orconfig.h" #define UTIL_PRIVATE #define SUBPROCESS_PRIVATE -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/log/log.h" #include "lib/process/subprocess.h" #include "lib/process/waitpid.h" diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index 7e54483b28..63e24d922c 100644 --- a/src/tools/tor-gencert.c +++ b/src/tools/tor-gencert.c @@ -33,9 +33,9 @@ ENABLE_GCC_WARNING(redundant-decls) #include -#include "lib/crypt_ops/crypto.h" #include "lib/crypt_ops/crypto_digest.h" #include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_rsa.h" #include "lib/crypt_ops/crypto_util.h" #include "lib/encoding/binascii.h" #include "lib/encoding/time_fmt.h" From c317e78dd75b6bf9c195c9bef5a0b8300d55411a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 14:46:28 -0400 Subject: [PATCH 08/76] Initialize and shut down NSS. This is largely conjectural, based on online documentation for NSS and NSPR. --- src/lib/crypt_ops/crypto_init.c | 18 ++++- src/lib/crypt_ops/crypto_nss_mgt.c | 95 ++++++++++++++++++++++++++ src/lib/crypt_ops/crypto_nss_mgt.h | 31 +++++++++ src/lib/crypt_ops/crypto_openssl_mgt.c | 11 ++- src/lib/crypt_ops/crypto_openssl_mgt.h | 8 ++- src/lib/crypt_ops/include.am | 10 ++- 6 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 src/lib/crypt_ops/crypto_nss_mgt.c create mode 100644 src/lib/crypt_ops/crypto_nss_mgt.h diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c index 01d5baf5be..7f5a63219b 100644 --- a/src/lib/crypt_ops/crypto_init.c +++ b/src/lib/crypt_ops/crypto_init.c @@ -18,6 +18,7 @@ #include "lib/crypt_ops/crypto_dh.h" #include "lib/crypt_ops/crypto_ed25519.h" #include "lib/crypt_ops/crypto_openssl_mgt.h" +#include "lib/crypt_ops/crypto_nss_mgt.h" #include "lib/crypt_ops/crypto_rand.h" #include "siphash.h" @@ -56,6 +57,9 @@ crypto_early_init(void) #ifdef ENABLE_OPENSSL crypto_openssl_early_init(); #endif +#ifdef ENABLE_NSS + crypto_nss_early_init(); +#endif if (crypto_seed_rng() < 0) return -1; @@ -80,7 +84,12 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) crypto_global_initialized_ = 1; #ifdef ENABLE_OPENSSL - return crypto_openssl_late_init(useAccel, accelName, accelDir); + if (crypto_openssl_late_init(useAccel, accelName, accelDir) < 0) + return -1; +#endif +#ifdef ENABLE_NSS + if (crypto_nss_late_init() < 0) + return -1; #endif } return 0; @@ -90,8 +99,8 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) void crypto_thread_cleanup(void) { -#ifndef NEW_THREAD_API - ERR_remove_thread_state(NULL); +#ifdef ENABLE_OPENSSL + crypto_openssl_thread_cleanup(); #endif } @@ -107,6 +116,9 @@ crypto_global_cleanup(void) #ifdef ENABLE_OPENSSL crypto_openssl_global_cleanup(); #endif +#ifdef ENABLE_NSS + crypto_nss_global_cleanup(); +#endif crypto_early_initialized_ = 0; crypto_global_initialized_ = 0; diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c new file mode 100644 index 0000000000..84d9f027a4 --- /dev/null +++ b/src/lib/crypt_ops/crypto_nss_mgt.c @@ -0,0 +1,95 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_nss_mgt.c + * + * \brief Manage the NSS library (if used) + **/ + +#include "lib/crypt_ops/crypto_nss_mgt.h" + +#include "lib/log/log.h" +#include "lib/log/util_bug.h" + +#include +#include +#include + +#include +#include +#include + +const char * +crypto_nss_get_version_str(void) +{ + return NSS_GetVersion(); +} +const char * +crypto_nss_get_header_version_str(void) +{ + return NSS_VERSION; +} + +/** A password function that always returns NULL. */ +static char * +nss_password_func_always_fail(PK11SlotInfo *slot, + PRBool retry, + void *arg) +{ + (void) slot; + (void) retry; + (void) arg; + return NULL; +} + +void +crypto_nss_early_init(void) +{ + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PK11_SetPasswordFunc(nss_password_func_always_fail); + + /* Eventually we should use NSS_Init() instead -- but that wants a + directory. The documentation says that we can't use this if we want + to use OpenSSL. */ + if (NSS_NoDB_Init(NULL) == SECFailure) { + log_err(LD_CRYPTO, "Unable to initialize NSS."); + crypto_nss_log_errors(LOG_ERR, "initializing NSS"); + tor_assert_unreached(); + } + + if (NSS_SetDomesticPolicy() == SECFailure) { + log_err(LD_CRYPTO, "Unable to set NSS cipher policy."); + crypto_nss_log_errors(LOG_ERR, "setting cipher policy"); + tor_assert_unreached(); + } +} + +void +crypto_nss_log_errors(int severity, const char *doing) +{ + PRErrorCode code = PR_GetError(); + /* XXXX how do I convert errors to strings? */ + if (doing) { + tor_log(severity, LD_CRYPTO, "NSS error %u while %s", code, doing); + } else { + tor_log(severity, LD_CRYPTO, "NSS error %u", code); + } +} + +int +crypto_nss_late_init(void) +{ + /* Possibly, SSL_OptionSetDefault? */ + + return 0; +} + +void +crypto_nss_global_cleanup(void) +{ + NSS_Shutdown(); +} diff --git a/src/lib/crypt_ops/crypto_nss_mgt.h b/src/lib/crypt_ops/crypto_nss_mgt.h new file mode 100644 index 0000000000..0e899bad06 --- /dev/null +++ b/src/lib/crypt_ops/crypto_nss_mgt.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_nss_mgt.h + * + * \brief Headers for crypto_nss_mgt.c + **/ + +#ifndef TOR_CRYPTO_NSS_MGT_H +#define TOR_CRYPTO_NSS_MGT_H + +#include "orconfig.h" + +#ifdef ENABLE_NSS +/* global nss state */ +const char *crypto_nss_get_version_str(void); +const char *crypto_nss_get_header_version_str(void); + +void crypto_nss_log_errors(int severity, const char *doing); + +void crypto_nss_early_init(void); +int crypto_nss_late_init(void); + +void crypto_nss_global_cleanup(void); +#endif + +#endif /* !defined(TOR_CRYPTO_NSS_H) */ diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.c b/src/lib/crypt_ops/crypto_openssl_mgt.c index bf5b96f9a5..125da0786b 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.c +++ b/src/lib/crypt_ops/crypto_openssl_mgt.c @@ -153,7 +153,7 @@ tor_set_openssl_thread_id(CRYPTO_THREADID *threadid) /** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being * multithreaded. Returns 0. */ -int +static int setup_openssl_threading(void) { #ifndef NEW_THREAD_API @@ -352,6 +352,15 @@ crypto_openssl_late_init(int useAccel, const char *accelName, return 0; } +/** Free crypto resources held by this thread. */ +void +crypto_openssl_thread_cleanup(void) +{ +#ifndef NEW_THREAD_API + ERR_remove_thread_state(NULL); +#endif +} + /** Clean up global resources held by openssl. */ void crypto_openssl_global_cleanup(void) diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.h b/src/lib/crypt_ops/crypto_openssl_mgt.h index 417bb36f01..3b288fb9d8 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.h +++ b/src/lib/crypt_ops/crypto_openssl_mgt.h @@ -14,6 +14,8 @@ #define TOR_CRYPTO_OPENSSL_H #include "orconfig.h" + +#ifdef ENABLE_OPENSSL #include /* @@ -75,13 +77,13 @@ void crypto_openssl_log_errors(int severity, const char *doing); const char * crypto_openssl_get_version_str(void); const char * crypto_openssl_get_header_version_str(void); -/* OpenSSL threading setup function */ -int setup_openssl_threading(void); - void crypto_openssl_early_init(void); int crypto_openssl_late_init(int useAccel, const char *accelName, const char *accelDir); +void crypto_openssl_thread_cleanup(void); void crypto_openssl_global_cleanup(void); +#endif /* ENABLE_OPENSSL */ + #endif /* !defined(TOR_CRYPTO_OPENSSL_H) */ diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 1caa3fdbc3..e96d6b0a5c 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -15,7 +15,6 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/crypto_format.c \ src/lib/crypt_ops/crypto_hkdf.c \ src/lib/crypt_ops/crypto_init.c \ - src/lib/crypt_ops/crypto_openssl_mgt.c \ src/lib/crypt_ops/crypto_pwbox.c \ src/lib/crypt_ops/crypto_rand.c \ src/lib/crypt_ops/crypto_rsa.c \ @@ -23,6 +22,15 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/crypto_util.c \ src/lib/crypt_ops/digestset.c +if USE_NSS +src_lib_libtor_crypt_ops_a_SOURCES += \ + src/lib/crypt_ops/crypto_nss_mgt.c +endif +if USE_OPENSSL +src_lib_libtor_crypt_ops_a_SOURCES += \ + src/lib/crypt_ops/crypto_openssl_mgt.c +endif + src_lib_libtor_crypt_ops_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) src_lib_libtor_crypt_ops_testing_a_SOURCES = \ From d811ce2421dcf1684db7e34f2b5998d9f360d5fb Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 15:36:54 -0400 Subject: [PATCH 09/76] Add postfork support for nss We need this in our unit tests, since otherwise NSS will notice we've forked and start cussing us out. I suspect we'll need a different hack for daemonizing, but this should be enough for tinytest to work. --- src/ext/tinytest.c | 8 ++++++++ src/lib/crypt_ops/crypto_init.c | 10 ++++++++++ src/lib/crypt_ops/crypto_init.h | 1 + src/lib/crypt_ops/crypto_nss_mgt.c | 7 +++++++ src/lib/crypt_ops/crypto_nss_mgt.h | 2 ++ src/test/testing_common.c | 7 +++++++ 6 files changed, 35 insertions(+) diff --git a/src/ext/tinytest.c b/src/ext/tinytest.c index 3fb1b39c71..8b2c71bebf 100644 --- a/src/ext/tinytest.c +++ b/src/ext/tinytest.c @@ -25,6 +25,7 @@ #ifdef TINYTEST_LOCAL #include "tinytest_local.h" #endif +#define TINYTEST_POSTFORK #include #include @@ -118,6 +119,12 @@ testcase_run_bare_(const struct testcase_t *testcase) #ifndef NO_FORKING +#ifdef TINYTEST_POSTFORK +void tinytest_postfork(void); +#else +static void tinytest_postfork(void) { } +#endif + static enum outcome testcase_run_forked_(const struct testgroup_t *group, const struct testcase_t *testcase) @@ -187,6 +194,7 @@ testcase_run_forked_(const struct testgroup_t *group, int test_r, write_r; char b[1]; close(outcome_pipe[0]); + tinytest_postfork(); test_r = testcase_run_bare_(testcase); assert(0<=(int)test_r && (int)test_r<=2); b[0] = "NYS"[test_r]; diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c index 7f5a63219b..b651474cf8 100644 --- a/src/lib/crypt_ops/crypto_init.c +++ b/src/lib/crypt_ops/crypto_init.c @@ -127,3 +127,13 @@ crypto_global_cleanup(void) return 0; } + +/** Run operations that the crypto library requires to be happy again + * after forking. */ +void +crypto_postfork(void) +{ +#ifdef ENABLE_NSS + crypto_nss_postfork(); +#endif +} diff --git a/src/lib/crypt_ops/crypto_init.h b/src/lib/crypt_ops/crypto_init.h index e450e2d894..3e32456b5c 100644 --- a/src/lib/crypt_ops/crypto_init.h +++ b/src/lib/crypt_ops/crypto_init.h @@ -24,5 +24,6 @@ int crypto_global_init(int hardwareAccel, void crypto_thread_cleanup(void); int crypto_global_cleanup(void); +void crypto_postfork(void); #endif /* !defined(TOR_CRYPTO_H) */ diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c index 84d9f027a4..6bcaeabd5a 100644 --- a/src/lib/crypt_ops/crypto_nss_mgt.c +++ b/src/lib/crypt_ops/crypto_nss_mgt.c @@ -93,3 +93,10 @@ crypto_nss_global_cleanup(void) { NSS_Shutdown(); } + +void +crypto_nss_postfork(void) +{ + crypto_nss_global_cleanup(); + crypto_nss_early_init(); +} diff --git a/src/lib/crypt_ops/crypto_nss_mgt.h b/src/lib/crypt_ops/crypto_nss_mgt.h index 0e899bad06..c4c94f4d89 100644 --- a/src/lib/crypt_ops/crypto_nss_mgt.h +++ b/src/lib/crypt_ops/crypto_nss_mgt.h @@ -26,6 +26,8 @@ void crypto_nss_early_init(void); int crypto_nss_late_init(void); void crypto_nss_global_cleanup(void); + +void crypto_nss_postfork(void); #endif #endif /* !defined(TOR_CRYPTO_NSS_H) */ diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 32d7bf7f0e..1611a54b6a 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -223,6 +223,13 @@ an_assertion_failed(void) tinytest_set_test_failed_(); } +void tinytest_postfork(void); +void +tinytest_postfork(void) +{ + crypto_postfork(); +} + /** Main entry point for unit test code: parse the command line, and run * some unit tests. */ int From 27dd2b1f1ff612977ec50c9e15dbe14607b0551b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 16:15:42 -0400 Subject: [PATCH 10/76] Make nss get initialized before we fuzz anything. --- src/test/fuzz/fuzzing_common.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c index 1222162bfa..1401e4c28d 100644 --- a/src/test/fuzz/fuzzing_common.c +++ b/src/test/fuzz/fuzzing_common.c @@ -7,8 +7,8 @@ #include "app/config/config.h" #include "test/fuzz/fuzzing.h" #include "lib/compress/compress.h" -#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_ed25519.h" +#include "lib/crypt_ops/crypto_init.h" static or_options_t *mock_options = NULL; static const or_options_t * @@ -96,15 +96,20 @@ global_init(void) { tor_threads_init(); tor_compress_init(); - { - struct sipkey sipkey = { 1337, 7331 }; - siphash_set_global_key(&sipkey); - } /* Initialise logging first */ init_logging(1); configure_backtrace_handler(get_version()); + if (crypto_global_init(0, NULL, NULL) < 0) + abort(); + + { + struct sipkey sipkey = { 1337, 7331 }; + siphash_unset_global_key(); + siphash_set_global_key(&sipkey); + } + /* set up the options. */ mock_options = tor_malloc_zero(sizeof(or_options_t)); MOCK(get_options, mock_get_options); From f64c9dccde9ac8261c9f80ed063acc7988d6836c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 11 Jul 2018 16:54:05 -0400 Subject: [PATCH 11/76] Use NSS's digest code in Tor. This was a fairly straightforward port, once I realized which layer I should be calling into. --- src/lib/crypt_ops/crypto_digest.c | 258 ++++++++++++++++++++++++++++-- src/lib/crypt_ops/crypto_digest.h | 3 + src/test/test_hs_ntor_cl.c | 5 + src/test/test_ntor_cl.c | 6 +- 4 files changed, 262 insertions(+), 10 deletions(-) diff --git a/src/lib/crypt_ops/crypto_digest.c b/src/lib/crypt_ops/crypto_digest.c index ee5c362e38..2bc5f08342 100644 --- a/src/lib/crypt_ops/crypto_digest.c +++ b/src/lib/crypt_ops/crypto_digest.c @@ -12,7 +12,6 @@ #include "lib/container/smartlist.h" #include "lib/crypt_ops/crypto_digest.h" -#include "lib/crypt_ops/crypto_openssl_mgt.h" #include "lib/crypt_ops/crypto_util.h" #include "lib/log/log.h" #include "lib/log/util_bug.h" @@ -24,12 +23,92 @@ #include "lib/arch/bytes.h" +#ifdef ENABLE_NSS +DISABLE_GCC_WARNING(strict-prototypes) +#include +ENABLE_GCC_WARNING(strict-prototypes) +#else + +#include "lib/crypt_ops/crypto_openssl_mgt.h" + DISABLE_GCC_WARNING(redundant-decls) #include #include ENABLE_GCC_WARNING(redundant-decls) +#endif + +#ifdef ENABLE_NSS +/** + * Convert a digest_algorithm_t (used by tor) to a HashType (used by NSS). + * On failure, return SEC_OID_UNKNOWN. */ +static SECOidTag +digest_alg_to_nss_oid(digest_algorithm_t alg) +{ + switch (alg) { + case DIGEST_SHA1: return SEC_OID_SHA1; + case DIGEST_SHA256: return SEC_OID_SHA256; + case DIGEST_SHA512: return SEC_OID_SHA512; + case DIGEST_SHA3_256: /* Fall through */ + case DIGEST_SHA3_512: /* Fall through */ + default: + return SEC_OID_UNKNOWN; + } +} + +/* Helper: get an unkeyed digest via pk11wrap */ +static int +digest_nss_internal(SECOidTag alg, + char *digest, unsigned len_out, + const char *msg, size_t msg_len) +{ + if (alg == SEC_OID_UNKNOWN) + return -1; + tor_assert(msg_len <= UINT_MAX); + + int rv = -1; + SECStatus s; + PK11Context *ctx = PK11_CreateDigestContext(alg); + if (!ctx) + return -1; + + s = PK11_DigestBegin(ctx); + if (s != SECSuccess) + goto done; + + s = PK11_DigestOp(ctx, (const unsigned char *)msg, (unsigned int)msg_len); + if (s != SECSuccess) + goto done; + + unsigned int len = 0; + s = PK11_DigestFinal(ctx, (unsigned char *)digest, &len, len_out); + if (s != SECSuccess) + goto done; + + rv = 0; + done: + PK11_DestroyContext(ctx, PR_TRUE); + return rv; +} + +/** True iff alg is implemented in our crypto library, and we want to use that + * implementation */ +static bool +library_supports_digest(digest_algorithm_t alg) +{ + switch (alg) { + case DIGEST_SHA1: /* Fall through */ + case DIGEST_SHA256: /* Fall through */ + case DIGEST_SHA512: /* Fall through */ + return true; + case DIGEST_SHA3_256: /* Fall through */ + case DIGEST_SHA3_512: /* Fall through */ + default: + return false; + } +} +#endif /* Crypto digest functions */ @@ -42,8 +121,13 @@ crypto_digest(char *digest, const char *m, size_t len) { tor_assert(m); tor_assert(digest); - if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL) +#ifdef ENABLE_NSS + return digest_nss_internal(SEC_OID_SHA1, digest, DIGEST_LEN, m, len); +#else + if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL) { return -1; + } +#endif return 0; } @@ -59,11 +143,16 @@ crypto_digest256(char *digest, const char *m, size_t len, tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256); int ret = 0; - if (algorithm == DIGEST_SHA256) + if (algorithm == DIGEST_SHA256) { +#ifdef ENABLE_NSS + return digest_nss_internal(SEC_OID_SHA256, digest, DIGEST256_LEN, m, len); +#else ret = (SHA256((const uint8_t*)m,len,(uint8_t*)digest) != NULL); - else +#endif + } else { ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len) > -1); + } if (!ret) return -1; @@ -82,12 +171,17 @@ crypto_digest512(char *digest, const char *m, size_t len, tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512); int ret = 0; - if (algorithm == DIGEST_SHA512) + if (algorithm == DIGEST_SHA512) { +#ifdef ENABLE_NSS + return digest_nss_internal(SEC_OID_SHA512, digest, DIGEST512_LEN, m, len); +#else ret = (SHA512((const unsigned char*)m,len,(unsigned char*)digest) != NULL); - else +#endif + } else { ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len) > -1); + } if (!ret) return -1; @@ -181,9 +275,13 @@ struct crypto_digest_t { * that space for other members might not even be allocated! */ union { +#ifdef ENABLE_NSS + PK11Context *ctx; +#else SHA_CTX sha1; /**< state for SHA1 */ SHA256_CTX sha2; /**< state for SHA256 */ SHA512_CTX sha512; /**< state for SHA512 */ +#endif keccak_state sha3; /**< state for SHA3-[256,512] */ } d; }; @@ -214,12 +312,19 @@ crypto_digest_alloc_bytes(digest_algorithm_t alg) #define END_OF_FIELD(f) (offsetof(crypto_digest_t, f) + \ STRUCT_FIELD_SIZE(crypto_digest_t, f)) switch (alg) { +#ifdef ENABLE_NSS + case DIGEST_SHA1: /* Fall through */ + case DIGEST_SHA256: /* Fall through */ + case DIGEST_SHA512: + return END_OF_FIELD(d.ctx); +#else case DIGEST_SHA1: return END_OF_FIELD(d.sha1); case DIGEST_SHA256: return END_OF_FIELD(d.sha2); case DIGEST_SHA512: return END_OF_FIELD(d.sha512); +#endif case DIGEST_SHA3_256: case DIGEST_SHA3_512: return END_OF_FIELD(d.sha3); @@ -243,6 +348,21 @@ crypto_digest_new_internal(digest_algorithm_t algorithm) switch (algorithm) { +#ifdef ENABLE_NSS + case DIGEST_SHA1: /* fall through */ + case DIGEST_SHA256: /* fall through */ + case DIGEST_SHA512: + r->d.ctx = PK11_CreateDigestContext(digest_alg_to_nss_oid(algorithm)); + if (BUG(!r->d.ctx)) { + tor_free(r); + return NULL; + } + if (BUG(SECSuccess != PK11_DigestBegin(r->d.ctx))) { + crypto_digest_free(r); + return NULL; + } + break; +#else case DIGEST_SHA1: SHA1_Init(&r->d.sha1); break; @@ -252,6 +372,7 @@ crypto_digest_new_internal(digest_algorithm_t algorithm) case DIGEST_SHA512: SHA512_Init(&r->d.sha512); break; +#endif case DIGEST_SHA3_256: keccak_digest_init(&r->d.sha3, 256); break; @@ -302,6 +423,11 @@ crypto_digest_free_(crypto_digest_t *digest) { if (!digest) return; +#ifdef ENABLE_NSS + if (library_supports_digest(digest->algorithm)) { + PK11_DestroyContext(digest->d.ctx, PR_TRUE); + } +#endif size_t bytes = crypto_digest_alloc_bytes(digest->algorithm); memwipe(digest, 0, bytes); tor_free(digest); @@ -324,6 +450,17 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, * just doing it ourselves. Hashes are fast. */ switch (digest->algorithm) { +#ifdef ENABLE_NSS + case DIGEST_SHA1: /* fall through */ + case DIGEST_SHA256: /* fall through */ + case DIGEST_SHA512: + tor_assert(len <= UINT_MAX); + SECStatus s = PK11_DigestOp(digest->d.ctx, + (const unsigned char *)data, + (unsigned int)len); + tor_assert(s == SECSuccess); + break; +#else case DIGEST_SHA1: SHA1_Update(&digest->d.sha1, (void*)data, len); break; @@ -333,6 +470,7 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, case DIGEST_SHA512: SHA512_Update(&digest->d.sha512, (void*)data, len); break; +#endif case DIGEST_SHA3_256: /* FALLSTHROUGH */ case DIGEST_SHA3_512: keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len); @@ -357,7 +495,6 @@ crypto_digest_get_digest(crypto_digest_t *digest, char *out, size_t out_len) { unsigned char r[DIGEST512_LEN]; - crypto_digest_t tmpenv; tor_assert(digest); tor_assert(out); tor_assert(out_len <= crypto_digest_algorithm_get_length(digest->algorithm)); @@ -370,7 +507,26 @@ crypto_digest_get_digest(crypto_digest_t *digest, return; } +#ifdef ENABLE_NSS + /* Copy into a temporary buffer since DigestFinal (alters) the context */ + unsigned char buf[1024]; + unsigned int saved_len = 0; + unsigned rlen; + unsigned char *saved = PK11_SaveContextAlloc(digest->d.ctx, + buf, sizeof(buf), + &saved_len); + tor_assert(saved); + SECStatus s = PK11_DigestFinal(digest->d.ctx, r, &rlen, sizeof(r)); + tor_assert(s == SECSuccess); + tor_assert(rlen >= out_len); + s = PK11_RestoreContext(digest->d.ctx, saved, saved_len); + tor_assert(s == SECSuccess); + if (saved != buf) { + PORT_ZFree(saved, saved_len); + } +#else const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm); + crypto_digest_t tmpenv; /* memcpy into a temporary ctx, since SHA*_Final clears the context */ memcpy(&tmpenv, digest, alloc_bytes); switch (digest->algorithm) { @@ -393,6 +549,7 @@ crypto_digest_get_digest(crypto_digest_t *digest, break; //LCOV_EXCL_STOP } +#endif memcpy(out, r, out_len); memwipe(r, 0, sizeof(r)); } @@ -408,7 +565,13 @@ crypto_digest_dup(const crypto_digest_t *digest) { tor_assert(digest); const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm); - return tor_memdup(digest, alloc_bytes); + crypto_digest_t *result = tor_memdup(digest, alloc_bytes); +#ifdef ENABLE_NSS + if (library_supports_digest(digest->algorithm)) { + result->d.ctx = PK11_CloneContext(digest->d.ctx); + } +#endif + return result; } /** Temporarily save the state of digest in checkpoint. @@ -420,6 +583,18 @@ crypto_digest_checkpoint(crypto_digest_checkpoint_t *checkpoint, { const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm); tor_assert(bytes <= sizeof(checkpoint->mem)); +#ifdef ENABLE_NSS + if (library_supports_digest(digest->algorithm)) { + unsigned char *allocated; + allocated = PK11_SaveContextAlloc(digest->d.ctx, + (unsigned char *)checkpoint->mem, + sizeof(checkpoint->mem), + &checkpoint->bytes_used); + /* No allocation is allowed here. */ + tor_assert(allocated == checkpoint->mem); + return; + } +#endif memcpy(checkpoint->mem, digest, bytes); } @@ -431,6 +606,15 @@ crypto_digest_restore(crypto_digest_t *digest, const crypto_digest_checkpoint_t *checkpoint) { const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm); +#ifdef ENABLE_NSS + if (library_supports_digest(digest->algorithm)) { + SECStatus s = PK11_RestoreContext(digest->d.ctx, + (unsigned char *)checkpoint->mem, + checkpoint->bytes_used); + tor_assert(s == SECSuccess); + return; + } +#endif memcpy(digest, checkpoint->mem, bytes); } @@ -446,6 +630,13 @@ crypto_digest_assign(crypto_digest_t *into, tor_assert(from); tor_assert(into->algorithm == from->algorithm); const size_t alloc_bytes = crypto_digest_alloc_bytes(from->algorithm); +#ifdef ENABLE_NSS + if (library_supports_digest(from->algorithm)) { + PK11_DestroyContext(into->d.ctx, PR_TRUE); + into->d.ctx = PK11_CloneContext(from->d.ctx); + return; + } +#endif memcpy(into,from,alloc_bytes); } @@ -496,14 +687,63 @@ crypto_hmac_sha256(char *hmac_out, const char *key, size_t key_len, const char *msg, size_t msg_len) { - unsigned char *rv = NULL; /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */ tor_assert(key_len < INT_MAX); tor_assert(msg_len < INT_MAX); tor_assert(hmac_out); +#ifdef ENABLE_NSS + PK11SlotInfo *slot = NULL; + PK11SymKey *symKey = NULL; + PK11Context *hmac = NULL; + + int ok = 0; + SECStatus s; + SECItem keyItem, paramItem; + keyItem.data = (unsigned char *)key; + keyItem.len = (unsigned)key_len; + paramItem.type = siBuffer; + paramItem.data = NULL; + paramItem.len = 0; + + slot = PK11_GetBestSlot(CKM_SHA256_HMAC, NULL); + if (!slot) + goto done; + symKey = PK11_ImportSymKey(slot, CKM_SHA256_HMAC, + PK11_OriginUnwrap, CKA_SIGN, &keyItem, NULL); + if (!symKey) + goto done; + + hmac = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, symKey, + ¶mItem); + if (!hmac) + goto done; + s = PK11_DigestBegin(hmac); + if (s != SECSuccess) + goto done; + s = PK11_DigestOp(hmac, (const unsigned char *)msg, (unsigned int)msg_len); + if (s != SECSuccess) + goto done; + unsigned int len=0; + s = PK11_DigestFinal(hmac, (unsigned char *)hmac_out, &len, DIGEST256_LEN); + if (s != SECSuccess || len != DIGEST256_LEN) + goto done; + ok = 1; + + done: + if (hmac) + PK11_DestroyContext(hmac, PR_TRUE); + if (symKey) + PK11_FreeSymKey(symKey); + if (slot) + PK11_FreeSlot(slot); + + tor_assert(ok); +#else + unsigned char *rv = NULL; rv = HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len, (unsigned char*)hmac_out, NULL); tor_assert(rv); +#endif } /** Compute a MAC using SHA3-256 of msg_len bytes in msg using a diff --git a/src/lib/crypt_ops/crypto_digest.h b/src/lib/crypt_ops/crypto_digest.h index 9facf3b981..59713d2b9f 100644 --- a/src/lib/crypt_ops/crypto_digest.h +++ b/src/lib/crypt_ops/crypto_digest.h @@ -51,6 +51,9 @@ typedef enum { /** Structure used to temporarily save the a digest object. Only implemented * for SHA1 digest for now. */ typedef struct crypto_digest_checkpoint_t { +#ifdef ENABLE_NSS + unsigned int bytes_used; +#endif uint8_t mem[DIGEST_CHECKPOINT_BYTES]; } crypto_digest_checkpoint_t; diff --git a/src/test/test_hs_ntor_cl.c b/src/test/test_hs_ntor_cl.c index c207741794..a4915c4f8a 100644 --- a/src/test/test_hs_ntor_cl.c +++ b/src/test/test_hs_ntor_cl.c @@ -18,6 +18,7 @@ #include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_ed25519.h" #include "lib/crypt_ops/crypto_format.h" +#include "lib/crypt_ops/crypto_init.h" #include "core/crypto/hs_ntor.h" #include "core/crypto/onion_ntor.h" @@ -240,7 +241,11 @@ main(int argc, char **argv) return 1; } + init_logging(1); curve25519_init(); + if (crypto_global_init(0, NULL, NULL) < 0) + return 1; + if (!strcmp(argv[1], "client1")) { return client1(argc, argv); } else if (!strcmp(argv[1], "server1")) { diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c index b8d3a8b426..3f914523a3 100644 --- a/src/test/test_ntor_cl.c +++ b/src/test/test_ntor_cl.c @@ -9,6 +9,7 @@ #include "core/or/or.h" #include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_curve25519.h" +#include "lib/crypt_ops/crypto_init.h" #include "core/crypto/onion_ntor.h" #define N_ARGS(n) STMT_BEGIN { \ @@ -153,7 +154,11 @@ main(int argc, char **argv) return 1; } + init_logging(1); curve25519_init(); + if (crypto_global_init(0, NULL, NULL) < 0) + return 1; + if (!strcmp(argv[1], "client1")) { return client1(argc, argv); } else if (!strcmp(argv[1], "server1")) { @@ -165,4 +170,3 @@ main(int argc, char **argv) return 1; } } - From be8d497b656b674e33726f49d9829cc03fccea09 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 Jul 2018 13:43:23 -0400 Subject: [PATCH 12/76] Make sure NSS is initialized before running benchmarks --- src/test/bench.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/bench.c b/src/test/bench.c index 50af9e0d69..2b90ccf734 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -681,9 +681,9 @@ main(int argc, const char **argv) tor_threads_init(); tor_compress_init(); + init_logging(1); if (argc == 4 && !strcmp(argv[1], "diff")) { - init_logging(1); const int N = 200; char *f1 = read_file_to_str(argv[2], RFTS_BIN, NULL); char *f2 = read_file_to_str(argv[3], RFTS_BIN, NULL); @@ -719,13 +719,12 @@ main(int argc, const char **argv) reset_perftime(); - if (crypto_seed_rng() < 0) { + if (crypto_global_init(0, NULL, NULL) < 0) { printf("Couldn't seed RNG; exiting.\n"); return 1; } init_protocol_warning_severity_level(); - crypto_init_siphash_key(); options = options_new(); init_logging(1); options->command = CMD_RUN_UNITTESTS; From 60705a5719d929a5f3180bc4e6906f972855f84b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 Jul 2018 14:35:51 -0400 Subject: [PATCH 13/76] Use NSS in crypto_rand.c This is comparatively straightforward too, except for a couple of twists: * For as long as we're building with two crypto libraries, we want to seed _both_ their RNGs, and use _both_ their RNGs to improve the output of crypto_strongest_rand() * The NSS prng will sometimes refuse to generate huge outputs. When it does, we stretch the output with SHAKE. We only need this for the tests. --- src/lib/crypt_ops/crypto_rand.c | 110 +++++++++++++++++++++++++++++--- src/test/test-timers.c | 5 ++ 2 files changed, 107 insertions(+), 8 deletions(-) diff --git a/src/lib/crypt_ops/crypto_rand.c b/src/lib/crypt_ops/crypto_rand.c index fb9d0c2c6c..9806714747 100644 --- a/src/lib/crypt_ops/crypto_rand.c +++ b/src/lib/crypt_ops/crypto_rand.c @@ -35,9 +35,22 @@ #include "lib/testsupport/testsupport.h" #include "lib/fs/files.h" +#ifdef ENABLE_NSS +#include "lib/crypt_ops/crypto_nss_mgt.h" +#include "lib/crypt_ops/crypto_digest.h" +#endif + +#ifdef ENABLE_OPENSSL DISABLE_GCC_WARNING(redundant-decls) #include ENABLE_GCC_WARNING(redundant-decls) +#endif + +#ifdef ENABLE_NSS +#include +#include +#include +#endif #if __GNUC__ && GCC_VERSION >= 402 #if GCC_VERSION >= 406 @@ -324,14 +337,21 @@ crypto_strongest_rand(uint8_t *out, size_t out_len) { #define DLEN SHA512_DIGEST_LENGTH /* We're going to hash DLEN bytes from the system RNG together with some - * bytes from the openssl PRNG, in order to yield DLEN bytes. + * bytes from the PRNGs from our crypto librar(y/ies), in order to yield + * DLEN bytes. */ - uint8_t inp[DLEN*2]; + uint8_t inp[DLEN*3]; uint8_t tmp[DLEN]; tor_assert(out); while (out_len) { - crypto_rand((char*) inp, DLEN); - if (crypto_strongest_rand_raw(inp+DLEN, DLEN) < 0) { + memset(inp, 0, sizeof(inp)); +#ifdef ENABLE_OPENSSL + RAND_bytes(inp, DLEN); +#endif +#ifdef ENABLE_NSS + PK11_GenerateRandom(inp+DLEN, DLEN); +#endif + if (crypto_strongest_rand_raw(inp+DLEN*2, DLEN) < 0) { // LCOV_EXCL_START log_err(LD_CRYPTO, "Failed to load strong entropy when generating an " "important key. Exiting."); @@ -354,12 +374,13 @@ crypto_strongest_rand(uint8_t *out, size_t out_len) #undef DLEN } +#ifdef ENABLE_OPENSSL /** * Seed OpenSSL's random number generator with bytes from the operating * system. Return 0 on success, -1 on failure. **/ -int -crypto_seed_rng(void) +static int +crypto_seed_openssl_rng(void) { int rand_poll_ok = 0, load_entropy_ok = 0; uint8_t buf[ADD_ENTROPY]; @@ -383,6 +404,52 @@ crypto_seed_rng(void) else return -1; } +#endif + +#ifdef ENABLE_NSS +/** + * Seed OpenSSL's random number generator with bytes from the operating + * system. Return 0 on success, -1 on failure. + **/ +static int +crypto_seed_nss_rng(void) +{ + uint8_t buf[ADD_ENTROPY]; + + int load_entropy_ok = !crypto_strongest_rand_raw(buf, sizeof(buf)); + if (load_entropy_ok) { + if (PK11_RandomUpdate(buf, sizeof(buf)) != SECSuccess) { + load_entropy_ok = 0; + } + } + + memwipe(buf, 0, sizeof(buf)); + + return load_entropy_ok ? 0 : -1; +} +#endif + +/** + * Seed the RNG for any and all crypto libraries that we're using with bytes + * from the operating system. Return 0 on success, -1 on failure. + */ +int +crypto_seed_rng(void) +{ + int seeded = 0; +#ifdef ENABLE_NSS + if (crypto_seed_nss_rng() < 0) + return -1; + ++seeded; +#endif +#ifdef ENABLE_OPENSSL + if (crypto_seed_openssl_rng() < 0) + return -1; + ++seeded; +#endif + tor_assert(seeded); + return 0; +} /** * Write n bytes of strong random data to to. Supports mocking @@ -407,17 +474,44 @@ crypto_rand, (char *to, size_t n)) void crypto_rand_unmocked(char *to, size_t n) { - int r; if (n == 0) return; tor_assert(n < INT_MAX); tor_assert(to); - r = RAND_bytes((unsigned char*)to, (int)n); + +#ifdef ENABLE_NSS + SECStatus s = PK11_GenerateRandom((unsigned char*)to, (int)n); + if (s != SECSuccess) { + /* NSS rather sensibly might refuse to generate huge amounts of random + * data at once. Unfortunately, our unit test do this in a couple of + * places. To solve this issue, we use our XOF to stretch a shorter + * output when a longer one is needed. + * + * Yes, this is secure. */ + + /* This is longer than it needs to be; 1600 bits == 200 bytes is the + * state-size of SHA3. */ +#define BUFLEN 512 + tor_assert(PR_GetError() == SEC_ERROR_INVALID_ARGS && n > BUFLEN); + unsigned char buf[BUFLEN]; + s = PK11_GenerateRandom(buf, BUFLEN); + tor_assert(s == SECSuccess); + crypto_xof_t *xof = crypto_xof_new(); + crypto_xof_add_bytes(xof, buf, BUFLEN); + crypto_xof_squeeze_bytes(xof, (unsigned char *)to, n); + crypto_xof_free(xof); + memwipe(buf, 0, BUFLEN); + +#undef BUFLEN + } +#else + int r = RAND_bytes((unsigned char*)to, (int)n); /* We consider a PRNG failure non-survivable. Let's assert so that we get a * stack trace about where it happened. */ tor_assert(r >= 0); +#endif } /** diff --git a/src/test/test-timers.c b/src/test/test-timers.c index f9276c25d6..923f51ecce 100644 --- a/src/test/test-timers.c +++ b/src/test/test-timers.c @@ -9,6 +9,7 @@ #include "lib/evloop/compat_libevent.h" #include "lib/evloop/timers.h" +#include "lib/crypt_ops/crypto_init.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/log/util_bug.h" #include "lib/time/compat_time.h" @@ -62,6 +63,10 @@ main(int argc, char **argv) memset(&cfg, 0, sizeof(cfg)); tor_libevent_initialize(&cfg); timers_initialize(); + init_logging(1); + + if (crypto_global_init(0, NULL, NULL) < 0) + return 1; int i; int ret; From 76e10ee6b9fbd5a0372f6d04e432c78aa560e9f9 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 16 Jul 2018 09:49:05 -0400 Subject: [PATCH 14/76] Use NSS for AES_CTR. --- src/lib/crypt_ops/aes_nss.c | 106 +++++++++++++++++++++ src/lib/crypt_ops/{aes.c => aes_openssl.c} | 4 +- src/lib/crypt_ops/include.am | 6 +- 3 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 src/lib/crypt_ops/aes_nss.c rename src/lib/crypt_ops/{aes.c => aes_openssl.c} (99%) diff --git a/src/lib/crypt_ops/aes_nss.c b/src/lib/crypt_ops/aes_nss.c new file mode 100644 index 0000000000..272edc5592 --- /dev/null +++ b/src/lib/crypt_ops/aes_nss.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file aes_nss.c + * \brief Use NSS to implement AES_CTR. + **/ + +#include "orconfig.h" +#include "lib/crypt_ops/aes.h" +#include "lib/crypt_ops/crypto_nss_mgt.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/log/util_bug.h" + +DISABLE_GCC_WARNING(strict-prototypes) +#include +#include +ENABLE_GCC_WARNING(strict-prototypes) + +aes_cnt_cipher_t * +aes_new_cipher(const uint8_t *key, const uint8_t *iv, + int key_bits) +{ + const CK_MECHANISM_TYPE ckm = CKM_AES_CTR; + SECItem keyItem = { .type = siBuffer, + .data = (unsigned char *)key, + .len = (key_bits / 8) }; + CK_AES_CTR_PARAMS params; + params.ulCounterBits = 128; + memcpy(params.cb, iv, 16); + SECItem ivItem = { .type = siBuffer, + .data = (unsigned char *)¶ms, + .len = sizeof(params) }; + PK11SlotInfo *slot = NULL; + PK11SymKey *keyObj = NULL; + SECItem *ivObj = NULL; + PK11Context *result = NULL; + + slot = PK11_GetBestSlot(ckm, NULL); + if (!slot) + goto err; + + keyObj = PK11_ImportSymKey(slot, ckm, PK11_OriginUnwrap, + CKA_ENCRYPT, &keyItem, NULL); + if (!keyObj) + goto err; + + ivObj = PK11_ParamFromIV(ckm, &ivItem); + if (!ivObj) + goto err; + + PORT_SetError(SEC_ERROR_IO); + result = PK11_CreateContextBySymKey(ckm, CKA_ENCRYPT, keyObj, ivObj); + + err: + memwipe(¶ms, 0, sizeof(params)); + if (ivObj) + SECITEM_FreeItem(ivObj, PR_TRUE); + if (keyObj) + PK11_FreeSymKey(keyObj); + if (slot) + PK11_FreeSlot(slot); + + tor_assert(result); + return (aes_cnt_cipher_t *)result; +} + +void +aes_cipher_free_(aes_cnt_cipher_t *cipher) +{ + if (!cipher) + return; + PK11_DestroyContext((PK11Context*) cipher, PR_TRUE); +} + +void +aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data_, size_t len_) +{ + tor_assert(len_ <= INT_MAX); + + SECStatus s; + PK11Context *ctx = (PK11Context*)cipher; + unsigned char *data = (unsigned char *)data_; + int len = (int) len_; + int result_len = 0; + + s = PK11_CipherOp(ctx, data, &result_len, len, data, len); + tor_assert(s == SECSuccess); + tor_assert(result_len == len); +} + +int +evaluate_evp_for_aes(int force_value) +{ + (void)force_value; + return 0; +} + +int +evaluate_ctr_for_aes(void) +{ + return 0; +} diff --git a/src/lib/crypt_ops/aes.c b/src/lib/crypt_ops/aes_openssl.c similarity index 99% rename from src/lib/crypt_ops/aes.c rename to src/lib/crypt_ops/aes_openssl.c index ff9d4d855c..387f5d3df0 100644 --- a/src/lib/crypt_ops/aes.c +++ b/src/lib/crypt_ops/aes_openssl.c @@ -5,8 +5,8 @@ /* See LICENSE for licensing information */ /** - * \file aes.c - * \brief Implements a counter-mode stream cipher on top of AES. + * \file aes_openssl.c + * \brief Use OpenSSL to implement AES_CTR. **/ #include "orconfig.h" diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index e96d6b0a5c..3ebb0b0f6b 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -6,7 +6,6 @@ noinst_LIBRARIES += src/lib/libtor-crypt-ops-testing.a endif src_lib_libtor_crypt_ops_a_SOURCES = \ - src/lib/crypt_ops/aes.c \ src/lib/crypt_ops/crypto_cipher.c \ src/lib/crypt_ops/crypto_curve25519.c \ src/lib/crypt_ops/crypto_dh.c \ @@ -24,8 +23,13 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ if USE_NSS src_lib_libtor_crypt_ops_a_SOURCES += \ + src/lib/crypt_ops/aes_nss.c \ src/lib/crypt_ops/crypto_nss_mgt.c +else +src_lib_libtor_crypt_ops_a_SOURCES += \ + src/lib/crypt_ops/aes_openssl.c endif + if USE_OPENSSL src_lib_libtor_crypt_ops_a_SOURCES += \ src/lib/crypt_ops/crypto_openssl_mgt.c From e7a061681769beb9b643dbb703427e6faca3ef21 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 16 Jul 2018 14:07:38 -0400 Subject: [PATCH 15/76] Changes files for feature26815 and 26816 --- changes/feature26815 | 3 +++ changes/feature26816 | 11 +++++++++++ 2 files changed, 14 insertions(+) create mode 100644 changes/feature26815 create mode 100644 changes/feature26816 diff --git a/changes/feature26815 b/changes/feature26815 new file mode 100644 index 0000000000..10e7e478f9 --- /dev/null +++ b/changes/feature26815 @@ -0,0 +1,3 @@ + o Major features (experimental, library support): + - When built with --enable-nss, Tor now uses the NSS library for digests, + AES, and pseudorandom numbers. Closes ticket 26815. diff --git a/changes/feature26816 b/changes/feature26816 new file mode 100644 index 0000000000..3c25597d85 --- /dev/null +++ b/changes/feature26816 @@ -0,0 +1,11 @@ + o Major features (experimental, library support): + - Tor now has _partial_ support for using the NSS cryptography library in + place of OpenSSL. When Tor is configured with --enable-nss, it will + use NSS for several (but not yet all) of its cryptography. (It still + relies on OpenSSL for the rest.) Eventually, if all goes as planned, + "--enable-nss" will produce a version of Tor that does not depend on + OpenSSL. Implements ticket 26816. + + WARNING: This feature is experimental. Don't use it for real security + yet, until the code has had much more review, and more bugs have been + shaken out. From 2d80673b9a7965d5b2af895c7527eb58bd6a00ce Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 18 Jul 2018 09:52:47 -0400 Subject: [PATCH 16/76] Fix "make distcheck." --- src/lib/crypt_ops/include.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 3ebb0b0f6b..018730627d 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -54,6 +54,7 @@ noinst_HEADERS += \ src/lib/crypt_ops/crypto_cipher.h \ src/lib/crypt_ops/crypto_hkdf.h \ src/lib/crypt_ops/crypto_init.h \ + src/lib/crypt_ops/crypto_nss_mgt.h \ src/lib/crypt_ops/crypto_openssl_mgt.h \ src/lib/crypt_ops/crypto_pwbox.h \ src/lib/crypt_ops/crypto_rand.h \ From 99beed152e5df41181703645a67fe10a98225d55 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 18 Jul 2018 10:40:52 -0400 Subject: [PATCH 17/76] Make the rust tests link. --- config.rust.in | 2 ++ src/rust/build.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/config.rust.in b/config.rust.in index 4ca5351aec..11b671b980 100644 --- a/config.rust.in +++ b/config.rust.in @@ -5,6 +5,7 @@ BUILDDIR=@BUILDDIR@ TOR_LDFLAGS_zlib=@TOR_LDFLAGS_zlib@ +TOR_LDFLAGS_nss=@TOR_LDFLAGS_nss@ TOR_LDFLAGS_openssl=@TOR_LDFLAGS_openssl@ TOR_LDFLAGS_libevent=@TOR_LDFLAGS_libevent@ TOR_ZLIB_LIBS=@TOR_ZLIB_LIBS@ @@ -20,3 +21,4 @@ TOR_LZMA_LIBS=@TOR_LZMA_LIBS@ TOR_ZSTD_LIBS=@TOR_ZSTD_LIBS@ LIBS=@LIBS@ LDFLAGS=@LDFLAGS@ +NSS_LIBS=@NSS_LIBS@ \ No newline at end of file diff --git a/src/rust/build.rs b/src/rust/build.rs index 2cf85b404a..a35e6868c6 100644 --- a/src/rust/build.rs +++ b/src/rust/build.rs @@ -173,6 +173,7 @@ pub fn main() { cfg.from_cflags("TOR_ZLIB_LIBS"); cfg.from_cflags("TOR_LIB_MATH"); + cfg.from_cflags("NSS_LIBS"); cfg.from_cflags("TOR_OPENSSL_LIBS"); cfg.from_cflags("TOR_LIBEVENT_LIBS"); cfg.from_cflags("TOR_LIB_WS32"); From 60a5b784803df5a1a9e467e0aa14c17b0991f68c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 17 Jul 2018 09:06:05 -0400 Subject: [PATCH 18/76] Extract the OpenSSL DH functionality to a new file. --- src/lib/crypt_ops/crypto_dh.c | 511 ++------------------------ src/lib/crypt_ops/crypto_dh.h | 4 + src/lib/crypt_ops/crypto_dh_openssl.c | 490 ++++++++++++++++++++++++ src/lib/crypt_ops/include.am | 1 + 4 files changed, 517 insertions(+), 489 deletions(-) create mode 100644 src/lib/crypt_ops/crypto_dh_openssl.c diff --git a/src/lib/crypt_ops/crypto_dh.c b/src/lib/crypt_ops/crypto_dh.c index ab06fd4969..8bb66379fd 100644 --- a/src/lib/crypt_ops/crypto_dh.c +++ b/src/lib/crypt_ops/crypto_dh.c @@ -7,6 +7,8 @@ /** * \file crypto_dh.c * \brief Block of functions related with DH utilities and operations. + * over Z_p. We aren't using this for any new crypto -- EC is more + * efficient. **/ #include "lib/crypt_ops/compat_openssl.h" @@ -17,495 +19,26 @@ #include "lib/log/log.h" #include "lib/log/util_bug.h" -DISABLE_GCC_WARNING(redundant-decls) - -#include - -ENABLE_GCC_WARNING(redundant-decls) - -#include -#include - -/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake - * while we're waiting for the second.*/ -struct crypto_dh_t { - DH *dh; /**< The openssl DH object */ -}; - -static int tor_check_dh_key(int severity, const BIGNUM *bn); - -/** Used by tortls.c: Get the DH* from a crypto_dh_t. - */ -DH * -crypto_dh_get_dh_(crypto_dh_t *dh) -{ - return dh->dh; -} - /** Our DH 'g' parameter */ -#define DH_GENERATOR 2 - -/** Shared P parameter for our circuit-crypto DH key exchanges. */ -static BIGNUM *dh_param_p = NULL; -/** Shared P parameter for our TLS DH key exchanges. */ -static BIGNUM *dh_param_p_tls = NULL; -/** Shared G parameter for our DH key exchanges. */ -static BIGNUM *dh_param_g = NULL; - -/** Validate a given set of Diffie-Hellman parameters. This is moderately - * computationally expensive (milliseconds), so should only be called when - * the DH parameters change. Returns 0 on success, * -1 on failure. +const unsigned DH_GENERATOR = 2; +/** This is the 1024-bit safe prime that Apache uses for its DH stuff; see + * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this + * prime. */ -static int -crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g) -{ - DH *dh = NULL; - int ret = -1; - - /* Copy into a temporary DH object, just so that DH_check() can be called. */ - if (!(dh = DH_new())) - goto out; -#ifdef OPENSSL_1_1_API - BIGNUM *dh_p, *dh_g; - if (!(dh_p = BN_dup(p))) - goto out; - if (!(dh_g = BN_dup(g))) - goto out; - if (!DH_set0_pqg(dh, dh_p, NULL, dh_g)) - goto out; -#else /* !(defined(OPENSSL_1_1_API)) */ - if (!(dh->p = BN_dup(p))) - goto out; - if (!(dh->g = BN_dup(g))) - goto out; -#endif /* defined(OPENSSL_1_1_API) */ - - /* Perform the validation. */ - int codes = 0; - if (!DH_check(dh, &codes)) - goto out; - if (BN_is_word(g, DH_GENERATOR_2)) { - /* Per https://wiki.openssl.org/index.php/Diffie-Hellman_parameters - * - * OpenSSL checks the prime is congruent to 11 when g = 2; while the - * IETF's primes are congruent to 23 when g = 2. - */ - BN_ULONG residue = BN_mod_word(p, 24); - if (residue == 11 || residue == 23) - codes &= ~DH_NOT_SUITABLE_GENERATOR; - } - if (codes != 0) /* Specifics on why the params suck is irrelevant. */ - goto out; - - /* Things are probably not evil. */ - ret = 0; - - out: - if (dh) - DH_free(dh); - return ret; -} - -/** Set the global Diffie-Hellman generator, used for both TLS and internal - * DH stuff. +const char TLS_DH_PRIME[] = + "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98" + "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A" + "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7" + "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68" + "B0E7393E0F24218EB3"; +/** + * This is from rfc2409, section 6.2. It's a safe prime, and + * supposedly it equals: + * 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }. */ -static void -crypto_set_dh_generator(void) -{ - BIGNUM *generator; - int r; - - if (dh_param_g) - return; - - generator = BN_new(); - tor_assert(generator); - - r = BN_set_word(generator, DH_GENERATOR); - tor_assert(r); - - dh_param_g = generator; -} - -/** Set the global TLS Diffie-Hellman modulus. Use the Apache mod_ssl DH - * modulus. */ -void -crypto_set_tls_dh_prime(void) -{ - BIGNUM *tls_prime = NULL; - int r; - - /* If the space is occupied, free the previous TLS DH prime */ - if (BUG(dh_param_p_tls)) { - /* LCOV_EXCL_START - * - * We shouldn't be calling this twice. - */ - BN_clear_free(dh_param_p_tls); - dh_param_p_tls = NULL; - /* LCOV_EXCL_STOP */ - } - - tls_prime = BN_new(); - tor_assert(tls_prime); - - /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see - * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this - * prime. - */ - r = BN_hex2bn(&tls_prime, - "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98" - "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A" - "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7" - "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68" - "B0E7393E0F24218EB3"); - tor_assert(r); - - tor_assert(tls_prime); - - dh_param_p_tls = tls_prime; - crypto_set_dh_generator(); - tor_assert(0 == crypto_validate_dh_params(dh_param_p_tls, dh_param_g)); -} - -/** Initialize dh_param_p and dh_param_g if they are not already - * set. */ -static void -init_dh_param(void) -{ - BIGNUM *circuit_dh_prime; - int r; - if (BUG(dh_param_p && dh_param_g)) - return; // LCOV_EXCL_LINE This function isn't supposed to be called twice. - - circuit_dh_prime = BN_new(); - tor_assert(circuit_dh_prime); - - /* This is from rfc2409, section 6.2. It's a safe prime, and - supposedly it equals: - 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }. - */ - r = BN_hex2bn(&circuit_dh_prime, - "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" - "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" - "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" - "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" - "49286651ECE65381FFFFFFFFFFFFFFFF"); - tor_assert(r); - - /* Set the new values as the global DH parameters. */ - dh_param_p = circuit_dh_prime; - crypto_set_dh_generator(); - tor_assert(0 == crypto_validate_dh_params(dh_param_p, dh_param_g)); - - if (!dh_param_p_tls) { - crypto_set_tls_dh_prime(); - } -} - -/** Number of bits to use when choosing the x or y value in a Diffie-Hellman - * handshake. Since we exponentiate by this value, choosing a smaller one - * lets our handhake go faster. - */ -#define DH_PRIVATE_KEY_BITS 320 - -/** Allocate and return a new DH object for a key exchange. Returns NULL on - * failure. - */ -crypto_dh_t * -crypto_dh_new(int dh_type) -{ - crypto_dh_t *res = tor_malloc_zero(sizeof(crypto_dh_t)); - - tor_assert(dh_type == DH_TYPE_CIRCUIT || dh_type == DH_TYPE_TLS || - dh_type == DH_TYPE_REND); - - if (!dh_param_p) - init_dh_param(); - - if (!(res->dh = DH_new())) - goto err; - -#ifdef OPENSSL_1_1_API - BIGNUM *dh_p = NULL, *dh_g = NULL; - - if (dh_type == DH_TYPE_TLS) { - dh_p = BN_dup(dh_param_p_tls); - } else { - dh_p = BN_dup(dh_param_p); - } - if (!dh_p) - goto err; - - dh_g = BN_dup(dh_param_g); - if (!dh_g) { - BN_free(dh_p); - goto err; - } - - if (!DH_set0_pqg(res->dh, dh_p, NULL, dh_g)) { - goto err; - } - - if (!DH_set_length(res->dh, DH_PRIVATE_KEY_BITS)) - goto err; -#else /* !(defined(OPENSSL_1_1_API)) */ - if (dh_type == DH_TYPE_TLS) { - if (!(res->dh->p = BN_dup(dh_param_p_tls))) - goto err; - } else { - if (!(res->dh->p = BN_dup(dh_param_p))) - goto err; - } - - if (!(res->dh->g = BN_dup(dh_param_g))) - goto err; - - res->dh->length = DH_PRIVATE_KEY_BITS; -#endif /* defined(OPENSSL_1_1_API) */ - - return res; - - /* LCOV_EXCL_START - * This error condition is only reached when an allocation fails */ - err: - crypto_openssl_log_errors(LOG_WARN, "creating DH object"); - if (res->dh) DH_free(res->dh); /* frees p and g too */ - tor_free(res); - return NULL; - /* LCOV_EXCL_STOP */ -} - -/** Return a copy of dh, sharing its internal state. */ -crypto_dh_t * -crypto_dh_dup(const crypto_dh_t *dh) -{ - crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t)); - tor_assert(dh); - tor_assert(dh->dh); - dh_new->dh = dh->dh; - DH_up_ref(dh->dh); - return dh_new; -} - -/** Return the length of the DH key in dh, in bytes. - */ -int -crypto_dh_get_bytes(crypto_dh_t *dh) -{ - tor_assert(dh); - return DH_size(dh->dh); -} - -/** Generate \ for our part of the key exchange. Return 0 on - * success, -1 on failure. - */ -int -crypto_dh_generate_public(crypto_dh_t *dh) -{ -#ifndef OPENSSL_1_1_API - again: -#endif - if (!DH_generate_key(dh->dh)) { - /* LCOV_EXCL_START - * To test this we would need some way to tell openssl to break DH. */ - crypto_openssl_log_errors(LOG_WARN, "generating DH key"); - return -1; - /* LCOV_EXCL_STOP */ - } -#ifdef OPENSSL_1_1_API - /* OpenSSL 1.1.x doesn't appear to let you regenerate a DH key, without - * recreating the DH object. I have no idea what sort of aliasing madness - * can occur here, so do the check, and just bail on failure. - */ - const BIGNUM *pub_key, *priv_key; - DH_get0_key(dh->dh, &pub_key, &priv_key); - if (tor_check_dh_key(LOG_WARN, pub_key)<0) { - log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-" - "the-universe chances really do happen. Treating as a failure."); - return -1; - } -#else /* !(defined(OPENSSL_1_1_API)) */ - if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) { - /* LCOV_EXCL_START - * If this happens, then openssl's DH implementation is busted. */ - log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-" - "the-universe chances really do happen. Trying again."); - /* Free and clear the keys, so OpenSSL will actually try again. */ - BN_clear_free(dh->dh->pub_key); - BN_clear_free(dh->dh->priv_key); - dh->dh->pub_key = dh->dh->priv_key = NULL; - goto again; - /* LCOV_EXCL_STOP */ - } -#endif /* defined(OPENSSL_1_1_API) */ - return 0; -} - -/** Generate g^x as necessary, and write the g^x for the key exchange - * as a pubkey_len-byte value into pubkey. Return 0 on - * success, -1 on failure. pubkey_len must be \>= DH1024_KEY_LEN. - */ -int -crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len) -{ - int bytes; - tor_assert(dh); - - const BIGNUM *dh_pub; - -#ifdef OPENSSL_1_1_API - const BIGNUM *dh_priv; - DH_get0_key(dh->dh, &dh_pub, &dh_priv); -#else - dh_pub = dh->dh->pub_key; -#endif /* defined(OPENSSL_1_1_API) */ - - if (!dh_pub) { - if (crypto_dh_generate_public(dh)<0) - return -1; - else { -#ifdef OPENSSL_1_1_API - DH_get0_key(dh->dh, &dh_pub, &dh_priv); -#else - dh_pub = dh->dh->pub_key; -#endif - } - } - - tor_assert(dh_pub); - bytes = BN_num_bytes(dh_pub); - tor_assert(bytes >= 0); - if (pubkey_len < (size_t)bytes) { - log_warn(LD_CRYPTO, - "Weird! pubkey_len (%d) was smaller than DH1024_KEY_LEN (%d)", - (int) pubkey_len, bytes); - return -1; - } - - memset(pubkey, 0, pubkey_len); - BN_bn2bin(dh_pub, (unsigned char*)(pubkey+(pubkey_len-bytes))); - - return 0; -} - -/** Check for bad Diffie-Hellman public keys (g^x). Return 0 if the key is - * okay (in the subgroup [2,p-2]), or -1 if it's bad. - * See http://www.cl.cam.ac.uk/ftp/users/rja14/psandqs.ps.gz for some tips. - */ -static int -tor_check_dh_key(int severity, const BIGNUM *bn) -{ - BIGNUM *x; - char *s; - tor_assert(bn); - x = BN_new(); - tor_assert(x); - if (BUG(!dh_param_p)) - init_dh_param(); //LCOV_EXCL_LINE we already checked whether we did this. - BN_set_word(x, 1); - if (BN_cmp(bn,x)<=0) { - log_fn(severity, LD_CRYPTO, "DH key must be at least 2."); - goto err; - } - BN_copy(x,dh_param_p); - BN_sub_word(x, 1); - if (BN_cmp(bn,x)>=0) { - log_fn(severity, LD_CRYPTO, "DH key must be at most p-2."); - goto err; - } - BN_clear_free(x); - return 0; - err: - BN_clear_free(x); - s = BN_bn2hex(bn); - log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s); - OPENSSL_free(s); - return -1; -} - -/** Given a DH key exchange object, and our peer's value of g^y (as a - * pubkey_len-byte value in pubkey) generate - * secret_bytes_out bytes of shared key material and write them - * to secret_out. Return the number of bytes generated on success, - * or -1 on failure. - * - * (We generate key material by computing - * SHA1( g^xy || "\x00" ) || SHA1( g^xy || "\x01" ) || ... - * where || is concatenation.) - */ -ssize_t -crypto_dh_compute_secret(int severity, crypto_dh_t *dh, - const char *pubkey, size_t pubkey_len, - char *secret_out, size_t secret_bytes_out) -{ - char *secret_tmp = NULL; - BIGNUM *pubkey_bn = NULL; - size_t secret_len=0, secret_tmp_len=0; - int result=0; - tor_assert(dh); - tor_assert(secret_bytes_out/DIGEST_LEN <= 255); - tor_assert(pubkey_len < INT_MAX); - - if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey, - (int)pubkey_len, NULL))) - goto error; - if (tor_check_dh_key(severity, pubkey_bn)<0) { - /* Check for invalid public keys. */ - log_fn(severity, LD_CRYPTO,"Rejected invalid g^x"); - goto error; - } - secret_tmp_len = crypto_dh_get_bytes(dh); - secret_tmp = tor_malloc(secret_tmp_len); - result = DH_compute_key((unsigned char*)secret_tmp, pubkey_bn, dh->dh); - if (result < 0) { - log_warn(LD_CRYPTO,"DH_compute_key() failed."); - goto error; - } - secret_len = result; - if (crypto_expand_key_material_TAP((uint8_t*)secret_tmp, secret_len, - (uint8_t*)secret_out, secret_bytes_out)<0) - goto error; - secret_len = secret_bytes_out; - - goto done; - error: - result = -1; - done: - crypto_openssl_log_errors(LOG_WARN, "completing DH handshake"); - if (pubkey_bn) - BN_clear_free(pubkey_bn); - if (secret_tmp) { - memwipe(secret_tmp, 0, secret_tmp_len); - tor_free(secret_tmp); - } - if (result < 0) - return result; - else - return secret_len; -} - -/** Free a DH key exchange object. - */ -void -crypto_dh_free_(crypto_dh_t *dh) -{ - if (!dh) - return; - tor_assert(dh->dh); - DH_free(dh->dh); - tor_free(dh); -} - -void -crypto_dh_free_all(void) -{ - if (dh_param_p) - BN_clear_free(dh_param_p); - if (dh_param_p_tls) - BN_clear_free(dh_param_p_tls); - if (dh_param_g) - BN_clear_free(dh_param_g); - - dh_param_p = dh_param_p_tls = dh_param_g = NULL; -} +const char OAKLEY_PRIME_2[] = + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE65381FFFFFFFFFFFFFFFF"; diff --git a/src/lib/crypt_ops/crypto_dh.h b/src/lib/crypt_ops/crypto_dh.h index 88e8a919a8..2d25ba8c85 100644 --- a/src/lib/crypt_ops/crypto_dh.h +++ b/src/lib/crypt_ops/crypto_dh.h @@ -19,6 +19,10 @@ typedef struct crypto_dh_t crypto_dh_t; +extern const unsigned DH_GENERATOR; +extern const char TLS_DH_PRIME[]; +extern const char OAKLEY_PRIME_2[]; + /* Key negotiation */ #define DH_TYPE_CIRCUIT 1 #define DH_TYPE_REND 2 diff --git a/src/lib/crypt_ops/crypto_dh_openssl.c b/src/lib/crypt_ops/crypto_dh_openssl.c new file mode 100644 index 0000000000..4457b18144 --- /dev/null +++ b/src/lib/crypt_ops/crypto_dh_openssl.c @@ -0,0 +1,490 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_dh_openssl.c + * \brief Implement Tor's Z_p diffie-hellman stuff for OpenSSL. + **/ + +#include "lib/crypt_ops/compat_openssl.h" +#include "lib/crypt_ops/crypto_dh.h" +#include "lib/crypt_ops/crypto_digest.h" +#include "lib/crypt_ops/crypto_hkdf.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/log/log.h" +#include "lib/log/util_bug.h" + +DISABLE_GCC_WARNING(redundant-decls) + +#include + +ENABLE_GCC_WARNING(redundant-decls) + +#include +#include + +static int tor_check_dh_key(int severity, const BIGNUM *bn); + +/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake + * while we're waiting for the second.*/ +struct crypto_dh_t { + DH *dh; /**< The openssl DH object */ +}; + +/** Used by tortls.c: Get the DH* from a crypto_dh_t. + */ +DH * +crypto_dh_get_dh_(crypto_dh_t *dh) +{ + return dh->dh; +} + +/** Shared P parameter for our circuit-crypto DH key exchanges. */ +static BIGNUM *dh_param_p = NULL; +/** Shared P parameter for our TLS DH key exchanges. */ +static BIGNUM *dh_param_p_tls = NULL; +/** Shared G parameter for our DH key exchanges. */ +static BIGNUM *dh_param_g = NULL; + +/** Validate a given set of Diffie-Hellman parameters. This is moderately + * computationally expensive (milliseconds), so should only be called when + * the DH parameters change. Returns 0 on success, * -1 on failure. + */ +static int +crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g) +{ + DH *dh = NULL; + int ret = -1; + + /* Copy into a temporary DH object, just so that DH_check() can be called. */ + if (!(dh = DH_new())) + goto out; +#ifdef OPENSSL_1_1_API + BIGNUM *dh_p, *dh_g; + if (!(dh_p = BN_dup(p))) + goto out; + if (!(dh_g = BN_dup(g))) + goto out; + if (!DH_set0_pqg(dh, dh_p, NULL, dh_g)) + goto out; +#else /* !(defined(OPENSSL_1_1_API)) */ + if (!(dh->p = BN_dup(p))) + goto out; + if (!(dh->g = BN_dup(g))) + goto out; +#endif /* defined(OPENSSL_1_1_API) */ + + /* Perform the validation. */ + int codes = 0; + if (!DH_check(dh, &codes)) + goto out; + if (BN_is_word(g, DH_GENERATOR_2)) { + /* Per https://wiki.openssl.org/index.php/Diffie-Hellman_parameters + * + * OpenSSL checks the prime is congruent to 11 when g = 2; while the + * IETF's primes are congruent to 23 when g = 2. + */ + BN_ULONG residue = BN_mod_word(p, 24); + if (residue == 11 || residue == 23) + codes &= ~DH_NOT_SUITABLE_GENERATOR; + } + if (codes != 0) /* Specifics on why the params suck is irrelevant. */ + goto out; + + /* Things are probably not evil. */ + ret = 0; + + out: + if (dh) + DH_free(dh); + return ret; +} + +/** Set the global Diffie-Hellman generator, used for both TLS and internal + * DH stuff. + */ +static void +crypto_set_dh_generator(void) +{ + BIGNUM *generator; + int r; + + if (dh_param_g) + return; + + generator = BN_new(); + tor_assert(generator); + + r = BN_set_word(generator, DH_GENERATOR); + tor_assert(r); + + dh_param_g = generator; +} + +/** Set the global TLS Diffie-Hellman modulus. Use the Apache mod_ssl DH + * modulus. */ +void +crypto_set_tls_dh_prime(void) +{ + BIGNUM *tls_prime = NULL; + int r; + + /* If the space is occupied, free the previous TLS DH prime */ + if (BUG(dh_param_p_tls)) { + /* LCOV_EXCL_START + * + * We shouldn't be calling this twice. + */ + BN_clear_free(dh_param_p_tls); + dh_param_p_tls = NULL; + /* LCOV_EXCL_STOP */ + } + + tls_prime = BN_new(); + tor_assert(tls_prime); + + r = BN_hex2bn(&tls_prime, TLS_DH_PRIME); + tor_assert(r); + + tor_assert(tls_prime); + + dh_param_p_tls = tls_prime; + crypto_set_dh_generator(); + tor_assert(0 == crypto_validate_dh_params(dh_param_p_tls, dh_param_g)); +} + +/** Initialize dh_param_p and dh_param_g if they are not already + * set. */ +static void +init_dh_param(void) +{ + BIGNUM *circuit_dh_prime; + int r; + if (BUG(dh_param_p && dh_param_g)) + return; // LCOV_EXCL_LINE This function isn't supposed to be called twice. + + circuit_dh_prime = BN_new(); + tor_assert(circuit_dh_prime); + + r = BN_hex2bn(&circuit_dh_prime, OAKLEY_PRIME_2); + tor_assert(r); + + /* Set the new values as the global DH parameters. */ + dh_param_p = circuit_dh_prime; + crypto_set_dh_generator(); + tor_assert(0 == crypto_validate_dh_params(dh_param_p, dh_param_g)); + + if (!dh_param_p_tls) { + crypto_set_tls_dh_prime(); + } +} + +/** Number of bits to use when choosing the x or y value in a Diffie-Hellman + * handshake. Since we exponentiate by this value, choosing a smaller one + * lets our handhake go faster. + */ +#define DH_PRIVATE_KEY_BITS 320 + +/** Allocate and return a new DH object for a key exchange. Returns NULL on + * failure. + */ +crypto_dh_t * +crypto_dh_new(int dh_type) +{ + crypto_dh_t *res = tor_malloc_zero(sizeof(crypto_dh_t)); + + tor_assert(dh_type == DH_TYPE_CIRCUIT || dh_type == DH_TYPE_TLS || + dh_type == DH_TYPE_REND); + + if (!dh_param_p) + init_dh_param(); + + if (!(res->dh = DH_new())) + goto err; + +#ifdef OPENSSL_1_1_API + BIGNUM *dh_p = NULL, *dh_g = NULL; + + if (dh_type == DH_TYPE_TLS) { + dh_p = BN_dup(dh_param_p_tls); + } else { + dh_p = BN_dup(dh_param_p); + } + if (!dh_p) + goto err; + + dh_g = BN_dup(dh_param_g); + if (!dh_g) { + BN_free(dh_p); + goto err; + } + + if (!DH_set0_pqg(res->dh, dh_p, NULL, dh_g)) { + goto err; + } + + if (!DH_set_length(res->dh, DH_PRIVATE_KEY_BITS)) + goto err; +#else /* !(defined(OPENSSL_1_1_API)) */ + if (dh_type == DH_TYPE_TLS) { + if (!(res->dh->p = BN_dup(dh_param_p_tls))) + goto err; + } else { + if (!(res->dh->p = BN_dup(dh_param_p))) + goto err; + } + + if (!(res->dh->g = BN_dup(dh_param_g))) + goto err; + + res->dh->length = DH_PRIVATE_KEY_BITS; +#endif /* defined(OPENSSL_1_1_API) */ + + return res; + + /* LCOV_EXCL_START + * This error condition is only reached when an allocation fails */ + err: + crypto_openssl_log_errors(LOG_WARN, "creating DH object"); + if (res->dh) DH_free(res->dh); /* frees p and g too */ + tor_free(res); + return NULL; + /* LCOV_EXCL_STOP */ +} + +/** Return a copy of dh, sharing its internal state. */ +crypto_dh_t * +crypto_dh_dup(const crypto_dh_t *dh) +{ + crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t)); + tor_assert(dh); + tor_assert(dh->dh); + dh_new->dh = dh->dh; + DH_up_ref(dh->dh); + return dh_new; +} + +/** Return the length of the DH key in dh, in bytes. + */ +int +crypto_dh_get_bytes(crypto_dh_t *dh) +{ + tor_assert(dh); + return DH_size(dh->dh); +} + +/** Generate \ for our part of the key exchange. Return 0 on + * success, -1 on failure. + */ +int +crypto_dh_generate_public(crypto_dh_t *dh) +{ +#ifndef OPENSSL_1_1_API + again: +#endif + if (!DH_generate_key(dh->dh)) { + /* LCOV_EXCL_START + * To test this we would need some way to tell openssl to break DH. */ + crypto_openssl_log_errors(LOG_WARN, "generating DH key"); + return -1; + /* LCOV_EXCL_STOP */ + } +#ifdef OPENSSL_1_1_API + /* OpenSSL 1.1.x doesn't appear to let you regenerate a DH key, without + * recreating the DH object. I have no idea what sort of aliasing madness + * can occur here, so do the check, and just bail on failure. + */ + const BIGNUM *pub_key, *priv_key; + DH_get0_key(dh->dh, &pub_key, &priv_key); + if (tor_check_dh_key(LOG_WARN, pub_key)<0) { + log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-" + "the-universe chances really do happen. Treating as a failure."); + return -1; + } +#else /* !(defined(OPENSSL_1_1_API)) */ + if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) { + /* LCOV_EXCL_START + * If this happens, then openssl's DH implementation is busted. */ + log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-" + "the-universe chances really do happen. Trying again."); + /* Free and clear the keys, so OpenSSL will actually try again. */ + BN_clear_free(dh->dh->pub_key); + BN_clear_free(dh->dh->priv_key); + dh->dh->pub_key = dh->dh->priv_key = NULL; + goto again; + /* LCOV_EXCL_STOP */ + } +#endif /* defined(OPENSSL_1_1_API) */ + return 0; +} + +/** Generate g^x as necessary, and write the g^x for the key exchange + * as a pubkey_len-byte value into pubkey. Return 0 on + * success, -1 on failure. pubkey_len must be \>= DH1024_KEY_LEN. + */ +int +crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len) +{ + int bytes; + tor_assert(dh); + + const BIGNUM *dh_pub; + +#ifdef OPENSSL_1_1_API + const BIGNUM *dh_priv; + DH_get0_key(dh->dh, &dh_pub, &dh_priv); +#else + dh_pub = dh->dh->pub_key; +#endif /* defined(OPENSSL_1_1_API) */ + + if (!dh_pub) { + if (crypto_dh_generate_public(dh)<0) + return -1; + else { +#ifdef OPENSSL_1_1_API + DH_get0_key(dh->dh, &dh_pub, &dh_priv); +#else + dh_pub = dh->dh->pub_key; +#endif + } + } + + tor_assert(dh_pub); + bytes = BN_num_bytes(dh_pub); + tor_assert(bytes >= 0); + if (pubkey_len < (size_t)bytes) { + log_warn(LD_CRYPTO, + "Weird! pubkey_len (%d) was smaller than DH1024_KEY_LEN (%d)", + (int) pubkey_len, bytes); + return -1; + } + + memset(pubkey, 0, pubkey_len); + BN_bn2bin(dh_pub, (unsigned char*)(pubkey+(pubkey_len-bytes))); + + return 0; +} + +/** Check for bad Diffie-Hellman public keys (g^x). Return 0 if the key is + * okay (in the subgroup [2,p-2]), or -1 if it's bad. + * See http://www.cl.cam.ac.uk/ftp/users/rja14/psandqs.ps.gz for some tips. + */ +static int +tor_check_dh_key(int severity, const BIGNUM *bn) +{ + BIGNUM *x; + char *s; + tor_assert(bn); + x = BN_new(); + tor_assert(x); + if (BUG(!dh_param_p)) + init_dh_param(); //LCOV_EXCL_LINE we already checked whether we did this. + BN_set_word(x, 1); + if (BN_cmp(bn,x)<=0) { + log_fn(severity, LD_CRYPTO, "DH key must be at least 2."); + goto err; + } + BN_copy(x,dh_param_p); + BN_sub_word(x, 1); + if (BN_cmp(bn,x)>=0) { + log_fn(severity, LD_CRYPTO, "DH key must be at most p-2."); + goto err; + } + BN_clear_free(x); + return 0; + err: + BN_clear_free(x); + s = BN_bn2hex(bn); + log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s); + OPENSSL_free(s); + return -1; +} + +/** Given a DH key exchange object, and our peer's value of g^y (as a + * pubkey_len-byte value in pubkey) generate + * secret_bytes_out bytes of shared key material and write them + * to secret_out. Return the number of bytes generated on success, + * or -1 on failure. + * + * (We generate key material by computing + * SHA1( g^xy || "\x00" ) || SHA1( g^xy || "\x01" ) || ... + * where || is concatenation.) + */ +ssize_t +crypto_dh_compute_secret(int severity, crypto_dh_t *dh, + const char *pubkey, size_t pubkey_len, + char *secret_out, size_t secret_bytes_out) +{ + char *secret_tmp = NULL; + BIGNUM *pubkey_bn = NULL; + size_t secret_len=0, secret_tmp_len=0; + int result=0; + tor_assert(dh); + tor_assert(secret_bytes_out/DIGEST_LEN <= 255); + tor_assert(pubkey_len < INT_MAX); + + if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey, + (int)pubkey_len, NULL))) + goto error; + if (tor_check_dh_key(severity, pubkey_bn)<0) { + /* Check for invalid public keys. */ + log_fn(severity, LD_CRYPTO,"Rejected invalid g^x"); + goto error; + } + secret_tmp_len = crypto_dh_get_bytes(dh); + secret_tmp = tor_malloc(secret_tmp_len); + result = DH_compute_key((unsigned char*)secret_tmp, pubkey_bn, dh->dh); + if (result < 0) { + log_warn(LD_CRYPTO,"DH_compute_key() failed."); + goto error; + } + secret_len = result; + if (crypto_expand_key_material_TAP((uint8_t*)secret_tmp, secret_len, + (uint8_t*)secret_out, secret_bytes_out)<0) + goto error; + secret_len = secret_bytes_out; + + goto done; + error: + result = -1; + done: + crypto_openssl_log_errors(LOG_WARN, "completing DH handshake"); + if (pubkey_bn) + BN_clear_free(pubkey_bn); + if (secret_tmp) { + memwipe(secret_tmp, 0, secret_tmp_len); + tor_free(secret_tmp); + } + if (result < 0) + return result; + else + return secret_len; +} + +/** Free a DH key exchange object. + */ +void +crypto_dh_free_(crypto_dh_t *dh) +{ + if (!dh) + return; + tor_assert(dh->dh); + DH_free(dh->dh); + tor_free(dh); +} + +void +crypto_dh_free_all(void) +{ + if (dh_param_p) + BN_clear_free(dh_param_p); + if (dh_param_p_tls) + BN_clear_free(dh_param_p_tls); + if (dh_param_g) + BN_clear_free(dh_param_g); + + dh_param_p = dh_param_p_tls = dh_param_g = NULL; +} diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 018730627d..a5541d49e8 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -9,6 +9,7 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/crypto_cipher.c \ src/lib/crypt_ops/crypto_curve25519.c \ src/lib/crypt_ops/crypto_dh.c \ + src/lib/crypt_ops/crypto_dh_openssl.c \ src/lib/crypt_ops/crypto_digest.c \ src/lib/crypt_ops/crypto_ed25519.c \ src/lib/crypt_ops/crypto_format.c \ From ac9a470c641fd3ba826cdad07b1a7a495c00acba Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 17 Jul 2018 09:16:11 -0400 Subject: [PATCH 19/76] Extract the shared part of crypto_dh_compute_secret. --- src/lib/crypt_ops/crypto_dh.c | 47 +++++++++++++++++++++++++++ src/lib/crypt_ops/crypto_dh.h | 7 +++- src/lib/crypt_ops/crypto_dh_openssl.c | 34 ++++++++----------- 3 files changed, 66 insertions(+), 22 deletions(-) diff --git a/src/lib/crypt_ops/crypto_dh.c b/src/lib/crypt_ops/crypto_dh.c index 8bb66379fd..6f763e37a1 100644 --- a/src/lib/crypt_ops/crypto_dh.c +++ b/src/lib/crypt_ops/crypto_dh.c @@ -42,3 +42,50 @@ const char OAKLEY_PRIME_2[] = "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" "49286651ECE65381FFFFFFFFFFFFFFFF"; + +/** Given a DH key exchange object, and our peer's value of g^y (as a + * pubkey_len-byte value in pubkey) generate + * secret_bytes_out bytes of shared key material and write them + * to secret_out. Return the number of bytes generated on success, + * or -1 on failure. + * + * (We generate key material by computing + * SHA1( g^xy || "\x00" ) || SHA1( g^xy || "\x01" ) || ... + * where || is concatenation.) + */ +ssize_t +crypto_dh_compute_secret(int severity, crypto_dh_t *dh, + const char *pubkey, size_t pubkey_len, + char *secret_out, size_t secret_bytes_out) +{ + tor_assert(secret_bytes_out/DIGEST_LEN <= 255); + + unsigned char *secret_tmp = NULL; + size_t secret_len=0, secret_tmp_len=0; + secret_tmp_len = crypto_dh_get_bytes(dh); + secret_tmp = tor_malloc(secret_tmp_len); + + ssize_t result = crypto_dh_handshake(severity, dh, pubkey, pubkey_len, + secret_tmp, secret_tmp_len); + if (result < 0) + goto error; + + secret_len = result; + if (crypto_expand_key_material_TAP(secret_tmp, secret_len, + (uint8_t*)secret_out, secret_bytes_out)<0) + goto error; + secret_len = secret_bytes_out; + + goto done; + error: + result = -1; + done: + if (secret_tmp) { + memwipe(secret_tmp, 0, secret_tmp_len); + tor_free(secret_tmp); + } + if (result < 0) + return result; + else + return secret_len; +} diff --git a/src/lib/crypt_ops/crypto_dh.h b/src/lib/crypt_ops/crypto_dh.h index 2d25ba8c85..f8e4e4f43e 100644 --- a/src/lib/crypt_ops/crypto_dh.h +++ b/src/lib/crypt_ops/crypto_dh.h @@ -40,7 +40,11 @@ ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh, void crypto_dh_free_(crypto_dh_t *dh); #define crypto_dh_free(dh) FREE_AND_NULL(crypto_dh_t, crypto_dh_free_, (dh)) -/* Crypto DH free */ +ssize_t crypto_dh_handshake(int severity, crypto_dh_t *dh, + const char *pubkey, size_t pubkey_len, + unsigned char *secret_out, + size_t secret_bytes_out); + void crypto_dh_free_all(void); /* Prototypes for private functions only used by tortls.c, crypto.c, and the @@ -48,4 +52,5 @@ void crypto_dh_free_all(void); struct dh_st; struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh); + #endif /* !defined(TOR_CRYPTO_DH_H) */ diff --git a/src/lib/crypt_ops/crypto_dh_openssl.c b/src/lib/crypt_ops/crypto_dh_openssl.c index 4457b18144..395058d92a 100644 --- a/src/lib/crypt_ops/crypto_dh_openssl.c +++ b/src/lib/crypt_ops/crypto_dh_openssl.c @@ -405,27 +405,29 @@ tor_check_dh_key(int severity, const BIGNUM *bn) /** Given a DH key exchange object, and our peer's value of g^y (as a * pubkey_len-byte value in pubkey) generate - * secret_bytes_out bytes of shared key material and write them - * to secret_out. Return the number of bytes generated on success, + * g^xy as a big-endian integer in secret_out. + * Return the number of bytes generated on success, * or -1 on failure. * - * (We generate key material by computing - * SHA1( g^xy || "\x00" ) || SHA1( g^xy || "\x01" ) || ... - * where || is concatenation.) + * This function MUST validate that g^y is actually in the group. */ ssize_t -crypto_dh_compute_secret(int severity, crypto_dh_t *dh, - const char *pubkey, size_t pubkey_len, - char *secret_out, size_t secret_bytes_out) +crypto_dh_handshake(int severity, crypto_dh_t *dh, + const char *pubkey, size_t pubkey_len, + unsigned char *secret_out, size_t secret_bytes_out) { - char *secret_tmp = NULL; BIGNUM *pubkey_bn = NULL; - size_t secret_len=0, secret_tmp_len=0; + size_t secret_len=0; int result=0; + tor_assert(dh); tor_assert(secret_bytes_out/DIGEST_LEN <= 255); tor_assert(pubkey_len < INT_MAX); + if (BUG(crypto_dh_get_bytes(dh) > (int)secret_bytes_out)) { + goto error; + } + if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey, (int)pubkey_len, NULL))) goto error; @@ -434,18 +436,12 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh, log_fn(severity, LD_CRYPTO,"Rejected invalid g^x"); goto error; } - secret_tmp_len = crypto_dh_get_bytes(dh); - secret_tmp = tor_malloc(secret_tmp_len); - result = DH_compute_key((unsigned char*)secret_tmp, pubkey_bn, dh->dh); + result = DH_compute_key(secret_out, pubkey_bn, dh->dh); if (result < 0) { log_warn(LD_CRYPTO,"DH_compute_key() failed."); goto error; } secret_len = result; - if (crypto_expand_key_material_TAP((uint8_t*)secret_tmp, secret_len, - (uint8_t*)secret_out, secret_bytes_out)<0) - goto error; - secret_len = secret_bytes_out; goto done; error: @@ -454,10 +450,6 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh, crypto_openssl_log_errors(LOG_WARN, "completing DH handshake"); if (pubkey_bn) BN_clear_free(pubkey_bn); - if (secret_tmp) { - memwipe(secret_tmp, 0, secret_tmp_len); - tor_free(secret_tmp); - } if (result < 0) return result; else From 32bbc8f6b5e7d964439c6ba91f87f7d5fba673cc Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 17 Jul 2018 11:23:53 -0400 Subject: [PATCH 20/76] Refactor the dependency between tortls and crypto_dh. We only ever need this to get us a DH ephemeral key object, so make a function that does just that. --- src/lib/crypt_ops/crypto_dh.h | 3 +- src/lib/crypt_ops/crypto_dh_openssl.c | 73 ++++++++++++++------------- src/lib/tls/tortls.c | 6 +-- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/lib/crypt_ops/crypto_dh.h b/src/lib/crypt_ops/crypto_dh.h index f8e4e4f43e..9533626968 100644 --- a/src/lib/crypt_ops/crypto_dh.h +++ b/src/lib/crypt_ops/crypto_dh.h @@ -50,7 +50,6 @@ void crypto_dh_free_all(void); /* Prototypes for private functions only used by tortls.c, crypto.c, and the * unit tests. */ struct dh_st; -struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh); - +struct dh_st *crypto_dh_new_openssl_tls(void); #endif /* !defined(TOR_CRYPTO_DH_H) */ diff --git a/src/lib/crypt_ops/crypto_dh_openssl.c b/src/lib/crypt_ops/crypto_dh_openssl.c index 395058d92a..d66031afd6 100644 --- a/src/lib/crypt_ops/crypto_dh_openssl.c +++ b/src/lib/crypt_ops/crypto_dh_openssl.c @@ -27,6 +27,7 @@ ENABLE_GCC_WARNING(redundant-decls) #include static int tor_check_dh_key(int severity, const BIGNUM *bn); +static DH *new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g); /** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake * while we're waiting for the second.*/ @@ -34,14 +35,6 @@ struct crypto_dh_t { DH *dh; /**< The openssl DH object */ }; -/** Used by tortls.c: Get the DH* from a crypto_dh_t. - */ -DH * -crypto_dh_get_dh_(crypto_dh_t *dh) -{ - return dh->dh; -} - /** Shared P parameter for our circuit-crypto DH key exchanges. */ static BIGNUM *dh_param_p = NULL; /** Shared P parameter for our TLS DH key exchanges. */ @@ -188,6 +181,14 @@ init_dh_param(void) */ #define DH_PRIVATE_KEY_BITS 320 +/** Used by tortls.c: Get the DH* for use with TLS. + */ +DH * +crypto_dh_new_openssl_tls(void) +{ + return new_openssl_dh_from_params(dh_param_p_tls, dh_param_g); +} + /** Allocate and return a new DH object for a key exchange. Returns NULL on * failure. */ @@ -202,55 +203,59 @@ crypto_dh_new(int dh_type) if (!dh_param_p) init_dh_param(); - if (!(res->dh = DH_new())) + BIGNUM *dh_p = NULL; + if (dh_type == DH_TYPE_TLS) { + dh_p = dh_param_p_tls; + } else { + dh_p = dh_param_p; + } + + res->dh = new_openssl_dh_from_params(dh_p, dh_param_g); + if (res->dh == NULL) + tor_free(res); // sets res to NULL. + return res; +} + +/** Create and return a new openssl DH from a given prime and generator. */ +static DH * +new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g) +{ + DH *res_dh; + if (!(res_dh = DH_new())) goto err; -#ifdef OPENSSL_1_1_API BIGNUM *dh_p = NULL, *dh_g = NULL; - - if (dh_type == DH_TYPE_TLS) { - dh_p = BN_dup(dh_param_p_tls); - } else { - dh_p = BN_dup(dh_param_p); - } + dh_p = BN_dup(p); if (!dh_p) goto err; - dh_g = BN_dup(dh_param_g); + dh_g = BN_dup(g); if (!dh_g) { BN_free(dh_p); goto err; } - if (!DH_set0_pqg(res->dh, dh_p, NULL, dh_g)) { +#ifdef OPENSSL_1_1_API + + if (!DH_set0_pqg(res_dh, dh_p, NULL, dh_g)) { goto err; } - if (!DH_set_length(res->dh, DH_PRIVATE_KEY_BITS)) + if (!DH_set_length(res_dh, DH_PRIVATE_KEY_BITS)) goto err; #else /* !(defined(OPENSSL_1_1_API)) */ - if (dh_type == DH_TYPE_TLS) { - if (!(res->dh->p = BN_dup(dh_param_p_tls))) - goto err; - } else { - if (!(res->dh->p = BN_dup(dh_param_p))) - goto err; - } - - if (!(res->dh->g = BN_dup(dh_param_g))) - goto err; - - res->dh->length = DH_PRIVATE_KEY_BITS; + res_dh->p = dh_p; + res_dh->g = dh_g; + res_dh->length = DH_PRIVATE_KEY_BITS; #endif /* defined(OPENSSL_1_1_API) */ - return res; + return res_dh; /* LCOV_EXCL_START * This error condition is only reached when an allocation fails */ err: crypto_openssl_log_errors(LOG_WARN, "creating DH object"); - if (res->dh) DH_free(res->dh); /* frees p and g too */ - tor_free(res); + if (res_dh) DH_free(res_dh); /* frees p and g too */ return NULL; /* LCOV_EXCL_STOP */ } diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 8d284dde17..875ed95f8c 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -1280,10 +1280,10 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, goto error; } { - crypto_dh_t *dh = crypto_dh_new(DH_TYPE_TLS); + DH *dh = crypto_dh_new_openssl_tls(); tor_assert(dh); - SSL_CTX_set_tmp_dh(result->ctx, crypto_dh_get_dh_(dh)); - crypto_dh_free(dh); + SSL_CTX_set_tmp_dh(result->ctx, dh); + DH_free(dh); } if (! is_client) { int nid; From 17ea931ac70af3cc11c1729b09b8e1ff17a53348 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 17 Jul 2018 11:27:08 -0400 Subject: [PATCH 21/76] Implement DH in NSS. --- src/lib/crypt_ops/crypto_dh.c | 22 +++ src/lib/crypt_ops/crypto_dh.h | 11 +- src/lib/crypt_ops/crypto_dh_nss.c | 207 ++++++++++++++++++++++++++ src/lib/crypt_ops/crypto_dh_openssl.c | 90 +++++------ src/lib/crypt_ops/crypto_init.c | 2 + src/lib/crypt_ops/include.am | 4 +- src/test/testing_common.c | 1 - 7 files changed, 281 insertions(+), 56 deletions(-) create mode 100644 src/lib/crypt_ops/crypto_dh_nss.c diff --git a/src/lib/crypt_ops/crypto_dh.c b/src/lib/crypt_ops/crypto_dh.c index 6f763e37a1..673ef311f9 100644 --- a/src/lib/crypt_ops/crypto_dh.c +++ b/src/lib/crypt_ops/crypto_dh.c @@ -43,6 +43,28 @@ const char OAKLEY_PRIME_2[] = "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" "49286651ECE65381FFFFFFFFFFFFFFFF"; +void +crypto_dh_init(void) +{ +#ifdef ENABLE_OPENSSL + crypto_dh_init_openssl(); +#endif +#ifdef ENABLE_NSS + crypto_dh_init_nss(); +#endif +} + +void +crypto_dh_free_all(void) +{ +#ifdef ENABLE_OPENSSL + crypto_dh_free_all_openssl(); +#endif +#ifdef ENABLE_NSS + crypto_dh_free_all_nss(); +#endif +} + /** Given a DH key exchange object, and our peer's value of g^y (as a * pubkey_len-byte value in pubkey) generate * secret_bytes_out bytes of shared key material and write them diff --git a/src/lib/crypt_ops/crypto_dh.h b/src/lib/crypt_ops/crypto_dh.h index 9533626968..6e79a6404c 100644 --- a/src/lib/crypt_ops/crypto_dh.h +++ b/src/lib/crypt_ops/crypto_dh.h @@ -27,7 +27,7 @@ extern const char OAKLEY_PRIME_2[]; #define DH_TYPE_CIRCUIT 1 #define DH_TYPE_REND 2 #define DH_TYPE_TLS 3 -void crypto_set_tls_dh_prime(void); +void crypto_dh_init(void); crypto_dh_t *crypto_dh_new(int dh_type); crypto_dh_t *crypto_dh_dup(const crypto_dh_t *dh); int crypto_dh_get_bytes(crypto_dh_t *dh); @@ -52,4 +52,13 @@ void crypto_dh_free_all(void); struct dh_st; struct dh_st *crypto_dh_new_openssl_tls(void); +#ifdef ENABLE_OPENSSL +void crypto_dh_init_openssl(void); +void crypto_dh_free_all_openssl(void); +#endif +#ifdef ENABLE_OPENSSL +void crypto_dh_init_nss(void); +void crypto_dh_free_all_nss(void); +#endif + #endif /* !defined(TOR_CRYPTO_DH_H) */ diff --git a/src/lib/crypt_ops/crypto_dh_nss.c b/src/lib/crypt_ops/crypto_dh_nss.c new file mode 100644 index 0000000000..647dfb4e57 --- /dev/null +++ b/src/lib/crypt_ops/crypto_dh_nss.c @@ -0,0 +1,207 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_dh_nss.h + * + * \brief NSS implementation of Diffie-Hellman over Z_p. + **/ + +#include "lib/crypt_ops/crypto_dh.h" +#include "lib/crypt_ops/crypto_nss_mgt.h" + +#include "lib/encoding/binascii.h" +#include "lib/log/util_bug.h" +#include "lib/malloc/malloc.h" + +#include +#include +#include + +static int dh_initialized = 0; +static SECKEYDHParams tls_dh_param, circuit_dh_param; +static unsigned char tls_dh_prime_data[DH1024_KEY_LEN]; +static unsigned char circuit_dh_prime_data[DH1024_KEY_LEN]; +static unsigned char dh_generator_data[1]; + +void +crypto_dh_init_nss(void) +{ + if (dh_initialized) + return; + + int r; + r = base16_decode((char*)tls_dh_prime_data, + sizeof(tls_dh_prime_data), + TLS_DH_PRIME, strlen(TLS_DH_PRIME)); + tor_assert(r == DH1024_KEY_LEN); + r = base16_decode((char*)circuit_dh_prime_data, + sizeof(circuit_dh_prime_data), + OAKLEY_PRIME_2, strlen(OAKLEY_PRIME_2)); + tor_assert(r == DH1024_KEY_LEN); + dh_generator_data[0] = DH_GENERATOR; + + tls_dh_param.prime.data = tls_dh_prime_data; + tls_dh_param.prime.len = DH1024_KEY_LEN; + tls_dh_param.base.data = dh_generator_data; + tls_dh_param.base.len = 1; + + circuit_dh_param.prime.data = circuit_dh_prime_data; + circuit_dh_param.prime.len = DH1024_KEY_LEN; + circuit_dh_param.base.data = dh_generator_data; + circuit_dh_param.base.len = 1; +} + +void +crypto_dh_free_all_nss(void) +{ + dh_initialized = 0; +} + +struct crypto_dh_t { + int dh_type; // XXXX let's remove this later on. + SECKEYPrivateKey *seckey; + SECKEYPublicKey *pubkey; +}; + +crypto_dh_t * +crypto_dh_new(int dh_type) +{ + crypto_dh_t *r = tor_malloc_zero(sizeof(crypto_dh_t)); + r->dh_type = dh_type; + return r; +} + +crypto_dh_t * +crypto_dh_dup(const crypto_dh_t *dh) +{ + tor_assert(dh); + crypto_dh_t *r = crypto_dh_new(dh->dh_type); + if (dh->seckey) + r->seckey = SECKEY_CopyPrivateKey(dh->seckey); + if (dh->pubkey) + r->pubkey = SECKEY_CopyPublicKey(dh->pubkey); + return r; +} + +int +crypto_dh_get_bytes(crypto_dh_t *dh) +{ + (void)dh; + return DH1024_KEY_LEN; +} + +int +crypto_dh_generate_public(crypto_dh_t *dh) +{ + tor_assert(dh); + SECKEYDHParams *p; + if (dh->dh_type == DH_TYPE_TLS) + p = &tls_dh_param; + else + p = &circuit_dh_param; + + dh->seckey = SECKEY_CreateDHPrivateKey(p, &dh->pubkey, NULL); + if (!dh->seckey || !dh->pubkey) + return -1; + else + return 0; +} +int +crypto_dh_get_public(crypto_dh_t *dh, char *pubkey_out, + size_t pubkey_out_len) +{ + tor_assert(dh); + tor_assert(pubkey_out); + if (!dh->pubkey) { + if (crypto_dh_generate_public(dh) < 0) + return -1; + } + + const SECItem *item = &dh->pubkey->u.dh.publicValue; + + if (item->len > pubkey_out_len) + return -1; + + /* Left-pad the result with 0s. */ + memset(pubkey_out, 0, pubkey_out_len); + memcpy(pubkey_out + pubkey_out_len - item->len, + item->data, + item->len); + + return 0; +} + +void +crypto_dh_free_(crypto_dh_t *dh) +{ + if (!dh) + return; + if (dh->seckey) + SECKEY_DestroyPrivateKey(dh->seckey); + if (dh->pubkey) + SECKEY_DestroyPublicKey(dh->pubkey); + tor_free(dh); +} + +ssize_t +crypto_dh_handshake(int severity, crypto_dh_t *dh, + const char *pubkey, size_t pubkey_len, + unsigned char *secret_out, + size_t secret_bytes_out) +{ + tor_assert(dh); + if (pubkey_len > DH1024_KEY_LEN) + return -1; + if (!dh->pubkey || !dh->seckey) + return -1; + if (secret_bytes_out < DH1024_KEY_LEN) + return -1; + + SECKEYPublicKey peer_key; + memset(&peer_key, 0, sizeof(peer_key)); + peer_key.keyType = dhKey; + peer_key.pkcs11ID = CK_INVALID_HANDLE; + + if (dh->dh_type == DH_TYPE_TLS) + peer_key.u.dh.prime.data = tls_dh_prime_data; // should never use this code + else + peer_key.u.dh.prime.data = circuit_dh_prime_data; + peer_key.u.dh.prime.len = DH1024_KEY_LEN; + peer_key.u.dh.base.data = dh_generator_data; + peer_key.u.dh.base.len = 1; + peer_key.u.dh.publicValue.data = (unsigned char *)pubkey; + peer_key.u.dh.publicValue.len = pubkey_len; + + PK11SymKey *sym = PK11_PubDerive(dh->seckey, &peer_key, + PR_FALSE, NULL, NULL, CKM_DH_PKCS_DERIVE, + CKM_GENERIC_SECRET_KEY_GEN /* ??? */, + CKA_DERIVE, 0, NULL); + if (! sym) { + crypto_nss_log_errors(severity, "deriving a DH shared secret"); + return -1; + } + + SECStatus s = PK11_ExtractKeyValue(sym); + if (s != SECSuccess) { + crypto_nss_log_errors(severity, "extracting a DH shared secret"); + PK11_FreeSymKey(sym); + return -1; + } + + SECItem *result = PK11_GetKeyData(sym); + tor_assert(result); // This cannot fail. + if (BUG(result->len > secret_bytes_out)) { + PK11_FreeSymKey(sym); + return -1; + } + + ssize_t len = result->len; + memcpy(secret_out, result->data, len); + PK11_FreeSymKey(sym); + + return len; +} diff --git a/src/lib/crypt_ops/crypto_dh_openssl.c b/src/lib/crypt_ops/crypto_dh_openssl.c index d66031afd6..54946458d5 100644 --- a/src/lib/crypt_ops/crypto_dh_openssl.c +++ b/src/lib/crypt_ops/crypto_dh_openssl.c @@ -26,14 +26,17 @@ ENABLE_GCC_WARNING(redundant-decls) #include #include +#ifndef ENABLE_NSS static int tor_check_dh_key(int severity, const BIGNUM *bn); -static DH *new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g); /** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake * while we're waiting for the second.*/ struct crypto_dh_t { DH *dh; /**< The openssl DH object */ }; +#endif + +static DH *new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g); /** Shared P parameter for our circuit-crypto DH key exchanges. */ static BIGNUM *dh_param_p = NULL; @@ -96,6 +99,22 @@ crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g) return ret; } +/** + * Helper: convert hex to a bignum, and return it. Assert that the + * operation was successful. + */ +static BIGNUM * +bignum_from_hex(const char *hex) +{ + BIGNUM *result = BN_new(); + tor_assert(result); + + int r = BN_hex2bn(&result, hex); + tor_assert(r); + tor_assert(result); + return result; +} + /** Set the global Diffie-Hellman generator, used for both TLS and internal * DH stuff. */ @@ -117,62 +136,23 @@ crypto_set_dh_generator(void) dh_param_g = generator; } -/** Set the global TLS Diffie-Hellman modulus. Use the Apache mod_ssl DH - * modulus. */ +/** Initialize our DH parameters. Idempotent. */ void -crypto_set_tls_dh_prime(void) +crypto_dh_init_openssl(void) { - BIGNUM *tls_prime = NULL; - int r; + if (dh_param_p && dh_param_g && dh_param_p_tls) + return; - /* If the space is occupied, free the previous TLS DH prime */ - if (BUG(dh_param_p_tls)) { - /* LCOV_EXCL_START - * - * We shouldn't be calling this twice. - */ - BN_clear_free(dh_param_p_tls); - dh_param_p_tls = NULL; - /* LCOV_EXCL_STOP */ - } + tor_assert(dh_param_g == NULL); + tor_assert(dh_param_p == NULL); + tor_assert(dh_param_p_tls == NULL); - tls_prime = BN_new(); - tor_assert(tls_prime); - - r = BN_hex2bn(&tls_prime, TLS_DH_PRIME); - tor_assert(r); - - tor_assert(tls_prime); - - dh_param_p_tls = tls_prime; crypto_set_dh_generator(); - tor_assert(0 == crypto_validate_dh_params(dh_param_p_tls, dh_param_g)); -} + dh_param_p = bignum_from_hex(OAKLEY_PRIME_2); + dh_param_p_tls = bignum_from_hex(TLS_DH_PRIME); -/** Initialize dh_param_p and dh_param_g if they are not already - * set. */ -static void -init_dh_param(void) -{ - BIGNUM *circuit_dh_prime; - int r; - if (BUG(dh_param_p && dh_param_g)) - return; // LCOV_EXCL_LINE This function isn't supposed to be called twice. - - circuit_dh_prime = BN_new(); - tor_assert(circuit_dh_prime); - - r = BN_hex2bn(&circuit_dh_prime, OAKLEY_PRIME_2); - tor_assert(r); - - /* Set the new values as the global DH parameters. */ - dh_param_p = circuit_dh_prime; - crypto_set_dh_generator(); tor_assert(0 == crypto_validate_dh_params(dh_param_p, dh_param_g)); - - if (!dh_param_p_tls) { - crypto_set_tls_dh_prime(); - } + tor_assert(0 == crypto_validate_dh_params(dh_param_p_tls, dh_param_g)); } /** Number of bits to use when choosing the x or y value in a Diffie-Hellman @@ -189,6 +169,7 @@ crypto_dh_new_openssl_tls(void) return new_openssl_dh_from_params(dh_param_p_tls, dh_param_g); } +#ifndef ENABLE_NSS /** Allocate and return a new DH object for a key exchange. Returns NULL on * failure. */ @@ -201,7 +182,7 @@ crypto_dh_new(int dh_type) dh_type == DH_TYPE_REND); if (!dh_param_p) - init_dh_param(); + crypto_dh_init(); BIGNUM *dh_p = NULL; if (dh_type == DH_TYPE_TLS) { @@ -215,6 +196,7 @@ crypto_dh_new(int dh_type) tor_free(res); // sets res to NULL. return res; } +#endif /** Create and return a new openssl DH from a given prime and generator. */ static DH * @@ -260,6 +242,7 @@ new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g) /* LCOV_EXCL_STOP */ } +#ifndef ENABLE_NSS /** Return a copy of dh, sharing its internal state. */ crypto_dh_t * crypto_dh_dup(const crypto_dh_t *dh) @@ -386,7 +369,7 @@ tor_check_dh_key(int severity, const BIGNUM *bn) x = BN_new(); tor_assert(x); if (BUG(!dh_param_p)) - init_dh_param(); //LCOV_EXCL_LINE we already checked whether we did this. + crypto_dh_init(); //LCOV_EXCL_LINE we already checked whether we did this. BN_set_word(x, 1); if (BN_cmp(bn,x)<=0) { log_fn(severity, LD_CRYPTO, "DH key must be at least 2."); @@ -472,9 +455,10 @@ crypto_dh_free_(crypto_dh_t *dh) DH_free(dh->dh); tor_free(dh); } +#endif void -crypto_dh_free_all(void) +crypto_dh_free_all_openssl(void) { if (dh_param_p) BN_clear_free(dh_param_p); diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c index b651474cf8..620fe8e1be 100644 --- a/src/lib/crypt_ops/crypto_init.c +++ b/src/lib/crypt_ops/crypto_init.c @@ -83,6 +83,8 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) crypto_global_initialized_ = 1; + crypto_dh_init(); + #ifdef ENABLE_OPENSSL if (crypto_openssl_late_init(useAccel, accelName, accelDir) < 0) return -1; diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index a5541d49e8..05451cc0e0 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -25,10 +25,12 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ if USE_NSS src_lib_libtor_crypt_ops_a_SOURCES += \ src/lib/crypt_ops/aes_nss.c \ + src/lib/crypt_ops/crypto_dh_nss.c \ src/lib/crypt_ops/crypto_nss_mgt.c else src_lib_libtor_crypt_ops_a_SOURCES += \ - src/lib/crypt_ops/aes_openssl.c + src/lib/crypt_ops/aes_openssl.c \ + src/lib/crypt_ops/crypto_dh_openssl.c endif if USE_OPENSSL diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 1611a54b6a..6957342c62 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -292,7 +292,6 @@ main(int c, const char **v) printf("Can't initialize crypto subsystem; exiting.\n"); return 1; } - crypto_set_tls_dh_prime(); if (crypto_seed_rng() < 0) { printf("Couldn't seed RNG; exiting.\n"); return 1; From f5e22358b0d8f71e4d6bad4b72859e2d3f38c700 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 17 Jul 2018 13:08:34 -0400 Subject: [PATCH 22/76] Additional tests for NSS DH Notably, there's a test to make sure that it round-trips with OpenSSL, if OpenSSL is enabled. --- src/test/test_crypto.c | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 17f23b96bd..029e98a630 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -26,6 +26,13 @@ #include #endif +#if defined(ENABLE_OPENSSL) +#include "lib/crypt_ops/compat_openssl.h" +DISABLE_GCC_WARNING(redundant-decls) +#include +ENABLE_GCC_WARNING(redundant-decls) +#endif + /** Run unit tests for Diffie-Hellman functionality. */ static void test_crypto_dh(void *arg) @@ -38,6 +45,11 @@ test_crypto_dh(void *arg) char s1[DH1024_KEY_LEN]; char s2[DH1024_KEY_LEN]; ssize_t s1len, s2len; +#ifdef ENABLE_OPENSSL + crypto_dh_t *dh3 = NULL; + DH *dh4 = NULL; + BIGNUM *pubkey_tmp = NULL; +#endif (void)arg; tt_int_op(crypto_dh_get_bytes(dh1),OP_EQ, DH1024_KEY_LEN); @@ -92,6 +104,10 @@ test_crypto_dh(void *arg) s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x02", 1, s1, 50); tt_int_op(50, OP_EQ, s1len); + /* 2 a second time is still okay, though weird. */ + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x02", 1, s1, 50); + tt_int_op(50, OP_EQ, s1len); + const char P[] = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" @@ -158,10 +174,50 @@ test_crypto_dh(void *arg) tt_int_op(s1len, OP_EQ, -1); } +#if defined(ENABLE_OPENSSL) + { + /* Make sure that our crypto library can handshake with openssl. */ + dh3 = crypto_dh_new(DH_TYPE_TLS); + tt_assert(!crypto_dh_get_public(dh3, p1, DH1024_KEY_LEN)); + + dh4 = crypto_dh_new_openssl_tls(); + tt_assert(DH_generate_key(dh4)); + const BIGNUM *pk=NULL; +#ifdef OPENSSL_1_1_API + const BIGNUM *sk=NULL; + DH_get0_key(dh4, &pk, &sk); +#else + pk = dh4->pub_key; +#endif + tt_assert(pk); + tt_int_op(BN_num_bytes(pk), OP_LE, DH1024_KEY_LEN); + tt_int_op(BN_num_bytes(pk), OP_GT, 0); + memset(p2, 0, sizeof(p2)); + /* right-pad. */ + BN_bn2bin(pk, (unsigned char *)(p2+DH1024_KEY_LEN-BN_num_bytes(pk))); + + s1len = crypto_dh_handshake(LOG_WARN, dh3, p2, DH1024_KEY_LEN, + (unsigned char *)s1, sizeof(s1)); + pubkey_tmp = BN_bin2bn((unsigned char *)p1, DH1024_KEY_LEN, NULL); + s2len = DH_compute_key((unsigned char *)s2, pubkey_tmp, dh4); + + tt_int_op(s1len, OP_EQ, s2len); + tt_int_op(s1len, OP_GT, 0); + tt_mem_op(s1, OP_EQ, s2, s1len); + } +#endif + done: crypto_dh_free(dh1); crypto_dh_free(dh2); crypto_dh_free(dh1_dup); +#ifdef ENABLE_OPENSSL + crypto_dh_free(dh3); + if (dh4) + DH_free(dh4); + if (pubkey_tmp) + BN_free(pubkey_tmp); +#endif } static void From 17f922d3719837fade1888dfa7cc99ac801ad800 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 18 Jul 2018 10:19:20 -0400 Subject: [PATCH 23/76] Only link crypto_dh_openssl.c once (We do this unconditionally, since we still need it for tortls.c) --- src/lib/crypt_ops/include.am | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 05451cc0e0..803d448852 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -29,8 +29,7 @@ src_lib_libtor_crypt_ops_a_SOURCES += \ src/lib/crypt_ops/crypto_nss_mgt.c else src_lib_libtor_crypt_ops_a_SOURCES += \ - src/lib/crypt_ops/aes_openssl.c \ - src/lib/crypt_ops/crypto_dh_openssl.c + src/lib/crypt_ops/aes_openssl.c endif if USE_OPENSSL From a4c0a0e81eb3616d7d66806e9ac8ea7c8d3bf439 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 31 Jul 2018 19:56:42 -0400 Subject: [PATCH 24/76] Fix issues with crypto_ope compilation now that crypto.h is gone --- src/lib/crypt_ops/crypto_ope.c | 2 +- src/test/test_crypto_ope.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/crypt_ops/crypto_ope.c b/src/lib/crypt_ops/crypto_ope.c index ca42ae4341..fd5d5f3770 100644 --- a/src/lib/crypt_ops/crypto_ope.c +++ b/src/lib/crypt_ops/crypto_ope.c @@ -18,8 +18,8 @@ #define CRYPTO_OPE_PRIVATE #include "lib/crypt_ops/crypto_ope.h" -#include "lib/crypt_ops/crypto.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/log/util_bug.h" #include "lib/malloc/malloc.h" #include "lib/arch/bytes.h" diff --git a/src/test/test_crypto_ope.c b/src/test/test_crypto_ope.c index 7dcad7b4b2..4e7b952327 100644 --- a/src/test/test_crypto_ope.c +++ b/src/test/test_crypto_ope.c @@ -7,9 +7,11 @@ #define CRYPTO_OPE_PRIVATE +#include "lib/cc/compat_compiler.h" #include "lib/crypt_ops/crypto_ope.h" -#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_cipher.h" #include "lib/encoding/binascii.h" +#include "lib/malloc/malloc.h" #include "test/test.h" #include "tinytest.h" From b590cc0449e1432a36e61bf259e92c679c8f5b6d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 2 Aug 2018 08:36:24 -0400 Subject: [PATCH 25/76] Add a cast to make clang happier. --- src/lib/crypt_ops/crypto_dh_nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/crypt_ops/crypto_dh_nss.c b/src/lib/crypt_ops/crypto_dh_nss.c index 647dfb4e57..9a14b809b4 100644 --- a/src/lib/crypt_ops/crypto_dh_nss.c +++ b/src/lib/crypt_ops/crypto_dh_nss.c @@ -174,7 +174,7 @@ crypto_dh_handshake(int severity, crypto_dh_t *dh, peer_key.u.dh.base.data = dh_generator_data; peer_key.u.dh.base.len = 1; peer_key.u.dh.publicValue.data = (unsigned char *)pubkey; - peer_key.u.dh.publicValue.len = pubkey_len; + peer_key.u.dh.publicValue.len = (int) pubkey_len; PK11SymKey *sym = PK11_PubDerive(dh->seckey, &peer_key, PR_FALSE, NULL, NULL, CKM_DH_PKCS_DERIVE, From 727f1676d68fca00f2f40f166b5afda3a5728dc4 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 2 Aug 2018 08:38:59 -0400 Subject: [PATCH 26/76] Fix double-link of crypto_openssl_mgt.c --- src/lib/crypt_ops/include.am | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 8647a91e8c..f6164dd213 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -16,7 +16,6 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/crypto_hkdf.c \ src/lib/crypt_ops/crypto_init.c \ src/lib/crypt_ops/crypto_ope.c \ - src/lib/crypt_ops/crypto_openssl_mgt.c \ src/lib/crypt_ops/crypto_pwbox.c \ src/lib/crypt_ops/crypto_rand.c \ src/lib/crypt_ops/crypto_rsa.c \ From f83b417bf819ef0cc5f6ff31dd683a53cd7f90f2 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 2 Aug 2018 08:41:33 -0400 Subject: [PATCH 27/76] Suppress strict-prototypes warning in crypto_nss_mgt.c --- src/lib/crypt_ops/crypto_nss_mgt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c index 6bcaeabd5a..89ef019218 100644 --- a/src/lib/crypt_ops/crypto_nss_mgt.c +++ b/src/lib/crypt_ops/crypto_nss_mgt.c @@ -15,6 +15,7 @@ #include "lib/log/log.h" #include "lib/log/util_bug.h" +DISABLE_GCC_WARNING(strict-prototypes) #include #include #include @@ -22,6 +23,7 @@ #include #include #include +ENABLE_GCC_WARNING(strict-prototypes) const char * crypto_nss_get_version_str(void) From 4f300d547d65e50ac1fd635f8b22714c1544ba33 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 8 Aug 2018 16:50:23 -0400 Subject: [PATCH 28/76] When RunAsDaemon is set, crypto_postfork() as needed --- src/app/config/config.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/config/config.c b/src/app/config/config.c index 1b1889779d..eb935fd4ee 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -81,6 +81,7 @@ #include "core/mainloop/cpuworker.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/crypto_init.h" #include "feature/dircache/dirserv.h" #include "feature/relay/dns.h" #include "core/or/dos.h" @@ -2026,7 +2027,9 @@ options_act(const or_options_t *old_options) /* Finish backgrounding the process */ if (options->RunAsDaemon) { /* We may be calling this for the n'th time (on SIGHUP), but it's safe. */ - finish_daemon(options->DataDirectory); + int forked = finish_daemon(options->DataDirectory); + if (forked) + crypto_postfork(); } /* See whether we need to enable/disable our once-a-second timer. */ From 622a2c6bee19af3bab0e84702a36ff2df39db7e7 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 8 Aug 2018 16:59:53 -0400 Subject: [PATCH 29/76] Make finish_daemon() return a boolean to say whether it did anything. --- src/lib/process/daemon.c | 9 ++++++--- src/lib/process/daemon.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib/process/daemon.c b/src/lib/process/daemon.c index 671579838e..4836b3951c 100644 --- a/src/lib/process/daemon.c +++ b/src/lib/process/daemon.c @@ -102,15 +102,16 @@ start_daemon(void) /** Finish putting the process into daemon mode: drop standard fds, and tell * the parent process to exit. (Note: it's safe to call this more than once: * calls after the first are ignored. Calls start_daemon first if it hasn't - * been called already.) + * been called already.) Return true if we actually did a fork; false if we + * didn't. */ -void +int finish_daemon(const char *desired_cwd) { int nullfd; char c = '.'; if (finish_daemon_called) - return; + return 0; if (!start_daemon_called) start_daemon(); finish_daemon_called = 1; @@ -149,6 +150,8 @@ finish_daemon(const char *desired_cwd) log_err(LD_GENERAL,"write failed. Exiting."); } close(daemon_filedes[1]); + + return 0; } #else /* !(!defined(_WIN32)) */ /* defined(_WIN32) */ diff --git a/src/lib/process/daemon.h b/src/lib/process/daemon.h index 1f26e92221..08cab17e12 100644 --- a/src/lib/process/daemon.h +++ b/src/lib/process/daemon.h @@ -12,6 +12,6 @@ #define TOR_DAEMON_H void start_daemon(void); -void finish_daemon(const char *desired_cwd); +int finish_daemon(const char *desired_cwd); #endif From a57c27a1c749add88b088f570838373687a0c6fd Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 8 Aug 2018 17:32:26 -0400 Subject: [PATCH 30/76] Call crypto_postfork on start_daemon() instead. --- src/app/config/config.c | 7 +++---- src/lib/process/daemon.c | 10 ++++++---- src/lib/process/daemon.h | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/app/config/config.c b/src/app/config/config.c index eb935fd4ee..d2ed295621 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -1411,7 +1411,8 @@ options_act_reversible(const or_options_t *old_options, char **msg) * processes. */ if (running_tor && options->RunAsDaemon) { /* No need to roll back, since you can't change the value. */ - start_daemon(); + if (start_daemon()) + crypto_postfork(); } #ifdef HAVE_SYSTEMD @@ -2027,9 +2028,7 @@ options_act(const or_options_t *old_options) /* Finish backgrounding the process */ if (options->RunAsDaemon) { /* We may be calling this for the n'th time (on SIGHUP), but it's safe. */ - int forked = finish_daemon(options->DataDirectory); - if (forked) - crypto_postfork(); + finish_daemon(options->DataDirectory); } /* See whether we need to enable/disable our once-a-second timer. */ diff --git a/src/lib/process/daemon.c b/src/lib/process/daemon.c index 4836b3951c..3fc2241045 100644 --- a/src/lib/process/daemon.c +++ b/src/lib/process/daemon.c @@ -41,15 +41,16 @@ static int daemon_filedes[2]; /** Start putting the process into daemon mode: fork and drop all resources * except standard fds. The parent process never returns, but stays around * until finish_daemon is called. (Note: it's safe to call this more - * than once: calls after the first are ignored.) + * than once: calls after the first are ignored.) Return true if we actually + * forked and this is the child; false otherwise. */ -void +int start_daemon(void) { pid_t pid; if (start_daemon_called) - return; + return 0; start_daemon_called = 1; if (pipe(daemon_filedes)) { @@ -80,6 +81,7 @@ start_daemon(void) exit(0); // exit ok: during daemonize, daemonizing. else exit(1); /* child reported error. exit ok: daemonize failed. */ + return 0; // LCOV_EXCL_LINE unreachable } else { /* Child */ close(daemon_filedes[0]); /* we only write */ @@ -95,7 +97,7 @@ start_daemon(void) } set_main_thread(); /* We are now the main thread. */ - return; + return 1; } } diff --git a/src/lib/process/daemon.h b/src/lib/process/daemon.h index 08cab17e12..c3b78029af 100644 --- a/src/lib/process/daemon.h +++ b/src/lib/process/daemon.h @@ -11,7 +11,7 @@ #ifndef TOR_DAEMON_H #define TOR_DAEMON_H -void start_daemon(void); +int start_daemon(void); int finish_daemon(const char *desired_cwd); #endif From e56f0c9d33a6d7f30f790ec304c87900e9ea7d51 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 14 Aug 2018 16:44:59 -0400 Subject: [PATCH 31/76] Adjust windows stubs for new start/finish_daemon() return types --- src/lib/process/daemon.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/process/daemon.c b/src/lib/process/daemon.c index 3fc2241045..c64affd8b9 100644 --- a/src/lib/process/daemon.c +++ b/src/lib/process/daemon.c @@ -157,13 +157,15 @@ finish_daemon(const char *desired_cwd) } #else /* !(!defined(_WIN32)) */ /* defined(_WIN32) */ -void +int start_daemon(void) { + return 0; } -void +int finish_daemon(const char *cp) { (void)cp; + return 0; } #endif /* !defined(_WIN32) */ From 752ffa21973c67a789c34511e13d079d8bec3858 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 19 Jul 2018 08:53:40 -0400 Subject: [PATCH 32/76] Extract openssl RSA functionality into its own file. --- src/lib/crypt_ops/crypto_rsa.c | 795 ------------------------ src/lib/crypt_ops/crypto_rsa_openssl.c | 815 +++++++++++++++++++++++++ src/lib/crypt_ops/include.am | 1 + 3 files changed, 816 insertions(+), 795 deletions(-) create mode 100644 src/lib/crypt_ops/crypto_rsa_openssl.c diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c index ffe2bd2ceb..328fd732d2 100644 --- a/src/lib/crypt_ops/crypto_rsa.c +++ b/src/lib/crypt_ops/crypto_rsa.c @@ -21,33 +21,11 @@ #include "lib/log/util_bug.h" #include "lib/fs/files.h" -DISABLE_GCC_WARNING(redundant-decls) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ENABLE_GCC_WARNING(redundant-decls) - #include "lib/log/log.h" #include "lib/encoding/binascii.h" #include -/** Declaration for crypto_pk_t structure. */ -struct crypto_pk_t -{ - int refs; /**< reference count, so we don't have to copy keys */ - RSA *key; /**< The key itself */ -}; - /** Return the number of bytes added by padding method padding. */ int @@ -72,442 +50,6 @@ crypto_get_rsa_padding(int padding) } } -/** used internally: quicly validate a crypto_pk_t object as a private key. - * Return 1 iff the public key is valid, 0 if obviously invalid. - */ -static int -crypto_pk_private_ok(const crypto_pk_t *k) -{ -#ifdef OPENSSL_1_1_API - if (!k || !k->key) - return 0; - - const BIGNUM *p, *q; - RSA_get0_factors(k->key, &p, &q); - return p != NULL; /* XXX/yawning: Should we check q? */ -#else /* !(defined(OPENSSL_1_1_API)) */ - return k && k->key && k->key->p; -#endif /* defined(OPENSSL_1_1_API) */ -} - -/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */ -crypto_pk_t * -crypto_new_pk_from_rsa_(RSA *rsa) -{ - crypto_pk_t *env; - tor_assert(rsa); - env = tor_malloc(sizeof(crypto_pk_t)); - env->refs = 1; - env->key = rsa; - return env; -} - -/** Helper, used by tor-gencert.c. Return the RSA from a - * crypto_pk_t. */ -RSA * -crypto_pk_get_rsa_(crypto_pk_t *env) -{ - return env->key; -} - -/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff - * private is set, include the private-key portion of the key. Return a valid - * pointer on success, and NULL on failure. */ -MOCK_IMPL(EVP_PKEY *, -crypto_pk_get_evp_pkey_,(crypto_pk_t *env, int private)) -{ - RSA *key = NULL; - EVP_PKEY *pkey = NULL; - tor_assert(env->key); - if (private) { - if (!(key = RSAPrivateKey_dup(env->key))) - goto error; - } else { - if (!(key = RSAPublicKey_dup(env->key))) - goto error; - } - if (!(pkey = EVP_PKEY_new())) - goto error; - if (!(EVP_PKEY_assign_RSA(pkey, key))) - goto error; - return pkey; - error: - if (pkey) - EVP_PKEY_free(pkey); - if (key) - RSA_free(key); - return NULL; -} - -/** Allocate and return storage for a public key. The key itself will not yet - * be set. - */ -MOCK_IMPL(crypto_pk_t *, -crypto_pk_new,(void)) -{ - RSA *rsa; - - rsa = RSA_new(); - tor_assert(rsa); - return crypto_new_pk_from_rsa_(rsa); -} - -/** Release a reference to an asymmetric key; when all the references - * are released, free the key. - */ -void -crypto_pk_free_(crypto_pk_t *env) -{ - if (!env) - return; - - if (--env->refs > 0) - return; - tor_assert(env->refs == 0); - - if (env->key) - RSA_free(env->key); - - tor_free(env); -} - -/** Generate a bits-bit new public/private keypair in env. - * Return 0 on success, -1 on failure. - */ -MOCK_IMPL(int, -crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)) -{ - tor_assert(env); - - if (env->key) { - RSA_free(env->key); - env->key = NULL; - } - - { - BIGNUM *e = BN_new(); - RSA *r = NULL; - if (!e) - goto done; - if (! BN_set_word(e, 65537)) - goto done; - r = RSA_new(); - if (!r) - goto done; - if (RSA_generate_key_ex(r, bits, e, NULL) == -1) - goto done; - - env->key = r; - r = NULL; - done: - if (e) - BN_clear_free(e); - if (r) - RSA_free(r); - } - - if (!env->key) { - crypto_openssl_log_errors(LOG_WARN, "generating RSA key"); - return -1; - } - - return 0; -} - -/** A PEM callback that always reports a failure to get a password */ -static int -pem_no_password_cb(char *buf, int size, int rwflag, void *u) -{ - (void)buf; - (void)size; - (void)rwflag; - (void)u; - return -1; -} - -/** Read a PEM-encoded private key from the len-byte string s - * into env. Return 0 on success, -1 on failure. If len is -1, - * the string is nul-terminated. - */ -int -crypto_pk_read_private_key_from_string(crypto_pk_t *env, - const char *s, ssize_t len) -{ - BIO *b; - - tor_assert(env); - tor_assert(s); - tor_assert(len < INT_MAX && len < SSIZE_T_CEILING); - - /* Create a read-only memory BIO, backed by the string 's' */ - b = BIO_new_mem_buf((char*)s, (int)len); - if (!b) - return -1; - - if (env->key) - RSA_free(env->key); - - env->key = PEM_read_bio_RSAPrivateKey(b,NULL,pem_no_password_cb,NULL); - - BIO_free(b); - - if (!env->key) { - crypto_openssl_log_errors(LOG_WARN, "Error parsing private key"); - return -1; - } - return 0; -} - -/** Read a PEM-encoded private key from the file named by - * keyfile into env. Return 0 on success, -1 on failure. - */ -int -crypto_pk_read_private_key_from_filename(crypto_pk_t *env, - const char *keyfile) -{ - char *contents; - int r; - - /* Read the file into a string. */ - contents = read_file_to_str(keyfile, 0, NULL); - if (!contents) { - log_warn(LD_CRYPTO, "Error reading private key from \"%s\"", keyfile); - return -1; - } - - /* Try to parse it. */ - r = crypto_pk_read_private_key_from_string(env, contents, -1); - memwipe(contents, 0, strlen(contents)); - tor_free(contents); - if (r) - return -1; /* read_private_key_from_string already warned, so we don't.*/ - - /* Make sure it's valid. */ - if (crypto_pk_check_key(env) <= 0) - return -1; - - return 0; -} - -/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on - * success, -1 on failure. */ -static int -crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest, - size_t *len, int is_public) -{ - BUF_MEM *buf; - BIO *b; - int r; - - tor_assert(env); - tor_assert(env->key); - tor_assert(dest); - - b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ - if (!b) - return -1; - - /* Now you can treat b as if it were a file. Just use the - * PEM_*_bio_* functions instead of the non-bio variants. - */ - if (is_public) - r = PEM_write_bio_RSAPublicKey(b, env->key); - else - r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL); - - if (!r) { - crypto_openssl_log_errors(LOG_WARN, "writing RSA key to string"); - BIO_free(b); - return -1; - } - - BIO_get_mem_ptr(b, &buf); - - *dest = tor_malloc(buf->length+1); - memcpy(*dest, buf->data, buf->length); - (*dest)[buf->length] = 0; /* nul terminate it */ - *len = buf->length; - - BIO_free(b); - - return 0; -} - -/** PEM-encode the public key portion of env and write it to a - * newly allocated string. On success, set *dest to the new - * string, *len to the string's length, and return 0. On - * failure, return -1. - */ -int -crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest, - size_t *len) -{ - return crypto_pk_write_key_to_string_impl(env, dest, len, 1); -} - -/** PEM-encode the private key portion of env and write it to a - * newly allocated string. On success, set *dest to the new - * string, *len to the string's length, and return 0. On - * failure, return -1. - */ -int -crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest, - size_t *len) -{ - return crypto_pk_write_key_to_string_impl(env, dest, len, 0); -} - -/** Read a PEM-encoded public key from the first len characters of - * src, and store the result in env. Return 0 on success, -1 on - * failure. - */ -int -crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, - size_t len) -{ - BIO *b; - - tor_assert(env); - tor_assert(src); - tor_assert(lenkey) - RSA_free(env->key); - env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL); - BIO_free(b); - if (!env->key) { - crypto_openssl_log_errors(LOG_WARN, "reading public key from string"); - return -1; - } - - return 0; -} - -/** Write the private key from env into the file named by fname, - * PEM-encoded. Return 0 on success, -1 on failure. - */ -int -crypto_pk_write_private_key_to_filename(crypto_pk_t *env, - const char *fname) -{ - BIO *bio; - char *cp; - long len; - char *s; - int r; - - tor_assert(crypto_pk_private_ok(env)); - - if (!(bio = BIO_new(BIO_s_mem()))) - return -1; - if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL) - == 0) { - crypto_openssl_log_errors(LOG_WARN, "writing private key"); - BIO_free(bio); - return -1; - } - len = BIO_get_mem_data(bio, &cp); - tor_assert(len >= 0); - s = tor_malloc(len+1); - memcpy(s, cp, len); - s[len]='\0'; - r = write_str_to_file(fname, s, 0); - BIO_free(bio); - memwipe(s, 0, strlen(s)); - tor_free(s); - return r; -} - -/** Return true iff env has a valid key. - */ -int -crypto_pk_check_key(crypto_pk_t *env) -{ - int r; - tor_assert(env); - - r = RSA_check_key(env->key); - if (r <= 0) - crypto_openssl_log_errors(LOG_WARN,"checking RSA key"); - return r; -} - -/** Return true iff key contains the private-key portion of the RSA - * key. */ -int -crypto_pk_key_is_private(const crypto_pk_t *key) -{ - tor_assert(key); - return crypto_pk_private_ok(key); -} - -/** Return true iff env contains a public key whose public exponent - * equals 65537. - */ -int -crypto_pk_public_exponent_ok(crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - - const BIGNUM *e; - -#ifdef OPENSSL_1_1_API - const BIGNUM *n, *d; - RSA_get0_key(env->key, &n, &e, &d); -#else - e = env->key->e; -#endif /* defined(OPENSSL_1_1_API) */ - return BN_is_word(e, 65537); -} - -/** Compare the public-key components of a and b. Return less than 0 - * if a\b. A NULL key is - * considered to be less than all non-NULL keys, and equal to itself. - * - * Note that this may leak information about the keys through timing. - */ -int -crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b) -{ - int result; - char a_is_non_null = (a != NULL) && (a->key != NULL); - char b_is_non_null = (b != NULL) && (b->key != NULL); - char an_argument_is_null = !a_is_non_null | !b_is_non_null; - - result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null)); - if (an_argument_is_null) - return result; - - const BIGNUM *a_n, *a_e; - const BIGNUM *b_n, *b_e; - -#ifdef OPENSSL_1_1_API - const BIGNUM *a_d, *b_d; - RSA_get0_key(a->key, &a_n, &a_e, &a_d); - RSA_get0_key(b->key, &b_n, &b_e, &b_d); -#else - a_n = a->key->n; - a_e = a->key->e; - b_n = b->key->n; - b_e = b->key->e; -#endif /* defined(OPENSSL_1_1_API) */ - - tor_assert(a_n != NULL && a_e != NULL); - tor_assert(b_n != NULL && b_e != NULL); - - result = BN_cmp(a_n, b_n); - if (result) - return result; - return BN_cmp(a_e, b_e); -} - /** Compare the public-key components of a and b. Return non-zero iff * a==b. A NULL key is considered to be distinct from all non-NULL * keys, and equal to itself. @@ -520,98 +62,6 @@ crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b) return (crypto_pk_cmp_keys(a, b) == 0); } -/** Return the size of the public key modulus in env, in bytes. */ -size_t -crypto_pk_keysize(const crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - - return (size_t) RSA_size((RSA*)env->key); -} - -/** Return the size of the public key modulus of env, in bits. */ -int -crypto_pk_num_bits(crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - -#ifdef OPENSSL_1_1_API - /* It's so stupid that there's no other way to check that n is valid - * before calling RSA_bits(). - */ - const BIGNUM *n, *e, *d; - RSA_get0_key(env->key, &n, &e, &d); - tor_assert(n != NULL); - - return RSA_bits(env->key); -#else /* !(defined(OPENSSL_1_1_API)) */ - tor_assert(env->key->n); - return BN_num_bits(env->key->n); -#endif /* defined(OPENSSL_1_1_API) */ -} - -/** Increase the reference count of env, and return it. - */ -crypto_pk_t * -crypto_pk_dup_key(crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - - env->refs++; - return env; -} - -#ifdef TOR_UNIT_TESTS -/** For testing: replace dest with src. (Dest must have a refcount - * of 1) */ -void -crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src) -{ - tor_assert(dest); - tor_assert(dest->refs == 1); - tor_assert(src); - RSA_free(dest->key); - dest->key = RSAPrivateKey_dup(src->key); -} -#endif /* defined(TOR_UNIT_TESTS) */ - -/** Make a real honest-to-goodness copy of env, and return it. - * Returns NULL on failure. */ -crypto_pk_t * -crypto_pk_copy_full(crypto_pk_t *env) -{ - RSA *new_key; - int privatekey = 0; - tor_assert(env); - tor_assert(env->key); - - if (crypto_pk_private_ok(env)) { - new_key = RSAPrivateKey_dup(env->key); - privatekey = 1; - } else { - new_key = RSAPublicKey_dup(env->key); - } - if (!new_key) { - /* LCOV_EXCL_START - * - * We can't cause RSA*Key_dup() to fail, so we can't really test this. - */ - log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.", - privatekey?"private":"public"); - crypto_openssl_log_errors(LOG_ERR, - privatekey ? "Duplicating a private key" : - "Duplicating a public key"); - tor_fragile_assert(); - return NULL; - /* LCOV_EXCL_STOP */ - } - - return crypto_new_pk_from_rsa_(new_key); -} - /** Perform a hybrid (public/secret) encryption on fromlen * bytes of data from from, with padding type 'padding', * storing the results on to. @@ -754,179 +204,6 @@ crypto_pk_obsolete_private_hybrid_decrypt(crypto_pk_t *env, return -1; } -/** Encrypt fromlen bytes from from with the public key - * in env, using the padding method padding. On success, - * write the result to to, and return the number of bytes - * written. On failure, return -1. - * - * tolen is the number of writable bytes in to, and must be - * at least the length of the modulus of env. - */ -int -crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen, int padding) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen= crypto_pk_keysize(env)); - - r = RSA_public_encrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - env->key, crypto_get_rsa_padding(padding)); - if (r<0) { - crypto_openssl_log_errors(LOG_WARN, "performing RSA encryption"); - return -1; - } - return r; -} - -/** Decrypt fromlen bytes from from with the private key - * in env, using the padding method padding. On success, - * write the result to to, and return the number of bytes - * written. On failure, return -1. - * - * tolen is the number of writable bytes in to, and must be - * at least the length of the modulus of env. - */ -int -crypto_pk_private_decrypt(crypto_pk_t *env, char *to, - size_t tolen, - const char *from, size_t fromlen, - int padding, int warnOnFailure) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(env->key); - tor_assert(fromlen= crypto_pk_keysize(env)); - if (!crypto_pk_key_is_private(env)) - /* Not a private key */ - return -1; - - r = RSA_private_decrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - env->key, crypto_get_rsa_padding(padding)); - - if (r<0) { - crypto_openssl_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG, - "performing RSA decryption"); - return -1; - } - return r; -} - -/** Check the signature in from (fromlen bytes long) with the - * public key in env, using PKCS1 padding. On success, write the - * signed data to to, and return the number of bytes written. - * On failure, return -1. - * - * tolen is the number of writable bytes in to, and must be - * at least the length of the modulus of env. - */ -MOCK_IMPL(int, -crypto_pk_public_checksig,(const crypto_pk_t *env, char *to, - size_t tolen, - const char *from, size_t fromlen)) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen < INT_MAX); - tor_assert(tolen >= crypto_pk_keysize(env)); - r = RSA_public_decrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - env->key, RSA_PKCS1_PADDING); - - if (r<0) { - crypto_openssl_log_errors(LOG_INFO, "checking RSA signature"); - return -1; - } - return r; -} - -/** Sign fromlen bytes of data from from with the private key in - * env, using PKCS1 padding. On success, write the signature to - * to, and return the number of bytes written. On failure, return - * -1. - * - * tolen is the number of writable bytes in to, and must be - * at least the length of the modulus of env. - */ -int -crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen < INT_MAX); - tor_assert(tolen >= crypto_pk_keysize(env)); - if (!crypto_pk_key_is_private(env)) - /* Not a private key */ - return -1; - - r = RSA_private_encrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - (RSA*)env->key, RSA_PKCS1_PADDING); - if (r<0) { - crypto_openssl_log_errors(LOG_WARN, "generating RSA signature"); - return -1; - } - return r; -} - -/** ASN.1-encode the public portion of pk into dest. - * Return -1 on error, or the number of characters used on success. - */ -int -crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len) -{ - int len; - unsigned char *buf = NULL; - - len = i2d_RSAPublicKey(pk->key, &buf); - if (len < 0 || buf == NULL) - return -1; - - if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) { - OPENSSL_free(buf); - return -1; - } - /* We don't encode directly into 'dest', because that would be illegal - * type-punning. (C99 is smarter than me, C99 is smarter than me...) - */ - memcpy(dest,buf,len); - OPENSSL_free(buf); - return len; -} - -/** Decode an ASN.1-encoded public key from str; return the result on - * success and NULL on failure. - */ -crypto_pk_t * -crypto_pk_asn1_decode(const char *str, size_t len) -{ - RSA *rsa; - unsigned char *buf; - const unsigned char *cp; - cp = buf = tor_malloc(len); - memcpy(buf,str,len); - rsa = d2i_RSAPublicKey(NULL, &cp, len); - tor_free(buf); - if (!rsa) { - crypto_openssl_log_errors(LOG_WARN,"decoding public key"); - return NULL; - } - return crypto_new_pk_from_rsa_(rsa); -} - /** Given a private or public key pk, put a fingerprint of the * public key into fp_out (must have at least FINGERPRINT_LEN+1 bytes of * space). Return 0 on success, -1 on failure. @@ -1111,75 +388,3 @@ crypto_pk_get_common_digests(crypto_pk_t *pk, common_digests_t *digests_out) tor_free(buf); return rv; } - -/** Given a crypto_pk_t pk, allocate a new buffer containing the - * Base64 encoding of the DER representation of the private key as a NUL - * terminated string, and return it via priv_out. Return 0 on - * success, -1 on failure. - * - * It is the caller's responsibility to sanitize and free the resulting buffer. - */ -int -crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out) -{ - unsigned char *der = NULL; - int der_len; - int ret = -1; - - *priv_out = NULL; - - der_len = i2d_RSAPrivateKey(pk->key, &der); - if (der_len < 0 || der == NULL) - return ret; - - size_t priv_len = base64_encode_size(der_len, 0) + 1; - char *priv = tor_malloc_zero(priv_len); - if (base64_encode(priv, priv_len, (char *)der, der_len, 0) >= 0) { - *priv_out = priv; - ret = 0; - } else { - tor_free(priv); - } - - memwipe(der, 0, der_len); - OPENSSL_free(der); - return ret; -} - -/** Given a string containing the Base64 encoded DER representation of the - * private key str, decode and return the result on success, or NULL - * on failure. - */ -crypto_pk_t * -crypto_pk_base64_decode(const char *str, size_t len) -{ - crypto_pk_t *pk = NULL; - - char *der = tor_malloc_zero(len + 1); - int der_len = base64_decode(der, len, str, len); - if (der_len <= 0) { - log_warn(LD_CRYPTO, "Stored RSA private key seems corrupted (base64)."); - goto out; - } - - const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */ - RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len); - if (!rsa) { - crypto_openssl_log_errors(LOG_WARN, "decoding private key"); - goto out; - } - - pk = crypto_new_pk_from_rsa_(rsa); - - /* Make sure it's valid. */ - if (crypto_pk_check_key(pk) <= 0) { - crypto_pk_free(pk); - pk = NULL; - goto out; - } - - out: - memwipe(der, 0, len + 1); - tor_free(der); - return pk; -} diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c new file mode 100644 index 0000000000..c57a2e39e0 --- /dev/null +++ b/src/lib/crypt_ops/crypto_rsa_openssl.c @@ -0,0 +1,815 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_rsa.c + * \brief OpenSSL implementations of our RSA code. + **/ + +#include "lib/crypt_ops/compat_openssl.h" +#include "lib/crypt_ops/crypto_rsa.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/ctime/di_ops.h" +#include "lib/log/util_bug.h" +#include "lib/fs/files.h" + +DISABLE_GCC_WARNING(redundant-decls) + +#include +#include +#include +#include +#include +#include +#include +#include + +ENABLE_GCC_WARNING(redundant-decls) + +#include "lib/log/log.h" +#include "lib/encoding/binascii.h" + +#include + +/** Declaration for crypto_pk_t structure. */ +struct crypto_pk_t +{ + int refs; /**< reference count, so we don't have to copy keys */ + RSA *key; /**< The key itself */ +}; + +/** used internally: quicly validate a crypto_pk_t object as a private key. + * Return 1 iff the public key is valid, 0 if obviously invalid. + */ +static int +crypto_pk_private_ok(const crypto_pk_t *k) +{ +#ifdef OPENSSL_1_1_API + if (!k || !k->key) + return 0; + + const BIGNUM *p, *q; + RSA_get0_factors(k->key, &p, &q); + return p != NULL; /* XXX/yawning: Should we check q? */ +#else /* !(defined(OPENSSL_1_1_API)) */ + return k && k->key && k->key->p; +#endif /* defined(OPENSSL_1_1_API) */ +} + +/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */ +crypto_pk_t * +crypto_new_pk_from_rsa_(RSA *rsa) +{ + crypto_pk_t *env; + tor_assert(rsa); + env = tor_malloc(sizeof(crypto_pk_t)); + env->refs = 1; + env->key = rsa; + return env; +} + +/** Helper, used by tor-gencert.c. Return the RSA from a + * crypto_pk_t. */ +RSA * +crypto_pk_get_rsa_(crypto_pk_t *env) +{ + return env->key; +} + +/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff + * private is set, include the private-key portion of the key. Return a valid + * pointer on success, and NULL on failure. */ +MOCK_IMPL(EVP_PKEY *, +crypto_pk_get_evp_pkey_,(crypto_pk_t *env, int private)) +{ + RSA *key = NULL; + EVP_PKEY *pkey = NULL; + tor_assert(env->key); + if (private) { + if (!(key = RSAPrivateKey_dup(env->key))) + goto error; + } else { + if (!(key = RSAPublicKey_dup(env->key))) + goto error; + } + if (!(pkey = EVP_PKEY_new())) + goto error; + if (!(EVP_PKEY_assign_RSA(pkey, key))) + goto error; + return pkey; + error: + if (pkey) + EVP_PKEY_free(pkey); + if (key) + RSA_free(key); + return NULL; +} + +/** Allocate and return storage for a public key. The key itself will not yet + * be set. + */ +MOCK_IMPL(crypto_pk_t *, +crypto_pk_new,(void)) +{ + RSA *rsa; + + rsa = RSA_new(); + tor_assert(rsa); + return crypto_new_pk_from_rsa_(rsa); +} + +/** Release a reference to an asymmetric key; when all the references + * are released, free the key. + */ +void +crypto_pk_free_(crypto_pk_t *env) +{ + if (!env) + return; + + if (--env->refs > 0) + return; + tor_assert(env->refs == 0); + + if (env->key) + RSA_free(env->key); + + tor_free(env); +} + +/** Generate a bits-bit new public/private keypair in env. + * Return 0 on success, -1 on failure. + */ +MOCK_IMPL(int, +crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)) +{ + tor_assert(env); + + if (env->key) { + RSA_free(env->key); + env->key = NULL; + } + + { + BIGNUM *e = BN_new(); + RSA *r = NULL; + if (!e) + goto done; + if (! BN_set_word(e, 65537)) + goto done; + r = RSA_new(); + if (!r) + goto done; + if (RSA_generate_key_ex(r, bits, e, NULL) == -1) + goto done; + + env->key = r; + r = NULL; + done: + if (e) + BN_clear_free(e); + if (r) + RSA_free(r); + } + + if (!env->key) { + crypto_openssl_log_errors(LOG_WARN, "generating RSA key"); + return -1; + } + + return 0; +} + +/** A PEM callback that always reports a failure to get a password */ +static int +pem_no_password_cb(char *buf, int size, int rwflag, void *u) +{ + (void)buf; + (void)size; + (void)rwflag; + (void)u; + return -1; +} + +/** Read a PEM-encoded private key from the len-byte string s + * into env. Return 0 on success, -1 on failure. If len is -1, + * the string is nul-terminated. + */ +int +crypto_pk_read_private_key_from_string(crypto_pk_t *env, + const char *s, ssize_t len) +{ + BIO *b; + + tor_assert(env); + tor_assert(s); + tor_assert(len < INT_MAX && len < SSIZE_T_CEILING); + + /* Create a read-only memory BIO, backed by the string 's' */ + b = BIO_new_mem_buf((char*)s, (int)len); + if (!b) + return -1; + + if (env->key) + RSA_free(env->key); + + env->key = PEM_read_bio_RSAPrivateKey(b,NULL,pem_no_password_cb,NULL); + + BIO_free(b); + + if (!env->key) { + crypto_openssl_log_errors(LOG_WARN, "Error parsing private key"); + return -1; + } + return 0; +} + +/** Read a PEM-encoded private key from the file named by + * keyfile into env. Return 0 on success, -1 on failure. + */ +int +crypto_pk_read_private_key_from_filename(crypto_pk_t *env, + const char *keyfile) +{ + char *contents; + int r; + + /* Read the file into a string. */ + contents = read_file_to_str(keyfile, 0, NULL); + if (!contents) { + log_warn(LD_CRYPTO, "Error reading private key from \"%s\"", keyfile); + return -1; + } + + /* Try to parse it. */ + r = crypto_pk_read_private_key_from_string(env, contents, -1); + memwipe(contents, 0, strlen(contents)); + tor_free(contents); + if (r) + return -1; /* read_private_key_from_string already warned, so we don't.*/ + + /* Make sure it's valid. */ + if (crypto_pk_check_key(env) <= 0) + return -1; + + return 0; +} + +/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on + * success, -1 on failure. */ +static int +crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest, + size_t *len, int is_public) +{ + BUF_MEM *buf; + BIO *b; + int r; + + tor_assert(env); + tor_assert(env->key); + tor_assert(dest); + + b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ + if (!b) + return -1; + + /* Now you can treat b as if it were a file. Just use the + * PEM_*_bio_* functions instead of the non-bio variants. + */ + if (is_public) + r = PEM_write_bio_RSAPublicKey(b, env->key); + else + r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL); + + if (!r) { + crypto_openssl_log_errors(LOG_WARN, "writing RSA key to string"); + BIO_free(b); + return -1; + } + + BIO_get_mem_ptr(b, &buf); + + *dest = tor_malloc(buf->length+1); + memcpy(*dest, buf->data, buf->length); + (*dest)[buf->length] = 0; /* nul terminate it */ + *len = buf->length; + + BIO_free(b); + + return 0; +} + +/** PEM-encode the public key portion of env and write it to a + * newly allocated string. On success, set *dest to the new + * string, *len to the string's length, and return 0. On + * failure, return -1. + */ +int +crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest, + size_t *len) +{ + return crypto_pk_write_key_to_string_impl(env, dest, len, 1); +} + +/** PEM-encode the private key portion of env and write it to a + * newly allocated string. On success, set *dest to the new + * string, *len to the string's length, and return 0. On + * failure, return -1. + */ +int +crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest, + size_t *len) +{ + return crypto_pk_write_key_to_string_impl(env, dest, len, 0); +} + +/** Read a PEM-encoded public key from the first len characters of + * src, and store the result in env. Return 0 on success, -1 on + * failure. + */ +int +crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, + size_t len) +{ + BIO *b; + + tor_assert(env); + tor_assert(src); + tor_assert(lenkey) + RSA_free(env->key); + env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL); + BIO_free(b); + if (!env->key) { + crypto_openssl_log_errors(LOG_WARN, "reading public key from string"); + return -1; + } + + return 0; +} + +/** Write the private key from env into the file named by fname, + * PEM-encoded. Return 0 on success, -1 on failure. + */ +int +crypto_pk_write_private_key_to_filename(crypto_pk_t *env, + const char *fname) +{ + BIO *bio; + char *cp; + long len; + char *s; + int r; + + tor_assert(crypto_pk_private_ok(env)); + + if (!(bio = BIO_new(BIO_s_mem()))) + return -1; + if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL) + == 0) { + crypto_openssl_log_errors(LOG_WARN, "writing private key"); + BIO_free(bio); + return -1; + } + len = BIO_get_mem_data(bio, &cp); + tor_assert(len >= 0); + s = tor_malloc(len+1); + memcpy(s, cp, len); + s[len]='\0'; + r = write_str_to_file(fname, s, 0); + BIO_free(bio); + memwipe(s, 0, strlen(s)); + tor_free(s); + return r; +} + +/** Return true iff env has a valid key. + */ +int +crypto_pk_check_key(crypto_pk_t *env) +{ + int r; + tor_assert(env); + + r = RSA_check_key(env->key); + if (r <= 0) + crypto_openssl_log_errors(LOG_WARN,"checking RSA key"); + return r; +} + +/** Return true iff key contains the private-key portion of the RSA + * key. */ +int +crypto_pk_key_is_private(const crypto_pk_t *key) +{ + tor_assert(key); + return crypto_pk_private_ok(key); +} + +/** Return true iff env contains a public key whose public exponent + * equals 65537. + */ +int +crypto_pk_public_exponent_ok(crypto_pk_t *env) +{ + tor_assert(env); + tor_assert(env->key); + + const BIGNUM *e; + +#ifdef OPENSSL_1_1_API + const BIGNUM *n, *d; + RSA_get0_key(env->key, &n, &e, &d); +#else + e = env->key->e; +#endif /* defined(OPENSSL_1_1_API) */ + return BN_is_word(e, 65537); +} + +/** Compare the public-key components of a and b. Return less than 0 + * if a\b. A NULL key is + * considered to be less than all non-NULL keys, and equal to itself. + * + * Note that this may leak information about the keys through timing. + */ +int +crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b) +{ + int result; + char a_is_non_null = (a != NULL) && (a->key != NULL); + char b_is_non_null = (b != NULL) && (b->key != NULL); + char an_argument_is_null = !a_is_non_null | !b_is_non_null; + + result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null)); + if (an_argument_is_null) + return result; + + const BIGNUM *a_n, *a_e; + const BIGNUM *b_n, *b_e; + +#ifdef OPENSSL_1_1_API + const BIGNUM *a_d, *b_d; + RSA_get0_key(a->key, &a_n, &a_e, &a_d); + RSA_get0_key(b->key, &b_n, &b_e, &b_d); +#else + a_n = a->key->n; + a_e = a->key->e; + b_n = b->key->n; + b_e = b->key->e; +#endif /* defined(OPENSSL_1_1_API) */ + + tor_assert(a_n != NULL && a_e != NULL); + tor_assert(b_n != NULL && b_e != NULL); + + result = BN_cmp(a_n, b_n); + if (result) + return result; + return BN_cmp(a_e, b_e); +} + +/** Return the size of the public key modulus in env, in bytes. */ +size_t +crypto_pk_keysize(const crypto_pk_t *env) +{ + tor_assert(env); + tor_assert(env->key); + + return (size_t) RSA_size((RSA*)env->key); +} + +/** Return the size of the public key modulus of env, in bits. */ +int +crypto_pk_num_bits(crypto_pk_t *env) +{ + tor_assert(env); + tor_assert(env->key); + +#ifdef OPENSSL_1_1_API + /* It's so stupid that there's no other way to check that n is valid + * before calling RSA_bits(). + */ + const BIGNUM *n, *e, *d; + RSA_get0_key(env->key, &n, &e, &d); + tor_assert(n != NULL); + + return RSA_bits(env->key); +#else /* !(defined(OPENSSL_1_1_API)) */ + tor_assert(env->key->n); + return BN_num_bits(env->key->n); +#endif /* defined(OPENSSL_1_1_API) */ +} + +/** Increase the reference count of env, and return it. + */ +crypto_pk_t * +crypto_pk_dup_key(crypto_pk_t *env) +{ + tor_assert(env); + tor_assert(env->key); + + env->refs++; + return env; +} + +#ifdef TOR_UNIT_TESTS +/** For testing: replace dest with src. (Dest must have a refcount + * of 1) */ +void +crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src) +{ + tor_assert(dest); + tor_assert(dest->refs == 1); + tor_assert(src); + RSA_free(dest->key); + dest->key = RSAPrivateKey_dup(src->key); +} +#endif /* defined(TOR_UNIT_TESTS) */ + +/** Make a real honest-to-goodness copy of env, and return it. + * Returns NULL on failure. */ +crypto_pk_t * +crypto_pk_copy_full(crypto_pk_t *env) +{ + RSA *new_key; + int privatekey = 0; + tor_assert(env); + tor_assert(env->key); + + if (crypto_pk_private_ok(env)) { + new_key = RSAPrivateKey_dup(env->key); + privatekey = 1; + } else { + new_key = RSAPublicKey_dup(env->key); + } + if (!new_key) { + /* LCOV_EXCL_START + * + * We can't cause RSA*Key_dup() to fail, so we can't really test this. + */ + log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.", + privatekey?"private":"public"); + crypto_openssl_log_errors(LOG_ERR, + privatekey ? "Duplicating a private key" : + "Duplicating a public key"); + tor_fragile_assert(); + return NULL; + /* LCOV_EXCL_STOP */ + } + + return crypto_new_pk_from_rsa_(new_key); +} + +/** Encrypt fromlen bytes from from with the public key + * in env, using the padding method padding. On success, + * write the result to to, and return the number of bytes + * written. On failure, return -1. + * + * tolen is the number of writable bytes in to, and must be + * at least the length of the modulus of env. + */ +int +crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, + const char *from, size_t fromlen, int padding) +{ + int r; + tor_assert(env); + tor_assert(from); + tor_assert(to); + tor_assert(fromlen= crypto_pk_keysize(env)); + + r = RSA_public_encrypt((int)fromlen, + (unsigned char*)from, (unsigned char*)to, + env->key, crypto_get_rsa_padding(padding)); + if (r<0) { + crypto_openssl_log_errors(LOG_WARN, "performing RSA encryption"); + return -1; + } + return r; +} + +/** Decrypt fromlen bytes from from with the private key + * in env, using the padding method padding. On success, + * write the result to to, and return the number of bytes + * written. On failure, return -1. + * + * tolen is the number of writable bytes in to, and must be + * at least the length of the modulus of env. + */ +int +crypto_pk_private_decrypt(crypto_pk_t *env, char *to, + size_t tolen, + const char *from, size_t fromlen, + int padding, int warnOnFailure) +{ + int r; + tor_assert(env); + tor_assert(from); + tor_assert(to); + tor_assert(env->key); + tor_assert(fromlen= crypto_pk_keysize(env)); + if (!crypto_pk_key_is_private(env)) + /* Not a private key */ + return -1; + + r = RSA_private_decrypt((int)fromlen, + (unsigned char*)from, (unsigned char*)to, + env->key, crypto_get_rsa_padding(padding)); + + if (r<0) { + crypto_openssl_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG, + "performing RSA decryption"); + return -1; + } + return r; +} + +/** Check the signature in from (fromlen bytes long) with the + * public key in env, using PKCS1 padding. On success, write the + * signed data to to, and return the number of bytes written. + * On failure, return -1. + * + * tolen is the number of writable bytes in to, and must be + * at least the length of the modulus of env. + */ +MOCK_IMPL(int, +crypto_pk_public_checksig,(const crypto_pk_t *env, char *to, + size_t tolen, + const char *from, size_t fromlen)) +{ + int r; + tor_assert(env); + tor_assert(from); + tor_assert(to); + tor_assert(fromlen < INT_MAX); + tor_assert(tolen >= crypto_pk_keysize(env)); + r = RSA_public_decrypt((int)fromlen, + (unsigned char*)from, (unsigned char*)to, + env->key, RSA_PKCS1_PADDING); + + if (r<0) { + crypto_openssl_log_errors(LOG_INFO, "checking RSA signature"); + return -1; + } + return r; +} + +/** Sign fromlen bytes of data from from with the private key in + * env, using PKCS1 padding. On success, write the signature to + * to, and return the number of bytes written. On failure, return + * -1. + * + * tolen is the number of writable bytes in to, and must be + * at least the length of the modulus of env. + */ +int +crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, + const char *from, size_t fromlen) +{ + int r; + tor_assert(env); + tor_assert(from); + tor_assert(to); + tor_assert(fromlen < INT_MAX); + tor_assert(tolen >= crypto_pk_keysize(env)); + if (!crypto_pk_key_is_private(env)) + /* Not a private key */ + return -1; + + r = RSA_private_encrypt((int)fromlen, + (unsigned char*)from, (unsigned char*)to, + (RSA*)env->key, RSA_PKCS1_PADDING); + if (r<0) { + crypto_openssl_log_errors(LOG_WARN, "generating RSA signature"); + return -1; + } + return r; +} + +/** ASN.1-encode the public portion of pk into dest. + * Return -1 on error, or the number of characters used on success. + */ +int +crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len) +{ + int len; + unsigned char *buf = NULL; + + len = i2d_RSAPublicKey(pk->key, &buf); + if (len < 0 || buf == NULL) + return -1; + + if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) { + OPENSSL_free(buf); + return -1; + } + /* We don't encode directly into 'dest', because that would be illegal + * type-punning. (C99 is smarter than me, C99 is smarter than me...) + */ + memcpy(dest,buf,len); + OPENSSL_free(buf); + return len; +} + +/** Decode an ASN.1-encoded public key from str; return the result on + * success and NULL on failure. + */ +crypto_pk_t * +crypto_pk_asn1_decode(const char *str, size_t len) +{ + RSA *rsa; + unsigned char *buf; + const unsigned char *cp; + cp = buf = tor_malloc(len); + memcpy(buf,str,len); + rsa = d2i_RSAPublicKey(NULL, &cp, len); + tor_free(buf); + if (!rsa) { + crypto_openssl_log_errors(LOG_WARN,"decoding public key"); + return NULL; + } + return crypto_new_pk_from_rsa_(rsa); +} + +/** Given a crypto_pk_t pk, allocate a new buffer containing the + * Base64 encoding of the DER representation of the private key as a NUL + * terminated string, and return it via priv_out. Return 0 on + * success, -1 on failure. + * + * It is the caller's responsibility to sanitize and free the resulting buffer. + */ +int +crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out) +{ + unsigned char *der = NULL; + int der_len; + int ret = -1; + + *priv_out = NULL; + + der_len = i2d_RSAPrivateKey(pk->key, &der); + if (der_len < 0 || der == NULL) + return ret; + + size_t priv_len = base64_encode_size(der_len, 0) + 1; + char *priv = tor_malloc_zero(priv_len); + if (base64_encode(priv, priv_len, (char *)der, der_len, 0) >= 0) { + *priv_out = priv; + ret = 0; + } else { + tor_free(priv); + } + + memwipe(der, 0, der_len); + OPENSSL_free(der); + return ret; +} + +/** Given a string containing the Base64 encoded DER representation of the + * private key str, decode and return the result on success, or NULL + * on failure. + */ +crypto_pk_t * +crypto_pk_base64_decode(const char *str, size_t len) +{ + crypto_pk_t *pk = NULL; + + char *der = tor_malloc_zero(len + 1); + int der_len = base64_decode(der, len, str, len); + if (der_len <= 0) { + log_warn(LD_CRYPTO, "Stored RSA private key seems corrupted (base64)."); + goto out; + } + + const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */ + RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len); + if (!rsa) { + crypto_openssl_log_errors(LOG_WARN, "decoding private key"); + goto out; + } + + pk = crypto_new_pk_from_rsa_(rsa); + + /* Make sure it's valid. */ + if (crypto_pk_check_key(pk) <= 0) { + crypto_pk_free(pk); + pk = NULL; + goto out; + } + + out: + memwipe(der, 0, len + 1); + tor_free(der); + return pk; +} diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index f6164dd213..69bd7c2dbf 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -19,6 +19,7 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/crypto_pwbox.c \ src/lib/crypt_ops/crypto_rand.c \ src/lib/crypt_ops/crypto_rsa.c \ + src/lib/crypt_ops/crypto_rsa_openssl.c \ src/lib/crypt_ops/crypto_s2k.c \ src/lib/crypt_ops/crypto_util.c \ src/lib/crypt_ops/digestset.c From 0f971d7c915e45556c582b8e02017927bf196f7e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 19 Jul 2018 08:56:51 -0400 Subject: [PATCH 33/76] Rename functions that encode/decode private keys It is not nice to expose a private key's contents without having the function name advertise the fact. Fortunately, we weren't misusing these yet. --- src/feature/control/control.c | 4 ++-- src/lib/crypt_ops/crypto_rsa.h | 4 ++-- src/lib/crypt_ops/crypto_rsa_openssl.c | 4 ++-- src/test/test_controller.c | 2 +- src/test/test_crypto.c | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/feature/control/control.c b/src/feature/control/control.c index f22df30e11..5b62be8a69 100644 --- a/src/feature/control/control.c +++ b/src/feature/control/control.c @@ -4994,7 +4994,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, if (!strcasecmp(key_type_rsa1024, key_type)) { /* "RSA:" - Loading a pre-existing RSA1024 key. */ - pk = crypto_pk_base64_decode(key_blob, strlen(key_blob)); + pk = crypto_pk_base64_decode_private(key_blob, strlen(key_blob)); if (!pk) { err_msg = tor_strdup("512 Failed to decode RSA key\r\n"); goto err; @@ -5029,7 +5029,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, goto err; } if (!discard_pk) { - if (crypto_pk_base64_encode(pk, &key_new_blob)) { + if (crypto_pk_base64_encode_private(pk, &key_new_blob)) { crypto_pk_free(pk); tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", key_type_rsa1024); diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index 45412d21ec..d1f9d57aa0 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -101,8 +101,8 @@ int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen, int crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out); int crypto_pk_get_common_digests(crypto_pk_t *pk, common_digests_t *digests_out); -int crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out); -crypto_pk_t *crypto_pk_base64_decode(const char *str, size_t len); +int crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out); +crypto_pk_t *crypto_pk_base64_decode_private(const char *str, size_t len); /* Prototypes for private functions only used by tortls.c, crypto.c, and the * unit tests. */ diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c index c57a2e39e0..a342c1e7dd 100644 --- a/src/lib/crypt_ops/crypto_rsa_openssl.c +++ b/src/lib/crypt_ops/crypto_rsa_openssl.c @@ -750,7 +750,7 @@ crypto_pk_asn1_decode(const char *str, size_t len) * It is the caller's responsibility to sanitize and free the resulting buffer. */ int -crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out) +crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out) { unsigned char *der = NULL; int der_len; @@ -781,7 +781,7 @@ crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out) * on failure. */ crypto_pk_t * -crypto_pk_base64_decode(const char *str, size_t len) +crypto_pk_base64_decode_private(const char *str, size_t len) { crypto_pk_t *pk = NULL; diff --git a/src/test/test_controller.c b/src/test/test_controller.c index d0aa868448..d5d51758fb 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -161,7 +161,7 @@ test_add_onion_helper_keyarg_v2(void *arg) /* Test loading a RSA1024 key. */ tor_free(err_msg); pk1 = pk_generate(0); - tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded)); + tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded)); tor_asprintf(&arg_str, "RSA1024:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, &pk, &hs_version, &err_msg); diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 029e98a630..81d43ff13f 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -1420,22 +1420,22 @@ test_crypto_pk_base64(void *arg) /* Test Base64 encoding a key. */ pk1 = pk_generate(0); tt_assert(pk1); - tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded)); + tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded)); tt_assert(encoded); /* Test decoding a valid key. */ - pk2 = crypto_pk_base64_decode(encoded, strlen(encoded)); + pk2 = crypto_pk_base64_decode_private(encoded, strlen(encoded)); tt_assert(pk2); tt_int_op(crypto_pk_cmp_keys(pk1, pk2), OP_EQ, 0); crypto_pk_free(pk2); /* Test decoding a invalid key (not Base64). */ static const char *invalid_b64 = "The key is in another castle!"; - pk2 = crypto_pk_base64_decode(invalid_b64, strlen(invalid_b64)); + pk2 = crypto_pk_base64_decode_private(invalid_b64, strlen(invalid_b64)); tt_ptr_op(pk2, OP_EQ, NULL); /* Test decoding a truncated Base64 blob. */ - pk2 = crypto_pk_base64_decode(encoded, strlen(encoded)/2); + pk2 = crypto_pk_base64_decode_private(encoded, strlen(encoded)/2); tt_ptr_op(pk2, OP_EQ, NULL); done: From 38212d2e40a1eae9f65c1a695e478854177c0783 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 19 Jul 2018 09:00:12 -0400 Subject: [PATCH 34/76] Remove a redundant function. --- src/lib/crypt_ops/crypto_rsa_openssl.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c index a342c1e7dd..cd9fb52667 100644 --- a/src/lib/crypt_ops/crypto_rsa_openssl.c +++ b/src/lib/crypt_ops/crypto_rsa_openssl.c @@ -41,11 +41,10 @@ struct crypto_pk_t RSA *key; /**< The key itself */ }; -/** used internally: quicly validate a crypto_pk_t object as a private key. - * Return 1 iff the public key is valid, 0 if obviously invalid. - */ -static int -crypto_pk_private_ok(const crypto_pk_t *k) +/** Return true iff key contains the private-key portion of the RSA + * key. */ +int +crypto_pk_key_is_private(const crypto_pk_t *k) { #ifdef OPENSSL_1_1_API if (!k || !k->key) @@ -371,7 +370,7 @@ crypto_pk_write_private_key_to_filename(crypto_pk_t *env, char *s; int r; - tor_assert(crypto_pk_private_ok(env)); + tor_assert(crypto_pk_key_is_private(env)); if (!(bio = BIO_new(BIO_s_mem()))) return -1; @@ -407,15 +406,6 @@ crypto_pk_check_key(crypto_pk_t *env) return r; } -/** Return true iff key contains the private-key portion of the RSA - * key. */ -int -crypto_pk_key_is_private(const crypto_pk_t *key) -{ - tor_assert(key); - return crypto_pk_private_ok(key); -} - /** Return true iff env contains a public key whose public exponent * equals 65537. */ @@ -545,7 +535,7 @@ crypto_pk_copy_full(crypto_pk_t *env) tor_assert(env); tor_assert(env->key); - if (crypto_pk_private_ok(env)) { + if (crypto_pk_key_is_private(env)) { new_key = RSAPrivateKey_dup(env->key); privatekey = 1; } else { From 824009cde52d40c937c23670b71e9c5b28d2e1f3 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 19 Jul 2018 09:07:08 -0400 Subject: [PATCH 35/76] Rename openssl-bridging functions in crypto_rsa These functions exist only to expose RSA keys to other places in Tor that use OpenSSL; let's be specific about their purpose. --- src/lib/crypt_ops/crypto_rsa.h | 10 ++++++---- src/lib/crypt_ops/crypto_rsa_openssl.c | 21 +++++++++++---------- src/lib/tls/tortls.c | 14 +++++++------- src/test/test_tortls.c | 6 +++--- src/tools/tor-gencert.c | 7 +++---- 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index d1f9d57aa0..88978bf370 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -104,14 +104,16 @@ int crypto_pk_get_common_digests(crypto_pk_t *pk, int crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out); crypto_pk_t *crypto_pk_base64_decode_private(const char *str, size_t len); +#ifdef ENABLE_OPENSSL /* Prototypes for private functions only used by tortls.c, crypto.c, and the * unit tests. */ struct rsa_st; -struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env); -crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa); -MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_evp_pkey_,(crypto_pk_t *env, - int private)); struct evp_pkey_st; +struct rsa_st *crypto_pk_get_openssl_rsa_(crypto_pk_t *env); +crypto_pk_t *crypto_new_pk_from_openssl_rsa_(struct rsa_st *rsa); +MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,( + crypto_pk_t *env,int private)); +#endif #ifdef TOR_UNIT_TESTS void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src); diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c index cd9fb52667..20be34cbd5 100644 --- a/src/lib/crypt_ops/crypto_rsa_openssl.c +++ b/src/lib/crypt_ops/crypto_rsa_openssl.c @@ -58,9 +58,10 @@ crypto_pk_key_is_private(const crypto_pk_t *k) #endif /* defined(OPENSSL_1_1_API) */ } -/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */ +/** used by tortls.c: wrap an RSA* in a crypto_pk_t. Takes ownership of + * its argument. */ crypto_pk_t * -crypto_new_pk_from_rsa_(RSA *rsa) +crypto_new_pk_from_openssl_rsa_(RSA *rsa) { crypto_pk_t *env; tor_assert(rsa); @@ -70,19 +71,19 @@ crypto_new_pk_from_rsa_(RSA *rsa) return env; } -/** Helper, used by tor-gencert.c. Return the RSA from a +/** Helper, used by tor-gencert.c. Return a copy of the private RSA from a * crypto_pk_t. */ RSA * -crypto_pk_get_rsa_(crypto_pk_t *env) +crypto_pk_get_openssl_rsa_(crypto_pk_t *env) { - return env->key; + return RSA_PrivateKeyDup(env->key); } /** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff * private is set, include the private-key portion of the key. Return a valid * pointer on success, and NULL on failure. */ MOCK_IMPL(EVP_PKEY *, -crypto_pk_get_evp_pkey_,(crypto_pk_t *env, int private)) +crypto_pk_get_openssl_evp_pkey_,(crypto_pk_t *env, int private)) { RSA *key = NULL; EVP_PKEY *pkey = NULL; @@ -117,7 +118,7 @@ crypto_pk_new,(void)) rsa = RSA_new(); tor_assert(rsa); - return crypto_new_pk_from_rsa_(rsa); + return crypto_new_pk_from_openssl_rsa_(rsa); } /** Release a reference to an asymmetric key; when all the references @@ -556,7 +557,7 @@ crypto_pk_copy_full(crypto_pk_t *env) /* LCOV_EXCL_STOP */ } - return crypto_new_pk_from_rsa_(new_key); + return crypto_new_pk_from_openssl_rsa_(new_key); } /** Encrypt fromlen bytes from from with the public key @@ -729,7 +730,7 @@ crypto_pk_asn1_decode(const char *str, size_t len) crypto_openssl_log_errors(LOG_WARN,"decoding public key"); return NULL; } - return crypto_new_pk_from_rsa_(rsa); + return crypto_new_pk_from_openssl_rsa_(rsa); } /** Given a crypto_pk_t pk, allocate a new buffer containing the @@ -789,7 +790,7 @@ crypto_pk_base64_decode_private(const char *str, size_t len) goto out; } - pk = crypto_new_pk_from_rsa_(rsa); + pk = crypto_new_pk_from_openssl_rsa_(rsa); /* Make sure it's valid. */ if (crypto_pk_check_key(pk) <= 0) { diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 875ed95f8c..466df8966c 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -539,9 +539,9 @@ tor_tls_create_certificate,(crypto_pk_t *rsa, tor_assert(cname); tor_assert(rsa_sign); tor_assert(cname_sign); - if (!(sign_pkey = crypto_pk_get_evp_pkey_(rsa_sign,1))) + if (!(sign_pkey = crypto_pk_get_openssl_evp_pkey_(rsa_sign,1))) goto error; - if (!(pkey = crypto_pk_get_evp_pkey_(rsa,0))) + if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,0))) goto error; if (!(x509 = X509_new())) goto error; @@ -746,7 +746,7 @@ tor_x509_cert_new,(X509 *x509_cert)) if ((pkey = X509_get_pubkey(x509_cert)) && (rsa = EVP_PKEY_get1_RSA(pkey))) { - crypto_pk_t *pk = crypto_new_pk_from_rsa_(rsa); + crypto_pk_t *pk = crypto_new_pk_from_openssl_rsa_(rsa); if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) { crypto_pk_free(pk); EVP_PKEY_free(pkey); @@ -915,7 +915,7 @@ tor_tls_cert_get_key(tor_x509_cert_t *cert) EVP_PKEY_free(pkey); return NULL; } - result = crypto_new_pk_from_rsa_(rsa); + result = crypto_new_pk_from_openssl_rsa_(rsa); EVP_PKEY_free(pkey); return result; } @@ -1270,7 +1270,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); if (!is_client) { tor_assert(rsa); - if (!(pkey = crypto_pk_get_evp_pkey_(rsa,1))) + if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,1))) goto error; if (!SSL_CTX_use_PrivateKey(result->ctx, pkey)) goto error; @@ -2277,7 +2277,7 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key) rsa = EVP_PKEY_get1_RSA(id_pkey); if (!rsa) goto done; - *identity_key = crypto_new_pk_from_rsa_(rsa); + *identity_key = crypto_new_pk_from_openssl_rsa_(rsa); r = 0; @@ -2362,7 +2362,7 @@ tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp, { X509 *newc = X509_dup(inp->cert); X509_time_adj(X509_get_notAfter(newc), 0, &new_expiration_time); - EVP_PKEY *pk = crypto_pk_get_evp_pkey_(signing_key, 1); + EVP_PKEY *pk = crypto_pk_get_openssl_evp_pkey_(signing_key, 1); tor_assert(X509_sign(newc, pk, EVP_sha256())); EVP_PKEY_free(pk); return tor_x509_cert_new(newc); diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index 54e5eca4f9..49a39e264b 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -690,7 +690,7 @@ test_tortls_get_my_client_auth_key(void *ignored) RSA *k = RSA_new(); ctx = tor_malloc_zero(sizeof(tor_tls_context_t)); - expected = crypto_new_pk_from_rsa_(k); + expected = crypto_new_pk_from_openssl_rsa_(k); ctx->auth_key = expected; client_tls_context = NULL; @@ -2609,7 +2609,7 @@ test_tortls_create_certificate(void *ignored) pk1 = crypto_pk_new(); pk2 = crypto_pk_new(); - MOCK(crypto_pk_get_evp_pkey_, fixed_crypto_pk_get_evp_pkey_); + MOCK(crypto_pk_get_openssl_evp_pkey_, fixed_crypto_pk_get_evp_pkey_); fixed_crypto_pk_get_evp_pkey_result_index = 0; fixed_crypto_pk_get_evp_pkey_result[0] = NULL; ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1); @@ -2628,7 +2628,7 @@ test_tortls_create_certificate(void *ignored) tt_assert(!ret); done: - UNMOCK(crypto_pk_get_evp_pkey_); + UNMOCK(crypto_pk_get_openssl_evp_pkey_); crypto_pk_free(pk1); crypto_pk_free(pk2); } diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index 63e24d922c..e0ac3dec80 100644 --- a/src/tools/tor-gencert.c +++ b/src/tools/tor-gencert.c @@ -239,8 +239,7 @@ generate_key(int bits) crypto_pk_t *env = crypto_pk_new(); if (crypto_pk_generate_key_with_bits(env,bits)<0) goto done; - rsa = crypto_pk_get_rsa_(env); - rsa = RSAPrivateKey_dup(rsa); + rsa = crypto_pk_get_openssl_rsa_(env); done: crypto_pk_free(env); return rsa; @@ -416,7 +415,7 @@ static int get_fingerprint(EVP_PKEY *pkey, char *out) { int r = -1; - crypto_pk_t *pk = crypto_new_pk_from_rsa_(EVP_PKEY_get1_RSA(pkey)); + crypto_pk_t *pk = crypto_new_pk_from_openssl_rsa_(EVP_PKEY_get1_RSA(pkey)); if (pk) { r = crypto_pk_get_fingerprint(pk, out, 0); crypto_pk_free(pk); @@ -429,7 +428,7 @@ static int get_digest(EVP_PKEY *pkey, char *out) { int r = -1; - crypto_pk_t *pk = crypto_new_pk_from_rsa_(EVP_PKEY_get1_RSA(pkey)); + crypto_pk_t *pk = crypto_new_pk_from_openssl_rsa_(EVP_PKEY_get1_RSA(pkey)); if (pk) { r = crypto_pk_get_digest(pk, out); crypto_pk_free(pk); From 0812f1cbc2f528f0acb785e2fea416b9f2113c7c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 19 Jul 2018 09:23:30 -0400 Subject: [PATCH 36/76] Use a constant for "65537" --- src/lib/crypt_ops/crypto_rsa.h | 3 +++ src/lib/crypt_ops/crypto_rsa_openssl.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index 88978bf370..4a5d92c6bb 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -33,6 +33,9 @@ * including terminating NUL. */ #define FINGERPRINT_LEN 49 +/** Value of 'e' to use in our public keys */ +#define TOR_RSA_EXPONENT 65537 + /** A public key, or a public/private key-pair. */ typedef struct crypto_pk_t crypto_pk_t; diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c index 20be34cbd5..d1b56c3b62 100644 --- a/src/lib/crypt_ops/crypto_rsa_openssl.c +++ b/src/lib/crypt_ops/crypto_rsa_openssl.c @@ -158,7 +158,7 @@ crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)) RSA *r = NULL; if (!e) goto done; - if (! BN_set_word(e, 65537)) + if (! BN_set_word(e, TOR_RSA_EXPONENT)) goto done; r = RSA_new(); if (!r) @@ -408,7 +408,7 @@ crypto_pk_check_key(crypto_pk_t *env) } /** Return true iff env contains a public key whose public exponent - * equals 65537. + * equals TOR_RSA_EXPONENT. */ int crypto_pk_public_exponent_ok(crypto_pk_t *env) @@ -424,7 +424,7 @@ crypto_pk_public_exponent_ok(crypto_pk_t *env) #else e = env->key->e; #endif /* defined(OPENSSL_1_1_API) */ - return BN_is_word(e, 65537); + return BN_is_word(e, TOR_RSA_EXPONENT); } /** Compare the public-key components of a and b. Return less than 0 From 9566ed6fd9e4aab2ce6b84afc2f7112550cf0483 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 19 Jul 2018 15:47:48 -0400 Subject: [PATCH 37/76] Add rudimentary support for PEM-encoding, since NSS doesn't do that. --- src/lib/encoding/.may_include | 1 + src/lib/encoding/include.am | 2 + src/lib/encoding/pem.c | 106 +++++++++++++++++++++++++++++ src/lib/encoding/pem.h | 26 ++++++++ src/test/include.am | 1 + src/test/test.c | 1 + src/test/test.h | 1 + src/test/test_pem.c | 122 ++++++++++++++++++++++++++++++++++ 8 files changed, 260 insertions(+) create mode 100644 src/lib/encoding/pem.c create mode 100644 src/lib/encoding/pem.h create mode 100644 src/test/test_pem.c diff --git a/src/lib/encoding/.may_include b/src/lib/encoding/.may_include index 92231b5133..7c2ef36929 100644 --- a/src/lib/encoding/.may_include +++ b/src/lib/encoding/.may_include @@ -1,5 +1,6 @@ orconfig.h lib/cc/*.h +lib/ctime/*.h lib/encoding/*.h lib/intmath/*.h lib/log/*.h diff --git a/src/lib/encoding/include.am b/src/lib/encoding/include.am index 868e531b6b..2d2aa3988a 100644 --- a/src/lib/encoding/include.am +++ b/src/lib/encoding/include.am @@ -9,6 +9,7 @@ src_lib_libtor_encoding_a_SOURCES = \ src/lib/encoding/confline.c \ src/lib/encoding/cstring.c \ src/lib/encoding/keyval.c \ + src/lib/encoding/pem.c \ src/lib/encoding/time_fmt.c src_lib_libtor_encoding_testing_a_SOURCES = \ @@ -21,4 +22,5 @@ noinst_HEADERS += \ src/lib/encoding/confline.h \ src/lib/encoding/cstring.h \ src/lib/encoding/keyval.h \ + src/lib/encoding/pem.h \ src/lib/encoding/time_fmt.h diff --git a/src/lib/encoding/pem.c b/src/lib/encoding/pem.c new file mode 100644 index 0000000000..0d4a814f6f --- /dev/null +++ b/src/lib/encoding/pem.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file pem.c + * + * \brief Implement a trivial version of PEM encoding, for use with NSS. + * + * We deliberately do not support any encryption here. + **/ + +#include "orconfig.h" + +#include "lib/encoding/pem.h" + +#include "lib/ctime/di_ops.h" +#include "lib/encoding/binascii.h" +#include "lib/log/util_bug.h" +#include "lib/malloc/malloc.h" +#include "lib/string/printf.h" +#include "lib/string/util_string.h" + +#include + +/** + * Return the length of a src_len-byte object when tagged with + * objtype and PEM-encoded. Includes terminating NUL. + */ +size_t +pem_encoded_size(size_t src_len, const char *objtype) +{ + return + strlen("-----BEGIN -----\n") + + strlen("-----END -----\n") + + strlen(objtype) * 2 + + base64_encode_size(src_len, BASE64_ENCODE_MULTILINE) + + 1; +} + +/** + * PEM-encode the srclen-byte object at src into the + * destlen<\b>-byte buffer at dest, tagging it with objtype. + * Return 0 on success and -1 on failure. + */ +int +pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen, + const char *objtype) +{ + if (tor_snprintf(dest, destlen, "-----BEGIN %s-----\n", objtype) < 0) + return -1; + + size_t offset = strlen(dest); + + int n = base64_encode(dest + offset, destlen - offset, + (const char *)src, srclen, BASE64_ENCODE_MULTILINE); + if (n < 0) + return -1; + offset += n; + if (BUG(offset > destlen)) + return -1; + + if (tor_snprintf(dest + offset, destlen - offset, + "-----END %s-----\n", objtype) < 0) + return -1; + + tor_assert(strlen(dest) + 1 <= pem_encoded_size(srclen, objtype)); + return 0; +} + +/** + * Given a PEM-encoded block of size srclen in src, if it has + * object type objtype, decode it into the destlen-byte buffer + * at dest. Return the number of characters decoded on success, or -1 + * on failure. + */ +int +pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen, + const char *objtype) +{ + const char *eos = src + srclen; + + src = eat_whitespace_eos(src, eos); + + char *tag = NULL; + tor_asprintf(&tag, "-----BEGIN %s-----\n", objtype); + if ((size_t)(eos-src) < strlen(tag) || fast_memneq(src, tag, strlen(tag))) { + tor_free(tag); + return -1; + } + src += strlen(tag); + tor_free(tag); + + // NOTE lack of trailing \n. We do not enforce its presence. + tor_asprintf(&tag, "\n-----END %s-----", objtype); + const char *end_of_base64 = tor_memstr(src, eos-src, tag); + tor_free(tag); + if (end_of_base64 == NULL) + return -1; + + /* Should we actually allow extra stuff at the end? */ + + return base64_decode((char*)dest, destlen, src, end_of_base64-src); +} diff --git a/src/lib/encoding/pem.h b/src/lib/encoding/pem.h new file mode 100644 index 0000000000..ba21228848 --- /dev/null +++ b/src/lib/encoding/pem.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file pem.h + * + * \brief Header for pem.c + **/ + +#ifndef TOR_PEM_H +#define TOR_PEM_H + +#include "orconfig.h" +#include +#include "lib/cc/torint.h" + +size_t pem_encoded_size(size_t src_len, const char *objtype); +int pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen, + const char *objtype); +int pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen, + const char *objtype); + +#endif diff --git a/src/test/include.am b/src/test/include.am index 46990597fd..68372adc76 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -151,6 +151,7 @@ src_test_test_SOURCES += \ src/test/test_oom.c \ src/test/test_oos.c \ src/test/test_options.c \ + src/test/test_pem.c \ src/test/test_periodic_event.c \ src/test/test_policy.c \ src/test/test_procmon.c \ diff --git a/src/test/test.c b/src/test/test.c index 745aa987aa..94eae5a4f4 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -867,6 +867,7 @@ struct testgroup_t testgroups[] = { { "crypto/", crypto_tests }, { "crypto/ope/", crypto_ope_tests }, { "crypto/openssl/", crypto_openssl_tests }, + { "crypto/pem/", pem_tests }, { "dir/", dir_tests }, { "dir_handle_get/", dir_handle_get_tests }, { "dir/md/", microdesc_tests }, diff --git a/src/test/test.h b/src/test/test.h index bfe50cbb8c..b3a73271ee 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -234,6 +234,7 @@ extern struct testcase_t nodelist_tests[]; extern struct testcase_t oom_tests[]; extern struct testcase_t oos_tests[]; extern struct testcase_t options_tests[]; +extern struct testcase_t pem_tests[]; extern struct testcase_t periodic_event_tests[]; extern struct testcase_t policy_tests[]; extern struct testcase_t procmon_tests[]; diff --git a/src/test/test_pem.c b/src/test/test_pem.c new file mode 100644 index 0000000000..2bae286e25 --- /dev/null +++ b/src/test/test_pem.c @@ -0,0 +1,122 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#include "lib/encoding/pem.h" +#include "lib/cc/compat_compiler.h" +#include "lib/malloc/malloc.h" + +#include "test/test.h" + +#include +#include +#include + +static const char example_pre[] = + "Lest you get the wrong impression, we wombats " + "are not in the habit of tunneling madly about, without any supplies " + "or even a map."; /* -- Ursula Vernon, _Digger_ */ +static const char expected[] = + "-----BEGIN WOMBAT QUOTE-----\n" + "TGVzdCB5b3UgZ2V0IHRoZSB3cm9uZyBpbXByZXNzaW9uLCB3ZSB3b21iYXRzIGFy\n" + "ZSBub3QgaW4gdGhlIGhhYml0IG9mIHR1bm5lbGluZyBtYWRseSBhYm91dCwgd2l0\n" + "aG91dCBhbnkgc3VwcGxpZXMgb3IgZXZlbiBhIG1hcC4=\n" + "-----END WOMBAT QUOTE-----\n"; + +static void +test_crypto_pem_encode(void *arg) +{ + (void)arg; + + char buf[4096]; + + int n = (int) pem_encoded_size(strlen(example_pre), "WOMBAT QUOTE"); + + int n2 = pem_encode(buf, sizeof(buf), + (const unsigned char *)example_pre, strlen(example_pre), + "WOMBAT QUOTE"); + tt_int_op(strlen(buf)+1, OP_EQ, n); + tt_int_op(n2, OP_EQ, 0); + tt_str_op(buf, OP_EQ, expected); + + /* Now make sure it succeeds if the buffer is exactly the length we want. */ + memset(buf, 0, sizeof(buf)); + n2 = pem_encode(buf, n, (const unsigned char *)example_pre, + strlen(example_pre), "WOMBAT QUOTE"); + tt_int_op(n2, OP_EQ, 0); + tt_str_op(buf, OP_EQ, expected); + + /* Make sure it fails if the buffer is too short. */ + memset(buf, 0, sizeof(buf)); + n2 = pem_encode(buf, n - 1, (const unsigned char *)example_pre, + strlen(example_pre), "WOMBAT QUOTE"); + tt_int_op(n2, OP_EQ, -1); + + done: + ; +} + +static void +test_crypto_pem_decode(void *arg) +{ + (void)arg; + + unsigned char buf[4096]; + + /* Try a straightforward decoding. */ + int n = pem_decode(buf, sizeof(buf), + expected, strlen(expected), + "WOMBAT QUOTE"); + tt_int_op(n, OP_EQ, strlen(example_pre)); + tt_mem_op(buf, OP_EQ, example_pre, n); + + /* Succeed if the buffer is exactly the right size. */ + memset(buf, 0xff, sizeof(buf)); + n = pem_decode(buf, strlen(example_pre), + expected, strlen(expected), + "WOMBAT QUOTE"); + tt_int_op(n, OP_EQ, strlen(example_pre)); + tt_mem_op(buf, OP_EQ, example_pre, n); + tt_int_op(buf[n], OP_EQ, 0xff); + + /* Verify that it fails if the buffer is too small. */ + memset(buf, 0xff, sizeof(buf)); + n = pem_decode(buf, strlen(example_pre) - 1, + expected, strlen(expected), + "WOMBAT QUOTE"); + tt_int_op(n, OP_EQ, -1); + + /* Verify that it fails with an incorrect tag. */ + memset(buf, 0xff, sizeof(buf)); + n = pem_decode(buf, sizeof(buf), + expected, strlen(expected), + "QUOKKA VOTE"); + tt_int_op(n, OP_EQ, -1); + + /* Try truncated buffers of different sizes. */ + size_t i; + for (i = 0; i <= strlen(expected); ++i) { + char *truncated = tor_memdup(expected, i); + n = pem_decode(buf, sizeof(buf), + truncated, i, + "WOMBAT QUOTE"); + tor_free(truncated); + if (i < strlen(expected) - 1) { + tt_int_op(n, OP_EQ, -1); + } else { + tt_int_op(n, OP_EQ, strlen(example_pre)); + } + } + + done: + ; +} + +struct testcase_t pem_tests[] = { + { "encode", test_crypto_pem_encode, 0, NULL, NULL }, + { "decode", test_crypto_pem_decode, 0, NULL, NULL }, + END_OF_TESTCASES +}; From b94e7de7dbf31a9b9bfe2d376013e279f15500f8 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 19 Jul 2018 17:09:23 -0400 Subject: [PATCH 38/76] Refactor crypto_rsa to use pem module. This cleans up a lot of junk from crypto_rsa_openssl, and will save us duplicated code in crypto_rsa_nss (when it exists). (Actually, it already exists, but I am going to use git rebase so that this commit precedes the creation of crypto_rsa_nss.) --- src/lib/crypt_ops/crypto_rsa.c | 253 +++++++++++++++++++ src/lib/crypt_ops/crypto_rsa.h | 16 +- src/lib/crypt_ops/crypto_rsa_openssl.c | 320 ++++--------------------- src/test/test_crypto.c | 3 +- src/test/testing_rsakeys.c | 3 +- 5 files changed, 317 insertions(+), 278 deletions(-) diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c index 328fd732d2..0f80bc967f 100644 --- a/src/lib/crypt_ops/crypto_rsa.c +++ b/src/lib/crypt_ops/crypto_rsa.c @@ -23,8 +23,12 @@ #include "lib/log/log.h" #include "lib/encoding/binascii.h" +#include "lib/encoding/pem.h" #include +#ifdef HAVE_SYS_STAT_H +#include +#endif /** Return the number of bytes added by padding method padding. */ @@ -388,3 +392,252 @@ crypto_pk_get_common_digests(crypto_pk_t *pk, common_digests_t *digests_out) tor_free(buf); return rv; } + +static const char RSA_PUBLIC_TAG[] = "RSA PUBLIC KEY"; +static const char RSA_PRIVATE_TAG[] = "RSA PRIVATE KEY"; + +/** PEM-encode the public key portion of env and write it to a + * newly allocated string. On success, set *dest to the new + * string, *len to the string's length, and return 0. On + * failure, return -1. + */ +int +crypto_pk_write_public_key_to_string(crypto_pk_t *env, + char **dest, size_t *len) +{ + size_t buflen = crypto_pk_keysize(env) * 3; + char *buf = tor_malloc(buflen); + char *result = NULL; + size_t resultlen = 0; + int rv = -1; + + int n = crypto_pk_asn1_encode(env, buf, buflen); + if (n < 0) + goto done; + + resultlen = pem_encoded_size(n, RSA_PUBLIC_TAG); + result = tor_malloc(resultlen); + if (pem_encode(result, resultlen, + (const unsigned char *)buf, n, RSA_PUBLIC_TAG) < 0) { + goto done; + } + + *dest = result; + *len = resultlen; + rv = 0; + + done: + if (rv < 0 && result) { + memwipe(result, 0, resultlen); + tor_free(result); + } + memwipe(buf, 0, buflen); + tor_free(buf); + return rv; +} + +/** PEM-encode the private key portion of env and write it to a + * newly allocated string. On success, set *dest to the new + * string, *len to the string's length, and return 0. On + * failure, return -1. + */ +int +crypto_pk_write_private_key_to_string(crypto_pk_t *env, + char **dest, size_t *len) +{ + size_t buflen = crypto_pk_keysize(env) * 16; + char *buf = tor_malloc(buflen); + char *result = NULL; + size_t resultlen = 0; + int rv = -1; + + int n = crypto_pk_asn1_encode_private(env, buf, buflen); + if (n < 0) + goto done; + + resultlen = pem_encoded_size(n, RSA_PRIVATE_TAG); + result = tor_malloc(resultlen); + if (pem_encode(result, resultlen, + (const unsigned char *)buf, n, RSA_PRIVATE_TAG) < 0) + goto done; + + *dest = result; + *len = resultlen; + rv = 0; + done: + if (rv < 0 && result) { + memwipe(result, 0, resultlen); + tor_free(result); + } + memwipe(buf, 0, buflen); + tor_free(buf); + return rv; +} + +/** Read a PEM-encoded public key from the first len characters of + * src, and store the result in env. Return 0 on success, -1 on + * failure. + */ +int +crypto_pk_read_public_key_from_string(crypto_pk_t *env, + const char *src, size_t len) +{ + if (len == (size_t)-1) + len = strlen(src); + + size_t buflen = len; + uint8_t *buf = tor_malloc(buflen); + int rv = -1; + + int n = pem_decode(buf, buflen, src, len, RSA_PUBLIC_TAG); + if (n < 0) + goto done; + + crypto_pk_t *pk = crypto_pk_asn1_decode((const char*)buf, n); + if (! pk) + goto done; + + crypto_pk_assign_public(env, pk); + crypto_pk_free(pk); + rv = 0; + + done: + memwipe(buf, 0, buflen); + tor_free(buf); + return rv; +} + +/** Read a PEM-encoded private key from the len-byte string s + * into env. Return 0 on success, -1 on failure. If len is -1, + * the string is nul-terminated. + */ +int +crypto_pk_read_private_key_from_string(crypto_pk_t *env, + const char *s, ssize_t len) +{ + if (len == -1) + len = strlen(s); + + size_t buflen = len; + uint8_t *buf = tor_malloc(buflen); + int rv = -1; + + int n = pem_decode(buf, buflen, s, len, RSA_PRIVATE_TAG); + if (n < 0) { + goto done; + } + + crypto_pk_t *pk = crypto_pk_asn1_decode_private((const char *)buf, n); + if (! pk) + goto done; + + crypto_pk_assign_private(env, pk); + crypto_pk_free(pk); + rv = 0; + + done: + memwipe(buf, 0, buflen); + tor_free(buf); + return rv; +} + +/** Read a PEM-encoded private key from the file named by + * keyfile into env. Return 0 on success, -1 on failure. + */ +int +crypto_pk_read_private_key_from_filename(crypto_pk_t *env, + const char *keyfile) +{ + struct stat st; + char *buf = read_file_to_str(keyfile, 0, &st); + if (!buf) + return -1; + + int rv = crypto_pk_read_private_key_from_string(env, buf, st.st_size); + memwipe(buf, 0, st.st_size); + tor_free(buf); + return rv; +} + +/** Write the private key from env into the file named by fname, + * PEM-encoded. Return 0 on success, -1 on failure. + */ +int +crypto_pk_write_private_key_to_filename(crypto_pk_t *env, + const char *fname) +{ + char *s = NULL; + size_t n = 0; + + if (crypto_pk_write_private_key_to_string(env, &s, &n) < 0) + return -1; + + int rv = write_bytes_to_file(fname, s, n, 0); + memwipe(s, 0, n); + tor_free(s); + return rv; +} + +/** Given a crypto_pk_t pk, allocate a new buffer containing the + * Base64 encoding of the DER representation of the private key as a NUL + * terminated string, and return it via priv_out. Return 0 on + * success, -1 on failure. + * + * It is the caller's responsibility to sanitize and free the resulting buffer. + */ +int +crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out) +{ + size_t buflen = crypto_pk_keysize(pk)*16; + char *buf = tor_malloc(buflen); + char *result = NULL; + size_t reslen = 0; + bool ok = false; + + int n = crypto_pk_asn1_encode_private(pk, buf, buflen); + + if (n < 0) + goto done; + + reslen = base64_encode_size(n, 0)+1; + result = tor_malloc(reslen); + if (base64_encode(result, reslen, buf, n, 0) < 0) + goto done; + + ok = true; + + done: + memwipe(buf, 0, buflen); + tor_free(buf); + if (result && ! ok) { + memwipe(result, 0, reslen); + tor_free(result); + } + *priv_out = result; + return ok ? 0 : -1; +} + +/** Given a string containing the Base64 encoded DER representation of the + * private key str, decode and return the result on success, or NULL + * on failure. + */ +crypto_pk_t * +crypto_pk_base64_decode_private(const char *str, size_t len) +{ + crypto_pk_t *pk = NULL; + + char *der = tor_malloc_zero(len + 1); + int der_len = base64_decode(der, len, str, len); + if (der_len <= 0) { + log_warn(LD_CRYPTO, "Stored RSA private key seems corrupted (base64)."); + goto out; + } + + pk = crypto_pk_asn1_decode_private(der, der_len); + + out: + memwipe(der, 0, len+1); + tor_free(der); + + return pk; +} diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index 4a5d92c6bb..bcb2b51dd1 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -93,6 +93,9 @@ int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); int crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len); crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len); +int crypto_pk_asn1_encode_private(const crypto_pk_t *pk, + char *dest, size_t dest_len); +crypto_pk_t *crypto_pk_asn1_decode_private(const char *str, size_t len); int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space); int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out); void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); @@ -107,6 +110,14 @@ int crypto_pk_get_common_digests(crypto_pk_t *pk, int crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out); crypto_pk_t *crypto_pk_base64_decode_private(const char *str, size_t len); +#ifdef TOR_UNIT_TESTS +#ifdef ENABLE_NSS +struct SECItemStr; +STATIC int secitem_uint_cmp(const struct SECItemStr *a, + const struct SECItemStr *b); +#endif +#endif + #ifdef ENABLE_OPENSSL /* Prototypes for private functions only used by tortls.c, crypto.c, and the * unit tests. */ @@ -118,8 +129,7 @@ MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,( crypto_pk_t *env,int private)); #endif -#ifdef TOR_UNIT_TESTS -void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src); -#endif +void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src); +void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src); #endif diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c index d1b56c3b62..9a3729a878 100644 --- a/src/lib/crypt_ops/crypto_rsa_openssl.c +++ b/src/lib/crypt_ops/crypto_rsa_openssl.c @@ -183,216 +183,6 @@ crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)) return 0; } -/** A PEM callback that always reports a failure to get a password */ -static int -pem_no_password_cb(char *buf, int size, int rwflag, void *u) -{ - (void)buf; - (void)size; - (void)rwflag; - (void)u; - return -1; -} - -/** Read a PEM-encoded private key from the len-byte string s - * into env. Return 0 on success, -1 on failure. If len is -1, - * the string is nul-terminated. - */ -int -crypto_pk_read_private_key_from_string(crypto_pk_t *env, - const char *s, ssize_t len) -{ - BIO *b; - - tor_assert(env); - tor_assert(s); - tor_assert(len < INT_MAX && len < SSIZE_T_CEILING); - - /* Create a read-only memory BIO, backed by the string 's' */ - b = BIO_new_mem_buf((char*)s, (int)len); - if (!b) - return -1; - - if (env->key) - RSA_free(env->key); - - env->key = PEM_read_bio_RSAPrivateKey(b,NULL,pem_no_password_cb,NULL); - - BIO_free(b); - - if (!env->key) { - crypto_openssl_log_errors(LOG_WARN, "Error parsing private key"); - return -1; - } - return 0; -} - -/** Read a PEM-encoded private key from the file named by - * keyfile into env. Return 0 on success, -1 on failure. - */ -int -crypto_pk_read_private_key_from_filename(crypto_pk_t *env, - const char *keyfile) -{ - char *contents; - int r; - - /* Read the file into a string. */ - contents = read_file_to_str(keyfile, 0, NULL); - if (!contents) { - log_warn(LD_CRYPTO, "Error reading private key from \"%s\"", keyfile); - return -1; - } - - /* Try to parse it. */ - r = crypto_pk_read_private_key_from_string(env, contents, -1); - memwipe(contents, 0, strlen(contents)); - tor_free(contents); - if (r) - return -1; /* read_private_key_from_string already warned, so we don't.*/ - - /* Make sure it's valid. */ - if (crypto_pk_check_key(env) <= 0) - return -1; - - return 0; -} - -/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on - * success, -1 on failure. */ -static int -crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest, - size_t *len, int is_public) -{ - BUF_MEM *buf; - BIO *b; - int r; - - tor_assert(env); - tor_assert(env->key); - tor_assert(dest); - - b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ - if (!b) - return -1; - - /* Now you can treat b as if it were a file. Just use the - * PEM_*_bio_* functions instead of the non-bio variants. - */ - if (is_public) - r = PEM_write_bio_RSAPublicKey(b, env->key); - else - r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL); - - if (!r) { - crypto_openssl_log_errors(LOG_WARN, "writing RSA key to string"); - BIO_free(b); - return -1; - } - - BIO_get_mem_ptr(b, &buf); - - *dest = tor_malloc(buf->length+1); - memcpy(*dest, buf->data, buf->length); - (*dest)[buf->length] = 0; /* nul terminate it */ - *len = buf->length; - - BIO_free(b); - - return 0; -} - -/** PEM-encode the public key portion of env and write it to a - * newly allocated string. On success, set *dest to the new - * string, *len to the string's length, and return 0. On - * failure, return -1. - */ -int -crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest, - size_t *len) -{ - return crypto_pk_write_key_to_string_impl(env, dest, len, 1); -} - -/** PEM-encode the private key portion of env and write it to a - * newly allocated string. On success, set *dest to the new - * string, *len to the string's length, and return 0. On - * failure, return -1. - */ -int -crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest, - size_t *len) -{ - return crypto_pk_write_key_to_string_impl(env, dest, len, 0); -} - -/** Read a PEM-encoded public key from the first len characters of - * src, and store the result in env. Return 0 on success, -1 on - * failure. - */ -int -crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, - size_t len) -{ - BIO *b; - - tor_assert(env); - tor_assert(src); - tor_assert(lenkey) - RSA_free(env->key); - env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL); - BIO_free(b); - if (!env->key) { - crypto_openssl_log_errors(LOG_WARN, "reading public key from string"); - return -1; - } - - return 0; -} - -/** Write the private key from env into the file named by fname, - * PEM-encoded. Return 0 on success, -1 on failure. - */ -int -crypto_pk_write_private_key_to_filename(crypto_pk_t *env, - const char *fname) -{ - BIO *bio; - char *cp; - long len; - char *s; - int r; - - tor_assert(crypto_pk_key_is_private(env)); - - if (!(bio = BIO_new(BIO_s_mem()))) - return -1; - if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL) - == 0) { - crypto_openssl_log_errors(LOG_WARN, "writing private key"); - BIO_free(bio); - return -1; - } - len = BIO_get_mem_data(bio, &cp); - tor_assert(len >= 0); - s = tor_malloc(len+1); - memcpy(s, cp, len); - s[len]='\0'; - r = write_str_to_file(fname, s, 0); - BIO_free(bio); - memwipe(s, 0, strlen(s)); - tor_free(s); - return r; -} - /** Return true iff env has a valid key. */ int @@ -512,11 +302,11 @@ crypto_pk_dup_key(crypto_pk_t *env) return env; } -#ifdef TOR_UNIT_TESTS -/** For testing: replace dest with src. (Dest must have a refcount - * of 1) */ +/** Replace dest with src (private key only). (Dest must have a refcount + * of 1) + */ void -crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src) +crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src) { tor_assert(dest); tor_assert(dest->refs == 1); @@ -524,7 +314,19 @@ crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src) RSA_free(dest->key); dest->key = RSAPrivateKey_dup(src->key); } -#endif /* defined(TOR_UNIT_TESTS) */ + +/** Replace dest with src (public key only). (Dest must have a refcount + * of 1) + */ +void +crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src) +{ + tor_assert(dest); + tor_assert(dest->refs == 1); + tor_assert(src); + RSA_free(dest->key); + dest->key = RSAPublicKey_dup(src->key); +} /** Make a real honest-to-goodness copy of env, and return it. * Returns NULL on failure. */ @@ -733,74 +535,48 @@ crypto_pk_asn1_decode(const char *str, size_t len) return crypto_new_pk_from_openssl_rsa_(rsa); } -/** Given a crypto_pk_t pk, allocate a new buffer containing the - * Base64 encoding of the DER representation of the private key as a NUL - * terminated string, and return it via priv_out. Return 0 on - * success, -1 on failure. - * - * It is the caller's responsibility to sanitize and free the resulting buffer. +/** ASN.1-encode the private portion of pk into dest. + * Return -1 on error, or the number of characters used on success. */ int -crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out) +crypto_pk_asn1_encode_private(const crypto_pk_t *pk, char *dest, + size_t dest_len) { - unsigned char *der = NULL; - int der_len; - int ret = -1; + int len; + unsigned char *buf = NULL; - *priv_out = NULL; + len = i2d_RSAPrivateKey(pk->key, &buf); + if (len < 0 || buf == NULL) + return -1; - der_len = i2d_RSAPrivateKey(pk->key, &der); - if (der_len < 0 || der == NULL) - return ret; - - size_t priv_len = base64_encode_size(der_len, 0) + 1; - char *priv = tor_malloc_zero(priv_len); - if (base64_encode(priv, priv_len, (char *)der, der_len, 0) >= 0) { - *priv_out = priv; - ret = 0; - } else { - tor_free(priv); + if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) { + OPENSSL_free(buf); + return -1; } - - memwipe(der, 0, der_len); - OPENSSL_free(der); - return ret; + /* We don't encode directly into 'dest', because that would be illegal + * type-punning. (C99 is smarter than me, C99 is smarter than me...) + */ + memcpy(dest,buf,len); + OPENSSL_free(buf); + return len; } -/** Given a string containing the Base64 encoded DER representation of the - * private key str, decode and return the result on success, or NULL - * on failure. +/** Decode an ASN.1-encoded private key from str; return the result on + * success and NULL on failure. */ crypto_pk_t * -crypto_pk_base64_decode_private(const char *str, size_t len) +crypto_pk_asn1_decode_private(const char *str, size_t len) { - crypto_pk_t *pk = NULL; - - char *der = tor_malloc_zero(len + 1); - int der_len = base64_decode(der, len, str, len); - if (der_len <= 0) { - log_warn(LD_CRYPTO, "Stored RSA private key seems corrupted (base64)."); - goto out; - } - - const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */ - RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len); + RSA *rsa; + unsigned char *buf; + const unsigned char *cp; + cp = buf = tor_malloc(len); + memcpy(buf,str,len); + rsa = d2i_RSAPrivateKey(NULL, &cp, len); + tor_free(buf); if (!rsa) { - crypto_openssl_log_errors(LOG_WARN, "decoding private key"); - goto out; + crypto_openssl_log_errors(LOG_WARN,"decoding public key"); + return NULL; } - - pk = crypto_new_pk_from_openssl_rsa_(rsa); - - /* Make sure it's valid. */ - if (crypto_pk_check_key(pk) <= 0) { - crypto_pk_free(pk); - pk = NULL; - goto out; - } - - out: - memwipe(der, 0, len + 1); - tor_free(der); - return pk; + return crypto_new_pk_from_openssl_rsa_(rsa); } diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 81d43ff13f..90fb8d468b 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -1519,7 +1519,8 @@ test_crypto_digests(void *arg) (void)arg; k = crypto_pk_new(); tt_assert(k); - r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_3, -1); + r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_3, + strlen(AUTHORITY_SIGNKEY_3)); tt_assert(!r); r = crypto_pk_get_digest(k, digest); diff --git a/src/test/testing_rsakeys.c b/src/test/testing_rsakeys.c index a8c9ce4ce8..c8062b82d5 100644 --- a/src/test/testing_rsakeys.c +++ b/src/test/testing_rsakeys.c @@ -490,7 +490,7 @@ crypto_pk_generate_key_with_bits__get_cached(crypto_pk_t *env, int bits) { if (bits == 1024 || bits == 2048) { crypto_pk_t *newkey = pk_generate_internal(bits); - crypto_pk_assign_(env, newkey); + crypto_pk_assign_private(env, newkey); crypto_pk_free(newkey); } else { return crypto_pk_generate_key_with_bits__real(env, bits); @@ -544,4 +544,3 @@ init_pregenerated_keys(void) crypto_pk_generate_key_with_bits__get_cached); #endif /* defined(USE_PREGENERATED_RSA_KEYS) */ } - From cb5cfe3177bbf841e5ad18c0103b3d2ebe443e07 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 31 Jul 2018 18:42:42 -0400 Subject: [PATCH 39/76] Also reinitialize the pregenerated keys postfork. --- src/test/testing_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 6957342c62..17a0756803 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -227,7 +227,9 @@ void tinytest_postfork(void); void tinytest_postfork(void) { + free_pregenerated_keys(); crypto_postfork(); + init_pregenerated_keys(); } /** Main entry point for unit test code: parse the command line, and run From aa45511250ed9509ca06b76497f66835796a7998 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 19 Jul 2018 12:03:01 -0400 Subject: [PATCH 40/76] Implement RSA for NSS. --- src/lib/crypt_ops/crypto_rsa.h | 16 +- src/lib/crypt_ops/crypto_rsa_nss.c | 699 +++++++++++++++++++++++++ src/lib/crypt_ops/crypto_rsa_openssl.c | 2 +- src/lib/crypt_ops/include.am | 7 +- 4 files changed, 712 insertions(+), 12 deletions(-) create mode 100644 src/lib/crypt_ops/crypto_rsa_nss.c diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index bcb2b51dd1..afc6c4201c 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -110,14 +110,6 @@ int crypto_pk_get_common_digests(crypto_pk_t *pk, int crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out); crypto_pk_t *crypto_pk_base64_decode_private(const char *str, size_t len); -#ifdef TOR_UNIT_TESTS -#ifdef ENABLE_NSS -struct SECItemStr; -STATIC int secitem_uint_cmp(const struct SECItemStr *a, - const struct SECItemStr *b); -#endif -#endif - #ifdef ENABLE_OPENSSL /* Prototypes for private functions only used by tortls.c, crypto.c, and the * unit tests. */ @@ -132,4 +124,12 @@ MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,( void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src); void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src); +#ifdef TOR_UNIT_TESTS +#ifdef ENABLE_NSS +struct SECItemStr; +STATIC int secitem_uint_cmp(const struct SECItemStr *a, + const struct SECItemStr *b); +#endif +#endif + #endif diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c new file mode 100644 index 0000000000..0411687b9a --- /dev/null +++ b/src/lib/crypt_ops/crypto_rsa_nss.c @@ -0,0 +1,699 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_rsa.c + * \brief NSS implementations of our RSA code. + **/ + +#include "lib/crypt_ops/crypto_rsa.h" + +#include "lib/crypt_ops/crypto_nss_mgt.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/ctime/di_ops.h" +#include "lib/encoding/binascii.h" +#include "lib/fs/files.h" +#include "lib/intmath/cmp.h" +#include "lib/intmath/muldiv.h" +#include "lib/log/log.h" +#include "lib/log/util_bug.h" + +#include + +#include +#include +#include + +#ifdef ENABLE_OPENSSL +#include +#include +#endif + +/** Declaration for crypto_pk_t structure. */ +struct crypto_pk_t +{ + SECKEYPrivateKey *seckey; + SECKEYPublicKey *pubkey; +}; + +/** Return true iff key contains the private-key portion of the RSA + * key. */ +int +crypto_pk_key_is_private(const crypto_pk_t *key) +{ + return key && key->seckey; +} + +#ifdef ENABLE_OPENSSL +/** used by tortls.c: wrap an RSA* in a crypto_pk_t. Take ownership of the + * RSA object. */ +crypto_pk_t * +crypto_new_pk_from_openssl_rsa_(RSA *rsa) +{ + crypto_pk_t *pk = NULL; + unsigned char *buf = NULL; + int len = i2d_RSAPublicKey(rsa, &buf); + RSA_free(rsa); + + if (len < 0 || buf == NULL) + goto end; + + pk = crypto_pk_asn1_decode((const char *)buf, len); + + end: + if (buf) + OPENSSL_free(buf); + return pk; +} + +/** Helper, used by tor-gencert.c. Return the RSA from a + * crypto_pk_t. */ +struct rsa_st * +crypto_pk_get_openssl_rsa_(crypto_pk_t *pk) +{ + size_t buflen = crypto_pk_keysize(pk)*16; + unsigned char *buf = tor_malloc_zero(buflen); + const unsigned char *cp = buf; + RSA *rsa = NULL; + + int used = crypto_pk_asn1_encode_private(pk, (char*)buf, buflen); + if (used < 0) + goto end; + rsa = d2i_RSAPrivateKey(NULL, &cp, used); + + end: + memwipe(buf, 0, buflen); + tor_free(buf); + return rsa; +} + +/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff + * private is set, include the private-key portion of the key. Return a valid + * pointer on success, and NULL on failure. */ +MOCK_IMPL(struct evp_pkey_st *, +crypto_pk_get_openssl_evp_pkey_,(crypto_pk_t *pk, int private)) +{ + size_t buflen = crypto_pk_keysize(pk)*16; + unsigned char *buf = tor_malloc_zero(buflen); + const unsigned char *cp = buf; + RSA *rsa = NULL; + EVP_PKEY *result = NULL; + + if (private) { + int len = crypto_pk_asn1_encode_private(pk, (char*)buf, buflen); + if (len < 0) + goto end; + rsa = d2i_RSAPrivateKey(NULL, &cp, len); + } else { + int len = crypto_pk_asn1_encode(pk, (char*)buf, buflen); + if (len < 0) + goto end; + rsa = d2i_RSAPublicKey(NULL, &cp, len); + } + if (!rsa) + goto end; + + if (!(result = EVP_PKEY_new())) + goto end; + if (!(EVP_PKEY_assign_RSA(result, rsa))) { + EVP_PKEY_free(result); + RSA_free(rsa); + result = NULL; + } + + end: + memwipe(buf, 0, buflen); + tor_free(buf); + return result; +} +#endif + +/** Allocate and return storage for a public key. The key itself will not yet + * be set. + */ +MOCK_IMPL(crypto_pk_t *, +crypto_pk_new,(void)) +{ + crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t)); + return result; +} + +/** Release the NSS objects held in key */ +static void +crypto_pk_clear(crypto_pk_t *key) +{ + if (key->pubkey) + SECKEY_DestroyPublicKey(key->pubkey); + if (key->seckey) + SECKEY_DestroyPrivateKey(key->seckey); + memset(key, 0, sizeof(crypto_pk_t)); +} + +/** Release a reference to an asymmetric key; when all the references + * are released, free the key. + */ +void +crypto_pk_free_(crypto_pk_t *key) +{ + if (!key) + return; + + crypto_pk_clear(key); + + tor_free(key); +} + +/** Generate a bits-bit new public/private keypair in env. + * Return 0 on success, -1 on failure. + */ +MOCK_IMPL(int, +crypto_pk_generate_key_with_bits,(crypto_pk_t *key, int bits)) +{ + tor_assert(key); + + PK11RSAGenParams params = { + .keySizeInBits = bits, + .pe = TOR_RSA_EXPONENT + }; + + int result = -1; + PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS_KEY_PAIR_GEN, NULL); + SECKEYPrivateKey *seckey = NULL; + SECKEYPublicKey *pubkey = NULL; + + if (!slot) { + crypto_nss_log_errors(LOG_WARN, "getting slot for RSA keygen"); + goto done; + } + + seckey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, ¶ms, + &pubkey, + PR_FALSE /*isPerm */, + PR_FALSE /*isSensitive*/, + NULL); + if (seckey == NULL || pubkey == NULL) { + crypto_nss_log_errors(LOG_WARN, "generating an RSA key"); + goto done; + } + + crypto_pk_clear(key); + key->seckey = seckey; + key->pubkey = pubkey; + seckey = NULL; + pubkey = NULL; + + result = 0; + done: + if (slot) + PK11_FreeSlot(slot); + if (pubkey) + SECKEY_DestroyPublicKey(pubkey); + if (seckey) + SECKEY_DestroyPrivateKey(seckey); + + return result; +} + +/** Return true iff env has a valid key. + */ +int +crypto_pk_check_key(crypto_pk_t *key) +{ + return key && key->pubkey; +} + +/** Return true iff env contains a public key whose public exponent + * equals 65537. + */ +int +crypto_pk_public_exponent_ok(crypto_pk_t *key) +{ + return key && + key->pubkey && + key->pubkey->keyType == rsaKey && + DER_GetUInteger(&key->pubkey->u.rsa.publicExponent) == TOR_RSA_EXPONENT; +} + +/** Compare two big-endian integers stored in a and b; return a tristate. + */ +STATIC int +secitem_uint_cmp(const SECItem *a, const SECItem *b) +{ + const unsigned abits = SECKEY_BigIntegerBitLength(a); + const unsigned bbits = SECKEY_BigIntegerBitLength(b); + + if (abits < bbits) + return -1; + else if (abits > bbits) + return 1; + + /* okay, they have the same number of bits set. Get a pair of aligned + * pointers to their bytes that are set... */ + const unsigned nbytes = CEIL_DIV(abits, 8); + tor_assert(nbytes <= a->len); + tor_assert(nbytes <= b->len); + + const unsigned char *aptr = a->data + (a->len - nbytes); + const unsigned char *bptr = b->data + (b->len - nbytes); + + /* And compare them. */ + return fast_memcmp(aptr, bptr, nbytes); +} + +/** Compare the public-key components of a and b. Return less than 0 + * if a\b. A NULL key is + * considered to be less than all non-NULL keys, and equal to itself. + * + * Note that this may leak information about the keys through timing. + */ +int +crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b) +{ + int result; + char a_is_non_null = (a != NULL) && (a->pubkey != NULL); + char b_is_non_null = (b != NULL) && (b->pubkey != NULL); + char an_argument_is_null = !a_is_non_null | !b_is_non_null; + + result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null)); + if (an_argument_is_null) + return result; + + // This is all Tor uses with this structure. + tor_assert(a->pubkey->keyType == rsaKey); + tor_assert(b->pubkey->keyType == rsaKey); + + const SECItem *a_n, *a_e, *b_n, *b_e; + a_n = &a->pubkey->u.rsa.modulus; + b_n = &b->pubkey->u.rsa.modulus; + a_e = &a->pubkey->u.rsa.publicExponent; + b_e = &b->pubkey->u.rsa.publicExponent; + + result = secitem_uint_cmp(a_n, b_n); + if (result) + return result; + return secitem_uint_cmp(a_e, b_e); +} + +/** Return the size of the public key modulus in env, in bytes. */ +size_t +crypto_pk_keysize(const crypto_pk_t *key) +{ + tor_assert(key); + tor_assert(key->pubkey); + return SECKEY_PublicKeyStrength(key->pubkey); +} + +/** Return the size of the public key modulus of env, in bits. */ +int +crypto_pk_num_bits(crypto_pk_t *key) +{ + tor_assert(key); + tor_assert(key->pubkey); + return SECKEY_PublicKeyStrengthInBits(key->pubkey); +} + +/** + * Make a copy of key and return it. + */ +crypto_pk_t * +crypto_pk_dup_key(crypto_pk_t *key) +{ + crypto_pk_t *result = crypto_pk_new(); + if (key->pubkey) + result->pubkey = SECKEY_CopyPublicKey(key->pubkey); + if (key->seckey) + result->seckey = SECKEY_CopyPrivateKey(key->seckey); + return result; +} + +/** For testing: replace dest with src. (Dest must have a refcount + * of 1) */ +void +crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src) +{ + crypto_pk_clear(dest); + if (src->pubkey) + dest->pubkey = SECKEY_CopyPublicKey(src->pubkey); +} + +/** For testing: replace dest with src. (Dest must have a refcount + * of 1) */ +void +crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src) +{ + crypto_pk_clear(dest); + if (src->pubkey) + dest->pubkey = SECKEY_CopyPublicKey(src->pubkey); + if (src->seckey) + dest->seckey = SECKEY_CopyPrivateKey(src->seckey); +} + +/** Make a real honest-to-goodness copy of env, and return it. + * Returns NULL on failure. */ +crypto_pk_t * +crypto_pk_copy_full(crypto_pk_t *key) +{ + // These aren't reference-counted is nss, so it's fine to just + // use the same function. + return crypto_pk_dup_key(key); +} + +static const CK_RSA_PKCS_OAEP_PARAMS oaep_params = { + .hashAlg = CKM_SHA_1, + .mgf = CKG_MGF1_SHA1, + .source = CKZ_DATA_SPECIFIED, + .pSourceData = NULL, + .ulSourceDataLen = 0 +}; +static const SECItem oaep_item = { + .type = siBuffer, + .data = (unsigned char *) &oaep_params, + .len = sizeof(oaep_params) +}; + +/** Return the mechanism code and parameters for a given padding method when + * used with RSA */ +static CK_MECHANISM_TYPE +padding_to_mechanism(int padding, SECItem **item_out) +{ + switch (padding) { + case PK_PKCS1_OAEP_PADDING: + *item_out = (SECItem *)&oaep_item; + return CKM_RSA_PKCS_OAEP; + default: + tor_assert_unreached(); + *item_out = NULL; + return CKM_INVALID_MECHANISM; + } +} + +/** Encrypt fromlen bytes from from with the public key + * in env, using the padding method padding. On success, + * write the result to to, and return the number of bytes + * written. On failure, return -1. + * + * tolen is the number of writable bytes in to, and must be + * at least the length of the modulus of env. + */ +int +crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, + const char *from, size_t fromlen, int padding) +{ + tor_assert(env); + tor_assert(to); + tor_assert(from); + tor_assert(tolen < INT_MAX); + tor_assert(fromlen < INT_MAX); + + if (BUG(!crypto_pk_check_key(env))) + return -1; + + unsigned int result_len = 0; + SECItem *item = NULL; + CK_MECHANISM_TYPE m = padding_to_mechanism(padding, &item); + + SECStatus s = PK11_PubEncrypt(env->pubkey, m, item, + (unsigned char *)to, &result_len, + (unsigned int)tolen, + (const unsigned char *)from, + (unsigned int)fromlen, + NULL); + if (s != SECSuccess) { + crypto_nss_log_errors(LOG_WARN, "encrypting to an RSA key"); + return -1; + } + + return (int)result_len; +} + +/** Decrypt fromlen bytes from from with the private key + * in env, using the padding method padding. On success, + * write the result to to, and return the number of bytes + * written. On failure, return -1. + * + * tolen is the number of writable bytes in to, and must be + * at least the length of the modulus of key. + */ +int +crypto_pk_private_decrypt(crypto_pk_t *key, char *to, + size_t tolen, + const char *from, size_t fromlen, + int padding, int warnOnFailure) +{ + tor_assert(key); + tor_assert(to); + tor_assert(from); + tor_assert(tolen < INT_MAX); + tor_assert(fromlen < INT_MAX); + + if (!crypto_pk_key_is_private(key)) + return -1; /* Not a private key. */ + + unsigned int result_len = 0; + SECItem *item = NULL; + CK_MECHANISM_TYPE m = padding_to_mechanism(padding, &item); + SECStatus s = PK11_PrivDecrypt(key->seckey, m, item, + (unsigned char *)to, &result_len, + (unsigned int)tolen, + (const unsigned char *)from, + (unsigned int)fromlen); + + if (s != SECSuccess) { + const int severity = warnOnFailure ? LOG_WARN : LOG_INFO; + crypto_nss_log_errors(severity, "decrypting with an RSA key"); + return -1; + } + + return (int)result_len; +} + +/** Check the signature in from (fromlen bytes long) with the + * public key in key, using PKCS1 padding. On success, write the + * signed data to to, and return the number of bytes written. + * On failure, return -1. + * + * tolen is the number of writable bytes in to, and must be + * at least the length of the modulus of key. + */ +MOCK_IMPL(int, +crypto_pk_public_checksig,(const crypto_pk_t *key, char *to, + size_t tolen, + const char *from, size_t fromlen)) +{ + tor_assert(key); + tor_assert(to); + tor_assert(from); + tor_assert(tolen < INT_MAX); + tor_assert(fromlen < INT_MAX); + tor_assert(key->pubkey); + + SECItem sig = { + .type = siBuffer, + .data = (unsigned char *) from, + .len = (unsigned int) fromlen, + }; + SECItem dsig = { + .type = siBuffer, + .data = (unsigned char *) to, + .len = (unsigned int) tolen + }; + SECStatus s; + s = PK11_VerifyRecover(key->pubkey, &sig, &dsig, NULL); + if (s != SECSuccess) + return -1; + + return (int)dsig.len; +} + +/** Sign fromlen bytes of data from from with the private key in + * env, using PKCS1 padding. On success, write the signature to + * to, and return the number of bytes written. On failure, return + * -1. + * + * tolen is the number of writable bytes in to, and must be + * at least the length of the modulus of env. + */ +int +crypto_pk_private_sign(const crypto_pk_t *key, char *to, size_t tolen, + const char *from, size_t fromlen) +{ + tor_assert(key); + tor_assert(to); + tor_assert(from); + tor_assert(tolen < INT_MAX); + tor_assert(fromlen < INT_MAX); + + if (BUG(!crypto_pk_key_is_private(key))) + return -1; + + SECItem sig = { + .type = siBuffer, + .data = (unsigned char *)to, + .len = (unsigned int) tolen + }; + SECItem hash = { + .type = siBuffer, + .data = (unsigned char *)from, + .len = (unsigned int) fromlen + }; + CK_MECHANISM_TYPE m = CKM_RSA_PKCS; + SECStatus s = PK11_SignWithMechanism(key->seckey, m, NULL, + &sig, &hash); + + if (s != SECSuccess) { + crypto_nss_log_errors(LOG_WARN, "signing with an RSA key"); + return -1; + } + + return (int)sig.len; +} + +/* "This has lead to people trading hard-to-find object identifiers and ASN.1 + * definitions like baseball cards" - Peter Gutmann, "X.509 Style Guide". */ +static const unsigned char RSA_OID[] = { + /* RSADSI */ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + /* PKCS1 */ 0x01, 0x01, + /* RSA */ 0x01 +}; + +/** ASN.1-encode the public portion of pk into dest. + * Return -1 on error, or the number of characters used on success. + */ +int +crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len) +{ + tor_assert(pk); + if (pk->pubkey == NULL) + return -1; + + CERTSubjectPublicKeyInfo *info; + info = SECKEY_CreateSubjectPublicKeyInfo(pk->pubkey); + if (! info) + return -1; + + const SECItem *item = &info->subjectPublicKey; + size_t actual_len = (item->len) >> 3; /* bits to bytes */ + size_t n_used = MIN(actual_len, dest_len); + memcpy(dest, item->data, n_used); + + SECKEY_DestroySubjectPublicKeyInfo(info); + return (int) n_used; +} + +/** Decode an ASN.1-encoded public key from str; return the result on + * success and NULL on failure. + */ +crypto_pk_t * +crypto_pk_asn1_decode(const char *str, size_t len) +{ + tor_assert(str); + if (len >= INT_MAX) + return NULL; + CERTSubjectPublicKeyInfo info = { + .algorithm = { + .algorithm = { + .type = siDEROID, + .data = (unsigned char *)RSA_OID, + .len = sizeof(RSA_OID) + } + }, + .subjectPublicKey = { + .type = siBuffer, + .data = (unsigned char *)str, + .len = (unsigned int)(len << 3) /* bytes to bits */ + } + }; + + SECKEYPublicKey *pub = SECKEY_ExtractPublicKey(&info); + if (pub == NULL) + return NULL; + + crypto_pk_t *result = crypto_pk_new(); + result->pubkey = pub; + return result; +} + +DISABLE_GCC_WARNING(unused-parameter) + +/** Given a crypto_pk_t pk, allocate a new buffer containing the Base64 + * encoding of the DER representation of the private key into the + * dest_len-byte buffer in dest. + * Return the number of bytes written on success, -1 on failure. + */ +int +crypto_pk_asn1_encode_private(const crypto_pk_t *pk, + char *dest, size_t destlen) +{ + tor_assert(destlen <= INT_MAX); + if (!crypto_pk_key_is_private(pk)) + return -1; + + SECKEYPrivateKeyInfo *info = PK11_ExportPrivKeyInfo(pk->seckey, NULL); + if (!info) + return -1; + SECItem *item = &info->privateKey; + + if (destlen < item->len) { + SECKEY_DestroyPrivateKeyInfo(info, PR_TRUE); + return -1; + } + int result = (int)item->len; + memcpy(dest, item->data, item->len); + SECKEY_DestroyPrivateKeyInfo(info, PR_TRUE); + + return result; +} + +/** Given a buffer containing the DER representation of the + * private key str, decode and return the result on success, or NULL + * on failure. + */ +crypto_pk_t * +crypto_pk_asn1_decode_private(const char *str, size_t len) +{ + tor_assert(str); + tor_assert(len < INT_MAX); + + SECKEYPrivateKeyInfo info = { + .algorithm = { + .algorithm = { + .type = siBuffer, + .data = (unsigned char *)RSA_OID, + .len = sizeof(RSA_OID) + } + }, + .privateKey = { + .type = siBuffer, + .data = (unsigned char *)str, + .len = (int)len, + } + }; + + PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS, NULL); + SECStatus s; + SECKEYPrivateKey *seckey = NULL; + + s = PK11_ImportPrivateKeyInfoAndReturnKey(slot, &info, + NULL /* nickname */, + NULL /* publicValue */, + PR_FALSE /* isPerm */, + PR_FALSE /* isPrivate */, + KU_ALL /* keyUsage */, + &seckey, NULL); + + crypto_pk_t *output = NULL; + + if (s == SECSuccess && seckey) { + output = crypto_pk_new(); + output->seckey = seckey; + output->pubkey = SECKEY_ConvertToPublicKey(seckey); + tor_assert(output->pubkey); + } else { + crypto_nss_log_errors(LOG_WARN, "decoding an RSA private key"); + } + + return output; +} diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c index 9a3729a878..7ff71f6dd4 100644 --- a/src/lib/crypt_ops/crypto_rsa_openssl.c +++ b/src/lib/crypt_ops/crypto_rsa_openssl.c @@ -76,7 +76,7 @@ crypto_new_pk_from_openssl_rsa_(RSA *rsa) RSA * crypto_pk_get_openssl_rsa_(crypto_pk_t *env) { - return RSA_PrivateKeyDup(env->key); + return RSAPrivateKey_dup(env->key); } /** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 69bd7c2dbf..195dac6bdb 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -19,7 +19,6 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/crypto_pwbox.c \ src/lib/crypt_ops/crypto_rand.c \ src/lib/crypt_ops/crypto_rsa.c \ - src/lib/crypt_ops/crypto_rsa_openssl.c \ src/lib/crypt_ops/crypto_s2k.c \ src/lib/crypt_ops/crypto_util.c \ src/lib/crypt_ops/digestset.c @@ -28,10 +27,12 @@ if USE_NSS src_lib_libtor_crypt_ops_a_SOURCES += \ src/lib/crypt_ops/aes_nss.c \ src/lib/crypt_ops/crypto_dh_nss.c \ - src/lib/crypt_ops/crypto_nss_mgt.c + src/lib/crypt_ops/crypto_nss_mgt.c \ + src/lib/crypt_ops/crypto_rsa_nss.c else src_lib_libtor_crypt_ops_a_SOURCES += \ - src/lib/crypt_ops/aes_openssl.c + src/lib/crypt_ops/aes_openssl.c \ + src/lib/crypt_ops/crypto_rsa_openssl.c endif if USE_OPENSSL From 3ccb94d7b6ff6806bc17d71ef461f4211a014879 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sat, 11 Aug 2018 16:36:05 -0400 Subject: [PATCH 41/76] The RSA_free in this test is no longer needed or wanted --- src/test/test_tortls.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index 49a39e264b..cd3435556c 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -702,7 +702,6 @@ test_tortls_get_my_client_auth_key(void *ignored) tt_assert(ret == expected); done: - RSA_free(k); tor_free(expected); tor_free(ctx); } From 9a4f05b05c12687e640d2aed9bb21229138bd1a5 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sat, 11 Aug 2018 18:16:04 -0400 Subject: [PATCH 42/76] Split X509 code out of tortls.c --- src/core/or/channeltls.c | 1 + src/core/or/connection_or.c | 1 + src/feature/nodelist/torcert.c | 1 + src/feature/relay/routerkeys.c | 1 + src/lib/tls/include.am | 6 +- src/lib/tls/tortls.c | 506 +------------------------------ src/lib/tls/tortls.h | 76 ++--- src/lib/tls/x509.c | 539 +++++++++++++++++++++++++++++++++ src/lib/tls/x509.h | 78 +++++ src/test/test_link_handshake.c | 1 + src/test/test_tortls.c | 1 + 11 files changed, 649 insertions(+), 562 deletions(-) create mode 100644 src/lib/tls/x509.c create mode 100644 src/lib/tls/x509.h diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c index 87f5a02b75..153813c4d6 100644 --- a/src/core/or/channeltls.c +++ b/src/core/or/channeltls.c @@ -70,6 +70,7 @@ #include "core/or/var_cell_st.h" #include "lib/tls/tortls.h" +#include "lib/tls/x509.h" /** How many CELL_PADDING cells have we received, ever? */ uint64_t stats_n_padding_cells_processed = 0; diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c index c5ff10f6a3..08371d1ad7 100644 --- a/src/core/or/connection_or.c +++ b/src/core/or/connection_or.c @@ -73,6 +73,7 @@ #include "lib/crypt_ops/crypto_format.h" #include "lib/tls/tortls.h" +#include "lib/tls/x509.h" static int connection_tls_finish_handshake(or_connection_t *conn); static int connection_or_launch_v3_or_handshake(or_connection_t *conn); diff --git a/src/feature/nodelist/torcert.c b/src/feature/nodelist/torcert.c index a276082021..fe67e56403 100644 --- a/src/feature/nodelist/torcert.c +++ b/src/feature/nodelist/torcert.c @@ -33,6 +33,7 @@ #include "lib/log/log.h" #include "trunnel/link_handshake.h" #include "lib/tls/tortls.h" +#include "lib/tls/x509.h" #include "core/or/or_handshake_certs_st.h" diff --git a/src/feature/relay/routerkeys.c b/src/feature/relay/routerkeys.c index f12eb3d332..d018f300f4 100644 --- a/src/feature/relay/routerkeys.c +++ b/src/feature/relay/routerkeys.c @@ -24,6 +24,7 @@ #include "lib/crypt_ops/crypto_util.h" #include "lib/term/getpass.h" #include "lib/tls/tortls.h" +#include "lib/tls/x509.h" #include "lib/crypt_ops/crypto_format.h" #define ENC_KEY_HEADER "Boxed Ed25519 key" diff --git a/src/lib/tls/include.am b/src/lib/tls/include.am index 9cc57ca777..1fd25a0b33 100644 --- a/src/lib/tls/include.am +++ b/src/lib/tls/include.am @@ -7,7 +7,8 @@ endif src_lib_libtor_tls_a_SOURCES = \ src/lib/tls/buffers_tls.c \ - src/lib/tls/tortls.c + src/lib/tls/tortls.c \ + src/lib/tls/x509.c src_lib_libtor_tls_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) @@ -20,4 +21,5 @@ src_lib_libtor_tls_testing_a_CFLAGS = \ noinst_HEADERS += \ src/lib/tls/ciphers.inc \ src/lib/tls/buffers_tls.h \ - src/lib/tls/tortls.h + src/lib/tls/tortls.h \ + src/lib/tls/x509.h diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 466df8966c..20e4320813 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -28,6 +28,7 @@ #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_dh.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/tls/x509.h" /* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ @@ -67,26 +68,6 @@ ENABLE_GCC_WARNING(redundant-decls) #include "lib/arch/bytes.h" -#ifdef OPENSSL_1_1_API -#define X509_get_notBefore_const(cert) \ - X509_get0_notBefore(cert) -#define X509_get_notAfter_const(cert) \ - X509_get0_notAfter(cert) -#ifndef X509_get_notBefore -#define X509_get_notBefore(cert) \ - X509_getm_notBefore(cert) -#endif -#ifndef X509_get_notAfter -#define X509_get_notAfter(cert) \ - X509_getm_notAfter(cert) -#endif -#else /* ! OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */ -#define X509_get_notBefore_const(cert) \ - ((const ASN1_TIME*) X509_get_notBefore((X509 *)cert)) -#define X509_get_notAfter_const(cert) \ - ((const ASN1_TIME*) X509_get_notAfter((X509 *)cert)) -#endif - /* Copied from or.h */ #define LEGAL_NICKNAME_CHARACTERS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" @@ -162,10 +143,6 @@ tor_tls_get_by_ssl(const SSL *ssl) static void tor_tls_context_decref(tor_tls_context_t *ctx); static void tor_tls_context_incref(tor_tls_context_t *ctx); -static int check_cert_lifetime_internal(int severity, const X509 *cert, - time_t now, - int past_tolerance, int future_tolerance); - /** Global TLS contexts. We keep them here because nobody else needs * to touch them. * @@ -267,7 +244,7 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, /** Log all pending tls errors at level severity in log domain * domain. Use doing to describe our current activities. */ -STATIC void +void tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) { unsigned long err; @@ -375,7 +352,7 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra, /** Initialize OpenSSL, unless it has already been initialized. */ -static void +void tor_tls_init(void) { check_no_tls_errors(); @@ -459,145 +436,6 @@ always_accept_verify_cb(int preverify_ok, return 1; } -/** Return a newly allocated X509 name with commonName cname. */ -static X509_NAME * -tor_x509_name_new(const char *cname) -{ - int nid; - X509_NAME *name; - /* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */ - if (!(name = X509_NAME_new())) - return NULL; - if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error; - if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC, - (unsigned char*)cname, -1, -1, 0))) - goto error; - /* LCOV_EXCL_BR_STOP */ - return name; - - /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/ - error: - X509_NAME_free(name); - return NULL; - /* LCOV_EXCL_STOP */ -} - -/** Generate and sign an X509 certificate with the public key rsa, - * signed by the private key rsa_sign. The commonName of the - * certificate will be cname; the commonName of the issuer will be - * cname_sign. The cert will be valid for cert_lifetime - * seconds, starting from some time in the past. - * - * Return a certificate on success, NULL on failure. - */ -MOCK_IMPL(STATIC X509 *, -tor_tls_create_certificate,(crypto_pk_t *rsa, - crypto_pk_t *rsa_sign, - const char *cname, - const char *cname_sign, - unsigned int cert_lifetime)) -{ - /* OpenSSL generates self-signed certificates with random 64-bit serial - * numbers, so let's do that too. */ -#define SERIAL_NUMBER_SIZE 8 - - time_t start_time, end_time; - BIGNUM *serial_number = NULL; - unsigned char serial_tmp[SERIAL_NUMBER_SIZE]; - EVP_PKEY *sign_pkey = NULL, *pkey=NULL; - X509 *x509 = NULL; - X509_NAME *name = NULL, *name_issuer=NULL; - - tor_tls_init(); - - /* Make sure we're part-way through the certificate lifetime, rather - * than having it start right now. Don't choose quite uniformly, since - * then we might pick a time where we're about to expire. Lastly, be - * sure to start on a day boundary. */ - time_t now = time(NULL); - /* Our certificate lifetime will be cert_lifetime no matter what, but if we - * start cert_lifetime in the past, we'll have 0 real lifetime. instead we - * start up to (cert_lifetime - min_real_lifetime - start_granularity) in - * the past. */ - const time_t min_real_lifetime = 24*3600; - const time_t start_granularity = 24*3600; - time_t earliest_start_time; - /* Don't actually start in the future! */ - if (cert_lifetime <= min_real_lifetime + start_granularity) { - earliest_start_time = now - 1; - } else { - earliest_start_time = now + min_real_lifetime + start_granularity - - cert_lifetime; - } - start_time = crypto_rand_time_range(earliest_start_time, now); - /* Round the start time back to the start of a day. */ - start_time -= start_time % start_granularity; - - end_time = start_time + cert_lifetime; - - tor_assert(rsa); - tor_assert(cname); - tor_assert(rsa_sign); - tor_assert(cname_sign); - if (!(sign_pkey = crypto_pk_get_openssl_evp_pkey_(rsa_sign,1))) - goto error; - if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,0))) - goto error; - if (!(x509 = X509_new())) - goto error; - if (!(X509_set_version(x509, 2))) - goto error; - - { /* our serial number is 8 random bytes. */ - crypto_rand((char *)serial_tmp, sizeof(serial_tmp)); - if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL))) - goto error; - if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509)))) - goto error; - } - - if (!(name = tor_x509_name_new(cname))) - goto error; - if (!(X509_set_subject_name(x509, name))) - goto error; - if (!(name_issuer = tor_x509_name_new(cname_sign))) - goto error; - if (!(X509_set_issuer_name(x509, name_issuer))) - goto error; - - if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time)) - goto error; - if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time)) - goto error; - if (!X509_set_pubkey(x509, pkey)) - goto error; - - if (!X509_sign(x509, sign_pkey, EVP_sha256())) - goto error; - - goto done; - error: - if (x509) { - X509_free(x509); - x509 = NULL; - } - done: - tls_log_errors(NULL, LOG_WARN, LD_NET, "generating certificate"); - if (sign_pkey) - EVP_PKEY_free(sign_pkey); - if (pkey) - EVP_PKEY_free(pkey); - if (serial_number) - BN_clear_free(serial_number); - if (name) - X509_NAME_free(name); - if (name_issuer) - X509_NAME_free(name_issuer); - return x509; - -#undef SERIAL_NUMBER_SIZE -} - /** List of ciphers that servers should select from when the client might be * claiming extra unsupported ciphers in order to avoid fingerprinting. */ static const char SERVER_CIPHER_LIST[] = @@ -697,156 +535,6 @@ static const char CLIENT_CIPHER_LIST[] = #undef CIPHER #undef XCIPHER -/** Free all storage held in cert */ -void -tor_x509_cert_free_(tor_x509_cert_t *cert) -{ - if (! cert) - return; - if (cert->cert) - X509_free(cert->cert); - tor_free(cert->encoded); - memwipe(cert, 0x03, sizeof(*cert)); - /* LCOV_EXCL_BR_START since cert will never be NULL here */ - tor_free(cert); - /* LCOV_EXCL_BR_STOP */ -} - -/** - * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert". - * - * Steals a reference to x509_cert. - */ -MOCK_IMPL(STATIC tor_x509_cert_t *, -tor_x509_cert_new,(X509 *x509_cert)) -{ - tor_x509_cert_t *cert; - EVP_PKEY *pkey; - RSA *rsa; - int length; - unsigned char *buf = NULL; - - if (!x509_cert) - return NULL; - - length = i2d_X509(x509_cert, &buf); - cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - if (length <= 0 || buf == NULL) { - goto err; - } - cert->encoded_len = (size_t) length; - cert->encoded = tor_malloc(length); - memcpy(cert->encoded, buf, length); - OPENSSL_free(buf); - - cert->cert = x509_cert; - - crypto_common_digests(&cert->cert_digests, - (char*)cert->encoded, cert->encoded_len); - - if ((pkey = X509_get_pubkey(x509_cert)) && - (rsa = EVP_PKEY_get1_RSA(pkey))) { - crypto_pk_t *pk = crypto_new_pk_from_openssl_rsa_(rsa); - if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) { - crypto_pk_free(pk); - EVP_PKEY_free(pkey); - goto err; - } - - cert->pkey_digests_set = 1; - crypto_pk_free(pk); - EVP_PKEY_free(pkey); - } - - return cert; - err: - /* LCOV_EXCL_START for the same reason as the exclusion above */ - tor_free(cert); - log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate."); - X509_free(x509_cert); - return NULL; - /* LCOV_EXCL_STOP */ -} - -/** Return a new copy of cert. */ -tor_x509_cert_t * -tor_x509_cert_dup(const tor_x509_cert_t *cert) -{ - tor_assert(cert); - X509 *x509 = cert->cert; - return tor_x509_cert_new(X509_dup(x509)); -} - -/** Read a DER-encoded X509 cert, of length exactly certificate_len, - * from a certificate. Return a newly allocated tor_x509_cert_t on - * success and NULL on failure. */ -tor_x509_cert_t * -tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len) -{ - X509 *x509; - const unsigned char *cp = (const unsigned char *)certificate; - tor_x509_cert_t *newcert; - tor_assert(certificate); - check_no_tls_errors(); - - if (certificate_len > INT_MAX) - goto err; - - x509 = d2i_X509(NULL, &cp, (int)certificate_len); - - if (!x509) - goto err; /* Couldn't decode */ - if (cp - certificate != (int)certificate_len) { - X509_free(x509); - goto err; /* Didn't use all the bytes */ - } - newcert = tor_x509_cert_new(x509); - if (!newcert) { - goto err; - } - if (newcert->encoded_len != certificate_len || - fast_memneq(newcert->encoded, certificate, certificate_len)) { - /* Cert wasn't in DER */ - tor_x509_cert_free(newcert); - goto err; - } - return newcert; - err: - tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "decoding a certificate"); - return NULL; -} - -/** Set *encoded_out and *size_out to cert's encoded DER - * representation and length, respectively. */ -void -tor_x509_cert_get_der(const tor_x509_cert_t *cert, - const uint8_t **encoded_out, size_t *size_out) -{ - tor_assert(cert); - tor_assert(encoded_out); - tor_assert(size_out); - *encoded_out = cert->encoded; - *size_out = cert->encoded_len; -} - -/** Return a set of digests for the public key in cert, or NULL if this - * cert's public key is not one we know how to take the digest of. */ -const common_digests_t * -tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert) -{ - if (cert->pkey_digests_set) - return &cert->pkey_digests; - else - return NULL; -} - -/** Return a set of digests for the public key in cert. */ -const common_digests_t * -tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert) -{ - return &cert->cert_digests; -} - /** Remove a reference to ctx, and free it if it has no more * references. */ static void @@ -898,28 +586,6 @@ tor_tls_get_my_client_auth_key(void) return client_tls_context->auth_key; } -/** - * Return a newly allocated copy of the public key that a certificate - * certifies. Watch out! This returns NULL if the cert's key is not RSA. - */ -crypto_pk_t * -tor_tls_cert_get_key(tor_x509_cert_t *cert) -{ - crypto_pk_t *result = NULL; - EVP_PKEY *pkey = X509_get_pubkey(cert->cert); - RSA *rsa; - if (!pkey) - return NULL; - rsa = EVP_PKEY_get1_RSA(pkey); - if (!rsa) { - EVP_PKEY_free(pkey); - return NULL; - } - result = crypto_new_pk_from_openssl_rsa_(rsa); - EVP_PKEY_free(pkey); - return result; -} - /** Return true iff the other side of tls has authenticated to us, and * the key certified in cert is the same as the key they used to do it. */ @@ -946,71 +612,6 @@ tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert)) return result; } -/** Check whether cert is well-formed, currently live, and correctly - * signed by the public key in signing_cert. If check_rsa_1024, - * make sure that it has an RSA key with 1024 bits; otherwise, just check that - * the key is long enough. Return 1 if the cert is good, and 0 if it's bad or - * we couldn't check it. */ -int -tor_tls_cert_is_valid(int severity, - const tor_x509_cert_t *cert, - const tor_x509_cert_t *signing_cert, - time_t now, - int check_rsa_1024) -{ - check_no_tls_errors(); - EVP_PKEY *cert_key; - int r, key_ok = 0; - - if (!signing_cert || !cert) - goto bad; - - EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert); - if (!signing_key) - goto bad; - r = X509_verify(cert->cert, signing_key); - EVP_PKEY_free(signing_key); - if (r <= 0) - goto bad; - - /* okay, the signature checked out right. Now let's check the check the - * lifetime. */ - if (check_cert_lifetime_internal(severity, cert->cert, now, - 48*60*60, 30*24*60*60) < 0) - goto bad; - - cert_key = X509_get_pubkey(cert->cert); - if (check_rsa_1024 && cert_key) { - RSA *rsa = EVP_PKEY_get1_RSA(cert_key); -#ifdef OPENSSL_1_1_API - if (rsa && RSA_bits(rsa) == 1024) -#else - if (rsa && BN_num_bits(rsa->n) == 1024) -#endif - key_ok = 1; - if (rsa) - RSA_free(rsa); - } else if (cert_key) { - int min_bits = 1024; -#ifdef EVP_PKEY_EC - if (EVP_PKEY_base_id(cert_key) == EVP_PKEY_EC) - min_bits = 128; -#endif - if (EVP_PKEY_bits(cert_key) >= min_bits) - key_ok = 1; - } - EVP_PKEY_free(cert_key); - if (!key_ok) - goto bad; - - /* XXXX compare DNs or anything? */ - - return 1; - bad: - tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "checking a certificate"); - return 0; -} - /** Increase the reference count of ctx. */ static void tor_tls_context_incref(tor_tls_context_t *ctx) @@ -2146,63 +1747,6 @@ tor_tls_get_own_cert,(tor_tls_t *tls)) return tor_x509_cert_new(duplicate); } -/** Warn that a certificate lifetime extends through a certain range. */ -static void -log_cert_lifetime(int severity, const X509 *cert, const char *problem, - time_t now) -{ - BIO *bio = NULL; - BUF_MEM *buf; - char *s1=NULL, *s2=NULL; - char mytime[33]; - struct tm tm; - size_t n; - - if (problem) - tor_log(severity, LD_GENERAL, - "Certificate %s. Either their clock is set wrong, or your clock " - "is wrong.", - problem); - - if (!(bio = BIO_new(BIO_s_mem()))) { - log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end; - } - if (!(ASN1_TIME_print(bio, X509_get_notBefore_const(cert)))) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime"); - goto end; - } - BIO_get_mem_ptr(bio, &buf); - s1 = tor_strndup(buf->data, buf->length); - - (void)BIO_reset(bio); - if (!(ASN1_TIME_print(bio, X509_get_notAfter_const(cert)))) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime"); - goto end; - } - BIO_get_mem_ptr(bio, &buf); - s2 = tor_strndup(buf->data, buf->length); - - n = strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm)); - if (n > 0) { - tor_log(severity, LD_GENERAL, - "(certificate lifetime runs from %s through %s. Your time is %s.)", - s1,s2,mytime); - } else { - tor_log(severity, LD_GENERAL, - "(certificate lifetime runs from %s through %s. " - "Couldn't get your time.)", - s1, s2); - } - - end: - /* Not expected to get invoked */ - tls_log_errors(NULL, LOG_WARN, LD_NET, "getting certificate lifetime"); - if (bio) - BIO_free(bio); - tor_free(s1); - tor_free(s2); -} - /** Helper function: try to extract a link certificate and an identity * certificate from tls, and store them in *cert_out and * *id_cert_out respectively. Log all messages at level @@ -2325,50 +1869,6 @@ tor_tls_check_lifetime(int severity, tor_tls_t *tls, return r; } -/** Helper: check whether cert is expired give or take - * past_tolerance seconds, or not-yet-valid give or take - * future_tolerance seconds. (Relative to the current time - * now.) If it is live, return 0. If it is not live, log a message - * and return -1. */ -static int -check_cert_lifetime_internal(int severity, const X509 *cert, - time_t now, - int past_tolerance, int future_tolerance) -{ - time_t t; - - t = now + future_tolerance; - if (X509_cmp_time(X509_get_notBefore_const(cert), &t) > 0) { - log_cert_lifetime(severity, cert, "not yet valid", now); - return -1; - } - t = now - past_tolerance; - if (X509_cmp_time(X509_get_notAfter_const(cert), &t) < 0) { - log_cert_lifetime(severity, cert, "already expired", now); - return -1; - } - - return 0; -} - -#ifdef TOR_UNIT_TESTS -/* Testing only: return a new x509 cert with the same contents as inp, - but with the expiration time new_expiration_time, signed with - signing_key. */ -STATIC tor_x509_cert_t * -tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp, - time_t new_expiration_time, - crypto_pk_t *signing_key) -{ - X509 *newc = X509_dup(inp->cert); - X509_time_adj(X509_get_notAfter(newc), 0, &new_expiration_time); - EVP_PKEY *pk = crypto_pk_get_openssl_evp_pkey_(signing_key, 1); - tor_assert(X509_sign(newc, pk, EVP_sha256())); - EVP_PKEY_free(pk); - return tor_x509_cert_new(newc); -} -#endif /* defined(TOR_UNIT_TESTS) */ - /** Return the number of bytes available for reading from tls. */ int diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h index fe192b2abc..a1d90c16b3 100644 --- a/src/lib/tls/tortls.h +++ b/src/lib/tls/tortls.h @@ -18,8 +18,7 @@ /* Opaque structure to hold a TLS connection. */ typedef struct tor_tls_t tor_tls_t; -/* Opaque structure to hold an X509 certificate. */ -typedef struct tor_x509_cert_t tor_x509_cert_t; +struct tor_x509_cert_t; /* Possible return values for most tor_tls_* functions. */ #define MIN_TOR_TLS_ERROR_VAL_ -9 @@ -62,10 +61,11 @@ typedef enum { } tor_tls_state_t; #define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t) -struct x509_st; +#ifdef ENABLE_OPENSSL struct ssl_st; struct ssl_ctx_st; struct ssl_session_st; +#endif /** Holds a SSL_CTX object and related state used to configure TLS * connections. @@ -73,23 +73,13 @@ struct ssl_session_st; typedef struct tor_tls_context_t { int refcnt; struct ssl_ctx_st *ctx; - tor_x509_cert_t *my_link_cert; - tor_x509_cert_t *my_id_cert; - tor_x509_cert_t *my_auth_cert; + struct tor_x509_cert_t *my_link_cert; + struct tor_x509_cert_t *my_id_cert; + struct tor_x509_cert_t *my_auth_cert; crypto_pk_t *link_key; crypto_pk_t *auth_key; } tor_tls_context_t; -/** Structure that we use for a single certificate. */ -struct tor_x509_cert_t { - struct x509_st *cert; - uint8_t *encoded; - size_t encoded_len; - unsigned pkey_digests_set : 1; - common_digests_t cert_digests; - common_digests_t pkey_digests; -}; - /** Holds a SSL object and its associated data. Members are only * accessed from within tortls.c. */ @@ -134,15 +124,15 @@ STATIC int tor_tls_get_error(tor_tls_t *tls, int r, int extra, const char *doing, int severity, int domain); STATIC tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl); STATIC void tor_tls_allocate_tor_tls_object_ex_data_index(void); +MOCK_DECL(STATIC void, try_to_extract_certs_from_tls, + (int severity, tor_tls_t *tls, struct x509_st **cert_out, + struct x509_st **id_cert_out)); #ifdef TORTLS_OPENSSL_PRIVATE STATIC int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx); STATIC int tor_tls_classify_client_ciphers(const struct ssl_st *ssl, STACK_OF(SSL_CIPHER) *peer_ciphers); #endif STATIC int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl); -MOCK_DECL(STATIC void, try_to_extract_certs_from_tls, - (int severity, tor_tls_t *tls, struct x509_st **cert_out, - struct x509_st **id_cert_out)); #ifndef HAVE_SSL_SESSION_GET_MASTER_KEY STATIC size_t SSL_SESSION_get_master_key(struct ssl_session_st *s, uint8_t *out, @@ -161,23 +151,13 @@ STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret, STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher); #endif /* defined(TORTLS_OPENSSL_PRIVATE) */ -MOCK_DECL(STATIC struct x509_st *, tor_tls_create_certificate, - (crypto_pk_t *rsa, - crypto_pk_t *rsa_sign, - const char *cname, - const char *cname_sign, - unsigned int cert_lifetime)); STATIC tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, unsigned flags, int is_client); -MOCK_DECL(STATIC tor_x509_cert_t *, tor_x509_cert_new, - (struct x509_st *x509_cert)); STATIC int tor_tls_context_init_one(tor_tls_context_t **ppcontext, crypto_pk_t *identity, unsigned int key_lifetime, unsigned int flags, int is_client); -STATIC void tls_log_errors(tor_tls_t *tls, int severity, int domain, - const char *doing); #ifdef TOR_UNIT_TESTS extern int tor_tls_object_ex_data_index; @@ -187,15 +167,10 @@ extern uint16_t v2_cipher_list[]; extern uint64_t total_bytes_written_over_tls; extern uint64_t total_bytes_written_by_tls; -STATIC tor_x509_cert_t *tor_x509_cert_replace_expiration( - const tor_x509_cert_t *inp, - time_t new_expiration_time, - crypto_pk_t *signing_key); #endif /* defined(TOR_UNIT_TESTS) */ #endif /* defined(TORTLS_PRIVATE) */ -tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert); const char *tor_tls_err_to_string(int err); void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz); @@ -205,6 +180,9 @@ void tor_tls_free_all(void); #define TOR_TLS_CTX_USE_ECDHE_P256 (1u<<1) #define TOR_TLS_CTX_USE_ECDHE_P224 (1u<<2) +void tor_tls_init(void); +void tls_log_errors(tor_tls_t *tls, int severity, int domain, + const char *doing); int tor_tls_context_init(unsigned flags, crypto_pk_t *client_identity, crypto_pk_t *server_identity, @@ -218,8 +196,8 @@ int tor_tls_is_server(tor_tls_t *tls); void tor_tls_free_(tor_tls_t *tls); #define tor_tls_free(tls) FREE_AND_NULL(tor_tls_t, tor_tls_free_, (tls)) int tor_tls_peer_has_cert(tor_tls_t *tls); -MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls)); -MOCK_DECL(tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls)); +MOCK_DECL(struct tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls)); +MOCK_DECL(struct tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls)); int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity); int tor_tls_check_lifetime(int severity, tor_tls_t *tls, time_t now, @@ -248,6 +226,8 @@ MOCK_DECL(double, tls_get_write_overhead_ratio, (void)); int tor_tls_used_v1_handshake(tor_tls_t *tls); int tor_tls_get_num_server_handshakes(tor_tls_t *tls); int tor_tls_server_got_renegotiate(tor_tls_t *tls); +MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls, + const struct tor_x509_cert_t *cert)); MOCK_DECL(int,tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out)); MOCK_DECL(int,tor_tls_export_key_material,( tor_tls_t *tls, uint8_t *secrets_out, @@ -263,29 +243,11 @@ void check_no_tls_errors_(const char *fname, int line); void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, int severity, int domain, const char *doing); -void tor_x509_cert_free_(tor_x509_cert_t *cert); -#define tor_x509_cert_free(c) \ - FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c)) -tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate, - size_t certificate_len); -void tor_x509_cert_get_der(const tor_x509_cert_t *cert, - const uint8_t **encoded_out, size_t *size_out); -const common_digests_t *tor_x509_cert_get_id_digests( - const tor_x509_cert_t *cert); -const common_digests_t *tor_x509_cert_get_cert_digests( - const tor_x509_cert_t *cert); int tor_tls_get_my_certs(int server, - const tor_x509_cert_t **link_cert_out, - const tor_x509_cert_t **id_cert_out); + const struct tor_x509_cert_t **link_cert_out, + const struct tor_x509_cert_t **id_cert_out); crypto_pk_t *tor_tls_get_my_client_auth_key(void); -crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert); -MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls, - const tor_x509_cert_t *cert)); -int tor_tls_cert_is_valid(int severity, - const tor_x509_cert_t *cert, - const tor_x509_cert_t *signing_cert, - time_t now, - int check_rsa_1024); + const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls); int evaluate_ecgroup_for_tls(const char *ecgroup); diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c new file mode 100644 index 0000000000..feded34737 --- /dev/null +++ b/src/lib/tls/x509.c @@ -0,0 +1,539 @@ +/* Copyright (c) 2003, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file x509.c + * \brief Wrapper functions to present a consistent interface to + * X.509 functions from OpenSSL. + **/ + +#include "lib/tls/x509.h" +#include "lib/tls/tortls.h" +//#include "lib/crypt_ops/crypto_cipher.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_util.h" + +/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in + * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ +DISABLE_GCC_WARNING(redundant-decls) + +#include + +#ifdef OPENSSL_NO_EC +#error "We require OpenSSL with ECC support" +#endif + +#include +#include +#include +#include +#include + +ENABLE_GCC_WARNING(redundant-decls) + +#include "lib/log/log.h" +#include "lib/log/util_bug.h" +#include "lib/ctime/di_ops.h" +#include "lib/encoding/time_fmt.h" + +#include +#include + +#ifdef OPENSSL_1_1_API +#define X509_get_notBefore_const(cert) \ + X509_get0_notBefore(cert) +#define X509_get_notAfter_const(cert) \ + X509_get0_notAfter(cert) +#ifndef X509_get_notBefore +#define X509_get_notBefore(cert) \ + X509_getm_notBefore(cert) +#endif +#ifndef X509_get_notAfter +#define X509_get_notAfter(cert) \ + X509_getm_notAfter(cert) +#endif +#else /* ! OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */ +#define X509_get_notBefore_const(cert) \ + ((const ASN1_TIME*) X509_get_notBefore((X509 *)cert)) +#define X509_get_notAfter_const(cert) \ + ((const ASN1_TIME*) X509_get_notAfter((X509 *)cert)) +#endif + +/** Return a newly allocated X509 name with commonName cname. */ +static X509_NAME * +tor_x509_name_new(const char *cname) +{ + int nid; + X509_NAME *name; + /* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */ + if (!(name = X509_NAME_new())) + return NULL; + if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error; + if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC, + (unsigned char*)cname, -1, -1, 0))) + goto error; + /* LCOV_EXCL_BR_STOP */ + return name; + + /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/ + error: + X509_NAME_free(name); + return NULL; + /* LCOV_EXCL_STOP */ +} + +/** Generate and sign an X509 certificate with the public key rsa, + * signed by the private key rsa_sign. The commonName of the + * certificate will be cname; the commonName of the issuer will be + * cname_sign. The cert will be valid for cert_lifetime + * seconds, starting from some time in the past. + * + * Return a certificate on success, NULL on failure. + */ +MOCK_IMPL(X509 *, +tor_tls_create_certificate,(crypto_pk_t *rsa, + crypto_pk_t *rsa_sign, + const char *cname, + const char *cname_sign, + unsigned int cert_lifetime)) +{ + /* OpenSSL generates self-signed certificates with random 64-bit serial + * numbers, so let's do that too. */ +#define SERIAL_NUMBER_SIZE 8 + + time_t start_time, end_time; + BIGNUM *serial_number = NULL; + unsigned char serial_tmp[SERIAL_NUMBER_SIZE]; + EVP_PKEY *sign_pkey = NULL, *pkey=NULL; + X509 *x509 = NULL; + X509_NAME *name = NULL, *name_issuer=NULL; + + tor_tls_init(); + + /* Make sure we're part-way through the certificate lifetime, rather + * than having it start right now. Don't choose quite uniformly, since + * then we might pick a time where we're about to expire. Lastly, be + * sure to start on a day boundary. */ + time_t now = time(NULL); + /* Our certificate lifetime will be cert_lifetime no matter what, but if we + * start cert_lifetime in the past, we'll have 0 real lifetime. instead we + * start up to (cert_lifetime - min_real_lifetime - start_granularity) in + * the past. */ + const time_t min_real_lifetime = 24*3600; + const time_t start_granularity = 24*3600; + time_t earliest_start_time; + /* Don't actually start in the future! */ + if (cert_lifetime <= min_real_lifetime + start_granularity) { + earliest_start_time = now - 1; + } else { + earliest_start_time = now + min_real_lifetime + start_granularity + - cert_lifetime; + } + start_time = crypto_rand_time_range(earliest_start_time, now); + /* Round the start time back to the start of a day. */ + start_time -= start_time % start_granularity; + + end_time = start_time + cert_lifetime; + + tor_assert(rsa); + tor_assert(cname); + tor_assert(rsa_sign); + tor_assert(cname_sign); + if (!(sign_pkey = crypto_pk_get_openssl_evp_pkey_(rsa_sign,1))) + goto error; + if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,0))) + goto error; + if (!(x509 = X509_new())) + goto error; + if (!(X509_set_version(x509, 2))) + goto error; + + { /* our serial number is 8 random bytes. */ + crypto_rand((char *)serial_tmp, sizeof(serial_tmp)); + if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL))) + goto error; + if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509)))) + goto error; + } + + if (!(name = tor_x509_name_new(cname))) + goto error; + if (!(X509_set_subject_name(x509, name))) + goto error; + if (!(name_issuer = tor_x509_name_new(cname_sign))) + goto error; + if (!(X509_set_issuer_name(x509, name_issuer))) + goto error; + + if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time)) + goto error; + if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time)) + goto error; + if (!X509_set_pubkey(x509, pkey)) + goto error; + + if (!X509_sign(x509, sign_pkey, EVP_sha256())) + goto error; + + goto done; + error: + if (x509) { + X509_free(x509); + x509 = NULL; + } + done: + tls_log_errors(NULL, LOG_WARN, LD_NET, "generating certificate"); + if (sign_pkey) + EVP_PKEY_free(sign_pkey); + if (pkey) + EVP_PKEY_free(pkey); + if (serial_number) + BN_clear_free(serial_number); + if (name) + X509_NAME_free(name); + if (name_issuer) + X509_NAME_free(name_issuer); + return x509; + +#undef SERIAL_NUMBER_SIZE +} + +/** Free all storage held in cert */ +void +tor_x509_cert_free_(tor_x509_cert_t *cert) +{ + if (! cert) + return; + if (cert->cert) + X509_free(cert->cert); + tor_free(cert->encoded); + memwipe(cert, 0x03, sizeof(*cert)); + /* LCOV_EXCL_BR_START since cert will never be NULL here */ + tor_free(cert); + /* LCOV_EXCL_BR_STOP */ +} + +/** + * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert". + * + * Steals a reference to x509_cert. + */ +MOCK_IMPL(tor_x509_cert_t *, +tor_x509_cert_new,(X509 *x509_cert)) +{ + tor_x509_cert_t *cert; + EVP_PKEY *pkey; + RSA *rsa; + int length; + unsigned char *buf = NULL; + + if (!x509_cert) + return NULL; + + length = i2d_X509(x509_cert, &buf); + cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); + if (length <= 0 || buf == NULL) { + goto err; + } + cert->encoded_len = (size_t) length; + cert->encoded = tor_malloc(length); + memcpy(cert->encoded, buf, length); + OPENSSL_free(buf); + + cert->cert = x509_cert; + + crypto_common_digests(&cert->cert_digests, + (char*)cert->encoded, cert->encoded_len); + + if ((pkey = X509_get_pubkey(x509_cert)) && + (rsa = EVP_PKEY_get1_RSA(pkey))) { + crypto_pk_t *pk = crypto_new_pk_from_openssl_rsa_(rsa); + if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) { + crypto_pk_free(pk); + EVP_PKEY_free(pkey); + goto err; + } + + cert->pkey_digests_set = 1; + crypto_pk_free(pk); + EVP_PKEY_free(pkey); + } + + return cert; + err: + /* LCOV_EXCL_START for the same reason as the exclusion above */ + tor_free(cert); + log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate."); + X509_free(x509_cert); + return NULL; + /* LCOV_EXCL_STOP */ +} + +/** Return a new copy of cert. */ +tor_x509_cert_t * +tor_x509_cert_dup(const tor_x509_cert_t *cert) +{ + tor_assert(cert); + X509 *x509 = cert->cert; + return tor_x509_cert_new(X509_dup(x509)); +} + +/** Read a DER-encoded X509 cert, of length exactly certificate_len, + * from a certificate. Return a newly allocated tor_x509_cert_t on + * success and NULL on failure. */ +tor_x509_cert_t * +tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len) +{ + X509 *x509; + const unsigned char *cp = (const unsigned char *)certificate; + tor_x509_cert_t *newcert; + tor_assert(certificate); + check_no_tls_errors(); + + if (certificate_len > INT_MAX) + goto err; + + x509 = d2i_X509(NULL, &cp, (int)certificate_len); + + if (!x509) + goto err; /* Couldn't decode */ + if (cp - certificate != (int)certificate_len) { + X509_free(x509); + goto err; /* Didn't use all the bytes */ + } + newcert = tor_x509_cert_new(x509); + if (!newcert) { + goto err; + } + if (newcert->encoded_len != certificate_len || + fast_memneq(newcert->encoded, certificate, certificate_len)) { + /* Cert wasn't in DER */ + tor_x509_cert_free(newcert); + goto err; + } + return newcert; + err: + tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "decoding a certificate"); + return NULL; +} + +/** Set *encoded_out and *size_out to cert's encoded DER + * representation and length, respectively. */ +void +tor_x509_cert_get_der(const tor_x509_cert_t *cert, + const uint8_t **encoded_out, size_t *size_out) +{ + tor_assert(cert); + tor_assert(encoded_out); + tor_assert(size_out); + *encoded_out = cert->encoded; + *size_out = cert->encoded_len; +} + +/** Return a set of digests for the public key in cert, or NULL if this + * cert's public key is not one we know how to take the digest of. */ +const common_digests_t * +tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert) +{ + if (cert->pkey_digests_set) + return &cert->pkey_digests; + else + return NULL; +} + +/** Return a set of digests for the public key in cert. */ +const common_digests_t * +tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert) +{ + return &cert->cert_digests; +} + +/** + * Return a newly allocated copy of the public key that a certificate + * certifies. Watch out! This returns NULL if the cert's key is not RSA. + */ +crypto_pk_t * +tor_tls_cert_get_key(tor_x509_cert_t *cert) +{ + crypto_pk_t *result = NULL; + EVP_PKEY *pkey = X509_get_pubkey(cert->cert); + RSA *rsa; + if (!pkey) + return NULL; + rsa = EVP_PKEY_get1_RSA(pkey); + if (!rsa) { + EVP_PKEY_free(pkey); + return NULL; + } + result = crypto_new_pk_from_openssl_rsa_(rsa); + EVP_PKEY_free(pkey); + return result; +} + +/** Check whether cert is well-formed, currently live, and correctly + * signed by the public key in signing_cert. If check_rsa_1024, + * make sure that it has an RSA key with 1024 bits; otherwise, just check that + * the key is long enough. Return 1 if the cert is good, and 0 if it's bad or + * we couldn't check it. */ +int +tor_tls_cert_is_valid(int severity, + const tor_x509_cert_t *cert, + const tor_x509_cert_t *signing_cert, + time_t now, + int check_rsa_1024) +{ + check_no_tls_errors(); + EVP_PKEY *cert_key; + int r, key_ok = 0; + + if (!signing_cert || !cert) + goto bad; + + EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert); + if (!signing_key) + goto bad; + r = X509_verify(cert->cert, signing_key); + EVP_PKEY_free(signing_key); + if (r <= 0) + goto bad; + + /* okay, the signature checked out right. Now let's check the check the + * lifetime. */ + if (check_cert_lifetime_internal(severity, cert->cert, now, + 48*60*60, 30*24*60*60) < 0) + goto bad; + + cert_key = X509_get_pubkey(cert->cert); + if (check_rsa_1024 && cert_key) { + RSA *rsa = EVP_PKEY_get1_RSA(cert_key); +#ifdef OPENSSL_1_1_API + if (rsa && RSA_bits(rsa) == 1024) +#else + if (rsa && BN_num_bits(rsa->n) == 1024) +#endif + key_ok = 1; + if (rsa) + RSA_free(rsa); + } else if (cert_key) { + int min_bits = 1024; +#ifdef EVP_PKEY_EC + if (EVP_PKEY_base_id(cert_key) == EVP_PKEY_EC) + min_bits = 128; +#endif + if (EVP_PKEY_bits(cert_key) >= min_bits) + key_ok = 1; + } + EVP_PKEY_free(cert_key); + if (!key_ok) + goto bad; + + /* XXXX compare DNs or anything? */ + + return 1; + bad: + tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "checking a certificate"); + return 0; +} + +/** Warn that a certificate lifetime extends through a certain range. */ +static void +log_cert_lifetime(int severity, const X509 *cert, const char *problem, + time_t now) +{ + BIO *bio = NULL; + BUF_MEM *buf; + char *s1=NULL, *s2=NULL; + char mytime[33]; + struct tm tm; + size_t n; + + if (problem) + tor_log(severity, LD_GENERAL, + "Certificate %s. Either their clock is set wrong, or your clock " + "is wrong.", + problem); + + if (!(bio = BIO_new(BIO_s_mem()))) { + log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end; + } + if (!(ASN1_TIME_print(bio, X509_get_notBefore_const(cert)))) { + tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime"); + goto end; + } + BIO_get_mem_ptr(bio, &buf); + s1 = tor_strndup(buf->data, buf->length); + + (void)BIO_reset(bio); + if (!(ASN1_TIME_print(bio, X509_get_notAfter_const(cert)))) { + tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime"); + goto end; + } + BIO_get_mem_ptr(bio, &buf); + s2 = tor_strndup(buf->data, buf->length); + + n = strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm)); + if (n > 0) { + tor_log(severity, LD_GENERAL, + "(certificate lifetime runs from %s through %s. Your time is %s.)", + s1,s2,mytime); + } else { + tor_log(severity, LD_GENERAL, + "(certificate lifetime runs from %s through %s. " + "Couldn't get your time.)", + s1, s2); + } + + end: + /* Not expected to get invoked */ + tls_log_errors(NULL, LOG_WARN, LD_NET, "getting certificate lifetime"); + if (bio) + BIO_free(bio); + tor_free(s1); + tor_free(s2); +} + +/** Helper: check whether cert is expired give or take + * past_tolerance seconds, or not-yet-valid give or take + * future_tolerance seconds. (Relative to the current time + * now.) If it is live, return 0. If it is not live, log a message + * and return -1. */ +int +check_cert_lifetime_internal(int severity, const X509 *cert, + time_t now, + int past_tolerance, int future_tolerance) +{ + time_t t; + + t = now + future_tolerance; + if (X509_cmp_time(X509_get_notBefore_const(cert), &t) > 0) { + log_cert_lifetime(severity, cert, "not yet valid", now); + return -1; + } + t = now - past_tolerance; + if (X509_cmp_time(X509_get_notAfter_const(cert), &t) < 0) { + log_cert_lifetime(severity, cert, "already expired", now); + return -1; + } + + return 0; +} + +#ifdef TOR_UNIT_TESTS +/* Testing only: return a new x509 cert with the same contents as inp, + but with the expiration time new_expiration_time, signed with + signing_key. */ +STATIC tor_x509_cert_t * +tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp, + time_t new_expiration_time, + crypto_pk_t *signing_key) +{ + X509 *newc = X509_dup(inp->cert); + X509_time_adj(X509_get_notAfter(newc), 0, &new_expiration_time); + EVP_PKEY *pk = crypto_pk_get_openssl_evp_pkey_(signing_key, 1); + tor_assert(X509_sign(newc, pk, EVP_sha256())); + EVP_PKEY_free(pk); + return tor_x509_cert_new(newc); +} +#endif /* defined(TOR_UNIT_TESTS) */ diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h new file mode 100644 index 0000000000..2f3f3a4101 --- /dev/null +++ b/src/lib/tls/x509.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_X509_H +#define TOR_X509_H + +/** + * \file x509.h + * \brief Headers for tortls.c + **/ + +#include "lib/crypt_ops/crypto_rsa.h" +#include "lib/crypt_ops/compat_openssl.h" +#include "lib/testsupport/testsupport.h" + +/* Opaque structure to hold an X509 certificate. */ +typedef struct tor_x509_cert_t tor_x509_cert_t; + +#ifdef ENABLE_OPENSSL +struct x509_st; +#endif + +/** Structure that we use for a single certificate. */ +struct tor_x509_cert_t { +#ifdef ENABLE_OPENSSL + struct x509_st *cert; +#endif + uint8_t *encoded; + size_t encoded_len; + unsigned pkey_digests_set : 1; + common_digests_t cert_digests; + common_digests_t pkey_digests; +}; + +MOCK_DECL(struct x509_st *, tor_tls_create_certificate, + (crypto_pk_t *rsa, + crypto_pk_t *rsa_sign, + const char *cname, + const char *cname_sign, + unsigned int cert_lifetime)); +MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new, + (struct x509_st *x509_cert)); + +#ifdef TOR_UNIT_TESTS +tor_x509_cert_t *tor_x509_cert_replace_expiration( + const tor_x509_cert_t *inp, + time_t new_expiration_time, + crypto_pk_t *signing_key); +#endif + +tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert); + +void tor_x509_cert_free_(tor_x509_cert_t *cert); +#define tor_x509_cert_free(c) \ + FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c)) +tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate, + size_t certificate_len); +void tor_x509_cert_get_der(const tor_x509_cert_t *cert, + const uint8_t **encoded_out, size_t *size_out); +const common_digests_t *tor_x509_cert_get_id_digests( + const tor_x509_cert_t *cert); +const common_digests_t *tor_x509_cert_get_cert_digests( + const tor_x509_cert_t *cert); + +crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert); +int tor_tls_cert_is_valid(int severity, + const tor_x509_cert_t *cert, + const tor_x509_cert_t *signing_cert, + time_t now, + int check_rsa_1024); + +int check_cert_lifetime_internal(int severity, const X509 *cert, + time_t now, + int past_tolerance, int future_tolerance); + +#endif diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index e0d12fb472..c1ede5420f 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -25,6 +25,7 @@ #include "core/or/var_cell_st.h" #include "lib/tls/tortls.h" +#include "lib/tls/x509.h" #include "test/test.h" #include "test/log_test_helpers.h" diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index cd3435556c..f5b11d4f2a 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -34,6 +34,7 @@ ENABLE_GCC_WARNING(redundant-decls) #include "lib/log/log.h" #include "app/config/config.h" #include "lib/tls/tortls.h" +#include "lib/tls/x509.h" #include "app/config/or_state_st.h" #include "test/test.h" From 598bc78bfa62e0879497c0ef03999d3700a5cd16 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sat, 11 Aug 2018 19:38:07 -0400 Subject: [PATCH 43/76] Extract tortls structures into a new header; clean up a little --- src/lib/tls/tortls.c | 5 ++- src/lib/tls/tortls.h | 58 +---------------------------- src/lib/tls/tortls_st.h | 67 ++++++++++++++++++++++++++++++++++ src/lib/tls/x509.c | 11 +++++- src/lib/tls/x509.h | 24 +++++++----- src/test/test_link_handshake.c | 1 + src/test/test_tortls.c | 3 ++ 7 files changed, 99 insertions(+), 70 deletions(-) create mode 100644 src/lib/tls/tortls_st.h diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 20e4320813..cb507057ed 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -28,6 +28,7 @@ #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_dh.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/compat_openssl.h" #include "lib/tls/x509.h" /* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in @@ -51,8 +52,8 @@ DISABLE_GCC_WARNING(redundant-decls) ENABLE_GCC_WARNING(redundant-decls) -#define TORTLS_PRIVATE #include "lib/tls/tortls.h" +#include "lib/tls/tortls_st.h" #include "lib/log/log.h" #include "lib/log/util_bug.h" #include "lib/container/smartlist.h" @@ -599,7 +600,7 @@ tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert)) if (!peercert) return 0; link_key = X509_get_pubkey(peercert); - cert_key = X509_get_pubkey(cert->cert); + cert_key = X509_get_pubkey((X509 *)tor_x509_cert_get_impl(cert)); result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1; diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h index a1d90c16b3..f46e732679 100644 --- a/src/lib/tls/tortls.h +++ b/src/lib/tls/tortls.h @@ -12,7 +12,6 @@ **/ #include "lib/crypt_ops/crypto_rsa.h" -#include "lib/crypt_ops/compat_openssl.h" #include "lib/testsupport/testsupport.h" /* Opaque structure to hold a TLS connection. */ @@ -52,14 +51,6 @@ struct tor_x509_cert_t; #define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE) #ifdef TORTLS_PRIVATE -#define TOR_TLS_MAGIC 0x71571571 - -typedef enum { - TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE, - TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE, - TOR_TLS_ST_BUFFEREVENT -} tor_tls_state_t; -#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t) #ifdef ENABLE_OPENSSL struct ssl_st; @@ -70,54 +61,7 @@ struct ssl_session_st; /** Holds a SSL_CTX object and related state used to configure TLS * connections. */ -typedef struct tor_tls_context_t { - int refcnt; - struct ssl_ctx_st *ctx; - struct tor_x509_cert_t *my_link_cert; - struct tor_x509_cert_t *my_id_cert; - struct tor_x509_cert_t *my_auth_cert; - crypto_pk_t *link_key; - crypto_pk_t *auth_key; -} tor_tls_context_t; - -/** Holds a SSL object and its associated data. Members are only - * accessed from within tortls.c. - */ -struct tor_tls_t { - uint32_t magic; - tor_tls_context_t *context; /** A link to the context object for this tls. */ - struct ssl_st *ssl; /**< An OpenSSL SSL object. */ - int socket; /**< The underlying file descriptor for this TLS connection. */ - char *address; /**< An address to log when describing this connection. */ - tor_tls_state_bitfield_t state : 3; /**< The current SSL state, - * depending on which operations - * have completed successfully. */ - unsigned int isServer:1; /**< True iff this is a server-side connection */ - unsigned int wasV2Handshake:1; /**< True iff the original handshake for - * this connection used the updated version - * of the connection protocol (client sends - * different cipher list, server sends only - * one certificate). */ - /** True iff we should call negotiated_callback when we're done reading. */ - unsigned int got_renegotiate:1; - /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't - * called that function yet. */ - int8_t client_cipher_list_type; - /** Incremented every time we start the server side of a handshake. */ - uint8_t server_handshake_count; - size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last - * time. */ - /** Last values retrieved from BIO_number_read()/write(); see - * tor_tls_get_n_raw_bytes() for usage. - */ - unsigned long last_write_count; - unsigned long last_read_count; - /** If set, a callback to invoke whenever the client tries to renegotiate - * the handshake. */ - void (*negotiated_callback)(tor_tls_t *tls, void *arg); - /** Argument to pass to negotiated_callback. */ - void *callback_arg; -}; +typedef struct tor_tls_context_t tor_tls_context_t; STATIC int tor_errno_to_tls_error(int e); STATIC int tor_tls_get_error(tor_tls_t *tls, int r, int extra, diff --git a/src/lib/tls/tortls_st.h b/src/lib/tls/tortls_st.h new file mode 100644 index 0000000000..897be497ef --- /dev/null +++ b/src/lib/tls/tortls_st.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_TORTLS_ST_H +#define TOR_TORTLS_ST_H + +#define TOR_TLS_MAGIC 0x71571571 + +typedef enum { + TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE, + TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE, + TOR_TLS_ST_BUFFEREVENT +} tor_tls_state_t; +#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t) + +struct tor_tls_context_t { + int refcnt; + struct ssl_ctx_st *ctx; + struct tor_x509_cert_t *my_link_cert; + struct tor_x509_cert_t *my_id_cert; + struct tor_x509_cert_t *my_auth_cert; + crypto_pk_t *link_key; + crypto_pk_t *auth_key; +}; + +/** Holds a SSL object and its associated data. Members are only + * accessed from within tortls.c. + */ +struct tor_tls_t { + uint32_t magic; + tor_tls_context_t *context; /** A link to the context object for this tls. */ + struct ssl_st *ssl; /**< An OpenSSL SSL object. */ + int socket; /**< The underlying file descriptor for this TLS connection. */ + char *address; /**< An address to log when describing this connection. */ + tor_tls_state_bitfield_t state : 3; /**< The current SSL state, + * depending on which operations + * have completed successfully. */ + unsigned int isServer:1; /**< True iff this is a server-side connection */ + unsigned int wasV2Handshake:1; /**< True iff the original handshake for + * this connection used the updated version + * of the connection protocol (client sends + * different cipher list, server sends only + * one certificate). */ + /** True iff we should call negotiated_callback when we're done reading. */ + unsigned int got_renegotiate:1; + /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't + * called that function yet. */ + int8_t client_cipher_list_type; + /** Incremented every time we start the server side of a handshake. */ + uint8_t server_handshake_count; + size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last + * time. */ + /** Last values retrieved from BIO_number_read()/write(); see + * tor_tls_get_n_raw_bytes() for usage. + */ + unsigned long last_write_count; + unsigned long last_read_count; + /** If set, a callback to invoke whenever the client tries to renegotiate + * the handshake. */ + void (*negotiated_callback)(tor_tls_t *tls, void *arg); + /** Argument to pass to negotiated_callback. */ + void *callback_arg; +}; + +#endif diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index feded34737..27cba1be6c 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -9,11 +9,12 @@ * X.509 functions from OpenSSL. **/ +#define TOR_X509_PRIVATE #include "lib/tls/x509.h" #include "lib/tls/tortls.h" -//#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/compat_openssl.h" /* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ @@ -332,6 +333,14 @@ tor_x509_cert_get_der(const tor_x509_cert_t *cert, *size_out = cert->encoded_len; } +/** Return the underlying implementation for cert */ +const tor_x509_cert_impl_t * +tor_x509_cert_get_impl(const tor_x509_cert_t *cert) +{ + tor_assert(cert); + return cert->cert; +} + /** Return a set of digests for the public key in cert, or NULL if this * cert's public key is not one we know how to take the digest of. */ const common_digests_t * diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h index 2f3f3a4101..4dadba06d7 100644 --- a/src/lib/tls/x509.h +++ b/src/lib/tls/x509.h @@ -12,36 +12,35 @@ **/ #include "lib/crypt_ops/crypto_rsa.h" -#include "lib/crypt_ops/compat_openssl.h" #include "lib/testsupport/testsupport.h" /* Opaque structure to hold an X509 certificate. */ typedef struct tor_x509_cert_t tor_x509_cert_t; #ifdef ENABLE_OPENSSL -struct x509_st; +typedef struct x509_st tor_x509_cert_impl_t; #endif +#ifdef TOR_X509_PRIVATE /** Structure that we use for a single certificate. */ struct tor_x509_cert_t { -#ifdef ENABLE_OPENSSL - struct x509_st *cert; -#endif + tor_x509_cert_impl_t *cert; uint8_t *encoded; size_t encoded_len; unsigned pkey_digests_set : 1; common_digests_t cert_digests; common_digests_t pkey_digests; }; +#endif -MOCK_DECL(struct x509_st *, tor_tls_create_certificate, +MOCK_DECL(tor_x509_cert_impl_t *, tor_tls_create_certificate, (crypto_pk_t *rsa, crypto_pk_t *rsa_sign, const char *cname, const char *cname_sign, unsigned int cert_lifetime)); MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new, - (struct x509_st *x509_cert)); + (tor_x509_cert_impl_t *x509_cert)); #ifdef TOR_UNIT_TESTS tor_x509_cert_t *tor_x509_cert_replace_expiration( @@ -57,22 +56,27 @@ void tor_x509_cert_free_(tor_x509_cert_t *cert); FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c)) tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len); +const tor_x509_cert_impl_t *tor_x509_cert_get_impl( + const tor_x509_cert_t *cert); void tor_x509_cert_get_der(const tor_x509_cert_t *cert, const uint8_t **encoded_out, size_t *size_out); + const common_digests_t *tor_x509_cert_get_id_digests( const tor_x509_cert_t *cert); const common_digests_t *tor_x509_cert_get_cert_digests( const tor_x509_cert_t *cert); crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert); + int tor_tls_cert_is_valid(int severity, const tor_x509_cert_t *cert, const tor_x509_cert_t *signing_cert, time_t now, int check_rsa_1024); -int check_cert_lifetime_internal(int severity, const X509 *cert, - time_t now, - int past_tolerance, int future_tolerance); +int check_cert_lifetime_internal(int severity, + const tor_x509_cert_impl_t *cert, + time_t now, + int past_tolerance, int future_tolerance); #endif diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index c1ede5420f..e4722b4dfc 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -24,6 +24,7 @@ #include "core/or/or_handshake_state_st.h" #include "core/or/var_cell_st.h" +#define TOR_X509_PRIVATE #include "lib/tls/tortls.h" #include "lib/tls/x509.h" diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index f5b11d4f2a..ec197ba997 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -3,6 +3,7 @@ #define TORTLS_PRIVATE #define TORTLS_OPENSSL_PRIVATE +#define TOR_X509_PRIVATE #define LOG_PRIVATE #include "orconfig.h" @@ -33,7 +34,9 @@ ENABLE_GCC_WARNING(redundant-decls) #include "core/or/or.h" #include "lib/log/log.h" #include "app/config/config.h" +#include "lib/crypt_ops/compat_openssl.h" #include "lib/tls/tortls.h" +#include "lib/tls/tortls_st.h" #include "lib/tls/x509.h" #include "app/config/or_state_st.h" From 91c1e88b7a6d41f93f88cd8754746c836b25721f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sat, 11 Aug 2018 19:54:11 -0400 Subject: [PATCH 44/76] Refactor some of the certificate-manipulation logic --- src/lib/tls/tortls.c | 5 ++-- src/lib/tls/x509.c | 68 +++++++++++++++++++++++++++----------------- src/lib/tls/x509.h | 14 ++++++--- 3 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index cb507057ed..4c5dedb573 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -1856,8 +1856,9 @@ tor_tls_check_lifetime(int severity, tor_tls_t *tls, if (!(cert = SSL_get_peer_certificate(tls->ssl))) goto done; - if (check_cert_lifetime_internal(severity, cert, now, - past_tolerance, future_tolerance) < 0) + if (tor_x509_check_cert_lifetime_internal(severity, cert, now, + past_tolerance, + future_tolerance) < 0) goto done; r = 0; diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index 27cba1be6c..8bed6f5a19 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -85,6 +85,42 @@ tor_x509_name_new(const char *cname) /* LCOV_EXCL_STOP */ } +/** Choose the start and end times for a certificate */ +void +tor_tls_pick_certificate_lifetime(time_t now, + unsigned int cert_lifetime, + time_t *start_time_out, + time_t *end_time_out) +{ + time_t start_time, end_time; + /* Make sure we're part-way through the certificate lifetime, rather + * than having it start right now. Don't choose quite uniformly, since + * then we might pick a time where we're about to expire. Lastly, be + * sure to start on a day boundary. */ + /* Our certificate lifetime will be cert_lifetime no matter what, but if we + * start cert_lifetime in the past, we'll have 0 real lifetime. instead we + * start up to (cert_lifetime - min_real_lifetime - start_granularity) in + * the past. */ + const time_t min_real_lifetime = 24*3600; + const time_t start_granularity = 24*3600; + time_t earliest_start_time; + /* Don't actually start in the future! */ + if (cert_lifetime <= min_real_lifetime + start_granularity) { + earliest_start_time = now - 1; + } else { + earliest_start_time = now + min_real_lifetime + start_granularity + - cert_lifetime; + } + start_time = crypto_rand_time_range(earliest_start_time, now); + /* Round the start time back to the start of a day. */ + start_time -= start_time % start_granularity; + + end_time = start_time + cert_lifetime; + + *start_time_out = start_time; + *end_time_out = end_time; +} + /** Generate and sign an X509 certificate with the public key rsa, * signed by the private key rsa_sign. The commonName of the * certificate will be cname; the commonName of the issuer will be @@ -113,30 +149,10 @@ tor_tls_create_certificate,(crypto_pk_t *rsa, tor_tls_init(); - /* Make sure we're part-way through the certificate lifetime, rather - * than having it start right now. Don't choose quite uniformly, since - * then we might pick a time where we're about to expire. Lastly, be - * sure to start on a day boundary. */ time_t now = time(NULL); - /* Our certificate lifetime will be cert_lifetime no matter what, but if we - * start cert_lifetime in the past, we'll have 0 real lifetime. instead we - * start up to (cert_lifetime - min_real_lifetime - start_granularity) in - * the past. */ - const time_t min_real_lifetime = 24*3600; - const time_t start_granularity = 24*3600; - time_t earliest_start_time; - /* Don't actually start in the future! */ - if (cert_lifetime <= min_real_lifetime + start_granularity) { - earliest_start_time = now - 1; - } else { - earliest_start_time = now + min_real_lifetime + start_granularity - - cert_lifetime; - } - start_time = crypto_rand_time_range(earliest_start_time, now); - /* Round the start time back to the start of a day. */ - start_time -= start_time % start_granularity; - end_time = start_time + cert_lifetime; + tor_tls_pick_certificate_lifetime(now, cert_lifetime, + &start_time, &end_time); tor_assert(rsa); tor_assert(cname); @@ -410,7 +426,7 @@ tor_tls_cert_is_valid(int severity, /* okay, the signature checked out right. Now let's check the check the * lifetime. */ - if (check_cert_lifetime_internal(severity, cert->cert, now, + if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now, 48*60*60, 30*24*60*60) < 0) goto bad; @@ -509,9 +525,9 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem, * now.) If it is live, return 0. If it is not live, log a message * and return -1. */ int -check_cert_lifetime_internal(int severity, const X509 *cert, - time_t now, - int past_tolerance, int future_tolerance) +tor_x509_check_cert_lifetime_internal(int severity, const X509 *cert, + time_t now, + int past_tolerance, int future_tolerance) { time_t t; diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h index 4dadba06d7..e3dfcf3934 100644 --- a/src/lib/tls/x509.h +++ b/src/lib/tls/x509.h @@ -33,6 +33,11 @@ struct tor_x509_cert_t { }; #endif +void tor_tls_pick_certificate_lifetime(time_t now, + unsigned cert_lifetime, + time_t *start_time_out, + time_t *end_time_out); + MOCK_DECL(tor_x509_cert_impl_t *, tor_tls_create_certificate, (crypto_pk_t *rsa, crypto_pk_t *rsa_sign, @@ -74,9 +79,10 @@ int tor_tls_cert_is_valid(int severity, time_t now, int check_rsa_1024); -int check_cert_lifetime_internal(int severity, - const tor_x509_cert_impl_t *cert, - time_t now, - int past_tolerance, int future_tolerance); +int tor_x509_check_cert_lifetime_internal(int severity, + const tor_x509_cert_impl_t *cert, + time_t now, + int past_tolerance, + int future_tolerance); #endif From 1992c761308538cffea64abecc9e45cbd47b1bda Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 12 Aug 2018 16:04:12 -0400 Subject: [PATCH 45/76] Split tls modules and their tests into openssl and generic. Also, add a stubbed-out nss version of the modules. The tests won't pass with NSS yet since the NSS modules don't do anything. This is a good patch to read with --color-moved. --- src/lib/tls/include.am | 12 + src/lib/tls/tortls.c | 2067 +------------------------ src/lib/tls/tortls.h | 64 +- src/lib/tls/tortls_internal.h | 65 + src/lib/tls/tortls_nss.c | 432 ++++++ src/lib/tls/tortls_openssl.c | 2032 +++++++++++++++++++++++++ src/lib/tls/x509.c | 479 +----- src/lib/tls/x509.h | 4 +- src/lib/tls/x509_nss.c | 122 ++ src/lib/tls/x509_openssl.c | 489 ++++++ src/test/include.am | 8 + src/test/test.c | 3 + src/test/test.h | 1 + src/test/test_tortls.c | 2584 +------------------------------ src/test/test_tortls_openssl.c | 2597 ++++++++++++++++++++++++++++++++ 15 files changed, 5805 insertions(+), 5154 deletions(-) create mode 100644 src/lib/tls/tortls_internal.h create mode 100644 src/lib/tls/tortls_nss.c create mode 100644 src/lib/tls/tortls_openssl.c create mode 100644 src/lib/tls/x509_nss.c create mode 100644 src/lib/tls/x509_openssl.c create mode 100644 src/test/test_tortls_openssl.c diff --git a/src/lib/tls/include.am b/src/lib/tls/include.am index 1fd25a0b33..1731049032 100644 --- a/src/lib/tls/include.am +++ b/src/lib/tls/include.am @@ -10,6 +10,16 @@ src_lib_libtor_tls_a_SOURCES = \ src/lib/tls/tortls.c \ src/lib/tls/x509.c +if USE_NSS +src_lib_libtor_tls_a_SOURCES += \ + src/lib/tls/tortls_nss.c \ + src/lib/tls/x509_nss.c +else +src_lib_libtor_tls_a_SOURCES += \ + src/lib/tls/tortls_openssl.c \ + src/lib/tls/x509_openssl.c +endif + src_lib_libtor_tls_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) src_lib_libtor_tls_testing_a_SOURCES = \ @@ -22,4 +32,6 @@ noinst_HEADERS += \ src/lib/tls/ciphers.inc \ src/lib/tls/buffers_tls.h \ src/lib/tls/tortls.h \ + src/lib/tls/tortls_internal.h \ + src/lib/tls/tortls_st.h \ src/lib/tls/x509.h diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 4c5dedb573..4b35177df9 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -3,146 +3,13 @@ * Copyright (c) 2007-2018, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -/** - * \file tortls.c - * \brief Wrapper functions to present a consistent interface to - * TLS, SSL, and X.509 functions from OpenSSL. - **/ - -/* (Unlike other tor functions, these - * are prefixed with tor_ in order to avoid conflicting with OpenSSL - * functions and variables.) - */ - -#include "orconfig.h" - #define TORTLS_PRIVATE -#define TORTLS_OPENSSL_PRIVATE - -#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include "*/ - #include - #include -#endif - -#include "lib/crypt_ops/crypto_cipher.h" -#include "lib/crypt_ops/crypto_rand.h" -#include "lib/crypt_ops/crypto_dh.h" -#include "lib/crypt_ops/crypto_util.h" -#include "lib/crypt_ops/compat_openssl.h" #include "lib/tls/x509.h" - -/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in - * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ -DISABLE_GCC_WARNING(redundant-decls) - -#include - -#ifdef OPENSSL_NO_EC -#error "We require OpenSSL with ECC support" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -ENABLE_GCC_WARNING(redundant-decls) - #include "lib/tls/tortls.h" #include "lib/tls/tortls_st.h" -#include "lib/log/log.h" +#include "lib/tls/tortls_internal.h" #include "lib/log/util_bug.h" -#include "lib/container/smartlist.h" -#include "lib/string/compat_string.h" -#include "lib/string/printf.h" -#include "lib/net/socket.h" #include "lib/intmath/cmp.h" -#include "lib/ctime/di_ops.h" -#include "lib/encoding/time_fmt.h" - -#include -#include - -#include "lib/arch/bytes.h" - -/* Copied from or.h */ -#define LEGAL_NICKNAME_CHARACTERS \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - -/** How long do identity certificates live? (sec) */ -#define IDENTITY_CERT_LIFETIME (365*24*60*60) - -#define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer") - -#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') -/* This is a version of OpenSSL before 1.0.0f. It does not have - * the CVE-2011-4576 fix, and as such it can't use RELEASE_BUFFERS and - * SSL3 safely at the same time. - */ -#define DISABLE_SSL3_HANDSHAKE -#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') */ - -/* We redefine these so that we can run correctly even if the vendor gives us - * a version of OpenSSL that does not match its header files. (Apple: I am - * looking at you.) - */ -#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION -#define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x00040000L -#endif -#ifndef SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION -#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010 -#endif - -/** Return values for tor_tls_classify_client_ciphers. - * - * @{ - */ -/** An error occurred when examining the client ciphers */ -#define CIPHERS_ERR -1 -/** The client cipher list indicates that a v1 handshake was in use. */ -#define CIPHERS_V1 1 -/** The client cipher list indicates that the client is using the v2 or the - * v3 handshake, but that it is (probably!) lying about what ciphers it - * supports */ -#define CIPHERS_V2 2 -/** The client cipher list indicates that the client is using the v2 or the - * v3 handshake, and that it is telling the truth about what ciphers it - * supports */ -#define CIPHERS_UNRESTRICTED 3 -/** @} */ - -/** The ex_data index in which we store a pointer to an SSL object's - * corresponding tor_tls_t object. */ -STATIC int tor_tls_object_ex_data_index = -1; - -/** Helper: Allocate tor_tls_object_ex_data_index. */ -STATIC void -tor_tls_allocate_tor_tls_object_ex_data_index(void) -{ - if (tor_tls_object_ex_data_index == -1) { - tor_tls_object_ex_data_index = - SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - tor_assert(tor_tls_object_ex_data_index != -1); - } -} - -/** Helper: given a SSL* pointer, return the tor_tls_t object using that - * pointer. */ -STATIC tor_tls_t * -tor_tls_get_by_ssl(const SSL *ssl) -{ - tor_tls_t *result = SSL_get_ex_data(ssl, tor_tls_object_ex_data_index); - if (result) - tor_assert(result->magic == TOR_TLS_MAGIC); - return result; -} - -static void tor_tls_context_decref(tor_tls_context_t *ctx); -static void tor_tls_context_incref(tor_tls_context_t *ctx); /** Global TLS contexts. We keep them here because nobody else needs * to touch them. @@ -152,126 +19,37 @@ STATIC tor_tls_context_t *server_tls_context = NULL; STATIC tor_tls_context_t *client_tls_context = NULL; /**@}*/ -/** True iff tor_tls_init() has been called. */ -static int tls_library_is_initialized = 0; - -/* Module-internal error codes. */ -#define TOR_TLS_SYSCALL_ (MIN_TOR_TLS_ERROR_VAL_ - 2) -#define TOR_TLS_ZERORETURN_ (MIN_TOR_TLS_ERROR_VAL_ - 1) - -/** Write a description of the current state of tls into the - * sz-byte buffer at buf. */ -void -tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz) -{ - const char *ssl_state; - const char *tortls_state; - - if (PREDICT_UNLIKELY(!tls || !tls->ssl)) { - strlcpy(buf, "(No SSL object)", sz); - return; - } - - ssl_state = SSL_state_string_long(tls->ssl); - switch (tls->state) { -#define CASE(st) case TOR_TLS_ST_##st: tortls_state = " in "#st ; break - CASE(HANDSHAKE); - CASE(OPEN); - CASE(GOTCLOSE); - CASE(SENTCLOSE); - CASE(CLOSED); - CASE(RENEGOTIATE); -#undef CASE - case TOR_TLS_ST_BUFFEREVENT: - tortls_state = ""; - break; - default: - tortls_state = " in unknown TLS state"; - break; - } - - tor_snprintf(buf, sz, "%s%s", ssl_state, tortls_state); -} - -/** Log a single error err as returned by ERR_get_error(), which was - * received while performing an operation doing on tls. Log - * the message at severity, in log domain domain. */ -void -tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, - int severity, int domain, const char *doing) -{ - const char *state = NULL, *addr; - const char *msg, *lib, *func; - - state = (tls && tls->ssl)?SSL_state_string_long(tls->ssl):"---"; - - addr = tls ? tls->address : NULL; - - /* Some errors are known-benign, meaning they are the fault of the other - * side of the connection. The caller doesn't know this, so override the - * priority for those cases. */ - switch (ERR_GET_REASON(err)) { - case SSL_R_HTTP_REQUEST: - case SSL_R_HTTPS_PROXY_REQUEST: - case SSL_R_RECORD_LENGTH_MISMATCH: -#ifndef OPENSSL_1_1_API - case SSL_R_RECORD_TOO_LARGE: -#endif - case SSL_R_UNKNOWN_PROTOCOL: - case SSL_R_UNSUPPORTED_PROTOCOL: - severity = LOG_INFO; - break; - default: - break; - } - - msg = (const char*)ERR_reason_error_string(err); - lib = (const char*)ERR_lib_error_string(err); - func = (const char*)ERR_func_error_string(err); - if (!msg) msg = "(null)"; - if (!lib) lib = "(null)"; - if (!func) func = "(null)"; - if (doing) { - tor_log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)", - doing, addr?" with ":"", addr?addr:"", - msg, lib, func, state); - } else { - tor_log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)", - addr?" with ":"", addr?addr:"", - msg, lib, func, state); - } -} - -/** Log all pending tls errors at level severity in log domain - * domain. Use doing to describe our current activities. +/** + * Return the appropriate TLS context. */ -void -tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) +tor_tls_context_t * +tor_tls_context_get(int is_server) { - unsigned long err; - - while ((err = ERR_get_error()) != 0) { - tor_tls_log_one_error(tls, err, severity, domain, doing); - } + return is_server ? server_tls_context : client_tls_context; } -/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error - * code. */ -STATIC int -tor_errno_to_tls_error(int e) +/** Increase the reference count of ctx. */ +void +tor_tls_context_incref(tor_tls_context_t *ctx) { - switch (e) { - case SOCK_ERRNO(ECONNRESET): // most common - return TOR_TLS_ERROR_CONNRESET; - case SOCK_ERRNO(ETIMEDOUT): - return TOR_TLS_ERROR_TIMEOUT; - case SOCK_ERRNO(EHOSTUNREACH): - case SOCK_ERRNO(ENETUNREACH): - return TOR_TLS_ERROR_NO_ROUTE; - case SOCK_ERRNO(ECONNREFUSED): - return TOR_TLS_ERROR_CONNREFUSED; // least common - default: - return TOR_TLS_ERROR_MISC; + ++ctx->refcnt; +} + +/** Free all global TLS structures. */ +void +tor_tls_free_all(void) +{ + check_no_tls_errors(); + + if (server_tls_context) { + tor_tls_context_t *ctx = server_tls_context; + server_tls_context = NULL; + tor_tls_context_decref(ctx); + } + if (client_tls_context) { + tor_tls_context_t *ctx = client_tls_context; + client_tls_context = NULL; + tor_tls_context_decref(ctx); } } @@ -295,331 +73,6 @@ tor_tls_err_to_string(int err) } } -#define CATCH_SYSCALL 1 -#define CATCH_ZERO 2 - -/** Given a TLS object and the result of an SSL_* call, use - * SSL_get_error to determine whether an error has occurred, and if so - * which one. Return one of TOR_TLS_{DONE|WANTREAD|WANTWRITE|ERROR}. - * If extra&CATCH_SYSCALL is true, return TOR_TLS_SYSCALL_ instead of - * reporting syscall errors. If extra&CATCH_ZERO is true, return - * TOR_TLS_ZERORETURN_ instead of reporting zero-return errors. - * - * If an error has occurred, log it at level severity and describe the - * current action as doing. - */ -STATIC int -tor_tls_get_error(tor_tls_t *tls, int r, int extra, - const char *doing, int severity, int domain) -{ - int err = SSL_get_error(tls->ssl, r); - int tor_error = TOR_TLS_ERROR_MISC; - switch (err) { - case SSL_ERROR_NONE: - return TOR_TLS_DONE; - case SSL_ERROR_WANT_READ: - return TOR_TLS_WANTREAD; - case SSL_ERROR_WANT_WRITE: - return TOR_TLS_WANTWRITE; - case SSL_ERROR_SYSCALL: - if (extra&CATCH_SYSCALL) - return TOR_TLS_SYSCALL_; - if (r == 0) { - tor_log(severity, LD_NET, "TLS error: unexpected close while %s (%s)", - doing, SSL_state_string_long(tls->ssl)); - tor_error = TOR_TLS_ERROR_IO; - } else { - int e = tor_socket_errno(tls->socket); - tor_log(severity, LD_NET, - "TLS error: (errno=%d: %s; state=%s)", - doing, e, tor_socket_strerror(e), - SSL_state_string_long(tls->ssl)); - tor_error = tor_errno_to_tls_error(e); - } - tls_log_errors(tls, severity, domain, doing); - return tor_error; - case SSL_ERROR_ZERO_RETURN: - if (extra&CATCH_ZERO) - return TOR_TLS_ZERORETURN_; - tor_log(severity, LD_NET, "TLS connection closed while %s in state %s", - doing, SSL_state_string_long(tls->ssl)); - tls_log_errors(tls, severity, domain, doing); - return TOR_TLS_CLOSE; - default: - tls_log_errors(tls, severity, domain, doing); - return TOR_TLS_ERROR_MISC; - } -} - -/** Initialize OpenSSL, unless it has already been initialized. - */ -void -tor_tls_init(void) -{ - check_no_tls_errors(); - - if (!tls_library_is_initialized) { -#ifdef OPENSSL_1_1_API - OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); -#else - SSL_library_init(); - SSL_load_error_strings(); -#endif - -#if (SIZEOF_VOID_P >= 8 && \ - OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1)) - long version = OpenSSL_version_num(); - - /* LCOV_EXCL_START : we can't test these lines on the same machine */ - if (version >= OPENSSL_V_SERIES(1,0,1)) { - /* Warn if we could *almost* be running with much faster ECDH. - If we're built for a 64-bit target, using OpenSSL 1.0.1, but we - don't have one of the built-in __uint128-based speedups, we are - just one build operation away from an accelerated handshake. - - (We could be looking at OPENSSL_NO_EC_NISTP_64_GCC_128 instead of - doing this test, but that gives compile-time options, not runtime - behavior.) - */ - EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - const EC_GROUP *g = key ? EC_KEY_get0_group(key) : NULL; - const EC_METHOD *m = g ? EC_GROUP_method_of(g) : NULL; - const int warn = (m == EC_GFp_simple_method() || - m == EC_GFp_mont_method() || - m == EC_GFp_nist_method()); - EC_KEY_free(key); - - if (warn) - log_notice(LD_GENERAL, "We were built to run on a 64-bit CPU, with " - "OpenSSL 1.0.1 or later, but with a version of OpenSSL " - "that apparently lacks accelerated support for the NIST " - "P-224 and P-256 groups. Building openssl with such " - "support (using the enable-ec_nistp_64_gcc_128 option " - "when configuring it) would make ECDH much faster."); - } - /* LCOV_EXCL_STOP */ -#endif /* (SIZEOF_VOID_P >= 8 && ... */ - - tor_tls_allocate_tor_tls_object_ex_data_index(); - - tls_library_is_initialized = 1; - } -} - -/** Free all global TLS structures. */ -void -tor_tls_free_all(void) -{ - check_no_tls_errors(); - - if (server_tls_context) { - tor_tls_context_t *ctx = server_tls_context; - server_tls_context = NULL; - tor_tls_context_decref(ctx); - } - if (client_tls_context) { - tor_tls_context_t *ctx = client_tls_context; - client_tls_context = NULL; - tor_tls_context_decref(ctx); - } -} - -/** We need to give OpenSSL a callback to verify certificates. This is - * it: We always accept peer certs and complete the handshake. We - * don't validate them until later. - */ -STATIC int -always_accept_verify_cb(int preverify_ok, - X509_STORE_CTX *x509_ctx) -{ - (void) preverify_ok; - (void) x509_ctx; - return 1; -} - -/** List of ciphers that servers should select from when the client might be - * claiming extra unsupported ciphers in order to avoid fingerprinting. */ -static const char SERVER_CIPHER_LIST[] = -#ifdef TLS1_3_TXT_AES_128_GCM_SHA256 - /* This one can never actually get selected, since if the client lists it, - * we will assume that the client is honest, and not use this list. - * Nonetheless we list it if it's available, so that the server doesn't - * conclude that it has no valid ciphers if it's running with TLS1.3. - */ - TLS1_3_TXT_AES_128_GCM_SHA256 ":" -#endif - TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" - TLS1_TXT_DHE_RSA_WITH_AES_128_SHA; - -/** List of ciphers that servers should select from when we actually have - * our choice of what cipher to use. */ -static const char UNRESTRICTED_SERVER_CIPHER_LIST[] = - /* Here are the TLS 1.3 ciphers we like, in the order we prefer. */ -#ifdef TLS1_3_TXT_AES_256_GCM_SHA384 - TLS1_3_TXT_AES_256_GCM_SHA384 ":" -#endif -#ifdef TLS1_3_TXT_CHACHA20_POLY1305_SHA256 - TLS1_3_TXT_CHACHA20_POLY1305_SHA256 ":" -#endif -#ifdef TLS1_3_TXT_AES_128_GCM_SHA256 - TLS1_3_TXT_AES_128_GCM_SHA256 ":" -#endif -#ifdef TLS1_3_TXT_AES_128_CCM_SHA256 - TLS1_3_TXT_AES_128_CCM_SHA256 ":" -#endif - - /* This list is autogenerated with the gen_server_ciphers.py script; - * don't hand-edit it. */ -#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ":" -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ":" -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 - TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 ":" -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 - TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 ":" -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA - TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":" -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA - TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":" -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 - TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 ":" -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 - TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 ":" -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_CCM - TLS1_TXT_DHE_RSA_WITH_AES_256_CCM ":" -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_CCM - TLS1_TXT_DHE_RSA_WITH_AES_128_CCM ":" -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 - TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 ":" -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 - TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 ":" -#endif - /* Required */ - TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" - /* Required */ - TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" -#ifdef TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 - TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 ":" -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305 - TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305 -#endif - ; - -/* Note: to set up your own private testing network with link crypto - * disabled, set your Tors' cipher list to - * (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate - * with any of the "real" Tors, though. */ - -#define CIPHER(id, name) name ":" -#define XCIPHER(id, name) -/** List of ciphers that clients should advertise, omitting items that - * our OpenSSL doesn't know about. */ -static const char CLIENT_CIPHER_LIST[] = -#include "ciphers.inc" - /* Tell it not to use SSLv2 ciphers, so that it can select an SSLv3 version - * of any cipher we say. */ - "!SSLv2" - ; -#undef CIPHER -#undef XCIPHER - -/** Remove a reference to ctx, and free it if it has no more - * references. */ -static void -tor_tls_context_decref(tor_tls_context_t *ctx) -{ - tor_assert(ctx); - if (--ctx->refcnt == 0) { - SSL_CTX_free(ctx->ctx); - tor_x509_cert_free(ctx->my_link_cert); - tor_x509_cert_free(ctx->my_id_cert); - tor_x509_cert_free(ctx->my_auth_cert); - crypto_pk_free(ctx->link_key); - crypto_pk_free(ctx->auth_key); - /* LCOV_EXCL_BR_START since ctx will never be NULL here */ - tor_free(ctx); - /* LCOV_EXCL_BR_STOP */ - } -} - -/** Set *link_cert_out and *id_cert_out to the link certificate - * and ID certificate that we're currently using for our V3 in-protocol - * handshake's certificate chain. If server is true, provide the certs - * that we use in server mode (auth, ID); otherwise, provide the certs that we - * use in client mode. (link, ID) */ -int -tor_tls_get_my_certs(int server, - const tor_x509_cert_t **link_cert_out, - const tor_x509_cert_t **id_cert_out) -{ - tor_tls_context_t *ctx = server ? server_tls_context : client_tls_context; - if (! ctx) - return -1; - if (link_cert_out) - *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert; - if (id_cert_out) - *id_cert_out = ctx->my_id_cert; - return 0; -} - -/** - * Return the authentication key that we use to authenticate ourselves as a - * client in the V3 in-protocol handshake. - */ -crypto_pk_t * -tor_tls_get_my_client_auth_key(void) -{ - if (! client_tls_context) - return NULL; - return client_tls_context->auth_key; -} - -/** Return true iff the other side of tls has authenticated to us, and - * the key certified in cert is the same as the key they used to do it. - */ -MOCK_IMPL(int, -tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert)) -{ - X509 *peercert = SSL_get_peer_certificate(tls->ssl); - EVP_PKEY *link_key = NULL, *cert_key = NULL; - int result; - - if (!peercert) - return 0; - link_key = X509_get_pubkey(peercert); - cert_key = X509_get_pubkey((X509 *)tor_x509_cert_get_impl(cert)); - - result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1; - - X509_free(peercert); - if (link_key) - EVP_PKEY_free(link_key); - if (cert_key) - EVP_PKEY_free(cert_key); - - return result; -} - -/** Increase the reference count of ctx. */ -static void -tor_tls_context_incref(tor_tls_context_t *ctx) -{ - ++ctx->refcnt; -} - /** Create new global client and server TLS contexts. * * If server_identity is NULL, this will not generate a server @@ -686,687 +139,6 @@ tor_tls_context_init(unsigned flags, return MIN(rv1, rv2); } -/** Create a new global TLS context. - * - * You can call this function multiple times. Each time you call it, - * it generates new certificates; all new connections will use - * the new SSL context. - */ -STATIC int -tor_tls_context_init_one(tor_tls_context_t **ppcontext, - crypto_pk_t *identity, - unsigned int key_lifetime, - unsigned int flags, - int is_client) -{ - tor_tls_context_t *new_ctx = tor_tls_context_new(identity, - key_lifetime, - flags, - is_client); - tor_tls_context_t *old_ctx = *ppcontext; - - if (new_ctx != NULL) { - *ppcontext = new_ctx; - - /* Free the old context if one existed. */ - if (old_ctx != NULL) { - /* This is safe even if there are open connections: we reference- - * count tor_tls_context_t objects. */ - tor_tls_context_decref(old_ctx); - } - } - - return ((new_ctx != NULL) ? 0 : -1); -} - -/** The group we should use for ecdhe when none was selected. */ -#define NID_tor_default_ecdhe_group NID_X9_62_prime256v1 - -#define RSA_LINK_KEY_BITS 2048 - -/** Create a new TLS context for use with Tor TLS handshakes. - * identity should be set to the identity key used to sign the - * certificate. - */ -STATIC tor_tls_context_t * -tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, - unsigned flags, int is_client) -{ - crypto_pk_t *rsa = NULL, *rsa_auth = NULL; - EVP_PKEY *pkey = NULL; - tor_tls_context_t *result = NULL; - X509 *cert = NULL, *idcert = NULL, *authcert = NULL; - char *nickname = NULL, *nn2 = NULL; - - tor_tls_init(); - nickname = crypto_random_hostname(8, 20, "www.", ".net"); -#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE - nn2 = crypto_random_hostname(8, 20, "www.", ".net"); -#else - nn2 = crypto_random_hostname(8, 20, "www.", ".com"); -#endif - - /* Generate short-term RSA key for use with TLS. */ - if (!(rsa = crypto_pk_new())) - goto error; - if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0) - goto error; - if (!is_client) { - /* Generate short-term RSA key for use in the in-protocol ("v3") - * authentication handshake. */ - if (!(rsa_auth = crypto_pk_new())) - goto error; - if (crypto_pk_generate_key(rsa_auth)<0) - goto error; - /* Create a link certificate signed by identity key. */ - cert = tor_tls_create_certificate(rsa, identity, nickname, nn2, - key_lifetime); - /* Create self-signed certificate for identity key. */ - idcert = tor_tls_create_certificate(identity, identity, nn2, nn2, - IDENTITY_CERT_LIFETIME); - /* Create an authentication certificate signed by identity key. */ - authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2, - key_lifetime); - if (!cert || !idcert || !authcert) { - log_warn(LD_CRYPTO, "Error creating certificate"); - goto error; - } - } - - result = tor_malloc_zero(sizeof(tor_tls_context_t)); - result->refcnt = 1; - if (!is_client) { - result->my_link_cert = tor_x509_cert_new(X509_dup(cert)); - result->my_id_cert = tor_x509_cert_new(X509_dup(idcert)); - result->my_auth_cert = tor_x509_cert_new(X509_dup(authcert)); - if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert) - goto error; - result->link_key = crypto_pk_dup_key(rsa); - result->auth_key = crypto_pk_dup_key(rsa_auth); - } - -#if 0 - /* Tell OpenSSL to only use TLS1. This may have subtly different results - * from SSLv23_method() with SSLv2 and SSLv3 disabled, so we need to do some - * investigation before we consider adjusting it. It should be compatible - * with existing Tors. */ - if (!(result->ctx = SSL_CTX_new(TLSv1_method()))) - goto error; -#endif /* 0 */ - - /* Tell OpenSSL to use TLS 1.0 or later but not SSL2 or SSL3. */ -#ifdef HAVE_TLS_METHOD - if (!(result->ctx = SSL_CTX_new(TLS_method()))) - goto error; -#else - if (!(result->ctx = SSL_CTX_new(SSLv23_method()))) - goto error; -#endif /* defined(HAVE_TLS_METHOD) */ - SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2); - SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3); - - /* Prefer the server's ordering of ciphers: the client's ordering has - * historically been chosen for fingerprinting resistance. */ - SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - - /* Disable TLS tickets if they're supported. We never want to use them; - * using them can make our perfect forward secrecy a little worse, *and* - * create an opportunity to fingerprint us (since it's unusual to use them - * with TLS sessions turned off). - * - * In 0.2.4, clients advertise support for them though, to avoid a TLS - * distinguishability vector. This can give us worse PFS, though, if we - * get a server that doesn't set SSL_OP_NO_TICKET. With luck, there will - * be few such servers by the time 0.2.4 is more stable. - */ -#ifdef SSL_OP_NO_TICKET - if (! is_client) { - SSL_CTX_set_options(result->ctx, SSL_OP_NO_TICKET); - } -#endif - - SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE); - SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE); - -#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION - SSL_CTX_set_options(result->ctx, - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); -#endif - /* Yes, we know what we are doing here. No, we do not treat a renegotiation - * as authenticating any earlier-received data. - */ - { - SSL_CTX_set_options(result->ctx, - SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); - } - - /* Don't actually allow compression; it uses RAM and time, it makes TLS - * vulnerable to CRIME-style attacks, and most of the data we transmit over - * TLS is encrypted (and therefore uncompressible) anyway. */ -#ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(result->ctx, SSL_OP_NO_COMPRESSION); -#endif -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) -#ifndef OPENSSL_NO_COMP - if (result->ctx->comp_methods) - result->ctx->comp_methods = NULL; -#endif -#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) */ - -#ifdef SSL_MODE_RELEASE_BUFFERS - SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS); -#endif - if (! is_client) { - if (cert && !SSL_CTX_use_certificate(result->ctx,cert)) - goto error; - X509_free(cert); /* We just added a reference to cert. */ - cert=NULL; - if (idcert) { - X509_STORE *s = SSL_CTX_get_cert_store(result->ctx); - tor_assert(s); - X509_STORE_add_cert(s, idcert); - X509_free(idcert); /* The context now owns the reference to idcert */ - idcert = NULL; - } - } - SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); - if (!is_client) { - tor_assert(rsa); - if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,1))) - goto error; - if (!SSL_CTX_use_PrivateKey(result->ctx, pkey)) - goto error; - EVP_PKEY_free(pkey); - pkey = NULL; - if (!SSL_CTX_check_private_key(result->ctx)) - goto error; - } - { - DH *dh = crypto_dh_new_openssl_tls(); - tor_assert(dh); - SSL_CTX_set_tmp_dh(result->ctx, dh); - DH_free(dh); - } - if (! is_client) { - int nid; - EC_KEY *ec_key; - if (flags & TOR_TLS_CTX_USE_ECDHE_P224) - nid = NID_secp224r1; - else if (flags & TOR_TLS_CTX_USE_ECDHE_P256) - nid = NID_X9_62_prime256v1; - else - nid = NID_tor_default_ecdhe_group; - /* Use P-256 for ECDHE. */ - ec_key = EC_KEY_new_by_curve_name(nid); - if (ec_key != NULL) /*XXXX Handle errors? */ - SSL_CTX_set_tmp_ecdh(result->ctx, ec_key); - EC_KEY_free(ec_key); - } - SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER, - always_accept_verify_cb); - /* let us realloc bufs that we're writing from */ - SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - if (rsa) - crypto_pk_free(rsa); - if (rsa_auth) - crypto_pk_free(rsa_auth); - X509_free(authcert); - tor_free(nickname); - tor_free(nn2); - return result; - - error: - tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context"); - tor_free(nickname); - tor_free(nn2); - if (pkey) - EVP_PKEY_free(pkey); - if (rsa) - crypto_pk_free(rsa); - if (rsa_auth) - crypto_pk_free(rsa_auth); - if (result) - tor_tls_context_decref(result); - if (cert) - X509_free(cert); - if (idcert) - X509_free(idcert); - if (authcert) - X509_free(authcert); - return NULL; -} - -/** Invoked when a TLS state changes: log the change at severity 'debug' */ -STATIC void -tor_tls_debug_state_callback(const SSL *ssl, int type, int val) -{ - /* LCOV_EXCL_START since this depends on whether debug is captured or not */ - log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", - ssl, SSL_state_string_long(ssl), type, val); - /* LCOV_EXCL_STOP */ -} - -/* Return the name of the negotiated ciphersuite in use on tls */ -const char * -tor_tls_get_ciphersuite_name(tor_tls_t *tls) -{ - return SSL_get_cipher(tls->ssl); -} - -/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to - * 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers - * that it claims to support. We'll prune this list to remove the ciphers - * *we* don't recognize. */ -STATIC uint16_t v2_cipher_list[] = { - 0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */ - 0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */ - 0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */ - 0x0038, /* TLS1_TXT_DHE_DSS_WITH_AES_256_SHA */ - 0xc00f, /* TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA */ - 0xc005, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA */ - 0x0035, /* TLS1_TXT_RSA_WITH_AES_256_SHA */ - 0xc007, /* TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA */ - 0xc009, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA */ - 0xc011, /* TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA */ - 0xc013, /* TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA */ - 0x0033, /* TLS1_TXT_DHE_RSA_WITH_AES_128_SHA */ - 0x0032, /* TLS1_TXT_DHE_DSS_WITH_AES_128_SHA */ - 0xc00c, /* TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA */ - 0xc00e, /* TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA */ - 0xc002, /* TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA */ - 0xc004, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA */ - 0x0004, /* SSL3_TXT_RSA_RC4_128_MD5 */ - 0x0005, /* SSL3_TXT_RSA_RC4_128_SHA */ - 0x002f, /* TLS1_TXT_RSA_WITH_AES_128_SHA */ - 0xc008, /* TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA */ - 0xc012, /* TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA */ - 0x0016, /* SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA */ - 0x0013, /* SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA */ - 0xc00d, /* TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA */ - 0xc003, /* TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA */ - 0xfeff, /* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA */ - 0x000a, /* SSL3_TXT_RSA_DES_192_CBC3_SHA */ - 0 -}; -/** Have we removed the unrecognized ciphers from v2_cipher_list yet? */ -static int v2_cipher_list_pruned = 0; - -/** Return 0 if m does not support the cipher with ID cipher; - * return 1 if it does support it, or if we have no way to tell. */ -STATIC int -find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher) -{ - const SSL_CIPHER *c; -#ifdef HAVE_SSL_CIPHER_FIND - (void) m; - { - unsigned char cipherid[3]; - tor_assert(ssl); - set_uint16(cipherid, tor_htons(cipher)); - cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting - * with a two-byte 'cipherid', it may look for a v2 - * cipher with the appropriate 3 bytes. */ - c = SSL_CIPHER_find((SSL*)ssl, cipherid); - if (c) - tor_assert((SSL_CIPHER_get_id(c) & 0xffff) == cipher); - return c != NULL; - } -#else /* !(defined(HAVE_SSL_CIPHER_FIND)) */ - -# if defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) - if (m && m->get_cipher_by_char) { - unsigned char cipherid[3]; - set_uint16(cipherid, tor_htons(cipher)); - cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting - * with a two-byte 'cipherid', it may look for a v2 - * cipher with the appropriate 3 bytes. */ - c = m->get_cipher_by_char(cipherid); - if (c) - tor_assert((c->id & 0xffff) == cipher); - return c != NULL; - } -#endif /* defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) */ -# ifndef OPENSSL_1_1_API - if (m && m->get_cipher && m->num_ciphers) { - /* It would seem that some of the "let's-clean-up-openssl" forks have - * removed the get_cipher_by_char function. Okay, so now you get a - * quadratic search. - */ - int i; - for (i = 0; i < m->num_ciphers(); ++i) { - c = m->get_cipher(i); - if (c && (c->id & 0xffff) == cipher) { - return 1; - } - } - return 0; - } -#endif /* !defined(OPENSSL_1_1_API) */ - (void) ssl; - (void) m; - (void) cipher; - return 1; /* No way to search */ -#endif /* defined(HAVE_SSL_CIPHER_FIND) */ -} - -/** Remove from v2_cipher_list every cipher that we don't support, so that - * comparing v2_cipher_list to a client's cipher list will give a sensible - * result. */ -static void -prune_v2_cipher_list(const SSL *ssl) -{ - uint16_t *inp, *outp; -#ifdef HAVE_TLS_METHOD - const SSL_METHOD *m = TLS_method(); -#else - const SSL_METHOD *m = SSLv23_method(); -#endif - - inp = outp = v2_cipher_list; - while (*inp) { - if (find_cipher_by_id(ssl, m, *inp)) { - *outp++ = *inp++; - } else { - inp++; - } - } - *outp = 0; - - v2_cipher_list_pruned = 1; -} - -/** Examine the client cipher list in ssl, and determine what kind of - * client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2, - * CIPHERS_UNRESTRICTED. - **/ -STATIC int -tor_tls_classify_client_ciphers(const SSL *ssl, - STACK_OF(SSL_CIPHER) *peer_ciphers) -{ - int i, res; - tor_tls_t *tor_tls; - if (PREDICT_UNLIKELY(!v2_cipher_list_pruned)) - prune_v2_cipher_list(ssl); - - tor_tls = tor_tls_get_by_ssl(ssl); - if (tor_tls && tor_tls->client_cipher_list_type) - return tor_tls->client_cipher_list_type; - - /* If we reached this point, we just got a client hello. See if there is - * a cipher list. */ - if (!peer_ciphers) { - log_info(LD_NET, "No ciphers on session"); - res = CIPHERS_ERR; - goto done; - } - /* Now we need to see if there are any ciphers whose presence means we're - * dealing with an updated Tor. */ - for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { - const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); - const char *ciphername = SSL_CIPHER_get_name(cipher); - if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) && - strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) && - strcmp(ciphername, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) && - strcmp(ciphername, "(NONE)")) { - log_debug(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername); - // return 1; - goto v2_or_higher; - } - } - res = CIPHERS_V1; - goto done; - v2_or_higher: - { - const uint16_t *v2_cipher = v2_cipher_list; - for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { - const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); - uint16_t id = SSL_CIPHER_get_id(cipher) & 0xffff; - if (id == 0x00ff) /* extended renegotiation indicator. */ - continue; - if (!id || id != *v2_cipher) { - res = CIPHERS_UNRESTRICTED; - goto dump_ciphers; - } - ++v2_cipher; - } - if (*v2_cipher != 0) { - res = CIPHERS_UNRESTRICTED; - goto dump_ciphers; - } - res = CIPHERS_V2; - } - - dump_ciphers: - { - smartlist_t *elts = smartlist_new(); - char *s; - for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { - const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); - const char *ciphername = SSL_CIPHER_get_name(cipher); - smartlist_add(elts, (char*)ciphername); - } - s = smartlist_join_strings(elts, ":", 0, NULL); - log_debug(LD_NET, "Got a %s V2/V3 cipher list from %s. It is: '%s'", - (res == CIPHERS_V2) ? "fictitious" : "real", ADDR(tor_tls), s); - tor_free(s); - smartlist_free(elts); - } - done: - if (tor_tls) - return tor_tls->client_cipher_list_type = res; - - return res; -} - -/** Return true iff the cipher list suggested by the client for ssl is - * a list that indicates that the client knows how to do the v2 TLS connection - * handshake. */ -STATIC int -tor_tls_client_is_using_v2_ciphers(const SSL *ssl) -{ - STACK_OF(SSL_CIPHER) *ciphers; -#ifdef HAVE_SSL_GET_CLIENT_CIPHERS - ciphers = SSL_get_client_ciphers(ssl); -#else - SSL_SESSION *session; - if (!(session = SSL_get_session((SSL *)ssl))) { - log_info(LD_NET, "No session on TLS?"); - return CIPHERS_ERR; - } - ciphers = session->ciphers; -#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */ - - return tor_tls_classify_client_ciphers(ssl, ciphers) >= CIPHERS_V2; -} - -/** Invoked when we're accepting a connection on ssl, and the connection - * changes state. We use this: - *
  • To alter the state of the handshake partway through, so we - * do not send or request extra certificates in v2 handshakes.
  • - *
  • To detect renegotiation
- */ -STATIC void -tor_tls_server_info_callback(const SSL *ssl, int type, int val) -{ - tor_tls_t *tls; - (void) val; - - IF_BUG_ONCE(ssl == NULL) { - return; // LCOV_EXCL_LINE - } - - tor_tls_debug_state_callback(ssl, type, val); - - if (type != SSL_CB_ACCEPT_LOOP) - return; - - OSSL_HANDSHAKE_STATE ssl_state = SSL_get_state(ssl); - if (! STATE_IS_SW_SERVER_HELLO(ssl_state)) - return; - tls = tor_tls_get_by_ssl(ssl); - if (tls) { - /* Check whether we're watching for renegotiates. If so, this is one! */ - if (tls->negotiated_callback) - tls->got_renegotiate = 1; - if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/ - ++tls->server_handshake_count; - } else { - log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); - return; - } - - /* Now check the cipher list. */ - if (tor_tls_client_is_using_v2_ciphers(ssl)) { - if (tls->wasV2Handshake) - return; /* We already turned this stuff off for the first handshake; - * This is a renegotiation. */ - - /* Yes, we're casting away the const from ssl. This is very naughty of us. - * Let's hope openssl doesn't notice! */ - - /* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */ - SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN); - /* Don't send a hello request. */ - SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL); - - if (tls) { - tls->wasV2Handshake = 1; - } else { - /* LCOV_EXCL_START this line is not reachable */ - log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); - /* LCOV_EXCL_STOP */ - } - } -} - -/** Callback to get invoked on a server after we've read the list of ciphers - * the client supports, but before we pick our own ciphersuite. - * - * We can't abuse an info_cb for this, since by the time one of the - * client_hello info_cbs is called, we've already picked which ciphersuite to - * use. - * - * Technically, this function is an abuse of this callback, since the point of - * a session_secret_cb is to try to set up and/or verify a shared-secret for - * authentication on the fly. But as long as we return 0, we won't actually be - * setting up a shared secret, and all will be fine. - */ -STATIC int -tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len, - STACK_OF(SSL_CIPHER) *peer_ciphers, - CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher, - void *arg) -{ - (void) secret; - (void) secret_len; - (void) peer_ciphers; - (void) cipher; - (void) arg; - - if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) == - CIPHERS_UNRESTRICTED) { - SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST); - } - - SSL_set_session_secret_cb(ssl, NULL, NULL); - - return 0; -} -static void -tor_tls_setup_session_secret_cb(tor_tls_t *tls) -{ - SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL); -} - -/** Create a new TLS object from a file descriptor, and a flag to - * determine whether it is functioning as a server. - */ -tor_tls_t * -tor_tls_new(int sock, int isServer) -{ - BIO *bio = NULL; - tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t)); - tor_tls_context_t *context = isServer ? server_tls_context : - client_tls_context; - result->magic = TOR_TLS_MAGIC; - - check_no_tls_errors(); - tor_assert(context); /* make sure somebody made it first */ - if (!(result->ssl = SSL_new(context->ctx))) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "creating SSL object"); - tor_free(result); - goto err; - } - -#ifdef SSL_set_tlsext_host_name - /* Browsers use the TLS hostname extension, so we should too. */ - if (!isServer) { - char *fake_hostname = crypto_random_hostname(4,25, "www.",".com"); - SSL_set_tlsext_host_name(result->ssl, fake_hostname); - tor_free(fake_hostname); - } -#endif /* defined(SSL_set_tlsext_host_name) */ - - if (!SSL_set_cipher_list(result->ssl, - isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "setting ciphers"); -#ifdef SSL_set_tlsext_host_name - SSL_set_tlsext_host_name(result->ssl, NULL); -#endif - SSL_free(result->ssl); - tor_free(result); - goto err; - } - result->socket = sock; - bio = BIO_new_socket(sock, BIO_NOCLOSE); - if (! bio) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "opening BIO"); -#ifdef SSL_set_tlsext_host_name - SSL_set_tlsext_host_name(result->ssl, NULL); -#endif - SSL_free(result->ssl); - tor_free(result); - goto err; - } - { - int set_worked = - SSL_set_ex_data(result->ssl, tor_tls_object_ex_data_index, result); - if (!set_worked) { - log_warn(LD_BUG, - "Couldn't set the tls for an SSL*; connection will fail"); - } - } - SSL_set_bio(result->ssl, bio, bio); - tor_tls_context_incref(context); - result->context = context; - result->state = TOR_TLS_ST_HANDSHAKE; - result->isServer = isServer; - result->wantwrite_n = 0; - result->last_write_count = (unsigned long) BIO_number_written(bio); - result->last_read_count = (unsigned long) BIO_number_read(bio); - if (result->last_write_count || result->last_read_count) { - log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu", - result->last_read_count, result->last_write_count); - } - if (isServer) { - SSL_set_info_callback(result->ssl, tor_tls_server_info_callback); - } else { - SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback); - } - - if (isServer) - tor_tls_setup_session_secret_cb(result); - - goto done; - err: - result = NULL; - done: - /* Not expected to get called. */ - tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object"); - return result; -} - /** Make future log messages about tls display the address * address. */ @@ -1378,64 +150,6 @@ tor_tls_set_logged_address(tor_tls_t *tls, const char *address) tls->address = tor_strdup(address); } -/** Set cb to be called with argument arg whenever tls - * next gets a client-side renegotiate in the middle of a read. Do not - * invoke this function until after initial handshaking is done! - */ -void -tor_tls_set_renegotiate_callback(tor_tls_t *tls, - void (*cb)(tor_tls_t *, void *arg), - void *arg) -{ - tls->negotiated_callback = cb; - tls->callback_arg = arg; - tls->got_renegotiate = 0; - if (cb) { - SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback); - } else { - SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback); - } -} - -/** If this version of openssl requires it, turn on renegotiation on - * tls. - */ -void -tor_tls_unblock_renegotiation(tor_tls_t *tls) -{ - /* Yes, we know what we are doing here. No, we do not treat a renegotiation - * as authenticating any earlier-received data. */ - SSL_set_options(tls->ssl, - SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); -} - -/** If this version of openssl supports it, turn off renegotiation on - * tls. (Our protocol never requires this for security, but it's nice - * to use belt-and-suspenders here.) - */ -void -tor_tls_block_renegotiation(tor_tls_t *tls) -{ -#ifdef SUPPORT_UNSAFE_RENEGOTIATION_FLAG - tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; -#else - (void) tls; -#endif -} - -/** Assert that the flags that allow legacy renegotiation are still set */ -void -tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) -{ -#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && \ - SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION != 0 - long options = SSL_get_options(tls->ssl); - tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); -#else - (void) tls; -#endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */ -} - /** Return whether this tls initiated the connect (client) or * received it (server). */ int @@ -1444,730 +158,3 @@ tor_tls_is_server(tor_tls_t *tls) tor_assert(tls); return tls->isServer; } - -/** Release resources associated with a TLS object. Does not close the - * underlying file descriptor. - */ -void -tor_tls_free_(tor_tls_t *tls) -{ - if (!tls) - return; - tor_assert(tls->ssl); - { - size_t r,w; - tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */ - } -#ifdef SSL_set_tlsext_host_name - SSL_set_tlsext_host_name(tls->ssl, NULL); -#endif - SSL_free(tls->ssl); - tls->ssl = NULL; - tls->negotiated_callback = NULL; - if (tls->context) - tor_tls_context_decref(tls->context); - tor_free(tls->address); - tls->magic = 0x99999999; - tor_free(tls); -} - -/** Underlying function for TLS reading. Reads up to len - * characters from tls into cp. On success, returns the - * number of characters read. On failure, returns TOR_TLS_ERROR, - * TOR_TLS_CLOSE, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE. - */ -MOCK_IMPL(int, -tor_tls_read,(tor_tls_t *tls, char *cp, size_t len)) -{ - int r, err; - tor_assert(tls); - tor_assert(tls->ssl); - tor_assert(tls->state == TOR_TLS_ST_OPEN); - tor_assert(lenssl, cp, (int)len); - if (r > 0) { - if (tls->got_renegotiate) { - /* Renegotiation happened! */ - log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls)); - if (tls->negotiated_callback) - tls->negotiated_callback(tls, tls->callback_arg); - tls->got_renegotiate = 0; - } - return r; - } - err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET); - if (err == TOR_TLS_ZERORETURN_ || err == TOR_TLS_CLOSE) { - log_debug(LD_NET,"read returned r=%d; TLS is closed",r); - tls->state = TOR_TLS_ST_CLOSED; - return TOR_TLS_CLOSE; - } else { - tor_assert(err != TOR_TLS_DONE); - log_debug(LD_NET,"read returned r=%d, err=%d",r,err); - return err; - } -} - -/** Total number of bytes that we've used TLS to send. Used to track TLS - * overhead. */ -STATIC uint64_t total_bytes_written_over_tls = 0; -/** Total number of bytes that TLS has put on the network for us. Used to - * track TLS overhead. */ -STATIC uint64_t total_bytes_written_by_tls = 0; - -/** Underlying function for TLS writing. Write up to n - * characters from cp onto tls. On success, returns the - * number of characters written. On failure, returns TOR_TLS_ERROR, - * TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE. - */ -int -tor_tls_write(tor_tls_t *tls, const char *cp, size_t n) -{ - int r, err; - tor_assert(tls); - tor_assert(tls->ssl); - tor_assert(tls->state == TOR_TLS_ST_OPEN); - tor_assert(n < INT_MAX); - if (n == 0) - return 0; - if (tls->wantwrite_n) { - /* if WANTWRITE last time, we must use the _same_ n as before */ - tor_assert(n >= tls->wantwrite_n); - log_debug(LD_NET,"resuming pending-write, (%d to flush, reusing %d)", - (int)n, (int)tls->wantwrite_n); - n = tls->wantwrite_n; - tls->wantwrite_n = 0; - } - r = SSL_write(tls->ssl, cp, (int)n); - err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO, LD_NET); - if (err == TOR_TLS_DONE) { - total_bytes_written_over_tls += r; - return r; - } - if (err == TOR_TLS_WANTWRITE || err == TOR_TLS_WANTREAD) { - tls->wantwrite_n = n; - } - return err; -} - -/** Perform initial handshake on tls. When finished, returns - * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, - * or TOR_TLS_WANTWRITE. - */ -int -tor_tls_handshake(tor_tls_t *tls) -{ - int r; - tor_assert(tls); - tor_assert(tls->ssl); - tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE); - - check_no_tls_errors(); - - OSSL_HANDSHAKE_STATE oldstate = SSL_get_state(tls->ssl); - - if (tls->isServer) { - log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls, - SSL_state_string_long(tls->ssl)); - r = SSL_accept(tls->ssl); - } else { - log_debug(LD_HANDSHAKE, "About to call SSL_connect on %p (%s)", tls, - SSL_state_string_long(tls->ssl)); - r = SSL_connect(tls->ssl); - } - - OSSL_HANDSHAKE_STATE newstate = SSL_get_state(tls->ssl); - - if (oldstate != newstate) - log_debug(LD_HANDSHAKE, "After call, %p was in state %s", - tls, SSL_state_string_long(tls->ssl)); - /* We need to call this here and not earlier, since OpenSSL has a penchant - * for clearing its flags when you say accept or connect. */ - tor_tls_unblock_renegotiation(tls); - r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO, LD_HANDSHAKE); - if (ERR_peek_error() != 0) { - tls_log_errors(tls, tls->isServer ? LOG_INFO : LOG_WARN, LD_HANDSHAKE, - "handshaking"); - return TOR_TLS_ERROR_MISC; - } - if (r == TOR_TLS_DONE) { - tls->state = TOR_TLS_ST_OPEN; - return tor_tls_finish_handshake(tls); - } - return r; -} - -/** Perform the final part of the initial TLS handshake on tls. This - * should be called for the first handshake only: it determines whether the v1 - * or the v2 handshake was used, and adjusts things for the renegotiation - * handshake as appropriate. - * - * tor_tls_handshake() calls this on its own; you only need to call this if - * bufferevent is doing the handshake for you. - */ -int -tor_tls_finish_handshake(tor_tls_t *tls) -{ - int r = TOR_TLS_DONE; - check_no_tls_errors(); - if (tls->isServer) { - SSL_set_info_callback(tls->ssl, NULL); - SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb); - SSL_clear_mode(tls->ssl, SSL_MODE_NO_AUTO_CHAIN); - if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) { - /* This check is redundant, but back when we did it in the callback, - * we might have not been able to look up the tor_tls_t if the code - * was buggy. Fixing that. */ - if (!tls->wasV2Handshake) { - log_warn(LD_BUG, "For some reason, wasV2Handshake didn't" - " get set. Fixing that."); - } - tls->wasV2Handshake = 1; - log_debug(LD_HANDSHAKE, "Completed V2 TLS handshake with client; waiting" - " for renegotiation."); - } else { - tls->wasV2Handshake = 0; - } - } else { - /* Client-side */ - tls->wasV2Handshake = 1; - /* XXXX this can move, probably? -NM */ - if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) { - tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers"); - r = TOR_TLS_ERROR_MISC; - } - } - tls_log_errors(NULL, LOG_WARN, LD_NET, "finishing the handshake"); - return r; -} - -/** Shut down an open tls connection tls. When finished, returns - * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, - * or TOR_TLS_WANTWRITE. - */ -int -tor_tls_shutdown(tor_tls_t *tls) -{ - int r, err; - char buf[128]; - tor_assert(tls); - tor_assert(tls->ssl); - check_no_tls_errors(); - - while (1) { - if (tls->state == TOR_TLS_ST_SENTCLOSE) { - /* If we've already called shutdown once to send a close message, - * we read until the other side has closed too. - */ - do { - r = SSL_read(tls->ssl, buf, 128); - } while (r>0); - err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down", - LOG_INFO, LD_NET); - if (err == TOR_TLS_ZERORETURN_) { - tls->state = TOR_TLS_ST_GOTCLOSE; - /* fall through... */ - } else { - return err; - } - } - - r = SSL_shutdown(tls->ssl); - if (r == 1) { - /* If shutdown returns 1, the connection is entirely closed. */ - tls->state = TOR_TLS_ST_CLOSED; - return TOR_TLS_DONE; - } - err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down", - LOG_INFO, LD_NET); - if (err == TOR_TLS_SYSCALL_) { - /* The underlying TCP connection closed while we were shutting down. */ - tls->state = TOR_TLS_ST_CLOSED; - return TOR_TLS_DONE; - } else if (err == TOR_TLS_ZERORETURN_) { - /* The TLS connection says that it sent a shutdown record, but - * isn't done shutting down yet. Make sure that this hasn't - * happened before, then go back to the start of the function - * and try to read. - */ - if (tls->state == TOR_TLS_ST_GOTCLOSE || - tls->state == TOR_TLS_ST_SENTCLOSE) { - log_warn(LD_NET, - "TLS returned \"half-closed\" value while already half-closed"); - return TOR_TLS_ERROR_MISC; - } - tls->state = TOR_TLS_ST_SENTCLOSE; - /* fall through ... */ - } else { - return err; - } - } /* end loop */ -} - -/** Return true iff this TLS connection is authenticated. - */ -int -tor_tls_peer_has_cert(tor_tls_t *tls) -{ - X509 *cert; - cert = SSL_get_peer_certificate(tls->ssl); - tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate"); - if (!cert) - return 0; - X509_free(cert); - return 1; -} - -/** Return a newly allocated copy of the peer certificate, or NULL if there - * isn't one. */ -MOCK_IMPL(tor_x509_cert_t *, -tor_tls_get_peer_cert,(tor_tls_t *tls)) -{ - X509 *cert; - cert = SSL_get_peer_certificate(tls->ssl); - tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate"); - if (!cert) - return NULL; - return tor_x509_cert_new(cert); -} - -/** Return a newly allocated copy of the cerficate we used on the connection, - * or NULL if somehow we didn't use one. */ -MOCK_IMPL(tor_x509_cert_t *, -tor_tls_get_own_cert,(tor_tls_t *tls)) -{ - X509 *cert = SSL_get_certificate(tls->ssl); - tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, - "getting own-connection certificate"); - if (!cert) - return NULL; - /* Fun inconsistency: SSL_get_peer_certificate increments the reference - * count, but SSL_get_certificate does not. */ - X509 *duplicate = X509_dup(cert); - if (BUG(duplicate == NULL)) - return NULL; - return tor_x509_cert_new(duplicate); -} - -/** Helper function: try to extract a link certificate and an identity - * certificate from tls, and store them in *cert_out and - * *id_cert_out respectively. Log all messages at level - * severity. - * - * Note that a reference is added to cert_out, so it needs to be - * freed. id_cert_out doesn't. */ -MOCK_IMPL(STATIC void, -try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, - X509 **cert_out, X509 **id_cert_out)) -{ - X509 *cert = NULL, *id_cert = NULL; - STACK_OF(X509) *chain = NULL; - int num_in_chain, i; - *cert_out = *id_cert_out = NULL; - if (!(cert = SSL_get_peer_certificate(tls->ssl))) - return; - *cert_out = cert; - if (!(chain = SSL_get_peer_cert_chain(tls->ssl))) - return; - num_in_chain = sk_X509_num(chain); - /* 1 means we're receiving (server-side), and it's just the id_cert. - * 2 means we're connecting (client-side), and it's both the link - * cert and the id_cert. - */ - if (num_in_chain < 1) { - log_fn(severity,LD_PROTOCOL, - "Unexpected number of certificates in chain (%d)", - num_in_chain); - return; - } - for (i=0; iidentity_key
to the identity certificate's key and return - * 0. Else, return -1 and log complaints with log-level severity. - */ -int -tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key) -{ - X509 *cert = NULL, *id_cert = NULL; - EVP_PKEY *id_pkey = NULL; - RSA *rsa; - int r = -1; - - check_no_tls_errors(); - *identity_key = NULL; - - try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert); - if (!cert) - goto done; - if (!id_cert) { - log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found"); - goto done; - } - tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate"); - - if (!(id_pkey = X509_get_pubkey(id_cert)) || - X509_verify(cert, id_pkey) <= 0) { - log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0"); - tls_log_errors(tls, severity, LD_HANDSHAKE, "verifying certificate"); - goto done; - } - - rsa = EVP_PKEY_get1_RSA(id_pkey); - if (!rsa) - goto done; - *identity_key = crypto_new_pk_from_openssl_rsa_(rsa); - - r = 0; - - done: - if (cert) - X509_free(cert); - if (id_pkey) - EVP_PKEY_free(id_pkey); - - /* This should never get invoked, but let's make sure in case OpenSSL - * acts unexpectedly. */ - tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "finishing tor_tls_verify"); - - return r; -} - -/** Check whether the certificate set on the connection tls is expired - * give or take past_tolerance seconds, or not-yet-valid give or take - * future_tolerance seconds. Return 0 for valid, -1 for failure. - * - * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime. - */ -int -tor_tls_check_lifetime(int severity, tor_tls_t *tls, - time_t now, - int past_tolerance, int future_tolerance) -{ - X509 *cert; - int r = -1; - - if (!(cert = SSL_get_peer_certificate(tls->ssl))) - goto done; - - if (tor_x509_check_cert_lifetime_internal(severity, cert, now, - past_tolerance, - future_tolerance) < 0) - goto done; - - r = 0; - done: - if (cert) - X509_free(cert); - /* Not expected to get invoked */ - tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime"); - - return r; -} - -/** Return the number of bytes available for reading from tls. - */ -int -tor_tls_get_pending_bytes(tor_tls_t *tls) -{ - tor_assert(tls); - return SSL_pending(tls->ssl); -} - -/** If tls requires that the next write be of a particular size, - * return that size. Otherwise, return 0. */ -size_t -tor_tls_get_forced_write_size(tor_tls_t *tls) -{ - return tls->wantwrite_n; -} - -/** Sets n_read and n_written to the number of bytes read and written, - * respectively, on the raw socket used by tls since the last time this - * function was called on tls. */ -void -tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written) -{ - BIO *wbio, *tmpbio; - unsigned long r, w; - r = (unsigned long) BIO_number_read(SSL_get_rbio(tls->ssl)); - /* We want the number of bytes actually for real written. Unfortunately, - * sometimes OpenSSL replaces the wbio on tls->ssl with a buffering bio, - * which makes the answer turn out wrong. Let's cope with that. Note - * that this approach will fail if we ever replace tls->ssl's BIOs with - * buffering bios for reasons of our own. As an alternative, we could - * save the original BIO for tls->ssl in the tor_tls_t structure, but - * that would be tempting fate. */ - wbio = SSL_get_wbio(tls->ssl); -#if OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) - /* BIO structure is opaque as of OpenSSL 1.1.0-pre5-dev. Again, not - * supposed to use this form of the version macro, but the OpenSSL developers - * introduced major API changes in the pre-release stage. - */ - if (BIO_method_type(wbio) == BIO_TYPE_BUFFER && - (tmpbio = BIO_next(wbio)) != NULL) - wbio = tmpbio; -#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5)) */ - if (wbio->method == BIO_f_buffer() && (tmpbio = BIO_next(wbio)) != NULL) - wbio = tmpbio; -#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) */ - w = (unsigned long) BIO_number_written(wbio); - - /* We are ok with letting these unsigned ints go "negative" here: - * If we wrapped around, this should still give us the right answer, unless - * we wrapped around by more than ULONG_MAX since the last time we called - * this function. - */ - *n_read = (size_t)(r - tls->last_read_count); - *n_written = (size_t)(w - tls->last_write_count); - if (*n_read > INT_MAX || *n_written > INT_MAX) { - log_warn(LD_BUG, "Preposterously large value in tor_tls_get_n_raw_bytes. " - "r=%lu, last_read=%lu, w=%lu, last_written=%lu", - r, tls->last_read_count, w, tls->last_write_count); - } - total_bytes_written_by_tls += *n_written; - tls->last_read_count = r; - tls->last_write_count = w; -} - -/** Return a ratio of the bytes that TLS has sent to the bytes that we've told - * it to send. Used to track whether our TLS records are getting too tiny. */ -MOCK_IMPL(double, -tls_get_write_overhead_ratio,(void)) -{ - if (total_bytes_written_over_tls == 0) - return 1.0; - - return ((double)total_bytes_written_by_tls) / - ((double)total_bytes_written_over_tls); -} - -/** Implement check_no_tls_errors: If there are any pending OpenSSL - * errors, log an error message. */ -void -check_no_tls_errors_(const char *fname, int line) -{ - if (ERR_peek_error() == 0) - return; - log_warn(LD_CRYPTO, "Unhandled OpenSSL errors found at %s:%d: ", - tor_fix_source_file(fname), line); - tls_log_errors(NULL, LOG_WARN, LD_NET, NULL); -} - -/** Return true iff the initial TLS connection at tls did not use a v2 - * TLS handshake. Output is undefined if the handshake isn't finished. */ -int -tor_tls_used_v1_handshake(tor_tls_t *tls) -{ - return ! tls->wasV2Handshake; -} - -/** Return the number of server handshakes that we've noticed doing on - * tls. */ -int -tor_tls_get_num_server_handshakes(tor_tls_t *tls) -{ - return tls->server_handshake_count; -} - -/** Return true iff the server TLS connection tls got the renegotiation - * request it was waiting for. */ -int -tor_tls_server_got_renegotiate(tor_tls_t *tls) -{ - return tls->got_renegotiate; -} - -#ifndef HAVE_SSL_GET_CLIENT_RANDOM -static size_t -SSL_get_client_random(SSL *s, uint8_t *out, size_t len) -{ - if (len == 0) - return SSL3_RANDOM_SIZE; - tor_assert(len == SSL3_RANDOM_SIZE); - tor_assert(s->s3); - memcpy(out, s->s3->client_random, len); - return len; -} -#endif /* !defined(HAVE_SSL_GET_CLIENT_RANDOM) */ - -#ifndef HAVE_SSL_GET_SERVER_RANDOM -static size_t -SSL_get_server_random(SSL *s, uint8_t *out, size_t len) -{ - if (len == 0) - return SSL3_RANDOM_SIZE; - tor_assert(len == SSL3_RANDOM_SIZE); - tor_assert(s->s3); - memcpy(out, s->s3->server_random, len); - return len; -} -#endif /* !defined(HAVE_SSL_GET_SERVER_RANDOM) */ - -#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY -STATIC size_t -SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len) -{ - tor_assert(s); - if (len == 0) - return s->master_key_length; - tor_assert(len == (size_t)s->master_key_length); - tor_assert(out); - memcpy(out, s->master_key, len); - return len; -} -#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */ - -/** Set the DIGEST256_LEN buffer at secrets_out to the value used in - * the v3 handshake to prove that the client knows the TLS secrets for the - * connection tls. Return 0 on success, -1 on failure. - */ -MOCK_IMPL(int, -tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out)) -{ -#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification" - uint8_t buf[128]; - size_t len; - tor_assert(tls); - - SSL *const ssl = tls->ssl; - SSL_SESSION *const session = SSL_get_session(ssl); - - tor_assert(ssl); - tor_assert(session); - - const size_t server_random_len = SSL_get_server_random(ssl, NULL, 0); - const size_t client_random_len = SSL_get_client_random(ssl, NULL, 0); - const size_t master_key_len = SSL_SESSION_get_master_key(session, NULL, 0); - - tor_assert(server_random_len); - tor_assert(client_random_len); - tor_assert(master_key_len); - - len = client_random_len + server_random_len + strlen(TLSSECRET_MAGIC) + 1; - tor_assert(len <= sizeof(buf)); - - { - size_t r = SSL_get_client_random(ssl, buf, client_random_len); - tor_assert(r == client_random_len); - } - - { - size_t r = SSL_get_server_random(ssl, - buf+client_random_len, - server_random_len); - tor_assert(r == server_random_len); - } - - uint8_t *master_key = tor_malloc_zero(master_key_len); - { - size_t r = SSL_SESSION_get_master_key(session, master_key, master_key_len); - tor_assert(r == master_key_len); - } - - uint8_t *nextbuf = buf + client_random_len + server_random_len; - memcpy(nextbuf, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1); - - /* - The value is an HMAC, using the TLS master key as the HMAC key, of - client_random | server_random | TLSSECRET_MAGIC - */ - crypto_hmac_sha256((char*)secrets_out, - (char*)master_key, - master_key_len, - (char*)buf, len); - memwipe(buf, 0, sizeof(buf)); - memwipe(master_key, 0, master_key_len); - tor_free(master_key); - - return 0; -} - -/** Using the RFC5705 key material exporting construction, and the - * provided context (context_len bytes long) and - * label (a NUL-terminated string), compute a 32-byte secret in - * secrets_out that only the parties to this TLS session can - * compute. Return 0 on success and -1 on failure. - */ -MOCK_IMPL(int, -tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out, - const uint8_t *context, - size_t context_len, - const char *label)) -{ - tor_assert(tls); - tor_assert(tls->ssl); - - int r = SSL_export_keying_material(tls->ssl, - secrets_out, DIGEST256_LEN, - label, strlen(label), - context, context_len, 1); - return (r == 1) ? 0 : -1; -} - -/** Examine the amount of memory used and available for buffers in tls. - * Set *rbuf_capacity to the amount of storage allocated for the read - * buffer and *rbuf_bytes to the amount actually used. - * Set *wbuf_capacity to the amount of storage allocated for the write - * buffer and *wbuf_bytes to the amount actually used. - * - * Return 0 on success, -1 on failure.*/ -int -tor_tls_get_buffer_sizes(tor_tls_t *tls, - size_t *rbuf_capacity, size_t *rbuf_bytes, - size_t *wbuf_capacity, size_t *wbuf_bytes) -{ -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) - (void)tls; - (void)rbuf_capacity; - (void)rbuf_bytes; - (void)wbuf_capacity; - (void)wbuf_bytes; - - return -1; -#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)) */ - if (tls->ssl->s3->rbuf.buf) - *rbuf_capacity = tls->ssl->s3->rbuf.len; - else - *rbuf_capacity = 0; - if (tls->ssl->s3->wbuf.buf) - *wbuf_capacity = tls->ssl->s3->wbuf.len; - else - *wbuf_capacity = 0; - *rbuf_bytes = tls->ssl->s3->rbuf.left; - *wbuf_bytes = tls->ssl->s3->wbuf.left; - return 0; -#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */ -} - -/** Check whether the ECC group requested is supported by the current OpenSSL - * library instance. Return 1 if the group is supported, and 0 if not. - */ -int -evaluate_ecgroup_for_tls(const char *ecgroup) -{ - EC_KEY *ec_key; - int nid; - int ret; - - if (!ecgroup) - nid = NID_tor_default_ecdhe_group; - else if (!strcasecmp(ecgroup, "P256")) - nid = NID_X9_62_prime256v1; - else if (!strcasecmp(ecgroup, "P224")) - nid = NID_secp224r1; - else - return 0; - - ec_key = EC_KEY_new_by_curve_name(nid); - ret = (ec_key != NULL); - EC_KEY_free(ec_key); - - return ret; -} diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h index f46e732679..306d321cd4 100644 --- a/src/lib/tls/tortls.h +++ b/src/lib/tls/tortls.h @@ -50,74 +50,13 @@ struct tor_x509_cert_t; #define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE) -#ifdef TORTLS_PRIVATE - -#ifdef ENABLE_OPENSSL -struct ssl_st; -struct ssl_ctx_st; -struct ssl_session_st; -#endif - /** Holds a SSL_CTX object and related state used to configure TLS * connections. */ typedef struct tor_tls_context_t tor_tls_context_t; -STATIC int tor_errno_to_tls_error(int e); -STATIC int tor_tls_get_error(tor_tls_t *tls, int r, int extra, - const char *doing, int severity, int domain); -STATIC tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl); -STATIC void tor_tls_allocate_tor_tls_object_ex_data_index(void); -MOCK_DECL(STATIC void, try_to_extract_certs_from_tls, - (int severity, tor_tls_t *tls, struct x509_st **cert_out, - struct x509_st **id_cert_out)); -#ifdef TORTLS_OPENSSL_PRIVATE -STATIC int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx); -STATIC int tor_tls_classify_client_ciphers(const struct ssl_st *ssl, - STACK_OF(SSL_CIPHER) *peer_ciphers); -#endif -STATIC int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl); -#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY -STATIC size_t SSL_SESSION_get_master_key(struct ssl_session_st *s, - uint8_t *out, - size_t len); -#endif -STATIC void tor_tls_debug_state_callback(const struct ssl_st *ssl, - int type, int val); -STATIC void tor_tls_server_info_callback(const struct ssl_st *ssl, - int type, int val); -#ifdef TORTLS_OPENSSL_PRIVATE -STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret, - int *secret_len, - STACK_OF(SSL_CIPHER) *peer_ciphers, - CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher, - void *arg); -STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, - uint16_t cipher); -#endif /* defined(TORTLS_OPENSSL_PRIVATE) */ -STATIC tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity, - unsigned int key_lifetime, unsigned flags, int is_client); -STATIC int tor_tls_context_init_one(tor_tls_context_t **ppcontext, - crypto_pk_t *identity, - unsigned int key_lifetime, - unsigned int flags, - int is_client); - -#ifdef TOR_UNIT_TESTS -extern int tor_tls_object_ex_data_index; -extern tor_tls_context_t *server_tls_context; -extern tor_tls_context_t *client_tls_context; -extern uint16_t v2_cipher_list[]; -extern uint64_t total_bytes_written_over_tls; -extern uint64_t total_bytes_written_by_tls; - -#endif /* defined(TOR_UNIT_TESTS) */ - -#endif /* defined(TORTLS_PRIVATE) */ - const char *tor_tls_err_to_string(int err); void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz); - void tor_tls_free_all(void); #define TOR_TLS_CTX_IS_PUBLIC_SERVER (1u<<0) @@ -131,6 +70,9 @@ int tor_tls_context_init(unsigned flags, crypto_pk_t *client_identity, crypto_pk_t *server_identity, unsigned int key_lifetime); +void tor_tls_context_incref(tor_tls_context_t *ctx); +void tor_tls_context_decref(tor_tls_context_t *ctx); +tor_tls_context_t *tor_tls_context_get(int is_server); tor_tls_t *tor_tls_new(int sock, int is_server); void tor_tls_set_logged_address(tor_tls_t *tls, const char *address); void tor_tls_set_renegotiate_callback(tor_tls_t *tls, diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h new file mode 100644 index 0000000000..f6afb348ca --- /dev/null +++ b/src/lib/tls/tortls_internal.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TORTLS_INTERNAL_H +#define TORTLS_INTERNAL_H + +#ifdef ENABLE_OPENSSL +struct ssl_st; +struct ssl_ctx_st; +struct ssl_session_st; +#endif + +int tor_errno_to_tls_error(int e); +int tor_tls_get_error(tor_tls_t *tls, int r, int extra, + const char *doing, int severity, int domain); +tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl); +void tor_tls_allocate_tor_tls_object_ex_data_index(void); +MOCK_DECL(void, try_to_extract_certs_from_tls, + (int severity, tor_tls_t *tls, + tor_x509_cert_impl_t **cert_out, + tor_x509_cert_impl_t **id_cert_out)); +#ifdef TORTLS_OPENSSL_PRIVATE +int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx); +int tor_tls_classify_client_ciphers(const struct ssl_st *ssl, + STACK_OF(SSL_CIPHER) *peer_ciphers); +#endif +int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl); +#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY +size_t SSL_SESSION_get_master_key(struct ssl_session_st *s, + uint8_t *out, + size_t len); +#endif +void tor_tls_debug_state_callback(const struct ssl_st *ssl, + int type, int val); +void tor_tls_server_info_callback(const struct ssl_st *ssl, + int type, int val); +#ifdef TORTLS_OPENSSL_PRIVATE +STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret, + int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher, + void *arg); +STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, + uint16_t cipher); +#endif /* defined(TORTLS_OPENSSL_PRIVATE) */ +tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity, + unsigned int key_lifetime, unsigned flags, int is_client); +int tor_tls_context_init_one(tor_tls_context_t **ppcontext, + crypto_pk_t *identity, + unsigned int key_lifetime, + unsigned int flags, + int is_client); + +#ifdef TOR_UNIT_TESTS +extern int tor_tls_object_ex_data_index; +extern tor_tls_context_t *server_tls_context; +extern tor_tls_context_t *client_tls_context; +extern uint16_t v2_cipher_list[]; +extern uint64_t total_bytes_written_over_tls; +extern uint64_t total_bytes_written_by_tls; +#endif /* defined(TOR_UNIT_TESTS) */ + +#endif /* defined(TORTLS_INTERNAL_H) */ diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c new file mode 100644 index 0000000000..078196ac5f --- /dev/null +++ b/src/lib/tls/tortls_nss.c @@ -0,0 +1,432 @@ +/* Copyright (c) 2003, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file tortls_nss.c + * \brief Wrapper functions to present a consistent interface to + * TLS and SSL X.509 functions from NSS. + **/ + +#include "orconfig.h" + +#define TORTLS_PRIVATE + +#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include "*/ + #include + #include +#endif + +#include "lib/crypt_ops/crypto_cipher.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_dh.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/tls/x509.h" +#include "lib/tls/tortls.h" +#include "lib/tls/tortls_internal.h" +#include "lib/log/util_bug.h" + +int +tor_errno_to_tls_error(int e) +{ + (void)e; + // XXXX + return -1; +} +int +tor_tls_get_error(tor_tls_t *tls, int r, int extra, + const char *doing, int severity, int domain) +{ + (void)tls; + (void)r; + (void)extra; + (void)doing; + (void)severity; + (void)domain; + // XXXX + return -1; +} +tor_tls_t * +tor_tls_get_by_ssl(const struct ssl_st *ssl) +{ + (void) ssl; + // XXXX + // XXXX refers to ssl_st. + return NULL; +} +void +tor_tls_allocate_tor_tls_object_ex_data_index(void) +{ + // XXXX openssl only. +} +MOCK_IMPL(void, +try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, + tor_x509_cert_impl_t **cert_out, + tor_x509_cert_impl_t **id_cert_out)) +{ + tor_assert(tls); + tor_assert(cert_out); + tor_assert(id_cert_out); + (void)severity; + // XXXX +} +int +tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl) +{ + (void) ssl; + // XXXX + // XXXX refers to ssl_st. + return 0; +} + +void +tor_tls_debug_state_callback(const struct ssl_st *ssl, + int type, int val) +{ + (void) ssl; + (void)type; + (void)val; + // XXXX + // XXXX refers to ssl_st. +} + +void +tor_tls_server_info_callback(const struct ssl_st *ssl, + int type, int val) +{ + (void)ssl; + (void)type; + (void)val; + // XXXX + // XXXX refers to ssl_st. +} +tor_tls_context_t * +tor_tls_context_new(crypto_pk_t *identity, + unsigned int key_lifetime, unsigned flags, int is_client) +{ + tor_assert(identity); + tor_assert(key_lifetime); + (void)flags; + (void)is_client; + // XXXX + return NULL; +} +int +tor_tls_context_init_one(tor_tls_context_t **ppcontext, + crypto_pk_t *identity, + unsigned int key_lifetime, + unsigned int flags, + int is_client) +{ + tor_assert(ppcontext); + tor_assert(identity); + tor_assert(key_lifetime); + (void)flags; + (void)is_client; + // XXXX + return -1; +} + +void +tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz) +{ + (void)tls; + (void)buf; + (void)sz; + // XXXX +} + +void +tor_tls_init(void) +{ + // XXXX +} +void +tls_log_errors(tor_tls_t *tls, int severity, int domain, + const char *doing) +{ + (void)tls; + (void)severity; + (void)domain; + (void)doing; + // XXXX +} + +tor_tls_t * +tor_tls_new(int sock, int is_server) +{ + (void)sock; + (void)is_server; + // XXXX + return NULL; +} +void +tor_tls_set_renegotiate_callback(tor_tls_t *tls, + void (*cb)(tor_tls_t *, void *arg), + void *arg) +{ + tor_assert(tls); + (void)cb; + (void)arg; + // XXXX; +} + +void +tor_tls_free_(tor_tls_t *tls) +{ + (void)tls; + // XXXX +} + +int +tor_tls_peer_has_cert(tor_tls_t *tls) +{ + (void)tls; + // XXXX + return -1; +} +MOCK_IMPL(tor_x509_cert_t *, +tor_tls_get_peer_cert,(tor_tls_t *tls)) +{ + tor_assert(tls); + // XXXX + return NULL; +} +MOCK_IMPL(tor_x509_cert_t *, +tor_tls_get_own_cert,(tor_tls_t *tls)) +{ + tor_assert(tls); + // XXXX + return NULL; +} +int +tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity) +{ + tor_assert(tls); + tor_assert(identity); + (void)severity; + // XXXX + return -1; +} +int +tor_tls_check_lifetime(int severity, + tor_tls_t *tls, time_t now, + int past_tolerance, + int future_tolerance) +{ + tor_assert(tls); + (void)severity; + (void)now; + (void)past_tolerance; + (void)future_tolerance; + // XXXX + return -1; +} +MOCK_IMPL(int, +tor_tls_read, (tor_tls_t *tls, char *cp, size_t len)) +{ + tor_assert(tls); + tor_assert(cp); + (void)len; + // XXXX + return -1; +} +int +tor_tls_write(tor_tls_t *tls, const char *cp, size_t n) +{ + tor_assert(tls); + tor_assert(cp); + (void)n; + // XXXX + return -1; +} +int +tor_tls_handshake(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX + return -1; +} +int +tor_tls_finish_handshake(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX + return -1; +} +void +tor_tls_unblock_renegotiation(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX +} +void +tor_tls_block_renegotiation(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX +} +void +tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX +} +int +tor_tls_shutdown(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX + return -1; +} +int +tor_tls_get_pending_bytes(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX + return -1; +} +size_t +tor_tls_get_forced_write_size(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX + return 0; +} +void +tor_tls_get_n_raw_bytes(tor_tls_t *tls, + size_t *n_read, size_t *n_written) +{ + tor_assert(tls); + tor_assert(n_read); + tor_assert(n_written); + // XXXX +} + +int +tor_tls_get_buffer_sizes(tor_tls_t *tls, + size_t *rbuf_capacity, size_t *rbuf_bytes, + size_t *wbuf_capacity, size_t *wbuf_bytes) +{ + tor_assert(tls); + tor_assert(rbuf_capacity); + tor_assert(rbuf_bytes); + tor_assert(wbuf_capacity); + tor_assert(wbuf_bytes); + // XXXX + return -1; +} +MOCK_IMPL(double, +tls_get_write_overhead_ratio, (void)) +{ + // XXXX + return 0.0; +} + +int +tor_tls_used_v1_handshake(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX + return -1; +} +int +tor_tls_get_num_server_handshakes(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX + return -1; +} +int +tor_tls_server_got_renegotiate(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX + return -1; +} +MOCK_IMPL(int, +tor_tls_cert_matches_key,(const tor_tls_t *tls, + const struct tor_x509_cert_t *cert)) +{ + tor_assert(tls); + tor_assert(cert); + // XXXX + return 0; +} +MOCK_IMPL(int, +tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out)) +{ + tor_assert(tls); + tor_assert(secrets_out); + // XXXX + return -1; +} +MOCK_IMPL(int, +tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out, + const uint8_t *context, + size_t context_len, + const char *label)) +{ + tor_assert(tls); + tor_assert(secrets_out); + tor_assert(context); + tor_assert(label); + (void)context_len; + // XXXX + return -1; +} + +void +check_no_tls_errors_(const char *fname, int line) +{ + (void)fname; + (void)line; + // XXXX +} +void +tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, + int severity, int domain, const char *doing) +{ + tor_assert(tls); + (void)err; + (void)severity; + (void)domain; + (void)doing; + // XXXX +} + +int +tor_tls_get_my_certs(int server, + const struct tor_x509_cert_t **link_cert_out, + const struct tor_x509_cert_t **id_cert_out) +{ + tor_assert(link_cert_out); + tor_assert(id_cert_out); + (void)server; + // XXXX + return -1; +} + +crypto_pk_t * +tor_tls_get_my_client_auth_key(void) +{ + // XXXX + return NULL; +} + +const char * +tor_tls_get_ciphersuite_name(tor_tls_t *tls) +{ + tor_assert(tls); + // XXXX + return NULL; +} + +int +evaluate_ecgroup_for_tls(const char *ecgroup) +{ + (void)ecgroup; + // XXXX + return -1; +} diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c new file mode 100644 index 0000000000..333b86481e --- /dev/null +++ b/src/lib/tls/tortls_openssl.c @@ -0,0 +1,2032 @@ +/* Copyright (c) 2003, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file tortls.c + * \brief Wrapper functions to present a consistent interface to + * TLS, SSL, and X.509 functions from OpenSSL. + **/ + +/* (Unlike other tor functions, these + * are prefixed with tor_ in order to avoid conflicting with OpenSSL + * functions and variables.) + */ + +#include "orconfig.h" + +#define TORTLS_PRIVATE +#define TORTLS_OPENSSL_PRIVATE + +#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include "*/ + #include + #include +#endif + +#include "lib/crypt_ops/crypto_cipher.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_dh.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/compat_openssl.h" +#include "lib/tls/x509.h" + +/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in + * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ +DISABLE_GCC_WARNING(redundant-decls) + +#include + +#ifdef OPENSSL_NO_EC +#error "We require OpenSSL with ECC support" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +ENABLE_GCC_WARNING(redundant-decls) + +#include "lib/tls/tortls.h" +#include "lib/tls/tortls_st.h" +#include "lib/tls/tortls_internal.h" +#include "lib/log/log.h" +#include "lib/log/util_bug.h" +#include "lib/container/smartlist.h" +#include "lib/string/compat_string.h" +#include "lib/string/printf.h" +#include "lib/net/socket.h" +#include "lib/intmath/cmp.h" +#include "lib/ctime/di_ops.h" +#include "lib/encoding/time_fmt.h" + +#include +#include + +#include "lib/arch/bytes.h" + +/* Copied from or.h */ +#define LEGAL_NICKNAME_CHARACTERS \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +/** How long do identity certificates live? (sec) */ +#define IDENTITY_CERT_LIFETIME (365*24*60*60) + +#define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer") + +#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') +/* This is a version of OpenSSL before 1.0.0f. It does not have + * the CVE-2011-4576 fix, and as such it can't use RELEASE_BUFFERS and + * SSL3 safely at the same time. + */ +#define DISABLE_SSL3_HANDSHAKE +#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') */ + +/* We redefine these so that we can run correctly even if the vendor gives us + * a version of OpenSSL that does not match its header files. (Apple: I am + * looking at you.) + */ +#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION +#define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x00040000L +#endif +#ifndef SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION +#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010 +#endif + +/** Return values for tor_tls_classify_client_ciphers. + * + * @{ + */ +/** An error occurred when examining the client ciphers */ +#define CIPHERS_ERR -1 +/** The client cipher list indicates that a v1 handshake was in use. */ +#define CIPHERS_V1 1 +/** The client cipher list indicates that the client is using the v2 or the + * v3 handshake, but that it is (probably!) lying about what ciphers it + * supports */ +#define CIPHERS_V2 2 +/** The client cipher list indicates that the client is using the v2 or the + * v3 handshake, and that it is telling the truth about what ciphers it + * supports */ +#define CIPHERS_UNRESTRICTED 3 +/** @} */ + +/** The ex_data index in which we store a pointer to an SSL object's + * corresponding tor_tls_t object. */ +STATIC int tor_tls_object_ex_data_index = -1; + +/** Helper: Allocate tor_tls_object_ex_data_index. */ +void +tor_tls_allocate_tor_tls_object_ex_data_index(void) +{ + if (tor_tls_object_ex_data_index == -1) { + tor_tls_object_ex_data_index = + SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + tor_assert(tor_tls_object_ex_data_index != -1); + } +} + +/** Helper: given a SSL* pointer, return the tor_tls_t object using that + * pointer. */ +tor_tls_t * +tor_tls_get_by_ssl(const SSL *ssl) +{ + tor_tls_t *result = SSL_get_ex_data(ssl, tor_tls_object_ex_data_index); + if (result) + tor_assert(result->magic == TOR_TLS_MAGIC); + return result; +} + +/** True iff tor_tls_init() has been called. */ +static int tls_library_is_initialized = 0; + +/* Module-internal error codes. */ +#define TOR_TLS_SYSCALL_ (MIN_TOR_TLS_ERROR_VAL_ - 2) +#define TOR_TLS_ZERORETURN_ (MIN_TOR_TLS_ERROR_VAL_ - 1) + +/** Write a description of the current state of tls into the + * sz-byte buffer at buf. */ +void +tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz) +{ + const char *ssl_state; + const char *tortls_state; + + if (PREDICT_UNLIKELY(!tls || !tls->ssl)) { + strlcpy(buf, "(No SSL object)", sz); + return; + } + + ssl_state = SSL_state_string_long(tls->ssl); + switch (tls->state) { +#define CASE(st) case TOR_TLS_ST_##st: tortls_state = " in "#st ; break + CASE(HANDSHAKE); + CASE(OPEN); + CASE(GOTCLOSE); + CASE(SENTCLOSE); + CASE(CLOSED); + CASE(RENEGOTIATE); +#undef CASE + case TOR_TLS_ST_BUFFEREVENT: + tortls_state = ""; + break; + default: + tortls_state = " in unknown TLS state"; + break; + } + + tor_snprintf(buf, sz, "%s%s", ssl_state, tortls_state); +} + +/** Log a single error err as returned by ERR_get_error(), which was + * received while performing an operation doing on tls. Log + * the message at severity, in log domain domain. */ +void +tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, + int severity, int domain, const char *doing) +{ + const char *state = NULL, *addr; + const char *msg, *lib, *func; + + state = (tls && tls->ssl)?SSL_state_string_long(tls->ssl):"---"; + + addr = tls ? tls->address : NULL; + + /* Some errors are known-benign, meaning they are the fault of the other + * side of the connection. The caller doesn't know this, so override the + * priority for those cases. */ + switch (ERR_GET_REASON(err)) { + case SSL_R_HTTP_REQUEST: + case SSL_R_HTTPS_PROXY_REQUEST: + case SSL_R_RECORD_LENGTH_MISMATCH: +#ifndef OPENSSL_1_1_API + case SSL_R_RECORD_TOO_LARGE: +#endif + case SSL_R_UNKNOWN_PROTOCOL: + case SSL_R_UNSUPPORTED_PROTOCOL: + severity = LOG_INFO; + break; + default: + break; + } + + msg = (const char*)ERR_reason_error_string(err); + lib = (const char*)ERR_lib_error_string(err); + func = (const char*)ERR_func_error_string(err); + if (!msg) msg = "(null)"; + if (!lib) lib = "(null)"; + if (!func) func = "(null)"; + if (doing) { + tor_log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)", + doing, addr?" with ":"", addr?addr:"", + msg, lib, func, state); + } else { + tor_log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)", + addr?" with ":"", addr?addr:"", + msg, lib, func, state); + } +} + +/** Log all pending tls errors at level severity in log domain + * domain. Use doing to describe our current activities. + */ +void +tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) +{ + unsigned long err; + + while ((err = ERR_get_error()) != 0) { + tor_tls_log_one_error(tls, err, severity, domain, doing); + } +} + +/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error + * code. */ +int +tor_errno_to_tls_error(int e) +{ + switch (e) { + case SOCK_ERRNO(ECONNRESET): // most common + return TOR_TLS_ERROR_CONNRESET; + case SOCK_ERRNO(ETIMEDOUT): + return TOR_TLS_ERROR_TIMEOUT; + case SOCK_ERRNO(EHOSTUNREACH): + case SOCK_ERRNO(ENETUNREACH): + return TOR_TLS_ERROR_NO_ROUTE; + case SOCK_ERRNO(ECONNREFUSED): + return TOR_TLS_ERROR_CONNREFUSED; // least common + default: + return TOR_TLS_ERROR_MISC; + } +} + +#define CATCH_SYSCALL 1 +#define CATCH_ZERO 2 + +/** Given a TLS object and the result of an SSL_* call, use + * SSL_get_error to determine whether an error has occurred, and if so + * which one. Return one of TOR_TLS_{DONE|WANTREAD|WANTWRITE|ERROR}. + * If extra&CATCH_SYSCALL is true, return TOR_TLS_SYSCALL_ instead of + * reporting syscall errors. If extra&CATCH_ZERO is true, return + * TOR_TLS_ZERORETURN_ instead of reporting zero-return errors. + * + * If an error has occurred, log it at level severity and describe the + * current action as doing. + */ +int +tor_tls_get_error(tor_tls_t *tls, int r, int extra, + const char *doing, int severity, int domain) +{ + int err = SSL_get_error(tls->ssl, r); + int tor_error = TOR_TLS_ERROR_MISC; + switch (err) { + case SSL_ERROR_NONE: + return TOR_TLS_DONE; + case SSL_ERROR_WANT_READ: + return TOR_TLS_WANTREAD; + case SSL_ERROR_WANT_WRITE: + return TOR_TLS_WANTWRITE; + case SSL_ERROR_SYSCALL: + if (extra&CATCH_SYSCALL) + return TOR_TLS_SYSCALL_; + if (r == 0) { + tor_log(severity, LD_NET, "TLS error: unexpected close while %s (%s)", + doing, SSL_state_string_long(tls->ssl)); + tor_error = TOR_TLS_ERROR_IO; + } else { + int e = tor_socket_errno(tls->socket); + tor_log(severity, LD_NET, + "TLS error: (errno=%d: %s; state=%s)", + doing, e, tor_socket_strerror(e), + SSL_state_string_long(tls->ssl)); + tor_error = tor_errno_to_tls_error(e); + } + tls_log_errors(tls, severity, domain, doing); + return tor_error; + case SSL_ERROR_ZERO_RETURN: + if (extra&CATCH_ZERO) + return TOR_TLS_ZERORETURN_; + tor_log(severity, LD_NET, "TLS connection closed while %s in state %s", + doing, SSL_state_string_long(tls->ssl)); + tls_log_errors(tls, severity, domain, doing); + return TOR_TLS_CLOSE; + default: + tls_log_errors(tls, severity, domain, doing); + return TOR_TLS_ERROR_MISC; + } +} + +/** Initialize OpenSSL, unless it has already been initialized. + */ +void +tor_tls_init(void) +{ + check_no_tls_errors(); + + if (!tls_library_is_initialized) { +#ifdef OPENSSL_1_1_API + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); +#else + SSL_library_init(); + SSL_load_error_strings(); +#endif + +#if (SIZEOF_VOID_P >= 8 && \ + OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1)) + long version = OpenSSL_version_num(); + + /* LCOV_EXCL_START : we can't test these lines on the same machine */ + if (version >= OPENSSL_V_SERIES(1,0,1)) { + /* Warn if we could *almost* be running with much faster ECDH. + If we're built for a 64-bit target, using OpenSSL 1.0.1, but we + don't have one of the built-in __uint128-based speedups, we are + just one build operation away from an accelerated handshake. + + (We could be looking at OPENSSL_NO_EC_NISTP_64_GCC_128 instead of + doing this test, but that gives compile-time options, not runtime + behavior.) + */ + EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + const EC_GROUP *g = key ? EC_KEY_get0_group(key) : NULL; + const EC_METHOD *m = g ? EC_GROUP_method_of(g) : NULL; + const int warn = (m == EC_GFp_simple_method() || + m == EC_GFp_mont_method() || + m == EC_GFp_nist_method()); + EC_KEY_free(key); + + if (warn) + log_notice(LD_GENERAL, "We were built to run on a 64-bit CPU, with " + "OpenSSL 1.0.1 or later, but with a version of OpenSSL " + "that apparently lacks accelerated support for the NIST " + "P-224 and P-256 groups. Building openssl with such " + "support (using the enable-ec_nistp_64_gcc_128 option " + "when configuring it) would make ECDH much faster."); + } + /* LCOV_EXCL_STOP */ +#endif /* (SIZEOF_VOID_P >= 8 && ... */ + + tor_tls_allocate_tor_tls_object_ex_data_index(); + + tls_library_is_initialized = 1; + } +} + +/** We need to give OpenSSL a callback to verify certificates. This is + * it: We always accept peer certs and complete the handshake. We + * don't validate them until later. + */ +int +always_accept_verify_cb(int preverify_ok, + X509_STORE_CTX *x509_ctx) +{ + (void) preverify_ok; + (void) x509_ctx; + return 1; +} + +/** List of ciphers that servers should select from when the client might be + * claiming extra unsupported ciphers in order to avoid fingerprinting. */ +static const char SERVER_CIPHER_LIST[] = +#ifdef TLS1_3_TXT_AES_128_GCM_SHA256 + /* This one can never actually get selected, since if the client lists it, + * we will assume that the client is honest, and not use this list. + * Nonetheless we list it if it's available, so that the server doesn't + * conclude that it has no valid ciphers if it's running with TLS1.3. + */ + TLS1_3_TXT_AES_128_GCM_SHA256 ":" +#endif + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA; + +/** List of ciphers that servers should select from when we actually have + * our choice of what cipher to use. */ +static const char UNRESTRICTED_SERVER_CIPHER_LIST[] = + /* Here are the TLS 1.3 ciphers we like, in the order we prefer. */ +#ifdef TLS1_3_TXT_AES_256_GCM_SHA384 + TLS1_3_TXT_AES_256_GCM_SHA384 ":" +#endif +#ifdef TLS1_3_TXT_CHACHA20_POLY1305_SHA256 + TLS1_3_TXT_CHACHA20_POLY1305_SHA256 ":" +#endif +#ifdef TLS1_3_TXT_AES_128_GCM_SHA256 + TLS1_3_TXT_AES_128_GCM_SHA256 ":" +#endif +#ifdef TLS1_3_TXT_AES_128_CCM_SHA256 + TLS1_3_TXT_AES_128_CCM_SHA256 ":" +#endif + + /* This list is autogenerated with the gen_server_ciphers.py script; + * don't hand-edit it. */ +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 + TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 + TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA + TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA + TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 + TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 + TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_CCM + TLS1_TXT_DHE_RSA_WITH_AES_256_CCM ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_CCM + TLS1_TXT_DHE_RSA_WITH_AES_128_CCM ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 ":" +#endif + /* Required */ + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" + /* Required */ + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" +#ifdef TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 + TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305 + TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305 +#endif + ; + +/* Note: to set up your own private testing network with link crypto + * disabled, set your Tors' cipher list to + * (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate + * with any of the "real" Tors, though. */ + +#define CIPHER(id, name) name ":" +#define XCIPHER(id, name) +/** List of ciphers that clients should advertise, omitting items that + * our OpenSSL doesn't know about. */ +static const char CLIENT_CIPHER_LIST[] = +#include "ciphers.inc" + /* Tell it not to use SSLv2 ciphers, so that it can select an SSLv3 version + * of any cipher we say. */ + "!SSLv2" + ; +#undef CIPHER +#undef XCIPHER + +/** Remove a reference to ctx, and free it if it has no more + * references. */ +void +tor_tls_context_decref(tor_tls_context_t *ctx) +{ + tor_assert(ctx); + if (--ctx->refcnt == 0) { + SSL_CTX_free(ctx->ctx); + tor_x509_cert_free(ctx->my_link_cert); + tor_x509_cert_free(ctx->my_id_cert); + tor_x509_cert_free(ctx->my_auth_cert); + crypto_pk_free(ctx->link_key); + crypto_pk_free(ctx->auth_key); + /* LCOV_EXCL_BR_START since ctx will never be NULL here */ + tor_free(ctx); + /* LCOV_EXCL_BR_STOP */ + } +} + +/** Set *link_cert_out and *id_cert_out to the link certificate + * and ID certificate that we're currently using for our V3 in-protocol + * handshake's certificate chain. If server is true, provide the certs + * that we use in server mode (auth, ID); otherwise, provide the certs that we + * use in client mode. (link, ID) */ +int +tor_tls_get_my_certs(int server, + const tor_x509_cert_t **link_cert_out, + const tor_x509_cert_t **id_cert_out) +{ + tor_tls_context_t *ctx = tor_tls_context_get(server); + if (! ctx) + return -1; + if (link_cert_out) + *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert; + if (id_cert_out) + *id_cert_out = ctx->my_id_cert; + return 0; +} + +/** + * Return the authentication key that we use to authenticate ourselves as a + * client in the V3 in-protocol handshake. + */ +crypto_pk_t * +tor_tls_get_my_client_auth_key(void) +{ + tor_tls_context_t *context = tor_tls_context_get(0); + if (! context) + return NULL; + return context->auth_key; +} + +/** Return true iff the other side of tls has authenticated to us, and + * the key certified in cert is the same as the key they used to do it. + */ +MOCK_IMPL(int, +tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert)) +{ + X509 *peercert = SSL_get_peer_certificate(tls->ssl); + EVP_PKEY *link_key = NULL, *cert_key = NULL; + int result; + + if (!peercert) + return 0; + link_key = X509_get_pubkey(peercert); + cert_key = X509_get_pubkey((X509 *)tor_x509_cert_get_impl(cert)); + + result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1; + + X509_free(peercert); + if (link_key) + EVP_PKEY_free(link_key); + if (cert_key) + EVP_PKEY_free(cert_key); + + return result; +} + +/** Create a new global TLS context. + * + * You can call this function multiple times. Each time you call it, + * it generates new certificates; all new connections will use + * the new SSL context. + */ +int +tor_tls_context_init_one(tor_tls_context_t **ppcontext, + crypto_pk_t *identity, + unsigned int key_lifetime, + unsigned int flags, + int is_client) +{ + tor_tls_context_t *new_ctx = tor_tls_context_new(identity, + key_lifetime, + flags, + is_client); + tor_tls_context_t *old_ctx = *ppcontext; + + if (new_ctx != NULL) { + *ppcontext = new_ctx; + + /* Free the old context if one existed. */ + if (old_ctx != NULL) { + /* This is safe even if there are open connections: we reference- + * count tor_tls_context_t objects. */ + tor_tls_context_decref(old_ctx); + } + } + + return ((new_ctx != NULL) ? 0 : -1); +} + +/** The group we should use for ecdhe when none was selected. */ +#define NID_tor_default_ecdhe_group NID_X9_62_prime256v1 + +#define RSA_LINK_KEY_BITS 2048 + +/** Create a new TLS context for use with Tor TLS handshakes. + * identity should be set to the identity key used to sign the + * certificate. + */ +tor_tls_context_t * +tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, + unsigned flags, int is_client) +{ + crypto_pk_t *rsa = NULL, *rsa_auth = NULL; + EVP_PKEY *pkey = NULL; + tor_tls_context_t *result = NULL; + X509 *cert = NULL, *idcert = NULL, *authcert = NULL; + char *nickname = NULL, *nn2 = NULL; + + tor_tls_init(); + nickname = crypto_random_hostname(8, 20, "www.", ".net"); +#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE + nn2 = crypto_random_hostname(8, 20, "www.", ".net"); +#else + nn2 = crypto_random_hostname(8, 20, "www.", ".com"); +#endif + + /* Generate short-term RSA key for use with TLS. */ + if (!(rsa = crypto_pk_new())) + goto error; + if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0) + goto error; + if (!is_client) { + /* Generate short-term RSA key for use in the in-protocol ("v3") + * authentication handshake. */ + if (!(rsa_auth = crypto_pk_new())) + goto error; + if (crypto_pk_generate_key(rsa_auth)<0) + goto error; + /* Create a link certificate signed by identity key. */ + cert = tor_tls_create_certificate(rsa, identity, nickname, nn2, + key_lifetime); + /* Create self-signed certificate for identity key. */ + idcert = tor_tls_create_certificate(identity, identity, nn2, nn2, + IDENTITY_CERT_LIFETIME); + /* Create an authentication certificate signed by identity key. */ + authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2, + key_lifetime); + if (!cert || !idcert || !authcert) { + log_warn(LD_CRYPTO, "Error creating certificate"); + goto error; + } + } + + result = tor_malloc_zero(sizeof(tor_tls_context_t)); + result->refcnt = 1; + if (!is_client) { + result->my_link_cert = tor_x509_cert_new(X509_dup(cert)); + result->my_id_cert = tor_x509_cert_new(X509_dup(idcert)); + result->my_auth_cert = tor_x509_cert_new(X509_dup(authcert)); + if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert) + goto error; + result->link_key = crypto_pk_dup_key(rsa); + result->auth_key = crypto_pk_dup_key(rsa_auth); + } + +#if 0 + /* Tell OpenSSL to only use TLS1. This may have subtly different results + * from SSLv23_method() with SSLv2 and SSLv3 disabled, so we need to do some + * investigation before we consider adjusting it. It should be compatible + * with existing Tors. */ + if (!(result->ctx = SSL_CTX_new(TLSv1_method()))) + goto error; +#endif /* 0 */ + + /* Tell OpenSSL to use TLS 1.0 or later but not SSL2 or SSL3. */ +#ifdef HAVE_TLS_METHOD + if (!(result->ctx = SSL_CTX_new(TLS_method()))) + goto error; +#else + if (!(result->ctx = SSL_CTX_new(SSLv23_method()))) + goto error; +#endif /* defined(HAVE_TLS_METHOD) */ + SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3); + + /* Prefer the server's ordering of ciphers: the client's ordering has + * historically been chosen for fingerprinting resistance. */ + SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + + /* Disable TLS tickets if they're supported. We never want to use them; + * using them can make our perfect forward secrecy a little worse, *and* + * create an opportunity to fingerprint us (since it's unusual to use them + * with TLS sessions turned off). + * + * In 0.2.4, clients advertise support for them though, to avoid a TLS + * distinguishability vector. This can give us worse PFS, though, if we + * get a server that doesn't set SSL_OP_NO_TICKET. With luck, there will + * be few such servers by the time 0.2.4 is more stable. + */ +#ifdef SSL_OP_NO_TICKET + if (! is_client) { + SSL_CTX_set_options(result->ctx, SSL_OP_NO_TICKET); + } +#endif + + SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE); + +#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + SSL_CTX_set_options(result->ctx, + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); +#endif + /* Yes, we know what we are doing here. No, we do not treat a renegotiation + * as authenticating any earlier-received data. + */ + { + SSL_CTX_set_options(result->ctx, + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); + } + + /* Don't actually allow compression; it uses RAM and time, it makes TLS + * vulnerable to CRIME-style attacks, and most of the data we transmit over + * TLS is encrypted (and therefore uncompressible) anyway. */ +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(result->ctx, SSL_OP_NO_COMPRESSION); +#endif +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) +#ifndef OPENSSL_NO_COMP + if (result->ctx->comp_methods) + result->ctx->comp_methods = NULL; +#endif +#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) */ + +#ifdef SSL_MODE_RELEASE_BUFFERS + SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS); +#endif + if (! is_client) { + if (cert && !SSL_CTX_use_certificate(result->ctx,cert)) + goto error; + X509_free(cert); /* We just added a reference to cert. */ + cert=NULL; + if (idcert) { + X509_STORE *s = SSL_CTX_get_cert_store(result->ctx); + tor_assert(s); + X509_STORE_add_cert(s, idcert); + X509_free(idcert); /* The context now owns the reference to idcert */ + idcert = NULL; + } + } + SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); + if (!is_client) { + tor_assert(rsa); + if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,1))) + goto error; + if (!SSL_CTX_use_PrivateKey(result->ctx, pkey)) + goto error; + EVP_PKEY_free(pkey); + pkey = NULL; + if (!SSL_CTX_check_private_key(result->ctx)) + goto error; + } + { + DH *dh = crypto_dh_new_openssl_tls(); + tor_assert(dh); + SSL_CTX_set_tmp_dh(result->ctx, dh); + DH_free(dh); + } + if (! is_client) { + int nid; + EC_KEY *ec_key; + if (flags & TOR_TLS_CTX_USE_ECDHE_P224) + nid = NID_secp224r1; + else if (flags & TOR_TLS_CTX_USE_ECDHE_P256) + nid = NID_X9_62_prime256v1; + else + nid = NID_tor_default_ecdhe_group; + /* Use P-256 for ECDHE. */ + ec_key = EC_KEY_new_by_curve_name(nid); + if (ec_key != NULL) /*XXXX Handle errors? */ + SSL_CTX_set_tmp_ecdh(result->ctx, ec_key); + EC_KEY_free(ec_key); + } + SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER, + always_accept_verify_cb); + /* let us realloc bufs that we're writing from */ + SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + if (rsa) + crypto_pk_free(rsa); + if (rsa_auth) + crypto_pk_free(rsa_auth); + X509_free(authcert); + tor_free(nickname); + tor_free(nn2); + return result; + + error: + tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context"); + tor_free(nickname); + tor_free(nn2); + if (pkey) + EVP_PKEY_free(pkey); + if (rsa) + crypto_pk_free(rsa); + if (rsa_auth) + crypto_pk_free(rsa_auth); + if (result) + tor_tls_context_decref(result); + if (cert) + X509_free(cert); + if (idcert) + X509_free(idcert); + if (authcert) + X509_free(authcert); + return NULL; +} + +/** Invoked when a TLS state changes: log the change at severity 'debug' */ +void +tor_tls_debug_state_callback(const SSL *ssl, int type, int val) +{ + /* LCOV_EXCL_START since this depends on whether debug is captured or not */ + log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", + ssl, SSL_state_string_long(ssl), type, val); + /* LCOV_EXCL_STOP */ +} + +/* Return the name of the negotiated ciphersuite in use on tls */ +const char * +tor_tls_get_ciphersuite_name(tor_tls_t *tls) +{ + return SSL_get_cipher(tls->ssl); +} + +/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to + * 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers + * that it claims to support. We'll prune this list to remove the ciphers + * *we* don't recognize. */ +STATIC uint16_t v2_cipher_list[] = { + 0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */ + 0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */ + 0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */ + 0x0038, /* TLS1_TXT_DHE_DSS_WITH_AES_256_SHA */ + 0xc00f, /* TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA */ + 0xc005, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA */ + 0x0035, /* TLS1_TXT_RSA_WITH_AES_256_SHA */ + 0xc007, /* TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA */ + 0xc009, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA */ + 0xc011, /* TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA */ + 0xc013, /* TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA */ + 0x0033, /* TLS1_TXT_DHE_RSA_WITH_AES_128_SHA */ + 0x0032, /* TLS1_TXT_DHE_DSS_WITH_AES_128_SHA */ + 0xc00c, /* TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA */ + 0xc00e, /* TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA */ + 0xc002, /* TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA */ + 0xc004, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA */ + 0x0004, /* SSL3_TXT_RSA_RC4_128_MD5 */ + 0x0005, /* SSL3_TXT_RSA_RC4_128_SHA */ + 0x002f, /* TLS1_TXT_RSA_WITH_AES_128_SHA */ + 0xc008, /* TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA */ + 0xc012, /* TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA */ + 0x0016, /* SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA */ + 0x0013, /* SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA */ + 0xc00d, /* TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA */ + 0xc003, /* TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA */ + 0xfeff, /* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA */ + 0x000a, /* SSL3_TXT_RSA_DES_192_CBC3_SHA */ + 0 +}; +/** Have we removed the unrecognized ciphers from v2_cipher_list yet? */ +static int v2_cipher_list_pruned = 0; + +/** Return 0 if m does not support the cipher with ID cipher; + * return 1 if it does support it, or if we have no way to tell. */ +int +find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher) +{ + const SSL_CIPHER *c; +#ifdef HAVE_SSL_CIPHER_FIND + (void) m; + { + unsigned char cipherid[3]; + tor_assert(ssl); + set_uint16(cipherid, tor_htons(cipher)); + cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting + * with a two-byte 'cipherid', it may look for a v2 + * cipher with the appropriate 3 bytes. */ + c = SSL_CIPHER_find((SSL*)ssl, cipherid); + if (c) + tor_assert((SSL_CIPHER_get_id(c) & 0xffff) == cipher); + return c != NULL; + } +#else /* !(defined(HAVE_SSL_CIPHER_FIND)) */ + +# if defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) + if (m && m->get_cipher_by_char) { + unsigned char cipherid[3]; + set_uint16(cipherid, tor_htons(cipher)); + cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting + * with a two-byte 'cipherid', it may look for a v2 + * cipher with the appropriate 3 bytes. */ + c = m->get_cipher_by_char(cipherid); + if (c) + tor_assert((c->id & 0xffff) == cipher); + return c != NULL; + } +#endif /* defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) */ +# ifndef OPENSSL_1_1_API + if (m && m->get_cipher && m->num_ciphers) { + /* It would seem that some of the "let's-clean-up-openssl" forks have + * removed the get_cipher_by_char function. Okay, so now you get a + * quadratic search. + */ + int i; + for (i = 0; i < m->num_ciphers(); ++i) { + c = m->get_cipher(i); + if (c && (c->id & 0xffff) == cipher) { + return 1; + } + } + return 0; + } +#endif /* !defined(OPENSSL_1_1_API) */ + (void) ssl; + (void) m; + (void) cipher; + return 1; /* No way to search */ +#endif /* defined(HAVE_SSL_CIPHER_FIND) */ +} + +/** Remove from v2_cipher_list every cipher that we don't support, so that + * comparing v2_cipher_list to a client's cipher list will give a sensible + * result. */ +static void +prune_v2_cipher_list(const SSL *ssl) +{ + uint16_t *inp, *outp; +#ifdef HAVE_TLS_METHOD + const SSL_METHOD *m = TLS_method(); +#else + const SSL_METHOD *m = SSLv23_method(); +#endif + + inp = outp = v2_cipher_list; + while (*inp) { + if (find_cipher_by_id(ssl, m, *inp)) { + *outp++ = *inp++; + } else { + inp++; + } + } + *outp = 0; + + v2_cipher_list_pruned = 1; +} + +/** Examine the client cipher list in ssl, and determine what kind of + * client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2, + * CIPHERS_UNRESTRICTED. + **/ +int +tor_tls_classify_client_ciphers(const SSL *ssl, + STACK_OF(SSL_CIPHER) *peer_ciphers) +{ + int i, res; + tor_tls_t *tor_tls; + if (PREDICT_UNLIKELY(!v2_cipher_list_pruned)) + prune_v2_cipher_list(ssl); + + tor_tls = tor_tls_get_by_ssl(ssl); + if (tor_tls && tor_tls->client_cipher_list_type) + return tor_tls->client_cipher_list_type; + + /* If we reached this point, we just got a client hello. See if there is + * a cipher list. */ + if (!peer_ciphers) { + log_info(LD_NET, "No ciphers on session"); + res = CIPHERS_ERR; + goto done; + } + /* Now we need to see if there are any ciphers whose presence means we're + * dealing with an updated Tor. */ + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); + const char *ciphername = SSL_CIPHER_get_name(cipher); + if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) && + strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) && + strcmp(ciphername, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) && + strcmp(ciphername, "(NONE)")) { + log_debug(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername); + // return 1; + goto v2_or_higher; + } + } + res = CIPHERS_V1; + goto done; + v2_or_higher: + { + const uint16_t *v2_cipher = v2_cipher_list; + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); + uint16_t id = SSL_CIPHER_get_id(cipher) & 0xffff; + if (id == 0x00ff) /* extended renegotiation indicator. */ + continue; + if (!id || id != *v2_cipher) { + res = CIPHERS_UNRESTRICTED; + goto dump_ciphers; + } + ++v2_cipher; + } + if (*v2_cipher != 0) { + res = CIPHERS_UNRESTRICTED; + goto dump_ciphers; + } + res = CIPHERS_V2; + } + + dump_ciphers: + { + smartlist_t *elts = smartlist_new(); + char *s; + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); + const char *ciphername = SSL_CIPHER_get_name(cipher); + smartlist_add(elts, (char*)ciphername); + } + s = smartlist_join_strings(elts, ":", 0, NULL); + log_debug(LD_NET, "Got a %s V2/V3 cipher list from %s. It is: '%s'", + (res == CIPHERS_V2) ? "fictitious" : "real", ADDR(tor_tls), s); + tor_free(s); + smartlist_free(elts); + } + done: + if (tor_tls) + return tor_tls->client_cipher_list_type = res; + + return res; +} + +/** Return true iff the cipher list suggested by the client for ssl is + * a list that indicates that the client knows how to do the v2 TLS connection + * handshake. */ +int +tor_tls_client_is_using_v2_ciphers(const SSL *ssl) +{ + STACK_OF(SSL_CIPHER) *ciphers; +#ifdef HAVE_SSL_GET_CLIENT_CIPHERS + ciphers = SSL_get_client_ciphers(ssl); +#else + SSL_SESSION *session; + if (!(session = SSL_get_session((SSL *)ssl))) { + log_info(LD_NET, "No session on TLS?"); + return CIPHERS_ERR; + } + ciphers = session->ciphers; +#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */ + + return tor_tls_classify_client_ciphers(ssl, ciphers) >= CIPHERS_V2; +} + +/** Invoked when we're accepting a connection on ssl, and the connection + * changes state. We use this: + *
  • To alter the state of the handshake partway through, so we + * do not send or request extra certificates in v2 handshakes.
  • + *
  • To detect renegotiation
+ */ +void +tor_tls_server_info_callback(const SSL *ssl, int type, int val) +{ + tor_tls_t *tls; + (void) val; + + IF_BUG_ONCE(ssl == NULL) { + return; // LCOV_EXCL_LINE + } + + tor_tls_debug_state_callback(ssl, type, val); + + if (type != SSL_CB_ACCEPT_LOOP) + return; + + OSSL_HANDSHAKE_STATE ssl_state = SSL_get_state(ssl); + if (! STATE_IS_SW_SERVER_HELLO(ssl_state)) + return; + tls = tor_tls_get_by_ssl(ssl); + if (tls) { + /* Check whether we're watching for renegotiates. If so, this is one! */ + if (tls->negotiated_callback) + tls->got_renegotiate = 1; + if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/ + ++tls->server_handshake_count; + } else { + log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); + return; + } + + /* Now check the cipher list. */ + if (tor_tls_client_is_using_v2_ciphers(ssl)) { + if (tls->wasV2Handshake) + return; /* We already turned this stuff off for the first handshake; + * This is a renegotiation. */ + + /* Yes, we're casting away the const from ssl. This is very naughty of us. + * Let's hope openssl doesn't notice! */ + + /* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */ + SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN); + /* Don't send a hello request. */ + SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL); + + if (tls) { + tls->wasV2Handshake = 1; + } else { + /* LCOV_EXCL_START this line is not reachable */ + log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); + /* LCOV_EXCL_STOP */ + } + } +} + +/** Callback to get invoked on a server after we've read the list of ciphers + * the client supports, but before we pick our own ciphersuite. + * + * We can't abuse an info_cb for this, since by the time one of the + * client_hello info_cbs is called, we've already picked which ciphersuite to + * use. + * + * Technically, this function is an abuse of this callback, since the point of + * a session_secret_cb is to try to set up and/or verify a shared-secret for + * authentication on the fly. But as long as we return 0, we won't actually be + * setting up a shared secret, and all will be fine. + */ +int +tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher, + void *arg) +{ + (void) secret; + (void) secret_len; + (void) peer_ciphers; + (void) cipher; + (void) arg; + + if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) == + CIPHERS_UNRESTRICTED) { + SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST); + } + + SSL_set_session_secret_cb(ssl, NULL, NULL); + + return 0; +} +static void +tor_tls_setup_session_secret_cb(tor_tls_t *tls) +{ + SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL); +} + +/** Create a new TLS object from a file descriptor, and a flag to + * determine whether it is functioning as a server. + */ +tor_tls_t * +tor_tls_new(int sock, int isServer) +{ + BIO *bio = NULL; + tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t)); + tor_tls_context_t *context = tor_tls_context_get(isServer); + result->magic = TOR_TLS_MAGIC; + + check_no_tls_errors(); + tor_assert(context); /* make sure somebody made it first */ + if (!(result->ssl = SSL_new(context->ctx))) { + tls_log_errors(NULL, LOG_WARN, LD_NET, "creating SSL object"); + tor_free(result); + goto err; + } + +#ifdef SSL_set_tlsext_host_name + /* Browsers use the TLS hostname extension, so we should too. */ + if (!isServer) { + char *fake_hostname = crypto_random_hostname(4,25, "www.",".com"); + SSL_set_tlsext_host_name(result->ssl, fake_hostname); + tor_free(fake_hostname); + } +#endif /* defined(SSL_set_tlsext_host_name) */ + + if (!SSL_set_cipher_list(result->ssl, + isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) { + tls_log_errors(NULL, LOG_WARN, LD_NET, "setting ciphers"); +#ifdef SSL_set_tlsext_host_name + SSL_set_tlsext_host_name(result->ssl, NULL); +#endif + SSL_free(result->ssl); + tor_free(result); + goto err; + } + result->socket = sock; + bio = BIO_new_socket(sock, BIO_NOCLOSE); + if (! bio) { + tls_log_errors(NULL, LOG_WARN, LD_NET, "opening BIO"); +#ifdef SSL_set_tlsext_host_name + SSL_set_tlsext_host_name(result->ssl, NULL); +#endif + SSL_free(result->ssl); + tor_free(result); + goto err; + } + { + int set_worked = + SSL_set_ex_data(result->ssl, tor_tls_object_ex_data_index, result); + if (!set_worked) { + log_warn(LD_BUG, + "Couldn't set the tls for an SSL*; connection will fail"); + } + } + SSL_set_bio(result->ssl, bio, bio); + tor_tls_context_incref(context); + result->context = context; + result->state = TOR_TLS_ST_HANDSHAKE; + result->isServer = isServer; + result->wantwrite_n = 0; + result->last_write_count = (unsigned long) BIO_number_written(bio); + result->last_read_count = (unsigned long) BIO_number_read(bio); + if (result->last_write_count || result->last_read_count) { + log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu", + result->last_read_count, result->last_write_count); + } + if (isServer) { + SSL_set_info_callback(result->ssl, tor_tls_server_info_callback); + } else { + SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback); + } + + if (isServer) + tor_tls_setup_session_secret_cb(result); + + goto done; + err: + result = NULL; + done: + /* Not expected to get called. */ + tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object"); + return result; +} + +/** Set cb to be called with argument arg whenever tls + * next gets a client-side renegotiate in the middle of a read. Do not + * invoke this function until after initial handshaking is done! + */ +void +tor_tls_set_renegotiate_callback(tor_tls_t *tls, + void (*cb)(tor_tls_t *, void *arg), + void *arg) +{ + tls->negotiated_callback = cb; + tls->callback_arg = arg; + tls->got_renegotiate = 0; + if (cb) { + SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback); + } else { + SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback); + } +} + +/** If this version of openssl requires it, turn on renegotiation on + * tls. + */ +void +tor_tls_unblock_renegotiation(tor_tls_t *tls) +{ + /* Yes, we know what we are doing here. No, we do not treat a renegotiation + * as authenticating any earlier-received data. */ + SSL_set_options(tls->ssl, + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); +} + +/** If this version of openssl supports it, turn off renegotiation on + * tls. (Our protocol never requires this for security, but it's nice + * to use belt-and-suspenders here.) + */ +void +tor_tls_block_renegotiation(tor_tls_t *tls) +{ +#ifdef SUPPORT_UNSAFE_RENEGOTIATION_FLAG + tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; +#else + (void) tls; +#endif +} + +/** Assert that the flags that allow legacy renegotiation are still set */ +void +tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) +{ +#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && \ + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION != 0 + long options = SSL_get_options(tls->ssl); + tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); +#else + (void) tls; +#endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */ +} + +/** Release resources associated with a TLS object. Does not close the + * underlying file descriptor. + */ +void +tor_tls_free_(tor_tls_t *tls) +{ + if (!tls) + return; + tor_assert(tls->ssl); + { + size_t r,w; + tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */ + } +#ifdef SSL_set_tlsext_host_name + SSL_set_tlsext_host_name(tls->ssl, NULL); +#endif + SSL_free(tls->ssl); + tls->ssl = NULL; + tls->negotiated_callback = NULL; + if (tls->context) + tor_tls_context_decref(tls->context); + tor_free(tls->address); + tls->magic = 0x99999999; + tor_free(tls); +} + +/** Underlying function for TLS reading. Reads up to len + * characters from tls into cp. On success, returns the + * number of characters read. On failure, returns TOR_TLS_ERROR, + * TOR_TLS_CLOSE, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE. + */ +MOCK_IMPL(int, +tor_tls_read,(tor_tls_t *tls, char *cp, size_t len)) +{ + int r, err; + tor_assert(tls); + tor_assert(tls->ssl); + tor_assert(tls->state == TOR_TLS_ST_OPEN); + tor_assert(lenssl, cp, (int)len); + if (r > 0) { + if (tls->got_renegotiate) { + /* Renegotiation happened! */ + log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls)); + if (tls->negotiated_callback) + tls->negotiated_callback(tls, tls->callback_arg); + tls->got_renegotiate = 0; + } + return r; + } + err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET); + if (err == TOR_TLS_ZERORETURN_ || err == TOR_TLS_CLOSE) { + log_debug(LD_NET,"read returned r=%d; TLS is closed",r); + tls->state = TOR_TLS_ST_CLOSED; + return TOR_TLS_CLOSE; + } else { + tor_assert(err != TOR_TLS_DONE); + log_debug(LD_NET,"read returned r=%d, err=%d",r,err); + return err; + } +} + +/** Total number of bytes that we've used TLS to send. Used to track TLS + * overhead. */ +STATIC uint64_t total_bytes_written_over_tls = 0; +/** Total number of bytes that TLS has put on the network for us. Used to + * track TLS overhead. */ +STATIC uint64_t total_bytes_written_by_tls = 0; + +/** Underlying function for TLS writing. Write up to n + * characters from cp onto tls. On success, returns the + * number of characters written. On failure, returns TOR_TLS_ERROR, + * TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE. + */ +int +tor_tls_write(tor_tls_t *tls, const char *cp, size_t n) +{ + int r, err; + tor_assert(tls); + tor_assert(tls->ssl); + tor_assert(tls->state == TOR_TLS_ST_OPEN); + tor_assert(n < INT_MAX); + if (n == 0) + return 0; + if (tls->wantwrite_n) { + /* if WANTWRITE last time, we must use the _same_ n as before */ + tor_assert(n >= tls->wantwrite_n); + log_debug(LD_NET,"resuming pending-write, (%d to flush, reusing %d)", + (int)n, (int)tls->wantwrite_n); + n = tls->wantwrite_n; + tls->wantwrite_n = 0; + } + r = SSL_write(tls->ssl, cp, (int)n); + err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO, LD_NET); + if (err == TOR_TLS_DONE) { + total_bytes_written_over_tls += r; + return r; + } + if (err == TOR_TLS_WANTWRITE || err == TOR_TLS_WANTREAD) { + tls->wantwrite_n = n; + } + return err; +} + +/** Perform initial handshake on tls. When finished, returns + * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, + * or TOR_TLS_WANTWRITE. + */ +int +tor_tls_handshake(tor_tls_t *tls) +{ + int r; + tor_assert(tls); + tor_assert(tls->ssl); + tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE); + + check_no_tls_errors(); + + OSSL_HANDSHAKE_STATE oldstate = SSL_get_state(tls->ssl); + + if (tls->isServer) { + log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls, + SSL_state_string_long(tls->ssl)); + r = SSL_accept(tls->ssl); + } else { + log_debug(LD_HANDSHAKE, "About to call SSL_connect on %p (%s)", tls, + SSL_state_string_long(tls->ssl)); + r = SSL_connect(tls->ssl); + } + + OSSL_HANDSHAKE_STATE newstate = SSL_get_state(tls->ssl); + + if (oldstate != newstate) + log_debug(LD_HANDSHAKE, "After call, %p was in state %s", + tls, SSL_state_string_long(tls->ssl)); + /* We need to call this here and not earlier, since OpenSSL has a penchant + * for clearing its flags when you say accept or connect. */ + tor_tls_unblock_renegotiation(tls); + r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO, LD_HANDSHAKE); + if (ERR_peek_error() != 0) { + tls_log_errors(tls, tls->isServer ? LOG_INFO : LOG_WARN, LD_HANDSHAKE, + "handshaking"); + return TOR_TLS_ERROR_MISC; + } + if (r == TOR_TLS_DONE) { + tls->state = TOR_TLS_ST_OPEN; + return tor_tls_finish_handshake(tls); + } + return r; +} + +/** Perform the final part of the initial TLS handshake on tls. This + * should be called for the first handshake only: it determines whether the v1 + * or the v2 handshake was used, and adjusts things for the renegotiation + * handshake as appropriate. + * + * tor_tls_handshake() calls this on its own; you only need to call this if + * bufferevent is doing the handshake for you. + */ +int +tor_tls_finish_handshake(tor_tls_t *tls) +{ + int r = TOR_TLS_DONE; + check_no_tls_errors(); + if (tls->isServer) { + SSL_set_info_callback(tls->ssl, NULL); + SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb); + SSL_clear_mode(tls->ssl, SSL_MODE_NO_AUTO_CHAIN); + if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) { + /* This check is redundant, but back when we did it in the callback, + * we might have not been able to look up the tor_tls_t if the code + * was buggy. Fixing that. */ + if (!tls->wasV2Handshake) { + log_warn(LD_BUG, "For some reason, wasV2Handshake didn't" + " get set. Fixing that."); + } + tls->wasV2Handshake = 1; + log_debug(LD_HANDSHAKE, "Completed V2 TLS handshake with client; waiting" + " for renegotiation."); + } else { + tls->wasV2Handshake = 0; + } + } else { + /* Client-side */ + tls->wasV2Handshake = 1; + /* XXXX this can move, probably? -NM */ + if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) { + tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers"); + r = TOR_TLS_ERROR_MISC; + } + } + tls_log_errors(NULL, LOG_WARN, LD_NET, "finishing the handshake"); + return r; +} + +/** Shut down an open tls connection tls. When finished, returns + * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, + * or TOR_TLS_WANTWRITE. + */ +int +tor_tls_shutdown(tor_tls_t *tls) +{ + int r, err; + char buf[128]; + tor_assert(tls); + tor_assert(tls->ssl); + check_no_tls_errors(); + + while (1) { + if (tls->state == TOR_TLS_ST_SENTCLOSE) { + /* If we've already called shutdown once to send a close message, + * we read until the other side has closed too. + */ + do { + r = SSL_read(tls->ssl, buf, 128); + } while (r>0); + err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down", + LOG_INFO, LD_NET); + if (err == TOR_TLS_ZERORETURN_) { + tls->state = TOR_TLS_ST_GOTCLOSE; + /* fall through... */ + } else { + return err; + } + } + + r = SSL_shutdown(tls->ssl); + if (r == 1) { + /* If shutdown returns 1, the connection is entirely closed. */ + tls->state = TOR_TLS_ST_CLOSED; + return TOR_TLS_DONE; + } + err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down", + LOG_INFO, LD_NET); + if (err == TOR_TLS_SYSCALL_) { + /* The underlying TCP connection closed while we were shutting down. */ + tls->state = TOR_TLS_ST_CLOSED; + return TOR_TLS_DONE; + } else if (err == TOR_TLS_ZERORETURN_) { + /* The TLS connection says that it sent a shutdown record, but + * isn't done shutting down yet. Make sure that this hasn't + * happened before, then go back to the start of the function + * and try to read. + */ + if (tls->state == TOR_TLS_ST_GOTCLOSE || + tls->state == TOR_TLS_ST_SENTCLOSE) { + log_warn(LD_NET, + "TLS returned \"half-closed\" value while already half-closed"); + return TOR_TLS_ERROR_MISC; + } + tls->state = TOR_TLS_ST_SENTCLOSE; + /* fall through ... */ + } else { + return err; + } + } /* end loop */ +} + +/** Return true iff this TLS connection is authenticated. + */ +int +tor_tls_peer_has_cert(tor_tls_t *tls) +{ + X509 *cert; + cert = SSL_get_peer_certificate(tls->ssl); + tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate"); + if (!cert) + return 0; + X509_free(cert); + return 1; +} + +/** Return a newly allocated copy of the peer certificate, or NULL if there + * isn't one. */ +MOCK_IMPL(tor_x509_cert_t *, +tor_tls_get_peer_cert,(tor_tls_t *tls)) +{ + X509 *cert; + cert = SSL_get_peer_certificate(tls->ssl); + tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate"); + if (!cert) + return NULL; + return tor_x509_cert_new(cert); +} + +/** Return a newly allocated copy of the cerficate we used on the connection, + * or NULL if somehow we didn't use one. */ +MOCK_IMPL(tor_x509_cert_t *, +tor_tls_get_own_cert,(tor_tls_t *tls)) +{ + X509 *cert = SSL_get_certificate(tls->ssl); + tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, + "getting own-connection certificate"); + if (!cert) + return NULL; + /* Fun inconsistency: SSL_get_peer_certificate increments the reference + * count, but SSL_get_certificate does not. */ + X509 *duplicate = X509_dup(cert); + if (BUG(duplicate == NULL)) + return NULL; + return tor_x509_cert_new(duplicate); +} + +/** Helper function: try to extract a link certificate and an identity + * certificate from tls, and store them in *cert_out and + * *id_cert_out respectively. Log all messages at level + * severity. + * + * Note that a reference is added to cert_out, so it needs to be + * freed. id_cert_out doesn't. */ +MOCK_IMPL(void, +try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, + X509 **cert_out, X509 **id_cert_out)) +{ + X509 *cert = NULL, *id_cert = NULL; + STACK_OF(X509) *chain = NULL; + int num_in_chain, i; + *cert_out = *id_cert_out = NULL; + if (!(cert = SSL_get_peer_certificate(tls->ssl))) + return; + *cert_out = cert; + if (!(chain = SSL_get_peer_cert_chain(tls->ssl))) + return; + num_in_chain = sk_X509_num(chain); + /* 1 means we're receiving (server-side), and it's just the id_cert. + * 2 means we're connecting (client-side), and it's both the link + * cert and the id_cert. + */ + if (num_in_chain < 1) { + log_fn(severity,LD_PROTOCOL, + "Unexpected number of certificates in chain (%d)", + num_in_chain); + return; + } + for (i=0; iidentity_key
to the identity certificate's key and return + * 0. Else, return -1 and log complaints with log-level severity. + */ +int +tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key) +{ + X509 *cert = NULL, *id_cert = NULL; + EVP_PKEY *id_pkey = NULL; + RSA *rsa; + int r = -1; + + check_no_tls_errors(); + *identity_key = NULL; + + try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert); + if (!cert) + goto done; + if (!id_cert) { + log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found"); + goto done; + } + tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate"); + + if (!(id_pkey = X509_get_pubkey(id_cert)) || + X509_verify(cert, id_pkey) <= 0) { + log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0"); + tls_log_errors(tls, severity, LD_HANDSHAKE, "verifying certificate"); + goto done; + } + + rsa = EVP_PKEY_get1_RSA(id_pkey); + if (!rsa) + goto done; + *identity_key = crypto_new_pk_from_openssl_rsa_(rsa); + + r = 0; + + done: + if (cert) + X509_free(cert); + if (id_pkey) + EVP_PKEY_free(id_pkey); + + /* This should never get invoked, but let's make sure in case OpenSSL + * acts unexpectedly. */ + tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "finishing tor_tls_verify"); + + return r; +} + +/** Check whether the certificate set on the connection tls is expired + * give or take past_tolerance seconds, or not-yet-valid give or take + * future_tolerance seconds. Return 0 for valid, -1 for failure. + * + * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime. + */ +int +tor_tls_check_lifetime(int severity, tor_tls_t *tls, + time_t now, + int past_tolerance, int future_tolerance) +{ + X509 *cert; + int r = -1; + + if (!(cert = SSL_get_peer_certificate(tls->ssl))) + goto done; + + if (tor_x509_check_cert_lifetime_internal(severity, cert, now, + past_tolerance, + future_tolerance) < 0) + goto done; + + r = 0; + done: + if (cert) + X509_free(cert); + /* Not expected to get invoked */ + tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime"); + + return r; +} + +/** Return the number of bytes available for reading from tls. + */ +int +tor_tls_get_pending_bytes(tor_tls_t *tls) +{ + tor_assert(tls); + return SSL_pending(tls->ssl); +} + +/** If tls requires that the next write be of a particular size, + * return that size. Otherwise, return 0. */ +size_t +tor_tls_get_forced_write_size(tor_tls_t *tls) +{ + return tls->wantwrite_n; +} + +/** Sets n_read and n_written to the number of bytes read and written, + * respectively, on the raw socket used by tls since the last time this + * function was called on tls. */ +void +tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written) +{ + BIO *wbio, *tmpbio; + unsigned long r, w; + r = (unsigned long) BIO_number_read(SSL_get_rbio(tls->ssl)); + /* We want the number of bytes actually for real written. Unfortunately, + * sometimes OpenSSL replaces the wbio on tls->ssl with a buffering bio, + * which makes the answer turn out wrong. Let's cope with that. Note + * that this approach will fail if we ever replace tls->ssl's BIOs with + * buffering bios for reasons of our own. As an alternative, we could + * save the original BIO for tls->ssl in the tor_tls_t structure, but + * that would be tempting fate. */ + wbio = SSL_get_wbio(tls->ssl); +#if OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) + /* BIO structure is opaque as of OpenSSL 1.1.0-pre5-dev. Again, not + * supposed to use this form of the version macro, but the OpenSSL developers + * introduced major API changes in the pre-release stage. + */ + if (BIO_method_type(wbio) == BIO_TYPE_BUFFER && + (tmpbio = BIO_next(wbio)) != NULL) + wbio = tmpbio; +#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5)) */ + if (wbio->method == BIO_f_buffer() && (tmpbio = BIO_next(wbio)) != NULL) + wbio = tmpbio; +#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) */ + w = (unsigned long) BIO_number_written(wbio); + + /* We are ok with letting these unsigned ints go "negative" here: + * If we wrapped around, this should still give us the right answer, unless + * we wrapped around by more than ULONG_MAX since the last time we called + * this function. + */ + *n_read = (size_t)(r - tls->last_read_count); + *n_written = (size_t)(w - tls->last_write_count); + if (*n_read > INT_MAX || *n_written > INT_MAX) { + log_warn(LD_BUG, "Preposterously large value in tor_tls_get_n_raw_bytes. " + "r=%lu, last_read=%lu, w=%lu, last_written=%lu", + r, tls->last_read_count, w, tls->last_write_count); + } + total_bytes_written_by_tls += *n_written; + tls->last_read_count = r; + tls->last_write_count = w; +} + +/** Return a ratio of the bytes that TLS has sent to the bytes that we've told + * it to send. Used to track whether our TLS records are getting too tiny. */ +MOCK_IMPL(double, +tls_get_write_overhead_ratio,(void)) +{ + if (total_bytes_written_over_tls == 0) + return 1.0; + + return ((double)total_bytes_written_by_tls) / + ((double)total_bytes_written_over_tls); +} + +/** Implement check_no_tls_errors: If there are any pending OpenSSL + * errors, log an error message. */ +void +check_no_tls_errors_(const char *fname, int line) +{ + if (ERR_peek_error() == 0) + return; + log_warn(LD_CRYPTO, "Unhandled OpenSSL errors found at %s:%d: ", + tor_fix_source_file(fname), line); + tls_log_errors(NULL, LOG_WARN, LD_NET, NULL); +} + +/** Return true iff the initial TLS connection at tls did not use a v2 + * TLS handshake. Output is undefined if the handshake isn't finished. */ +int +tor_tls_used_v1_handshake(tor_tls_t *tls) +{ + return ! tls->wasV2Handshake; +} + +/** Return the number of server handshakes that we've noticed doing on + * tls. */ +int +tor_tls_get_num_server_handshakes(tor_tls_t *tls) +{ + return tls->server_handshake_count; +} + +/** Return true iff the server TLS connection tls got the renegotiation + * request it was waiting for. */ +int +tor_tls_server_got_renegotiate(tor_tls_t *tls) +{ + return tls->got_renegotiate; +} + +#ifndef HAVE_SSL_GET_CLIENT_RANDOM +static size_t +SSL_get_client_random(SSL *s, uint8_t *out, size_t len) +{ + if (len == 0) + return SSL3_RANDOM_SIZE; + tor_assert(len == SSL3_RANDOM_SIZE); + tor_assert(s->s3); + memcpy(out, s->s3->client_random, len); + return len; +} +#endif /* !defined(HAVE_SSL_GET_CLIENT_RANDOM) */ + +#ifndef HAVE_SSL_GET_SERVER_RANDOM +static size_t +SSL_get_server_random(SSL *s, uint8_t *out, size_t len) +{ + if (len == 0) + return SSL3_RANDOM_SIZE; + tor_assert(len == SSL3_RANDOM_SIZE); + tor_assert(s->s3); + memcpy(out, s->s3->server_random, len); + return len; +} +#endif /* !defined(HAVE_SSL_GET_SERVER_RANDOM) */ + +#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY +size_t +SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len) +{ + tor_assert(s); + if (len == 0) + return s->master_key_length; + tor_assert(len == (size_t)s->master_key_length); + tor_assert(out); + memcpy(out, s->master_key, len); + return len; +} +#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */ + +/** Set the DIGEST256_LEN buffer at secrets_out to the value used in + * the v3 handshake to prove that the client knows the TLS secrets for the + * connection tls. Return 0 on success, -1 on failure. + */ +MOCK_IMPL(int, +tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out)) +{ +#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification" + uint8_t buf[128]; + size_t len; + tor_assert(tls); + + SSL *const ssl = tls->ssl; + SSL_SESSION *const session = SSL_get_session(ssl); + + tor_assert(ssl); + tor_assert(session); + + const size_t server_random_len = SSL_get_server_random(ssl, NULL, 0); + const size_t client_random_len = SSL_get_client_random(ssl, NULL, 0); + const size_t master_key_len = SSL_SESSION_get_master_key(session, NULL, 0); + + tor_assert(server_random_len); + tor_assert(client_random_len); + tor_assert(master_key_len); + + len = client_random_len + server_random_len + strlen(TLSSECRET_MAGIC) + 1; + tor_assert(len <= sizeof(buf)); + + { + size_t r = SSL_get_client_random(ssl, buf, client_random_len); + tor_assert(r == client_random_len); + } + + { + size_t r = SSL_get_server_random(ssl, + buf+client_random_len, + server_random_len); + tor_assert(r == server_random_len); + } + + uint8_t *master_key = tor_malloc_zero(master_key_len); + { + size_t r = SSL_SESSION_get_master_key(session, master_key, master_key_len); + tor_assert(r == master_key_len); + } + + uint8_t *nextbuf = buf + client_random_len + server_random_len; + memcpy(nextbuf, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1); + + /* + The value is an HMAC, using the TLS master key as the HMAC key, of + client_random | server_random | TLSSECRET_MAGIC + */ + crypto_hmac_sha256((char*)secrets_out, + (char*)master_key, + master_key_len, + (char*)buf, len); + memwipe(buf, 0, sizeof(buf)); + memwipe(master_key, 0, master_key_len); + tor_free(master_key); + + return 0; +} + +/** Using the RFC5705 key material exporting construction, and the + * provided context (context_len bytes long) and + * label (a NUL-terminated string), compute a 32-byte secret in + * secrets_out that only the parties to this TLS session can + * compute. Return 0 on success and -1 on failure. + */ +MOCK_IMPL(int, +tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out, + const uint8_t *context, + size_t context_len, + const char *label)) +{ + tor_assert(tls); + tor_assert(tls->ssl); + + int r = SSL_export_keying_material(tls->ssl, + secrets_out, DIGEST256_LEN, + label, strlen(label), + context, context_len, 1); + return (r == 1) ? 0 : -1; +} + +/** Examine the amount of memory used and available for buffers in tls. + * Set *rbuf_capacity to the amount of storage allocated for the read + * buffer and *rbuf_bytes to the amount actually used. + * Set *wbuf_capacity to the amount of storage allocated for the write + * buffer and *wbuf_bytes to the amount actually used. + * + * Return 0 on success, -1 on failure.*/ +int +tor_tls_get_buffer_sizes(tor_tls_t *tls, + size_t *rbuf_capacity, size_t *rbuf_bytes, + size_t *wbuf_capacity, size_t *wbuf_bytes) +{ +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) + (void)tls; + (void)rbuf_capacity; + (void)rbuf_bytes; + (void)wbuf_capacity; + (void)wbuf_bytes; + + return -1; +#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)) */ + if (tls->ssl->s3->rbuf.buf) + *rbuf_capacity = tls->ssl->s3->rbuf.len; + else + *rbuf_capacity = 0; + if (tls->ssl->s3->wbuf.buf) + *wbuf_capacity = tls->ssl->s3->wbuf.len; + else + *wbuf_capacity = 0; + *rbuf_bytes = tls->ssl->s3->rbuf.left; + *wbuf_bytes = tls->ssl->s3->wbuf.left; + return 0; +#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */ +} + +/** Check whether the ECC group requested is supported by the current OpenSSL + * library instance. Return 1 if the group is supported, and 0 if not. + */ +int +evaluate_ecgroup_for_tls(const char *ecgroup) +{ + EC_KEY *ec_key; + int nid; + int ret; + + if (!ecgroup) + nid = NID_tor_default_ecdhe_group; + else if (!strcasecmp(ecgroup, "P256")) + nid = NID_X9_62_prime256v1; + else if (!strcasecmp(ecgroup, "P224")) + nid = NID_secp224r1; + else + return 0; + + ec_key = EC_KEY_new_by_curve_name(nid); + ret = (ec_key != NULL); + EC_KEY_free(ec_key); + + return ret; +} diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index 8bed6f5a19..dbf1dd927c 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -4,86 +4,15 @@ /* See LICENSE for licensing information */ /** - * \file x509.c + * \file x509_openssl.c * \brief Wrapper functions to present a consistent interface to - * X.509 functions from OpenSSL. + * X.509 functions. **/ #define TOR_X509_PRIVATE #include "lib/tls/x509.h" -#include "lib/tls/tortls.h" -#include "lib/crypt_ops/crypto_rand.h" -#include "lib/crypt_ops/crypto_util.h" -#include "lib/crypt_ops/compat_openssl.h" - -/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in - * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ -DISABLE_GCC_WARNING(redundant-decls) - -#include - -#ifdef OPENSSL_NO_EC -#error "We require OpenSSL with ECC support" -#endif - -#include -#include -#include -#include -#include - -ENABLE_GCC_WARNING(redundant-decls) - -#include "lib/log/log.h" #include "lib/log/util_bug.h" -#include "lib/ctime/di_ops.h" -#include "lib/encoding/time_fmt.h" - -#include -#include - -#ifdef OPENSSL_1_1_API -#define X509_get_notBefore_const(cert) \ - X509_get0_notBefore(cert) -#define X509_get_notAfter_const(cert) \ - X509_get0_notAfter(cert) -#ifndef X509_get_notBefore -#define X509_get_notBefore(cert) \ - X509_getm_notBefore(cert) -#endif -#ifndef X509_get_notAfter -#define X509_get_notAfter(cert) \ - X509_getm_notAfter(cert) -#endif -#else /* ! OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */ -#define X509_get_notBefore_const(cert) \ - ((const ASN1_TIME*) X509_get_notBefore((X509 *)cert)) -#define X509_get_notAfter_const(cert) \ - ((const ASN1_TIME*) X509_get_notAfter((X509 *)cert)) -#endif - -/** Return a newly allocated X509 name with commonName cname. */ -static X509_NAME * -tor_x509_name_new(const char *cname) -{ - int nid; - X509_NAME *name; - /* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */ - if (!(name = X509_NAME_new())) - return NULL; - if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error; - if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC, - (unsigned char*)cname, -1, -1, 0))) - goto error; - /* LCOV_EXCL_BR_STOP */ - return name; - - /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/ - error: - X509_NAME_free(name); - return NULL; - /* LCOV_EXCL_STOP */ -} +#include "lib/crypt_ops/crypto_rand.h" /** Choose the start and end times for a certificate */ void @@ -121,221 +50,6 @@ tor_tls_pick_certificate_lifetime(time_t now, *end_time_out = end_time; } -/** Generate and sign an X509 certificate with the public key rsa, - * signed by the private key rsa_sign. The commonName of the - * certificate will be cname; the commonName of the issuer will be - * cname_sign. The cert will be valid for cert_lifetime - * seconds, starting from some time in the past. - * - * Return a certificate on success, NULL on failure. - */ -MOCK_IMPL(X509 *, -tor_tls_create_certificate,(crypto_pk_t *rsa, - crypto_pk_t *rsa_sign, - const char *cname, - const char *cname_sign, - unsigned int cert_lifetime)) -{ - /* OpenSSL generates self-signed certificates with random 64-bit serial - * numbers, so let's do that too. */ -#define SERIAL_NUMBER_SIZE 8 - - time_t start_time, end_time; - BIGNUM *serial_number = NULL; - unsigned char serial_tmp[SERIAL_NUMBER_SIZE]; - EVP_PKEY *sign_pkey = NULL, *pkey=NULL; - X509 *x509 = NULL; - X509_NAME *name = NULL, *name_issuer=NULL; - - tor_tls_init(); - - time_t now = time(NULL); - - tor_tls_pick_certificate_lifetime(now, cert_lifetime, - &start_time, &end_time); - - tor_assert(rsa); - tor_assert(cname); - tor_assert(rsa_sign); - tor_assert(cname_sign); - if (!(sign_pkey = crypto_pk_get_openssl_evp_pkey_(rsa_sign,1))) - goto error; - if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,0))) - goto error; - if (!(x509 = X509_new())) - goto error; - if (!(X509_set_version(x509, 2))) - goto error; - - { /* our serial number is 8 random bytes. */ - crypto_rand((char *)serial_tmp, sizeof(serial_tmp)); - if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL))) - goto error; - if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509)))) - goto error; - } - - if (!(name = tor_x509_name_new(cname))) - goto error; - if (!(X509_set_subject_name(x509, name))) - goto error; - if (!(name_issuer = tor_x509_name_new(cname_sign))) - goto error; - if (!(X509_set_issuer_name(x509, name_issuer))) - goto error; - - if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time)) - goto error; - if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time)) - goto error; - if (!X509_set_pubkey(x509, pkey)) - goto error; - - if (!X509_sign(x509, sign_pkey, EVP_sha256())) - goto error; - - goto done; - error: - if (x509) { - X509_free(x509); - x509 = NULL; - } - done: - tls_log_errors(NULL, LOG_WARN, LD_NET, "generating certificate"); - if (sign_pkey) - EVP_PKEY_free(sign_pkey); - if (pkey) - EVP_PKEY_free(pkey); - if (serial_number) - BN_clear_free(serial_number); - if (name) - X509_NAME_free(name); - if (name_issuer) - X509_NAME_free(name_issuer); - return x509; - -#undef SERIAL_NUMBER_SIZE -} - -/** Free all storage held in cert */ -void -tor_x509_cert_free_(tor_x509_cert_t *cert) -{ - if (! cert) - return; - if (cert->cert) - X509_free(cert->cert); - tor_free(cert->encoded); - memwipe(cert, 0x03, sizeof(*cert)); - /* LCOV_EXCL_BR_START since cert will never be NULL here */ - tor_free(cert); - /* LCOV_EXCL_BR_STOP */ -} - -/** - * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert". - * - * Steals a reference to x509_cert. - */ -MOCK_IMPL(tor_x509_cert_t *, -tor_x509_cert_new,(X509 *x509_cert)) -{ - tor_x509_cert_t *cert; - EVP_PKEY *pkey; - RSA *rsa; - int length; - unsigned char *buf = NULL; - - if (!x509_cert) - return NULL; - - length = i2d_X509(x509_cert, &buf); - cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - if (length <= 0 || buf == NULL) { - goto err; - } - cert->encoded_len = (size_t) length; - cert->encoded = tor_malloc(length); - memcpy(cert->encoded, buf, length); - OPENSSL_free(buf); - - cert->cert = x509_cert; - - crypto_common_digests(&cert->cert_digests, - (char*)cert->encoded, cert->encoded_len); - - if ((pkey = X509_get_pubkey(x509_cert)) && - (rsa = EVP_PKEY_get1_RSA(pkey))) { - crypto_pk_t *pk = crypto_new_pk_from_openssl_rsa_(rsa); - if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) { - crypto_pk_free(pk); - EVP_PKEY_free(pkey); - goto err; - } - - cert->pkey_digests_set = 1; - crypto_pk_free(pk); - EVP_PKEY_free(pkey); - } - - return cert; - err: - /* LCOV_EXCL_START for the same reason as the exclusion above */ - tor_free(cert); - log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate."); - X509_free(x509_cert); - return NULL; - /* LCOV_EXCL_STOP */ -} - -/** Return a new copy of cert. */ -tor_x509_cert_t * -tor_x509_cert_dup(const tor_x509_cert_t *cert) -{ - tor_assert(cert); - X509 *x509 = cert->cert; - return tor_x509_cert_new(X509_dup(x509)); -} - -/** Read a DER-encoded X509 cert, of length exactly certificate_len, - * from a certificate. Return a newly allocated tor_x509_cert_t on - * success and NULL on failure. */ -tor_x509_cert_t * -tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len) -{ - X509 *x509; - const unsigned char *cp = (const unsigned char *)certificate; - tor_x509_cert_t *newcert; - tor_assert(certificate); - check_no_tls_errors(); - - if (certificate_len > INT_MAX) - goto err; - - x509 = d2i_X509(NULL, &cp, (int)certificate_len); - - if (!x509) - goto err; /* Couldn't decode */ - if (cp - certificate != (int)certificate_len) { - X509_free(x509); - goto err; /* Didn't use all the bytes */ - } - newcert = tor_x509_cert_new(x509); - if (!newcert) { - goto err; - } - if (newcert->encoded_len != certificate_len || - fast_memneq(newcert->encoded, certificate, certificate_len)) { - /* Cert wasn't in DER */ - tor_x509_cert_free(newcert); - goto err; - } - return newcert; - err: - tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "decoding a certificate"); - return NULL; -} - /** Set *encoded_out and *size_out to cert's encoded DER * representation and length, respectively. */ void @@ -375,190 +89,3 @@ tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert) return &cert->cert_digests; } -/** - * Return a newly allocated copy of the public key that a certificate - * certifies. Watch out! This returns NULL if the cert's key is not RSA. - */ -crypto_pk_t * -tor_tls_cert_get_key(tor_x509_cert_t *cert) -{ - crypto_pk_t *result = NULL; - EVP_PKEY *pkey = X509_get_pubkey(cert->cert); - RSA *rsa; - if (!pkey) - return NULL; - rsa = EVP_PKEY_get1_RSA(pkey); - if (!rsa) { - EVP_PKEY_free(pkey); - return NULL; - } - result = crypto_new_pk_from_openssl_rsa_(rsa); - EVP_PKEY_free(pkey); - return result; -} - -/** Check whether cert is well-formed, currently live, and correctly - * signed by the public key in signing_cert. If check_rsa_1024, - * make sure that it has an RSA key with 1024 bits; otherwise, just check that - * the key is long enough. Return 1 if the cert is good, and 0 if it's bad or - * we couldn't check it. */ -int -tor_tls_cert_is_valid(int severity, - const tor_x509_cert_t *cert, - const tor_x509_cert_t *signing_cert, - time_t now, - int check_rsa_1024) -{ - check_no_tls_errors(); - EVP_PKEY *cert_key; - int r, key_ok = 0; - - if (!signing_cert || !cert) - goto bad; - - EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert); - if (!signing_key) - goto bad; - r = X509_verify(cert->cert, signing_key); - EVP_PKEY_free(signing_key); - if (r <= 0) - goto bad; - - /* okay, the signature checked out right. Now let's check the check the - * lifetime. */ - if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now, - 48*60*60, 30*24*60*60) < 0) - goto bad; - - cert_key = X509_get_pubkey(cert->cert); - if (check_rsa_1024 && cert_key) { - RSA *rsa = EVP_PKEY_get1_RSA(cert_key); -#ifdef OPENSSL_1_1_API - if (rsa && RSA_bits(rsa) == 1024) -#else - if (rsa && BN_num_bits(rsa->n) == 1024) -#endif - key_ok = 1; - if (rsa) - RSA_free(rsa); - } else if (cert_key) { - int min_bits = 1024; -#ifdef EVP_PKEY_EC - if (EVP_PKEY_base_id(cert_key) == EVP_PKEY_EC) - min_bits = 128; -#endif - if (EVP_PKEY_bits(cert_key) >= min_bits) - key_ok = 1; - } - EVP_PKEY_free(cert_key); - if (!key_ok) - goto bad; - - /* XXXX compare DNs or anything? */ - - return 1; - bad: - tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "checking a certificate"); - return 0; -} - -/** Warn that a certificate lifetime extends through a certain range. */ -static void -log_cert_lifetime(int severity, const X509 *cert, const char *problem, - time_t now) -{ - BIO *bio = NULL; - BUF_MEM *buf; - char *s1=NULL, *s2=NULL; - char mytime[33]; - struct tm tm; - size_t n; - - if (problem) - tor_log(severity, LD_GENERAL, - "Certificate %s. Either their clock is set wrong, or your clock " - "is wrong.", - problem); - - if (!(bio = BIO_new(BIO_s_mem()))) { - log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end; - } - if (!(ASN1_TIME_print(bio, X509_get_notBefore_const(cert)))) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime"); - goto end; - } - BIO_get_mem_ptr(bio, &buf); - s1 = tor_strndup(buf->data, buf->length); - - (void)BIO_reset(bio); - if (!(ASN1_TIME_print(bio, X509_get_notAfter_const(cert)))) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime"); - goto end; - } - BIO_get_mem_ptr(bio, &buf); - s2 = tor_strndup(buf->data, buf->length); - - n = strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm)); - if (n > 0) { - tor_log(severity, LD_GENERAL, - "(certificate lifetime runs from %s through %s. Your time is %s.)", - s1,s2,mytime); - } else { - tor_log(severity, LD_GENERAL, - "(certificate lifetime runs from %s through %s. " - "Couldn't get your time.)", - s1, s2); - } - - end: - /* Not expected to get invoked */ - tls_log_errors(NULL, LOG_WARN, LD_NET, "getting certificate lifetime"); - if (bio) - BIO_free(bio); - tor_free(s1); - tor_free(s2); -} - -/** Helper: check whether cert is expired give or take - * past_tolerance seconds, or not-yet-valid give or take - * future_tolerance seconds. (Relative to the current time - * now.) If it is live, return 0. If it is not live, log a message - * and return -1. */ -int -tor_x509_check_cert_lifetime_internal(int severity, const X509 *cert, - time_t now, - int past_tolerance, int future_tolerance) -{ - time_t t; - - t = now + future_tolerance; - if (X509_cmp_time(X509_get_notBefore_const(cert), &t) > 0) { - log_cert_lifetime(severity, cert, "not yet valid", now); - return -1; - } - t = now - past_tolerance; - if (X509_cmp_time(X509_get_notAfter_const(cert), &t) < 0) { - log_cert_lifetime(severity, cert, "already expired", now); - return -1; - } - - return 0; -} - -#ifdef TOR_UNIT_TESTS -/* Testing only: return a new x509 cert with the same contents as inp, - but with the expiration time new_expiration_time, signed with - signing_key. */ -STATIC tor_x509_cert_t * -tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp, - time_t new_expiration_time, - crypto_pk_t *signing_key) -{ - X509 *newc = X509_dup(inp->cert); - X509_time_adj(X509_get_notAfter(newc), 0, &new_expiration_time); - EVP_PKEY *pk = crypto_pk_get_openssl_evp_pkey_(signing_key, 1); - tor_assert(X509_sign(newc, pk, EVP_sha256())); - EVP_PKEY_free(pk); - return tor_x509_cert_new(newc); -} -#endif /* defined(TOR_UNIT_TESTS) */ diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h index e3dfcf3934..f75d15d7e9 100644 --- a/src/lib/tls/x509.h +++ b/src/lib/tls/x509.h @@ -17,7 +17,9 @@ /* Opaque structure to hold an X509 certificate. */ typedef struct tor_x509_cert_t tor_x509_cert_t; -#ifdef ENABLE_OPENSSL +#ifdef ENABLE_NSS +typedef struct CERTCertificateStr tor_x509_cert_impl_t; +#elif defined(ENABLE_OPENSSL) typedef struct x509_st tor_x509_cert_impl_t; #endif diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c new file mode 100644 index 0000000000..e856c95187 --- /dev/null +++ b/src/lib/tls/x509_nss.c @@ -0,0 +1,122 @@ +/* Copyright (c) 2003, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file x509_nss.c + * \brief Wrapper functions to present a consistent interface to + * X.509 functions from NSS. + **/ + +#define TOR_X509_PRIVATE +#include "lib/tls/x509.h" +#include "lib/tls/tortls.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/log/util_bug.h" + +MOCK_IMPL(tor_x509_cert_impl_t *, +tor_tls_create_certificate,(crypto_pk_t *rsa, + crypto_pk_t *rsa_sign, + const char *cname, + const char *cname_sign, + unsigned int cert_lifetime)) +{ + tor_assert(rsa); + tor_assert(rsa_sign); + tor_assert(cname); + tor_assert(cname_sign); + (void) cert_lifetime; + // XXXX + return NULL; +} + +MOCK_IMPL(tor_x509_cert_t *, +tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert)) +{ + tor_assert(x509_cert); + // XXXX + return NULL; +} + +tor_x509_cert_t * +tor_x509_cert_dup(const tor_x509_cert_t *cert) +{ + tor_assert(cert); + // XXXX + return NULL; +} + +void +tor_x509_cert_free_(tor_x509_cert_t *cert) +{ + (void)cert; + // XXXX +} + +tor_x509_cert_t * +tor_x509_cert_decode(const uint8_t *certificate, + size_t certificate_len) +{ + tor_assert(certificate); + (void) certificate_len; + // XXXX + return NULL; +} + +crypto_pk_t * +tor_tls_cert_get_key(tor_x509_cert_t *cert) +{ + tor_assert(cert); + // XXXXX + return NULL; +} + +int +tor_tls_cert_is_valid(int severity, + const tor_x509_cert_t *cert, + const tor_x509_cert_t *signing_cert, + time_t now, + int check_rsa_1024) +{ + tor_assert(cert); + tor_assert(signing_cert); + (void)severity; + (void)now; + (void)check_rsa_1024; + // XXXXX + + return 0; +} + +int +tor_x509_check_cert_lifetime_internal(int severity, + const tor_x509_cert_impl_t *cert, + time_t now, + int past_tolerance, + int future_tolerance) +{ + tor_assert(cert); + (void)severity; + (void)now; + (void)past_tolerance; + (void)future_tolerance; + // XXXX + return -1; +} + +#ifdef TOR_UNIT_TESTS +tor_x509_cert_t * +tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp, + time_t new_expiration_time, + crypto_pk_t *signing_key) +{ + tor_assert(inp); + tor_assert(signing_key); + (void)new_expiration_time; + + // XXXX + return NULL; +} +#endif diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c new file mode 100644 index 0000000000..c003d4704b --- /dev/null +++ b/src/lib/tls/x509_openssl.c @@ -0,0 +1,489 @@ +/* Copyright (c) 2003, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file x509_openssl.c + * \brief Wrapper functions to present a consistent interface to + * X.509 functions from OpenSSL. + **/ + +#define TOR_X509_PRIVATE +#include "lib/tls/x509.h" +#include "lib/tls/tortls.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/compat_openssl.h" + +/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in + * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ +DISABLE_GCC_WARNING(redundant-decls) + +#include + +#ifdef OPENSSL_NO_EC +#error "We require OpenSSL with ECC support" +#endif + +#include +#include +#include +#include +#include + +ENABLE_GCC_WARNING(redundant-decls) + +#include "lib/log/log.h" +#include "lib/log/util_bug.h" +#include "lib/ctime/di_ops.h" +#include "lib/encoding/time_fmt.h" + +#include +#include + +#ifdef OPENSSL_1_1_API +#define X509_get_notBefore_const(cert) \ + X509_get0_notBefore(cert) +#define X509_get_notAfter_const(cert) \ + X509_get0_notAfter(cert) +#ifndef X509_get_notBefore +#define X509_get_notBefore(cert) \ + X509_getm_notBefore(cert) +#endif +#ifndef X509_get_notAfter +#define X509_get_notAfter(cert) \ + X509_getm_notAfter(cert) +#endif +#else /* ! OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */ +#define X509_get_notBefore_const(cert) \ + ((const ASN1_TIME*) X509_get_notBefore((X509 *)cert)) +#define X509_get_notAfter_const(cert) \ + ((const ASN1_TIME*) X509_get_notAfter((X509 *)cert)) +#endif + +/** Return a newly allocated X509 name with commonName cname. */ +static X509_NAME * +tor_x509_name_new(const char *cname) +{ + int nid; + X509_NAME *name; + /* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */ + if (!(name = X509_NAME_new())) + return NULL; + if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error; + if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC, + (unsigned char*)cname, -1, -1, 0))) + goto error; + /* LCOV_EXCL_BR_STOP */ + return name; + + /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/ + error: + X509_NAME_free(name); + return NULL; + /* LCOV_EXCL_STOP */ +} + +/** Generate and sign an X509 certificate with the public key rsa, + * signed by the private key rsa_sign. The commonName of the + * certificate will be cname; the commonName of the issuer will be + * cname_sign. The cert will be valid for cert_lifetime + * seconds, starting from some time in the past. + * + * Return a certificate on success, NULL on failure. + */ +MOCK_IMPL(X509 *, +tor_tls_create_certificate,(crypto_pk_t *rsa, + crypto_pk_t *rsa_sign, + const char *cname, + const char *cname_sign, + unsigned int cert_lifetime)) +{ + /* OpenSSL generates self-signed certificates with random 64-bit serial + * numbers, so let's do that too. */ +#define SERIAL_NUMBER_SIZE 8 + + time_t start_time, end_time; + BIGNUM *serial_number = NULL; + unsigned char serial_tmp[SERIAL_NUMBER_SIZE]; + EVP_PKEY *sign_pkey = NULL, *pkey=NULL; + X509 *x509 = NULL; + X509_NAME *name = NULL, *name_issuer=NULL; + + tor_tls_init(); + + time_t now = time(NULL); + + tor_tls_pick_certificate_lifetime(now, cert_lifetime, + &start_time, &end_time); + + tor_assert(rsa); + tor_assert(cname); + tor_assert(rsa_sign); + tor_assert(cname_sign); + if (!(sign_pkey = crypto_pk_get_openssl_evp_pkey_(rsa_sign,1))) + goto error; + if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,0))) + goto error; + if (!(x509 = X509_new())) + goto error; + if (!(X509_set_version(x509, 2))) + goto error; + + { /* our serial number is 8 random bytes. */ + crypto_rand((char *)serial_tmp, sizeof(serial_tmp)); + if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL))) + goto error; + if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509)))) + goto error; + } + + if (!(name = tor_x509_name_new(cname))) + goto error; + if (!(X509_set_subject_name(x509, name))) + goto error; + if (!(name_issuer = tor_x509_name_new(cname_sign))) + goto error; + if (!(X509_set_issuer_name(x509, name_issuer))) + goto error; + + if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time)) + goto error; + if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time)) + goto error; + if (!X509_set_pubkey(x509, pkey)) + goto error; + + if (!X509_sign(x509, sign_pkey, EVP_sha256())) + goto error; + + goto done; + error: + if (x509) { + X509_free(x509); + x509 = NULL; + } + done: + tls_log_errors(NULL, LOG_WARN, LD_NET, "generating certificate"); + if (sign_pkey) + EVP_PKEY_free(sign_pkey); + if (pkey) + EVP_PKEY_free(pkey); + if (serial_number) + BN_clear_free(serial_number); + if (name) + X509_NAME_free(name); + if (name_issuer) + X509_NAME_free(name_issuer); + return x509; + +#undef SERIAL_NUMBER_SIZE +} + +/** Free all storage held in cert */ +void +tor_x509_cert_free_(tor_x509_cert_t *cert) +{ + if (! cert) + return; + if (cert->cert) + X509_free(cert->cert); + tor_free(cert->encoded); + memwipe(cert, 0x03, sizeof(*cert)); + /* LCOV_EXCL_BR_START since cert will never be NULL here */ + tor_free(cert); + /* LCOV_EXCL_BR_STOP */ +} + +/** + * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert". + * + * Steals a reference to x509_cert. + */ +MOCK_IMPL(tor_x509_cert_t *, +tor_x509_cert_new,(X509 *x509_cert)) +{ + tor_x509_cert_t *cert; + EVP_PKEY *pkey; + RSA *rsa; + int length; + unsigned char *buf = NULL; + + if (!x509_cert) + return NULL; + + length = i2d_X509(x509_cert, &buf); + cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); + if (length <= 0 || buf == NULL) { + goto err; + } + cert->encoded_len = (size_t) length; + cert->encoded = tor_malloc(length); + memcpy(cert->encoded, buf, length); + OPENSSL_free(buf); + + cert->cert = x509_cert; + + crypto_common_digests(&cert->cert_digests, + (char*)cert->encoded, cert->encoded_len); + + if ((pkey = X509_get_pubkey(x509_cert)) && + (rsa = EVP_PKEY_get1_RSA(pkey))) { + crypto_pk_t *pk = crypto_new_pk_from_openssl_rsa_(rsa); + if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) { + crypto_pk_free(pk); + EVP_PKEY_free(pkey); + goto err; + } + + cert->pkey_digests_set = 1; + crypto_pk_free(pk); + EVP_PKEY_free(pkey); + } + + return cert; + err: + /* LCOV_EXCL_START for the same reason as the exclusion above */ + tor_free(cert); + log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate."); + X509_free(x509_cert); + return NULL; + /* LCOV_EXCL_STOP */ +} + +/** Return a new copy of cert. */ +tor_x509_cert_t * +tor_x509_cert_dup(const tor_x509_cert_t *cert) +{ + tor_assert(cert); + X509 *x509 = cert->cert; + return tor_x509_cert_new(X509_dup(x509)); +} + +/** Read a DER-encoded X509 cert, of length exactly certificate_len, + * from a certificate. Return a newly allocated tor_x509_cert_t on + * success and NULL on failure. */ +tor_x509_cert_t * +tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len) +{ + X509 *x509; + const unsigned char *cp = (const unsigned char *)certificate; + tor_x509_cert_t *newcert; + tor_assert(certificate); + check_no_tls_errors(); + + if (certificate_len > INT_MAX) + goto err; + + x509 = d2i_X509(NULL, &cp, (int)certificate_len); + + if (!x509) + goto err; /* Couldn't decode */ + if (cp - certificate != (int)certificate_len) { + X509_free(x509); + goto err; /* Didn't use all the bytes */ + } + newcert = tor_x509_cert_new(x509); + if (!newcert) { + goto err; + } + if (newcert->encoded_len != certificate_len || + fast_memneq(newcert->encoded, certificate, certificate_len)) { + /* Cert wasn't in DER */ + tor_x509_cert_free(newcert); + goto err; + } + return newcert; + err: + tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "decoding a certificate"); + return NULL; +} + +/** + * Return a newly allocated copy of the public key that a certificate + * certifies. Watch out! This returns NULL if the cert's key is not RSA. + */ +crypto_pk_t * +tor_tls_cert_get_key(tor_x509_cert_t *cert) +{ + crypto_pk_t *result = NULL; + EVP_PKEY *pkey = X509_get_pubkey(cert->cert); + RSA *rsa; + if (!pkey) + return NULL; + rsa = EVP_PKEY_get1_RSA(pkey); + if (!rsa) { + EVP_PKEY_free(pkey); + return NULL; + } + result = crypto_new_pk_from_openssl_rsa_(rsa); + EVP_PKEY_free(pkey); + return result; +} + +/** Check whether cert is well-formed, currently live, and correctly + * signed by the public key in signing_cert. If check_rsa_1024, + * make sure that it has an RSA key with 1024 bits; otherwise, just check that + * the key is long enough. Return 1 if the cert is good, and 0 if it's bad or + * we couldn't check it. */ +int +tor_tls_cert_is_valid(int severity, + const tor_x509_cert_t *cert, + const tor_x509_cert_t *signing_cert, + time_t now, + int check_rsa_1024) +{ + check_no_tls_errors(); + EVP_PKEY *cert_key; + int r, key_ok = 0; + + if (!signing_cert || !cert) + goto bad; + + EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert); + if (!signing_key) + goto bad; + r = X509_verify(cert->cert, signing_key); + EVP_PKEY_free(signing_key); + if (r <= 0) + goto bad; + + /* okay, the signature checked out right. Now let's check the check the + * lifetime. */ + if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now, + 48*60*60, 30*24*60*60) < 0) + goto bad; + + cert_key = X509_get_pubkey(cert->cert); + if (check_rsa_1024 && cert_key) { + RSA *rsa = EVP_PKEY_get1_RSA(cert_key); +#ifdef OPENSSL_1_1_API + if (rsa && RSA_bits(rsa) == 1024) +#else + if (rsa && BN_num_bits(rsa->n) == 1024) +#endif + key_ok = 1; + if (rsa) + RSA_free(rsa); + } else if (cert_key) { + int min_bits = 1024; +#ifdef EVP_PKEY_EC + if (EVP_PKEY_base_id(cert_key) == EVP_PKEY_EC) + min_bits = 128; +#endif + if (EVP_PKEY_bits(cert_key) >= min_bits) + key_ok = 1; + } + EVP_PKEY_free(cert_key); + if (!key_ok) + goto bad; + + /* XXXX compare DNs or anything? */ + + return 1; + bad: + tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "checking a certificate"); + return 0; +} + +/** Warn that a certificate lifetime extends through a certain range. */ +static void +log_cert_lifetime(int severity, const X509 *cert, const char *problem, + time_t now) +{ + BIO *bio = NULL; + BUF_MEM *buf; + char *s1=NULL, *s2=NULL; + char mytime[33]; + struct tm tm; + size_t n; + + if (problem) + tor_log(severity, LD_GENERAL, + "Certificate %s. Either their clock is set wrong, or your clock " + "is wrong.", + problem); + + if (!(bio = BIO_new(BIO_s_mem()))) { + log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end; + } + if (!(ASN1_TIME_print(bio, X509_get_notBefore_const(cert)))) { + tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime"); + goto end; + } + BIO_get_mem_ptr(bio, &buf); + s1 = tor_strndup(buf->data, buf->length); + + (void)BIO_reset(bio); + if (!(ASN1_TIME_print(bio, X509_get_notAfter_const(cert)))) { + tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime"); + goto end; + } + BIO_get_mem_ptr(bio, &buf); + s2 = tor_strndup(buf->data, buf->length); + + n = strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm)); + if (n > 0) { + tor_log(severity, LD_GENERAL, + "(certificate lifetime runs from %s through %s. Your time is %s.)", + s1,s2,mytime); + } else { + tor_log(severity, LD_GENERAL, + "(certificate lifetime runs from %s through %s. " + "Couldn't get your time.)", + s1, s2); + } + + end: + /* Not expected to get invoked */ + tls_log_errors(NULL, LOG_WARN, LD_NET, "getting certificate lifetime"); + if (bio) + BIO_free(bio); + tor_free(s1); + tor_free(s2); +} + +/** Helper: check whether cert is expired give or take + * past_tolerance seconds, or not-yet-valid give or take + * future_tolerance seconds. (Relative to the current time + * now.) If it is live, return 0. If it is not live, log a message + * and return -1. */ +int +tor_x509_check_cert_lifetime_internal(int severity, const X509 *cert, + time_t now, + int past_tolerance, int future_tolerance) +{ + time_t t; + + t = now + future_tolerance; + if (X509_cmp_time(X509_get_notBefore_const(cert), &t) > 0) { + log_cert_lifetime(severity, cert, "not yet valid", now); + return -1; + } + t = now - past_tolerance; + if (X509_cmp_time(X509_get_notAfter_const(cert), &t) < 0) { + log_cert_lifetime(severity, cert, "already expired", now); + return -1; + } + + return 0; +} + +#ifdef TOR_UNIT_TESTS +/* Testing only: return a new x509 cert with the same contents as inp, + but with the expiration time new_expiration_time, signed with + signing_key. */ +STATIC tor_x509_cert_t * +tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp, + time_t new_expiration_time, + crypto_pk_t *signing_key) +{ + X509 *newc = X509_dup(inp->cert); + X509_time_adj(X509_get_notAfter(newc), 0, &new_expiration_time); + EVP_PKEY *pk = crypto_pk_get_openssl_evp_pkey_(signing_key, 1); + tor_assert(X509_sign(newc, pk, EVP_sha256())); + EVP_PKEY_free(pk); + return tor_x509_cert_new(newc); +} +#endif /* defined(TOR_UNIT_TESTS) */ diff --git a/src/test/include.am b/src/test/include.am index 68372adc76..c2e08aa3df 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -184,6 +184,14 @@ src_test_test_SOURCES += \ src/test/testing_common.c \ src/test/testing_rsakeys.c \ src/ext/tinytest.c + +if USE_NSS +# ... +else +src_test_test_SOURCES += \ + src/test/test_tortls_openssl.c +endif + endif src_test_test_slow_SOURCES = diff --git a/src/test/test.c b/src/test/test.c index 94eae5a4f4..3b63f1c07e 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -919,6 +919,9 @@ struct testgroup_t testgroups[] = { { "status/" , status_tests }, { "storagedir/", storagedir_tests }, { "tortls/", tortls_tests }, +#ifndef ENABLE_NSS + { "tortls/openssl/", tortls_openssl_tests }, +#endif { "util/", util_tests }, { "util/format/", util_format_tests }, { "util/logging/", logging_tests }, diff --git a/src/test/test.h b/src/test/test.h index b3a73271ee..94aa9443d6 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -257,6 +257,7 @@ extern struct testcase_t socks_tests[]; extern struct testcase_t status_tests[]; extern struct testcase_t thread_tests[]; extern struct testcase_t tortls_tests[]; +extern struct testcase_t tortls_openssl_tests[]; extern struct testcase_t util_tests[]; extern struct testcase_t util_format_tests[]; extern struct testcase_t util_process_tests[]; diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index ec197ba997..d20bc5fa6c 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -2,7 +2,6 @@ /* See LICENSE for licensing information */ #define TORTLS_PRIVATE -#define TORTLS_OPENSSL_PRIVATE #define TOR_X509_PRIVATE #define LOG_PRIVATE #include "orconfig.h" @@ -11,64 +10,24 @@ #include #endif #include +#include #include "lib/cc/compat_compiler.h" -/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in - * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ -DISABLE_GCC_WARNING(redundant-decls) - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -ENABLE_GCC_WARNING(redundant-decls) - #include "core/or/or.h" #include "lib/log/log.h" #include "app/config/config.h" #include "lib/crypt_ops/compat_openssl.h" +#include "lib/tls/x509.h" #include "lib/tls/tortls.h" #include "lib/tls/tortls_st.h" -#include "lib/tls/x509.h" +#include "lib/tls/tortls_internal.h" #include "app/config/or_state_st.h" #include "test/test.h" #include "test/log_test_helpers.h" -#define NS_MODULE tortls -#ifndef HAVE_SSL_STATE -#define OPENSSL_OPAQUE -#endif - -#if defined(OPENSSL_OPAQUE) && !defined(LIBRESSL_VERSION_NUMBER) -#define SSL_STATE_STR "before SSL initialization" -#else -#define SSL_STATE_STR "before/accept initialization" -#endif - -#ifndef OPENSSL_OPAQUE -static SSL_METHOD * -give_me_a_test_method(void) -{ - SSL_METHOD *method = tor_malloc_zero(sizeof(SSL_METHOD)); - memcpy(method, TLSv1_method(), sizeof(SSL_METHOD)); - return method; -} - -static int -fake_num_ciphers(void) -{ - return 0; -} -#endif /* !defined(OPENSSL_OPAQUE) */ +#include "tinytest.h" static void test_tortls_errno_to_tls_error(void *data) @@ -120,66 +79,6 @@ mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert) return 1; } -static void -test_tortls_tor_tls_new(void *data) -{ - (void) data; - MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); - crypto_pk_t *key1 = NULL, *key2 = NULL; - SSL_METHOD *method = NULL; - - key1 = pk_generate(2); - key2 = pk_generate(3); - - tor_tls_t *tls = NULL; - tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, - key1, key2, 86400), OP_EQ, 0); - tls = tor_tls_new(-1, 0); - tt_want(tls); - tor_tls_free(tls); tls = NULL; - - SSL_CTX_free(client_tls_context->ctx); - client_tls_context->ctx = NULL; - tls = tor_tls_new(-1, 0); - tt_ptr_op(tls, OP_EQ, NULL); - -#ifndef OPENSSL_OPAQUE - method = give_me_a_test_method(); - SSL_CTX *ctx = SSL_CTX_new(method); - method->num_ciphers = fake_num_ciphers; - client_tls_context->ctx = ctx; - tls = tor_tls_new(-1, 0); - tt_ptr_op(tls, OP_EQ, NULL); -#endif /* !defined(OPENSSL_OPAQUE) */ - - done: - UNMOCK(tor_tls_cert_matches_key); - crypto_pk_free(key1); - crypto_pk_free(key2); - tor_tls_free(tls); - tor_free(method); - tor_tls_free_all(); -} - -#define NS_MODULE tortls -NS_DECL(void, logv, (int severity, log_domain_mask_t domain, - const char *funcname, const char *suffix, - const char *format, va_list ap)); - -static void -NS(logv)(int severity, log_domain_mask_t domain, - const char *funcname, const char *suffix, const char *format, - va_list ap) -{ - (void) severity; - (void) domain; - (void) funcname; - (void) suffix; - (void) format; - (void) ap; // XXXX look at this. - CALLED(logv)++; -} - static void test_tortls_tor_tls_get_error(void *data) { @@ -193,11 +92,10 @@ test_tortls_tor_tls_get_error(void *data) tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, key1, key2, 86400), OP_EQ, 0); tls = tor_tls_new(-1, 0); - NS_MOCK(logv); - tt_int_op(CALLED(logv), OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); tor_tls_get_error(tls, 0, 0, - (const char *)"test", 0, 0); - tt_int_op(CALLED(logv), OP_EQ, 1); + (const char *)"in unit test", LOG_WARN, LD_GENERAL); + expect_single_log_msg_containing("unexpected close while in unit test"); done: UNMOCK(tor_tls_cert_matches_key); @@ -207,332 +105,6 @@ test_tortls_tor_tls_get_error(void *data) tor_tls_free(tls); } -static void -library_init(void) -{ -#ifdef OPENSSL_1_1_API - OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); -#else - SSL_library_init(); - SSL_load_error_strings(); -#endif -} - -static void -test_tortls_get_state_description(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - char *buf; - SSL_CTX *ctx; - - library_init(); - ctx = SSL_CTX_new(SSLv23_method()); - - buf = tor_malloc_zero(1000); - tls = tor_malloc_zero(sizeof(tor_tls_t)); - - tor_tls_get_state_description(NULL, buf, 20); - tt_str_op(buf, OP_EQ, "(No SSL object)"); - - SSL_free(tls->ssl); - tls->ssl = NULL; - tor_tls_get_state_description(tls, buf, 20); - tt_str_op(buf, OP_EQ, "(No SSL object)"); - - tls->ssl = SSL_new(ctx); - tor_tls_get_state_description(tls, buf, 200); - tt_str_op(buf, OP_EQ, SSL_STATE_STR " in HANDSHAKE"); - - tls->state = TOR_TLS_ST_OPEN; - tor_tls_get_state_description(tls, buf, 200); - tt_str_op(buf, OP_EQ, SSL_STATE_STR " in OPEN"); - - tls->state = TOR_TLS_ST_GOTCLOSE; - tor_tls_get_state_description(tls, buf, 200); - tt_str_op(buf, OP_EQ, SSL_STATE_STR " in GOTCLOSE"); - - tls->state = TOR_TLS_ST_SENTCLOSE; - tor_tls_get_state_description(tls, buf, 200); - tt_str_op(buf, OP_EQ, SSL_STATE_STR " in SENTCLOSE"); - - tls->state = TOR_TLS_ST_CLOSED; - tor_tls_get_state_description(tls, buf, 200); - tt_str_op(buf, OP_EQ, SSL_STATE_STR " in CLOSED"); - - tls->state = TOR_TLS_ST_RENEGOTIATE; - tor_tls_get_state_description(tls, buf, 200); - tt_str_op(buf, OP_EQ, SSL_STATE_STR " in RENEGOTIATE"); - - tls->state = TOR_TLS_ST_BUFFEREVENT; - tor_tls_get_state_description(tls, buf, 200); - tt_str_op(buf, OP_EQ, SSL_STATE_STR); - - tls->state = 7; - tor_tls_get_state_description(tls, buf, 200); - tt_str_op(buf, OP_EQ, SSL_STATE_STR " in unknown TLS state"); - - done: - SSL_CTX_free(ctx); - SSL_free(tls->ssl); - tor_free(buf); - tor_free(tls); -} - -static void -test_tortls_get_by_ssl(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - tor_tls_t *res; - SSL_CTX *ctx; - SSL *ssl; - - library_init(); - tor_tls_allocate_tor_tls_object_ex_data_index(); - - ctx = SSL_CTX_new(SSLv23_method()); - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->magic = TOR_TLS_MAGIC; - - ssl = SSL_new(ctx); - - res = tor_tls_get_by_ssl(ssl); - tt_assert(!res); - - SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls); - - res = tor_tls_get_by_ssl(ssl); - tt_assert(res == tls); - - done: - SSL_free(ssl); - SSL_CTX_free(ctx); - tor_free(tls); -} - -static void -test_tortls_allocate_tor_tls_object_ex_data_index(void *ignored) -{ - (void)ignored; - int first; - - tor_tls_allocate_tor_tls_object_ex_data_index(); - - first = tor_tls_object_ex_data_index; - tor_tls_allocate_tor_tls_object_ex_data_index(); - tt_int_op(first, OP_EQ, tor_tls_object_ex_data_index); - - done: - (void)0; -} - -static void -test_tortls_log_one_error(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - SSL_CTX *ctx; - SSL *ssl = NULL; - - library_init(); - - ctx = SSL_CTX_new(SSLv23_method()); - tls = tor_malloc_zero(sizeof(tor_tls_t)); - setup_capture_of_logs(LOG_INFO); - - tor_tls_log_one_error(NULL, 0, LOG_WARN, 0, "something"); - expect_log_msg("TLS error while something: " - "(null) (in (null):(null):---)\n"); - - mock_clean_saved_logs(); - tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL); - expect_log_msg("TLS error: (null) " - "(in (null):(null):---)\n"); - - mock_clean_saved_logs(); - tls->address = tor_strdup("127.hello"); - tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL); - expect_log_msg("TLS error with 127.hello: " - "(null) (in (null):(null):---)\n"); - tor_free(tls->address); - - mock_clean_saved_logs(); - tls->address = tor_strdup("127.hello"); - tor_tls_log_one_error(tls, 0, LOG_WARN, 0, "blarg"); - expect_log_msg("TLS error while blarg with " - "127.hello: (null) (in (null):(null):---)\n"); - - mock_clean_saved_logs(); - tor_tls_log_one_error(tls, ERR_PACK(1, 2, 3), LOG_WARN, 0, NULL); - expect_log_msg("TLS error with 127.hello: " - "BN lib (in unknown library:(null):---)\n"); - - mock_clean_saved_logs(); - tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTP_REQUEST), - LOG_WARN, 0, NULL); - expect_log_severity(LOG_INFO); - - mock_clean_saved_logs(); - tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTPS_PROXY_REQUEST), - LOG_WARN, 0, NULL); - expect_log_severity(LOG_INFO); - - mock_clean_saved_logs(); - tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_LENGTH_MISMATCH), - LOG_WARN, 0, NULL); - expect_log_severity(LOG_INFO); - -#ifndef OPENSSL_1_1_API - mock_clean_saved_logs(); - tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_TOO_LARGE), - LOG_WARN, 0, NULL); - expect_log_severity(LOG_INFO); -#endif /* !defined(OPENSSL_1_1_API) */ - - mock_clean_saved_logs(); - tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNKNOWN_PROTOCOL), - LOG_WARN, 0, NULL); - expect_log_severity(LOG_INFO); - - mock_clean_saved_logs(); - tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNSUPPORTED_PROTOCOL), - LOG_WARN, 0, NULL); - expect_log_severity(LOG_INFO); - - tls->ssl = SSL_new(ctx); - - mock_clean_saved_logs(); - tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL); - expect_log_msg("TLS error with 127.hello: (null)" - " (in (null):(null):" SSL_STATE_STR ")\n"); - - done: - teardown_capture_of_logs(); - SSL_free(ssl); - SSL_CTX_free(ctx); - if (tls && tls->ssl) - SSL_free(tls->ssl); - if (tls) - tor_free(tls->address); - tor_free(tls); -} - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_get_error(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - int ret; - SSL_CTX *ctx; - - library_init(); - - ctx = SSL_CTX_new(SSLv23_method()); - setup_capture_of_logs(LOG_INFO); - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = SSL_new(ctx); - SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL); - - ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0); - tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_IO); - expect_log_msg("TLS error: unexpected close while" - " something (before/accept initialization)\n"); - - mock_clean_saved_logs(); - ret = tor_tls_get_error(tls, 2, 0, "something", LOG_WARN, 0); - tt_int_op(ret, OP_EQ, 0); - expect_no_log_entry(); - - mock_clean_saved_logs(); - ret = tor_tls_get_error(tls, 0, 1, "something", LOG_WARN, 0); - tt_int_op(ret, OP_EQ, -11); - expect_no_log_entry(); - - mock_clean_saved_logs(); - ERR_clear_error(); - ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99); - ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0); - tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); - expect_log_msg("TLS error while something: (null)" - " (in bignum routines:(null):before/accept initialization)\n"); - - mock_clean_saved_logs(); - ERR_clear_error(); - tls->ssl->rwstate = SSL_READING; - SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ; - ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0); - tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD); - expect_no_log_entry(); - - mock_clean_saved_logs(); - ERR_clear_error(); - tls->ssl->rwstate = SSL_READING; - SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE; - ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0); - tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE); - expect_no_log_entry(); - - mock_clean_saved_logs(); - ERR_clear_error(); - tls->ssl->rwstate = 0; - tls->ssl->shutdown = SSL_RECEIVED_SHUTDOWN; - tls->ssl->s3->warn_alert =SSL_AD_CLOSE_NOTIFY; - ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0); - tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE); - expect_log_entry(); - - mock_clean_saved_logs(); - ret = tor_tls_get_error(tls, 0, 2, "something", LOG_WARN, 0); - tt_int_op(ret, OP_EQ, -10); - expect_no_log_entry(); - - mock_clean_saved_logs(); - ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99); - ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0); - tt_int_op(ret, OP_EQ, -9); - expect_log_msg("TLS error while something: (null) (in system library:" - "connect:before/accept initialization)\n"); - - done: - teardown_capture_of_logs(); - SSL_free(tls->ssl); - tor_free(tls); - SSL_CTX_free(ctx); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -static void -test_tortls_always_accept_verify_cb(void *ignored) -{ - (void)ignored; - int ret; - - ret = always_accept_verify_cb(0, NULL); - tt_int_op(ret, OP_EQ, 1); - - done: - (void)0; -} - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_x509_cert_free(void *ignored) -{ - (void)ignored; - tor_x509_cert_t *cert; - - cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - tor_x509_cert_free(cert); - - cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - cert->cert = X509_new(); - cert->encoded = tor_malloc_zero(1); - tor_x509_cert_free(cert); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - static void test_tortls_x509_cert_get_id_digests(void *ignored) { @@ -557,159 +129,6 @@ test_tortls_x509_cert_get_id_digests(void *ignored) tor_free(d); } -#ifndef OPENSSL_OPAQUE -static int -fixed_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) -{ - (void) a; (void) b; - return 1; -} - -/* - * Use only for the matching fake_x509_free() call - */ -static X509 * -fake_x509_malloc(void) -{ - return tor_malloc_zero(sizeof(X509)); -} - -static void -fake_x509_free(X509 *cert) -{ - if (cert) { - if (cert->cert_info) { - if (cert->cert_info->key) { - if (cert->cert_info->key->pkey) { - tor_free(cert->cert_info->key->pkey); - } - tor_free(cert->cert_info->key); - } - tor_free(cert->cert_info); - } - tor_free(cert); - } -} - -static void -test_tortls_cert_matches_key(void *ignored) -{ - (void)ignored; - int res; - tor_tls_t *tls; - tor_x509_cert_t *cert; - X509 *one = NULL, *two = NULL; - EVP_PKEY_ASN1_METHOD *meth = EVP_PKEY_asn1_new(999, 0, NULL, NULL); - EVP_PKEY_asn1_set_public(meth, NULL, NULL, fixed_pub_cmp, NULL, NULL, NULL); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - one = fake_x509_malloc(); - one->references = 1; - two = fake_x509_malloc(); - two->references = 1; - - res = tor_tls_cert_matches_key(tls, cert); - tt_int_op(res, OP_EQ, 0); - - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); - tls->ssl->session->peer = one; - res = tor_tls_cert_matches_key(tls, cert); - tt_int_op(res, OP_EQ, 0); - - cert->cert = two; - res = tor_tls_cert_matches_key(tls, cert); - tt_int_op(res, OP_EQ, 0); - - one->cert_info = tor_malloc_zero(sizeof(X509_CINF)); - one->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY)); - one->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY)); - one->cert_info->key->pkey->references = 1; - one->cert_info->key->pkey->ameth = meth; - one->cert_info->key->pkey->type = 1; - - two->cert_info = tor_malloc_zero(sizeof(X509_CINF)); - two->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY)); - two->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY)); - two->cert_info->key->pkey->references = 1; - two->cert_info->key->pkey->ameth = meth; - two->cert_info->key->pkey->type = 2; - - res = tor_tls_cert_matches_key(tls, cert); - tt_int_op(res, OP_EQ, 0); - - one->cert_info->key->pkey->type = 1; - two->cert_info->key->pkey->type = 1; - res = tor_tls_cert_matches_key(tls, cert); - tt_int_op(res, OP_EQ, 1); - - done: - EVP_PKEY_asn1_free(meth); - tor_free(tls->ssl->session); - tor_free(tls->ssl); - tor_free(tls); - tor_free(cert); - fake_x509_free(one); - fake_x509_free(two); -} - -static void -test_tortls_cert_get_key(void *ignored) -{ - (void)ignored; - tor_x509_cert_t *cert = NULL; - crypto_pk_t *res = NULL; - cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - X509 *key = NULL; - key = fake_x509_malloc(); - key->references = 1; - - res = tor_tls_cert_get_key(cert); - tt_assert(!res); - - cert->cert = key; - key->cert_info = tor_malloc_zero(sizeof(X509_CINF)); - key->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY)); - key->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY)); - key->cert_info->key->pkey->references = 1; - key->cert_info->key->pkey->type = 2; - res = tor_tls_cert_get_key(cert); - tt_assert(!res); - - done: - fake_x509_free(key); - tor_free(cert); - crypto_pk_free(res); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -static void -test_tortls_get_my_client_auth_key(void *ignored) -{ - (void)ignored; - crypto_pk_t *ret; - crypto_pk_t *expected; - tor_tls_context_t *ctx; - RSA *k = RSA_new(); - - ctx = tor_malloc_zero(sizeof(tor_tls_context_t)); - expected = crypto_new_pk_from_openssl_rsa_(k); - ctx->auth_key = expected; - - client_tls_context = NULL; - ret = tor_tls_get_my_client_auth_key(); - tt_assert(!ret); - - client_tls_context = ctx; - ret = tor_tls_get_my_client_auth_key(); - tt_assert(ret == expected); - - done: - tor_free(expected); - tor_free(ctx); -} - static void test_tortls_get_my_certs(void *ignored) { @@ -745,437 +164,6 @@ test_tortls_get_my_certs(void *ignored) (void)1; } -#ifndef HAVE_SSL_GET_CLIENT_CIPHERS -static SSL_CIPHER * -get_cipher_by_name(const char *name) -{ - int i; - const SSL_METHOD *method = SSLv23_method(); - int num = method->num_ciphers(); - - for (i = 0; i < num; ++i) { - const SSL_CIPHER *cipher = method->get_cipher(i); - const char *ciphername = SSL_CIPHER_get_name(cipher); - if (!strcmp(ciphername, name)) { - return (SSL_CIPHER *)cipher; - } - } - - return NULL; -} -#endif /* !defined(HAVE_SSL_GET_CLIENT_CIPHERS) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_get_ciphersuite_name(void *ignored) -{ - (void)ignored; - const char *ret; - tor_tls_t *ctx; - ctx = tor_malloc_zero(sizeof(tor_tls_t)); - ctx->ssl = tor_malloc_zero(sizeof(SSL)); - - ret = tor_tls_get_ciphersuite_name(ctx); - tt_str_op(ret, OP_EQ, "(NONE)"); - - done: - tor_free(ctx->ssl); - tor_free(ctx); -} - -static SSL_CIPHER * -get_cipher_by_id(uint16_t id) -{ - int i; - const SSL_METHOD *method = SSLv23_method(); - int num = method->num_ciphers(); - for (i = 0; i < num; ++i) { - const SSL_CIPHER *cipher = method->get_cipher(i); - if (id == (SSL_CIPHER_get_id(cipher) & 0xffff)) { - return (SSL_CIPHER *)cipher; - } - } - - return NULL; -} - -static void -test_tortls_classify_client_ciphers(void *ignored) -{ - (void)ignored; - int i; - int ret; - SSL_CTX *ctx; - SSL *ssl; - tor_tls_t *tls; - STACK_OF(SSL_CIPHER) *ciphers; - SSL_CIPHER *tmp_cipher; - - library_init(); - - tor_tls_allocate_tor_tls_object_ex_data_index(); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->magic = TOR_TLS_MAGIC; - - ctx = SSL_CTX_new(TLSv1_method()); - ssl = SSL_new(ctx); - tls->ssl = ssl; - - ciphers = sk_SSL_CIPHER_new_null(); - - ret = tor_tls_classify_client_ciphers(ssl, NULL); - tt_int_op(ret, OP_EQ, -1); - - SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls); - tls->client_cipher_list_type = 42; - - ret = tor_tls_classify_client_ciphers(ssl, NULL); - tt_int_op(ret, OP_EQ, 42); - - tls->client_cipher_list_type = 0; - ret = tor_tls_classify_client_ciphers(ssl, ciphers); - tt_int_op(ret, OP_EQ, 1); - tt_int_op(tls->client_cipher_list_type, OP_EQ, 1); - - tls->client_cipher_list_type = 0; - ret = tor_tls_classify_client_ciphers(ssl, SSL_get_ciphers(ssl)); - tt_int_op(ret, OP_EQ, 3); - tt_int_op(tls->client_cipher_list_type, OP_EQ, 3); - - SSL_CIPHER *one = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_128_SHA), - *two = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA), - *three = get_cipher_by_name(SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA), - *four = NULL; - sk_SSL_CIPHER_push(ciphers, one); - sk_SSL_CIPHER_push(ciphers, two); - sk_SSL_CIPHER_push(ciphers, three); - sk_SSL_CIPHER_push(ciphers, four); - - tls->client_cipher_list_type = 0; - ret = tor_tls_classify_client_ciphers(ssl, ciphers); - tt_int_op(ret, OP_EQ, 1); - tt_int_op(tls->client_cipher_list_type, OP_EQ, 1); - - sk_SSL_CIPHER_zero(ciphers); - - one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384"); - tt_assert(one); - one->id = 0x00ff; - two = get_cipher_by_name("ECDHE-RSA-AES128-GCM-SHA256"); - tt_assert(two); - two->id = 0x0000; - sk_SSL_CIPHER_push(ciphers, one); - tls->client_cipher_list_type = 0; - ret = tor_tls_classify_client_ciphers(ssl, ciphers); - tt_int_op(ret, OP_EQ, 3); - tt_int_op(tls->client_cipher_list_type, OP_EQ, 3); - - sk_SSL_CIPHER_push(ciphers, two); - tls->client_cipher_list_type = 0; - ret = tor_tls_classify_client_ciphers(ssl, ciphers); - tt_int_op(ret, OP_EQ, 3); - tt_int_op(tls->client_cipher_list_type, OP_EQ, 3); - - one->id = 0xC00A; - tls->client_cipher_list_type = 0; - ret = tor_tls_classify_client_ciphers(ssl, ciphers); - tt_int_op(ret, OP_EQ, 3); - tt_int_op(tls->client_cipher_list_type, OP_EQ, 3); - - sk_SSL_CIPHER_zero(ciphers); - for (i=0; v2_cipher_list[i]; i++) { - tmp_cipher = get_cipher_by_id(v2_cipher_list[i]); - tt_assert(tmp_cipher); - sk_SSL_CIPHER_push(ciphers, tmp_cipher); - } - tls->client_cipher_list_type = 0; - ret = tor_tls_classify_client_ciphers(ssl, ciphers); - tt_int_op(ret, OP_EQ, 2); - tt_int_op(tls->client_cipher_list_type, OP_EQ, 2); - - done: - sk_SSL_CIPHER_free(ciphers); - SSL_free(tls->ssl); - tor_free(tls); - SSL_CTX_free(ctx); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -static void -test_tortls_client_is_using_v2_ciphers(void *ignored) -{ - (void)ignored; - -#ifdef HAVE_SSL_GET_CLIENT_CIPHERS - tt_skip(); - done: - (void)1; -#else - int ret; - SSL_CTX *ctx; - SSL *ssl; - SSL_SESSION *sess; - STACK_OF(SSL_CIPHER) *ciphers; - - library_init(); - - ctx = SSL_CTX_new(TLSv1_method()); - ssl = SSL_new(ctx); - sess = SSL_SESSION_new(); - - ret = tor_tls_client_is_using_v2_ciphers(ssl); - tt_int_op(ret, OP_EQ, -1); - - ssl->session = sess; - ret = tor_tls_client_is_using_v2_ciphers(ssl); - tt_int_op(ret, OP_EQ, 0); - - ciphers = sk_SSL_CIPHER_new_null(); - SSL_CIPHER *one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384"); - tt_assert(one); - one->id = 0x00ff; - sk_SSL_CIPHER_push(ciphers, one); - sess->ciphers = ciphers; - ret = tor_tls_client_is_using_v2_ciphers(ssl); - tt_int_op(ret, OP_EQ, 1); - done: - SSL_free(ssl); - SSL_CTX_free(ctx); -#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */ -} - -#ifndef OPENSSL_OPAQUE -static X509 *fixed_try_to_extract_certs_from_tls_cert_out_result = NULL; -static X509 *fixed_try_to_extract_certs_from_tls_id_cert_out_result = NULL; - -static void -fixed_try_to_extract_certs_from_tls(int severity, tor_tls_t *tls, - X509 **cert_out, X509 **id_cert_out) -{ - (void) severity; - (void) tls; - *cert_out = fixed_try_to_extract_certs_from_tls_cert_out_result; - *id_cert_out = fixed_try_to_extract_certs_from_tls_id_cert_out_result; -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static const char* notCompletelyValidCertString = - "-----BEGIN CERTIFICATE-----\n" - "MIICVjCCAb8CAg37MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n" - "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n" - "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n" - "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n" - "ODIyMDUyNzIzWhcNMTcwODIxMDUyNzIzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n" - "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n" - "ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYBBrx5PlP0WNI/ZdzD\n" - "+6Pktmurn+F2kQYbtc7XQh8/LTBvCo+P6iZoLEmUA9e7EXLRxgU1CVqeAi7QcAn9\n" - "MwBlc8ksFJHB0rtf9pmf8Oza9E0Bynlq/4/Kb1x+d+AyhL7oK9tQwB24uHOueHi1\n" - "C/iVv8CSWKiYe6hzN1txYe8rAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAASPdjigJ\n" - "kXCqKWpnZ/Oc75EUcMi6HztaW8abUMlYXPIgkV2F7YanHOB7K4f7OOLjiz8DTPFf\n" - "jC9UeuErhaA/zzWi8ewMTFZW/WshOrm3fNvcMrMLKtH534JKvcdMg6qIdjTFINIr\n" - "evnAhf0cwULaebn+lMs8Pdl7y37+sfluVok=\n" - "-----END CERTIFICATE-----\n"; -#endif /* !defined(OPENSSL_OPAQUE) */ - -static const char* validCertString = "-----BEGIN CERTIFICATE-----\n" - "MIIDpTCCAY0CAg3+MA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMREwDwYD\n" - "VQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIGA1UECgwLVG9yIFRl\n" - "c3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkwNjEzMzk1OVoXDTQz\n" - "MDEyMjEzMzk1OVowVjELMAkGA1UEBhMCVVMxEDAOBgNVBAcMB0NoaWNhZ28xFDAS\n" - "BgNVBAoMC1RvciBUZXN0aW5nMR8wHQYDVQQDDBZ0ZXN0aW5nLnRvcnByb2plY3Qu\n" - "b3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoT6uyVVhWyOF3wkHjjYbd\n" - "nKaykyRv4JVtKQdZ4OpEErmX1zw4MmyzpQNV6iR4bQnWiyLfzyVJMZDIC/WILBfX\n" - "w2Pza/yuLgUvDc3twMuhOACzOQVO8PrEF/aVv2+hbCCy2udXvKhnYn+CCXl3ozc8\n" - "XcKYvujTXDyvGWY3xwAjlQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCUvnhzQWuQ\n" - "MrN+pERkE+zcTI/9dGS90rUMMLgu8VDNqTa0TUQh8uO0EQ6uDvI8Js6e8tgwS0BR\n" - "UBahqb7ZHv+rejGCBr5OudqD+x4STiiuPNJVs86JTLN8SpM9CHjIBH5WCCN2KOy3\n" - "mevNoRcRRyYJzSFULCunIK6FGulszigMYGscrO4oiTkZiHPh9KvWT40IMiHfL+Lw\n" - "EtEWiLex6064LcA2YQ1AMuSZyCexks63lcfaFmQbkYOKqXa1oLkIRuDsOaSVjTfe\n" - "vec+X6jvf12cFTKS5WIeqkKF2Irt+dJoiHEGTe5RscUMN/f+gqHPzfFz5dR23sxo\n" - "g+HC6MZHlFkLAOx3wW6epPS8A/m1mw3zMPoTnb2U2YYt8T0dJMMlUn/7Y1sEAa+a\n" - "dSTMaeUf6VnJ//11m454EZl1to9Z7oJOgqmFffSrdD4BGIWe8f7hhW6L1Enmqe/J\n" - "BKL3wbzZh80O1W0bndAwhnEEhlzneFY84cbBo9pmVxpODHkUcStpr5Z7pBDrcL21\n" - "Ss/aB/1YrsVXhdvJdOGxl3Mnl9dUY57CympLGlT8f0pPS6GAKOelECOhFMHmJd8L\n" - "dj3XQSmKtYHevZ6IvuMXSlB/fJvSjSlkCuLo5+kJoaqPuRu+i/S1qxeRy3CBwmnE\n" - "LdSNdcX4N79GQJ996PA8+mUCQG7YRtK+WA==\n" - "-----END CERTIFICATE-----\n"; - -static const char* caCertString = "-----BEGIN CERTIFICATE-----\n" - "MIIFjzCCA3egAwIBAgIJAKd5WgyfPMYRMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV\n" - "BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIG\n" - "A1UECgwLVG9yIFRlc3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkw\n" - "NjEzMzc0MVoXDTQzMDEyMjEzMzc0MVowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM\n" - "CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRQwEgYDVQQKDAtUb3IgVGVzdGlu\n" - "ZzEUMBIGA1UEAwwLVG9yIFRlc3RpbmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n" - "ggIKAoICAQCpLMUEiLW5leUgBZoEJms2V7lZRhIAjnJBhVMHD0e3UubNknmaQoxf\n" - "ARz3rvqOaRd0JlV+qM9qE0DjiYcCVP1cAfqAo9d83uS1vwY3YMVJzADlaIiHfyVW\n" - "uEgBy0vvkeUBqaua24dYlcwsemOiXYLu41yM1wkcGHW1AhBNHppY6cznb8TyLgNM\n" - "2x3SGUdzc5XMyAFx51faKGBA3wjs+Hg1PLY7d30nmCgEOBavpm5I1disM/0k+Mcy\n" - "YmAKEo/iHJX/rQzO4b9znP69juLlR8PDBUJEVIG/CYb6+uw8MjjUyiWXYoqfVmN2\n" - "hm/lH8b6rXw1a2Aa3VTeD0DxaWeacMYHY/i01fd5n7hCoDTRNdSw5KJ0L3Z0SKTu\n" - "0lzffKzDaIfyZGlpW5qdouACkWYzsaitQOePVE01PIdO30vUfzNTFDfy42ccx3Di\n" - "59UCu+IXB+eMtrBfsok0Qc63vtF1linJgjHW1z/8ujk8F7/qkOfODhk4l7wngc2A\n" - "EmwWFIFoGaiTEZHB9qteXr4unbXZ0AHpM02uGGwZEGohjFyebEb73M+J57WKKAFb\n" - "PqbLcGUksL1SHNBNAJcVLttX55sO4nbidOS/kA3m+F1R04MBTyQF9qA6YDDHqdI3\n" - "h/3pw0Z4fxVouTYT4/NfRnX4JTP4u+7Mpcoof28VME0qWqD1LnRhFQIDAQABo1Aw\n" - "TjAdBgNVHQ4EFgQUMoAgIXH7pZ3QMRwTjT+DM9Yo/v0wHwYDVR0jBBgwFoAUMoAg\n" - "IXH7pZ3QMRwTjT+DM9Yo/v0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n" - "AgEAUJxacjXR9sT+Xs6ISFiUsyd0T6WVKMnV46xrYJHirGfx+krWHrjxMY+ZtxYD\n" - "DBDGlo11Qc4v6QrclNf5QUBfIiGQsP9Cm6hHcQ+Tpg9HHCgSqG1YNPwCPReCR4br\n" - "BLvLfrfkcBL2IWM0PdQdCze+59DBfipsULD2mEn9fjYRXQEwb2QWtQ9qRc20Yb/x\n" - "Q4b/+CvUodLkaq7B8MHz0BV8HHcBoph6DYaRmO/N+hPauIuSp6XyaGYcEefGKVKj\n" - "G2+fcsdyXsoijNdL8vNKwm4j2gVwCBnw16J00yfFoV46YcbfqEdJB2je0XSvwXqt\n" - "14AOTngxso2h9k9HLtrfpO1ZG/B5AcCMs1lzbZ2fp5DPHtjvvmvA2RJqgo3yjw4W\n" - "4DHAuTglYFlC3mDHNfNtcGP20JvepcQNzNP2UzwcpOc94hfKikOFw+gf9Vf1qd0y\n" - "h/Sk6OZHn2+JVUPiWHIQV98Vtoh4RmUZDJD+b55ia3fQGTGzt4z1XFzQYSva5sfs\n" - "wocS/papthqWldQU7x+3wofNd5CNU1x6WKXG/yw30IT/4F8ADJD6GeygNT8QJYvt\n" - "u/8lAkbOy6B9xGmSvr0Kk1oq9P2NshA6kalxp1Oz/DTNDdL4AeBXV3JmM6WWCjGn\n" - "Yy1RT69d0rwYc5u/vnqODz1IjvT90smsrkBumGt791FAFeg=\n" - "-----END CERTIFICATE-----\n"; - -static X509 * -read_cert_from(const char *str) -{ - BIO *bio = BIO_new(BIO_s_mem()); - BIO_write(bio, str, (int) strlen(str)); - X509 *res = PEM_read_bio_X509(bio, NULL, NULL, NULL); - BIO_free(bio); - return res; -} - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_verify(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - crypto_pk_t *k = NULL; - X509 *cert1 = NULL, *cert2 = NULL, *invalidCert = NULL, - *validCert = NULL, *caCert = NULL; - - cert1 = tor_malloc_zero(sizeof(X509)); - cert1->references = 10; - - cert2 = tor_malloc_zero(sizeof(X509)); - cert2->references = 10; - - validCert = read_cert_from(validCertString); - caCert = read_cert_from(caCertString); - invalidCert = read_cert_from(notCompletelyValidCertString); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - ret = tor_tls_verify(LOG_WARN, tls, &k); - tt_int_op(ret, OP_EQ, -1); - - MOCK(try_to_extract_certs_from_tls, fixed_try_to_extract_certs_from_tls); - - fixed_try_to_extract_certs_from_tls_cert_out_result = cert1; - ret = tor_tls_verify(LOG_WARN, tls, &k); - tt_int_op(ret, OP_EQ, -1); - - fixed_try_to_extract_certs_from_tls_id_cert_out_result = cert2; - ret = tor_tls_verify(LOG_WARN, tls, &k); - tt_int_op(ret, OP_EQ, -1); - - fixed_try_to_extract_certs_from_tls_cert_out_result = invalidCert; - fixed_try_to_extract_certs_from_tls_id_cert_out_result = invalidCert; - - ret = tor_tls_verify(LOG_WARN, tls, &k); - tt_int_op(ret, OP_EQ, -1); - - fixed_try_to_extract_certs_from_tls_cert_out_result = validCert; - fixed_try_to_extract_certs_from_tls_id_cert_out_result = caCert; - - ret = tor_tls_verify(LOG_WARN, tls, &k); - tt_int_op(ret, OP_EQ, 0); - tt_assert(k); - - done: - UNMOCK(try_to_extract_certs_from_tls); - tor_free(cert1); - tor_free(cert2); - tor_free(tls); - tor_free(k); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_check_lifetime(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - X509 *validCert = read_cert_from(validCertString); - time_t now = time(NULL); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0); - tt_int_op(ret, OP_EQ, -1); - - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); - tls->ssl->session->peer = validCert; - ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0); - tt_int_op(ret, OP_EQ, 0); - - ASN1_STRING_free(validCert->cert_info->validity->notBefore); - validCert->cert_info->validity->notBefore = ASN1_TIME_set(NULL, now-10); - ASN1_STRING_free(validCert->cert_info->validity->notAfter); - validCert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, now+60); - - ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, -1000); - tt_int_op(ret, OP_EQ, -1); - - ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), -1000, 0); - tt_int_op(ret, OP_EQ, -1); - - done: - tor_free(tls->ssl->session); - tor_free(tls->ssl); - tor_free(tls); - X509_free(validCert); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static int fixed_ssl_pending_result = 0; - -static int -fixed_ssl_pending(const SSL *ignored) -{ - (void)ignored; - return fixed_ssl_pending_result; -} - -static void -test_tortls_get_pending_bytes(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - SSL_METHOD *method; - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - method = tor_malloc_zero(sizeof(SSL_METHOD)); - method->ssl_pending = fixed_ssl_pending; - tls->ssl->method = method; - - fixed_ssl_pending_result = 42; - ret = tor_tls_get_pending_bytes(tls); - tt_int_op(ret, OP_EQ, 42); - - done: - tor_free(method); - tor_free(tls->ssl); - tor_free(tls); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - static void test_tortls_get_forced_write_size(void *ignored) { @@ -1193,30 +181,6 @@ test_tortls_get_forced_write_size(void *ignored) tor_free(tls); } -static void -test_tortls_get_write_overhead_ratio(void *ignored) -{ - (void)ignored; - double ret; - - total_bytes_written_over_tls = 0; - ret = tls_get_write_overhead_ratio(); - tt_double_op(fabs(ret - 1.0), OP_LT, 1E-12); - - total_bytes_written_by_tls = 10; - total_bytes_written_over_tls = 1; - ret = tls_get_write_overhead_ratio(); - tt_double_op(fabs(ret - 10.0), OP_LT, 1E-12); - - total_bytes_written_by_tls = 10; - total_bytes_written_over_tls = 2; - ret = tls_get_write_overhead_ratio(); - tt_double_op(fabs(ret - 5.0), OP_LT, 1E-12); - - done: - (void)0; -} - static void test_tortls_used_v1_handshake(void *ignored) { @@ -1272,116 +236,6 @@ test_tortls_server_got_renegotiate(void *ignored) tor_free(tls); } -#ifndef OPENSSL_OPAQUE -static void -test_tortls_SSL_SESSION_get_master_key(void *ignored) -{ - (void)ignored; - size_t ret; - tor_tls_t *tls; - uint8_t *out; - out = tor_malloc_zero(1); - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); - tls->ssl->session->master_key_length = 1; - -#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY - tls->ssl->session->master_key[0] = 43; - ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 0); - tt_int_op(ret, OP_EQ, 1); - tt_int_op(out[0], OP_EQ, 0); - - ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 1); - tt_int_op(ret, OP_EQ, 1); - tt_int_op(out[0], OP_EQ, 43); - - done: -#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */ - tor_free(tls->ssl->session); - tor_free(tls->ssl); - tor_free(tls); - tor_free(out); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_get_tlssecrets(void *ignored) -{ - (void)ignored; - int ret; - uint8_t *secret_out = tor_malloc_zero(DIGEST256_LEN); - tor_tls_t *tls; - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); - tls->ssl->session->master_key_length = 1; - tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE)); - - ret = tor_tls_get_tlssecrets(tls, secret_out); - tt_int_op(ret, OP_EQ, 0); - - done: - tor_free(secret_out); - tor_free(tls->ssl->s3); - tor_free(tls->ssl->session); - tor_free(tls->ssl); - tor_free(tls); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_get_buffer_sizes(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - size_t rbuf_c=-1, rbuf_b=-1, wbuf_c=-1, wbuf_b=-1; - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE)); - - tls->ssl->s3->rbuf.buf = NULL; - tls->ssl->s3->rbuf.len = 1; - tls->ssl->s3->rbuf.offset = 0; - tls->ssl->s3->rbuf.left = 42; - - tls->ssl->s3->wbuf.buf = NULL; - tls->ssl->s3->wbuf.len = 2; - tls->ssl->s3->wbuf.offset = 0; - tls->ssl->s3->wbuf.left = 43; - - ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b); -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) - tt_int_op(ret, OP_EQ, -1); -#else - tt_int_op(ret, OP_EQ, 0); - tt_int_op(rbuf_c, OP_EQ, 0); - tt_int_op(wbuf_c, OP_EQ, 0); - tt_int_op(rbuf_b, OP_EQ, 42); - tt_int_op(wbuf_b, OP_EQ, 43); - - tls->ssl->s3->rbuf.buf = tor_malloc_zero(1); - tls->ssl->s3->wbuf.buf = tor_malloc_zero(1); - ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b); - tt_int_op(ret, OP_EQ, 0); - tt_int_op(rbuf_c, OP_EQ, 1); - tt_int_op(wbuf_c, OP_EQ, 2); - -#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */ - - done: - tor_free(tls->ssl->s3->rbuf.buf); - tor_free(tls->ssl->s3->wbuf.buf); - tor_free(tls->ssl->s3); - tor_free(tls->ssl); - tor_free(tls); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - static void test_tortls_evaluate_ecgroup_for_tls(void *ignored) { @@ -1405,1441 +259,19 @@ test_tortls_evaluate_ecgroup_for_tls(void *ignored) (void)0; } -#ifndef OPENSSL_OPAQUE -typedef struct cert_pkey_st_local -{ - X509 *x509; - EVP_PKEY *privatekey; - const EVP_MD *digest; -} CERT_PKEY_local; - -typedef struct sess_cert_st_local -{ - STACK_OF(X509) *cert_chain; - int peer_cert_type; - CERT_PKEY_local *peer_key; - CERT_PKEY_local peer_pkeys[8]; - int references; -} SESS_CERT_local; - -static void -test_tortls_try_to_extract_certs_from_tls(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - X509 *cert = NULL, *id_cert = NULL, *c1 = NULL, *c2 = NULL; - SESS_CERT_local *sess = NULL; - - c1 = read_cert_from(validCertString); - c2 = read_cert_from(caCertString); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); - sess = tor_malloc_zero(sizeof(SESS_CERT_local)); - tls->ssl->session->sess_cert = (void *)sess; - - try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert); - tt_assert(!cert); - tt_assert(!id_cert); - - tls->ssl->session->peer = c1; - try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert); - tt_assert(cert == c1); - tt_assert(!id_cert); - X509_free(cert); /* decrease refcnt */ - - sess->cert_chain = sk_X509_new_null(); - try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert); - tt_assert(cert == c1); - tt_assert(!id_cert); - X509_free(cert); /* decrease refcnt */ - - sk_X509_push(sess->cert_chain, c1); - sk_X509_push(sess->cert_chain, c2); - - try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert); - tt_assert(cert == c1); - tt_assert(id_cert); - X509_free(cert); /* decrease refcnt */ - - done: - sk_X509_free(sess->cert_chain); - tor_free(sess); - tor_free(tls->ssl->session); - tor_free(tls->ssl); - tor_free(tls); - X509_free(c1); - X509_free(c2); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_get_peer_cert(void *ignored) -{ - (void)ignored; - tor_x509_cert_t *ret; - tor_tls_t *tls; - X509 *cert = NULL; - - cert = read_cert_from(validCertString); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); - - ret = tor_tls_get_peer_cert(tls); - tt_assert(!ret); - - tls->ssl->session->peer = cert; - ret = tor_tls_get_peer_cert(tls); - tt_assert(ret); - tt_assert(ret->cert == cert); - - done: - tor_x509_cert_free(ret); - tor_free(tls->ssl->session); - tor_free(tls->ssl); - tor_free(tls); - X509_free(cert); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_peer_has_cert(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - X509 *cert = NULL; - - cert = read_cert_from(validCertString); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); - - ret = tor_tls_peer_has_cert(tls); - tt_assert(!ret); - - tls->ssl->session->peer = cert; - ret = tor_tls_peer_has_cert(tls); - tt_assert(ret); - - done: - tor_free(tls->ssl->session); - tor_free(tls->ssl); - tor_free(tls); - X509_free(cert); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -static void -test_tortls_is_server(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - int ret; - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->isServer = 1; - ret = tor_tls_is_server(tls); - tt_int_op(ret, OP_EQ, 1); - - done: - tor_free(tls); -} - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_session_secret_cb(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - SSL_CTX *ctx; - STACK_OF(SSL_CIPHER) *ciphers = NULL; - SSL_CIPHER *one; - - library_init(); - - tor_tls_allocate_tor_tls_object_ex_data_index(); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - - tls->magic = TOR_TLS_MAGIC; - - ctx = SSL_CTX_new(TLSv1_method()); - tls->ssl = SSL_new(ctx); - SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls); - - SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL); - - tor_tls_session_secret_cb(tls->ssl, NULL, NULL, NULL, NULL, NULL); - tt_assert(!tls->ssl->tls_session_secret_cb); - - one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384"); - one->id = 0x00ff; - ciphers = sk_SSL_CIPHER_new_null(); - sk_SSL_CIPHER_push(ciphers, one); - - tls->client_cipher_list_type = 0; - tor_tls_session_secret_cb(tls->ssl, NULL, NULL, ciphers, NULL, NULL); - tt_assert(!tls->ssl->tls_session_secret_cb); - - done: - sk_SSL_CIPHER_free(ciphers); - SSL_free(tls->ssl); - SSL_CTX_free(ctx); - tor_free(tls); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -/* TODO: It seems block_renegotiation and unblock_renegotiation and - * using different blags. This might not be correct */ -static void -test_tortls_block_renegotiation(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE)); -#ifndef SUPPORT_UNSAFE_RENEGOTIATION_FLAG -#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0 -#endif - - tls->ssl->s3->flags = SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; - - tor_tls_block_renegotiation(tls); - -#ifndef OPENSSL_1_1_API - tt_assert(!(tls->ssl->s3->flags & - SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); -#endif - - done: - tor_free(tls->ssl->s3); - tor_free(tls->ssl); - tor_free(tls); -} - -static void -test_tortls_unblock_renegotiation(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tor_tls_unblock_renegotiation(tls); - - tt_uint_op(SSL_get_options(tls->ssl) & - SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, OP_EQ, - SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); - - done: - tor_free(tls->ssl); - tor_free(tls); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_assert_renegotiation_unblocked(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tor_tls_unblock_renegotiation(tls); - tor_tls_assert_renegotiation_unblocked(tls); - /* No assertion here - this test will fail if tor_assert is turned on - * and things are bad. */ - - tor_free(tls->ssl); - tor_free(tls); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -static void -test_tortls_set_logged_address(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - - tor_tls_set_logged_address(tls, "foo bar"); - - tt_str_op(tls->address, OP_EQ, "foo bar"); - - tor_tls_set_logged_address(tls, "foo bar 2"); - tt_str_op(tls->address, OP_EQ, "foo bar 2"); - - done: - tor_free(tls->address); - tor_free(tls); -} - -#ifndef OPENSSL_OPAQUE -static void -example_cb(tor_tls_t *t, void *arg) -{ - (void)t; - (void)arg; -} - -static void -test_tortls_set_renegotiate_callback(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - const char *arg = "hello"; - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - - tor_tls_set_renegotiate_callback(tls, example_cb, (void*)arg); - tt_assert(tls->negotiated_callback == example_cb); - tt_assert(tls->callback_arg == arg); - tt_assert(!tls->got_renegotiate); - - /* Assumes V2_HANDSHAKE_SERVER */ - tt_assert(tls->ssl->info_callback == tor_tls_server_info_callback); - - tor_tls_set_renegotiate_callback(tls, NULL, (void*)arg); - tt_assert(tls->ssl->info_callback == tor_tls_debug_state_callback); - - done: - tor_free(tls->ssl); - tor_free(tls); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static SSL_CIPHER *fixed_cipher1 = NULL; -static SSL_CIPHER *fixed_cipher2 = NULL; -static const SSL_CIPHER * -fake_get_cipher(unsigned ncipher) -{ - - switch (ncipher) { - case 1: - return fixed_cipher1; - case 2: - return fixed_cipher2; - default: - return NULL; - } -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_find_cipher_by_id(void *ignored) -{ - (void)ignored; - int ret; - SSL *ssl; - SSL_CTX *ctx; - const SSL_METHOD *m = TLSv1_method(); - SSL_METHOD *empty_method = tor_malloc_zero(sizeof(SSL_METHOD)); - - fixed_cipher1 = tor_malloc_zero(sizeof(SSL_CIPHER)); - fixed_cipher2 = tor_malloc_zero(sizeof(SSL_CIPHER)); - fixed_cipher2->id = 0xC00A; - - library_init(); - - ctx = SSL_CTX_new(m); - ssl = SSL_new(ctx); - - ret = find_cipher_by_id(ssl, NULL, 0xC00A); - tt_int_op(ret, OP_EQ, 1); - - ret = find_cipher_by_id(ssl, m, 0xC00A); - tt_int_op(ret, OP_EQ, 1); - - ret = find_cipher_by_id(ssl, m, 0xFFFF); - tt_int_op(ret, OP_EQ, 0); - - ret = find_cipher_by_id(ssl, empty_method, 0xC00A); - tt_int_op(ret, OP_EQ, 1); - - ret = find_cipher_by_id(ssl, empty_method, 0xFFFF); -#ifdef HAVE_SSL_CIPHER_FIND - tt_int_op(ret, OP_EQ, 0); -#else - tt_int_op(ret, OP_EQ, 1); -#endif - - empty_method->get_cipher = fake_get_cipher; - ret = find_cipher_by_id(ssl, empty_method, 0xC00A); - tt_int_op(ret, OP_EQ, 1); - - empty_method->get_cipher = m->get_cipher; - empty_method->num_ciphers = m->num_ciphers; - ret = find_cipher_by_id(ssl, empty_method, 0xC00A); - tt_int_op(ret, OP_EQ, 1); - - empty_method->get_cipher = fake_get_cipher; - empty_method->num_ciphers = m->num_ciphers; - ret = find_cipher_by_id(ssl, empty_method, 0xC00A); - tt_int_op(ret, OP_EQ, 1); - - empty_method->num_ciphers = fake_num_ciphers; - ret = find_cipher_by_id(ssl, empty_method, 0xC00A); -#ifdef HAVE_SSL_CIPHER_FIND - tt_int_op(ret, OP_EQ, 1); -#else - tt_int_op(ret, OP_EQ, 0); -#endif - - done: - tor_free(empty_method); - SSL_free(ssl); - SSL_CTX_free(ctx); - tor_free(fixed_cipher1); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_debug_state_callback(void *ignored) -{ - (void)ignored; - SSL *ssl; - char *buf = tor_malloc_zero(1000); - int n; - - setup_capture_of_logs(LOG_DEBUG); - - ssl = tor_malloc_zero(sizeof(SSL)); - - tor_tls_debug_state_callback(ssl, 32, 45); - - n = tor_snprintf(buf, 1000, "SSL %p is now in state unknown" - " state [type=32,val=45].\n", ssl); - /* tor's snprintf returns -1 on error */ - tt_int_op(n, OP_NE, -1); - expect_log_msg(buf); - - done: - teardown_capture_of_logs(); - tor_free(buf); - tor_free(ssl); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_server_info_callback(void *ignored) -{ - (void)ignored; - tor_tls_t *tls; - SSL_CTX *ctx; - SSL *ssl; - - library_init(); - - ctx = SSL_CTX_new(TLSv1_method()); - ssl = SSL_new(ctx); - - tor_tls_allocate_tor_tls_object_ex_data_index(); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->magic = TOR_TLS_MAGIC; - tls->ssl = ssl; - - setup_full_capture_of_logs(LOG_WARN); - SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_A); - mock_clean_saved_logs(); - tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); - expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n"); - - SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B); - mock_clean_saved_logs(); - tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); - expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n"); - - SSL_set_state(ssl, 99); - mock_clean_saved_logs(); - tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); - expect_no_log_entry(); - teardown_capture_of_logs(); - - SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls); - SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B); - tls->negotiated_callback = 0; - tls->server_handshake_count = 120; - tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); - tt_int_op(tls->server_handshake_count, OP_EQ, 121); - - tls->server_handshake_count = 127; - tls->negotiated_callback = (void *)1; - tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); - tt_int_op(tls->server_handshake_count, OP_EQ, 127); - tt_int_op(tls->got_renegotiate, OP_EQ, 1); - - tls->ssl->session = SSL_SESSION_new(); - tls->wasV2Handshake = 0; - tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); - tt_int_op(tls->wasV2Handshake, OP_EQ, 0); - - done: - teardown_capture_of_logs(); - SSL_free(ssl); - SSL_CTX_free(ctx); - tor_free(tls); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static int fixed_ssl_read_result_index; -static int fixed_ssl_read_result[5]; -static int fixed_ssl_shutdown_result; - -static int -fixed_ssl_read(SSL *s, void *buf, int len) -{ - (void)s; - (void)buf; - (void)len; - return fixed_ssl_read_result[fixed_ssl_read_result_index++]; -} - -static int -fixed_ssl_shutdown(SSL *s) -{ - (void)s; - return fixed_ssl_shutdown_result; -} - -#ifndef LIBRESSL_VERSION_NUMBER -static int fixed_ssl_state_to_set; -static tor_tls_t *fixed_tls; - -static int -setting_version_ssl_shutdown(SSL *s) -{ - s->version = SSL2_VERSION; - return fixed_ssl_shutdown_result; -} - -static int -setting_version_and_state_ssl_shutdown(SSL *s) -{ - fixed_tls->state = fixed_ssl_state_to_set; - s->version = SSL2_VERSION; - return fixed_ssl_shutdown_result; -} -#endif /* !defined(LIBRESSL_VERSION_NUMBER) */ - -static int -dummy_handshake_func(SSL *s) -{ - (void)s; - return 1; -} - -static void -test_tortls_shutdown(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - SSL_METHOD *method = give_me_a_test_method(); - setup_capture_of_logs(LOG_WARN); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->method = method; - method->ssl_read = fixed_ssl_read; - method->ssl_shutdown = fixed_ssl_shutdown; - - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, -9); - - tls->state = TOR_TLS_ST_SENTCLOSE; - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = -1; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, -9); - -#ifndef LIBRESSL_VERSION_NUMBER - tls->ssl->handshake_func = dummy_handshake_func; - - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = 42; - fixed_ssl_read_result[2] = 0; - fixed_ssl_shutdown_result = 1; - ERR_clear_error(); - tls->ssl->version = SSL2_VERSION; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_DONE); - tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED); - - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = 42; - fixed_ssl_read_result[2] = 0; - fixed_ssl_shutdown_result = 0; - ERR_clear_error(); - tls->ssl->version = 0; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_DONE); - tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED); - - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = 42; - fixed_ssl_read_result[2] = 0; - fixed_ssl_shutdown_result = 0; - ERR_clear_error(); - tls->ssl->version = 0; - method->ssl_shutdown = setting_version_ssl_shutdown; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); - - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = 42; - fixed_ssl_read_result[2] = 0; - fixed_ssl_shutdown_result = 0; - fixed_tls = tls; - fixed_ssl_state_to_set = TOR_TLS_ST_GOTCLOSE; - ERR_clear_error(); - tls->ssl->version = 0; - method->ssl_shutdown = setting_version_and_state_ssl_shutdown; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); - - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = 42; - fixed_ssl_read_result[2] = 0; - fixed_ssl_read_result[3] = -1; - fixed_ssl_shutdown_result = 0; - fixed_tls = tls; - fixed_ssl_state_to_set = 0; - ERR_clear_error(); - tls->ssl->version = 0; - method->ssl_shutdown = setting_version_and_state_ssl_shutdown; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); -#endif /* !defined(LIBRESSL_VERSION_NUMBER) */ - - done: - teardown_capture_of_logs(); - tor_free(method); - tor_free(tls->ssl); - tor_free(tls); -} - -static int negotiated_callback_called; - -static void -negotiated_callback_setter(tor_tls_t *t, void *arg) -{ - (void)t; - (void)arg; - negotiated_callback_called++; -} - -static void -test_tortls_read(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - char buf[100]; - SSL_METHOD *method = give_me_a_test_method(); - setup_capture_of_logs(LOG_WARN); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->state = TOR_TLS_ST_OPEN; - - ret = tor_tls_read(tls, buf, 10); - tt_int_op(ret, OP_EQ, -9); - - /* These tests assume that V2_HANDSHAKE_SERVER is set */ - tls->ssl->handshake_func = dummy_handshake_func; - tls->ssl->method = method; - method->ssl_read = fixed_ssl_read; - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 42; - tls->state = TOR_TLS_ST_OPEN; - ERR_clear_error(); - ret = tor_tls_read(tls, buf, 10); - tt_int_op(ret, OP_EQ, 42); - - tls->state = TOR_TLS_ST_OPEN; - tls->got_renegotiate = 1; - fixed_ssl_read_result_index = 0; - ERR_clear_error(); - ret = tor_tls_read(tls, buf, 10); - tt_int_op(tls->got_renegotiate, OP_EQ, 0); - - tls->state = TOR_TLS_ST_OPEN; - tls->got_renegotiate = 1; - negotiated_callback_called = 0; - tls->negotiated_callback = negotiated_callback_setter; - fixed_ssl_read_result_index = 0; - ERR_clear_error(); - ret = tor_tls_read(tls, buf, 10); - tt_int_op(negotiated_callback_called, OP_EQ, 1); - -#ifndef LIBRESSL_VERSION_NUMBER - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 0; - tls->ssl->version = SSL2_VERSION; - ERR_clear_error(); - ret = tor_tls_read(tls, buf, 10); - tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE); - tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED); -#endif /* !defined(LIBRESSL_VERSION_NUMBER) */ - // TODO: fill up - - done: - teardown_capture_of_logs(); - tor_free(tls->ssl); - tor_free(tls); - tor_free(method); -} - -static int fixed_ssl_write_result; - -static int -fixed_ssl_write(SSL *s, const void *buf, int len) -{ - (void)s; - (void)buf; - (void)len; - return fixed_ssl_write_result; -} - -static void -test_tortls_write(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - SSL_METHOD *method = give_me_a_test_method(); - char buf[100]; - setup_capture_of_logs(LOG_WARN); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->state = TOR_TLS_ST_OPEN; - - ret = tor_tls_write(tls, buf, 0); - tt_int_op(ret, OP_EQ, 0); - - ret = tor_tls_write(tls, buf, 10); - tt_int_op(ret, OP_EQ, -9); - - tls->ssl->method = method; - tls->wantwrite_n = 1; - ret = tor_tls_write(tls, buf, 10); - tt_int_op(tls->wantwrite_n, OP_EQ, 0); - - method->ssl_write = fixed_ssl_write; - tls->ssl->handshake_func = dummy_handshake_func; - fixed_ssl_write_result = 1; - ERR_clear_error(); - ret = tor_tls_write(tls, buf, 10); - tt_int_op(ret, OP_EQ, 1); - - fixed_ssl_write_result = -1; - ERR_clear_error(); - tls->ssl->rwstate = SSL_READING; - SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL); - SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ; - ret = tor_tls_write(tls, buf, 10); - tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD); - - ERR_clear_error(); - tls->ssl->rwstate = SSL_READING; - SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL); - SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE; - ret = tor_tls_write(tls, buf, 10); - tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE); - - done: - teardown_capture_of_logs(); - BIO_free(tls->ssl->rbio); - tor_free(tls->ssl); - tor_free(tls); - tor_free(method); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static int fixed_ssl_accept_result; -static int fixed_ssl_connect_result; - -static int -setting_error_ssl_accept(SSL *ssl) -{ - (void)ssl; - ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99); - ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99); - return fixed_ssl_accept_result; -} - -static int -setting_error_ssl_connect(SSL *ssl) -{ - (void)ssl; - ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99); - ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99); - return fixed_ssl_connect_result; -} - -static int -fixed_ssl_accept(SSL *ssl) -{ - (void) ssl; - return fixed_ssl_accept_result; -} - -static void -test_tortls_handshake(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - SSL_CTX *ctx; - SSL_METHOD *method = give_me_a_test_method(); - setup_capture_of_logs(LOG_INFO); - - SSL_library_init(); - SSL_load_error_strings(); - - ctx = SSL_CTX_new(TLSv1_method()); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = SSL_new(ctx); - tls->state = TOR_TLS_ST_HANDSHAKE; - - ret = tor_tls_handshake(tls); - tt_int_op(ret, OP_EQ, -9); - - tls->isServer = 1; - tls->state = TOR_TLS_ST_HANDSHAKE; - ret = tor_tls_handshake(tls); - tt_int_op(ret, OP_EQ, -9); - - tls->ssl->method = method; - method->ssl_accept = fixed_ssl_accept; - fixed_ssl_accept_result = 2; - ERR_clear_error(); - tls->state = TOR_TLS_ST_HANDSHAKE; - ret = tor_tls_handshake(tls); - tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_OPEN); - - method->ssl_accept = setting_error_ssl_accept; - fixed_ssl_accept_result = 1; - ERR_clear_error(); - mock_clean_saved_logs(); - tls->state = TOR_TLS_ST_HANDSHAKE; - ret = tor_tls_handshake(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); - expect_log_entry(); - /* This fails on jessie. Investigate why! */ -#if 0 - expect_log_msg("TLS error while handshaking: (null) (in bignum routines:" - "(null):SSLv3 write client hello B)\n"); - expect_log_msg("TLS error while handshaking: (null) (in system library:" - "connect:SSLv3 write client hello B)\n"); -#endif /* 0 */ - expect_log_severity(LOG_INFO); - - tls->isServer = 0; - method->ssl_connect = setting_error_ssl_connect; - fixed_ssl_connect_result = 1; - ERR_clear_error(); - mock_clean_saved_logs(); - tls->state = TOR_TLS_ST_HANDSHAKE; - ret = tor_tls_handshake(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); - expect_log_entry(); -#if 0 - /* See above */ - expect_log_msg("TLS error while handshaking: " - "(null) (in bignum routines:(null):SSLv3 write client hello B)\n"); - expect_log_msg("TLS error while handshaking: " - "(null) (in system library:connect:SSLv3 write client hello B)\n"); -#endif /* 0 */ - expect_log_severity(LOG_WARN); - - done: - teardown_capture_of_logs(); - SSL_free(tls->ssl); - SSL_CTX_free(ctx); - tor_free(tls); - tor_free(method); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_finish_handshake(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - SSL_CTX *ctx; - SSL_METHOD *method = give_me_a_test_method(); - SSL_library_init(); - SSL_load_error_strings(); - - X509 *c1 = read_cert_from(validCertString); - SESS_CERT_local *sess = NULL; - - ctx = SSL_CTX_new(method); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = SSL_new(ctx); - tls->state = TOR_TLS_ST_OPEN; - - ret = tor_tls_finish_handshake(tls); - tt_int_op(ret, OP_EQ, 0); - - tls->isServer = 1; - tls->wasV2Handshake = 0; - setup_full_capture_of_logs(LOG_WARN); - ret = tor_tls_finish_handshake(tls); - tt_int_op(ret, OP_EQ, 0); - tt_int_op(tls->wasV2Handshake, OP_EQ, 1); - expect_single_log_msg_containing("For some reason, wasV2Handshake didn't " - "get set."); - teardown_capture_of_logs(); - - tls->wasV2Handshake = 1; - ret = tor_tls_finish_handshake(tls); - tt_int_op(ret, OP_EQ, 0); - tt_int_op(tls->wasV2Handshake, OP_EQ, 1); - - tls->wasV2Handshake = 1; - tls->ssl->session = SSL_SESSION_new(); - ret = tor_tls_finish_handshake(tls); - tt_int_op(ret, OP_EQ, 0); - tt_int_op(tls->wasV2Handshake, OP_EQ, 0); - - tls->isServer = 0; - - sess = tor_malloc_zero(sizeof(SESS_CERT_local)); - tls->ssl->session->sess_cert = (void *)sess; - sess->cert_chain = sk_X509_new_null(); - sk_X509_push(sess->cert_chain, c1); - tls->ssl->session->peer = c1; - tls->wasV2Handshake = 0; - ret = tor_tls_finish_handshake(tls); - tt_int_op(ret, OP_EQ, 0); - tt_int_op(tls->wasV2Handshake, OP_EQ, 1); - - method->num_ciphers = fake_num_ciphers; - ret = tor_tls_finish_handshake(tls); - tt_int_op(ret, OP_EQ, -9); - - done: - if (sess) - sk_X509_free(sess->cert_chain); - if (tls->ssl && tls->ssl->session) { - tor_free(tls->ssl->session->sess_cert); - } - SSL_free(tls->ssl); - tor_free(tls); - SSL_CTX_free(ctx); - tor_free(method); - teardown_capture_of_logs(); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -static int fixed_crypto_pk_new_result_index; -static crypto_pk_t *fixed_crypto_pk_new_result[5]; - -static crypto_pk_t * -fixed_crypto_pk_new(void) -{ - return fixed_crypto_pk_new_result[fixed_crypto_pk_new_result_index++]; -} - -#ifndef OPENSSL_OPAQUE -static int fixed_crypto_pk_generate_key_with_bits_result_index; -static int fixed_crypto_pk_generate_key_with_bits_result[5]; -static int fixed_tor_tls_create_certificate_result_index; -static X509 *fixed_tor_tls_create_certificate_result[5]; -static int fixed_tor_x509_cert_new_result_index; -static tor_x509_cert_t *fixed_tor_x509_cert_new_result[5]; - -static int -fixed_crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits) -{ - (void)env; - (void)bits; - return fixed_crypto_pk_generate_key_with_bits_result[ - fixed_crypto_pk_generate_key_with_bits_result_index++]; -} - -static X509 * -fixed_tor_tls_create_certificate(crypto_pk_t *rsa, - crypto_pk_t *rsa_sign, - const char *cname, - const char *cname_sign, - unsigned int cert_lifetime) -{ - (void)rsa; - (void)rsa_sign; - (void)cname; - (void)cname_sign; - (void)cert_lifetime; - return fixed_tor_tls_create_certificate_result[ - fixed_tor_tls_create_certificate_result_index++]; -} - -static tor_x509_cert_t * -fixed_tor_x509_cert_new(X509 *x509_cert) -{ - (void) x509_cert; - return fixed_tor_x509_cert_new_result[ - fixed_tor_x509_cert_new_result_index++]; -} - -static void -test_tortls_context_new(void *ignored) -{ - (void)ignored; - tor_tls_context_t *ret; - crypto_pk_t *pk1, *pk2, *pk3, *pk4, *pk5, *pk6, *pk7, *pk8, *pk9, *pk10, - *pk11, *pk12, *pk13, *pk14, *pk15, *pk16, *pk17, *pk18; - - pk1 = crypto_pk_new(); - pk2 = crypto_pk_new(); - pk3 = crypto_pk_new(); - pk4 = crypto_pk_new(); - pk5 = crypto_pk_new(); - pk6 = crypto_pk_new(); - pk7 = crypto_pk_new(); - pk8 = crypto_pk_new(); - pk9 = crypto_pk_new(); - pk10 = crypto_pk_new(); - pk11 = crypto_pk_new(); - pk12 = crypto_pk_new(); - pk13 = crypto_pk_new(); - pk14 = crypto_pk_new(); - pk15 = crypto_pk_new(); - pk16 = crypto_pk_new(); - pk17 = crypto_pk_new(); - pk18 = crypto_pk_new(); - - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = NULL; - MOCK(crypto_pk_new, fixed_crypto_pk_new); - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - /* note: we already override this in testing_common.c, so we - * run this unit test in a subprocess. */ - MOCK(crypto_pk_generate_key_with_bits, - fixed_crypto_pk_generate_key_with_bits); - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = pk1; - fixed_crypto_pk_new_result[1] = NULL; - fixed_crypto_pk_generate_key_with_bits_result[0] = -1; - fixed_crypto_pk_generate_key_with_bits_result_index = 0; - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = pk2; - fixed_crypto_pk_new_result[1] = NULL; - fixed_crypto_pk_generate_key_with_bits_result[0] = 0; - fixed_crypto_pk_generate_key_with_bits_result_index = 0; - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = pk3; - fixed_crypto_pk_new_result[1] = pk4; - fixed_crypto_pk_new_result[2] = NULL; - fixed_crypto_pk_generate_key_with_bits_result[0] = 0; - fixed_crypto_pk_generate_key_with_bits_result[1] = -1; - fixed_crypto_pk_generate_key_with_bits_result_index = 0; - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - MOCK(tor_tls_create_certificate, fixed_tor_tls_create_certificate); - - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = pk5; - fixed_crypto_pk_new_result[1] = pk6; - fixed_crypto_pk_new_result[2] = NULL; - fixed_crypto_pk_generate_key_with_bits_result_index = 0; - fixed_crypto_pk_generate_key_with_bits_result[1] = 0; - fixed_tor_tls_create_certificate_result_index = 0; - fixed_tor_tls_create_certificate_result[0] = NULL; - fixed_tor_tls_create_certificate_result[1] = X509_new(); - fixed_tor_tls_create_certificate_result[2] = X509_new(); - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = pk7; - fixed_crypto_pk_new_result[1] = pk8; - fixed_crypto_pk_new_result[2] = NULL; - fixed_crypto_pk_generate_key_with_bits_result_index = 0; - fixed_tor_tls_create_certificate_result_index = 0; - fixed_tor_tls_create_certificate_result[0] = X509_new(); - fixed_tor_tls_create_certificate_result[1] = NULL; - fixed_tor_tls_create_certificate_result[2] = X509_new(); - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = pk9; - fixed_crypto_pk_new_result[1] = pk10; - fixed_crypto_pk_new_result[2] = NULL; - fixed_crypto_pk_generate_key_with_bits_result_index = 0; - fixed_tor_tls_create_certificate_result_index = 0; - fixed_tor_tls_create_certificate_result[0] = X509_new(); - fixed_tor_tls_create_certificate_result[1] = X509_new(); - fixed_tor_tls_create_certificate_result[2] = NULL; - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - MOCK(tor_x509_cert_new, fixed_tor_x509_cert_new); - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = pk11; - fixed_crypto_pk_new_result[1] = pk12; - fixed_crypto_pk_new_result[2] = NULL; - fixed_crypto_pk_generate_key_with_bits_result_index = 0; - fixed_tor_tls_create_certificate_result_index = 0; - fixed_tor_tls_create_certificate_result[0] = X509_new(); - fixed_tor_tls_create_certificate_result[1] = X509_new(); - fixed_tor_tls_create_certificate_result[2] = X509_new(); - fixed_tor_x509_cert_new_result_index = 0; - fixed_tor_x509_cert_new_result[0] = NULL; - fixed_tor_x509_cert_new_result[1] = NULL; - fixed_tor_x509_cert_new_result[2] = NULL; - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = pk13; - fixed_crypto_pk_new_result[1] = pk14; - fixed_crypto_pk_new_result[2] = NULL; - fixed_crypto_pk_generate_key_with_bits_result_index = 0; - fixed_tor_tls_create_certificate_result_index = 0; - fixed_tor_tls_create_certificate_result[0] = X509_new(); - fixed_tor_tls_create_certificate_result[1] = X509_new(); - fixed_tor_tls_create_certificate_result[2] = X509_new(); - fixed_tor_x509_cert_new_result_index = 0; - fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t)); - fixed_tor_x509_cert_new_result[1] = NULL; - fixed_tor_x509_cert_new_result[2] = NULL; - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = pk15; - fixed_crypto_pk_new_result[1] = pk16; - fixed_crypto_pk_new_result[2] = NULL; - fixed_crypto_pk_generate_key_with_bits_result_index = 0; - fixed_tor_tls_create_certificate_result_index = 0; - fixed_tor_tls_create_certificate_result[0] = X509_new(); - fixed_tor_tls_create_certificate_result[1] = X509_new(); - fixed_tor_tls_create_certificate_result[2] = X509_new(); - fixed_tor_x509_cert_new_result_index = 0; - fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t)); - fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t)); - fixed_tor_x509_cert_new_result[2] = NULL; - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = pk17; - fixed_crypto_pk_new_result[1] = pk18; - fixed_crypto_pk_new_result[2] = NULL; - fixed_crypto_pk_generate_key_with_bits_result_index = 0; - fixed_tor_tls_create_certificate_result_index = 0; - fixed_tor_tls_create_certificate_result[0] = X509_new(); - fixed_tor_tls_create_certificate_result[1] = X509_new(); - fixed_tor_tls_create_certificate_result[2] = X509_new(); - fixed_tor_x509_cert_new_result_index = 0; - fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t)); - fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t)); - fixed_tor_x509_cert_new_result[2] = tor_malloc_zero(sizeof(tor_x509_cert_t)); - ret = tor_tls_context_new(NULL, 0, 0, 0); - tt_assert(!ret); - - done: - UNMOCK(tor_x509_cert_new); - UNMOCK(tor_tls_create_certificate); - UNMOCK(crypto_pk_generate_key_with_bits); - UNMOCK(crypto_pk_new); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -static int fixed_crypto_pk_get_evp_pkey_result_index = 0; -static EVP_PKEY *fixed_crypto_pk_get_evp_pkey_result[5]; - -static EVP_PKEY * -fixed_crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private) -{ - (void) env; - (void) private; - return fixed_crypto_pk_get_evp_pkey_result[ - fixed_crypto_pk_get_evp_pkey_result_index++]; -} - -static void -test_tortls_create_certificate(void *ignored) -{ - (void)ignored; - X509 *ret; - crypto_pk_t *pk1, *pk2; - - pk1 = crypto_pk_new(); - pk2 = crypto_pk_new(); - - MOCK(crypto_pk_get_openssl_evp_pkey_, fixed_crypto_pk_get_evp_pkey_); - fixed_crypto_pk_get_evp_pkey_result_index = 0; - fixed_crypto_pk_get_evp_pkey_result[0] = NULL; - ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1); - tt_assert(!ret); - - fixed_crypto_pk_get_evp_pkey_result_index = 0; - fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new(); - fixed_crypto_pk_get_evp_pkey_result[1] = NULL; - ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1); - tt_assert(!ret); - - fixed_crypto_pk_get_evp_pkey_result_index = 0; - fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new(); - fixed_crypto_pk_get_evp_pkey_result[1] = EVP_PKEY_new(); - ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1); - tt_assert(!ret); - - done: - UNMOCK(crypto_pk_get_openssl_evp_pkey_); - crypto_pk_free(pk1); - crypto_pk_free(pk2); -} - -static void -test_tortls_cert_new(void *ignored) -{ - (void)ignored; - tor_x509_cert_t *ret; - X509 *cert = read_cert_from(validCertString); - - ret = tor_x509_cert_new(NULL); - tt_assert(!ret); - - ret = tor_x509_cert_new(cert); - tt_assert(ret); - tor_x509_cert_free(ret); - ret = NULL; - -#if 0 - cert = read_cert_from(validCertString); - /* XXX this doesn't do what you think: it alters a copy of the pubkey. */ - X509_get_pubkey(cert)->type = EVP_PKEY_DSA; - ret = tor_x509_cert_new(cert); - tt_assert(ret); -#endif /* 0 */ - -#ifndef OPENSSL_OPAQUE - cert = read_cert_from(validCertString); - X509_CINF_free(cert->cert_info); - cert->cert_info = NULL; - ret = tor_x509_cert_new(cert); - tt_assert(ret); -#endif /* !defined(OPENSSL_OPAQUE) */ - - done: - tor_x509_cert_free(ret); -} - -static void -test_tortls_cert_is_valid(void *ignored) -{ - (void)ignored; - int ret; - tor_x509_cert_t *cert = NULL, *scert = NULL; - - scert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); - tt_int_op(ret, OP_EQ, 0); - - cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); - tt_int_op(ret, OP_EQ, 0); - tor_free(scert); - tor_free(cert); - - cert = tor_x509_cert_new(read_cert_from(validCertString)); - scert = tor_x509_cert_new(read_cert_from(caCertString)); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); - tt_int_op(ret, OP_EQ, 1); - -#ifndef OPENSSL_OPAQUE - tor_x509_cert_free(cert); - tor_x509_cert_free(scert); - cert = tor_x509_cert_new(read_cert_from(validCertString)); - scert = tor_x509_cert_new(read_cert_from(caCertString)); - ASN1_TIME_free(cert->cert->cert_info->validity->notAfter); - cert->cert->cert_info->validity->notAfter = - ASN1_TIME_set(NULL, time(NULL)-1000000); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); - tt_int_op(ret, OP_EQ, 0); - - tor_x509_cert_free(cert); - tor_x509_cert_free(scert); - cert = tor_x509_cert_new(read_cert_from(validCertString)); - scert = tor_x509_cert_new(read_cert_from(caCertString)); - X509_PUBKEY_free(cert->cert->cert_info->key); - cert->cert->cert_info->key = NULL; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); - tt_int_op(ret, OP_EQ, 0); -#endif /* !defined(OPENSSL_OPAQUE) */ - -#if 0 - tor_x509_cert_free(cert); - tor_x509_cert_free(scert); - cert = tor_x509_cert_new(read_cert_from(validCertString)); - scert = tor_x509_cert_new(read_cert_from(caCertString)); - /* This doesn't actually change the key in the cert. XXXXXX */ - BN_one(EVP_PKEY_get1_RSA(X509_get_pubkey(cert->cert))->n); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); - tt_int_op(ret, OP_EQ, 0); - - tor_x509_cert_free(cert); - tor_x509_cert_free(scert); - cert = tor_x509_cert_new(read_cert_from(validCertString)); - scert = tor_x509_cert_new(read_cert_from(caCertString)); - /* This doesn't actually change the key in the cert. XXXXXX */ - X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); - tt_int_op(ret, OP_EQ, 0); - - tor_x509_cert_free(cert); - tor_x509_cert_free(scert); - cert = tor_x509_cert_new(read_cert_from(validCertString)); - scert = tor_x509_cert_new(read_cert_from(caCertString)); - /* This doesn't actually change the key in the cert. XXXXXX */ - X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); - tt_int_op(ret, OP_EQ, 1); - - tor_x509_cert_free(cert); - tor_x509_cert_free(scert); - cert = tor_x509_cert_new(read_cert_from(validCertString)); - scert = tor_x509_cert_new(read_cert_from(caCertString)); - /* This doesn't actually change the key in the cert. XXXXXX */ - X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; - X509_get_pubkey(cert->cert)->ameth = NULL; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); - tt_int_op(ret, OP_EQ, 0); -#endif /* 0 */ - - done: - tor_x509_cert_free(cert); - tor_x509_cert_free(scert); -} - -static void -test_tortls_context_init_one(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_context_t *old = NULL; - - MOCK(crypto_pk_new, fixed_crypto_pk_new); - - fixed_crypto_pk_new_result_index = 0; - fixed_crypto_pk_new_result[0] = NULL; - ret = tor_tls_context_init_one(&old, NULL, 0, 0, 0); - tt_int_op(ret, OP_EQ, -1); - - done: - UNMOCK(crypto_pk_new); -} - -#define LOCAL_TEST_CASE(name, flags) \ +#define LOCAL_TEST_CASE(name, flags) \ { #name, test_tortls_##name, (flags|TT_FORK), NULL, NULL } -#ifdef OPENSSL_OPAQUE -#define INTRUSIVE_TEST_CASE(name, flags) \ - { #name, NULL, TT_SKIP, NULL, NULL } -#else -#define INTRUSIVE_TEST_CASE(name, flags) LOCAL_TEST_CASE(name, flags) -#endif /* defined(OPENSSL_OPAQUE) */ - struct testcase_t tortls_tests[] = { LOCAL_TEST_CASE(errno_to_tls_error, 0), LOCAL_TEST_CASE(err_to_string, 0), - LOCAL_TEST_CASE(tor_tls_new, TT_FORK), LOCAL_TEST_CASE(tor_tls_get_error, 0), - LOCAL_TEST_CASE(get_state_description, TT_FORK), - LOCAL_TEST_CASE(get_by_ssl, TT_FORK), - LOCAL_TEST_CASE(allocate_tor_tls_object_ex_data_index, TT_FORK), - LOCAL_TEST_CASE(log_one_error, TT_FORK), - INTRUSIVE_TEST_CASE(get_error, TT_FORK), - LOCAL_TEST_CASE(always_accept_verify_cb, 0), - INTRUSIVE_TEST_CASE(x509_cert_free, 0), LOCAL_TEST_CASE(x509_cert_get_id_digests, 0), - INTRUSIVE_TEST_CASE(cert_matches_key, 0), - INTRUSIVE_TEST_CASE(cert_get_key, 0), - LOCAL_TEST_CASE(get_my_client_auth_key, TT_FORK), LOCAL_TEST_CASE(get_my_certs, TT_FORK), - INTRUSIVE_TEST_CASE(get_ciphersuite_name, 0), - INTRUSIVE_TEST_CASE(classify_client_ciphers, 0), - LOCAL_TEST_CASE(client_is_using_v2_ciphers, 0), - INTRUSIVE_TEST_CASE(verify, 0), - INTRUSIVE_TEST_CASE(check_lifetime, 0), - INTRUSIVE_TEST_CASE(get_pending_bytes, 0), LOCAL_TEST_CASE(get_forced_write_size, 0), - LOCAL_TEST_CASE(get_write_overhead_ratio, TT_FORK), LOCAL_TEST_CASE(used_v1_handshake, TT_FORK), LOCAL_TEST_CASE(get_num_server_handshakes, 0), LOCAL_TEST_CASE(server_got_renegotiate, 0), - INTRUSIVE_TEST_CASE(SSL_SESSION_get_master_key, 0), - INTRUSIVE_TEST_CASE(get_tlssecrets, 0), - INTRUSIVE_TEST_CASE(get_buffer_sizes, 0), LOCAL_TEST_CASE(evaluate_ecgroup_for_tls, 0), - INTRUSIVE_TEST_CASE(try_to_extract_certs_from_tls, 0), - INTRUSIVE_TEST_CASE(get_peer_cert, 0), - INTRUSIVE_TEST_CASE(peer_has_cert, 0), - INTRUSIVE_TEST_CASE(shutdown, 0), - INTRUSIVE_TEST_CASE(finish_handshake, 0), - INTRUSIVE_TEST_CASE(handshake, 0), - INTRUSIVE_TEST_CASE(write, 0), - INTRUSIVE_TEST_CASE(read, 0), - INTRUSIVE_TEST_CASE(server_info_callback, 0), - LOCAL_TEST_CASE(is_server, 0), - INTRUSIVE_TEST_CASE(assert_renegotiation_unblocked, 0), - INTRUSIVE_TEST_CASE(block_renegotiation, 0), - INTRUSIVE_TEST_CASE(unblock_renegotiation, 0), - INTRUSIVE_TEST_CASE(set_renegotiate_callback, 0), - LOCAL_TEST_CASE(set_logged_address, 0), - INTRUSIVE_TEST_CASE(find_cipher_by_id, 0), - INTRUSIVE_TEST_CASE(session_secret_cb, 0), - INTRUSIVE_TEST_CASE(debug_state_callback, 0), - INTRUSIVE_TEST_CASE(context_new, TT_FORK /* redundant */), - LOCAL_TEST_CASE(create_certificate, 0), - LOCAL_TEST_CASE(cert_new, 0), - LOCAL_TEST_CASE(cert_is_valid, 0), - LOCAL_TEST_CASE(context_init_one, 0), END_OF_TESTCASES }; diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c new file mode 100644 index 0000000000..a9336a6f1b --- /dev/null +++ b/src/test/test_tortls_openssl.c @@ -0,0 +1,2597 @@ +/* Copyright (c) 2010-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define TORTLS_PRIVATE +#define TORTLS_OPENSSL_PRIVATE +#define TOR_X509_PRIVATE +#define LOG_PRIVATE +#include "orconfig.h" + +#ifdef _WIN32 +#include +#endif +#include + +#include "lib/cc/compat_compiler.h" + +/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in + * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ +DISABLE_GCC_WARNING(redundant-decls) + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +ENABLE_GCC_WARNING(redundant-decls) + +#include "core/or/or.h" +#include "lib/log/log.h" +#include "app/config/config.h" +#include "lib/crypt_ops/compat_openssl.h" +#include "lib/tls/x509.h" +#include "lib/tls/tortls.h" +#include "lib/tls/tortls_st.h" +#include "lib/tls/tortls_internal.h" +#include "app/config/or_state_st.h" + +#include "test/test.h" +#include "test/log_test_helpers.h" +#define NS_MODULE tortls + +#ifndef HAVE_SSL_STATE +#define OPENSSL_OPAQUE +#endif + +#if defined(OPENSSL_OPAQUE) && !defined(LIBRESSL_VERSION_NUMBER) +#define SSL_STATE_STR "before SSL initialization" +#else +#define SSL_STATE_STR "before/accept initialization" +#endif + +#ifndef OPENSSL_OPAQUE +static SSL_METHOD * +give_me_a_test_method(void) +{ + SSL_METHOD *method = tor_malloc_zero(sizeof(SSL_METHOD)); + memcpy(method, TLSv1_method(), sizeof(SSL_METHOD)); + return method; +} + +static int +fake_num_ciphers(void) +{ + return 0; +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +static int +mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert) +{ + (void) tls; + (void) cert; // XXXX look at this. + return 1; +} + +static void +test_tortls_tor_tls_new(void *data) +{ + (void) data; + MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); + crypto_pk_t *key1 = NULL, *key2 = NULL; + SSL_METHOD *method = NULL; + + key1 = pk_generate(2); + key2 = pk_generate(3); + + tor_tls_t *tls = NULL; + tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + key1, key2, 86400), OP_EQ, 0); + tls = tor_tls_new(-1, 0); + tt_want(tls); + tor_tls_free(tls); tls = NULL; + + SSL_CTX_free(client_tls_context->ctx); + client_tls_context->ctx = NULL; + tls = tor_tls_new(-1, 0); + tt_ptr_op(tls, OP_EQ, NULL); + +#ifndef OPENSSL_OPAQUE + method = give_me_a_test_method(); + SSL_CTX *ctx = SSL_CTX_new(method); + method->num_ciphers = fake_num_ciphers; + client_tls_context->ctx = ctx; + tls = tor_tls_new(-1, 0); + tt_ptr_op(tls, OP_EQ, NULL); +#endif /* !defined(OPENSSL_OPAQUE) */ + + done: + UNMOCK(tor_tls_cert_matches_key); + crypto_pk_free(key1); + crypto_pk_free(key2); + tor_tls_free(tls); + tor_free(method); + tor_tls_free_all(); +} + +#define NS_MODULE tortls + +static void +library_init(void) +{ +#ifdef OPENSSL_1_1_API + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); +#else + SSL_library_init(); + SSL_load_error_strings(); +#endif +} + +static void +test_tortls_get_state_description(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + char *buf; + SSL_CTX *ctx; + + library_init(); + ctx = SSL_CTX_new(SSLv23_method()); + + buf = tor_malloc_zero(1000); + tls = tor_malloc_zero(sizeof(tor_tls_t)); + + tor_tls_get_state_description(NULL, buf, 20); + tt_str_op(buf, OP_EQ, "(No SSL object)"); + + SSL_free(tls->ssl); + tls->ssl = NULL; + tor_tls_get_state_description(tls, buf, 20); + tt_str_op(buf, OP_EQ, "(No SSL object)"); + + tls->ssl = SSL_new(ctx); + tor_tls_get_state_description(tls, buf, 200); + tt_str_op(buf, OP_EQ, SSL_STATE_STR " in HANDSHAKE"); + + tls->state = TOR_TLS_ST_OPEN; + tor_tls_get_state_description(tls, buf, 200); + tt_str_op(buf, OP_EQ, SSL_STATE_STR " in OPEN"); + + tls->state = TOR_TLS_ST_GOTCLOSE; + tor_tls_get_state_description(tls, buf, 200); + tt_str_op(buf, OP_EQ, SSL_STATE_STR " in GOTCLOSE"); + + tls->state = TOR_TLS_ST_SENTCLOSE; + tor_tls_get_state_description(tls, buf, 200); + tt_str_op(buf, OP_EQ, SSL_STATE_STR " in SENTCLOSE"); + + tls->state = TOR_TLS_ST_CLOSED; + tor_tls_get_state_description(tls, buf, 200); + tt_str_op(buf, OP_EQ, SSL_STATE_STR " in CLOSED"); + + tls->state = TOR_TLS_ST_RENEGOTIATE; + tor_tls_get_state_description(tls, buf, 200); + tt_str_op(buf, OP_EQ, SSL_STATE_STR " in RENEGOTIATE"); + + tls->state = TOR_TLS_ST_BUFFEREVENT; + tor_tls_get_state_description(tls, buf, 200); + tt_str_op(buf, OP_EQ, SSL_STATE_STR); + + tls->state = 7; + tor_tls_get_state_description(tls, buf, 200); + tt_str_op(buf, OP_EQ, SSL_STATE_STR " in unknown TLS state"); + + done: + SSL_CTX_free(ctx); + SSL_free(tls->ssl); + tor_free(buf); + tor_free(tls); +} + +static void +test_tortls_get_by_ssl(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + tor_tls_t *res; + SSL_CTX *ctx; + SSL *ssl; + + library_init(); + tor_tls_allocate_tor_tls_object_ex_data_index(); + + ctx = SSL_CTX_new(SSLv23_method()); + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->magic = TOR_TLS_MAGIC; + + ssl = SSL_new(ctx); + + res = tor_tls_get_by_ssl(ssl); + tt_assert(!res); + + SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls); + + res = tor_tls_get_by_ssl(ssl); + tt_assert(res == tls); + + done: + SSL_free(ssl); + SSL_CTX_free(ctx); + tor_free(tls); +} + +static void +test_tortls_allocate_tor_tls_object_ex_data_index(void *ignored) +{ + (void)ignored; + int first; + + tor_tls_allocate_tor_tls_object_ex_data_index(); + + first = tor_tls_object_ex_data_index; + tor_tls_allocate_tor_tls_object_ex_data_index(); + tt_int_op(first, OP_EQ, tor_tls_object_ex_data_index); + + done: + (void)0; +} + +static void +test_tortls_log_one_error(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + SSL_CTX *ctx; + SSL *ssl = NULL; + + library_init(); + + ctx = SSL_CTX_new(SSLv23_method()); + tls = tor_malloc_zero(sizeof(tor_tls_t)); + setup_capture_of_logs(LOG_INFO); + + tor_tls_log_one_error(NULL, 0, LOG_WARN, 0, "something"); + expect_log_msg("TLS error while something: " + "(null) (in (null):(null):---)\n"); + + mock_clean_saved_logs(); + tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL); + expect_log_msg("TLS error: (null) " + "(in (null):(null):---)\n"); + + mock_clean_saved_logs(); + tls->address = tor_strdup("127.hello"); + tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL); + expect_log_msg("TLS error with 127.hello: " + "(null) (in (null):(null):---)\n"); + tor_free(tls->address); + + mock_clean_saved_logs(); + tls->address = tor_strdup("127.hello"); + tor_tls_log_one_error(tls, 0, LOG_WARN, 0, "blarg"); + expect_log_msg("TLS error while blarg with " + "127.hello: (null) (in (null):(null):---)\n"); + + mock_clean_saved_logs(); + tor_tls_log_one_error(tls, ERR_PACK(1, 2, 3), LOG_WARN, 0, NULL); + expect_log_msg("TLS error with 127.hello: " + "BN lib (in unknown library:(null):---)\n"); + + mock_clean_saved_logs(); + tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTP_REQUEST), + LOG_WARN, 0, NULL); + expect_log_severity(LOG_INFO); + + mock_clean_saved_logs(); + tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTPS_PROXY_REQUEST), + LOG_WARN, 0, NULL); + expect_log_severity(LOG_INFO); + + mock_clean_saved_logs(); + tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_LENGTH_MISMATCH), + LOG_WARN, 0, NULL); + expect_log_severity(LOG_INFO); + +#ifndef OPENSSL_1_1_API + mock_clean_saved_logs(); + tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_TOO_LARGE), + LOG_WARN, 0, NULL); + expect_log_severity(LOG_INFO); +#endif /* !defined(OPENSSL_1_1_API) */ + + mock_clean_saved_logs(); + tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNKNOWN_PROTOCOL), + LOG_WARN, 0, NULL); + expect_log_severity(LOG_INFO); + + mock_clean_saved_logs(); + tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNSUPPORTED_PROTOCOL), + LOG_WARN, 0, NULL); + expect_log_severity(LOG_INFO); + + tls->ssl = SSL_new(ctx); + + mock_clean_saved_logs(); + tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL); + expect_log_msg("TLS error with 127.hello: (null)" + " (in (null):(null):" SSL_STATE_STR ")\n"); + + done: + teardown_capture_of_logs(); + SSL_free(ssl); + SSL_CTX_free(ctx); + if (tls && tls->ssl) + SSL_free(tls->ssl); + if (tls) + tor_free(tls->address); + tor_free(tls); +} + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_get_error(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + int ret; + SSL_CTX *ctx; + + library_init(); + + ctx = SSL_CTX_new(SSLv23_method()); + setup_capture_of_logs(LOG_INFO); + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = SSL_new(ctx); + SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL); + + ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0); + tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_IO); + expect_log_msg("TLS error: unexpected close while" + " something (before/accept initialization)\n"); + + mock_clean_saved_logs(); + ret = tor_tls_get_error(tls, 2, 0, "something", LOG_WARN, 0); + tt_int_op(ret, OP_EQ, 0); + expect_no_log_entry(); + + mock_clean_saved_logs(); + ret = tor_tls_get_error(tls, 0, 1, "something", LOG_WARN, 0); + tt_int_op(ret, OP_EQ, -11); + expect_no_log_entry(); + + mock_clean_saved_logs(); + ERR_clear_error(); + ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99); + ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0); + tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); + expect_log_msg("TLS error while something: (null)" + " (in bignum routines:(null):before/accept initialization)\n"); + + mock_clean_saved_logs(); + ERR_clear_error(); + tls->ssl->rwstate = SSL_READING; + SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ; + ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0); + tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD); + expect_no_log_entry(); + + mock_clean_saved_logs(); + ERR_clear_error(); + tls->ssl->rwstate = SSL_READING; + SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE; + ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0); + tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE); + expect_no_log_entry(); + + mock_clean_saved_logs(); + ERR_clear_error(); + tls->ssl->rwstate = 0; + tls->ssl->shutdown = SSL_RECEIVED_SHUTDOWN; + tls->ssl->s3->warn_alert =SSL_AD_CLOSE_NOTIFY; + ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0); + tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE); + expect_log_entry(); + + mock_clean_saved_logs(); + ret = tor_tls_get_error(tls, 0, 2, "something", LOG_WARN, 0); + tt_int_op(ret, OP_EQ, -10); + expect_no_log_entry(); + + mock_clean_saved_logs(); + ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99); + ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0); + tt_int_op(ret, OP_EQ, -9); + expect_log_msg("TLS error while something: (null) (in system library:" + "connect:before/accept initialization)\n"); + + done: + teardown_capture_of_logs(); + SSL_free(tls->ssl); + tor_free(tls); + SSL_CTX_free(ctx); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +static void +test_tortls_always_accept_verify_cb(void *ignored) +{ + (void)ignored; + int ret; + + ret = always_accept_verify_cb(0, NULL); + tt_int_op(ret, OP_EQ, 1); + + done: + (void)0; +} + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_x509_cert_free(void *ignored) +{ + (void)ignored; + tor_x509_cert_t *cert; + + cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); + tor_x509_cert_free(cert); + + cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); + cert->cert = X509_new(); + cert->encoded = tor_malloc_zero(1); + tor_x509_cert_free(cert); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static int +fixed_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) +{ + (void) a; (void) b; + return 1; +} + +/* + * Use only for the matching fake_x509_free() call + */ +static X509 * +fake_x509_malloc(void) +{ + return tor_malloc_zero(sizeof(X509)); +} + +static void +fake_x509_free(X509 *cert) +{ + if (cert) { + if (cert->cert_info) { + if (cert->cert_info->key) { + if (cert->cert_info->key->pkey) { + tor_free(cert->cert_info->key->pkey); + } + tor_free(cert->cert_info->key); + } + tor_free(cert->cert_info); + } + tor_free(cert); + } +} + +static void +test_tortls_cert_matches_key(void *ignored) +{ + (void)ignored; + int res; + tor_tls_t *tls; + tor_x509_cert_t *cert; + X509 *one = NULL, *two = NULL; + EVP_PKEY_ASN1_METHOD *meth = EVP_PKEY_asn1_new(999, 0, NULL, NULL); + EVP_PKEY_asn1_set_public(meth, NULL, NULL, fixed_pub_cmp, NULL, NULL, NULL); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); + one = fake_x509_malloc(); + one->references = 1; + two = fake_x509_malloc(); + two->references = 1; + + res = tor_tls_cert_matches_key(tls, cert); + tt_int_op(res, OP_EQ, 0); + + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); + tls->ssl->session->peer = one; + res = tor_tls_cert_matches_key(tls, cert); + tt_int_op(res, OP_EQ, 0); + + cert->cert = two; + res = tor_tls_cert_matches_key(tls, cert); + tt_int_op(res, OP_EQ, 0); + + one->cert_info = tor_malloc_zero(sizeof(X509_CINF)); + one->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY)); + one->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY)); + one->cert_info->key->pkey->references = 1; + one->cert_info->key->pkey->ameth = meth; + one->cert_info->key->pkey->type = 1; + + two->cert_info = tor_malloc_zero(sizeof(X509_CINF)); + two->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY)); + two->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY)); + two->cert_info->key->pkey->references = 1; + two->cert_info->key->pkey->ameth = meth; + two->cert_info->key->pkey->type = 2; + + res = tor_tls_cert_matches_key(tls, cert); + tt_int_op(res, OP_EQ, 0); + + one->cert_info->key->pkey->type = 1; + two->cert_info->key->pkey->type = 1; + res = tor_tls_cert_matches_key(tls, cert); + tt_int_op(res, OP_EQ, 1); + + done: + EVP_PKEY_asn1_free(meth); + tor_free(tls->ssl->session); + tor_free(tls->ssl); + tor_free(tls); + tor_free(cert); + fake_x509_free(one); + fake_x509_free(two); +} + +static void +test_tortls_cert_get_key(void *ignored) +{ + (void)ignored; + tor_x509_cert_t *cert = NULL; + crypto_pk_t *res = NULL; + cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); + X509 *key = NULL; + key = fake_x509_malloc(); + key->references = 1; + + res = tor_tls_cert_get_key(cert); + tt_assert(!res); + + cert->cert = key; + key->cert_info = tor_malloc_zero(sizeof(X509_CINF)); + key->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY)); + key->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY)); + key->cert_info->key->pkey->references = 1; + key->cert_info->key->pkey->type = 2; + res = tor_tls_cert_get_key(cert); + tt_assert(!res); + + done: + fake_x509_free(key); + tor_free(cert); + crypto_pk_free(res); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +static void +test_tortls_get_my_client_auth_key(void *ignored) +{ + (void)ignored; + crypto_pk_t *ret; + crypto_pk_t *expected; + tor_tls_context_t *ctx; + RSA *k = RSA_new(); + + ctx = tor_malloc_zero(sizeof(tor_tls_context_t)); + expected = crypto_new_pk_from_openssl_rsa_(k); + ctx->auth_key = expected; + + client_tls_context = NULL; + ret = tor_tls_get_my_client_auth_key(); + tt_assert(!ret); + + client_tls_context = ctx; + ret = tor_tls_get_my_client_auth_key(); + tt_assert(ret == expected); + + done: + tor_free(expected); + tor_free(ctx); +} + +#ifndef HAVE_SSL_GET_CLIENT_CIPHERS +static SSL_CIPHER * +get_cipher_by_name(const char *name) +{ + int i; + const SSL_METHOD *method = SSLv23_method(); + int num = method->num_ciphers(); + + for (i = 0; i < num; ++i) { + const SSL_CIPHER *cipher = method->get_cipher(i); + const char *ciphername = SSL_CIPHER_get_name(cipher); + if (!strcmp(ciphername, name)) { + return (SSL_CIPHER *)cipher; + } + } + + return NULL; +} +#endif /* !defined(HAVE_SSL_GET_CLIENT_CIPHERS) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_get_ciphersuite_name(void *ignored) +{ + (void)ignored; + const char *ret; + tor_tls_t *ctx; + ctx = tor_malloc_zero(sizeof(tor_tls_t)); + ctx->ssl = tor_malloc_zero(sizeof(SSL)); + + ret = tor_tls_get_ciphersuite_name(ctx); + tt_str_op(ret, OP_EQ, "(NONE)"); + + done: + tor_free(ctx->ssl); + tor_free(ctx); +} + +static SSL_CIPHER * +get_cipher_by_id(uint16_t id) +{ + int i; + const SSL_METHOD *method = SSLv23_method(); + int num = method->num_ciphers(); + for (i = 0; i < num; ++i) { + const SSL_CIPHER *cipher = method->get_cipher(i); + if (id == (SSL_CIPHER_get_id(cipher) & 0xffff)) { + return (SSL_CIPHER *)cipher; + } + } + + return NULL; +} + +static void +test_tortls_classify_client_ciphers(void *ignored) +{ + (void)ignored; + int i; + int ret; + SSL_CTX *ctx; + SSL *ssl; + tor_tls_t *tls; + STACK_OF(SSL_CIPHER) *ciphers; + SSL_CIPHER *tmp_cipher; + + library_init(); + + tor_tls_allocate_tor_tls_object_ex_data_index(); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->magic = TOR_TLS_MAGIC; + + ctx = SSL_CTX_new(TLSv1_method()); + ssl = SSL_new(ctx); + tls->ssl = ssl; + + ciphers = sk_SSL_CIPHER_new_null(); + + ret = tor_tls_classify_client_ciphers(ssl, NULL); + tt_int_op(ret, OP_EQ, -1); + + SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls); + tls->client_cipher_list_type = 42; + + ret = tor_tls_classify_client_ciphers(ssl, NULL); + tt_int_op(ret, OP_EQ, 42); + + tls->client_cipher_list_type = 0; + ret = tor_tls_classify_client_ciphers(ssl, ciphers); + tt_int_op(ret, OP_EQ, 1); + tt_int_op(tls->client_cipher_list_type, OP_EQ, 1); + + tls->client_cipher_list_type = 0; + ret = tor_tls_classify_client_ciphers(ssl, SSL_get_ciphers(ssl)); + tt_int_op(ret, OP_EQ, 3); + tt_int_op(tls->client_cipher_list_type, OP_EQ, 3); + + SSL_CIPHER *one = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_128_SHA), + *two = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA), + *three = get_cipher_by_name(SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA), + *four = NULL; + sk_SSL_CIPHER_push(ciphers, one); + sk_SSL_CIPHER_push(ciphers, two); + sk_SSL_CIPHER_push(ciphers, three); + sk_SSL_CIPHER_push(ciphers, four); + + tls->client_cipher_list_type = 0; + ret = tor_tls_classify_client_ciphers(ssl, ciphers); + tt_int_op(ret, OP_EQ, 1); + tt_int_op(tls->client_cipher_list_type, OP_EQ, 1); + + sk_SSL_CIPHER_zero(ciphers); + + one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384"); + tt_assert(one); + one->id = 0x00ff; + two = get_cipher_by_name("ECDHE-RSA-AES128-GCM-SHA256"); + tt_assert(two); + two->id = 0x0000; + sk_SSL_CIPHER_push(ciphers, one); + tls->client_cipher_list_type = 0; + ret = tor_tls_classify_client_ciphers(ssl, ciphers); + tt_int_op(ret, OP_EQ, 3); + tt_int_op(tls->client_cipher_list_type, OP_EQ, 3); + + sk_SSL_CIPHER_push(ciphers, two); + tls->client_cipher_list_type = 0; + ret = tor_tls_classify_client_ciphers(ssl, ciphers); + tt_int_op(ret, OP_EQ, 3); + tt_int_op(tls->client_cipher_list_type, OP_EQ, 3); + + one->id = 0xC00A; + tls->client_cipher_list_type = 0; + ret = tor_tls_classify_client_ciphers(ssl, ciphers); + tt_int_op(ret, OP_EQ, 3); + tt_int_op(tls->client_cipher_list_type, OP_EQ, 3); + + sk_SSL_CIPHER_zero(ciphers); + for (i=0; v2_cipher_list[i]; i++) { + tmp_cipher = get_cipher_by_id(v2_cipher_list[i]); + tt_assert(tmp_cipher); + sk_SSL_CIPHER_push(ciphers, tmp_cipher); + } + tls->client_cipher_list_type = 0; + ret = tor_tls_classify_client_ciphers(ssl, ciphers); + tt_int_op(ret, OP_EQ, 2); + tt_int_op(tls->client_cipher_list_type, OP_EQ, 2); + + done: + sk_SSL_CIPHER_free(ciphers); + SSL_free(tls->ssl); + tor_free(tls); + SSL_CTX_free(ctx); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +static void +test_tortls_client_is_using_v2_ciphers(void *ignored) +{ + (void)ignored; + +#ifdef HAVE_SSL_GET_CLIENT_CIPHERS + tt_skip(); + done: + (void)1; +#else + int ret; + SSL_CTX *ctx; + SSL *ssl; + SSL_SESSION *sess; + STACK_OF(SSL_CIPHER) *ciphers; + + library_init(); + + ctx = SSL_CTX_new(TLSv1_method()); + ssl = SSL_new(ctx); + sess = SSL_SESSION_new(); + + ret = tor_tls_client_is_using_v2_ciphers(ssl); + tt_int_op(ret, OP_EQ, -1); + + ssl->session = sess; + ret = tor_tls_client_is_using_v2_ciphers(ssl); + tt_int_op(ret, OP_EQ, 0); + + ciphers = sk_SSL_CIPHER_new_null(); + SSL_CIPHER *one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384"); + tt_assert(one); + one->id = 0x00ff; + sk_SSL_CIPHER_push(ciphers, one); + sess->ciphers = ciphers; + ret = tor_tls_client_is_using_v2_ciphers(ssl); + tt_int_op(ret, OP_EQ, 1); + done: + SSL_free(ssl); + SSL_CTX_free(ctx); +#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */ +} + +#ifndef OPENSSL_OPAQUE +static X509 *fixed_try_to_extract_certs_from_tls_cert_out_result = NULL; +static X509 *fixed_try_to_extract_certs_from_tls_id_cert_out_result = NULL; + +static void +fixed_try_to_extract_certs_from_tls(int severity, tor_tls_t *tls, + tor_x509_cert_impl_t **cert_out, + tor_x509_cert_impl_t **id_cert_out) +{ + (void) severity; + (void) tls; + *cert_out = fixed_try_to_extract_certs_from_tls_cert_out_result; + *id_cert_out = fixed_try_to_extract_certs_from_tls_id_cert_out_result; +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static const char* notCompletelyValidCertString = + "-----BEGIN CERTIFICATE-----\n" + "MIICVjCCAb8CAg37MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n" + "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n" + "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n" + "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n" + "ODIyMDUyNzIzWhcNMTcwODIxMDUyNzIzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n" + "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n" + "ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYBBrx5PlP0WNI/ZdzD\n" + "+6Pktmurn+F2kQYbtc7XQh8/LTBvCo+P6iZoLEmUA9e7EXLRxgU1CVqeAi7QcAn9\n" + "MwBlc8ksFJHB0rtf9pmf8Oza9E0Bynlq/4/Kb1x+d+AyhL7oK9tQwB24uHOueHi1\n" + "C/iVv8CSWKiYe6hzN1txYe8rAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAASPdjigJ\n" + "kXCqKWpnZ/Oc75EUcMi6HztaW8abUMlYXPIgkV2F7YanHOB7K4f7OOLjiz8DTPFf\n" + "jC9UeuErhaA/zzWi8ewMTFZW/WshOrm3fNvcMrMLKtH534JKvcdMg6qIdjTFINIr\n" + "evnAhf0cwULaebn+lMs8Pdl7y37+sfluVok=\n" + "-----END CERTIFICATE-----\n"; +#endif /* !defined(OPENSSL_OPAQUE) */ + +static const char* validCertString = "-----BEGIN CERTIFICATE-----\n" + "MIIDpTCCAY0CAg3+MA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMREwDwYD\n" + "VQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIGA1UECgwLVG9yIFRl\n" + "c3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkwNjEzMzk1OVoXDTQz\n" + "MDEyMjEzMzk1OVowVjELMAkGA1UEBhMCVVMxEDAOBgNVBAcMB0NoaWNhZ28xFDAS\n" + "BgNVBAoMC1RvciBUZXN0aW5nMR8wHQYDVQQDDBZ0ZXN0aW5nLnRvcnByb2plY3Qu\n" + "b3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoT6uyVVhWyOF3wkHjjYbd\n" + "nKaykyRv4JVtKQdZ4OpEErmX1zw4MmyzpQNV6iR4bQnWiyLfzyVJMZDIC/WILBfX\n" + "w2Pza/yuLgUvDc3twMuhOACzOQVO8PrEF/aVv2+hbCCy2udXvKhnYn+CCXl3ozc8\n" + "XcKYvujTXDyvGWY3xwAjlQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCUvnhzQWuQ\n" + "MrN+pERkE+zcTI/9dGS90rUMMLgu8VDNqTa0TUQh8uO0EQ6uDvI8Js6e8tgwS0BR\n" + "UBahqb7ZHv+rejGCBr5OudqD+x4STiiuPNJVs86JTLN8SpM9CHjIBH5WCCN2KOy3\n" + "mevNoRcRRyYJzSFULCunIK6FGulszigMYGscrO4oiTkZiHPh9KvWT40IMiHfL+Lw\n" + "EtEWiLex6064LcA2YQ1AMuSZyCexks63lcfaFmQbkYOKqXa1oLkIRuDsOaSVjTfe\n" + "vec+X6jvf12cFTKS5WIeqkKF2Irt+dJoiHEGTe5RscUMN/f+gqHPzfFz5dR23sxo\n" + "g+HC6MZHlFkLAOx3wW6epPS8A/m1mw3zMPoTnb2U2YYt8T0dJMMlUn/7Y1sEAa+a\n" + "dSTMaeUf6VnJ//11m454EZl1to9Z7oJOgqmFffSrdD4BGIWe8f7hhW6L1Enmqe/J\n" + "BKL3wbzZh80O1W0bndAwhnEEhlzneFY84cbBo9pmVxpODHkUcStpr5Z7pBDrcL21\n" + "Ss/aB/1YrsVXhdvJdOGxl3Mnl9dUY57CympLGlT8f0pPS6GAKOelECOhFMHmJd8L\n" + "dj3XQSmKtYHevZ6IvuMXSlB/fJvSjSlkCuLo5+kJoaqPuRu+i/S1qxeRy3CBwmnE\n" + "LdSNdcX4N79GQJ996PA8+mUCQG7YRtK+WA==\n" + "-----END CERTIFICATE-----\n"; + +static const char* caCertString = "-----BEGIN CERTIFICATE-----\n" + "MIIFjzCCA3egAwIBAgIJAKd5WgyfPMYRMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV\n" + "BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIG\n" + "A1UECgwLVG9yIFRlc3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkw\n" + "NjEzMzc0MVoXDTQzMDEyMjEzMzc0MVowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM\n" + "CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRQwEgYDVQQKDAtUb3IgVGVzdGlu\n" + "ZzEUMBIGA1UEAwwLVG9yIFRlc3RpbmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n" + "ggIKAoICAQCpLMUEiLW5leUgBZoEJms2V7lZRhIAjnJBhVMHD0e3UubNknmaQoxf\n" + "ARz3rvqOaRd0JlV+qM9qE0DjiYcCVP1cAfqAo9d83uS1vwY3YMVJzADlaIiHfyVW\n" + "uEgBy0vvkeUBqaua24dYlcwsemOiXYLu41yM1wkcGHW1AhBNHppY6cznb8TyLgNM\n" + "2x3SGUdzc5XMyAFx51faKGBA3wjs+Hg1PLY7d30nmCgEOBavpm5I1disM/0k+Mcy\n" + "YmAKEo/iHJX/rQzO4b9znP69juLlR8PDBUJEVIG/CYb6+uw8MjjUyiWXYoqfVmN2\n" + "hm/lH8b6rXw1a2Aa3VTeD0DxaWeacMYHY/i01fd5n7hCoDTRNdSw5KJ0L3Z0SKTu\n" + "0lzffKzDaIfyZGlpW5qdouACkWYzsaitQOePVE01PIdO30vUfzNTFDfy42ccx3Di\n" + "59UCu+IXB+eMtrBfsok0Qc63vtF1linJgjHW1z/8ujk8F7/qkOfODhk4l7wngc2A\n" + "EmwWFIFoGaiTEZHB9qteXr4unbXZ0AHpM02uGGwZEGohjFyebEb73M+J57WKKAFb\n" + "PqbLcGUksL1SHNBNAJcVLttX55sO4nbidOS/kA3m+F1R04MBTyQF9qA6YDDHqdI3\n" + "h/3pw0Z4fxVouTYT4/NfRnX4JTP4u+7Mpcoof28VME0qWqD1LnRhFQIDAQABo1Aw\n" + "TjAdBgNVHQ4EFgQUMoAgIXH7pZ3QMRwTjT+DM9Yo/v0wHwYDVR0jBBgwFoAUMoAg\n" + "IXH7pZ3QMRwTjT+DM9Yo/v0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n" + "AgEAUJxacjXR9sT+Xs6ISFiUsyd0T6WVKMnV46xrYJHirGfx+krWHrjxMY+ZtxYD\n" + "DBDGlo11Qc4v6QrclNf5QUBfIiGQsP9Cm6hHcQ+Tpg9HHCgSqG1YNPwCPReCR4br\n" + "BLvLfrfkcBL2IWM0PdQdCze+59DBfipsULD2mEn9fjYRXQEwb2QWtQ9qRc20Yb/x\n" + "Q4b/+CvUodLkaq7B8MHz0BV8HHcBoph6DYaRmO/N+hPauIuSp6XyaGYcEefGKVKj\n" + "G2+fcsdyXsoijNdL8vNKwm4j2gVwCBnw16J00yfFoV46YcbfqEdJB2je0XSvwXqt\n" + "14AOTngxso2h9k9HLtrfpO1ZG/B5AcCMs1lzbZ2fp5DPHtjvvmvA2RJqgo3yjw4W\n" + "4DHAuTglYFlC3mDHNfNtcGP20JvepcQNzNP2UzwcpOc94hfKikOFw+gf9Vf1qd0y\n" + "h/Sk6OZHn2+JVUPiWHIQV98Vtoh4RmUZDJD+b55ia3fQGTGzt4z1XFzQYSva5sfs\n" + "wocS/papthqWldQU7x+3wofNd5CNU1x6WKXG/yw30IT/4F8ADJD6GeygNT8QJYvt\n" + "u/8lAkbOy6B9xGmSvr0Kk1oq9P2NshA6kalxp1Oz/DTNDdL4AeBXV3JmM6WWCjGn\n" + "Yy1RT69d0rwYc5u/vnqODz1IjvT90smsrkBumGt791FAFeg=\n" + "-----END CERTIFICATE-----\n"; + +static X509 * +read_cert_from(const char *str) +{ + BIO *bio = BIO_new(BIO_s_mem()); + BIO_write(bio, str, (int) strlen(str)); + X509 *res = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + return res; +} + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_verify(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + crypto_pk_t *k = NULL; + X509 *cert1 = NULL, *cert2 = NULL, *invalidCert = NULL, + *validCert = NULL, *caCert = NULL; + + cert1 = tor_malloc_zero(sizeof(X509)); + cert1->references = 10; + + cert2 = tor_malloc_zero(sizeof(X509)); + cert2->references = 10; + + validCert = read_cert_from(validCertString); + caCert = read_cert_from(caCertString); + invalidCert = read_cert_from(notCompletelyValidCertString); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + ret = tor_tls_verify(LOG_WARN, tls, &k); + tt_int_op(ret, OP_EQ, -1); + + MOCK(try_to_extract_certs_from_tls, fixed_try_to_extract_certs_from_tls); + + fixed_try_to_extract_certs_from_tls_cert_out_result = cert1; + ret = tor_tls_verify(LOG_WARN, tls, &k); + tt_int_op(ret, OP_EQ, -1); + + fixed_try_to_extract_certs_from_tls_id_cert_out_result = cert2; + ret = tor_tls_verify(LOG_WARN, tls, &k); + tt_int_op(ret, OP_EQ, -1); + + fixed_try_to_extract_certs_from_tls_cert_out_result = invalidCert; + fixed_try_to_extract_certs_from_tls_id_cert_out_result = invalidCert; + + ret = tor_tls_verify(LOG_WARN, tls, &k); + tt_int_op(ret, OP_EQ, -1); + + fixed_try_to_extract_certs_from_tls_cert_out_result = validCert; + fixed_try_to_extract_certs_from_tls_id_cert_out_result = caCert; + + ret = tor_tls_verify(LOG_WARN, tls, &k); + tt_int_op(ret, OP_EQ, 0); + tt_assert(k); + + done: + UNMOCK(try_to_extract_certs_from_tls); + tor_free(cert1); + tor_free(cert2); + tor_free(tls); + tor_free(k); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_check_lifetime(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + X509 *validCert = read_cert_from(validCertString); + time_t now = time(NULL); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0); + tt_int_op(ret, OP_EQ, -1); + + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); + tls->ssl->session->peer = validCert; + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0); + tt_int_op(ret, OP_EQ, 0); + + ASN1_STRING_free(validCert->cert_info->validity->notBefore); + validCert->cert_info->validity->notBefore = ASN1_TIME_set(NULL, now-10); + ASN1_STRING_free(validCert->cert_info->validity->notAfter); + validCert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, now+60); + + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, -1000); + tt_int_op(ret, OP_EQ, -1); + + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), -1000, 0); + tt_int_op(ret, OP_EQ, -1); + + done: + tor_free(tls->ssl->session); + tor_free(tls->ssl); + tor_free(tls); + X509_free(validCert); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static int fixed_ssl_pending_result = 0; + +static int +fixed_ssl_pending(const SSL *ignored) +{ + (void)ignored; + return fixed_ssl_pending_result; +} + +static void +test_tortls_get_pending_bytes(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + SSL_METHOD *method; + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + method = tor_malloc_zero(sizeof(SSL_METHOD)); + method->ssl_pending = fixed_ssl_pending; + tls->ssl->method = method; + + fixed_ssl_pending_result = 42; + ret = tor_tls_get_pending_bytes(tls); + tt_int_op(ret, OP_EQ, 42); + + done: + tor_free(method); + tor_free(tls->ssl); + tor_free(tls); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_SSL_SESSION_get_master_key(void *ignored) +{ + (void)ignored; + size_t ret; + tor_tls_t *tls; + uint8_t *out; + out = tor_malloc_zero(1); + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); + tls->ssl->session->master_key_length = 1; + +#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY + tls->ssl->session->master_key[0] = 43; + ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 0); + tt_int_op(ret, OP_EQ, 1); + tt_int_op(out[0], OP_EQ, 0); + + ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 1); + tt_int_op(ret, OP_EQ, 1); + tt_int_op(out[0], OP_EQ, 43); + + done: +#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */ + tor_free(tls->ssl->session); + tor_free(tls->ssl); + tor_free(tls); + tor_free(out); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_get_tlssecrets(void *ignored) +{ + (void)ignored; + int ret; + uint8_t *secret_out = tor_malloc_zero(DIGEST256_LEN); + tor_tls_t *tls; + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); + tls->ssl->session->master_key_length = 1; + tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE)); + + ret = tor_tls_get_tlssecrets(tls, secret_out); + tt_int_op(ret, OP_EQ, 0); + + done: + tor_free(secret_out); + tor_free(tls->ssl->s3); + tor_free(tls->ssl->session); + tor_free(tls->ssl); + tor_free(tls); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_get_buffer_sizes(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + size_t rbuf_c=-1, rbuf_b=-1, wbuf_c=-1, wbuf_b=-1; + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE)); + + tls->ssl->s3->rbuf.buf = NULL; + tls->ssl->s3->rbuf.len = 1; + tls->ssl->s3->rbuf.offset = 0; + tls->ssl->s3->rbuf.left = 42; + + tls->ssl->s3->wbuf.buf = NULL; + tls->ssl->s3->wbuf.len = 2; + tls->ssl->s3->wbuf.offset = 0; + tls->ssl->s3->wbuf.left = 43; + + ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b); +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) + tt_int_op(ret, OP_EQ, -1); +#else + tt_int_op(ret, OP_EQ, 0); + tt_int_op(rbuf_c, OP_EQ, 0); + tt_int_op(wbuf_c, OP_EQ, 0); + tt_int_op(rbuf_b, OP_EQ, 42); + tt_int_op(wbuf_b, OP_EQ, 43); + + tls->ssl->s3->rbuf.buf = tor_malloc_zero(1); + tls->ssl->s3->wbuf.buf = tor_malloc_zero(1); + ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(rbuf_c, OP_EQ, 1); + tt_int_op(wbuf_c, OP_EQ, 2); + +#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */ + + done: + tor_free(tls->ssl->s3->rbuf.buf); + tor_free(tls->ssl->s3->wbuf.buf); + tor_free(tls->ssl->s3); + tor_free(tls->ssl); + tor_free(tls); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +typedef struct cert_pkey_st_local +{ + X509 *x509; + EVP_PKEY *privatekey; + const EVP_MD *digest; +} CERT_PKEY_local; + +typedef struct sess_cert_st_local +{ + STACK_OF(X509) *cert_chain; + int peer_cert_type; + CERT_PKEY_local *peer_key; + CERT_PKEY_local peer_pkeys[8]; + int references; +} SESS_CERT_local; + +static void +test_tortls_try_to_extract_certs_from_tls(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + X509 *cert = NULL, *id_cert = NULL, *c1 = NULL, *c2 = NULL; + SESS_CERT_local *sess = NULL; + + c1 = read_cert_from(validCertString); + c2 = read_cert_from(caCertString); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); + sess = tor_malloc_zero(sizeof(SESS_CERT_local)); + tls->ssl->session->sess_cert = (void *)sess; + + try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert); + tt_assert(!cert); + tt_assert(!id_cert); + + tls->ssl->session->peer = c1; + try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert); + tt_assert(cert == c1); + tt_assert(!id_cert); + X509_free(cert); /* decrease refcnt */ + + sess->cert_chain = sk_X509_new_null(); + try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert); + tt_assert(cert == c1); + tt_assert(!id_cert); + X509_free(cert); /* decrease refcnt */ + + sk_X509_push(sess->cert_chain, c1); + sk_X509_push(sess->cert_chain, c2); + + try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert); + tt_assert(cert == c1); + tt_assert(id_cert); + X509_free(cert); /* decrease refcnt */ + + done: + sk_X509_free(sess->cert_chain); + tor_free(sess); + tor_free(tls->ssl->session); + tor_free(tls->ssl); + tor_free(tls); + X509_free(c1); + X509_free(c2); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_get_peer_cert(void *ignored) +{ + (void)ignored; + tor_x509_cert_t *ret; + tor_tls_t *tls; + X509 *cert = NULL; + + cert = read_cert_from(validCertString); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); + + ret = tor_tls_get_peer_cert(tls); + tt_assert(!ret); + + tls->ssl->session->peer = cert; + ret = tor_tls_get_peer_cert(tls); + tt_assert(ret); + tt_assert(ret->cert == cert); + + done: + tor_x509_cert_free(ret); + tor_free(tls->ssl->session); + tor_free(tls->ssl); + tor_free(tls); + X509_free(cert); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_peer_has_cert(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + X509 *cert = NULL; + + cert = read_cert_from(validCertString); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); + + ret = tor_tls_peer_has_cert(tls); + tt_assert(!ret); + + tls->ssl->session->peer = cert; + ret = tor_tls_peer_has_cert(tls); + tt_assert(ret); + + done: + tor_free(tls->ssl->session); + tor_free(tls->ssl); + tor_free(tls); + X509_free(cert); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +static void +test_tortls_get_write_overhead_ratio(void *ignored) +{ + (void)ignored; + double ret; + + total_bytes_written_over_tls = 0; + ret = tls_get_write_overhead_ratio(); + tt_double_op(fabs(ret - 1.0), OP_LT, 1E-12); + + total_bytes_written_by_tls = 10; + total_bytes_written_over_tls = 1; + ret = tls_get_write_overhead_ratio(); + tt_double_op(fabs(ret - 10.0), OP_LT, 1E-12); + + total_bytes_written_by_tls = 10; + total_bytes_written_over_tls = 2; + ret = tls_get_write_overhead_ratio(); + tt_double_op(fabs(ret - 5.0), OP_LT, 1E-12); + + done: + (void)0; +} + +static void +test_tortls_is_server(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + int ret; + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->isServer = 1; + ret = tor_tls_is_server(tls); + tt_int_op(ret, OP_EQ, 1); + + done: + tor_free(tls); +} + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_session_secret_cb(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + SSL_CTX *ctx; + STACK_OF(SSL_CIPHER) *ciphers = NULL; + SSL_CIPHER *one; + + library_init(); + + tor_tls_allocate_tor_tls_object_ex_data_index(); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + + tls->magic = TOR_TLS_MAGIC; + + ctx = SSL_CTX_new(TLSv1_method()); + tls->ssl = SSL_new(ctx); + SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls); + + SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL); + + tor_tls_session_secret_cb(tls->ssl, NULL, NULL, NULL, NULL, NULL); + tt_assert(!tls->ssl->tls_session_secret_cb); + + one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384"); + one->id = 0x00ff; + ciphers = sk_SSL_CIPHER_new_null(); + sk_SSL_CIPHER_push(ciphers, one); + + tls->client_cipher_list_type = 0; + tor_tls_session_secret_cb(tls->ssl, NULL, NULL, ciphers, NULL, NULL); + tt_assert(!tls->ssl->tls_session_secret_cb); + + done: + sk_SSL_CIPHER_free(ciphers); + SSL_free(tls->ssl); + SSL_CTX_free(ctx); + tor_free(tls); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +/* TODO: It seems block_renegotiation and unblock_renegotiation and + * using different blags. This might not be correct */ +static void +test_tortls_block_renegotiation(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE)); +#ifndef SUPPORT_UNSAFE_RENEGOTIATION_FLAG +#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0 +#endif + + tls->ssl->s3->flags = SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; + + tor_tls_block_renegotiation(tls); + +#ifndef OPENSSL_1_1_API + tt_assert(!(tls->ssl->s3->flags & + SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); +#endif + + done: + tor_free(tls->ssl->s3); + tor_free(tls->ssl); + tor_free(tls); +} + +static void +test_tortls_unblock_renegotiation(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tor_tls_unblock_renegotiation(tls); + + tt_uint_op(SSL_get_options(tls->ssl) & + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, OP_EQ, + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); + + done: + tor_free(tls->ssl); + tor_free(tls); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_assert_renegotiation_unblocked(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tor_tls_unblock_renegotiation(tls); + tor_tls_assert_renegotiation_unblocked(tls); + /* No assertion here - this test will fail if tor_assert is turned on + * and things are bad. */ + + tor_free(tls->ssl); + tor_free(tls); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +static void +test_tortls_set_logged_address(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + + tor_tls_set_logged_address(tls, "foo bar"); + + tt_str_op(tls->address, OP_EQ, "foo bar"); + + tor_tls_set_logged_address(tls, "foo bar 2"); + tt_str_op(tls->address, OP_EQ, "foo bar 2"); + + done: + tor_free(tls->address); + tor_free(tls); +} + +#ifndef OPENSSL_OPAQUE +static void +example_cb(tor_tls_t *t, void *arg) +{ + (void)t; + (void)arg; +} + +static void +test_tortls_set_renegotiate_callback(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + const char *arg = "hello"; + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + + tor_tls_set_renegotiate_callback(tls, example_cb, (void*)arg); + tt_assert(tls->negotiated_callback == example_cb); + tt_assert(tls->callback_arg == arg); + tt_assert(!tls->got_renegotiate); + + /* Assumes V2_HANDSHAKE_SERVER */ + tt_assert(tls->ssl->info_callback == tor_tls_server_info_callback); + + tor_tls_set_renegotiate_callback(tls, NULL, (void*)arg); + tt_assert(tls->ssl->info_callback == tor_tls_debug_state_callback); + + done: + tor_free(tls->ssl); + tor_free(tls); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static SSL_CIPHER *fixed_cipher1 = NULL; +static SSL_CIPHER *fixed_cipher2 = NULL; +static const SSL_CIPHER * +fake_get_cipher(unsigned ncipher) +{ + + switch (ncipher) { + case 1: + return fixed_cipher1; + case 2: + return fixed_cipher2; + default: + return NULL; + } +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_find_cipher_by_id(void *ignored) +{ + (void)ignored; + int ret; + SSL *ssl; + SSL_CTX *ctx; + const SSL_METHOD *m = TLSv1_method(); + SSL_METHOD *empty_method = tor_malloc_zero(sizeof(SSL_METHOD)); + + fixed_cipher1 = tor_malloc_zero(sizeof(SSL_CIPHER)); + fixed_cipher2 = tor_malloc_zero(sizeof(SSL_CIPHER)); + fixed_cipher2->id = 0xC00A; + + library_init(); + + ctx = SSL_CTX_new(m); + ssl = SSL_new(ctx); + + ret = find_cipher_by_id(ssl, NULL, 0xC00A); + tt_int_op(ret, OP_EQ, 1); + + ret = find_cipher_by_id(ssl, m, 0xC00A); + tt_int_op(ret, OP_EQ, 1); + + ret = find_cipher_by_id(ssl, m, 0xFFFF); + tt_int_op(ret, OP_EQ, 0); + + ret = find_cipher_by_id(ssl, empty_method, 0xC00A); + tt_int_op(ret, OP_EQ, 1); + + ret = find_cipher_by_id(ssl, empty_method, 0xFFFF); +#ifdef HAVE_SSL_CIPHER_FIND + tt_int_op(ret, OP_EQ, 0); +#else + tt_int_op(ret, OP_EQ, 1); +#endif + + empty_method->get_cipher = fake_get_cipher; + ret = find_cipher_by_id(ssl, empty_method, 0xC00A); + tt_int_op(ret, OP_EQ, 1); + + empty_method->get_cipher = m->get_cipher; + empty_method->num_ciphers = m->num_ciphers; + ret = find_cipher_by_id(ssl, empty_method, 0xC00A); + tt_int_op(ret, OP_EQ, 1); + + empty_method->get_cipher = fake_get_cipher; + empty_method->num_ciphers = m->num_ciphers; + ret = find_cipher_by_id(ssl, empty_method, 0xC00A); + tt_int_op(ret, OP_EQ, 1); + + empty_method->num_ciphers = fake_num_ciphers; + ret = find_cipher_by_id(ssl, empty_method, 0xC00A); +#ifdef HAVE_SSL_CIPHER_FIND + tt_int_op(ret, OP_EQ, 1); +#else + tt_int_op(ret, OP_EQ, 0); +#endif + + done: + tor_free(empty_method); + SSL_free(ssl); + SSL_CTX_free(ctx); + tor_free(fixed_cipher1); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_debug_state_callback(void *ignored) +{ + (void)ignored; + SSL *ssl; + char *buf = tor_malloc_zero(1000); + int n; + + setup_capture_of_logs(LOG_DEBUG); + + ssl = tor_malloc_zero(sizeof(SSL)); + + tor_tls_debug_state_callback(ssl, 32, 45); + + n = tor_snprintf(buf, 1000, "SSL %p is now in state unknown" + " state [type=32,val=45].\n", ssl); + /* tor's snprintf returns -1 on error */ + tt_int_op(n, OP_NE, -1); + expect_log_msg(buf); + + done: + teardown_capture_of_logs(); + tor_free(buf); + tor_free(ssl); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_server_info_callback(void *ignored) +{ + (void)ignored; + tor_tls_t *tls; + SSL_CTX *ctx; + SSL *ssl; + + library_init(); + + ctx = SSL_CTX_new(TLSv1_method()); + ssl = SSL_new(ctx); + + tor_tls_allocate_tor_tls_object_ex_data_index(); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->magic = TOR_TLS_MAGIC; + tls->ssl = ssl; + + setup_full_capture_of_logs(LOG_WARN); + SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_A); + mock_clean_saved_logs(); + tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); + expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n"); + + SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B); + mock_clean_saved_logs(); + tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); + expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n"); + + SSL_set_state(ssl, 99); + mock_clean_saved_logs(); + tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); + expect_no_log_entry(); + teardown_capture_of_logs(); + + SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls); + SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B); + tls->negotiated_callback = 0; + tls->server_handshake_count = 120; + tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); + tt_int_op(tls->server_handshake_count, OP_EQ, 121); + + tls->server_handshake_count = 127; + tls->negotiated_callback = (void *)1; + tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); + tt_int_op(tls->server_handshake_count, OP_EQ, 127); + tt_int_op(tls->got_renegotiate, OP_EQ, 1); + + tls->ssl->session = SSL_SESSION_new(); + tls->wasV2Handshake = 0; + tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); + tt_int_op(tls->wasV2Handshake, OP_EQ, 0); + + done: + teardown_capture_of_logs(); + SSL_free(ssl); + SSL_CTX_free(ctx); + tor_free(tls); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static int fixed_ssl_read_result_index; +static int fixed_ssl_read_result[5]; +static int fixed_ssl_shutdown_result; + +static int +fixed_ssl_read(SSL *s, void *buf, int len) +{ + (void)s; + (void)buf; + (void)len; + return fixed_ssl_read_result[fixed_ssl_read_result_index++]; +} + +static int +fixed_ssl_shutdown(SSL *s) +{ + (void)s; + return fixed_ssl_shutdown_result; +} + +#ifndef LIBRESSL_VERSION_NUMBER +static int fixed_ssl_state_to_set; +static tor_tls_t *fixed_tls; + +static int +setting_version_ssl_shutdown(SSL *s) +{ + s->version = SSL2_VERSION; + return fixed_ssl_shutdown_result; +} + +static int +setting_version_and_state_ssl_shutdown(SSL *s) +{ + fixed_tls->state = fixed_ssl_state_to_set; + s->version = SSL2_VERSION; + return fixed_ssl_shutdown_result; +} +#endif /* !defined(LIBRESSL_VERSION_NUMBER) */ + +static int +dummy_handshake_func(SSL *s) +{ + (void)s; + return 1; +} + +static void +test_tortls_shutdown(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + SSL_METHOD *method = give_me_a_test_method(); + setup_capture_of_logs(LOG_WARN); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->ssl->method = method; + method->ssl_read = fixed_ssl_read; + method->ssl_shutdown = fixed_ssl_shutdown; + + ret = tor_tls_shutdown(tls); + tt_int_op(ret, OP_EQ, -9); + + tls->state = TOR_TLS_ST_SENTCLOSE; + fixed_ssl_read_result_index = 0; + fixed_ssl_read_result[0] = 10; + fixed_ssl_read_result[1] = -1; + ret = tor_tls_shutdown(tls); + tt_int_op(ret, OP_EQ, -9); + +#ifndef LIBRESSL_VERSION_NUMBER + tls->ssl->handshake_func = dummy_handshake_func; + + fixed_ssl_read_result_index = 0; + fixed_ssl_read_result[0] = 10; + fixed_ssl_read_result[1] = 42; + fixed_ssl_read_result[2] = 0; + fixed_ssl_shutdown_result = 1; + ERR_clear_error(); + tls->ssl->version = SSL2_VERSION; + ret = tor_tls_shutdown(tls); + tt_int_op(ret, OP_EQ, TOR_TLS_DONE); + tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED); + + fixed_ssl_read_result_index = 0; + fixed_ssl_read_result[0] = 10; + fixed_ssl_read_result[1] = 42; + fixed_ssl_read_result[2] = 0; + fixed_ssl_shutdown_result = 0; + ERR_clear_error(); + tls->ssl->version = 0; + ret = tor_tls_shutdown(tls); + tt_int_op(ret, OP_EQ, TOR_TLS_DONE); + tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED); + + fixed_ssl_read_result_index = 0; + fixed_ssl_read_result[0] = 10; + fixed_ssl_read_result[1] = 42; + fixed_ssl_read_result[2] = 0; + fixed_ssl_shutdown_result = 0; + ERR_clear_error(); + tls->ssl->version = 0; + method->ssl_shutdown = setting_version_ssl_shutdown; + ret = tor_tls_shutdown(tls); + tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); + + fixed_ssl_read_result_index = 0; + fixed_ssl_read_result[0] = 10; + fixed_ssl_read_result[1] = 42; + fixed_ssl_read_result[2] = 0; + fixed_ssl_shutdown_result = 0; + fixed_tls = tls; + fixed_ssl_state_to_set = TOR_TLS_ST_GOTCLOSE; + ERR_clear_error(); + tls->ssl->version = 0; + method->ssl_shutdown = setting_version_and_state_ssl_shutdown; + ret = tor_tls_shutdown(tls); + tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); + + fixed_ssl_read_result_index = 0; + fixed_ssl_read_result[0] = 10; + fixed_ssl_read_result[1] = 42; + fixed_ssl_read_result[2] = 0; + fixed_ssl_read_result[3] = -1; + fixed_ssl_shutdown_result = 0; + fixed_tls = tls; + fixed_ssl_state_to_set = 0; + ERR_clear_error(); + tls->ssl->version = 0; + method->ssl_shutdown = setting_version_and_state_ssl_shutdown; + ret = tor_tls_shutdown(tls); + tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); +#endif /* !defined(LIBRESSL_VERSION_NUMBER) */ + + done: + teardown_capture_of_logs(); + tor_free(method); + tor_free(tls->ssl); + tor_free(tls); +} + +static int negotiated_callback_called; + +static void +negotiated_callback_setter(tor_tls_t *t, void *arg) +{ + (void)t; + (void)arg; + negotiated_callback_called++; +} + +static void +test_tortls_read(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + char buf[100]; + SSL_METHOD *method = give_me_a_test_method(); + setup_capture_of_logs(LOG_WARN); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->state = TOR_TLS_ST_OPEN; + + ret = tor_tls_read(tls, buf, 10); + tt_int_op(ret, OP_EQ, -9); + + /* These tests assume that V2_HANDSHAKE_SERVER is set */ + tls->ssl->handshake_func = dummy_handshake_func; + tls->ssl->method = method; + method->ssl_read = fixed_ssl_read; + fixed_ssl_read_result_index = 0; + fixed_ssl_read_result[0] = 42; + tls->state = TOR_TLS_ST_OPEN; + ERR_clear_error(); + ret = tor_tls_read(tls, buf, 10); + tt_int_op(ret, OP_EQ, 42); + + tls->state = TOR_TLS_ST_OPEN; + tls->got_renegotiate = 1; + fixed_ssl_read_result_index = 0; + ERR_clear_error(); + ret = tor_tls_read(tls, buf, 10); + tt_int_op(tls->got_renegotiate, OP_EQ, 0); + + tls->state = TOR_TLS_ST_OPEN; + tls->got_renegotiate = 1; + negotiated_callback_called = 0; + tls->negotiated_callback = negotiated_callback_setter; + fixed_ssl_read_result_index = 0; + ERR_clear_error(); + ret = tor_tls_read(tls, buf, 10); + tt_int_op(negotiated_callback_called, OP_EQ, 1); + +#ifndef LIBRESSL_VERSION_NUMBER + fixed_ssl_read_result_index = 0; + fixed_ssl_read_result[0] = 0; + tls->ssl->version = SSL2_VERSION; + ERR_clear_error(); + ret = tor_tls_read(tls, buf, 10); + tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE); + tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED); +#endif /* !defined(LIBRESSL_VERSION_NUMBER) */ + // TODO: fill up + + done: + teardown_capture_of_logs(); + tor_free(tls->ssl); + tor_free(tls); + tor_free(method); +} + +static int fixed_ssl_write_result; + +static int +fixed_ssl_write(SSL *s, const void *buf, int len) +{ + (void)s; + (void)buf; + (void)len; + return fixed_ssl_write_result; +} + +static void +test_tortls_write(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + SSL_METHOD *method = give_me_a_test_method(); + char buf[100]; + setup_capture_of_logs(LOG_WARN); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = tor_malloc_zero(sizeof(SSL)); + tls->state = TOR_TLS_ST_OPEN; + + ret = tor_tls_write(tls, buf, 0); + tt_int_op(ret, OP_EQ, 0); + + ret = tor_tls_write(tls, buf, 10); + tt_int_op(ret, OP_EQ, -9); + + tls->ssl->method = method; + tls->wantwrite_n = 1; + ret = tor_tls_write(tls, buf, 10); + tt_int_op(tls->wantwrite_n, OP_EQ, 0); + + method->ssl_write = fixed_ssl_write; + tls->ssl->handshake_func = dummy_handshake_func; + fixed_ssl_write_result = 1; + ERR_clear_error(); + ret = tor_tls_write(tls, buf, 10); + tt_int_op(ret, OP_EQ, 1); + + fixed_ssl_write_result = -1; + ERR_clear_error(); + tls->ssl->rwstate = SSL_READING; + SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL); + SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ; + ret = tor_tls_write(tls, buf, 10); + tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD); + + ERR_clear_error(); + tls->ssl->rwstate = SSL_READING; + SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL); + SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE; + ret = tor_tls_write(tls, buf, 10); + tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE); + + done: + teardown_capture_of_logs(); + BIO_free(tls->ssl->rbio); + tor_free(tls->ssl); + tor_free(tls); + tor_free(method); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static int fixed_ssl_accept_result; +static int fixed_ssl_connect_result; + +static int +setting_error_ssl_accept(SSL *ssl) +{ + (void)ssl; + ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99); + ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99); + return fixed_ssl_accept_result; +} + +static int +setting_error_ssl_connect(SSL *ssl) +{ + (void)ssl; + ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99); + ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99); + return fixed_ssl_connect_result; +} + +static int +fixed_ssl_accept(SSL *ssl) +{ + (void) ssl; + return fixed_ssl_accept_result; +} + +static void +test_tortls_handshake(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + SSL_CTX *ctx; + SSL_METHOD *method = give_me_a_test_method(); + setup_capture_of_logs(LOG_INFO); + + SSL_library_init(); + SSL_load_error_strings(); + + ctx = SSL_CTX_new(TLSv1_method()); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = SSL_new(ctx); + tls->state = TOR_TLS_ST_HANDSHAKE; + + ret = tor_tls_handshake(tls); + tt_int_op(ret, OP_EQ, -9); + + tls->isServer = 1; + tls->state = TOR_TLS_ST_HANDSHAKE; + ret = tor_tls_handshake(tls); + tt_int_op(ret, OP_EQ, -9); + + tls->ssl->method = method; + method->ssl_accept = fixed_ssl_accept; + fixed_ssl_accept_result = 2; + ERR_clear_error(); + tls->state = TOR_TLS_ST_HANDSHAKE; + ret = tor_tls_handshake(tls); + tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_OPEN); + + method->ssl_accept = setting_error_ssl_accept; + fixed_ssl_accept_result = 1; + ERR_clear_error(); + mock_clean_saved_logs(); + tls->state = TOR_TLS_ST_HANDSHAKE; + ret = tor_tls_handshake(tls); + tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); + expect_log_entry(); + /* This fails on jessie. Investigate why! */ +#if 0 + expect_log_msg("TLS error while handshaking: (null) (in bignum routines:" + "(null):SSLv3 write client hello B)\n"); + expect_log_msg("TLS error while handshaking: (null) (in system library:" + "connect:SSLv3 write client hello B)\n"); +#endif /* 0 */ + expect_log_severity(LOG_INFO); + + tls->isServer = 0; + method->ssl_connect = setting_error_ssl_connect; + fixed_ssl_connect_result = 1; + ERR_clear_error(); + mock_clean_saved_logs(); + tls->state = TOR_TLS_ST_HANDSHAKE; + ret = tor_tls_handshake(tls); + tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); + expect_log_entry(); +#if 0 + /* See above */ + expect_log_msg("TLS error while handshaking: " + "(null) (in bignum routines:(null):SSLv3 write client hello B)\n"); + expect_log_msg("TLS error while handshaking: " + "(null) (in system library:connect:SSLv3 write client hello B)\n"); +#endif /* 0 */ + expect_log_severity(LOG_WARN); + + done: + teardown_capture_of_logs(); + SSL_free(tls->ssl); + SSL_CTX_free(ctx); + tor_free(tls); + tor_free(method); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +#ifndef OPENSSL_OPAQUE +static void +test_tortls_finish_handshake(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + SSL_CTX *ctx; + SSL_METHOD *method = give_me_a_test_method(); + SSL_library_init(); + SSL_load_error_strings(); + + X509 *c1 = read_cert_from(validCertString); + SESS_CERT_local *sess = NULL; + + ctx = SSL_CTX_new(method); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->ssl = SSL_new(ctx); + tls->state = TOR_TLS_ST_OPEN; + + ret = tor_tls_finish_handshake(tls); + tt_int_op(ret, OP_EQ, 0); + + tls->isServer = 1; + tls->wasV2Handshake = 0; + setup_full_capture_of_logs(LOG_WARN); + ret = tor_tls_finish_handshake(tls); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(tls->wasV2Handshake, OP_EQ, 1); + expect_single_log_msg_containing("For some reason, wasV2Handshake didn't " + "get set."); + teardown_capture_of_logs(); + + tls->wasV2Handshake = 1; + ret = tor_tls_finish_handshake(tls); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(tls->wasV2Handshake, OP_EQ, 1); + + tls->wasV2Handshake = 1; + tls->ssl->session = SSL_SESSION_new(); + ret = tor_tls_finish_handshake(tls); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(tls->wasV2Handshake, OP_EQ, 0); + + tls->isServer = 0; + + sess = tor_malloc_zero(sizeof(SESS_CERT_local)); + tls->ssl->session->sess_cert = (void *)sess; + sess->cert_chain = sk_X509_new_null(); + sk_X509_push(sess->cert_chain, c1); + tls->ssl->session->peer = c1; + tls->wasV2Handshake = 0; + ret = tor_tls_finish_handshake(tls); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(tls->wasV2Handshake, OP_EQ, 1); + + method->num_ciphers = fake_num_ciphers; + ret = tor_tls_finish_handshake(tls); + tt_int_op(ret, OP_EQ, -9); + + done: + if (sess) + sk_X509_free(sess->cert_chain); + if (tls->ssl && tls->ssl->session) { + tor_free(tls->ssl->session->sess_cert); + } + SSL_free(tls->ssl); + tor_free(tls); + SSL_CTX_free(ctx); + tor_free(method); + teardown_capture_of_logs(); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +static int fixed_crypto_pk_new_result_index; +static crypto_pk_t *fixed_crypto_pk_new_result[5]; + +static crypto_pk_t * +fixed_crypto_pk_new(void) +{ + return fixed_crypto_pk_new_result[fixed_crypto_pk_new_result_index++]; +} + +#ifndef OPENSSL_OPAQUE +static int fixed_crypto_pk_generate_key_with_bits_result_index; +static int fixed_crypto_pk_generate_key_with_bits_result[5]; +static int fixed_tor_tls_create_certificate_result_index; +static X509 *fixed_tor_tls_create_certificate_result[5]; +static int fixed_tor_x509_cert_new_result_index; +static tor_x509_cert_t *fixed_tor_x509_cert_new_result[5]; + +static int +fixed_crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits) +{ + (void)env; + (void)bits; + return fixed_crypto_pk_generate_key_with_bits_result[ + fixed_crypto_pk_generate_key_with_bits_result_index++]; +} + +static X509 * +fixed_tor_tls_create_certificate(crypto_pk_t *rsa, + crypto_pk_t *rsa_sign, + const char *cname, + const char *cname_sign, + unsigned int cert_lifetime) +{ + (void)rsa; + (void)rsa_sign; + (void)cname; + (void)cname_sign; + (void)cert_lifetime; + return fixed_tor_tls_create_certificate_result[ + fixed_tor_tls_create_certificate_result_index++]; +} + +static tor_x509_cert_t * +fixed_tor_x509_cert_new(tor_x509_cert_impl_t *x509_cert) +{ + (void) x509_cert; + return fixed_tor_x509_cert_new_result[ + fixed_tor_x509_cert_new_result_index++]; +} + +static void +test_tortls_context_new(void *ignored) +{ + (void)ignored; + tor_tls_context_t *ret; + crypto_pk_t *pk1, *pk2, *pk3, *pk4, *pk5, *pk6, *pk7, *pk8, *pk9, *pk10, + *pk11, *pk12, *pk13, *pk14, *pk15, *pk16, *pk17, *pk18; + + pk1 = crypto_pk_new(); + pk2 = crypto_pk_new(); + pk3 = crypto_pk_new(); + pk4 = crypto_pk_new(); + pk5 = crypto_pk_new(); + pk6 = crypto_pk_new(); + pk7 = crypto_pk_new(); + pk8 = crypto_pk_new(); + pk9 = crypto_pk_new(); + pk10 = crypto_pk_new(); + pk11 = crypto_pk_new(); + pk12 = crypto_pk_new(); + pk13 = crypto_pk_new(); + pk14 = crypto_pk_new(); + pk15 = crypto_pk_new(); + pk16 = crypto_pk_new(); + pk17 = crypto_pk_new(); + pk18 = crypto_pk_new(); + + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = NULL; + MOCK(crypto_pk_new, fixed_crypto_pk_new); + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + /* note: we already override this in testing_common.c, so we + * run this unit test in a subprocess. */ + MOCK(crypto_pk_generate_key_with_bits, + fixed_crypto_pk_generate_key_with_bits); + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = pk1; + fixed_crypto_pk_new_result[1] = NULL; + fixed_crypto_pk_generate_key_with_bits_result[0] = -1; + fixed_crypto_pk_generate_key_with_bits_result_index = 0; + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = pk2; + fixed_crypto_pk_new_result[1] = NULL; + fixed_crypto_pk_generate_key_with_bits_result[0] = 0; + fixed_crypto_pk_generate_key_with_bits_result_index = 0; + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = pk3; + fixed_crypto_pk_new_result[1] = pk4; + fixed_crypto_pk_new_result[2] = NULL; + fixed_crypto_pk_generate_key_with_bits_result[0] = 0; + fixed_crypto_pk_generate_key_with_bits_result[1] = -1; + fixed_crypto_pk_generate_key_with_bits_result_index = 0; + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + MOCK(tor_tls_create_certificate, fixed_tor_tls_create_certificate); + + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = pk5; + fixed_crypto_pk_new_result[1] = pk6; + fixed_crypto_pk_new_result[2] = NULL; + fixed_crypto_pk_generate_key_with_bits_result_index = 0; + fixed_crypto_pk_generate_key_with_bits_result[1] = 0; + fixed_tor_tls_create_certificate_result_index = 0; + fixed_tor_tls_create_certificate_result[0] = NULL; + fixed_tor_tls_create_certificate_result[1] = X509_new(); + fixed_tor_tls_create_certificate_result[2] = X509_new(); + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = pk7; + fixed_crypto_pk_new_result[1] = pk8; + fixed_crypto_pk_new_result[2] = NULL; + fixed_crypto_pk_generate_key_with_bits_result_index = 0; + fixed_tor_tls_create_certificate_result_index = 0; + fixed_tor_tls_create_certificate_result[0] = X509_new(); + fixed_tor_tls_create_certificate_result[1] = NULL; + fixed_tor_tls_create_certificate_result[2] = X509_new(); + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = pk9; + fixed_crypto_pk_new_result[1] = pk10; + fixed_crypto_pk_new_result[2] = NULL; + fixed_crypto_pk_generate_key_with_bits_result_index = 0; + fixed_tor_tls_create_certificate_result_index = 0; + fixed_tor_tls_create_certificate_result[0] = X509_new(); + fixed_tor_tls_create_certificate_result[1] = X509_new(); + fixed_tor_tls_create_certificate_result[2] = NULL; + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + MOCK(tor_x509_cert_new, fixed_tor_x509_cert_new); + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = pk11; + fixed_crypto_pk_new_result[1] = pk12; + fixed_crypto_pk_new_result[2] = NULL; + fixed_crypto_pk_generate_key_with_bits_result_index = 0; + fixed_tor_tls_create_certificate_result_index = 0; + fixed_tor_tls_create_certificate_result[0] = X509_new(); + fixed_tor_tls_create_certificate_result[1] = X509_new(); + fixed_tor_tls_create_certificate_result[2] = X509_new(); + fixed_tor_x509_cert_new_result_index = 0; + fixed_tor_x509_cert_new_result[0] = NULL; + fixed_tor_x509_cert_new_result[1] = NULL; + fixed_tor_x509_cert_new_result[2] = NULL; + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = pk13; + fixed_crypto_pk_new_result[1] = pk14; + fixed_crypto_pk_new_result[2] = NULL; + fixed_crypto_pk_generate_key_with_bits_result_index = 0; + fixed_tor_tls_create_certificate_result_index = 0; + fixed_tor_tls_create_certificate_result[0] = X509_new(); + fixed_tor_tls_create_certificate_result[1] = X509_new(); + fixed_tor_tls_create_certificate_result[2] = X509_new(); + fixed_tor_x509_cert_new_result_index = 0; + fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t)); + fixed_tor_x509_cert_new_result[1] = NULL; + fixed_tor_x509_cert_new_result[2] = NULL; + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = pk15; + fixed_crypto_pk_new_result[1] = pk16; + fixed_crypto_pk_new_result[2] = NULL; + fixed_crypto_pk_generate_key_with_bits_result_index = 0; + fixed_tor_tls_create_certificate_result_index = 0; + fixed_tor_tls_create_certificate_result[0] = X509_new(); + fixed_tor_tls_create_certificate_result[1] = X509_new(); + fixed_tor_tls_create_certificate_result[2] = X509_new(); + fixed_tor_x509_cert_new_result_index = 0; + fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t)); + fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t)); + fixed_tor_x509_cert_new_result[2] = NULL; + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = pk17; + fixed_crypto_pk_new_result[1] = pk18; + fixed_crypto_pk_new_result[2] = NULL; + fixed_crypto_pk_generate_key_with_bits_result_index = 0; + fixed_tor_tls_create_certificate_result_index = 0; + fixed_tor_tls_create_certificate_result[0] = X509_new(); + fixed_tor_tls_create_certificate_result[1] = X509_new(); + fixed_tor_tls_create_certificate_result[2] = X509_new(); + fixed_tor_x509_cert_new_result_index = 0; + fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t)); + fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t)); + fixed_tor_x509_cert_new_result[2] = tor_malloc_zero(sizeof(tor_x509_cert_t)); + ret = tor_tls_context_new(NULL, 0, 0, 0); + tt_assert(!ret); + + done: + UNMOCK(tor_x509_cert_new); + UNMOCK(tor_tls_create_certificate); + UNMOCK(crypto_pk_generate_key_with_bits); + UNMOCK(crypto_pk_new); +} +#endif /* !defined(OPENSSL_OPAQUE) */ + +static int fixed_crypto_pk_get_evp_pkey_result_index = 0; +static EVP_PKEY *fixed_crypto_pk_get_evp_pkey_result[5]; + +static EVP_PKEY * +fixed_crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private) +{ + (void) env; + (void) private; + return fixed_crypto_pk_get_evp_pkey_result[ + fixed_crypto_pk_get_evp_pkey_result_index++]; +} + +static void +test_tortls_create_certificate(void *ignored) +{ + (void)ignored; + X509 *ret; + crypto_pk_t *pk1, *pk2; + + pk1 = crypto_pk_new(); + pk2 = crypto_pk_new(); + + MOCK(crypto_pk_get_openssl_evp_pkey_, fixed_crypto_pk_get_evp_pkey_); + fixed_crypto_pk_get_evp_pkey_result_index = 0; + fixed_crypto_pk_get_evp_pkey_result[0] = NULL; + ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1); + tt_assert(!ret); + + fixed_crypto_pk_get_evp_pkey_result_index = 0; + fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new(); + fixed_crypto_pk_get_evp_pkey_result[1] = NULL; + ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1); + tt_assert(!ret); + + fixed_crypto_pk_get_evp_pkey_result_index = 0; + fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new(); + fixed_crypto_pk_get_evp_pkey_result[1] = EVP_PKEY_new(); + ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1); + tt_assert(!ret); + + done: + UNMOCK(crypto_pk_get_openssl_evp_pkey_); + crypto_pk_free(pk1); + crypto_pk_free(pk2); +} + +static void +test_tortls_cert_new(void *ignored) +{ + (void)ignored; + tor_x509_cert_t *ret; + X509 *cert = read_cert_from(validCertString); + + ret = tor_x509_cert_new(NULL); + tt_assert(!ret); + + ret = tor_x509_cert_new(cert); + tt_assert(ret); + tor_x509_cert_free(ret); + ret = NULL; + +#if 0 + cert = read_cert_from(validCertString); + /* XXX this doesn't do what you think: it alters a copy of the pubkey. */ + X509_get_pubkey(cert)->type = EVP_PKEY_DSA; + ret = tor_x509_cert_new(cert); + tt_assert(ret); +#endif /* 0 */ + +#ifndef OPENSSL_OPAQUE + cert = read_cert_from(validCertString); + X509_CINF_free(cert->cert_info); + cert->cert_info = NULL; + ret = tor_x509_cert_new(cert); + tt_assert(ret); +#endif /* !defined(OPENSSL_OPAQUE) */ + + done: + tor_x509_cert_free(ret); +} + +static void +test_tortls_cert_is_valid(void *ignored) +{ + (void)ignored; + int ret; + tor_x509_cert_t *cert = NULL, *scert = NULL; + + scert = tor_malloc_zero(sizeof(tor_x509_cert_t)); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); + tt_int_op(ret, OP_EQ, 0); + + cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); + tt_int_op(ret, OP_EQ, 0); + tor_free(scert); + tor_free(cert); + + cert = tor_x509_cert_new(read_cert_from(validCertString)); + scert = tor_x509_cert_new(read_cert_from(caCertString)); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); + tt_int_op(ret, OP_EQ, 1); + +#ifndef OPENSSL_OPAQUE + tor_x509_cert_free(cert); + tor_x509_cert_free(scert); + cert = tor_x509_cert_new(read_cert_from(validCertString)); + scert = tor_x509_cert_new(read_cert_from(caCertString)); + ASN1_TIME_free(cert->cert->cert_info->validity->notAfter); + cert->cert->cert_info->validity->notAfter = + ASN1_TIME_set(NULL, time(NULL)-1000000); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); + tt_int_op(ret, OP_EQ, 0); + + tor_x509_cert_free(cert); + tor_x509_cert_free(scert); + cert = tor_x509_cert_new(read_cert_from(validCertString)); + scert = tor_x509_cert_new(read_cert_from(caCertString)); + X509_PUBKEY_free(cert->cert->cert_info->key); + cert->cert->cert_info->key = NULL; + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); + tt_int_op(ret, OP_EQ, 0); +#endif /* !defined(OPENSSL_OPAQUE) */ + +#if 0 + tor_x509_cert_free(cert); + tor_x509_cert_free(scert); + cert = tor_x509_cert_new(read_cert_from(validCertString)); + scert = tor_x509_cert_new(read_cert_from(caCertString)); + /* This doesn't actually change the key in the cert. XXXXXX */ + BN_one(EVP_PKEY_get1_RSA(X509_get_pubkey(cert->cert))->n); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); + tt_int_op(ret, OP_EQ, 0); + + tor_x509_cert_free(cert); + tor_x509_cert_free(scert); + cert = tor_x509_cert_new(read_cert_from(validCertString)); + scert = tor_x509_cert_new(read_cert_from(caCertString)); + /* This doesn't actually change the key in the cert. XXXXXX */ + X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); + tt_int_op(ret, OP_EQ, 0); + + tor_x509_cert_free(cert); + tor_x509_cert_free(scert); + cert = tor_x509_cert_new(read_cert_from(validCertString)); + scert = tor_x509_cert_new(read_cert_from(caCertString)); + /* This doesn't actually change the key in the cert. XXXXXX */ + X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); + tt_int_op(ret, OP_EQ, 1); + + tor_x509_cert_free(cert); + tor_x509_cert_free(scert); + cert = tor_x509_cert_new(read_cert_from(validCertString)); + scert = tor_x509_cert_new(read_cert_from(caCertString)); + /* This doesn't actually change the key in the cert. XXXXXX */ + X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; + X509_get_pubkey(cert->cert)->ameth = NULL; + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); + tt_int_op(ret, OP_EQ, 0); +#endif /* 0 */ + + done: + tor_x509_cert_free(cert); + tor_x509_cert_free(scert); +} + +static void +test_tortls_context_init_one(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_context_t *old = NULL; + + MOCK(crypto_pk_new, fixed_crypto_pk_new); + + fixed_crypto_pk_new_result_index = 0; + fixed_crypto_pk_new_result[0] = NULL; + ret = tor_tls_context_init_one(&old, NULL, 0, 0, 0); + tt_int_op(ret, OP_EQ, -1); + + done: + UNMOCK(crypto_pk_new); +} + +#define LOCAL_TEST_CASE(name, flags) \ + { #name, test_tortls_##name, (flags|TT_FORK), NULL, NULL } + +#ifdef OPENSSL_OPAQUE +#define INTRUSIVE_TEST_CASE(name, flags) \ + { #name, NULL, TT_SKIP, NULL, NULL } +#else +#define INTRUSIVE_TEST_CASE(name, flags) LOCAL_TEST_CASE(name, flags) +#endif /* defined(OPENSSL_OPAQUE) */ + +struct testcase_t tortls_openssl_tests[] = { + LOCAL_TEST_CASE(tor_tls_new, TT_FORK), + LOCAL_TEST_CASE(get_state_description, TT_FORK), + LOCAL_TEST_CASE(get_by_ssl, TT_FORK), + LOCAL_TEST_CASE(allocate_tor_tls_object_ex_data_index, TT_FORK), + LOCAL_TEST_CASE(log_one_error, TT_FORK), + INTRUSIVE_TEST_CASE(get_error, TT_FORK), + LOCAL_TEST_CASE(always_accept_verify_cb, 0), + INTRUSIVE_TEST_CASE(x509_cert_free, 0), + INTRUSIVE_TEST_CASE(cert_matches_key, 0), + INTRUSIVE_TEST_CASE(cert_get_key, 0), + LOCAL_TEST_CASE(get_my_client_auth_key, TT_FORK), + INTRUSIVE_TEST_CASE(get_ciphersuite_name, 0), + INTRUSIVE_TEST_CASE(classify_client_ciphers, 0), + LOCAL_TEST_CASE(client_is_using_v2_ciphers, 0), + INTRUSIVE_TEST_CASE(verify, 0), + INTRUSIVE_TEST_CASE(check_lifetime, 0), + INTRUSIVE_TEST_CASE(get_pending_bytes, 0), + INTRUSIVE_TEST_CASE(SSL_SESSION_get_master_key, 0), + INTRUSIVE_TEST_CASE(get_tlssecrets, 0), + INTRUSIVE_TEST_CASE(get_buffer_sizes, 0), + INTRUSIVE_TEST_CASE(try_to_extract_certs_from_tls, 0), + INTRUSIVE_TEST_CASE(get_peer_cert, 0), + INTRUSIVE_TEST_CASE(peer_has_cert, 0), + INTRUSIVE_TEST_CASE(shutdown, 0), + INTRUSIVE_TEST_CASE(finish_handshake, 0), + INTRUSIVE_TEST_CASE(handshake, 0), + INTRUSIVE_TEST_CASE(write, 0), + INTRUSIVE_TEST_CASE(read, 0), + INTRUSIVE_TEST_CASE(server_info_callback, 0), + LOCAL_TEST_CASE(get_write_overhead_ratio, TT_FORK), + LOCAL_TEST_CASE(is_server, 0), + INTRUSIVE_TEST_CASE(assert_renegotiation_unblocked, 0), + INTRUSIVE_TEST_CASE(block_renegotiation, 0), + INTRUSIVE_TEST_CASE(unblock_renegotiation, 0), + INTRUSIVE_TEST_CASE(set_renegotiate_callback, 0), + LOCAL_TEST_CASE(set_logged_address, 0), + INTRUSIVE_TEST_CASE(find_cipher_by_id, 0), + INTRUSIVE_TEST_CASE(session_secret_cb, 0), + INTRUSIVE_TEST_CASE(debug_state_callback, 0), + INTRUSIVE_TEST_CASE(context_new, TT_FORK /* redundant */), + LOCAL_TEST_CASE(create_certificate, 0), + LOCAL_TEST_CASE(cert_new, 0), + LOCAL_TEST_CASE(cert_is_valid, 0), + LOCAL_TEST_CASE(context_init_one, 0), + END_OF_TESTCASES +}; From 6a88d8f6b413efdac4b0176cfb78431be46ca9e0 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 12 Aug 2018 17:18:41 -0400 Subject: [PATCH 46/76] When enabling NSS, disable OpenSSL. We used to link both libraries at once, but now that I'm working on TLS, there's nothing left to keep OpenSSL around for when NSS is enabled. Note that this patch causes a couple of places that still assumed OpenSSL to be disabled when NSS is enabled - tor-gencert - pbkdf2 --- configure.ac | 14 ++++++-- src/app/config/config.c | 12 +++++++ src/core/mainloop/main.c | 5 +-- src/lib/crypt_ops/compat_openssl.h | 6 ++++ src/lib/crypt_ops/crypto_dh.h | 2 +- src/lib/crypt_ops/crypto_ed25519.c | 1 + src/lib/crypt_ops/crypto_format.c | 1 + src/lib/crypt_ops/crypto_hkdf.c | 2 ++ src/lib/crypt_ops/crypto_init.c | 42 ++++++++++++++++++++++ src/lib/crypt_ops/crypto_init.h | 4 +++ src/lib/crypt_ops/crypto_rand.c | 14 +++++--- src/lib/crypt_ops/crypto_rsa.c | 6 ++-- src/lib/crypt_ops/crypto_s2k.c | 7 ++++ src/lib/crypt_ops/crypto_util.c | 6 ++-- src/lib/crypt_ops/include.am | 2 +- src/lib/tls/tortls_internal.h | 56 ++++++++++++++++-------------- src/lib/tls/tortls_nss.c | 42 ---------------------- src/test/bench.c | 8 +++++ src/test/include.am | 2 +- src/test/test.c | 2 ++ src/test/test_crypto.c | 4 +++ src/test/test_crypto_slow.c | 2 ++ src/tools/include.am | 14 ++++++-- src/tools/tor-gencert.c | 2 ++ 24 files changed, 170 insertions(+), 86 deletions(-) diff --git a/configure.ac b/configure.ac index aa9b2ba6bd..f99697a445 100644 --- a/configure.ac +++ b/configure.ac @@ -67,14 +67,15 @@ AM_CONDITIONAL(LIBFUZZER_ENABLED, test "x$enable_libfuzzer" = "xyes") AM_CONDITIONAL(OSS_FUZZ_ENABLED, test "x$enable_oss_fuzz" = "xyes") AM_CONDITIONAL(USE_RUST, test "x$enable_rust" = "xyes") AM_CONDITIONAL(USE_NSS, test "x$enable_nss" = "xyes") -AM_CONDITIONAL(USE_OPENSSL, true) +AM_CONDITIONAL(USE_OPENSSL, test "x$enable_nss" != "xyes") if test "x$enable_nss" = "xyes"; then AC_DEFINE(ENABLE_NSS, 1, [Defined if we're building with NSS in addition to OpenSSL.]) +else + AC_DEFINE(ENABLE_OPENSSL, 1, + [Defined if we're building with OpenSSL or LibreSSL]) fi -AC_DEFINE(ENABLE_OPENSSL, 1, - [Defined if we're building with OpenSSL or LibreSSL]) if test "$enable_static_tor" = "yes"; then enable_static_libevent="yes"; @@ -872,6 +873,8 @@ fi dnl ------------------------------------------------------ dnl Where do you live, openssl? And how do we call you? +if test "x$enable_nss" != "xyes"; then + tor_openssl_pkg_redhat="openssl" tor_openssl_pkg_debian="libssl-dev" tor_openssl_devpkg_redhat="openssl-devel" @@ -971,6 +974,11 @@ AC_CHECK_SIZEOF(SHA_CTX, , [AC_INCLUDES_DEFAULT() #include ]) +fi # enable_nss + +dnl ====================================================================== +dnl Can we use KIST? + dnl Define the set of checks for KIST scheduler support. AC_DEFUN([CHECK_KIST_SUPPORT],[ dnl KIST needs struct tcp_info and for certain members to exist. diff --git a/src/app/config/config.c b/src/app/config/config.c index d2ed295621..d7c9f6d610 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -82,6 +82,11 @@ #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" #include "lib/crypt_ops/crypto_init.h" +#ifdef ENABLE_NSS +#include "lib/crypt_ops/crypto_nss_mgt.h" +#else +#include "lib/crypt_ops/crypto_openssl_mgt.h" +#endif #include "feature/dircache/dirserv.h" #include "feature/relay/dns.h" #include "core/or/dos.h" @@ -5238,9 +5243,16 @@ options_init_from_torrc(int argc, char **argv) printf("Libevent\t\t%-15s\t\t%s\n", tor_libevent_get_header_version_str(), tor_libevent_get_version_str()); +#ifdef ENABLE_OPENSSL printf("OpenSSL \t\t%-15s\t\t%s\n", crypto_openssl_get_header_version_str(), crypto_openssl_get_version_str()); +#endif +#ifdef ENABLE_NSS + printf("NSS \t\t%-15s\t\t%s\n", + crypto_nss_get_header_version_str(), + crypto_nss_get_version_str()); +#endif if (tor_compress_supports_method(ZLIB_METHOD)) { printf("Zlib \t\t%-15s\t\t%s\n", tor_compress_version_str(ZLIB_METHOD), diff --git a/src/core/mainloop/main.c b/src/core/mainloop/main.c index ad8c1ead67..f40639d087 100644 --- a/src/core/mainloop/main.c +++ b/src/core/mainloop/main.c @@ -3504,10 +3504,11 @@ tor_init(int argc, char *argv[]) const char *version = get_version(); log_notice(LD_GENERAL, "Tor %s running on %s with Libevent %s, " - "OpenSSL %s, Zlib %s, Liblzma %s, and Libzstd %s.", version, + "%s %s, Zlib %s, Liblzma %s, and Libzstd %s.", version, get_uname(), tor_libevent_get_version_str(), - crypto_openssl_get_version_str(), + crypto_get_library_name(), + crypto_get_library_version_string(), tor_compress_supports_method(ZLIB_METHOD) ? tor_compress_version_str(ZLIB_METHOD) : "N/A", tor_compress_supports_method(LZMA_METHOD) ? diff --git a/src/lib/crypt_ops/compat_openssl.h b/src/lib/crypt_ops/compat_openssl.h index 317c01134a..f2f632ab40 100644 --- a/src/lib/crypt_ops/compat_openssl.h +++ b/src/lib/crypt_ops/compat_openssl.h @@ -7,6 +7,10 @@ #ifndef TOR_COMPAT_OPENSSL_H #define TOR_COMPAT_OPENSSL_H +#include "orconfig.h" + +#ifdef ENABLE_OPENSSL + #include #include "lib/crypt_ops/crypto_openssl_mgt.h" @@ -47,5 +51,7 @@ #define CONST_IF_OPENSSL_1_1_API const #endif /* !defined(OPENSSL_1_1_API) */ +#endif /* defined(ENABLE_OPENSSL) */ + #endif /* !defined(TOR_COMPAT_OPENSSL_H) */ diff --git a/src/lib/crypt_ops/crypto_dh.h b/src/lib/crypt_ops/crypto_dh.h index 6e79a6404c..3ee343a278 100644 --- a/src/lib/crypt_ops/crypto_dh.h +++ b/src/lib/crypt_ops/crypto_dh.h @@ -56,7 +56,7 @@ struct dh_st *crypto_dh_new_openssl_tls(void); void crypto_dh_init_openssl(void); void crypto_dh_free_all_openssl(void); #endif -#ifdef ENABLE_OPENSSL +#ifdef ENABLE_NSS void crypto_dh_init_nss(void); void crypto_dh_free_all_nss(void); #endif diff --git a/src/lib/crypt_ops/crypto_ed25519.c b/src/lib/crypt_ops/crypto_ed25519.c index 9d2c9e9fab..11c1f56aef 100644 --- a/src/lib/crypt_ops/crypto_ed25519.c +++ b/src/lib/crypt_ops/crypto_ed25519.c @@ -37,6 +37,7 @@ #include "ed25519/donna/ed25519_donna_tor.h" #include +#include static void pick_ed25519_impl(void); diff --git a/src/lib/crypt_ops/crypto_format.c b/src/lib/crypt_ops/crypto_format.c index 50916a8d68..09ec753a00 100644 --- a/src/lib/crypt_ops/crypto_format.c +++ b/src/lib/crypt_ops/crypto_format.c @@ -29,6 +29,7 @@ #include "lib/fs/files.h" #include +#include /** Write the datalen bytes from data to the file named * fname in the tagged-data format. This format contains a diff --git a/src/lib/crypt_ops/crypto_hkdf.c b/src/lib/crypt_ops/crypto_hkdf.c index 1873632a9d..a63d9131d9 100644 --- a/src/lib/crypt_ops/crypto_hkdf.c +++ b/src/lib/crypt_ops/crypto_hkdf.c @@ -17,12 +17,14 @@ #include "lib/intmath/cmp.h" #include "lib/log/util_bug.h" +#ifdef ENABLE_OPENSSL #include #if defined(HAVE_ERR_LOAD_KDF_STRINGS) #include #define HAVE_OPENSSL_HKDF 1 #endif +#endif #include diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c index 620fe8e1be..f9b077e9e7 100644 --- a/src/lib/crypt_ops/crypto_init.c +++ b/src/lib/crypt_ops/crypto_init.c @@ -88,6 +88,10 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) #ifdef ENABLE_OPENSSL if (crypto_openssl_late_init(useAccel, accelName, accelDir) < 0) return -1; +#else + (void)useAccel; + (void)accelName; + (void)accelDir; #endif #ifdef ENABLE_NSS if (crypto_nss_late_init() < 0) @@ -139,3 +143,41 @@ crypto_postfork(void) crypto_nss_postfork(); #endif } + +/** Return the name of the crypto library we're using. */ +const char * +crypto_get_library_name(void) +{ +#ifdef ENABLE_OPENSSL + return "OpenSSL"; +#endif +#ifdef ENABLE_NSS + return "NSS"; +#endif +} + +/** Return the version of the crypto library we are using, as given in the + * library. */ +const char * +crypto_get_library_version_string(void) +{ +#ifdef ENABLE_OPENSSL + return crypto_openssl_get_version_str(); +#endif +#ifdef ENABLE_NSS + return crypto_nss_get_version_str(); +#endif +} + +/** Return the version of the crypto library we're using, as given in the + * headers. */ +const char * +crypto_get_header_version_string(void) +{ +#ifdef ENABLE_OPENSSL + return crypto_openssl_get_header_version_str(); +#endif +#ifdef ENABLE_NSS + return crypto_nss_get_header_version_str(); +#endif +} diff --git a/src/lib/crypt_ops/crypto_init.h b/src/lib/crypt_ops/crypto_init.h index 3e32456b5c..05b281720c 100644 --- a/src/lib/crypt_ops/crypto_init.h +++ b/src/lib/crypt_ops/crypto_init.h @@ -26,4 +26,8 @@ void crypto_thread_cleanup(void); int crypto_global_cleanup(void); void crypto_postfork(void); +const char *crypto_get_library_name(void); +const char *crypto_get_library_version_string(void); +const char *crypto_get_header_version_string(void); + #endif /* !defined(TOR_CRYPTO_H) */ diff --git a/src/lib/crypt_ops/crypto_rand.c b/src/lib/crypt_ops/crypto_rand.c index 9806714747..78471bf398 100644 --- a/src/lib/crypt_ops/crypto_rand.c +++ b/src/lib/crypt_ops/crypto_rand.c @@ -35,9 +35,11 @@ #include "lib/testsupport/testsupport.h" #include "lib/fs/files.h" +#include "lib/defs/digest_sizes.h" +#include "lib/crypt_ops/crypto_digest.h" + #ifdef ENABLE_NSS #include "lib/crypt_ops/crypto_nss_mgt.h" -#include "lib/crypt_ops/crypto_digest.h" #endif #ifdef ENABLE_OPENSSL @@ -80,6 +82,7 @@ ENABLE_GCC_WARNING(redundant-decls) #endif #include +#include /** * How many bytes of entropy we add at once. @@ -335,7 +338,8 @@ crypto_strongest_rand_raw(uint8_t *out, size_t out_len) void crypto_strongest_rand(uint8_t *out, size_t out_len) { -#define DLEN SHA512_DIGEST_LENGTH +#define DLEN DIGEST512_LEN + /* We're going to hash DLEN bytes from the system RNG together with some * bytes from the PRNGs from our crypto librar(y/ies), in order to yield * DLEN bytes. @@ -360,11 +364,11 @@ crypto_strongest_rand(uint8_t *out, size_t out_len) // LCOV_EXCL_STOP } if (out_len >= DLEN) { - SHA512(inp, sizeof(inp), out); + crypto_digest512((char*)out, (char*)inp, sizeof(inp), DIGEST_SHA512); out += DLEN; out_len -= DLEN; } else { - SHA512(inp, sizeof(inp), tmp); + crypto_digest512((char*)tmp, (char*)inp, sizeof(inp), DIGEST_SHA512); memcpy(out, tmp, out_len); break; } @@ -699,6 +703,7 @@ smartlist_shuffle(smartlist_t *sl) int crypto_force_rand_ssleay(void) { +#ifdef ENABLE_OPENSSL RAND_METHOD *default_method; default_method = RAND_OpenSSL(); if (RAND_get_rand_method() != default_method) { @@ -708,6 +713,7 @@ crypto_force_rand_ssleay(void) RAND_set_rand_method(default_method); return 1; } +#endif return 0; } diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c index 0f80bc967f..31497e6509 100644 --- a/src/lib/crypt_ops/crypto_rsa.c +++ b/src/lib/crypt_ops/crypto_rsa.c @@ -37,11 +37,12 @@ crypto_get_rsa_padding_overhead(int padding) { switch (padding) { - case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD; + case PK_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD; default: tor_assert(0); return -1; // LCOV_EXCL_LINE } } +#ifdef ENABLE_OPENSSL /** Given a padding method padding, return the correct OpenSSL constant. */ int @@ -53,6 +54,7 @@ crypto_get_rsa_padding(int padding) default: tor_assert(0); return -1; // LCOV_EXCL_LINE } } +#endif /** Compare the public-key components of a and b. Return non-zero iff * a==b. A NULL key is considered to be distinct from all non-NULL @@ -100,7 +102,7 @@ crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env, tor_assert(to); tor_assert(fromlen < SIZE_T_CEILING); - overhead = crypto_get_rsa_padding_overhead(crypto_get_rsa_padding(padding)); + overhead = crypto_get_rsa_padding_overhead(padding); pkeylen = crypto_pk_keysize(env); if (!force && fromlen+overhead <= pkeylen) { diff --git a/src/lib/crypt_ops/crypto_s2k.c b/src/lib/crypt_ops/crypto_s2k.c index 0e151f0a6c..433fbb026d 100644 --- a/src/lib/crypt_ops/crypto_s2k.c +++ b/src/lib/crypt_ops/crypto_s2k.c @@ -21,7 +21,9 @@ #include "lib/ctime/di_ops.h" #include "lib/log/util_bug.h" +#ifdef ENABLE_OPENSSL #include +#endif #if defined(HAVE_LIBSCRYPT_H) && defined(HAVE_LIBSCRYPT_SCRYPT) #define HAVE_SCRYPT @@ -265,6 +267,7 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len, return (int)key_out_len; case S2K_TYPE_PBKDF2: { +#ifdef ENABLE_OPENSSL uint8_t log_iters; if (spec_len < 1 || secret_len > INT_MAX || spec_len > INT_MAX) return S2K_BAD_LEN; @@ -278,6 +281,10 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len, if (rv < 0) return S2K_FAILED; return (int)key_out_len; +#else + // XXXXXXXXXXXXXXXXXXXXXXXX implement me. + return S2K_NO_SCRYPT_SUPPORT; +#endif } case S2K_TYPE_SCRYPT: { diff --git a/src/lib/crypt_ops/crypto_util.c b/src/lib/crypt_ops/crypto_util.c index a645321bfb..7af80291ef 100644 --- a/src/lib/crypt_ops/crypto_util.c +++ b/src/lib/crypt_ops/crypto_util.c @@ -23,12 +23,14 @@ #include #endif /* defined(_WIN32) */ -DISABLE_GCC_WARNING(redundant-decls) +#include +#ifdef ENABLE_OPENSSL +DISABLE_GCC_WARNING(redundant-decls) #include #include - ENABLE_GCC_WARNING(redundant-decls) +#endif #include "lib/log/log.h" #include "lib/log/util_bug.h" diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 195dac6bdb..1022096fdc 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -9,7 +9,6 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/crypto_cipher.c \ src/lib/crypt_ops/crypto_curve25519.c \ src/lib/crypt_ops/crypto_dh.c \ - src/lib/crypt_ops/crypto_dh_openssl.c \ src/lib/crypt_ops/crypto_digest.c \ src/lib/crypt_ops/crypto_ed25519.c \ src/lib/crypt_ops/crypto_format.c \ @@ -37,6 +36,7 @@ endif if USE_OPENSSL src_lib_libtor_crypt_ops_a_SOURCES += \ + src/lib/crypt_ops/crypto_dh_openssl.c \ src/lib/crypt_ops/crypto_openssl_mgt.c endif diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h index f6afb348ca..c58379e19b 100644 --- a/src/lib/tls/tortls_internal.h +++ b/src/lib/tls/tortls_internal.h @@ -15,36 +15,11 @@ struct ssl_session_st; int tor_errno_to_tls_error(int e); int tor_tls_get_error(tor_tls_t *tls, int r, int extra, const char *doing, int severity, int domain); -tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl); -void tor_tls_allocate_tor_tls_object_ex_data_index(void); MOCK_DECL(void, try_to_extract_certs_from_tls, (int severity, tor_tls_t *tls, tor_x509_cert_impl_t **cert_out, tor_x509_cert_impl_t **id_cert_out)); -#ifdef TORTLS_OPENSSL_PRIVATE -int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx); -int tor_tls_classify_client_ciphers(const struct ssl_st *ssl, - STACK_OF(SSL_CIPHER) *peer_ciphers); -#endif -int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl); -#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY -size_t SSL_SESSION_get_master_key(struct ssl_session_st *s, - uint8_t *out, - size_t len); -#endif -void tor_tls_debug_state_callback(const struct ssl_st *ssl, - int type, int val); -void tor_tls_server_info_callback(const struct ssl_st *ssl, - int type, int val); -#ifdef TORTLS_OPENSSL_PRIVATE -STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret, - int *secret_len, - STACK_OF(SSL_CIPHER) *peer_ciphers, - CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher, - void *arg); -STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, - uint16_t cipher); -#endif /* defined(TORTLS_OPENSSL_PRIVATE) */ + tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, unsigned flags, int is_client); int tor_tls_context_init_one(tor_tls_context_t **ppcontext, @@ -53,6 +28,35 @@ int tor_tls_context_init_one(tor_tls_context_t **ppcontext, unsigned int flags, int is_client); +#ifdef ENABLE_OPENSSL +tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl); +int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl); +void tor_tls_debug_state_callback(const struct ssl_st *ssl, + int type, int val); +void tor_tls_server_info_callback(const struct ssl_st *ssl, + int type, int val); +void tor_tls_allocate_tor_tls_object_ex_data_index(void); + +#if !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) +size_t SSL_SESSION_get_master_key(struct ssl_session_st *s, + uint8_t *out, + size_t len); +#endif + +#ifdef TORTLS_OPENSSL_PRIVATE +int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx); +int tor_tls_classify_client_ciphers(const struct ssl_st *ssl, + STACK_OF(SSL_CIPHER) *peer_ciphers); +STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret, + int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher, + void *arg); +STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, + uint16_t cipher); +#endif +#endif + #ifdef TOR_UNIT_TESTS extern int tor_tls_object_ex_data_index; extern tor_tls_context_t *server_tls_context; diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index 078196ac5f..98fecdaf16 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -47,19 +47,6 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra, // XXXX return -1; } -tor_tls_t * -tor_tls_get_by_ssl(const struct ssl_st *ssl) -{ - (void) ssl; - // XXXX - // XXXX refers to ssl_st. - return NULL; -} -void -tor_tls_allocate_tor_tls_object_ex_data_index(void) -{ - // XXXX openssl only. -} MOCK_IMPL(void, try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, tor_x509_cert_impl_t **cert_out, @@ -71,36 +58,7 @@ try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, (void)severity; // XXXX } -int -tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl) -{ - (void) ssl; - // XXXX - // XXXX refers to ssl_st. - return 0; -} -void -tor_tls_debug_state_callback(const struct ssl_st *ssl, - int type, int val) -{ - (void) ssl; - (void)type; - (void)val; - // XXXX - // XXXX refers to ssl_st. -} - -void -tor_tls_server_info_callback(const struct ssl_st *ssl, - int type, int val) -{ - (void)ssl; - (void)type; - (void)val; - // XXXX - // XXXX refers to ssl_st. -} tor_tls_context_t * tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, unsigned flags, int is_client) diff --git a/src/test/bench.c b/src/test/bench.c index 2b90ccf734..3594059057 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -13,11 +13,14 @@ #include "core/or/or.h" #include "core/crypto/onion_tap.h" #include "core/crypto/relay_crypto.h" + +#ifdef ENABLE_OPENSSL #include #include #include #include #include +#endif #include "core/or/circuitlist.h" #include "app/config/config.h" @@ -580,6 +583,7 @@ bench_dh(void) " %f millisec each.\n", NANOCOUNT(start, end, iters)/1e6); } +#ifdef ENABLE_OPENSSL static void bench_ecdh_impl(int nid, const char *name) { @@ -629,6 +633,7 @@ bench_ecdh_p224(void) { bench_ecdh_impl(NID_secp224r1, "P-224"); } +#endif typedef void (*bench_fn)(void); @@ -652,8 +657,11 @@ static struct benchmark_t benchmarks[] = { ENT(cell_aes), ENT(cell_ops), ENT(dh), + +#ifdef ENABLE_OPENSSL ENT(ecdh_p256), ENT(ecdh_p224), +#endif {NULL,NULL,0} }; diff --git a/src/test/include.am b/src/test/include.am index c2e08aa3df..05149b8654 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -118,7 +118,6 @@ src_test_test_SOURCES += \ src/test/test_controller_events.c \ src/test/test_crypto.c \ src/test/test_crypto_ope.c \ - src/test/test_crypto_openssl.c \ src/test/test_data.c \ src/test/test_dir.c \ src/test/test_dir_common.c \ @@ -189,6 +188,7 @@ if USE_NSS # ... else src_test_test_SOURCES += \ + src/test/test_crypto_openssl.c \ src/test/test_tortls_openssl.c endif diff --git a/src/test/test.c b/src/test/test.c index 3b63f1c07e..9623443057 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -866,7 +866,9 @@ struct testgroup_t testgroups[] = { { "control/event/", controller_event_tests }, { "crypto/", crypto_tests }, { "crypto/ope/", crypto_ope_tests }, +#ifdef ENABLE_OPENSSL { "crypto/openssl/", crypto_openssl_tests }, +#endif { "crypto/pem/", pem_tests }, { "dir/", dir_tests }, { "dir_handle_get/", dir_handle_get_tests }, diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 90fb8d468b..04077b42fb 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -224,6 +224,9 @@ static void test_crypto_openssl_version(void *arg) { (void)arg; +#ifdef ENABLE_NSS + tt_skip(); +#else const char *version = crypto_openssl_get_version_str(); const char *h_version = crypto_openssl_get_header_version_str(); tt_assert(version); @@ -243,6 +246,7 @@ test_crypto_openssl_version(void *arg) tt_int_op(a, OP_GE, 0); tt_int_op(b, OP_GE, 0); tt_int_op(c, OP_GE, 0); +#endif done: ; diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c index 88b31ad9af..ca6b7b8d4d 100644 --- a/src/test/test_crypto_slow.c +++ b/src/test/test_crypto_slow.c @@ -18,7 +18,9 @@ #include #endif +#ifdef ENABLE_OPENSSL #include +#endif /** Run unit tests for our secret-to-key passphrase hashing functionality. */ static void diff --git a/src/tools/include.am b/src/tools/include.am index cdd5616fb1..73ec86935f 100644 --- a/src/tools/include.am +++ b/src/tools/include.am @@ -1,7 +1,7 @@ -bin_PROGRAMS+= src/tools/tor-resolve src/tools/tor-gencert src/tools/tor-print-ed-signing-cert +bin_PROGRAMS+= src/tools/tor-resolve src/tools/tor-print-ed-signing-cert if COVERAGE_ENABLED -noinst_PROGRAMS+= src/tools/tor-cov-resolve src/tools/tor-cov-gencert +noinst_PROGRAMS+= src/tools/tor-cov-resolve endif src_tools_tor_resolve_SOURCES = src/tools/tor-resolve.c @@ -20,6 +20,10 @@ src_tools_tor_cov_resolve_LDADD = \ @TOR_LIB_MATH@ @TOR_LIB_WS32@ endif +if USE_NSS +# ... +else +bin_PROGRAMS += src/tools/tor-gencert src_tools_tor_gencert_SOURCES = src/tools/tor-gencert.c src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) src_tools_tor_gencert_LDADD = \ @@ -28,6 +32,7 @@ src_tools_tor_gencert_LDADD = \ $(rust_ldadd) \ @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ +endif src_tools_tor_print_ed_signing_cert_SOURCES = src/tools/tor-print-ed-signing-cert.c src_tools_tor_print_ed_signing_cert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @@ -38,7 +43,11 @@ src_tools_tor_print_ed_signing_cert_LDADD = \ @TOR_LIB_MATH@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_USERENV@ +if USE_NSS +# ... +else if COVERAGE_ENABLED +noinst_PROGRAMS += src/tools/tor-cov-gencert src_tools_tor_cov_gencert_SOURCES = src/tools/tor-gencert.c src_tools_tor_cov_gencert_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_tools_tor_cov_gencert_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) @@ -49,6 +58,7 @@ src_tools_tor_cov_gencert_LDADD = \ @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ endif +endif if BUILD_LIBTORRUNNER noinst_LIBRARIES += src/tools/libtorrunner.a diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index e0ac3dec80..a498c205b7 100644 --- a/src/tools/tor-gencert.c +++ b/src/tools/tor-gencert.c @@ -17,6 +17,7 @@ #include "lib/crypt_ops/crypto_init.h" #include "lib/crypt_ops/crypto_openssl_mgt.h" +#ifdef ENABLE_OPENSSL /* Some versions of OpenSSL declare X509_STORE_CTX_set_verify_cb twice in * x509.h and x509_vfy.h. Suppress the GCC warning so we can build with * -Wredundant-decl. */ @@ -30,6 +31,7 @@ DISABLE_GCC_WARNING(redundant-decls) #include ENABLE_GCC_WARNING(redundant-decls) +#endif #include From 96f8e1980204e83bb943fbff31e308a03b41160c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 12 Aug 2018 17:46:53 -0400 Subject: [PATCH 47/76] Implement PBKDF2 with NSS. This was a gap that we left in the last commit. --- src/lib/crypt_ops/crypto_s2k.c | 47 +++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/lib/crypt_ops/crypto_s2k.c b/src/lib/crypt_ops/crypto_s2k.c index 433fbb026d..e0b2f40bb3 100644 --- a/src/lib/crypt_ops/crypto_s2k.c +++ b/src/lib/crypt_ops/crypto_s2k.c @@ -20,10 +20,14 @@ #include "lib/crypt_ops/crypto_util.h" #include "lib/ctime/di_ops.h" #include "lib/log/util_bug.h" +#include "lib/intmath/cmp.h" #ifdef ENABLE_OPENSSL #include #endif +#ifdef ENABLE_NSS +#include +#endif #if defined(HAVE_LIBSCRYPT_H) && defined(HAVE_LIBSCRYPT_SCRYPT) #define HAVE_SCRYPT @@ -267,13 +271,13 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len, return (int)key_out_len; case S2K_TYPE_PBKDF2: { -#ifdef ENABLE_OPENSSL uint8_t log_iters; if (spec_len < 1 || secret_len > INT_MAX || spec_len > INT_MAX) return S2K_BAD_LEN; log_iters = spec[spec_len-1]; if (log_iters > 31) return S2K_BAD_PARAMS; +#ifdef ENABLE_OPENSSL rv = PKCS5_PBKDF2_HMAC_SHA1(secret, (int)secret_len, spec, (int)spec_len-1, (1<len, (int)key_out_len); + memcpy(key_out, iptr->data, rv); + + nss_pbkdf_err: + if (key) + PK11_FreeSymKey(key); + if (alg) + SECOID_DestroyAlgorithmID(alg, PR_TRUE); + return rv; #endif } From 108d9879eb814bc06095a1819d98b1c7c9a38e88 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 12 Aug 2018 17:54:06 -0400 Subject: [PATCH 48/76] Extract the non-generic part of tor_tls_context_decref(). --- src/lib/tls/tortls.c | 19 +++++++++++++++++++ src/lib/tls/tortls_internal.h | 7 +++++++ src/lib/tls/tortls_nss.c | 7 +++++++ src/lib/tls/tortls_openssl.c | 28 +++++++++------------------- 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 4b35177df9..395f0148e2 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -35,6 +35,25 @@ tor_tls_context_incref(tor_tls_context_t *ctx) ++ctx->refcnt; } +/** Remove a reference to ctx, and free it if it has no more + * references. */ +void +tor_tls_context_decref(tor_tls_context_t *ctx) +{ + tor_assert(ctx); + if (--ctx->refcnt == 0) { + tor_tls_context_impl_free(ctx->ctx); + tor_x509_cert_free(ctx->my_link_cert); + tor_x509_cert_free(ctx->my_id_cert); + tor_x509_cert_free(ctx->my_auth_cert); + crypto_pk_free(ctx->link_key); + crypto_pk_free(ctx->auth_key); + /* LCOV_EXCL_BR_START since ctx will never be NULL here */ + tor_free(ctx); + /* LCOV_EXCL_BR_STOP */ + } +} + /** Free all global TLS structures. */ void tor_tls_free_all(void) diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h index c58379e19b..b997ee3e4a 100644 --- a/src/lib/tls/tortls_internal.h +++ b/src/lib/tls/tortls_internal.h @@ -28,6 +28,13 @@ int tor_tls_context_init_one(tor_tls_context_t **ppcontext, unsigned int flags, int is_client); +#ifdef ENABLE_OPENSSL +void tor_tls_context_impl_free(struct ssl_ctx_st *); +#else +struct ssl_ctx_st; // XXXX replace +void tor_tls_context_impl_free(struct ssl_ctx_st *); +#endif + #ifdef ENABLE_OPENSSL tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl); int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl); diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index 98fecdaf16..3ab5c753d0 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -85,6 +85,13 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext, // XXXX return -1; } +void +tor_tls_context_impl_free(struct ssl_ctx_st *ctx) +{ + (void)ctx; + // XXXX + // XXXX openssl type. +} void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz) diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index 333b86481e..5f54312355 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -30,6 +30,7 @@ #include "lib/crypt_ops/crypto_util.h" #include "lib/crypt_ops/compat_openssl.h" #include "lib/tls/x509.h" +#include "lib/tls/x509_internal.h" /* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ @@ -488,25 +489,6 @@ static const char CLIENT_CIPHER_LIST[] = #undef CIPHER #undef XCIPHER -/** Remove a reference to ctx, and free it if it has no more - * references. */ -void -tor_tls_context_decref(tor_tls_context_t *ctx) -{ - tor_assert(ctx); - if (--ctx->refcnt == 0) { - SSL_CTX_free(ctx->ctx); - tor_x509_cert_free(ctx->my_link_cert); - tor_x509_cert_free(ctx->my_id_cert); - tor_x509_cert_free(ctx->my_auth_cert); - crypto_pk_free(ctx->link_key); - crypto_pk_free(ctx->auth_key); - /* LCOV_EXCL_BR_START since ctx will never be NULL here */ - tor_free(ctx); - /* LCOV_EXCL_BR_STOP */ - } -} - /** Set *link_cert_out and *id_cert_out to the link certificate * and ID certificate that we're currently using for our V3 in-protocol * handshake's certificate chain. If server is true, provide the certs @@ -599,6 +581,14 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext, return ((new_ctx != NULL) ? 0 : -1); } +void +tor_tls_context_impl_free(struct ssl_ctx_st *ctx) +{ + if (!ctx) + return; + SSL_CTX_free(ctx); +} + /** The group we should use for ecdhe when none was selected. */ #define NID_tor_default_ecdhe_group NID_X9_62_prime256v1 From b9ca8f2356a98630a262951486cd10436963e169 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 12 Aug 2018 18:01:14 -0400 Subject: [PATCH 49/76] Extract internal-only parts of x509.h --- src/lib/tls/include.am | 3 ++- src/lib/tls/x509.c | 1 + src/lib/tls/x509.h | 11 ----------- src/lib/tls/x509_internal.h | 28 ++++++++++++++++++++++++++++ src/lib/tls/x509_nss.c | 1 + src/lib/tls/x509_openssl.c | 1 + src/test/test_tortls.c | 1 + src/test/test_tortls_openssl.c | 1 + 8 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 src/lib/tls/x509_internal.h diff --git a/src/lib/tls/include.am b/src/lib/tls/include.am index 1731049032..b25e2e16bf 100644 --- a/src/lib/tls/include.am +++ b/src/lib/tls/include.am @@ -34,4 +34,5 @@ noinst_HEADERS += \ src/lib/tls/tortls.h \ src/lib/tls/tortls_internal.h \ src/lib/tls/tortls_st.h \ - src/lib/tls/x509.h + src/lib/tls/x509.h \ + src/lib/tls/x509_internal.h diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index dbf1dd927c..fc6139aceb 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -11,6 +11,7 @@ #define TOR_X509_PRIVATE #include "lib/tls/x509.h" +#include "lib/tls/x509_internal.h" #include "lib/log/util_bug.h" #include "lib/crypt_ops/crypto_rand.h" diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h index f75d15d7e9..ccaa92184e 100644 --- a/src/lib/tls/x509.h +++ b/src/lib/tls/x509.h @@ -40,15 +40,6 @@ void tor_tls_pick_certificate_lifetime(time_t now, time_t *start_time_out, time_t *end_time_out); -MOCK_DECL(tor_x509_cert_impl_t *, tor_tls_create_certificate, - (crypto_pk_t *rsa, - crypto_pk_t *rsa_sign, - const char *cname, - const char *cname_sign, - unsigned int cert_lifetime)); -MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new, - (tor_x509_cert_impl_t *x509_cert)); - #ifdef TOR_UNIT_TESTS tor_x509_cert_t *tor_x509_cert_replace_expiration( const tor_x509_cert_t *inp, @@ -63,8 +54,6 @@ void tor_x509_cert_free_(tor_x509_cert_t *cert); FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c)) tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len); -const tor_x509_cert_impl_t *tor_x509_cert_get_impl( - const tor_x509_cert_t *cert); void tor_x509_cert_get_der(const tor_x509_cert_t *cert, const uint8_t **encoded_out, size_t *size_out); diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h new file mode 100644 index 0000000000..2cca393d21 --- /dev/null +++ b/src/lib/tls/x509_internal.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_X509_INTERNAL_H +#define TOR_X509_INTERNAL_H + +/** + * \file x509.h + * \brief Internal headers for tortls.c + **/ + +#include "lib/crypt_ops/crypto_rsa.h" +#include "lib/testsupport/testsupport.h" + +MOCK_DECL(tor_x509_cert_impl_t *, tor_tls_create_certificate, + (crypto_pk_t *rsa, + crypto_pk_t *rsa_sign, + const char *cname, + const char *cname_sign, + unsigned int cert_lifetime)); +MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new, + (tor_x509_cert_impl_t *x509_cert)); +const tor_x509_cert_impl_t *tor_x509_cert_get_impl( + const tor_x509_cert_t *cert); + +#endif diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c index e856c95187..e0087eae6c 100644 --- a/src/lib/tls/x509_nss.c +++ b/src/lib/tls/x509_nss.c @@ -11,6 +11,7 @@ #define TOR_X509_PRIVATE #include "lib/tls/x509.h" +#include "lib/tls/x509_internal.h" #include "lib/tls/tortls.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c index c003d4704b..43d33d781f 100644 --- a/src/lib/tls/x509_openssl.c +++ b/src/lib/tls/x509_openssl.c @@ -11,6 +11,7 @@ #define TOR_X509_PRIVATE #include "lib/tls/x509.h" +#include "lib/tls/x509_internal.h" #include "lib/tls/tortls.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index d20bc5fa6c..eedf0dd3c9 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -19,6 +19,7 @@ #include "app/config/config.h" #include "lib/crypt_ops/compat_openssl.h" #include "lib/tls/x509.h" +#include "lib/tls/x509_internal.h" #include "lib/tls/tortls.h" #include "lib/tls/tortls_st.h" #include "lib/tls/tortls_internal.h" diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c index a9336a6f1b..12a05b3033 100644 --- a/src/test/test_tortls_openssl.c +++ b/src/test/test_tortls_openssl.c @@ -36,6 +36,7 @@ ENABLE_GCC_WARNING(redundant-decls) #include "app/config/config.h" #include "lib/crypt_ops/compat_openssl.h" #include "lib/tls/x509.h" +#include "lib/tls/x509_internal.h" #include "lib/tls/tortls.h" #include "lib/tls/tortls_st.h" #include "lib/tls/tortls_internal.h" From 5245a296c58eb8aba712e94a78d5bcaa2a2f25fb Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 12 Aug 2018 19:40:47 -0400 Subject: [PATCH 50/76] Make some x509 functions generic; remove some fields NSS doesn't need --- src/lib/tls/x509.c | 79 ++++++++++++++++++++++++++------ src/lib/tls/x509.h | 2 + src/lib/tls/x509_internal.h | 7 +++ src/lib/tls/x509_nss.c | 35 ++++++++++----- src/lib/tls/x509_openssl.c | 82 ++++++++++------------------------ src/test/test_link_handshake.c | 8 ++-- 6 files changed, 127 insertions(+), 86 deletions(-) diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index fc6139aceb..d2270f9108 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -14,6 +14,7 @@ #include "lib/tls/x509_internal.h" #include "lib/log/util_bug.h" #include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_util.h" /** Choose the start and end times for a certificate */ void @@ -51,19 +52,6 @@ tor_tls_pick_certificate_lifetime(time_t now, *end_time_out = end_time; } -/** Set *encoded_out and *size_out to cert's encoded DER - * representation and length, respectively. */ -void -tor_x509_cert_get_der(const tor_x509_cert_t *cert, - const uint8_t **encoded_out, size_t *size_out) -{ - tor_assert(cert); - tor_assert(encoded_out); - tor_assert(size_out); - *encoded_out = cert->encoded; - *size_out = cert->encoded_len; -} - /** Return the underlying implementation for cert */ const tor_x509_cert_impl_t * tor_x509_cert_get_impl(const tor_x509_cert_t *cert) @@ -90,3 +78,68 @@ tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert) return &cert->cert_digests; } +/** Free all storage held in cert */ +void +tor_x509_cert_free_(tor_x509_cert_t *cert) +{ + if (! cert) + return; + if (cert->cert) + tor_x509_cert_impl_free_(cert->cert); +#ifdef ENABLE_OPENSSL + tor_free(cert->encoded); +#endif + memwipe(cert, 0x03, sizeof(*cert)); + /* LCOV_EXCL_BR_START since cert will never be NULL here */ + tor_free(cert); + /* LCOV_EXCL_BR_STOP */ +} + +/** + * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert". + * + * Steals a reference to x509_cert. + */ +MOCK_IMPL(tor_x509_cert_t *, +tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert)) +{ + tor_x509_cert_t *cert; + + if (!x509_cert) + return NULL; + + cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); + cert->cert = x509_cert; + + if (tor_x509_cert_set_cached_der_encoding(cert) < 0) + goto err; + + { + const uint8_t *encoded=NULL; + size_t encoded_len=0; + tor_x509_cert_get_der(cert, &encoded, &encoded_len); + tor_assert(encoded); + crypto_common_digests(&cert->cert_digests, (char *)encoded, encoded_len); + } + + { + crypto_pk_t *pk = tor_tls_cert_get_key(cert); + if (pk) { + if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) { + crypto_pk_free(pk); + goto err; + } + } + cert->pkey_digests_set = 1; + crypto_pk_free(pk); + } + + return cert; + err: + /* LCOV_EXCL_START for the same reason as the exclusion above */ + tor_free(cert); + log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate."); + tor_x509_cert_impl_free_(x509_cert); + return NULL; + /* LCOV_EXCL_STOP */ +} diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h index ccaa92184e..8316df75a3 100644 --- a/src/lib/tls/x509.h +++ b/src/lib/tls/x509.h @@ -27,8 +27,10 @@ typedef struct x509_st tor_x509_cert_impl_t; /** Structure that we use for a single certificate. */ struct tor_x509_cert_t { tor_x509_cert_impl_t *cert; +#ifdef ENABLE_OPENSSL uint8_t *encoded; size_t encoded_len; +#endif unsigned pkey_digests_set : 1; common_digests_t cert_digests; common_digests_t pkey_digests; diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h index 2cca393d21..86f5a0de59 100644 --- a/src/lib/tls/x509_internal.h +++ b/src/lib/tls/x509_internal.h @@ -25,4 +25,11 @@ MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new, const tor_x509_cert_impl_t *tor_x509_cert_get_impl( const tor_x509_cert_t *cert); +void tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert); +#ifdef ENABLE_OPENSSL +int tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert); +#else +#define tor_x509_cert_set_cached_der_encoding(cert) (0) +#endif + #endif diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c index e0087eae6c..ac9e6658d8 100644 --- a/src/lib/tls/x509_nss.c +++ b/src/lib/tls/x509_nss.c @@ -17,6 +17,9 @@ #include "lib/crypt_ops/crypto_util.h" #include "lib/log/util_bug.h" +#include +#include + MOCK_IMPL(tor_x509_cert_impl_t *, tor_tls_create_certificate,(crypto_pk_t *rsa, crypto_pk_t *rsa_sign, @@ -33,12 +36,27 @@ tor_tls_create_certificate,(crypto_pk_t *rsa, return NULL; } -MOCK_IMPL(tor_x509_cert_t *, -tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert)) +/** Set *encoded_out and *size_out to cert's encoded DER + * representation and length, respectively. */ +void +tor_x509_cert_get_der(const tor_x509_cert_t *cert, + const uint8_t **encoded_out, size_t *size_out) { - tor_assert(x509_cert); - // XXXX - return NULL; + tor_assert(cert); + tor_assert(cert->cert); + tor_assert(encoded_out); + tor_assert(size_out); + + const SECItem *item = &cert->cert->derCert; + *encoded_out = item->data; + *size_out = (size_t)item->len; +} + +void +tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert) +{ + if (cert) + CERT_DestroyCertificate(cert); } tor_x509_cert_t * @@ -49,13 +67,6 @@ tor_x509_cert_dup(const tor_x509_cert_t *cert) return NULL; } -void -tor_x509_cert_free_(tor_x509_cert_t *cert) -{ - (void)cert; - // XXXX -} - tor_x509_cert_t * tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len) diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c index 43d33d781f..43bd6b4d48 100644 --- a/src/lib/tls/x509_openssl.c +++ b/src/lib/tls/x509_openssl.c @@ -182,75 +182,41 @@ tor_tls_create_certificate,(crypto_pk_t *rsa, #undef SERIAL_NUMBER_SIZE } -/** Free all storage held in cert */ -void -tor_x509_cert_free_(tor_x509_cert_t *cert) +/** Set the 'encoded' and 'encoded_len' fields of "cert" from cert->cert. */ +int +tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert) { - if (! cert) - return; - if (cert->cert) - X509_free(cert->cert); - tor_free(cert->encoded); - memwipe(cert, 0x03, sizeof(*cert)); - /* LCOV_EXCL_BR_START since cert will never be NULL here */ - tor_free(cert); - /* LCOV_EXCL_BR_STOP */ -} - -/** - * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert". - * - * Steals a reference to x509_cert. - */ -MOCK_IMPL(tor_x509_cert_t *, -tor_x509_cert_new,(X509 *x509_cert)) -{ - tor_x509_cert_t *cert; - EVP_PKEY *pkey; - RSA *rsa; - int length; unsigned char *buf = NULL; + int length = i2d_X509(cert->cert, &buf); - if (!x509_cert) - return NULL; - - length = i2d_X509(x509_cert, &buf); - cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); if (length <= 0 || buf == NULL) { - goto err; + return -1; } cert->encoded_len = (size_t) length; cert->encoded = tor_malloc(length); memcpy(cert->encoded, buf, length); OPENSSL_free(buf); + return 0; +} - cert->cert = x509_cert; +void +tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert) +{ + if (cert) + X509_free(cert); +} - crypto_common_digests(&cert->cert_digests, - (char*)cert->encoded, cert->encoded_len); - - if ((pkey = X509_get_pubkey(x509_cert)) && - (rsa = EVP_PKEY_get1_RSA(pkey))) { - crypto_pk_t *pk = crypto_new_pk_from_openssl_rsa_(rsa); - if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) { - crypto_pk_free(pk); - EVP_PKEY_free(pkey); - goto err; - } - - cert->pkey_digests_set = 1; - crypto_pk_free(pk); - EVP_PKEY_free(pkey); - } - - return cert; - err: - /* LCOV_EXCL_START for the same reason as the exclusion above */ - tor_free(cert); - log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate."); - X509_free(x509_cert); - return NULL; - /* LCOV_EXCL_STOP */ +/** Set *encoded_out and *size_out to cert's encoded DER + * representation and length, respectively. */ +void +tor_x509_cert_get_der(const tor_x509_cert_t *cert, + const uint8_t **encoded_out, size_t *size_out) +{ + tor_assert(cert); + tor_assert(encoded_out); + tor_assert(size_out); + *encoded_out = cert->encoded; + *size_out = cert->encoded_len; } /** Return a new copy of cert. */ diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index e4722b4dfc..6772765993 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -811,9 +811,11 @@ CERTS_FAIL(expired_rsa_id, /* both */ tor_x509_cert_t *newc; time_t new_end = time(NULL) - 86400 * 10; newc = tor_x509_cert_replace_expiration(idc, new_end, d->key2); - certs_cell_cert_setlen_body(cert, newc->encoded_len); - memcpy(certs_cell_cert_getarray_body(cert), - newc->encoded, newc->encoded_len); + const uint8_t *encoded; + size_t encoded_len; + tor_x509_cert_get_der(newc, &encoded, &encoded_len); + certs_cell_cert_setlen_body(cert, encoded_len); + memcpy(certs_cell_cert_getarray_body(cert), encoded, encoded_len); REENCODE(); tor_x509_cert_free(newc); }) From 8148c0717dc6b05b0cda18ade94e03ed947bc7a3 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 22 Aug 2018 10:31:44 -0400 Subject: [PATCH 51/76] Change log_test_helpers macros to use printf, not pasting This ensures that our test failure messages actually tell us what strings Tor was expecting. I will need this to debug some test failures. --- src/test/log_test_helpers.h | 35 ++++++++++++++++++----------------- src/test/test_shared_random.c | 2 +- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h index fc9768c125..330da38118 100644 --- a/src/test/log_test_helpers.h +++ b/src/test/log_test_helpers.h @@ -32,7 +32,7 @@ void mock_dump_saved_logs(void); #define assert_log_predicate(predicate, failure_msg) \ do { \ if (!(predicate)) { \ - tt_fail_msg((failure_msg)); \ + TT_FAIL(failure_msg); \ mock_dump_saved_logs(); \ TT_EXIT_TEST_FUNCTION; \ } \ @@ -40,70 +40,71 @@ void mock_dump_saved_logs(void); #define expect_log_msg(str) \ assert_log_predicate(mock_saved_log_has_message(str), \ - "expected log to contain " # str); + ("expected log to contain \"%s\"", str)); #define expect_log_msg_containing(str) \ assert_log_predicate(mock_saved_log_has_message_containing(str), \ - "expected log to contain " # str); + ("expected log to contain \"%s\"", str)); #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 \"%s\" or \"%s\"", str1, 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); + ("expected log to contain \"%s\" or \"%s\" or \"%s\"", \ + str1, str2, 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); + ("expected log to contain \"%s\" or \"%s\" or \"%s\" or \"%s\"", \ + str1, str2, str3, str4)) #define expect_single_log_msg(str) \ do { \ \ assert_log_predicate(mock_saved_log_has_message_containing(str) && \ mock_saved_log_n_entries() == 1, \ - "expected log to contain exactly 1 message " # str); \ + ("expected log to contain exactly 1 message \"%s\"", \ + str)); \ } while (0); #define expect_single_log_msg_containing(str) \ do { \ assert_log_predicate(mock_saved_log_has_message_containing(str)&& \ mock_saved_log_n_entries() == 1 , \ - "expected log to contain 1 message, containing " # str); \ + ("expected log to contain 1 message, containing \"%s\"",\ + str)); \ } while (0); #define expect_no_log_msg(str) \ assert_log_predicate(!mock_saved_log_has_message(str), \ - "expected log to not contain " # str); + ("expected log to not contain \"%s\"",str)); #define expect_no_log_msg_containing(str) \ assert_log_predicate(!mock_saved_log_has_message_containing(str), \ - "expected log to not contain " # str); + ("expected log to not contain \"%s\"", str)); #define expect_log_severity(severity) \ assert_log_predicate(mock_saved_log_has_severity(severity), \ - "expected log to contain severity " # severity); + ("expected log to contain severity " # severity)); #define expect_no_log_severity(severity) \ assert_log_predicate(!mock_saved_log_has_severity(severity), \ - "expected log to not contain severity " # severity); + ("expected log to not contain severity " # severity)); #define expect_log_entry() \ assert_log_predicate(mock_saved_log_has_entry(), \ - "expected log to contain entries"); + ("expected log to contain entries")); #define expect_no_log_entry() \ assert_log_predicate(!mock_saved_log_has_entry(), \ - "expected log to not contain entries"); + ("expected log to not contain entries")); #endif /* !defined(TOR_LOG_TEST_HELPERS_H) */ - diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index d2defdf680..4137877e67 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -1284,7 +1284,7 @@ test_keep_commit(void *arg) expect_log_msg_containing("doesn't match the commit value."); expect_log_msg_containing("has an invalid reveal value."); assert_log_predicate(mock_saved_log_n_entries() == 2, - "expected 2 log entries"); + ("expected 2 log entries")); teardown_capture_of_logs(); memcpy(commit->hashed_reveal, place_holder.hashed_reveal, sizeof(commit->hashed_reveal)); From 7c5339677fd4d524a95bc8c18af223f710ca94e2 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 22 Aug 2018 11:03:33 -0400 Subject: [PATCH 52/76] Log error strings in crypto_nss_log_errors(). I'll need this for debugging. --- src/lib/crypt_ops/crypto_nss_mgt.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c index 89ef019218..85b18e00cd 100644 --- a/src/lib/crypt_ops/crypto_nss_mgt.c +++ b/src/lib/crypt_ops/crypto_nss_mgt.c @@ -14,6 +14,7 @@ #include "lib/log/log.h" #include "lib/log/util_bug.h" +#include "lib/string/printf.h" DISABLE_GCC_WARNING(strict-prototypes) #include @@ -74,11 +75,20 @@ void crypto_nss_log_errors(int severity, const char *doing) { PRErrorCode code = PR_GetError(); - /* XXXX how do I convert errors to strings? */ + const char *string = PORT_ErrorToString(code); + const char *name = PORT_ErrorToName(code); + char buf[16]; + if (!string) + string = ""; + if (!name) { + tor_snprintf(buf, sizeof(buf), "%d", code); + name = buf; + } if (doing) { - tor_log(severity, LD_CRYPTO, "NSS error %u while %s", code, doing); + tor_log(severity, LD_CRYPTO, "NSS error %s while %s: %s", + name, doing, string); } else { - tor_log(severity, LD_CRYPTO, "NSS error %u", code); + tor_log(severity, LD_CRYPTO, "NSS error %s: %s", name, string); } } From c567b8fcb4e4851d6db19946cce8c4d5e75535f5 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 13 Aug 2018 14:54:35 -0400 Subject: [PATCH 53/76] NSS support for x509 certs 7 unit tests are failing at this point, but they're all TLS-related. --- src/lib/crypt_ops/crypto_nss_mgt.c | 9 + src/lib/crypt_ops/crypto_rsa.h | 10 + src/lib/crypt_ops/crypto_rsa_nss.c | 27 +++ src/lib/tls/tortls.c | 157 +++++++++++++ src/lib/tls/tortls_internal.h | 4 + src/lib/tls/tortls_nss.c | 61 ++--- src/lib/tls/tortls_openssl.c | 157 ++----------- src/lib/tls/x509.h | 6 - src/lib/tls/x509_internal.h | 17 ++ src/lib/tls/x509_nss.c | 362 +++++++++++++++++++++++++++-- src/lib/tls/x509_openssl.c | 3 +- src/test/test_link_handshake.c | 24 +- 12 files changed, 620 insertions(+), 217 deletions(-) diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c index 85b18e00cd..187f556bd2 100644 --- a/src/lib/crypt_ops/crypto_nss_mgt.c +++ b/src/lib/crypt_ops/crypto_nss_mgt.c @@ -69,6 +69,15 @@ crypto_nss_early_init(void) crypto_nss_log_errors(LOG_ERR, "setting cipher policy"); tor_assert_unreached(); } + + /* We need to override the default here, or NSS will reject all the + * legacy Tor certificates. */ + SECStatus rv = NSS_OptionSet(NSS_RSA_MIN_KEY_SIZE, 1024); + if (rv != SECSuccess) { + log_err(LD_CRYPTO, "Unable to set NSS min RSA key size"); + crypto_nss_log_errors(LOG_ERR, "setting cipher option."); + tor_assert_unreached(); + } } void diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index afc6c4201c..aaf32ec1b0 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -121,6 +121,16 @@ MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,( crypto_pk_t *env,int private)); #endif +#ifdef ENABLE_NSS +struct SECKEYPublicKeyStr; +struct SECKEYPrivateKeyStr; +crypto_pk_t *crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub); +const struct SECKEYPublicKeyStr *crypto_pk_get_nss_pubkey( + const crypto_pk_t *key); +const struct SECKEYPrivateKeyStr *crypto_pk_get_nss_privkey( + const crypto_pk_t *key); +#endif + void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src); void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src); diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c index 0411687b9a..517faa5c7b 100644 --- a/src/lib/crypt_ops/crypto_rsa_nss.c +++ b/src/lib/crypt_ops/crypto_rsa_nss.c @@ -47,6 +47,33 @@ crypto_pk_key_is_private(const crypto_pk_t *key) return key && key->seckey; } +/** used by tortls.c: wrap a SecKEYPublicKey in a crypto_pk_t. Take ownership + * of the RSA object. */ +crypto_pk_t * +crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub) +{ + crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t)); + result->pubkey = pub; + return result; +} + +/** Return the SECKEYPublicKey for the provided crypto_pk_t. */ +const SECKEYPublicKey * +crypto_pk_get_nss_pubkey(const crypto_pk_t *key) +{ + tor_assert(key); + return key->pubkey; +} + +/** Return the SECKEYPrivateKey for the provided crypto_pk_t, or NULL if it + * does not exist. */ +const SECKEYPrivateKey * +crypto_pk_get_nss_privkey(const crypto_pk_t *key) +{ + tor_assert(key); + return key->seckey; +} + #ifdef ENABLE_OPENSSL /** used by tortls.c: wrap an RSA* in a crypto_pk_t. Take ownership of the * RSA object. */ diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 395f0148e2..0b14b69f44 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -5,11 +5,14 @@ #define TORTLS_PRIVATE #include "lib/tls/x509.h" +#include "lib/tls/x509_internal.h" #include "lib/tls/tortls.h" #include "lib/tls/tortls_st.h" #include "lib/tls/tortls_internal.h" #include "lib/log/util_bug.h" #include "lib/intmath/cmp.h" +#include "lib/crypt_ops/crypto_rsa.h" +#include "lib/crypt_ops/crypto_rand.h" /** Global TLS contexts. We keep them here because nobody else needs * to touch them. @@ -28,6 +31,39 @@ tor_tls_context_get(int is_server) return is_server ? server_tls_context : client_tls_context; } +/** Set *link_cert_out and *id_cert_out to the link certificate + * and ID certificate that we're currently using for our V3 in-protocol + * handshake's certificate chain. If server is true, provide the certs + * that we use in server mode (auth, ID); otherwise, provide the certs that we + * use in client mode. (link, ID) */ +int +tor_tls_get_my_certs(int server, + const tor_x509_cert_t **link_cert_out, + const tor_x509_cert_t **id_cert_out) +{ + tor_tls_context_t *ctx = tor_tls_context_get(server); + if (! ctx) + return -1; + if (link_cert_out) + *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert; + if (id_cert_out) + *id_cert_out = ctx->my_id_cert; + return 0; +} + +/** + * Return the authentication key that we use to authenticate ourselves as a + * client in the V3 in-protocol handshake. + */ +crypto_pk_t * +tor_tls_get_my_client_auth_key(void) +{ + tor_tls_context_t *context = tor_tls_context_get(0); + if (! context) + return NULL; + return context->auth_key; +} + /** Increase the reference count of ctx. */ void tor_tls_context_incref(tor_tls_context_t *ctx) @@ -158,6 +194,127 @@ tor_tls_context_init(unsigned flags, return MIN(rv1, rv2); } +/** Create a new global TLS context. + * + * You can call this function multiple times. Each time you call it, + * it generates new certificates; all new connections will use + * the new SSL context. + */ +int +tor_tls_context_init_one(tor_tls_context_t **ppcontext, + crypto_pk_t *identity, + unsigned int key_lifetime, + unsigned int flags, + int is_client) +{ + tor_tls_context_t *new_ctx = tor_tls_context_new(identity, + key_lifetime, + flags, + is_client); + tor_tls_context_t *old_ctx = *ppcontext; + + if (new_ctx != NULL) { + *ppcontext = new_ctx; + + /* Free the old context if one existed. */ + if (old_ctx != NULL) { + /* This is safe even if there are open connections: we reference- + * count tor_tls_context_t objects. */ + tor_tls_context_decref(old_ctx); + } + } + + return ((new_ctx != NULL) ? 0 : -1); +} + +/** Size of the RSA key to use for our TLS link keys */ +#define RSA_LINK_KEY_BITS 2048 + +/** How long do identity certificates live? (sec) */ +#define IDENTITY_CERT_LIFETIME (365*24*60*60) + +/** + * Initialize the certificates and keys for a TLS context result + * + * Other arguments as for tor_tls_context_new(). + */ +int +tor_tls_context_init_certificates(tor_tls_context_t *result, + crypto_pk_t *identity, + unsigned key_lifetime, + unsigned flags) +{ + (void)flags; + int rv = -1; + char *nickname = NULL, *nn2 = NULL; + crypto_pk_t *rsa = NULL, *rsa_auth = NULL; + tor_x509_cert_impl_t *cert = NULL, *idcert = NULL, *authcert = NULL; + + nickname = crypto_random_hostname(8, 20, "www.", ".net"); + +#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE + nn2 = crypto_random_hostname(8, 20, "www.", ".net"); +#else + nn2 = crypto_random_hostname(8, 20, "www.", ".com"); +#endif + + /* Generate short-term RSA key for use with TLS. */ + if (!(rsa = crypto_pk_new())) + goto error; + if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0) + goto error; + + /* Generate short-term RSA key for use in the in-protocol ("v3") + * authentication handshake. */ + if (!(rsa_auth = crypto_pk_new())) + goto error; + if (crypto_pk_generate_key(rsa_auth)<0) + goto error; + + /* Create a link certificate signed by identity key. */ + cert = tor_tls_create_certificate(rsa, identity, nickname, nn2, + key_lifetime); + /* Create self-signed certificate for identity key. */ + idcert = tor_tls_create_certificate(identity, identity, nn2, nn2, + IDENTITY_CERT_LIFETIME); + /* Create an authentication certificate signed by identity key. */ + authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2, + key_lifetime); + if (!cert || !idcert || !authcert) { + log_warn(LD_CRYPTO, "Error creating certificate"); + goto error; + } + + result->my_link_cert = tor_x509_cert_new(cert); + cert = NULL; + result->my_id_cert = tor_x509_cert_new(idcert); + idcert = NULL; + result->my_auth_cert = tor_x509_cert_new(authcert); + authcert = NULL; + if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert) + goto error; + result->link_key = rsa; + rsa = NULL; + result->auth_key = rsa_auth; + rsa_auth = NULL; + + rv = 0; + error: + + tor_free(nickname); + tor_free(nn2); + + if (cert) + tor_x509_cert_impl_free_(cert); + if (idcert) + tor_x509_cert_impl_free_(idcert); + if (authcert) + tor_x509_cert_impl_free_(authcert); + crypto_pk_free(rsa); + crypto_pk_free(rsa_auth); + + return rv; +} /** Make future log messages about tls display the address * address. */ diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h index b997ee3e4a..2920e96585 100644 --- a/src/lib/tls/tortls_internal.h +++ b/src/lib/tls/tortls_internal.h @@ -27,6 +27,10 @@ int tor_tls_context_init_one(tor_tls_context_t **ppcontext, unsigned int key_lifetime, unsigned int flags, int is_client); +int tor_tls_context_init_certificates(tor_tls_context_t *result, + crypto_pk_t *identity, + unsigned key_lifetime, + unsigned flags); #ifdef ENABLE_OPENSSL void tor_tls_context_impl_free(struct ssl_ctx_st *); diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index 3ab5c753d0..35dbc27d9c 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -23,7 +23,9 @@ #include "lib/crypt_ops/crypto_dh.h" #include "lib/crypt_ops/crypto_util.h" #include "lib/tls/x509.h" +#include "lib/tls/x509_internal.h" #include "lib/tls/tortls.h" +#include "lib/tls/tortls_st.h" #include "lib/tls/tortls_internal.h" #include "lib/log/util_bug.h" @@ -64,27 +66,27 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, unsigned flags, int is_client) { tor_assert(identity); - tor_assert(key_lifetime); - (void)flags; - (void)is_client; - // XXXX - return NULL; -} -int -tor_tls_context_init_one(tor_tls_context_t **ppcontext, - crypto_pk_t *identity, - unsigned int key_lifetime, - unsigned int flags, - int is_client) -{ - tor_assert(ppcontext); - tor_assert(identity); - tor_assert(key_lifetime); - (void)flags; - (void)is_client; - // XXXX - return -1; + + tor_tls_context_t *ctx = tor_malloc_zero(sizeof(tor_tls_context_t)); + ctx->refcnt = 1; + + if (! is_client) { + if (tor_tls_context_init_certificates(ctx, identity, + key_lifetime, flags) < 0) { + goto err; + } + } + + // XXXX write the main body. + + goto done; + err: + tor_tls_context_decref(ctx); + ctx = NULL; + done: + return ctx; } + void tor_tls_context_impl_free(struct ssl_ctx_st *ctx) { @@ -361,25 +363,6 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, // XXXX } -int -tor_tls_get_my_certs(int server, - const struct tor_x509_cert_t **link_cert_out, - const struct tor_x509_cert_t **id_cert_out) -{ - tor_assert(link_cert_out); - tor_assert(id_cert_out); - (void)server; - // XXXX - return -1; -} - -crypto_pk_t * -tor_tls_get_my_client_auth_key(void) -{ - // XXXX - return NULL; -} - const char * tor_tls_get_ciphersuite_name(tor_tls_t *tls) { diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index 5f54312355..fb6328bcd0 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -18,6 +18,7 @@ #define TORTLS_PRIVATE #define TORTLS_OPENSSL_PRIVATE +#define TOR_X509_PRIVATE #ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include "*/ #include @@ -75,9 +76,6 @@ ENABLE_GCC_WARNING(redundant-decls) #define LEGAL_NICKNAME_CHARACTERS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" -/** How long do identity certificates live? (sec) */ -#define IDENTITY_CERT_LIFETIME (365*24*60*60) - #define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer") #if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') @@ -489,39 +487,6 @@ static const char CLIENT_CIPHER_LIST[] = #undef CIPHER #undef XCIPHER -/** Set *link_cert_out and *id_cert_out to the link certificate - * and ID certificate that we're currently using for our V3 in-protocol - * handshake's certificate chain. If server is true, provide the certs - * that we use in server mode (auth, ID); otherwise, provide the certs that we - * use in client mode. (link, ID) */ -int -tor_tls_get_my_certs(int server, - const tor_x509_cert_t **link_cert_out, - const tor_x509_cert_t **id_cert_out) -{ - tor_tls_context_t *ctx = tor_tls_context_get(server); - if (! ctx) - return -1; - if (link_cert_out) - *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert; - if (id_cert_out) - *id_cert_out = ctx->my_id_cert; - return 0; -} - -/** - * Return the authentication key that we use to authenticate ourselves as a - * client in the V3 in-protocol handshake. - */ -crypto_pk_t * -tor_tls_get_my_client_auth_key(void) -{ - tor_tls_context_t *context = tor_tls_context_get(0); - if (! context) - return NULL; - return context->auth_key; -} - /** Return true iff the other side of tls has authenticated to us, and * the key certified in cert is the same as the key they used to do it. */ @@ -548,39 +513,6 @@ tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert)) return result; } -/** Create a new global TLS context. - * - * You can call this function multiple times. Each time you call it, - * it generates new certificates; all new connections will use - * the new SSL context. - */ -int -tor_tls_context_init_one(tor_tls_context_t **ppcontext, - crypto_pk_t *identity, - unsigned int key_lifetime, - unsigned int flags, - int is_client) -{ - tor_tls_context_t *new_ctx = tor_tls_context_new(identity, - key_lifetime, - flags, - is_client); - tor_tls_context_t *old_ctx = *ppcontext; - - if (new_ctx != NULL) { - *ppcontext = new_ctx; - - /* Free the old context if one existed. */ - if (old_ctx != NULL) { - /* This is safe even if there are open connections: we reference- - * count tor_tls_context_t objects. */ - tor_tls_context_decref(old_ctx); - } - } - - return ((new_ctx != NULL) ? 0 : -1); -} - void tor_tls_context_impl_free(struct ssl_ctx_st *ctx) { @@ -592,8 +524,6 @@ tor_tls_context_impl_free(struct ssl_ctx_st *ctx) /** The group we should use for ecdhe when none was selected. */ #define NID_tor_default_ecdhe_group NID_X9_62_prime256v1 -#define RSA_LINK_KEY_BITS 2048 - /** Create a new TLS context for use with Tor TLS handshakes. * identity should be set to the identity key used to sign the * certificate. @@ -602,57 +532,19 @@ tor_tls_context_t * tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, unsigned flags, int is_client) { - crypto_pk_t *rsa = NULL, *rsa_auth = NULL; EVP_PKEY *pkey = NULL; tor_tls_context_t *result = NULL; - X509 *cert = NULL, *idcert = NULL, *authcert = NULL; - char *nickname = NULL, *nn2 = NULL; tor_tls_init(); - nickname = crypto_random_hostname(8, 20, "www.", ".net"); -#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE - nn2 = crypto_random_hostname(8, 20, "www.", ".net"); -#else - nn2 = crypto_random_hostname(8, 20, "www.", ".com"); -#endif - - /* Generate short-term RSA key for use with TLS. */ - if (!(rsa = crypto_pk_new())) - goto error; - if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0) - goto error; - if (!is_client) { - /* Generate short-term RSA key for use in the in-protocol ("v3") - * authentication handshake. */ - if (!(rsa_auth = crypto_pk_new())) - goto error; - if (crypto_pk_generate_key(rsa_auth)<0) - goto error; - /* Create a link certificate signed by identity key. */ - cert = tor_tls_create_certificate(rsa, identity, nickname, nn2, - key_lifetime); - /* Create self-signed certificate for identity key. */ - idcert = tor_tls_create_certificate(identity, identity, nn2, nn2, - IDENTITY_CERT_LIFETIME); - /* Create an authentication certificate signed by identity key. */ - authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2, - key_lifetime); - if (!cert || !idcert || !authcert) { - log_warn(LD_CRYPTO, "Error creating certificate"); - goto error; - } - } result = tor_malloc_zero(sizeof(tor_tls_context_t)); result->refcnt = 1; - if (!is_client) { - result->my_link_cert = tor_x509_cert_new(X509_dup(cert)); - result->my_id_cert = tor_x509_cert_new(X509_dup(idcert)); - result->my_auth_cert = tor_x509_cert_new(X509_dup(authcert)); - if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert) + + if (! is_client) { + if (tor_tls_context_init_certificates(result, identity, key_lifetime, + flags) < 0) { goto error; - result->link_key = crypto_pk_dup_key(rsa); - result->auth_key = crypto_pk_dup_key(rsa_auth); + } } #if 0 @@ -727,22 +619,21 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS); #endif if (! is_client) { - if (cert && !SSL_CTX_use_certificate(result->ctx,cert)) + if (result->my_link_cert && + !SSL_CTX_use_certificate(result->ctx, + result->my_link_cert->cert)) { goto error; - X509_free(cert); /* We just added a reference to cert. */ - cert=NULL; - if (idcert) { + } + if (result->my_id_cert) { X509_STORE *s = SSL_CTX_get_cert_store(result->ctx); tor_assert(s); - X509_STORE_add_cert(s, idcert); - X509_free(idcert); /* The context now owns the reference to idcert */ - idcert = NULL; + X509_STORE_add_cert(s, X509_dup(result->my_id_cert->cert)); } } SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); if (!is_client) { - tor_assert(rsa); - if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,1))) + tor_assert(result->link_key); + if (!(pkey = crypto_pk_get_openssl_evp_pkey_(result->link_key,1))) goto error; if (!SSL_CTX_use_PrivateKey(result->ctx, pkey)) goto error; @@ -751,6 +642,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, if (!SSL_CTX_check_private_key(result->ctx)) goto error; } + { DH *dh = crypto_dh_new_openssl_tls(); tor_assert(dh); @@ -777,33 +669,14 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, /* let us realloc bufs that we're writing from */ SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - if (rsa) - crypto_pk_free(rsa); - if (rsa_auth) - crypto_pk_free(rsa_auth); - X509_free(authcert); - tor_free(nickname); - tor_free(nn2); return result; error: tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context"); - tor_free(nickname); - tor_free(nn2); if (pkey) EVP_PKEY_free(pkey); - if (rsa) - crypto_pk_free(rsa); - if (rsa_auth) - crypto_pk_free(rsa_auth); if (result) tor_tls_context_decref(result); - if (cert) - X509_free(cert); - if (idcert) - X509_free(idcert); - if (authcert) - X509_free(authcert); return NULL; } diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h index 8316df75a3..e7440a192f 100644 --- a/src/lib/tls/x509.h +++ b/src/lib/tls/x509.h @@ -72,10 +72,4 @@ int tor_tls_cert_is_valid(int severity, time_t now, int check_rsa_1024); -int tor_x509_check_cert_lifetime_internal(int severity, - const tor_x509_cert_impl_t *cert, - time_t now, - int past_tolerance, - int future_tolerance); - #endif diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h index 86f5a0de59..4b49f1dec2 100644 --- a/src/lib/tls/x509_internal.h +++ b/src/lib/tls/x509_internal.h @@ -14,6 +14,17 @@ #include "lib/crypt_ops/crypto_rsa.h" #include "lib/testsupport/testsupport.h" +/** + * How skewed do we allow our clock to be with respect to certificates that + * seem to be expired? (seconds) + */ +#define TOR_X509_PAST_SLOP (2*24*60*60) +/** + * How skewed do we allow our clock to be with respect to certificates that + * seem to come from the future? (seconds) + */ +#define TOR_X509_FUTURE_SLOP (30*24*60*60) + MOCK_DECL(tor_x509_cert_impl_t *, tor_tls_create_certificate, (crypto_pk_t *rsa, crypto_pk_t *rsa_sign, @@ -25,6 +36,12 @@ MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new, const tor_x509_cert_impl_t *tor_x509_cert_get_impl( const tor_x509_cert_t *cert); +int tor_x509_check_cert_lifetime_internal(int severity, + const tor_x509_cert_impl_t *cert, + time_t now, + int past_tolerance, + int future_tolerance); + void tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert); #ifdef ENABLE_OPENSSL int tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert); diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c index ac9e6658d8..35b3d2542b 100644 --- a/src/lib/tls/x509_nss.c +++ b/src/lib/tls/x509_nss.c @@ -15,10 +15,144 @@ #include "lib/tls/tortls.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/crypto_nss_mgt.h" #include "lib/log/util_bug.h" +#include "lib/encoding/time_fmt.h" +#include "lib/string/printf.h" #include +#include #include +#include +#include + +/* Units of PRTime per second. + * + * (PRTime is based in microseconds since the Unix + * epoch.) */ +#define PRTIME_PER_SEC (1000*1000) + +static tor_x509_cert_impl_t *tor_x509_cert_decode_internal( + const uint8_t *certificate, int certificate_len); + +static tor_x509_cert_impl_t * +tor_tls_create_certificate_internal(crypto_pk_t *rsa, + crypto_pk_t *rsa_sign, + CERTName *subject_dn, + CERTName *issuer_dn, + time_t start_time, + time_t end_time) +{ + if (! crypto_pk_key_is_private(rsa_sign)) { + return NULL; + } + + const SECKEYPublicKey *subject_key = crypto_pk_get_nss_pubkey(rsa); + const SECKEYPrivateKey *signing_key = crypto_pk_get_nss_privkey(rsa_sign); + SECStatus s; + + CERTSubjectPublicKeyInfo *subject_spki = NULL; + CERTCertificateRequest *request = NULL; + CERTValidity *validity = NULL; + CERTCertificate *cert = NULL; + SECItem der = { .data = NULL, .len = 0 }; + SECItem signed_der = { .data = NULL, .len = 0 }; + + CERTCertificate *result_cert = NULL; + + validity = CERT_CreateValidity(((PRTime)start_time) * PRTIME_PER_SEC, + ((PRTime)end_time) * PRTIME_PER_SEC); + if (! validity) { + crypto_nss_log_errors(LOG_WARN, "creating a validity object"); + goto err; + } + + unsigned long serial_number; + crypto_rand((char*)&serial_number, sizeof(serial_number)); + + subject_spki = SECKEY_CreateSubjectPublicKeyInfo(subject_key); + if (!subject_spki) + goto err; + + /* Make a CSR ... */ + // XXX do we need to set any attributes? + request = CERT_CreateCertificateRequest(subject_dn, + subject_spki, + NULL /* attributes */); + if (!request) + goto err; + + /* Put it into a certificate ... */ + cert = CERT_CreateCertificate(serial_number, + issuer_dn, + validity, + request); + if (!cert) + goto err; + + /* version 3 cert */ + *cert->version.data = 2; /* 2 means version 3. */ + cert->version.len = 1; + + // XXX do we need to set anything else on the cert? + + /* Sign it. */ + KeyType privkey_type = SECKEY_GetPrivateKeyType(signing_key); + SECOidTag oid_tag = SEC_GetSignatureAlgorithmOidTag(privkey_type, + SEC_OID_SHA256); + if (oid_tag == SEC_OID_UNKNOWN) + goto err; + s = SECOID_SetAlgorithmID(cert->arena, &cert->signature, oid_tag, NULL); + if (s != SECSuccess) + goto err; + + void *tmp; + tmp = SEC_ASN1EncodeItem(cert->arena, &der, cert, + SEC_ASN1_GET(CERT_CertificateTemplate)); + if (!tmp) + goto err; + + s = SEC_DerSignDataWithAlgorithmID(cert->arena, + &signed_der, + der.data, der.len, + (SECKEYPrivateKey *)signing_key,//const + &cert->signature); + + if (s != SECSuccess) + goto err; + + /* Re-parse it, to make sure all the certificates we actually use + * appear via being decoded. */ + result_cert = tor_x509_cert_decode_internal(signed_der.data, signed_der.len); + +#if 1 + { + // Can we check the cert we just signed? + tor_assert(result_cert); + SECKEYPublicKey *issuer_pk = (SECKEYPublicKey *) + crypto_pk_get_nss_pubkey(rsa_sign); + SECStatus cert_ok = CERT_VerifySignedDataWithPublicKey( + &result_cert->signatureWrap, issuer_pk, NULL); + tor_assert(cert_ok == SECSuccess); + } +#endif + + err: + if (subject_spki) + SECKEY_DestroySubjectPublicKeyInfo(subject_spki); + if (request) + CERT_DestroyCertificateRequest(request); + if (validity) + CERT_DestroyValidity(validity); + + // unnecessary, since these are allocated in the cert's arena. + //SECITEM_FreeItem(&der, PR_FALSE); + //SECITEM_FreeItem(&signed_der, PR_FALSE); + if (cert) + CERT_DestroyCertificate(cert); + + return result_cert; +} MOCK_IMPL(tor_x509_cert_impl_t *, tor_tls_create_certificate,(crypto_pk_t *rsa, @@ -31,9 +165,39 @@ tor_tls_create_certificate,(crypto_pk_t *rsa, tor_assert(rsa_sign); tor_assert(cname); tor_assert(cname_sign); - (void) cert_lifetime; - // XXXX - return NULL; + + char *cname_rfc_1485 = NULL, *cname_sign_rfc_1485 = NULL; + CERTName *subject_dn = NULL, *issuer_dn = NULL; + time_t start_time; + time_t end_time; + CERTCertificate *result = NULL; + + tor_asprintf(&cname_rfc_1485, "CN=%s", cname); + tor_asprintf(&cname_sign_rfc_1485, "CN=%s", cname_sign); + + subject_dn = CERT_AsciiToName(cname_rfc_1485); + issuer_dn = CERT_AsciiToName(cname_sign_rfc_1485); + if (!subject_dn || !issuer_dn) + goto err; + + tor_tls_pick_certificate_lifetime(time(NULL), cert_lifetime, + &start_time, &end_time); + + result = tor_tls_create_certificate_internal(rsa, + rsa_sign, + subject_dn, + issuer_dn, + start_time, + end_time); + err: + tor_free(cname_rfc_1485); + tor_free(cname_sign_rfc_1485); + if (subject_dn) + CERT_DestroyName(subject_dn); + if (issuer_dn) + CERT_DestroyName(issuer_dn); + + return result; } /** Set *encoded_out and *size_out to cert's encoded DER @@ -63,26 +227,63 @@ tor_x509_cert_t * tor_x509_cert_dup(const tor_x509_cert_t *cert) { tor_assert(cert); - // XXXX - return NULL; + return tor_x509_cert_new(CERT_DupCertificate(cert->cert)); +} + +/** + * As tor_x509_cert_decode, but return the NSS certificate type +*/ +static tor_x509_cert_impl_t * +tor_x509_cert_decode_internal(const uint8_t *certificate, + int certificate_len) +{ + tor_assert(certificate); + if (certificate_len > INT_MAX) + return NULL; + + SECItem der = { .type = siBuffer, + .data = (unsigned char *)certificate, + .len = certificate_len }; + CERTCertDBHandle *certdb = CERT_GetDefaultCertDB(); + tor_assert(certdb); + return CERT_NewTempCertificate(certdb, + &der, + NULL /* nickname */, + PR_FALSE, /* isPerm */ + PR_TRUE /* CopyDER */); } tor_x509_cert_t * tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len) { - tor_assert(certificate); - (void) certificate_len; - // XXXX - return NULL; + CERTCertificate *cert = tor_x509_cert_decode_internal(certificate, + (int)certificate_len); + if (! cert) { + crypto_nss_log_errors(LOG_INFO, "decoding certificate"); + return NULL; + } + + tor_x509_cert_t *newcert = tor_x509_cert_new(cert); + + return newcert; } crypto_pk_t * tor_tls_cert_get_key(tor_x509_cert_t *cert) { tor_assert(cert); - // XXXXX - return NULL; + CERTSubjectPublicKeyInfo *spki = &cert->cert->subjectPublicKeyInfo; + SECKEYPublicKey *pub = SECKEY_ExtractPublicKey(spki); // we own this pointer + if (pub == NULL) + return NULL; + + if (SECKEY_GetPublicKeyType(pub) != rsaKey) { + SECKEY_DestroyPublicKey(pub); + return NULL; + } + + return crypto_pk_new_from_nss_pubkey(pub); } int @@ -92,14 +293,80 @@ tor_tls_cert_is_valid(int severity, time_t now, int check_rsa_1024) { + int result = 0; + tor_assert(cert); tor_assert(signing_cert); - (void)severity; - (void)now; - (void)check_rsa_1024; - // XXXXX - return 0; + SECKEYPublicKey *pk = CERT_ExtractPublicKey(signing_cert->cert); + if (pk == NULL) { + log_fn(severity, LD_CRYPTO, + "Invalid certificate: could not extract issuer key"); + goto fail; + } + + SECStatus s = CERT_VerifySignedDataWithPublicKey(&cert->cert->signatureWrap, + pk, NULL); + if (s != SECSuccess) { + log_fn(severity, LD_CRYPTO, + "Invalid certificate: could not validate signature."); + goto fail; + } + + if (tor_x509_check_cert_lifetime_internal(severity, + cert->cert, + now, + TOR_X509_PAST_SLOP, + TOR_X509_FUTURE_SLOP) < 0) + goto fail; + + if (check_rsa_1024) { + /* We require that this is a 1024-bit RSA key, for legacy reasons .:p */ + if (SECKEY_GetPublicKeyType(pk) != rsaKey || + SECKEY_PublicKeyStrengthInBits(pk) != 1024) { + log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is not RSA1024."); + goto fail; + } + } else { + /* We require that this key is at least minimally strong. */ + unsigned min_bits = (SECKEY_GetPublicKeyType(pk) == ecKey) ? 128: 1024; + if (SECKEY_PublicKeyStrengthInBits(pk) < min_bits) { + log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is too weak."); + goto fail; + } + } + + /* The certificate is valid. */ + result = 1; + + fail: + if (pk) + SECKEY_DestroyPublicKey(pk); + return result; +} + +static void +log_cert_lifetime(int severity, + const char *status, + time_t now, + PRTime notBefore, + PRTime notAfter) +{ + log_fn(severity, LD_GENERAL, + "Certificate %s. Either their clock is set wrong, or your clock " + "is incorrect.", status); + + char nowbuf[ISO_TIME_LEN+1]; + char nbbuf[ISO_TIME_LEN+1]; + char nabuf[ISO_TIME_LEN+1]; + + format_iso_time(nowbuf, now); + format_iso_time(nbbuf, notBefore / PRTIME_PER_SEC); + format_iso_time(nabuf, notAfter / PRTIME_PER_SEC); + + log_fn(severity, LD_GENERAL, + "(The certificate is valid from %s until %s. Your time is %s.)", + nbbuf, nabuf, nowbuf); } int @@ -110,12 +377,33 @@ tor_x509_check_cert_lifetime_internal(int severity, int future_tolerance) { tor_assert(cert); - (void)severity; - (void)now; - (void)past_tolerance; - (void)future_tolerance; - // XXXX - return -1; + + PRTime notBefore=0, notAfter=0; + int64_t t; + SECStatus r = CERT_GetCertTimes(cert, ¬Before, ¬After); + if (r != SECSuccess) { + log_fn(severity, LD_CRYPTO, + "Couldn't get validity times from certificate"); + return -1; + } + + t = ((int64_t)now) + future_tolerance; + t *= PRTIME_PER_SEC; + if (notBefore > t) { + log_cert_lifetime(severity, "not yet valid", now, + notBefore, notAfter); + return -1; + } + + t = ((int64_t)now) - past_tolerance; + t *= PRTIME_PER_SEC; + if (notAfter < t) { + log_cert_lifetime(severity, "already expired", now, + notBefore, notAfter); + return -1; + } + + return 0; } #ifdef TOR_UNIT_TESTS @@ -126,9 +414,33 @@ tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp, { tor_assert(inp); tor_assert(signing_key); - (void)new_expiration_time; - // XXXX - return NULL; + PRTime notBefore=0, notAfter=0; + SECStatus r = CERT_GetCertTimes(inp->cert, ¬Before, ¬After); + if (r != SECSuccess) + return NULL; + + time_t start_time = notBefore / PRTIME_PER_SEC; + if (new_expiration_time < start_time) { + /* This prevents an NSS error. */ + start_time = new_expiration_time - 10; + } + + crypto_pk_t *subject_key = tor_tls_cert_get_key((tor_x509_cert_t *)inp); + if (!subject_key) + return NULL; + + CERTCertificate *newcert; + + newcert = tor_tls_create_certificate_internal(subject_key, + signing_key, + &inp->cert->subject, + &inp->cert->issuer, + start_time, + new_expiration_time); + + crypto_pk_free(subject_key); + + return newcert ? tor_x509_cert_new(newcert) : NULL; } #endif diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c index 43bd6b4d48..28a30b66e1 100644 --- a/src/lib/tls/x509_openssl.c +++ b/src/lib/tls/x509_openssl.c @@ -319,7 +319,8 @@ tor_tls_cert_is_valid(int severity, /* okay, the signature checked out right. Now let's check the check the * lifetime. */ if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now, - 48*60*60, 30*24*60*60) < 0) + TOR_X509_PAST_SLOP, + TOR_X509_FUTURE_SLOP) < 0) goto bad; cert_key = X509_get_pubkey(cert->cert); diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index 6772765993..df3fa67eb1 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -795,11 +795,26 @@ CERTS_FAIL(bad_rsa_id_cert, /*ed25519*/ { require_failure_message = "legacy RSA ID certificate was not valid"; certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 1); - uint8_t *body = certs_cell_cert_getarray_body(cert); - ssize_t body_len = certs_cell_cert_getlen_body(cert); - /* Frob a byte in the signature */ - body[body_len - 13] ^= 7; + uint8_t *body; + /* Frob a byte in the signature, after making a new cert. (NSS won't let + * us just frob the old cert, since it will see that the issuer & serial + * number are the same, which will make it fail at an earlier stage than + * signature verification.) */ + const tor_x509_cert_t *idc; + tor_x509_cert_t *newc; + tor_tls_get_my_certs(1, NULL, &idc); + time_t new_end = time(NULL) + 86400 * 10; + newc = tor_x509_cert_replace_expiration(idc, new_end, d->key2); + const uint8_t *encoded; + size_t encoded_len; + tor_x509_cert_get_der(newc, &encoded, &encoded_len); + certs_cell_cert_setlen_body(cert, encoded_len); + certs_cell_cert_set_cert_len(cert, encoded_len); + body = certs_cell_cert_getarray_body(cert); + memcpy(body, encoded, encoded_len); + body[encoded_len - 13] ^= 7; REENCODE(); + tor_x509_cert_free(newc); }) CERTS_FAIL(expired_rsa_id, /* both */ { @@ -815,6 +830,7 @@ CERTS_FAIL(expired_rsa_id, /* both */ size_t encoded_len; tor_x509_cert_get_der(newc, &encoded, &encoded_len); certs_cell_cert_setlen_body(cert, encoded_len); + certs_cell_cert_set_cert_len(cert, encoded_len); memcpy(certs_cell_cert_getarray_body(cert), encoded, encoded_len); REENCODE(); tor_x509_cert_free(newc); From 5205c7fd903cb5bd751812bddb5497ac76e1f30b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 17 Aug 2018 11:24:50 -0400 Subject: [PATCH 54/76] Initial NSS support for TLS. This is enough to get a chutney network to bootstrap, though a bunch of work remains. --- src/lib/tls/tortls.c | 122 +++++++ src/lib/tls/tortls.h | 23 +- src/lib/tls/tortls_internal.h | 16 +- src/lib/tls/tortls_nss.c | 621 ++++++++++++++++++++++++++-------- src/lib/tls/tortls_openssl.c | 137 +------- src/lib/tls/tortls_st.h | 17 +- src/test/test_tortls.c | 26 +- 7 files changed, 656 insertions(+), 306 deletions(-) diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 0b14b69f44..cc9738599e 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -4,6 +4,7 @@ /* See LICENSE for licensing information */ #define TORTLS_PRIVATE +#define TOR_X509_PRIVATE #include "lib/tls/x509.h" #include "lib/tls/x509_internal.h" #include "lib/tls/tortls.h" @@ -14,6 +15,8 @@ #include "lib/crypt_ops/crypto_rsa.h" #include "lib/crypt_ops/crypto_rand.h" +#include + /** Global TLS contexts. We keep them here because nobody else needs * to touch them. * @@ -31,6 +34,26 @@ tor_tls_context_get(int is_server) return is_server ? server_tls_context : client_tls_context; } +/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error + * code. */ +int +tor_errno_to_tls_error(int e) +{ + switch (e) { + case SOCK_ERRNO(ECONNRESET): // most common + return TOR_TLS_ERROR_CONNRESET; + case SOCK_ERRNO(ETIMEDOUT): + return TOR_TLS_ERROR_TIMEOUT; + case SOCK_ERRNO(EHOSTUNREACH): + case SOCK_ERRNO(ENETUNREACH): + return TOR_TLS_ERROR_NO_ROUTE; + case SOCK_ERRNO(ECONNREFUSED): + return TOR_TLS_ERROR_CONNREFUSED; // least common + default: + return TOR_TLS_ERROR_MISC; + } +} + /** Set *link_cert_out and *id_cert_out to the link certificate * and ID certificate that we're currently using for our V3 in-protocol * handshake's certificate chain. If server is true, provide the certs @@ -334,3 +357,102 @@ tor_tls_is_server(tor_tls_t *tls) tor_assert(tls); return tls->isServer; } + +/** Release resources associated with a TLS object. Does not close the + * underlying file descriptor. + */ +void +tor_tls_free_(tor_tls_t *tls) +{ + if (!tls) + return; + tor_assert(tls->ssl); + { + size_t r,w; + tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */ + } + tor_tls_impl_free_(tls->ssl); + tls->ssl = NULL; +#ifdef ENABLE_OPENSSL + tls->negotiated_callback = NULL; +#endif + if (tls->context) + tor_tls_context_decref(tls->context); + tor_free(tls->address); + tls->magic = 0x99999999; + tor_free(tls); +} + +/** If the provided tls connection is authenticated and has a + * certificate chain that is currently valid and signed, then set + * *identity_key to the identity certificate's key and return + * 0. Else, return -1 and log complaints with log-level severity. + */ +int +tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity) +{ + tor_x509_cert_impl_t *cert = NULL, *id_cert = NULL; + tor_x509_cert_t *peer_x509 = NULL, *id_x509 = NULL; + tor_assert(tls); + tor_assert(identity); + int rv = -1; + + try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert); + if (!cert) + goto done; + if (!id_cert) { + log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found"); + goto done; + } + peer_x509 = tor_x509_cert_new(cert); + id_x509 = tor_x509_cert_new(id_cert); + cert = id_cert = NULL; /* Prevent double-free */ + + if (! tor_tls_cert_is_valid(severity, peer_x509, id_x509, time(NULL), 0)) { + goto done; + } + + *identity = tor_tls_cert_get_key(id_x509); + rv = 0; + + done: + if (cert) + tor_x509_cert_impl_free_(cert); + if (id_cert) + tor_x509_cert_impl_free_(id_cert); + tor_x509_cert_free(peer_x509); + tor_x509_cert_free(id_x509); + + return rv; +} + +/** Check whether the certificate set on the connection tls is expired + * give or take past_tolerance seconds, or not-yet-valid give or take + * future_tolerance seconds. Return 0 for valid, -1 for failure. + * + * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime. + */ +int +tor_tls_check_lifetime(int severity, tor_tls_t *tls, + time_t now, + int past_tolerance, int future_tolerance) +{ + tor_x509_cert_t *cert; + int r = -1; + + if (!(cert = tor_tls_get_peer_cert(tls))) + goto done; + + if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now, + past_tolerance, + future_tolerance) < 0) + goto done; + + r = 0; + done: + tor_x509_cert_free(cert); + /* Not expected to get invoked */ + tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime"); + + return r; +} diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h index 306d321cd4..7bbb42b2fd 100644 --- a/src/lib/tls/tortls.h +++ b/src/lib/tls/tortls.h @@ -13,10 +13,25 @@ #include "lib/crypt_ops/crypto_rsa.h" #include "lib/testsupport/testsupport.h" +#include "lib/net/nettypes.h" /* Opaque structure to hold a TLS connection. */ typedef struct tor_tls_t tor_tls_t; +#ifdef TORTLS_PRIVATE +#ifdef ENABLE_OPENSSL +struct ssl_st; +struct ssl_ctx_st; +struct ssl_session_st; +typedef struct ssl_ctx_st tor_tls_context_impl_t; +typedef struct ssl_st tor_tls_impl_t; +#else +struct PRFileDesc; +typedef struct PRFileDesc tor_tls_context_impl_t; +typedef struct PRFileDesc tor_tls_impl_t; +#endif +#endif + struct tor_x509_cert_t; /* Possible return values for most tor_tls_* functions. */ @@ -73,7 +88,7 @@ int tor_tls_context_init(unsigned flags, void tor_tls_context_incref(tor_tls_context_t *ctx); void tor_tls_context_decref(tor_tls_context_t *ctx); tor_tls_context_t *tor_tls_context_get(int is_server); -tor_tls_t *tor_tls_new(int sock, int is_server); +tor_tls_t *tor_tls_new(tor_socket_t sock, int is_server); void tor_tls_set_logged_address(tor_tls_t *tls, const char *address); void tor_tls_set_renegotiate_callback(tor_tls_t *tls, void (*cb)(tor_tls_t *, void *arg), @@ -121,13 +136,17 @@ MOCK_DECL(int,tor_tls_export_key_material,( size_t context_len, const char *label)); +#ifdef ENABLE_OPENSSL /* Log and abort if there are unhandled TLS errors in OpenSSL's error stack. */ #define check_no_tls_errors() check_no_tls_errors_(__FILE__,__LINE__) - void check_no_tls_errors_(const char *fname, int line); + void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, int severity, int domain, const char *doing); +#else +#define check_no_tls_errors() STMT_NIL +#endif int tor_tls_get_my_certs(int server, const struct tor_x509_cert_t **link_cert_out, diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h index 2920e96585..b9e01e0c54 100644 --- a/src/lib/tls/tortls_internal.h +++ b/src/lib/tls/tortls_internal.h @@ -6,15 +6,11 @@ #ifndef TORTLS_INTERNAL_H #define TORTLS_INTERNAL_H -#ifdef ENABLE_OPENSSL -struct ssl_st; -struct ssl_ctx_st; -struct ssl_session_st; -#endif - int tor_errno_to_tls_error(int e); +#ifdef ENABLE_OPENSSL int tor_tls_get_error(tor_tls_t *tls, int r, int extra, const char *doing, int severity, int domain); +#endif MOCK_DECL(void, try_to_extract_certs_from_tls, (int severity, tor_tls_t *tls, tor_x509_cert_impl_t **cert_out, @@ -31,13 +27,9 @@ int tor_tls_context_init_certificates(tor_tls_context_t *result, crypto_pk_t *identity, unsigned key_lifetime, unsigned flags); +void tor_tls_impl_free_(tor_tls_impl_t *ssl); -#ifdef ENABLE_OPENSSL -void tor_tls_context_impl_free(struct ssl_ctx_st *); -#else -struct ssl_ctx_st; // XXXX replace -void tor_tls_context_impl_free(struct ssl_ctx_st *); -#endif +void tor_tls_context_impl_free(tor_tls_context_impl_t *); #ifdef ENABLE_OPENSSL tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl); diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index 35dbc27d9c..d2b81ad084 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -12,6 +12,7 @@ #include "orconfig.h" #define TORTLS_PRIVATE +#define TOR_X509_PRIVATE #ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include "*/ #include @@ -22,6 +23,9 @@ #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_dh.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/crypto_nss_mgt.h" +#include "lib/string/printf.h" + #include "lib/tls/x509.h" #include "lib/tls/x509_internal.h" #include "lib/tls/tortls.h" @@ -29,26 +33,16 @@ #include "lib/tls/tortls_internal.h" #include "lib/log/util_bug.h" -int -tor_errno_to_tls_error(int e) -{ - (void)e; - // XXXX - return -1; -} -int -tor_tls_get_error(tor_tls_t *tls, int r, int extra, - const char *doing, int severity, int domain) -{ - (void)tls; - (void)r; - (void)extra; - (void)doing; - (void)severity; - (void)domain; - // XXXX - return -1; -} +#include +// For access to raw sockets. +#include +#include +#include +#include +#include + +static SECStatus always_accept_cert_cb(void *, PRFileDesc *, PRBool, PRBool); + MOCK_IMPL(void, try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, tor_x509_cert_impl_t **cert_out, @@ -57,14 +51,109 @@ try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, tor_assert(tls); tor_assert(cert_out); tor_assert(id_cert_out); - (void)severity; - // XXXX + (void) severity; + + *cert_out = *id_cert_out = NULL; + + CERTCertificate *peer = SSL_PeerCertificate(tls->ssl); + if (!peer) + return; + *cert_out = peer; /* Now owns pointer. */ + + CERTCertList *chain = SSL_PeerCertificateChain(tls->ssl); + CERTCertListNode *c = CERT_LIST_HEAD(chain); + for (; !CERT_LIST_END(c, chain); c = CERT_LIST_NEXT(c)) { + if (CERT_CompareCerts(c->cert, peer) == PR_FALSE) { + *id_cert_out = CERT_DupCertificate(c->cert); + break; + } + } + CERT_DestroyCertList(chain); +} + +static bool +we_like_ssl_cipher(SSLCipherAlgorithm ca) +{ + switch (ca) { + case ssl_calg_null: return false; + case ssl_calg_rc4: return false; + case ssl_calg_rc2: return false; + case ssl_calg_des: return false; + case ssl_calg_3des: return false; /* ???? */ + case ssl_calg_idea: return false; + case ssl_calg_fortezza: return false; + case ssl_calg_camellia: return false; + case ssl_calg_seed: return false; + + case ssl_calg_aes: return true; + case ssl_calg_aes_gcm: return true; + case ssl_calg_chacha20: return true; + default: return true; + } +} +static bool +we_like_ssl_kea(SSLKEAType kt) +{ + switch (kt) { + case ssl_kea_null: return false; + case ssl_kea_rsa: return false; /* ??? */ + case ssl_kea_fortezza: return false; + case ssl_kea_ecdh_psk: return false; + case ssl_kea_dh_psk: return false; + + case ssl_kea_dh: return true; + case ssl_kea_ecdh: return true; + case ssl_kea_tls13_any: return true; + + case ssl_kea_size: return true; /* prevent a warning. */ + default: return true; + } +} + +static bool +we_like_mac_algorithm(SSLMACAlgorithm ma) +{ + switch (ma) { + case ssl_mac_null: return false; + case ssl_mac_md5: return false; + case ssl_hmac_md5: return false; + + case ssl_mac_sha: return true; + case ssl_hmac_sha: return true; + case ssl_hmac_sha256: return true; + case ssl_mac_aead: return true; + case ssl_hmac_sha384: return true; + default: return true; + } +} + +static bool +we_like_auth_type(SSLAuthType at) +{ + switch (at) { + case ssl_auth_null: return false; + case ssl_auth_rsa_decrypt: return false; + case ssl_auth_dsa: return false; + case ssl_auth_kea: return false; + + case ssl_auth_ecdsa: return true; + case ssl_auth_ecdh_rsa: return true; + case ssl_auth_ecdh_ecdsa: return true; + case ssl_auth_rsa_sign: return true; + case ssl_auth_rsa_pss: return true; + case ssl_auth_psk: return true; + case ssl_auth_tls13_any: return true; + + case ssl_auth_size: return true; /* prevent a warning. */ + default: return true; + } } tor_tls_context_t * tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, unsigned flags, int is_client) { + SECStatus s; tor_assert(identity); tor_tls_context_t *ctx = tor_malloc_zero(sizeof(tor_tls_context_t)); @@ -77,7 +166,128 @@ tor_tls_context_new(crypto_pk_t *identity, } } - // XXXX write the main body. + { + /* Create the "model" PRFileDesc that we will use to base others on. */ + PRFileDesc *tcp = PR_NewTCPSocket(); + if (!tcp) + goto err; + + ctx->ctx = SSL_ImportFD(NULL, tcp); + if (!ctx->ctx) { + PR_Close(tcp); + goto err; + } + } + + // Configure the certificate. + if (!is_client) { + s = SSL_ConfigServerCert(ctx->ctx, + ctx->my_link_cert->cert, + (SECKEYPrivateKey *) + crypto_pk_get_nss_privkey(ctx->link_key), + NULL, /* ExtraServerCertData */ + 0 /* DataLen */); + if (s != SECSuccess) + goto err; + } + + // We need a certificate from the other side. + if (is_client) { + // XXXX does this do anything? + s = SSL_OptionSet(ctx->ctx, SSL_REQUIRE_CERTIFICATE, PR_TRUE); + if (s != SECSuccess) + goto err; + } + + // Always accept other side's cert; we'll check it ourselves in goofy + // tor ways. + s = SSL_AuthCertificateHook(ctx->ctx, always_accept_cert_cb, NULL); + + // We allow simultaneous read and write. + s = SSL_OptionSet(ctx->ctx, SSL_ENABLE_FDX, PR_TRUE); + if (s != SECSuccess) + goto err; + // XXXX SSL_ROLLBACK_DETECTION?? + // XXXX SSL_ENABLE_ALPN?? + + // Force client-mode or server_mode. + s = SSL_OptionSet(ctx->ctx, + is_client ? SSL_HANDSHAKE_AS_CLIENT : SSL_HANDSHAKE_AS_SERVER, + PR_TRUE); + if (s != SECSuccess) + goto err; + + // Disable everything before TLS 1.0; support everything else. + { + SSLVersionRange vrange; + memset(&vrange, 0, sizeof(vrange)); + s = SSL_VersionRangeGetSupported(ssl_variant_stream, &vrange); + if (s != SECSuccess) + goto err; + if (vrange.min < SSL_LIBRARY_VERSION_TLS_1_0) + vrange.min = SSL_LIBRARY_VERSION_TLS_1_0; + s = SSL_VersionRangeSet(ctx->ctx, &vrange); + if (s != SECSuccess) + goto err; + } + + // Only support strong ciphers. + { + const PRUint16 *ciphers = SSL_GetImplementedCiphers(); + const PRUint16 n_ciphers = SSL_GetNumImplementedCiphers(); + PRUint16 i; + for (i = 0; i < n_ciphers; ++i) { + SSLCipherSuiteInfo info; + memset(&info, 0, sizeof(info)); + s = SSL_GetCipherSuiteInfo(ciphers[i], &info, sizeof(info)); + if (s != SECSuccess) + goto err; + if (BUG(info.cipherSuite != ciphers[i])) + goto err; + int disable = info.effectiveKeyBits < 128 || + info.macBits < 128 || + !we_like_ssl_cipher(info.symCipher) || + !we_like_ssl_kea(info.keaType) || + !we_like_mac_algorithm(info.macAlgorithm) || + !we_like_auth_type(info.authType)/* Requires NSS 3.24 */; + + s = SSL_CipherPrefSet(ctx->ctx, ciphers[i], + disable ? PR_FALSE : PR_TRUE); + if (s != SECSuccess) + goto err; + } + } + + // Only use DH and ECDH keys once. + s = SSL_OptionSet(ctx->ctx, SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); + if (s != SECSuccess) + goto err; + + // don't cache sessions. + s = SSL_OptionSet(ctx->ctx, SSL_NO_CACHE, PR_TRUE); + if (s != SECSuccess) + goto err; + + // Enable DH. + s = SSL_OptionSet(ctx->ctx, SSL_ENABLE_SERVER_DHE, PR_TRUE); + if (s != SECSuccess) + goto err; + + // Set DH and ECDH groups. + SSLNamedGroup groups[] = { + ssl_grp_ec_curve25519, + ssl_grp_ec_secp256r1, + ssl_grp_ec_secp224r1, + ssl_grp_ffdhe_2048, + }; + s = SSL_NamedGroupConfig(ctx->ctx, groups, ARRAY_LENGTH(groups)); + if (s != SECSuccess) + goto err; + + // These features are off by default, so we don't need to disable them: + // Session tickets + // Renegotiation + // Compression goto done; err: @@ -88,11 +298,9 @@ tor_tls_context_new(crypto_pk_t *identity, } void -tor_tls_context_impl_free(struct ssl_ctx_st *ctx) +tor_tls_context_impl_free(tor_tls_context_impl_t *ctx) { - (void)ctx; - // XXXX - // XXXX openssl type. + PR_Close(ctx); } void @@ -101,33 +309,82 @@ tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz) (void)tls; (void)buf; (void)sz; - // XXXX + // AFAICT, NSS doesn't expose its internal state. + buf[0]=0; } void tor_tls_init(void) { - // XXXX + /* We don't have any global setup to do yet, but that will change */ } + void tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) { + /* XXXX This implementation isn't right for NSS -- it logs the last error + whether anything actually failed or not. */ + (void)tls; - (void)severity; - (void)domain; - (void)doing; - // XXXX + PRErrorCode code = PORT_GetError(); + + const char *string = PORT_ErrorToString(code); + const char *name = PORT_ErrorToName(code); + char buf[16]; + if (!string) + string = ""; + if (!name) { + tor_snprintf(buf, sizeof(buf), "%d", code); + name = buf; + } + + if (doing) { + log_fn(severity, domain, "TLS error %s while %s: %s", name, doing, string); + } else { + log_fn(severity, domain, "TLS error %s: %s", name, string); + } } tor_tls_t * -tor_tls_new(int sock, int is_server) +tor_tls_new(tor_socket_t sock, int is_server) { (void)sock; - (void)is_server; - // XXXX - return NULL; + tor_tls_context_t *ctx = tor_tls_context_get(is_server); + + PRFileDesc *tcp = PR_ImportTCPSocket(sock); + if (!tcp) + return NULL; + + PRFileDesc *ssl = SSL_ImportFD(ctx->ctx, tcp); + if (!ssl) { + PR_Close(tcp); + return NULL; + } + + tor_tls_t *tls = tor_malloc_zero(sizeof(tor_tls_t)); + tls->magic = TOR_TLS_MAGIC; + tls->context = ctx; + tor_tls_context_incref(ctx); + tls->ssl = ssl; + tls->socket = sock; + tls->state = TOR_TLS_ST_HANDSHAKE; + tls->isServer = !!is_server; + + if (!is_server) { + /* Set a random SNI */ + char *fake_hostname = crypto_random_hostname(4,25, "www.",".com"); + SSL_SetURL(tls->ssl, fake_hostname); + tor_free(fake_hostname); + } + SECStatus s = SSL_ResetHandshake(ssl, is_server ? PR_TRUE : PR_FALSE); + if (s != SECSuccess) { + crypto_nss_log_errors(LOG_WARN, "resetting handshake state"); + } + + return tls; } + void tor_tls_set_renegotiate_callback(tor_tls_t *tls, void (*cb)(tor_tls_t *, void *arg), @@ -136,131 +393,175 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls, tor_assert(tls); (void)cb; (void)arg; - // XXXX; + + /* We don't support renegotiation-based TLS with NSS. */ } void -tor_tls_free_(tor_tls_t *tls) +tor_tls_impl_free_(tor_tls_impl_t *tls) { - (void)tls; - // XXXX + // XXXX This will close the underlying fd, which our OpenSSL version does + // not do! + + PR_Close(tls); } int tor_tls_peer_has_cert(tor_tls_t *tls) { - (void)tls; - // XXXX - return -1; + CERTCertificate *cert = SSL_PeerCertificate(tls->ssl); + int result = (cert != NULL); + CERT_DestroyCertificate(cert); + return result; } + MOCK_IMPL(tor_x509_cert_t *, tor_tls_get_peer_cert,(tor_tls_t *tls)) { - tor_assert(tls); - // XXXX - return NULL; + CERTCertificate *cert = SSL_PeerCertificate(tls->ssl); + if (cert) + return tor_x509_cert_new(cert); + else + return NULL; } + MOCK_IMPL(tor_x509_cert_t *, tor_tls_get_own_cert,(tor_tls_t *tls)) { tor_assert(tls); - // XXXX - return NULL; -} -int -tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity) -{ - tor_assert(tls); - tor_assert(identity); - (void)severity; - // XXXX - return -1; -} -int -tor_tls_check_lifetime(int severity, - tor_tls_t *tls, time_t now, - int past_tolerance, - int future_tolerance) -{ - tor_assert(tls); - (void)severity; - (void)now; - (void)past_tolerance; - (void)future_tolerance; - // XXXX - return -1; + CERTCertificate *cert = SSL_LocalCertificate(tls->ssl); + if (cert) + return tor_x509_cert_new(cert); + else + return NULL; } + MOCK_IMPL(int, tor_tls_read, (tor_tls_t *tls, char *cp, size_t len)) { tor_assert(tls); tor_assert(cp); - (void)len; - // XXXX - return -1; + tor_assert(len < INT_MAX); + + PRInt32 rv = PR_Read(tls->ssl, cp, (int)len); + // log_debug(LD_NET, "PR_Read(%zu) returned %d", n, (int)rv); + if (rv > 0) { + tls->n_read_since_last_check += rv; + return rv; + } + if (rv == 0) + return TOR_TLS_CLOSE; + PRErrorCode err = PORT_GetError(); + if (err == PR_WOULD_BLOCK_ERROR) { + return TOR_TLS_WANTREAD; // XXXX ???? + } else { + crypto_nss_log_errors(LOG_NOTICE, "reading"); // XXXX + return TOR_TLS_ERROR_MISC; // ???? + } } + int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n) { tor_assert(tls); - tor_assert(cp); - (void)n; - // XXXX - return -1; + tor_assert(cp || n == 0); + tor_assert(n < INT_MAX); + + PRInt32 rv = PR_Write(tls->ssl, cp, (int)n); + // log_debug(LD_NET, "PR_Write(%zu) returned %d", n, (int)rv); + if (rv > 0) { + tls->n_written_since_last_check += rv; + return rv; + } + if (rv == 0) + return TOR_TLS_ERROR_MISC; + PRErrorCode err = PORT_GetError(); + + if (err == PR_WOULD_BLOCK_ERROR) { + return TOR_TLS_WANTWRITE; // XXXX ???? + } else { + crypto_nss_log_errors(LOG_NOTICE, "writing"); // XXXX + return TOR_TLS_ERROR_MISC; // ???? + } } + int tor_tls_handshake(tor_tls_t *tls) { tor_assert(tls); - // XXXX - return -1; + tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE); + + SECStatus s = SSL_ForceHandshake(tls->ssl); + if (s == SECSuccess) { + tls->state = TOR_TLS_ST_OPEN; + log_debug(LD_NET, "SSL handshake is supposedly complete."); + return tor_tls_finish_handshake(tls); + } + if (PORT_GetError() == PR_WOULD_BLOCK_ERROR) + return TOR_TLS_WANTREAD; /* XXXX What about wantwrite? */ + + return TOR_TLS_ERROR_MISC; // XXXX } + int tor_tls_finish_handshake(tor_tls_t *tls) { tor_assert(tls); - // XXXX - return -1; + // We don't need to do any of the weird handshake nonsense stuff on NSS, + // since we only support recent handshakes. + return TOR_TLS_DONE; } + void tor_tls_unblock_renegotiation(tor_tls_t *tls) { tor_assert(tls); - // XXXX + /* We don't support renegotiation with NSS. */ } + void tor_tls_block_renegotiation(tor_tls_t *tls) { tor_assert(tls); - // XXXX + /* We don't support renegotiation with NSS. */ } + void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) { tor_assert(tls); - // XXXX + /* We don't support renegotiation with NSS. */ } + int tor_tls_shutdown(tor_tls_t *tls) { tor_assert(tls); - // XXXX + /* XXXX This is not actually used, so I'm not implementing it. We can + * XXXX remove this function entirely someday. */ return -1; } + int tor_tls_get_pending_bytes(tor_tls_t *tls) { tor_assert(tls); - // XXXX - return -1; + int n = SSL_DataPending(tls->ssl); + if (n < 0) { + crypto_nss_log_errors(LOG_WARN, "Looking up pending bytes"); + return 0; + } + return (int)n; } + size_t tor_tls_get_forced_write_size(tor_tls_t *tls) { tor_assert(tls); - // XXXX + /* NSS doesn't have the same "forced write" restriction as openssl. */ return 0; } + void tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written) @@ -268,7 +569,13 @@ tor_tls_get_n_raw_bytes(tor_tls_t *tls, tor_assert(tls); tor_assert(n_read); tor_assert(n_written); - // XXXX + /* XXXX We don't curently have a way to measure this information correctly + * in NSS; we could do that with a PRIO layer, but it'll take a little + * coding. For now, we just track the number of bytes sent _in_ the TLS + * stream. Doing this will make our rate-limiting slightly inaccurate. */ + *n_read = tls->n_read_since_last_check; + *n_written = tls->n_written_since_last_check; + tls->n_read_since_last_check = tls->n_written_since_last_check = 0; } int @@ -281,54 +588,70 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls, tor_assert(rbuf_bytes); tor_assert(wbuf_capacity); tor_assert(wbuf_bytes); - // XXXX + + /* This is an acceptable way to say "we can't measure this." */ return -1; } + MOCK_IMPL(double, tls_get_write_overhead_ratio, (void)) { - // XXXX - return 0.0; + /* XXX We don't currently have a way to measure this in NSS; we could do that + * XXX with a PRIO layer, but it'll take a little coding. */ + return 0.95; } int tor_tls_used_v1_handshake(tor_tls_t *tls) { tor_assert(tls); - // XXXX - return -1; -} -int -tor_tls_get_num_server_handshakes(tor_tls_t *tls) -{ - tor_assert(tls); - // XXXX - return -1; + /* We don't support or allow the V1 handshake with NSS. + */ + return 0; } + int tor_tls_server_got_renegotiate(tor_tls_t *tls) { tor_assert(tls); - // XXXX - return -1; + return 0; /* We don't support renegotiation with NSS */ } + MOCK_IMPL(int, tor_tls_cert_matches_key,(const tor_tls_t *tls, const struct tor_x509_cert_t *cert)) { tor_assert(tls); tor_assert(cert); - // XXXX - return 0; + int rv = 0; + + CERTCertificate *peercert = SSL_PeerCertificate(tls->ssl); + if (!peercert) + goto done; + CERTSubjectPublicKeyInfo *peer_info = &peercert->subjectPublicKeyInfo; + CERTSubjectPublicKeyInfo *cert_info = &cert->cert->subjectPublicKeyInfo; + rv = SECOID_CompareAlgorithmID(&peer_info->algorithm, + &cert_info->algorithm) == 0 && + SECITEM_ItemsAreEqual(&peer_info->subjectPublicKey, + &cert_info->subjectPublicKey); + + done: + if (peercert) + CERT_DestroyCertificate(peercert); + return rv; } + MOCK_IMPL(int, tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out)) { tor_assert(tls); tor_assert(secrets_out); - // XXXX + + /* There's no way to get this information out of NSS. */ + return -1; } + MOCK_IMPL(int, tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out, const uint8_t *context, @@ -339,42 +662,72 @@ tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out, tor_assert(secrets_out); tor_assert(context); tor_assert(label); - (void)context_len; - // XXXX - return -1; -} + tor_assert(strlen(label) <= UINT_MAX); + tor_assert(context_len <= UINT_MAX); -void -check_no_tls_errors_(const char *fname, int line) -{ - (void)fname; - (void)line; - // XXXX -} -void -tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, - int severity, int domain, const char *doing) -{ - tor_assert(tls); - (void)err; - (void)severity; - (void)domain; - (void)doing; - // XXXX + SECStatus s; + s = SSL_ExportKeyingMaterial(tls->ssl, + label, (unsigned)strlen(label), + PR_TRUE, context, (unsigned)context_len, + secrets_out, DIGEST256_LEN); + + return (s == SECSuccess) ? 0 : -1; } const char * tor_tls_get_ciphersuite_name(tor_tls_t *tls) { tor_assert(tls); - // XXXX - return NULL; + + SSLChannelInfo channel_info; + SSLCipherSuiteInfo cipher_info; + + memset(&channel_info, 0, sizeof(channel_info)); + memset(&cipher_info, 0, sizeof(cipher_info)); + + SECStatus s = SSL_GetChannelInfo(tls->ssl, + &channel_info, sizeof(channel_info)); + if (s != SECSuccess) + return NULL; + + s = SSL_GetCipherSuiteInfo(channel_info.cipherSuite, + &cipher_info, sizeof(cipher_info)); + if (s != SECSuccess) + return NULL; + + return cipher_info.cipherSuiteName; } +/** The group we should use for ecdhe when none was selected. */ +#define SEC_OID_TOR_DEFAULT_ECDHE_GROUP SEC_OID_ANSIX962_EC_PRIME256V1 + int evaluate_ecgroup_for_tls(const char *ecgroup) { - (void)ecgroup; - // XXXX - return -1; + SECOidTag tag; + + if (!ecgroup) + tag = SEC_OID_TOR_DEFAULT_ECDHE_GROUP; + else if (!strcasecmp(ecgroup, "P256")) + tag = SEC_OID_ANSIX962_EC_PRIME256V1; + else if (!strcasecmp(ecgroup, "P224")) + tag = SEC_OID_SECG_EC_SECP224R1; + else + return 0; + + /* I don't think we need any additional tests here for NSS */ + (void) tag; + + return 1; +} + +static SECStatus +always_accept_cert_cb(void *arg, PRFileDesc *ssl, PRBool checkSig, + PRBool isServer) +{ + (void)arg; + (void)ssl; + (void)checkSig; + (void)isServer; + return SECSuccess; } diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index fb6328bcd0..c4e9e7770f 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -244,26 +244,6 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) } } -/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error - * code. */ -int -tor_errno_to_tls_error(int e) -{ - switch (e) { - case SOCK_ERRNO(ECONNRESET): // most common - return TOR_TLS_ERROR_CONNRESET; - case SOCK_ERRNO(ETIMEDOUT): - return TOR_TLS_ERROR_TIMEOUT; - case SOCK_ERRNO(EHOSTUNREACH): - case SOCK_ERRNO(ENETUNREACH): - return TOR_TLS_ERROR_NO_ROUTE; - case SOCK_ERRNO(ECONNREFUSED): - return TOR_TLS_ERROR_CONNREFUSED; // least common - default: - return TOR_TLS_ERROR_MISC; - } -} - #define CATCH_SYSCALL 1 #define CATCH_ZERO 2 @@ -952,8 +932,6 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) /* Check whether we're watching for renegotiates. If so, this is one! */ if (tls->negotiated_callback) tls->got_renegotiate = 1; - if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/ - ++tls->server_handshake_count; } else { log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); return; @@ -1167,30 +1145,13 @@ tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) #endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */ } -/** Release resources associated with a TLS object. Does not close the - * underlying file descriptor. - */ void -tor_tls_free_(tor_tls_t *tls) +tor_tls_impl_free_(tor_tls_impl_t *ssl) { - if (!tls) - return; - tor_assert(tls->ssl); - { - size_t r,w; - tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */ - } #ifdef SSL_set_tlsext_host_name - SSL_set_tlsext_host_name(tls->ssl, NULL); + SSL_set_tlsext_host_name(ssl, NULL); #endif - SSL_free(tls->ssl); - tls->ssl = NULL; - tls->negotiated_callback = NULL; - if (tls->context) - tor_tls_context_decref(tls->context); - tor_free(tls->address); - tls->magic = 0x99999999; - tor_free(tls); + SSL_free(ssl); } /** Underlying function for TLS reading. Reads up to len @@ -1509,90 +1470,6 @@ try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, *id_cert_out = id_cert; } -/** If the provided tls connection is authenticated and has a - * certificate chain that is currently valid and signed, then set - * *identity_key to the identity certificate's key and return - * 0. Else, return -1 and log complaints with log-level severity. - */ -int -tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key) -{ - X509 *cert = NULL, *id_cert = NULL; - EVP_PKEY *id_pkey = NULL; - RSA *rsa; - int r = -1; - - check_no_tls_errors(); - *identity_key = NULL; - - try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert); - if (!cert) - goto done; - if (!id_cert) { - log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found"); - goto done; - } - tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate"); - - if (!(id_pkey = X509_get_pubkey(id_cert)) || - X509_verify(cert, id_pkey) <= 0) { - log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0"); - tls_log_errors(tls, severity, LD_HANDSHAKE, "verifying certificate"); - goto done; - } - - rsa = EVP_PKEY_get1_RSA(id_pkey); - if (!rsa) - goto done; - *identity_key = crypto_new_pk_from_openssl_rsa_(rsa); - - r = 0; - - done: - if (cert) - X509_free(cert); - if (id_pkey) - EVP_PKEY_free(id_pkey); - - /* This should never get invoked, but let's make sure in case OpenSSL - * acts unexpectedly. */ - tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "finishing tor_tls_verify"); - - return r; -} - -/** Check whether the certificate set on the connection tls is expired - * give or take past_tolerance seconds, or not-yet-valid give or take - * future_tolerance seconds. Return 0 for valid, -1 for failure. - * - * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime. - */ -int -tor_tls_check_lifetime(int severity, tor_tls_t *tls, - time_t now, - int past_tolerance, int future_tolerance) -{ - X509 *cert; - int r = -1; - - if (!(cert = SSL_get_peer_certificate(tls->ssl))) - goto done; - - if (tor_x509_check_cert_lifetime_internal(severity, cert, now, - past_tolerance, - future_tolerance) < 0) - goto done; - - r = 0; - done: - if (cert) - X509_free(cert); - /* Not expected to get invoked */ - tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime"); - - return r; -} - /** Return the number of bytes available for reading from tls. */ int @@ -1690,14 +1567,6 @@ tor_tls_used_v1_handshake(tor_tls_t *tls) return ! tls->wasV2Handshake; } -/** Return the number of server handshakes that we've noticed doing on - * tls. */ -int -tor_tls_get_num_server_handshakes(tor_tls_t *tls) -{ - return tls->server_handshake_count; -} - /** Return true iff the server TLS connection tls got the renegotiation * request it was waiting for. */ int diff --git a/src/lib/tls/tortls_st.h b/src/lib/tls/tortls_st.h index 897be497ef..a1b59a37af 100644 --- a/src/lib/tls/tortls_st.h +++ b/src/lib/tls/tortls_st.h @@ -6,6 +6,8 @@ #ifndef TOR_TORTLS_ST_H #define TOR_TORTLS_ST_H +#include "lib/net/socket.h" + #define TOR_TLS_MAGIC 0x71571571 typedef enum { @@ -17,7 +19,7 @@ typedef enum { struct tor_tls_context_t { int refcnt; - struct ssl_ctx_st *ctx; + tor_tls_context_impl_t *ctx; struct tor_x509_cert_t *my_link_cert; struct tor_x509_cert_t *my_id_cert; struct tor_x509_cert_t *my_auth_cert; @@ -31,8 +33,9 @@ struct tor_tls_context_t { struct tor_tls_t { uint32_t magic; tor_tls_context_t *context; /** A link to the context object for this tls. */ - struct ssl_st *ssl; /**< An OpenSSL SSL object. */ - int socket; /**< The underlying file descriptor for this TLS connection. */ + tor_tls_impl_t *ssl; /**< An OpenSSL SSL object or NSS PRFileDesc. */ + tor_socket_t socket; /**< The underlying file descriptor for this TLS + * connection. */ char *address; /**< An address to log when describing this connection. */ tor_tls_state_bitfield_t state : 3; /**< The current SSL state, * depending on which operations @@ -45,11 +48,10 @@ struct tor_tls_t { * one certificate). */ /** True iff we should call negotiated_callback when we're done reading. */ unsigned int got_renegotiate:1; +#ifdef ENABLE_OPENSSL /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't * called that function yet. */ int8_t client_cipher_list_type; - /** Incremented every time we start the server side of a handshake. */ - uint8_t server_handshake_count; size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last * time. */ /** Last values retrieved from BIO_number_read()/write(); see @@ -62,6 +64,11 @@ struct tor_tls_t { void (*negotiated_callback)(tor_tls_t *tls, void *arg); /** Argument to pass to negotiated_callback. */ void *callback_arg; +#endif +#ifdef ENABLE_NSS + size_t n_read_since_last_check; + size_t n_written_since_last_check; +#endif }; #endif diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index eedf0dd3c9..0e4b5afafa 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -72,6 +72,7 @@ test_tortls_err_to_string(void *data) (void)1; } +#ifdef ENABLE_OPENSSL static int mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert) { @@ -105,6 +106,7 @@ test_tortls_tor_tls_get_error(void *data) crypto_pk_free(key2); tor_tls_free(tls); } +#endif static void test_tortls_x509_cert_get_id_digests(void *ignored) @@ -165,6 +167,7 @@ test_tortls_get_my_certs(void *ignored) (void)1; } +#ifdef ENABLE_OPENSSL static void test_tortls_get_forced_write_size(void *ignored) { @@ -203,23 +206,6 @@ test_tortls_used_v1_handshake(void *ignored) tor_free(tls); } -static void -test_tortls_get_num_server_handshakes(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - - tls->server_handshake_count = 3; - ret = tor_tls_get_num_server_handshakes(tls); - tt_int_op(ret, OP_EQ, 3); - - done: - tor_free(tls); -} - static void test_tortls_server_got_renegotiate(void *ignored) { @@ -236,6 +222,7 @@ test_tortls_server_got_renegotiate(void *ignored) done: tor_free(tls); } +#endif static void test_tortls_evaluate_ecgroup_for_tls(void *ignored) @@ -266,13 +253,14 @@ test_tortls_evaluate_ecgroup_for_tls(void *ignored) struct testcase_t tortls_tests[] = { LOCAL_TEST_CASE(errno_to_tls_error, 0), LOCAL_TEST_CASE(err_to_string, 0), - LOCAL_TEST_CASE(tor_tls_get_error, 0), LOCAL_TEST_CASE(x509_cert_get_id_digests, 0), LOCAL_TEST_CASE(get_my_certs, TT_FORK), +#ifdef ENABLE_OPENSSL + LOCAL_TEST_CASE(tor_tls_get_error, 0), LOCAL_TEST_CASE(get_forced_write_size, 0), LOCAL_TEST_CASE(used_v1_handshake, TT_FORK), - LOCAL_TEST_CASE(get_num_server_handshakes, 0), LOCAL_TEST_CASE(server_got_renegotiate, 0), +#endif LOCAL_TEST_CASE(evaluate_ecgroup_for_tls, 0), END_OF_TESTCASES }; From dd04fc35c665976f9fc9ff586cbf7fe34d9cc241 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 23 Aug 2018 09:32:20 -0400 Subject: [PATCH 55/76] Remove tor_tls_shutdown() This function was supposed to implement a half-duplex mode for our TLS connections. However, nothing in Tor actually uses it (besides some unit tests), and the implementation looks really questionable to me. It's probably best to remove it. We can add a tested one later if we need one in the future. --- src/lib/tls/tortls.h | 1 - src/lib/tls/tortls_nss.c | 9 ---- src/lib/tls/tortls_openssl.c | 63 ---------------------- src/test/test_tortls_openssl.c | 96 ---------------------------------- 4 files changed, 169 deletions(-) diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h index 7bbb42b2fd..a8bc7370a7 100644 --- a/src/lib/tls/tortls.h +++ b/src/lib/tls/tortls.h @@ -111,7 +111,6 @@ int tor_tls_finish_handshake(tor_tls_t *tls); void tor_tls_unblock_renegotiation(tor_tls_t *tls); void tor_tls_block_renegotiation(tor_tls_t *tls); void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls); -int tor_tls_shutdown(tor_tls_t *tls); int tor_tls_get_pending_bytes(tor_tls_t *tls); size_t tor_tls_get_forced_write_size(tor_tls_t *tls); diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index d2b81ad084..671c018471 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -533,15 +533,6 @@ tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) /* We don't support renegotiation with NSS. */ } -int -tor_tls_shutdown(tor_tls_t *tls) -{ - tor_assert(tls); - /* XXXX This is not actually used, so I'm not implementing it. We can - * XXXX remove this function entirely someday. */ - return -1; -} - int tor_tls_get_pending_bytes(tor_tls_t *tls) { diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index c4e9e7770f..a9bab67a05 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -1323,69 +1323,6 @@ tor_tls_finish_handshake(tor_tls_t *tls) return r; } -/** Shut down an open tls connection tls. When finished, returns - * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, - * or TOR_TLS_WANTWRITE. - */ -int -tor_tls_shutdown(tor_tls_t *tls) -{ - int r, err; - char buf[128]; - tor_assert(tls); - tor_assert(tls->ssl); - check_no_tls_errors(); - - while (1) { - if (tls->state == TOR_TLS_ST_SENTCLOSE) { - /* If we've already called shutdown once to send a close message, - * we read until the other side has closed too. - */ - do { - r = SSL_read(tls->ssl, buf, 128); - } while (r>0); - err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down", - LOG_INFO, LD_NET); - if (err == TOR_TLS_ZERORETURN_) { - tls->state = TOR_TLS_ST_GOTCLOSE; - /* fall through... */ - } else { - return err; - } - } - - r = SSL_shutdown(tls->ssl); - if (r == 1) { - /* If shutdown returns 1, the connection is entirely closed. */ - tls->state = TOR_TLS_ST_CLOSED; - return TOR_TLS_DONE; - } - err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down", - LOG_INFO, LD_NET); - if (err == TOR_TLS_SYSCALL_) { - /* The underlying TCP connection closed while we were shutting down. */ - tls->state = TOR_TLS_ST_CLOSED; - return TOR_TLS_DONE; - } else if (err == TOR_TLS_ZERORETURN_) { - /* The TLS connection says that it sent a shutdown record, but - * isn't done shutting down yet. Make sure that this hasn't - * happened before, then go back to the start of the function - * and try to read. - */ - if (tls->state == TOR_TLS_ST_GOTCLOSE || - tls->state == TOR_TLS_ST_SENTCLOSE) { - log_warn(LD_NET, - "TLS returned \"half-closed\" value while already half-closed"); - return TOR_TLS_ERROR_MISC; - } - tls->state = TOR_TLS_ST_SENTCLOSE; - /* fall through ... */ - } else { - return err; - } - } /* end loop */ -} - /** Return true iff this TLS connection is authenticated. */ int diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c index 12a05b3033..b7e28f3768 100644 --- a/src/test/test_tortls_openssl.c +++ b/src/test/test_tortls_openssl.c @@ -1710,102 +1710,6 @@ dummy_handshake_func(SSL *s) return 1; } -static void -test_tortls_shutdown(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - SSL_METHOD *method = give_me_a_test_method(); - setup_capture_of_logs(LOG_WARN); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->method = method; - method->ssl_read = fixed_ssl_read; - method->ssl_shutdown = fixed_ssl_shutdown; - - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, -9); - - tls->state = TOR_TLS_ST_SENTCLOSE; - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = -1; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, -9); - -#ifndef LIBRESSL_VERSION_NUMBER - tls->ssl->handshake_func = dummy_handshake_func; - - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = 42; - fixed_ssl_read_result[2] = 0; - fixed_ssl_shutdown_result = 1; - ERR_clear_error(); - tls->ssl->version = SSL2_VERSION; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_DONE); - tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED); - - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = 42; - fixed_ssl_read_result[2] = 0; - fixed_ssl_shutdown_result = 0; - ERR_clear_error(); - tls->ssl->version = 0; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_DONE); - tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED); - - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = 42; - fixed_ssl_read_result[2] = 0; - fixed_ssl_shutdown_result = 0; - ERR_clear_error(); - tls->ssl->version = 0; - method->ssl_shutdown = setting_version_ssl_shutdown; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); - - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = 42; - fixed_ssl_read_result[2] = 0; - fixed_ssl_shutdown_result = 0; - fixed_tls = tls; - fixed_ssl_state_to_set = TOR_TLS_ST_GOTCLOSE; - ERR_clear_error(); - tls->ssl->version = 0; - method->ssl_shutdown = setting_version_and_state_ssl_shutdown; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); - - fixed_ssl_read_result_index = 0; - fixed_ssl_read_result[0] = 10; - fixed_ssl_read_result[1] = 42; - fixed_ssl_read_result[2] = 0; - fixed_ssl_read_result[3] = -1; - fixed_ssl_shutdown_result = 0; - fixed_tls = tls; - fixed_ssl_state_to_set = 0; - ERR_clear_error(); - tls->ssl->version = 0; - method->ssl_shutdown = setting_version_and_state_ssl_shutdown; - ret = tor_tls_shutdown(tls); - tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC); -#endif /* !defined(LIBRESSL_VERSION_NUMBER) */ - - done: - teardown_capture_of_logs(); - tor_free(method); - tor_free(tls->ssl); - tor_free(tls); -} - static int negotiated_callback_called; static void From 52d5f4da12cf4a9647a896395209121cbf9483c4 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 23 Aug 2018 10:13:32 -0400 Subject: [PATCH 56/76] Avoid spurious error logs when using NSS The tls_log_errors() function now behaves differently for NSS than it did for OpenSSL, so we need to tweak it a bit. --- src/lib/tls/tortls.c | 13 +++++++++++-- src/lib/tls/tortls_nss.c | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index cc9738599e..edf421b4db 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -189,6 +189,9 @@ tor_tls_context_init(unsigned flags, if (old_ctx != NULL) { tor_tls_context_decref(old_ctx); } + } else { + tls_log_errors(NULL, LOG_WARN, LD_CRYPTO, + "constructing a TLS context"); } } else { if (server_identity != NULL) { @@ -197,6 +200,9 @@ tor_tls_context_init(unsigned flags, key_lifetime, flags, 0); + if (rv1 < 0) + tls_log_errors(NULL, LOG_WARN, LD_CRYPTO, + "constructing a server TLS context"); } else { tor_tls_context_t *old_ctx = server_tls_context; server_tls_context = NULL; @@ -211,9 +217,11 @@ tor_tls_context_init(unsigned flags, key_lifetime, flags, 1); + if (rv2 < 0) + tls_log_errors(NULL, LOG_WARN, LD_CRYPTO, + "constructing a client TLS context"); } - tls_log_errors(NULL, LOG_WARN, LD_CRYPTO, "constructing a TLS context"); return MIN(rv1, rv2); } @@ -451,8 +459,9 @@ tor_tls_check_lifetime(int severity, tor_tls_t *tls, r = 0; done: tor_x509_cert_free(cert); - /* Not expected to get invoked */ +#ifdef ENABLE_OPENSSL tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime"); +#endif return r; } diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index 671c018471..40a98dd87e 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -323,8 +323,10 @@ void tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) { - /* XXXX This implementation isn't right for NSS -- it logs the last error - whether anything actually failed or not. */ + /* This implementation is a little different for NSS than it is for OpenSSL + -- it logs the last error whether anything actually failed or not. So we + have to only call it when something has gone wrong and we have a real + error to report. */ (void)tls; PRErrorCode code = PORT_GetError(); From c18967d6c80f6a40acc78556490a4f40bc0e7003 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 23 Aug 2018 11:45:34 -0400 Subject: [PATCH 57/76] changes file for our NSS support --- changes/NSS | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changes/NSS diff --git a/changes/NSS b/changes/NSS new file mode 100644 index 0000000000..fc13867b40 --- /dev/null +++ b/changes/NSS @@ -0,0 +1,7 @@ + o Major features (portability, cryptography, experimental, TLS): + - Tor now has the option to compile with the NSS library instead of + OpenSSL. This feature is experimental, and we expect that bugs may + remain. It is mainly intended for environments where Tor's performance + is not CPU-bound, and where NSS is already known to be installed. + To try it out, configure Tor with the --enable-nss flag. + Closes ticket 26631. From b5fddbd24144a94580e50886cd07a82968a1b86c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 23 Aug 2018 13:02:12 -0400 Subject: [PATCH 58/76] Avoid double-close on TCP sockets under NSS. --- src/core/mainloop/connection.c | 25 +++++++++++++++++-------- src/lib/tls/tortls_openssl.c | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index ed789d5208..91ae410749 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -449,6 +449,20 @@ connection_new(int type, int socket_family) } } +static void +connection_close_and_invalidate_socket(connection_t *conn) +{ + if (connection_speaks_cells(conn)) { + or_connection_t *or_conn = TO_OR_CONN(conn); + tor_tls_free(or_conn->tls); + or_conn->tls = NULL; + or_conn->base_.s = TOR_INVALID_SOCKET; + } else { + tor_close_socket(conn->s); + conn->s = TOR_INVALID_SOCKET; + } +} + /** Initializes conn. (you must call connection_add() to link it into the main * array). * @@ -614,9 +628,8 @@ connection_free_minimal(connection_t *conn) tor_free(conn->address); if (connection_speaks_cells(conn)) { + connection_close_and_invalidate_socket(conn); or_connection_t *or_conn = TO_OR_CONN(conn); - tor_tls_free(or_conn->tls); - or_conn->tls = NULL; or_handshake_state_free(or_conn->handshake_state); or_conn->handshake_state = NULL; tor_free(or_conn->nickname); @@ -692,9 +705,7 @@ connection_free_minimal(connection_t *conn) } if (SOCKET_OK(conn->s)) { - log_debug(LD_NET,"closing fd %d.",(int)conn->s); - tor_close_socket(conn->s); - conn->s = TOR_INVALID_SOCKET; + connection_close_and_invalidate_socket(conn); } if (conn->type == CONN_TYPE_OR && @@ -820,9 +831,7 @@ connection_close_immediate(connection_t *conn) conn->read_blocked_on_bw = 0; conn->write_blocked_on_bw = 0; - if (SOCKET_OK(conn->s)) - tor_close_socket(conn->s); - conn->s = TOR_INVALID_SOCKET; + connection_close_and_invalidate_socket(conn); if (conn->linked) conn->linked_conn_is_closed = 1; if (conn->outbuf) diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index a9bab67a05..e455693dfd 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -1039,7 +1039,7 @@ tor_tls_new(int sock, int isServer) goto err; } result->socket = sock; - bio = BIO_new_socket(sock, BIO_NOCLOSE); + bio = BIO_new_socket(sock, 0); if (! bio) { tls_log_errors(NULL, LOG_WARN, LD_NET, "opening BIO"); #ifdef SSL_set_tlsext_host_name From 02086a216f15fd8c45e603a8d9bab482f60753f4 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 23 Aug 2018 13:05:38 -0400 Subject: [PATCH 59/76] Remove tor_x509_get_cert_impl as unneeded. --- src/lib/tls/tortls_openssl.c | 2 +- src/lib/tls/x509.c | 8 -------- src/lib/tls/x509_internal.h | 2 -- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index e455693dfd..d10e50a975 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -480,7 +480,7 @@ tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert)) if (!peercert) return 0; link_key = X509_get_pubkey(peercert); - cert_key = X509_get_pubkey((X509 *)tor_x509_cert_get_impl(cert)); + cert_key = X509_get_pubkey(cert->cert); result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1; diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index d2270f9108..cff1c1302c 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -52,14 +52,6 @@ tor_tls_pick_certificate_lifetime(time_t now, *end_time_out = end_time; } -/** Return the underlying implementation for cert */ -const tor_x509_cert_impl_t * -tor_x509_cert_get_impl(const tor_x509_cert_t *cert) -{ - tor_assert(cert); - return cert->cert; -} - /** Return a set of digests for the public key in cert, or NULL if this * cert's public key is not one we know how to take the digest of. */ const common_digests_t * diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h index 4b49f1dec2..0e186ec849 100644 --- a/src/lib/tls/x509_internal.h +++ b/src/lib/tls/x509_internal.h @@ -33,8 +33,6 @@ MOCK_DECL(tor_x509_cert_impl_t *, tor_tls_create_certificate, unsigned int cert_lifetime)); MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new, (tor_x509_cert_impl_t *x509_cert)); -const tor_x509_cert_impl_t *tor_x509_cert_get_impl( - const tor_x509_cert_t *cert); int tor_x509_check_cert_lifetime_internal(int severity, const tor_x509_cert_impl_t *cert, From 7163389b550a36fa017f700713405fc3c89dc234 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 23 Aug 2018 14:03:00 -0400 Subject: [PATCH 60/76] Several unit tests to improve test coverage of x509*.c --- src/lib/crypt_ops/crypto_digest.c | 4 +- src/lib/crypt_ops/crypto_digest.h | 3 +- src/lib/tls/x509.c | 3 +- src/lib/tls/x509_nss.c | 4 +- src/lib/tls/x509_openssl.c | 8 +- src/test/include.am | 1 + src/test/test.c | 1 + src/test/test.h | 1 + src/test/test_x509.c | 204 ++++++++++++++++++++++++++++++ 9 files changed, 221 insertions(+), 8 deletions(-) create mode 100644 src/test/test_x509.c diff --git a/src/lib/crypt_ops/crypto_digest.c b/src/lib/crypt_ops/crypto_digest.c index 2bc5f08342..77cf18dca9 100644 --- a/src/lib/crypt_ops/crypto_digest.c +++ b/src/lib/crypt_ops/crypto_digest.c @@ -116,8 +116,8 @@ library_supports_digest(digest_algorithm_t alg) * m. Write the DIGEST_LEN byte result into digest. * Return 0 on success, -1 on failure. */ -int -crypto_digest(char *digest, const char *m, size_t len) +MOCK_IMPL(int, +crypto_digest,(char *digest, const char *m, size_t len)) { tor_assert(m); tor_assert(digest); diff --git a/src/lib/crypt_ops/crypto_digest.h b/src/lib/crypt_ops/crypto_digest.h index 59713d2b9f..204f1aaff3 100644 --- a/src/lib/crypt_ops/crypto_digest.h +++ b/src/lib/crypt_ops/crypto_digest.h @@ -16,6 +16,7 @@ #include "lib/cc/torint.h" #include "lib/defs/digest_sizes.h" #include "lib/malloc/malloc.h" +#include "lib/testsupport/testsupport.h" /** Length of a sha1 message digest when encoded in base32 with trailing = * signs removed. */ @@ -75,7 +76,7 @@ typedef struct crypto_xof_t crypto_xof_t; struct smartlist_t; /* SHA-1 and other digests */ -int crypto_digest(char *digest, const char *m, size_t len); +MOCK_DECL(int, crypto_digest,(char *digest, const char *m, size_t len)); int crypto_digest256(char *digest, const char *m, size_t len, digest_algorithm_t algorithm); int crypto_digest512(char *digest, const char *m, size_t len, diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index cff1c1302c..c88298b6cf 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -118,6 +118,7 @@ tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert)) crypto_pk_t *pk = tor_tls_cert_get_key(cert); if (pk) { if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) { + log_warn(LD_CRYPTO, "unable to compute digests of certificate key"); crypto_pk_free(pk); goto err; } @@ -128,10 +129,8 @@ tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert)) return cert; err: - /* LCOV_EXCL_START for the same reason as the exclusion above */ tor_free(cert); log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate."); tor_x509_cert_impl_free_(x509_cert); return NULL; - /* LCOV_EXCL_STOP */ } diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c index 35b3d2542b..68aebbb69a 100644 --- a/src/lib/tls/x509_nss.c +++ b/src/lib/tls/x509_nss.c @@ -62,9 +62,11 @@ tor_tls_create_certificate_internal(crypto_pk_t *rsa, validity = CERT_CreateValidity(((PRTime)start_time) * PRTIME_PER_SEC, ((PRTime)end_time) * PRTIME_PER_SEC); - if (! validity) { + if (BUG(! validity)) { + /* LCOV_EXCL_START */ crypto_nss_log_errors(LOG_WARN, "creating a validity object"); goto err; + /* LCOV_EXCL_STOP */ } unsigned long serial_number; diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c index 28a30b66e1..f315b88f36 100644 --- a/src/lib/tls/x509_openssl.c +++ b/src/lib/tls/x509_openssl.c @@ -327,11 +327,15 @@ tor_tls_cert_is_valid(int severity, if (check_rsa_1024 && cert_key) { RSA *rsa = EVP_PKEY_get1_RSA(cert_key); #ifdef OPENSSL_1_1_API - if (rsa && RSA_bits(rsa) == 1024) + if (rsa && RSA_bits(rsa) == 1024) { #else - if (rsa && BN_num_bits(rsa->n) == 1024) + if (rsa && BN_num_bits(rsa->n) == 1024) { #endif key_ok = 1; + } else { + log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is not RSA1024."); + } + if (rsa) RSA_free(rsa); } else if (cert_key) { diff --git a/src/test/include.am b/src/test/include.am index 05149b8654..69680f9691 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -178,6 +178,7 @@ src_test_test_SOURCES += \ src/test/test_util_format.c \ src/test/test_util_process.c \ src/test/test_voting_schedule.c \ + src/test/test_x509.c \ src/test/test_helpers.c \ src/test/test_dns.c \ src/test/testing_common.c \ diff --git a/src/test/test.c b/src/test/test.c index 9623443057..df586fc76b 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -924,6 +924,7 @@ struct testgroup_t testgroups[] = { #ifndef ENABLE_NSS { "tortls/openssl/", tortls_openssl_tests }, #endif + { "tortls/x509/", x509_tests }, { "util/", util_tests }, { "util/format/", util_format_tests }, { "util/logging/", logging_tests }, diff --git a/src/test/test.h b/src/test/test.h index 94aa9443d6..a46fedf3e0 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -265,6 +265,7 @@ extern struct testcase_t voting_schedule_tests[]; extern struct testcase_t dns_tests[]; extern struct testcase_t handle_tests[]; extern struct testcase_t sr_tests[]; +extern struct testcase_t x509_tests[]; extern struct testcase_t slow_crypto_tests[]; extern struct testcase_t slow_util_tests[]; diff --git a/src/test/test_x509.c b/src/test/test_x509.c new file mode 100644 index 0000000000..9163977bd8 --- /dev/null +++ b/src/test/test_x509.c @@ -0,0 +1,204 @@ +/* Copyright (c) 2010-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define TOR_X509_PRIVATE +#include "orconfig.h" + +#ifdef _WIN32 +#include +#endif +#include +#include + +#include "lib/cc/compat_compiler.h" + +#include "core/or/or.h" +#include "lib/log/log.h" +#include "app/config/config.h" +#include "lib/tls/x509.h" +#include "lib/tls/x509_internal.h" +#include "app/config/or_state_st.h" + +#include "test/test.h" +#include "test/log_test_helpers.h" + +#include "tinytest.h" + +/* A mock replacement for crypto_digest that always fails. */ +static int +mock_failing_digest(char *digest, const char *m, size_t len) +{ + (void)digest; + (void)m; + (void)len; + return -1; +} + +static void +test_x509_cert_new_failing_digest(void *arg) +{ + (void)arg; + crypto_pk_t *pk1=NULL, *pk2=NULL; + tor_x509_cert_impl_t *impl = NULL; + tor_x509_cert_t *cert = NULL; + pk1 = pk_generate(0); + pk2 = pk_generate(1); + + impl = tor_tls_create_certificate(pk1, pk2, "hello", "world", 86400*100); + tt_assert(impl); + MOCK(crypto_digest, mock_failing_digest); + + setup_full_capture_of_logs(LOG_WARN); + cert = tor_x509_cert_new(impl); + tt_assert(!cert); + expect_log_msg_containing("Couldn't wrap encoded X509 certificate"); + expect_log_msg_containing("unable to compute digests of certificate key"); + + done: + crypto_pk_free(pk1); + crypto_pk_free(pk2); + if (impl) + tor_x509_cert_impl_free_(impl); + UNMOCK(crypto_digest); + teardown_capture_of_logs(); +} + +static tor_x509_cert_t * +cert_from_der64(const char *der64) +{ + size_t der64len = strlen(der64); + unsigned char *der = tor_malloc_zero(der64len); + int derlen; + tor_x509_cert_t *cert = NULL; + + derlen = base64_decode((char*)der, der64len, + der64, der64len); + if (derlen >= 0) + cert = tor_x509_cert_decode(der, derlen); + tor_free(der); + return cert; +} + +static void +test_x509_consume_ec_cert(void *arg) +{ + (void)arg; + /* This is a small self-signed EC certificate. */ + const char certificate[] = + "MIIBEzCBugIJAIdl5svgOZ0OMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMMB1Rlc3Rp\n" + "bmcwHhcNMTgwODIzMTcyMzI1WhcNMTkwODIzMTcyMzI1WjASMRAwDgYDVQQDDAdU\n" + "ZXN0aW5nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExMDpnRc0Btic3tIyCKNE\n" + "iNY4j4gzcaYzS2sTYRoVK3RAukG29Qg6/c8e8XcnsSquU4fItYxDRbi/3nhYk4CP\n" + "GDAKBggqhkjOPQQDAgNIADBFAiA0h1q03C2xlONUgAOonJLrlV1SUtMeKDxNsxsU\n" + "+FSPvQIhAM7kY9Tlt0ELmyMnORPp1VJieXn/qhL5VoxGxSedTbny\n"; + const time_t now = 1535045321; /* when I'm writing this test. */ + tor_x509_cert_t *cert = cert_from_der64(certificate); + tt_assert(cert); + + tt_ptr_op(NULL, OP_EQ, tor_tls_cert_get_key(cert)); + + /* It's a self-signed cert -- make sure it signed itself. */ + tt_assert(tor_tls_cert_is_valid(LOG_ERR, cert, cert, now, 0)); + + /* Make sure we detect its key as non-RSA1024 */ + setup_capture_of_logs(LOG_INFO); + tt_assert(! tor_tls_cert_is_valid(LOG_INFO, cert, cert, now, 1)); + expect_log_msg_containing("Key is not RSA1024"); + + done: + tor_x509_cert_free(cert); + teardown_capture_of_logs(); +} + +static void +test_x509_reject_tiny_keys(void *arg) +{ + (void)arg; + const char *certificates[] = { + /* Self-signed RSA512 */ + "MIIBXDCCAQYCCQDKikjJYZI5uDANBgkqhkiG9w0BAQsFADA1MRUwEwYDVQQHDAxE\n" + "ZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwHhcNMTgw\n" + "ODIzMTczNjQ4WhcNMTkwODIzMTczNjQ4WjA1MRUwEwYDVQQHDAxEZWZhdWx0IENp\n" + "dHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwXDANBgkqhkiG9w0BAQEF\n" + "AANLADBIAkEAqOvVKzrSpmKOTNqDzBG/iZrUdhCrMRsymFXyIScJcdsyn7jB8RMy\n" + "fbHqG8EqB8HHLU/eqt/+zhh2w08Lx3+5QwIDAQABMA0GCSqGSIb3DQEBCwUAA0EA\n" + "RSCq0sNbD9uWfcBqF0U4MtfFjU5x+RQQCeBVtAzwC9bggSILKZfB9XUvtGh6vqig\n", + /* Self-signed secp112r2 */ + "MIIBLTCB+QIJAI0LtN9uWxy3MAoGCCqGSM49BAMCMEUxCzAJBgNVBAYTAkFVMRMw\n" + "EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0\n" + "eSBMdGQwHhcNMTgwODIzMTc0MTQ4WhcNMTkwODIzMTc0MTQ4WjBFMQswCQYDVQQG\n" + "EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk\n" + "Z2l0cyBQdHkgTHRkMDIwEAYHKoZIzj0CAQYFK4EEAAcDHgAEf7dFHo7xhCtIcgyo\n" + "Px+IDcUUlntZCtar6V4O0zAKBggqhkjOPQQDAgMjADAgAg4yhBJMEmpkNbZU95Zf\n" + "uwIOJAan4J1ETxUII1RrGmw=\n" + }; + const time_t now = 1535046182; + tor_x509_cert_t *cert = NULL; + + unsigned i; + for (i = 0; i < ARRAY_LENGTH(certificates); ++i) { + cert = cert_from_der64(certificates[i]); + /* It might parse okay, depending on our version of NSS or OpenSSL. */ + if (cert == NULL) + continue; + /* But it should not validate. */ + tt_assert(! tor_tls_cert_is_valid(LOG_INFO, cert, cert, now, 0)); + tor_x509_cert_free(cert); + } + + done: + tor_x509_cert_free(cert); +} + +static void +test_x509_expiration(void *arg) +{ + (void)arg; + /* a 365-day RSA2048 cert, created between 0 and 60 minutes before "now" */ + const char certificate[] = + "MIICzjCCAbYCCQDxIONWIQ9OGDANBgkqhkiG9w0BAQsFADApMQswCQYDVQQGEwJV\n" + "UzEaMBgGA1UEAwwRSW50ZXJlc3RpbmcgdGltZXMwHhcNMTgwODIzMTc1NTE4WhcN\n" + "MTkwODIzMTc1NTE4WjApMQswCQYDVQQGEwJVUzEaMBgGA1UEAwwRSW50ZXJlc3Rp\n" + "bmcgdGltZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0Blz1fBii\n" + "OffpFlzMrmfPah/vkPcNrwoyx5YiosbHErYUpqdCtfNb7rbBM5xcac1LmF9kjnOQ\n" + "uAw1jsCNE82QHwWMlXOqaZCEJsnttNo0Y7yaSR/ChbGJ54XCp+Lx2acyTeH9cBWU\n" + "de8/sKAQ4NqpbEP01pBH4+1mPu2MYWjVWVicUxmw0mJ3cfkJCWUzt0nC4ls8+Itk\n" + "7XliKb216Z9uQXu/zD/JGkxAljnFs1jXCX4NyWz46xnJFzXbYCeyQnBz0tUbAvgg\n" + "uRdryYtHzD46hd8LTXH6oK2gV64ILAhDnRb1aBjnCXxbex24XoW3hjSrKGTdNsXA\n" + "RMWU/8QZaoiBAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFIYDBcbit2kOMrHECZK\n" + "ctem40A3s+0ZifzZ2KLhW8dTr/2Zb6DnlqVm2iUOV4cG/o1RAn/HzkQQuWEq+oBG\n" + "yOPVHudvCyGs+2ZQWudgAv9xq8N7KtZwJhnn42c2YSoreqRXDQgJqGFatyr+XdR7\n" + "gdQapLI4BFbZToeXp49Nl+q9330hKaSmIYmWEZ7R/33R64PU2el7X9/apYEcuZQT\n" + "+FjEqcO1lJ8/dTwM/2C1BJZqUeFTAu+ac1M+4//qyJRUUc6xSJLhiens8atWaxwL\n" + "eBCT8fCY8oPOwA1eImc/yWWmWXpv8bBWVe8OeLCMKM/OZoIdFqQpqSdcyGoh/kIW\n" + "Dws=\n"; + const time_t now = 1535046996; + + tor_x509_cert_t *cert = cert_from_der64(certificate); + tt_assert(cert); + + tt_assert(tor_tls_cert_is_valid(LOG_ERR, cert, cert, now, 0)); + + tt_assert(tor_tls_cert_is_valid(LOG_ERR, cert, cert, + now-TOR_X509_FUTURE_SLOP, 0)); + tt_assert(tor_tls_cert_is_valid(LOG_ERR, cert, cert, + now+365*86400+TOR_X509_PAST_SLOP - 3600, 0)); + + tt_assert(! tor_tls_cert_is_valid(LOG_INFO, cert, cert, + now-TOR_X509_FUTURE_SLOP - 3600, 0)); + tt_assert(! tor_tls_cert_is_valid(LOG_INFO, cert, cert, + now+365*86400+TOR_X509_FUTURE_SLOP, 0)); + + done: + tor_x509_cert_free(cert); +} + +#define TEST(name) { #name, test_x509_ ## name, TT_FORK, 0, NULL } + +struct testcase_t x509_tests[] = { + TEST(cert_new_failing_digest), + TEST(consume_ec_cert), + TEST(reject_tiny_keys), + TEST(expiration), + END_OF_TESTCASES +}; From 52ac539b9938af6c47355b67ce1def1236f4ee39 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 23 Aug 2018 18:57:26 -0400 Subject: [PATCH 61/76] Test a few more tortls.c functions --- src/lib/tls/tortls_nss.c | 17 ++++++--- src/test/test_tortls.c | 82 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index 40a98dd87e..df430765ad 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -331,6 +331,7 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, (void)tls; PRErrorCode code = PORT_GetError(); + const char *addr = tls ? tls->address : NULL; const char *string = PORT_ErrorToString(code); const char *name = PORT_ErrorToName(code); char buf[16]; @@ -341,10 +342,14 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, name = buf; } + const char *with = addr ? " with " : ""; + addr = addr ? addr : ""; if (doing) { - log_fn(severity, domain, "TLS error %s while %s: %s", name, doing, string); + log_fn(severity, domain, "TLS error %s while %s%s%s: %s", + name, doing, with, addr, string); } else { - log_fn(severity, domain, "TLS error %s: %s", name, string); + log_fn(severity, domain, "TLS error %s%s%s: %s", name, string, + with, addr); } } @@ -381,7 +386,7 @@ tor_tls_new(tor_socket_t sock, int is_server) } SECStatus s = SSL_ResetHandshake(ssl, is_server ? PR_TRUE : PR_FALSE); if (s != SECSuccess) { - crypto_nss_log_errors(LOG_WARN, "resetting handshake state"); + tls_log_errors(tls, LOG_WARN, LD_CRYPTO, "resetting handshake state"); } return tls; @@ -457,7 +462,7 @@ tor_tls_read, (tor_tls_t *tls, char *cp, size_t len)) if (err == PR_WOULD_BLOCK_ERROR) { return TOR_TLS_WANTREAD; // XXXX ???? } else { - crypto_nss_log_errors(LOG_NOTICE, "reading"); // XXXX + tls_log_errors(tls, LOG_NOTICE, LD_CRYPTO, "reading"); // XXXX return TOR_TLS_ERROR_MISC; // ???? } } @@ -482,7 +487,7 @@ tor_tls_write(tor_tls_t *tls, const char *cp, size_t n) if (err == PR_WOULD_BLOCK_ERROR) { return TOR_TLS_WANTWRITE; // XXXX ???? } else { - crypto_nss_log_errors(LOG_NOTICE, "writing"); // XXXX + tls_log_errors(tls, LOG_NOTICE, LD_CRYPTO, "writing"); // XXXX return TOR_TLS_ERROR_MISC; // ???? } } @@ -541,7 +546,7 @@ tor_tls_get_pending_bytes(tor_tls_t *tls) tor_assert(tls); int n = SSL_DataPending(tls->ssl); if (n < 0) { - crypto_nss_log_errors(LOG_WARN, "Looking up pending bytes"); + tls_log_errors(tls, LOG_WARN, LD_CRYPTO, "looking up pending bytes"); return 0; } return (int)n; diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index 0e4b5afafa..b16a880585 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -247,6 +247,85 @@ test_tortls_evaluate_ecgroup_for_tls(void *ignored) (void)0; } +static void +test_tortls_double_init(void *arg) +{ + (void) arg; + /* If we call tor_tls_context_init() a second time, nothing should go + * wrong. + */ + crypto_pk_t *pk1 = NULL, *pk2 = NULL; + pk1 = pk_generate(2); + pk2 = pk_generate(0); + + int r = tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + pk1, pk2, 86400); + tt_int_op(r, OP_EQ, 0); + + r = tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + pk2, pk1, 86400); + tt_int_op(r, OP_EQ, 0); + + done: + crypto_pk_free(pk1); + crypto_pk_free(pk2); +} + +static void +test_tortls_address(void *arg) +{ + (void)arg; + tor_tls_t *tls = NULL; + crypto_pk_t *pk1=NULL, *pk2=NULL; + pk1 = pk_generate(2); + pk2 = pk_generate(0); + + int r = tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + pk1, pk2, 86400); + tt_int_op(r, OP_EQ, 0); + + tls = tor_tls_new(-1, 0); + tls->state = TOR_TLS_ST_OPEN; + tor_tls_set_logged_address(tls, "zombo.com"); + + /* This write should fail, since the fd is -1. */ + setup_capture_of_logs(LOG_INFO); + int n = tor_tls_write(tls, "welcome", 7); + tt_int_op(n, OP_LT, 0); + expect_log_msg_containing("with zombo.com"); + + done: + teardown_capture_of_logs(); + tor_tls_free(tls); + crypto_pk_free(pk1); + crypto_pk_free(pk2); +} + +static void +test_tortls_is_server(void *arg) +{ + (void)arg; + crypto_pk_t *pk1=NULL, *pk2=NULL; + tor_tls_t *tls1=NULL, *tls2=NULL; + pk1 = pk_generate(2); + pk2 = pk_generate(0); + + int r = tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + pk1, pk2, 86400); + tt_int_op(r, OP_EQ, 0); + tls1 = tor_tls_new(-1, 0); + tls2 = tor_tls_new(-1, 1); + + tt_assert(! tor_tls_is_server(tls1)); + tt_assert(tor_tls_is_server(tls2)); + + done: + tor_tls_free(tls1); + tor_tls_free(tls2); + crypto_pk_free(pk1); + crypto_pk_free(pk2); +} + #define LOCAL_TEST_CASE(name, flags) \ { #name, test_tortls_##name, (flags|TT_FORK), NULL, NULL } @@ -262,5 +341,8 @@ struct testcase_t tortls_tests[] = { LOCAL_TEST_CASE(server_got_renegotiate, 0), #endif LOCAL_TEST_CASE(evaluate_ecgroup_for_tls, 0), + LOCAL_TEST_CASE(double_init, TT_FORK), + LOCAL_TEST_CASE(address, TT_FORK), + LOCAL_TEST_CASE(is_server, 0), END_OF_TESTCASES }; From 36f3bdac032523c6d98022c84059d8ebd69dfdfe Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 29 Aug 2018 08:39:19 -0400 Subject: [PATCH 62/76] Update prefork and postfork NSS code for unit tests. --- src/ext/tinytest.c | 5 ++++- src/lib/crypt_ops/crypto_init.c | 12 +++++++++++- src/lib/crypt_ops/crypto_init.h | 1 + src/lib/crypt_ops/crypto_nss_mgt.c | 19 ++++++++++++++----- src/lib/crypt_ops/crypto_nss_mgt.h | 3 ++- src/test/testing_common.c | 8 +++++++- 6 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/ext/tinytest.c b/src/ext/tinytest.c index 2a475bd917..052fb6483f 100644 --- a/src/ext/tinytest.c +++ b/src/ext/tinytest.c @@ -120,8 +120,10 @@ testcase_run_bare_(const struct testcase_t *testcase) #ifndef NO_FORKING #ifdef TINYTEST_POSTFORK +void tinytest_prefork(void); void tinytest_postfork(void); #else +static void tinytest_prefork(void) { } static void tinytest_postfork(void) { } #endif @@ -185,16 +187,17 @@ testcase_run_forked_(const struct testgroup_t *group, if (opt_verbosity>0) printf("[forking] "); + tinytest_prefork(); pid = fork(); #ifdef FORK_BREAKS_GCOV vproc_transaction_begin(0); #endif + tinytest_postfork(); if (!pid) { /* child. */ int test_r, write_r; char b[1]; close(outcome_pipe[0]); - tinytest_postfork(); test_r = testcase_run_bare_(testcase); assert(0<=(int)test_r && (int)test_r<=2); b[0] = "NYS"[test_r]; diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c index f9b077e9e7..6fc8e5d94c 100644 --- a/src/lib/crypt_ops/crypto_init.c +++ b/src/lib/crypt_ops/crypto_init.c @@ -58,7 +58,7 @@ crypto_early_init(void) crypto_openssl_early_init(); #endif #ifdef ENABLE_NSS - crypto_nss_early_init(); + crypto_nss_early_init(0); #endif if (crypto_seed_rng() < 0) @@ -134,6 +134,16 @@ crypto_global_cleanup(void) return 0; } +/** Run operations that the crypto library requires to be happy again + * after forking. */ +void +crypto_prefork(void) +{ +#ifdef ENABLE_NSS + crypto_nss_prefork(); +#endif +} + /** Run operations that the crypto library requires to be happy again * after forking. */ void diff --git a/src/lib/crypt_ops/crypto_init.h b/src/lib/crypt_ops/crypto_init.h index 05b281720c..5b6d65d48c 100644 --- a/src/lib/crypt_ops/crypto_init.h +++ b/src/lib/crypt_ops/crypto_init.h @@ -24,6 +24,7 @@ int crypto_global_init(int hardwareAccel, void crypto_thread_cleanup(void); int crypto_global_cleanup(void); +void crypto_prefork(void); void crypto_postfork(void); const char *crypto_get_library_name(void); diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c index 187f556bd2..a1da74aff5 100644 --- a/src/lib/crypt_ops/crypto_nss_mgt.c +++ b/src/lib/crypt_ops/crypto_nss_mgt.c @@ -50,10 +50,12 @@ nss_password_func_always_fail(PK11SlotInfo *slot, } void -crypto_nss_early_init(void) +crypto_nss_early_init(int nss_only) { - PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); - PK11_SetPasswordFunc(nss_password_func_always_fail); + if (! nss_only) { + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PK11_SetPasswordFunc(nss_password_func_always_fail); + } /* Eventually we should use NSS_Init() instead -- but that wants a directory. The documentation says that we can't use this if we want @@ -111,6 +113,14 @@ crypto_nss_late_init(void) void crypto_nss_global_cleanup(void) +{ + NSS_Shutdown(); + PL_ArenaFinish(); + PR_Cleanup(); +} + +void +crypto_nss_prefork(void) { NSS_Shutdown(); } @@ -118,6 +128,5 @@ crypto_nss_global_cleanup(void) void crypto_nss_postfork(void) { - crypto_nss_global_cleanup(); - crypto_nss_early_init(); + crypto_nss_early_init(1); } diff --git a/src/lib/crypt_ops/crypto_nss_mgt.h b/src/lib/crypt_ops/crypto_nss_mgt.h index c4c94f4d89..27793dcc45 100644 --- a/src/lib/crypt_ops/crypto_nss_mgt.h +++ b/src/lib/crypt_ops/crypto_nss_mgt.h @@ -22,11 +22,12 @@ const char *crypto_nss_get_header_version_str(void); void crypto_nss_log_errors(int severity, const char *doing); -void crypto_nss_early_init(void); +void crypto_nss_early_init(int nss_only); int crypto_nss_late_init(void); void crypto_nss_global_cleanup(void); +void crypto_nss_prefork(void); void crypto_nss_postfork(void); #endif diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 17a0756803..42b5190ca0 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -223,11 +223,17 @@ an_assertion_failed(void) tinytest_set_test_failed_(); } +void tinytest_prefork(void); void tinytest_postfork(void); void +tinytest_prefork(void) +{ + free_pregenerated_keys(); + crypto_prefork(); +} +void tinytest_postfork(void) { - free_pregenerated_keys(); crypto_postfork(); init_pregenerated_keys(); } From f46a7eafb88494922f331d198500f94dee0a6d8e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 29 Aug 2018 15:17:47 -0400 Subject: [PATCH 63/76] Do not leak a reference to "slot" when decoding private key. --- src/lib/crypt_ops/crypto_rsa_nss.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c index 517faa5c7b..b6d8bb647d 100644 --- a/src/lib/crypt_ops/crypto_rsa_nss.c +++ b/src/lib/crypt_ops/crypto_rsa_nss.c @@ -683,6 +683,9 @@ crypto_pk_asn1_decode_private(const char *str, size_t len) { tor_assert(str); tor_assert(len < INT_MAX); + PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS, NULL); + if (!slot) + return NULL; SECKEYPrivateKeyInfo info = { .algorithm = { @@ -699,7 +702,6 @@ crypto_pk_asn1_decode_private(const char *str, size_t len) } }; - PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS, NULL); SECStatus s; SECKEYPrivateKey *seckey = NULL; @@ -722,5 +724,8 @@ crypto_pk_asn1_decode_private(const char *str, size_t len) crypto_nss_log_errors(LOG_WARN, "decoding an RSA private key"); } + if (slot) + PK11_FreeSlot(slot); + return output; } From b892133fb9307aba2b9e7931472b9ad9a1628bb2 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 29 Aug 2018 16:00:49 -0400 Subject: [PATCH 64/76] Do not leave a certificate allocated after testing dirvote_add() --- src/test/test_dir_handle_get.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index 571b0386e2..09799a0e5f 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -2102,6 +2102,7 @@ test_dir_handle_get_status_vote_d(void* data) clear_dir_servers(); dirvote_free_all(); + routerlist_free_all(); } static void @@ -2638,4 +2639,3 @@ struct testcase_t dir_handle_get_tests[] = { DIR_HANDLE_CMD(parse_accept_encoding, 0), END_OF_TESTCASES }; - From 3b5d6ef15bec26c3bda22057ba09e1301613ee71 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 29 Aug 2018 16:43:54 -0400 Subject: [PATCH 65/76] Unify functions for reading/writing PEM keys, to avoid duplication. --- src/lib/crypt_ops/crypto_rsa.c | 149 ++++++++++++++++----------------- 1 file changed, 73 insertions(+), 76 deletions(-) diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c index 31497e6509..6a9e2948f1 100644 --- a/src/lib/crypt_ops/crypto_rsa.c +++ b/src/lib/crypt_ops/crypto_rsa.c @@ -398,29 +398,43 @@ crypto_pk_get_common_digests(crypto_pk_t *pk, common_digests_t *digests_out) static const char RSA_PUBLIC_TAG[] = "RSA PUBLIC KEY"; static const char RSA_PRIVATE_TAG[] = "RSA PRIVATE KEY"; -/** PEM-encode the public key portion of env and write it to a - * newly allocated string. On success, set *dest to the new - * string, *len to the string's length, and return 0. On - * failure, return -1. +/* These are overestimates for how many extra bytes we might need to encode + * a key in DER */ +#define PRIVATE_ASN_MAX_OVERHEAD_FACTOR 16 +#define PUBLIC_ASN_MAX_OVERHEAD_FACTOR 3 + +/** Helper: PEM-encode env and write it to a newly allocated string. + * If private_key, write the private part of env; otherwise + * write only the public portion. On success, set *dest to the new + * string, *len to the string's length, and return 0. On failure, + * return -1. */ -int -crypto_pk_write_public_key_to_string(crypto_pk_t *env, - char **dest, size_t *len) +static int +crypto_pk_write_to_string_generic(crypto_pk_t *env, + char **dest, size_t *len, + bool private_key) { - size_t buflen = crypto_pk_keysize(env) * 3; + const int factor = + private_key ? PRIVATE_ASN_MAX_OVERHEAD_FACTOR + : PUBLIC_ASN_MAX_OVERHEAD_FACTOR; + size_t buflen = crypto_pk_keysize(env) * factor; + const char *tag = + private_key ? RSA_PRIVATE_TAG : RSA_PUBLIC_TAG; char *buf = tor_malloc(buflen); char *result = NULL; size_t resultlen = 0; int rv = -1; - int n = crypto_pk_asn1_encode(env, buf, buflen); + int n = private_key + ? crypto_pk_asn1_encode_private(env, buf, buflen) + : crypto_pk_asn1_encode(env, buf, buflen); if (n < 0) goto done; - resultlen = pem_encoded_size(n, RSA_PUBLIC_TAG); + resultlen = pem_encoded_size(n, tag); result = tor_malloc(resultlen); if (pem_encode(result, resultlen, - (const unsigned char *)buf, n, RSA_PUBLIC_TAG) < 0) { + (const unsigned char *)buf, n, tag) < 0) { goto done; } @@ -438,6 +452,18 @@ crypto_pk_write_public_key_to_string(crypto_pk_t *env, return rv; } +/** PEM-encode the public key portion of env and write it to a + * newly allocated string. On success, set *dest to the new + * string, *len to the string's length, and return 0. On + * failure, return -1. + */ +int +crypto_pk_write_public_key_to_string(crypto_pk_t *env, + char **dest, size_t *len) +{ + return crypto_pk_write_to_string_generic(env, dest, len, false); +} + /** PEM-encode the private key portion of env and write it to a * newly allocated string. On success, set *dest to the new * string, *len to the string's length, and return 0. On @@ -447,30 +473,46 @@ int crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest, size_t *len) { - size_t buflen = crypto_pk_keysize(env) * 16; - char *buf = tor_malloc(buflen); - char *result = NULL; - size_t resultlen = 0; + return crypto_pk_write_to_string_generic(env, dest, len, true); +} + +/** + * Helper. Read a PEM-encoded RSA from the first len characters of + * src, and store the result in env. If private_key, + * expect a private key; otherwise expect a public key. Return 0 on success, + * -1 on failure. If len is -1, the string is nul-terminated. + */ +static int +crypto_pk_read_from_string_generic(crypto_pk_t *env, const char *src, + size_t len, bool private_key) +{ + if (len == (size_t)-1) // "-1" indicates "use the length of the string." + len = strlen(src); + + const char *tag = + private_key ? RSA_PRIVATE_TAG : RSA_PUBLIC_TAG; + size_t buflen = len; + uint8_t *buf = tor_malloc(buflen); int rv = -1; - int n = crypto_pk_asn1_encode_private(env, buf, buflen); + int n = pem_decode(buf, buflen, src, len, tag); if (n < 0) goto done; - resultlen = pem_encoded_size(n, RSA_PRIVATE_TAG); - result = tor_malloc(resultlen); - if (pem_encode(result, resultlen, - (const unsigned char *)buf, n, RSA_PRIVATE_TAG) < 0) + crypto_pk_t *pk = private_key + ? crypto_pk_asn1_decode_private((const char*)buf, n) + : crypto_pk_asn1_decode((const char*)buf, n); + if (! pk) goto done; - *dest = result; - *len = resultlen; + if (private_key) + crypto_pk_assign_private(env, pk); + else + crypto_pk_assign_public(env, pk); + crypto_pk_free(pk); rv = 0; + done: - if (rv < 0 && result) { - memwipe(result, 0, resultlen); - tor_free(result); - } memwipe(buf, 0, buflen); tor_free(buf); return rv; @@ -478,69 +520,24 @@ crypto_pk_write_private_key_to_string(crypto_pk_t *env, /** Read a PEM-encoded public key from the first len characters of * src, and store the result in env. Return 0 on success, -1 on - * failure. + * failure. If len is -1, the string is nul-terminated. */ int crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, size_t len) { - if (len == (size_t)-1) - len = strlen(src); - - size_t buflen = len; - uint8_t *buf = tor_malloc(buflen); - int rv = -1; - - int n = pem_decode(buf, buflen, src, len, RSA_PUBLIC_TAG); - if (n < 0) - goto done; - - crypto_pk_t *pk = crypto_pk_asn1_decode((const char*)buf, n); - if (! pk) - goto done; - - crypto_pk_assign_public(env, pk); - crypto_pk_free(pk); - rv = 0; - - done: - memwipe(buf, 0, buflen); - tor_free(buf); - return rv; + return crypto_pk_read_from_string_generic(env, src, len, false); } -/** Read a PEM-encoded private key from the len-byte string s +/** Read a PEM-encoded private key from the len-byte string src * into env. Return 0 on success, -1 on failure. If len is -1, * the string is nul-terminated. */ int crypto_pk_read_private_key_from_string(crypto_pk_t *env, - const char *s, ssize_t len) + const char *src, ssize_t len) { - if (len == -1) - len = strlen(s); - - size_t buflen = len; - uint8_t *buf = tor_malloc(buflen); - int rv = -1; - - int n = pem_decode(buf, buflen, s, len, RSA_PRIVATE_TAG); - if (n < 0) { - goto done; - } - - crypto_pk_t *pk = crypto_pk_asn1_decode_private((const char *)buf, n); - if (! pk) - goto done; - - crypto_pk_assign_private(env, pk); - crypto_pk_free(pk); - rv = 0; - - done: - memwipe(buf, 0, buflen); - tor_free(buf); - return rv; + return crypto_pk_read_from_string_generic(env, src, len, true); } /** Read a PEM-encoded private key from the file named by From 600e046ed32f9826bcbdf223d99ed8af23a67504 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 31 Aug 2018 16:15:20 -0400 Subject: [PATCH 66/76] Rename crypto_pk_check_key(), use it more reasonably, add tests This function was a wrapper around RSA_check_key() in openssl, which checks for invalid RSA private keys (like those where p or q are composite, or where d is not the inverse of e, or where n != p*q). We don't need a function like this in NSS, since unlike OpenSSL, NSS won't let you import a bogus private key. I've renamed the function and changed its return type to make it more reasonable, and added a unit test for trying to read a key where n != p*q. --- src/feature/relay/router.c | 2 +- src/feature/rend/rendservice.c | 2 +- src/lib/crypt_ops/crypto_rsa.h | 2 +- src/lib/crypt_ops/crypto_rsa_nss.c | 15 +++++-- src/lib/crypt_ops/crypto_rsa_openssl.c | 18 ++++++--- src/test/test_crypto.c | 55 ++++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 7685760ac6..55e8f1403e 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -590,7 +590,7 @@ init_key_from_file(const char *fname, int generate, int severity, tor_log(severity, LD_GENERAL,"Error generating onion key"); goto error; } - if (crypto_pk_check_key(prkey) <= 0) { + if (! crypto_pk_is_valid_private_key(prkey)) { tor_log(severity, LD_GENERAL,"Generated key seems invalid"); goto error; } diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c index 1a99bd56ed..77775602b5 100644 --- a/src/feature/rend/rendservice.c +++ b/src/feature/rend/rendservice.c @@ -1629,7 +1629,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) crypto_pk_free(prkey); goto err; } - if (crypto_pk_check_key(prkey) <= 0) { + if (! crypto_pk_is_valid_private_key(prkey)) { log_warn(LD_BUG,"Generated client key seems invalid"); crypto_pk_free(prkey); goto err; diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index aaf32ec1b0..dd25bc31bd 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -64,7 +64,7 @@ int crypto_pk_read_private_key_from_string(crypto_pk_t *env, int crypto_pk_write_private_key_to_filename(crypto_pk_t *env, const char *fname); -int crypto_pk_check_key(crypto_pk_t *env); +int crypto_pk_is_valid_private_key(crypto_pk_t *env); int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b); int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b); size_t crypto_pk_keysize(const crypto_pk_t *env); diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c index b6d8bb647d..90e9c34d30 100644 --- a/src/lib/crypt_ops/crypto_rsa_nss.c +++ b/src/lib/crypt_ops/crypto_rsa_nss.c @@ -244,12 +244,14 @@ crypto_pk_generate_key_with_bits,(crypto_pk_t *key, int bits)) return result; } -/** Return true iff env has a valid key. +/** Return true iff env is a valid private key. */ int -crypto_pk_check_key(crypto_pk_t *key) +crypto_pk_is_valid_private_key(crypto_pk_t *key) { - return key && key->pubkey; + /* We don't need to do validation here, since unlike OpenSSL, NSS won't let + * us load private keys without validating them. */ + return key && key->seckey; } /** Return true iff env contains a public key whose public exponent @@ -435,7 +437,7 @@ crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, tor_assert(tolen < INT_MAX); tor_assert(fromlen < INT_MAX); - if (BUG(!crypto_pk_check_key(env))) + if (BUG(! env->pubkey)) return -1; unsigned int result_len = 0; @@ -724,6 +726,11 @@ crypto_pk_asn1_decode_private(const char *str, size_t len) crypto_nss_log_errors(LOG_WARN, "decoding an RSA private key"); } + if (! crypto_pk_is_valid_private_key(output)) { + crypto_pk_free(output); + output = NULL; + } + if (slot) PK11_FreeSlot(slot); diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c index 7ff71f6dd4..f203a06835 100644 --- a/src/lib/crypt_ops/crypto_rsa_openssl.c +++ b/src/lib/crypt_ops/crypto_rsa_openssl.c @@ -183,18 +183,21 @@ crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)) return 0; } -/** Return true iff env has a valid key. +/** Return true if env has a valid key; false otherwise. */ int -crypto_pk_check_key(crypto_pk_t *env) +crypto_pk_is_valid_private_key(crypto_pk_t *env) { int r; tor_assert(env); r = RSA_check_key(env->key); - if (r <= 0) + if (r <= 0) { crypto_openssl_log_errors(LOG_WARN,"checking RSA key"); - return r; + return 0; + } else { + return 1; + } } /** Return true iff env contains a public key whose public exponent @@ -578,5 +581,10 @@ crypto_pk_asn1_decode_private(const char *str, size_t len) crypto_openssl_log_errors(LOG_WARN,"decoding public key"); return NULL; } - return crypto_new_pk_from_openssl_rsa_(rsa); + crypto_pk_t *result = crypto_new_pk_from_openssl_rsa_(rsa); + if (! crypto_pk_is_valid_private_key(result)) { + crypto_pk_free(result); + return NULL; + } + return result; } diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 04077b42fb..b08c5cbc22 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -18,6 +18,7 @@ #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_init.h" #include "ed25519_vectors.inc" +#include "test/log_test_helpers.h" #ifdef HAVE_SYS_STAT_H #include @@ -1488,6 +1489,58 @@ test_crypto_pk_pem_encrypted(void *arg) done: crypto_pk_free(pk); } + +static void +test_crypto_pk_invalid_private_key(void *arg) +{ + (void)arg; + /* Here is a simple invalid private key: it was produced by making + * a regular private key, and then adding 2 to the modulus. */ + const char pem[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpQIBAAKCAQEAskRyZrs+YAukvBmZlgo6/rCxyKF2xyUk073ap+2CgRUnSfGG\n" + "mflHlzqVq7tpH50DafpS+fFAbaEaNV/ac20QG0rUZi38HTB4qURWOu6n0Bws6E4l\n" + "UX/AkvDlWnuYH0pHHi2c3DGNFjwoJpjKuUTk+cRffVR8X3Kjr62SUDUaBNW0Kecz\n" + "3SYLbmgmZI16dFZ+g9sNM3znXZbhvb33WwPqpZSSPs37cPgF7eS6mAw/gUMx6zfE\n" + "HRmUnOQSzUdS05rvc/hsiCLhiIZ8hgfkD07XnTT1Ds8DwE55k7BUWY2wvwWCNLsH\n" + "qtqAxTr615XdkMxVkYgImpqPybarpfNYhFqkOwIDAQABAoIBACPC3VxEdbfYvhxJ\n" + "2mih9sG++nswAN7kUaX0cRe86rAwaShJPmJHApiQ1ROVTfpciiHJaLnhLraPWe2Z\n" + "I/6Bw3hmI4O399p3Lc1u+wlpdNqnvE6B1rSptx0DHE9xecvVH70rE0uM2Su7t6Y+\n" + "gnR2IKUGQs2mlCilm7aTUEWs0WJkkl4CG1dyxItuOSdNBjOEzXimJyiB10jEBFsp\n" + "SZeCF2FZ7AJbck5CVC42+oTsiDbZrHTHOn7v26rFGdONeHD1wOI1v7JwHFpCB923\n" + "aEHBzsPbMeq7DWG1rjzCYpcXHhTDBDBWSia4SEhyr2Nl7m7qxWWWwR+x4dqAj3rD\n" + "HeTmos0CgYEA6uf1CLpjPpOs5IaW1DQI8dJA/xFEAC/6GVgq4nFOGHZrm8G3L5o+\n" + "qvtQNMpDs2naWuZpqROFqv24o01DykHygR72GlPIY6uvmmf5tvJLoGnbFUay33L4\n" + "7b9dkNhuEIBNPzVDie0pgS77WgaPbYkVv5fnDwgPuVnkqfakEt7Pz2MCgYEAwkZ5\n" + "R1wLuTQEA2Poo6Gf4L8Bg6yNYI46LHDqDIs818iYLjtcnEEvbPfaoKNpOn7s7s4O\n" + "Pc+4HnT1aIQs0IKVLRTp+5a/9wfOkPZnobWOUHZk9UzBL3Hc1uy/qhp93iE3tSzx\n" + "v0O1pvR+hr3guTCZx8wZnDvaMgG3hlyPnVlHdrMCgYEAzQQxGbMC1ySv6quEjCP2\n" + "AogMbhE1lixJTUFj/EoDbNo9xKznIkauly/Lqqc1OysRhfA/G2+MY9YZBX1zwtyX\n" + "uBW7mPKynDrFgi9pBECnvJNmwET57Ic9ttIj6Tzbos83nAjyrzgr1zGX8dRz7ZeN\n" + "QbBj2vygLJbGOYinXkjUeh0CgYEAhN5aF9n2EqZmkEMGWtMxWy6HRJ0A3Cap1rcq\n" + "+4VHCXWhzwy+XAeg/e/N0MuyLlWcif7XcqLcE8h+BwtO8xQ8HmcNWApUJAls12wO\n" + "mGRpftJaXgIupdpD5aJpu1b++qrRRNIGTH9sf1D8L/8w8LcylZkbcuTkaAsQj45C\n" + "kqT64U0CgYEAq47IKS6xc3CDc17BqExR6t+1yRe+4ml+z1zcVbfUKony4pGvl1yo\n" + "rk0IYDN5Vd8h5xtXrkPdX9h+ywmohnelDKsayEuE+opyqEpSU4/96Bb22RZUoucb\n" + "LWkV5gZx5hFnDFtEd4vadMIiY4jVv/3JqiZDKwMVBJKlHRXJEEmIEBk=\n" + "-----END RSA PRIVATE KEY-----\n"; + + crypto_pk_t *pk = NULL; + + pk = crypto_pk_new(); + setup_capture_of_logs(LOG_WARN); + tt_int_op(-1, OP_EQ, + crypto_pk_read_private_key_from_string(pk, pem, strlen(pem))); +#ifdef ENABLE_NSS + expect_single_log_msg_containing("received bad data"); +#else + expect_single_log_msg_containing("while checking RSA key"); +#endif + done: + teardown_capture_of_logs(); + crypto_pk_free(pk); +} + #ifdef HAVE_TRUNCATE #define do_truncate truncate #else @@ -3109,6 +3162,8 @@ struct testcase_t crypto_tests[] = { { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL }, { "pk_base64", test_crypto_pk_base64, TT_FORK, NULL, NULL }, { "pk_pem_encrypted", test_crypto_pk_pem_encrypted, TT_FORK, NULL, NULL }, + { "pk_invalid_private_key", test_crypto_pk_invalid_private_key, 0, + NULL, NULL }, CRYPTO_LEGACY(digests), { "digest_names", test_crypto_digest_names, 0, NULL, NULL }, { "sha3", test_crypto_sha3, TT_FORK, NULL, NULL}, From edbb5ef5b258db15459407f8964b83b6dd4c3d22 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Sep 2018 09:51:03 -0400 Subject: [PATCH 67/76] Make some additional RSA functions const --- src/lib/crypt_ops/crypto_rsa.h | 4 ++-- src/lib/crypt_ops/crypto_rsa_nss.c | 4 ++-- src/lib/crypt_ops/crypto_rsa_openssl.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index dd25bc31bd..007964b268 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/src/lib/crypt_ops/crypto_rsa.h @@ -64,7 +64,7 @@ int crypto_pk_read_private_key_from_string(crypto_pk_t *env, int crypto_pk_write_private_key_to_filename(crypto_pk_t *env, const char *fname); -int crypto_pk_is_valid_private_key(crypto_pk_t *env); +int crypto_pk_is_valid_private_key(const crypto_pk_t *env); int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b); int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b); size_t crypto_pk_keysize(const crypto_pk_t *env); @@ -72,7 +72,7 @@ int crypto_pk_num_bits(crypto_pk_t *env); crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig); crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig); int crypto_pk_key_is_private(const crypto_pk_t *key); -int crypto_pk_public_exponent_ok(crypto_pk_t *env); +int crypto_pk_public_exponent_ok(const crypto_pk_t *env); int crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c index 90e9c34d30..dc282d7c9d 100644 --- a/src/lib/crypt_ops/crypto_rsa_nss.c +++ b/src/lib/crypt_ops/crypto_rsa_nss.c @@ -247,7 +247,7 @@ crypto_pk_generate_key_with_bits,(crypto_pk_t *key, int bits)) /** Return true iff env is a valid private key. */ int -crypto_pk_is_valid_private_key(crypto_pk_t *key) +crypto_pk_is_valid_private_key(const crypto_pk_t *key) { /* We don't need to do validation here, since unlike OpenSSL, NSS won't let * us load private keys without validating them. */ @@ -258,7 +258,7 @@ crypto_pk_is_valid_private_key(crypto_pk_t *key) * equals 65537. */ int -crypto_pk_public_exponent_ok(crypto_pk_t *key) +crypto_pk_public_exponent_ok(const crypto_pk_t *key) { return key && key->pubkey && diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c index f203a06835..df81c963eb 100644 --- a/src/lib/crypt_ops/crypto_rsa_openssl.c +++ b/src/lib/crypt_ops/crypto_rsa_openssl.c @@ -186,7 +186,7 @@ crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)) /** Return true if env has a valid key; false otherwise. */ int -crypto_pk_is_valid_private_key(crypto_pk_t *env) +crypto_pk_is_valid_private_key(const crypto_pk_t *env) { int r; tor_assert(env); @@ -204,7 +204,7 @@ crypto_pk_is_valid_private_key(crypto_pk_t *env) * equals TOR_RSA_EXPONENT. */ int -crypto_pk_public_exponent_ok(crypto_pk_t *env) +crypto_pk_public_exponent_ok(const crypto_pk_t *env) { tor_assert(env); tor_assert(env->key); From 3b10108a2abec89a9f1f8ba57a99a8b10a67515c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Sep 2018 09:51:39 -0400 Subject: [PATCH 68/76] Fix documentation of ENABLE_NSS --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index f99697a445..de53b1ab25 100644 --- a/configure.ac +++ b/configure.ac @@ -71,7 +71,7 @@ AM_CONDITIONAL(USE_OPENSSL, test "x$enable_nss" != "xyes") if test "x$enable_nss" = "xyes"; then AC_DEFINE(ENABLE_NSS, 1, - [Defined if we're building with NSS in addition to OpenSSL.]) + [Defined if we're building with NSS.]) else AC_DEFINE(ENABLE_OPENSSL, 1, [Defined if we're building with OpenSSL or LibreSSL]) From 70c27b7e39b9437bf18c0342b15a5c6a3022e204 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Sep 2018 10:51:26 -0400 Subject: [PATCH 69/76] Fix documentation of initialized fields in crypto_init.c --- src/lib/crypt_ops/crypto_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c index 6fc8e5d94c..c731662d49 100644 --- a/src/lib/crypt_ops/crypto_init.c +++ b/src/lib/crypt_ops/crypto_init.c @@ -23,10 +23,10 @@ #include "siphash.h" -/** Boolean: has OpenSSL's crypto been initialized? */ +/** Boolean: has our crypto library been initialized? (early phase) */ static int crypto_early_initialized_ = 0; -/** Boolean: has OpenSSL's crypto been initialized? */ +/** Boolean: has our crypto library been initialized? (late phase) */ static int crypto_global_initialized_ = 0; static int have_seeded_siphash = 0; From 7acb8c8d1835f919fee09585c966dee837f2e9f6 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Sep 2018 10:55:15 -0400 Subject: [PATCH 70/76] Document winsock includes better --- src/lib/tls/tortls_nss.c | 2 +- src/lib/tls/tortls_openssl.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index df430765ad..266455049e 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -14,7 +14,7 @@ #define TORTLS_PRIVATE #define TOR_X509_PRIVATE -#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include "*/ +#ifdef _WIN32 #include #include #endif diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index d10e50a975..6ff03ee818 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -20,7 +20,9 @@ #define TORTLS_OPENSSL_PRIVATE #define TOR_X509_PRIVATE -#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include "*/ +#ifdef _WIN32 + /* We need to include these here, or else the dtls1.h header will include + * and mess things up, in at least some openssl versions. */ #include #include #endif From 3cdf0497f9672bc281b0aac2606123249d7b9ddc Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Sep 2018 12:08:06 -0400 Subject: [PATCH 71/76] Add unit test for bridge-style TLS initialization. --- src/test/test_tortls.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index b16a880585..3d0ca5e85f 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -265,12 +265,34 @@ test_tortls_double_init(void *arg) r = tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, pk2, pk1, 86400); tt_int_op(r, OP_EQ, 0); + /* For a public server context, these are the same */ + tt_ptr_op(tor_tls_context_get(0), OP_EQ, tor_tls_context_get(1)); done: crypto_pk_free(pk1); crypto_pk_free(pk2); } +static void +test_tortls_bridge_init(void *arg) +{ + (void)arg; + crypto_pk_t *pk1 = NULL, *pk2 = NULL; + pk1 = pk_generate(2); + pk2 = pk_generate(0); + + /* If we pass in a server identity key but not the + TOR_TLS_CTX_IS_PUBLIC_SERVER flag, we should get a bridge-style + configuration, with two distinct contexts. */ + int r = tor_tls_context_init(0 /* flags */, pk1, pk2, 86400); + + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(tor_tls_context_get(0), OP_NE, tor_tls_context_get(1)); + done: + crypto_pk_free(pk1); + crypto_pk_free(pk2); +} + static void test_tortls_address(void *arg) { @@ -344,5 +366,6 @@ struct testcase_t tortls_tests[] = { LOCAL_TEST_CASE(double_init, TT_FORK), LOCAL_TEST_CASE(address, TT_FORK), LOCAL_TEST_CASE(is_server, 0), + LOCAL_TEST_CASE(bridge_init, TT_FORK), END_OF_TESTCASES }; From 59c1b34b72ec6c55ca4de0c56a9be3da3d1c3e08 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Sep 2018 12:09:43 -0400 Subject: [PATCH 72/76] Remove tor_tls_check_lifetime as unused. Everything that might have used it, uses tor_tls_cert_is_valid() instead. --- src/lib/tls/tortls.c | 32 --------------------------- src/lib/tls/tortls.h | 4 ---- src/test/test_tortls_openssl.c | 40 ---------------------------------- 3 files changed, 76 deletions(-) diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index edf421b4db..64c26f7e14 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -433,35 +433,3 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity) return rv; } - -/** Check whether the certificate set on the connection tls is expired - * give or take past_tolerance seconds, or not-yet-valid give or take - * future_tolerance seconds. Return 0 for valid, -1 for failure. - * - * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime. - */ -int -tor_tls_check_lifetime(int severity, tor_tls_t *tls, - time_t now, - int past_tolerance, int future_tolerance) -{ - tor_x509_cert_t *cert; - int r = -1; - - if (!(cert = tor_tls_get_peer_cert(tls))) - goto done; - - if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now, - past_tolerance, - future_tolerance) < 0) - goto done; - - r = 0; - done: - tor_x509_cert_free(cert); -#ifdef ENABLE_OPENSSL - tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime"); -#endif - - return r; -} diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h index a8bc7370a7..4591927081 100644 --- a/src/lib/tls/tortls.h +++ b/src/lib/tls/tortls.h @@ -100,10 +100,6 @@ int tor_tls_peer_has_cert(tor_tls_t *tls); MOCK_DECL(struct tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls)); MOCK_DECL(struct tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls)); int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity); -int tor_tls_check_lifetime(int severity, - tor_tls_t *tls, time_t now, - int past_tolerance, - int future_tolerance); MOCK_DECL(int, tor_tls_read, (tor_tls_t *tls, char *cp, size_t len)); int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n); int tor_tls_handshake(tor_tls_t *tls); diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c index b7e28f3768..3ae0f04b7d 100644 --- a/src/test/test_tortls_openssl.c +++ b/src/test/test_tortls_openssl.c @@ -960,45 +960,6 @@ test_tortls_verify(void *ignored) } #endif /* !defined(OPENSSL_OPAQUE) */ -#ifndef OPENSSL_OPAQUE -static void -test_tortls_check_lifetime(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - X509 *validCert = read_cert_from(validCertString); - time_t now = time(NULL); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0); - tt_int_op(ret, OP_EQ, -1); - - tls->ssl = tor_malloc_zero(sizeof(SSL)); - tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); - tls->ssl->session->peer = validCert; - ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0); - tt_int_op(ret, OP_EQ, 0); - - ASN1_STRING_free(validCert->cert_info->validity->notBefore); - validCert->cert_info->validity->notBefore = ASN1_TIME_set(NULL, now-10); - ASN1_STRING_free(validCert->cert_info->validity->notAfter); - validCert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, now+60); - - ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, -1000); - tt_int_op(ret, OP_EQ, -1); - - ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), -1000, 0); - tt_int_op(ret, OP_EQ, -1); - - done: - tor_free(tls->ssl->session); - tor_free(tls->ssl); - tor_free(tls); - X509_free(validCert); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - #ifndef OPENSSL_OPAQUE static int fixed_ssl_pending_result = 0; @@ -2469,7 +2430,6 @@ struct testcase_t tortls_openssl_tests[] = { INTRUSIVE_TEST_CASE(classify_client_ciphers, 0), LOCAL_TEST_CASE(client_is_using_v2_ciphers, 0), INTRUSIVE_TEST_CASE(verify, 0), - INTRUSIVE_TEST_CASE(check_lifetime, 0), INTRUSIVE_TEST_CASE(get_pending_bytes, 0), INTRUSIVE_TEST_CASE(SSL_SESSION_get_master_key, 0), INTRUSIVE_TEST_CASE(get_tlssecrets, 0), From ad94d43fc50525e8814b6e99f78d4b9635fa80ca Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Sep 2018 12:59:47 -0400 Subject: [PATCH 73/76] Port test_tortls_verify to not depend on openssl internals --- src/lib/tls/x509.c | 9 ++ src/lib/tls/x509_internal.h | 1 + src/lib/tls/x509_nss.c | 10 +- src/lib/tls/x509_openssl.c | 18 ++-- src/test/include.am | 1 + src/test/test_tortls.c | 157 ++++++++++++++++++++++++++++++++ src/test/test_tortls.h | 13 +++ src/test/test_tortls_openssl.c | 161 +-------------------------------- 8 files changed, 198 insertions(+), 172 deletions(-) create mode 100644 src/test/test_tortls.h diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index c88298b6cf..2e70206462 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -134,3 +134,12 @@ tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert)) tor_x509_cert_impl_free_(x509_cert); return NULL; } + +/** Return a new copy of cert. */ +tor_x509_cert_t * +tor_x509_cert_dup(const tor_x509_cert_t *cert) +{ + tor_assert(cert); + tor_assert(cert->cert); + return tor_x509_cert_new(tor_x509_cert_impl_dup_(cert->cert)); +} diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h index 0e186ec849..c08705cb25 100644 --- a/src/lib/tls/x509_internal.h +++ b/src/lib/tls/x509_internal.h @@ -41,6 +41,7 @@ int tor_x509_check_cert_lifetime_internal(int severity, int future_tolerance); void tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert); +tor_x509_cert_impl_t *tor_x509_cert_impl_dup_(tor_x509_cert_impl_t *cert); #ifdef ENABLE_OPENSSL int tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert); #else diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c index 68aebbb69a..5bb7940c90 100644 --- a/src/lib/tls/x509_nss.c +++ b/src/lib/tls/x509_nss.c @@ -225,11 +225,13 @@ tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert) CERT_DestroyCertificate(cert); } -tor_x509_cert_t * -tor_x509_cert_dup(const tor_x509_cert_t *cert) +tor_x509_cert_impl_t * +tor_x509_cert_impl_dup_(tor_x509_cert_impl_t *cert) { - tor_assert(cert); - return tor_x509_cert_new(CERT_DupCertificate(cert->cert)); + if (cert) + return CERT_DupCertificate(cert); + else + return NULL; } /** diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c index f315b88f36..dd74e84418 100644 --- a/src/lib/tls/x509_openssl.c +++ b/src/lib/tls/x509_openssl.c @@ -206,6 +206,15 @@ tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert) X509_free(cert); } +tor_x509_cert_impl_t * +tor_x509_cert_impl_dup_(tor_x509_cert_impl_t *cert) +{ + if (cert) + return X509_dup(cert); + else + return NULL; +} + /** Set *encoded_out and *size_out to cert's encoded DER * representation and length, respectively. */ void @@ -219,15 +228,6 @@ tor_x509_cert_get_der(const tor_x509_cert_t *cert, *size_out = cert->encoded_len; } -/** Return a new copy of cert. */ -tor_x509_cert_t * -tor_x509_cert_dup(const tor_x509_cert_t *cert) -{ - tor_assert(cert); - X509 *x509 = cert->cert; - return tor_x509_cert_new(X509_dup(x509)); -} - /** Read a DER-encoded X509 cert, of length exactly certificate_len, * from a certificate. Return a newly allocated tor_x509_cert_t on * success and NULL on failure. */ diff --git a/src/test/include.am b/src/test/include.am index 69680f9691..c553e6fe41 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -301,6 +301,7 @@ noinst_HEADERS+= \ src/test/test_helpers.h \ src/test/test_dir_common.h \ src/test/test_connection.h \ + src/test/test_tortls.h \ src/test/test_descriptors.inc \ src/test/example_extrainfo.inc \ src/test/failing_routerdescs.inc \ diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index 3d0ca5e85f..b260310adb 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -23,13 +23,120 @@ #include "lib/tls/tortls.h" #include "lib/tls/tortls_st.h" #include "lib/tls/tortls_internal.h" +#include "lib/encoding/pem.h" #include "app/config/or_state_st.h" #include "test/test.h" #include "test/log_test_helpers.h" +#include "test/test_tortls.h" #include "tinytest.h" +const char* notCompletelyValidCertString = + "-----BEGIN CERTIFICATE-----\n" + "MIICVjCCAb8CAg37MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n" + "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n" + "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n" + "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n" + "ODIyMDUyNzIzWhcNMTcwODIxMDUyNzIzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n" + "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n" + "ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYBBrx5PlP0WNI/ZdzD\n" + "+6Pktmurn+F2kQYbtc7XQh8/LTBvCo+P6iZoLEmUA9e7EXLRxgU1CVqeAi7QcAn9\n" + "MwBlc8ksFJHB0rtf9pmf8Oza9E0Bynlq/4/Kb1x+d+AyhL7oK9tQwB24uHOueHi1\n" + "C/iVv8CSWKiYe6hzN1txYe8rAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAASPdjigJ\n" + "kXCqKWpnZ/Oc75EUcMi6HztaW8abUMlYXPIgkV2F7YanHOB7K4f7OOLjiz8DTPFf\n" + "jC9UeuErhaA/zzWi8ewMTFZW/WshOrm3fNvcMrMLKtH534JKvcdMg6qIdjTFINIr\n" + "evnAhf0cwULaebn+lMs8Pdl7y37+sfluVok=\n" + "-----END CERTIFICATE-----\n"; + +const char* validCertString = "-----BEGIN CERTIFICATE-----\n" + "MIIDpTCCAY0CAg3+MA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMREwDwYD\n" + "VQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIGA1UECgwLVG9yIFRl\n" + "c3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkwNjEzMzk1OVoXDTQz\n" + "MDEyMjEzMzk1OVowVjELMAkGA1UEBhMCVVMxEDAOBgNVBAcMB0NoaWNhZ28xFDAS\n" + "BgNVBAoMC1RvciBUZXN0aW5nMR8wHQYDVQQDDBZ0ZXN0aW5nLnRvcnByb2plY3Qu\n" + "b3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoT6uyVVhWyOF3wkHjjYbd\n" + "nKaykyRv4JVtKQdZ4OpEErmX1zw4MmyzpQNV6iR4bQnWiyLfzyVJMZDIC/WILBfX\n" + "w2Pza/yuLgUvDc3twMuhOACzOQVO8PrEF/aVv2+hbCCy2udXvKhnYn+CCXl3ozc8\n" + "XcKYvujTXDyvGWY3xwAjlQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCUvnhzQWuQ\n" + "MrN+pERkE+zcTI/9dGS90rUMMLgu8VDNqTa0TUQh8uO0EQ6uDvI8Js6e8tgwS0BR\n" + "UBahqb7ZHv+rejGCBr5OudqD+x4STiiuPNJVs86JTLN8SpM9CHjIBH5WCCN2KOy3\n" + "mevNoRcRRyYJzSFULCunIK6FGulszigMYGscrO4oiTkZiHPh9KvWT40IMiHfL+Lw\n" + "EtEWiLex6064LcA2YQ1AMuSZyCexks63lcfaFmQbkYOKqXa1oLkIRuDsOaSVjTfe\n" + "vec+X6jvf12cFTKS5WIeqkKF2Irt+dJoiHEGTe5RscUMN/f+gqHPzfFz5dR23sxo\n" + "g+HC6MZHlFkLAOx3wW6epPS8A/m1mw3zMPoTnb2U2YYt8T0dJMMlUn/7Y1sEAa+a\n" + "dSTMaeUf6VnJ//11m454EZl1to9Z7oJOgqmFffSrdD4BGIWe8f7hhW6L1Enmqe/J\n" + "BKL3wbzZh80O1W0bndAwhnEEhlzneFY84cbBo9pmVxpODHkUcStpr5Z7pBDrcL21\n" + "Ss/aB/1YrsVXhdvJdOGxl3Mnl9dUY57CympLGlT8f0pPS6GAKOelECOhFMHmJd8L\n" + "dj3XQSmKtYHevZ6IvuMXSlB/fJvSjSlkCuLo5+kJoaqPuRu+i/S1qxeRy3CBwmnE\n" + "LdSNdcX4N79GQJ996PA8+mUCQG7YRtK+WA==\n" + "-----END CERTIFICATE-----\n"; + +const char* caCertString = "-----BEGIN CERTIFICATE-----\n" + "MIIFjzCCA3egAwIBAgIJAKd5WgyfPMYRMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV\n" + "BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIG\n" + "A1UECgwLVG9yIFRlc3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkw\n" + "NjEzMzc0MVoXDTQzMDEyMjEzMzc0MVowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM\n" + "CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRQwEgYDVQQKDAtUb3IgVGVzdGlu\n" + "ZzEUMBIGA1UEAwwLVG9yIFRlc3RpbmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n" + "ggIKAoICAQCpLMUEiLW5leUgBZoEJms2V7lZRhIAjnJBhVMHD0e3UubNknmaQoxf\n" + "ARz3rvqOaRd0JlV+qM9qE0DjiYcCVP1cAfqAo9d83uS1vwY3YMVJzADlaIiHfyVW\n" + "uEgBy0vvkeUBqaua24dYlcwsemOiXYLu41yM1wkcGHW1AhBNHppY6cznb8TyLgNM\n" + "2x3SGUdzc5XMyAFx51faKGBA3wjs+Hg1PLY7d30nmCgEOBavpm5I1disM/0k+Mcy\n" + "YmAKEo/iHJX/rQzO4b9znP69juLlR8PDBUJEVIG/CYb6+uw8MjjUyiWXYoqfVmN2\n" + "hm/lH8b6rXw1a2Aa3VTeD0DxaWeacMYHY/i01fd5n7hCoDTRNdSw5KJ0L3Z0SKTu\n" + "0lzffKzDaIfyZGlpW5qdouACkWYzsaitQOePVE01PIdO30vUfzNTFDfy42ccx3Di\n" + "59UCu+IXB+eMtrBfsok0Qc63vtF1linJgjHW1z/8ujk8F7/qkOfODhk4l7wngc2A\n" + "EmwWFIFoGaiTEZHB9qteXr4unbXZ0AHpM02uGGwZEGohjFyebEb73M+J57WKKAFb\n" + "PqbLcGUksL1SHNBNAJcVLttX55sO4nbidOS/kA3m+F1R04MBTyQF9qA6YDDHqdI3\n" + "h/3pw0Z4fxVouTYT4/NfRnX4JTP4u+7Mpcoof28VME0qWqD1LnRhFQIDAQABo1Aw\n" + "TjAdBgNVHQ4EFgQUMoAgIXH7pZ3QMRwTjT+DM9Yo/v0wHwYDVR0jBBgwFoAUMoAg\n" + "IXH7pZ3QMRwTjT+DM9Yo/v0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n" + "AgEAUJxacjXR9sT+Xs6ISFiUsyd0T6WVKMnV46xrYJHirGfx+krWHrjxMY+ZtxYD\n" + "DBDGlo11Qc4v6QrclNf5QUBfIiGQsP9Cm6hHcQ+Tpg9HHCgSqG1YNPwCPReCR4br\n" + "BLvLfrfkcBL2IWM0PdQdCze+59DBfipsULD2mEn9fjYRXQEwb2QWtQ9qRc20Yb/x\n" + "Q4b/+CvUodLkaq7B8MHz0BV8HHcBoph6DYaRmO/N+hPauIuSp6XyaGYcEefGKVKj\n" + "G2+fcsdyXsoijNdL8vNKwm4j2gVwCBnw16J00yfFoV46YcbfqEdJB2je0XSvwXqt\n" + "14AOTngxso2h9k9HLtrfpO1ZG/B5AcCMs1lzbZ2fp5DPHtjvvmvA2RJqgo3yjw4W\n" + "4DHAuTglYFlC3mDHNfNtcGP20JvepcQNzNP2UzwcpOc94hfKikOFw+gf9Vf1qd0y\n" + "h/Sk6OZHn2+JVUPiWHIQV98Vtoh4RmUZDJD+b55ia3fQGTGzt4z1XFzQYSva5sfs\n" + "wocS/papthqWldQU7x+3wofNd5CNU1x6WKXG/yw30IT/4F8ADJD6GeygNT8QJYvt\n" + "u/8lAkbOy6B9xGmSvr0Kk1oq9P2NshA6kalxp1Oz/DTNDdL4AeBXV3JmM6WWCjGn\n" + "Yy1RT69d0rwYc5u/vnqODz1IjvT90smsrkBumGt791FAFeg=\n" + "-----END CERTIFICATE-----\n"; + +tor_x509_cert_impl_t * +read_cert_from(const char *str) +{ + size_t len = strlen(str); + uint8_t *raw_cert = tor_malloc(len); + size_t true_len = pem_decode(raw_cert, len, str, len, "CERTIFICATE"); + tor_x509_cert_t *cert = tor_x509_cert_decode(raw_cert, true_len); + tor_free(raw_cert); + if (! cert) { + return NULL; + } + tor_x509_cert_impl_t *res = tor_x509_cert_impl_dup_(cert->cert); + tor_x509_cert_free(cert); + return res; +} + +static tor_x509_cert_impl_t * + fixed_try_to_extract_certs_from_tls_cert_out_result = NULL; +static tor_x509_cert_impl_t * + fixed_try_to_extract_certs_from_tls_id_cert_out_result = NULL; + +static void +fixed_try_to_extract_certs_from_tls(int severity, tor_tls_t *tls, + tor_x509_cert_impl_t **cert_out, + tor_x509_cert_impl_t **id_cert_out) +{ + (void) severity; + (void) tls; + *cert_out = fixed_try_to_extract_certs_from_tls_cert_out_result; + *id_cert_out = fixed_try_to_extract_certs_from_tls_id_cert_out_result; +} + static void test_tortls_errno_to_tls_error(void *data) { @@ -348,6 +455,55 @@ test_tortls_is_server(void *arg) crypto_pk_free(pk2); } +static void +test_tortls_verify(void *ignored) +{ + (void)ignored; + int ret; + tor_tls_t *tls; + crypto_pk_t *k = NULL; + tor_x509_cert_impl_t *cert1 = NULL, *cert2 = NULL, *invalidCert = NULL, + *validCert = NULL, *caCert = NULL; + + validCert = read_cert_from(validCertString); + caCert = read_cert_from(caCertString); + invalidCert = read_cert_from(notCompletelyValidCertString); + + tls = tor_malloc_zero(sizeof(tor_tls_t)); + + MOCK(try_to_extract_certs_from_tls, fixed_try_to_extract_certs_from_tls); + + fixed_try_to_extract_certs_from_tls_cert_out_result = cert1; + ret = tor_tls_verify(LOG_WARN, tls, &k); + tt_int_op(ret, OP_EQ, -1); + + fixed_try_to_extract_certs_from_tls_id_cert_out_result = cert2; + ret = tor_tls_verify(LOG_WARN, tls, &k); + tt_int_op(ret, OP_EQ, -1); + + fixed_try_to_extract_certs_from_tls_cert_out_result = invalidCert; + fixed_try_to_extract_certs_from_tls_id_cert_out_result = invalidCert; + + ret = tor_tls_verify(LOG_WARN, tls, &k); + tt_int_op(ret, OP_EQ, -1); + + fixed_try_to_extract_certs_from_tls_cert_out_result = validCert; + fixed_try_to_extract_certs_from_tls_id_cert_out_result = caCert; + + ret = tor_tls_verify(LOG_WARN, tls, &k); + tt_int_op(ret, OP_EQ, 0); + tt_assert(k); + + done: + UNMOCK(try_to_extract_certs_from_tls); + if (cert1) + tor_x509_cert_impl_free_(cert1); + if (cert2) + tor_x509_cert_impl_free_(cert2); + tor_free(tls); + crypto_pk_free(k); +} + #define LOCAL_TEST_CASE(name, flags) \ { #name, test_tortls_##name, (flags|TT_FORK), NULL, NULL } @@ -367,5 +523,6 @@ struct testcase_t tortls_tests[] = { LOCAL_TEST_CASE(address, TT_FORK), LOCAL_TEST_CASE(is_server, 0), LOCAL_TEST_CASE(bridge_init, TT_FORK), + LOCAL_TEST_CASE(verify, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_tortls.h b/src/test/test_tortls.h new file mode 100644 index 0000000000..c997934ebc --- /dev/null +++ b/src/test/test_tortls.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2010-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TEST_TORTLS_H +#define TEST_TORTLS_H + +tor_x509_cert_impl_t *read_cert_from(const char *str); + +extern const char *notCompletelyValidCertString; +extern const char *validCertString; +extern const char *caCertString; + +#endif diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c index 3ae0f04b7d..3abf412655 100644 --- a/src/test/test_tortls_openssl.c +++ b/src/test/test_tortls_openssl.c @@ -44,6 +44,8 @@ ENABLE_GCC_WARNING(redundant-decls) #include "test/test.h" #include "test/log_test_helpers.h" +#include "test/test_tortls.h" + #define NS_MODULE tortls #ifndef HAVE_SSL_STATE @@ -802,164 +804,6 @@ test_tortls_client_is_using_v2_ciphers(void *ignored) #endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */ } -#ifndef OPENSSL_OPAQUE -static X509 *fixed_try_to_extract_certs_from_tls_cert_out_result = NULL; -static X509 *fixed_try_to_extract_certs_from_tls_id_cert_out_result = NULL; - -static void -fixed_try_to_extract_certs_from_tls(int severity, tor_tls_t *tls, - tor_x509_cert_impl_t **cert_out, - tor_x509_cert_impl_t **id_cert_out) -{ - (void) severity; - (void) tls; - *cert_out = fixed_try_to_extract_certs_from_tls_cert_out_result; - *id_cert_out = fixed_try_to_extract_certs_from_tls_id_cert_out_result; -} -#endif /* !defined(OPENSSL_OPAQUE) */ - -#ifndef OPENSSL_OPAQUE -static const char* notCompletelyValidCertString = - "-----BEGIN CERTIFICATE-----\n" - "MIICVjCCAb8CAg37MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n" - "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n" - "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n" - "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n" - "ODIyMDUyNzIzWhcNMTcwODIxMDUyNzIzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n" - "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n" - "ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYBBrx5PlP0WNI/ZdzD\n" - "+6Pktmurn+F2kQYbtc7XQh8/LTBvCo+P6iZoLEmUA9e7EXLRxgU1CVqeAi7QcAn9\n" - "MwBlc8ksFJHB0rtf9pmf8Oza9E0Bynlq/4/Kb1x+d+AyhL7oK9tQwB24uHOueHi1\n" - "C/iVv8CSWKiYe6hzN1txYe8rAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAASPdjigJ\n" - "kXCqKWpnZ/Oc75EUcMi6HztaW8abUMlYXPIgkV2F7YanHOB7K4f7OOLjiz8DTPFf\n" - "jC9UeuErhaA/zzWi8ewMTFZW/WshOrm3fNvcMrMLKtH534JKvcdMg6qIdjTFINIr\n" - "evnAhf0cwULaebn+lMs8Pdl7y37+sfluVok=\n" - "-----END CERTIFICATE-----\n"; -#endif /* !defined(OPENSSL_OPAQUE) */ - -static const char* validCertString = "-----BEGIN CERTIFICATE-----\n" - "MIIDpTCCAY0CAg3+MA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMREwDwYD\n" - "VQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIGA1UECgwLVG9yIFRl\n" - "c3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkwNjEzMzk1OVoXDTQz\n" - "MDEyMjEzMzk1OVowVjELMAkGA1UEBhMCVVMxEDAOBgNVBAcMB0NoaWNhZ28xFDAS\n" - "BgNVBAoMC1RvciBUZXN0aW5nMR8wHQYDVQQDDBZ0ZXN0aW5nLnRvcnByb2plY3Qu\n" - "b3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoT6uyVVhWyOF3wkHjjYbd\n" - "nKaykyRv4JVtKQdZ4OpEErmX1zw4MmyzpQNV6iR4bQnWiyLfzyVJMZDIC/WILBfX\n" - "w2Pza/yuLgUvDc3twMuhOACzOQVO8PrEF/aVv2+hbCCy2udXvKhnYn+CCXl3ozc8\n" - "XcKYvujTXDyvGWY3xwAjlQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCUvnhzQWuQ\n" - "MrN+pERkE+zcTI/9dGS90rUMMLgu8VDNqTa0TUQh8uO0EQ6uDvI8Js6e8tgwS0BR\n" - "UBahqb7ZHv+rejGCBr5OudqD+x4STiiuPNJVs86JTLN8SpM9CHjIBH5WCCN2KOy3\n" - "mevNoRcRRyYJzSFULCunIK6FGulszigMYGscrO4oiTkZiHPh9KvWT40IMiHfL+Lw\n" - "EtEWiLex6064LcA2YQ1AMuSZyCexks63lcfaFmQbkYOKqXa1oLkIRuDsOaSVjTfe\n" - "vec+X6jvf12cFTKS5WIeqkKF2Irt+dJoiHEGTe5RscUMN/f+gqHPzfFz5dR23sxo\n" - "g+HC6MZHlFkLAOx3wW6epPS8A/m1mw3zMPoTnb2U2YYt8T0dJMMlUn/7Y1sEAa+a\n" - "dSTMaeUf6VnJ//11m454EZl1to9Z7oJOgqmFffSrdD4BGIWe8f7hhW6L1Enmqe/J\n" - "BKL3wbzZh80O1W0bndAwhnEEhlzneFY84cbBo9pmVxpODHkUcStpr5Z7pBDrcL21\n" - "Ss/aB/1YrsVXhdvJdOGxl3Mnl9dUY57CympLGlT8f0pPS6GAKOelECOhFMHmJd8L\n" - "dj3XQSmKtYHevZ6IvuMXSlB/fJvSjSlkCuLo5+kJoaqPuRu+i/S1qxeRy3CBwmnE\n" - "LdSNdcX4N79GQJ996PA8+mUCQG7YRtK+WA==\n" - "-----END CERTIFICATE-----\n"; - -static const char* caCertString = "-----BEGIN CERTIFICATE-----\n" - "MIIFjzCCA3egAwIBAgIJAKd5WgyfPMYRMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV\n" - "BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIG\n" - "A1UECgwLVG9yIFRlc3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkw\n" - "NjEzMzc0MVoXDTQzMDEyMjEzMzc0MVowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM\n" - "CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRQwEgYDVQQKDAtUb3IgVGVzdGlu\n" - "ZzEUMBIGA1UEAwwLVG9yIFRlc3RpbmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n" - "ggIKAoICAQCpLMUEiLW5leUgBZoEJms2V7lZRhIAjnJBhVMHD0e3UubNknmaQoxf\n" - "ARz3rvqOaRd0JlV+qM9qE0DjiYcCVP1cAfqAo9d83uS1vwY3YMVJzADlaIiHfyVW\n" - "uEgBy0vvkeUBqaua24dYlcwsemOiXYLu41yM1wkcGHW1AhBNHppY6cznb8TyLgNM\n" - "2x3SGUdzc5XMyAFx51faKGBA3wjs+Hg1PLY7d30nmCgEOBavpm5I1disM/0k+Mcy\n" - "YmAKEo/iHJX/rQzO4b9znP69juLlR8PDBUJEVIG/CYb6+uw8MjjUyiWXYoqfVmN2\n" - "hm/lH8b6rXw1a2Aa3VTeD0DxaWeacMYHY/i01fd5n7hCoDTRNdSw5KJ0L3Z0SKTu\n" - "0lzffKzDaIfyZGlpW5qdouACkWYzsaitQOePVE01PIdO30vUfzNTFDfy42ccx3Di\n" - "59UCu+IXB+eMtrBfsok0Qc63vtF1linJgjHW1z/8ujk8F7/qkOfODhk4l7wngc2A\n" - "EmwWFIFoGaiTEZHB9qteXr4unbXZ0AHpM02uGGwZEGohjFyebEb73M+J57WKKAFb\n" - "PqbLcGUksL1SHNBNAJcVLttX55sO4nbidOS/kA3m+F1R04MBTyQF9qA6YDDHqdI3\n" - "h/3pw0Z4fxVouTYT4/NfRnX4JTP4u+7Mpcoof28VME0qWqD1LnRhFQIDAQABo1Aw\n" - "TjAdBgNVHQ4EFgQUMoAgIXH7pZ3QMRwTjT+DM9Yo/v0wHwYDVR0jBBgwFoAUMoAg\n" - "IXH7pZ3QMRwTjT+DM9Yo/v0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n" - "AgEAUJxacjXR9sT+Xs6ISFiUsyd0T6WVKMnV46xrYJHirGfx+krWHrjxMY+ZtxYD\n" - "DBDGlo11Qc4v6QrclNf5QUBfIiGQsP9Cm6hHcQ+Tpg9HHCgSqG1YNPwCPReCR4br\n" - "BLvLfrfkcBL2IWM0PdQdCze+59DBfipsULD2mEn9fjYRXQEwb2QWtQ9qRc20Yb/x\n" - "Q4b/+CvUodLkaq7B8MHz0BV8HHcBoph6DYaRmO/N+hPauIuSp6XyaGYcEefGKVKj\n" - "G2+fcsdyXsoijNdL8vNKwm4j2gVwCBnw16J00yfFoV46YcbfqEdJB2je0XSvwXqt\n" - "14AOTngxso2h9k9HLtrfpO1ZG/B5AcCMs1lzbZ2fp5DPHtjvvmvA2RJqgo3yjw4W\n" - "4DHAuTglYFlC3mDHNfNtcGP20JvepcQNzNP2UzwcpOc94hfKikOFw+gf9Vf1qd0y\n" - "h/Sk6OZHn2+JVUPiWHIQV98Vtoh4RmUZDJD+b55ia3fQGTGzt4z1XFzQYSva5sfs\n" - "wocS/papthqWldQU7x+3wofNd5CNU1x6WKXG/yw30IT/4F8ADJD6GeygNT8QJYvt\n" - "u/8lAkbOy6B9xGmSvr0Kk1oq9P2NshA6kalxp1Oz/DTNDdL4AeBXV3JmM6WWCjGn\n" - "Yy1RT69d0rwYc5u/vnqODz1IjvT90smsrkBumGt791FAFeg=\n" - "-----END CERTIFICATE-----\n"; - -static X509 * -read_cert_from(const char *str) -{ - BIO *bio = BIO_new(BIO_s_mem()); - BIO_write(bio, str, (int) strlen(str)); - X509 *res = PEM_read_bio_X509(bio, NULL, NULL, NULL); - BIO_free(bio); - return res; -} - -#ifndef OPENSSL_OPAQUE -static void -test_tortls_verify(void *ignored) -{ - (void)ignored; - int ret; - tor_tls_t *tls; - crypto_pk_t *k = NULL; - X509 *cert1 = NULL, *cert2 = NULL, *invalidCert = NULL, - *validCert = NULL, *caCert = NULL; - - cert1 = tor_malloc_zero(sizeof(X509)); - cert1->references = 10; - - cert2 = tor_malloc_zero(sizeof(X509)); - cert2->references = 10; - - validCert = read_cert_from(validCertString); - caCert = read_cert_from(caCertString); - invalidCert = read_cert_from(notCompletelyValidCertString); - - tls = tor_malloc_zero(sizeof(tor_tls_t)); - ret = tor_tls_verify(LOG_WARN, tls, &k); - tt_int_op(ret, OP_EQ, -1); - - MOCK(try_to_extract_certs_from_tls, fixed_try_to_extract_certs_from_tls); - - fixed_try_to_extract_certs_from_tls_cert_out_result = cert1; - ret = tor_tls_verify(LOG_WARN, tls, &k); - tt_int_op(ret, OP_EQ, -1); - - fixed_try_to_extract_certs_from_tls_id_cert_out_result = cert2; - ret = tor_tls_verify(LOG_WARN, tls, &k); - tt_int_op(ret, OP_EQ, -1); - - fixed_try_to_extract_certs_from_tls_cert_out_result = invalidCert; - fixed_try_to_extract_certs_from_tls_id_cert_out_result = invalidCert; - - ret = tor_tls_verify(LOG_WARN, tls, &k); - tt_int_op(ret, OP_EQ, -1); - - fixed_try_to_extract_certs_from_tls_cert_out_result = validCert; - fixed_try_to_extract_certs_from_tls_id_cert_out_result = caCert; - - ret = tor_tls_verify(LOG_WARN, tls, &k); - tt_int_op(ret, OP_EQ, 0); - tt_assert(k); - - done: - UNMOCK(try_to_extract_certs_from_tls); - tor_free(cert1); - tor_free(cert2); - tor_free(tls); - tor_free(k); -} -#endif /* !defined(OPENSSL_OPAQUE) */ - #ifndef OPENSSL_OPAQUE static int fixed_ssl_pending_result = 0; @@ -2429,7 +2273,6 @@ struct testcase_t tortls_openssl_tests[] = { INTRUSIVE_TEST_CASE(get_ciphersuite_name, 0), INTRUSIVE_TEST_CASE(classify_client_ciphers, 0), LOCAL_TEST_CASE(client_is_using_v2_ciphers, 0), - INTRUSIVE_TEST_CASE(verify, 0), INTRUSIVE_TEST_CASE(get_pending_bytes, 0), INTRUSIVE_TEST_CASE(SSL_SESSION_get_master_key, 0), INTRUSIVE_TEST_CASE(get_tlssecrets, 0), From 274efb126324f3f8a7e98b15a697c0038a9803d0 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Sep 2018 14:37:42 -0400 Subject: [PATCH 74/76] Use FREE_AND_NULL for impl types --- src/lib/tls/tortls.c | 17 ++++++----------- src/lib/tls/tortls_internal.h | 6 +++++- src/lib/tls/tortls_nss.c | 6 +++++- src/lib/tls/tortls_openssl.c | 5 ++++- src/lib/tls/x509.c | 5 ++--- src/lib/tls/x509_internal.h | 2 ++ src/test/test_tortls.c | 6 ++---- src/test/test_x509.c | 3 +-- 8 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 64c26f7e14..923b0db4c4 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -335,12 +335,9 @@ tor_tls_context_init_certificates(tor_tls_context_t *result, tor_free(nickname); tor_free(nn2); - if (cert) - tor_x509_cert_impl_free_(cert); - if (idcert) - tor_x509_cert_impl_free_(idcert); - if (authcert) - tor_x509_cert_impl_free_(authcert); + tor_x509_cert_impl_free(cert); + tor_x509_cert_impl_free(idcert); + tor_x509_cert_impl_free(authcert); crypto_pk_free(rsa); crypto_pk_free(rsa_auth); @@ -379,7 +376,7 @@ tor_tls_free_(tor_tls_t *tls) size_t r,w; tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */ } - tor_tls_impl_free_(tls->ssl); + tor_tls_impl_free(tls->ssl); tls->ssl = NULL; #ifdef ENABLE_OPENSSL tls->negotiated_callback = NULL; @@ -424,10 +421,8 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity) rv = 0; done: - if (cert) - tor_x509_cert_impl_free_(cert); - if (id_cert) - tor_x509_cert_impl_free_(id_cert); + tor_x509_cert_impl_free(cert); + tor_x509_cert_impl_free(id_cert); tor_x509_cert_free(peer_x509); tor_x509_cert_free(id_x509); diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h index b9e01e0c54..2bf2212104 100644 --- a/src/lib/tls/tortls_internal.h +++ b/src/lib/tls/tortls_internal.h @@ -28,8 +28,12 @@ int tor_tls_context_init_certificates(tor_tls_context_t *result, unsigned key_lifetime, unsigned flags); void tor_tls_impl_free_(tor_tls_impl_t *ssl); +#define tor_tls_impl_free(tls) \ + FREE_AND_NULL(tor_tls_impl_t, tor_tls_impl_free_, (tls)) -void tor_tls_context_impl_free(tor_tls_context_impl_t *); +void tor_tls_context_impl_free_(tor_tls_context_impl_t *); +#define tor_tls_context_impl_free(ctx) \ + FREE_AND_NULL(tor_tls_context_impl_t, tor_tls_context_impl_free_, (ctx)) #ifdef ENABLE_OPENSSL tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl); diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index 266455049e..548a817e63 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -298,8 +298,10 @@ tor_tls_context_new(crypto_pk_t *identity, } void -tor_tls_context_impl_free(tor_tls_context_impl_t *ctx) +tor_tls_context_impl_free_(tor_tls_context_impl_t *ctx) { + if (!ctx) + return; PR_Close(ctx); } @@ -409,6 +411,8 @@ tor_tls_impl_free_(tor_tls_impl_t *tls) { // XXXX This will close the underlying fd, which our OpenSSL version does // not do! + if (!tls) + return; PR_Close(tls); } diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index 6ff03ee818..370e7e4367 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -496,7 +496,7 @@ tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert)) } void -tor_tls_context_impl_free(struct ssl_ctx_st *ctx) +tor_tls_context_impl_free_(struct ssl_ctx_st *ctx) { if (!ctx) return; @@ -1150,6 +1150,9 @@ tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) void tor_tls_impl_free_(tor_tls_impl_t *ssl) { + if (!ssl) + return; + #ifdef SSL_set_tlsext_host_name SSL_set_tlsext_host_name(ssl, NULL); #endif diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index 2e70206462..442cdf2c74 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -76,8 +76,7 @@ tor_x509_cert_free_(tor_x509_cert_t *cert) { if (! cert) return; - if (cert->cert) - tor_x509_cert_impl_free_(cert->cert); + tor_x509_cert_impl_free(cert->cert); #ifdef ENABLE_OPENSSL tor_free(cert->encoded); #endif @@ -131,7 +130,7 @@ tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert)) err: tor_free(cert); log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate."); - tor_x509_cert_impl_free_(x509_cert); + tor_x509_cert_impl_free(x509_cert); return NULL; } diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h index c08705cb25..139ecedd23 100644 --- a/src/lib/tls/x509_internal.h +++ b/src/lib/tls/x509_internal.h @@ -41,6 +41,8 @@ int tor_x509_check_cert_lifetime_internal(int severity, int future_tolerance); void tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert); +#define tor_x509_cert_impl_free(cert) \ + FREE_AND_NULL(tor_x509_cert_impl_t, tor_x509_cert_impl_free_, (cert)) tor_x509_cert_impl_t *tor_x509_cert_impl_dup_(tor_x509_cert_impl_t *cert); #ifdef ENABLE_OPENSSL int tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert); diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index b260310adb..8e8487a408 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -496,10 +496,8 @@ test_tortls_verify(void *ignored) done: UNMOCK(try_to_extract_certs_from_tls); - if (cert1) - tor_x509_cert_impl_free_(cert1); - if (cert2) - tor_x509_cert_impl_free_(cert2); + tor_x509_cert_impl_free(cert1); + tor_x509_cert_impl_free(cert2); tor_free(tls); crypto_pk_free(k); } diff --git a/src/test/test_x509.c b/src/test/test_x509.c index 9163977bd8..9ec0657d83 100644 --- a/src/test/test_x509.c +++ b/src/test/test_x509.c @@ -57,8 +57,7 @@ test_x509_cert_new_failing_digest(void *arg) done: crypto_pk_free(pk1); crypto_pk_free(pk2); - if (impl) - tor_x509_cert_impl_free_(impl); + tor_x509_cert_impl_free(impl); UNMOCK(crypto_digest); teardown_capture_of_logs(); } From c50537fd9462af45f0a53aa4f0d5ca03a08fc41e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Sep 2018 19:23:55 -0400 Subject: [PATCH 75/76] Fix a pair of remaining leaks in tortls_nss.c Fun fact: PR_Close leaks memory if its socket is not valid. --- src/lib/tls/tortls_nss.c | 8 +++++++- src/test/test_router.c | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index 548a817e63..8e6c0d2d77 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -361,7 +361,13 @@ tor_tls_new(tor_socket_t sock, int is_server) (void)sock; tor_tls_context_t *ctx = tor_tls_context_get(is_server); - PRFileDesc *tcp = PR_ImportTCPSocket(sock); + PRFileDesc *tcp = NULL; + if (SOCKET_OK(sock)) { + tcp = PR_ImportTCPSocket(sock); + } else { + tcp = PR_NewTCPSocket(); + } + if (!tcp) return NULL; diff --git a/src/test/test_router.c b/src/test/test_router.c index c6a2452c8c..14e739d6fc 100644 --- a/src/test/test_router.c +++ b/src/test/test_router.c @@ -52,6 +52,8 @@ NS(router_get_my_routerinfo)(void) mock_routerinfo->onion_pkey = crypto_pk_dup_key(tap_key); mock_routerinfo->bandwidthrate = 9001; mock_routerinfo->bandwidthburst = 9002; + crypto_pk_free(ident_key); + crypto_pk_free(tap_key); } return mock_routerinfo; From d644c93ae9373f99d95870c7b752b790f8714201 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Sep 2018 19:41:59 -0400 Subject: [PATCH 76/76] Resolve openssl-only memory leaks --- src/lib/tls/tortls_openssl.c | 2 +- src/lib/tls/x509.c | 3 +-- src/test/test_tortls_openssl.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index 370e7e4367..9371c9393f 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -609,7 +609,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, if (result->my_id_cert) { X509_STORE *s = SSL_CTX_get_cert_store(result->ctx); tor_assert(s); - X509_STORE_add_cert(s, X509_dup(result->my_id_cert->cert)); + X509_STORE_add_cert(s, result->my_id_cert->cert); } } SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index 442cdf2c74..0d62ee41af 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -128,9 +128,8 @@ tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert)) return cert; err: - tor_free(cert); log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate."); - tor_x509_cert_impl_free(x509_cert); + tor_x509_cert_free(cert); return NULL; } diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c index 3abf412655..2e71de43ae 100644 --- a/src/test/test_tortls_openssl.c +++ b/src/test/test_tortls_openssl.c @@ -600,7 +600,7 @@ test_tortls_get_my_client_auth_key(void *ignored) tt_assert(ret == expected); done: - tor_free(expected); + crypto_pk_free(expected); tor_free(ctx); }