Commit 5205c7fd authored by Nick Mathewson's avatar Nick Mathewson 🥄
Browse files

Initial NSS support for TLS.

This is enough to get a chutney network to bootstrap, though a bunch
of work remains.
parent c567b8fc
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
/* See LICENSE for licensing information */ /* See LICENSE for licensing information */
#define TORTLS_PRIVATE #define TORTLS_PRIVATE
#define TOR_X509_PRIVATE
#include "lib/tls/x509.h" #include "lib/tls/x509.h"
#include "lib/tls/x509_internal.h" #include "lib/tls/x509_internal.h"
#include "lib/tls/tortls.h" #include "lib/tls/tortls.h"
...@@ -14,6 +15,8 @@ ...@@ -14,6 +15,8 @@
#include "lib/crypt_ops/crypto_rsa.h" #include "lib/crypt_ops/crypto_rsa.h"
#include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_rand.h"
#include <time.h>
/** Global TLS contexts. We keep them here because nobody else needs /** Global TLS contexts. We keep them here because nobody else needs
* to touch them. * to touch them.
* *
...@@ -31,6 +34,26 @@ tor_tls_context_get(int is_server) ...@@ -31,6 +34,26 @@ tor_tls_context_get(int is_server)
return is_server ? server_tls_context : client_tls_context; return is_server ? server_tls_context : client_tls_context;
} }
/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
* code. */
int
tor_errno_to_tls_error(int e)
{
switch (e) {
case SOCK_ERRNO(ECONNRESET): // most common
return TOR_TLS_ERROR_CONNRESET;
case SOCK_ERRNO(ETIMEDOUT):
return TOR_TLS_ERROR_TIMEOUT;
case SOCK_ERRNO(EHOSTUNREACH):
case SOCK_ERRNO(ENETUNREACH):
return TOR_TLS_ERROR_NO_ROUTE;
case SOCK_ERRNO(ECONNREFUSED):
return TOR_TLS_ERROR_CONNREFUSED; // least common
default:
return TOR_TLS_ERROR_MISC;
}
}
/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate /** 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 * 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 * handshake's certificate chain. If <b>server</b> is true, provide the certs
...@@ -334,3 +357,102 @@ tor_tls_is_server(tor_tls_t *tls) ...@@ -334,3 +357,102 @@ tor_tls_is_server(tor_tls_t *tls)
tor_assert(tls); tor_assert(tls);
return tls->isServer; return tls->isServer;
} }
/** Release resources associated with a TLS object. Does not close the
* underlying file descriptor.
*/
void
tor_tls_free_(tor_tls_t *tls)
{
if (!tls)
return;
tor_assert(tls->ssl);
{
size_t r,w;
tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */
}
tor_tls_impl_free_(tls->ssl);
tls->ssl = NULL;
#ifdef ENABLE_OPENSSL
tls->negotiated_callback = NULL;
#endif
if (tls->context)
tor_tls_context_decref(tls->context);
tor_free(tls->address);
tls->magic = 0x99999999;
tor_free(tls);
}
/** If the provided tls connection is authenticated and has a
* certificate chain that is currently valid and signed, then set
* *<b>identity_key</b> to the identity certificate's key and return
* 0. Else, return -1 and log complaints with log-level <b>severity</b>.
*/
int
tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity)
{
tor_x509_cert_impl_t *cert = NULL, *id_cert = NULL;
tor_x509_cert_t *peer_x509 = NULL, *id_x509 = NULL;
tor_assert(tls);
tor_assert(identity);
int rv = -1;
try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert);
if (!cert)
goto done;
if (!id_cert) {
log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
goto done;
}
peer_x509 = tor_x509_cert_new(cert);
id_x509 = tor_x509_cert_new(id_cert);
cert = id_cert = NULL; /* Prevent double-free */
if (! tor_tls_cert_is_valid(severity, peer_x509, id_x509, time(NULL), 0)) {
goto done;
}
*identity = tor_tls_cert_get_key(id_x509);
rv = 0;
done:
if (cert)
tor_x509_cert_impl_free_(cert);
if (id_cert)
tor_x509_cert_impl_free_(id_cert);
tor_x509_cert_free(peer_x509);
tor_x509_cert_free(id_x509);
return rv;
}
/** Check whether the certificate set on the connection <b>tls</b> is expired
* give or take <b>past_tolerance</b> seconds, or not-yet-valid give or take
* <b>future_tolerance</b> seconds. Return 0 for valid, -1 for failure.
*
* NOTE: you should call tor_tls_verify before tor_tls_check_lifetime.
*/
int
tor_tls_check_lifetime(int severity, tor_tls_t *tls,
time_t now,
int past_tolerance, int future_tolerance)
{
tor_x509_cert_t *cert;
int r = -1;
if (!(cert = tor_tls_get_peer_cert(tls)))
goto done;
if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now,
past_tolerance,
future_tolerance) < 0)
goto done;
r = 0;
done:
tor_x509_cert_free(cert);
/* Not expected to get invoked */
tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime");
return r;
}
...@@ -13,10 +13,25 @@ ...@@ -13,10 +13,25 @@
#include "lib/crypt_ops/crypto_rsa.h" #include "lib/crypt_ops/crypto_rsa.h"
#include "lib/testsupport/testsupport.h" #include "lib/testsupport/testsupport.h"
#include "lib/net/nettypes.h"
/* Opaque structure to hold a TLS connection. */ /* Opaque structure to hold a TLS connection. */
typedef struct tor_tls_t tor_tls_t; typedef struct tor_tls_t tor_tls_t;
#ifdef TORTLS_PRIVATE
#ifdef ENABLE_OPENSSL
struct ssl_st;
struct ssl_ctx_st;
struct ssl_session_st;
typedef struct ssl_ctx_st tor_tls_context_impl_t;
typedef struct ssl_st tor_tls_impl_t;
#else
struct PRFileDesc;
typedef struct PRFileDesc tor_tls_context_impl_t;
typedef struct PRFileDesc tor_tls_impl_t;
#endif
#endif
struct tor_x509_cert_t; struct tor_x509_cert_t;
/* Possible return values for most tor_tls_* functions. */ /* Possible return values for most tor_tls_* functions. */
...@@ -73,7 +88,7 @@ int tor_tls_context_init(unsigned flags, ...@@ -73,7 +88,7 @@ int tor_tls_context_init(unsigned flags,
void tor_tls_context_incref(tor_tls_context_t *ctx); void tor_tls_context_incref(tor_tls_context_t *ctx);
void tor_tls_context_decref(tor_tls_context_t *ctx); void tor_tls_context_decref(tor_tls_context_t *ctx);
tor_tls_context_t *tor_tls_context_get(int is_server); tor_tls_context_t *tor_tls_context_get(int is_server);
tor_tls_t *tor_tls_new(int sock, int is_server); tor_tls_t *tor_tls_new(tor_socket_t sock, int is_server);
void tor_tls_set_logged_address(tor_tls_t *tls, const char *address); void tor_tls_set_logged_address(tor_tls_t *tls, const char *address);
void tor_tls_set_renegotiate_callback(tor_tls_t *tls, void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
void (*cb)(tor_tls_t *, void *arg), void (*cb)(tor_tls_t *, void *arg),
...@@ -121,13 +136,17 @@ MOCK_DECL(int,tor_tls_export_key_material,( ...@@ -121,13 +136,17 @@ MOCK_DECL(int,tor_tls_export_key_material,(
size_t context_len, size_t context_len,
const char *label)); const char *label));
#ifdef ENABLE_OPENSSL
/* Log and abort if there are unhandled TLS errors in OpenSSL's error stack. /* Log and abort if there are unhandled TLS errors in OpenSSL's error stack.
*/ */
#define check_no_tls_errors() check_no_tls_errors_(__FILE__,__LINE__) #define check_no_tls_errors() check_no_tls_errors_(__FILE__,__LINE__)
void check_no_tls_errors_(const char *fname, int line); void check_no_tls_errors_(const char *fname, int line);
void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
int severity, int domain, const char *doing); int severity, int domain, const char *doing);
#else
#define check_no_tls_errors() STMT_NIL
#endif
int tor_tls_get_my_certs(int server, int tor_tls_get_my_certs(int server,
const struct tor_x509_cert_t **link_cert_out, const struct tor_x509_cert_t **link_cert_out,
......
...@@ -6,15 +6,11 @@ ...@@ -6,15 +6,11 @@
#ifndef TORTLS_INTERNAL_H #ifndef TORTLS_INTERNAL_H
#define TORTLS_INTERNAL_H #define TORTLS_INTERNAL_H
#ifdef ENABLE_OPENSSL
struct ssl_st;
struct ssl_ctx_st;
struct ssl_session_st;
#endif
int tor_errno_to_tls_error(int e); int tor_errno_to_tls_error(int e);
#ifdef ENABLE_OPENSSL
int tor_tls_get_error(tor_tls_t *tls, int r, int extra, int tor_tls_get_error(tor_tls_t *tls, int r, int extra,
const char *doing, int severity, int domain); const char *doing, int severity, int domain);
#endif
MOCK_DECL(void, try_to_extract_certs_from_tls, MOCK_DECL(void, try_to_extract_certs_from_tls,
(int severity, tor_tls_t *tls, (int severity, tor_tls_t *tls,
tor_x509_cert_impl_t **cert_out, tor_x509_cert_impl_t **cert_out,
...@@ -31,13 +27,9 @@ int tor_tls_context_init_certificates(tor_tls_context_t *result, ...@@ -31,13 +27,9 @@ int tor_tls_context_init_certificates(tor_tls_context_t *result,
crypto_pk_t *identity, crypto_pk_t *identity,
unsigned key_lifetime, unsigned key_lifetime,
unsigned flags); unsigned flags);
void tor_tls_impl_free_(tor_tls_impl_t *ssl);
#ifdef ENABLE_OPENSSL void tor_tls_context_impl_free(tor_tls_context_impl_t *);
void tor_tls_context_impl_free(struct ssl_ctx_st *);
#else
struct ssl_ctx_st; // XXXX replace
void tor_tls_context_impl_free(struct ssl_ctx_st *);
#endif
#ifdef ENABLE_OPENSSL #ifdef ENABLE_OPENSSL
tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl); tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl);
......
This diff is collapsed.
...@@ -244,26 +244,6 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) ...@@ -244,26 +244,6 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
} }
} }
/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
* code. */
int
tor_errno_to_tls_error(int e)
{
switch (e) {
case SOCK_ERRNO(ECONNRESET): // most common
return TOR_TLS_ERROR_CONNRESET;
case SOCK_ERRNO(ETIMEDOUT):
return TOR_TLS_ERROR_TIMEOUT;
case SOCK_ERRNO(EHOSTUNREACH):
case SOCK_ERRNO(ENETUNREACH):
return TOR_TLS_ERROR_NO_ROUTE;
case SOCK_ERRNO(ECONNREFUSED):
return TOR_TLS_ERROR_CONNREFUSED; // least common
default:
return TOR_TLS_ERROR_MISC;
}
}
#define CATCH_SYSCALL 1 #define CATCH_SYSCALL 1
#define CATCH_ZERO 2 #define CATCH_ZERO 2
...@@ -952,8 +932,6 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) ...@@ -952,8 +932,6 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
/* Check whether we're watching for renegotiates. If so, this is one! */ /* Check whether we're watching for renegotiates. If so, this is one! */
if (tls->negotiated_callback) if (tls->negotiated_callback)
tls->got_renegotiate = 1; tls->got_renegotiate = 1;
if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/
++tls->server_handshake_count;
} else { } else {
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
return; return;
...@@ -1167,30 +1145,13 @@ tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) ...@@ -1167,30 +1145,13 @@ tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
#endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */ #endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */
} }
/** Release resources associated with a TLS object. Does not close the
* underlying file descriptor.
*/
void void
tor_tls_free_(tor_tls_t *tls) tor_tls_impl_free_(tor_tls_impl_t *ssl)
{ {
if (!tls)
return;
tor_assert(tls->ssl);
{
size_t r,w;
tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */
}
#ifdef SSL_set_tlsext_host_name #ifdef SSL_set_tlsext_host_name
SSL_set_tlsext_host_name(tls->ssl, NULL); SSL_set_tlsext_host_name(ssl, NULL);
#endif #endif
SSL_free(tls->ssl); SSL_free(ssl);
tls->ssl = NULL;
tls->negotiated_callback = NULL;
if (tls->context)
tor_tls_context_decref(tls->context);
tor_free(tls->address);
tls->magic = 0x99999999;
tor_free(tls);
} }
/** Underlying function for TLS reading. Reads up to <b>len</b> /** Underlying function for TLS reading. Reads up to <b>len</b>
...@@ -1509,90 +1470,6 @@ try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls, ...@@ -1509,90 +1470,6 @@ try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
*id_cert_out = id_cert; *id_cert_out = id_cert;
} }
/** If the provided tls connection is authenticated and has a
* certificate chain that is currently valid and signed, then set
* *<b>identity_key</b> to the identity certificate's key and return
* 0. Else, return -1 and log complaints with log-level <b>severity</b>.
*/
int
tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key)
{
X509 *cert = NULL, *id_cert = NULL;
EVP_PKEY *id_pkey = NULL;
RSA *rsa;
int r = -1;
check_no_tls_errors();
*identity_key = NULL;
try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert);
if (!cert)
goto done;
if (!id_cert) {
log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
goto done;
}
tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate");
if (!(id_pkey = X509_get_pubkey(id_cert)) ||
X509_verify(cert, id_pkey) <= 0) {
log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
tls_log_errors(tls, severity, LD_HANDSHAKE, "verifying certificate");
goto done;
}
rsa = EVP_PKEY_get1_RSA(id_pkey);
if (!rsa)
goto done;
*identity_key = crypto_new_pk_from_openssl_rsa_(rsa);
r = 0;
done:
if (cert)
X509_free(cert);
if (id_pkey)
EVP_PKEY_free(id_pkey);
/* This should never get invoked, but let's make sure in case OpenSSL
* acts unexpectedly. */
tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "finishing tor_tls_verify");
return r;
}
/** Check whether the certificate set on the connection <b>tls</b> is expired
* give or take <b>past_tolerance</b> seconds, or not-yet-valid give or take
* <b>future_tolerance</b> seconds. Return 0 for valid, -1 for failure.
*
* NOTE: you should call tor_tls_verify before tor_tls_check_lifetime.
*/
int
tor_tls_check_lifetime(int severity, tor_tls_t *tls,
time_t now,
int past_tolerance, int future_tolerance)
{
X509 *cert;
int r = -1;
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
goto done;
if (tor_x509_check_cert_lifetime_internal(severity, cert, now,
past_tolerance,
future_tolerance) < 0)
goto done;
r = 0;
done:
if (cert)
X509_free(cert);
/* Not expected to get invoked */
tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime");
return r;
}
/** Return the number of bytes available for reading from <b>tls</b>. /** Return the number of bytes available for reading from <b>tls</b>.
*/ */
int int
...@@ -1690,14 +1567,6 @@ tor_tls_used_v1_handshake(tor_tls_t *tls) ...@@ -1690,14 +1567,6 @@ tor_tls_used_v1_handshake(tor_tls_t *tls)
return ! tls->wasV2Handshake; return ! tls->wasV2Handshake;
} }
/** Return the number of server handshakes that we've noticed doing on
* <b>tls</b>. */
int
tor_tls_get_num_server_handshakes(tor_tls_t *tls)
{
return tls->server_handshake_count;
}
/** Return true iff the server TLS connection <b>tls</b> got the renegotiation /** Return true iff the server TLS connection <b>tls</b> got the renegotiation
* request it was waiting for. */ * request it was waiting for. */
int int
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#ifndef TOR_TORTLS_ST_H #ifndef TOR_TORTLS_ST_H
#define TOR_TORTLS_ST_H #define TOR_TORTLS_ST_H
#include "lib/net/socket.h"
#define TOR_TLS_MAGIC 0x71571571 #define TOR_TLS_MAGIC 0x71571571
typedef enum { typedef enum {
...@@ -17,7 +19,7 @@ typedef enum { ...@@ -17,7 +19,7 @@ typedef enum {
struct tor_tls_context_t { struct tor_tls_context_t {
int refcnt; int refcnt;
struct ssl_ctx_st *ctx; tor_tls_context_impl_t *ctx;
struct tor_x509_cert_t *my_link_cert; struct tor_x509_cert_t *my_link_cert;
struct tor_x509_cert_t *my_id_cert; struct tor_x509_cert_t *my_id_cert;
struct tor_x509_cert_t *my_auth_cert; struct tor_x509_cert_t *my_auth_cert;
...@@ -31,8 +33,9 @@ struct tor_tls_context_t { ...@@ -31,8 +33,9 @@ struct tor_tls_context_t {
struct tor_tls_t { struct tor_tls_t {
uint32_t magic; uint32_t magic;
tor_tls_context_t *context; /** A link to the context object for this tls. */ tor_tls_context_t *context; /** A link to the context object for this tls. */
struct ssl_st *ssl; /**< An OpenSSL SSL object. */ tor_tls_impl_t *ssl; /**< An OpenSSL SSL object or NSS PRFileDesc. */
int socket; /**< The underlying file descriptor for this TLS connection. */ tor_socket_t socket; /**< The underlying file descriptor for this TLS
* connection. */
char *address; /**< An address to log when describing this connection. */ char *address; /**< An address to log when describing this connection. */
tor_tls_state_bitfield_t state : 3; /**< The current SSL state, tor_tls_state_bitfield_t state : 3; /**< The current SSL state,
* depending on which operations * depending on which operations
...@@ -45,11 +48,10 @@ struct tor_tls_t { ...@@ -45,11 +48,10 @@ struct tor_tls_t {
* one certificate). */ * 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; unsigned int got_renegotiate:1;
#ifdef ENABLE_OPENSSL
/** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
* called that function yet. */ * called that function yet. */
int8_t client_cipher_list_type; 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 size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
* time. */ * time. */
/** Last values retrieved from BIO_number_read()/write(); see /** Last values retrieved from BIO_number_read()/write(); see
...@@ -62,6 +64,11 @@ struct tor_tls_t { ...@@ -62,6 +64,11 @@ struct tor_tls_t {
void (*negotiated_callback)(tor_tls_t *tls, void *arg); void (*negotiated_callback)(tor_tls_t *tls, void *arg);
/** Argument to pass to negotiated_callback. */ /** Argument to pass to negotiated_callback. */
void *callback_arg; void *callback_arg;
#endif
#ifdef ENABLE_NSS
size_t n_read_since_last_check;
size_t n_written_since_last_check;
#endif
}; };
#endif #endif
...@@ -72,6 +72,7 @@ test_tortls_err_to_string(void *data) ...@@ -72,6 +72,7 @@ test_tortls_err_to_string(void *data)
(void)1; (void)1;
} }
#ifdef ENABLE_OPENSSL
static int static int
mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert) mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert)
{ {
...@@ -105,6 +106,7 @@ test_tortls_tor_tls_get_error(void *data) ...@@ -105,6 +106,7 @@ test_tortls_tor_tls_get_error(void *data)
crypto_pk_free(key2); crypto_pk_free(key2);
tor_tls_free(tls); tor_tls_free(tls);
} }
#endif
static void static void
test_tortls_x509_cert_get_id_digests(void *ignored) test_tortls_x509_cert_get_id_digests(void *ignored)
...@@ -165,6 +167,7 @@ test_tortls_get_my_certs(void *ignored) ...@@ -165,6 +167,7 @@ test_tortls_get_my_certs(void *ignored)
(void)1; (void)1;
} }
#ifdef ENABLE_OPENSSL
static void static void
test_tortls_get_forced_write_size(void *ignored) test_tortls_get_forced_write_size(void *ignored)
{ {
...@@ -203,23 +206,6 @@ test_tortls_used_v1_handshake(void *ignored) ...@@ -203,23 +206,6 @@ test_tortls_used_v1_handshake(void *ignored)
tor_free(tls); tor_free(tls);
} }
static void
test_tortls_get_num_server_handshakes(void *ignored)
{
(void)ignored;
int ret;
tor_tls_t *tls;
tls = tor_malloc_zero(sizeof(tor_tls_t));
tls->server_handshake_count = 3;
ret = tor_tls_get_num_server_handshakes(tls);
tt_int_op(ret, OP_EQ, 3);
done:
tor_free(tls);
}
static void static void
test_tortls_server_got_renegotiate(void *ignored) test_tortls_server_got_renegotiate(void *ignored)
{ {
...@@ -236,6 +222,7 @@ test_tortls_server_got_renegotiate(void *ignored) ...@@ -236,6 +222,7 @@ test_tortls_server_got_renegotiate(void *ignored)
done: done:
tor_free(tls);