From dcf69a9e12d013563575e50b7d6f547832f92f66 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 13 Sep 2011 14:32:51 -0400 Subject: New function to get all digests of a public key --- src/common/crypto.c | 26 ++++++++++++++++++++++++++ src/common/crypto.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/common/crypto.c b/src/common/crypto.c index 9ad7575a7..edb0d42ac 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1249,6 +1249,32 @@ crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out) return 0; } +/** Compute all digests of the DER encoding of pk, and store them + * in digests_out. Return 0 on success, -1 on failure. */ +int +crypto_pk_get_all_digests(crypto_pk_env_t *pk, digests_t *digests_out) +{ + unsigned char *buf, *bufp; + int len; + + len = i2d_RSAPublicKey(pk->key, NULL); + if (len < 0) + return -1; + buf = bufp = tor_malloc(len+1); + len = i2d_RSAPublicKey(pk->key, &bufp); + if (len < 0) { + crypto_log_errors(LOG_WARN,"encoding public key"); + tor_free(buf); + return -1; + } + if (crypto_digest_all(digests_out, (char*)buf, len) < 0) { + tor_free(buf); + return -1; + } + tor_free(buf); + return 0; +} + /** Copy in to the outlen-byte buffer out, adding spaces * every four spaces. */ /* static */ void diff --git a/src/common/crypto.h b/src/common/crypto.h index 9b4eee622..d50d9f906 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -150,6 +150,7 @@ int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, char *to, int crypto_pk_asn1_encode(crypto_pk_env_t *pk, char *dest, size_t dest_len); crypto_pk_env_t *crypto_pk_asn1_decode(const char *str, size_t len); int crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out); +int crypto_pk_get_all_digests(crypto_pk_env_t *pk, digests_t *digests_out); int crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out,int add_space); int crypto_pk_check_fingerprint_syntax(const char *s); -- cgit v1.2.3 From c0bbcf138fa4e7d8e1974ed91718025d0dd5cc7a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 13 Sep 2011 11:37:15 -0400 Subject: Turn X509 certificates into a first-class type and add some functions --- src/common/tortls.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/common/tortls.h | 11 ++++ 2 files changed, 155 insertions(+), 6 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 1bb9c74ef..b7119675e 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -97,14 +97,23 @@ 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; + 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; + tor_cert_t *my_cert; + tor_cert_t *my_id_cert; crypto_pk_env_t *key; } tor_tls_context_t; @@ -670,6 +679,115 @@ static const int N_CLIENT_CIPHERS = SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) #endif +/** Free all storage held in cert */ +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 = i2d_X509(x509_cert, NULL), length2; + unsigned char *cp; + + 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_env_t *pk = _crypto_new_pk_env_rsa(rsa); + crypto_pk_get_all_digests(pk, &cert->pkey_digests); + crypto_free_pk_env(pk); + EVP_PKEY_free(pkey); + } + + return cert; +} + +/** Read a DER-encoded X509 cert, of length exactly certificate_len, + * from a certificate. 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 < 0x00908000l + /* 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) { + X509_free(x509); + 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 *encoded_out and *size_out/b> to cert'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; +} + /** Remove a reference to ctx, and free it if it has no more * references. */ static void @@ -678,13 +796,33 @@ 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); + tor_cert_free(ctx->my_cert); + tor_cert_free(ctx->my_id_cert); crypto_free_pk_env(ctx->key); tor_free(ctx); } } +/** Set *link_cert_out and *id_cert_out to the link certificate + * and ID certificate that we're currently using for our V3 in-protocol + * handshake's certificate chain. If server 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 = ctx->my_cert; + if (id_cert_out) + *id_cert_out = ctx->my_id_cert; + return 0; +} + /** Increase the reference count of ctx. */ static void tor_tls_context_incref(tor_tls_context_t *ctx) @@ -813,8 +951,8 @@ 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; - result->my_cert = X509_dup(cert); - result->my_id_cert = X509_dup(idcert); + result->my_cert = tor_cert_new(X509_dup(cert)); + result->my_id_cert = tor_cert_new(X509_dup(idcert)); result->key = crypto_pk_dup_key(rsa); #ifdef EVERYONE_HAS_AES diff --git a/src/common/tortls.h b/src/common/tortls.h index 9b8108b42..c55da4afd 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -17,6 +17,9 @@ /* Opaque structure to hold a TLS connection. */ typedef struct tor_tls_t tor_tls_t; +/* Opaque structure to hold an X509 certificate. */ +typedef struct tor_cert_t tor_cert_t; + /* Possible return values for most tor_tls_* functions. */ #define _MIN_TOR_TLS_ERROR_VAL -9 #define TOR_TLS_ERROR_MISC -9 @@ -104,5 +107,13 @@ struct bufferevent *tor_tls_init_bufferevent(tor_tls_t *tls, int filter); #endif +void tor_cert_free(tor_cert_t *cert); +tor_cert_t *tor_cert_decode(const uint8_t *certificate, size_t certificate_len); +void tor_cert_get_der(const tor_cert_t *cert, + const uint8_t **encoded_out, size_t *size_out); +int tor_tls_get_my_certs(int server, + const tor_cert_t **link_cert_out, + const tor_cert_t **id_cert_out); + #endif -- cgit v1.2.3 From fdbb9cdf746bbf0c39c34188baa8872471183ff7 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 13 Sep 2011 11:38:13 -0400 Subject: Add a sha256 hmac function, with tests --- src/common/crypto.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++ src/common/crypto.h | 3 +++ src/test/test_crypto.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src/common/crypto.c b/src/common/crypto.c index edb0d42ac..c39719972 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1740,6 +1740,74 @@ crypto_hmac_sha1(char *hmac_out, (unsigned char*)hmac_out, NULL); } +/** Compute the HMAC-SHA-256 of the msg_len bytes in msg, using + * the key of length key_len. Store the DIGEST_LEN-byte result + * in hmac_out. + */ +void +crypto_hmac_sha256(char *hmac_out, + const char *key, size_t key_len, + const char *msg, size_t msg_len) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x00908000l) + /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */ + tor_assert(key_len < INT_MAX); + tor_assert(msg_len < INT_MAX); + HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len, + (unsigned char*)hmac_out, NULL); +#else + /* OpenSSL doesn't have an EVP implementation for SHA256. We'll need + to do HMAC on our own. + + HMAC isn't so hard: To compute HMAC(key, msg): + 1. If len(key) > blocksize, key = H(key). + 2. If len(key) < blocksize, right-pad key up to blocksize with 0 bytes. + 3. let ipad = key xor 0x363636363636....36 + let opad = key xor 0x5c5c5c5c5c5c....5c + The result is H(opad | H( ipad | msg ) ) + */ +#define BLOCKSIZE 64 +#define DIGESTSIZE 32 + uint8_t k[BLOCKSIZE]; + uint8_t pad[BLOCKSIZE]; + uint8_t d[DIGESTSIZE]; + int i; + SHA256_CTX st; + + tor_assert(key_len < INT_MAX); + tor_assert(msg_len < INT_MAX); + + if (key_len <= BLOCKSIZE) { + memset(k, 0, sizeof(k)); + memcpy(k, key, key_len); /* not time invariant in key_len */ + } else { + SHA256((const uint8_t *)key, key_len, k); + memset(k+DIGESTSIZE, 0, sizeof(k)-DIGESTSIZE); + } + for (i = 0; i < BLOCKSIZE; ++i) + pad[i] = k[i] ^ 0x36; + SHA256_Init(&st); + SHA256_Update(&st, pad, BLOCKSIZE); + SHA256_Update(&st, (uint8_t*)msg, msg_len); + SHA256_Final(d, &st); + + for (i = 0; i < BLOCKSIZE; ++i) + pad[i] = k[i] ^ 0x5c; + SHA256_Init(&st); + SHA256_Update(&st, pad, BLOCKSIZE); + SHA256_Update(&st, d, DIGESTSIZE); + SHA256_Final((uint8_t*)hmac_out, &st); + + /* Now clear everything. */ + memset(k, 0, sizeof(k)); + memset(pad, 0, sizeof(pad)); + memset(d, 0, sizeof(d)); + memset(&st, 0, sizeof(st)); +#undef BLOCKSIZE +#undef DIGESTSIZE +#endif +} + /* DH */ /** Shared P parameter for our circuit-crypto DH key exchanges. */ diff --git a/src/common/crypto.h b/src/common/crypto.h index d50d9f906..80c10296a 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -196,6 +196,9 @@ void crypto_digest_assign(crypto_digest_env_t *into, void crypto_hmac_sha1(char *hmac_out, const char *key, size_t key_len, const char *msg, size_t msg_len); +void crypto_hmac_sha256(char *hmac_out, + const char *key, size_t key_len, + const char *msg, size_t msg_len); /* Key negotiation */ #define DH_TYPE_CIRCUIT 1 diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index dca0d3a28..1b338a29a 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -231,7 +231,7 @@ test_crypto_sha(void) { crypto_digest_env_t *d1 = NULL, *d2 = NULL; int i; - char key[80]; + char key[160]; char digest[32]; char data[50]; char d_out1[DIGEST_LEN], d_out2[DIGEST256_LEN]; @@ -276,6 +276,75 @@ test_crypto_sha(void) test_streq(hex_str(digest, 20), "AA4AE5E15272D00E95705637CE8A3B55ED402112"); + /* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */ + + /* Case empty (wikipedia) */ + crypto_hmac_sha256(digest, "", 0, "", 0); + test_streq(hex_str(digest, 32), + "B613679A0814D9EC772F95D778C35FC5FF1697C493715653C6C712144292C5AD"); + + /* Case quick-brown (wikipedia) */ + crypto_hmac_sha256(digest, "key", 3, + "The quick brown fox jumps over the lazy dog", 43); + test_streq(hex_str(digest, 32), + "F7BC83F430538424B13298E6AA6FB143EF4D59A14946175997479DBC2D1A3CD8"); + + /* "Test Case 1" from RFC 4231 */ + memset(key, 0x0b, 20); + crypto_hmac_sha256(digest, key, 20, "Hi There", 8); + test_memeq_hex(digest, + "b0344c61d8db38535ca8afceaf0bf12b" + "881dc200c9833da726e9376c2e32cff7"); + + /* "Test Case 2" from RFC 4231 */ + memset(key, 0x0b, 20); + crypto_hmac_sha256(digest, "Jefe", 4, "what do ya want for nothing?", 28); + test_memeq_hex(digest, + "5bdcc146bf60754e6a042426089575c7" + "5a003f089d2739839dec58b964ec3843"); + + /* "Test case 3" from RFC 4231 */ + memset(key, 0xaa, 20); + memset(data, 0xdd, 50); + crypto_hmac_sha256(digest, key, 20, data, 50); + test_memeq_hex(digest, + "773ea91e36800e46854db8ebd09181a7" + "2959098b3ef8c122d9635514ced565fe"); + + /* "Test case 4" from RFC 4231 */ + base16_decode(key, 25, + "0102030405060708090a0b0c0d0e0f10111213141516171819", 50); + memset(data, 0xcd, 50); + crypto_hmac_sha256(digest, key, 25, data, 50); + test_memeq_hex(digest, + "82558a389a443c0ea4cc819899f2083a" + "85f0faa3e578f8077a2e3ff46729665b"); + + /* "Test case 5" from RFC 4231 */ + memset(key, 0x0c, 20); + crypto_hmac_sha256(digest, key, 20, "Test With Truncation", 20); + test_memeq_hex(digest, + "a3b6167473100ee06e0c796c2955552b"); + + /* "Test case 6" from RFC 4231 */ + memset(key, 0xaa, 131); + crypto_hmac_sha256(digest, key, 131, + "Test Using Larger Than Block-Size Key - Hash Key First", + 54); + test_memeq_hex(digest, + "60e431591ee0b67f0d8a26aacbf5b77f" + "8e0bc6213728c5140546040f0ee37f54"); + + /* "Test case 7" from RFC 4231 */ + memset(key, 0xaa, 131); + crypto_hmac_sha256(digest, key, 131, + "This is a test using a larger than block-size key and a " + "larger than block-size data. The key needs to be hashed " + "before being used by the HMAC algorithm.", 152); + test_memeq_hex(digest, + "9b09ffa71b942fcb27635fbcd5b0e944" + "bfdc63644f0713938a7f51535c3a35e2"); + /* Incremental digest code. */ d1 = crypto_new_digest_env(); test_assert(d1); -- cgit v1.2.3 From 1b0645acba905c37759194c222aacbbe40771223 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 13 Sep 2011 10:03:09 -0400 Subject: Cell types and states for new OR handshake Also, define all commands > 128 as variable-length when using v3 or later link protocol. Running into a var cell with an unrecognized type is no longer a bug. --- src/or/buffers.c | 38 ++++++++++++++++++++++++++++---------- src/or/command.c | 11 +++++------ src/or/connection.c | 9 ++++++--- src/or/connection_or.c | 6 +++--- src/or/or.h | 32 +++++++++++++++++++------------- 5 files changed, 61 insertions(+), 35 deletions(-) diff --git a/src/or/buffers.c b/src/or/buffers.c index 1025cedad..c589fbaef 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -1005,6 +1005,32 @@ fetch_from_buf(char *string, size_t string_len, buf_t *buf) return (int)buf->datalen; } +/** True iff the cell command command is one that implies a variable-length + * cell in Tor link protocol linkproto. */ +static inline int +cell_command_is_var_length(uint8_t command, int linkproto) +{ + /* If linkproto is v2 (2), CELL_VERSIONS is the only variable-length cells work as + * implemented here. If it's 1, there are no variable-length cells. Tor + * does not support other versions right now, and so can't negotiate them. + */ + switch (linkproto) { + case 1: + /* Link protocol version 1 has no variable-length cells. */ + return 0; + case 2: + /* In link protocol version 2, VERSIONS is the only variable-length cell */ + return command == CELL_VERSIONS; + case 0: + case 3: + default: + /* In link protocol version 3 and later, and in version "unknown", + * commands 128 and higher indicate variable-length. VERSIONS is + * grandfathered in. */ + return command == CELL_VERSIONS || command >= 128; + } +} + /** Check buf for a variable-length cell according to the rules of link * protocol version linkproto. If one is found, pull it off the buffer * and assign a newly allocated var_cell_t to *out, and return 1. @@ -1019,12 +1045,6 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto) var_cell_t *result; uint8_t command; uint16_t length; - /* If linkproto is unknown (0) or v2 (2), variable-length cells work as - * implemented here. If it's 1, there are no variable-length cells. Tor - * does not support other versions right now, and so can't negotiate them. - */ - if (linkproto == 1) - return 0; check(); *out = NULL; if (buf->datalen < VAR_CELL_HEADER_SIZE) @@ -1032,7 +1052,7 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto) peek_from_buf(hdr, sizeof(hdr), buf); command = get_uint8(hdr+2); - if (!(CELL_COMMAND_IS_VAR_LENGTH(command))) + if (!(cell_command_is_var_length(command, linkproto))) return 0; length = ntohs(get_uint16(hdr+3)); @@ -1101,8 +1121,6 @@ fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out, uint16_t cell_length; var_cell_t *cell; int result = 0; - if (linkproto == 1) - return 0; *out = NULL; buf_len = evbuffer_get_length(buf); @@ -1113,7 +1131,7 @@ fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out, tor_assert(n >= VAR_CELL_HEADER_SIZE); command = get_uint8(hdr+2); - if (!(CELL_COMMAND_IS_VAR_LENGTH(command))) { + if (!(cell_command_is_var_length(command, linkproto))) { goto done; } diff --git a/src/or/command.c b/src/or/command.c index d24373eec..72d8cd7cf 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -93,7 +93,7 @@ command_time_process_cell(cell_t *cell, or_connection_t *conn, int *time, void command_process_cell(cell_t *cell, or_connection_t *conn) { - int handshaking = (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING); + int handshaking = (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V2); #ifdef KEEP_TIMING_STATS /* how many of each cell have we seen so far this second? needs better * name. */ @@ -207,7 +207,7 @@ command_process_var_cell(var_cell_t *cell, or_connection_t *conn) #endif /* reject all when not handshaking. */ - if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING) + if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2) return; switch (cell->command) { @@ -216,10 +216,9 @@ command_process_var_cell(var_cell_t *cell, or_connection_t *conn) PROCESS_CELL(versions, cell, conn); break; default: - log_warn(LD_BUG, + log_fn(LOG_INFO, LD_PROTOCOL, "Variable-length cell of unknown type (%d) received.", cell->command); - tor_fragile_assert(); break; } } @@ -506,7 +505,7 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn) int highest_supported_version = 0; const uint8_t *cp, *end; if (conn->link_proto != 0 || - conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING || + conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2 || (conn->handshake_state && conn->handshake_state->received_versions)) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received a VERSIONS cell on a connection with its version " @@ -572,7 +571,7 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) conn->link_proto == 0 ? "non-versioned" : "a v1"); return; } - if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING) { + if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received a NETINFO cell on non-handshaking connection; dropping."); return; diff --git a/src/or/connection.c b/src/or/connection.c index 45a12715a..2bd2d07e9 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -140,10 +140,13 @@ conn_state_to_string(int type, int state) case OR_CONN_STATE_PROXY_HANDSHAKING: return "handshaking (proxy)"; case OR_CONN_STATE_TLS_HANDSHAKING: return "handshaking (TLS)"; case OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING: - return "renegotiating (TLS)"; + return "renegotiating (TLS, v2 handshake)"; case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: - return "waiting for renegotiation (TLS)"; - case OR_CONN_STATE_OR_HANDSHAKING: return "handshaking (Tor)"; + return "waiting for renegotiation or V3 handshake"; + case OR_CONN_STATE_OR_HANDSHAKING_V2: + return "handshaking (Tor, v2 handshake)"; + case OR_CONN_STATE_OR_HANDSHAKING_V3: + return "handshaking (Tor, v3 handshake)"; case OR_CONN_STATE_OPEN: return "open"; } break; diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 29f0f8de7..b146efb41 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -387,7 +387,7 @@ connection_or_process_inbuf(or_connection_t *conn) /* fall through. */ #endif case OR_CONN_STATE_OPEN: - case OR_CONN_STATE_OR_HANDSHAKING: + case OR_CONN_STATE_OR_HANDSHAKING_V2: return connection_or_process_cells_from_inbuf(conn); default: return 0; /* don't do anything */ @@ -439,7 +439,7 @@ connection_or_finished_flushing(or_connection_t *conn) switch (conn->_base.state) { case OR_CONN_STATE_PROXY_HANDSHAKING: case OR_CONN_STATE_OPEN: - case OR_CONN_STATE_OR_HANDSHAKING: + case OR_CONN_STATE_OR_HANDSHAKING_V2: break; default: log_err(LD_BUG,"Called in unexpected state %d.", conn->_base.state); @@ -1476,7 +1476,7 @@ connection_tls_finish_handshake(or_connection_t *conn) tor_tls_block_renegotiation(conn->tls); return connection_or_set_state_open(conn); } else { - conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING; + conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V2; if (connection_init_or_handshake_state(conn, started_here) < 0) return -1; if (!started_here) { diff --git a/src/or/or.h b/src/or/or.h index d6eaeb6f1..ef4ddbdb8 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -283,22 +283,27 @@ typedef enum { #define OR_CONN_STATE_CONNECTING 1 /** State for a connection to an OR: waiting for proxy handshake to complete */ #define OR_CONN_STATE_PROXY_HANDSHAKING 2 -/** State for a connection to an OR or client: SSL is handshaking, not done +/** State for an OR connection client: SSL is handshaking, not done * yet. */ #define OR_CONN_STATE_TLS_HANDSHAKING 3 /** State for a connection to an OR: We're doing a second SSL handshake for - * renegotiation purposes. */ + * renegotiation purposes. (V2 handshake only.) */ #define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 4 /** State for a connection at an OR: We're waiting for the client to - * renegotiate. */ + * renegotiate (to indicate a v2 handshake) or send a versions cell (to + * indicate a v3 handshake) */ #define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 5 -/** State for a connection to an OR: We're done with our SSL handshake, but we - * haven't yet negotiated link protocol versions and sent a netinfo cell. - */ -#define OR_CONN_STATE_OR_HANDSHAKING 6 -/** State for a connection to an OR: Ready to send/receive cells. */ -#define OR_CONN_STATE_OPEN 7 -#define _OR_CONN_STATE_MAX 7 +/** State for an OR connection: We're done with our SSL handshake, we've done + * renegotiation, but we haven't yet negotiated link protocol versions and + * sent a netinfo cell. */ +#define OR_CONN_STATE_OR_HANDSHAKING_V2 6 +/** State for an OR connection: We're done with our SSL handshake, but we + * haven't yet negotiated link protocol versions, done a V3 handshake, and + * sent a netinfo cell. */ +#define OR_CONN_STATE_OR_HANDSHAKING_V3 7 +/** State for an OR connection:: Ready to send/receive cells. */ +#define OR_CONN_STATE_OPEN 8 +#define _OR_CONN_STATE_MAX 8 #define _EXIT_CONN_STATE_MIN 1 /** State for an exit connection: waiting for response from DNS farm. */ @@ -820,9 +825,10 @@ typedef enum { #define CELL_NETINFO 8 #define CELL_RELAY_EARLY 9 -/** True iff the cell command x is one that implies a variable-length - * cell. */ -#define CELL_COMMAND_IS_VAR_LENGTH(x) ((x) == CELL_VERSIONS) +#define CELL_VPADDING 128 +#define CELL_CERT 129 +#define CELL_AUTH_CHALLENGE 130 +#define CELL_AUTHENTICATE 131 /** How long to test reachability before complaining to the user. */ #define TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT (20*60) -- cgit v1.2.3 From df78daa5da0fd27fdd2fd8ad13aa12e74696a4ef Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 13 Sep 2011 11:38:38 -0400 Subject: Functions to send cert and auth_challenge cells. --- src/or/connection_or.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/or/connection_or.h | 3 ++ src/or/or.h | 15 +++++++++- 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index b146efb41..dcb838b27 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1761,3 +1761,84 @@ connection_or_send_netinfo(or_connection_t *conn) return 0; } +/** DOCDOC */ +#define OR_CERT_TYPE_TLS_LINK 1 +#define OR_CERT_TYPE_ID_1024 2 + +/** Send a CERT cell on the connection conn. Return 0 on success, -1 + * on failure. */ +int +connection_or_send_cert_cell(or_connection_t *conn) +{ + const tor_cert_t *link_cert = NULL, *id_cert = NULL; + const uint8_t *link_encoded = NULL, *id_encoded = NULL; + size_t link_len, id_len; + var_cell_t *cell; + size_t cell_len; + int pos; + + tor_assert(conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3); + + if (! conn->handshake_state) + return -1; + if (tor_tls_get_my_certs(! conn->handshake_state->started_here, + &link_cert, &id_cert) < 0) + return -1; + tor_cert_get_der(link_cert, &link_encoded, &link_len); + tor_cert_get_der(id_cert, &id_encoded, &id_len); + + cell_len = 1 /* 1 octet: num certs in cell */ + + 2 * ( 1 + 2 ) /* For each cert: 1 octet for type, 2 for length */ + + link_len + id_len; + cell = var_cell_new(cell_len); + cell->command = CELL_CERT; + cell->payload[0] = 2; + pos = 1; + + cell->payload[pos] = OR_CERT_TYPE_TLS_LINK; /* Link cert */ + set_uint16(&cell->payload[pos+1], htons(link_len)); + memcpy(&cell->payload[pos+3], link_encoded, link_len); + pos += 3 + link_len; + + cell->payload[pos] = OR_CERT_TYPE_ID_1024; /* ID cert */ + set_uint16(&cell->payload[pos+1], htons(id_len)); + memcpy(&cell->payload[pos+3], id_encoded, id_len); + pos += 3 + id_len; + + tor_assert(pos == (int)cell_len); /* Otherwise we just smashed the heap */ + + connection_or_write_var_cell_to_buf(cell, conn); + var_cell_free(cell); + + return 0; +} + +/** Send an AUTH_CHALLENGE cell on the connection conn. Return 0 + * on success, -1 on failure. */ +int +connection_or_send_auth_challenge_cell(or_connection_t *conn) +{ + var_cell_t *cell; + uint8_t *cp; + uint8_t challenge[OR_AUTH_CHALLENGE_LEN]; + tor_assert(conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3); + + if (! conn->handshake_state) + return -1; + + if (crypto_rand((char*)challenge, OR_AUTH_CHALLENGE_LEN) < 0) + return -1; + cell = var_cell_new(OR_AUTH_CHALLENGE_LEN + 4); + cell->command = CELL_AUTH_CHALLENGE; + memcpy(cell->payload, challenge, OR_AUTH_CHALLENGE_LEN); + cp = cell->payload + OR_AUTH_CHALLENGE_LEN; + set_uint16(cp, htons(1)); /* We recognize one authentication type. */ + set_uint16(cp+2, htons(AUTHTYPE_RSA_SHA256_TLSSECRET)); + + connection_or_write_var_cell_to_buf(cell, conn); + var_cell_free(cell); + memset(challenge, 0, sizeof(challenge)); + + return 0; +} + diff --git a/src/or/connection_or.h b/src/or/connection_or.h index 072edbd8c..6e50e29e4 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -50,6 +50,9 @@ void connection_or_write_var_cell_to_buf(const var_cell_t *cell, int connection_or_send_destroy(circid_t circ_id, or_connection_t *conn, int reason); int connection_or_send_netinfo(or_connection_t *conn); +int connection_or_send_cert_cell(or_connection_t *conn); +int connection_or_send_auth_challenge_cell(or_connection_t *conn); + int is_or_protocol_version_known(uint16_t version); void cell_pack(packed_cell_t *dest, const cell_t *src); diff --git a/src/or/or.h b/src/or/or.h index ef4ddbdb8..ebf9ab569 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1084,7 +1084,10 @@ typedef struct listener_connection_t { } listener_connection_t; -/** Stores flags and information related to the portion of a v2 Tor OR +/** Minimum length of the random part of an AUTH_CHALLENGE cell. */ +#define OR_AUTH_CHALLENGE_LEN 32 + +/** Stores flags and information related to the portion of a v2/v3 Tor OR * connection handshake that happens after the TLS handshake is finished. */ typedef struct or_handshake_state_t { @@ -1095,6 +1098,16 @@ typedef struct or_handshake_state_t { unsigned int started_here : 1; /** True iff we have received and processed a VERSIONS cell. */ unsigned int received_versions : 1; + + /** Digests of the cells that we have sent or received as part of a V3 + * handshake. Used for making and checking AUTHENTICATE cells. + * + * @{ + */ + crypto_digest_env_t *digest_sent; + crypto_digest_env_t *digest_received; + /** @} */ + } or_handshake_state_t; /** Subtype of connection_t for an "OR connection" -- that is, one that speaks -- cgit v1.2.3 From c39688de6c5d4bf19739ecffb2e98aa560a4630a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 13 Sep 2011 13:46:21 -0400 Subject: Function to extract the TLSSECRETS field for v3 handshakes --- src/common/tortls.c | 30 ++++++++++++++++++++++++++++++ src/common/tortls.h | 1 + 2 files changed, 31 insertions(+) diff --git a/src/common/tortls.c b/src/common/tortls.c index b7119675e..2b12eea8d 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -1985,6 +1985,36 @@ tor_tls_server_got_renegotiate(tor_tls_t *tls) return tls->got_renegotiate; } +/** Set the DIGEST256_LEN buffer at secrets_out to the value used in + * the v3 handshake to prove that the client knows the TLS secrets for the + * connection tls. 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 tls. * Set *rbuf_capacity to the amount of storage allocated for the read * buffer and *rbuf_bytes to the amount actually used. diff --git a/src/common/tortls.h b/src/common/tortls.h index c55da4afd..a6aed2985 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -90,6 +90,7 @@ void tor_tls_get_buffer_sizes(tor_tls_t *tls, int tor_tls_used_v1_handshake(tor_tls_t *tls); int tor_tls_get_num_server_handshakes(tor_tls_t *tls); int tor_tls_server_got_renegotiate(tor_tls_t *tls); +int tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out); /* Log and abort if there are unhandled TLS errors in OpenSSL's error stack. */ -- cgit v1.2.3 From f4c1fa2a04c310c4e0274129bb2fff2aacb59248 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 14 Sep 2011 13:04:48 -0400 Subject: More functions to manipulate certs received in cells --- src/common/tortls.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++------ src/common/tortls.h | 3 ++ 2 files changed, 108 insertions(+), 13 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 2b12eea8d..0ba47baa4 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -207,6 +207,7 @@ static int tor_tls_context_init_one(tor_tls_context_t **ppcontext, unsigned int key_lifetime); static tor_tls_context_t *tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime); +static int check_cert_lifetime_internal(const X509 *cert, int tolerance); /** Global TLS contexts. We keep them here because nobody else needs * to touch them. */ @@ -823,6 +824,84 @@ tor_tls_get_my_certs(int server, return 0; } +/** Return true iff a and 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 tls has authenticated to us, and + * the key certified in cert 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 wither cert is well-formed, currently live, and correctly + * signed by the public key in signing_cert. Return 0 if the cert is + * good, and -1 if it's bad or we couldn't check it. */ +int +tor_tls_cert_is_valid(const tor_cert_t *cert, + const tor_cert_t *signing_cert) +{ + EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert); + int r; + 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. */ + /*XXXX tolerance might be iffy here */ + if (check_cert_lifetime_internal(cert->cert, 60*60) < 0) + return 0; + + /* XXXX compare DNs or anything? */ + + return -1; +} + /** Increase the reference count of ctx. */ static void tor_tls_context_incref(tor_tls_context_t *ctx) @@ -1709,7 +1788,7 @@ tor_tls_peer_has_cert(tor_tls_t *tls) /** Warn that a certificate lifetime extends through a certain range. */ static void -log_cert_lifetime(X509 *cert, const char *problem) +log_cert_lifetime(const X509 *cert, const char *problem) { BIO *bio = NULL; BUF_MEM *buf; @@ -1856,25 +1935,14 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key) int tor_tls_check_lifetime(tor_tls_t *tls, int 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(cert, 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: @@ -1886,6 +1954,30 @@ tor_tls_check_lifetime(tor_tls_t *tls, int tolerance) return r; } +/** Helper: check whether cert is currently live, give or take + * tolerance seconds. If it is live, return 0. If it is not live, + * log a message and return -1. */ +static int +check_cert_lifetime_internal(const X509 *cert, int tolerance) +{ + time_t now, t; + + now = time(NULL); + + t = now + tolerance; + if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) { + log_cert_lifetime(cert, "not yet valid"); + return -1; + } + t = now - tolerance; + if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) { + log_cert_lifetime(cert, "already expired"); + return -1; + } + + return 0; +} + /** Return the number of bytes available for reading from tls. */ int diff --git a/src/common/tortls.h b/src/common/tortls.h index a6aed2985..36309afd4 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -115,6 +115,9 @@ void tor_cert_get_der(const tor_cert_t *cert, int tor_tls_get_my_certs(int server, const tor_cert_t **link_cert_out, const tor_cert_t **id_cert_out); +int tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert); +int tor_tls_cert_is_valid(const tor_cert_t *cert, + const tor_cert_t *signing_cert); #endif -- cgit v1.2.3 From 8c9fdecfe9e6c93e678a5917e302aa18ada8a3b6 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 14 Sep 2011 14:43:44 -0400 Subject: Function to get digests of the certs and their keys --- src/common/tortls.c | 14 ++++++++++++++ src/common/tortls.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/src/common/tortls.c b/src/common/tortls.c index 0ba47baa4..143522398 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -789,6 +789,20 @@ tor_cert_get_der(const tor_cert_t *cert, *size_out = cert->encoded_len; } +/** Return a set of digests for the public key in cert. */ +const digests_t * +tor_cert_get_id_digests(const tor_cert_t *cert) +{ + return &cert->pkey_digests; +} + +/** Return a set of digests for the public key in cert. */ +const digests_t * +tor_cert_get_cert_digests(const tor_cert_t *cert) +{ + return &cert->cert_digests; +} + /** Remove a reference to ctx, and free it if it has no more * references. */ static void diff --git a/src/common/tortls.h b/src/common/tortls.h index 36309afd4..40eb8306e 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -112,6 +112,8 @@ void tor_cert_free(tor_cert_t *cert); tor_cert_t *tor_cert_decode(const uint8_t *certificate, size_t certificate_len); void tor_cert_get_der(const tor_cert_t *cert, const uint8_t **encoded_out, size_t *size_out); +const digests_t *tor_cert_get_id_digests(const tor_cert_t *cert); +const digests_t *tor_cert_get_cert_digests(const tor_cert_t *cert); int tor_tls_get_my_certs(int server, const tor_cert_t **link_cert_out, const tor_cert_t **id_cert_out); -- cgit v1.2.3 From 92602345e001d8e66038d5d98cbb21eea5ef40c9 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 16 Sep 2011 17:48:20 -0400 Subject: Function to detect certificate types that signal v3 certificates --- src/common/tortls.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/common/tortls.h | 1 + 2 files changed, 72 insertions(+) diff --git a/src/common/tortls.c b/src/common/tortls.c index 143522398..332d78465 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -2075,6 +2075,77 @@ tor_tls_used_v1_handshake(tor_tls_t *tls) return 1; } +/** Return true iff name 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) +{ + 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; +} + +/** Return true iff the peer certificate we're received on tls + * 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; + X509_NAME *issuer_name, *subject_name; + + if (!cert) { + log_warn(LD_BUG, "Called on a connection with no peer certificate"); + return 0; + } + + subject_name = X509_get_subject_name(cert); + issuer_name = X509_get_issuer_name(cert); + + if (X509_name_cmp(subject_name, issuer_name) == 0) + return 1; /* purportedly self signed */ + + if (dn_indicates_v3_cert(subject_name) || + dn_indicates_v3_cert(issuer_name)) + return 1; /* DN is fancy */ + + key = X509_get_pubkey(cert); + if (EVP_PKEY_bits(key) != 1024 || + EVP_PKEY_type(key->type) != EVP_PKEY_RSA) { + EVP_PKEY_free(key); + return 1; /* Key is fancy */ + } + + EVP_PKEY_free(key); + return 0; +} + /** Return the number of server handshakes that we've noticed doing on * tls. */ int diff --git a/src/common/tortls.h b/src/common/tortls.h index 40eb8306e..70d24a530 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -88,6 +88,7 @@ void tor_tls_get_buffer_sizes(tor_tls_t *tls, size_t *wbuf_capacity, size_t *wbuf_bytes); int tor_tls_used_v1_handshake(tor_tls_t *tls); +int tor_tls_received_v3_certificate(tor_tls_t *tls); int tor_tls_get_num_server_handshakes(tor_tls_t *tls); int tor_tls_server_got_renegotiate(tor_tls_t *tls); int tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out); -- cgit v1.2.3 From 0a4f56277290d4736db3b15dc4c2071000f7883f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 22 Sep 2011 10:18:17 -0400 Subject: Functions to get a public RSA key from a cert --- src/common/tortls.c | 34 ++++++++++++++++++++++++++++++++++ src/common/tortls.h | 2 ++ 2 files changed, 36 insertions(+) diff --git a/src/common/tortls.c b/src/common/tortls.c index 332d78465..5d36fd04a 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -838,6 +838,40 @@ tor_tls_get_my_certs(int server, return 0; } +/** + * Return the authentication key that we use to authenticate ourselves as a + * client in the V3 in-protocol handshake. + */ +crypto_pk_env_t * +tor_tls_get_my_client_auth_key(void) +{ + if (! client_tls_context) + return NULL; + return client_tls_context->auth_key; +} + +/** + * Return the public key that a cetificate certifies. Return NULL if the + * cert's key is not RSA. + */ +crypto_pk_env_t * +tor_tls_cert_get_key(tor_cert_t *cert) +{ + crypto_pk_env_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_env_rsa(rsa); + EVP_PKEY_free(pkey); + return result; +} + /** Return true iff a and b represent the same public key. */ static int pkey_eq(EVP_PKEY *a, EVP_PKEY *b) diff --git a/src/common/tortls.h b/src/common/tortls.h index 70d24a530..b522dd112 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -118,6 +118,8 @@ const digests_t *tor_cert_get_cert_digests(const tor_cert_t *cert); int tor_tls_get_my_certs(int server, const tor_cert_t **link_cert_out, const tor_cert_t **id_cert_out); +crypto_pk_env_t *tor_tls_get_my_client_auth_key(void); +crypto_pk_env_t *tor_tls_cert_get_key(tor_cert_t *cert); int tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert); int tor_tls_cert_is_valid(const tor_cert_t *cert, const tor_cert_t *signing_cert); -- cgit v1.2.3 From a6fc5059cdb3263c0053ac76c39bef43a61269cc Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 16 Sep 2011 11:21:30 -0400 Subject: Add AUTH keys as specified in proposal 176 Our keys and x.509 certs are proliferating here. Previously we had: An ID cert (using the main ID key), self-signed A link cert (using a shorter-term link key), signed by the ID key Once proposal 176 and 179 are done, we will also have: Optionally, a presentation cert (using the link key), signed by whomever. An authentication cert (using a shorter-term ID key), signed by the ID key. These new keys are managed as part of the tls context infrastructure, since you want to rotate them under exactly the same circumstances, and since they need X509 certificates. --- src/common/tortls.c | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 5d36fd04a..e27530711 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -112,9 +112,11 @@ struct tor_cert_t { typedef struct tor_tls_context_t { int refcnt; SSL_CTX *ctx; - tor_cert_t *my_cert; + tor_cert_t *my_link_cert; tor_cert_t *my_id_cert; - crypto_pk_env_t *key; + tor_cert_t *my_auth_cert; + crypto_pk_env_t *link_key; + crypto_pk_env_t *auth_key; } tor_tls_context_t; #define TOR_TLS_MAGIC 0x71571571 @@ -811,9 +813,11 @@ 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_cert); + tor_cert_free(ctx->my_link_cert); tor_cert_free(ctx->my_id_cert); - crypto_free_pk_env(ctx->key); + tor_cert_free(ctx->my_auth_cert); + crypto_free_pk_env(ctx->link_key); + crypto_free_pk_env(ctx->auth_key); tor_free(ctx); } } @@ -832,7 +836,7 @@ tor_tls_get_my_certs(int server, if (! ctx) return -1; if (link_cert_out) - *link_cert_out = ctx->my_cert; + *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; @@ -1050,37 +1054,48 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext, static tor_tls_context_t * tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime) { - crypto_pk_env_t *rsa = NULL; + crypto_pk_env_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"); nn2 = crypto_random_hostname(8, 20, "www.", ".net"); - /* Generate short-term RSA key. */ + /* Generate short-term RSA key for use with TLS. */ if (!(rsa = crypto_new_pk_env())) goto error; if (crypto_pk_generate_key(rsa)<0) goto error; - /* Create certificate signed by identity key. */ + /* Generate short-term RSA key for use in the in-protocol ("v3") + * authentication handshake. */ + if (!(rsa_auth = crypto_new_pk_env())) + 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; } result = tor_malloc_zero(sizeof(tor_tls_context_t)); result->refcnt = 1; - result->my_cert = tor_cert_new(X509_dup(cert)); + result->my_link_cert = tor_cert_new(X509_dup(cert)); result->my_id_cert = tor_cert_new(X509_dup(idcert)); - result->key = crypto_pk_dup_key(rsa); + result->my_auth_cert = tor_cert_new(X509_dup(authcert)); + result->link_key = crypto_pk_dup_key(rsa); + result->auth_key = crypto_pk_dup_key(rsa_auth); #ifdef EVERYONE_HAS_AES /* Tell OpenSSL to only use TLS1 */ @@ -1146,6 +1161,9 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime) if (rsa) crypto_free_pk_env(rsa); + if (rsa_auth) + crypto_free_pk_env(rsa_auth); + X509_free(authcert); tor_free(nickname); tor_free(nn2); return result; @@ -1158,12 +1176,16 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime) EVP_PKEY_free(pkey); if (rsa) crypto_free_pk_env(rsa); + if (rsa_auth) + crypto_free_pk_env(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; } -- cgit v1.2.3 From e48e47fa03fa09d89527c1bbfaee4c9d7d3eee6e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 22 Sep 2011 11:01:14 -0400 Subject: Function to return peer cert as tor_tls_cert --- src/common/tortls.c | 12 ++++++++++++ src/common/tortls.h | 1 + 2 files changed, 13 insertions(+) diff --git a/src/common/tortls.c b/src/common/tortls.c index e27530711..aa90f1828 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -1856,6 +1856,18 @@ 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(const X509 *cert, const char *problem) diff --git a/src/common/tortls.h b/src/common/tortls.h index b522dd112..00bf4066d 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -66,6 +66,7 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls, int tor_tls_is_server(tor_tls_t *tls); void tor_tls_free(tor_tls_t *tls); int tor_tls_peer_has_cert(tor_tls_t *tls); +tor_cert_t *tor_tls_get_peer_cert(tor_tls_t *tls); int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity); int tor_tls_check_lifetime(tor_tls_t *tls, int tolerance); int tor_tls_read(tor_tls_t *tls, char *cp, size_t len); -- cgit v1.2.3 From 81024f43ec3a3ab32683764cb925606bfcb603d7 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 14 Sep 2011 14:44:42 -0400 Subject: Basic function to write authenticate cells Also, tweak the cert cell code to send auth certs --- src/or/connection_or.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++- src/or/connection_or.h | 5 ++ 2 files changed, 197 insertions(+), 3 deletions(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index dcb838b27..c72d89d7c 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1776,13 +1776,14 @@ connection_or_send_cert_cell(or_connection_t *conn) var_cell_t *cell; size_t cell_len; int pos; + int server_mode; tor_assert(conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3); if (! conn->handshake_state) return -1; - if (tor_tls_get_my_certs(! conn->handshake_state->started_here, - &link_cert, &id_cert) < 0) + server_mode = ! conn->handshake_state->started_here; + if (tor_tls_get_my_certs(server_mode, &link_cert, &id_cert) < 0) return -1; tor_cert_get_der(link_cert, &link_encoded, &link_len); tor_cert_get_der(id_cert, &id_encoded, &id_len); @@ -1795,7 +1796,10 @@ connection_or_send_cert_cell(or_connection_t *conn) cell->payload[0] = 2; pos = 1; - cell->payload[pos] = OR_CERT_TYPE_TLS_LINK; /* Link cert */ + if (server_mode) + cell->payload[pos] = OR_CERT_TYPE_TLS_LINK; /* Link cert */ + else + cell->payload[pos] = OR_CERT_TYPE_AUTH_1024; /* client authentication */ set_uint16(&cell->payload[pos+1], htons(link_len)); memcpy(&cell->payload[pos+3], link_encoded, link_len); pos += 3 + link_len; @@ -1842,3 +1846,188 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn) return 0; } +/** DOCDOC */ +#define V3_HS_AUTH_FIXED_PART_LEN (8+(32*6)) +#define V3_HS_AUTH_BODY_LEN (V3_HS_AUTH_FIXED_PART_LEN + 8 + 16) + +#define AUTHTYPE_RSA_SHA256_TLSSECRET 1 + +/** Compute the main body of an AUTHENTICATE cell that a client can use + * to authenticate itself on a v3 handshake for conn. Write it to the + * outlen-byte buffer at out. + * + * If server is true, only calculate the first + * V3_HS_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's + * determined by the rest of the handshake, and which match the provided value + * exactly. + * + * If server is false and signing_key is NULL, calculate the + * first V3_HS_AUTH_BODY_LEN bytes of the authenticator (that is, everything + * that should be signed), but don't actually sign it. + * + * If server is false and signing_key is provided, calculate the + * entire authenticator, signed with signing_key. + */ +int +connection_or_compute_authenticate_cell_body(or_connection_t *conn, + uint8_t *out, size_t outlen, + crypto_pk_env_t *signing_key, + int server) +{ + uint8_t *ptr; + + /* assert state is reasonable XXXX */ + + if (outlen < V3_HS_AUTH_FIXED_PART_LEN || + (!server && outlen < V3_HS_AUTH_BODY_LEN)) + return -1; + + ptr = out; + + /* Type: 8 bytes. */ + memcpy(ptr, "AUTH0001", 8); + ptr += 8; + + { + const tor_cert_t *id_cert=NULL, *link_cert=NULL; + const uint8_t *my_id, *their_id, *client_id, *server_id; + if (tor_tls_get_my_certs(0, &link_cert, &id_cert)) + return -1; + my_id = (uint8_t*)tor_cert_get_id_digests(id_cert)->d[DIGEST_SHA256]; + their_id = (uint8_t*) + tor_cert_get_id_digests(conn->handshake_state->id_cert)->d[DIGEST_SHA256]; + client_id = server ? their_id : my_id; + server_id = server ? my_id : their_id; + + /* Client ID digest: 32 octets. */ + memcpy(ptr, client_id, 32); + ptr += 32; + + /* Server ID digest: 32 octets. */ + memcpy(ptr, server_id, 32); + ptr += 32; + } + + { + crypto_digest_env_t *server_d, *client_d; + if (server) { + server_d = conn->handshake_state->digest_sent; + client_d = conn->handshake_state->digest_received; + } else { + client_d = conn->handshake_state->digest_sent; + server_d = conn->handshake_state->digest_received; + } + + /* Server log digest : 32 octets */ + crypto_digest_get_digest(server_d, (char*)ptr, 32); + ptr += 32; + + /* Client log digest : 32 octets */ + crypto_digest_get_digest(client_d, (char*)ptr, 32); + ptr += 32; + } + + { + /* Digest of cert used on TLS link : 32 octets. */ + const tor_cert_t *cert = NULL; + tor_cert_t *freecert = NULL; + if (server) { + tor_tls_get_my_certs(1, &cert, NULL); + } else { + freecert = tor_tls_get_peer_cert(conn->tls); + cert = freecert; + } + if (!cert) + return -1; + memcpy(ptr, tor_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); + + if (freecert) + tor_cert_free(freecert); + ptr += 32; + } + + /* HMAC of clientrandom and serverrandom using master key : 32 octets */ + tor_tls_get_tlssecrets(conn->tls, ptr); + ptr += 32; + + tor_assert(ptr - out == V3_HS_AUTH_FIXED_PART_LEN); + + if (server) + return ptr-out; + + /* Time: 8 octets. */ + { + uint64_t now = time(NULL); + if ((time_t)now < 0) + return -1; + set_uint32(ptr, htonl((uint32_t)(now>>32))); + set_uint32(ptr+4, htonl((uint32_t)now)); + ptr += 8; + } + + /* Nonce: 16 octets. */ + crypto_rand((char*)ptr, 16); + ptr += 16; + + tor_assert(ptr - out == V3_HS_AUTH_BODY_LEN); + + if (!signing_key) + return ptr - out; + + { + int siglen; + char d[32]; + crypto_digest256(d, (char*)out, ptr-out, DIGEST_SHA256); + siglen = crypto_pk_private_sign(signing_key, + (char*)ptr, outlen - (ptr-out), + d, 32); + if (siglen < 0) + return -1; + + ptr += siglen; + tor_assert(ptr <= out+outlen); + return ptr - out; + } +} + +/** Send an AUTHENTICATE cell on the connection conn. Return 0 on + * success, -1 on failure */ +int +connection_or_send_authenticate_cell(or_connection_t *conn) +{ + var_cell_t *cell; + crypto_pk_env_t *pk = tor_tls_get_my_client_auth_key(); + int authlen; + int cell_maxlen; + /* XXXX make sure we're actually supposed to send this! */ + + if (!pk) + return -1;/*XXXX log*/ + cell_maxlen = 4 + /* overhead */ + V3_HS_AUTH_BODY_LEN + /* Authentication body */ + crypto_pk_keysize(pk) + /* Max signature length */ + 16 /* just in case XXXX */ ; + + cell = var_cell_new(cell_maxlen); + set_uint16(cell->payload, htons(AUTHTYPE_RSA_SHA256_TLSSECRET)); + /* skip over length ; we don't know that yet. */ + + authlen = connection_or_compute_authenticate_cell_body(conn, + cell->payload+4, + cell_maxlen-4, + pk, + 0 /* not server */); + if (authlen < 0) { + /* XXXX log */ + var_cell_free(cell); + return -1; + } + tor_assert(authlen + 4 <= cell->payload_len); + set_uint16(cell->payload+2, htons(authlen)); + cell->payload_len = authlen + 4; + + connection_or_write_var_cell_to_buf(cell, conn); + var_cell_free(cell); + + return 0; +} diff --git a/src/or/connection_or.h b/src/or/connection_or.h index 6e50e29e4..ba441c45d 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -52,6 +52,11 @@ int connection_or_send_destroy(circid_t circ_id, or_connection_t *conn, int connection_or_send_netinfo(or_connection_t *conn); int connection_or_send_cert_cell(or_connection_t *conn); int connection_or_send_auth_challenge_cell(or_connection_t *conn); +int connection_or_compute_authenticate_cell_body(or_connection_t *conn, + uint8_t *out, size_t outlen, + crypto_pk_env_t *signing_key, + int server); +int connection_or_send_authenticate_cell(or_connection_t *conn); int is_or_protocol_version_known(uint16_t version); -- cgit v1.2.3 From 6c7f28454e80da733e3bfb4f71101faf09b7ac24 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 13 Sep 2011 16:24:49 -0400 Subject: Implement cert/auth cell reading --- src/or/command.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++++ src/or/connection_or.c | 24 ++--- src/or/or.h | 53 ++++++++++ 3 files changed, 338 insertions(+), 17 deletions(-) diff --git a/src/or/command.c b/src/or/command.c index 72d8cd7cf..aad971fc9 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -660,3 +660,281 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) assert_connection_ok(TO_CONN(conn),time(NULL)); } +/** Process a CERT cell from an OR connection. + * + * If the other side should not have sent us a CERT cell, or the cell is + * malformed, or it is supposed to authenticate the TLS key but it doesn't, + * then mark the connection. + * + * If the cell has a good cert chain and we're doing a v3 handshake, then + * store the certificates in or_handshake_state. If this is the client side + * of the connection, we then authenticate the server or mark the connection. + * If it's the server side, wait for an AUTHENTICATE cell. + */ +static void +command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) +{ +#define ERR(s) \ + do { \ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ + "Received a bad CERT cell from %s:%d: %s", \ + conn->_base.address, conn->_base.port, (s)); \ + connection_mark_for_close(TO_CONN(conn)); \ + goto err; \ + } while (0) + + tor_cert_t *link_cert = NULL; + tor_cert_t *id_cert = NULL; + tor_cert_t *auth_cert = NULL; + + uint8_t *ptr; + int n_certs, i; + + if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) + ERR("We're not doing a v3 handshake!"); + if (conn->handshake_state->received_cert_cell) + ERR("We already got one"); + if (cell->payload_len < 1) + ERR("It had no body"); + if (cell->circ_id) + ERR("It had a nonzero circuit ID"); + + n_certs = cell->payload[0]; + ptr = cell->payload + 1; + for (i = 0; i < n_certs; ++i) { + uint8_t cert_type; + uint16_t cert_len; + if (ptr + 3 > cell->payload + cell->payload_len) { + goto truncated; + } + cert_type = *ptr; + cert_len = ntohs(get_uint16(ptr+1)); + if (ptr + 3 + cert_len > cell->payload + cell->payload_len) { + goto truncated; + } + if (cert_type == OR_CERT_TYPE_TLS_LINK || + cert_type == OR_CERT_TYPE_ID_1024 || + cert_type == OR_CERT_TYPE_AUTH_1024) { + tor_cert_t *cert = tor_cert_decode(ptr + 3, cert_len); + if (!cert) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received undecodable certificate in CERT cell from %s:%d", + conn->_base.address, conn->_base.port); + } else { + if (cert_type == OR_CERT_TYPE_TLS_LINK && !link_cert) + link_cert = cert; + else if (cert_type == OR_CERT_TYPE_ID_1024 && !id_cert) + id_cert = cert; + else if (cert_type == OR_CERT_TYPE_AUTH_1024 && !auth_cert) + auth_cert = cert; + else + tor_cert_free(cert); + } + } + ptr += 3 + cert_len; + continue; + + truncated: + ERR("It ends in the middle of a certificate"); + } + + if (conn->handshake_state->started_here) { + if (! (id_cert && link_cert)) + ERR("The certs we wanted were missing"); + /* Okay. We should be able to check the certificates now. */ + if (! tor_tls_cert_matches_key(conn->tls, link_cert)) { + ERR("The link certificate didn't match the TLS public key"); + } + if (! tor_tls_cert_is_valid(link_cert, id_cert)) + ERR("The link certificate was not valid"); + if (! tor_tls_cert_is_valid(id_cert, id_cert)) + ERR("The ID certificate was not valid"); + + /* XXXX okay, we just got authentication. Do something about that. */ + + conn->handshake_state->id_cert = id_cert; + id_cert = NULL; + } else { + if (! (id_cert && auth_cert)) + ERR("The certs we wanted were missing"); + + /* Remember these certificates so we can check an AUTHENTICATE cell */ + conn->handshake_state->id_cert = id_cert; + conn->handshake_state->auth_cert = auth_cert; + if (! tor_tls_cert_is_valid(auth_cert, id_cert)) + ERR("The authentication certificate was not valid"); + if (! tor_tls_cert_is_valid(id_cert, id_cert)) + ERR("The ID certificate was not valid"); + + /* XXXX check more stuff? */ + + id_cert = auth_cert = NULL; + } + + conn->handshake_state->received_cert_cell = 1; +err: + tor_cert_free(id_cert); + tor_cert_free(link_cert); + tor_cert_free(auth_cert); +#undef ERR +} + +/** Process an AUTH_CHALLENGE cell from an OR connection. + * + * If we weren't supposed to get one (for example, because we're not the + * originator of the connection), or it's ill-formed, or we aren't doing a v3 + * handshake, mark the connection. If the cell is well-formed but we don't + * want to authenticate, just drop it. If the cell is well-formed *and* we + * want to authenticate, send an AUTHENTICATE cell. */ +static void +command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) +{ + int n_types, i, use_type = -1; + uint8_t *cp; + +#define ERR(s) \ + do { \ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ + "Received a bad AUTH_CHALLENGE cell from %s:%d: %s", \ + conn->_base.address, conn->_base.port, (s)); \ + connection_mark_for_close(TO_CONN(conn)); \ + return; \ + } while (0) + + if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) + ERR("We're not currently doing a v3 handshake"); + if (! conn->handshake_state->started_here) + ERR("We didn't originate this connection"); + if (conn->handshake_state->received_auth_challenge) + ERR("We already received one"); + if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2) + ERR("It was too short"); + if (cell->circ_id) + ERR("It had a nonzero circuit ID"); + + n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN)); + if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types) + ERR("It looks truncated"); + + memcpy(conn->handshake_state->auth_challenge, cell->payload, + OR_AUTH_CHALLENGE_LEN); + + /* Now see if there is an authentication type we can use */ + cp=cell->payload+OR_AUTH_CHALLENGE_LEN+2; + for (i=0; i < n_types; ++i, cp += 2) { + uint16_t authtype = ntohs(get_uint16(cp)); + if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET) + use_type = authtype; + } + + conn->handshake_state->received_auth_challenge = 1; + + /* Send back authentication if we want, and if use_type is set */ +#undef ERR +} + +/** Process an AUTHENTICATE cell from an OR connection. + * + * If it's ill-formed or we weren't supposed to get one or we're not doing a + * v3 handshake, then mark the connection. If it does not authenticate the + * other side of the connection successfully (because it isn't signed right, + * we didn't get a CERT cell, etc) mark the connection. Otherwise, accept + * the identity of the router on the other side of the connection. + */ +static void +command_process_authenticate_cell(or_connection_t *conn, var_cell_t *cell) +{ + uint8_t expected[V3_AUTH_FIXED_PART_LEN]; + const uint8_t *auth; + int authlen; + +#define ERR(s) \ + do { \ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ + "Received a bad AUTHETNICATE cell from %s:%d: %s", \ + conn->_base.address, conn->_base.port, (s)); \ + connection_mark_for_close(TO_CONN(conn)); \ + return; \ + } while (0) + + if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) + ERR("We're not doing a v3 handshake"); + if (! conn->handshake_state->started_here) + ERR("We originated this connection"); + if (conn->handshake_state->received_authenticate) + ERR("We already got one!"); + if (conn->handshake_state->auth_cert == NULL) + ERR("We never got an authentication certificate"); + if (cell->payload_len < 4) + ERR("Cell was way too short"); + + auth = cell->payload; + { + uint16_t type = ntohs(get_uint16(auth)); + uint16_t len = ntohs(get_uint16(auth+2)); + if (4 + len > cell->payload_len) + ERR("Authenticator was truncated"); + + if (type != AUTHTYPE_RSA_SHA256_TLSSECRET) + ERR("Authenticator type was not recognized"); + + auth += 4; + authlen = len; + } + + if (authlen < V3_AUTH_BODY_LEN + 1) + ERR("Authenticator was too short"); + + if (connection_or_compute_authenticate_cell_body( + conn, expected, sizeof(expected), NULL, 1) < 0) + ERR("Couldn't compute expected AUTHENTICATE cell body"); + + if (tor_memneq(expected, auth, sizeof(expected))) + ERR("Some field in the AUTHENTICATE cell body was not as expected"); + + { + crypto_pk_env_t *pk = tor_tls_cert_get_key( + conn->handshake_state->auth_cert); + char d[DIGEST256_LEN]; + char *signed_data; + size_t keysize; + int signed_len; + + crypto_digest256(d, (char*)auth, V3_AUTH_BODY_LEN, DIGEST_SHA256); + + keysize = crypto_pk_keysize(pk); + signed_data = tor_malloc(keysize); + signed_len = crypto_pk_public_checksig(pk, signed_data, keysize, + (char*)auth + V3_AUTH_BODY_LEN, + authlen - V3_AUTH_BODY_LEN); + if (signed_len < 0) { + tor_free(signed_data); + ERR("Signature wasn't valid"); + } + if (signed_len < DIGEST256_LEN) { + tor_free(signed_data); + ERR("Not enough data was signed"); + } + if (tor_memneq(signed_data, d, DIGEST256_LEN)) { + tor_free(signed_data); + ERR("Signature did not match data to be signed."); + } + tor_free(signed_data); + } + + /* XXXX we're authenticated. Now remember the fact, and remember whom we're + authenticated to. */ + + conn->handshake_state->received_authenticate = 1; +#undef ERR +} + + +void dummy_function(void); +void dummy_function(void) +{ + /* this is only here to avoid 'static function isn't used' warnings */ + command_process_auth_challenge_cell(NULL, NULL); + command_process_cert_cell(NULL, NULL); + command_process_authenticate_cell(NULL, NULL); +} diff --git a/src/or/connection_or.c b/src/or/connection_or.c index c72d89d7c..93b0b3a2c 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1761,10 +1761,6 @@ connection_or_send_netinfo(or_connection_t *conn) return 0; } -/** DOCDOC */ -#define OR_CERT_TYPE_TLS_LINK 1 -#define OR_CERT_TYPE_ID_1024 2 - /** Send a CERT cell on the connection conn. Return 0 on success, -1 * on failure. */ int @@ -1846,23 +1842,17 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn) return 0; } -/** DOCDOC */ -#define V3_HS_AUTH_FIXED_PART_LEN (8+(32*6)) -#define V3_HS_AUTH_BODY_LEN (V3_HS_AUTH_FIXED_PART_LEN + 8 + 16) - -#define AUTHTYPE_RSA_SHA256_TLSSECRET 1 - /** Compute the main body of an AUTHENTICATE cell that a client can use * to authenticate itself on a v3 handshake for conn. Write it to the * outlen-byte buffer at out. * * If server is true, only calculate the first - * V3_HS_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's + * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's * determined by the rest of the handshake, and which match the provided value * exactly. * * If server is false and signing_key is NULL, calculate the - * first V3_HS_AUTH_BODY_LEN bytes of the authenticator (that is, everything + * first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything * that should be signed), but don't actually sign it. * * If server is false and signing_key is provided, calculate the @@ -1878,8 +1868,8 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, /* assert state is reasonable XXXX */ - if (outlen < V3_HS_AUTH_FIXED_PART_LEN || - (!server && outlen < V3_HS_AUTH_BODY_LEN)) + if (outlen < V3_AUTH_FIXED_PART_LEN || + (!server && outlen < V3_AUTH_BODY_LEN)) return -1; ptr = out; @@ -1950,7 +1940,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, tor_tls_get_tlssecrets(conn->tls, ptr); ptr += 32; - tor_assert(ptr - out == V3_HS_AUTH_FIXED_PART_LEN); + tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN); if (server) return ptr-out; @@ -1969,7 +1959,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, crypto_rand((char*)ptr, 16); ptr += 16; - tor_assert(ptr - out == V3_HS_AUTH_BODY_LEN); + tor_assert(ptr - out == V3_AUTH_BODY_LEN); if (!signing_key) return ptr - out; @@ -2004,7 +1994,7 @@ connection_or_send_authenticate_cell(or_connection_t *conn) if (!pk) return -1;/*XXXX log*/ cell_maxlen = 4 + /* overhead */ - V3_HS_AUTH_BODY_LEN + /* Authentication body */ + V3_AUTH_BODY_LEN + /* Authentication body */ crypto_pk_keysize(pk) + /* Max signature length */ 16 /* just in case XXXX */ ; diff --git a/src/or/or.h b/src/or/or.h index ebf9ab569..a40598fb4 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1087,6 +1087,43 @@ typedef struct listener_connection_t { /** Minimum length of the random part of an AUTH_CHALLENGE cell. */ #define OR_AUTH_CHALLENGE_LEN 32 +/** + * @name Certificate types for CERT cells. + * + * These values are defined by the protocol, and affect how an X509 + * certificate in a CERT cell is interpreted and used. + * + * @{ */ +/** A certificate that authenticates a TLS link key. The subject key + * must match the key used in the TLS handshake; it must be signed by + * the identity key. */ +#define OR_CERT_TYPE_TLS_LINK 1 +/** A self-signed identity certificate. The subject key must be a + * 1024-bit RSA key. */ +#define OR_CERT_TYPE_ID_1024 2 +/** A certificate that authenticates a key used in an AUTHENTICATE cell + * in the v3 handshake. The subject key must be a 1024-bit RSA key; it + * must be signed by the identity key */ +#define OR_CERT_TYPE_AUTH_1024 3 +/**@}*/ + +/** The one currently supported type of AUTHENTICATE cell. It contains + * a bunch of structures signed with an RSA1024 key. The signed + * structures include a HMAC using negotiated TLS secrets, and a digest + * of all cells sent or received before the AUTHENTICATE cell (including + * the random server-generated AUTH_CHALLENGE cell). + */ +#define AUTHTYPE_RSA_SHA256_TLSSECRET 1 + +/** The length of the part of the AUTHENTICATE cell body that the client and + * server can generate independently (when using RSA_SHA256_TLSSECRET). It + * contains everything except the client's timestamp, the client's randomly + * generated nonce, and the signature. */ +#define V3_AUTH_FIXED_PART_LEN (8+(32*6)) +/** The length of the part of the AUTHENTICATE cell body that the client + * signs. */ +#define V3_AUTH_BODY_LEN (V3_AUTH_FIXED_PART_LEN + 8 + 16) + /** Stores flags and information related to the portion of a v2/v3 Tor OR * connection handshake that happens after the TLS handshake is finished. */ @@ -1098,6 +1135,12 @@ typedef struct or_handshake_state_t { unsigned int started_here : 1; /** True iff we have received and processed a VERSIONS cell. */ unsigned int received_versions : 1; + /** True iff we have received and processed an AUTH_CHALLENGE cell */ + unsigned int received_auth_challenge : 1; + /** True iff we have received and processed a CERT cell. */ + unsigned int received_cert_cell : 1; + /** True iff we have received and processed an AUTHENTICATE cell */ + unsigned int received_authenticate : 1; /** Digests of the cells that we have sent or received as part of a V3 * handshake. Used for making and checking AUTHENTICATE cells. @@ -1108,6 +1151,16 @@ typedef struct or_handshake_state_t { crypto_digest_env_t *digest_received; /** @} */ + /** Certificates that a connection initiator sent us in a CERT cell; we're + * holding on to them until we get an AUTHENTICATE cell. + * + * @{ + */ + /** The cert for the key that's supposed to sign the AUTHENTICATE cell */ + tor_cert_t *auth_cert; + /** A self-signed identity certificate */ + tor_cert_t *id_cert; + /**@}*/ } or_handshake_state_t; /** Subtype of connection_t for an "OR connection" -- that is, one that speaks -- cgit v1.2.3 From 3f22ec179c6f90b9c2af9483e2c8000132d2f33e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 16 Sep 2011 18:32:11 -0400 Subject: New functions to record digests of cells during v3 handshake Also, free all of the new fields in or_handshake_state_t --- src/or/connection_or.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/or/connection_or.h | 7 ++++++ 2 files changed, 71 insertions(+) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 93b0b3a2c..4caa3d369 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1504,10 +1504,74 @@ or_handshake_state_free(or_handshake_state_t *state) { if (!state) return; + crypto_free_digest_env(state->digest_sent); + crypto_free_digest_env(state->digest_received); + tor_cert_free(state->auth_cert); + tor_cert_free(state->id_cert); memset(state, 0xBE, sizeof(or_handshake_state_t)); tor_free(state); } +/** + * Remember that cell has been transmitted (if incoming is + * false) or received (if incoming is true) during a V3 handshake using + * state. + * + * (We don't record the cell, but we keep a digest of everything sent or + * received during the v3 handshake, and the client signs it in an + * authenticate cell.) + */ +void +or_handshake_state_record_cell(or_handshake_state_t *state, + const cell_t *cell, + int incoming) +{ + crypto_digest_env_t *d, **dptr; + packed_cell_t packed; + if (!incoming) { + log_warn(LD_BUG, "We shouldn't be sending any non-variable-length cells " + "whilemaking a handshake digest. But we think we are."); + } + dptr = incoming ? &state->digest_received : &state->digest_sent; + if (! *dptr) + *dptr = crypto_new_digest256_env(DIGEST_SHA256); + + d = *dptr; + /* Re-packing like this is a little inefficient, but we don't have to do + this very often at all. */ + cell_pack(&packed, cell); + crypto_digest_add_bytes(d, packed.body, sizeof(packed.body)); + memset(&packed, 0, sizeof(packed)); +} + +/** Remember that a variable-length cell has been transmitted (if + * incoming is false) or received (if incoming is true) during a V3 + * handshake using state. + * + * (We don't record the cell, but we keep a digest of everything sent or + * received during the v3 handshake, and the client signs it in an + * authenticate cell.) + */ +void +or_handshake_state_record_var_cell(or_handshake_state_t *state, + const var_cell_t *cell, + int incoming) +{ + crypto_digest_env_t *d, **dptr; + char buf[VAR_CELL_HEADER_SIZE]; + dptr = incoming ? &state->digest_received : &state->digest_sent; + if (! *dptr) + *dptr = crypto_new_digest256_env(DIGEST_SHA256); + + d = *dptr; + + var_cell_pack_header(cell, buf); + crypto_digest_add_bytes(d, buf, sizeof(buf)); + crypto_digest_add_bytes(d, (const char *)cell->payload, cell->payload_len); + + memset(buf, 0, sizeof(buf)); +} + /** Set conn's state to OR_CONN_STATE_OPEN, and tell other subsystems * as appropriate. Called when we are done with all TLS and OR handshaking. */ diff --git a/src/or/connection_or.h b/src/or/connection_or.h index ba441c45d..a4d3be092 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -42,6 +42,13 @@ int connection_tls_start_handshake(or_connection_t *conn, int receiving); int connection_tls_continue_handshake(or_connection_t *conn); void or_handshake_state_free(or_handshake_state_t *state); +void or_handshake_state_record_cell(or_handshake_state_t *state, + const cell_t *cell, + int incoming); +void or_handshake_state_record_var_cell(or_handshake_state_t *state, + const var_cell_t *cell, + int incoming); + int connection_or_set_state_open(or_connection_t *conn); void connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn); -- cgit v1.2.3 From 9a77ebc794cff2df50bb2d47788461864f4bc8c9 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 22 Sep 2011 10:01:41 -0400 Subject: Make tor_tls_cert_is_valid check key lengths --- src/common/tortls.c | 34 +++++++++++++++++++++++++++++----- src/common/tortls.h | 3 ++- src/or/command.c | 8 ++++---- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index aa90f1828..01a0f8ea9 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -928,14 +928,18 @@ tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert) } /** Check wither cert is well-formed, currently live, and correctly - * signed by the public key in signing_cert. Return 0 if the cert is - * good, and -1 if it's bad or we couldn't check it. */ + * signed by the public key in signing_cert. If check_rsa_1024, + * 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(const tor_cert_t *cert, - const tor_cert_t *signing_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; + int r, key_ok = 0; if (!signing_key) return 0; r = X509_verify(cert->cert, signing_key); @@ -949,9 +953,29 @@ tor_tls_cert_is_valid(const tor_cert_t *cert, if (check_cert_lifetime_internal(cert->cert, 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; + return 1; } /** Increase the reference count of ctx. */ diff --git a/src/common/tortls.h b/src/common/tortls.h index 00bf4066d..90e76e4a9 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -123,7 +123,8 @@ crypto_pk_env_t *tor_tls_get_my_client_auth_key(void); crypto_pk_env_t *tor_tls_cert_get_key(tor_cert_t *cert); int tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert); int tor_tls_cert_is_valid(const tor_cert_t *cert, - const tor_cert_t *signing_cert); + const tor_cert_t *signing_cert, + int check_rsa_1024); #endif diff --git a/src/or/command.c b/src/or/command.c index aad971fc9..a32671f0b 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -745,9 +745,9 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) if (! tor_tls_cert_matches_key(conn->tls, link_cert)) { ERR("The link certificate didn't match the TLS public key"); } - if (! tor_tls_cert_is_valid(link_cert, id_cert)) + if (! tor_tls_cert_is_valid(link_cert, id_cert, 0)) ERR("The link certificate was not valid"); - if (! tor_tls_cert_is_valid(id_cert, id_cert)) + if (! tor_tls_cert_is_valid(id_cert, id_cert, 1)) ERR("The ID certificate was not valid"); /* XXXX okay, we just got authentication. Do something about that. */ @@ -761,9 +761,9 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) /* Remember these certificates so we can check an AUTHENTICATE cell */ conn->handshake_state->id_cert = id_cert; conn->handshake_state->auth_cert = auth_cert; - if (! tor_tls_cert_is_valid(auth_cert, id_cert)) + if (! tor_tls_cert_is_valid(auth_cert, id_cert, 1)) ERR("The authentication certificate was not valid"); - if (! tor_tls_cert_is_valid(id_cert, id_cert)) + if (! tor_tls_cert_is_valid(id_cert, id_cert, 1)) ERR("The ID certificate was not valid"); /* XXXX check more stuff? */ -- cgit v1.2.3 From 445f94789039bf2d25800741b1ad77c99ab4044d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 26 Sep 2011 11:41:23 -0400 Subject: Remove a no-longer-relevant comment --- src/common/crypto.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/common/crypto.c b/src/common/crypto.c index c39719972..0076c1d35 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1206,9 +1206,6 @@ crypto_pk_asn1_decode(const char *str, size_t len) { RSA *rsa; unsigned char *buf; - /* This ifdef suppresses a type warning. Take out the first case once - * everybody is using OpenSSL 0.9.7 or later. - */ const unsigned char *cp; cp = buf = tor_malloc(len); memcpy(buf,str,len); -- cgit v1.2.3 From 83bb9742b541ff53e72c407f45093c137e13a073 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 27 Sep 2011 13:15:36 -0400 Subject: Hook up all of the prop176 code; allow v3 negotiations to actually work --- src/or/command.c | 273 +++++++++++++++++++++++++++++++++++++++++++------ src/or/connection_or.c | 242 ++++++++++++++++++++++++++++++------------- src/or/connection_or.h | 12 ++- src/or/or.h | 10 ++ 4 files changed, 435 insertions(+), 102 deletions(-) diff --git a/src/or/command.c b/src/or/command.c index a32671f0b..a8e68e109 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -46,6 +46,15 @@ uint64_t stats_n_versions_cells_processed = 0; /** How many CELL_NETINFO cells have we received, ever? */ uint64_t stats_n_netinfo_cells_processed = 0; +/** How many CELL_VPADDING cells have we received, ever? */ +uint64_t stats_n_vpadding_cells_processed = 0; +/** How many CELL_CERTS cells have we received, ever? */ +uint64_t stats_n_cert_cells_processed = 0; +/** How many CELL_AUTH_CHALLENGE cells have we received, ever? */ +uint64_t stats_n_auth_challenge_cells_processed = 0; +/** How many CELL_AUTHENTICATE cells have we received, ever? */ +uint64_t stats_n_authenticate_cells_processed = 0; + /* These are the main functions for processing cells */ static void command_process_create_cell(cell_t *cell, or_connection_t *conn); static void command_process_created_cell(cell_t *cell, or_connection_t *conn); @@ -54,6 +63,12 @@ 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); +static void command_process_cert_cell(var_cell_t *cell, + or_connection_t *conn); +static void command_process_auth_challenge_cell(var_cell_t *cell, + or_connection_t *conn); +static void command_process_authenticate_cell(var_cell_t *cell, + or_connection_t *conn); #ifdef KEEP_TIMING_STATS /** This is a wrapper function around the actual function that processes the @@ -93,7 +108,7 @@ command_time_process_cell(cell_t *cell, or_connection_t *conn, int *time, void command_process_cell(cell_t *cell, or_connection_t *conn) { - int handshaking = (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V2); + int handshaking = (conn->_base.state != OR_CONN_STATE_OPEN); #ifdef KEEP_TIMING_STATS /* how many of each cell have we seen so far this second? needs better * name. */ @@ -137,6 +152,10 @@ command_process_cell(cell_t *cell, or_connection_t *conn) if (handshaking && cell->command != CELL_VERSIONS && cell->command != CELL_NETINFO) return; + /* XXXX VERSIONS should be impossible; it's variable-length. */ + + if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) + or_handshake_state_record_cell(conn->handshake_state, cell, 1); switch (cell->command) { case CELL_PADDING: @@ -206,15 +225,55 @@ command_process_var_cell(var_cell_t *cell, or_connection_t *conn) } #endif - /* reject all when not handshaking. */ - if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2) - return; + switch (conn->_base.state) + { + case OR_CONN_STATE_OR_HANDSHAKING_V2: + if (cell->command != CELL_VERSIONS) + return; + break; + case OR_CONN_STATE_TLS_HANDSHAKING: + /* If we're using bufferevents, it's entirely possible for us to + * notice "hey, data arrived!" before we notice "hey, the handshake + * finished!" And we need to be accepting both at once to handle both + * the v2 and v3 handshakes. */ + + /* fall through */ + case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: + if (cell->command != CELL_VERSIONS) + return; /*XXXX023 log*/ + break; + case OR_CONN_STATE_OR_HANDSHAKING_V3: + or_handshake_state_record_var_cell(conn->handshake_state, cell, 1); + break; /* Everything is allowed */ + case OR_CONN_STATE_OPEN: + if (conn->link_proto < 3) + return; + default: + /*XXXX023 log */ + return; + } switch (cell->command) { case CELL_VERSIONS: ++stats_n_versions_cells_processed; PROCESS_CELL(versions, cell, conn); break; + case CELL_VPADDING: + ++stats_n_vpadding_cells_processed; + PROCESS_CELL(versions, cell, conn); + break; + case CELL_CERT: + ++stats_n_cert_cells_processed; + PROCESS_CELL(cert, cell, conn); + break; + case CELL_AUTH_CHALLENGE: + ++stats_n_auth_challenge_cells_processed; + PROCESS_CELL(auth_challenge, cell, conn); + break; + case CELL_AUTHENTICATE: + ++stats_n_authenticate_cells_processed; + PROCESS_CELL(authenticate, cell, conn); + break; default: log_fn(LOG_INFO, LD_PROTOCOL, "Variable-length cell of unknown type (%d) received.", @@ -504,14 +563,40 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn) { int highest_supported_version = 0; const uint8_t *cp, *end; + const int started_here = connection_or_nonopen_was_started_here(conn); if (conn->link_proto != 0 || - conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2 || (conn->handshake_state && conn->handshake_state->received_versions)) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received a VERSIONS cell on a connection with its version " "already set to %d; dropping", (int) conn->link_proto); return; } + switch (conn->_base.state) + { + case OR_CONN_STATE_OR_HANDSHAKING_V2: + break; + case OR_CONN_STATE_TLS_HANDSHAKING: + case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: + if (started_here) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Received a versions cell while TLS-handshaking not in " + "OR_HANDSHAKING_V3 on a connection we originated."); + } + conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + if (connection_init_or_handshake_state(conn, started_here) < 0) { + connection_mark_for_close(TO_CONN(conn)); + return; + } + or_handshake_state_record_var_cell(conn->handshake_state, cell, 1); + break; + case OR_CONN_STATE_OR_HANDSHAKING_V3: + break; + default: + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "VERSIONS cell while in unexpected state"); + return; + } + tor_assert(conn->handshake_state); end = cell->payload + cell->payload_len; for (cp = cell->payload; cp+1 < end; ++cp) { @@ -533,19 +618,80 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn) "That's crazily non-compliant. Closing connection."); connection_mark_for_close(TO_CONN(conn)); return; + } else if (highest_supported_version < 3 && + conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Negotiated link protocol 2 or lower after doing a v3 TLS " + "handshake. Closing connection."); + connection_mark_for_close(TO_CONN(conn)); + return; } + conn->link_proto = highest_supported_version; conn->handshake_state->received_versions = 1; - log_info(LD_OR, "Negotiated version %d with %s:%d; sending NETINFO.", - highest_supported_version, - safe_str_client(conn->_base.address), - conn->_base.port); - tor_assert(conn->link_proto >= 2); + if (conn->link_proto == 2) { + log_info(LD_OR, "Negotiated version %d with %s:%d; sending NETINFO.", + highest_supported_version, + safe_str_client(conn->_base.address), + conn->_base.port); - if (connection_or_send_netinfo(conn) < 0) { - connection_mark_for_close(TO_CONN(conn)); - return; + if (connection_or_send_netinfo(conn) < 0) { + connection_mark_for_close(TO_CONN(conn)); + return; + } + } else { + const int send_versions = !started_here; + /* If we want to authenticate, send a CERTS cell */ + const int send_certs = !started_here || public_server_mode(get_options()); + /* If we're a relay that got a connection, ask for authentication. */ + const int send_chall = !started_here && public_server_mode(get_options()); + /* If our certs cell will authenticate us, or if we have no intention of + * authenticating, send a netinfo cell right now. */ + const int send_netinfo = + !(started_here && public_server_mode(get_options())); + const int send_any = + send_versions || send_certs || send_chall || send_netinfo; + tor_assert(conn->link_proto >= 3); + + log_info(LD_OR, "Negotiated version %d with %s:%d; %s%s%s%s%s", + highest_supported_version, + safe_str_client(conn->_base.address), + conn->_base.port, + send_any ? "Sending cells:" : "Waiting for CERTS cell", + send_versions ? " VERSIONS" : "", + send_certs ? " CERTS" : "", + send_versions ? " AUTH_CHALLENGE" : "", + send_netinfo ? " NETINFO" : ""); + + if (send_versions) { + if (connection_or_send_versions(conn, 1) < 0) { + log_warn(LD_OR, "Couldn't send versions cell"); + connection_mark_for_close(TO_CONN(conn)); + return; + } + } + if (send_certs) { + if (connection_or_send_cert_cell(conn) < 0) { + log_warn(LD_OR, "Couldn't send cert cell"); + connection_mark_for_close(TO_CONN(conn)); + return; + } + } + if (send_chall) { + if (connection_or_send_auth_challenge_cell(conn) < 0) { + log_warn(LD_OR, "Couldn't send auth_challenge cell"); + connection_mark_for_close(TO_CONN(conn)); + return; + } + } + if (send_netinfo) { + if (connection_or_send_netinfo(conn) < 0) { + log_warn(LD_OR, "Couldn't send netinfo cell"); + connection_mark_for_close(TO_CONN(conn)); + return; + } + } } } @@ -571,13 +717,26 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) conn->link_proto == 0 ? "non-versioned" : "a v1"); return; } - if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2) { + if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2 && + conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received a NETINFO cell on non-handshaking connection; dropping."); return; } tor_assert(conn->handshake_state && conn->handshake_state->received_versions); + + if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) { + tor_assert(conn->link_proto >= 3); + if (conn->handshake_state->started_here) { + if (!conn->handshake_state->authenticated) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got a NETINFO cell from server, " + "but no authentication. Closing the connection."); + connection_mark_for_close(TO_CONN(conn)); + } + } + } + /* Decode the cell. */ timestamp = ntohl(get_uint32(cell->payload)); if (labs(now - conn->handshake_state->sent_versions_at) < 180) { @@ -692,8 +851,14 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) ERR("We're not doing a v3 handshake!"); + if (conn->link_proto < 3) + ERR("We're not using link protocol >= 3"); if (conn->handshake_state->received_cert_cell) ERR("We already got one"); + if (conn->handshake_state->authenticated) { + /* Should be unreachable, but let's make sure. */ + ERR("We're already authenticated!"); + } if (cell->payload_len < 1) ERR("It had no body"); if (cell->circ_id) @@ -750,7 +915,20 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) if (! tor_tls_cert_is_valid(id_cert, id_cert, 1)) ERR("The ID certificate was not valid"); - /* XXXX okay, we just got authentication. Do something about that. */ + conn->handshake_state->authenticated = 1; + { + crypto_pk_env_t *identity_rcvd = tor_tls_cert_get_key(id_cert); + const digests_t *id_digests = tor_cert_get_id_digests(id_cert); + memcpy(conn->handshake_state->authenticated_peer_id, + id_digests->d[DIGEST_SHA1], DIGEST_LEN); + connection_or_set_circid_type(conn, identity_rcvd); + crypto_free_pk_env(identity_rcvd); + } + + if (connection_or_client_learned_peer_id(conn, + conn->handshake_state->authenticated_peer_id) < 0) + ERR("Problem setting or checking peer id"); + conn->handshake_state->id_cert = id_cert; id_cert = NULL; @@ -803,10 +981,14 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) ERR("We're not currently doing a v3 handshake"); + if (conn->link_proto < 3) + ERR("We're not using link protocol >= 3"); if (! conn->handshake_state->started_here) ERR("We didn't originate this connection"); if (conn->handshake_state->received_auth_challenge) ERR("We already received one"); + if (! conn->handshake_state->received_cert_cell) + ERR("We haven't gotten a CERTS cell yet"); if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2) ERR("It was too short"); if (cell->circ_id) @@ -829,7 +1011,19 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) conn->handshake_state->received_auth_challenge = 1; - /* Send back authentication if we want, and if use_type is set */ + if (use_type && public_server_mode(get_options())) { + if (connection_or_send_authenticate_cell(conn, use_type) < 0) { + log_warn(LD_OR, "Couldn't send authenticate cell"); + connection_mark_for_close(TO_CONN(conn)); + return; + } + if (connection_or_send_netinfo(conn) < 0) { + log_warn(LD_OR, "Couldn't send netinfo cell"); + connection_mark_for_close(TO_CONN(conn)); + return; + } + } + #undef ERR } @@ -842,7 +1036,7 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) * the identity of the router on the other side of the connection. */ static void -command_process_authenticate_cell(or_connection_t *conn, var_cell_t *cell) +command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn) { uint8_t expected[V3_AUTH_FIXED_PART_LEN]; const uint8_t *auth; @@ -859,12 +1053,22 @@ command_process_authenticate_cell(or_connection_t *conn, var_cell_t *cell) if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) ERR("We're not doing a v3 handshake"); + if (conn->link_proto < 3) + ERR("We're not using link protocol >= 3"); if (! conn->handshake_state->started_here) ERR("We originated this connection"); if (conn->handshake_state->received_authenticate) ERR("We already got one!"); + if (conn->handshake_state->authenticated) { + /* Should be impossible given other checks */ + ERR("The peer is already authenticated"); + } + if (! conn->handshake_state->received_cert_cell) + ERR("We never got a cert cell"); if (conn->handshake_state->auth_cert == NULL) ERR("We never got an authentication certificate"); + if (conn->handshake_state->id_cert == NULL) + ERR("We never got an identity certificate"); if (cell->payload_len < 4) ERR("Cell was way too short"); @@ -915,6 +1119,8 @@ command_process_authenticate_cell(or_connection_t *conn, var_cell_t *cell) tor_free(signed_data); ERR("Not enough data was signed"); } + /* Note that we deliberately allow *more* than DIGEST256_LEN bytes here, + * in case they're later used to hold a SHA3 digest or something. */ if (tor_memneq(signed_data, d, DIGEST256_LEN)) { tor_free(signed_data); ERR("Signature did not match data to be signed."); @@ -922,19 +1128,28 @@ command_process_authenticate_cell(or_connection_t *conn, var_cell_t *cell) tor_free(signed_data); } - /* XXXX we're authenticated. Now remember the fact, and remember whom we're - authenticated to. */ - + /* Okay, we are authenticated. */ conn->handshake_state->received_authenticate = 1; + conn->handshake_state->authenticated = 1; + { + crypto_pk_env_t *identity_rcvd = + tor_tls_cert_get_key(conn->handshake_state->id_cert); + const digests_t *id_digests = + tor_cert_get_id_digests(conn->handshake_state->id_cert); + + memcpy(conn->handshake_state->authenticated_peer_id, + id_digests->d[DIGEST_SHA1], DIGEST_LEN); + + connection_or_set_circid_type(conn, identity_rcvd); + crypto_free_pk_env(identity_rcvd); + + connection_or_init_conn_from_address(conn, + &conn->_base.addr, + conn->_base.port, + (const char*)conn->handshake_state->authenticated_peer_id, + 0); + } + #undef ERR } - -void dummy_function(void); -void dummy_function(void) -{ - /* this is only here to avoid 'static function isn't used' warnings */ - command_process_auth_challenge_cell(NULL, NULL); - command_process_cert_cell(NULL, NULL); - command_process_authenticate_cell(NULL, NULL); -} diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 4caa3d369..a391ca7c0 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -35,10 +35,8 @@ #endif static int connection_tls_finish_handshake(or_connection_t *conn); +static int connection_or_launch_v3_or_handshake(or_connection_t *conn); static int connection_or_process_cells_from_inbuf(or_connection_t *conn); -static int connection_or_send_versions(or_connection_t *conn); -static int connection_init_or_handshake_state(or_connection_t *conn, - int started_here); static int connection_or_check_valid_tls_handshake(or_connection_t *conn, int started_here, char *digest_rcvd_out); @@ -388,6 +386,7 @@ connection_or_process_inbuf(or_connection_t *conn) #endif case OR_CONN_STATE_OPEN: case OR_CONN_STATE_OR_HANDSHAKING_V2: + case OR_CONN_STATE_OR_HANDSHAKING_V3: return connection_or_process_cells_from_inbuf(conn); default: return 0; /* don't do anything */ @@ -627,7 +626,7 @@ connection_or_update_token_buckets(smartlist_t *conns, /** If we don't necessarily know the router we're connecting to, but we * have an addr/port/id_digest, then fill in as much as we can. Start * by checking to see if this describes a router we know. */ -static void +void connection_or_init_conn_from_address(or_connection_t *conn, const tor_addr_t *addr, uint16_t port, const char *id_digest, @@ -1180,16 +1179,22 @@ connection_tls_continue_handshake(or_connection_t *conn) if (! tor_tls_used_v1_handshake(conn->tls)) { if (!tor_tls_is_server(conn->tls)) { if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) { - log_debug(LD_OR, "Done with initial SSL handshake (client-side). " - "Requesting renegotiation."); - conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING; - goto again; + if (tor_tls_received_v3_certificate(conn->tls)) { + log_notice(LD_OR, "Client got a v3 cert! Moving on to v3 " + "handshake."); + return connection_or_launch_v3_or_handshake(conn); + } else { + log_debug(LD_OR, "Done with initial SSL handshake (client-side). " + "Requesting renegotiation."); + conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING; + goto again; + } } // log_notice(LD_OR,"Done. state was %d.", conn->_base.state); } else { - /* improved handshake, but not a client. */ + /* v2/v3 handshake, but not a client. */ log_debug(LD_OR, "Done with initial SSL handshake (server-side). " - "Expecting renegotiation."); + "Expecting renegotiation or VERSIONS cell"); tor_tls_set_renegotiate_callback(conn->tls, connection_or_tls_renegotiated_cb, conn); @@ -1234,17 +1239,24 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event, if (! tor_tls_used_v1_handshake(conn->tls)) { if (!tor_tls_is_server(conn->tls)) { if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) { - conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING; - tor_tls_unblock_renegotiation(conn->tls); - if (bufferevent_ssl_renegotiate(conn->_base.bufev)<0) { - log_warn(LD_OR, "Start_renegotiating went badly."); - connection_mark_for_close(TO_CONN(conn)); + if (tor_tls_received_v3_certificate(conn->tls)) { + log_notice(LD_OR, "Client got a v3 cert!"); + if (connection_or_launch_v3_or_handshake(conn) < 0) + connection_mark_for_close(TO_CONN(conn)); + return; + } else { + conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING; + tor_tls_unblock_renegotiation(conn->tls); + if (bufferevent_ssl_renegotiate(conn->_base.bufev)<0) { + log_warn(LD_OR, "Start_renegotiating went badly."); + connection_mark_for_close(TO_CONN(conn)); + } + tor_tls_unblock_renegotiation(conn->tls); + return; /* ???? */ } - tor_tls_unblock_renegotiation(conn->tls); - return; /* ???? */ } } else if (tor_tls_get_num_server_handshakes(conn->tls) == 1) { - /* improved handshake, as a server. Only got one handshake, so + /* v2 or v3 handshake, as a server. Only got one handshake, so * wait for the next one. */ tor_tls_set_renegotiate_callback(conn->tls, connection_or_tls_renegotiated_cb, @@ -1256,7 +1268,7 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event, const int handshakes = tor_tls_get_num_server_handshakes(conn->tls); tor_assert(handshakes >= 2); if (handshakes == 2) { - /* improved handshake, as a server. Two handshakes happened already, + /* v2 handshake, as a server. Two handshakes happened already, * so we treat renegotiation as done. */ connection_or_tls_renegotiated_cb(conn->tls, conn); @@ -1300,6 +1312,29 @@ connection_or_nonopen_was_started_here(or_connection_t *conn) return !tor_tls_is_server(conn->tls); } +/** Set the circid_type field of conn (which determines which part of + * the circuit ID space we're willing to use) based on comparing our ID to + * identity_rcvd */ +void +connection_or_set_circid_type(or_connection_t *conn, + crypto_pk_env_t *identity_rcvd) +{ + const int started_here = connection_or_nonopen_was_started_here(conn); + crypto_pk_env_t *our_identity = + started_here ? get_tlsclient_identity_key() : + get_server_identity_key(); + + if (identity_rcvd) { + if (crypto_pk_cmp_keys(our_identity, identity_rcvd)<0) { + conn->circ_id_type = CIRC_ID_TYPE_LOWER; + } else { + conn->circ_id_type = CIRC_ID_TYPE_HIGHER; + } + } else { + conn->circ_id_type = CIRC_ID_TYPE_NEITHER; + } +} + /** Conn just completed its handshake. Return 0 if all is well, and * return -1 if he is lying, broken, or otherwise something is wrong. * @@ -1337,10 +1372,7 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, started_here ? conn->_base.address : safe_str_client(conn->_base.address); const char *conn_type = started_here ? "outgoing" : "incoming"; - crypto_pk_env_t *our_identity = - started_here ? get_tlsclient_identity_key() : - get_server_identity_key(); - int has_cert = 0, has_identity=0; + int has_cert = 0; check_no_tls_errors(); has_cert = tor_tls_peer_has_cert(conn->tls); @@ -1375,21 +1407,46 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, } if (identity_rcvd) { - has_identity = 1; crypto_pk_get_digest(identity_rcvd, digest_rcvd_out); - if (crypto_pk_cmp_keys(our_identity, identity_rcvd)<0) { - conn->circ_id_type = CIRC_ID_TYPE_LOWER; - } else { - conn->circ_id_type = CIRC_ID_TYPE_HIGHER; - } - crypto_free_pk_env(identity_rcvd); } else { memset(digest_rcvd_out, 0, DIGEST_LEN); - conn->circ_id_type = CIRC_ID_TYPE_NEITHER; } - if (started_here && tor_digest_is_zero(conn->identity_digest)) { - connection_or_set_identity_digest(conn, digest_rcvd_out); + connection_or_set_circid_type(conn, identity_rcvd); + crypto_free_pk_env(identity_rcvd); + + if (started_here) + return connection_or_client_learned_peer_id(conn, + (const uint8_t*)digest_rcvd_out); + + return 0; +} + +/** Called when we (as a connection initiator) have definitively, + * authenticatedly, learned that ID of the Tor instance on the other + * side of conn is peer_id. For v1 and v2 handshakes, + * this is right after we get a certificate chain in a TLS handshake + * or renegotiation. For v3 handshakes, this is right after we get a + * certificate chain in a CERT cell. + * + * If we want any particular ID before, record the one we got. + * + * If we wanted an ID, but we didn't get it, log a warning and return -1. + * + * If we're testing reachability, remember what we learned. + * + * Return 0 on success, -1 on failure. + */ +int +connection_or_client_learned_peer_id(or_connection_t *conn, + const uint8_t *peer_id) +{ + int as_expected = 1; + const or_options_t *options = get_options(); + int severity = server_mode(options) ? LOG_PROTOCOL_WARN : LOG_WARN; + + if (tor_digest_is_zero(conn->identity_digest)) { + connection_or_set_identity_digest(conn, (const char*)peer_id); tor_free(conn->nickname); conn->nickname = tor_malloc(HEX_DIGEST_LEN+2); conn->nickname[0] = '$'; @@ -1401,43 +1458,39 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, /* if it's a bridge and we didn't know its identity fingerprint, now * we do -- remember it for future attempts. */ learned_router_identity(&conn->_base.addr, conn->_base.port, - digest_rcvd_out); + (const char*)peer_id); } - if (started_here) { - int as_advertised = 1; - tor_assert(has_cert); - tor_assert(has_identity); - if (tor_memneq(digest_rcvd_out, conn->identity_digest, DIGEST_LEN)) { - /* I was aiming for a particular digest. I didn't get it! */ - char seen[HEX_DIGEST_LEN+1]; - char expected[HEX_DIGEST_LEN+1]; - base16_encode(seen, sizeof(seen), digest_rcvd_out, DIGEST_LEN); - base16_encode(expected, sizeof(expected), conn->identity_digest, - DIGEST_LEN); - log_fn(severity, LD_HANDSHAKE, - "Tried connecting to router at %s:%d, but identity key was not " - "as expected: wanted %s but got %s.", - conn->_base.address, conn->_base.port, expected, seen); - entry_guard_register_connect_status(conn->identity_digest, 0, 1, - time(NULL)); - control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, - END_OR_CONN_REASON_OR_IDENTITY); - if (!authdir_mode_tests_reachability(options)) - control_event_bootstrap_problem("foo", END_OR_CONN_REASON_OR_IDENTITY); - as_advertised = 0; - } - if (authdir_mode_tests_reachability(options)) { - dirserv_orconn_tls_done(conn->_base.address, conn->_base.port, - digest_rcvd_out, as_advertised); - } - if (!as_advertised) - return -1; + if (tor_memneq(peer_id, conn->identity_digest, DIGEST_LEN)) { + /* I was aiming for a particular digest. I didn't get it! */ + char seen[HEX_DIGEST_LEN+1]; + char expected[HEX_DIGEST_LEN+1]; + base16_encode(seen, sizeof(seen), (const char*)peer_id, DIGEST_LEN); + base16_encode(expected, sizeof(expected), conn->identity_digest, + DIGEST_LEN); + log_fn(severity, LD_HANDSHAKE, + "Tried connecting to router at %s:%d, but identity key was not " + "as expected: wanted %s but got %s.", + conn->_base.address, conn->_base.port, expected, seen); + entry_guard_register_connect_status(conn->identity_digest, 0, 1, + time(NULL)); + control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, + END_OR_CONN_REASON_OR_IDENTITY); + if (!authdir_mode_tests_reachability(options)) + control_event_bootstrap_problem("foo", END_OR_CONN_REASON_OR_IDENTITY); + as_expected = 0; + } + if (authdir_mode_tests_reachability(options)) { + dirserv_orconn_tls_done(conn->_base.address, conn->_base.port, + (const char*)peer_id, as_expected); } + if (!as_expected) + return -1; + return 0; } -/** The tls handshake is finished. +/** The v1/v2 TLS handshake is finished. * * Make sure we are happy with the person we just handshaked with. * @@ -1447,6 +1500,8 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, * If all is successful, call circuit_n_conn_done() to handle events * that have been pending on the _base.addr, conn->_base.port, digest_rcvd, 0); } - return connection_or_send_versions(conn); + return connection_or_send_versions(conn, 0); } } +/** + * Called as client when initial TLS handshake is done, and we notice + * that we got a v3-handshake signalling certificate from the server. + * Set up structures, do bookkeeping, and send the versions cell. + * Return 0 on success and -1 on failure. + */ +static int +connection_or_launch_v3_or_handshake(or_connection_t *conn) +{ + tor_assert(connection_or_nonopen_was_started_here(conn)); + tor_assert(tor_tls_received_v3_certificate(conn->tls)); + + circuit_build_times_network_is_live(&circ_times); + + conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + if (connection_init_or_handshake_state(conn, 1) < 0) + return -1; + + return connection_or_send_versions(conn, 1); +} + + /** Allocate a new connection handshake state for the connection * conn. Return 0 on success, -1 on failure. */ -static int +int connection_init_or_handshake_state(or_connection_t *conn, int started_here) { or_handshake_state_t *s; @@ -1639,6 +1716,9 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn) connection_write_to_buf(networkcell.body, CELL_NETWORK_SIZE, TO_CONN(conn)); + if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) + or_handshake_state_record_cell(conn->handshake_state, cell, 0); + if (cell->command != CELL_PADDING) conn->timestamp_last_added_nonpadding = approx_time(); } @@ -1658,6 +1738,8 @@ connection_or_write_var_cell_to_buf(const var_cell_t *cell, connection_write_to_buf(hdr, sizeof(hdr), TO_CONN(conn)); connection_write_to_buf((char*)cell->payload, cell->payload_len, TO_CONN(conn)); + if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) + or_handshake_state_record_var_cell(conn->handshake_state, cell, 0); if (cell->command != CELL_PADDING) conn->timestamp_last_added_nonpadding = approx_time(); } @@ -1742,7 +1824,7 @@ connection_or_send_destroy(circid_t circ_id, or_connection_t *conn, int reason) } /** Array of recognized link protocol versions. */ -static const uint16_t or_protocol_versions[] = { 1, 2 }; +static const uint16_t or_protocol_versions[] = { 1, 2, 3 }; /** Number of versions in or_protocol_versions. */ static const int n_or_protocol_versions = (int)( sizeof(or_protocol_versions)/sizeof(uint16_t) ); @@ -1761,20 +1843,33 @@ is_or_protocol_version_known(uint16_t v) } /** Send a VERSIONS cell on conn, telling the other host about the - * link protocol versions that this Tor can support. */ -static int -connection_or_send_versions(or_connection_t *conn) + * link protocol versions that this Tor can support. + * + * If v3_plus, this is part of a V3 protocol handshake, so only + * allow protocol version v3 or later. If not v3_plus, this is + * not part of a v3 protocol handshake, so don't allow protocol v3 or + * later. + **/ +int +connection_or_send_versions(or_connection_t *conn, int v3_plus) { var_cell_t *cell; int i; + int n_versions = 0; + const int min_version = v3_plus ? 3 : 0; + const int max_version = v3_plus ? UINT16_MAX : 2; tor_assert(conn->handshake_state && !conn->handshake_state->sent_versions_at); cell = var_cell_new(n_or_protocol_versions * 2); cell->command = CELL_VERSIONS; for (i = 0; i < n_or_protocol_versions; ++i) { uint16_t v = or_protocol_versions[i]; - set_uint16(cell->payload+(2*i), htons(v)); + if (v < min_version || v > max_version) + continue; + set_uint16(cell->payload+(2*n_versions), htons(v)); + ++n_versions; } + cell->payload_len = n_versions * 2; connection_or_write_var_cell_to_buf(cell, conn); conn->handshake_state->sent_versions_at = time(NULL); @@ -2047,7 +2142,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, /** Send an AUTHENTICATE cell on the connection conn. Return 0 on * success, -1 on failure */ int -connection_or_send_authenticate_cell(or_connection_t *conn) +connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) { var_cell_t *cell; crypto_pk_env_t *pk = tor_tls_get_my_client_auth_key(); @@ -2057,6 +2152,9 @@ connection_or_send_authenticate_cell(or_connection_t *conn) if (!pk) return -1;/*XXXX log*/ + if (authtype != AUTHTYPE_RSA_SHA256_TLSSECRET) + return -1;/*XXXX log*/ + cell_maxlen = 4 + /* overhead */ V3_AUTH_BODY_LEN + /* Authentication body */ crypto_pk_keysize(pk) + /* Max signature length */ diff --git a/src/or/connection_or.h b/src/or/connection_or.h index a4d3be092..de3a989a6 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -41,6 +41,15 @@ void connection_or_report_broken_states(int severity, int domain); int connection_tls_start_handshake(or_connection_t *conn, int receiving); int connection_tls_continue_handshake(or_connection_t *conn); +int connection_init_or_handshake_state(or_connection_t *conn, int started_here); +void connection_or_init_conn_from_address(or_connection_t *conn, + const tor_addr_t *addr, uint16_t port, + const char *id_digest, + int started_here); +int connection_or_client_learned_peer_id(or_connection_t *conn, + const uint8_t *peer_id); +void connection_or_set_circid_type(or_connection_t *conn, + crypto_pk_env_t *identity_rcvd); void or_handshake_state_free(or_handshake_state_t *state); void or_handshake_state_record_cell(or_handshake_state_t *state, const cell_t *cell, @@ -56,6 +65,7 @@ void connection_or_write_var_cell_to_buf(const var_cell_t *cell, or_connection_t *conn); int connection_or_send_destroy(circid_t circ_id, or_connection_t *conn, int reason); +int connection_or_send_versions(or_connection_t *conn, int v3_plus); int connection_or_send_netinfo(or_connection_t *conn); int connection_or_send_cert_cell(or_connection_t *conn); int connection_or_send_auth_challenge_cell(or_connection_t *conn); @@ -63,7 +73,7 @@ int connection_or_compute_authenticate_cell_body(or_connection_t *conn, uint8_t *out, size_t outlen, crypto_pk_env_t *signing_key, int server); -int connection_or_send_authenticate_cell(or_connection_t *conn); +int connection_or_send_authenticate_cell(or_connection_t *conn, int type); int is_or_protocol_version_known(uint16_t version); diff --git a/src/or/or.h b/src/or/or.h index a40598fb4..887dcb6ad 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1142,6 +1142,16 @@ typedef struct or_handshake_state_t { /** True iff we have received and processed an AUTHENTICATE cell */ unsigned int received_authenticate : 1; + /* True iff we've received valid authentication to some identity. */ + unsigned int authenticated : 1; + + /** Identity digest that we have received and authenticated for our peer + * on this connection. */ + uint8_t authenticated_peer_id[DIGEST_LEN]; + + /** DOCDOC */ + uint8_t auth_challenge[OR_AUTH_CHALLENGE_LEN]; + /** Digests of the cells that we have sent or received as part of a V3 * handshake. Used for making and checking AUTHENTICATE cells. * -- cgit v1.2.3 From 7935c4bdfab55dfa3e87345f84b73772dbc50110 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 27 Sep 2011 13:40:39 -0400 Subject: Allow "finished flushing" during v3 handshake --- src/or/connection_or.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index a391ca7c0..ecd4af371 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -439,6 +439,7 @@ connection_or_finished_flushing(or_connection_t *conn) case OR_CONN_STATE_PROXY_HANDSHAKING: case OR_CONN_STATE_OPEN: case OR_CONN_STATE_OR_HANDSHAKING_V2: + case OR_CONN_STATE_OR_HANDSHAKING_V3: break; default: log_err(LD_BUG,"Called in unexpected state %d.", conn->_base.state); -- cgit v1.2.3 From 6bfb31ff56f4e6d67ab667932a11d93ce607ac67 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 28 Sep 2011 15:03:42 -0400 Subject: Generate certificates that enable v3 handshake --- src/common/tortls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 01a0f8ea9..206ac3be4 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -1086,7 +1086,7 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime) tor_tls_init(); nickname = crypto_random_hostname(8, 20, "www.", ".net"); - nn2 = crypto_random_hostname(8, 20, "www.", ".net"); + nn2 = crypto_random_hostname(8, 20, "www.", ".com"); /* Generate short-term RSA key for use with TLS. */ if (!(rsa = crypto_new_pk_env())) -- cgit v1.2.3 From 40f343e176c7f049adf9603f65c575e95b6e6faa Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 27 Sep 2011 14:04:21 -0400 Subject: Actually accept cells in SERVER_RENEGOTIATING --- src/or/connection_or.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index ecd4af371..adaaa2cbf 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -376,8 +376,8 @@ connection_or_process_inbuf(or_connection_t *conn) } return ret; -#ifdef USE_BUFFEREVENTS case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: +#ifdef USE_BUFFEREVENTS if (tor_tls_server_got_renegotiate(conn->tls)) connection_or_tls_renegotiated_cb(conn->tls, conn); if (conn->_base.marked_for_close) -- cgit v1.2.3 From f726c67dd485bf67e02c12fbb04e99447e407487 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 27 Sep 2011 14:39:57 -0400 Subject: more verbose log for recording an odd cell --- src/or/connection_or.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index adaaa2cbf..004c19f08 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1608,7 +1608,8 @@ or_handshake_state_record_cell(or_handshake_state_t *state, packed_cell_t packed; if (!incoming) { log_warn(LD_BUG, "We shouldn't be sending any non-variable-length cells " - "whilemaking a handshake digest. But we think we are."); + "while making a handshake digest. But we think we are sending " + "one with type %d.", (int)cell->command); } dptr = incoming ? &state->digest_received : &state->digest_sent; if (! *dptr) -- cgit v1.2.3 From 610cb0ecc43506d237074868050630b30d046a6d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 27 Sep 2011 14:40:27 -0400 Subject: Fix log message about what cells we are sending --- src/or/command.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/or/command.c b/src/or/command.c index a8e68e109..551924589 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -661,7 +661,7 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn) send_any ? "Sending cells:" : "Waiting for CERTS cell", send_versions ? " VERSIONS" : "", send_certs ? " CERTS" : "", - send_versions ? " AUTH_CHALLENGE" : "", + send_chall ? " AUTH_CHALLENGE" : "", send_netinfo ? " NETINFO" : ""); if (send_versions) { -- cgit v1.2.3 From 41b250d7ea6b2d635d0e0b70cf7e1d5c1ed9ca4f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 27 Sep 2011 15:20:17 -0400 Subject: Bugfixes for authenticate handling and generation --- src/or/command.c | 2 +- src/or/connection_or.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/or/command.c b/src/or/command.c index 551924589..4da5f8600 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -1055,7 +1055,7 @@ command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn) ERR("We're not doing a v3 handshake"); if (conn->link_proto < 3) ERR("We're not using link protocol >= 3"); - if (! conn->handshake_state->started_here) + if (conn->handshake_state->started_here) ERR("We originated this connection"); if (conn->handshake_state->received_authenticate) ERR("We already got one!"); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 004c19f08..1b40f36df 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -2163,6 +2163,7 @@ connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) 16 /* just in case XXXX */ ; cell = var_cell_new(cell_maxlen); + cell->command = CELL_AUTHENTICATE; set_uint16(cell->payload, htons(AUTHTYPE_RSA_SHA256_TLSSECRET)); /* skip over length ; we don't know that yet. */ -- cgit v1.2.3 From 7aadae606b51460810163cac0a5e695ebbefa3b9 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 28 Sep 2011 10:31:56 -0400 Subject: Make sure we stop putting cells into our hash at the right time. --- src/or/command.c | 4 +++- src/or/connection_or.c | 19 +++++++++++++++++++ src/or/or.h | 13 +++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/or/command.c b/src/or/command.c index 4da5f8600..d63b9dd35 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -243,7 +243,8 @@ command_process_var_cell(var_cell_t *cell, or_connection_t *conn) return; /*XXXX023 log*/ break; case OR_CONN_STATE_OR_HANDSHAKING_V3: - or_handshake_state_record_var_cell(conn->handshake_state, cell, 1); + if (cell->command != CELL_AUTHENTICATE) + or_handshake_state_record_var_cell(conn->handshake_state, cell, 1); break; /* Everything is allowed */ case OR_CONN_STATE_OPEN: if (conn->link_proto < 3) @@ -1131,6 +1132,7 @@ command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn) /* Okay, we are authenticated. */ conn->handshake_state->received_authenticate = 1; conn->handshake_state->authenticated = 1; + conn->handshake_state->digest_received_data = 0; { crypto_pk_env_t *identity_rcvd = tor_tls_cert_get_key(conn->handshake_state->id_cert); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 1b40f36df..7cdea8219 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1573,6 +1573,8 @@ connection_init_or_handshake_state(or_connection_t *conn, int started_here) or_handshake_state_t *s; s = conn->handshake_state = tor_malloc_zero(sizeof(or_handshake_state_t)); s->started_here = started_here ? 1 : 0; + s->digest_sent_data = 1; + s->digest_received_data = 1; return 0; } @@ -1606,6 +1608,13 @@ or_handshake_state_record_cell(or_handshake_state_t *state, { crypto_digest_env_t *d, **dptr; packed_cell_t packed; + if (incoming) { + if (!state->digest_received_data) + return; + } else { + if (!state->digest_sent_data) + return; + } if (!incoming) { log_warn(LD_BUG, "We shouldn't be sending any non-variable-length cells " "while making a handshake digest. But we think we are sending " @@ -1638,6 +1647,13 @@ or_handshake_state_record_var_cell(or_handshake_state_t *state, { crypto_digest_env_t *d, **dptr; char buf[VAR_CELL_HEADER_SIZE]; + if (incoming) { + if (!state->digest_received_data) + return; + } else { + if (!state->digest_sent_data) + return; + } dptr = incoming ? &state->digest_received : &state->digest_sent; if (! *dptr) *dptr = crypto_new_digest256_env(DIGEST_SHA256); @@ -1891,6 +1907,8 @@ connection_or_send_netinfo(or_connection_t *conn) int len; uint8_t *out; + tor_assert(conn->handshake_state); + memset(&cell, 0, sizeof(cell_t)); cell.command = CELL_NETINFO; @@ -1917,6 +1935,7 @@ connection_or_send_netinfo(or_connection_t *conn) *out = 0; } + conn->handshake_state->digest_sent_data = 0; connection_or_write_cell_to_buf(&cell, conn); return 0; diff --git a/src/or/or.h b/src/or/or.h index 887dcb6ad..aa12e31d6 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1145,6 +1145,19 @@ typedef struct or_handshake_state_t { /* True iff we've received valid authentication to some identity. */ unsigned int authenticated : 1; + /** True iff we should feed outgoing cells into digest_sent and + * digest_received respectively. + * + * From the server's side of the v3 handshake, we want to capture everything + * from the VERSIONS cell through and including the AUTH_CHALLENGE cell. + * From the client's, we want to capture everything from the VERSIONS cell + * through but *not* including the AUTHENTICATE cell. + * + * @{ */ + unsigned int digest_sent_data : 1; + unsigned int digest_received_data : 1; + /**@}*/ + /** Identity digest that we have received and authenticated for our peer * on this connection. */ uint8_t authenticated_peer_id[DIGEST_LEN]; -- cgit v1.2.3 From 23664fb3b8461bfdbfab403371e90b4af1b42450 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 28 Sep 2011 13:10:40 -0400 Subject: Set up network parameters on non-authenticated incoming connections Also add some info log messages for the steps of the v3 handshake. Now my test network bootstraps! --- src/or/command.c | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/or/command.c b/src/or/command.c index d63b9dd35..ca248e66b 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -734,6 +734,21 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got a NETINFO cell from server, " "but no authentication. Closing the connection."); connection_mark_for_close(TO_CONN(conn)); + return; + } + } else { + /* we're the server. If the client never authenticated, we have + some housekeeping to do.*/ + if (!conn->handshake_state->authenticated) { + tor_assert(tor_digest_is_zero( + (const char*)conn->handshake_state->authenticated_peer_id)); + connection_or_set_circid_type(conn, NULL); + + connection_or_init_conn_from_address(conn, + &conn->_base.addr, + conn->_base.port, + (const char*)conn->handshake_state->authenticated_peer_id, + 0); } } } @@ -810,13 +825,19 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) * trustworthy. */ (void)my_apparent_addr; - if (connection_or_set_state_open(conn)<0) + if (connection_or_set_state_open(conn)<0) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got good NETINFO cell from %s:%d; but " + "was unable to make the OR connection become open.", + safe_str_client(conn->_base.address), + conn->_base.port); connection_mark_for_close(TO_CONN(conn)); - else + } else { log_info(LD_OR, "Got good NETINFO cell from %s:%d; OR connection is now " - "open, using protocol version %d", + "open, using protocol version %d. Its ID digest is %s", safe_str_client(conn->_base.address), - conn->_base.port, (int)conn->link_proto); + conn->_base.port, (int)conn->link_proto, + hex_str(conn->identity_digest, DIGEST_LEN)); + } assert_connection_ok(TO_CONN(conn),time(NULL)); } @@ -930,6 +951,8 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) conn->handshake_state->authenticated_peer_id) < 0) ERR("Problem setting or checking peer id"); + log_info(LD_OR, "Got some good certifcates from %s:%d: Authenticated it.", + conn->_base.address, conn->_base.port); conn->handshake_state->id_cert = id_cert; id_cert = NULL; @@ -945,6 +968,10 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) if (! tor_tls_cert_is_valid(id_cert, id_cert, 1)) ERR("The ID certificate was not valid"); + + log_info(LD_OR, "Got some good certifcates from %s:%d: " + "Waiting for AUTHENTICATE.", + conn->_base.address, conn->_base.port); /* XXXX check more stuff? */ id_cert = auth_cert = NULL; @@ -1013,6 +1040,10 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) conn->handshake_state->received_auth_challenge = 1; if (use_type && public_server_mode(get_options())) { + log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Sending " + "authentication", + conn->_base.address, conn->_base.port); + if (connection_or_send_authenticate_cell(conn, use_type) < 0) { log_warn(LD_OR, "Couldn't send authenticate cell"); connection_mark_for_close(TO_CONN(conn)); @@ -1023,8 +1054,10 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) connection_mark_for_close(TO_CONN(conn)); return; } + } else { + log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Not authenticating", + conn->_base.address, conn->_base.port); } - #undef ERR } @@ -1150,6 +1183,9 @@ command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn) conn->_base.port, (const char*)conn->handshake_state->authenticated_peer_id, 0); + + log_info(LD_OR, "Got an AUTHENTICATE cell from %s:%d: Looks good.", + conn->_base.address, conn->_base.port); } #undef ERR -- cgit v1.2.3 From ce102f7a59eb286b18d5f7522467aa152bff7e82 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 28 Sep 2011 13:19:55 -0400 Subject: Make more safe_str usage happen for new logs in command.c --- src/or/command.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/or/command.c b/src/or/command.c index ca248e66b..91486c14b 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -859,7 +859,7 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) do { \ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ "Received a bad CERT cell from %s:%d: %s", \ - conn->_base.address, conn->_base.port, (s)); \ + safe_str(conn->_base.address), conn->_base.port, (s)); \ connection_mark_for_close(TO_CONN(conn)); \ goto err; \ } while (0) @@ -906,7 +906,7 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) if (!cert) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received undecodable certificate in CERT cell from %s:%d", - conn->_base.address, conn->_base.port); + safe_str(conn->_base.address), conn->_base.port); } else { if (cert_type == OR_CERT_TYPE_TLS_LINK && !link_cert) link_cert = cert; @@ -952,7 +952,7 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) ERR("Problem setting or checking peer id"); log_info(LD_OR, "Got some good certifcates from %s:%d: Authenticated it.", - conn->_base.address, conn->_base.port); + safe_str(conn->_base.address), conn->_base.port); conn->handshake_state->id_cert = id_cert; id_cert = NULL; @@ -971,7 +971,7 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) log_info(LD_OR, "Got some good certifcates from %s:%d: " "Waiting for AUTHENTICATE.", - conn->_base.address, conn->_base.port); + safe_str(conn->_base.address), conn->_base.port); /* XXXX check more stuff? */ id_cert = auth_cert = NULL; @@ -1002,7 +1002,7 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) do { \ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ "Received a bad AUTH_CHALLENGE cell from %s:%d: %s", \ - conn->_base.address, conn->_base.port, (s)); \ + safe_str(conn->_base.address), conn->_base.port, (s)); \ connection_mark_for_close(TO_CONN(conn)); \ return; \ } while (0) @@ -1042,7 +1042,7 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) if (use_type && public_server_mode(get_options())) { log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Sending " "authentication", - conn->_base.address, conn->_base.port); + safe_str(conn->_base.address), conn->_base.port); if (connection_or_send_authenticate_cell(conn, use_type) < 0) { log_warn(LD_OR, "Couldn't send authenticate cell"); @@ -1056,7 +1056,7 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) } } else { log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Not authenticating", - conn->_base.address, conn->_base.port); + safe_str(conn->_base.address), conn->_base.port); } #undef ERR } @@ -1080,7 +1080,7 @@ command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn) do { \ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ "Received a bad AUTHETNICATE cell from %s:%d: %s", \ - conn->_base.address, conn->_base.port, (s)); \ + safe_str(conn->_base.address), conn->_base.port, (s)); \ connection_mark_for_close(TO_CONN(conn)); \ return; \ } while (0) @@ -1185,7 +1185,7 @@ command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn) 0); log_info(LD_OR, "Got an AUTHENTICATE cell from %s:%d: Looks good.", - conn->_base.address, conn->_base.port); + safe_str(conn->_base.address), conn->_base.port); } #undef ERR -- cgit v1.2.3 From 40f0d111c2263b44d30d47a292b3bb9ef3a01a08 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 5 Oct 2011 10:25:42 -0400 Subject: Fix some more issues wrt tor_cert_new found by asn --- src/common/tortls.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 206ac3be4..247e9eec0 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -706,9 +706,13 @@ tor_cert_new(X509 *x509_cert) tor_cert_t *cert; EVP_PKEY *pkey; RSA *rsa; - int length = i2d_X509(x509_cert, NULL), length2; + 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); @@ -766,7 +770,6 @@ tor_cert_decode(const uint8_t *certificate, size_t certificate_len) } newcert = tor_cert_new(x509); if (!newcert) { - X509_free(x509); return NULL; } if (newcert->encoded_len != certificate_len || @@ -1118,6 +1121,8 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime) 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); -- cgit v1.2.3 From e56d7a3809611e85b48474f27b3feb461e82e109 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 5 Oct 2011 10:33:39 -0400 Subject: Give tor_cert_get_id_digests() fail-fast behavior Right now we can take the digests only of an RSA key, and only expect to take the digests of an RSA key. The old tor_cert_get_id_digests() would return a good set of digests for an RSA key, and an all-zero one for a non-RSA key. This behavior is too error-prone: it carries the risk that we will someday check two non-RSA keys for equality and conclude that they must be equal because they both have the same (zero) "digest". Instead, let's have tor_cert_get_id_digests() return NULL for keys we can't handle, and make its callers explicitly test for NULL. --- src/common/tortls.c | 10 ++++++++-- src/or/command.c | 9 ++++++++- src/or/connection_or.c | 11 ++++++++--- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 247e9eec0..9600464ad 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -102,6 +102,7 @@ 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; }; @@ -734,6 +735,7 @@ tor_cert_new(X509 *x509_cert) (rsa = EVP_PKEY_get1_RSA(pkey))) { crypto_pk_env_t *pk = _crypto_new_pk_env_rsa(rsa); crypto_pk_get_all_digests(pk, &cert->pkey_digests); + cert->pkey_digests_set = 1; crypto_free_pk_env(pk); EVP_PKEY_free(pkey); } @@ -794,11 +796,15 @@ tor_cert_get_der(const tor_cert_t *cert, *size_out = cert->encoded_len; } -/** Return a set of digests for the public key in cert. */ +/** Return a set of digests for the public key in cert, 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) { - return &cert->pkey_digests; + if (cert->pkey_digests_set) + return &cert->pkey_digests; + else + return NULL; } /** Return a set of digests for the public key in cert. */ diff --git a/src/or/command.c b/src/or/command.c index 91486c14b..c1e2f5e8e 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -939,8 +939,12 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) conn->handshake_state->authenticated = 1; { - crypto_pk_env_t *identity_rcvd = tor_tls_cert_get_key(id_cert); const digests_t *id_digests = tor_cert_get_id_digests(id_cert); + crypto_pk_env_t *identity_rcvd; + if (!id_digests) + ERR("Couldn't compute digests for key in ID cert"); + + identity_rcvd = tor_tls_cert_get_key(id_cert); memcpy(conn->handshake_state->authenticated_peer_id, id_digests->d[DIGEST_SHA1], DIGEST_LEN); connection_or_set_circid_type(conn, identity_rcvd); @@ -1172,6 +1176,9 @@ command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn) const digests_t *id_digests = tor_cert_get_id_digests(conn->handshake_state->id_cert); + /* This must exist; we checked key type when reading the cert. */ + tor_assert(id_digests); + memcpy(conn->handshake_state->authenticated_peer_id, id_digests->d[DIGEST_SHA1], DIGEST_LEN); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 7cdea8219..a5b965b8d 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -2060,12 +2060,17 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, { const tor_cert_t *id_cert=NULL, *link_cert=NULL; + const digests_t *my_digests, *their_digests; const uint8_t *my_id, *their_id, *client_id, *server_id; if (tor_tls_get_my_certs(0, &link_cert, &id_cert)) return -1; - my_id = (uint8_t*)tor_cert_get_id_digests(id_cert)->d[DIGEST_SHA256]; - their_id = (uint8_t*) - tor_cert_get_id_digests(conn->handshake_state->id_cert)->d[DIGEST_SHA256]; + my_digests = tor_cert_get_id_digests(id_cert); + their_digests = tor_cert_get_id_digests(conn->handshake_state->id_cert); + tor_assert(my_digests); + tor_assert(their_digests); + my_id = (uint8_t*)my_digests->d[DIGEST_SHA256]; + their_id = (uint8_t*)their_digests->d[DIGEST_SHA256]; + client_id = server ? their_id : my_id; server_id = server ? my_id : their_id; -- cgit v1.2.3 From d79ff2ce94ac1e0e4938517403f29c4e9aaf799c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 5 Oct 2011 10:44:22 -0400 Subject: spec conformance: allow only one cert of each type --- src/or/command.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/or/command.c b/src/or/command.c index c1e2f5e8e..3bd6dd7fd 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -908,14 +908,27 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) "Received undecodable certificate in CERT cell from %s:%d", safe_str(conn->_base.address), conn->_base.port); } else { - if (cert_type == OR_CERT_TYPE_TLS_LINK && !link_cert) + if (cert_type == OR_CERT_TYPE_TLS_LINK) { + if (link_cert) { + tor_cert_free(cert); + ERR("Too many TLS_LINK certificates"); + } link_cert = cert; - else if (cert_type == OR_CERT_TYPE_ID_1024 && !id_cert) + } else if (cert_type == OR_CERT_TYPE_ID_1024) { + if (id_cert) { + tor_cert_free(cert); + ERR("Too many ID_1024 certificates"); + } id_cert = cert; - else if (cert_type == OR_CERT_TYPE_AUTH_1024 && !auth_cert) + } else if (cert_type == OR_CERT_TYPE_AUTH_1024) { + if (auth_cert) { + tor_cert_free(cert); + ERR("Too many AUTH_1024 certificates"); + } auth_cert = cert; - else + } else { tor_cert_free(cert); + } } } ptr += 3 + cert_len; -- cgit v1.2.3 From 059d3d06132048135bd45a1ecf23a62731b3a8cc Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 6 Oct 2011 14:41:02 -0400 Subject: Remove auth_challenge field from or_handshake_state_t We didn't need to record this value; it was already recorded implicitly while computing cell digests for later examination in the authenticate cells. --- src/or/command.c | 3 --- src/or/or.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/or/command.c b/src/or/command.c index 3bd6dd7fd..7efd18fce 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -1043,9 +1043,6 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types) ERR("It looks truncated"); - memcpy(conn->handshake_state->auth_challenge, cell->payload, - OR_AUTH_CHALLENGE_LEN); - /* Now see if there is an authentication type we can use */ cp=cell->payload+OR_AUTH_CHALLENGE_LEN+2; for (i=0; i < n_types; ++i, cp += 2) { diff --git a/src/or/or.h b/src/or/or.h index aa12e31d6..7268ae6f0 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1162,9 +1162,6 @@ typedef struct or_handshake_state_t { * on this connection. */ uint8_t authenticated_peer_id[DIGEST_LEN]; - /** DOCDOC */ - uint8_t auth_challenge[OR_AUTH_CHALLENGE_LEN]; - /** Digests of the cells that we have sent or received as part of a V3 * handshake. Used for making and checking AUTHENTICATE cells. * -- cgit v1.2.3 From 1bd65680bdfcd46e1c96e71e3912cbdef4fc158a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 6 Oct 2011 14:58:59 -0400 Subject: Add more log statements for protocol/internal failures --- src/or/command.c | 36 ++++++++++++++++++++++++++++++------ src/or/connection_or.c | 17 +++++++++++------ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/or/command.c b/src/or/command.c index 7efd18fce..8cf6c4640 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -149,10 +149,15 @@ command_process_cell(cell_t *cell, or_connection_t *conn) #endif /* Reject all but VERSIONS and NETINFO when handshaking. */ + /* (VERSIONS should actually be impossible; it's variable-length.) */ if (handshaking && cell->command != CELL_VERSIONS && - cell->command != CELL_NETINFO) + cell->command != CELL_NETINFO) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received unexpected cell command %d in state %s; ignoring it.", + (int)cell->command, + conn_state_to_string(CONN_TYPE_OR,conn->_base.state)); return; - /* XXXX VERSIONS should be impossible; it's variable-length. */ + } if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) or_handshake_state_record_cell(conn->handshake_state, cell, 1); @@ -239,18 +244,37 @@ command_process_var_cell(var_cell_t *cell, or_connection_t *conn) /* fall through */ case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: - if (cell->command != CELL_VERSIONS) - return; /*XXXX023 log*/ + if (cell->command != CELL_VERSIONS) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received a non-VERSIONS cell with command %d in state %s; " + "ignoring it.", + (int)cell->command, + conn_state_to_string(CONN_TYPE_OR,conn->_base.state)); + return; + } break; case OR_CONN_STATE_OR_HANDSHAKING_V3: if (cell->command != CELL_AUTHENTICATE) or_handshake_state_record_var_cell(conn->handshake_state, cell, 1); break; /* Everything is allowed */ case OR_CONN_STATE_OPEN: - if (conn->link_proto < 3) + if (conn->link_proto < 3) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received a variable-length cell with command %d in state %s " + "with link protocol %d; ignoring it.", + (int)cell->command, + conn_state_to_string(CONN_TYPE_OR,conn->_base.state), + (int)conn->link_proto); return; + } + break; default: - /*XXXX023 log */ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received var-length cell with command %d in unexpected state " + "%s [%d]; ignoring it.", + (int)cell->command, + conn_state_to_string(CONN_TYPE_OR,conn->_base.state), + (int)conn->_base.state); return; } diff --git a/src/or/connection_or.c b/src/or/connection_or.c index a5b965b8d..b4c1fd0e6 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -2176,15 +2176,20 @@ connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) int cell_maxlen; /* XXXX make sure we're actually supposed to send this! */ - if (!pk) - return -1;/*XXXX log*/ - if (authtype != AUTHTYPE_RSA_SHA256_TLSSECRET) - return -1;/*XXXX log*/ + if (!pk) { + log_warn(LD_BUG, "Unable to compute authenticate cell: no client auth key"); + return -1; + } + if (authtype != AUTHTYPE_RSA_SHA256_TLSSECRET) { + log_warn(LD_BUG, "Tried to send authenticate cell with unknown " + "authentication type %d", authtype); + return -1; + } cell_maxlen = 4 + /* overhead */ V3_AUTH_BODY_LEN + /* Authentication body */ crypto_pk_keysize(pk) + /* Max signature length */ - 16 /* just in case XXXX */ ; + 16 /* add a few extra bytes just in case. */; cell = var_cell_new(cell_maxlen); cell->command = CELL_AUTHENTICATE; @@ -2197,7 +2202,7 @@ connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) pk, 0 /* not server */); if (authlen < 0) { - /* XXXX log */ + log_warn(LD_BUG, "Unable to compute authenticate cell!"); var_cell_free(cell); return -1; } -- cgit v1.2.3 From 66200320ffe584893acaf4ea5a55851039e2e92c Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Tue, 11 Oct 2011 03:06:41 +0200 Subject: Fix a few 64bit compiler warnings --- src/or/connection_or.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index b4c1fd0e6..42fcea275 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1951,7 +1951,7 @@ connection_or_send_cert_cell(or_connection_t *conn) size_t link_len, id_len; var_cell_t *cell; size_t cell_len; - int pos; + ssize_t pos; int server_mode; tor_assert(conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3); @@ -2037,6 +2037,7 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn) * * If server is false and signing_key is provided, calculate the * entire authenticator, signed with signing_key. + * DOCDOC return value */ int connection_or_compute_authenticate_cell_body(or_connection_t *conn, @@ -2128,7 +2129,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN); if (server) - return ptr-out; + return V3_AUTH_FIXED_PART_LEN; // ptr-out /* Time: 8 octets. */ { @@ -2147,7 +2148,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, tor_assert(ptr - out == V3_AUTH_BODY_LEN); if (!signing_key) - return ptr - out; + return V3_AUTH_BODY_LEN; // ptr - out { int siglen; @@ -2161,7 +2162,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, ptr += siglen; tor_assert(ptr <= out+outlen); - return ptr - out; + return (int)(ptr - out); } } @@ -2173,7 +2174,7 @@ connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) var_cell_t *cell; crypto_pk_env_t *pk = tor_tls_get_my_client_auth_key(); int authlen; - int cell_maxlen; + size_t cell_maxlen; /* XXXX make sure we're actually supposed to send this! */ if (!pk) { -- cgit v1.2.3 From 35fe4825fc7da01367f37bc73ea4fc5c9d61ed2d Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Tue, 11 Oct 2011 04:24:33 +0200 Subject: Quiet two notices, and spelling mistake cleanup --- src/common/tortls.c | 2 +- src/or/command.c | 4 ++-- src/or/connection_or.c | 4 ++-- src/or/or.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 9600464ad..b393bfd4a 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -936,7 +936,7 @@ tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert) return result; } -/** Check wither cert is well-formed, currently live, and correctly +/** Check whether cert is well-formed, currently live, and correctly * signed by the public key in signing_cert. If check_rsa_1024, * 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 diff --git a/src/or/command.c b/src/or/command.c index 8cf6c4640..738bf35d9 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -992,7 +992,7 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) conn->handshake_state->authenticated_peer_id) < 0) ERR("Problem setting or checking peer id"); - log_info(LD_OR, "Got some good certifcates from %s:%d: Authenticated it.", + log_info(LD_OR, "Got some good certificates from %s:%d: Authenticated it.", safe_str(conn->_base.address), conn->_base.port); conn->handshake_state->id_cert = id_cert; @@ -1010,7 +1010,7 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn) ERR("The ID certificate was not valid"); - log_info(LD_OR, "Got some good certifcates from %s:%d: " + log_info(LD_OR, "Got some good certificates from %s:%d: " "Waiting for AUTHENTICATE.", safe_str(conn->_base.address), conn->_base.port); /* XXXX check more stuff? */ diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 42fcea275..bcae075c5 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1181,7 +1181,7 @@ connection_tls_continue_handshake(or_connection_t *conn) if (!tor_tls_is_server(conn->tls)) { if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) { if (tor_tls_received_v3_certificate(conn->tls)) { - log_notice(LD_OR, "Client got a v3 cert! Moving on to v3 " + log_info(LD_OR, "Client got a v3 cert! Moving on to v3 " "handshake."); return connection_or_launch_v3_or_handshake(conn); } else { @@ -1241,7 +1241,7 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event, if (!tor_tls_is_server(conn->tls)) { if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) { if (tor_tls_received_v3_certificate(conn->tls)) { - log_notice(LD_OR, "Client got a v3 cert!"); + log_info(LD_OR, "Client got a v3 cert!"); if (connection_or_launch_v3_or_handshake(conn) < 0) connection_mark_for_close(TO_CONN(conn)); return; diff --git a/src/or/or.h b/src/or/or.h index 7268ae6f0..6969a9cff 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -301,7 +301,7 @@ typedef enum { * haven't yet negotiated link protocol versions, done a V3 handshake, and * sent a netinfo cell. */ #define OR_CONN_STATE_OR_HANDSHAKING_V3 7 -/** State for an OR connection:: Ready to send/receive cells. */ +/** State for an OR connection: Ready to send/receive cells. */ #define OR_CONN_STATE_OPEN 8 #define _OR_CONN_STATE_MAX 8 -- cgit v1.2.3 From cc07b5a12936f0cd8c5791c94f66c20e1929b0ab Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 10 Oct 2011 23:06:25 -0400 Subject: Changes file for prop176 branch --- changes/prop176 | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 changes/prop176 diff --git a/changes/prop176 b/changes/prop176 new file mode 100644 index 000000000..faedc1adf --- /dev/null +++ b/changes/prop176 @@ -0,0 +1,8 @@ + o Major features + - Implement a new handshake protocol for authenticating Tors to + each other over TLS. It should be more resistant to fingerprinting + than previous protocols, and should require less TLS hacking for + future Tor implementations. Implements Proposal 185. + - Allow variable-length padding cells to disguise the length of + Tor's TLS records. Implements part of Proposal 184. + -- cgit v1.2.3 From 8af0cfc10d33186adaa114f215e8fa69af91a84a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 10 Oct 2011 23:12:29 -0400 Subject: Add some points to make it easy to turn off v3 support --- src/common/tortls.c | 9 +++++++++ src/or/command.c | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/src/common/tortls.c b/src/common/tortls.c index b393bfd4a..9a3c02b5b 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -1095,7 +1095,11 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime) 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 for use with TLS. */ if (!(rsa = crypto_new_pk_env())) @@ -2183,6 +2187,10 @@ tor_tls_used_v1_handshake(tor_tls_t *tls) 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; @@ -2206,6 +2214,7 @@ dn_indicates_v3_cert(X509_NAME *name) r = fast_memneq(s + len - 4, ".net", 4); OPENSSL_free(s); return r; +#endif } /** Return true iff the peer certificate we're received on tls diff --git a/src/or/command.c b/src/or/command.c index 738bf35d9..a51115631 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -689,6 +689,13 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn) send_chall ? " AUTH_CHALLENGE" : "", send_netinfo ? " NETINFO" : ""); +#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE + if (1) { + connection_mark_for_close(TO_CONN(conn)); + return; + } +#endif + if (send_versions) { if (connection_or_send_versions(conn, 1) < 0) { log_warn(LD_OR, "Couldn't send versions cell"); -- cgit v1.2.3