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_or.h"
#include "control.h"
#include "link_handshake.h"
#include "relay.h"
#include "router.h"
#include "routerlist.h"
@ -1740,13 +1741,14 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
STATIC void
channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
{
tor_x509_cert_t *link_cert = NULL;
tor_x509_cert_t *id_cert = NULL;
tor_x509_cert_t *auth_cert = NULL;
uint8_t *ptr;
#define MAX_CERT_TYPE_WANTED OR_CERT_TYPE_AUTH_1024
tor_x509_cert_t *certs[MAX_CERT_TYPE_WANTED + 1];
int n_certs, i;
certs_cell_t *cc = NULL;
int send_netinfo = 0;
memset(certs, 0, sizeof(certs));
tor_assert(cell);
tor_assert(chan);
tor_assert(chan->conn);
@ -1776,63 +1778,41 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
if (cell->circ_id)
ERR("It had a nonzero circuit ID");
n_certs = cell->payload[0];
ptr = cell->payload + 1;
if (certs_cell_parse(&cc, cell->payload, cell->payload_len) < 0)
ERR("It couldn't be parsed.");
n_certs = cc->n_certs;
for (i = 0; i < n_certs; ++i) {
uint8_t cert_type;
uint16_t cert_len;
if (cell->payload_len < 3)
goto truncated;
if (ptr > cell->payload + cell->payload_len - 3) {
goto truncated;
}
cert_type = *ptr;
cert_len = ntohs(get_uint16(ptr+1));
if (cell->payload_len < 3 + 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) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Received undecodable certificate in CERTS cell from %s:%d",
safe_str(chan->conn->base_.address),
chan->conn->base_.port);
certs_cell_cert_t *c = certs_cell_get_certs(cc, i);
uint16_t cert_type = c->cert_type;
uint16_t cert_len = c->cert_len;
uint8_t *cert_body = certs_cell_cert_getarray_body(c);
if (cert_type > MAX_CERT_TYPE_WANTED)
continue;
tor_x509_cert_t *cert = tor_x509_cert_decode(cert_body, cert_len);
if (!cert) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Received undecodable certificate in CERTS cell from %s:%d",
safe_str(chan->conn->base_.address),
chan->conn->base_.port);
} else {
if (certs[cert_type]) {
tor_x509_cert_free(cert);
ERR("Duplicate x509 certificate");
} else {
if (cert_type == OR_CERT_TYPE_TLS_LINK) {
if (link_cert) {
tor_x509_cert_free(cert);
ERR("Too many TLS_LINK certificates");
}
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 {
tor_x509_cert_free(cert);
}
certs[cert_type] = cert;
}
}
ptr += 3 + cert_len;
continue;
truncated:
ERR("It ends in the middle of a certificate");
}
tor_x509_cert_t *id_cert = certs[OR_CERT_TYPE_ID_1024];
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) {
int severity;
if (! (id_cert && link_cert))
@ -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);
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 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->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;
@ -1922,9 +1902,10 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
}
err:
tor_x509_cert_free(id_cert);
tor_x509_cert_free(link_cert);
tor_x509_cert_free(auth_cert);
for (unsigned i = 0; i < ARRAY_LENGTH(certs); ++i) {
tor_x509_cert_free(certs[i]);
}
certs_cell_free(cc);
#undef ERR
}
@ -1943,7 +1924,7 @@ STATIC void
channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
{
int n_types, i, use_type = -1;
uint8_t *cp;
auth_challenge_cell_t *ac = NULL;
tor_assert(cell);
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), \
chan->conn->base_.port, (s)); \
connection_or_close_for_error(chan->conn, 0); \
return; \
goto done; \
} while (0)
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");
if (!(chan->conn->handshake_state->received_certs_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)
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");
if (auth_challenge_cell_parse(&ac, cell->payload, cell->payload_len) < 0)
ERR("It was not well-formed.");
n_types = ac->n_methods;
/* 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));
for (i = 0; i < n_types; ++i) {
uint16_t authtype = auth_challenge_cell_get_methods(ac, i);
if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET)
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
connection we originated, and we already sent a NETINFO cell when we
got the CERTS cell. We have nothing more to do. */
return;
goto done;
}
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,
"Couldn't send authenticate cell");
connection_or_close_for_error(chan->conn, 0);
return;
goto done;
}
} else {
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) {
log_warn(LD_OR, "Couldn't send netinfo cell");
connection_or_close_for_error(chan->conn, 0);
return;
goto done;
}
done:
auth_challenge_cell_free(ac);
#undef ERR
}
@ -2038,7 +2020,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
STATIC void
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;
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)
ERR("Authenticator was too short");
if (connection_or_compute_authenticate_cell_body(
chan->conn, expected, sizeof(expected), NULL, 1) < 0)
ssize_t bodylen =
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");
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");
{

View File

@ -30,6 +30,7 @@
#include "entrynodes.h"
#include "geoip.h"
#include "main.h"
#include "link_handshake.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "reasons.h"
@ -2279,28 +2280,37 @@ connection_or_send_certs_cell(or_connection_t *conn)
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];
var_cell_t *cell = NULL;
int r = -1;
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);
auth_challenge_cell_t *ac = auth_challenge_cell_new();
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;
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);
memwipe(challenge, 0, sizeof(challenge));
r = 0;
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
@ -2327,19 +2337,18 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
crypto_pk_t *signing_key,
int server)
{
uint8_t *ptr;
auth1_t *auth = NULL;
auth_ctx_t *ctx = auth_ctx_new();
int result;
/* assert state is reasonable XXXX */
if (outlen < V3_AUTH_FIXED_PART_LEN ||
(!server && outlen < V3_AUTH_BODY_LEN))
return -1;
ctx->is_ed = 0;
ptr = out;
auth = auth1_new();
/* Type: 8 bytes. */
memcpy(ptr, "AUTH0001", 8);
ptr += 8;
memcpy(auth1_getarray_type(auth), "AUTH0001", 8);
{
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;
/* Client ID digest: 32 octets. */
memcpy(ptr, client_id, 32);
ptr += 32;
memcpy(auth->cid, client_id, 32);
/* Server ID digest: 32 octets. */
memcpy(ptr, server_id, 32);
ptr += 32;
memcpy(auth->sid, server_id, 32);
}
{
@ -2378,12 +2385,10 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
}
/* Server log digest : 32 octets */
crypto_digest_get_digest(server_d, (char*)ptr, 32);
ptr += 32;
crypto_digest_get_digest(server_d, (char*)auth->slog, 32);
/* Client log digest : 32 octets */
crypto_digest_get_digest(client_d, (char*)ptr, 32);
ptr += 32;
crypto_digest_get_digest(client_d, (char*)auth->clog, 32);
}
{
@ -2396,49 +2401,79 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
freecert = tor_tls_get_peer_cert(conn->tls);
cert = freecert;
}
if (!cert)
return -1;
memcpy(ptr, tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
if (!cert) {
log_warn(LD_OR, "Unable to find cert when making AUTH1 data.");
goto err;
}
memcpy(auth->scert,
tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
if (freecert)
tor_x509_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_AUTH_FIXED_PART_LEN);
if (server)
return V3_AUTH_FIXED_PART_LEN; // ptr-out
tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets);
/* 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
* checks it. That's followed by 16 bytes of nonce. */
crypto_rand((char*)ptr, 24);
ptr += 24;
crypto_rand((char*)auth->rand, 24);
tor_assert(ptr - out == V3_AUTH_BODY_LEN);
if (!signing_key)
return V3_AUTH_BODY_LEN; // 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 (int)(ptr - out);
ssize_t len;
if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) {
log_warn(LD_OR, "Unable to encode signed part of AUTH1 data.");
goto err;
}
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