aboutsummaryrefslogtreecommitdiff
path: root/src/common/tortls.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/tortls.c')
-rw-r--r--src/common/tortls.c1237
1 files changed, 430 insertions, 807 deletions
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 221b47e009..d61cc2e58a 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -16,35 +16,25 @@
#include "orconfig.h"
-#if defined (WINCE)
-#include <WinSock2.h>
-#endif
+#define TORTLS_PRIVATE
#include <assert.h>
#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
- #ifndef _WIN32_WINNT
- #define _WIN32_WINNT 0x0501
- #endif
- #define WIN32_LEAN_AND_MEAN
- #if defined(_MSC_VER) && (_MSC_VER < 1300)
- #include <winsock.h>
- #else
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #endif
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
#endif
-#ifdef __GNUC__
-#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#endif
+#include "compat.h"
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic push
-#endif
/* 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. */
-#pragma GCC diagnostic ignored "-Wredundant-decls"
+DISABLE_GCC_WARNING(redundant-decls)
+
+#include <openssl/opensslv.h>
+#include "crypto.h"
+
+#ifdef OPENSSL_NO_EC
+#error "We require OpenSSL with ECC support"
#endif
#include <openssl/ssl.h>
@@ -53,38 +43,22 @@
#include <openssl/tls1.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
-#include <openssl/opensslv.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic pop
-#else
-#pragma GCC diagnostic warning "-Wredundant-decls"
-#endif
-#endif
+ENABLE_GCC_WARNING(redundant-decls)
-#ifdef USE_BUFFEREVENTS
-#include <event2/bufferevent_ssl.h>
-#include <event2/buffer.h>
-#include <event2/event.h>
-#include "compat_libevent.h"
-#endif
-
-#include "crypto.h"
+#define TORTLS_PRIVATE
#include "tortls.h"
#include "util.h"
#include "torlog.h"
#include "container.h"
#include <string.h>
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8)
-#error "We require OpenSSL >= 0.9.8"
-#endif
-
-/* Enable the "v2" TLS handshake.
- */
-#define V2_HANDSHAKE_SERVER
-#define V2_HANDSHAKE_CLIENT
+#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))
/* Copied from or.h */
#define LEGAL_NICKNAME_CHARACTERS \
@@ -95,10 +69,8 @@
#define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer")
-#if (OPENSSL_VERSION_NUMBER < OPENSSL_V(0,9,8,'s') || \
- (OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(0,9,9) && \
- OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f')))
-/* This is a version of OpenSSL before 0.9.8s/1.0.0f. It does not have
+#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.
*/
@@ -116,36 +88,6 @@
#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010
#endif
-/** Does the run-time openssl version look like we need
- * SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION? */
-static int use_unsafe_renegotiation_op = 0;
-/** Does the run-time openssl version look like we need
- * SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION? */
-static int use_unsafe_renegotiation_flag = 0;
-
-/** Structure that we use for a single certificate. */
-struct tor_cert_t {
- X509 *cert;
- uint8_t *encoded;
- size_t encoded_len;
- unsigned pkey_digests_set : 1;
- digests_t cert_digests;
- digests_t pkey_digests;
-};
-
-/** Holds a SSL_CTX object and related state used to configure TLS
- * connections.
- */
-typedef struct tor_tls_context_t {
- int refcnt;
- SSL_CTX *ctx;
- tor_cert_t *my_link_cert;
- tor_cert_t *my_id_cert;
- tor_cert_t *my_auth_cert;
- crypto_pk_t *link_key;
- crypto_pk_t *auth_key;
-} tor_tls_context_t;
-
/** Return values for tor_tls_classify_client_ciphers.
*
* @{
@@ -164,70 +106,12 @@ typedef struct tor_tls_context_t {
#define CIPHERS_UNRESTRICTED 3
/** @} */
-#define TOR_TLS_MAGIC 0x71571571
-
-typedef enum {
- TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
- TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
- TOR_TLS_ST_BUFFEREVENT
-} tor_tls_state_t;
-#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
-
-/** Holds a SSL object and its associated data. Members are only
- * accessed from within tortls.c.
- */
-struct tor_tls_t {
- uint32_t magic;
- tor_tls_context_t *context; /** A link to the context object for this tls. */
- SSL *ssl; /**< An OpenSSL SSL object. */
- int socket; /**< The underlying file descriptor for this TLS connection. */
- char *address; /**< An address to log when describing this connection. */
- tor_tls_state_bitfield_t state : 3; /**< The current SSL state,
- * depending on which operations
- * have completed successfully. */
- unsigned int isServer:1; /**< True iff this is a server-side connection */
- unsigned int wasV2Handshake:1; /**< True iff the original handshake for
- * this connection used the updated version
- * of the connection protocol (client sends
- * different cipher list, server sends only
- * one certificate). */
- /** True iff we should call negotiated_callback when we're done reading. */
- unsigned int got_renegotiate:1;
- /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
- * called that function yet. */
- int8_t client_cipher_list_type;
- /** Incremented every time we start the server side of a handshake. */
- uint8_t server_handshake_count;
- size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
- * time. */
- /** Last values retrieved from BIO_number_read()/write(); see
- * tor_tls_get_n_raw_bytes() for usage.
- */
- unsigned long last_write_count;
- unsigned long last_read_count;
- /** If set, a callback to invoke whenever the client tries to renegotiate
- * the handshake. */
- void (*negotiated_callback)(tor_tls_t *tls, void *arg);
- /** Argument to pass to negotiated_callback. */
- void *callback_arg;
-};
-
-#ifdef V2_HANDSHAKE_CLIENT
-/** An array of fake SSL_CIPHER objects that we use in order to trick OpenSSL
- * in client mode into advertising the ciphers we want. See
- * rectify_client_ciphers() for details. */
-static SSL_CIPHER *CLIENT_CIPHER_DUMMIES = NULL;
-/** A stack of SSL_CIPHER objects, some real, some fake.
- * See rectify_client_ciphers() for details. */
-static STACK_OF(SSL_CIPHER) *CLIENT_CIPHER_STACK = NULL;
-#endif
-
/** 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;
+STATIC int tor_tls_object_ex_data_index = -1;
/** Helper: Allocate tor_tls_object_ex_data_index. */
-static void
+STATIC void
tor_tls_allocate_tor_tls_object_ex_data_index(void)
{
if (tor_tls_object_ex_data_index == -1) {
@@ -239,7 +123,7 @@ tor_tls_allocate_tor_tls_object_ex_data_index(void)
/** Helper: given a SSL* pointer, return the tor_tls_t object using that
* pointer. */
-static INLINE tor_tls_t *
+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);
@@ -250,21 +134,7 @@ tor_tls_get_by_ssl(const SSL *ssl)
static void tor_tls_context_decref(tor_tls_context_t *ctx);
static void tor_tls_context_incref(tor_tls_context_t *ctx);
-static 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);
-
-static int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
- crypto_pk_t *identity,
- unsigned int key_lifetime,
- unsigned int flags,
- int is_client);
-static tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
- unsigned int key_lifetime,
- unsigned int flags,
- int is_client);
+
static int check_cert_lifetime_internal(int severity, const X509 *cert,
int past_tolerance, int future_tolerance);
@@ -272,8 +142,8 @@ static int check_cert_lifetime_internal(int severity, const X509 *cert,
* to touch them.
*
* @{ */
-static tor_tls_context_t *server_tls_context = NULL;
-static tor_tls_context_t *client_tls_context = NULL;
+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. */
@@ -338,7 +208,9 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long 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;
@@ -367,7 +239,7 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
/** 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.
*/
-static void
+STATIC void
tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
{
unsigned long err;
@@ -379,7 +251,7 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
* code. */
-static int
+STATIC int
tor_errno_to_tls_error(int e)
{
switch (e) {
@@ -430,7 +302,7 @@ tor_tls_err_to_string(int err)
* If an error has occurred, log it at level <b>severity</b> and describe the
* current action as <b>doing</b>.
*/
-static int
+STATIC int
tor_tls_get_error(tor_tls_t *tls, int r, int extra,
const char *doing, int severity, int domain)
{
@@ -478,66 +350,17 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra,
static void
tor_tls_init(void)
{
+ check_no_tls_errors();
+
if (!tls_library_is_initialized) {
- long version;
SSL_library_init();
SSL_load_error_strings();
- version = SSLeay();
-
- /* OpenSSL 0.9.8l introduced SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
- * here, but without thinking too hard about it: it turns out that the
- * flag in question needed to be set at the last minute, and that it
- * conflicted with an existing flag number that had already been added
- * in the OpenSSL 1.0.0 betas. OpenSSL 0.9.8m thoughtfully replaced
- * the flag with an option and (it seems) broke anything that used
- * SSL3_FLAGS_* for the purpose. So we need to know how to do both,
- * and we mustn't use the SSL3_FLAGS option with anything besides
- * OpenSSL 0.9.8l.
- *
- * No, we can't just set flag 0x0010 everywhere. It breaks Tor with
- * OpenSSL 1.0.0beta3 and later. On the other hand, we might be able to
- * set option 0x00040000L everywhere.
- *
- * No, we can't simply detect whether the flag or the option is present
- * in the headers at build-time: some vendors (notably Apple) like to
- * leave their headers out of sync with their libraries.
- *
- * Yes, it _is_ almost as if the OpenSSL developers decided that no
- * program should be allowed to use renegotiation unless it first passed
- * a test of intelligence and determination.
- */
- if (version > OPENSSL_V(0,9,8,'k') && version <= OPENSSL_V(0,9,8,'l')) {
- log_info(LD_GENERAL, "OpenSSL %s looks like version 0.9.8l, but "
- "some vendors have backported renegotiation code from "
- "0.9.8m without updating the version number. "
- "I will try SSL3_FLAGS and SSL_OP to enable renegotation.",
- SSLeay_version(SSLEAY_VERSION));
- use_unsafe_renegotiation_flag = 1;
- use_unsafe_renegotiation_op = 1;
- } else if (version > OPENSSL_V(0,9,8,'l')) {
- log_info(LD_GENERAL, "OpenSSL %s looks like version 0.9.8m or later; "
- "I will try SSL_OP to enable renegotiation",
- SSLeay_version(SSLEAY_VERSION));
- use_unsafe_renegotiation_op = 1;
- } else if (version <= OPENSSL_V(0,9,8,'k')) {
- log_info(LD_GENERAL, "OpenSSL %s [%lx] looks like it's older than "
- "0.9.8l, but some vendors have backported 0.9.8l's "
- "renegotiation code to earlier versions, and some have "
- "backported the code from 0.9.8m or 0.9.8n. I'll set both "
- "SSL3_FLAGS and SSL_OP just to be safe.",
- SSLeay_version(SSLEAY_VERSION), version);
- use_unsafe_renegotiation_flag = 1;
- use_unsafe_renegotiation_op = 1;
- } else {
- /* this is dead code, yes? */
- log_info(LD_GENERAL, "OpenSSL %s has version %lx",
- SSLeay_version(SSLEAY_VERSION), version);
- }
-
#if (SIZEOF_VOID_P >= 8 && \
- !defined(OPENSSL_NO_EC) && \
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
@@ -564,6 +387,7 @@ tor_tls_init(void)
"support (using the enable-ec_nistp_64_gcc_128 option "
"when configuring it) would make ECDH much faster.");
}
+ /* LCOV_EXCL_STOP */
#endif
tor_tls_allocate_tor_tls_object_ex_data_index();
@@ -576,6 +400,8 @@ tor_tls_init(void)
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;
@@ -586,19 +412,13 @@ tor_tls_free_all(void)
client_tls_context = NULL;
tor_tls_context_decref(ctx);
}
-#ifdef V2_HANDSHAKE_CLIENT
- if (CLIENT_CIPHER_DUMMIES)
- tor_free(CLIENT_CIPHER_DUMMIES);
- if (CLIENT_CIPHER_STACK)
- sk_SSL_CIPHER_free(CLIENT_CIPHER_STACK);
-#endif
}
/** 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
+STATIC int
always_accept_verify_cb(int preverify_ok,
X509_STORE_CTX *x509_ctx)
{
@@ -613,16 +433,20 @@ 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;
error:
+ /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/
X509_NAME_free(name);
return NULL;
+ /* LCOV_EXCL_STOP */
}
/** Generate and sign an X509 certificate with the public key <b>rsa</b>,
@@ -633,12 +457,12 @@ tor_x509_name_new(const char *cname)
*
* Return a certificate on success, NULL on failure.
*/
-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)
+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. */
@@ -657,7 +481,8 @@ tor_tls_create_certificate(crypto_pk_t *rsa,
* 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. */
- start_time = time(NULL) - crypto_rand_int(cert_lifetime) + 2*24*3600;
+ time_t now = time(NULL);
+ start_time = crypto_rand_time_range(now - cert_lifetime, now) + 2*24*3600;
start_time -= start_time % (24*3600);
tor_assert(rsa);
@@ -674,8 +499,7 @@ tor_tls_create_certificate(crypto_pk_t *rsa,
goto error;
{ /* our serial number is 8 random bytes. */
- if (crypto_rand((char *)serial_tmp, sizeof(serial_tmp)) < 0)
- goto error;
+ 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))))
@@ -728,12 +552,11 @@ tor_tls_create_certificate(crypto_pk_t *rsa,
* claiming extra unsupported ciphers in order to avoid fingerprinting. */
#define SERVER_CIPHER_LIST \
(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" \
- TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
- SSL3_TXT_EDH_RSA_DES_192_CBC3_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. */
-const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
+static const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
/* 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
@@ -769,25 +592,20 @@ const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
/* Required */
TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
/* Required */
- TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":"
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA
- TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA ":"
-#endif
- /* Required */
- SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA;
+ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA
+ ;
/* 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. */
-#ifdef V2_HANDSHAKE_CLIENT
#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"
+#include "ciphers.inc"
/* Tell it not to use SSLv2 ciphers, so that it can select an SSLv3 version
* of any cipher we say. */
"!SSLv2"
@@ -795,32 +613,9 @@ static const char CLIENT_CIPHER_LIST[] =
#undef CIPHER
#undef XCIPHER
-/** Holds a cipher that we want to advertise, and its 2-byte ID. */
-typedef struct cipher_info_t { unsigned id; const char *name; } cipher_info_t;
-/** A list of all the ciphers that clients should advertise, including items
- * that OpenSSL might not know about. */
-static const cipher_info_t CLIENT_CIPHER_INFO_LIST[] = {
-#define CIPHER(id, name) { id, name },
-#define XCIPHER(id, name) { id, #name },
-#include "./ciphers.inc"
-#undef CIPHER
-#undef XCIPHER
-};
-
-/** The length of CLIENT_CIPHER_INFO_LIST and CLIENT_CIPHER_DUMMIES. */
-static const int N_CLIENT_CIPHERS =
- sizeof(CLIENT_CIPHER_INFO_LIST)/sizeof(CLIENT_CIPHER_INFO_LIST[0]);
-#endif
-
-#ifndef V2_HANDSHAKE_CLIENT
-#undef CLIENT_CIPHER_LIST
-#define CLIENT_CIPHER_LIST (TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
- SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
-#endif
-
/** Free all storage held in <b>cert</b> */
void
-tor_cert_free(tor_cert_t *cert)
+tor_x509_cert_free(tor_x509_cert_t *cert)
{
if (! cert)
return;
@@ -828,18 +623,20 @@ tor_cert_free(tor_cert_t *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_cert_t to hold the certificate "x509_cert".
+ * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert".
*
* Steals a reference to x509_cert.
*/
-static tor_cert_t *
-tor_cert_new(X509 *x509_cert)
+MOCK_IMPL(STATIC tor_x509_cert_t *,
+ tor_x509_cert_new,(X509 *x509_cert))
{
- tor_cert_t *cert;
+ tor_x509_cert_t *cert;
EVP_PKEY *pkey;
RSA *rsa;
int length;
@@ -849,12 +646,14 @@ tor_cert_new(X509 *x509_cert)
return NULL;
length = i2d_X509(x509_cert, &buf);
- cert = tor_malloc_zero(sizeof(tor_cert_t));
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
if (length <= 0 || buf == NULL) {
+ /* LCOV_EXCL_START for the same reason as the exclusion above */
tor_free(cert);
log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate");
X509_free(x509_cert);
return NULL;
+ /* LCOV_EXCL_STOP */
}
cert->encoded_len = (size_t) length;
cert->encoded = tor_malloc(length);
@@ -863,13 +662,13 @@ tor_cert_new(X509 *x509_cert)
cert->cert = x509_cert;
- crypto_digest_all(&cert->cert_digests,
+ 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);
- crypto_pk_get_all_digests(pk, &cert->pkey_digests);
+ crypto_pk_get_common_digests(pk, &cert->pkey_digests);
cert->pkey_digests_set = 1;
crypto_pk_free(pk);
EVP_PKEY_free(pkey);
@@ -878,45 +677,58 @@ tor_cert_new(X509 *x509_cert)
return cert;
}
+/** 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_cert_t on success
- * and NULL on failure. */
-tor_cert_t *
-tor_cert_decode(const uint8_t *certificate, size_t certificate_len)
+ * 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_cert_t *newcert;
+ tor_x509_cert_t *newcert;
tor_assert(certificate);
+ check_no_tls_errors();
if (certificate_len > INT_MAX)
- return NULL;
+ goto err;
x509 = d2i_X509(NULL, &cp, (int)certificate_len);
if (!x509)
- return NULL; /* Couldn't decode */
+ goto err; /* Couldn't decode */
if (cp - certificate != (int)certificate_len) {
X509_free(x509);
- return NULL; /* Didn't use all the bytes */
+ goto err; /* Didn't use all the bytes */
}
- newcert = tor_cert_new(x509);
+ newcert = tor_x509_cert_new(x509);
if (!newcert) {
- return NULL;
+ goto err;
}
if (newcert->encoded_len != certificate_len ||
fast_memneq(newcert->encoded, certificate, certificate_len)) {
/* Cert wasn't in DER */
- tor_cert_free(newcert);
- return NULL;
+ 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_cert_get_der(const tor_cert_t *cert,
+tor_x509_cert_get_der(const tor_x509_cert_t *cert,
const uint8_t **encoded_out, size_t *size_out)
{
tor_assert(cert);
@@ -928,8 +740,8 @@ tor_cert_get_der(const tor_cert_t *cert,
/** 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 digests_t *
-tor_cert_get_id_digests(const tor_cert_t *cert)
+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;
@@ -938,8 +750,8 @@ tor_cert_get_id_digests(const tor_cert_t *cert)
}
/** Return a set of digests for the public key in <b>cert</b>. */
-const digests_t *
-tor_cert_get_cert_digests(const tor_cert_t *cert)
+const common_digests_t *
+tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert)
{
return &cert->cert_digests;
}
@@ -952,12 +764,14 @@ tor_tls_context_decref(tor_tls_context_t *ctx)
tor_assert(ctx);
if (--ctx->refcnt == 0) {
SSL_CTX_free(ctx->ctx);
- tor_cert_free(ctx->my_link_cert);
- tor_cert_free(ctx->my_id_cert);
- tor_cert_free(ctx->my_auth_cert);
+ 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 */
}
}
@@ -968,8 +782,8 @@ tor_tls_context_decref(tor_tls_context_t *ctx)
* client mode. */
int
tor_tls_get_my_certs(int server,
- const tor_cert_t **link_cert_out,
- const tor_cert_t **id_cert_out)
+ const tor_x509_cert_t **link_cert_out,
+ const tor_x509_cert_t **id_cert_out)
{
tor_tls_context_t *ctx = server ? server_tls_context : client_tls_context;
if (! ctx)
@@ -998,7 +812,7 @@ tor_tls_get_my_client_auth_key(void)
* certifies. Return NULL if the cert's key is not RSA.
*/
crypto_pk_t *
-tor_tls_cert_get_key(tor_cert_t *cert)
+tor_tls_cert_get_key(tor_x509_cert_t *cert)
{
crypto_pk_t *result = NULL;
EVP_PKEY *pkey = X509_get_pubkey(cert->cert);
@@ -1018,8 +832,8 @@ tor_tls_cert_get_key(tor_cert_t *cert)
/** 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.
*/
-int
-tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert)
+MOCK_IMPL(int,
+tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert))
{
X509 *peercert = SSL_get_peer_certificate(tls->ssl);
EVP_PKEY *link_key = NULL, *cert_key = NULL;
@@ -1048,37 +862,46 @@ tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert)
* we couldn't check it. */
int
tor_tls_cert_is_valid(int severity,
- const tor_cert_t *cert,
- const tor_cert_t *signing_cert,
+ const tor_x509_cert_t *cert,
+ const tor_x509_cert_t *signing_cert,
int check_rsa_1024)
{
+ check_no_tls_errors();
EVP_PKEY *cert_key;
- EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
int r, key_ok = 0;
+
+ if (!signing_cert || !cert)
+ goto bad;
+
+ EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
if (!signing_key)
- return 0;
+ goto bad;
r = X509_verify(cert->cert, signing_key);
EVP_PKEY_free(signing_key);
if (r <= 0)
- return 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,
48*60*60, 30*24*60*60) < 0)
- return 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_type(cert_key->type) == 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)
@@ -1086,11 +909,14 @@ tor_tls_cert_is_valid(int severity,
}
EVP_PKEY_free(cert_key);
if (!key_ok)
- return 0;
+ 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>. */
@@ -1117,6 +943,7 @@ tor_tls_context_init(unsigned flags,
int rv1 = 0;
int rv2 = 0;
const int is_public_server = flags & TOR_TLS_CTX_IS_PUBLIC_SERVER;
+ check_no_tls_errors();
if (is_public_server) {
tor_tls_context_t *new_ctx;
@@ -1161,6 +988,7 @@ tor_tls_context_init(unsigned flags,
1);
}
+ tls_log_errors(NULL, LOG_WARN, LD_CRYPTO, "constructing a TLS context");
return MIN(rv1, rv2);
}
@@ -1170,7 +998,7 @@ tor_tls_context_init(unsigned flags,
* it generates new certificates; all new connections will use
* the new SSL context.
*/
-static int
+STATIC int
tor_tls_context_init_one(tor_tls_context_t **ppcontext,
crypto_pk_t *identity,
unsigned int key_lifetime,
@@ -1197,11 +1025,14 @@ 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
+
/** 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.
*/
-static tor_tls_context_t *
+STATIC tor_tls_context_t *
tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
unsigned flags, int is_client)
{
@@ -1249,9 +1080,9 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
result = tor_malloc_zero(sizeof(tor_tls_context_t));
result->refcnt = 1;
if (!is_client) {
- result->my_link_cert = tor_cert_new(X509_dup(cert));
- result->my_id_cert = tor_cert_new(X509_dup(idcert));
- result->my_auth_cert = tor_cert_new(X509_dup(authcert));
+ 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);
@@ -1268,8 +1099,13 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
#endif
/* 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
SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3);
@@ -1277,23 +1113,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
* historically been chosen for fingerprinting resistance. */
SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
- /* Disable TLS1.1 and TLS1.2 if they exist. We need to do this to
- * workaround a bug present in all OpenSSL 1.0.1 versions (as of 1
- * June 2012), wherein renegotiating while using one of these TLS
- * protocols will cause the client to send a TLS 1.0 ServerHello
- * rather than a ServerHello written with the appropriate protocol
- * version. Once some version of OpenSSL does TLS1.1 and TLS1.2
- * renegotiation properly, we can turn them back on when built with
- * that version. */
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,1,'e')
-#ifdef SSL_OP_NO_TLSv1_2
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_TLSv1_2);
-#endif
-#ifdef SSL_OP_NO_TLSv1_1
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_TLSv1_1);
-#endif
-#endif
-
/* 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
@@ -1310,24 +1129,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
}
#endif
- /* XXX This block is now obsolete. */
- if (
-#ifdef DISABLE_SSL3_HANDSHAKE
- 1 ||
-#endif
- SSLeay() < OPENSSL_V(0,9,8,'s') ||
- (SSLeay() >= OPENSSL_V_SERIES(0,9,9) &&
- SSLeay() < OPENSSL_V(1,0,0,'f'))) {
- /* And not SSL3 if it's subject to CVE-2011-4576. */
- log_info(LD_NET, "Disabling SSLv3 because this OpenSSL version "
- "might otherwise be vulnerable to CVE-2011-4576 "
- "(compile-time version %08lx (%s); "
- "runtime version %08lx (%s))",
- (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT,
- (unsigned long)SSLeay(), SSLeay_version(SSLEAY_VERSION));
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3);
- }
-
SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE);
SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE);
@@ -1338,16 +1139,21 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
/* Yes, we know what we are doing here. No, we do not treat a renegotiation
* as authenticating any earlier-received data.
*/
- if (use_unsafe_renegotiation_op) {
+ {
SSL_CTX_set_options(result->ctx,
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
}
+#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
/* Don't actually allow compression; it uses ram and time, but the data
* we transmit is all encrypted anyway. */
if (result->ctx->comp_methods)
result->ctx->comp_methods = NULL;
#endif
+#endif
#ifdef SSL_MODE_RELEASE_BUFFERS
SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS);
#endif
@@ -1382,8 +1188,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
SSL_CTX_set_tmp_dh(result->ctx, crypto_dh_get_dh_(dh));
crypto_dh_free(dh);
}
-#if (!defined(OPENSSL_NO_EC) && \
- OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0))
if (! is_client) {
int nid;
EC_KEY *ec_key;
@@ -1392,16 +1196,13 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
else if (flags & TOR_TLS_CTX_USE_ECDHE_P256)
nid = NID_X9_62_prime256v1;
else
- nid = NID_X9_62_prime256v1;
+ 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);
}
-#else
- (void)flags;
-#endif
SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
always_accept_verify_cb);
/* let us realloc bufs that we're writing from */
@@ -1438,11 +1239,13 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
}
/** Invoked when a TLS state changes: log the change at severity 'debug' */
-static void
+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> */
@@ -1452,13 +1255,11 @@ tor_tls_get_ciphersuite_name(tor_tls_t *tls)
return SSL_get_cipher(tls->ssl);
}
-#ifdef V2_HANDSHAKE_SERVER
-
/* 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[] = {
+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 */
@@ -1494,11 +1295,27 @@ 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_METHOD *m, uint16_t cipher)
+STATIC int
+find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
{
const SSL_CIPHER *c;
-#ifdef HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR
+#ifdef HAVE_SSL_CIPHER_FIND
+ (void) m;
+ {
+ unsigned char cipherid[3];
+ tor_assert(ssl);
+ set_uint16(cipherid, 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
+
+# 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, htons(cipher));
@@ -1509,8 +1326,9 @@ find_cipher_by_id(const SSL_METHOD *m, uint16_t cipher)
if (c)
tor_assert((c->id & 0xffff) == cipher);
return c != NULL;
- } else
-#endif
+ }
+# endif
+# 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
@@ -1524,23 +1342,31 @@ find_cipher_by_id(const SSL_METHOD *m, uint16_t cipher)
}
}
return 0;
- } else {
- return 1; /* No way to search */
}
+# endif
+ (void) ssl;
+ (void) m;
+ (void) cipher;
+ return 1; /* No way to search */
+#endif
}
/** 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(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(m, *inp)) {
+ if (find_cipher_by_id(ssl, m, *inp)) {
*outp++ = *inp++;
} else {
inp++;
@@ -1555,14 +1381,14 @@ prune_v2_cipher_list(void)
* client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2,
* CIPHERS_UNRESTRICTED.
**/
-static int
+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();
+ prune_v2_cipher_list(ssl);
tor_tls = tor_tls_get_by_ssl(ssl);
if (tor_tls && tor_tls->client_cipher_list_type)
@@ -1578,7 +1404,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
/* 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) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(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) &&
@@ -1595,8 +1421,8 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
{
const uint16_t *v2_cipher = v2_cipher_list;
for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
- uint16_t id = cipher->id & 0xffff;
+ 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) {
@@ -1617,7 +1443,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
smartlist_t *elts = smartlist_new();
char *s;
for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(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);
}
@@ -1637,16 +1463,22 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
/** 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
+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
- return tor_tls_classify_client_ciphers(ssl, session->ciphers) >= CIPHERS_V2;
+ return tor_tls_classify_client_ciphers(ssl, ciphers) >= CIPHERS_V2;
}
/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection
@@ -1655,20 +1487,24 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
* do not send or request extra certificates in v2 handshakes.</li>
* <li>To detect renegotiation</li></ul>
*/
-static void
+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;
- if ((ssl->state != SSL3_ST_SW_SRVR_HELLO_A) &&
- (ssl->state != SSL3_ST_SW_SRVR_HELLO_B))
- 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! */
@@ -1697,18 +1533,14 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
if (tls) {
tls->wasV2Handshake = 1;
-#ifdef USE_BUFFEREVENTS
- if (use_unsafe_renegotiation_flag)
- tls->ssl->s3->flags |= SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
-#endif
} 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 */
}
}
}
-#endif
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)
/** 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.
*
@@ -1721,10 +1553,11 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
* 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
+STATIC int
tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len,
STACK_OF(SSL_CIPHER) *peer_ciphers,
- SSL_CIPHER **cipher, void *arg)
+ CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher,
+ void *arg)
{
(void) secret;
(void) secret_len;
@@ -1746,128 +1579,6 @@ tor_tls_setup_session_secret_cb(tor_tls_t *tls)
{
SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
}
-#else
-#define tor_tls_setup_session_secret_cb(tls) STMT_NIL
-#endif
-
-/** Explain which ciphers we're missing. */
-static void
-log_unsupported_ciphers(smartlist_t *unsupported)
-{
- char *joined;
-
- log_notice(LD_NET, "We weren't able to find support for all of the "
- "TLS ciphersuites that we wanted to advertise. This won't "
- "hurt security, but it might make your Tor (if run as a client) "
- "more easy for censors to block.");
-
- if (SSLeay() < 0x10000000L) {
- log_notice(LD_NET, "To correct this, use a more recent OpenSSL, "
- "built without disabling any secure ciphers or features.");
- } else {
- log_notice(LD_NET, "To correct this, use a version of OpenSSL "
- "built with none of its ciphers disabled.");
- }
-
- joined = smartlist_join_strings(unsupported, ":", 0, NULL);
- log_info(LD_NET, "The unsupported ciphers were: %s", joined);
- tor_free(joined);
-}
-
-/** Replace *<b>ciphers</b> with a new list of SSL ciphersuites: specifically,
- * a list designed to mimic a common web browser. We might not be able to do
- * that if OpenSSL doesn't support all the ciphers we want. Some of the
- * ciphers in the list won't actually be implemented by OpenSSL: that's okay
- * so long as the server doesn't select them.
- *
- * [If the server <b>does</b> select a bogus cipher, we won't crash or
- * anything; we'll just fail later when we try to look up the cipher in
- * ssl->cipher_list_by_id.]
- */
-static void
-rectify_client_ciphers(STACK_OF(SSL_CIPHER) **ciphers)
-{
-#ifdef V2_HANDSHAKE_CLIENT
- if (PREDICT_UNLIKELY(!CLIENT_CIPHER_STACK)) {
- /* We need to set CLIENT_CIPHER_STACK to an array of the ciphers
- * we want to use/advertise. */
- int i = 0, j = 0;
- smartlist_t *unsupported = smartlist_new();
-
- /* First, create a dummy SSL_CIPHER for every cipher. */
- CLIENT_CIPHER_DUMMIES =
- tor_malloc_zero(sizeof(SSL_CIPHER)*N_CLIENT_CIPHERS);
- for (i=0; i < N_CLIENT_CIPHERS; ++i) {
- CLIENT_CIPHER_DUMMIES[i].valid = 1;
- /* The "3<<24" here signifies that the cipher is supposed to work with
- * SSL3 and TLS1. */
- CLIENT_CIPHER_DUMMIES[i].id = CLIENT_CIPHER_INFO_LIST[i].id | (3<<24);
- CLIENT_CIPHER_DUMMIES[i].name = CLIENT_CIPHER_INFO_LIST[i].name;
- }
-
- CLIENT_CIPHER_STACK = sk_SSL_CIPHER_new_null();
- tor_assert(CLIENT_CIPHER_STACK);
-
- log_debug(LD_NET, "List was: %s", CLIENT_CIPHER_LIST);
- for (j = 0; j < sk_SSL_CIPHER_num(*ciphers); ++j) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(*ciphers, j);
- log_debug(LD_NET, "Cipher %d: %lx %s", j, cipher->id, cipher->name);
- }
-
- /* Then copy as many ciphers as we can from the good list, inserting
- * dummies as needed. Let j be an index into list of ciphers we have
- * (*ciphers) and let i be an index into the ciphers we want
- * (CLIENT_INFO_CIPHER_LIST). We are building a list of ciphers in
- * CLIENT_CIPHER_STACK.
- */
- for (i = j = 0; i < N_CLIENT_CIPHERS; ) {
- SSL_CIPHER *cipher = NULL;
- if (j < sk_SSL_CIPHER_num(*ciphers))
- cipher = sk_SSL_CIPHER_value(*ciphers, j);
- if (cipher && ((cipher->id >> 24) & 0xff) != 3) {
- /* Skip over non-v3 ciphers entirely. (This should no longer be
- * needed, thanks to saying !SSLv2 above.) */
- log_debug(LD_NET, "Skipping v%d cipher %s",
- (int)((cipher->id>>24) & 0xff),
- cipher->name);
- ++j;
- } else if (cipher &&
- (cipher->id & 0xffff) == CLIENT_CIPHER_INFO_LIST[i].id) {
- /* "cipher" is the cipher we expect. Put it on the list. */
- log_debug(LD_NET, "Found cipher %s", cipher->name);
- sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, cipher);
- ++j;
- ++i;
- } else if (!strcmp(CLIENT_CIPHER_DUMMIES[i].name,
- "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA")) {
- /* We found bogus cipher 0xfeff, which OpenSSL doesn't support and
- * never has. For this one, we need a dummy. */
- log_debug(LD_NET, "Inserting fake %s", CLIENT_CIPHER_DUMMIES[i].name);
- sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, &CLIENT_CIPHER_DUMMIES[i]);
- ++i;
- } else {
- /* OpenSSL doesn't have this one. */
- log_debug(LD_NET, "Completely omitting unsupported cipher %s",
- CLIENT_CIPHER_INFO_LIST[i].name);
- smartlist_add(unsupported, (char*) CLIENT_CIPHER_INFO_LIST[i].name);
- ++i;
- }
- }
-
- if (smartlist_len(unsupported))
- log_unsupported_ciphers(unsupported);
-
- smartlist_free(unsupported);
- }
-
- sk_SSL_CIPHER_free(*ciphers);
- *ciphers = sk_SSL_CIPHER_dup(CLIENT_CIPHER_STACK);
- tor_assert(*ciphers);
-
-#else
- (void)ciphers;
-#endif
-}
/** Create a new TLS object from a file descriptor, and a flag to
* determine whether it is functioning as a server.
@@ -1881,11 +1592,12 @@ tor_tls_new(int sock, int isServer)
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);
- return NULL;
+ goto err;
}
#ifdef SSL_set_tlsext_host_name
@@ -1905,10 +1617,8 @@ tor_tls_new(int sock, int isServer)
#endif
SSL_free(result->ssl);
tor_free(result);
- return NULL;
+ goto err;
}
- if (!isServer)
- rectify_client_ciphers(&result->ssl->cipher_list);
result->socket = sock;
bio = BIO_new_socket(sock, BIO_NOCLOSE);
if (! bio) {
@@ -1918,7 +1628,7 @@ tor_tls_new(int sock, int isServer)
#endif
SSL_free(result->ssl);
tor_free(result);
- return NULL;
+ goto err;
}
{
int set_worked =
@@ -1934,24 +1644,25 @@ tor_tls_new(int sock, int isServer)
result->state = TOR_TLS_ST_HANDSHAKE;
result->isServer = isServer;
result->wantwrite_n = 0;
- result->last_write_count = BIO_number_written(bio);
- result->last_read_count = BIO_number_read(bio);
+ 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);
}
-#ifdef V2_HANDSHAKE_SERVER
if (isServer) {
SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
- } else
-#endif
- {
+ } 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;
@@ -1980,13 +1691,11 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
tls->negotiated_callback = cb;
tls->callback_arg = arg;
tls->got_renegotiate = 0;
-#ifdef V2_HANDSHAKE_SERVER
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);
}
-#endif
}
/** If this version of openssl requires it, turn on renegotiation on
@@ -1997,13 +1706,8 @@ 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. */
- if (use_unsafe_renegotiation_flag) {
- tls->ssl->s3->flags |= SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
- }
- if (use_unsafe_renegotiation_op) {
- SSL_set_options(tls->ssl,
- SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
- }
+ SSL_set_options(tls->ssl,
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
}
/** If this version of openssl supports it, turn off renegotiation on
@@ -2013,21 +1717,24 @@ tor_tls_unblock_renegotiation(tor_tls_t *tls)
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 (use_unsafe_renegotiation_flag) {
- tor_assert(0 != (tls->ssl->s3->flags &
- SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
- }
- if (use_unsafe_renegotiation_op) {
- long options = SSL_get_options(tls->ssl);
- tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
- }
+#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
}
/** Return whether this tls initiated the connect (client) or
@@ -2070,8 +1777,8 @@ tor_tls_free(tor_tls_t *tls)
* number of characters read. On failure, returns TOR_TLS_ERROR,
* TOR_TLS_CLOSE, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.
*/
-int
-tor_tls_read(tor_tls_t *tls, char *cp, size_t len)
+MOCK_IMPL(int,
+tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
{
int r, err;
tor_assert(tls);
@@ -2080,7 +1787,6 @@ tor_tls_read(tor_tls_t *tls, char *cp, size_t len)
tor_assert(len<INT_MAX);
r = SSL_read(tls->ssl, cp, (int)len);
if (r > 0) {
-#ifdef V2_HANDSHAKE_SERVER
if (tls->got_renegotiate) {
/* Renegotiation happened! */
log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls));
@@ -2088,7 +1794,6 @@ tor_tls_read(tor_tls_t *tls, char *cp, size_t len)
tls->negotiated_callback(tls, tls->callback_arg);
tls->got_renegotiate = 0;
}
-#endif
return r;
}
err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET);
@@ -2105,10 +1810,10 @@ tor_tls_read(tor_tls_t *tls, char *cp, size_t len)
/** 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;
+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;
+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
@@ -2153,12 +1858,14 @@ int
tor_tls_handshake(tor_tls_t *tls)
{
int r;
- int oldstate;
tor_assert(tls);
tor_assert(tls->ssl);
tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
+
check_no_tls_errors();
- oldstate = tls->ssl->state;
+
+ 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));
@@ -2168,7 +1875,10 @@ tor_tls_handshake(tor_tls_t *tls)
SSL_state_string_long(tls->ssl));
r = SSL_connect(tls->ssl);
}
- if (oldstate != tls->ssl->state)
+
+ 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
@@ -2199,12 +1909,11 @@ 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);
- /* There doesn't seem to be a clear OpenSSL API to clear mode flags. */
- tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN;
-#ifdef V2_HANDSHAKE_SERVER
+ 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
@@ -2219,78 +1928,19 @@ tor_tls_finish_handshake(tor_tls_t *tls)
} else {
tls->wasV2Handshake = 0;
}
-#endif
} else {
-#ifdef V2_HANDSHAKE_CLIENT
- /* If we got no ID cert, we're a v2 handshake. */
- X509 *cert = SSL_get_peer_certificate(tls->ssl);
- STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl);
- int n_certs = sk_X509_num(chain);
- if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0))) {
- log_debug(LD_HANDSHAKE, "Server sent back multiple certificates; it "
- "looks like a v1 handshake on %p", tls);
- tls->wasV2Handshake = 0;
- } else {
- log_debug(LD_HANDSHAKE,
- "Server sent back a single certificate; looks like "
- "a v2 handshake on %p.", tls);
- tls->wasV2Handshake = 1;
- }
- if (cert)
- X509_free(cert);
-#endif
+ /* 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;
}
-#ifdef USE_BUFFEREVENTS
-/** Put <b>tls</b>, which must be a client connection, into renegotiation
- * mode. */
-int
-tor_tls_start_renegotiating(tor_tls_t *tls)
-{
- int r = SSL_renegotiate(tls->ssl);
- if (r <= 0) {
- return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN,
- LD_HANDSHAKE);
- }
- return 0;
-}
-#endif
-
-/** Client only: Renegotiate a TLS session. When finished, returns
- * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, or
- * TOR_TLS_WANTWRITE.
- */
-int
-tor_tls_renegotiate(tor_tls_t *tls)
-{
- int r;
- tor_assert(tls);
- /* We could do server-initiated renegotiation too, but that would be tricky.
- * Instead of "SSL_renegotiate, then SSL_do_handshake until done" */
- tor_assert(!tls->isServer);
- if (tls->state != TOR_TLS_ST_RENEGOTIATE) {
- int r = SSL_renegotiate(tls->ssl);
- if (r <= 0) {
- return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN,
- LD_HANDSHAKE);
- }
- tls->state = TOR_TLS_ST_RENEGOTIATE;
- }
- r = SSL_do_handshake(tls->ssl);
- if (r == 1) {
- tls->state = TOR_TLS_ST_OPEN;
- return TOR_TLS_DONE;
- } else
- return tor_tls_get_error(tls, r, 0, "renegotiating handshake", LOG_INFO,
- LD_HANDSHAKE);
-}
-
/** 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.
@@ -2302,6 +1952,7 @@ tor_tls_shutdown(tor_tls_t *tls)
char buf[128];
tor_assert(tls);
tor_assert(tls->ssl);
+ check_no_tls_errors();
while (1) {
if (tls->state == TOR_TLS_ST_SENTCLOSE) {
@@ -2367,16 +2018,35 @@ tor_tls_peer_has_cert(tor_tls_t *tls)
return 1;
}
-/** Return the peer certificate, or NULL if there isn't one. */
-tor_cert_t *
-tor_tls_get_peer_cert(tor_tls_t *tls)
+/** 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_cert_new(cert);
+ 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. */
@@ -2400,7 +2070,7 @@ log_cert_lifetime(int severity, const X509 *cert, const char *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(cert)))) {
+ if (!(ASN1_TIME_print(bio, X509_get_notBefore_const(cert)))) {
tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime");
goto end;
}
@@ -2408,7 +2078,7 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem)
s1 = tor_strndup(buf->data, buf->length);
(void)BIO_reset(bio);
- if (!(ASN1_TIME_print(bio, X509_get_notAfter(cert)))) {
+ if (!(ASN1_TIME_print(bio, X509_get_notAfter_const(cert)))) {
tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime");
goto end;
}
@@ -2443,15 +2113,14 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem)
*
* Note that a reference is added to cert_out, so it needs to be
* freed. id_cert_out doesn't. */
-static void
-try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
- X509 **cert_out, X509 **id_cert_out)
+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;
@@ -2489,6 +2158,7 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key)
RSA *rsa;
int r = -1;
+ check_no_tls_errors();
*identity_key = NULL;
try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert);
@@ -2570,12 +2240,12 @@ check_cert_lifetime_internal(int severity, const X509 *cert,
now = time(NULL);
t = now + future_tolerance;
- if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) {
+ if (X509_cmp_time(X509_get_notBefore_const(cert), &t) > 0) {
log_cert_lifetime(severity, cert, "not yet valid");
return -1;
}
t = now - past_tolerance;
- if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) {
+ if (X509_cmp_time(X509_get_notAfter_const(cert), &t) < 0) {
log_cert_lifetime(severity, cert, "already expired");
return -1;
}
@@ -2608,7 +2278,7 @@ 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 = BIO_number_read(SSL_get_rbio(tls->ssl));
+ 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
@@ -2617,9 +2287,19 @@ tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written)
* 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
if (wbio->method == BIO_f_buffer() && (tmpbio = BIO_next(wbio)) != NULL)
wbio = tmpbio;
- w = BIO_number_written(wbio);
+#endif
+ 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
@@ -2667,106 +2347,7 @@ check_no_tls_errors_(const char *fname, int line)
int
tor_tls_used_v1_handshake(tor_tls_t *tls)
{
- if (tls->isServer) {
-#ifdef V2_HANDSHAKE_SERVER
- return ! tls->wasV2Handshake;
-#endif
- } else {
-#ifdef V2_HANDSHAKE_CLIENT
- return ! tls->wasV2Handshake;
-#endif
- }
- return 1;
-}
-
-/** Return true iff <b>name</b> is a DN of a kind that could only
- * occur in a v3-handshake-indicating certificate */
-static int
-dn_indicates_v3_cert(X509_NAME *name)
-{
-#ifdef DISABLE_V3_LINKPROTO_CLIENTSIDE
- (void)name;
- return 0;
-#else
- X509_NAME_ENTRY *entry;
- int n_entries;
- ASN1_OBJECT *obj;
- ASN1_STRING *str;
- unsigned char *s;
- int len, r;
-
- n_entries = X509_NAME_entry_count(name);
- if (n_entries != 1)
- return 1; /* More than one entry in the DN. */
- entry = X509_NAME_get_entry(name, 0);
-
- obj = X509_NAME_ENTRY_get_object(entry);
- if (OBJ_obj2nid(obj) != OBJ_txt2nid("commonName"))
- return 1; /* The entry isn't a commonName. */
-
- str = X509_NAME_ENTRY_get_data(entry);
- len = ASN1_STRING_to_UTF8(&s, str);
- if (len < 0)
- return 0;
- if (len < 4) {
- OPENSSL_free(s);
- return 1;
- }
- r = fast_memneq(s + len - 4, ".net", 4);
- OPENSSL_free(s);
- return r;
-#endif
-}
-
-/** Return true iff the peer certificate we're received on <b>tls</b>
- * indicates that this connection should use the v3 (in-protocol)
- * authentication handshake.
- *
- * Only the connection initiator should use this, and only once the initial
- * handshake is done; the responder detects a v1 handshake by cipher types,
- * and a v3/v2 handshake by Versions cell vs renegotiation.
- */
-int
-tor_tls_received_v3_certificate(tor_tls_t *tls)
-{
- X509 *cert = SSL_get_peer_certificate(tls->ssl);
- EVP_PKEY *key = NULL;
- X509_NAME *issuer_name, *subject_name;
- int is_v3 = 0;
-
- if (!cert) {
- log_warn(LD_BUG, "Called on a connection with no peer certificate");
- goto done;
- }
-
- subject_name = X509_get_subject_name(cert);
- issuer_name = X509_get_issuer_name(cert);
-
- if (X509_name_cmp(subject_name, issuer_name) == 0) {
- is_v3 = 1; /* purportedly self signed */
- goto done;
- }
-
- if (dn_indicates_v3_cert(subject_name) ||
- dn_indicates_v3_cert(issuer_name)) {
- is_v3 = 1; /* DN is fancy */
- goto done;
- }
-
- key = X509_get_pubkey(cert);
- if (EVP_PKEY_bits(key) != 1024 ||
- EVP_PKEY_type(key->type) != EVP_PKEY_RSA) {
- is_v3 = 1; /* Key is fancy */
- goto done;
- }
-
- done:
- if (key)
- EVP_PKEY_free(key);
- if (cert)
- X509_free(cert);
-
- return is_v3;
+ return ! tls->wasV2Handshake;
}
/** Return the number of server handshakes that we've noticed doing on
@@ -2785,33 +2366,108 @@ 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
+
+#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
+
+#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
+
/** 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.
*/
-int
-tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out)
+MOCK_IMPL(int,
+tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
{
#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
- char buf[128];
+ uint8_t buf[128];
size_t len;
tor_assert(tls);
- tor_assert(tls->ssl);
- tor_assert(tls->ssl->s3);
- tor_assert(tls->ssl->session);
+
+ 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
*/
- memcpy(buf + 0, tls->ssl->s3->client_random, 32);
- memcpy(buf + 32, tls->ssl->s3->server_random, 32);
- memcpy(buf + 64, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1);
- len = 64 + strlen(TLSSECRET_MAGIC) + 1;
crypto_hmac_sha256((char*)secrets_out,
- (char*)tls->ssl->session->master_key,
- tls->ssl->session->master_key_length,
- buf, len);
+ (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;
}
@@ -2819,12 +2475,23 @@ tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out)
* 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. */
-void
+ * 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
if (tls->ssl->s3->rbuf.buf)
*rbuf_capacity = tls->ssl->s3->rbuf.len;
else
@@ -2835,77 +2502,33 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls,
*wbuf_capacity = 0;
*rbuf_bytes = tls->ssl->s3->rbuf.left;
*wbuf_bytes = tls->ssl->s3->wbuf.left;
+ return 0;
+#endif
}
-#ifdef USE_BUFFEREVENTS
-/** Construct and return an TLS-encrypting bufferevent to send data over
- * <b>socket</b>, which must match the socket of the underlying bufferevent
- * <b>bufev_in</b>. The TLS object <b>tls</b> is used for encryption.
- *
- * This function will either create a filtering bufferevent that wraps around
- * <b>bufev_in</b>, or it will free bufev_in and return a new bufferevent that
- * uses the <b>tls</b> to talk to the network directly. Do not use
- * <b>bufev_in</b> after calling this function.
- *
- * The connection will start out doing a server handshake if <b>receiving</b>
- * is strue, and a client handshake otherwise.
- *
- * Returns NULL on failure.
+/** 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.
*/
-struct bufferevent *
-tor_tls_init_bufferevent(tor_tls_t *tls, struct bufferevent *bufev_in,
- evutil_socket_t socket, int receiving,
- int filter)
-{
- struct bufferevent *out;
- const enum bufferevent_ssl_state state = receiving ?
- BUFFEREVENT_SSL_ACCEPTING : BUFFEREVENT_SSL_CONNECTING;
-
- if (filter || tor_libevent_using_iocp_bufferevents()) {
- /* Grab an extra reference to the SSL, since BEV_OPT_CLOSE_ON_FREE
- means that the SSL will get freed too.
-
- This increment makes our SSL usage not-threadsafe, BTW. We should
- see if we're allowed to use CRYPTO_add from outside openssl. */
- tls->ssl->references += 1;
- out = bufferevent_openssl_filter_new(tor_libevent_get_base(),
- bufev_in,
- tls->ssl,
- state,
- BEV_OPT_DEFER_CALLBACKS|
- BEV_OPT_CLOSE_ON_FREE);
- /* Tell the underlying bufferevent when to accept more data from the SSL
- filter (only when it's got less than 32K to write), and when to notify
- the SSL filter that it could write more (when it drops under 24K). */
- bufferevent_setwatermark(bufev_in, EV_WRITE, 24*1024, 32*1024);
- } else {
- if (bufev_in) {
- evutil_socket_t s = bufferevent_getfd(bufev_in);
- tor_assert(s == -1 || s == socket);
- tor_assert(evbuffer_get_length(bufferevent_get_input(bufev_in)) == 0);
- tor_assert(evbuffer_get_length(bufferevent_get_output(bufev_in)) == 0);
- tor_assert(BIO_number_read(SSL_get_rbio(tls->ssl)) == 0);
- tor_assert(BIO_number_written(SSL_get_rbio(tls->ssl)) == 0);
- bufferevent_free(bufev_in);
- }
-
- /* Current versions (as of 2.0.x) of Libevent need to defer
- * bufferevent_openssl callbacks, or else our callback functions will
- * get called reentrantly, which is bad for us.
- */
- out = bufferevent_openssl_socket_new(tor_libevent_get_base(),
- socket,
- tls->ssl,
- state,
- BEV_OPT_DEFER_CALLBACKS);
- }
- tls->state = TOR_TLS_ST_BUFFEREVENT;
+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;
- /* Unblock _after_ creating the bufferevent, since accept/connect tend to
- * clear flags. */
- tor_tls_unblock_renegotiation(tls);
+ ec_key = EC_KEY_new_by_curve_name(nid);
+ ret = (ec_key != NULL);
+ EC_KEY_free(ec_key);
- return out;
+ return ret;
}
-#endif