diff options
Diffstat (limited to 'src/common/util.c')
-rw-r--r-- | src/common/util.c | 226 |
1 files changed, 172 insertions, 54 deletions
diff --git a/src/common/util.c b/src/common/util.c index f4d293c838..50097dac93 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ + * Copyright (c) 2007-2014, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -195,33 +195,40 @@ tor_malloc_zero_(size_t size DMALLOC_PARAMS) return result; } +/* The square root of SIZE_MAX + 1. If a is less than this, and b is less + * than this, then a*b is less than SIZE_MAX. (For example, if size_t is + * 32 bits, then SIZE_MAX is 0xffffffff and this value is 0x10000. If a and + * b are less than this, then their product is at most (65535*65535) == + * 0xfffe0001. */ +#define SQRT_SIZE_MAX_P1 (((size_t)1) << (sizeof(size_t)*4)) + +/** Return non-zero if and only if the product of the arguments is exact. */ +static INLINE int +size_mul_check(const size_t x, const size_t y) +{ + /* This first check is equivalent to + (x < SQRT_SIZE_MAX_P1 && y < SQRT_SIZE_MAX_P1) + + Rationale: if either one of x or y is >= SQRT_SIZE_MAX_P1, then it + will have some bit set in its most significant half. + */ + return ((x|y) < SQRT_SIZE_MAX_P1 || + y == 0 || + x <= SIZE_MAX / y); +} + /** Allocate a chunk of <b>nmemb</b>*<b>size</b> bytes of memory, fill * the memory with zero bytes, and return a pointer to the result. * Log and terminate the process on error. (Same as * calloc(<b>nmemb</b>,<b>size</b>), but never returns NULL.) - * - * XXXX This implementation probably asserts in cases where it could - * work, because it only tries dividing SIZE_MAX by size (according to - * the calloc(3) man page, the size of an element of the nmemb-element - * array to be allocated), not by nmemb (which could in theory be - * smaller than size). Don't do that then. + * The second argument (<b>size</b>) should preferably be non-zero + * and a compile-time constant. */ void * tor_calloc_(size_t nmemb, size_t size DMALLOC_PARAMS) { - /* You may ask yourself, "wouldn't it be smart to use calloc instead of - * malloc+memset? Perhaps libc's calloc knows some nifty optimization trick - * we don't!" Indeed it does, but its optimizations are only a big win when - * we're allocating something very big (it knows if it just got the memory - * from the OS in a pre-zeroed state). We don't want to use tor_malloc_zero - * for big stuff, so we don't bother with calloc. */ - void *result; - size_t max_nmemb = (size == 0) ? SIZE_MAX : SIZE_MAX/size; - - tor_assert(nmemb < max_nmemb); - - result = tor_malloc_zero_((nmemb * size) DMALLOC_FN_ARGS); - return result; + tor_assert(size_mul_check(nmemb, size)); + return tor_malloc_zero_((nmemb * size) DMALLOC_FN_ARGS); } /** Change the size of the memory block pointed to by <b>ptr</b> to <b>size</b> @@ -264,7 +271,7 @@ tor_reallocarray_(void *ptr, size_t sz1, size_t sz2 DMALLOC_PARAMS) { /* XXXX we can make this return 0, but we would need to check all the * reallocarray users. */ - tor_assert(sz2 == 0 || sz1 < SIZE_T_CEILING / sz2); + tor_assert(size_mul_check(sz1, sz2)); return tor_realloc(ptr, (sz1 * sz2) DMALLOC_FN_ARGS); } @@ -957,6 +964,68 @@ string_is_key_value(int severity, const char *string) return 1; } +/** Return true if <b>string</b> represents a valid IPv4 adddress in + * 'a.b.c.d' form. + */ +int +string_is_valid_ipv4_address(const char *string) +{ + struct in_addr addr; + + return (tor_inet_pton(AF_INET,string,&addr) == 1); +} + +/** Return true if <b>string</b> represents a valid IPv6 address in + * a form that inet_pton() can parse. + */ +int +string_is_valid_ipv6_address(const char *string) +{ + struct in6_addr addr; + + return (tor_inet_pton(AF_INET6,string,&addr) == 1); +} + +/** Return true iff <b>string</b> matches a pattern of DNS names + * that we allow Tor clients to connect to. + */ +int +string_is_valid_hostname(const char *string) +{ + int result = 1; + smartlist_t *components; + + components = smartlist_new(); + + smartlist_split_string(components,string,".",0,0); + + SMARTLIST_FOREACH_BEGIN(components, char *, c) { + if (c[0] == '-') { + result = 0; + break; + } + + do { + if ((*c >= 'a' && *c <= 'z') || + (*c >= 'A' && *c <= 'Z') || + (*c >= '0' && *c <= '9') || + (*c == '-')) + c++; + else + result = 0; + } while (result && *c); + + } SMARTLIST_FOREACH_END(c); + + SMARTLIST_FOREACH_BEGIN(components, char *, c) { + tor_free(c); + } SMARTLIST_FOREACH_END(c); + + smartlist_free(components); + + return result; +} + /** Return true iff the DIGEST256_LEN bytes in digest are all zero. */ int tor_digest256_is_zero(const char *digest) @@ -1376,7 +1445,8 @@ n_leapdays(int y1, int y2) --y2; return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400); } -/** Number of days per month in non-leap year; used by tor_timegm. */ +/** Number of days per month in non-leap year; used by tor_timegm and + * parse_rfc1123_time. */ static const int days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; @@ -1390,10 +1460,32 @@ tor_timegm(const struct tm *tm, time_t *time_out) * It's way more brute-force than fiddling with tzset(). */ time_t year, days, hours, minutes, seconds; - int i; - year = tm->tm_year + 1900; - if (year < 1970 || tm->tm_mon < 0 || tm->tm_mon > 11 || - tm->tm_year >= INT32_MAX-1900) { + int i, invalid_year, dpm; + /* avoid int overflow on addition */ + if (tm->tm_year < INT32_MAX-1900) { + year = tm->tm_year + 1900; + } else { + /* clamp year */ + year = INT32_MAX; + } + invalid_year = (year < 1970 || tm->tm_year >= INT32_MAX-1900); + + if (tm->tm_mon >= 0 && tm->tm_mon <= 11) { + dpm = days_per_month[tm->tm_mon]; + if (tm->tm_mon == 1 && !invalid_year && IS_LEAPYEAR(tm->tm_year)) { + dpm = 29; + } + } else { + /* invalid month - default to 0 days per month */ + dpm = 0; + } + + if (invalid_year || + tm->tm_mon < 0 || tm->tm_mon > 11 || + tm->tm_mday < 1 || tm->tm_mday > dpm || + tm->tm_hour < 0 || tm->tm_hour > 23 || + tm->tm_min < 0 || tm->tm_min > 59 || + tm->tm_sec < 0 || tm->tm_sec > 60) { log_warn(LD_BUG, "Out-of-range argument to tor_timegm"); return -1; } @@ -1457,8 +1549,9 @@ parse_rfc1123_time(const char *buf, time_t *t) struct tm tm; char month[4]; char weekday[4]; - int i, m; + int i, m, invalid_year; unsigned tm_mday, tm_year, tm_hour, tm_min, tm_sec; + unsigned dpm; if (strlen(buf) != RFC1123_TIME_LEN) return -1; @@ -1471,18 +1564,6 @@ parse_rfc1123_time(const char *buf, time_t *t) tor_free(esc); return -1; } - if (tm_mday < 1 || tm_mday > 31 || tm_hour > 23 || tm_min > 59 || - tm_sec > 60 || tm_year >= INT32_MAX || tm_year < 1970) { - char *esc = esc_for_log(buf); - log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc); - tor_free(esc); - return -1; - } - tm.tm_mday = (int)tm_mday; - tm.tm_year = (int)tm_year; - tm.tm_hour = (int)tm_hour; - tm.tm_min = (int)tm_min; - tm.tm_sec = (int)tm_sec; m = -1; for (i = 0; i < 12; ++i) { @@ -1499,6 +1580,26 @@ parse_rfc1123_time(const char *buf, time_t *t) } tm.tm_mon = m; + invalid_year = (tm_year >= INT32_MAX || tm_year < 1970); + tor_assert(m >= 0 && m <= 11); + dpm = days_per_month[m]; + if (m == 1 && !invalid_year && IS_LEAPYEAR(tm_year)) { + dpm = 29; + } + + if (invalid_year || tm_mday < 1 || tm_mday > dpm || + tm_hour > 23 || tm_min > 59 || tm_sec > 60) { + char *esc = esc_for_log(buf); + log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc); + tor_free(esc); + return -1; + } + tm.tm_mday = (int)tm_mday; + tm.tm_year = (int)tm_year; + tm.tm_hour = (int)tm_hour; + tm.tm_min = (int)tm_min; + tm.tm_sec = (int)tm_sec; + if (tm.tm_year < 1970) { char *esc = esc_for_log(buf); log_warn(LD_GENERAL, @@ -1670,7 +1771,11 @@ format_time_interval(char *out, size_t out_len, long interval) { /* We only report seconds if there's no hours. */ long sec = 0, min = 0, hour = 0, day = 0; - if (interval < 0) + + /* -LONG_MIN is LONG_MAX + 1, which causes signed overflow */ + if (interval < -LONG_MAX) + interval = LONG_MAX; + else if (interval < 0) interval = -interval; if (interval >= 86400) { @@ -1890,8 +1995,12 @@ file_status(const char *fname) * <b>check</b>&CPD_CHECK, and we think we can create it, return 0. Else * return -1. If CPD_GROUP_OK is set, then it's okay if the directory * is group-readable, but in all cases we create the directory mode 0700. - * If CPD_CHECK_MODE_ONLY is set, then we don't alter the directory permissions - * if they are too permissive: we just return -1. + * If CPD_GROUP_READ is set, existing directory behaves as CPD_GROUP_OK and + * if the directory is created it will use mode 0750 with group read + * permission. Group read privileges also assume execute permission + * as norm for directories. If CPD_CHECK_MODE_ONLY is set, then we don't + * alter the directory permissions if they are too permissive: + * we just return -1. * When effective_user is not NULL, check permissions against the given user * and its primary group. */ @@ -1903,7 +2012,7 @@ check_private_dir(const char *dirname, cpd_check_t check, struct stat st; char *f; #ifndef _WIN32 - int mask; + unsigned unwanted_bits = 0; const struct passwd *pw = NULL; uid_t running_uid; gid_t running_gid; @@ -1928,7 +2037,11 @@ check_private_dir(const char *dirname, cpd_check_t check, #if defined (_WIN32) r = mkdir(dirname); #else - r = mkdir(dirname, 0700); + if (check & CPD_GROUP_READ) { + r = mkdir(dirname, 0750); + } else { + r = mkdir(dirname, 0700); + } #endif if (r) { log_warn(LD_FS, "Error creating directory %s: %s", dirname, @@ -1981,7 +2094,8 @@ check_private_dir(const char *dirname, cpd_check_t check, tor_free(process_ownername); return -1; } - if ((check & CPD_GROUP_OK) && st.st_gid != running_gid) { + if ( (check & (CPD_GROUP_OK|CPD_GROUP_READ)) + && (st.st_gid != running_gid) ) { struct group *gr; char *process_groupname = NULL; gr = getgrgid(running_gid); @@ -1996,12 +2110,12 @@ check_private_dir(const char *dirname, cpd_check_t check, tor_free(process_groupname); return -1; } - if (check & CPD_GROUP_OK) { - mask = 0027; + if (check & (CPD_GROUP_OK|CPD_GROUP_READ)) { + unwanted_bits = 0027; } else { - mask = 0077; + unwanted_bits = 0077; } - if (st.st_mode & mask) { + if ((st.st_mode & unwanted_bits) != 0) { unsigned new_mode; if (check & CPD_CHECK_MODE_ONLY) { log_warn(LD_FS, "Permissions on directory %s are too permissive.", @@ -2011,10 +2125,13 @@ check_private_dir(const char *dirname, cpd_check_t check, log_warn(LD_FS, "Fixing permissions on directory %s", dirname); new_mode = st.st_mode; new_mode |= 0700; /* Owner should have rwx */ - new_mode &= ~mask; /* Clear the other bits that we didn't want set...*/ + if (check & CPD_GROUP_READ) { + new_mode |= 0050; /* Group should have rx */ + } + new_mode &= ~unwanted_bits; /* Clear the bits that we didn't want set...*/ if (chmod(dirname, new_mode)) { log_warn(LD_FS, "Could not chmod directory %s: %s", dirname, - strerror(errno)); + strerror(errno)); return -1; } else { return 0; @@ -3422,8 +3539,9 @@ format_win_cmdline_argument(const char *arg) smartlist_add(arg_chars, (void*)&backslash); /* Allocate space for argument, quotes (if needed), and terminator */ - formatted_arg = tor_calloc(sizeof(char), - (smartlist_len(arg_chars) + (need_quotes ? 2 : 0) + 1)); + const size_t formatted_arg_len = smartlist_len(arg_chars) + + (need_quotes ? 2 : 0) + 1; + formatted_arg = tor_malloc_zero(formatted_arg_len); /* Add leading quote */ i=0; @@ -5061,7 +5179,7 @@ tor_check_port_forwarding(const char *filename, for each smartlist element (one for "-p" and one for the ports), and one for the final NULL. */ args_n = 1 + 2*smartlist_len(ports_to_forward) + 1; - argv = tor_calloc(sizeof(char *), args_n); + argv = tor_calloc(args_n, sizeof(char *)); argv[argv_index++] = filename; SMARTLIST_FOREACH_BEGIN(ports_to_forward, const char *, port) { |