Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tor Trac #22029]: Allow ed25519 keys to be banned in the approved-routers file (Revision 3) #970

Closed
wants to merge 47 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
7267177
Allow ed25519 keys to be banned in approved-routers
neelchauhan Mar 17, 2019
8bafb7f
Add teor's suggestions to the ed25519 key banning code
neelchauhan Mar 23, 2019
089efba
Make relay fingerprint list available to unit tests
neelchauhan Apr 19, 2019
5544ebf
Add add_ed25519_to_dir() and add_fingerprint_to_dir() tests
neelchauhan Apr 19, 2019
626c9d9
Make dirserv_get_status_impl() process ed25519 keys
neelchauhan Apr 2, 2019
5076e16
Add dirserv_load_fingerprint_file() test
neelchauhan Apr 2, 2019
b2976ee
Clarify error for unrecognized key in dirserv_load_fingerprint_file()
neelchauhan Apr 19, 2019
7fc6505
Make dirserv_get_status_impl() take in a ed25519_key
neelchauhan Apr 22, 2019
7517985
Rename add_fingerprint_to_dir() to add_rsa_fingerprint_to_dir()
neelchauhan Apr 24, 2019
2c77fd9
Remove redundant ed25519/RSA key checking where possible when loading…
neelchauhan Apr 24, 2019
2282d4c
Add test for dirserv_router_get_status()
neelchauhan Apr 25, 2019
d456a33
Add test for dirserv_would_reject_router()
neelchauhan Apr 26, 2019
97df970
Update tor.1.txt for DataDirectory/approved-routers
neelchauhan Apr 26, 2019
46d7d14
fixup! Make dirserv_get_status_impl() take in a ed25519_key
neelchauhan May 27, 2019
b6465c0
fixup! Make dirserv_get_status_impl() take in a ed25519_key
neelchauhan May 27, 2019
0d36d1f
Make ed25519_signing_key in routerstatus_t a pointer
neelchauhan May 28, 2019
730563d
Fix merge conflict with voteflags.c
neelchauhan May 28, 2019
790d838
fixup! Fix merge conflict with voteflags.c
neelchauhan May 29, 2019
5df55a3
fixup! Make ed25519_signing_key in routerstatus_t a pointer
neelchauhan May 29, 2019
9520e28
fixup! Remove redundant ed25519/RSA key checking where possible when …
neelchauhan May 29, 2019
4631fbd
fixup! Add test for dirserv_router_get_status()
neelchauhan May 29, 2019
38a1cd8
fixup! Add test for dirserv_would_reject_router()
neelchauhan May 29, 2019
c2d6009
fixup! Add test for dirserv_router_get_status()
neelchauhan May 29, 2019
89f4a25
Update tor.1 man page for bug 22029
neelchauhan May 29, 2019
8639549
fixup! Make ed25519_signing_key in routerstatus_t a pointer
neelchauhan May 29, 2019
e312885
fixup! Add test for dirserv_would_reject_router()
neelchauhan May 29, 2019
f464f6d
fixup! Update tor.1 man page for bug 22029
neelchauhan May 29, 2019
8c0a166
Move base64 decoding in dirserv_load_fingerprint_file() outside the i…
neelchauhan Jun 11, 2019
ca35f23
Move FP_REJECT and similar constants to process_descs.h
neelchauhan Jun 15, 2019
14352cc
Merge and preserve our ed25519 checking code
neelchauhan Jun 15, 2019
e9357f1
fixup! Merge and preserve our ed25519 checking code
neelchauhan Jun 15, 2019
2a83d0a
fixup! Move FP_REJECT and similar constants to process_descs.h
neelchauhan Jun 19, 2019
f1a236a
Add our own base-64 encoded ed25519 public key in dirserv_add_own_fin…
neelchauhan Jun 19, 2019
0629138
fixup! Add our own base-64 encoded ed25519 public key in dirserv_add_…
neelchauhan Jun 19, 2019
a4e4854
fixup! Add our own base-64 encoded ed25519 public key in dirserv_add_…
neelchauhan Jun 21, 2019
c812f07
Add dirserv_add_own_fingerprint() test
neelchauhan Jun 21, 2019
d568b7d
fixup! Add dirserv_load_fingerprint_file() test
neelchauhan Jun 21, 2019
8ee81fc
Don't duplicate calls to dirserv_get_status_impl() in dirserv_router_…
neelchauhan Sep 18, 2019
de30a66
Remove the ed25519 key from the routerstatus_t structure
neelchauhan Sep 23, 2019
c70c9ef
Merge the latest Tor master for better mergability
neelchauhan Sep 23, 2019
cfc3c08
fixup! Remove the ed25519 key from the routerstatus_t structure
neelchauhan Sep 23, 2019
2461fcc
fixup! Remove the ed25519 key from the routerstatus_t structure
neelchauhan Sep 23, 2019
7a95152
fixup! Remove the ed25519 key from the routerstatus_t structure
neelchauhan Sep 23, 2019
3686bd3
fixup! Merge the latest Tor master for better mergability
neelchauhan Sep 23, 2019
fcdde9d
fixup! Merge the latest Tor master for better mergability
neelchauhan Sep 23, 2019
d492f2f
fixup! Merge the latest Tor master for better mergability
neelchauhan Sep 23, 2019
68b68e3
fixup! Remove the ed25519 key from the routerstatus_t structure
neelchauhan Sep 23, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions changes/bug22029
@@ -0,0 +1,5 @@
o Major features (directory authority, ed25519):
Add support for banning a relay's ed25519 keys in the approved-routers
file. This will allow us to migrate away from RSA keys in the future.
Previously, only RSA keys could be banned in approved-routers. Resolves
ticket 22029. Patch by Neel Chauhan.
158 changes: 108 additions & 50 deletions src/feature/dirauth/process_descs.c
Expand Up @@ -38,19 +38,18 @@
#include "feature/nodelist/routerstatus_st.h"

