diff options
author | Nick Mathewson <nickm@torproject.org> | 2011-09-13 16:24:49 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2011-10-10 23:14:17 -0400 |
commit | 6c7f28454e80da733e3bfb4f71101faf09b7ac24 (patch) | |
tree | f4f5f65a58221ebfdf620c81a7a2829c341c90c8 /src/or/command.c | |
parent | 81024f43ec3a3ab32683764cb925606bfcb603d7 (diff) | |
download | tor-6c7f28454e80da733e3bfb4f71101faf09b7ac24.tar.gz tor-6c7f28454e80da733e3bfb4f71101faf09b7ac24.zip |
Implement cert/auth cell reading
Diffstat (limited to 'src/or/command.c')
-rw-r--r-- | src/or/command.c | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/src/or/command.c b/src/or/command.c index 72d8cd7cf6..aad971fc91 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); +} |