Implement cert/auth cell reading

This commit is contained in:
Nick Mathewson 2011-09-13 16:24:49 -04:00
parent 81024f43ec
commit 6c7f28454e
3 changed files with 338 additions and 17 deletions

View File

@ -660,3 +660,281 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn)
assert_connection_ok(TO_CONN(conn),time(NULL)); 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);
}

View File

@ -1761,10 +1761,6 @@ connection_or_send_netinfo(or_connection_t *conn)
return 0; 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 /** Send a CERT cell on the connection <b>conn</b>. Return 0 on success, -1
* on failure. */ * on failure. */
int int
@ -1846,23 +1842,17 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn)
return 0; 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 /** 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 * to authenticate itself on a v3 handshake for <b>conn</b>. Write it to the
* <b>outlen</b>-byte buffer at <b>out</b>. * <b>outlen</b>-byte buffer at <b>out</b>.
* *
* If <b>server</b> is true, only calculate the first * 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 * determined by the rest of the handshake, and which match the provided value
* exactly. * exactly.
* *
* If <b>server</b> is false and <b>signing_key</b> is NULL, calculate the * 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. * 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 * 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 */ /* assert state is reasonable XXXX */
if (outlen < V3_HS_AUTH_FIXED_PART_LEN || if (outlen < V3_AUTH_FIXED_PART_LEN ||
(!server && outlen < V3_HS_AUTH_BODY_LEN)) (!server && outlen < V3_AUTH_BODY_LEN))
return -1; return -1;
ptr = out; ptr = out;
@ -1950,7 +1940,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
tor_tls_get_tlssecrets(conn->tls, ptr); tor_tls_get_tlssecrets(conn->tls, ptr);
ptr += 32; ptr += 32;
tor_assert(ptr - out == V3_HS_AUTH_FIXED_PART_LEN); tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN);
if (server) if (server)
return ptr-out; return ptr-out;
@ -1969,7 +1959,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
crypto_rand((char*)ptr, 16); crypto_rand((char*)ptr, 16);
ptr += 16; ptr += 16;
tor_assert(ptr - out == V3_HS_AUTH_BODY_LEN); tor_assert(ptr - out == V3_AUTH_BODY_LEN);
if (!signing_key) if (!signing_key)
return ptr - out; return ptr - out;
@ -2004,7 +1994,7 @@ connection_or_send_authenticate_cell(or_connection_t *conn)
if (!pk) if (!pk)
return -1;/*XXXX log*/ return -1;/*XXXX log*/
cell_maxlen = 4 + /* overhead */ cell_maxlen = 4 + /* overhead */
V3_HS_AUTH_BODY_LEN + /* Authentication body */ V3_AUTH_BODY_LEN + /* Authentication body */
crypto_pk_keysize(pk) + /* Max signature length */ crypto_pk_keysize(pk) + /* Max signature length */
16 /* just in case XXXX */ ; 16 /* just in case XXXX */ ;

View File

@ -1087,6 +1087,43 @@ typedef struct listener_connection_t {
/** Minimum length of the random part of an AUTH_CHALLENGE cell. */ /** Minimum length of the random part of an AUTH_CHALLENGE cell. */
#define OR_AUTH_CHALLENGE_LEN 32 #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 /** Stores flags and information related to the portion of a v2/v3 Tor OR
* connection handshake that happens after the TLS handshake is finished. * 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; unsigned int started_here : 1;
/** True iff we have received and processed a VERSIONS cell. */ /** True iff we have received and processed a VERSIONS cell. */
unsigned int received_versions : 1; 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 /** Digests of the cells that we have sent or received as part of a V3
* handshake. Used for making and checking AUTHENTICATE cells. * handshake. Used for making and checking AUTHENTICATE cells.
@ -1108,6 +1151,16 @@ typedef struct or_handshake_state_t {
crypto_digest_env_t *digest_received; 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; } or_handshake_state_t;
/** Subtype of connection_t for an "OR connection" -- that is, one that speaks /** Subtype of connection_t for an "OR connection" -- that is, one that speaks