#include "lib/encoding/confline.h"
#include "lib/crypt_ops/crypto_format.h"

/** How far in the future do we allow a router to get? (seconds) */
#define ROUTER_ALLOW_SKEW (60*60*12)

static void directory_remove_invalid(void);
struct authdir_config_t;
static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei,
const char **msg);
static uint32_t
dirserv_get_status_impl(const char *fp, const char *nickname,
uint32_t addr, uint16_t or_port,
const char *platform, const char **msg,
int severity);
dirserv_get_status_impl(const char *fp, const uint8_t *ed25519_key,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new argument should be "const ed25519_public_key_t *ed25519_public_key".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const char *nickname, uint32_t addr, uint16_t or_port,
const char *platform, const char **msg, int severity);

/* 1 Historically used to indicate Named */
#define FP_INVALID 2 /**< Believed invalid. */
Expand All @@ -59,20 +58,6 @@ dirserv_get_status_impl(const char *fp, const char *nickname,
#define FP_BADEXIT 16 /**< We'll tell clients not to use this as an exit. */
/* 32 Historically used to indicade Unnamed */

/** Target of status_by_digest map. */
typedef uint32_t router_status_t;

static void add_fingerprint_to_dir(const char *fp,
struct authdir_config_t *list,
router_status_t add_status);

/** List of nickname-\>identity fingerprint mappings for all the routers
* that we name. Used to prevent router impersonation. */
typedef struct authdir_config_t {
strmap_t *fp_by_name; /**< Map from lc nickname to fingerprint. */
digestmap_t *status_by_digest; /**< Map from digest to router_status_t. */
} authdir_config_t;

/** Should be static; exposed for testing. */
static authdir_config_t *fingerprint_list = NULL;

Expand All @@ -83,16 +68,35 @@ authdir_config_new(void)
authdir_config_t *list = tor_malloc_zero(sizeof(authdir_config_t));
list->fp_by_name = strmap_new();
list->status_by_digest = digestmap_new();
list->status_by_digest256 = digest256map_new();
return list;
}

#ifdef TOR_UNIT_TESTS

/** Initialize fingerprint_list to a new authdir_config_t. Used for tests. */
void
authdir_init_fingerprint_list(void)
{
fingerprint_list = authdir_config_new();
}

