From c7e8c2098abd93bf24439f869016c385067e5e5b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 7 Oct 2004 21:10:40 +0000 Subject: [PATCH] Include a dir-signing-key token in directories to tell the parsing entity which key is being used to sign. This is the first step in obsoleting the dirservers file. svn:r2428 --- src/or/dirserv.c | 16 ++++- src/or/routerlist.c | 1 + src/or/routerparse.c | 140 ++++++++++++++++++++++++++++++++++++------- src/or/test.c | 6 ++ 4 files changed, 138 insertions(+), 25 deletions(-) diff --git a/src/or/dirserv.c b/src/or/dirserv.c index a9e37c7074..39dc7bc89a 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -552,7 +552,7 @@ dirserv_dump_directory_to_string(char *s, unsigned int maxlen, crypto_pk_env_t *private_key) { char *cp, *eos; - char *identity_pkey; /* Identity key, PEM-encoded. */ + char *identity_pkey; /* Identity key, DER64-encoded. */ char digest[20]; char signature[128]; char published[33]; @@ -764,20 +764,32 @@ static int generate_runningrouters(crypto_pk_env_t *private_key) char published[33]; size_t len; time_t published_on; + char *identity_pkey; /* Identity key, DER64-encoded. */ len = 1024+(MAX_HEX_NICKNAME_LEN+2)*smartlist_len(descriptor_list); s = tor_malloc_zero(len); if (list_running_servers(&cp)) return -1; + /* ASN.1-encode the public key. This is a temporary measure; once + * everyone is running 0.0.9pre3 or later, we can shift to using a + * PEM-encoded key instead. + */ + if(crypto_pk_DER64_encode_public_key(private_key, &identity_pkey)<0) { + log_fn(LOG_WARN,"write identity_pkey to string failed!"); + tor_free(cp); + return -1; + } published_on = time(NULL); format_iso_time(published, published_on); sprintf(s, "network-status\n" "published %s\n" "running-routers %s\n" + "opt dir-signing-key %s\n" "directory-signature %s\n" "-----BEGIN SIGNATURE-----\n", - published, cp, options.Nickname); + published, cp, identity_pkey, options.Nickname); tor_free(cp); + tor_free(identity_pkey); if (router_get_runningrouters_hash(s,digest)) { log_fn(LOG_WARN,"couldn't compute digest"); return -1; diff --git a/src/or/routerlist.c b/src/or/routerlist.c index cb63ee5072..66a8d7c5e1 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -512,6 +512,7 @@ routerinfo_t *router_get_by_digest(const char *digest) { routerinfo_t *router; tor_assert(digest); + if (!routerlist) return NULL; for(i=0;irouters);i++) { router = smartlist_get(routerlist->routers, i); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 9f8ae7dde7..4ce8684bb3 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -40,6 +40,7 @@ typedef enum { K_CONTACT, K_NETWORK_STATUS, K_UPTIME, + K_DIR_SIGNING_KEY, _UNRECOGNIZED, _ERR, _EOF, @@ -113,6 +114,7 @@ static struct { { "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ, ANY }, { "network-status", K_NETWORK_STATUS, NO_ARGS, NO_OBJ, DIR_ONLY }, { "uptime", K_UPTIME, ARGS, NO_OBJ, RTR_ONLY }, + { "dir-signing-key", K_DIR_SIGNING_KEY, ARGS, OBJ_OK, DIR_ONLY }, { NULL, -1 } }; @@ -130,8 +132,9 @@ static int tokenize_string(const char *start, const char *end, static directory_token_t *get_next_token(const char **s, where_syntax where); static int check_directory_signature(const char *digest, directory_token_t *tok, - crypto_pk_env_t *pkey); - + crypto_pk_env_t *pkey, + crypto_pk_env_t *declared_key); +static crypto_pk_env_t *find_dir_signing_key(const char *str); /** Set digest to the SHA-1 digest of the hash of the directory in * s. Return 0 on success, nonzero on failure. @@ -298,6 +301,7 @@ router_parse_routerlist_from_directory(const char *str, const char *end, *cp; smartlist_t *tokens = NULL; char dirnickname[MAX_NICKNAME_LEN+1]; + crypto_pk_env_t *declared_key = NULL; if (router_get_dir_hash(str, digest)) { log_fn(LOG_WARN, "Unable to compute digest of directory"); @@ -324,9 +328,9 @@ router_parse_routerlist_from_directory(const char *str, if(tok->tp != K_DIRECTORY_SIGNATURE) { log_fn(LOG_WARN,"Expected a single directory signature"); goto err; } - if (check_directory_signature(digest, tok, pkey)<0) { + declared_key = find_dir_signing_key(str); + if (check_directory_signature(digest, tok, pkey, declared_key)<0) goto err; - } /* now we know tok->n_args == 1, so it's safe to access tok->args[0] */ strlcpy(dirnickname, tok->args[0], sizeof(dirnickname)); @@ -439,6 +443,7 @@ router_parse_routerlist_from_directory(const char *str, routerlist_free(new_dir); tor_free(versions); done: + if (declared_key) crypto_free_pk_env(declared_key); if (tokens) { SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok)); smartlist_free(tokens); @@ -458,7 +463,7 @@ router_parse_runningrouters(const char *str) directory_token_t *tok; time_t published_on; int i; - + crypto_pk_env_t *declared_key = NULL; smartlist_t *tokens = NULL; if (router_get_runningrouters_hash(str, digest)) { @@ -505,15 +510,16 @@ router_parse_runningrouters(const char *str) log_fn(LOG_WARN, "Missing signature on directory"); goto err; } - if (check_directory_signature(digest, tok, NULL)<0) { + declared_key = find_dir_signing_key(str); + if (check_directory_signature(digest, tok, NULL, declared_key) < 0) goto err; - } goto done; err: running_routers_free(new_list); new_list = NULL; done: + if (declared_key) crypto_free_pk_env(declared_key); if (tokens) { SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok)); smartlist_free(tokens); @@ -521,9 +527,91 @@ router_parse_runningrouters(const char *str) return new_list; } +/** Given a directory or running-routers string in str, try to + * find the its dir-signing-key token (if any). If this token is + * present, extract and return the key. Return NULL on failure. */ +static crypto_pk_env_t *find_dir_signing_key(const char *str) +{ + const char *cp; + directory_token_t *tok; + crypto_pk_env_t *key = NULL; + + /* Is there a dir-signing-key in the directory? */ + cp = strstr(str, "\nopt dir-signing-key"); + if (!cp) + cp = strstr(str, "\ndir-signing-key"); + if (!cp) + return NULL; + ++cp; /* Now cp points to the start of the token. */ + + tok = get_next_token(&cp, DIR_ONLY); + if (!tok) { + log_fn(LOG_WARN, "Unparseable dir-signing-key token"); + return NULL; + } + if (tok->tp != K_DIR_SIGNING_KEY) { + log_fn(LOG_WARN, "Dir-signing-key token did not parse as expected"); + return NULL; + } + + if (tok->key) { + key = tok->key; + tok->key = NULL; /* steal reference. */ + } else if (tok->n_args >= 1) { + key = crypto_pk_DER64_decode_public_key(tok->args[0]); + if (!key) { + log_fn(LOG_WARN, "Unparseable dir-signing-key argument"); + return NULL; + } + } else { + log_fn(LOG_WARN, "Dir-signing-key token contained no key"); + return NULL; + } + + token_free(tok); + return key; +} + +/** Return true iff key is allowed to sign directories. + */ +static int dir_signing_key_is_trusted(crypto_pk_env_t *key) +{ + char digest[DIGEST_LEN]; + routerinfo_t *r; + if (!key) return 0; + if (crypto_pk_get_digest(key, digest) < 0) { + log_fn(LOG_WARN, "Error computing dir-signing-key digest"); + return 0; + } + if (!(r = router_get_by_digest(digest))) { + log_fn(LOG_WARN, "No router known with given dir-signing-key digest"); + return 0; + } + if (! r->is_trusted_dir) { + log_fn(LOG_WARN, "Listed dir-signing-key is not trusted"); + return 0; + } + return 1; +} + +/** Check whether the K_DIRECTORY_SIGNATURE token in tok has a + * good signature for digest. + * + * If declared_key is set, the directory has declared what key + * was used to sign it, so we will use that key only if it is an + * authoritative directory signing key. + * + * Otherwise, try to look up the router whose nickname is given in the + * directory-signature token. If this fails, or the named router is + * not authoritative, try to use pkey. + * + * (New callers should always use declared_key when possible; + * pkey is only for debugging.) + */ static int check_directory_signature(const char *digest, - directory_token_t *tok, - crypto_pk_env_t *pkey) + directory_token_t *tok, + crypto_pk_env_t *pkey, + crypto_pk_env_t *declared_key) { char signed_digest[PK_BYTES]; routerinfo_t *r; @@ -533,21 +621,27 @@ static int check_directory_signature(const char *digest, return -1; } - r = router_get_by_nickname(tok->args[0]); - log_fn(LOG_DEBUG, "Got directory signed by %s", tok->args[0]); - if (r && r->is_trusted_dir) { - pkey = r->identity_pkey; - } else if (!r && pkey) { - /* pkey provided for debugging purposes. */ - } else if (!r) { - log_fn(LOG_WARN, "Directory was signed by unrecognized server %s", - tok->args[0]); - return -1; - } else if (r && !r->is_trusted_dir) { - log_fn(LOG_WARN, "Directory was signed by non-trusted server %s", - tok->args[0]); - return -1; + if (declared_key) { + if (dir_signing_key_is_trusted(declared_key)) + pkey = declared_key; + } else { + r = router_get_by_nickname(tok->args[0]); + log_fn(LOG_DEBUG, "Got directory signed by %s", tok->args[0]); + if (r && r->is_trusted_dir) { + pkey = r->identity_pkey; + } else if (!r && pkey) { + /* pkey provided for debugging purposes. */ + } else if (!r) { + log_fn(LOG_WARN, "Directory was signed by unrecognized server %s", + tok->args[0]); + return -1; + } else if (r && !r->is_trusted_dir) { + log_fn(LOG_WARN, "Directory was signed by non-trusted server %s", + tok->args[0]); + return -1; + } } + if (strcmp(tok->object_type, "SIGNATURE") || tok->object_size != 128) { log_fn(LOG_WARN, "Bad object type or length on directory signature"); return -1; diff --git a/src/or/test.c b/src/or/test.c index 48a30cec64..ed89df1f3f 100644 --- a/src/or/test.c +++ b/src/or/test.c @@ -619,6 +619,12 @@ test_util() { test_eq(5, tor_strstrip(buf, "!? ")); test_streq(buf, "Testing123"); + /* Test tor_strpartition() */ + test_assert(! tor_strpartition(buf, sizeof(buf), "abcdefg", "##", 3)); + test_streq(buf, "abc##def##g"); + test_assert(! tor_strpartition(buf, sizeof(buf), "abcdefghi", "##", 3)); + test_streq(buf, "abc##def##ghi##"); + /* XXXX test older functions. */ smartlist_free(sl); }