r15087@tombo: nickm | 2007-11-30 22:32:26 -0500

Start getting freaky with openssl callbacks in tortls.c: detect client ciphers, and if the list doesn't look like the list current Tors use, present only a single cert do not ask for a client cert. Also, support for client-side renegotiation.  None of this is enabled unless you define V2_HANDSHAKE_SERVER.


svn:r12622
This commit is contained in:
Nick Mathewson
2007-12-01 08:09:46 +00:00
parent f8df8d791e
commit 1789f94668
6 changed files with 138 additions and 16 deletions
+14
View File
@@ -31,6 +31,20 @@ Things we'd like to do in 0.2.0.x:
D 118 if feasible and obvious
D Maintain a skew estimate and use ftime consistently.
- 105+TLS, if possible.
. TLS backend work
- New list of ciphers for clients
o Servers detect new ciphers, and only send ID cert when they
get an older cipher list, and only request client cert when
they get an older cipher list.
- Clients only send certificates when asked for them.
o Servers disable callback once negotiation is finished, so
that renegotiation happens according to the old rules.
- Clients initiate renegotiation immediately on completing
a v2 connection.
- Servers detect renegotiation, and if there is now a client
cert, they adust the client ID.
o Detect.
- Adjust.
o Add a separate handshake structure that handles version negotiation,
and stores netinfo data until authentication is done.
o Revise versions and netinfo to use separate structure; make
+107 -4
View File
@@ -34,6 +34,8 @@ const char tortls_c_id[] =
#include "./log.h"
#include <string.h>
// #define V2_HANDSHAKE_SERVER
/* Copied from or.h */
#define LEGAL_NICKNAME_CHARACTERS \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
@@ -59,10 +61,11 @@ struct tor_tls_t {
int socket; /**< The underlying file descriptor for this TLS connection. */
enum {
TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED
} state : 7; /**< The current SSL state, depending on which operations have
TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
} state : 6; /**< 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 hadCert:1; /**< Docdoc */
size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
* time. */
unsigned long last_write_count;
@@ -455,7 +458,7 @@ tor_tls_context_new(crypto_pk_env_t *identity, const char *nickname,
goto error;
X509_free(cert); /* We just added a reference to cert. */
cert=NULL;
#if 1
#if 0
if (idcert && !SSL_CTX_add_extra_chain_cert(result->ctx,idcert))
goto error;
#else
@@ -511,6 +514,55 @@ tor_tls_context_new(crypto_pk_env_t *identity, const char *nickname,
return -1;
}
#ifdef V2_HANDSHAKE_SERVER
static void
tor_tls_server_info_callback(const SSL *ssl, int type, int val)
{
int all_ciphers_in_v1_list = 1;
int i;
SSL_SESSION *session;
(void) val;
if (type != SSL_CB_ACCEPT_LOOP)
return;
if (ssl->state != SSL3_ST_SW_SRVR_HELLO_A)
return;
/* If we reached this point, we just got a client hello. See if there is
* a cipher list. */
if (!(session = SSL_get_session(ssl))) {
log_warn(LD_NET, "No session on TLS?");
return;
}
if (!session->ciphers) {
log_warn(LD_NET, "No ciphers on session");
return;
}
/* 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(session->ciphers); ++i) {
SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i);
const char *ciphername = SSL_CIPHER_get_name(cipher);
if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) &&
strcmp(ciphername, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) &&
strcmp(ciphername, "(NONE)")) {
/* XXXX should be ld_debug */
log_info(LD_NET, "Got a non-version-1 cipher called '%s'",ciphername);
all_ciphers_in_v1_list = 0;
break;
}
}
if (!all_ciphers_in_v1_list) {
/* Yes, we're casting away the const from ssl. This is very naughty of us.
* Let's hope openssl doesn't notice! */
/* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */
SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN);
/* Don't send a hello request. */
SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL);
}
}
#endif
/** Create a new TLS object from a file descriptor, and a flag to
* determine whether it is functioning as a server.
*/
@@ -544,6 +596,11 @@ tor_tls_new(int sock, int isServer)
result->state = TOR_TLS_ST_HANDSHAKE;
result->isServer = isServer;
result->wantwrite_n = 0;
#ifdef V2_HANDSHAKE_SERVER
if (isServer) {
SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
}
#endif
/* Not expected to get called. */
tls_log_errors(LOG_WARN, "generating TLS context");
return result;
@@ -585,8 +642,17 @@ tor_tls_read(tor_tls_t *tls, char *cp, size_t len)
tor_assert(tls->ssl);
tor_assert(tls->state == TOR_TLS_ST_OPEN);
r = SSL_read(tls->ssl, cp, len);
if (r > 0)
if (r > 0) {
#ifdef V2_HANDSHAKE_SERVER
if (!tls->hadCert && tls->ssl->session && tls->ssl->session->peer) {
tls->hadCert = 1;
/* New certificate! */
log_info(LD_NET, "Got a TLS renegotiation.");
/* XXXX020 call some kind of 'there was a renegotiation' callback. */
}
#endif
return r;
}
err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG);
if (err == _TOR_TLS_ZERORETURN) {
log_debug(LD_NET,"read returned r=%d; TLS is closed",r);
@@ -657,10 +723,45 @@ tor_tls_handshake(tor_tls_t *tls)
}
if (r == TOR_TLS_DONE) {
tls->state = TOR_TLS_ST_OPEN;
tls->hadCert = tor_tls_peer_has_cert(tls) ? 1 : 0;
if (tls->isServer) {
SSL_set_info_callback(tls->ssl, NULL);
SSL_set_verify(tls->ssl, SSL_VERIFY_NONE, always_accept_verify_cb);
tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN;
}
}
return r;
}
/** 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, CATCH_SYSCALL|CATCH_ZERO,
"renegotiating", LOG_WARN);
}
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, CATCH_SYSCALL|CATCH_ZERO,
"renegotiating handshake", LOG_WARN);
}
/** 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.
@@ -923,6 +1024,7 @@ tor_tls_verify_v1(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
return r;
}
#if 0
/** DOCDOC
*
* Returns 1 on "verification is done", 0 on "still need LINK_AUTH."
@@ -1025,6 +1127,7 @@ tor_tls_verify_certs_v2(int severity, tor_tls_t *tls,
return r;
}
#endif
/** Check whether the certificate set on the connection <b>tls</b> is
* expired or not-yet-valid, give or take <b>tolerance</b>
+3
View File
@@ -62,6 +62,7 @@ char *tor_tls_encode_my_certificate(tor_tls_t *tls, size_t *size_out,
crypto_pk_env_t *tor_tls_dup_private_key(tor_tls_t *tls);
int tor_tls_verify_v1(int severity, tor_tls_t *tls,
crypto_pk_env_t **identity);
#if 0
int tor_tls_verify_certs_v2(int severity, tor_tls_t *tls,
const char *cert_str, size_t cert_len,
const char *id_cert_str, size_t id_cert_len,
@@ -69,10 +70,12 @@ int tor_tls_verify_certs_v2(int severity, tor_tls_t *tls,
char *conn_cert_digest_out,
crypto_pk_env_t **id_key_out,
char *id_digest_out);
#endif
int tor_tls_check_lifetime(tor_tls_t *tls, int tolerance);
int tor_tls_read(tor_tls_t *tls, char *cp, size_t len);
int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n);
int tor_tls_handshake(tor_tls_t *tls);
int tor_tls_renegotiate(tor_tls_t *tls);
int tor_tls_shutdown(tor_tls_t *tls);
int tor_tls_get_pending_bytes(tor_tls_t *tls);
size_t tor_tls_get_forced_write_size(tor_tls_t *tls);
+7 -12
View File
@@ -38,8 +38,10 @@ static void command_process_destroy_cell(cell_t *cell, or_connection_t *conn);
static void command_process_versions_cell(var_cell_t *cell,
or_connection_t *conn);
static void command_process_netinfo_cell(cell_t *cell, or_connection_t *conn);
#if 0
static void command_process_cert_cell(var_cell_t *cell, or_connection_t *conn);
static void command_process_link_auth_cell(cell_t *cell,or_connection_t *conn);
#endif
#ifdef KEEP_TIMING_STATS
/** This is a wrapper function around the actual function that processes the
@@ -151,13 +153,6 @@ command_process_cell(cell_t *cell, or_connection_t *conn)
++stats_n_netinfo_cells_processed;
PROCESS_CELL(netinfo, cell, conn);
break;
case CELL_CERT:
tor_fragile_assert();
break;
case CELL_LINK_AUTH:
++stats_n_link_auth_cells_processed;
PROCESS_CELL(link_auth, cell, conn);
break;
default:
log_fn(LOG_INFO, LD_PROTOCOL,
"Cell of unknown type (%d) received. Dropping.", cell->command);
@@ -201,10 +196,6 @@ command_process_var_cell(var_cell_t *cell, or_connection_t *conn)
++stats_n_versions_cells_processed;
PROCESS_CELL(versions, cell, conn);
break;
case CELL_CERT:
++stats_n_cert_cells_processed;
PROCESS_CELL(cert, cell, conn);
break;
default:
log_warn(LD_BUG,
"Variable-length cell of unknown type (%d) received.",
@@ -484,6 +475,8 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn)
conn->link_proto = highest_supported_version;
conn->handshake_state->received_versions = 1;
#if 0
/*XXXX020 not right; references dead functions */
if (highest_supported_version >= 2) {
if (connection_or_send_netinfo(conn) < 0 ||
connection_or_send_cert(conn) < 0) {
@@ -495,6 +488,7 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn)
} else {
/* XXXX020 finish v1 verification. */
}
#endif
}
/** Process a 'netinfo' cell. DOCDOC say more. */
@@ -612,6 +606,7 @@ connection_or_act_on_netinfo(or_connection_t *conn)
return 0;
}
#if 0
/*DOCDOC*/
static void
command_process_cert_cell(var_cell_t *cell, or_connection_t *conn)
@@ -780,4 +775,4 @@ command_process_link_auth_cell(cell_t *cell, or_connection_t *conn)
tor_free(checked);
connection_mark_for_close(TO_CONN(conn));
}
#endif
+2
View File
@@ -1057,6 +1057,7 @@ connection_or_send_netinfo(or_connection_t *conn)
return 0;
}
#if 0
#define LINK_AUTH_STRING "Tor initiator certificate verification"
/** DOCDOC */
int
@@ -1166,4 +1167,5 @@ connection_or_send_link_auth(or_connection_t *conn)
return 0;
}
#endif
+5
View File
@@ -656,12 +656,17 @@ typedef enum {
#define CELL_CREATED_FAST 6
#define CELL_VERSIONS 7
#define CELL_NETINFO 8
#if 0
#define CELL_CERT 9
#define CELL_LINK_AUTH 10
#endif
#define CELL_RELAY_EARLY 11 /*DOCDOC*/
#if 0
#define CELL_COMMAND_IS_VAR_LENGTH(x) \
((x) == CELL_CERT || (x) == CELL_VERSIONS)
#endif
#define CELL_COMMAND_IS_VAR_LENGTH(x) ((x) == CELL_VERSIONS)
/** How long to test reachability before complaining to the user. */
#define TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT (20*60)