Merge remote-tracking branch 'andrea/bug18322_v3_squashed'

This commit is contained in:
Nick Mathewson 2016-06-30 11:18:00 -04:00
commit c6846d7bf0
12 changed files with 1652 additions and 38 deletions

4
changes/bug18322 Normal file
View File

@ -0,0 +1,4 @@
o Bugfixes:
- When dumping unparseable router descriptors, optionally store them in
separate filenames by hash, up to a configurable limit.
Fixes bug 18322; bugfix on ???.

View File

@ -602,6 +602,13 @@ GENERAL OPTIONS
message currently has at least one domain; most currently have exactly
one. This doesn't affect controller log messages. (Default: 0)
[[MaxUnparseableDescSizeToLog]] **MaxUnparseableDescSizeToLog** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**::
Unparseable descriptors (e.g. for votes, consensuses, routers) are logged
in separate files by hash, up to the specified size in total. Note that
only files logged during the lifetime of this Tor process count toward the
total; this is intended to be used to debug problems without opening live
servers to resource exhaustion attacks. (Default: 10 MB)
[[OutboundBindAddress]] **OutboundBindAddress** __IP__::
Make all outbound connections originate from the IP address specified. This
is only useful when you have multiple network interfaces, and you want all

View File

@ -6,8 +6,10 @@
#ifdef TOR_UNIT_TESTS
#define STATIC
#define EXTERN(type, name) extern type name;
#else
#define STATIC static
#define EXTERN(type, name)
#endif
/** Quick and dirty macros to implement test mocking.
@ -60,6 +62,12 @@
#define MOCK_IMPL(rv, funcname, arglist) \
rv(*funcname) arglist = funcname ##__real; \
rv funcname ##__real arglist
#define MOCK_DECL_ATTR(rv, funcname, arglist, attr) \
rv funcname ##__real arglist attr; \
extern rv(*funcname) arglist
#define MOCK_IMPL(rv, funcname, arglist) \
rv(*funcname) arglist = funcname ##__real; \
rv funcname ##__real arglist
#define MOCK(func, replacement) \
do { \
(func) = (replacement); \
@ -71,6 +79,8 @@
#else
#define MOCK_DECL(rv, funcname, arglist) \
rv funcname arglist
#define MOCK_DECL_ATTR(rv, funcname, arglist, attr) \
rv funcname arglist attr
#define MOCK_IMPL(rv, funcname, arglist) \
rv funcname arglist
#endif

View File

@ -2103,6 +2103,16 @@ clean_name_for_stat(char *name)
#endif
}
/** Wrapper for unlink() to make it mockable for the test suite; returns 0
* if unlinking the file succeeded, -1 and sets errno if unlinking fails.
*/
MOCK_IMPL(int,
tor_unlink,(const char *pathname))
{
return unlink(pathname);
}
/** Return:
* FN_ERROR if filename can't be read, is NULL, or is zero-length,
* FN_NOENT if it doesn't exist,
@ -2166,9 +2176,9 @@ file_status(const char *fname)
* When effective_user is not NULL, check permissions against the given user
* and its primary group.
*/
int
check_private_dir(const char *dirname, cpd_check_t check,
const char *effective_user)
MOCK_IMPL(int,
check_private_dir,(const char *dirname, cpd_check_t check,
const char *effective_user))
{
int r;
struct stat st;
@ -2396,8 +2406,8 @@ check_private_dir(const char *dirname, cpd_check_t check,
* function, and all other functions in util.c that create files, create them
* with mode 0600.
*/
int
write_str_to_file(const char *fname, const char *str, int bin)
MOCK_IMPL(int,
write_str_to_file,(const char *fname, const char *str, int bin))
{
#ifdef _WIN32
if (!bin && strchr(str, '\r')) {
@ -2756,8 +2766,8 @@ read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out)
* the call to stat and the call to read_all: the resulting string will
* be truncated.
*/
char *
read_file_to_str(const char *filename, int flags, struct stat *stat_out)
MOCK_IMPL(char *,
read_file_to_str, (const char *filename, int flags, struct stat *stat_out))
{
int fd; /* router file */
struct stat statbuf;
@ -3492,8 +3502,8 @@ smartlist_add_vasprintf(struct smartlist_t *sl, const char *pattern,
/** Return a new list containing the filenames in the directory <b>dirname</b>.
* Return NULL on error or if <b>dirname</b> is not a directory.
*/
smartlist_t *
tor_listdir(const char *dirname)
MOCK_IMPL(smartlist_t *,
tor_listdir, (const char *dirname))
{
smartlist_t *result;
#ifdef _WIN32

View File

@ -309,6 +309,8 @@ const char *stream_status_to_string(enum stream_status stream_status);
enum stream_status get_string_from_pipe(FILE *stream, char *buf, size_t count);
MOCK_DECL(int,tor_unlink,(const char *pathname));
/** Return values from file_status(); see that function's documentation
* for details. */
typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR, FN_EMPTY } file_status_t;
@ -324,8 +326,9 @@ typedef unsigned int cpd_check_t;
#define CPD_GROUP_READ (1u << 3)
#define CPD_CHECK_MODE_ONLY (1u << 4)
#define CPD_RELAX_DIRMODE_CHECK (1u << 5)
int check_private_dir(const char *dirname, cpd_check_t check,
const char *effective_user);
MOCK_DECL(int, check_private_dir,
(const char *dirname, cpd_check_t check,
const char *effective_user));
#define OPEN_FLAGS_REPLACE (O_WRONLY|O_CREAT|O_TRUNC)
#define OPEN_FLAGS_APPEND (O_WRONLY|O_CREAT|O_APPEND)
@ -338,7 +341,8 @@ FILE *start_writing_to_stdio_file(const char *fname, int open_flags, int mode,
FILE *fdopen_file(open_file_t *file_data);
int finish_writing_to_file(open_file_t *file_data);
int abort_writing_to_file(open_file_t *file_data);
int write_str_to_file(const char *fname, const char *str, int bin);
MOCK_DECL(int,
write_str_to_file,(const char *fname, const char *str, int bin));
MOCK_DECL(int,
write_bytes_to_file,(const char *fname, const char *str, size_t len,
int bin));
@ -363,8 +367,9 @@ int write_bytes_to_new_file(const char *fname, const char *str, size_t len,
#ifndef _WIN32
struct stat;
#endif
char *read_file_to_str(const char *filename, int flags, struct stat *stat_out)
ATTR_MALLOC;
MOCK_DECL_ATTR(char *, read_file_to_str,
(const char *filename, int flags, struct stat *stat_out),
ATTR_MALLOC);
char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read,
size_t *sz_out)
ATTR_MALLOC;
@ -372,7 +377,7 @@ const char *parse_config_line_from_str_verbose(const char *line,
char **key_out, char **value_out,
const char **err_out);
char *expand_filename(const char *filename);
struct smartlist_t *tor_listdir(const char *dirname);
MOCK_DECL(struct smartlist_t *, tor_listdir, (const char *dirname));
int path_is_relative(const char *filename);
/* Process helpers */

View File

@ -325,6 +325,7 @@ static config_var_t option_vars_[] = {
VAR("MaxMemInQueues", MEMUNIT, MaxMemInQueues_raw, "0"),
OBSOLETE("MaxOnionsPending"),
V(MaxOnionQueueDelay, MSEC_INTERVAL, "1750 msec"),
V(MaxUnparseableDescSizeToLog, MEMUNIT, "10 MB"),
V(MinMeasuredBWsForAuthToIgnoreAdvertised, INT, "500"),
V(MyFamily, STRING, NULL),
V(NewCircuitPeriod, INTERVAL, "30 seconds"),
@ -7228,10 +7229,10 @@ init_libevent(const or_options_t *options)
*
* Note: Consider using the get_datadir_fname* macros in or.h.
*/
char *
options_get_datadir_fname2_suffix(const or_options_t *options,
const char *sub1, const char *sub2,
const char *suffix)
MOCK_IMPL(char *,
options_get_datadir_fname2_suffix,(const or_options_t *options,
const char *sub1, const char *sub2,
const char *suffix))
{
char *fname = NULL;
size_t len;

View File

@ -53,9 +53,11 @@ config_line_t *option_get_assignment(const or_options_t *options,
const char *key);
int options_save_current(void);
const char *get_torrc_fname(int defaults_fname);
char *options_get_datadir_fname2_suffix(const or_options_t *options,
const char *sub1, const char *sub2,
const char *suffix);
MOCK_DECL(char *,
options_get_datadir_fname2_suffix,
(const or_options_t *options,
const char *sub1, const char *sub2,
const char *suffix));
#define get_datadir_fname2_suffix(sub1, sub2, suffix) \
options_get_datadir_fname2_suffix(get_options(), (sub1), (sub2), (suffix))
/** Return a newly allocated string containing datadir/sub1. See

View File

@ -3045,6 +3045,9 @@ tor_init(int argc, char *argv[])
log_warn(LD_NET, "Problem initializing libevent RNG.");
}
/* Scan/clean unparseable descroptors; after reading config */
routerparse_init();
return 0;
}
@ -3146,6 +3149,7 @@ tor_free_all(int postfork)
scheduler_free_all();
nodelist_free_all();
microdesc_free_all();
routerparse_free_all();
ext_orport_free_all();
control_free_all();
sandbox_free_getaddrinfo_cache();

View File

@ -4498,6 +4498,11 @@ typedef struct {
/** Autobool: Do we try to retain capabilities if we can? */
int KeepBindCapabilities;
/** Maximum total size of unparseable descriptors to log during the
* lifetime of this Tor process.
*/
uint64_t MaxUnparseableDescSizeToLog;
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */

View File

@ -28,6 +28,7 @@
#include "routerparse.h"
#include "entrynodes.h"
#include "torcert.h"
#include "sandbox.h"
#undef log
#include <math.h>
@ -585,32 +586,561 @@ static int check_signature_token(const char *digest,
#define DUMP_AREA(a,name) STMT_NIL
#endif
/** Last time we dumped a descriptor to disk. */
static time_t last_desc_dumped = 0;
/* Dump mechanism for unparseable descriptors */
/** List of dumped descriptors for FIFO cleanup purposes */
STATIC smartlist_t *descs_dumped = NULL;
/** Total size of dumped descriptors for FIFO cleanup */
STATIC uint64_t len_descs_dumped = 0;
/** Directory to stash dumps in */
static int have_dump_desc_dir = 0;
static int problem_with_dump_desc_dir = 0;
#define DESC_DUMP_DATADIR_SUBDIR "unparseable-descs"
#define DESC_DUMP_BASE_FILENAME "unparseable-desc"
/** Find the dump directory and check if we'll be able to create it */
static void
dump_desc_init(void)
{
char *dump_desc_dir;
dump_desc_dir = get_datadir_fname(DESC_DUMP_DATADIR_SUBDIR);
/*
* We just check for it, don't create it at this point; we'll
* create it when we need it if it isn't already there.
*/
if (check_private_dir(dump_desc_dir, CPD_CHECK, get_options()->User) < 0) {
/* Error, log and flag it as having a problem */
log_notice(LD_DIR,
"Doesn't look like we'll be able to create descriptor dump "
"directory %s; dumps will be disabled.",
dump_desc_dir);
problem_with_dump_desc_dir = 1;
tor_free(dump_desc_dir);
return;
}
/* Check if it exists */
switch (file_status(dump_desc_dir)) {
case FN_DIR:
/* We already have a directory */
have_dump_desc_dir = 1;
break;
case FN_NOENT:
/* Nothing, we'll need to create it later */
have_dump_desc_dir = 0;
break;
case FN_ERROR:
/* Log and flag having a problem */
log_notice(LD_DIR,
"Couldn't check whether descriptor dump directory %s already"
" exists: %s",
dump_desc_dir, strerror(errno));
problem_with_dump_desc_dir = 1;
case FN_FILE:
case FN_EMPTY:
default:
/* Something else was here! */
log_notice(LD_DIR,
"Descriptor dump directory %s already exists and isn't a "
"directory",
dump_desc_dir);
problem_with_dump_desc_dir = 1;
}
if (have_dump_desc_dir && !problem_with_dump_desc_dir) {
dump_desc_populate_fifo_from_directory(dump_desc_dir);
}
tor_free(dump_desc_dir);
}
/** Create the dump directory if needed and possible */
static void
dump_desc_create_dir(void)
{
char *dump_desc_dir;
/* If the problem flag is set, skip it */
if (problem_with_dump_desc_dir) return;
/* Do we need it? */
if (!have_dump_desc_dir) {
dump_desc_dir = get_datadir_fname(DESC_DUMP_DATADIR_SUBDIR);
if (check_private_dir(dump_desc_dir, CPD_CREATE,
get_options()->User) < 0) {
log_notice(LD_DIR,
"Failed to create descriptor dump directory %s",
dump_desc_dir);
problem_with_dump_desc_dir = 1;
}
/* Okay, we created it */
have_dump_desc_dir = 1;
tor_free(dump_desc_dir);
}
}
/** Dump desc FIFO/cleanup; take ownership of the given filename, add it to
* the FIFO, and clean up the oldest entries to the extent they exceed the
* configured cap. If any old entries with a matching hash existed, they
* just got overwritten right before this was called and we should adjust
* the total size counter without deleting them.
*/
static void
dump_desc_fifo_add_and_clean(char *filename, const uint8_t *digest_sha256,
size_t len)
{
dumped_desc_t *ent = NULL, *tmp;
uint64_t max_len;
tor_assert(filename != NULL);
tor_assert(digest_sha256 != NULL);
if (descs_dumped == NULL) {
/* We better have no length, then */
tor_assert(len_descs_dumped == 0);
/* Make a smartlist */
descs_dumped = smartlist_new();
}
/* Make a new entry to put this one in */
ent = tor_malloc_zero(sizeof(*ent));
ent->filename = filename;
ent->len = len;
ent->when = time(NULL);
memcpy(ent->digest_sha256, digest_sha256, DIGEST256_LEN);
/* Do we need to do some cleanup? */
max_len = get_options()->MaxUnparseableDescSizeToLog;
/* Iterate over the list until we've freed enough space */
while (len > max_len - len_descs_dumped &&
smartlist_len(descs_dumped) > 0) {
/* Get the oldest thing on the list */
tmp = (dumped_desc_t *)(smartlist_get(descs_dumped, 0));
/*
* Check if it matches the filename we just added, so we don't delete
* something we just emitted if we get repeated identical descriptors.
*/
if (strcmp(tmp->filename, filename) != 0) {
/* Delete it and adjust the length counter */
tor_unlink(tmp->filename);
tor_assert(len_descs_dumped >= tmp->len);
len_descs_dumped -= tmp->len;
log_info(LD_DIR,
"Deleting old unparseable descriptor dump %s due to "
"space limits",
tmp->filename);
} else {
/*
* Don't delete, but do adjust the counter since we will bump it
* later
*/
tor_assert(len_descs_dumped >= tmp->len);
len_descs_dumped -= tmp->len;
log_info(LD_DIR,
"Replacing old descriptor dump %s with new identical one",
tmp->filename);
}
/* Free it and remove it from the list */
smartlist_del_keeporder(descs_dumped, 0);
tor_free(tmp->filename);
tor_free(tmp);
}
/* Append our entry to the end of the list and bump the counter */
smartlist_add(descs_dumped, ent);
len_descs_dumped += len;
}
/** Check if we already have a descriptor for this hash and move it to the
* head of the queue if so. Return 1 if one existed and 0 otherwise.
*/
static int
dump_desc_fifo_bump_hash(const uint8_t *digest_sha256)
{
dumped_desc_t *match = NULL;
tor_assert(digest_sha256);
if (descs_dumped) {
/* Find a match if one exists */
SMARTLIST_FOREACH_BEGIN(descs_dumped, dumped_desc_t *, ent) {
if (ent &&
memcmp(ent->digest_sha256, digest_sha256, DIGEST256_LEN) == 0) {
/*
* Save a pointer to the match and remove it from its current
* position.
*/
match = ent;
SMARTLIST_DEL_CURRENT_KEEPORDER(descs_dumped, ent);
break;
}
} SMARTLIST_FOREACH_END(ent);
if (match) {
/* Update the timestamp */
match->when = time(NULL);
/* Add it back at the end of the list */
smartlist_add(descs_dumped, match);
/* Indicate we found one */
return 1;
}
}
return 0;
}
/** Clean up on exit; just memory, leave the dumps behind
*/
STATIC void
dump_desc_fifo_cleanup(void)
{
if (descs_dumped) {
/* Free each descriptor */
SMARTLIST_FOREACH_BEGIN(descs_dumped, dumped_desc_t *, ent) {
tor_assert(ent);
tor_free(ent->filename);
tor_free(ent);
} SMARTLIST_FOREACH_END(ent);
/* Free the list */
smartlist_free(descs_dumped);
descs_dumped = NULL;
len_descs_dumped = 0;
}
}
/** Handle one file for dump_desc_populate_fifo_from_directory(); make sure
* the filename is sensibly formed and matches the file content, and either
* return a dumped_desc_t for it or remove the file and return NULL.
*/
MOCK_IMPL(STATIC dumped_desc_t *,
dump_desc_populate_one_file, (const char *dirname, const char *f))
{
dumped_desc_t *ent = NULL;
char *path = NULL, *desc = NULL;
const char *digest_str;
char digest[DIGEST256_LEN], content_digest[DIGEST256_LEN];
/* Expected prefix before digest in filenames */
const char *f_pfx = DESC_DUMP_BASE_FILENAME ".";
/*
* Stat while reading; this is important in case the file
* contains a NUL character.
*/
struct stat st;
/* Sanity-check args */
tor_assert(dirname != NULL);
tor_assert(f != NULL);
/* Form the full path */
tor_asprintf(&path, "%s" PATH_SEPARATOR "%s", dirname, f);
/* Check that f has the form DESC_DUMP_BASE_FILENAME.<digest256> */
if (!strcmpstart(f, f_pfx)) {
/* It matches the form, but is the digest parseable as such? */
digest_str = f + strlen(f_pfx);
if (base16_decode(digest, DIGEST256_LEN,
digest_str, strlen(digest_str)) != DIGEST256_LEN) {
/* We failed to decode it */
digest_str = NULL;
}
} else {
/* No match */
digest_str = NULL;
}
if (!digest_str) {
/* We couldn't get a sensible digest */
log_notice(LD_DIR,
"Removing unrecognized filename %s from unparseable "
"descriptors directory", f);
tor_unlink(path);
/* We're done */
goto done;
}
/*
* The filename has the form DESC_DUMP_BASE_FILENAME "." <digest256> and
* we've decoded the digest. Next, check that we can read it and the
* content matches this digest. We are relying on the fact that if the
* file contains a '\0', read_file_to_str() will allocate space for and
* read the entire file and return the correct size in st.
*/
desc = read_file_to_str(path, RFTS_IGNORE_MISSING, &st);
if (!desc) {
/* We couldn't read it */
log_notice(LD_DIR,
"Failed to read %s from unparseable descriptors directory; "
"attempting to remove it.", f);
tor_unlink(path);
/* We're done */
goto done;
}
/*
* We got one; now compute its digest and check that it matches the
* filename.
*/
if (crypto_digest256((char *)content_digest, desc, st.st_size,
DIGEST_SHA256) != 0) {
/* Weird, but okay */
log_info(LD_DIR,
"Unable to hash content of %s from unparseable descriptors "
"directory", f);
tor_unlink(path);
/* We're done */
goto done;
}
/* Compare the digests */
if (memcmp(digest, content_digest, DIGEST256_LEN) != 0) {
/* No match */
log_info(LD_DIR,
"Hash of %s from unparseable descriptors directory didn't "
"match its filename; removing it", f);
tor_unlink(path);
/* We're done */
goto done;
}
/* Okay, it's a match, we should prepare ent */
ent = tor_malloc_zero(sizeof(dumped_desc_t));
ent->filename = path;
memcpy(ent->digest_sha256, digest, DIGEST256_LEN);
ent->len = st.st_size;
ent->when = st.st_mtime;
/* Null out path so we don't free it out from under ent */
path = NULL;
done:
/* Free allocations if we had them */
tor_free(desc);
tor_free(path);
return ent;
}
/** Sort helper for dump_desc_populate_fifo_from_directory(); compares
* the when field of dumped_desc_ts in a smartlist to put the FIFO in
* the correct order after reconstructing it from the directory.
*/
static int
dump_desc_compare_fifo_entries(const void **a_v, const void **b_v)
{
const dumped_desc_t **a = (const dumped_desc_t **)a_v;
const dumped_desc_t **b = (const dumped_desc_t **)b_v;
if ((a != NULL) && (*a != NULL)) {
if ((b != NULL) && (*b != NULL)) {
/* We have sensible dumped_desc_ts to compare */
if ((*a)->when < (*b)->when) {
return -1;
} else if ((*a)->when == (*b)->when) {
return 0;
} else {
return 1;
}
} else {
/*
* We shouldn't see this, but what the hell, NULLs precede everythin
* else
*/
return 1;
}
} else {
return -1;
}
}
/** Scan the contents of the directory, and update FIFO/counters; this will
* consistency-check descriptor dump filenames against hashes of descriptor
* dump file content, and remove any inconsistent/unreadable dumps, and then
* reconstruct the dump FIFO as closely as possible for the last time the
* tor process shut down. If a previous dump was repeated more than once and
* moved ahead in the FIFO, the mtime will not have been updated and the
* reconstructed order will be wrong, but will always be a permutation of
* the original.
*/
STATIC void
dump_desc_populate_fifo_from_directory(const char *dirname)
{
smartlist_t *files = NULL;
dumped_desc_t *ent = NULL;
tor_assert(dirname != NULL);
/* Get a list of files */
files = tor_listdir(dirname);
if (!files) {
log_notice(LD_DIR,
"Unable to get contents of unparseable descriptor dump "
"directory %s",
dirname);
return;
}
/*
* Iterate through the list and decide which files should go in the
* FIFO and which should be purged.
*/
SMARTLIST_FOREACH_BEGIN(files, char *, f) {
/* Try to get a FIFO entry */
ent = dump_desc_populate_one_file(dirname, f);
if (ent) {
/*
* We got one; add it to the FIFO. No need for duplicate checking
* here since we just verified the name and digest match.
*/
/* Make sure we have a list to add it to */
if (!descs_dumped) {
descs_dumped = smartlist_new();
len_descs_dumped = 0;
}
/* Add it and adjust the counter */
smartlist_add(descs_dumped, ent);
len_descs_dumped += ent->len;
}
/*
* If we didn't, we will have unlinked the file if necessary and
* possible, and emitted a log message about it, so just go on to
* the next.
*/
} SMARTLIST_FOREACH_END(f);
/* Did we get anything? */
if (descs_dumped != NULL) {
/* Sort the FIFO in order of increasing timestamp */
smartlist_sort(descs_dumped, dump_desc_compare_fifo_entries);
/* Log some stats */
log_info(LD_DIR,
"Reloaded unparseable descriptor dump FIFO with %d dump(s) "
"totaling " U64_FORMAT " bytes",
smartlist_len(descs_dumped), U64_PRINTF_ARG(len_descs_dumped));
}
/* Free the original list */
SMARTLIST_FOREACH(files, char *, f, tor_free(f));
smartlist_free(files);
}
/** For debugging purposes, dump unparseable descriptor *<b>desc</b> of
* type *<b>type</b> to file $DATADIR/unparseable-desc. Do not write more
* than one descriptor to disk per minute. If there is already such a
* file in the data directory, overwrite it. */
static void
STATIC void
dump_desc(const char *desc, const char *type)
{
time_t now = time(NULL);
tor_assert(desc);
tor_assert(type);
if (!last_desc_dumped || last_desc_dumped + 60 < now) {
char *debugfile = get_datadir_fname("unparseable-desc");
size_t filelen = 50 + strlen(type) + strlen(desc);
char *content = tor_malloc_zero(filelen);
tor_snprintf(content, filelen, "Unable to parse descriptor of type "
"%s:\n%s", type, desc);
write_str_to_file(debugfile, content, 1);
log_info(LD_DIR, "Unable to parse descriptor of type %s. See file "
"unparseable-desc in data directory for details.", type);
tor_free(content);
tor_free(debugfile);
last_desc_dumped = now;
size_t len;
/* The SHA256 of the string */
uint8_t digest_sha256[DIGEST256_LEN];
char digest_sha256_hex[HEX_DIGEST256_LEN+1];
/* Filename to log it to */
char *debugfile, *debugfile_base;
/* Get the hash for logging purposes anyway */
len = strlen(desc);
if (crypto_digest256((char *)digest_sha256, desc, len,
DIGEST_SHA256) != 0) {
log_info(LD_DIR,
"Unable to parse descriptor of type %s, and unable to even hash"
" it!", type);
goto err;
}
base16_encode(digest_sha256_hex, sizeof(digest_sha256_hex),
(const char *)digest_sha256, sizeof(digest_sha256));
/*
* We mention type and hash in the main log; don't clutter up the files
* with anything but the exact dump.
*/
tor_asprintf(&debugfile_base,
DESC_DUMP_BASE_FILENAME ".%s", digest_sha256_hex);
debugfile = get_datadir_fname2(DESC_DUMP_DATADIR_SUBDIR, debugfile_base);
/*
* Check if the sandbox is active or will become active; see comment
* below at the log message for why.
*/
if (!(sandbox_is_active() || get_options()->Sandbox)) {
if (len <= get_options()->MaxUnparseableDescSizeToLog) {
if (!dump_desc_fifo_bump_hash(digest_sha256)) {
/* Create the directory if needed */
dump_desc_create_dir();
/* Make sure we've got it */
if (have_dump_desc_dir && !problem_with_dump_desc_dir) {
/* Write it, and tell the main log about it */
write_str_to_file(debugfile, desc, 1);
log_info(LD_DIR,
"Unable to parse descriptor of type %s with hash %s and "
"length %lu. See file %s in data directory for details.",
type, digest_sha256_hex, (unsigned long)len,
debugfile_base);
dump_desc_fifo_add_and_clean(debugfile, digest_sha256, len);
/* Since we handed ownership over, don't free debugfile later */
debugfile = NULL;
} else {
/* Problem with the subdirectory */
log_info(LD_DIR,
"Unable to parse descriptor of type %s with hash %s and "
"length %lu. Descriptor not dumped because we had a "
"problem creating the " DESC_DUMP_DATADIR_SUBDIR
" subdirectory",
type, digest_sha256_hex, (unsigned long)len);
/* We do have to free debugfile in this case */
}
} else {
/* We already had one with this hash dumped */
log_info(LD_DIR,
"Unable to parse descriptor of type %s with hash %s and "
"length %lu. Descriptor not dumped because one with that "
"hash has already been dumped.",
type, digest_sha256_hex, (unsigned long)len);
/* We do have to free debugfile in this case */
}
} else {
/* Just log that it happened without dumping */
log_info(LD_DIR,
"Unable to parse descriptor of type %s with hash %s and "
"length %lu. Descriptor not dumped because it exceeds maximum"
" log size all by itself.",
type, digest_sha256_hex, (unsigned long)len);
/* We do have to free debugfile in this case */
}
} else {
/*
* Not logging because the sandbox is active and seccomp2 apparently
* doesn't have a sensible way to allow filenames according to a pattern
* match. (If we ever figure out how to say "allow writes to /regex/",
* remove this checK).
*/
log_info(LD_DIR,
"Unable to parse descriptor of type %s with hash %s and "
"length %lu. Descriptor not dumped because the sandbox is "
"configured",
type, digest_sha256_hex, (unsigned long)len);
}
tor_free(debugfile_base);
tor_free(debugfile);
err:
return;
}
/** Set <b>digest</b> to the SHA-1 digest of the hash of the directory in
@ -5468,3 +5998,27 @@ rend_parse_client_keys(strmap_t *parsed_clients, const char *ckstr)
return result;
}
/** Called on startup; right now we just handle scanning the unparseable
* descriptor dumps, but hang anything else we might need to do in the
* future here as well.
*/
void
routerparse_init(void)
{
/*
* Check both if the sandbox is active and whether it's configured; no
* point in loading all that if we won't be able to use it after the
* sandbox becomes active.
*/
if (!(sandbox_is_active() || get_options()->Sandbox)) {
dump_desc_init();
}
}
/** Clean up all data structures used by routerparse.c at exit */
void
routerparse_free_all(void)
{
dump_desc_fifo_cleanup();
}

View File

@ -85,11 +85,33 @@ int rend_parse_introduction_points(rend_service_descriptor_t *parsed,
size_t intro_points_encoded_size);
int rend_parse_client_keys(strmap_t *parsed_clients, const char *str);
void routerparse_init(void);
void routerparse_free_all(void);
#ifdef ROUTERPARSE_PRIVATE
/*
* One entry in the list of dumped descriptors; filename dumped to, length,
* SHA-256 and timestamp.
*/
typedef struct {
char *filename;
size_t len;
uint8_t digest_sha256[DIGEST256_LEN];
time_t when;
} dumped_desc_t;
EXTERN(size_t, len_descs_dumped);
EXTERN(smartlist_t *, descs_dumped);
STATIC int routerstatus_parse_guardfraction(const char *guardfraction_str,
networkstatus_t *vote,
vote_routerstatus_t *vote_rs,
routerstatus_t *rs);
MOCK_DECL(STATIC dumped_desc_t *, dump_desc_populate_one_file,
(const char *dirname, const char *f));
STATIC void dump_desc_populate_fifo_from_directory(const char *dirname);
STATIC void dump_desc(const char *desc, const char *type);
STATIC void dump_desc_fifo_cleanup(void);
#endif
#define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1"

File diff suppressed because it is too large Load Diff