commit 6c7f28454e80da733e3bfb4f71101faf09b7ac24 Author: Nick Mathewson nickm@torproject.org Date: Tue Sep 13 16:24:49 2011 -0400
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 72d8cd7..aad971f 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 c72d89d..93b0b3a 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 <b>conn</b>. 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 <b>conn</b>. Write it to the * <b>outlen</b>-byte buffer at <b>out</b>. * * If <b>server</b> 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 <b>server</b> is false and <b>signing_key</b> 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 <b>server</b> is false and <b>signing_key</b> 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 ebf9ab5..a40598f 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
tor-commits@lists.torproject.org