diff options
Diffstat (limited to 'src/lib/tls/tortls.c')
-rw-r--r-- | src/lib/tls/tortls.c | 2547 |
1 files changed, 155 insertions, 2392 deletions
diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 7135820d2f..3ae3a1a096 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -3,168 +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. @@ -174,112 +31,18 @@ STATIC tor_tls_context_t *server_tls_context = NULL; STATIC tor_tls_context_t *client_tls_context = NULL; /**@}*/ -/** True iff tor_tls_init() has been called. */ -static int tls_library_is_initialized = 0; - -/* Module-internal error codes. */ -#define TOR_TLS_SYSCALL_ (MIN_TOR_TLS_ERROR_VAL_ - 2) -#define TOR_TLS_ZERORETURN_ (MIN_TOR_TLS_ERROR_VAL_ - 1) - -/** Write a description of the current state of <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. +/** + * Return the appropriate TLS context. */ -STATIC void -tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) +tor_tls_context_t * +tor_tls_context_get(int is_server) { - unsigned long err; - - while ((err = ERR_get_error()) != 0) { - tor_tls_log_one_error(tls, err, severity, domain, doing); - } + return is_server ? server_tls_context : client_tls_context; } /** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error * code. */ -STATIC int +int tor_errno_to_tls_error(int e) { switch (e) { @@ -297,575 +60,6 @@ tor_errno_to_tls_error(int e) } } -/** Given a TOR_TLS_* error code, return a string equivalent. */ -const char * -tor_tls_err_to_string(int err) -{ - if (err >= 0) - return "[Not an error.]"; - switch (err) { - case TOR_TLS_ERROR_MISC: return "misc error"; - case TOR_TLS_ERROR_IO: return "unexpected close"; - case TOR_TLS_ERROR_CONNREFUSED: return "connection refused"; - case TOR_TLS_ERROR_CONNRESET: return "connection reset"; - case TOR_TLS_ERROR_NO_ROUTE: return "host unreachable"; - case TOR_TLS_ERROR_TIMEOUT: return "connection timed out"; - case TOR_TLS_CLOSE: return "closed"; - case TOR_TLS_WANTREAD: return "want to read"; - case TOR_TLS_WANTWRITE: return "want to write"; - default: return "(unknown error code)"; - } -} - -#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>. - */ -STATIC int -tor_tls_get_error(tor_tls_t *tls, int r, int extra, - const char *doing, int severity, int domain) -{ - int err = SSL_get_error(tls->ssl, r); - int tor_error = TOR_TLS_ERROR_MISC; - switch (err) { - case SSL_ERROR_NONE: - return TOR_TLS_DONE; - case SSL_ERROR_WANT_READ: - return TOR_TLS_WANTREAD; - case SSL_ERROR_WANT_WRITE: - return TOR_TLS_WANTWRITE; - case SSL_ERROR_SYSCALL: - if (extra&CATCH_SYSCALL) - return TOR_TLS_SYSCALL_; - if (r == 0) { - tor_log(severity, LD_NET, "TLS error: unexpected close while %s (%s)", - doing, SSL_state_string_long(tls->ssl)); - tor_error = TOR_TLS_ERROR_IO; - } else { - int e = tor_socket_errno(tls->socket); - tor_log(severity, LD_NET, - "TLS error: <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. - */ -static void -tor_tls_init(void) -{ - check_no_tls_errors(); - - if (!tls_library_is_initialized) { -#ifdef OPENSSL_1_1_API - OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); -#else - SSL_library_init(); - SSL_load_error_strings(); -#endif - -#if (SIZEOF_VOID_P >= 8 && \ - OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1)) - long version = OpenSSL_version_num(); - - /* LCOV_EXCL_START : we can't test these lines on the same machine */ - if (version >= OPENSSL_V_SERIES(1,0,1)) { - /* Warn if we could *almost* be running with much faster ECDH. - If we're built for a 64-bit target, using OpenSSL 1.0.1, but we - don't have one of the built-in __uint128-based speedups, we are - just one build operation away from an accelerated handshake. - - (We could be looking at OPENSSL_NO_EC_NISTP_64_GCC_128 instead of - doing this test, but that gives compile-time options, not runtime - behavior.) - */ - EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - const EC_GROUP *g = key ? EC_KEY_get0_group(key) : NULL; - const EC_METHOD *m = g ? EC_GROUP_method_of(g) : NULL; - const int warn = (m == EC_GFp_simple_method() || - m == EC_GFp_mont_method() || - m == EC_GFp_nist_method()); - EC_KEY_free(key); - - if (warn) - log_notice(LD_GENERAL, "We were built to run on a 64-bit CPU, with " - "OpenSSL 1.0.1 or later, but with a version of OpenSSL " - "that apparently lacks accelerated support for the NIST " - "P-224 and P-256 groups. Building openssl with such " - "support (using the enable-ec_nistp_64_gcc_128 option " - "when configuring it) would make ECDH much faster."); - } - /* LCOV_EXCL_STOP */ -#endif /* (SIZEOF_VOID_P >= 8 && ... */ - - tor_tls_allocate_tor_tls_object_ex_data_index(); - - tls_library_is_initialized = 1; - } -} - -/** Free all global TLS structures. */ -void -tor_tls_free_all(void) -{ - check_no_tls_errors(); - - if (server_tls_context) { - tor_tls_context_t *ctx = server_tls_context; - server_tls_context = NULL; - tor_tls_context_decref(ctx); - } - if (client_tls_context) { - tor_tls_context_t *ctx = client_tls_context; - client_tls_context = NULL; - tor_tls_context_decref(ctx); - } -} - -/** We need to give OpenSSL a callback to verify certificates. This is - * it: We always accept peer certs and complete the handshake. We - * don't validate them until later. - */ -STATIC int -always_accept_verify_cb(int preverify_ok, - X509_STORE_CTX *x509_ctx) -{ - (void) preverify_ok; - (void) x509_ctx; - return 1; -} - -/** Return a newly allocated X509 name with commonName <b>cname</b>. */ -static X509_NAME * -tor_x509_name_new(const char *cname) -{ - int nid; - X509_NAME *name; - /* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */ - if (!(name = X509_NAME_new())) - return NULL; - if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error; - if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC, - (unsigned char*)cname, -1, -1, 0))) - goto error; - /* LCOV_EXCL_BR_STOP */ - return name; - - /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/ - error: - X509_NAME_free(name); - return NULL; - /* LCOV_EXCL_STOP */ -} - -/** Generate and sign an X509 certificate with the public key <b>rsa</b>, - * signed by the private key <b>rsa_sign</b>. The commonName of the - * certificate will be <b>cname</b>; the commonName of the issuer will be - * <b>cname_sign</b>. The cert will be valid for <b>cert_lifetime</b> - * seconds, starting from some time in the past. - * - * Return a certificate on success, NULL on failure. - */ -MOCK_IMPL(STATIC X509 *, -tor_tls_create_certificate,(crypto_pk_t *rsa, - crypto_pk_t *rsa_sign, - const char *cname, - const char *cname_sign, - unsigned int cert_lifetime)) -{ - /* OpenSSL generates self-signed certificates with random 64-bit serial - * numbers, so let's do that too. */ -#define SERIAL_NUMBER_SIZE 8 - - time_t start_time, end_time; - BIGNUM *serial_number = NULL; - unsigned char serial_tmp[SERIAL_NUMBER_SIZE]; - EVP_PKEY *sign_pkey = NULL, *pkey=NULL; - X509 *x509 = NULL; - X509_NAME *name = NULL, *name_issuer=NULL; - - tor_tls_init(); - - /* Make sure we're part-way through the certificate lifetime, rather - * than having it start right now. Don't choose quite uniformly, since - * then we might pick a time where we're about to expire. Lastly, be - * sure to start on a day boundary. */ - time_t now = time(NULL); - /* Our certificate lifetime will be cert_lifetime no matter what, but if we - * start cert_lifetime in the past, we'll have 0 real lifetime. instead we - * start up to (cert_lifetime - min_real_lifetime - start_granularity) in - * the past. */ - const time_t min_real_lifetime = 24*3600; - const time_t start_granularity = 24*3600; - time_t earliest_start_time; - /* Don't actually start in the future! */ - if (cert_lifetime <= min_real_lifetime + start_granularity) { - earliest_start_time = now - 1; - } else { - earliest_start_time = now + min_real_lifetime + start_granularity - - cert_lifetime; - } - start_time = crypto_rand_time_range(earliest_start_time, now); - /* Round the start time back to the start of a day. */ - start_time -= start_time % start_granularity; - - end_time = start_time + cert_lifetime; - - tor_assert(rsa); - tor_assert(cname); - tor_assert(rsa_sign); - tor_assert(cname_sign); - if (!(sign_pkey = crypto_pk_get_evp_pkey_(rsa_sign,1))) - goto error; - if (!(pkey = crypto_pk_get_evp_pkey_(rsa,0))) - goto error; - if (!(x509 = X509_new())) - goto error; - if (!(X509_set_version(x509, 2))) - goto error; - - { /* our serial number is 8 random bytes. */ - crypto_rand((char *)serial_tmp, sizeof(serial_tmp)); - if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL))) - goto error; - if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509)))) - goto error; - } - - if (!(name = tor_x509_name_new(cname))) - goto error; - if (!(X509_set_subject_name(x509, name))) - goto error; - if (!(name_issuer = tor_x509_name_new(cname_sign))) - goto error; - if (!(X509_set_issuer_name(x509, name_issuer))) - goto error; - - if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time)) - goto error; - if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time)) - goto error; - if (!X509_set_pubkey(x509, pkey)) - goto error; - - if (!X509_sign(x509, sign_pkey, EVP_sha256())) - goto error; - - goto done; - error: - if (x509) { - X509_free(x509); - x509 = NULL; - } - done: - tls_log_errors(NULL, LOG_WARN, LD_NET, "generating certificate"); - if (sign_pkey) - EVP_PKEY_free(sign_pkey); - if (pkey) - EVP_PKEY_free(pkey); - if (serial_number) - BN_clear_free(serial_number); - if (name) - X509_NAME_free(name); - if (name_issuer) - X509_NAME_free(name_issuer); - return x509; - -#undef SERIAL_NUMBER_SIZE -} - -/** List of ciphers that servers should select from when the client might be - * claiming extra unsupported ciphers in order to avoid fingerprinting. */ -static const char SERVER_CIPHER_LIST[] = -#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 - -/** Free all storage held in <b>cert</b> */ -void -tor_x509_cert_free_(tor_x509_cert_t *cert) -{ - if (! cert) - return; - if (cert->cert) - X509_free(cert->cert); - tor_free(cert->encoded); - memwipe(cert, 0x03, sizeof(*cert)); - /* LCOV_EXCL_BR_START since cert will never be NULL here */ - tor_free(cert); - /* LCOV_EXCL_BR_STOP */ -} - -/** - * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert". - * - * Steals a reference to x509_cert. - */ -MOCK_IMPL(STATIC tor_x509_cert_t *, -tor_x509_cert_new,(X509 *x509_cert)) -{ - tor_x509_cert_t *cert; - EVP_PKEY *pkey; - RSA *rsa; - int length; - unsigned char *buf = NULL; - - if (!x509_cert) - return NULL; - - length = i2d_X509(x509_cert, &buf); - cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - if (length <= 0 || buf == NULL) { - goto err; - } - cert->encoded_len = (size_t) length; - cert->encoded = tor_malloc(length); - memcpy(cert->encoded, buf, length); - OPENSSL_free(buf); - - cert->cert = x509_cert; - - crypto_common_digests(&cert->cert_digests, - (char*)cert->encoded, cert->encoded_len); - - if ((pkey = X509_get_pubkey(x509_cert)) && - (rsa = EVP_PKEY_get1_RSA(pkey))) { - crypto_pk_t *pk = crypto_new_pk_from_rsa_(rsa); - if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) { - crypto_pk_free(pk); - EVP_PKEY_free(pkey); - goto err; - } - - cert->pkey_digests_set = 1; - crypto_pk_free(pk); - EVP_PKEY_free(pkey); - } - - return cert; - err: - /* LCOV_EXCL_START for the same reason as the exclusion above */ - tor_free(cert); - log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate."); - X509_free(x509_cert); - return NULL; - /* LCOV_EXCL_STOP */ -} - -/** Return a new copy of <b>cert</b>. */ -tor_x509_cert_t * -tor_x509_cert_dup(const tor_x509_cert_t *cert) -{ - tor_assert(cert); - X509 *x509 = cert->cert; - return tor_x509_cert_new(X509_dup(x509)); -} - -/** Read a DER-encoded X509 cert, of length exactly <b>certificate_len</b>, - * from a <b>certificate</b>. Return a newly allocated tor_x509_cert_t on - * success and NULL on failure. */ -tor_x509_cert_t * -tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len) -{ - X509 *x509; - const unsigned char *cp = (const unsigned char *)certificate; - tor_x509_cert_t *newcert; - tor_assert(certificate); - check_no_tls_errors(); - - if (certificate_len > INT_MAX) - goto err; - - x509 = d2i_X509(NULL, &cp, (int)certificate_len); - - if (!x509) - goto err; /* Couldn't decode */ - if (cp - certificate != (int)certificate_len) { - X509_free(x509); - goto err; /* Didn't use all the bytes */ - } - newcert = tor_x509_cert_new(x509); - if (!newcert) { - goto err; - } - if (newcert->encoded_len != certificate_len || - fast_memneq(newcert->encoded, certificate, certificate_len)) { - /* Cert wasn't in DER */ - tor_x509_cert_free(newcert); - goto err; - } - return newcert; - err: - tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "decoding a certificate"); - return NULL; -} - -/** Set *<b>encoded_out</b> and *<b>size_out</b> to <b>cert</b>'s encoded DER - * representation and length, respectively. */ -void -tor_x509_cert_get_der(const tor_x509_cert_t *cert, - const uint8_t **encoded_out, size_t *size_out) -{ - tor_assert(cert); - tor_assert(encoded_out); - tor_assert(size_out); - *encoded_out = cert->encoded; - *size_out = cert->encoded_len; -} - -/** Return a set of digests for the public key in <b>cert</b>, or NULL if this - * cert's public key is not one we know how to take the digest of. */ -const common_digests_t * -tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert) -{ - if (cert->pkey_digests_set) - return &cert->pkey_digests; - else - return NULL; -} - -/** Return a set of digests for the public key in <b>cert</b>. */ -const common_digests_t * -tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert) -{ - return &cert->cert_digests; -} - -/** Remove a reference to <b>ctx</b>, and free it if it has no more - * references. */ -static void -tor_tls_context_decref(tor_tls_context_t *ctx) -{ - tor_assert(ctx); - if (--ctx->refcnt == 0) { - SSL_CTX_free(ctx->ctx); - tor_x509_cert_free(ctx->my_link_cert); - tor_x509_cert_free(ctx->my_id_cert); - tor_x509_cert_free(ctx->my_auth_cert); - crypto_pk_free(ctx->link_key); - crypto_pk_free(ctx->auth_key); - /* LCOV_EXCL_BR_START since ctx will never be NULL here */ - tor_free(ctx); - /* LCOV_EXCL_BR_STOP */ - } -} - /** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate * and ID certificate that we're currently using for our V3 in-protocol * handshake's certificate chain. If <b>server</b> is true, provide the certs @@ -876,7 +70,7 @@ tor_tls_get_my_certs(int server, const tor_x509_cert_t **link_cert_out, const tor_x509_cert_t **id_cert_out) { - tor_tls_context_t *ctx = server ? server_tls_context : client_tls_context; + tor_tls_context_t *ctx = tor_tls_context_get(server); if (! ctx) return -1; if (link_cert_out) @@ -893,131 +87,74 @@ tor_tls_get_my_certs(int server, crypto_pk_t * tor_tls_get_my_client_auth_key(void) { - if (! client_tls_context) + tor_tls_context_t *context = tor_tls_context_get(0); + if (! context) return NULL; - return client_tls_context->auth_key; + return context->auth_key; } -/** - * Return a newly allocated copy of the public key that a certificate - * certifies. Watch out! This returns NULL if the cert's key is not RSA. - */ -crypto_pk_t * -tor_tls_cert_get_key(tor_x509_cert_t *cert) +/** Increase the reference count of <b>ctx</b>. */ +void +tor_tls_context_incref(tor_tls_context_t *ctx) { - crypto_pk_t *result = NULL; - EVP_PKEY *pkey = X509_get_pubkey(cert->cert); - RSA *rsa; - if (!pkey) - return NULL; - rsa = EVP_PKEY_get1_RSA(pkey); - if (!rsa) { - EVP_PKEY_free(pkey); - return NULL; - } - result = crypto_new_pk_from_rsa_(rsa); - EVP_PKEY_free(pkey); - return result; + ++ctx->refcnt; } -/** 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)) +/** Remove a reference to <b>ctx</b>, and free it if it has no more + * references. */ +void +tor_tls_context_decref(tor_tls_context_t *ctx) { - 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; - - 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; - - tor_x509_cert_free(peer); - if (link_key) - EVP_PKEY_free(link_key); - if (cert_key) - EVP_PKEY_free(cert_key); - - return result; + tor_assert(ctx); + if (--ctx->refcnt == 0) { + tor_tls_context_impl_free(ctx->ctx); + tor_x509_cert_free(ctx->my_link_cert); + tor_x509_cert_free(ctx->my_id_cert); + tor_x509_cert_free(ctx->my_auth_cert); + crypto_pk_free(ctx->link_key); + crypto_pk_free(ctx->auth_key); + /* LCOV_EXCL_BR_START since ctx will never be NULL here */ + tor_free(ctx); + /* LCOV_EXCL_BR_STOP */ + } } -/** Check whether <b>cert</b> is well-formed, currently live, and correctly - * signed by the public key in <b>signing_cert</b>. If <b>check_rsa_1024</b>, - * make sure that it has an RSA key with 1024 bits; otherwise, just check that - * the key is long enough. Return 1 if the cert is good, and 0 if it's bad or - * we couldn't check it. */ -int -tor_tls_cert_is_valid(int severity, - const tor_x509_cert_t *cert, - const tor_x509_cert_t *signing_cert, - time_t now, - int check_rsa_1024) +/** Free all global TLS structures. */ +void +tor_tls_free_all(void) { check_no_tls_errors(); - EVP_PKEY *cert_key; - int r, key_ok = 0; - - if (!signing_cert || !cert) - goto bad; - - EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert); - if (!signing_key) - goto bad; - r = X509_verify(cert->cert, signing_key); - EVP_PKEY_free(signing_key); - if (r <= 0) - goto bad; - /* okay, the signature checked out right. Now let's check the check the - * lifetime. */ - if (check_cert_lifetime_internal(severity, cert->cert, now, - 48*60*60, 30*24*60*60) < 0) - goto bad; - - cert_key = X509_get_pubkey(cert->cert); - if (check_rsa_1024 && cert_key) { - RSA *rsa = EVP_PKEY_get1_RSA(cert_key); -#ifdef OPENSSL_1_1_API - if (rsa && RSA_bits(rsa) == 1024) -#else - if (rsa && BN_num_bits(rsa->n) == 1024) -#endif - key_ok = 1; - if (rsa) - RSA_free(rsa); - } else if (cert_key) { - int min_bits = 1024; -#ifdef EVP_PKEY_EC - if (EVP_PKEY_base_id(cert_key) == EVP_PKEY_EC) - min_bits = 128; -#endif - if (EVP_PKEY_bits(cert_key) >= min_bits) - key_ok = 1; + if (server_tls_context) { + tor_tls_context_t *ctx = server_tls_context; + server_tls_context = NULL; + tor_tls_context_decref(ctx); + } + if (client_tls_context) { + tor_tls_context_t *ctx = client_tls_context; + client_tls_context = NULL; + tor_tls_context_decref(ctx); } - EVP_PKEY_free(cert_key); - if (!key_ok) - goto bad; - - /* XXXX compare DNs or anything? */ - - return 1; - bad: - tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "checking a certificate"); - return 0; } -/** Increase the reference count of <b>ctx</b>. */ -static void -tor_tls_context_incref(tor_tls_context_t *ctx) +/** Given a TOR_TLS_* error code, return a string equivalent. */ +const char * +tor_tls_err_to_string(int err) { - ++ctx->refcnt; + if (err >= 0) + return "[Not an error.]"; + switch (err) { + case TOR_TLS_ERROR_MISC: return "misc error"; + case TOR_TLS_ERROR_IO: return "unexpected close"; + case TOR_TLS_ERROR_CONNREFUSED: return "connection refused"; + case TOR_TLS_ERROR_CONNRESET: return "connection reset"; + case TOR_TLS_ERROR_NO_ROUTE: return "host unreachable"; + case TOR_TLS_ERROR_TIMEOUT: return "connection timed out"; + case TOR_TLS_CLOSE: return "closed"; + case TOR_TLS_WANTREAD: return "want to read"; + case TOR_TLS_WANTWRITE: return "want to write"; + default: return "(unknown error code)"; + } } /** Create new global client and server TLS contexts. @@ -1058,6 +195,9 @@ tor_tls_context_init(unsigned flags, if (old_ctx != NULL) { tor_tls_context_decref(old_ctx); } + } else { + tls_log_errors(NULL, LOG_WARN, LD_CRYPTO, + "constructing a TLS context"); } } else { if (server_identity != NULL) { @@ -1066,6 +206,9 @@ tor_tls_context_init(unsigned flags, key_lifetime, flags, 0); + if (rv1 < 0) + tls_log_errors(NULL, LOG_WARN, LD_CRYPTO, + "constructing a server TLS context"); } else { tor_tls_context_t *old_ctx = server_tls_context; server_tls_context = NULL; @@ -1080,9 +223,11 @@ tor_tls_context_init(unsigned flags, key_lifetime, flags, 1); + if (rv2 < 0) + tls_log_errors(NULL, LOG_WARN, LD_CRYPTO, + "constructing a client TLS context"); } - tls_log_errors(NULL, LOG_WARN, LD_CRYPTO, "constructing a TLS context"); return MIN(rv1, rv2); } @@ -1092,7 +237,7 @@ tor_tls_context_init(unsigned flags, * it generates new certificates; all new connections will use * the new SSL context. */ -STATIC int +int tor_tls_context_init_one(tor_tls_context_t **ppcontext, crypto_pk_t *identity, unsigned int key_lifetime, @@ -1119,27 +264,31 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext, return ((new_ctx != NULL) ? 0 : -1); } -/** The group we should use for ecdhe when none was selected. */ -#define NID_tor_default_ecdhe_group NID_X9_62_prime256v1 - +/** Size of the RSA key to use for our TLS link keys */ #define RSA_LINK_KEY_BITS 2048 -/** 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. +/** How long do identity certificates live? (sec) */ +#define IDENTITY_CERT_LIFETIME (365*24*60*60) + +/** + * Initialize the certificates and keys for a TLS context <b>result</b> + * + * Other arguments as for tor_tls_context_new(). */ -STATIC tor_tls_context_t * -tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, - unsigned flags, int is_client) +int +tor_tls_context_init_certificates(tor_tls_context_t *result, + crypto_pk_t *identity, + unsigned key_lifetime, + unsigned flags) { - crypto_pk_t *rsa = NULL, *rsa_auth = NULL; - EVP_PKEY *pkey = NULL; - tor_tls_context_t *result = NULL; - X509 *cert = NULL, *idcert = NULL, *authcert = NULL; + (void)flags; + int rv = -1; char *nickname = NULL, *nn2 = NULL; + crypto_pk_t *rsa = NULL, *rsa_auth = NULL; + tor_x509_cert_impl_t *cert = NULL, *idcert = NULL, *authcert = NULL; - tor_tls_init(); nickname = crypto_random_hostname(8, 20, "www.", ".net"); + #ifdef DISABLE_V3_LINKPROTO_SERVERSIDE nn2 = crypto_random_hostname(8, 20, "www.", ".net"); #else @@ -1151,622 +300,55 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, goto error; if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0) goto error; - if (!is_client) { - /* Generate short-term RSA key for use in the in-protocol ("v3") - * authentication handshake. */ - if (!(rsa_auth = crypto_pk_new())) - goto error; - if (crypto_pk_generate_key(rsa_auth)<0) - goto error; - /* Create a link certificate signed by identity key. */ - cert = tor_tls_create_certificate(rsa, identity, nickname, nn2, - key_lifetime); - /* Create self-signed certificate for identity key. */ - idcert = tor_tls_create_certificate(identity, identity, nn2, nn2, - IDENTITY_CERT_LIFETIME); - /* Create an authentication certificate signed by identity key. */ - authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2, - key_lifetime); - if (!cert || !idcert || !authcert) { - log_warn(LD_CRYPTO, "Error creating certificate"); - goto error; - } - } - result = tor_malloc_zero(sizeof(tor_tls_context_t)); - result->refcnt = 1; - if (!is_client) { - result->my_link_cert = tor_x509_cert_new(X509_dup(cert)); - result->my_id_cert = tor_x509_cert_new(X509_dup(idcert)); - result->my_auth_cert = tor_x509_cert_new(X509_dup(authcert)); - if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert) - goto error; - result->link_key = crypto_pk_dup_key(rsa); - result->auth_key = crypto_pk_dup_key(rsa_auth); - } - -#if 0 - /* Tell OpenSSL to only use TLS1. This may have subtly different results - * from SSLv23_method() with SSLv2 and SSLv3 disabled, so we need to do some - * investigation before we consider adjusting it. It should be compatible - * with existing Tors. */ - if (!(result->ctx = SSL_CTX_new(TLSv1_method()))) + /* Generate short-term RSA key for use in the in-protocol ("v3") + * authentication handshake. */ + if (!(rsa_auth = crypto_pk_new())) 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()))) + if (crypto_pk_generate_key(rsa_auth)<0) goto error; -#else - if (!(result->ctx = SSL_CTX_new(SSLv23_method()))) - goto error; -#endif /* defined(HAVE_TLS_METHOD) */ - SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2); - SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3); - - /* Prefer the server's ordering of ciphers: the client's ordering has - * historically been chosen for fingerprinting resistance. */ - SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - - /* Disable TLS tickets if they're supported. We never want to use them; - * using them can make our perfect forward secrecy a little worse, *and* - * create an opportunity to fingerprint us (since it's unusual to use them - * with TLS sessions turned off). - * - * In 0.2.4, clients advertise support for them though, to avoid a TLS - * distinguishability vector. This can give us worse PFS, though, if we - * get a server that doesn't set SSL_OP_NO_TICKET. With luck, there will - * be few such servers by the time 0.2.4 is more stable. - */ -#ifdef SSL_OP_NO_TICKET - if (! is_client) { - SSL_CTX_set_options(result->ctx, SSL_OP_NO_TICKET); - } -#endif - - SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE); - SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE); - -#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION - SSL_CTX_set_options(result->ctx, - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); -#endif - /* Yes, we know what we are doing here. No, we do not treat a renegotiation - * as authenticating any earlier-received data. - */ - { - SSL_CTX_set_options(result->ctx, - SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); - } - /* Don't actually allow compression; it uses RAM and time, it makes TLS - * vulnerable to CRIME-style attacks, and most of the data we transmit over - * TLS is encrypted (and therefore uncompressible) anyway. */ -#ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(result->ctx, SSL_OP_NO_COMPRESSION); -#endif -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) -#ifndef OPENSSL_NO_COMP - if (result->ctx->comp_methods) - result->ctx->comp_methods = NULL; -#endif -#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) */ - -#ifdef SSL_MODE_RELEASE_BUFFERS - SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS); -#endif - if (! is_client) { - if (cert && !SSL_CTX_use_certificate(result->ctx,cert)) - goto error; - X509_free(cert); /* We just added a reference to cert. */ - cert=NULL; - if (idcert) { - X509_STORE *s = SSL_CTX_get_cert_store(result->ctx); - tor_assert(s); - X509_STORE_add_cert(s, idcert); - X509_free(idcert); /* The context now owns the reference to idcert */ - idcert = NULL; - } - } - SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); - if (!is_client) { - tor_assert(rsa); - if (!(pkey = crypto_pk_get_evp_pkey_(rsa,1))) - goto error; - if (!SSL_CTX_use_PrivateKey(result->ctx, pkey)) - goto error; - EVP_PKEY_free(pkey); - pkey = NULL; - if (!SSL_CTX_check_private_key(result->ctx)) - goto error; - } - { - crypto_dh_t *dh = crypto_dh_new(DH_TYPE_TLS); - tor_assert(dh); - SSL_CTX_set_tmp_dh(result->ctx, crypto_dh_get_dh_(dh)); - crypto_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); + /* Create a link certificate signed by identity key. */ + cert = tor_tls_create_certificate(rsa, identity, nickname, nn2, + key_lifetime); + /* Create self-signed certificate for identity key. */ + idcert = tor_tls_create_certificate(identity, identity, nn2, nn2, + IDENTITY_CERT_LIFETIME); + /* Create an authentication certificate signed by identity key. */ + authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2, + key_lifetime); + if (!cert || !idcert || !authcert) { + log_warn(LD_CRYPTO, "Error creating certificate"); + goto error; } - SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER, - always_accept_verify_cb); - /* let us realloc bufs that we're writing from */ - SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - if (rsa) - crypto_pk_free(rsa); - if (rsa_auth) - crypto_pk_free(rsa_auth); - X509_free(authcert); - tor_free(nickname); - tor_free(nn2); - return result; + result->my_link_cert = tor_x509_cert_new(cert); + cert = NULL; + result->my_id_cert = tor_x509_cert_new(idcert); + idcert = NULL; + result->my_auth_cert = tor_x509_cert_new(authcert); + authcert = NULL; + if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert) + goto error; + result->link_key = rsa; + rsa = NULL; + result->auth_key = rsa_auth; + rsa_auth = NULL; + rv = 0; error: - tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context"); + tor_free(nickname); tor_free(nn2); - if (pkey) - EVP_PKEY_free(pkey); - if (rsa) - crypto_pk_free(rsa); - if (rsa_auth) - crypto_pk_free(rsa_auth); - if (result) - tor_tls_context_decref(result); - if (cert) - X509_free(cert); - if (idcert) - X509_free(idcert); - if (authcert) - X509_free(authcert); - return NULL; -} - -/** Invoked when a TLS state changes: log the change at severity 'debug' */ -STATIC void -tor_tls_debug_state_callback(const SSL *ssl, int type, int val) -{ - /* LCOV_EXCL_START since this depends on whether debug is captured or not */ - log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", - ssl, SSL_state_string_long(ssl), type, val); - /* LCOV_EXCL_STOP */ -} - -/* Return the name of the negotiated ciphersuite in use on <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. */ -STATIC int -find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher) -{ - const SSL_CIPHER *c; -#ifdef HAVE_SSL_CIPHER_FIND - (void) m; - { - unsigned char cipherid[3]; - tor_assert(ssl); - set_uint16(cipherid, tor_htons(cipher)); - cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting - * with a two-byte 'cipherid', it may look for a v2 - * cipher with the appropriate 3 bytes. */ - c = SSL_CIPHER_find((SSL*)ssl, cipherid); - if (c) - tor_assert((SSL_CIPHER_get_id(c) & 0xffff) == cipher); - return c != NULL; - } -#else /* !(defined(HAVE_SSL_CIPHER_FIND)) */ - -# if defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) - if (m && m->get_cipher_by_char) { - unsigned char cipherid[3]; - set_uint16(cipherid, tor_htons(cipher)); - cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting - * with a two-byte 'cipherid', it may look for a v2 - * cipher with the appropriate 3 bytes. */ - c = m->get_cipher_by_char(cipherid); - if (c) - tor_assert((c->id & 0xffff) == cipher); - return c != NULL; - } -#endif /* defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) */ -# ifndef OPENSSL_1_1_API - if (m && m->get_cipher && m->num_ciphers) { - /* It would seem that some of the "let's-clean-up-openssl" forks have - * removed the get_cipher_by_char function. Okay, so now you get a - * quadratic search. - */ - int i; - for (i = 0; i < m->num_ciphers(); ++i) { - c = m->get_cipher(i); - if (c && (c->id & 0xffff) == cipher) { - return 1; - } - } - return 0; - } -#endif /* !defined(OPENSSL_1_1_API) */ - (void) ssl; - (void) m; - (void) cipher; - return 1; /* No way to search */ -#endif /* defined(HAVE_SSL_CIPHER_FIND) */ -} - -/** Remove from v2_cipher_list every cipher that we don't support, so that - * comparing v2_cipher_list to a client's cipher list will give a sensible - * result. */ -static void -prune_v2_cipher_list(const SSL *ssl) -{ - uint16_t *inp, *outp; -#ifdef HAVE_TLS_METHOD - const SSL_METHOD *m = TLS_method(); -#else - const SSL_METHOD *m = SSLv23_method(); -#endif - inp = outp = v2_cipher_list; - while (*inp) { - if (find_cipher_by_id(ssl, m, *inp)) { - *outp++ = *inp++; - } else { - inp++; - } - } - *outp = 0; + tor_x509_cert_impl_free(cert); + tor_x509_cert_impl_free(idcert); + tor_x509_cert_impl_free(authcert); + crypto_pk_free(rsa); + crypto_pk_free(rsa_auth); - v2_cipher_list_pruned = 1; + return rv; } - -/** 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. - **/ -STATIC int -tor_tls_classify_client_ciphers(const SSL *ssl, - STACK_OF(SSL_CIPHER) *peer_ciphers) -{ - int i, res; - tor_tls_t *tor_tls; - if (PREDICT_UNLIKELY(!v2_cipher_list_pruned)) - prune_v2_cipher_list(ssl); - - tor_tls = tor_tls_get_by_ssl(ssl); - if (tor_tls && tor_tls->client_cipher_list_type) - return tor_tls->client_cipher_list_type; - - /* If we reached this point, we just got a client hello. See if there is - * a cipher list. */ - if (!peer_ciphers) { - log_info(LD_NET, "No ciphers on session"); - res = CIPHERS_ERR; - goto done; - } - /* Now we need to see if there are any ciphers whose presence means we're - * dealing with an updated Tor. */ - for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { - const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); - const char *ciphername = SSL_CIPHER_get_name(cipher); - if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) && - strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) && - strcmp(ciphername, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) && - strcmp(ciphername, "(NONE)")) { - log_debug(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername); - // return 1; - goto v2_or_higher; - } - } - res = CIPHERS_V1; - goto done; - v2_or_higher: - { - const uint16_t *v2_cipher = v2_cipher_list; - for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { - const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); - uint16_t id = SSL_CIPHER_get_id(cipher) & 0xffff; - if (id == 0x00ff) /* extended renegotiation indicator. */ - continue; - if (!id || id != *v2_cipher) { - res = CIPHERS_UNRESTRICTED; - goto dump_ciphers; - } - ++v2_cipher; - } - if (*v2_cipher != 0) { - res = CIPHERS_UNRESTRICTED; - goto dump_ciphers; - } - res = CIPHERS_V2; - } - - dump_ciphers: - { - smartlist_t *elts = smartlist_new(); - char *s; - for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { - const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); - const char *ciphername = SSL_CIPHER_get_name(cipher); - smartlist_add(elts, (char*)ciphername); - } - s = smartlist_join_strings(elts, ":", 0, NULL); - log_debug(LD_NET, "Got a %s V2/V3 cipher list from %s. It is: '%s'", - (res == CIPHERS_V2) ? "fictitious" : "real", ADDR(tor_tls), s); - tor_free(s); - smartlist_free(elts); - } - done: - if (tor_tls) - return tor_tls->client_cipher_list_type = res; - - return res; -} - -/** Return true iff the cipher list suggested by the client for <b>ssl</b> is - * a list that indicates that the client knows how to do the v2 TLS connection - * handshake. */ -STATIC int -tor_tls_client_is_using_v2_ciphers(const SSL *ssl) -{ - STACK_OF(SSL_CIPHER) *ciphers; -#ifdef HAVE_SSL_GET_CLIENT_CIPHERS - ciphers = SSL_get_client_ciphers(ssl); -#else - SSL_SESSION *session; - if (!(session = SSL_get_session((SSL *)ssl))) { - log_info(LD_NET, "No session on TLS?"); - return CIPHERS_ERR; - } - ciphers = session->ciphers; -#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */ - - return tor_tls_classify_client_ciphers(ssl, ciphers) >= CIPHERS_V2; -} - -/** Invoked when we're accepting a connection on <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> - */ -STATIC void -tor_tls_server_info_callback(const SSL *ssl, int type, int val) -{ - tor_tls_t *tls; - (void) val; - - IF_BUG_ONCE(ssl == NULL) { - return; // LCOV_EXCL_LINE - } - - tor_tls_debug_state_callback(ssl, type, val); - - if (type != SSL_CB_ACCEPT_LOOP) - return; - - OSSL_HANDSHAKE_STATE ssl_state = SSL_get_state(ssl); - if (! STATE_IS_SW_SERVER_HELLO(ssl_state)) - return; - tls = tor_tls_get_by_ssl(ssl); - if (tls) { - /* Check whether we're watching for renegotiates. If so, this is one! */ - if (tls->negotiated_callback) - tls->got_renegotiate = 1; - if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/ - ++tls->server_handshake_count; - } else { - log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); - return; - } - - /* Now check the cipher list. */ - if (tor_tls_client_is_using_v2_ciphers(ssl)) { - if (tls->wasV2Handshake) - return; /* We already turned this stuff off for the first handshake; - * This is a renegotiation. */ - - /* Yes, we're casting away the const from ssl. This is very naughty of us. - * Let's hope openssl doesn't notice! */ - - /* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */ - SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN); - /* Don't send a hello request. */ - SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL); - - if (tls) { - tls->wasV2Handshake = 1; - } else { - /* LCOV_EXCL_START this line is not reachable */ - log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); - /* LCOV_EXCL_STOP */ - } - } -} - -/** Callback to get invoked on a server after we've read the list of ciphers - * the client supports, but before we pick our own ciphersuite. - * - * We can't abuse an info_cb for this, since by the time one of the - * client_hello info_cbs is called, we've already picked which ciphersuite to - * use. - * - * Technically, this function is an abuse of this callback, since the point of - * a session_secret_cb is to try to set up and/or verify a shared-secret for - * authentication on the fly. But as long as we return 0, we won't actually be - * setting up a shared secret, and all will be fine. - */ -STATIC int -tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len, - STACK_OF(SSL_CIPHER) *peer_ciphers, - CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher, - void *arg) -{ - (void) secret; - (void) secret_len; - (void) peer_ciphers; - (void) cipher; - (void) arg; - - if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) == - CIPHERS_UNRESTRICTED) { - SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST); - } - - SSL_set_session_secret_cb(ssl, NULL, NULL); - - return 0; -} -static void -tor_tls_setup_session_secret_cb(tor_tls_t *tls) -{ - SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL); -} - -/** Create a new TLS object from a file descriptor, and a flag to - * determine whether it is functioning as a server. - */ -tor_tls_t * -tor_tls_new(int sock, int isServer) -{ - BIO *bio = NULL; - tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t)); - tor_tls_context_t *context = isServer ? server_tls_context : - client_tls_context; - result->magic = TOR_TLS_MAGIC; - - check_no_tls_errors(); - tor_assert(context); /* make sure somebody made it first */ - if (!(result->ssl = SSL_new(context->ctx))) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "creating SSL object"); - tor_free(result); - goto err; - } - -#ifdef SSL_set_tlsext_host_name - /* Browsers use the TLS hostname extension, so we should too. */ - if (!isServer) { - char *fake_hostname = crypto_random_hostname(4,25, "www.",".com"); - SSL_set_tlsext_host_name(result->ssl, fake_hostname); - tor_free(fake_hostname); - } -#endif /* defined(SSL_set_tlsext_host_name) */ - - if (!SSL_set_cipher_list(result->ssl, - isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "setting ciphers"); -#ifdef SSL_set_tlsext_host_name - SSL_set_tlsext_host_name(result->ssl, NULL); -#endif - SSL_free(result->ssl); - tor_free(result); - goto err; - } - result->socket = sock; - bio = BIO_new_socket(sock, BIO_NOCLOSE); - if (! bio) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "opening BIO"); -#ifdef SSL_set_tlsext_host_name - SSL_set_tlsext_host_name(result->ssl, NULL); -#endif - SSL_free(result->ssl); - tor_free(result); - goto err; - } - { - int set_worked = - SSL_set_ex_data(result->ssl, tor_tls_object_ex_data_index, result); - if (!set_worked) { - log_warn(LD_BUG, - "Couldn't set the tls for an SSL*; connection will fail"); - } - } - SSL_set_bio(result->ssl, bio, bio); - tor_tls_context_incref(context); - result->context = context; - result->state = TOR_TLS_ST_HANDSHAKE; - result->isServer = isServer; - result->wantwrite_n = 0; - result->last_write_count = (unsigned long) BIO_number_written(bio); - result->last_read_count = (unsigned long) BIO_number_read(bio); - if (result->last_write_count || result->last_read_count) { - log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu", - result->last_read_count, result->last_write_count); - } - if (isServer) { - SSL_set_info_callback(result->ssl, tor_tls_server_info_callback); - } else { - SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback); - } - - if (isServer) - tor_tls_setup_session_secret_cb(result); - - goto done; - err: - result = NULL; - done: - /* Not expected to get called. */ - tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object"); - return result; -} - /** Make future log messages about <b>tls</b> display the address * <b>address</b>. */ @@ -1778,64 +360,6 @@ tor_tls_set_logged_address(tor_tls_t *tls, const char *address) tls->address = tor_strdup(address); } -/** 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) && ... */ -} - /** Return whether this tls initiated the connect (client) or * received it (server). */ int @@ -1858,12 +382,11 @@ tor_tls_free_(tor_tls_t *tls) size_t r,w; tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */ } -#ifdef SSL_set_tlsext_host_name - SSL_set_tlsext_host_name(tls->ssl, NULL); -#endif - SSL_free(tls->ssl); + tor_tls_impl_free(tls->ssl); tls->ssl = NULL; +#ifdef ENABLE_OPENSSL tls->negotiated_callback = NULL; +#endif if (tls->context) tor_tls_context_decref(tls->context); tor_free(tls->address); @@ -1871,394 +394,19 @@ tor_tls_free_(tor_tls_t *tls) tor_free(tls); } -/** 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; -} - -/** Shut down an open tls connection <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_shutdown(tor_tls_t *tls) -{ - int r, err; - char buf[128]; - tor_assert(tls); - tor_assert(tls->ssl); - check_no_tls_errors(); - - while (1) { - if (tls->state == TOR_TLS_ST_SENTCLOSE) { - /* If we've already called shutdown once to send a close message, - * we read until the other side has closed too. - */ - do { - r = SSL_read(tls->ssl, buf, 128); - } while (r>0); - err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down", - LOG_INFO, LD_NET); - if (err == TOR_TLS_ZERORETURN_) { - tls->state = TOR_TLS_ST_GOTCLOSE; - /* fall through... */ - } else { - return err; - } - } - - r = SSL_shutdown(tls->ssl); - if (r == 1) { - /* If shutdown returns 1, the connection is entirely closed. */ - tls->state = TOR_TLS_ST_CLOSED; - return TOR_TLS_DONE; - } - err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down", - LOG_INFO, LD_NET); - if (err == TOR_TLS_SYSCALL_) { - /* The underlying TCP connection closed while we were shutting down. */ - tls->state = TOR_TLS_ST_CLOSED; - return TOR_TLS_DONE; - } else if (err == TOR_TLS_ZERORETURN_) { - /* The TLS connection says that it sent a shutdown record, but - * isn't done shutting down yet. Make sure that this hasn't - * happened before, then go back to the start of the function - * and try to read. - */ - if (tls->state == TOR_TLS_ST_GOTCLOSE || - tls->state == TOR_TLS_ST_SENTCLOSE) { - log_warn(LD_NET, - "TLS returned \"half-closed\" value while already half-closed"); - return TOR_TLS_ERROR_MISC; - } - tls->state = TOR_TLS_ST_SENTCLOSE; - /* fall through ... */ - } else { - return err; - } - } /* end loop */ -} - -/** Return true iff this TLS connection is authenticated. - */ -int -tor_tls_peer_has_cert(tor_tls_t *tls) -{ - X509 *cert; - cert = SSL_get_peer_certificate(tls->ssl); - tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate"); - if (!cert) - return 0; - X509_free(cert); - return 1; -} - -/** Return a newly allocated copy of the peer certificate, or NULL if there - * isn't one. */ -MOCK_IMPL(tor_x509_cert_t *, -tor_tls_get_peer_cert,(tor_tls_t *tls)) -{ - X509 *cert; - cert = SSL_get_peer_certificate(tls->ssl); - tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate"); - if (!cert) - return NULL; - return tor_x509_cert_new(cert); -} - -/** Return a newly allocated copy of the cerficate we used on the connection, - * or NULL if somehow we didn't use one. */ -MOCK_IMPL(tor_x509_cert_t *, -tor_tls_get_own_cert,(tor_tls_t *tls)) -{ - X509 *cert = SSL_get_certificate(tls->ssl); - tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, - "getting own-connection certificate"); - if (!cert) - return NULL; - /* Fun inconsistency: SSL_get_peer_certificate increments the reference - * count, but SSL_get_certificate does not. */ - X509 *duplicate = X509_dup(cert); - if (BUG(duplicate == NULL)) - return NULL; - return tor_x509_cert_new(duplicate); -} - -/** Warn that a certificate lifetime extends through a certain range. */ -static void -log_cert_lifetime(int severity, const X509 *cert, const char *problem, - time_t now) -{ - BIO *bio = NULL; - BUF_MEM *buf; - char *s1=NULL, *s2=NULL; - char mytime[33]; - struct tm tm; - size_t n; - - if (problem) - tor_log(severity, LD_GENERAL, - "Certificate %s. Either their clock is set wrong, or your clock " - "is wrong.", - problem); - - if (!(bio = BIO_new(BIO_s_mem()))) { - log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end; - } - if (!(ASN1_TIME_print(bio, X509_get_notBefore_const(cert)))) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime"); - goto end; - } - BIO_get_mem_ptr(bio, &buf); - s1 = tor_strndup(buf->data, buf->length); - - (void)BIO_reset(bio); - if (!(ASN1_TIME_print(bio, X509_get_notAfter_const(cert)))) { - tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime"); - goto end; - } - BIO_get_mem_ptr(bio, &buf); - s2 = tor_strndup(buf->data, buf->length); - - n = strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm)); - if (n > 0) { - tor_log(severity, LD_GENERAL, - "(certificate lifetime runs from %s through %s. Your time is %s.)", - s1,s2,mytime); - } else { - tor_log(severity, LD_GENERAL, - "(certificate lifetime runs from %s through %s. " - "Couldn't get your time.)", - s1, s2); - } - - end: - /* Not expected to get invoked */ - tls_log_errors(NULL, LOG_WARN, LD_NET, "getting certificate lifetime"); - if (bio) - BIO_free(bio); - tor_free(s1); - tor_free(s2); -} - -/** Helper function: try to extract a link certificate and an identity - * certificate from <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(STATIC void, -try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, - X509 **cert_out, X509 **id_cert_out)) -{ - X509 *cert = NULL, *id_cert = NULL; - STACK_OF(X509) *chain = NULL; - int num_in_chain, i; - *cert_out = *id_cert_out = NULL; - if (!(cert = SSL_get_peer_certificate(tls->ssl))) - return; - *cert_out = cert; - if (!(chain = SSL_get_peer_cert_chain(tls->ssl))) - return; - num_in_chain = sk_X509_num(chain); - /* 1 means we're receiving (server-side), and it's just the id_cert. - * 2 means we're connecting (client-side), and it's both the link - * cert and the id_cert. - */ - if (num_in_chain < 1) { - log_fn(severity,LD_PROTOCOL, - "Unexpected number of certificates in chain (%d)", - num_in_chain); - return; - } - for (i=0; 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; -} - /** If the provided tls connection is authenticated and has a * certificate chain that is currently valid and signed, then set * *<b>identity_key</b> to the identity certificate's key and return * 0. Else, return -1 and log complaints with log-level <b>severity</b>. */ int -tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key) +tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity) { - X509 *cert = NULL, *id_cert = NULL; - EVP_PKEY *id_pkey = NULL; - RSA *rsa; - int r = -1; - - check_no_tls_errors(); - *identity_key = NULL; + tor_x509_cert_impl_t *cert = NULL, *id_cert = NULL; + tor_x509_cert_t *peer_x509 = NULL, *id_x509 = NULL; + tor_assert(tls); + tor_assert(identity); + int rv = -1; try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert); if (!cert) @@ -2267,407 +415,22 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key) log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found"); goto done; } - tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate"); + peer_x509 = tor_x509_cert_new(cert); + id_x509 = tor_x509_cert_new(id_cert); + cert = id_cert = NULL; /* Prevent double-free */ - if (!(id_pkey = X509_get_pubkey(id_cert)) || - X509_verify(cert, id_pkey) <= 0) { - log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0"); - tls_log_errors(tls, severity, LD_HANDSHAKE, "verifying certificate"); + if (! tor_tls_cert_is_valid(severity, peer_x509, id_x509, time(NULL), 0)) { goto done; } - rsa = EVP_PKEY_get1_RSA(id_pkey); - if (!rsa) - goto done; - *identity_key = crypto_new_pk_from_rsa_(rsa); - - r = 0; + *identity = tor_tls_cert_get_key(id_x509); + rv = 0; done: - if (cert) - X509_free(cert); - if (id_pkey) - EVP_PKEY_free(id_pkey); - - /* This should never get invoked, but let's make sure in case OpenSSL - * acts unexpectedly. */ - tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "finishing tor_tls_verify"); - - return r; -} - -/** Check whether the certificate set on the connection <b>tls</b> is expired - * give or take <b>past_tolerance</b> seconds, or not-yet-valid give or take - * <b>future_tolerance</b> seconds. Return 0 for valid, -1 for failure. - * - * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime. - */ -int -tor_tls_check_lifetime(int severity, tor_tls_t *tls, - time_t now, - int past_tolerance, int future_tolerance) -{ - X509 *cert; - int r = -1; - - if (!(cert = SSL_get_peer_certificate(tls->ssl))) - goto done; - - if (check_cert_lifetime_internal(severity, cert, now, - past_tolerance, future_tolerance) < 0) - goto done; - - r = 0; - done: - if (cert) - X509_free(cert); - /* Not expected to get invoked */ - tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime"); - - return r; -} - -/** Helper: check whether <b>cert</b> is expired give or take - * <b>past_tolerance</b> seconds, or not-yet-valid give or take - * <b>future_tolerance</b> seconds. (Relative to the current time - * <b>now</b>.) If it is live, return 0. If it is not live, log a message - * and return -1. */ -static int -check_cert_lifetime_internal(int severity, const X509 *cert, - time_t now, - int past_tolerance, int future_tolerance) -{ - time_t t; - - t = now + future_tolerance; - if (X509_cmp_time(X509_get_notBefore_const(cert), &t) > 0) { - log_cert_lifetime(severity, cert, "not yet valid", now); - return -1; - } - t = now - past_tolerance; - if (X509_cmp_time(X509_get_notAfter_const(cert), &t) < 0) { - log_cert_lifetime(severity, cert, "already expired", now); - return -1; - } - - return 0; -} - -#ifdef TOR_UNIT_TESTS -/* Testing only: return a new x509 cert with the same contents as <b>inp</b>, - but with the expiration time <b>new_expiration_time</b>, signed with - <b>signing_key</b>. */ -STATIC tor_x509_cert_t * -tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp, - time_t new_expiration_time, - crypto_pk_t *signing_key) -{ - X509 *newc = X509_dup(inp->cert); - X509_time_adj(X509_get_notAfter(newc), 0, &new_expiration_time); - EVP_PKEY *pk = crypto_pk_get_evp_pkey_(signing_key, 1); - tor_assert(X509_sign(newc, pk, EVP_sha256())); - EVP_PKEY_free(pk); - return tor_x509_cert_new(newc); -} -#endif /* defined(TOR_UNIT_TESTS) */ - -/** Return the number of bytes available for reading from <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 the number of server handshakes that we've noticed doing on - * <b>tls</b>. */ -int -tor_tls_get_num_server_handshakes(tor_tls_t *tls) -{ - return tls->server_handshake_count; -} - -/** 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 -STATIC size_t -SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len) -{ - tor_assert(s); - if (len == 0) - return s->master_key_length; - tor_assert(len == (size_t)s->master_key_length); - tor_assert(out); - memcpy(out, s->master_key, len); - return len; -} -#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */ - -/** Set the DIGEST256_LEN buffer at <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); + tor_x509_cert_impl_free(cert); + tor_x509_cert_impl_free(id_cert); + tor_x509_cert_free(peer_x509); + tor_x509_cert_free(id_x509); - return ret; + return rv; } |