commit 0db5c549571eb0098a3f709ffb25c2e4909ca01d Merge: fd994f55c d644c93ae Author: Nick Mathewson nickm@torproject.org Date: Tue Sep 4 20:21:07 2018 -0400
Merge branch 'nss_squashed' into nss_merge
Makefile.am | 8 + changes/NSS | 7 + changes/feature26815 | 3 + changes/feature26816 | 11 + config.rust.in | 2 + configure.ac | 30 + src/app/config/config.c | 16 +- src/app/include.am | 8 +- src/core/crypto/onion_ntor.c | 2 +- src/core/crypto/relay_crypto.c | 2 +- src/core/mainloop/connection.c | 25 +- src/core/mainloop/main.c | 6 +- src/core/or/channeltls.c | 1 + src/core/or/connection_or.c | 1 + src/core/or/or.h | 3 +- src/ext/tinytest.c | 11 + src/feature/control/control.c | 4 +- src/feature/nodelist/parsecommon.c | 2 +- src/feature/nodelist/torcert.c | 1 + src/feature/relay/router.c | 3 +- src/feature/relay/routerkeys.c | 1 + src/feature/rend/rendmid.c | 2 +- src/feature/rend/rendservice.c | 2 +- src/lib/crypt_ops/aes_nss.c | 106 + src/lib/crypt_ops/{aes.c => aes_openssl.c} | 4 +- src/lib/crypt_ops/compat_openssl.h | 6 + src/lib/crypt_ops/crypto.c | 509 ---- src/lib/crypt_ops/crypto_cipher.c | 190 ++ src/lib/crypt_ops/{crypto.h => crypto_cipher.h} | 25 +- src/lib/crypt_ops/crypto_dh.c | 484 +--- src/lib/crypt_ops/crypto_dh.h | 23 +- src/lib/crypt_ops/crypto_dh_nss.c | 207 ++ src/lib/crypt_ops/crypto_dh_openssl.c | 471 ++++ src/lib/crypt_ops/crypto_digest.c | 262 ++- src/lib/crypt_ops/crypto_digest.h | 6 +- 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 | 193 ++ src/lib/crypt_ops/crypto_init.h | 34 + src/lib/crypt_ops/crypto_nss_mgt.c | 132 ++ src/lib/crypt_ops/crypto_nss_mgt.h | 34 + src/lib/crypt_ops/crypto_ope.c | 2 +- src/lib/crypt_ops/crypto_openssl_mgt.c | 228 +- src/lib/crypt_ops/crypto_openssl_mgt.h | 15 +- src/lib/crypt_ops/crypto_pwbox.c | 2 +- src/lib/crypt_ops/crypto_rand.c | 122 +- src/lib/crypt_ops/crypto_rsa.c | 1001 ++------ src/lib/crypt_ops/crypto_rsa.h | 48 +- src/lib/crypt_ops/crypto_rsa_nss.c | 738 ++++++ src/lib/crypt_ops/crypto_rsa_openssl.c | 590 +++++ src/lib/crypt_ops/crypto_s2k.c | 50 +- src/lib/crypt_ops/crypto_util.c | 29 +- src/lib/crypt_ops/crypto_util.h | 9 - src/lib/crypt_ops/include.am | 32 +- 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/lib/include.libdonna.am | 2 +- src/lib/process/daemon.c | 25 +- src/lib/process/daemon.h | 4 +- src/lib/tls/include.am | 24 +- src/lib/tls/tortls.c | 2673 ++------------------- src/lib/tls/tortls.h | 210 +- src/lib/tls/tortls_internal.h | 76 + src/lib/tls/tortls_nss.c | 741 ++++++ src/lib/tls/tortls_openssl.c | 1708 ++++++++++++++ src/lib/tls/tortls_st.h | 74 + src/lib/tls/x509.c | 143 ++ src/lib/tls/x509.h | 75 + src/lib/tls/x509_internal.h | 53 + src/lib/tls/x509_nss.c | 450 ++++ src/lib/tls/x509_openssl.c | 461 ++++ src/rust/build.rs | 1 + src/test/bench.c | 14 +- src/test/fuzz/fuzzing_common.c | 15 +- src/test/fuzz/include.am | 4 +- src/test/include.am | 35 +- src/test/log_test_helpers.h | 37 +- src/test/test-timers.c | 5 + src/test/test.c | 7 + src/test/test.h | 3 + src/test/test_controller.c | 2 +- src/test/test_crypto.c | 127 +- src/test/test_crypto_ope.c | 4 +- src/test/test_crypto_slow.c | 2 + src/test/test_dir_handle_get.c | 2 +- src/test/test_hs_client.c | 2 +- src/test/test_hs_ntor_cl.c | 7 +- src/test/test_introduce.c | 2 +- src/test/test_link_handshake.c | 34 +- src/test/test_ntor_cl.c | 8 +- src/test/test_pem.c | 122 + src/test/test_relaycell.c | 2 +- src/test/test_router.c | 2 + src/test/test_routerkeys.c | 2 +- src/test/test_shared_random.c | 2 +- src/test/test_tortls.c | 2863 +++-------------------- src/test/test_tortls.h | 13 + src/test/test_tortls_openssl.c | 2277 ++++++++++++++++++ src/test/test_util_slow.c | 2 +- src/test/test_workqueue.c | 1 + src/test/test_x509.c | 203 ++ src/test/testing_common.c | 17 +- src/test/testing_rsakeys.c | 3 +- src/tools/include.am | 24 +- src/tools/tor-gencert.c | 23 +- 108 files changed, 11268 insertions(+), 7160 deletions(-)
diff --cc src/lib/tls/tortls.c index 7135820d2,923b0db4c..3ae3a1a09 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@@ -3,168 -3,19 +3,25 @@@ * 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 <winsock.h>"*/ - #include <winsock2.h> - #include <ws2tcpip.h> - #endif - - #include "lib/crypt_ops/crypto.h" - #include "lib/crypt_ops/crypto_rand.h" - #include "lib/crypt_ops/crypto_dh.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 <openssl/opensslv.h> - - #ifdef OPENSSL_NO_EC - #error "We require OpenSSL with ECC support" - #endif - - #include <openssl/ssl.h> - #include <openssl/ssl3.h> - #include <openssl/err.h> - #include <openssl/tls1.h> - #include <openssl/asn1.h> - #include <openssl/bio.h> - #include <openssl/bn.h> - #include <openssl/rsa.h> - - ENABLE_GCC_WARNING(redundant-decls) - #define TORTLS_PRIVATE + #define TOR_X509_PRIVATE + #include "lib/tls/x509.h" + #include "lib/tls/x509_internal.h" #include "lib/tls/tortls.h" - #include "lib/log/log.h" + #include "lib/tls/tortls_st.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 <stdlib.h> - #include <string.h> - - #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" - - /** 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') */ + #include "lib/crypt_ops/crypto_rsa.h" + #include "lib/crypt_ops/crypto_rand.h" ++#include "lib/net/socket.h" + - /* 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 ++#ifdef _WIN32 ++ #include <winsock2.h> ++ #include <ws2tcpip.h> +#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); - - static int check_cert_lifetime_internal(int severity, const X509 *cert, - time_t now, - int past_tolerance, int future_tolerance); + #include <time.h>
/** Global TLS contexts. We keep them here because nobody else needs * to touch them. diff --cc src/lib/tls/tortls_openssl.c index 000000000,9371c9393..ab9712962 mode 000000,100644..100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@@ -1,0 -1,1706 +1,1708 @@@ + /* 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 + #define TOR_X509_PRIVATE + + #ifdef _WIN32 + /* We need to include these here, or else the dtls1.h header will include + * <winsock.h> and mess things up, in at least some openssl versions. */ + #include <winsock2.h> + #include <ws2tcpip.h> + #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" + #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. */ + DISABLE_GCC_WARNING(redundant-decls) + + #include <openssl/opensslv.h> + + #ifdef OPENSSL_NO_EC + #error "We require OpenSSL with ECC support" + #endif + + #include <openssl/ssl.h> + #include <openssl/ssl3.h> + #include <openssl/err.h> + #include <openssl/tls1.h> + #include <openssl/asn1.h> + #include <openssl/bio.h> + #include <openssl/bn.h> + #include <openssl/rsa.h> + + 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 <stdlib.h> + #include <string.h> + + #include "lib/arch/bytes.h" + + /* Copied from or.h */ + #define LEGAL_NICKNAME_CHARACTERS \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + + #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 <b>tls</b> into the + * <b>sz</b>-byte buffer at <b>buf</b>. */ + 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 <b>err</b> as returned by ERR_get_error(), which was + * received while performing an operation <b>doing</b> on <b>tls</b>. Log + * the message at <b>severity</b>, in log domain <b>domain</b>. */ + 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 <b>severity</b> in log domain + * <b>domain</b>. Use <b>doing</b> 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); + } + } + + #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 <b>severity</b> and describe the + * current action as <b>doing</b>. + */ + 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: <syscall error while %s> (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 + + /** Return true iff the other side of <b>tls</b> has authenticated to us, and + * the key certified in <b>cert</b> 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); ++ tor_x509_cert_t *peer = tor_tls_get_peer_cert((tor_tls_t *)tls); ++ if (!peer) ++ return 0; ++ ++ X509 *peercert = peer->cert; + 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(cert->cert); + + result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1; + - X509_free(peercert); ++ tor_x509_cert_free(peer); + if (link_key) + EVP_PKEY_free(link_key); + if (cert_key) + EVP_PKEY_free(cert_key); + + return result; + } + + 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 + + /** Create a new TLS context for use with Tor TLS handshakes. + * <b>identity</b> 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) + { + EVP_PKEY *pkey = NULL; + tor_tls_context_t *result = NULL; + + tor_tls_init(); + + result = tor_malloc_zero(sizeof(tor_tls_context_t)); + result->refcnt = 1; + + if (! is_client) { + if (tor_tls_context_init_certificates(result, identity, key_lifetime, + flags) < 0) { + goto error; + } + } + + #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 (result->my_link_cert && + !SSL_CTX_use_certificate(result->ctx, + result->my_link_cert->cert)) { + goto error; + } + if (result->my_id_cert) { + X509_STORE *s = SSL_CTX_get_cert_store(result->ctx); + tor_assert(s); + X509_STORE_add_cert(s, result->my_id_cert->cert); + } + } + SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); + if (!is_client) { + 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; + 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); + + return result; + + error: + tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context"); + if (pkey) + EVP_PKEY_free(pkey); + if (result) + tor_tls_context_decref(result); + 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 <b>tls</b> */ + 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 <b>m</b> does not support the cipher with ID <b>cipher</b>; + * 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 <b>ssl</b>, 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 <b>ssl</b> 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 <b>ssl</b>, and the connection + * changes state. We use this: + * <ul><li>To alter the state of the handshake partway through, so we + * do not send or request extra certificates in v2 handshakes.</li> + * <li>To detect renegotiation</li></ul> + */ + 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; + } 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, 0); + 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 <b>cb</b> to be called with argument <b>arg</b> whenever <b>tls</b> + * next gets a client-side renegotiate in the middle of a read. Do not + * invoke this function until <em>after</em> 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 + * <b>tls</b>. + */ + 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 + * <b>tls</b>. (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) && ... */ + } + + 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 + SSL_free(ssl); + } + + /** Underlying function for TLS reading. Reads up to <b>len</b> + * characters from <b>tls</b> into <b>cp</b>. 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(len<INT_MAX); + r = SSL_read(tls->ssl, 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 <b>n</b> + * characters from <b>cp</b> onto <b>tls</b>. 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 <b>tls</b>. 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 <b>tls</b>. 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; + } + + /** 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 <b>tls</b>, and store them in *<b>cert_out</b> and + * *<b>id_cert_out</b> respectively. Log all messages at level + * <b>severity</b>. + * + * 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; i<num_in_chain; ++i) { + id_cert = sk_X509_value(chain, i); + if (X509_cmp(id_cert, cert) != 0) + break; + } + *id_cert_out = id_cert; + } + + /** Return the number of bytes available for reading from <b>tls</b>. + */ + int + tor_tls_get_pending_bytes(tor_tls_t *tls) + { + tor_assert(tls); + return SSL_pending(tls->ssl); + } + + /** If <b>tls</b> 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 <b>tls</b> since the last time this + * function was called on <b>tls</b>. */ + 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 <b>tls</b> 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 true iff the server TLS connection <b>tls</b> 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 <b>secrets_out</b> to the value used in + * the v3 handshake to prove that the client knows the TLS secrets for the + * connection <b>tls</b>. 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 <b>context</b> (<b>context_len</b> bytes long) and + * <b>label</b> (a NUL-terminated string), compute a 32-byte secret in + * <b>secrets_out</b> 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 <b>tls</b>. + * Set *<b>rbuf_capacity</b> to the amount of storage allocated for the read + * buffer and *<b>rbuf_bytes</b> to the amount actually used. + * Set *<b>wbuf_capacity</b> to the amount of storage allocated for the write + * buffer and *<b>wbuf_bytes</b> 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 --cc src/test/log_test_helpers.h index dcd329734,330da3811..6a774cdfc --- a/src/test/log_test_helpers.h +++ b/src/test/log_test_helpers.h @@@ -45,12 -44,8 +45,12 @@@ void mock_dump_saved_logs(void)
#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_not_containing(str) \ + assert_log_predicate(mock_saved_log_has_message_not_containing(str), \ - "expected log to not contain " # str); ++ ("expected log to not 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), \ diff --cc src/test/test_router.c index 6e64131fc,14e739d6f..533135669 --- a/src/test/test_router.c +++ b/src/test/test_router.c @@@ -52,10 -49,11 +52,12 @@@ NS(router_get_my_routerinfo)(void mock_routerinfo->platform = tor_strdup("unittest"); mock_routerinfo->cache_info.published_on = now; mock_routerinfo->identity_pkey = crypto_pk_dup_key(ident_key); - mock_routerinfo->onion_pkey = crypto_pk_dup_key(tap_key); + router_set_rsa_onion_pkey(tap_key, &mock_routerinfo->onion_pkey, + &mock_routerinfo->onion_pkey_len); mock_routerinfo->bandwidthrate = 9001; mock_routerinfo->bandwidthburst = 9002; + crypto_pk_free(ident_key); + crypto_pk_free(tap_key); }
return mock_routerinfo; diff --cc src/test/test_tortls_openssl.c index 000000000,2e71de43a..608625288 mode 000000,100644..100644 --- a/src/test/test_tortls_openssl.c +++ b/src/test/test_tortls_openssl.c @@@ -1,0 -1,2305 +1,2277 @@@ + /* 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 <winsock2.h> + #endif + #include <math.h> + + #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 <openssl/opensslv.h> + + #include <openssl/ssl.h> + #include <openssl/ssl3.h> + #include <openssl/err.h> + #include <openssl/asn1t.h> + #include <openssl/x509.h> + #include <openssl/rsa.h> + #include <openssl/evp.h> + #include <openssl/bn.h> + + 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/x509_internal.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" + #include "test/test_tortls.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); + } + } ++#endif ++ ++static tor_x509_cert_t *fixed_x509_cert = NULL; ++static tor_x509_cert_t * ++get_peer_cert_mock_return_fixed(tor_tls_t *tls) ++{ ++ (void)tls; ++ if (fixed_x509_cert) ++ return tor_x509_cert_dup(fixed_x509_cert); ++ else ++ return NULL; ++} + + 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; ++ X509 *cert1 = NULL, *cert2 = NULL, *cert3 = NULL, *cert4 = NULL; ++ tor_x509_cert_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL; ++ crypto_pk_t *k1 = NULL, *k2 = NULL, *k3 = NULL; + - res = tor_tls_cert_matches_key(tls, cert); - tt_int_op(res, OP_EQ, 0); ++ k1 = pk_generate(1); ++ k2 = pk_generate(2); ++ k3 = pk_generate(3); + - 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); ++ cert1 = tor_tls_create_certificate(k1, k2, "A", "B", 1000); ++ cert2 = tor_tls_create_certificate(k1, k3, "C", "D", 1000); ++ cert3 = tor_tls_create_certificate(k2, k3, "C", "D", 1000); ++ cert4 = tor_tls_create_certificate(k3, k2, "E", "F", 1000); ++ ++ tt_assert(cert1 && cert2 && cert3 && cert4); ++ ++ c1 = tor_x509_cert_new(cert1); cert1 = NULL; ++ c2 = tor_x509_cert_new(cert2); cert2 = NULL; ++ c3 = tor_x509_cert_new(cert3); cert3 = NULL; ++ c4 = tor_x509_cert_new(cert4); cert4 = NULL; ++ ++ tt_assert(c1 && c2 && c3 && c4); ++ ++ MOCK(tor_tls_get_peer_cert, get_peer_cert_mock_return_fixed); ++ ++ fixed_x509_cert = NULL; ++ /* If the peer has no certificate, it shouldn't match anything. */ ++ tt_assert(! tor_tls_cert_matches_key(NULL, c1)); ++ tt_assert(! tor_tls_cert_matches_key(NULL, c2)); ++ tt_assert(! tor_tls_cert_matches_key(NULL, c3)); ++ tt_assert(! tor_tls_cert_matches_key(NULL, c4)); ++ fixed_x509_cert = c1; ++ /* If the peer has a certificate, it should match every cert with the same ++ * subject key. */ ++ tt_assert(tor_tls_cert_matches_key(NULL, c1)); ++ tt_assert(tor_tls_cert_matches_key(NULL, c2)); ++ tt_assert(! tor_tls_cert_matches_key(NULL, c3)); ++ tt_assert(! tor_tls_cert_matches_key(NULL, c4)); + + 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); ++ tor_x509_cert_free(c1); ++ tor_x509_cert_free(c2); ++ tor_x509_cert_free(c3); ++ tor_x509_cert_free(c4); ++ if (cert1) X509_free(cert1); ++ if (cert2) X509_free(cert2); ++ if (cert3) X509_free(cert3); ++ if (cert4) X509_free(cert4); ++ crypto_pk_free(k1); ++ crypto_pk_free(k2); ++ crypto_pk_free(k3); ++ UNMOCK(tor_tls_get_peer_cert); + } + ++#ifndef OPENSSL_OPAQUE + 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: + crypto_pk_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 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; ++ //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); ++ //tt_int_op(tls->server_handshake_count, OP_EQ, 121); + - tls->server_handshake_count = 127; ++ //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->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 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), ++ LOCAL_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(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 + };
tor-commits@lists.torproject.org