diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/compat_libevent.c | 9 | ||||
-rw-r--r-- | src/common/compat_libevent.h | 1 | ||||
-rw-r--r-- | src/common/compress.c | 49 | ||||
-rw-r--r-- | src/common/compress.h | 1 | ||||
-rw-r--r-- | src/common/compress_lzma.c | 13 | ||||
-rw-r--r-- | src/common/compress_none.c | 53 | ||||
-rw-r--r-- | src/common/compress_none.h | 20 | ||||
-rw-r--r-- | src/common/confline.c | 211 | ||||
-rw-r--r-- | src/common/confline.h | 4 | ||||
-rw-r--r-- | src/common/include.am | 4 | ||||
-rw-r--r-- | src/common/sandbox.c | 1 | ||||
-rw-r--r-- | src/common/util.c | 35 | ||||
-rw-r--r-- | src/common/util.h | 1 | ||||
-rw-r--r-- | src/common/util_format.h | 8 |
14 files changed, 375 insertions, 35 deletions
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 1146d02168..31eb4ac496 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -280,6 +280,15 @@ tor_gettimeofday_cache_set(const struct timeval *tv) tor_assert(tv); memcpy(&cached_time_hires, tv, sizeof(*tv)); } + +/** For testing: called post-fork to make libevent reinitialize + * kernel structures. */ +void +tor_libevent_postfork(void) +{ + int r = event_reinit(tor_libevent_get_base()); + tor_assert(r == 0); +} #endif #endif diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 96c48d53a1..904938415c 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -54,6 +54,7 @@ void tor_gettimeofday_cached(struct timeval *tv); void tor_gettimeofday_cache_clear(void); #ifdef TOR_UNIT_TESTS void tor_gettimeofday_cache_set(const struct timeval *tv); +void tor_libevent_postfork(void); #endif #ifdef COMPAT_LIBEVENT_PRIVATE diff --git a/src/common/compress.c b/src/common/compress.c index 8502dee25c..6513029f9c 100644 --- a/src/common/compress.c +++ b/src/common/compress.c @@ -24,6 +24,7 @@ #include "torlog.h" #include "compress.h" #include "compress_lzma.h" +#include "compress_none.h" #include "compress_zlib.h" #include "compress_zstd.h" @@ -67,8 +68,12 @@ guess_compress_size(int compress, compress_method_t method, size_t in_len) { // ignore these for now. - (void)method; (void)compression_level; + if (method == NO_METHOD) { + /* Guess that we'll need an extra byte, to avoid a needless realloc + * for nul-termination */ + return (in_len < SIZE_MAX) ? in_len + 1 : in_len; + } /* Always guess a factor of 2. */ if (compress) { @@ -255,8 +260,8 @@ detect_compression_method(const char *in, size_t in_len) } else if (in_len > 2 && (in[0] & 0x0f) == 8 && (ntohs(get_uint16(in)) % 31) == 0) { return ZLIB_METHOD; - } else if (in_len > 3 && - fast_memeq(in, "\x5d\x00\x00\x00", 4)) { + } else if (in_len > 2 && + fast_memeq(in, "\x5d\x00\x00", 3)) { return LZMA_METHOD; } else if (in_len > 3 && fast_memeq(in, "\x28\xb5\x2f\xfd", 4)) { @@ -279,6 +284,7 @@ tor_compress_supports_method(compress_method_t method) case ZSTD_METHOD: return tor_zstd_method_supported(); case NO_METHOD: + return 1; case UNKNOWN_METHOD: default: return 0; @@ -313,7 +319,9 @@ static const struct { } compression_method_names[] = { { "gzip", GZIP_METHOD }, { "deflate", ZLIB_METHOD }, - { "x-lzma", LZMA_METHOD }, + // We call this "x-tor-lzma" rather than "x-lzma", because we impose a + // lower maximum memory usage on the decoding side. + { "x-tor-lzma", LZMA_METHOD }, { "x-zstd" , ZSTD_METHOD }, { "identity", NO_METHOD }, @@ -335,6 +343,32 @@ compression_method_get_name(compress_method_t method) return NULL; } +/** Table of compression human readable method names. */ +static const struct { + compress_method_t method; + const char *name; +} compression_method_human_names[] = { + { NO_METHOD, "uncompressed" }, + { GZIP_METHOD, "gzipped" }, + { ZLIB_METHOD, "deflated" }, + { LZMA_METHOD, "LZMA compressed" }, + { ZSTD_METHOD, "Zstandard compressed" }, + { UNKNOWN_METHOD, "unknown encoding" }, +}; + +/** Return a human readable string representation of the compression method + * <b>method</b>, or NULL if the method isn't recognized. */ +const char * +compression_method_get_human_name(compress_method_t method) +{ + unsigned i; + for (i = 0; i < ARRAY_LENGTH(compression_method_human_names); ++i) { + if (method == compression_method_human_names[i].method) + return compression_method_human_names[i].name; + } + return NULL; +} + /** Return the compression method represented by the string <b>name</b>, or * UNKNOWN_METHOD if the string isn't recognized. */ compress_method_t @@ -456,7 +490,9 @@ tor_compress_new(int compress, compress_method_t method, state->u.zstd_state = zstd_state; break; } - case NO_METHOD: + case NO_METHOD: { + break; + } case UNKNOWN_METHOD: goto err; } @@ -504,6 +540,8 @@ tor_compress_process(tor_compress_state_t *state, out, out_len, in, in_len, finish); case NO_METHOD: + return tor_cnone_compress_process(out, out_len, in, in_len, + finish); case UNKNOWN_METHOD: goto err; } @@ -531,6 +569,7 @@ tor_compress_free(tor_compress_state_t *state) tor_zstd_compress_free(state->u.zstd_state); break; case NO_METHOD: + break; case UNKNOWN_METHOD: break; } diff --git a/src/common/compress.h b/src/common/compress.h index 5b47c5d458..7c0dc14061 100644 --- a/src/common/compress.h +++ b/src/common/compress.h @@ -50,6 +50,7 @@ int tor_compress_is_compression_bomb(size_t size_in, size_t size_out); int tor_compress_supports_method(compress_method_t method); unsigned tor_compress_get_supported_method_bitmask(void); const char * compression_method_get_name(compress_method_t method); +const char *compression_method_get_human_name(compress_method_t method); compress_method_t compression_method_get_by_name(const char *name); const char *tor_compress_version_str(compress_method_t method); diff --git a/src/common/compress_lzma.c b/src/common/compress_lzma.c index b721bae8aa..b5393a6ba6 100644 --- a/src/common/compress_lzma.c +++ b/src/common/compress_lzma.c @@ -22,6 +22,9 @@ #include <lzma.h> #endif +/** The maximum amount of memory we allow the LZMA decoder to use, in bytes. */ +#define MEMORY_LIMIT (16 * 1024 * 1024) + /** Total number of bytes allocated for LZMA state. */ static atomic_counter_t total_lzma_allocation; @@ -33,9 +36,9 @@ memory_level(compression_level_t level) switch (level) { default: case BEST_COMPRESSION: - case HIGH_COMPRESSION: return 9; - case MEDIUM_COMPRESSION: return 6; - case LOW_COMPRESSION: return 3; + case HIGH_COMPRESSION: return 6; + case MEDIUM_COMPRESSION: return 4; + case LOW_COMPRESSION: return 2; } } @@ -191,9 +194,7 @@ tor_lzma_compress_new(int compress, goto err; } } else { - // FIXME(ahf): This should be something more sensible than - // UINT64_MAX: See #21665. - retval = lzma_alone_decoder(&result->stream, UINT64_MAX); + retval = lzma_alone_decoder(&result->stream, MEMORY_LIMIT); if (retval != LZMA_OK) { log_warn(LD_GENERAL, "Error from LZMA decoder: %s (%u).", diff --git a/src/common/compress_none.c b/src/common/compress_none.c new file mode 100644 index 0000000000..b76e6010ec --- /dev/null +++ b/src/common/compress_none.c @@ -0,0 +1,53 @@ +/* Copyright (c) 2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file compress_lzma.c + * \brief Compression backend for identity compression. + * + * We actually define this backend so that we can treat the identity transform + * as another case of compression. + * + * This module should never be invoked directly. Use the compress module + * instead. + **/ + +#include "orconfig.h" + +#include "util.h" +#include "torlog.h" +#include "compress.h" +#include "compress_none.h" + +/** Transfer some bytes using the identity transformation. Read up to + * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes + * to *<b>out</b>, adjusting the values as we go. If <b>finish</b> is true, + * we've reached the end of the input. + * + * Return TOR_COMPRESS_DONE if we've finished the entire + * compression/decompression. + * Return TOR_COMPRESS_OK if we're processed everything from the input. + * Return TOR_COMPRESS_BUFFER_FULL if we're out of space on <b>out</b>. + * Return TOR_COMPRESS_ERROR if the stream is corrupt. + */ +tor_compress_output_t +tor_cnone_compress_process(char **out, size_t *out_len, + const char **in, size_t *in_len, + int finish) +{ + size_t n_to_copy = MIN(*in_len, *out_len); + + memcpy(*out, *in, n_to_copy); + *out += n_to_copy; + *in += n_to_copy; + *out_len -= n_to_copy; + *in_len -= n_to_copy; + if (*in_len == 0) { + return finish ? TOR_COMPRESS_DONE : TOR_COMPRESS_OK; + } else { + return TOR_COMPRESS_BUFFER_FULL; + } +} + diff --git a/src/common/compress_none.h b/src/common/compress_none.h new file mode 100644 index 0000000000..d1ebb4b625 --- /dev/null +++ b/src/common/compress_none.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file compress_none.h + * \brief Header for compress_none.c + **/ + +#ifndef TOR_COMPRESS_NONE_H +#define TOR_COMPRESS_NONE_H + +tor_compress_output_t +tor_cnone_compress_process(char **out, size_t *out_len, + const char **in, size_t *in_len, + int finish); + +#endif // TOR_COMPRESS_NONE_H. + diff --git a/src/common/confline.c b/src/common/confline.c index d4468f80ea..e078b1da04 100644 --- a/src/common/confline.c +++ b/src/common/confline.c @@ -8,6 +8,19 @@ #include "confline.h" #include "torlog.h" #include "util.h" +#include "container.h" + +static int config_get_lines_aux(const char *string, config_line_t **result, + int extended, int allow_include, + int *has_include, int recursion_level, + config_line_t **last); +static smartlist_t *config_get_file_list(const char *path); +static int config_get_included_list(const char *path, int recursion_level, + int extended, config_line_t **list, + config_line_t **list_last); +static int config_process_include(const char *path, int recursion_level, + int extended, config_line_t ***next, + config_line_t **list_last); /** Helper: allocate a new configuration option mapping 'key' to 'val', * append it to *<b>lst</b>. */ @@ -65,19 +78,25 @@ config_line_find(const config_line_t *lines, return NULL; } -/** Helper: parse the config string and strdup into key/value - * strings. Set *result to the list, or NULL if parsing the string - * failed. Return 0 on success, -1 on failure. Warn and ignore any - * misformatted lines. - * - * If <b>extended</b> is set, then treat keys beginning with / and with + as - * indicating "clear" and "append" respectively. */ -int -config_get_lines(const char *string, config_line_t **result, int extended) +/** Auxiliary function that does all the work of config_get_lines. + * <b>recursion_level</b> is the count of how many nested %includes we have. + * Returns the a pointer to the last element of the <b>result</b> in + * <b>last</b>. */ +static int +config_get_lines_aux(const char *string, config_line_t **result, int extended, + int allow_include, int *has_include, int recursion_level, + config_line_t **last) { - config_line_t *list = NULL, **next; + config_line_t *list = NULL, **next, *list_last = NULL; char *k, *v; const char *parse_err; + int include_used = 0; + + if (recursion_level > MAX_INCLUDE_RECURSION_LEVEL) { + log_warn(LD_CONFIG, "Error while parsing configuration: more than %d " + "nested %%includes.", MAX_INCLUDE_RECURSION_LEVEL); + return -1; + } next = &list; do { @@ -108,25 +127,179 @@ config_get_lines(const char *string, config_line_t **result, int extended) command = CONFIG_LINE_CLEAR; } } - /* This list can get long, so we keep a pointer to the end of it - * rather than using config_line_append over and over and getting - * n^2 performance. */ - *next = tor_malloc_zero(sizeof(config_line_t)); - (*next)->key = k; - (*next)->value = v; - (*next)->next = NULL; - (*next)->command = command; - next = &((*next)->next); + + if (allow_include && !strcmp(k, "%include")) { + tor_free(k); + include_used = 1; + + if (config_process_include(v, recursion_level, extended, &next, + &list_last) < 0) { + log_warn(LD_CONFIG, "Error reading included configuration " + "file or directory: \"%s\".", v); + config_free_lines(list); + tor_free(v); + return -1; + } + tor_free(v); + } else { + /* This list can get long, so we keep a pointer to the end of it + * rather than using config_line_append over and over and getting + * n^2 performance. */ + *next = tor_malloc_zero(sizeof(**next)); + (*next)->key = k; + (*next)->value = v; + (*next)->next = NULL; + (*next)->command = command; + list_last = *next; + next = &((*next)->next); + } } else { tor_free(k); tor_free(v); } } while (*string); + if (last) { + *last = list_last; + } + if (has_include) { + *has_include = include_used; + } *result = list; return 0; } +/** Helper: parse the config string and strdup into key/value + * strings. Set *result to the list, or NULL if parsing the string + * failed. Set *has_include to 1 if <b>result</b> has values from + * %included files. Return 0 on success, -1 on failure. Warn and ignore any + * misformatted lines. + * + * If <b>extended</b> is set, then treat keys beginning with / and with + as + * indicating "clear" and "append" respectively. */ +int +config_get_lines_include(const char *string, config_line_t **result, + int extended, int *has_include) +{ + return config_get_lines_aux(string, result, extended, 1, has_include, 1, + NULL); +} + +/** Same as config_get_lines_include but does not allow %include */ +int +config_get_lines(const char *string, config_line_t **result, int extended) +{ + return config_get_lines_aux(string, result, extended, 0, NULL, 1, NULL); +} + +/** Adds a list of configuration files present on <b>path</b> to + * <b>file_list</b>. <b>path</b> can be a file or a directory. If it is a file, + * only that file will be added to <b>file_list</b>. If it is a directory, + * all paths for files on that directory root (no recursion) except for files + * whose name starts with a dot will be added to <b>file_list</b>. + * Return 0 on success, -1 on failure. Ignores empty files. + */ +static smartlist_t * +config_get_file_list(const char *path) +{ + smartlist_t *file_list = smartlist_new(); + file_status_t file_type = file_status(path); + if (file_type == FN_FILE) { + smartlist_add_strdup(file_list, path); + return file_list; + } else if (file_type == FN_DIR) { + smartlist_t *all_files = tor_listdir(path); + if (!all_files) { + smartlist_free(file_list); + return NULL; + } + smartlist_sort_strings(all_files); + SMARTLIST_FOREACH_BEGIN(all_files, char *, f) { + if (f[0] == '.') { + tor_free(f); + continue; + } + + char *fullname; + tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f); + tor_free(f); + + if (file_status(fullname) != FN_FILE) { + tor_free(fullname); + continue; + } + smartlist_add(file_list, fullname); + } SMARTLIST_FOREACH_END(f); + smartlist_free(all_files); + return file_list; + } else if (file_type == FN_EMPTY) { + return file_list; + } else { + smartlist_free(file_list); + return NULL; + } +} + +/** Creates a list of config lines present on included <b>path</b>. + * Set <b>list</b> to the list and <b>list_last</b> to the last element of + * <b>list</b>. Return 0 on success, -1 on failure. */ +static int +config_get_included_list(const char *path, int recursion_level, int extended, + config_line_t **list, config_line_t **list_last) +{ + char *included_conf = read_file_to_str(path, 0, NULL); + if (!included_conf) { + return -1; + } + + if (config_get_lines_aux(included_conf, list, extended, 1, NULL, + recursion_level+1, list_last) < 0) { + tor_free(included_conf); + return -1; + } + + tor_free(included_conf); + return 0; +} + +/** Process an %include <b>path</b> in a config file. Set <b>next</b> to a + * pointer to the next pointer of the last element of the config_line_t list + * obtained from the config file and <b>list_last</b> to the last element of + * the same list. Return 0 on success, -1 on failure. */ +static int +config_process_include(const char *path, int recursion_level, int extended, + config_line_t ***next, config_line_t **list_last) +{ + char *unquoted_path = get_unquoted_path(path); + if (!unquoted_path) { + return -1; + } + + smartlist_t *config_files = config_get_file_list(unquoted_path); + if (!config_files) { + tor_free(unquoted_path); + return -1; + } + tor_free(unquoted_path); + + SMARTLIST_FOREACH_BEGIN(config_files, char *, config_file) { + config_line_t *included_list = NULL; + if (config_get_included_list(config_file, recursion_level, extended, + &included_list, list_last) < 0) { + SMARTLIST_FOREACH(config_files, char *, f, tor_free(f)); + smartlist_free(config_files); + return -1; + } + tor_free(config_file); + + **next = included_list; + *next = &(*list_last)->next; + + } SMARTLIST_FOREACH_END(config_file); + smartlist_free(config_files); + return 0; +} + /** * Free all the configuration lines on the linked list <b>front</b>. */ diff --git a/src/common/confline.h b/src/common/confline.h index 477c6929a2..eb863e8fd8 100644 --- a/src/common/confline.h +++ b/src/common/confline.h @@ -15,6 +15,8 @@ /* Removes all previous configuration for an option. */ #define CONFIG_LINE_CLEAR 2 +#define MAX_INCLUDE_RECURSION_LEVEL 31 + /** A linked list of lines in a config file, or elsewhere */ typedef struct config_line_t { char *key; @@ -41,6 +43,8 @@ const config_line_t *config_line_find(const config_line_t *lines, int config_lines_eq(config_line_t *a, config_line_t *b); int config_count_key(const config_line_t *a, const char *key); int config_get_lines(const char *string, config_line_t **result, int extended); +int config_get_lines_include(const char *string, config_line_t **result, + int extended, int *has_include); void config_free_lines(config_line_t *front); const char *parse_config_line_from_str_verbose(const char *line, char **key_out, char **value_out, diff --git a/src/common/include.am b/src/common/include.am index b37b363b82..1253888815 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -112,6 +112,7 @@ LIBOR_CRYPTO_A_SRC = \ src/common/aes.c \ src/common/compress.c \ src/common/compress_lzma.c \ + src/common/compress_none.c \ src/common/compress_zlib.c \ src/common/compress_zstd.c \ src/common/crypto.c \ @@ -154,8 +155,9 @@ COMMONHEADERS = \ src/common/compat_rust.h \ src/common/compat_threads.h \ src/common/compat_time.h \ - src/common/compress.h \ + src/common/compress.h \ src/common/compress_lzma.h \ + src/common/compress_none.h \ src/common/compress_zlib.h \ src/common/compress_zstd.h \ src/common/confline.h \ diff --git a/src/common/sandbox.c b/src/common/sandbox.c index ab2de52592..7826b2d40c 100644 --- a/src/common/sandbox.c +++ b/src/common/sandbox.c @@ -155,6 +155,7 @@ static int filter_nopar_gen[] = { #ifdef __NR_getgid32 SCMP_SYS(getgid32), #endif + SCMP_SYS(getpid), #ifdef __NR_getrlimit SCMP_SYS(getrlimit), #endif diff --git a/src/common/util.c b/src/common/util.c index ca2a0c4a9c..5c455585e7 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -3045,6 +3045,41 @@ unescape_string(const char *s, char **result, size_t *size_out) } } +/** Removes enclosing quotes from <b>path</b> and unescapes quotes between the + * enclosing quotes. Backslashes are not unescaped. Return the unquoted + * <b>path</b> on sucess or 0 if <b>path</b> is not quoted correctly. */ +char * +get_unquoted_path(const char *path) +{ + int len = strlen(path); + + if (len == 0) { + return tor_strdup(""); + } + + int has_start_quote = (path[0] == '\"'); + int has_end_quote = (len > 0 && path[len-1] == '\"'); + if (has_start_quote != has_end_quote || (len == 1 && has_start_quote)) { + return NULL; + } + + char *unquoted_path = tor_malloc(len - has_start_quote - has_end_quote + 1); + char *s = unquoted_path; + int i; + for (i = has_start_quote; i < len - has_end_quote; i++) { + if (path[i] == '\"' && (i > 0 && path[i-1] == '\\')) { + *(s-1) = path[i]; + } else if (path[i] != '\"') { + *s++ = path[i]; + } else { /* unescaped quote */ + tor_free(unquoted_path); + return NULL; + } + } + *s = '\0'; + return unquoted_path; +} + /** Expand any homedir prefix on <b>filename</b>; return a newly allocated * string. */ char * diff --git a/src/common/util.h b/src/common/util.h index 18eb57f1bc..d56abcee2e 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -389,6 +389,7 @@ char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out) ATTR_MALLOC; const char *unescape_string(const char *s, char **result, size_t *size_out); +char *get_unquoted_path(const char *path); char *expand_filename(const char *filename); MOCK_DECL(struct smartlist_t *, tor_listdir, (const char *dirname)); int path_is_relative(const char *filename); diff --git a/src/common/util_format.h b/src/common/util_format.h index adf48c0077..4af8832bbe 100644 --- a/src/common/util_format.h +++ b/src/common/util_format.h @@ -23,11 +23,11 @@ #define BASE32_BUFSIZE(n) (BASE32_LEN(n) + 1) #define BASE16_BUFSIZE(n) (BASE16_LEN(n) + 1) -#define BASE64_NOPAD_LEN(n) (CEIL_DIV((n) * 4, 3) -#define BASE32_NOPAD_LEN(n) (CEIL_DIV((n) * 8, 5) +#define BASE64_NOPAD_LEN(n) (CEIL_DIV((n) * 4, 3)) +#define BASE32_NOPAD_LEN(n) (CEIL_DIV((n) * 8, 5)) -#define BASE64_NOPAD_BUFSIZE(n) (BASE64_NOPAD_LEN(n) + 1)) -#define BASE32_NOPAD_BUFSIZE(n) (BASE32_NOPAD_LEN(n) + 1)) +#define BASE64_NOPAD_BUFSIZE(n) (BASE64_NOPAD_LEN(n) + 1) +#define BASE32_NOPAD_BUFSIZE(n) (BASE32_NOPAD_LEN(n) + 1) /** @} */ #define BASE64_ENCODE_MULTILINE 1 |