/* Return the current fingerprint_list. Used for tests. */
authdir_config_t *
authdir_return_fingerprint_list(void)
{
return fingerprint_list;
}

#endif /* defined(TOR_UNIT_TESTS) */

/** Add the fingerprint <b>fp</b> to the smartlist of fingerprint_entry_t's
* <b>list</b>, or-ing the currently set status flags with
* <b>add_status</b>.
* <b>list</b>, or-ing the currently set status flags with <b>add_status</b>.
* Return -1 if we were unable to decode the key, else return 0.
*/
/* static */ void
add_fingerprint_to_dir(const char *fp, authdir_config_t *list,
router_status_t add_status)
int
add_rsa_fingerprint_to_dir(const char *fp, authdir_config_t *list,
router_status_t add_status)
{
char *fingerprint;
char d[DIGEST_LEN];
Expand All @@ -104,10 +108,8 @@ add_fingerprint_to_dir(const char *fp, authdir_config_t *list,
tor_strstrip(fingerprint, " ");
if (base16_decode(d, DIGEST_LEN,
fingerprint, strlen(fingerprint)) != DIGEST_LEN) {
log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"",
escaped(fp));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't delete this warning: you're hiding failures for every caller of this function.

Instead, do a minimal length check in dirserv_load_fingerprint_file().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tor_free(fingerprint);
return;
return -1;
}

status = digestmap_get(list->status_by_digest, d);
Expand All @@ -118,7 +120,36 @@ add_fingerprint_to_dir(const char *fp, authdir_config_t *list,

tor_free(fingerprint);
*status |= add_status;
return;
return 0;
}

/** Add the ed25519 key <b>edkey</b> to the smartlist of fingerprint_entry_t's
* <b>list</b>, or-ing the currently set status flags with <b>add_status</b>.
* Return -1 if we were unable to decode the key, else return 0.
*/
int
add_ed25519_to_dir(const ed25519_public_key_t *edkey, authdir_config_t *list,
router_status_t add_status)
{
router_status_t *status;

tor_assert(edkey);
tor_assert(list);

if (ed25519_validate_pubkey(edkey) < 0) {
char ed25519_base64_key[ED25519_BASE64_LEN+1];
ed25519_public_to_base64(ed25519_base64_key, edkey);
return -1;
}

status = digest256map_get(list->status_by_digest256, edkey->pubkey);
if (!status) {
status = tor_malloc_zero(sizeof(router_status_t));
digest256map_set(list->status_by_digest256, edkey->pubkey, status);
}

*status |= add_status;
return 0;
}

/** Add the fingerprint for this OR to the global list of recognized
Expand All @@ -133,7 +164,7 @@ dirserv_add_own_fingerprint(crypto_pk_t *pk)
}
if (!fingerprint_list)
fingerprint_list = authdir_config_new();
add_fingerprint_to_dir(fp, fingerprint_list, 0);
add_rsa_fingerprint_to_dir(fp, fingerprint_list, 0);
return 0;
}

Expand Down Expand Up @@ -174,27 +205,44 @@ dirserv_load_fingerprint_file(void)
fingerprint_list_new = authdir_config_new();

for (list=front; list; list=list->next) {
char digest_tmp[DIGEST_LEN];
router_status_t add_status = 0;
nickname = list->key; fingerprint = list->value;
tor_strstrip(fingerprint, " "); /* remove spaces */
if (strlen(fingerprint) != HEX_DIGEST_LEN ||
base16_decode(digest_tmp, sizeof(digest_tmp),
fingerprint, HEX_DIGEST_LEN) != sizeof(digest_tmp)) {
log_notice(LD_CONFIG,
"Invalid fingerprint (nickname '%s', "
"fingerprint %s). Skipping.",
nickname, fingerprint);
continue;
}

/* Determine what we should do with the relay with the nickname field. */
if (!strcasecmp(nickname, "!reject")) {
add_status = FP_REJECT;
} else if (!strcasecmp(nickname, "!badexit")) {
add_status = FP_BADEXIT;
} else if (!strcasecmp(nickname, "!invalid")) {
add_status = FP_INVALID;
}
add_fingerprint_to_dir(fingerprint, fingerprint_list_new, add_status);

/* Check if fingerprint is RSA or ed25519 by verifying it. */
int ed25519_not_ok, rsa_not_ok;

/* Attempt to add the RSA key. */
rsa_not_ok = add_rsa_fingerprint_to_dir(fingerprint, fingerprint_list_new,
add_status);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is unsafe: a malicious relay could have an ed25519 base64 encoded public key, that also passes the hexdigest check.

Please check the length of the string before passing it to this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/* Check ed25519 key. We check the size to prevent buffer overflows.
* If valid, attempt to add it, */
ed25519_public_key_t ed25519_pubkey_tmp;
if (strlen(fingerprint) == BASE64_DIGEST256_LEN &&
digest256_from_base64((char *) ed25519_pubkey_tmp.pubkey,
Copy link
Contributor

@teor2345 teor2345 May 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a logic error: digest256_from_base64() returns 0 on success.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your tests didn't pick this up: please add a commit with a test that fails on this code.
And then write a fix to make the test pass.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code would be easier to read, if the decoding was outside the "if" expression.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9520e28

However, I didn't move the decoding outside the "if" expression.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened a new ticket for the decoding refactor:
https://trac.torproject.org/projects/tor/ticket/30691

And the missing unit test cases:
https://trac.torproject.org/projects/tor/ticket/30692#ticket

fingerprint)) {
ed25519_not_ok = add_ed25519_to_dir(&ed25519_pubkey_tmp,
fingerprint_list_new, add_status);
} else {
ed25519_not_ok = -1;
}

/* If this is not a valid key, log and skip */
if (ed25519_not_ok && rsa_not_ok) {
log_warn(LD_CONFIG, "Invalid fingerprint (nickname '%s', "
"fingerprint %s). Skipping.", nickname, fingerprint);
continue;
}
}

