summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2007-12-01 08:09:46 +0000
committerNick Mathewson <nickm@torproject.org>2007-12-01 08:09:46 +0000
commit1789f94668f8da029d18efb51bc3d0652488f706 (patch)
treeeda08e0e6866bd45859f43acb422efe595e3f918 /src/common
parentf8df8d791e4a58ab65d8903a0522b4cfa55cc163 (diff)
downloadtor-1789f94668f8da029d18efb51bc3d0652488f706.tar.gz
tor-1789f94668f8da029d18efb51bc3d0652488f706.zip
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
Diffstat (limited to 'src/common')
-rw-r--r--src/common/tortls.c111
-rw-r--r--src/common/tortls.h3
2 files changed, 110 insertions, 4 deletions
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 87e2f3aea6..8949c3b0d4 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -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>
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 63380f54eb..c8155de2f8 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -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);