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.c1096
1 files changed, 895 insertions, 201 deletions
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 4c9d2188d4..1120f3e8be 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -21,9 +21,10 @@
#endif
#include <assert.h>
-#ifdef MS_WINDOWS /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
- #define WIN32_WINNT 0x400
- #define _WIN32_WINNT 0x400
+#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>
@@ -40,20 +41,27 @@
#include <openssl/bio.h>
#include <openssl/opensslv.h>
-#if OPENSSL_VERSION_NUMBER < 0x00907000l
-#error "We require OpenSSL >= 0.9.7"
+#ifdef USE_BUFFEREVENTS
+#include <event2/bufferevent_ssl.h>
+#include <event2/buffer.h>
+#include <event2/event.h>
+#include "compat_libevent.h"
#endif
#define CRYPTO_PRIVATE /* to import prototypes from crypto.h */
+#define TORTLS_PRIVATE
#include "crypto.h"
#include "tortls.h"
#include "util.h"
#include "torlog.h"
#include "container.h"
-#include "ht.h"
#include <string.h>
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,7)
+#error "We require OpenSSL >= 0.9.7"
+#endif
+
/* Enable the "v2" TLS handshake.
*/
#define V2_HANDSHAKE_SERVER
@@ -68,9 +76,9 @@
#define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer")
-#if (OPENSSL_VERSION_NUMBER < 0x0090813fL || \
- (OPENSSL_VERSION_NUMBER >= 0x00909000L && \
- OPENSSL_VERSION_NUMBER < 0x1000006fL))
+#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
* the CVE-2011-4576 fix, and as such it can't use RELEASE_BUFFERS and
* SSL3 safely at the same time.
@@ -96,22 +104,36 @@ static int use_unsafe_renegotiation_op = 0;
* 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;
- X509 *my_cert;
- X509 *my_id_cert;
- crypto_pk_env_t *key;
+ 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;
+#define TOR_TLS_MAGIC 0x71571571
+
/** Holds a SSL object and its associated data. Members are only
* accessed from within tortls.c.
*/
struct tor_tls_t {
- HT_ENTRY(tor_tls_t) node;
+ 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. */
@@ -119,6 +141,7 @@ struct tor_tls_t {
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
} 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 */
@@ -127,8 +150,10 @@ struct tor_tls_t {
* 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. */
+ /** True iff we should call negotiated_callback when we're done reading. */
unsigned int got_renegotiate:1;
+ /** 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
@@ -153,65 +178,55 @@ static SSL_CIPHER *CLIENT_CIPHER_DUMMIES = NULL;
static STACK_OF(SSL_CIPHER) *CLIENT_CIPHER_STACK = NULL;
#endif
-/** Helper: compare tor_tls_t objects by its SSL. */
-static INLINE int
-tor_tls_entries_eq(const tor_tls_t *a, const tor_tls_t *b)
-{
- return a->ssl == b->ssl;
-}
+/** 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: return a hash value for a tor_tls_t by its SSL. */
-static INLINE unsigned int
-tor_tls_entry_hash(const tor_tls_t *a)
+/** Helper: Allocate tor_tls_object_ex_data_index. */
+static void
+tor_tls_allocate_tor_tls_object_ex_data_index(void)
{
-#if SIZEOF_INT == SIZEOF_VOID_P
- return ((unsigned int)(uintptr_t)a->ssl);
-#else
- return (unsigned int) ((((uint64_t)a->ssl)>>2) & UINT_MAX);
-#endif
+ 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);
+ }
}
-/** Map from SSL* pointers to tor_tls_t objects using those pointers.
- */
-static HT_HEAD(tlsmap, tor_tls_t) tlsmap_root = HT_INITIALIZER();
-
-HT_PROTOTYPE(tlsmap, tor_tls_t, node, tor_tls_entry_hash,
- tor_tls_entries_eq)
-HT_GENERATE(tlsmap, tor_tls_t, node, tor_tls_entry_hash,
- tor_tls_entries_eq, 0.6, malloc, realloc, free)
-
/** Helper: given a SSL* pointer, return the tor_tls_t object using that
* pointer. */
static INLINE tor_tls_t *
tor_tls_get_by_ssl(const SSL *ssl)
{
- tor_tls_t search, *result;
- memset(&search, 0, sizeof(search));
- search.ssl = (SSL*)ssl;
- result = HT_FIND(tlsmap, &tlsmap_root, &search);
+ 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 X509* tor_tls_create_certificate(crypto_pk_env_t *rsa,
- crypto_pk_env_t *rsa_sign,
+static X509* tor_tls_create_certificate(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
const char *cname,
const char *cname_sign,
unsigned int lifetime);
-static void tor_tls_unblock_renegotiation(tor_tls_t *tls);
+
static int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
- crypto_pk_env_t *identity,
+ crypto_pk_t *identity,
unsigned int key_lifetime,
int is_client);
-static tor_tls_context_t *tor_tls_context_new(crypto_pk_env_t *identity,
+static tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
unsigned int key_lifetime,
int is_client);
+static int check_cert_lifetime_internal(int severity, const X509 *cert,
+ int past_tolerance, int future_tolerance);
/** Global TLS contexts. We keep them here because nobody else needs
* to touch them. */
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;
@@ -219,20 +234,82 @@ static int tls_library_is_initialized = 0;
#define _TOR_TLS_SYSCALL (_MIN_TOR_TLS_ERROR_VAL - 2)
#define _TOR_TLS_ZERORETURN (_MIN_TOR_TLS_ERROR_VAL - 1)
-#include "tortls_states.h"
+/** 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);
+}
-/** Return the symbolic name of an OpenSSL state. */
-static const char *
-ssl_state_to_string(int ssl_state)
+void
+tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
+ int severity, int domain, const char *doing)
{
- static char buf[40];
- int i;
- for (i = 0; state_map[i].name; ++i) {
- if (state_map[i].state == ssl_state)
- return state_map[i].name;
+ 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:
+ case SSL_R_RECORD_TOO_LARGE:
+ 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) {
+ log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)",
+ doing, addr?" with ":"", addr?addr:"",
+ msg, lib, func, state);
+ } else {
+ log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)",
+ addr?" with ":"", addr?addr:"",
+ msg, lib, func, state);
}
- tor_snprintf(buf, sizeof(buf), "Unknown state %d", ssl_state);
- return buf;
}
/** Log all pending tls errors at level <b>severity</b>. Use
@@ -241,30 +318,10 @@ ssl_state_to_string(int ssl_state)
static void
tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
{
- const char *state = NULL;
- int st;
unsigned long err;
- const char *msg, *lib, *func, *addr;
- addr = tls ? tls->address : NULL;
- st = (tls && tls->ssl) ? tls->ssl->state : -1;
+
while ((err = ERR_get_error()) != 0) {
- msg = (const char*)ERR_reason_error_string(err);
- lib = (const char*)ERR_lib_error_string(err);
- func = (const char*)ERR_func_error_string(err);
- if (!state)
- state = (st>=0)?ssl_state_to_string(st):"---";
- if (!msg) msg = "(null)";
- if (!lib) lib = "(null)";
- if (!func) func = "(null)";
- if (doing) {
- log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)",
- doing, addr?" with ":"", addr?addr:"",
- msg, lib, func, state);
- } else {
- log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)",
- addr?" with ":"", addr?addr:"",
- msg, lib, func, state);
- }
+ tor_tls_log_one_error(tls, err, severity, domain, doing);
}
}
@@ -273,7 +330,7 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
static int
tor_errno_to_tls_error(int e)
{
-#if defined(MS_WINDOWS)
+#if defined(_WIN32)
switch (e) {
case WSAECONNRESET: // most common
return TOR_TLS_ERROR_CONNRESET;
@@ -355,14 +412,14 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra,
return _TOR_TLS_SYSCALL;
if (r == 0) {
log(severity, LD_NET, "TLS error: unexpected close while %s (%s)",
- doing, ssl_state_to_string(tls->ssl->state));
+ doing, SSL_state_string_long(tls->ssl));
tor_error = TOR_TLS_ERROR_IO;
} else {
int e = tor_socket_errno(tls->socket);
log(severity, LD_NET,
"TLS error: <syscall error while %s> (errno=%d: %s; state=%s)",
doing, e, tor_socket_strerror(e),
- ssl_state_to_string(tls->ssl->state));
+ SSL_state_string_long(tls->ssl));
tor_error = tor_errno_to_tls_error(e);
}
tls_log_errors(tls, severity, domain, doing);
@@ -371,7 +428,7 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra,
if (extra&CATCH_ZERO)
return _TOR_TLS_ZERORETURN;
log(severity, LD_NET, "TLS connection closed while %s in state %s",
- doing, ssl_state_to_string(tls->ssl->state));
+ doing, SSL_state_string_long(tls->ssl));
tls_log_errors(tls, severity, domain, doing);
return TOR_TLS_CLOSE;
default:
@@ -414,18 +471,20 @@ tor_tls_init(void)
* program should be allowed to use renegotiation unless it first passed
* a test of intelligence and determination.
*/
- if (version >= 0x009080c0L && version < 0x009080d0L) {
- log_notice(LD_GENERAL, "OpenSSL %s looks like version 0.9.8l; "
- "I will try SSL3_FLAGS to enable renegotation.",
+ if (version > OPENSSL_V(0,9,8,'k') && version <= OPENSSL_V(0,9,8,'l')) {
+ log_notice(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 >= 0x009080d0L) {
+ } else if (version > OPENSSL_V(0,9,8,'l')) {
log_notice(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 < 0x009080c0L) {
+ } else if (version <= OPENSSL_V(0,9,8,'k')) {
log_notice(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 "
@@ -435,10 +494,13 @@ tor_tls_init(void)
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);
}
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
tls_library_is_initialized = 1;
}
}
@@ -457,10 +519,6 @@ tor_tls_free_all(void)
client_tls_context = NULL;
tor_tls_context_decref(ctx);
}
- if (!HT_EMPTY(&tlsmap_root)) {
- log_warn(LD_MM, "Still have entries in the tlsmap at shutdown.");
- }
- HT_CLEAR(tlsmap, &tlsmap_root);
#ifdef V2_HANDSHAKE_CLIENT
if (CLIENT_CIPHER_DUMMIES)
tor_free(CLIENT_CIPHER_DUMMIES);
@@ -508,13 +566,19 @@ tor_x509_name_new(const char *cname)
* failure.
*/
static X509 *
-tor_tls_create_certificate(crypto_pk_env_t *rsa,
- crypto_pk_env_t *rsa_sign,
+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;
@@ -527,16 +591,23 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa,
tor_assert(cname);
tor_assert(rsa_sign);
tor_assert(cname_sign);
- if (!(sign_pkey = _crypto_pk_env_get_evp_pkey(rsa_sign,1)))
+ if (!(sign_pkey = _crypto_pk_get_evp_pkey(rsa_sign,1)))
goto error;
- if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,0)))
+ 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;
- if (!(ASN1_INTEGER_set(X509_get_serialNumber(x509), (long)start_time)))
- goto error;
+
+ { /* our serial number is 8 random bytes. */
+ if (crypto_rand((char *)serial_tmp, sizeof(serial_tmp)) < 0)
+ goto error;
+ 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;
@@ -569,11 +640,15 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa,
EVP_PKEY_free(sign_pkey);
if (pkey)
EVP_PKEY_free(pkey);
+ if (serial_number)
+ BN_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.*/
@@ -620,6 +695,137 @@ static const int N_CLIENT_CIPHERS =
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)
+{
+ if (! cert)
+ return;
+ if (cert->cert)
+ X509_free(cert->cert);
+ tor_free(cert->encoded);
+ memset(cert, 0x03, sizeof(*cert));
+ tor_free(cert);
+}
+
+/**
+ * Allocate a new tor_cert_t to hold the certificate "x509_cert".
+ *
+ * Steals a reference to x509_cert.
+ */
+static tor_cert_t *
+tor_cert_new(X509 *x509_cert)
+{
+ tor_cert_t *cert;
+ EVP_PKEY *pkey;
+ RSA *rsa;
+ int length, length2;
+ unsigned char *cp;
+
+ if (!x509_cert)
+ return NULL;
+
+ length = i2d_X509(x509_cert, NULL);
+ cert = tor_malloc_zero(sizeof(tor_cert_t));
+ if (length <= 0) {
+ tor_free(cert);
+ log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate");
+ X509_free(x509_cert);
+ return NULL;
+ }
+ cert->encoded_len = (size_t) length;
+ cp = cert->encoded = tor_malloc(length);
+ length2 = i2d_X509(x509_cert, &cp);
+ tor_assert(length2 == length);
+
+ cert->cert = x509_cert;
+
+ crypto_digest_all(&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);
+ cert->pkey_digests_set = 1;
+ crypto_pk_free(pk);
+ EVP_PKEY_free(pkey);
+ }
+
+ return cert;
+}
+
+/** 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)
+{
+ X509 *x509;
+ const unsigned char *cp = (const unsigned char *)certificate;
+ tor_cert_t *newcert;
+ tor_assert(certificate);
+
+ if (certificate_len > INT_MAX)
+ return NULL;
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8)
+ /* This ifdef suppresses a type warning. Take out this case once everybody
+ * is using OpenSSL 0.9.8 or later. */
+ x509 = d2i_X509(NULL, (unsigned char**)&cp, (int)certificate_len);
+#else
+ x509 = d2i_X509(NULL, &cp, (int)certificate_len);
+#endif
+ if (!x509)
+ return NULL; /* Couldn't decode */
+ if (cp - certificate != (int)certificate_len) {
+ X509_free(x509);
+ return NULL; /* Didn't use all the bytes */
+ }
+ newcert = tor_cert_new(x509);
+ if (!newcert) {
+ return NULL;
+ }
+ if (newcert->encoded_len != certificate_len ||
+ fast_memneq(newcert->encoded, certificate, certificate_len)) {
+ /* Cert wasn't in DER */
+ tor_cert_free(newcert);
+ return NULL;
+ }
+ return newcert;
+}
+
+/** 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,
+ 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 digests_t *
+tor_cert_get_id_digests(const tor_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 digests_t *
+tor_cert_get_cert_digests(const tor_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
@@ -628,13 +834,172 @@ tor_tls_context_decref(tor_tls_context_t *ctx)
tor_assert(ctx);
if (--ctx->refcnt == 0) {
SSL_CTX_free(ctx->ctx);
- X509_free(ctx->my_cert);
- X509_free(ctx->my_id_cert);
- crypto_free_pk_env(ctx->key);
+ tor_cert_free(ctx->my_link_cert);
+ tor_cert_free(ctx->my_id_cert);
+ tor_cert_free(ctx->my_auth_cert);
+ crypto_pk_free(ctx->link_key);
+ crypto_pk_free(ctx->auth_key);
tor_free(ctx);
}
}
+/** 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
+ * that we use in server mode; otherwise, provide the certs that we use in
+ * client mode. */
+int
+tor_tls_get_my_certs(int server,
+ const tor_cert_t **link_cert_out,
+ const tor_cert_t **id_cert_out)
+{
+ tor_tls_context_t *ctx = server ? server_tls_context : client_tls_context;
+ if (! ctx)
+ return -1;
+ if (link_cert_out)
+ *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert;
+ if (id_cert_out)
+ *id_cert_out = ctx->my_id_cert;
+ return 0;
+}
+
+/**
+ * Return the authentication key that we use to authenticate ourselves as a
+ * client in the V3 in-protocol handshake.
+ */
+crypto_pk_t *
+tor_tls_get_my_client_auth_key(void)
+{
+ if (! client_tls_context)
+ return NULL;
+ return client_tls_context->auth_key;
+}
+
+/**
+ * Return a newly allocated copy of the public key that a certificate
+ * certifies. Return NULL if the cert's key is not RSA.
+ */
+crypto_pk_t *
+tor_tls_cert_get_key(tor_cert_t *cert)
+{
+ crypto_pk_t *result = NULL;
+ EVP_PKEY *pkey = X509_get_pubkey(cert->cert);
+ RSA *rsa;
+ if (!pkey)
+ return NULL;
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ if (!rsa) {
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ result = _crypto_new_pk_from_rsa(rsa);
+ EVP_PKEY_free(pkey);
+ return result;
+}
+
+/** Return true iff <b>a</b> and <b>b</b> represent the same public key. */
+static int
+pkey_eq(EVP_PKEY *a, EVP_PKEY *b)
+{
+ /* We'd like to do this, but openssl 0.9.7 doesn't have it:
+ return EVP_PKEY_cmp(a,b) == 1;
+ */
+ unsigned char *a_enc=NULL, *b_enc=NULL, *a_ptr, *b_ptr;
+ int a_len1, b_len1, a_len2, b_len2, result;
+ a_len1 = i2d_PublicKey(a, NULL);
+ b_len1 = i2d_PublicKey(b, NULL);
+ if (a_len1 != b_len1)
+ return 0;
+ a_ptr = a_enc = tor_malloc(a_len1);
+ b_ptr = b_enc = tor_malloc(b_len1);
+ a_len2 = i2d_PublicKey(a, &a_ptr);
+ b_len2 = i2d_PublicKey(b, &b_ptr);
+ tor_assert(a_len2 == a_len1);
+ tor_assert(b_len2 == b_len1);
+ result = tor_memeq(a_enc, b_enc, a_len1);
+ tor_free(a_enc);
+ tor_free(b_enc);
+ return result;
+}
+
+/** 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)
+{
+ X509 *peercert = SSL_get_peer_certificate(tls->ssl);
+ EVP_PKEY *link_key = NULL, *cert_key = NULL;
+ int result;
+
+ if (!peercert)
+ return 0;
+ link_key = X509_get_pubkey(peercert);
+ cert_key = X509_get_pubkey(cert->cert);
+
+ result = link_key && cert_key && pkey_eq(cert_key, link_key);
+
+ X509_free(peercert);
+ if (link_key)
+ EVP_PKEY_free(link_key);
+ if (cert_key)
+ EVP_PKEY_free(cert_key);
+
+ return result;
+}
+
+/** 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_cert_t *cert,
+ const tor_cert_t *signing_cert,
+ int check_rsa_1024)
+{
+ EVP_PKEY *cert_key;
+ EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
+ int r, key_ok = 0;
+ if (!signing_key)
+ return 0;
+ r = X509_verify(cert->cert, signing_key);
+ EVP_PKEY_free(signing_key);
+ if (r <= 0)
+ return 0;
+
+ /* 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;
+
+ cert_key = X509_get_pubkey(cert->cert);
+ if (check_rsa_1024 && cert_key) {
+ RSA *rsa = EVP_PKEY_get1_RSA(cert_key);
+ if (rsa && BN_num_bits(rsa->n) == 1024)
+ 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)
+ min_bits = 128;
+#endif
+ if (EVP_PKEY_bits(cert_key) >= min_bits)
+ key_ok = 1;
+ }
+ EVP_PKEY_free(cert_key);
+ if (!key_ok)
+ return 0;
+
+ /* XXXX compare DNs or anything? */
+
+ return 1;
+}
+
/** Increase the reference count of <b>ctx</b>. */
static void
tor_tls_context_incref(tor_tls_context_t *ctx)
@@ -650,8 +1015,8 @@ tor_tls_context_incref(tor_tls_context_t *ctx)
* ignore <b>client_identity</b>. */
int
tor_tls_context_init(int is_public_server,
- crypto_pk_env_t *client_identity,
- crypto_pk_env_t *server_identity,
+ crypto_pk_t *client_identity,
+ crypto_pk_t *server_identity,
unsigned int key_lifetime)
{
int rv1 = 0;
@@ -709,7 +1074,7 @@ tor_tls_context_init(int is_public_server,
*/
static int
tor_tls_context_init_one(tor_tls_context_t **ppcontext,
- crypto_pk_env_t *identity,
+ crypto_pk_t *identity,
unsigned int key_lifetime,
int is_client)
{
@@ -737,32 +1102,45 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext,
* certificate.
*/
static tor_tls_context_t *
-tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
+tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
int is_client)
{
- crypto_pk_env_t *rsa = NULL;
+ crypto_pk_t *rsa = NULL, *rsa_auth = NULL;
EVP_PKEY *pkey = NULL;
tor_tls_context_t *result = NULL;
- X509 *cert = NULL, *idcert = NULL;
+ X509 *cert = NULL, *idcert = NULL, *authcert = NULL;
char *nickname = NULL, *nn2 = NULL;
tor_tls_init();
nickname = crypto_random_hostname(8, 20, "www.", ".net");
+#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
nn2 = crypto_random_hostname(8, 20, "www.", ".net");
+#else
+ nn2 = crypto_random_hostname(8, 20, "www.", ".com");
+#endif
- /* Generate short-term RSA key. */
- if (!(rsa = crypto_new_pk_env()))
+ /* Generate short-term RSA key for use with TLS. */
+ if (!(rsa = crypto_pk_new()))
goto error;
if (crypto_pk_generate_key(rsa)<0)
goto error;
if (!is_client) {
- /* Create certificate signed by identity key. */
+ /* 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);
- if (!cert || !idcert) {
+ /* 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(LOG_WARN, LD_CRYPTO, "Error creating certificate");
goto error;
}
@@ -771,9 +1149,13 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
result = tor_malloc_zero(sizeof(tor_tls_context_t));
result->refcnt = 1;
if (!is_client) {
- result->my_cert = X509_dup(cert);
- result->my_id_cert = X509_dup(idcert);
- result->key = crypto_pk_dup_key(rsa);
+ 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));
+ 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
@@ -794,9 +1176,9 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
#ifdef DISABLE_SSL3_HANDSHAKE
1 ||
#endif
- SSLeay() < 0x0090813fL ||
- (SSLeay() >= 0x00909000L &&
- SSLeay() < 0x1000006fL)) {
+ 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 "
@@ -843,7 +1225,7 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF);
if (!is_client) {
tor_assert(rsa);
- if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,1)))
+ if (!(pkey = _crypto_pk_get_evp_pkey(rsa,1)))
goto error;
if (!SSL_CTX_use_PrivateKey(result->ctx, pkey))
goto error;
@@ -853,9 +1235,9 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
goto error;
}
{
- crypto_dh_env_t *dh = crypto_dh_new(DH_TYPE_TLS);
+ crypto_dh_t *dh = crypto_dh_new(DH_TYPE_TLS);
tor_assert(dh);
- SSL_CTX_set_tmp_dh(result->ctx, _crypto_dh_env_get_dh(dh));
+ SSL_CTX_set_tmp_dh(result->ctx, _crypto_dh_get_dh(dh));
crypto_dh_free(dh);
}
SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
@@ -864,7 +1246,10 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
if (rsa)
- crypto_free_pk_env(rsa);
+ crypto_pk_free(rsa);
+ if (rsa_auth)
+ crypto_pk_free(rsa_auth);
+ X509_free(authcert);
tor_free(nickname);
tor_free(nn2);
return result;
@@ -876,13 +1261,17 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
if (pkey)
EVP_PKEY_free(pkey);
if (rsa)
- crypto_free_pk_env(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;
}
@@ -922,7 +1311,7 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
return 0;
dump_list:
{
- smartlist_t *elts = smartlist_create();
+ smartlist_t *elts = smartlist_new();
char *s;
for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) {
SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i);
@@ -938,6 +1327,13 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
return 1;
}
+static void
+tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
+{
+ log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
+ ssl, SSL_state_string_long(ssl), type, val);
+}
+
/** 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
@@ -949,6 +1345,9 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
{
tor_tls_t *tls;
(void) val;
+
+ tor_tls_debug_state_callback(ssl, type, val);
+
if (type != SSL_CB_ACCEPT_LOOP)
return;
if (ssl->state != SSL3_ST_SW_SRVR_HELLO_A)
@@ -959,13 +1358,18 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
/* Check whether we're watching for renegotiates. If so, this is one! */
if (tls->negotiated_callback)
tls->got_renegotiate = 1;
+ if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/
+ ++tls->server_handshake_count;
} else {
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
+ return;
}
/* Now check the cipher list. */
if (tor_tls_client_is_using_v2_ciphers(ssl, ADDR(tls))) {
- /*XXXX_TLS keep this from happening more than once! */
+ 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! */
@@ -977,6 +1381,10 @@ 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 {
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
}
@@ -1064,6 +1472,7 @@ tor_tls_new(int sock, int isServer)
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;
tor_assert(context); /* make sure somebody made it first */
if (!(result->ssl = SSL_new(context->ctx))) {
@@ -1104,7 +1513,14 @@ tor_tls_new(int sock, int isServer)
tor_free(result);
return NULL;
}
- HT_INSERT(tlsmap, &tlsmap_root, result);
+ {
+ 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;
@@ -1120,8 +1536,11 @@ tor_tls_new(int sock, int isServer)
#ifdef V2_HANDSHAKE_SERVER
if (isServer) {
SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
- }
+ } else
#endif
+ {
+ SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback);
+ }
/* Not expected to get called. */
tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object");
@@ -1155,7 +1574,7 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
if (cb) {
SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback);
} else {
- SSL_set_info_callback(tls->ssl, NULL);
+ SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback);
}
#endif
}
@@ -1163,7 +1582,7 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
/** If this version of openssl requires it, turn on renegotiation on
* <b>tls</b>.
*/
-static void
+void
tor_tls_unblock_renegotiation(tor_tls_t *tls)
{
/* Yes, we know what we are doing here. No, we do not treat a renegotiation
@@ -1187,6 +1606,19 @@ tor_tls_block_renegotiation(tor_tls_t *tls)
tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
}
+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));
+ }
+}
+
/** Return whether this tls initiated the connect (client) or
* received it (server). */
int
@@ -1202,14 +1634,9 @@ tor_tls_is_server(tor_tls_t *tls)
void
tor_tls_free(tor_tls_t *tls)
{
- tor_tls_t *removed;
if (!tls)
return;
tor_assert(tls->ssl);
- removed = HT_REMOVE(tlsmap, &tlsmap_root, tls);
- if (!removed) {
- log_warn(LD_BUG, "Freeing a TLS that was not in the ssl->tls map.");
- }
#ifdef SSL_set_tlsext_host_name
SSL_set_tlsext_host_name(tls->ssl, NULL);
#endif
@@ -1219,6 +1646,7 @@ tor_tls_free(tor_tls_t *tls)
if (tls->context)
tor_tls_context_decref(tls->context);
tor_free(tls->address);
+ tls->magic = 0x99999999;
tor_free(tls);
}
@@ -1310,16 +1738,16 @@ tor_tls_handshake(tor_tls_t *tls)
oldstate = tls->ssl->state;
if (tls->isServer) {
log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls,
- ssl_state_to_string(tls->ssl->state));
+ 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_to_string(tls->ssl->state));
+ SSL_state_string_long(tls->ssl));
r = SSL_connect(tls->ssl);
}
if (oldstate != tls->ssl->state)
log_debug(LD_HANDSHAKE, "After call, %p was in state %s",
- tls, ssl_state_to_string(tls->ssl->state));
+ 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);
@@ -1331,56 +1759,86 @@ tor_tls_handshake(tor_tls_t *tls)
}
if (r == TOR_TLS_DONE) {
tls->state = TOR_TLS_ST_OPEN;
- 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;
+ return tor_tls_finish_handshake(tls);
+ }
+ return r;
+}
+
+/** Perform the final part of the intial 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;
+ 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
- if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) {
- /* 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;
+ if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) {
+ /* 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.");
}
-#endif
+ tls->wasV2Handshake = 1;
+ log_debug(LD_HANDSHAKE, "Completed V2 TLS handshake with client; waiting"
+ " for renegotiation.");
} 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);
+ /* 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
- 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;
- }
+ 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;
}
}
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.
@@ -1486,9 +1944,21 @@ 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)
+{
+ 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);
+}
+
/** Warn that a certificate lifetime extends through a certain range. */
static void
-log_cert_lifetime(X509 *cert, const char *problem)
+log_cert_lifetime(int severity, const X509 *cert, const char *problem)
{
BIO *bio = NULL;
BUF_MEM *buf;
@@ -1498,9 +1968,10 @@ log_cert_lifetime(X509 *cert, const char *problem)
struct tm tm;
if (problem)
- log_warn(LD_GENERAL,
- "Certificate %s: is your system clock set incorrectly?",
- problem);
+ 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;
@@ -1522,9 +1993,9 @@ log_cert_lifetime(X509 *cert, const char *problem)
strftime(mytime, 32, "%b %d %H:%M:%S %Y GMT", tor_gmtime_r(&now, &tm));
- log_warn(LD_GENERAL,
- "(certificate lifetime runs from %s through %s. Your time is %s.)",
- s1,s2,mytime);
+ log(severity, LD_GENERAL,
+ "(certificate lifetime runs from %s through %s. Your time is %s.)",
+ s1,s2,mytime);
end:
/* Not expected to get invoked */
@@ -1581,7 +2052,7 @@ try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
* 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_env_t **identity_key)
+tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key)
{
X509 *cert = NULL, *id_cert = NULL;
EVP_PKEY *id_pkey = NULL;
@@ -1597,6 +2068,8 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_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");
+
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");
@@ -1607,7 +2080,7 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
rsa = EVP_PKEY_get1_RSA(id_pkey);
if (!rsa)
goto done;
- *identity_key = _crypto_new_pk_env_rsa(rsa);
+ *identity_key = _crypto_new_pk_from_rsa(rsa);
r = 0;
@@ -1624,34 +2097,25 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
return r;
}
-/** Check whether the certificate set on the connection <b>tls</b> is
- * expired or not-yet-valid, give or take <b>tolerance</b>
- * seconds. Return 0 for valid, -1 for failure.
+/** 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(tor_tls_t *tls, int tolerance)
+tor_tls_check_lifetime(int severity, tor_tls_t *tls,
+ int past_tolerance, int future_tolerance)
{
- time_t now, t;
X509 *cert;
int r = -1;
- now = time(NULL);
-
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
goto done;
- t = now + tolerance;
- if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) {
- log_cert_lifetime(cert, "not yet valid");
+ if (check_cert_lifetime_internal(severity, cert,
+ past_tolerance, future_tolerance) < 0)
goto done;
- }
- t = now - tolerance;
- if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) {
- log_cert_lifetime(cert, "already expired");
- goto done;
- }
r = 0;
done:
@@ -1663,6 +2127,32 @@ tor_tls_check_lifetime(tor_tls_t *tls, int tolerance)
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. 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,
+ int past_tolerance, int future_tolerance)
+{
+ time_t now, t;
+
+ now = time(NULL);
+
+ t = now + future_tolerance;
+ if (X509_cmp_time(X509_get_notBefore(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) {
+ log_cert_lifetime(severity, cert, "already expired");
+ return -1;
+ }
+
+ return 0;
+}
+
/** Return the number of bytes available for reading from <b>tls</b>.
*/
int
@@ -1746,6 +2236,138 @@ tor_tls_used_v1_handshake(tor_tls_t *tls)
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;
+ 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 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;
+}
+
+/** 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)
+{
+#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
+ char buf[128];
+ size_t len;
+ tor_assert(tls);
+ tor_assert(tls->ssl);
+ tor_assert(tls->ssl->s3);
+ tor_assert(tls->ssl->session);
+ /*
+ 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);
+ memset(buf, 0, sizeof(buf));
+ return 0;
+}
+
/** 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.
@@ -1768,3 +2390,75 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls,
*wbuf_bytes = tls->ssl->s3->wbuf.left;
}
+#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.
+ */
+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;
+
+ /* Unblock _after_ creating the bufferevent, since accept/connect tend to
+ * clear flags. */
+ tor_tls_unblock_renegotiation(tls);
+
+ return out;
+}
+#endif
+