config_free_lines(front);
Expand Down Expand Up @@ -237,9 +285,11 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
}

/* Check for the more usual versions to reject a router first. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't make sense.
Let's fix it while we are here?

"First, check for the more common reasons to reject a router"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const uint32_t r = dirserv_get_status_impl(d, router->nickname,
router->addr, router->or_port,
router->platform, msg, severity);
uint32_t r = dirserv_get_status_impl(d,
router->cache_info.signing_key_cert->signing_key.pubkey,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the router does not have an ed25519 key?
This code will crash.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (router->cache_info.signing_key_cert) {
/* This has an ed25519 identity key. */

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

router->nickname, router->addr, router->or_port, router->platform, msg,
severity);

if (r)
return r;

Expand Down Expand Up @@ -303,23 +353,23 @@ dirserv_would_reject_router(const routerstatus_t *rs)
{
uint32_t res;

res = dirserv_get_status_impl(rs->identity_digest, rs->nickname,
rs->addr, rs->or_port,
NULL, NULL, LOG_DEBUG);
res = dirserv_get_status_impl(rs->identity_digest,
rs->ed25519_signing_key.pubkey, rs->nickname,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the router does not have an ed25519 key?
This code will crash.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rs->addr, rs->or_port, NULL, NULL, LOG_DEBUG);

return (res & FP_REJECT) != 0;
}

