📄 dirserv.c
字号:
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
const char dirserv_c_id[] =
"$Id$";
#define DIRSERV_PRIVATE
#include "or.h"
/**
* \file dirserv.c
* \brief Directory server core implementation. Manages directory
* contents and generates directories.
*/
/** How far in the future do we allow a router to get? (seconds) */
#define ROUTER_ALLOW_SKEW (60*60*12)
/** How many seconds do we wait before regenerating the directory? */
#define DIR_REGEN_SLACK_TIME 30
/** If we're a cache, keep this many networkstatuses around from non-trusted
* directory authorities. */
#define MAX_UNTRUSTED_NETWORKSTATUSES 16
/** If a v1 directory is older than this, discard it. */
#define MAX_V1_DIRECTORY_AGE (30*24*60*60)
/** If a v1 running-routers is older than this, discard it. */
#define MAX_V1_RR_AGE (7*24*60*60)
extern time_t time_of_process_start; /* from main.c */
/** Do we need to regenerate the directory when someone asks for it? */
static time_t the_directory_is_dirty = 1;
static time_t runningrouters_is_dirty = 1;
static time_t the_v2_networkstatus_is_dirty = 1;
/** Most recently generated encoded signed v1 directory. (v1 auth dirservers
* only.) */
static cached_dir_t *the_directory = NULL;
/** For authoritative directories: the current (v1) network status. */
static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0, -1 };
static void directory_remove_invalid(void);
static cached_dir_t *dirserv_regenerate_directory(void);
static char *format_versions_list(config_line_t *ln);
struct authdir_config_t;
static int add_fingerprint_to_dir(const char *nickname, const char *fp,
struct authdir_config_t *list);
static uint32_t dirserv_router_get_status(const routerinfo_t *router,
const char **msg);
static uint32_t
dirserv_get_status_impl(const char *fp, const char *nickname,
const char *address,
uint32_t addr, uint16_t or_port,
const char *platform, const char *contact,
const char **msg, int should_log);
static void clear_cached_dir(cached_dir_t *d);
static signed_descriptor_t *get_signed_descriptor_by_fp(const char *fp,
int extrainfo,
time_t publish_cutoff);
static int dirserv_add_extrainfo(extrainfo_t *ei, const char **msg);
/************** Fingerprint handling code ************/
#define FP_NAMED 1 /**< Listed in fingerprint file. */
#define FP_INVALID 2 /**< Believed invalid. */
#define FP_REJECT 4 /**< We will not publish this router. */
#define FP_BADDIR 8 /**< We'll tell clients to avoid using this as a dir. */
#define FP_BADEXIT 16 /**< We'll tell clients not to use this as an exit. */
#define FP_UNNAMED 32 /**< Another router has this name in fingerprint file. */
/** Encapsulate a nickname and an FP_* status; target of status_by_digest
* map. */
typedef struct router_status_t {
char nickname[MAX_NICKNAME_LEN+1];
uint32_t status;
} router_status_t;
/** 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;
/** Allocate and return a new, empty, authdir_config_t. */
static authdir_config_t *
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();
return list;
}
/** Add the fingerprint <b>fp</b> for the nickname <b>nickname</b> to
* the smartlist of fingerprint_entry_t's <b>list</b>. Return 0 if it's
* new, or 1 if we replaced the old value.
*/
/* static */ int
add_fingerprint_to_dir(const char *nickname, const char *fp,
authdir_config_t *list)
{
char *fingerprint;
char d[DIGEST_LEN];
router_status_t *status;
tor_assert(nickname);
tor_assert(fp);
tor_assert(list);
fingerprint = tor_strdup(fp);
tor_strstrip(fingerprint, " ");
if (base16_decode(d, DIGEST_LEN, fingerprint, strlen(fingerprint))) {
log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"",
escaped(fp));
tor_free(fingerprint);
return 0;
}
if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME)) {
log_warn(LD_DIRSERV, "Tried to add a mapping for reserved nickname %s",
UNNAMED_ROUTER_NICKNAME);
tor_free(fingerprint);
return 0;
}
status = digestmap_get(list->status_by_digest, d);
if (!status) {
status = tor_malloc_zero(sizeof(router_status_t));
digestmap_set(list->status_by_digest, d, status);
}
if (nickname[0] != '!') {
char *old_fp = strmap_get_lc(list->fp_by_name, nickname);
if (old_fp && !strcasecmp(fingerprint, old_fp)) {
tor_free(fingerprint);
} else {
tor_free(old_fp);
strmap_set_lc(list->fp_by_name, nickname, fingerprint);
}
status->status |= FP_NAMED;
strlcpy(status->nickname, nickname, sizeof(status->nickname));
} else {
tor_free(fingerprint);
if (!strcasecmp(nickname, "!reject")) {
status->status |= FP_REJECT;
} else if (!strcasecmp(nickname, "!invalid")) {
status->status |= FP_INVALID;
} else if (!strcasecmp(nickname, "!baddir")) {
status->status |= FP_BADDIR;
} else if (!strcasecmp(nickname, "!badexit")) {
status->status |= FP_BADEXIT;
}
}
return 0;
}
/** Add the nickname and fingerprint for this OR to the
* global list of recognized identity key fingerprints. */
int
dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk)
{
char fp[FINGERPRINT_LEN+1];
if (crypto_pk_get_fingerprint(pk, fp, 0)<0) {
log_err(LD_BUG, "Error computing fingerprint");
return -1;
}
if (!fingerprint_list)
fingerprint_list = authdir_config_new();
add_fingerprint_to_dir(nickname, fp, fingerprint_list);
return 0;
}
/** Load the nickname-\>fingerprint mappings stored in the approved-routers
* file. The file format is line-based, with each non-blank holding one
* nickname, some space, and a fingerprint for that nickname. On success,
* replace the current fingerprint list with the new list and return 0. On
* failure, leave the current fingerprint list untouched, and
* return -1. */
int
dirserv_load_fingerprint_file(void)
{
char *fname;
char *cf;
char *nickname, *fingerprint;
authdir_config_t *fingerprint_list_new;
int result;
config_line_t *front=NULL, *list;
or_options_t *options = get_options();
fname = get_datadir_fname("approved-routers");
log_info(LD_GENERAL,
"Reloading approved fingerprints from \"%s\"...", fname);
cf = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
if (!cf) {
if (options->NamingAuthoritativeDir) {
log_warn(LD_FS, "Cannot open fingerprint file '%s'. Failing.", fname);
tor_free(fname);
return -1;
} else {
log_info(LD_FS, "Cannot open fingerprint file '%s'. That's ok.", fname);
tor_free(fname);
return 0;
}
}
tor_free(fname);
result = config_get_lines(cf, &front);
tor_free(cf);
if (result < 0) {
log_warn(LD_CONFIG, "Error reading from fingerprint file");
return -1;
}
fingerprint_list_new = authdir_config_new();
for (list=front; list; list=list->next) {
nickname = list->key; fingerprint = list->value;
if (strlen(nickname) > MAX_NICKNAME_LEN) {
log_notice(LD_CONFIG,
"Nickname '%s' too long in fingerprint file. Skipping.",
nickname);
continue;
}
if (!is_legal_nickname(nickname) &&
strcasecmp(nickname, "!reject") &&
strcasecmp(nickname, "!invalid") &&
strcasecmp(nickname, "!badexit")) {
log_notice(LD_CONFIG,
"Invalid nickname '%s' in fingerprint file. Skipping.",
nickname);
continue;
}
if (strlen(fingerprint) != FINGERPRINT_LEN ||
!crypto_pk_check_fingerprint_syntax(fingerprint)) {
log_notice(LD_CONFIG,
"Invalid fingerprint (nickname '%s', "
"fingerprint %s). Skipping.",
nickname, fingerprint);
continue;
}
if (0==strcasecmp(nickname, DEFAULT_CLIENT_NICKNAME)) {
/* If you approved an OR called "client", then clients who use
* the default nickname could all be rejected. That's no good. */
log_notice(LD_CONFIG,
"Authorizing nickname '%s' would break "
"many clients; skipping.",
DEFAULT_CLIENT_NICKNAME);
continue;
}
if (0==strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME)) {
/* If you approved an OR called "unnamed", then clients will be
* confused. */
log_notice(LD_CONFIG,
"Authorizing nickname '%s' is not allowed; skipping.",
UNNAMED_ROUTER_NICKNAME);
continue;
}
if (add_fingerprint_to_dir(nickname, fingerprint, fingerprint_list_new)
!= 0)
log_notice(LD_CONFIG, "Duplicate nickname '%s'.", nickname);
}
config_free_lines(front);
dirserv_free_fingerprint_list();
fingerprint_list = fingerprint_list_new;
/* Delete any routers whose fingerprints we no longer recognize */
directory_remove_invalid();
return 0;
}
/** Check whether <b>router</b> has a nickname/identity key combination that
* we recognize from the fingerprint list, or an IP we automatically act on
* according to our configuration. Return the appropriate router status.
*
* If the status is 'FP_REJECT' and <b>msg</b> is provided, set
* *<b>msg</b> to an explanation of why. */
static uint32_t
dirserv_router_get_status(const routerinfo_t *router, const char **msg)
{
char d[DIGEST_LEN];
if (crypto_pk_get_digest(router->identity_pkey, d)) {
log_warn(LD_BUG,"Error computing fingerprint");
if (msg)
*msg = "Bug: Error computing fingerprint";
return FP_REJECT;
}
return dirserv_get_status_impl(d, router->nickname,
router->address,
router->addr, router->or_port,
router->platform, router->contact_info,
msg, 1);
}
/** Return true if there is no point in downloading the router described by
* <b>rs</b> because this directory would reject it. */
int
dirserv_would_reject_router(routerstatus_t *rs)
{
uint32_t res;
res = dirserv_get_status_impl(rs->identity_digest, rs->nickname,
"", /* address is only used in logs */
rs->addr, rs->or_port,
NULL, NULL,
NULL, 0);
return (res & FP_REJECT) != 0;
}
/** Helper: Based only on the ID/Nickname combination,
* return FP_UNNAMED (unnamed), FP_NAMED (named), or 0 (neither).
*/
static uint32_t
dirserv_get_name_status(const char *id_digest, const char *nickname)
{
char fp[HEX_DIGEST_LEN+1];
char *fp_by_name;
base16_encode(fp, sizeof(fp), id_digest, DIGEST_LEN);
if ((fp_by_name =
strmap_get_lc(fingerprint_list->fp_by_name, nickname))) {
if (!strcasecmp(fp, fp_by_name)) {
return FP_NAMED;
} else {
return FP_UNNAMED; /* Wrong fingerprint. */
}
}
return 0;
}
/** Helper: As dirserv_get_router_status, but takes the router fingerprint
* (hex, no spaces), nickname, address (used for logging only), IP address, OR
* port, platform (logging only) and contact info (logging only) as arguments.
*
* If should_log is false, do not log messages. (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,
const char *address,
uint32_t addr, uint16_t or_port,
const char *platform, const char *contact,
const char **msg, int should_log)
{
int reject_unlisted = get_options()->AuthDirRejectUnlisted;
uint32_t result = 0;
router_status_t *status_by_digest;
if (!fingerprint_list)
fingerprint_list = authdir_config_new();
if (should_log)
log_debug(LD_DIRSERV, "%d fingerprints, %d digests known.",
strmap_size(fingerprint_list->fp_by_name),
digestmap_size(fingerprint_list->status_by_digest));
result = dirserv_get_name_status(id_digest, nickname);
if (result & FP_NAMED) {
if (should_log)
log_debug(LD_DIRSERV,"Good fingerprint for '%s'",nickname);
}
if (result & FP_UNNAMED) {
if (should_log) {
char *esc_contact = esc_for_log(contact);
log_info(LD_DIRSERV,
"Mismatched fingerprint for '%s'. "
"ContactInfo '%s', platform '%s'.)",
nickname,
esc_contact,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -