Skip to content

Commit

Permalink
Allow ed25519 keys to be banned in approved-routers
Browse files Browse the repository at this point in the history
  • Loading branch information
neelchauhan committed Apr 19, 2019
1 parent ef97e6c commit 6afa953
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 36 deletions.
5 changes: 5 additions & 0 deletions changes/bug22029
Original file line number Diff line number Diff line change
@@ -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: 122 additions & 36 deletions src/feature/dirauth/process_descs.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@
#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
Expand All @@ -59,20 +59,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,14 +69,33 @@ 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
int
add_fingerprint_to_dir(const char *fp, authdir_config_t *list,
router_status_t add_status)
{
Expand All @@ -107,7 +112,7 @@ add_fingerprint_to_dir(const char *fp, authdir_config_t *list,
log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"",
escaped(fp));
tor_free(fingerprint);
return;
return -1;
}

status = digestmap_get(list->status_by_digest, d);
Expand All @@ -118,7 +123,37 @@ 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);
log_warn(LD_DIRSERV, "Invalid ed25519 key \"%s\"", ed25519_base64_key);
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 Down Expand Up @@ -174,27 +209,62 @@ 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 fingerprint type by key length, and if it's RSA or ed25519. */
int is_ed25519 = (strlen(fingerprint) == BASE64_DIGEST256_LEN);
int is_rsa = (strlen(fingerprint) == HEX_DIGEST_LEN);
bool is_valid_fpr = true;

/* We only accept ed25519 or RSA keys */
if (!is_ed25519 && !is_rsa) {
is_valid_fpr = false;

log_warn(LD_CONFIG, "Unrecognized fingerprint type (nickname '%s', "
"fingerprint %s). Skipping.", nickname, fingerprint);
continue;
}

/* Decode ed25519 keys */
ed25519_public_key_t ed25519_pubkey_tmp;
if (is_ed25519 && digest256_from_base64((char *) ed25519_pubkey_tmp.pubkey,
fingerprint) < 0) {
is_valid_fpr = false;
}

/* Decode RSA keys */
char digest_tmp[DIGEST_LEN];
if (is_rsa &&
base16_decode(digest_tmp, sizeof(digest_tmp), fingerprint,
HEX_DIGEST_LEN) != sizeof(digest_tmp)) {
is_valid_fpr = false;
}

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

/* It's a valid key, register it. */
if (is_ed25519) {
add_ed25519_to_dir(&ed25519_pubkey_tmp,
fingerprint_list_new, add_status);
} else if (is_rsa) {
add_fingerprint_to_dir(fingerprint, fingerprint_list_new, add_status);
}
}

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

/* Check for the more usual versions to reject a router first. */
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->nickname, router->addr,
router->or_port, router->platform,
msg, severity);
if (r)
return r;

Expand Down Expand Up @@ -272,6 +342,14 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
}
return FP_REJECT;
}

/* Check status with ed25519 key. */
r = dirserv_get_status_impl(
(char *) router->cache_info.signing_key_cert->signing_key.pubkey,
router->nickname, router->addr, router->or_port,
router->platform, msg, severity);
if (r)
return r;
}
} else {
/* No ed25519 key */
Expand Down Expand Up @@ -324,6 +402,7 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
{
uint32_t result = 0;
router_status_t *status_by_digest;
int is_ed25519 = (strlen(id_digest) == ED25519_PUBKEY_LEN);

if (!fingerprint_list)
fingerprint_list = authdir_config_new();
Expand Down Expand Up @@ -361,8 +440,14 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
return FP_REJECT;
}

status_by_digest = digestmap_get(fingerprint_list->status_by_digest,
id_digest);
if (is_ed25519) {
status_by_digest = digest256map_get(fingerprint_list->status_by_digest256,
(uint8_t *) id_digest);
} else { /* RSA */
status_by_digest = digestmap_get(fingerprint_list->status_by_digest,
id_digest);
}

if (status_by_digest)
result |= *status_by_digest;

Expand Down Expand Up @@ -414,6 +499,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
Original file line number Diff line number Diff line change
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_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

0 comments on commit 6afa953

Please sign in to comment.