diff options
Diffstat (limited to 'src/libstrongswan/utils')
-rw-r--r-- | src/libstrongswan/utils/backtrace.c | 117 | ||||
-rw-r--r-- | src/libstrongswan/utils/backtrace.h | 8 | ||||
-rw-r--r-- | src/libstrongswan/utils/capabilities.c | 152 | ||||
-rw-r--r-- | src/libstrongswan/utils/capabilities.h | 36 | ||||
-rw-r--r-- | src/libstrongswan/utils/chunk.c | 251 | ||||
-rw-r--r-- | src/libstrongswan/utils/chunk.h | 60 | ||||
-rw-r--r-- | src/libstrongswan/utils/enum.c | 2 | ||||
-rw-r--r-- | src/libstrongswan/utils/identification.c | 101 | ||||
-rw-r--r-- | src/libstrongswan/utils/identification.h | 1 | ||||
-rw-r--r-- | src/libstrongswan/utils/integrity_checker.c | 4 | ||||
-rw-r--r-- | src/libstrongswan/utils/leak_detective.c | 660 | ||||
-rw-r--r-- | src/libstrongswan/utils/leak_detective.h | 10 | ||||
-rw-r--r-- | src/libstrongswan/utils/printf_hook.c | 1 | ||||
-rw-r--r-- | src/libstrongswan/utils/settings.c | 21 | ||||
-rw-r--r-- | src/libstrongswan/utils/settings.h | 10 | ||||
-rw-r--r-- | src/libstrongswan/utils/utils.c | 28 | ||||
-rw-r--r-- | src/libstrongswan/utils/utils.h | 71 |
17 files changed, 1160 insertions, 373 deletions
diff --git a/src/libstrongswan/utils/backtrace.c b/src/libstrongswan/utils/backtrace.c index 77137f9f1..93031908a 100644 --- a/src/libstrongswan/utils/backtrace.c +++ b/src/libstrongswan/utils/backtrace.c @@ -53,6 +53,11 @@ struct private_backtrace_t { }; /** + * Forward declaration of method getter + */ +static backtrace_t get_methods(); + +/** * Write a format string with arguments to a FILE line, if it is NULL to DBG */ static void println(FILE *file, char *format, ...) @@ -299,7 +304,7 @@ static bfd_entry_t *get_bfd_entry(char *filename) /** * Print the source file with line number to file, libbfd variant */ -static void print_sourceline(FILE *file, char *filename, void *ptr) +static void print_sourceline(FILE *file, char *filename, void *ptr, void *base) { bfd_entry_t *entry; bfd_find_data_t data = { @@ -334,13 +339,20 @@ void backtrace_deinit() {} /** * Print the source file with line number to file, slow addr2line variant */ -static void print_sourceline(FILE *file, char *filename, void *ptr) +static void print_sourceline(FILE *file, char *filename, void *ptr, void* base) { char buf[1024]; FILE *output; int c, i = 0; +#ifdef __APPLE__ + snprintf(buf, sizeof(buf), "atos -o %s -l %p %p 2>&1 | tail -n1", + filename, base, ptr); +#else /* !__APPLE__ */ snprintf(buf, sizeof(buf), "addr2line -e %s %p", filename, ptr); +#endif /* __APPLE__ */ + + output = popen(buf, "r"); if (output) { @@ -373,11 +385,9 @@ void backtrace_deinit() {} METHOD(backtrace_t, log_, void, private_backtrace_t *this, FILE *file, bool detailed) { -#ifdef HAVE_BACKTRACE +#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) size_t i; - char **strings; - - strings = backtrace_symbols(this->frames, this->frame_count); + char **strings = NULL; println(file, " dumping %d stack frame addresses:", this->frame_count); for (i = 0; i < this->frame_count; i++) @@ -410,19 +420,33 @@ METHOD(backtrace_t, log_, void, } if (detailed && info.dli_fname[0]) { - print_sourceline(file, (char*)info.dli_fname, ptr); + print_sourceline(file, (char*)info.dli_fname, + ptr, info.dli_fbase); } } else #endif /* HAVE_DLADDR */ { - println(file, " %s", strings[i]); +#ifdef HAVE_BACKTRACE + if (!strings) + { + strings = backtrace_symbols(this->frames, this->frame_count); + } + if (strings) + { + println(file, " %s", strings[i]); + } + else +#endif /* HAVE_BACKTRACE */ + { + println(file, " %p", this->frames[i]); + } } } - free (strings); -#else /* !HAVE_BACKTRACE */ - println(file, "C library does not support backtrace()."); -#endif /* HAVE_BACKTRACE */ + free(strings); +#else /* !HAVE_BACKTRACE && !HAVE_LIBUNWIND_H */ + println(file, "no support for backtrace()/libunwind"); +#endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */ } METHOD(backtrace_t, contains_function, bool, @@ -512,12 +536,69 @@ METHOD(backtrace_t, create_frame_enumerator, enumerator_t*, return &enumerator->public; } +METHOD(backtrace_t, clone, backtrace_t*, + private_backtrace_t *this) +{ + private_backtrace_t *clone; + + clone = malloc(sizeof(private_backtrace_t) + + this->frame_count * sizeof(void*)); + memcpy(clone->frames, this->frames, this->frame_count * sizeof(void*)); + clone->frame_count = this->frame_count; + + clone->public = get_methods(); + + return &clone->public; +} + METHOD(backtrace_t, destroy, void, private_backtrace_t *this) { free(this); } +#ifdef HAVE_LIBUNWIND_H +#define UNW_LOCAL_ONLY +#include <libunwind.h> + +/** + * libunwind variant for glibc backtrace() + */ +static inline int backtrace_unwind(void **frames, int count) +{ + unw_context_t context; + unw_cursor_t cursor; + unw_word_t ip; + int depth = 0; + + unw_getcontext(&context); + unw_init_local(&cursor, &context); + do + { + unw_get_reg(&cursor, UNW_REG_IP, &ip); + frames[depth++] = (void*)ip; + } + while (depth < count && unw_step(&cursor) > 0); + + return depth; +} +#endif /* HAVE_UNWIND */ + +/** + * Get implementation methods of backtrace_t + */ +static backtrace_t get_methods() +{ + return (backtrace_t) { + .log = _log_, + .contains_function = _contains_function, + .equals = _equals, + .clone = _clone, + .create_frame_enumerator = _create_frame_enumerator, + .destroy = _destroy, + }; +} + /** * See header */ @@ -527,7 +608,9 @@ backtrace_t *backtrace_create(int skip) void *frames[50]; int frame_count = 0; -#ifdef HAVE_BACKTRACE +#ifdef HAVE_LIBUNWIND_H + frame_count = backtrace_unwind(frames, countof(frames)); +#elif defined(HAVE_BACKTRACE) frame_count = backtrace(frames, countof(frames)); #endif /* HAVE_BACKTRACE */ frame_count = max(frame_count - skip, 0); @@ -535,13 +618,7 @@ backtrace_t *backtrace_create(int skip) memcpy(this->frames, frames + skip, frame_count * sizeof(void*)); this->frame_count = frame_count; - this->public = (backtrace_t) { - .log = _log_, - .contains_function = _contains_function, - .equals = _equals, - .create_frame_enumerator = _create_frame_enumerator, - .destroy = _destroy, - }; + this->public = get_methods(); return &this->public; } diff --git a/src/libstrongswan/utils/backtrace.h b/src/libstrongswan/utils/backtrace.h index 62104238d..416f58898 100644 --- a/src/libstrongswan/utils/backtrace.h +++ b/src/libstrongswan/utils/backtrace.h @@ -59,6 +59,14 @@ struct backtrace_t { * @return TRUE if backtraces are equal */ bool (*equals)(backtrace_t *this, backtrace_t *other); + + /** + * Create a copy of this backtrace. + * + * @return cloned copy + */ + backtrace_t* (*clone)(backtrace_t *this); + /** * Create an enumerator over the stack frame addresses. * diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c index 44a14496c..c5e90b6c3 100644 --- a/src/libstrongswan/utils/capabilities.c +++ b/src/libstrongswan/utils/capabilities.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2013 Tobias Brunner * Hochschule fuer Technik Rapperswil * Copyright (C) 2012 Martin Willi * Copyright (C) 2012 revosec AG @@ -76,8 +76,116 @@ struct private_capabilities_t { #endif }; -METHOD(capabilities_t, keep, void, - private_capabilities_t *this, u_int cap) +/** + * Returns TRUE if the current process/user is member of the given group + */ +static bool has_group(gid_t group) +{ + gid_t *groups; + long ngroups, i; + bool found = FALSE; + + if (group == getegid()) + { /* it's unspecified if this is part of the list below or not */ + return TRUE; + } + ngroups = sysconf(_SC_NGROUPS_MAX); + if (ngroups == -1) + { + DBG1(DBG_LIB, "getting groups for current process failed: %s", + strerror(errno)); + return FALSE; + } + groups = calloc(ngroups + 1, sizeof(gid_t)); + ngroups = getgroups(ngroups, groups); + if (ngroups == -1) + { + DBG1(DBG_LIB, "getting groups for current process failed: %s", + strerror(errno)); + free(groups); + return FALSE; + } + for (i = 0; i < ngroups; i++) + { + if (group == groups[i]) + { + found = TRUE; + break; + } + } + free(groups); + return found; +} + +/** + * Verify that the current process has the given capability + */ +static bool has_capability(private_capabilities_t *this, u_int cap, + bool *ignore) +{ + if (cap == CAP_CHOWN) + { /* if new files/UNIX sockets are created they should be owned by the + * configured user and group. This requires a call to chown(2). But + * CAP_CHOWN is not always required. */ + if (!this->uid || geteuid() == this->uid) + { /* if the owner does not change CAP_CHOWN is not needed */ + if (!this->gid || has_group(this->gid)) + { /* the same applies if the owner is a member of the group */ + if (ignore) + { /* we don't have to keep this, if requested */ + *ignore = TRUE; + } + return TRUE; + } + } + } +#ifndef CAPABILITIES + /* if we can't check the actual capabilities assume only root has it */ + return geteuid() == 0; +#endif /* !CAPABILITIES */ +#ifdef CAPABILITIES_LIBCAP + cap_flag_value_t val; + cap_t caps; + bool ok; + + caps = cap_get_proc(); + if (!caps) + { + return FALSE; + } + ok = cap_get_flag(caps, cap, CAP_PERMITTED, &val) == 0 && val == CAP_SET; + cap_free(caps); + return ok; +#endif /* CAPABILITIES_LIBCAP */ +#ifdef CAPABILITIES_NATIVE + struct __user_cap_header_struct header = { +#if defined(_LINUX_CAPABILITY_VERSION_3) + .version = _LINUX_CAPABILITY_VERSION_3, +#elif defined(_LINUX_CAPABILITY_VERSION_2) + .version = _LINUX_CAPABILITY_VERSION_2, +#elif defined(_LINUX_CAPABILITY_VERSION_1) + .version = _LINUX_CAPABILITY_VERSION_1, +#else + .version = _LINUX_CAPABILITY_VERSION, +#endif + }; + struct __user_cap_data_struct caps[2]; + int i = 0; + + if (cap >= 32) + { + i++; + cap -= 32; + } + return capget(&header, caps) == 0 && caps[i].permitted & (1 << cap); +#endif /* CAPABILITIES_NATIVE */ +} + +/** + * Keep the given capability if it is held by the current process. Returns + * FALSE, if this is not the case. + */ +static bool keep_capability(private_capabilities_t *this, u_int cap) { #ifdef CAPABILITIES_LIBCAP cap_set_flag(this->caps, CAP_EFFECTIVE, 1, &cap, CAP_SET); @@ -96,18 +204,41 @@ METHOD(capabilities_t, keep, void, this->caps[i].permitted |= 1 << cap; this->caps[i].inheritable |= 1 << cap; #endif /* CAPABILITIES_NATIVE */ + return TRUE; +} + +METHOD(capabilities_t, keep, bool, + private_capabilities_t *this, u_int cap) +{ + bool ignore = FALSE; + + if (!has_capability(this, cap, &ignore)) + { + return FALSE; + } + else if (ignore) + { /* don't keep capabilities that are not required */ + return TRUE; + } + return keep_capability(this, cap); +} + +METHOD(capabilities_t, check, bool, + private_capabilities_t *this, u_int cap) +{ + return has_capability(this, cap, NULL); } METHOD(capabilities_t, get_uid, uid_t, private_capabilities_t *this) { - return this->uid; + return this->uid ?: geteuid(); } METHOD(capabilities_t, get_gid, gid_t, private_capabilities_t *this) { - return this->gid; + return this->gid ?: getegid(); } METHOD(capabilities_t, set_uid, void, @@ -225,7 +356,7 @@ METHOD(capabilities_t, drop, bool, prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); #endif - if (!init_supplementary_groups(this)) + if (this->uid && !init_supplementary_groups(this)) { DBG1(DBG_LIB, "initializing supplementary groups for %u failed", this->uid); @@ -271,7 +402,7 @@ METHOD(capabilities_t, drop, bool, #endif /* CAPABILITIES_NATIVE */ #ifdef CAPABILITIES DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u", - this->uid, this->gid); + geteuid(), getegid()); #endif /* CAPABILITIES */ return TRUE; } @@ -298,6 +429,7 @@ capabilities_t *capabilities_create() INIT(this, .public = { .keep = _keep, + .check = _check, .get_uid = _get_uid, .get_gid = _get_gid, .set_uid = _set_uid, @@ -309,15 +441,9 @@ capabilities_t *capabilities_create() }, ); -#ifdef CAPABILITIES #ifdef CAPABILITIES_LIBCAP this->caps = cap_init(); #endif /* CAPABILITIES_LIBCAP */ - if (lib->leak_detective) - { - keep(this, CAP_SYS_NICE); - } -#endif /* CAPABILITIES */ #ifdef EMULATE_R_FUNCS this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); diff --git a/src/libstrongswan/utils/capabilities.h b/src/libstrongswan/utils/capabilities.h index cd23cbf10..fe11a4dfc 100644 --- a/src/libstrongswan/utils/capabilities.h +++ b/src/libstrongswan/utils/capabilities.h @@ -1,4 +1,6 @@ /* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil * Copyright (C) 2012 Martin Willi * Copyright (C) 2012 revosec AG * @@ -21,6 +23,8 @@ #ifndef CAPABILITIES_H_ #define CAPABILITIES_H_ +typedef struct capabilities_t capabilities_t; + #include <library.h> #ifdef HAVE_SYS_CAPABILITY_H # include <sys/capability.h> @@ -28,7 +32,18 @@ # include <linux/capability.h> #endif -typedef struct capabilities_t capabilities_t; +#ifndef CAP_CHOWN +# define CAP_CHOWN 0 +#endif +#ifndef CAP_NET_BIND_SERVICE +# define CAP_NET_BIND_SERVICE 10 +#endif +#ifndef CAP_NET_ADMIN +# define CAP_NET_ADMIN 12 +#endif +#ifndef CAP_NET_RAW +# define CAP_NET_RAW 13 +#endif /** * POSIX capability dropping abstraction layer. @@ -36,11 +51,26 @@ typedef struct capabilities_t capabilities_t; struct capabilities_t { /** - * Register a capability to keep while calling drop(). + * Register a capability to keep while calling drop(). Verifies that the + * capability is currently held. + * + * @note CAP_CHOWN is handled specially as it might not be required. * * @param cap capability to keep + * @return FALSE if the capability is currently not held + */ + bool (*keep)(capabilities_t *this, + u_int cap) __attribute__((warn_unused_result)); + + /** + * Check if the given capability is currently held. + * + * @note CAP_CHOWN is handled specially as it might not be required. + * + * @param cap capability to check + * @return TRUE if the capability is currently held */ - void (*keep)(capabilities_t *this, u_int cap); + bool (*check)(capabilities_t *this, u_int cap); /** * Get the user ID set through set_uid/resolve_uid. diff --git a/src/libstrongswan/utils/chunk.c b/src/libstrongswan/utils/chunk.c index d7f1c31d9..04f3eea7d 100644 --- a/src/libstrongswan/utils/chunk.c +++ b/src/libstrongswan/utils/chunk.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Tobias Brunner + * Copyright (C) 2008-2013 Tobias Brunner * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -16,24 +16,17 @@ */ #include <stdio.h> +#include <sys/types.h> #include <sys/stat.h> +#include <fcntl.h> #include <unistd.h> #include <errno.h> +#include <pthread.h> #include <ctype.h> #include "chunk.h" #include "debug.h" -/* required for chunk_hash */ -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) -#define get16bits(d) (*((const u_int16_t*)(d))) -#endif -#if !defined (get16bits) -#define get16bits(d) ((((u_int32_t)(((const u_int8_t*)(d))[1])) << 8)\ - + (u_int32_t)(((const u_int8_t*)(d))[0]) ) -#endif - /** * Empty chunk. */ @@ -579,72 +572,193 @@ bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace) } /** - * Described in header. - * - * The implementation is based on Paul Hsieh's SuperFastHash: - * http://www.azillionmonkeys.com/qed/hash.html + * Helper functions for chunk_mac() */ -u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash) +static inline u_int64_t sipget(u_char *in) { - u_char *data = chunk.ptr; - size_t len = chunk.len; - u_int32_t tmp; - int rem; + u_int64_t v = 0; + int i; - if (!len || data == NULL) + for (i = 0; i < 64; i += 8, ++in) { - return 0; + v |= ((u_int64_t)*in) << i; } + return v; +} - rem = len & 3; - len >>= 2; +static inline u_int64_t siprotate(u_int64_t v, int shift) +{ + return (v << shift) | (v >> (64 - shift)); +} - /* Main loop */ - for (; len > 0; --len) - { - hash += get16bits(data); - tmp = (get16bits(data + 2) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - data += 2 * sizeof(u_int16_t); - hash += hash >> 11; - } +static inline void sipround(u_int64_t *v0, u_int64_t *v1, u_int64_t *v2, + u_int64_t *v3) +{ + *v0 += *v1; + *v1 = siprotate(*v1, 13); + *v1 ^= *v0; + *v0 = siprotate(*v0, 32); + + *v2 += *v3; + *v3 = siprotate(*v3, 16); + *v3 ^= *v2; + + *v2 += *v1; + *v1 = siprotate(*v1, 17); + *v1 ^= *v2; + *v2 = siprotate(*v2, 32); + + *v0 += *v3; + *v3 = siprotate(*v3, 21); + *v3 ^= *v0; +} + +static inline void sipcompress(u_int64_t *v0, u_int64_t *v1, u_int64_t *v2, + u_int64_t *v3, u_int64_t m) +{ + *v3 ^= m; + sipround(v0, v1, v2, v3); + sipround(v0, v1, v2, v3); + *v0 ^= m; +} - /* Handle end cases */ +static inline u_int64_t siplast(size_t len, u_char *pos) +{ + u_int64_t b; + int rem = len & 7; + + b = ((u_int64_t)len) << 56; switch (rem) { + case 7: + b |= ((u_int64_t)pos[6]) << 48; + case 6: + b |= ((u_int64_t)pos[5]) << 40; + case 5: + b |= ((u_int64_t)pos[4]) << 32; + case 4: + b |= ((u_int64_t)pos[3]) << 24; case 3: - { - hash += get16bits(data); - hash ^= hash << 16; - hash ^= data[sizeof(u_int16_t)] << 18; - hash += hash >> 11; - break; - } + b |= ((u_int64_t)pos[2]) << 16; case 2: - { - hash += get16bits(data); - hash ^= hash << 11; - hash += hash >> 17; + b |= ((u_int64_t)pos[1]) << 8; + case 1: + b |= ((u_int64_t)pos[0]); + break; + case 0: break; + } + return b; +} + +/** + * Caculate SipHash-2-4 with an optional first block given as argument. + */ +static u_int64_t chunk_mac_inc(chunk_t chunk, u_char *key, u_int64_t m) +{ + u_int64_t v0, v1, v2, v3, k0, k1; + size_t len = chunk.len; + u_char *pos = chunk.ptr, *end; + + end = chunk.ptr + len - (len % 8); + + k0 = sipget(key); + k1 = sipget(key + 8); + + v0 = k0 ^ 0x736f6d6570736575ULL; + v1 = k1 ^ 0x646f72616e646f6dULL; + v2 = k0 ^ 0x6c7967656e657261ULL; + v3 = k1 ^ 0x7465646279746573ULL; + + if (m) + { + sipcompress(&v0, &v1, &v2, &v3, m); + } + + /* compression with c = 2 */ + for (; pos != end; pos += 8) + { + m = sipget(pos); + sipcompress(&v0, &v1, &v2, &v3, m); + } + sipcompress(&v0, &v1, &v2, &v3, siplast(len, pos)); + + /* finalization with d = 4 */ + v2 ^= 0xff; + sipround(&v0, &v1, &v2, &v3); + sipround(&v0, &v1, &v2, &v3); + sipround(&v0, &v1, &v2, &v3); + sipround(&v0, &v1, &v2, &v3); + return v0 ^ v1 ^ v2 ^ v3; +} + +/** + * Described in header. + */ +u_int64_t chunk_mac(chunk_t chunk, u_char *key) +{ + return chunk_mac_inc(chunk, key, 0); +} + +/** + * Secret key allocated randomly during first use. + */ +static u_char key[16]; + +/** + * Static key used in case predictable hash values are required. + */ +static u_char static_key[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + +/** + * Only allocate the key once + */ +static pthread_once_t key_allocated = PTHREAD_ONCE_INIT; + +/** + * Allocate a key on first use, we do this manually to avoid dependencies on + * plugins. + */ +static void allocate_key() +{ + ssize_t len; + size_t done = 0; + int fd; + + fd = open("/dev/urandom", O_RDONLY); + if (fd >= 0) + { + while (done < sizeof(key)) + { + len = read(fd, key + done, sizeof(key) - done); + if (len < 0) + { + break; + } + done += len; } - case 1: + close(fd); + } + /* on error we use random() to generate the key (better than nothing) */ + if (done < sizeof(key)) + { + srandom(time(NULL) + getpid()); + for (; done < sizeof(key); done++) { - hash += *data; - hash ^= hash << 10; - hash += hash >> 1; - break; + key[done] = (u_char)random(); } } +} - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return hash; +/** + * Described in header. + */ +u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash) +{ + pthread_once(&key_allocated, allocate_key); + /* we could use a mac of the previous hash, but this is faster */ + return chunk_mac_inc(chunk, key, ((u_int64_t)hash) << 32 | hash); } /** @@ -652,7 +766,24 @@ u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash) */ u_int32_t chunk_hash(chunk_t chunk) { - return chunk_hash_inc(chunk, chunk.len); + pthread_once(&key_allocated, allocate_key); + return chunk_mac(chunk, key); +} + +/** + * Described in header. + */ +u_int32_t chunk_hash_static_inc(chunk_t chunk, u_int32_t hash) +{ /* we could use a mac of the previous hash, but this is faster */ + return chunk_mac_inc(chunk, static_key, ((u_int64_t)hash) << 32 | hash); +} + +/** + * Described in header. + */ +u_int32_t chunk_hash_static(chunk_t chunk) +{ + return chunk_mac(chunk, static_key); } /** diff --git a/src/libstrongswan/utils/chunk.h b/src/libstrongswan/utils/chunk.h index bc14b7394..34ba77357 100644 --- a/src/libstrongswan/utils/chunk.h +++ b/src/libstrongswan/utils/chunk.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Tobias Brunner + * Copyright (C) 2008-2013 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -191,9 +191,9 @@ static inline void chunk_clear(chunk_t *chunk) #define chunk_from_thing(thing) chunk_create((char*)&(thing), sizeof(thing)) /** - * Initialize a chunk from a static string, not containing 0-terminator + * Initialize a chunk from a string, not containing 0-terminator */ -#define chunk_from_str(str) chunk_create(str, strlen(str)) +#define chunk_from_str(str) ({char *x = (str); chunk_create(x, strlen(x));}) /** * Allocate a chunk on the heap @@ -301,16 +301,68 @@ bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace); /** * Computes a 32 bit hash of the given chunk. - * Note: This hash is only intended for hash tables not for cryptographic purposes. + * + * @note The output of this function is randomized, that is, it will only + * produce the same output for the same input when calling it from the same + * process. For a more predictable hash function use chunk_hash_static() + * instead. + * + * @note This hash is only intended for hash tables not for cryptographic + * purposes. + * + * @param chunk data to hash + * @return hash value */ u_int32_t chunk_hash(chunk_t chunk); /** * Incremental version of chunk_hash. Use this to hash two or more chunks. + * + * @param chunk data to hash + * @param hash previous hash value + * @return hash value */ u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash); /** + * Computes a 32 bit hash of the given chunk. + * + * Compared to chunk_hash() this will always calculate the same output for the + * same input. Therefore, it should not be used for hash tables (to prevent + * hash flooding). + * + * @note This hash is not intended for cryptographic purposes. + * + * @param chunk data to hash + * @return hash value + */ +u_int32_t chunk_hash_static(chunk_t chunk); + +/** + * Incremental version of chunk_hash_static(). Use this to hash two or more + * chunks in a predictable way. + * + * @param chunk data to hash + * @param hash previous hash value + * @return hash value + */ +u_int32_t chunk_hash_static_inc(chunk_t chunk, u_int32_t hash); + +/** + * Computes a quick MAC from the given chunk and key using SipHash. + * + * The key must have a length of 128-bit (16 bytes). + * + * @note While SipHash has strong features using it for cryptographic purposes + * is not recommended (in particular because of the rather short output size). + * + * @param chunk data to process + * @param key key to use + * @return MAC for given input and key + */ +u_int64_t chunk_mac(chunk_t chunk, u_char *key); + +/** * printf hook function for chunk_t. * * Arguments are: diff --git a/src/libstrongswan/utils/enum.c b/src/libstrongswan/utils/enum.c index 9b3c4d566..3db9a34e0 100644 --- a/src/libstrongswan/utils/enum.c +++ b/src/libstrongswan/utils/enum.c @@ -47,7 +47,7 @@ int enum_from_name(enum_name_t *e, char *name) for (i = 0; i < count; i++) { - if (strcaseeq(name, e->names[i])) + if (name && strcaseeq(name, e->names[i])) { return e->first + i; } diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c index 4176320dc..5df3e5fe2 100644 --- a/src/libstrongswan/utils/identification.c +++ b/src/libstrongswan/utils/identification.c @@ -277,6 +277,23 @@ METHOD(identification_t, create_part_enumerator, enumerator_t*, } /** + * Print a separator between two RDNs + */ +static inline bool print_separator(char **buf, size_t *len) +{ + int written; + + written = snprintf(*buf, *len, ", "); + if (written < 0 || written >= *len) + { + return FALSE; + } + *buf += written; + *len -= written; + return TRUE; +} + +/** * Print a DN with all its RDN in a buffer to present it to the user */ static void dntoa(chunk_t dn, char *buf, size_t len) @@ -292,8 +309,14 @@ static void dntoa(chunk_t dn, char *buf, size_t len) { empty = FALSE; - oid = asn1_known_oid(oid_data); + /* previous RDN was empty but it wasn't the last one */ + if (finished && !print_separator(&buf, &len)) + { + break; + } + finished = FALSE; + oid = asn1_known_oid(oid_data); if (oid == OID_UNKNOWN) { written = snprintf(buf, len, "%#B=", &oid_data); @@ -319,21 +342,19 @@ static void dntoa(chunk_t dn, char *buf, size_t len) buf += written; len -= written; - if (data.ptr + data.len != dn.ptr + dn.len) - { - written = snprintf(buf, len, ", "); - if (written < 0 || written >= len) - { - break; - } - buf += written; - len -= written; + if (!data.ptr) + { /* we can't calculate if we're finished, assume we are */ + finished = TRUE; } - else + else if (data.ptr + data.len == dn.ptr + dn.len) { finished = TRUE; break; } + else if (!print_separator(&buf, &len)) + { + break; + } } if (empty) { @@ -377,7 +398,7 @@ static status_t atodn(char *src, chunk_t *dn) switch (state) { case SEARCH_OID: - if (*src != ' ' && *src != '/' && *src != ',') + if (*src != ' ' && *src != '/' && *src != ',' && *src != '\0') { oid.ptr = src; oid.len = 1; @@ -418,7 +439,7 @@ static status_t atodn(char *src, chunk_t *dn) { break; } - else if (*src != ',' && *src != '/') + else if (*src != ',' && *src != '/' && *src != '\0') { name.ptr = src; name.len = 1; @@ -481,6 +502,11 @@ static status_t atodn(char *src, chunk_t *dn) } } while (*src++ != '\0'); + if (state == READ_OID) + { /* unterminated OID */ + status = INVALID_ARG; + } + /* build the distinguished name sequence */ { int i; @@ -493,7 +519,6 @@ static status_t atodn(char *src, chunk_t *dn) free(rdns[i].ptr); } } - if (status != SUCCESS) { free(dn->ptr); @@ -799,7 +824,7 @@ int identification_printf_hook(printf_hook_data_t *data, dntoa(this->encoded, buf, sizeof(buf)); break; case ID_DER_ASN1_GN: - snprintf(buf, sizeof(buf), "(ASN.1 general Name"); + snprintf(buf, sizeof(buf), "(ASN.1 general name)"); break; case ID_KEY_ID: if (chunk_printable(this->encoded, NULL, '?') && @@ -915,14 +940,15 @@ identification_t *identification_create_from_string(char *string) else { this = identification_create(ID_KEY_ID); - this->encoded = chunk_clone(chunk_create(string, strlen(string))); + this->encoded = chunk_from_str(strdup(string)); } return &this->public; } else if (strchr(string, '@') == NULL) { - if (streq(string, "%any") - || streq(string, "%any6") + if (streq(string, "") + || streq(string, "%any") + || streq(string, "%any6") || streq(string, "0.0.0.0") || streq(string, "*") || streq(string, "::") @@ -947,11 +973,7 @@ identification_t *identification_create_from_string(char *string) else { /* not IPv4, mostly FQDN */ this = identification_create(ID_FQDN); - this->encoded.len = strlen(string); - if (this->encoded.len) - { - this->encoded.ptr = strdup(string); - } + this->encoded = chunk_from_str(strdup(string)); } return &this->public; } @@ -968,11 +990,7 @@ identification_t *identification_create_from_string(char *string) else { /* not IPv4/6 fallback to KEY_ID */ this = identification_create(ID_KEY_ID); - this->encoded.len = strlen(string); - if (this->encoded.len) - { - this->encoded.ptr = strdup(string); - } + this->encoded = chunk_from_str(strdup(string)); } return &this->public; } @@ -982,34 +1000,30 @@ identification_t *identification_create_from_string(char *string) { if (*string == '@') { - if (*(string + 1) == '#') + string++; + if (*string == '#') { this = identification_create(ID_KEY_ID); - string += 2; - this->encoded = chunk_from_hex( - chunk_create(string, strlen(string)), NULL); + this->encoded = chunk_from_hex(chunk_from_str(string + 1), NULL); + return &this->public; + } + else if (*string == '@') + { + this = identification_create(ID_USER_FQDN); + this->encoded = chunk_clone(chunk_from_str(string + 1)); return &this->public; } else { this = identification_create(ID_FQDN); - string += 1; - this->encoded.len = strlen(string); - if (this->encoded.len) - { - this->encoded.ptr = strdup(string); - } + this->encoded = chunk_clone(chunk_from_str(string)); return &this->public; } } else { this = identification_create(ID_RFC822_ADDR); - this->encoded.len = strlen(string); - if (this->encoded.len) - { - this->encoded.ptr = strdup(string); - } + this->encoded = chunk_from_str(strdup(string)); return &this->public; } } @@ -1079,4 +1093,3 @@ identification_t *identification_create_from_sockaddr(sockaddr_t *sockaddr) } } } - diff --git a/src/libstrongswan/utils/identification.h b/src/libstrongswan/utils/identification.h index 00d740765..e62446879 100644 --- a/src/libstrongswan/utils/identification.h +++ b/src/libstrongswan/utils/identification.h @@ -241,7 +241,6 @@ struct identification_t { * no match at all, 1 means a bad match, and 2 a slightly better match. * * @param other the ID containing one or more wildcards - * @param wildcards returns the number of wildcards, may be NULL * @return match value as described above */ id_match_t (*matches) (identification_t *this, identification_t *other); diff --git a/src/libstrongswan/utils/integrity_checker.c b/src/libstrongswan/utils/integrity_checker.c index e962aba70..d59a76232 100644 --- a/src/libstrongswan/utils/integrity_checker.c +++ b/src/libstrongswan/utils/integrity_checker.c @@ -91,7 +91,7 @@ METHOD(integrity_checker_t, build_file, u_int32_t, *len = sb.st_size; contents = chunk_create(addr, sb.st_size); - checksum = chunk_hash(contents); + checksum = chunk_hash_static(contents); munmap(addr, sb.st_size); close(fd); @@ -153,7 +153,7 @@ METHOD(integrity_checker_t, build_segment, u_int32_t, segment = chunk_create(dli.dli_fbase, dli.dli_saddr - dli.dli_fbase); *len = segment.len; - return chunk_hash(segment); + return chunk_hash_static(segment); } /** diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 6bf4d63cd..ffbc62085 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006-2008 Martin Willi + * Copyright (C) 2013 Tobias Brunner + * Copyright (C) 2006-2013 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -14,20 +15,29 @@ */ #define _GNU_SOURCE -#include <sched.h> #include <stddef.h> #include <string.h> #include <stdio.h> -#include <malloc.h> #include <signal.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <syslog.h> -#include <pthread.h> #include <netdb.h> #include <locale.h> +#include <dlfcn.h> +#include <time.h> +#include <errno.h> + +#ifdef __APPLE__ +#include <sys/mman.h> +#include <malloc/malloc.h> +/* overload some of our types clashing with mach */ +#define host_t strongswan_host_t +#define processor_t strongswan_processor_t +#define thread_t strongswan_thread_t +#endif /* __APPLE__ */ #include "leak_detective.h" @@ -35,6 +45,8 @@ #include <utils/debug.h> #include <utils/backtrace.h> #include <collections/hashtable.h> +#include <threading/thread_value.h> +#include <threading/spinlock.h> typedef struct private_leak_detective_t private_leak_detective_t; @@ -69,21 +81,6 @@ struct private_leak_detective_t { */ #define MEMORY_ALLOC_PATTERN 0xEE - -static void install_hooks(void); -static void uninstall_hooks(void); -static void *malloc_hook(size_t, const void *); -static void *realloc_hook(void *, size_t, const void *); -static void free_hook(void*, const void *); - -void *(*old_malloc_hook)(size_t, const void *); -void *(*old_realloc_hook)(void *, size_t, const void *); -void (*old_free_hook)(void*, const void *); - -static u_int count_malloc = 0; -static u_int count_free = 0; -static u_int count_realloc = 0; - typedef struct memory_header_t memory_header_t; typedef struct memory_tail_t memory_tail_t; @@ -108,6 +105,11 @@ struct memory_header_t { backtrace_t *backtrace; /** + * Padding to make sizeof(memory_header_t) == 32 + */ + u_int32_t padding[sizeof(void*) == sizeof(u_int32_t) ? 3 : 0]; + + /** * Number of bytes following after the header */ u_int32_t bytes; @@ -136,50 +138,337 @@ struct memory_tail_t { * the others on it... */ static memory_header_t first_header = { - magic: MEMORY_HEADER_MAGIC, - bytes: 0, - backtrace: NULL, - previous: NULL, - next: NULL + .magic = MEMORY_HEADER_MAGIC, }; /** - * are the hooks currently installed? + * Spinlock to access header linked list + */ +static spinlock_t *lock; + +/** + * Is leak detection currently enabled? + */ +static bool enabled = FALSE; + +/** + * Is leak detection disabled for the current thread? */ -static bool installed = FALSE; +static thread_value_t *thread_disabled; /** * Installs the malloc hooks, enables leak detection */ -static void install_hooks() +static void enable_leak_detective() +{ + enabled = TRUE; +} + +/** + * Uninstalls the malloc hooks, disables leak detection + */ +static void disable_leak_detective() +{ + enabled = FALSE; +} + +/** + * Enable/Disable leak detective for the current thread + * + * @return Previous value + */ +static bool enable_thread(bool enable) +{ + bool before; + + before = thread_disabled->get(thread_disabled) == NULL; + thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE); + return before; +} + +/** + * Add a header to the beginning of the list + */ +static void add_hdr(memory_header_t *hdr) { - if (!installed) + lock->lock(lock); + hdr->next = first_header.next; + if (hdr->next) { - old_malloc_hook = __malloc_hook; - old_realloc_hook = __realloc_hook; - old_free_hook = __free_hook; - __malloc_hook = malloc_hook; - __realloc_hook = realloc_hook; - __free_hook = free_hook; - installed = TRUE; + hdr->next->previous = hdr; } + hdr->previous = &first_header; + first_header.next = hdr; + lock->unlock(lock); } /** - * Uninstalls the malloc hooks, disables leak detection + * Remove a header from the list + */ +static void remove_hdr(memory_header_t *hdr) +{ + lock->lock(lock); + if (hdr->next) + { + hdr->next->previous = hdr->previous; + } + hdr->previous->next = hdr->next; + lock->unlock(lock); +} + +/** + * Check if a header is in the list + */ +static bool has_hdr(memory_header_t *hdr) +{ + memory_header_t *current; + bool found = FALSE; + + lock->lock(lock); + for (current = &first_header; current != NULL; current = current->next) + { + if (current == hdr) + { + found = TRUE; + break; + } + } + lock->unlock(lock); + + return found; +} + +#ifdef __APPLE__ + +/** + * Copy of original default zone, with functions we call in hooks + */ +static malloc_zone_t original; + +/** + * Call original malloc() + */ +static void* real_malloc(size_t size) +{ + return original.malloc(malloc_default_zone(), size); +} + +/** + * Call original free() + */ +static void real_free(void *ptr) +{ + original.free(malloc_default_zone(), ptr); +} + +/** + * Call original realloc() + */ +static void* real_realloc(void *ptr, size_t size) +{ + return original.realloc(malloc_default_zone(), ptr, size); +} + +/** + * Hook definition: static function with _hook suffix, takes additional zone + */ +#define HOOK(ret, name, ...) \ + static ret name ## _hook(malloc_zone_t *_z, __VA_ARGS__) + +/** + * forward declaration of hooks + */ +HOOK(void*, malloc, size_t bytes); +HOOK(void*, calloc, size_t nmemb, size_t size); +HOOK(void*, valloc, size_t size); +HOOK(void, free, void *ptr); +HOOK(void*, realloc, void *old, size_t bytes); + +/** + * malloc zone size(), must consider the memory header prepended + */ +HOOK(size_t, size, const void *ptr) +{ + bool before; + size_t size; + + if (enabled) + { + before = enable_thread(FALSE); + if (before) + { + ptr -= sizeof(memory_header_t); + } + } + size = original.size(malloc_default_zone(), ptr); + if (enabled) + { + enable_thread(before); + } + return size; +} + +/** + * Version of malloc zones we currently support + */ +#define MALLOC_ZONE_VERSION 8 /* Snow Leopard */ + +/** + * Hook-in our malloc functions into the default zone + */ +static bool register_hooks() +{ + malloc_zone_t *zone; + void *page; + + zone = malloc_default_zone(); + if (zone->version != MALLOC_ZONE_VERSION) + { + DBG1(DBG_CFG, "malloc zone version %d unsupported (requiring %d)", + zone->version, MALLOC_ZONE_VERSION); + return FALSE; + } + + original = *zone; + + page = (void*)((uintptr_t)zone / getpagesize() * getpagesize()); + if (mprotect(page, getpagesize(), PROT_WRITE | PROT_READ) != 0) + { + DBG1(DBG_CFG, "malloc zone unprotection failed: %s", strerror(errno)); + return FALSE; + } + + zone->size = size_hook; + zone->malloc = malloc_hook; + zone->calloc = calloc_hook; + zone->valloc = valloc_hook; + zone->free = free_hook; + zone->realloc = realloc_hook; + + /* those other functions can be NULLed out to not use them */ + zone->batch_malloc = NULL; + zone->batch_free = NULL; + zone->memalign = NULL; + zone->free_definite_size = NULL; + + return TRUE; +} + +#else /* !__APPLE__ */ + +/** + * dlsym() might do a malloc(), but we can't do one before we get the malloc() + * function pointer. Use this minimalistic malloc implementation instead. + */ +static void* malloc_for_dlsym(size_t size) +{ + static char buf[1024] = {}; + static size_t used = 0; + char *ptr; + + /* roundup to a multiple of 32 */ + size = (size - 1) / 32 * 32 + 32; + + if (used + size > sizeof(buf)) + { + return NULL; + } + ptr = buf + used; + used += size; + return ptr; +} + +/** + * Lookup a malloc function, while disabling wrappers */ -static void uninstall_hooks() +static void* get_malloc_fn(char *name) { - if (installed) + bool before = FALSE; + void *fn; + + if (enabled) + { + before = enable_thread(FALSE); + } + fn = dlsym(RTLD_NEXT, name); + if (enabled) + { + enable_thread(before); + } + return fn; +} + +/** + * Call original malloc() + */ +static void* real_malloc(size_t size) +{ + static void* (*fn)(size_t size); + static int recursive = 0; + + if (!fn) { - __malloc_hook = old_malloc_hook; - __free_hook = old_free_hook; - __realloc_hook = old_realloc_hook; - installed = FALSE; + /* checking recursiveness should actually be thread-specific. But as + * it is very likely that the first allocation is done before we go + * multi-threaded, we keep it simple. */ + if (recursive) + { + return malloc_for_dlsym(size); + } + recursive++; + fn = get_malloc_fn("malloc"); + recursive--; } + return fn(size); } /** + * Call original free() + */ +static void real_free(void *ptr) +{ + static void (*fn)(void *ptr); + + if (!fn) + { + fn = get_malloc_fn("free"); + } + return fn(ptr); +} + +/** + * Call original realloc() + */ +static void* real_realloc(void *ptr, size_t size) +{ + static void* (*fn)(void *ptr, size_t size); + + if (!fn) + { + fn = get_malloc_fn("realloc"); + } + return fn(ptr, size); +} + +/** + * Hook definition: plain function overloading existing malloc calls + */ +#define HOOK(ret, name, ...) ret name(__VA_ARGS__) + +/** + * Hook initialization when not using hooks, resolve functions. + */ +static bool register_hooks() +{ + void *buf = real_malloc(8); + real_realloc(buf, 16); + real_free(buf); + return TRUE; +} + +#endif /* !__APPLE__ */ + +/** * Leak report white list * * List of functions using static allocation buffers or should be suppressed @@ -194,12 +483,6 @@ char *whitelist[] = { "pthread_setspecific", "__pthread_setspecific", /* glibc functions */ - "mktime", - "ctime", - "__gmtime_r", - "localtime_r", - "tzset", - "time_printf_hook", "inet_ntoa", "strerror", "getprotobyname", @@ -247,18 +530,16 @@ char *whitelist[] = { "Curl_client_write", /* ClearSilver */ "nerr_init", - /* OpenSSL */ - "RSA_new_method", - "DH_new_method", - "ENGINE_load_builtin_engines", - "OPENSSL_config", - "ecdsa_check", - "ERR_put_error", /* libgcrypt */ "gcry_control", "gcry_check_version", "gcry_randomize", "gcry_create_nonce", + /* OpenSSL: These are needed for unit-tests only, the openssl plugin + * does properly clean up any memory during destroy(). */ + "ECDSA_do_sign_ex", + "ECDSA_verify", + "RSA_new_method", /* NSPR */ "PR_CallOnce", /* libapr */ @@ -277,6 +558,14 @@ char *whitelist[] = { "gnutls_global_init", }; +/** + * Some functions are hard to whitelist, as they don't use a symbol directly. + * Use some static initialization to suppress them on leak reports + */ +static void init_static_allocations() +{ + tzset(); +} /** * Hashtable hash function @@ -309,7 +598,8 @@ static bool equals(backtrace_t *a, backtrace_t *b) * Summarize and print backtraces */ static int print_traces(private_leak_detective_t *this, - FILE *out, int thresh, bool detailed, int *whitelisted) + FILE *out, int thresh, int thresh_count, + bool detailed, int *whitelisted, size_t *sum) { int leaks = 0; memory_header_t *hdr; @@ -323,11 +613,13 @@ static int print_traces(private_leak_detective_t *this, /** number of allocations */ u_int count; } *entry; + bool before; - uninstall_hooks(); + before = enable_thread(FALSE); entries = hashtable_create((hashtable_hash_t)hash, (hashtable_equals_t)equals, 1024); + lock->lock(lock); for (hdr = first_header.next; hdr != NULL; hdr = hdr->next) { if (whitelisted && @@ -346,29 +638,37 @@ static int print_traces(private_leak_detective_t *this, else { INIT(entry, - .backtrace = hdr->backtrace, + .backtrace = hdr->backtrace->clone(hdr->backtrace), .bytes = hdr->bytes, .count = 1, ); - entries->put(entries, hdr->backtrace, entry); + entries->put(entries, entry->backtrace, entry); + } + if (sum) + { + *sum += hdr->bytes; } leaks++; } + lock->unlock(lock); enumerator = entries->create_enumerator(entries); while (enumerator->enumerate(enumerator, NULL, &entry)) { - if (!thresh || entry->bytes >= thresh) + if (out && + (!thresh || entry->bytes >= thresh) && + (!thresh_count || entry->count >= thresh_count)) { fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n", entry->bytes, entry->count, entry->bytes / entry->count); entry->backtrace->log(entry->backtrace, out, detailed); } + entry->backtrace->destroy(entry->backtrace); free(entry); } enumerator->destroy(enumerator); entries->destroy(entries); - install_hooks(); + enable_thread(before); return leaks; } @@ -377,9 +677,10 @@ METHOD(leak_detective_t, report, void, { if (lib->leak_detective) { - int leaks = 0, whitelisted = 0; + int leaks, whitelisted = 0; + size_t sum = 0; - leaks = print_traces(this, stderr, 0, detailed, &whitelisted); + leaks = print_traces(this, stderr, 0, 0, detailed, &whitelisted, &sum); switch (leaks) { case 0: @@ -389,7 +690,7 @@ METHOD(leak_detective_t, report, void, fprintf(stderr, "One leak detected"); break; default: - fprintf(stderr, "%d leaks detected", leaks); + fprintf(stderr, "%d leaks detected, %zu bytes", leaks, sum); break; } fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted); @@ -400,114 +701,115 @@ METHOD(leak_detective_t, report, void, } } -METHOD(leak_detective_t, set_state, bool, - private_leak_detective_t *this, bool enable) +METHOD(leak_detective_t, leaks, int, + private_leak_detective_t *this) { - static struct sched_param oldparams; - static int oldpolicy; - struct sched_param params; - pthread_t thread_id; - - if (enable == installed) - { - return installed; - } - thread_id = pthread_self(); - if (enable) - { - install_hooks(); - pthread_setschedparam(thread_id, oldpolicy, &oldparams); - } - else + if (lib->leak_detective) { - pthread_getschedparam(thread_id, &oldpolicy, &oldparams); - params.__sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_setschedparam(thread_id, SCHED_FIFO, ¶ms); - uninstall_hooks(); + int leaks, whitelisted = 0; + + leaks = print_traces(this, NULL, 0, 0, FALSE, &whitelisted, NULL); + return leaks; } - installed = enable; - return !installed; + return 0; +} + +METHOD(leak_detective_t, set_state, bool, + private_leak_detective_t *this, bool enable) +{ + return enable_thread(enable); } METHOD(leak_detective_t, usage, void, private_leak_detective_t *this, FILE *out) { - int oldpolicy, thresh; bool detailed; - pthread_t thread_id = pthread_self(); - struct sched_param oldparams, params; + int thresh, thresh_count; + size_t sum = 0; thresh = lib->settings->get_int(lib->settings, "libstrongswan.leak_detective.usage_threshold", 10240); + thresh_count = lib->settings->get_int(lib->settings, + "libstrongswan.leak_detective.usage_threshold_count", 0); detailed = lib->settings->get_bool(lib->settings, "libstrongswan.leak_detective.detailed", TRUE); - pthread_getschedparam(thread_id, &oldpolicy, &oldparams); - params.__sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_setschedparam(thread_id, SCHED_FIFO, ¶ms); - - print_traces(this, out, thresh, detailed, NULL); + print_traces(this, out, thresh, thresh_count, detailed, NULL, &sum); - pthread_setschedparam(thread_id, oldpolicy, &oldparams); + fprintf(out, "Total memory usage: %zu\n", sum); } /** - * Hook function for malloc() + * Wrapped malloc() function */ -void *malloc_hook(size_t bytes, const void *caller) +HOOK(void*, malloc, size_t bytes) { memory_header_t *hdr; memory_tail_t *tail; - pthread_t thread_id = pthread_self(); - int oldpolicy; - struct sched_param oldparams, params; + bool before; - pthread_getschedparam(thread_id, &oldpolicy, &oldparams); - - params.__sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_setschedparam(thread_id, SCHED_FIFO, ¶ms); + if (!enabled || thread_disabled->get(thread_disabled)) + { + return real_malloc(bytes); + } - count_malloc++; - uninstall_hooks(); - hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); + hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); tail = ((void*)hdr) + bytes + sizeof(memory_header_t); /* set to something which causes crashes */ memset(hdr, MEMORY_ALLOC_PATTERN, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); + before = enable_thread(FALSE); + hdr->backtrace = backtrace_create(2); + enable_thread(before); + hdr->magic = MEMORY_HEADER_MAGIC; hdr->bytes = bytes; - hdr->backtrace = backtrace_create(2); tail->magic = MEMORY_TAIL_MAGIC; - install_hooks(); - - /* insert at the beginning of the list */ - hdr->next = first_header.next; - if (hdr->next) - { - hdr->next->previous = hdr; - } - hdr->previous = &first_header; - first_header.next = hdr; - pthread_setschedparam(thread_id, oldpolicy, &oldparams); + add_hdr(hdr); return hdr + 1; } /** - * Hook function for free() + * Wrapped calloc() function */ -void free_hook(void *ptr, const void *caller) +HOOK(void*, calloc, size_t nmemb, size_t size) { - memory_header_t *hdr, *current; + void *ptr; + + size *= nmemb; + ptr = malloc(size); + memset(ptr, 0, size); + + return ptr; +} + +/** + * Wrapped valloc(), TODO: currently not supported + */ +HOOK(void*, valloc, size_t size) +{ + DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing"); + return NULL; +} + +/** + * Wrapped free() function + */ +HOOK(void, free, void *ptr) +{ + memory_header_t *hdr; memory_tail_t *tail; backtrace_t *backtrace; - pthread_t thread_id = pthread_self(); - int oldpolicy; - struct sched_param oldparams, params; - bool found = FALSE; + bool before; + if (!enabled || thread_disabled->get(thread_disabled)) + { + real_free(ptr); + return; + } /* allow freeing of NULL */ if (ptr == NULL) { @@ -516,25 +818,11 @@ void free_hook(void *ptr, const void *caller) hdr = ptr - sizeof(memory_header_t); tail = ptr + hdr->bytes; - pthread_getschedparam(thread_id, &oldpolicy, &oldparams); - - params.__sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_setschedparam(thread_id, SCHED_FIFO, ¶ms); - - count_free++; - uninstall_hooks(); + before = enable_thread(FALSE); if (hdr->magic != MEMORY_HEADER_MAGIC || tail->magic != MEMORY_TAIL_MAGIC) { - for (current = &first_header; current != NULL; current = current->next) - { - if (current == hdr) - { - found = TRUE; - break; - } - } - if (found) + if (has_hdr(hdr)) { /* memory was allocated by our hooks but is corrupted */ fprintf(stderr, "freeing corrupted memory (%p): " @@ -544,7 +832,7 @@ void free_hook(void *ptr, const void *caller) else { /* memory was not allocated by our hooks */ - fprintf(stderr, "freeing invalid memory (%p)", ptr); + fprintf(stderr, "freeing invalid memory (%p)\n", ptr); } backtrace = backtrace_create(2); backtrace->log(backtrace, stderr, TRUE); @@ -552,53 +840,50 @@ void free_hook(void *ptr, const void *caller) } else { - /* remove item from list */ - if (hdr->next) - { - hdr->next->previous = hdr->previous; - } - hdr->previous->next = hdr->next; + remove_hdr(hdr); + hdr->backtrace->destroy(hdr->backtrace); /* clear MAGIC, set mem to something remarkable */ memset(hdr, MEMORY_FREE_PATTERN, sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t)); - free(hdr); + real_free(hdr); } - - install_hooks(); - pthread_setschedparam(thread_id, oldpolicy, &oldparams); + enable_thread(before); } /** - * Hook function for realloc() + * Wrapped realloc() function */ -void *realloc_hook(void *old, size_t bytes, const void *caller) +HOOK(void*, realloc, void *old, size_t bytes) { memory_header_t *hdr; memory_tail_t *tail; backtrace_t *backtrace; - pthread_t thread_id = pthread_self(); - int oldpolicy; - struct sched_param oldparams, params; + bool before; + if (!enabled || thread_disabled->get(thread_disabled)) + { + return real_realloc(old, bytes); + } /* allow reallocation of NULL */ if (old == NULL) { - return malloc_hook(bytes, caller); + return malloc(bytes); + } + /* handle zero size as a free() */ + if (bytes == 0) + { + free(old); + return NULL; } hdr = old - sizeof(memory_header_t); tail = old + hdr->bytes; - pthread_getschedparam(thread_id, &oldpolicy, &oldparams); + remove_hdr(hdr); - params.__sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_setschedparam(thread_id, SCHED_FIFO, ¶ms); - - count_realloc++; - uninstall_hooks(); if (hdr->magic != MEMORY_HEADER_MAGIC || tail->magic != MEMORY_TAIL_MAGIC) { @@ -613,33 +898,30 @@ void *realloc_hook(void *old, size_t bytes, const void *caller) /* clear tail magic, allocate, set tail magic */ memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic)); } - hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); + hdr = real_realloc(hdr, + sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); tail = ((void*)hdr) + bytes + sizeof(memory_header_t); tail->magic = MEMORY_TAIL_MAGIC; /* update statistics */ hdr->bytes = bytes; + + before = enable_thread(FALSE); hdr->backtrace->destroy(hdr->backtrace); hdr->backtrace = backtrace_create(2); + enable_thread(before); + + add_hdr(hdr); - /* update header of linked list neighbours */ - if (hdr->next) - { - hdr->next->previous = hdr; - } - hdr->previous->next = hdr; - install_hooks(); - pthread_setschedparam(thread_id, oldpolicy, &oldparams); return hdr + 1; } METHOD(leak_detective_t, destroy, void, private_leak_detective_t *this) { - if (installed) - { - uninstall_hooks(); - } + disable_leak_detective(); + lock->destroy(lock); + thread_disabled->destroy(thread_disabled); free(this); } @@ -653,26 +935,24 @@ leak_detective_t *leak_detective_create() INIT(this, .public = { .report = _report, + .leaks = _leaks, .usage = _usage, .set_state = _set_state, .destroy = _destroy, }, ); - if (getenv("LEAK_DETECTIVE_DISABLE") == NULL) - { - cpu_set_t mask; + lock = spinlock_create(); + thread_disabled = thread_value_create(NULL); - CPU_ZERO(&mask); - CPU_SET(0, &mask); + init_static_allocations(); - if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0) + if (getenv("LEAK_DETECTIVE_DISABLE") == NULL) + { + if (register_hooks()) { - fprintf(stderr, "setting CPU affinity failed: %m"); + enable_leak_detective(); } - - install_hooks(); } return &this->public; } - diff --git a/src/libstrongswan/utils/leak_detective.h b/src/libstrongswan/utils/leak_detective.h index 55d7e44d9..7a29e81d7 100644 --- a/src/libstrongswan/utils/leak_detective.h +++ b/src/libstrongswan/utils/leak_detective.h @@ -43,6 +43,13 @@ struct leak_detective_t { void (*report)(leak_detective_t *this, bool detailed); /** + * Number of detected leaks. + * + * @return number of leaks + */ + int (*leaks)(leak_detective_t *this); + + /** * Report current memory usage to out. * * @param out target to write usage report to @@ -50,7 +57,7 @@ struct leak_detective_t { void (*usage)(leak_detective_t *this, FILE *out); /** - * Enable/disable leak detective hooks. + * Enable/disable leak detective hooks for the current thread. * * @param TRUE to enable, FALSE to disable * @return state active before calling set_state @@ -69,4 +76,3 @@ struct leak_detective_t { leak_detective_t *leak_detective_create(); #endif /** LEAK_DETECTIVE_H_ @}*/ - diff --git a/src/libstrongswan/utils/printf_hook.c b/src/libstrongswan/utils/printf_hook.c index 6e51aa4c3..f030f45c8 100644 --- a/src/libstrongswan/utils/printf_hook.c +++ b/src/libstrongswan/utils/printf_hook.c @@ -474,7 +474,6 @@ METHOD(printf_hook_t, destroy, void, /* freeing the Vstr_conf of the main thread */ vstr_conf->destroy(vstr_conf); vstr_conf = NULL; - vstr_free_conf(conf); vstr_exit(); #endif free(this); diff --git a/src/libstrongswan/utils/settings.c b/src/libstrongswan/utils/settings.c index 712ea6ee2..809ca10ab 100644 --- a/src/libstrongswan/utils/settings.c +++ b/src/libstrongswan/utils/settings.c @@ -644,6 +644,26 @@ METHOD(settings_t, set_time, void, va_end(args); } +METHOD(settings_t, set_default_str, bool, + private_settings_t *this, char *key, char *value, ...) +{ + char *old; + va_list args; + + va_start(args, value); + old = find_value(this, this->top, key, args); + va_end(args); + + if (!old) + { + va_start(args, value); + set_value(this, this->top, key, args, value); + va_end(args); + return TRUE; + } + return FALSE; +} + /** * Enumerate section names, not sections */ @@ -1209,6 +1229,7 @@ settings_t *settings_create(char *file) .set_double = _set_double, .set_time = _set_time, .set_bool = _set_bool, + .set_default_str = _set_default_str, .create_section_enumerator = _create_section_enumerator, .create_key_value_enumerator = _create_key_value_enumerator, .load_files = _load_files, diff --git a/src/libstrongswan/utils/settings.h b/src/libstrongswan/utils/settings.h index a861325f5..df0c534e9 100644 --- a/src/libstrongswan/utils/settings.h +++ b/src/libstrongswan/utils/settings.h @@ -239,6 +239,16 @@ struct settings_t { void (*set_time)(settings_t *this, char *key, u_int32_t value, ...); /** + * Set a default for string value. + * + * @param key key including sections, printf style format + * @param def value to set if unconfigured + * @param ... argument list for key + * @return TRUE if a new default value for key has been set + */ + bool (*set_default_str)(settings_t *this, char *key, char *value, ...); + + /** * Create an enumerator over subsection names of a section. * * @param section section including parents, printf style format diff --git a/src/libstrongswan/utils/utils.c b/src/libstrongswan/utils/utils.c index 2f38d8a93..30084cd81 100644 --- a/src/libstrongswan/utils/utils.c +++ b/src/libstrongswan/utils/utils.c @@ -48,19 +48,6 @@ ENUM(status_names, SUCCESS, NEED_MORE, /** * Described in header. */ -void *clalloc(void * pointer, size_t size) -{ - void *data; - data = malloc(size); - - memcpy(data, pointer, size); - - return (data); -} - -/** - * Described in header. - */ void memxor(u_int8_t dst[], u_int8_t src[], size_t n) { int m, i; @@ -115,7 +102,12 @@ void memwipe_noinline(void *ptr, size_t n) void *memstr(const void *haystack, const char *needle, size_t n) { unsigned const char *pos = haystack; - size_t l = strlen(needle); + size_t l; + + if (!haystack || !needle || (l = strlen(needle)) == 0) + { + return NULL; + } for (; n >= l; ++pos, --n) { if (memeq(pos, needle, l)) @@ -474,11 +466,15 @@ static pthread_mutex_t ref_mutex = PTHREAD_MUTEX_INITIALIZER; /** * Increase refcount */ -void ref_get(refcount_t *ref) +refcount_t ref_get(refcount_t *ref) { + refcount_t current; + pthread_mutex_lock(&ref_mutex); - (*ref)++; + current = ++(*ref); pthread_mutex_unlock(&ref_mutex); + + return current; } /** diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h index c66c665e0..d055f712d 100644 --- a/src/libstrongswan/utils/utils.h +++ b/src/libstrongswan/utils/utils.h @@ -81,9 +81,20 @@ static inline bool streq(const char *x, const char *y) } /** - * Macro compares two strings for equality, length limited + * Helper function that compares two strings for equality, length limited */ -#define strneq(x,y,len) (strncmp(x, y, len) == 0) +static inline bool strneq(const char *x, const char *y, size_t len) +{ + return strncmp(x, y, len) == 0; +} + +/** + * Helper function that checks if a string starts with a given prefix + */ +static inline bool strpfx(const char *x, const char *prefix) +{ + return strneq(x, prefix, strlen(prefix)); +} /** * Helper function that compares two strings for equality ignoring case @@ -94,9 +105,12 @@ static inline bool strcaseeq(const char *x, const char *y) } /** - * Macro compares two strings for equality ignoring case, length limited + * Helper function that compares two strings for equality ignoring case, length limited */ -#define strncaseeq(x,y,len) (strncasecmp(x, y, len) == 0) +static inline bool strncaseeq(const char *x, const char *y, size_t len) +{ + return strncasecmp(x, y, len) == 0; +} /** * NULL-safe strdup variant @@ -107,9 +121,12 @@ static inline char *strdupnull(const char *s) } /** - * Macro compares two binary blobs for equality + * Helper function that compares two binary blobs for equality */ -#define memeq(x,y,len) (memcmp(x, y, len) == 0) +static inline bool memeq(const void *x, const void *y, size_t len) +{ + return memcmp(x, y, len) == 0; +} /** * Macro gives back larger of two values. @@ -376,11 +393,6 @@ typedef struct timespec timespec_t; typedef struct sockaddr sockaddr_t; /** - * Clone a data to a newly allocated buffer - */ -void *clalloc(void *pointer, size_t size); - -/** * Same as memcpy, but XORs src into dst instead of copy */ void memxor(u_int8_t dest[], u_int8_t src[], size_t n); @@ -423,6 +435,10 @@ static inline void memwipe_inline(void *ptr, size_t n) */ static inline void memwipe(void *ptr, size_t n) { + if (!ptr) + { + return; + } if (__builtin_constant_p(n)) { memwipe_inline(ptr, n); @@ -503,7 +519,7 @@ time_t time_monotonic(timeval_t *tv); static inline void timeval_add_ms(timeval_t *tv, u_int ms) { tv->tv_usec += ms * 1000; - while (tv->tv_usec > 1000000 /* 1s */) + while (tv->tv_usec >= 1000000 /* 1s */) { tv->tv_usec -= 1000000; tv->tv_sec++; @@ -655,14 +671,36 @@ static inline u_int64_t untoh64(void *network) } /** - * Special type to count references + * Round up size to be multiple of alignement */ -typedef volatile u_int refcount_t; +static inline size_t round_up(size_t size, int alignement) +{ + int remainder; + + remainder = size % alignement; + if (remainder) + { + size += alignement - remainder; + } + return size; +} +/** + * Round down size to be a multiple of alignement + */ +static inline size_t round_down(size_t size, int alignement) +{ + return size - (size % alignement); +} + +/** + * Special type to count references + */ +typedef u_int refcount_t; #ifdef HAVE_GCC_ATOMIC_OPERATIONS -#define ref_get(ref) {__sync_fetch_and_add(ref, 1); } +#define ref_get(ref) __sync_add_and_fetch(ref, 1) #define ref_put(ref) (!__sync_sub_and_fetch(ref, 1)) #define cas_bool(ptr, oldval, newval) \ @@ -678,8 +716,9 @@ typedef volatile u_int refcount_t; * Increments the reference counter atomic. * * @param ref pointer to ref counter + * @return new value of ref */ -void ref_get(refcount_t *ref); +refcount_t ref_get(refcount_t *ref); /** * Put back a unused reference. |