r12898@catbus: nickm | 2007-05-22 13:11:04 -0400

More v3 directory code: have authorities load certificates; have everybody store certificates to disk and load them; provide a way to configure v3 authorities.


svn:r10293
This commit is contained in:
Nick Mathewson 2007-05-22 17:58:25 +00:00
parent 6975a093e9
commit c9fa4e6583
7 changed files with 282 additions and 45 deletions

View File

@ -56,7 +56,7 @@ N . Document transport and natdport
Things we'd like to do in 0.2.0.x:
- Proposals:
. 101: Voting on the Tor Directory System
. 101: Voting on the Tor Directory System (plus 103)
o Prepare ASAP for new voting formats
o Don't flip out with warnings when voting-related URLs are
uploaded/downloaded.
@ -68,16 +68,21 @@ Things we'd like to do in 0.2.0.x:
o Parse key certificates
- Parse votes and consensuses
- Unit tests for above
- Code to manage key certificates
- Cache on disk
- Download as needed
. Code to manage key certificates
o Generate certificates
o Authorities load certificates
o Clients cache certificates on disk
- Download as needed.
- Serve list as needed.
- Avoid double-checking signatures every time we get a vote.
o Avoid double-checking signatures every time we get a vote.
- Warn about expired stuff.
- Code to generate votes
- Code to generate consensus from a list of votes
- Add a signature to a consensus.
- Code to check signatures on a consensus
- Push/pull documents as appropriate.
o Have clients know which authorities are v3 authorities, and what
their keys are.
- Start caching consensus documents once authorities make them
- Start downloading and using consensus documents once caches serve them
. 104: Long and Short Router Descriptors (by Jun 1)
@ -98,8 +103,8 @@ Things we'd like to do in 0.2.0.x:
- 105: Version negotiation for the Tor protocol (finalize by Jun 1)
- 108: Base "Stable" Flag on Mean Time Between Failures
- 109: No more than one server per IP address
- 103: Splitting identity key from regularly used signing key
- Merge with 101 into a new dir-spec.txt
o 103: Splitting identity key from regularly used signing key
o Merge with 101 into a new dir-spec.txt
- 113: Simplifying directory authority administration
- 110: prevent infinite-length circuits (phase one)
- servers should recognize relay_extend cells and pass them

View File

@ -1381,8 +1381,8 @@ append_bytes_to_file(const char *fname, const char *str, size_t len,
/** Read the contents of <b>filename</b> into a newly allocated
* string; return the string on success or NULL on failure.
*
* If <b>size_out</b> is provided, store the length of the result in
* <b>size_out</b>.
* If <b>stat_out</b> is provided, store the result of stat()ing the
* file into <b>stat_out</b>.
*
* If <b>flags</b> &amp; RFTS_BIN, open the file in binary mode.
* If <b>flags</b> &amp; RFTS_IGNORE_MISSING, don't warn if the file

View File

@ -261,6 +261,7 @@ static config_var_t _option_vars[] = {
VAR("User", STRING, User, NULL),
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
VAR("V2AuthoritativeDirectory",BOOL, V2AuthoritativeDir, "0"),
VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"),
VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
VAR("VirtualAddrNetwork", STRING, VirtualAddrNetwork, "127.192.0.0/10"),
VAR("__AllDirActionsPrivate",BOOL, AllDirActionsPrivate, "0"),
@ -1047,6 +1048,7 @@ options_act(or_options_t *old_options)
if (dns_reset())
return -1;
}
/* XXXX020 init_keys() again if v3authoritativedir is newly set. */
}
/* Check if we need to parse and add the EntryNodes config option. */
@ -2346,6 +2348,8 @@ parse_authority_type_from_list(smartlist_t *list, authority_type_t *auth,
*auth |= V1_AUTHORITY | V2_AUTHORITY;
else if (!strcasecmp(string, "v2"))
*auth |= V2_AUTHORITY;
else if (!strcasecmp(string, "v3"))
*auth |= V3_AUTHORITY;
else if (!strcasecmp(string, "bridge"))
*auth |= BRIDGE_AUTHORITY;
else if (!strcasecmp(string, "hidserv"))
@ -2580,6 +2584,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
"extra-info documents. Setting DownloadExtraInfo.");
options->DownloadExtraInfo = 1;
}
/* XXXX020 Check that at least one of Bridge/HS/V1/V2/V2{AoritativeDir}
* is set. */
}
if (options->AuthoritativeDir && !options->DirPort)
@ -3588,6 +3594,7 @@ parse_dir_server_line(const char *line, int validate_only)
char *addrport=NULL, *address=NULL, *nickname=NULL, *fingerprint=NULL;
uint16_t dir_port = 0, or_port = 0;
char digest[DIGEST_LEN];
char v3_digest[DIGEST_LEN];
authority_type_t type = V2_AUTHORITY;
int is_not_hidserv_authority = 0, is_not_v2_authority = 0;
@ -3625,6 +3632,15 @@ parse_dir_server_line(const char *line, int validate_only)
if (!ok)
log_warn(LD_CONFIG, "Invalid orport '%s' on DirServer line.",
portstring);
} else if (!strcasecmpstart(flag, "v3ident=")) {
char *idstr = flag + strlen("v3ident=");
if (strlen(idstr) != HEX_DIGEST_LEN ||
base16_decode(v3_digest, DIGEST_LEN, idstr, HEX_DIGEST_LEN)<0) {
log_warn(LD_CONFIG, "Bad v3 identity digest '%s' on DirServer line",
flag);
} else {
type |= V3_AUTHORITY;
}
} else {
log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirServer line",
flag);

