Merge branch 'bug22460_030_01' into maint-0.3.0

This commit is contained in:
Nick Mathewson 2017-06-05 15:44:36 -04:00
commit d5acdadaef
10 changed files with 97 additions and 29 deletions

16
changes/bug22460_case1 Normal file
View File

@ -0,0 +1,16 @@
o Major bugfixes (relays, key management):
- Regenerate link and authentication certificates whenever the key that
signs them changes; also, regenerate link certificates whenever the
signed key changes. Previously, these processes were only weakly
coupled, and we relays could (for minutes to hours) wind up with an
inconsistent set of keys and certificates, which other relays
would not accept. Fixes two cases of bug 22460; bugfix on
0.3.0.1-alpha.
- When sending an Ed25519 signing->link certificate in a CERTS cell,
send the certificate that matches the x509 certificate that we used
on the TLS connection. Previously, there was a race condition if
the TLS context rotated after we began the TLS handshake but
before we sent the CERTS cell. Fixes a case of bug 22460; bugfix
on 0.3.0.1-alpha.

View File

@ -1855,6 +1855,9 @@ connection_init_or_handshake_state(or_connection_t *conn, int started_here)
s->started_here = started_here ? 1 : 0;
s->digest_sent_data = 1;
s->digest_received_data = 1;
if (! started_here && get_current_link_cert_cert()) {
s->own_link_cert = tor_cert_dup(get_current_link_cert_cert());
}
s->certs = or_handshake_certs_new();
s->certs->started_here = s->started_here;
return 0;
@ -1869,6 +1872,7 @@ or_handshake_state_free(or_handshake_state_t *state)
crypto_digest_free(state->digest_sent);
crypto_digest_free(state->digest_received);
or_handshake_certs_free(state->certs);
tor_cert_free(state->own_link_cert);
memwipe(state, 0xBE, sizeof(or_handshake_state_t));
tor_free(state);
}
@ -2234,7 +2238,8 @@ add_certs_cell_cert_helper(certs_cell_t *certs_cell,
/** Add an encoded X509 cert (stored as <b>cert_len</b> bytes at
* <b>cert_encoded</b>) to the trunnel certs_cell_t object that we are
* building in <b>certs_cell</b>. Set its type field to <b>cert_type</b>. */
* building in <b>certs_cell</b>. Set its type field to <b>cert_type</b>.
* (If <b>cert</b> is NULL, take no action.) */
static void
add_x509_cert(certs_cell_t *certs_cell,
uint8_t cert_type,
@ -2252,7 +2257,7 @@ add_x509_cert(certs_cell_t *certs_cell,
/** Add an Ed25519 cert from <b>cert</b> to the trunnel certs_cell_t object
* that we are building in <b>certs_cell</b>. Set its type field to
* <b>cert_type</b>. */
* <b>cert_type</b>. (If <b>cert</b> is NULL, take no action.) */
static void
add_ed25519_cert(certs_cell_t *certs_cell,
uint8_t cert_type,
@ -2315,9 +2320,10 @@ connection_or_send_certs_cell(or_connection_t *conn)
CERTTYPE_ED_ID_SIGN,
get_master_signing_key_cert());
if (conn_in_server_mode) {
tor_assert_nonfatal(conn->handshake_state->own_link_cert);
add_ed25519_cert(certs_cell,
CERTTYPE_ED_SIGN_LINK,
get_current_link_cert_cert());
conn->handshake_state->own_link_cert);
} else {
add_ed25519_cert(certs_cell,
CERTTYPE_ED_SIGN_AUTH,

View File

@ -1506,8 +1506,9 @@ check_ed_keys_callback(time_t now, const or_options_t *options)
{
if (server_mode(options)) {
if (should_make_new_ed_keys(options, now)) {
if (load_ed_keys(options, now) < 0 ||
generate_ed_link_cert(options, now)) {
int new_signing_key = load_ed_keys(options, now);
if (new_signing_key < 0 ||
generate_ed_link_cert(options, now, new_signing_key > 0)) {
log_err(LD_OR, "Unable to update Ed25519 keys! Exiting.");
tor_cleanup();
exit(0);
@ -1559,6 +1560,11 @@ rotate_x509_certificate_callback(time_t now, const or_options_t *options)
log_err(LD_BUG, "Error reinitializing TLS context");
tor_assert_unreached();
}
if (generate_ed_link_cert(options, now, 1)) {
log_err(LD_OR, "Unable to update Ed25519->TLS link certificate for "
"new TLS context.");
tor_assert_unreached();
}
/* We also make sure to rotate the TLS connections themselves if they've
* been up for too long -- but that's done via is_bad_for_new_circs in
@ -2298,8 +2304,9 @@ do_hup(void)
/* Maybe we've been given a new ed25519 key or certificate?
*/
time_t now = approx_time();
if (load_ed_keys(options, now) < 0 ||
generate_ed_link_cert(options, now)) {
int new_signing_key = load_ed_keys(options, now);
if (new_signing_key < 0 ||
generate_ed_link_cert(options, now, new_signing_key > 0)) {
log_warn(LD_OR, "Problem reloading Ed25519 keys; still using old keys.");
}
@ -3627,7 +3634,7 @@ tor_main(int argc, char *argv[])
result = do_main_loop();
break;
case CMD_KEYGEN:
result = load_ed_keys(get_options(), time(NULL));
result = load_ed_keys(get_options(), time(NULL)) < 0;
break;
case CMD_LIST_FINGERPRINT:
result = do_list_fingerprint();

View File

@ -1449,6 +1449,12 @@ typedef struct or_handshake_state_t {
/* True iff we have sent a netinfo cell */
unsigned int sent_netinfo : 1;
/** The signing->ed25519 link certificate corresponding to the x509
* certificate we used on the TLS connection (if this is a server-side
* connection). We make a copy of this here to prevent a race condition
* caused by TLS context rotation. */
struct tor_cert_st *own_link_cert;
/** True iff we should feed outgoing cells into digest_sent and
* digest_received respectively.
*

View File

@ -906,7 +906,8 @@ init_keys(void)
}
/* 1d. Load all ed25519 keys */
if (load_ed_keys(options,now) < 0)
const int new_signing_key = load_ed_keys(options,now);
if (new_signing_key < 0)
return -1;
/* 2. Read onion key. Make it if none is found. */
@ -976,7 +977,7 @@ init_keys(void)
/* 3b. Get an ed25519 link certificate. Note that we need to do this
* after we set up the TLS context */
if (generate_ed_link_cert(options, now) < 0) {
if (generate_ed_link_cert(options, now, new_signing_key > 0) < 0) {
log_err(LD_GENERAL,"Couldn't make link cert");
return -1;
}

View File

@ -673,6 +673,9 @@ static time_t rsa_ed_crosscert_expiration = 0;
/**
* Running as a server: load, reload, or refresh our ed25519 keys and
* certificates, creating and saving new ones as needed.
*
* Return -1 on failure; 0 on success if the signing key was not replaced;
* and 1 on success if the signing key was replaced.
*/
int
load_ed_keys(const or_options_t *options, time_t now)
@ -685,6 +688,7 @@ load_ed_keys(const or_options_t *options, time_t now)
const tor_cert_t *check_signing_cert = NULL;
tor_cert_t *sign_cert = NULL;
tor_cert_t *auth_cert = NULL;
int signing_key_changed = 0;
#define FAIL(msg) do { \
log_warn(LD_OR, (msg)); \
@ -722,7 +726,23 @@ load_ed_keys(const or_options_t *options, time_t now)
use_signing = sign;
}
if (use_signing) {
/* We loaded a signing key with its certificate. */
if (! master_signing_key) {
/* We didn't know one before! */
signing_key_changed = 1;
} else if (! ed25519_pubkey_eq(&use_signing->pubkey,
&master_signing_key->pubkey) ||
! tor_memeq(use_signing->seckey.seckey,
master_signing_key->seckey.seckey,
ED25519_SECKEY_LEN)) {
/* We loaded a different signing key than the one we knew before. */
signing_key_changed = 1;
}
}
if (!use_signing && master_signing_key) {
/* We couldn't load a signing key, but we already had one loaded */
check_signing_cert = signing_key_cert;
use_signing = master_signing_key;
}
@ -882,6 +902,7 @@ load_ed_keys(const or_options_t *options, time_t now)
if (!sign)
FAIL("Missing signing key");
use_signing = sign;
signing_key_changed = 1;
tor_assert(sign_cert->signing_key_included);
tor_assert(ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey));
@ -918,6 +939,7 @@ load_ed_keys(const or_options_t *options, time_t now)
}
if (!current_auth_key ||
signing_key_changed ||
EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop)) {
auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT,
now,
@ -945,7 +967,7 @@ load_ed_keys(const or_options_t *options, time_t now)
SET_CERT(auth_key_cert, auth_cert);
}
return 0;
return signing_key_changed;
err:
ed25519_keypair_free(id);
ed25519_keypair_free(sign);
@ -959,16 +981,18 @@ load_ed_keys(const or_options_t *options, time_t now)
* Retrieve our currently-in-use Ed25519 link certificate and id certificate,
* and, if they would expire soon (based on the time <b>now</b>, generate new
* certificates (without embedding the public part of the signing key inside).
* If <b>force</b> is true, always generate a new certificate.
*
* The signed_key from the expiring certificate will be used to sign the new
* key within newly generated X509 certificate.
* The signed_key from the current id->signing certificate will be used to
* sign the new key within newly generated X509 certificate.
*
* Returns -1 upon error. Otherwise, returns 0 upon success (either when the
* current certificate is still valid, or when a new certificate was
* successfully generated).
*/
int
generate_ed_link_cert(const or_options_t *options, time_t now)
generate_ed_link_cert(const or_options_t *options, time_t now,
int force)
{
const tor_x509_cert_t *link_ = NULL, *id = NULL;
tor_cert_t *link_cert = NULL;
@ -980,7 +1004,8 @@ generate_ed_link_cert(const or_options_t *options, time_t now)
const common_digests_t *digests = tor_x509_cert_get_cert_digests(link_);
if (link_cert_cert &&
if (force == 0 &&
link_cert_cert &&
! EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop) &&
fast_memeq(digests->d[DIGEST_SHA256], link_cert_cert->signed_key.pubkey,
DIGEST256_LEN)) {
@ -1082,7 +1107,7 @@ init_mock_ed_keys(const crypto_pk_t *rsa_identity_key)
MAKECERT(auth_key_cert,
master_signing_key, current_auth_key, CERT_TYPE_SIGNING_AUTH, 0);
if (generate_ed_link_cert(get_options(), time(NULL)) < 0) {
if (generate_ed_link_cert(get_options(), time(NULL), 0) < 0) {
log_warn(LD_BUG, "Couldn't make link certificate");
goto err;
}

View File

@ -66,7 +66,7 @@ MOCK_DECL(int, check_tap_onion_key_crosscert,(const uint8_t *crosscert,
int load_ed_keys(const or_options_t *options, time_t now);
int should_make_new_ed_keys(const or_options_t *options, const time_t now);
int generate_ed_link_cert(const or_options_t *options, time_t now);
int generate_ed_link_cert(const or_options_t *options, time_t now, int force);
int read_encrypted_secret_key(ed25519_secret_key_t *out,
const char *fname);

View File

@ -911,6 +911,11 @@ test_link_handshake_send_authchallenge(void *arg)
or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET);
var_cell_t *cell1=NULL, *cell2=NULL;
crypto_pk_t *rsa0 = pk_generate(0), *rsa1 = pk_generate(1);
tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
rsa0, rsa1, 86400), ==, 0);
init_mock_ed_keys(rsa0);
MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell);
tt_int_op(connection_init_or_handshake_state(c1, 0), ==, 0);
@ -936,6 +941,8 @@ test_link_handshake_send_authchallenge(void *arg)
connection_free_(TO_CONN(c1));
tor_free(cell1);
tor_free(cell2);
crypto_pk_free(rsa0);
crypto_pk_free(rsa1);
}
typedef struct authchallenge_data_s {

View File

@ -450,8 +450,8 @@ test_routerkeys_ed_keys_init_all(void *arg)
options->DataDirectory = dir;
tt_int_op(0, ==, load_ed_keys(options, now));
tt_int_op(0, ==, generate_ed_link_cert(options, now));
tt_int_op(1, ==, load_ed_keys(options, now));
tt_int_op(0, ==, generate_ed_link_cert(options, now, 0));
tt_assert(get_master_identity_key());
tt_assert(get_master_identity_key());
tt_assert(get_master_signing_keypair());
@ -466,7 +466,7 @@ test_routerkeys_ed_keys_init_all(void *arg)
/* Call load_ed_keys again, but nothing has changed. */
tt_int_op(0, ==, load_ed_keys(options, now));
tt_int_op(0, ==, generate_ed_link_cert(options, now));
tt_int_op(0, ==, generate_ed_link_cert(options, now, 0));
tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
tt_mem_op(&auth, ==, get_current_auth_keypair(), sizeof(auth));
@ -474,8 +474,8 @@ test_routerkeys_ed_keys_init_all(void *arg)
/* Force a reload: we make new link/auth keys. */
routerkeys_free_all();
tt_int_op(0, ==, load_ed_keys(options, now));
tt_int_op(0, ==, generate_ed_link_cert(options, now));
tt_int_op(1, ==, load_ed_keys(options, now));
tt_int_op(0, ==, generate_ed_link_cert(options, now, 0));
tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
tt_assert(tor_cert_eq(link_cert, get_current_link_cert_cert()));
@ -489,7 +489,7 @@ test_routerkeys_ed_keys_init_all(void *arg)
/* Force a link/auth-key regeneration by advancing time. */
tt_int_op(0, ==, load_ed_keys(options, now+3*86400));
tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400));
tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400, 0));
tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert()));
@ -502,8 +502,8 @@ test_routerkeys_ed_keys_init_all(void *arg)
memcpy(&auth, get_current_auth_keypair(), sizeof(auth));
/* Force a signing-key regeneration by advancing time. */
tt_int_op(0, ==, load_ed_keys(options, now+100*86400));
tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400));
tt_int_op(1, ==, load_ed_keys(options, now+100*86400));
tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400, 0));
tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
tt_mem_op(&sign, !=, get_master_signing_keypair(), sizeof(sign));
tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert()));
@ -520,8 +520,8 @@ test_routerkeys_ed_keys_init_all(void *arg)
routerkeys_free_all();
unlink(get_fname("test_ed_keys_init_all/keys/"
"ed25519_master_id_secret_key"));
tt_int_op(0, ==, load_ed_keys(options, now));
tt_int_op(0, ==, generate_ed_link_cert(options, now));
tt_int_op(1, ==, load_ed_keys(options, now));
tt_int_op(0, ==, generate_ed_link_cert(options, now, 0));
tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert()));

View File

@ -48,7 +48,7 @@ init_authority_state(void)
mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
tt_assert(mock_cert);
options->AuthoritativeDir = 1;
tt_int_op(0, ==, load_ed_keys(options, time(NULL)));
tt_int_op(load_ed_keys(options, time(NULL)), OP_GE, 0);
sr_state_init(0, 0);
/* It's possible a commit has been generated in our state depending on
* the phase we are currently in which uses "now" as the starting
@ -286,7 +286,7 @@ test_sr_commit(void *arg)
tt_assert(auth_cert);
options->AuthoritativeDir = 1;
tt_int_op(0, ==, load_ed_keys(options, now));
tt_int_op(load_ed_keys(options, time(NULL)), OP_GE, 0);
}
/* Generate our commit object and validate it has the appropriate field