Refactor link handshake cell type implementations to use trunnel

Unit tests still pass.
This commit is contained in:
Nick Mathewson 2014-10-17 17:06:31 -04:00
parent 4d1a0ece5c
commit b29c1530c7
2 changed files with 155 additions and 136 deletions

View File

@ -24,6 +24,7 @@
#include "connection.h" #include "connection.h"
#include "connection_or.h" #include "connection_or.h"
#include "control.h" #include "control.h"
#include "link_handshake.h"
#include "relay.h" #include "relay.h"
#include "router.h" #include "router.h"
#include "routerlist.h" #include "routerlist.h"
@ -1740,13 +1741,14 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
STATIC void STATIC void
channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
{ {
tor_x509_cert_t *link_cert = NULL; #define MAX_CERT_TYPE_WANTED OR_CERT_TYPE_AUTH_1024
tor_x509_cert_t *id_cert = NULL; tor_x509_cert_t *certs[MAX_CERT_TYPE_WANTED + 1];
tor_x509_cert_t *auth_cert = NULL;
uint8_t *ptr;
int n_certs, i; int n_certs, i;
certs_cell_t *cc = NULL;
int send_netinfo = 0; int send_netinfo = 0;
memset(certs, 0, sizeof(certs));
tor_assert(cell); tor_assert(cell);
tor_assert(chan); tor_assert(chan);
tor_assert(chan->conn); tor_assert(chan->conn);
@ -1776,62 +1778,40 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
if (cell->circ_id) if (cell->circ_id)
ERR("It had a nonzero circuit ID"); ERR("It had a nonzero circuit ID");
n_certs = cell->payload[0]; if (certs_cell_parse(&cc, cell->payload, cell->payload_len) < 0)
ptr = cell->payload + 1; ERR("It couldn't be parsed.");
n_certs = cc->n_certs;
for (i = 0; i < n_certs; ++i) { for (i = 0; i < n_certs; ++i) {
uint8_t cert_type; certs_cell_cert_t *c = certs_cell_get_certs(cc, i);
uint16_t cert_len;
if (cell->payload_len < 3) uint16_t cert_type = c->cert_type;
goto truncated; uint16_t cert_len = c->cert_len;
if (ptr > cell->payload + cell->payload_len - 3) { uint8_t *cert_body = certs_cell_cert_getarray_body(c);
goto truncated;
} if (cert_type > MAX_CERT_TYPE_WANTED)
cert_type = *ptr; continue;
cert_len = ntohs(get_uint16(ptr+1));
if (cell->payload_len < 3 + cert_len) tor_x509_cert_t *cert = tor_x509_cert_decode(cert_body, cert_len);
goto truncated;
if (ptr > cell->payload + cell->payload_len - cert_len - 3) {
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_x509_cert_t *cert = tor_x509_cert_decode(ptr + 3, cert_len);
if (!cert) { if (!cert) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Received undecodable certificate in CERTS cell from %s:%d", "Received undecodable certificate in CERTS cell from %s:%d",
safe_str(chan->conn->base_.address), safe_str(chan->conn->base_.address),
chan->conn->base_.port); chan->conn->base_.port);
} else { } else {
if (cert_type == OR_CERT_TYPE_TLS_LINK) { if (certs[cert_type]) {
if (link_cert) {
tor_x509_cert_free(cert); tor_x509_cert_free(cert);
ERR("Too many TLS_LINK certificates"); ERR("Duplicate x509 certificate");
}
link_cert = cert;
} else if (cert_type == OR_CERT_TYPE_ID_1024) {
if (id_cert) {
tor_x509_cert_free(cert);
ERR("Too many ID_1024 certificates");
}
id_cert = cert;
} else if (cert_type == OR_CERT_TYPE_AUTH_1024) {
if (auth_cert) {
tor_x509_cert_free(cert);
ERR("Too many AUTH_1024 certificates");
}
auth_cert = cert;
} else { } else {
tor_x509_cert_free(cert); certs[cert_type] = cert;
} }
} }
} }
ptr += 3 + cert_len;
continue;
truncated: tor_x509_cert_t *id_cert = certs[OR_CERT_TYPE_ID_1024];
ERR("It ends in the middle of a certificate"); tor_x509_cert_t *auth_cert = certs[OR_CERT_TYPE_AUTH_1024];
} tor_x509_cert_t *link_cert = certs[OR_CERT_TYPE_TLS_LINK];
if (chan->conn->handshake_state->started_here) { if (chan->conn->handshake_state->started_here) {
int severity; int severity;
@ -1881,7 +1861,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
safe_str(chan->conn->base_.address), chan->conn->base_.port); safe_str(chan->conn->base_.address), chan->conn->base_.port);
chan->conn->handshake_state->id_cert = id_cert; chan->conn->handshake_state->id_cert = id_cert;
id_cert = NULL; certs[OR_CERT_TYPE_ID_1024] = NULL;
if (!public_server_mode(get_options())) { if (!public_server_mode(get_options())) {
/* If we initiated the connection and we are not a public server, we /* If we initiated the connection and we are not a public server, we
@ -1908,7 +1888,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
chan->conn->handshake_state->id_cert = id_cert; chan->conn->handshake_state->id_cert = id_cert;
chan->conn->handshake_state->auth_cert = auth_cert; chan->conn->handshake_state->auth_cert = auth_cert;
id_cert = auth_cert = NULL; certs[OR_CERT_TYPE_ID_1024] = certs[OR_CERT_TYPE_AUTH_1024] = NULL;
} }
chan->conn->handshake_state->received_certs_cell = 1; chan->conn->handshake_state->received_certs_cell = 1;
@ -1922,9 +1902,10 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
} }
err: err:
tor_x509_cert_free(id_cert); for (unsigned i = 0; i < ARRAY_LENGTH(certs); ++i) {
tor_x509_cert_free(link_cert); tor_x509_cert_free(certs[i]);
tor_x509_cert_free(auth_cert); }
certs_cell_free(cc);
#undef ERR #undef ERR
} }
@ -1943,7 +1924,7 @@ STATIC void
channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
{ {
int n_types, i, use_type = -1; int n_types, i, use_type = -1;
uint8_t *cp; auth_challenge_cell_t *ac = NULL;
tor_assert(cell); tor_assert(cell);
tor_assert(chan); tor_assert(chan);
@ -1956,7 +1937,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
safe_str(chan->conn->base_.address), \ safe_str(chan->conn->base_.address), \
chan->conn->base_.port, (s)); \ chan->conn->base_.port, (s)); \
connection_or_close_for_error(chan->conn, 0); \ connection_or_close_for_error(chan->conn, 0); \
return; \ goto done; \
} while (0) } while (0)
if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3) if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
@ -1969,19 +1950,17 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
ERR("We already received one"); ERR("We already received one");
if (!(chan->conn->handshake_state->received_certs_cell)) if (!(chan->conn->handshake_state->received_certs_cell))
ERR("We haven't gotten a CERTS cell yet"); 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) if (cell->circ_id)
ERR("It had a nonzero circuit ID"); ERR("It had a nonzero circuit ID");
n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN)); if (auth_challenge_cell_parse(&ac, cell->payload, cell->payload_len) < 0)
if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types) ERR("It was not well-formed.");
ERR("It looks truncated");
n_types = ac->n_methods;
/* Now see if there is an authentication type we can use */ /* 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) {
for (i = 0; i < n_types; ++i, cp += 2) { uint16_t authtype = auth_challenge_cell_get_methods(ac, i);
uint16_t authtype = ntohs(get_uint16(cp));
if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET) if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET)
use_type = authtype; use_type = authtype;
} }
@ -1992,7 +1971,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
/* If we're not a public server then we don't want to authenticate on a /* If we're not a public server then we don't want to authenticate on a
connection we originated, and we already sent a NETINFO cell when we connection we originated, and we already sent a NETINFO cell when we
got the CERTS cell. We have nothing more to do. */ got the CERTS cell. We have nothing more to do. */
return; goto done;
} }
if (use_type >= 0) { if (use_type >= 0) {
@ -2006,7 +1985,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
log_warn(LD_OR, log_warn(LD_OR,
"Couldn't send authenticate cell"); "Couldn't send authenticate cell");
connection_or_close_for_error(chan->conn, 0); connection_or_close_for_error(chan->conn, 0);
return; goto done;
} }
} else { } else {
log_info(LD_OR, log_info(LD_OR,
@ -2019,9 +1998,12 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
if (connection_or_send_netinfo(chan->conn) < 0) { if (connection_or_send_netinfo(chan->conn) < 0) {
log_warn(LD_OR, "Couldn't send netinfo cell"); log_warn(LD_OR, "Couldn't send netinfo cell");
connection_or_close_for_error(chan->conn, 0); connection_or_close_for_error(chan->conn, 0);
return; goto done;
} }
done:
auth_challenge_cell_free(ac);
#undef ERR #undef ERR
} }
@ -2038,7 +2020,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
STATIC void STATIC void
channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
{ {
uint8_t expected[V3_AUTH_FIXED_PART_LEN]; uint8_t expected[V3_AUTH_FIXED_PART_LEN+256];
const uint8_t *auth; const uint8_t *auth;
int authlen; int authlen;
@ -2094,11 +2076,13 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
if (authlen < V3_AUTH_BODY_LEN + 1) if (authlen < V3_AUTH_BODY_LEN + 1)
ERR("Authenticator was too short"); ERR("Authenticator was too short");
if (connection_or_compute_authenticate_cell_body( ssize_t bodylen =
chan->conn, expected, sizeof(expected), NULL, 1) < 0) connection_or_compute_authenticate_cell_body(
chan->conn, expected, sizeof(expected), NULL, 1);
if (bodylen < 0 || bodylen != V3_AUTH_FIXED_PART_LEN)
ERR("Couldn't compute expected AUTHENTICATE cell body"); ERR("Couldn't compute expected AUTHENTICATE cell body");
if (tor_memneq(expected, auth, sizeof(expected))) if (tor_memneq(expected, auth, bodylen))
ERR("Some field in the AUTHENTICATE cell body was not as expected"); ERR("Some field in the AUTHENTICATE cell body was not as expected");
{ {

View File

@ -30,6 +30,7 @@
#include "entrynodes.h" #include "entrynodes.h"
#include "geoip.h" #include "geoip.h"
#include "main.h" #include "main.h"
#include "link_handshake.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "nodelist.h" #include "nodelist.h"
#include "reasons.h" #include "reasons.h"
@ -2279,28 +2280,37 @@ connection_or_send_certs_cell(or_connection_t *conn)
int int
connection_or_send_auth_challenge_cell(or_connection_t *conn) connection_or_send_auth_challenge_cell(or_connection_t *conn)
{ {
var_cell_t *cell; var_cell_t *cell = NULL;
uint8_t *cp; int r = -1;
uint8_t challenge[OR_AUTH_CHALLENGE_LEN];
tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
if (! conn->handshake_state) if (! conn->handshake_state)
return -1; return -1;
if (crypto_rand((char*)challenge, OR_AUTH_CHALLENGE_LEN) < 0) auth_challenge_cell_t *ac = auth_challenge_cell_new();
return -1;
cell = var_cell_new(OR_AUTH_CHALLENGE_LEN + 4); if (crypto_rand((char*)ac->challenge, sizeof(ac->challenge)) < 0)
goto done;
auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET);
auth_challenge_cell_set_n_methods(ac,
auth_challenge_cell_getlen_methods(ac));
cell = var_cell_new(auth_challenge_cell_encoded_len(ac));
ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len,
ac);
if (len != cell->payload_len)
goto done;
cell->command = CELL_AUTH_CHALLENGE; 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); connection_or_write_var_cell_to_buf(cell, conn);
var_cell_free(cell); r = 0;
memwipe(challenge, 0, sizeof(challenge));
return 0; done:
var_cell_free(cell);
auth_challenge_cell_free(ac);
return r;
} }
/** 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
@ -2327,19 +2337,18 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
crypto_pk_t *signing_key, crypto_pk_t *signing_key,
int server) int server)
{ {
uint8_t *ptr; auth1_t *auth = NULL;
auth_ctx_t *ctx = auth_ctx_new();
int result;
/* assert state is reasonable XXXX */ /* assert state is reasonable XXXX */
if (outlen < V3_AUTH_FIXED_PART_LEN || ctx->is_ed = 0;
(!server && outlen < V3_AUTH_BODY_LEN))
return -1;
ptr = out; auth = auth1_new();
/* Type: 8 bytes. */ /* Type: 8 bytes. */
memcpy(ptr, "AUTH0001", 8); memcpy(auth1_getarray_type(auth), "AUTH0001", 8);
ptr += 8;
{ {
const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL; const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL;
@ -2359,12 +2368,10 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
server_id = server ? my_id : their_id; server_id = server ? my_id : their_id;
/* Client ID digest: 32 octets. */ /* Client ID digest: 32 octets. */
memcpy(ptr, client_id, 32); memcpy(auth->cid, client_id, 32);
ptr += 32;
/* Server ID digest: 32 octets. */ /* Server ID digest: 32 octets. */
memcpy(ptr, server_id, 32); memcpy(auth->sid, server_id, 32);
ptr += 32;
} }
{ {
@ -2378,12 +2385,10 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
} }
/* Server log digest : 32 octets */ /* Server log digest : 32 octets */
crypto_digest_get_digest(server_d, (char*)ptr, 32); crypto_digest_get_digest(server_d, (char*)auth->slog, 32);
ptr += 32;
/* Client log digest : 32 octets */ /* Client log digest : 32 octets */
crypto_digest_get_digest(client_d, (char*)ptr, 32); crypto_digest_get_digest(client_d, (char*)auth->clog, 32);
ptr += 32;
} }
{ {
@ -2396,49 +2401,79 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
freecert = tor_tls_get_peer_cert(conn->tls); freecert = tor_tls_get_peer_cert(conn->tls);
cert = freecert; cert = freecert;
} }
if (!cert) if (!cert) {
return -1; log_warn(LD_OR, "Unable to find cert when making AUTH1 data.");
memcpy(ptr, tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); goto err;
}
memcpy(auth->scert,
tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
if (freecert) if (freecert)
tor_x509_cert_free(freecert); tor_x509_cert_free(freecert);
ptr += 32;
} }
/* HMAC of clientrandom and serverrandom using master key : 32 octets */ /* HMAC of clientrandom and serverrandom using master key : 32 octets */
tor_tls_get_tlssecrets(conn->tls, ptr); tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets);
ptr += 32;
tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN);
if (server)
return V3_AUTH_FIXED_PART_LEN; // ptr-out
/* 8 octets were reserved for the current time, but we're trying to get out /* 8 octets were reserved for the current time, but we're trying to get out
* of the habit of sending time around willynilly. Fortunately, nothing * of the habit of sending time around willynilly. Fortunately, nothing
* checks it. That's followed by 16 bytes of nonce. */ * checks it. That's followed by 16 bytes of nonce. */
crypto_rand((char*)ptr, 24); crypto_rand((char*)auth->rand, 24);
ptr += 24;
tor_assert(ptr - out == V3_AUTH_BODY_LEN); ssize_t len;
if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) {
if (!signing_key) log_warn(LD_OR, "Unable to encode signed part of AUTH1 data.");
return V3_AUTH_BODY_LEN; // ptr - out goto err;
{
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 (int)(ptr - out);
} }
if (server) {
auth1_t *tmp = NULL;
ssize_t len2 = auth1_parse(&tmp, out, len, ctx);
if (!tmp) {
log_warn(LD_OR, "Unable to parse signed part of AUTH1 data.");
goto err;
}
result = (int) (tmp->end_of_fixed_part - out);
auth1_free(tmp);
if (len2 != len) {
log_warn(LD_OR, "Mismatched length when re-parsing AUTH1 data.");
goto err;
}
goto done;
}
if (signing_key) {
auth1_setlen_sig(auth, crypto_pk_keysize(signing_key));
char d[32];
crypto_digest256(d, (char*)out, len, DIGEST_SHA256);
int siglen = crypto_pk_private_sign(signing_key,
(char*)auth1_getarray_sig(auth),
auth1_getlen_sig(auth),
d, 32);
if (siglen < 0) {
log_warn(LD_OR, "Unable to sign AUTH1 data.");
return -1;
}
auth1_setlen_sig(auth, siglen);
len = auth1_encode(out, outlen, auth, ctx);
if (len < 0) {
log_warn(LD_OR, "Unable to encode signed AUTH1 data.");
goto err;
}
}
result = (int) len;
goto done;
err:
result = -1;
done:
auth1_free(auth);
auth_ctx_free(ctx);
return result;
} }
/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on /** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on