View File

@ -1334,9 +1334,10 @@ typedef enum {
NO_AUTHORITY = 0,
V1_AUTHORITY = 1 << 0,
V2_AUTHORITY = 1 << 1,
HIDSERV_AUTHORITY = 1 << 2,
BRIDGE_AUTHORITY = 1 << 3,
EXTRAINFO_CACHE = 1 << 4, /* not precisely an authority type. */
V3_AUTHORITY = 1 << 2,
HIDSERV_AUTHORITY = 1 << 3,
BRIDGE_AUTHORITY = 1 << 4,
EXTRAINFO_CACHE = 1 << 5, /* not precisely an authority type. */
} authority_type_t;
#define CRYPT_PATH_MAGIC 0x70127012u
@ -1758,6 +1759,8 @@ typedef struct {
* for version 1 directories? */
int V2AuthoritativeDir; /**< Boolean: is this an authoritative directory
* for version 2 directories? */
int V3AuthoritativeDir; /**< Boolean: is this an authoritative directory
* for version 3 directories? */
int HSAuthoritativeDir; /**< Boolean: does this an authoritative directory
* handle hidden service requests? */
int HSAuthorityRecordStats; /**< Boolean: does this HS authoritative
@ -3057,6 +3060,9 @@ typedef struct trusted_dir_server_t {
uint16_t dir_port; /**< Directory port. */
uint16_t or_port; /**< OR port: Used for tunneling connections. */
char digest[DIGEST_LEN]; /**< Digest of identity key. */
char v3_identity_digest[DIGEST_LEN]; /**< Digest of v3 (authority only,
* high-security) identity key. */
unsigned int is_running:1; /**< True iff we think this server is running. */
/** True iff this server has accepted the most recent server descriptor
@ -3066,6 +3072,8 @@ typedef struct trusted_dir_server_t {
/** DOCDOC */
authority_type_t type;
authority_cert_t *v3_cert; /**< V3 key certificate for this authority */
int n_networkstatus_failures; /**< How many times have we asked for this
* server's network-status unsuccessfully? */
local_routerstatus_t fake_status; /**< Used when we need to pass this trusted
@ -3088,6 +3096,8 @@ routerstatus_t *router_pick_trusteddirserver(authority_type_t type,
int retry_if_no_servers);
trusted_dir_server_t *router_get_trusteddirserver_by_digest(
const char *digest);
trusted_dir_server_t *trusteddirserver_get_by_v3_auth_digest(
const char *digest);
void routerlist_add_family(smartlist_t *sl, routerinfo_t *router);
void add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
int must_be_running);
@ -3197,6 +3207,10 @@ int getinfo_helper_networkstatus(control_connection_t *conn,
void routerlist_assert_ok(routerlist_t *rl);
void routerlist_check_bug_417(void);
int trusted_dirs_reload_certs(void);
int trusted_dirs_load_certs_from_string(const char *contents, int from_store);
void trusted_dirs_flush_certs_to_disk(void);
/********************************* routerparse.c ************************/
#define MAX_STATUS_TAG_LEN 32
@ -3278,7 +3292,7 @@ networkstatus_t *networkstatus_parse_from_string(const char *s);
void authority_cert_free(authority_cert_t *cert);
authority_cert_t *authority_cert_parse_from_string(const char *s,
char **end_of_string);
const char **end_of_string);
#endif

View File