/** Helper: As dirserv_router_get_status, but takes the router fingerprint
* (hex, no spaces), nickname, address (used for logging only), IP address, OR
* port and platform (logging only) as arguments.
* (hex, no spaces), ed25510 key, nickname, address (used for logging only),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: ed25519

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* IP address, OR port and platform (logging only) as arguments.
*
* Log messages at 'severity'. (There's not much point in
* logging that we're rejecting servers we'll not download.)
*/
static uint32_t
dirserv_get_status_impl(const char *id_digest, const char *nickname,
uint32_t addr, uint16_t or_port,
dirserv_get_status_impl(const char *id_digest, const uint8_t *ed25519_key,
const char *nickname, uint32_t addr, uint16_t or_port,
const char *platform, const char **msg, int severity)
{
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
uint32_t result = 0;
Expand Down Expand Up @@ -366,6 +416,13 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
if (status_by_digest)
result |= *status_by_digest;

if (ed25519_key) {
status_by_digest = digest256map_get(fingerprint_list->status_by_digest256,
ed25519_key);
if (status_by_digest)
result |= *status_by_digest;
}

if (result & FP_REJECT) {
if (msg)
*msg = "Fingerprint is marked rejected -- if you think this is a "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to update this message to say "mentioning your RSA fingerprint and ed25519 identity"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expand Down Expand Up @@ -414,6 +471,7 @@ dirserv_free_fingerprint_list(void)

strmap_free(fingerprint_list->fp_by_name, tor_free_);
digestmap_free(fingerprint_list->status_by_digest, tor_free_);
digest256map_free(fingerprint_list->status_by_digest256, tor_free_);
tor_free(fingerprint_list);
}

Expand Down
31 changes: 31 additions & 0 deletions src/feature/dirauth/process_descs.h
Expand Up @@ -12,6 +12,37 @@
#ifndef TOR_RECV_UPLOADS_H
#define TOR_RECV_UPLOADS_H

#include "src/lib/crypt_ops/crypto_ed25519.h"

struct authdir_config_t;

/** Target of status_by_digest map. */
typedef uint32_t router_status_t;

int add_rsa_fingerprint_to_dir(const char *fp, struct authdir_config_t *list,
router_status_t add_status);

int add_ed25519_to_dir(const ed25519_public_key_t *edkey,
struct authdir_config_t *list,
router_status_t add_status);

/** List of nickname-\>identity fingerprint mappings for all the routers
* that we name. Used to prevent router impersonation. */
typedef struct authdir_config_t {
strmap_t *fp_by_name; /**< Map from lc nickname to fingerprint. */
digestmap_t *status_by_digest; /**< Map from digest to router_status_t. */
digest256map_t *status_by_digest256; /**< Map from digest256 to
* router_status_t. */
} authdir_config_t;

#ifdef TOR_UNIT_TESTS

void authdir_init_fingerprint_list(void);

authdir_config_t *authdir_return_fingerprint_list(void);

#endif /* defined(TOR_UNIT_TESTS) */

int dirserv_load_fingerprint_file(void);
void dirserv_free_fingerprint_list(void);
int dirserv_add_own_fingerprint(crypto_pk_t *pk);
Expand Down
6 changes: 6 additions & 0 deletions src/feature/dirauth/voteflags.c
Expand Up @@ -30,6 +30,7 @@
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/vote_routerstatus_st.h"
#include "feature/nodelist/torcert.h"

#include "lib/container/order.h"

Expand Down Expand Up @@ -608,6 +609,11 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
rs->dir_port = ri->dir_port;
rs->is_v2_dir = ri->supports_tunnelled_dir_requests;

if (ri->cache_info.signing_key_cert) {
ed25519_pubkey_copy(&rs->ed25519_signing_key,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code leaves the key zeroed-out if the relay does not have an ed25529 key.
Instead, allocate memory for the key if it is non-NULL.
And free that memory in routerstatus_free_().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&ri->cache_info.signing_key_cert->signing_key);
}

rs->is_staledesc =
(ri->cache_info.published_on + DESC_IS_STALE_INTERVAL) < now;

Expand Down
3 changes: 3 additions & 0 deletions src/feature/nodelist/routerstatus_st.h
Expand Up @@ -8,6 +8,7 @@
#define ROUTERSTATUS_ST_H

#include "feature/dirclient/download_status_st.h"
#include "lib/crypt_ops/crypto_ed25519.h"

/** Contents of a single router entry in a network status object.
*/
Expand All @@ -20,6 +21,8 @@ struct routerstatus_t {
/** Digest of the router's most recent descriptor or microdescriptor.
* If it's a descriptor, we only use the first DIGEST_LEN bytes. */
char descriptor_digest[DIGEST256_LEN];
ed25519_public_key_t ed25519_signing_key; /**< The ed25519 signing key of
* this router. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be a pointer: "ed25519_public_key_t *ed25519_signing_key".
Some routers do not have an ed25519 key.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uint32_t addr; /**< IPv4 address for this router, in host order. */
uint16_t or_port; /**< IPv4 OR port for this router. */
uint16_t dir_port; /**< Directory port for this router. */
Expand Down