@ -38,6 +38,11 @@ static crypto_pk_env_t *lastonionkey=NULL;
static crypto_pk_env_t *identitykey=NULL;
/** Digest of identitykey. */
static char identitykey_digest[DIGEST_LEN];
/** Signing key used for v3 directory material; only set for authorities. */
static crypto_pk_env_t *authority_signing_key = NULL;
/** Key certificate to authenticate v3 directory material; only set for
* authorities. */
static authority_cert_t *authority_key_certificate = NULL;
/** Replace the current onion key with <b>k</b>. Does not affect lastonionkey;
* to update onionkey correctly, call rotate_onion_key().
@ -170,46 +175,48 @@ rotate_onion_key(void)
log_warn(LD_GENERAL, "Couldn't rotate onion key.");
}
/** Try to read an RSA key from <b>fname</b>. If <b>fname</b> doesn't exist,
* create a new RSA key and save it in <b>fname</b>. Return the read/created
* key, or NULL on error.
*/
crypto_pk_env_t *
init_key_from_file(const char *fname)
/** DOCDOC */
static crypto_pk_env_t *
init_key_from_file_impl(const char *fname, int generate, int severity)
{
crypto_pk_env_t *prkey = NULL;
FILE *file = NULL;
if (!(prkey = crypto_new_pk_env())) {
log_err(LD_GENERAL,"Error constructing key");
log(severity, LD_GENERAL,"Error constructing key");
goto error;
}
switch (file_status(fname)) {
case FN_DIR:
case FN_ERROR:
log_err(LD_FS,"Can't read key from \"%s\"", fname);
log(severity, LD_FS,"Can't read key from \"%s\"", fname);
goto error;
case FN_NOENT:
log_info(LD_GENERAL, "No key found in \"%s\"; generating fresh key.",
fname);
if (crypto_pk_generate_key(prkey)) {
log_err(LD_GENERAL,"Error generating onion key");
goto error;
}
if (crypto_pk_check_key(prkey) <= 0) {
log_err(LD_GENERAL,"Generated key seems invalid");
goto error;
}
log_info(LD_GENERAL, "Generated key seems valid");
if (crypto_pk_write_private_key_to_filename(prkey, fname)) {
log_err(LD_FS,"Couldn't write generated key to \"%s\".", fname);
goto error;
if (generate) {
log_info(LD_GENERAL, "No key found in \"%s\"; generating fresh key.",
fname);
if (crypto_pk_generate_key(prkey)) {
log(severity, LD_GENERAL,"Error generating onion key");
goto error;
}
if (crypto_pk_check_key(prkey) <= 0) {
log(severity, LD_GENERAL,"Generated key seems invalid");
goto error;
}
log_info(LD_GENERAL, "Generated key seems valid");
if (crypto_pk_write_private_key_to_filename(prkey, fname)) {
log(severity, LD_FS,
"Couldn't write generated key to \"%s\".", fname);
goto error;
}
} else {
log_info(LD_GENERAL, "No key found in \"%s\"", fname);
}
return prkey;
case FN_FILE:
if (crypto_pk_read_private_key_from_filename(prkey, fname)) {
log_err(LD_GENERAL,"Error loading private key.");
log(severity, LD_GENERAL,"Error loading private key.");
goto error;
}
return prkey;
@ -225,6 +232,71 @@ init_key_from_file(const char *fname)
return NULL;
}
/** Try to read an RSA key from <b>fname</b>. If <b>fname</b> doesn't exist,
* create a new RSA key and save it in <b>fname</b>. Return the read/created
* key, or NULL on error.
*/
crypto_pk_env_t *
init_key_from_file(const char *fname)
{
return init_key_from_file_impl(fname, 1, LOG_ERR);
}
/** DOCDOC; XXXX020 maybe move to dirserv.c */
static void
init_v3_authority_keys(const char *keydir)
{
char *fname = NULL, *cert = NULL;
const char *eos = NULL;
size_t fname_len = strlen(keydir) + 64;
crypto_pk_env_t *signing_key = NULL;
authority_cert_t *parsed = NULL;
fname = tor_malloc(fname_len);
tor_snprintf(fname, fname_len, "%s"PATH_SEPARATOR"authority_signing_key",
keydir);
signing_key = init_key_from_file_impl(fname, 0, LOG_INFO);
if (!signing_key) {
log_warn(LD_DIR, "No version 3 directory key found in %s", fname);
goto done;
}
tor_snprintf(fname, fname_len, "%s"PATH_SEPARATOR"authority_certificate",
keydir);
cert = read_file_to_str(fname, 0, NULL);
if (!cert) {
log_warn(LD_DIR, "Signing key found, but no certificate found in %s",
fname);
goto done;
}
parsed = authority_cert_parse_from_string(cert, &eos);
if (!parsed) {
log_warn(LD_DIR, "Unable to parse certificate in %s", fname);
goto done;
}
if (crypto_pk_cmp_keys(signing_key, parsed->signing_key) != 0) {
log_warn(LD_DIR, "Stored signing key does not match signing key in "
"certificate");
goto done;
}
parsed->cache_info.signed_descriptor_body = cert;
parsed->cache_info.signed_descriptor_len = eos-cert;
cert = NULL;
authority_key_certificate = parsed;
authority_signing_key = signing_key;
parsed = NULL;
signing_key = NULL;
done:
tor_free(fname);
tor_free(cert);
if (signing_key)
crypto_free_pk_env(signing_key);
if (parsed)
authority_cert_free(parsed);
}
/** Initialize all OR private keys, and the TLS context, as necessary.
* On OPs, this only initializes the tls context. Return 0 on success,
* or -1 if Tor should die.
@ -282,6 +354,11 @@ init_keys(void)
prkey = init_key_from_file(keydir);
if (!prkey) return -1;
set_identity_key(prkey);
/* 1b. Read v3 directory authority key/cert information. */
if (authdir_mode(options) && options->V3AuthoritativeDir)
init_v3_authority_keys(keydir);
/* 2. Read onion key. Make it if none is found. */
tor_snprintf(keydir,sizeof(keydir),
"%s"PATH_SEPARATOR"keys"PATH_SEPARATOR"secret_onion_key",datadir);
@ -1592,6 +1669,10 @@ router_free_all(void)
routerinfo_free(desc_routerinfo);
if (desc_extrainfo)
extrainfo_free(desc_extrainfo);
if (authority_signing_key)
crypto_free_pk_env(authority_signing_key);
if (authority_key_certificate)
authority_cert_free(authority_key_certificate);
if (warned_nonexistent_family) {
SMARTLIST_FOREACH(warned_nonexistent_family, char *, cp, tor_free(cp));

View File

@ -49,6 +49,8 @@ static void router_dir_info_changed(void);
/** Global list of a trusted_dir_server_t object for each trusted directory
* server. */
static smartlist_t *trusted_dir_servers = NULL;
/** DOCDOC */
static int trusted_dir_servers_certs_changed = 0;
/** Global list of all of the routers that we know about. */
static routerlist_t *routerlist = NULL;
@ -163,6 +165,89 @@ router_reload_networkstatus(void)
return 0;
}
/** DOCDOC */
int
trusted_dirs_reload_certs(void)
{
char filename[512];
char *contents;
int r;
tor_snprintf(filename,sizeof(filename),"%s"PATH_SEPARATOR"cached-certs",
get_options()->DataDirectory);
contents = read_file_to_str(filename, 0, NULL);
if (!contents)
return -1;
r = trusted_dirs_load_certs_from_string(contents, 1);
tor_free(contents);
return r;
}
/** DOCDOC */
int
trusted_dirs_load_certs_from_string(const char *contents, int from_store)
{
trusted_dir_server_t *ds;
const char *s, *eos;
for (s = contents; *s; s = eos) {
authority_cert_t *cert = authority_cert_parse_from_string(s, &eos);
if (!cert)
break;
ds = trusteddirserver_get_by_v3_auth_digest(
cert->cache_info.identity_digest);
if (!ds) {
log_info(LD_DIR, "Found cached certificate whose key didn't match "
"any v3 authority we recognized; skipping.");
authority_cert_free(cert);
continue;
}
if (ds->v3_cert) {
if (ds->v3_cert->expires < cert->expires) {
authority_cert_free(ds->v3_cert);
} else {
authority_cert_free(cert);
continue;
}
}
cert->cache_info.signed_descriptor_body = tor_strndup(s, eos-s);
cert->cache_info.signed_descriptor_len = eos-s;
ds->v3_cert = cert;
if (!from_store)
trusted_dir_servers_certs_changed = 1;
}
return 0;
}
/** DOCDOC */
void
trusted_dirs_flush_certs_to_disk(void)
{
char filename[512];
smartlist_t *chunks = smartlist_create();
tor_snprintf(filename,sizeof(filename),"%s"PATH_SEPARATOR"cached-certs",
get_options()->DataDirectory);
SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
{
if (ds->v3_cert) {
sized_chunk_t *c = tor_malloc(sizeof(sized_chunk_t));
c->bytes = ds->v3_cert->cache_info.signed_descriptor_body;
c->len = ds->v3_cert->cache_info.signed_descriptor_len;
smartlist_add(chunks, c);
}
});
if (write_chunks_to_file(filename, chunks, 0)) {
log_warn(LD_FS, "Error writing certificates to disk.");
}
SMARTLIST_FOREACH(chunks, sized_chunk_t *, c, tor_free(c));
smartlist_free(chunks);
trusted_dir_servers_certs_changed = 0;
}
/* Router descriptor storage.
*
* Routerdescs are stored in a big file, named "cached-routers". As new
@ -573,6 +658,24 @@ router_get_trusteddirserver_by_digest(const char *digest)
return NULL;
}
/** Return the trusted_dir_server_t for the directory authority whose identity
* key hashes to <b>digest</b>, or NULL if no such authority is known.
*/
trusted_dir_server_t *
trusteddirserver_get_by_v3_auth_digest(const char *digest)
{
if (!trusted_dir_servers)
return NULL;
SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
{
if (!memcmp(ds->v3_identity_digest, digest, DIGEST_LEN))
return ds;
});
return NULL;
}
/** Try to find a running trusted dirserver. If there are no running
* trusted dirservers and <b>retry_if_no_servers</b> is non-zero,
* set them all as running again, and try again.
@ -3477,6 +3580,8 @@ add_trusted_dir_server(const char *nickname, const char *address,
static void
trusted_dir_server_free(trusted_dir_server_t *ds)
{
if (ds->v3_cert)
authority_cert_free(ds->v3_cert);
tor_free(ds->nickname);
tor_free(ds->description);
tor_free(ds->address);

View File

@ -1289,16 +1289,19 @@ authority_cert_free(authority_cert_t *cert)
/** Parse a key certificate from <b>s</b>; point <b>end-of-string</b> to
* the first character after the certificate. */
authority_cert_t *
authority_cert_parse_from_string(const char *s, char **end_of_string)
authority_cert_parse_from_string(const char *s, const char **end_of_string)
{
authority_cert_t *cert = NULL;
smartlist_t *tokens = NULL;
char digest[DIGEST_LEN];
directory_token_t *tok;
char fp_declared[DIGEST_LEN];
char *eos = strstr(s, "\n-----END SIGNATURE-----\n");
char *eos;
size_t len;
trusted_dir_server_t *ds;
s = eat_whitespace(s);
eos = strstr(s, "\n-----END SIGNATURE-----\n");
if (! eos) {
log_warn(LD_DIR, "No end-of-signature found on key certificate");
return NULL;
@ -1324,6 +1327,7 @@ authority_cert_parse_from_string(const char *s, char **end_of_string)
}
cert = tor_malloc_zero(sizeof(authority_cert_t));
memcpy(cert->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
tok = find_first_by_keyword(tokens, K_DIR_SIGNING_KEY);
tor_assert(tok && tok->key);
@ -1371,11 +1375,20 @@ authority_cert_parse_from_string(const char *s, char **end_of_string)
goto err;
}
/* XXXXX This doesn't check whether the key is an authority. IS that what we
* want? */
if (check_signature_token(digest, tok, cert->identity_key, 0,
"key certificate")) {
goto err;
/* If we already have this cert, don't bother checking the signature. */
ds = trusteddirserver_get_by_v3_auth_digest(
cert->cache_info.identity_digest);
if (ds && ds->v3_cert &&
ds->v3_cert->cache_info.signed_descriptor_len == len &&
ds->v3_cert->cache_info.signed_descriptor_body &&
! memcmp(s, ds->v3_cert->cache_info.signed_descriptor_body, len)) {
log_debug(LD_DIR, "We already checked the signature on this certificate;"
" no need to do so again.");
} else {
if (check_signature_token(digest, tok, cert->identity_key, 0,
"key certificate")) {
goto err;
}
}
cert->cache_info.signed_descriptor_len = len;
@ -1383,7 +1396,10 @@ authority_cert_parse_from_string(const char *s, char **end_of_string)
memcpy(cert->cache_info.signed_descriptor_body, s, len);
cert->cache_info.signed_descriptor_body[len] = 0;
cert->cache_info.saved_location = SAVED_NOWHERE;
*end_of_string = eos;
if (end_of_string) {
*end_of_string = eat_whitespace(eos);
}
return cert;
err:
authority_cert_free(cert);