diff options
Diffstat (limited to 'src/common')
48 files changed, 7621 insertions, 4919 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am deleted file mode 100644 index 5e7684259..000000000 --- a/src/common/Makefile.am +++ /dev/null @@ -1,67 +0,0 @@ - -noinst_LIBRARIES = libor.a libor-crypto.a libor-event.a - -EXTRA_DIST = common_sha1.i sha256.c Makefile.nmake - -#CFLAGS = -Wall -Wpointer-arith -O2 - -if USE_OPENBSD_MALLOC -libor_extra_source=OpenBSD_malloc_Linux.c -else -libor_extra_source= -endif - -libor_a_SOURCES = \ - address.c \ - compat.c \ - container.c \ - di_ops.c \ - log.c \ - memarea.c \ - mempool.c \ - procmon.c \ - util.c \ - util_codedigest.c \ - $(libor_extra_source) - -libor_crypto_a_SOURCES = \ - aes.c \ - crypto.c \ - torgzip.c \ - tortls.c - -libor_event_a_SOURCES = compat_libevent.c - -noinst_HEADERS = \ - address.h \ - aes.h \ - ciphers.inc \ - compat.h \ - compat_libevent.h \ - container.h \ - crypto.h \ - di_ops.h \ - ht.h \ - memarea.h \ - mempool.h \ - procmon.h \ - strlcat.c \ - strlcpy.c \ - torgzip.h \ - torint.h \ - torlog.h \ - tortls.h \ - util.h - -common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) - if test "@SHA1SUM@" != none; then \ - (cd "$(srcdir)" && "@SHA1SUM@" $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > common_sha1.i; \ - elif test "@OPENSSL@" != none; then \ - (cd "$(srcdir)" && "@OPENSSL@" sha1 $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > common_sha1.i; \ - else \ - rm common_sha1.i; \ - touch common_sha1.i; \ - fi - -util_codedigest.o: common_sha1.i -crypto.o: sha256.c diff --git a/src/common/Makefile.nmake b/src/common/Makefile.nmake index e54827367..0ebeaaaf7 100644 --- a/src/common/Makefile.nmake +++ b/src/common/Makefile.nmake @@ -1,15 +1,19 @@ all: libor.lib libor-crypto.lib libor-event.lib -CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include +CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\ext LIBOR_OBJECTS = address.obj compat.obj container.obj di_ops.obj \ log.obj memarea.obj mempool.obj procmon.obj util.obj \ util_codedigest.obj -LIBOR_CRYPTO_OBJECTS = aes.obj crypto.obj torgzip.obj tortls.obj +LIBOR_CRYPTO_OBJECTS = aes.obj crypto.obj torgzip.obj tortls.obj \ + crypto_curve25519.obj curve25519-donna.obj LIBOR_EVENT_OBJECTS = compat_libevent.obj +curve25519-donna.obj: ..\ext\curve25519_donna\curve25519-donna.c + $(CC) $(CFLAGS) /D inline=_inline /c ..\ext\curve25519_donna\curve25519-donna.c + libor.lib: $(LIBOR_OBJECTS) lib $(LIBOR_OBJECTS) /out:libor.lib @@ -18,3 +22,6 @@ libor-crypto.lib: $(LIBOR_CRYPTO_OBJECTS) libor-event.lib: $(LIBOR_EVENT_OBJECTS) lib $(LIBOR_EVENT_OBJECTS) /out:libor-event.lib + +clean: + del *.obj *.lib libor*.lib diff --git a/src/common/OpenBSD_malloc_Linux.c b/src/common/OpenBSD_malloc_Linux.c deleted file mode 100644 index da8272981..000000000 --- a/src/common/OpenBSD_malloc_Linux.c +++ /dev/null @@ -1,2057 +0,0 @@ -/* Version 1.83 for Linux. - * Compilation: gcc -shared -fPIC -O2 OpenBSD_malloc_Linux.c -o malloc.so - * Launching: LD_PRELOAD=/path/to/malloc.so firefox - */ - -/* $OpenBSD: malloc.c,v 1.83 2006/05/14 19:53:40 otto Exp $ */ - -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp - * ---------------------------------------------------------------------------- - */ - -/* We use this macro to remove some code that we don't actually want, - * rather than to fix its warnings. */ -#define BUILDING_FOR_TOR - -/* - * Defining MALLOC_EXTRA_SANITY will enable extra checks which are - * related to internal conditions and consistency in malloc.c. This has - * a noticeable runtime performance hit, and generally will not do you - * any good unless you fiddle with the internals of malloc or want - * to catch random pointer corruption as early as possible. - */ -#ifndef MALLOC_EXTRA_SANITY -#undef MALLOC_EXTRA_SANITY -#endif - -/* - * Defining MALLOC_STATS will enable you to call malloc_dump() and set - * the [dD] options in the MALLOC_OPTIONS environment variable. - * It has no run-time performance hit, but does pull in stdio... - */ -#ifndef MALLOC_STATS -#undef MALLOC_STATS -#endif - -/* - * What to use for Junk. This is the byte value we use to fill with - * when the 'J' option is enabled. - */ -#define SOME_JUNK 0xd0 /* as in "Duh" :-) */ - -#include <sys/types.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <sys/param.h> -#include <sys/mman.h> -#include <sys/uio.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <limits.h> -#include <errno.h> -#include <err.h> -/* For SIZE_T_MAX */ -#include "torint.h" - -//#include "thread_private.h" - -/* - * The basic parameters you can tweak. - * - * malloc_pageshift pagesize = 1 << malloc_pageshift - * It's probably best if this is the native - * page size, but it shouldn't have to be. - * - * malloc_minsize minimum size of an allocation in bytes. - * If this is too small it's too much work - * to manage them. This is also the smallest - * unit of alignment used for the storage - * returned by malloc/realloc. - * - */ - -static int align = 0; -static size_t g_alignment = 0; - -extern int __libc_enable_secure; - -#ifndef HAVE_ISSETUGID -static int issetugid(void) -{ - if (__libc_enable_secure) return 1; - if (getuid() != geteuid()) return 1; - if (getgid() != getegid()) return 1; - return 0; -} -#endif - -#define PGSHIFT 12 -#undef MADV_FREE -#define MADV_FREE MADV_DONTNEED -#include <pthread.h> -static pthread_mutex_t gen_mutex = PTHREAD_MUTEX_INITIALIZER; - -#define _MALLOC_LOCK_INIT() {;} -#define _MALLOC_LOCK() {pthread_mutex_lock(&gen_mutex);} -#define _MALLOC_UNLOCK() {pthread_mutex_unlock(&gen_mutex);} - -#if defined(__sparc__) || defined(__alpha__) -#define malloc_pageshift 13U -#endif -#if defined(__ia64__) -#define malloc_pageshift 14U -#endif - -#ifndef malloc_pageshift -#define malloc_pageshift (PGSHIFT) -#endif - -/* - * No user serviceable parts behind this point. - * - * This structure describes a page worth of chunks. - */ -struct pginfo { - struct pginfo *next; /* next on the free list */ - void *page; /* Pointer to the page */ - u_short size; /* size of this page's chunks */ - u_short shift; /* How far to shift for this size chunks */ - u_short free; /* How many free chunks */ - u_short total; /* How many chunk */ - u_long bits[1];/* Which chunks are free */ -}; - -/* How many bits per u_long in the bitmap */ -#define MALLOC_BITS (int)((NBBY * sizeof(u_long))) - -/* - * This structure describes a number of free pages. - */ -struct pgfree { - struct pgfree *next; /* next run of free pages */ - struct pgfree *prev; /* prev run of free pages */ - void *page; /* pointer to free pages */ - void *pdir; /* pointer to the base page's dir */ - size_t size; /* number of bytes free */ -}; - -/* - * Magic values to put in the page_directory - */ -#define MALLOC_NOT_MINE ((struct pginfo*) 0) -#define MALLOC_FREE ((struct pginfo*) 1) -#define MALLOC_FIRST ((struct pginfo*) 2) -#define MALLOC_FOLLOW ((struct pginfo*) 3) -#define MALLOC_MAGIC ((struct pginfo*) 4) - -#ifndef malloc_minsize -#define malloc_minsize 16UL -#endif - -#if !defined(malloc_pagesize) -#define malloc_pagesize (1UL<<malloc_pageshift) -#endif - -#if ((1UL<<malloc_pageshift) != malloc_pagesize) -#error "(1UL<<malloc_pageshift) != malloc_pagesize" -#endif - -#ifndef malloc_maxsize -#define malloc_maxsize ((malloc_pagesize)>>1) -#endif - -/* A mask for the offset inside a page. */ -#define malloc_pagemask ((malloc_pagesize)-1) - -#define pageround(foo) (((foo) + (malloc_pagemask)) & ~malloc_pagemask) -#define ptr2index(foo) (((u_long)(foo) >> malloc_pageshift)+malloc_pageshift) -#define index2ptr(idx) ((void*)(((idx)-malloc_pageshift)<<malloc_pageshift)) - -/* Set when initialization has been done */ -static unsigned int malloc_started; - -/* Number of free pages we cache */ -static unsigned int malloc_cache = 16; - -/* Structure used for linking discrete directory pages. */ -struct pdinfo { - struct pginfo **base; - struct pdinfo *prev; - struct pdinfo *next; - u_long dirnum; -}; -static struct pdinfo *last_dir; /* Caches to the last and previous */ -static struct pdinfo *prev_dir; /* referenced directory pages. */ - -static size_t pdi_off; -static u_long pdi_mod; -#define PD_IDX(num) ((num) / (malloc_pagesize/sizeof(struct pginfo *))) -#define PD_OFF(num) ((num) & ((malloc_pagesize/sizeof(struct pginfo *))-1)) -#define PI_IDX(index) ((index) / pdi_mod) -#define PI_OFF(index) ((index) % pdi_mod) - -/* The last index in the page directory we care about */ -static u_long last_index; - -/* Pointer to page directory. Allocated "as if with" malloc */ -static struct pginfo **page_dir; - -/* Free pages line up here */ -static struct pgfree free_list; - -/* Abort(), user doesn't handle problems. */ -static int malloc_abort = 2; - -/* Are we trying to die ? */ -static int suicide; - -#ifdef MALLOC_STATS -/* dump statistics */ -static int malloc_stats; -#endif - -/* avoid outputting warnings? */ -static int malloc_silent; - -/* always realloc ? */ -static int malloc_realloc; - -/* mprotect free pages PROT_NONE? */ -static int malloc_freeprot; - -/* use guard pages after allocations? */ -static size_t malloc_guard = 0; -static size_t malloc_guarded; -/* align pointers to end of page? */ -static int malloc_ptrguard; - -static int malloc_hint = 1; - -/* xmalloc behaviour ? */ -static int malloc_xmalloc; - -/* zero fill ? */ -static int malloc_zero; - -/* junk fill ? */ -static int malloc_junk; - -#ifdef __FreeBSD__ -/* utrace ? */ -static int malloc_utrace; - -struct ut { - void *p; - size_t s; - void *r; -}; - -void utrace(struct ut *, int); - -#define UTRACE(a, b, c) \ - if (malloc_utrace) \ - {struct ut u; u.p=a; u.s = b; u.r=c; utrace(&u, sizeof u);} -#else /* !__FreeBSD__ */ -#define UTRACE(a,b,c) -#endif - -/* Status of malloc. */ -static int malloc_active; - -/* Allocated memory. */ -static size_t malloc_used; - -/* My last break. */ -static caddr_t malloc_brk; - -/* One location cache for free-list holders. */ -static struct pgfree *px; - -/* Compile-time options. */ -char *malloc_options; - -/* Name of the current public function. */ -static const char *malloc_func; - -#define MMAP(size) \ - mmap((void *)0, (size), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, \ - -1, (off_t)0) - -/* - * Necessary function declarations. - */ -static void *imalloc(size_t size); -static void ifree(void *ptr); -static void *irealloc(void *ptr, size_t size); -static void *malloc_bytes(size_t size); -void *memalign(size_t boundary, size_t size); -size_t malloc_good_size(size_t size); - -/* - * Function for page directory lookup. - */ -static int -pdir_lookup(u_long index, struct pdinfo ** pdi) -{ - struct pdinfo *spi; - u_long pidx = PI_IDX(index); - - if (last_dir != NULL && PD_IDX(last_dir->dirnum) == pidx) - *pdi = last_dir; - else if (prev_dir != NULL && PD_IDX(prev_dir->dirnum) == pidx) - *pdi = prev_dir; - else if (last_dir != NULL && prev_dir != NULL) { - if ((PD_IDX(last_dir->dirnum) > pidx) ? - (PD_IDX(last_dir->dirnum) - pidx) : - (pidx - PD_IDX(last_dir->dirnum)) - < (PD_IDX(prev_dir->dirnum) > pidx) ? - (PD_IDX(prev_dir->dirnum) - pidx) : - (pidx - PD_IDX(prev_dir->dirnum))) - *pdi = last_dir; - else - *pdi = prev_dir; - - if (PD_IDX((*pdi)->dirnum) > pidx) { - for (spi = (*pdi)->prev; - spi != NULL && PD_IDX(spi->dirnum) > pidx; - spi = spi->prev) - *pdi = spi; - if (spi != NULL) - *pdi = spi; - } else - for (spi = (*pdi)->next; - spi != NULL && PD_IDX(spi->dirnum) <= pidx; - spi = spi->next) - *pdi = spi; - } else { - *pdi = (struct pdinfo *) ((caddr_t) page_dir + pdi_off); - for (spi = *pdi; - spi != NULL && PD_IDX(spi->dirnum) <= pidx; - spi = spi->next) - *pdi = spi; - } - - return ((PD_IDX((*pdi)->dirnum) == pidx) ? 0 : - (PD_IDX((*pdi)->dirnum) > pidx) ? 1 : -1); -} - -#ifdef MALLOC_STATS -void -malloc_dump(int fd) -{ - char buf[1024]; - struct pginfo **pd; - struct pgfree *pf; - struct pdinfo *pi; - u_long j; - - pd = page_dir; - pi = (struct pdinfo *) ((caddr_t) pd + pdi_off); - - /* print out all the pages */ - for (j = 0; j <= last_index;) { - snprintf(buf, sizeof buf, "%08lx %5lu ", j << malloc_pageshift, j); - write(fd, buf, strlen(buf)); - if (pd[PI_OFF(j)] == MALLOC_NOT_MINE) { - for (j++; j <= last_index && pd[PI_OFF(j)] == MALLOC_NOT_MINE;) { - if (!PI_OFF(++j)) { - if ((pi = pi->next) == NULL || - PD_IDX(pi->dirnum) != PI_IDX(j)) - break; - pd = pi->base; - j += pdi_mod; - } - } - j--; - snprintf(buf, sizeof buf, ".. %5lu not mine\n", j); - write(fd, buf, strlen(buf)); - } else if (pd[PI_OFF(j)] == MALLOC_FREE) { - for (j++; j <= last_index && pd[PI_OFF(j)] == MALLOC_FREE;) { - if (!PI_OFF(++j)) { - if ((pi = pi->next) == NULL || - PD_IDX(pi->dirnum) != PI_IDX(j)) - break; - pd = pi->base; - j += pdi_mod; - } - } - j--; - snprintf(buf, sizeof buf, ".. %5lu free\n", j); - write(fd, buf, strlen(buf)); - } else if (pd[PI_OFF(j)] == MALLOC_FIRST) { - for (j++; j <= last_index && pd[PI_OFF(j)] == MALLOC_FOLLOW;) { - if (!PI_OFF(++j)) { - if ((pi = pi->next) == NULL || - PD_IDX(pi->dirnum) != PI_IDX(j)) - break; - pd = pi->base; - j += pdi_mod; - } - } - j--; - snprintf(buf, sizeof buf, ".. %5lu in use\n", j); - write(fd, buf, strlen(buf)); - } else if (pd[PI_OFF(j)] < MALLOC_MAGIC) { - snprintf(buf, sizeof buf, "(%p)\n", pd[PI_OFF(j)]); - write(fd, buf, strlen(buf)); - } else { - snprintf(buf, sizeof buf, "%p %d (of %d) x %d @ %p --> %p\n", - pd[PI_OFF(j)], pd[PI_OFF(j)]->free, - pd[PI_OFF(j)]->total, pd[PI_OFF(j)]->size, - pd[PI_OFF(j)]->page, pd[PI_OFF(j)]->next); - write(fd, buf, strlen(buf)); - } - if (!PI_OFF(++j)) { - if ((pi = pi->next) == NULL) - break; - pd = pi->base; - j += (1 + PD_IDX(pi->dirnum) - PI_IDX(j)) * pdi_mod; - } - } - - for (pf = free_list.next; pf; pf = pf->next) { - snprintf(buf, sizeof buf, "Free: @%p [%p...%p[ %ld ->%p <-%p\n", - pf, pf->page, (char *)pf->page + pf->size, - pf->size, pf->prev, pf->next); - write(fd, buf, strlen(buf)); - if (pf == pf->next) { - snprintf(buf, sizeof buf, "Free_list loops\n"); - write(fd, buf, strlen(buf)); - break; - } - } - - /* print out various info */ - snprintf(buf, sizeof buf, "Minsize\t%lu\n", malloc_minsize); - write(fd, buf, strlen(buf)); - snprintf(buf, sizeof buf, "Maxsize\t%lu\n", malloc_maxsize); - write(fd, buf, strlen(buf)); - snprintf(buf, sizeof buf, "Pagesize\t%lu\n", malloc_pagesize); - write(fd, buf, strlen(buf)); - snprintf(buf, sizeof buf, "Pageshift\t%u\n", malloc_pageshift); - write(fd, buf, strlen(buf)); - snprintf(buf, sizeof buf, "In use\t%lu\n", (u_long) malloc_used); - write(fd, buf, strlen(buf)); - snprintf(buf, sizeof buf, "Guarded\t%lu\n", (u_long) malloc_guarded); - write(fd, buf, strlen(buf)); -} -#endif /* MALLOC_STATS */ - -extern char *__progname; - -static void -wrterror(const char *p) -{ -#ifndef BUILDING_FOR_TOR - const char *q = " error: "; - struct iovec iov[5]; - - iov[0].iov_base = __progname; - iov[0].iov_len = strlen(__progname); - iov[1].iov_base = (char*)malloc_func; - iov[1].iov_len = strlen(malloc_func); - iov[2].iov_base = (char*)q; - iov[2].iov_len = strlen(q); - iov[3].iov_base = (char*)p; - iov[3].iov_len = strlen(p); - iov[4].iov_base = (char*)"\n"; - iov[4].iov_len = 1; - writev(STDERR_FILENO, iov, 5); -#else - (void)p; -#endif - suicide = 1; -#ifdef MALLOC_STATS - if (malloc_stats) - malloc_dump(STDERR_FILENO); -#endif /* MALLOC_STATS */ - malloc_active--; - if (malloc_abort) - abort(); -} - -static void -wrtwarning(const char *p) -{ -#ifndef BUILDING_FOR_TOR - const char *q = " warning: "; - struct iovec iov[5]; -#endif - - if (malloc_abort) - wrterror(p); - else if (malloc_silent) - return; - -#ifndef BUILDING_FOR_TOR - iov[0].iov_base = __progname; - iov[0].iov_len = strlen(__progname); - iov[1].iov_base = (char*)malloc_func; - iov[1].iov_len = strlen(malloc_func); - iov[2].iov_base = (char*)q; - iov[2].iov_len = strlen(q); - iov[3].iov_base = (char*)p; - iov[3].iov_len = strlen(p); - iov[4].iov_base = (char*)"\n"; - iov[4].iov_len = 1; - - (void) writev(STDERR_FILENO, iov, 5); -#else - (void)p; -#endif -} - -#ifdef MALLOC_STATS -static void -malloc_exit(void) -{ - char *q = "malloc() warning: Couldn't dump stats\n"; - int save_errno = errno, fd; - - fd = open("malloc.out", O_RDWR|O_APPEND); - if (fd != -1) { - malloc_dump(fd); - close(fd); - } else - write(STDERR_FILENO, q, strlen(q)); - errno = save_errno; -} -#endif /* MALLOC_STATS */ - -/* - * Allocate aligned mmaped chunk - */ - -static void *MMAP_A(size_t pages, size_t alignment) -{ - void *j, *p; - size_t first_size, rest, begin, end; - if (pages%malloc_pagesize != 0) - pages = pages - pages%malloc_pagesize + malloc_pagesize; - first_size = pages + alignment - malloc_pagesize; - p = MMAP(first_size); - rest = ((size_t)p) % alignment; - j = (rest == 0) ? p : (void*) ((size_t)p + alignment - rest); - begin = (size_t)j - (size_t)p; - if (begin != 0) munmap(p, begin); - end = (size_t)p + first_size - ((size_t)j + pages); - if(end != 0) munmap( (void*) ((size_t)j + pages), end); - - return j; -} - - -/* - * Allocate a number of pages from the OS - */ -static void * -map_pages(size_t pages) -{ - struct pdinfo *pi, *spi; - struct pginfo **pd; - u_long idx, pidx, lidx; - caddr_t result, tail; - u_long index, lindex; - void *pdregion = NULL; - size_t dirs, cnt; - - pages <<= malloc_pageshift; - if (!align) - result = MMAP(pages + malloc_guard); - else { - result = MMAP_A(pages + malloc_guard, g_alignment); - } - if (result == MAP_FAILED) { -#ifdef MALLOC_EXTRA_SANITY - wrtwarning("(ES): map_pages fails"); -#endif /* MALLOC_EXTRA_SANITY */ - errno = ENOMEM; - return (NULL); - } - index = ptr2index(result); - tail = result + pages + malloc_guard; - lindex = ptr2index(tail) - 1; - if (malloc_guard) - mprotect(result + pages, malloc_guard, PROT_NONE); - - pidx = PI_IDX(index); - lidx = PI_IDX(lindex); - - if (tail > malloc_brk) { - malloc_brk = tail; - last_index = lindex; - } - - dirs = lidx - pidx; - - /* Insert directory pages, if needed. */ - if (pdir_lookup(index, &pi) != 0) - dirs++; - - if (dirs > 0) { - pdregion = MMAP(malloc_pagesize * dirs); - if (pdregion == MAP_FAILED) { - munmap(result, tail - result); -#ifdef MALLOC_EXTRA_SANITY - wrtwarning("(ES): map_pages fails"); -#endif - errno = ENOMEM; - return (NULL); - } - } - - cnt = 0; - for (idx = pidx, spi = pi; idx <= lidx; idx++) { - if (pi == NULL || PD_IDX(pi->dirnum) != idx) { - pd = (struct pginfo **)((char *)pdregion + - cnt * malloc_pagesize); - cnt++; - memset(pd, 0, malloc_pagesize); - pi = (struct pdinfo *) ((caddr_t) pd + pdi_off); - pi->base = pd; - pi->prev = spi; - pi->next = spi->next; - pi->dirnum = idx * (malloc_pagesize / - sizeof(struct pginfo *)); - - if (spi->next != NULL) - spi->next->prev = pi; - spi->next = pi; - } - if (idx > pidx && idx < lidx) { - pi->dirnum += pdi_mod; - } else if (idx == pidx) { - if (pidx == lidx) { - pi->dirnum += (u_long)(tail - result) >> - malloc_pageshift; - } else { - pi->dirnum += pdi_mod - PI_OFF(index); - } - } else { - pi->dirnum += PI_OFF(ptr2index(tail - 1)) + 1; - } -#ifdef MALLOC_EXTRA_SANITY - if (PD_OFF(pi->dirnum) > pdi_mod || PD_IDX(pi->dirnum) > idx) { - wrterror("(ES): pages directory overflow"); - errno = EFAULT; - return (NULL); - } -#endif /* MALLOC_EXTRA_SANITY */ - if (idx == pidx && pi != last_dir) { - prev_dir = last_dir; - last_dir = pi; - } - spi = pi; - pi = spi->next; - } -#ifdef MALLOC_EXTRA_SANITY - if (cnt > dirs) - wrtwarning("(ES): cnt > dirs"); -#endif /* MALLOC_EXTRA_SANITY */ - if (cnt < dirs) - munmap((char *)pdregion + cnt * malloc_pagesize, - (dirs - cnt) * malloc_pagesize); - - return (result); -} - -/* - * Initialize the world - */ -static void -malloc_init(void) -{ - char *p, b[64]; - int i, j, save_errno = errno; - - _MALLOC_LOCK_INIT(); - -#ifdef MALLOC_EXTRA_SANITY - malloc_junk = 1; -#endif /* MALLOC_EXTRA_SANITY */ - - for (i = 0; i < 3; i++) { - switch (i) { - case 0: - j = (int) readlink("/etc/malloc.conf", b, sizeof b - 1); - if (j <= 0) - continue; - b[j] = '\0'; - p = b; - break; - case 1: - if (issetugid() == 0) - p = getenv("MALLOC_OPTIONS"); - else - continue; - break; - case 2: - p = malloc_options; - break; - default: - p = NULL; - } - - for (; p != NULL && *p != '\0'; p++) { - switch (*p) { - case '>': - malloc_cache <<= 1; - break; - case '<': - malloc_cache >>= 1; - break; - case 'a': - malloc_abort = 0; - break; - case 'A': - malloc_abort = 1; - break; -#ifdef MALLOC_STATS - case 'd': - malloc_stats = 0; - break; - case 'D': - malloc_stats = 1; - break; -#endif /* MALLOC_STATS */ - case 'f': - malloc_freeprot = 0; - break; - case 'F': - malloc_freeprot = 1; - break; - case 'g': - malloc_guard = 0; - break; - case 'G': - malloc_guard = malloc_pagesize; - break; - case 'h': - malloc_hint = 0; - break; - case 'H': - malloc_hint = 1; - break; - case 'j': - malloc_junk = 0; - break; - case 'J': - malloc_junk = 1; - break; - case 'n': - malloc_silent = 0; - break; - case 'N': - malloc_silent = 1; - break; - case 'p': - malloc_ptrguard = 0; - break; - case 'P': - malloc_ptrguard = 1; - break; - case 'r': - malloc_realloc = 0; - break; - case 'R': - malloc_realloc = 1; - break; -#ifdef __FreeBSD__ - case 'u': - malloc_utrace = 0; - break; - case 'U': - malloc_utrace = 1; - break; -#endif /* __FreeBSD__ */ - case 'x': - malloc_xmalloc = 0; - break; - case 'X': - malloc_xmalloc = 1; - break; - case 'z': - malloc_zero = 0; - break; - case 'Z': - malloc_zero = 1; - break; - default: - j = malloc_abort; - malloc_abort = 0; - wrtwarning("unknown char in MALLOC_OPTIONS"); - malloc_abort = j; - break; - } - } - } - - UTRACE(0, 0, 0); - - /* - * We want junk in the entire allocation, and zero only in the part - * the user asked for. - */ - if (malloc_zero) - malloc_junk = 1; - -#ifdef MALLOC_STATS - if (malloc_stats && (atexit(malloc_exit) == -1)) - wrtwarning("atexit(2) failed." - " Will not be able to dump malloc stats on exit"); -#endif /* MALLOC_STATS */ - - if (malloc_pagesize != getpagesize()) { - wrterror("malloc() replacement compiled with a different " - "page size from what we're running with. Failing."); - errno = ENOMEM; - return; - } - - /* Allocate one page for the page directory. */ - page_dir = (struct pginfo **)MMAP(malloc_pagesize); - - if (page_dir == MAP_FAILED) { - wrterror("mmap(2) failed, check limits"); - errno = ENOMEM; - return; - } - pdi_off = (malloc_pagesize - sizeof(struct pdinfo)) & ~(malloc_minsize - 1); - pdi_mod = pdi_off / sizeof(struct pginfo *); - - last_dir = (struct pdinfo *) ((caddr_t) page_dir + pdi_off); - last_dir->base = page_dir; - last_dir->prev = last_dir->next = NULL; - last_dir->dirnum = malloc_pageshift; - - /* Been here, done that. */ - malloc_started++; - - /* Recalculate the cache size in bytes, and make sure it's nonzero. */ - if (!malloc_cache) - malloc_cache++; - malloc_cache <<= malloc_pageshift; - errno = save_errno; -} - -/* - * Allocate a number of complete pages - */ -static void * -malloc_pages(size_t size) -{ - void *p, *delay_free = NULL, *tp; - size_t i; - struct pginfo **pd; - struct pdinfo *pi; - u_long pidx, index; - struct pgfree *pf; - - size = pageround(size) + malloc_guard; - - p = NULL; - /* Look for free pages before asking for more */ - if (!align) - for (pf = free_list.next; pf; pf = pf->next) { - -#ifdef MALLOC_EXTRA_SANITY - if (pf->size & malloc_pagemask) { - wrterror("(ES): junk length entry on free_list"); - errno = EFAULT; - return (NULL); - } - if (!pf->size) { - wrterror("(ES): zero length entry on free_list"); - errno = EFAULT; - return (NULL); - } - if (pf->page > (pf->page + pf->size)) { - wrterror("(ES): sick entry on free_list"); - errno = EFAULT; - return (NULL); - } - if ((pi = pf->pdir) == NULL) { - wrterror("(ES): invalid page directory on free-list"); - errno = EFAULT; - return (NULL); - } - if ((pidx = PI_IDX(ptr2index(pf->page))) != PD_IDX(pi->dirnum)) { - wrterror("(ES): directory index mismatch on free-list"); - errno = EFAULT; - return (NULL); - } - pd = pi->base; - if (pd[PI_OFF(ptr2index(pf->page))] != MALLOC_FREE) { - wrterror("(ES): non-free first page on free-list"); - errno = EFAULT; - return (NULL); - } - pidx = PI_IDX(ptr2index((pf->page) + (pf->size)) - 1); - for (pi = pf->pdir; pi != NULL && PD_IDX(pi->dirnum) < pidx; - pi = pi->next) - ; - if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { - wrterror("(ES): last page not referenced in page directory"); - errno = EFAULT; - return (NULL); - } - pd = pi->base; - if (pd[PI_OFF(ptr2index((pf->page) + (pf->size)) - 1)] != MALLOC_FREE) { - wrterror("(ES): non-free last page on free-list"); - errno = EFAULT; - return (NULL); - } -#endif /* MALLOC_EXTRA_SANITY */ - - if (pf->size < size) - continue; - - if (pf->size == size) { - p = pf->page; - pi = pf->pdir; - if (pf->next != NULL) - pf->next->prev = pf->prev; - pf->prev->next = pf->next; - delay_free = pf; - break; - } - p = pf->page; - pf->page = (char *) pf->page + size; - pf->size -= size; - pidx = PI_IDX(ptr2index(pf->page)); - for (pi = pf->pdir; pi != NULL && PD_IDX(pi->dirnum) < pidx; - pi = pi->next) - ; - if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { - wrterror("(ES): hole in directories"); - errno = EFAULT; - return (NULL); - } - tp = pf->pdir; - pf->pdir = pi; - pi = tp; - break; - } - - size -= malloc_guard; - -#ifdef MALLOC_EXTRA_SANITY - if (p != NULL && pi != NULL) { - pidx = PD_IDX(pi->dirnum); - pd = pi->base; - } - if (p != NULL && pd[PI_OFF(ptr2index(p))] != MALLOC_FREE) { - wrterror("(ES): allocated non-free page on free-list"); - errno = EFAULT; - return (NULL); - } -#endif /* MALLOC_EXTRA_SANITY */ - - if (p != NULL && (malloc_guard || malloc_freeprot)) - mprotect(p, size, PROT_READ | PROT_WRITE); - - size >>= malloc_pageshift; - - /* Map new pages */ - if (p == NULL) - p = map_pages(size); - - if (p != NULL) { - index = ptr2index(p); - pidx = PI_IDX(index); - pdir_lookup(index, &pi); -#ifdef MALLOC_EXTRA_SANITY - if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { - wrterror("(ES): mapped pages not found in directory"); - errno = EFAULT; - return (NULL); - } -#endif /* MALLOC_EXTRA_SANITY */ - if (pi != last_dir) { - prev_dir = last_dir; - last_dir = pi; - } - pd = pi->base; - pd[PI_OFF(index)] = MALLOC_FIRST; - - for (i = 1; i < size; i++) { - if (!PI_OFF(index + i)) { - pidx++; - pi = pi->next; -#ifdef MALLOC_EXTRA_SANITY - if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { - wrterror("(ES): hole in mapped pages directory"); - errno = EFAULT; - return (NULL); - } -#endif /* MALLOC_EXTRA_SANITY */ - pd = pi->base; - } - pd[PI_OFF(index + i)] = MALLOC_FOLLOW; - } - if (malloc_guard) { - if (!PI_OFF(index + i)) { - pidx++; - pi = pi->next; -#ifdef MALLOC_EXTRA_SANITY - if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { - wrterror("(ES): hole in mapped pages directory"); - errno = EFAULT; - return (NULL); - } -#endif /* MALLOC_EXTRA_SANITY */ - pd = pi->base; - } - pd[PI_OFF(index + i)] = MALLOC_FIRST; - } - - malloc_used += size << malloc_pageshift; - malloc_guarded += malloc_guard; - - if (malloc_junk) - memset(p, SOME_JUNK, size << malloc_pageshift); - } - if (delay_free) { - if (px == NULL) - px = delay_free; - else - ifree(delay_free); - } - return (p); -} - -/* - * Allocate a page of fragments - */ - -static __inline__ int -malloc_make_chunks(int bits) -{ - struct pginfo *bp, **pd; - struct pdinfo *pi; -#ifdef MALLOC_EXTRA_SANITY - u_long pidx; -#endif /* MALLOC_EXTRA_SANITY */ - void *pp; - long i, k; - size_t l; - - /* Allocate a new bucket */ - pp = malloc_pages((size_t) malloc_pagesize); - if (pp == NULL) - return (0); - - /* Find length of admin structure */ - l = sizeof *bp - sizeof(u_long); - l += sizeof(u_long) * - (((malloc_pagesize >> bits) + MALLOC_BITS - 1) / MALLOC_BITS); - - /* Don't waste more than two chunks on this */ - - /* - * If we are to allocate a memory protected page for the malloc(0) - * case (when bits=0), it must be from a different page than the - * pginfo page. - * --> Treat it like the big chunk alloc, get a second data page. - */ - if (bits != 0 && (1UL << (bits)) <= l + l) { - bp = (struct pginfo *) pp; - } else { - bp = (struct pginfo *) imalloc(l); - if (bp == NULL) { - ifree(pp); - return (0); - } - } - - /* memory protect the page allocated in the malloc(0) case */ - if (bits == 0) { - bp->size = 0; - bp->shift = 1; - i = malloc_minsize - 1; - while (i >>= 1) - bp->shift++; - bp->total = bp->free = malloc_pagesize >> bp->shift; - bp->page = pp; - - k = mprotect(pp, malloc_pagesize, PROT_NONE); - if (k < 0) { - ifree(pp); - ifree(bp); - return (0); - } - } else { - bp->size = (1UL << bits); - bp->shift = bits; - bp->total = bp->free = malloc_pagesize >> bits; - bp->page = pp; - } - - /* set all valid bits in the bitmap */ - k = bp->total; - i = 0; - - /* Do a bunch at a time */ - for (; (k - i) >= MALLOC_BITS; i += MALLOC_BITS) - bp->bits[i / MALLOC_BITS] = ~0UL; - - for (; i < k; i++) - bp->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS); - - k = (long)l; - if (bp == bp->page) { - /* Mark the ones we stole for ourselves */ - for (i = 0; k > 0; i++) { - bp->bits[i / MALLOC_BITS] &= ~(1UL << (i % MALLOC_BITS)); - bp->free--; - bp->total--; - k -= (1 << bits); - } - } - /* MALLOC_LOCK */ - - pdir_lookup(ptr2index(pp), &pi); -#ifdef MALLOC_EXTRA_SANITY - pidx = PI_IDX(ptr2index(pp)); - if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { - wrterror("(ES): mapped pages not found in directory"); - errno = EFAULT; - return (0); - } -#endif /* MALLOC_EXTRA_SANITY */ - if (pi != last_dir) { - prev_dir = last_dir; - last_dir = pi; - } - pd = pi->base; - pd[PI_OFF(ptr2index(pp))] = bp; - - bp->next = page_dir[bits]; - page_dir[bits] = bp; - - /* MALLOC_UNLOCK */ - return (1); -} - -/* - * Allocate a fragment - */ -static void * -malloc_bytes(size_t size) -{ - int i, j; - size_t k; - u_long u, *lp; - struct pginfo *bp; - - /* Don't bother with anything less than this */ - /* unless we have a malloc(0) requests */ - if (size != 0 && size < malloc_minsize) - size = malloc_minsize; - - /* Find the right bucket */ - if (size == 0) - j = 0; - else { - size_t ii; - j = 1; - ii = size - 1; - while (ii >>= 1) - j++; - } - - /* If it's empty, make a page more of that size chunks */ - if (page_dir[j] == NULL && !malloc_make_chunks(j)) - return (NULL); - - bp = page_dir[j]; - - /* Find first word of bitmap which isn't empty */ - for (lp = bp->bits; !*lp; lp++); - - /* Find that bit, and tweak it */ - u = 1; - k = 0; - while (!(*lp & u)) { - u += u; - k++; - } - - if (malloc_guard) { - /* Walk to a random position. */ -// i = arc4random() % bp->free; - i = rand() % bp->free; - while (i > 0) { - u += u; - k++; - if (k >= MALLOC_BITS) { - lp++; - u = 1; - k = 0; - } -#ifdef MALLOC_EXTRA_SANITY - if (lp - bp->bits > (bp->total - 1) / MALLOC_BITS) { - wrterror("chunk overflow"); - errno = EFAULT; - return (NULL); - } -#endif /* MALLOC_EXTRA_SANITY */ - if (*lp & u) - i--; - } - } - *lp ^= u; - - /* If there are no more free, remove from free-list */ - if (!--bp->free) { - page_dir[j] = bp->next; - bp->next = NULL; - } - /* Adjust to the real offset of that chunk */ - k += (lp - bp->bits) * MALLOC_BITS; - k <<= bp->shift; - - if (malloc_junk && bp->size != 0) - memset((char *)bp->page + k, SOME_JUNK, (size_t)bp->size); - - return ((u_char *) bp->page + k); -} - -/* - * Magic so that malloc(sizeof(ptr)) is near the end of the page. - */ -#define PTR_GAP (malloc_pagesize - sizeof(void *)) -#define PTR_SIZE (sizeof(void *)) -#define PTR_ALIGNED(p) (((unsigned long)p & malloc_pagemask) == PTR_GAP) - -/* - * Allocate a piece of memory - */ -static void * -imalloc(size_t size) -{ - void *result; - int ptralloc = 0; - - if (!malloc_started) - malloc_init(); - - if (suicide) - abort(); - - /* does not matter if malloc_bytes fails */ - if (px == NULL) - px = malloc_bytes(sizeof *px); - - if (malloc_ptrguard && size == PTR_SIZE) { - ptralloc = 1; - size = malloc_pagesize; - } - if (size > SIZE_MAX - malloc_pagesize) { /* Check for overflow */ - result = NULL; - errno = ENOMEM; - } else if (size <= malloc_maxsize) - result = malloc_bytes(size); - else - result = malloc_pages(size); - - if (malloc_abort == 1 && result == NULL) - wrterror("allocation failed"); - - if (malloc_zero && result != NULL) - memset(result, 0, size); - - if (result && ptralloc) - return ((char *) result + PTR_GAP); - return (result); -} - -/* - * Change the size of an allocation. - */ -static void * -irealloc(void *ptr, size_t size) -{ - void *p; - size_t osize; - u_long index, i; - struct pginfo **mp; - struct pginfo **pd; - struct pdinfo *pi; -#ifdef MALLOC_EXTRA_SANITY - u_long pidx; -#endif /* MALLOC_EXTRA_SANITY */ - - if (suicide) - abort(); - - if (!malloc_started) { - wrtwarning("malloc() has never been called"); - return (NULL); - } - if (malloc_ptrguard && PTR_ALIGNED(ptr)) { - if (size <= PTR_SIZE) - return (ptr); - - p = imalloc(size); - if (p) - memcpy(p, ptr, PTR_SIZE); - ifree(ptr); - return (p); - } - index = ptr2index(ptr); - - if (index < malloc_pageshift) { - wrtwarning("junk pointer, too low to make sense"); - return (NULL); - } - if (index > last_index) { - wrtwarning("junk pointer, too high to make sense"); - return (NULL); - } - pdir_lookup(index, &pi); -#ifdef MALLOC_EXTRA_SANITY - pidx = PI_IDX(index); - if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { - wrterror("(ES): mapped pages not found in directory"); - errno = EFAULT; - return (NULL); - } -#endif /* MALLOC_EXTRA_SANITY */ - if (pi != last_dir) { - prev_dir = last_dir; - last_dir = pi; - } - pd = pi->base; - mp = &pd[PI_OFF(index)]; - - if (*mp == MALLOC_FIRST) { /* Page allocation */ - - /* Check the pointer */ - if ((u_long) ptr & malloc_pagemask) { - wrtwarning("modified (page-) pointer"); - return (NULL); - } - /* Find the size in bytes */ - i = index; - if (!PI_OFF(++i)) { - pi = pi->next; - if (pi != NULL && PD_IDX(pi->dirnum) != PI_IDX(i)) - pi = NULL; - if (pi != NULL) - pd = pi->base; - } - for (osize = malloc_pagesize; - pi != NULL && pd[PI_OFF(i)] == MALLOC_FOLLOW;) { - osize += malloc_pagesize; - if (!PI_OFF(++i)) { - pi = pi->next; - if (pi != NULL && PD_IDX(pi->dirnum) != PI_IDX(i)) - pi = NULL; - if (pi != NULL) - pd = pi->base; - } - } - - if (!malloc_realloc && size <= osize && - size > osize - malloc_pagesize) { - if (malloc_junk) - memset((char *)ptr + size, SOME_JUNK, osize - size); - return (ptr); /* ..don't do anything else. */ - } - } else if (*mp >= MALLOC_MAGIC) { /* Chunk allocation */ - - /* Check the pointer for sane values */ - if ((u_long) ptr & ((1UL << ((*mp)->shift)) - 1)) { - wrtwarning("modified (chunk-) pointer"); - return (NULL); - } - /* Find the chunk index in the page */ - i = ((u_long) ptr & malloc_pagemask) >> (*mp)->shift; - - /* Verify that it isn't a free chunk already */ - if ((*mp)->bits[i / MALLOC_BITS] & (1UL << (i % MALLOC_BITS))) { - wrtwarning("chunk is already free"); - return (NULL); - } - osize = (*mp)->size; - - if (!malloc_realloc && size <= osize && - (size > osize / 2 || osize == malloc_minsize)) { - if (malloc_junk) - memset((char *) ptr + size, SOME_JUNK, osize - size); - return (ptr); /* ..don't do anything else. */ - } - } else { - wrtwarning("irealloc: pointer to wrong page"); - return (NULL); - } - - p = imalloc(size); - - if (p != NULL) { - /* copy the lesser of the two sizes, and free the old one */ - /* Don't move from/to 0 sized region !!! */ - if (osize != 0 && size != 0) { - if (osize < size) - memcpy(p, ptr, osize); - else - memcpy(p, ptr, size); - } - ifree(ptr); - } - return (p); -} - -/* - * Free a sequence of pages - */ -static __inline__ void -free_pages(void *ptr, u_long index, struct pginfo * info) -{ - u_long i, pidx, lidx; - size_t l, cachesize = 0; - struct pginfo **pd; - struct pdinfo *pi, *spi; - struct pgfree *pf, *pt = NULL; - caddr_t tail; - - if (info == MALLOC_FREE) { - wrtwarning("page is already free"); - return; - } - if (info != MALLOC_FIRST) { - wrtwarning("free_pages: pointer to wrong page"); - return; - } - if ((u_long) ptr & malloc_pagemask) { - wrtwarning("modified (page-) pointer"); - return; - } - /* Count how many pages and mark them free at the same time */ - pidx = PI_IDX(index); - pdir_lookup(index, &pi); -#ifdef MALLOC_EXTRA_SANITY - if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { - wrterror("(ES): mapped pages not found in directory"); - errno = EFAULT; - return; - } -#endif /* MALLOC_EXTRA_SANITY */ - - spi = pi; /* Save page index for start of region. */ - - pd = pi->base; - pd[PI_OFF(index)] = MALLOC_FREE; - i = 1; - if (!PI_OFF(index + i)) { - pi = pi->next; - if (pi == NULL || PD_IDX(pi->dirnum) != PI_IDX(index + i)) - pi = NULL; - else - pd = pi->base; - } - while (pi != NULL && pd[PI_OFF(index + i)] == MALLOC_FOLLOW) { - pd[PI_OFF(index + i)] = MALLOC_FREE; - i++; - if (!PI_OFF(index + i)) { - if ((pi = pi->next) == NULL || - PD_IDX(pi->dirnum) != PI_IDX(index + i)) - pi = NULL; - else - pd = pi->base; - } - } - - l = i << malloc_pageshift; - - if (malloc_junk) - memset(ptr, SOME_JUNK, l); - - malloc_used -= l; - malloc_guarded -= malloc_guard; - if (malloc_guard) { -#ifdef MALLOC_EXTRA_SANITY - if (pi == NULL || PD_IDX(pi->dirnum) != PI_IDX(index + i)) { - wrterror("(ES): hole in mapped pages directory"); - errno = EFAULT; - return; - } -#endif /* MALLOC_EXTRA_SANITY */ - pd[PI_OFF(index + i)] = MALLOC_FREE; - l += malloc_guard; - } - tail = (caddr_t)ptr + l; - - if (malloc_hint) - madvise(ptr, l, MADV_FREE); - - if (malloc_freeprot) - mprotect(ptr, l, PROT_NONE); - - /* Add to free-list. */ - if (px == NULL && (px = malloc_bytes(sizeof *px)) == NULL) - goto not_return; - px->page = ptr; - px->pdir = spi; - px->size = l; - - if (free_list.next == NULL) { - /* Nothing on free list, put this at head. */ - px->next = NULL; - px->prev = &free_list; - free_list.next = px; - pf = px; - px = NULL; - } else { - /* - * Find the right spot, leave pf pointing to the modified - * entry. - */ - - /* Race ahead here, while calculating cache size. */ - for (pf = free_list.next; - (caddr_t)ptr > ((caddr_t)pf->page + pf->size) - && pf->next != NULL; - pf = pf->next) - cachesize += pf->size; - - /* Finish cache size calculation. */ - pt = pf; - while (pt) { - cachesize += pt->size; - pt = pt->next; - } - - if ((caddr_t)pf->page > tail) { - /* Insert before entry */ - px->next = pf; - px->prev = pf->prev; - pf->prev = px; - px->prev->next = px; - pf = px; - px = NULL; - } else if (((caddr_t)pf->page + pf->size) == ptr) { - /* Append to the previous entry. */ - cachesize -= pf->size; - pf->size += l; - if (pf->next != NULL && - pf->next->page == ((caddr_t)pf->page + pf->size)) { - /* And collapse the next too. */ - pt = pf->next; - pf->size += pt->size; - pf->next = pt->next; - if (pf->next != NULL) - pf->next->prev = pf; - } - } else if (pf->page == tail) { - /* Prepend to entry. */ - cachesize -= pf->size; - pf->size += l; - pf->page = ptr; - pf->pdir = spi; - } else if (pf->next == NULL) { - /* Append at tail of chain. */ - px->next = NULL; - px->prev = pf; - pf->next = px; - pf = px; - px = NULL; - } else { - wrterror("freelist is destroyed"); - errno = EFAULT; - return; - } - } - - if (pf->pdir != last_dir) { - prev_dir = last_dir; - last_dir = pf->pdir; - } - - /* Return something to OS ? */ - if (pf->size > (malloc_cache - cachesize)) { - - /* - * Keep the cache intact. Notice that the '>' above guarantees that - * the pf will always have at least one page afterwards. - */ - if (munmap((char *) pf->page + (malloc_cache - cachesize), - pf->size - (malloc_cache - cachesize)) != 0) - goto not_return; - tail = (caddr_t)pf->page + pf->size; - lidx = ptr2index(tail) - 1; - pf->size = malloc_cache - cachesize; - - index = ptr2index((caddr_t)pf->page + pf->size); - - pidx = PI_IDX(index); - if (prev_dir != NULL && PD_IDX(prev_dir->dirnum) >= pidx) - prev_dir = NULL; /* Will be wiped out below ! */ - - for (pi = pf->pdir; pi != NULL && PD_IDX(pi->dirnum) < pidx; - pi = pi->next) - ; - - spi = pi; - if (pi != NULL && PD_IDX(pi->dirnum) == pidx) { - pd = pi->base; - - for (i = index; i <= lidx;) { - if (pd[PI_OFF(i)] != MALLOC_NOT_MINE) { - pd[PI_OFF(i)] = MALLOC_NOT_MINE; -#ifdef MALLOC_EXTRA_SANITY - if (!PD_OFF(pi->dirnum)) { - wrterror("(ES): pages directory underflow"); - errno = EFAULT; - return; - } -#endif /* MALLOC_EXTRA_SANITY */ - pi->dirnum--; - } -#ifdef MALLOC_EXTRA_SANITY - else - wrtwarning("(ES): page already unmapped"); -#endif /* MALLOC_EXTRA_SANITY */ - i++; - if (!PI_OFF(i)) { - /* - * If no page in that dir, free - * directory page. - */ - if (!PD_OFF(pi->dirnum)) { - /* Remove from list. */ - if (spi == pi) - spi = pi->prev; - if (pi->prev != NULL) - pi->prev->next = pi->next; - if (pi->next != NULL) - pi->next->prev = pi->prev; - pi = pi->next; - munmap(pd, malloc_pagesize); - } else - pi = pi->next; - if (pi == NULL || - PD_IDX(pi->dirnum) != PI_IDX(i)) - break; - pd = pi->base; - } - } - if (pi && !PD_OFF(pi->dirnum)) { - /* Resulting page dir is now empty. */ - /* Remove from list. */ - if (spi == pi) /* Update spi only if first. */ - spi = pi->prev; - if (pi->prev != NULL) - pi->prev->next = pi->next; - if (pi->next != NULL) - pi->next->prev = pi->prev; - pi = pi->next; - munmap(pd, malloc_pagesize); - } - } - if (pi == NULL && malloc_brk == tail) { - /* Resize down the malloc upper boundary. */ - last_index = index - 1; - malloc_brk = index2ptr(index); - } - - /* XXX: We could realloc/shrink the pagedir here I guess. */ - if (pf->size == 0) { /* Remove from free-list as well. */ - if (px) - ifree(px); - if ((px = pf->prev) != &free_list) { - if (pi == NULL && last_index == (index - 1)) { - if (spi == NULL) { - malloc_brk = NULL; - i = 11; - } else { - pd = spi->base; - if (PD_IDX(spi->dirnum) < pidx) - index = - ((PD_IDX(spi->dirnum) + 1) * - pdi_mod) - 1; - for (pi = spi, i = index; - pd[PI_OFF(i)] == MALLOC_NOT_MINE; - i--) -#ifdef MALLOC_EXTRA_SANITY - if (!PI_OFF(i)) { - pi = pi->prev; - if (pi == NULL || i == 0) - break; - pd = pi->base; - i = (PD_IDX(pi->dirnum) + 1) * pdi_mod; - } -#else /* !MALLOC_EXTRA_SANITY */ - { - } -#endif /* MALLOC_EXTRA_SANITY */ - malloc_brk = index2ptr(i + 1); - } - last_index = i; - } - if ((px->next = pf->next) != NULL) - px->next->prev = px; - } else { - if ((free_list.next = pf->next) != NULL) - free_list.next->prev = &free_list; - } - px = pf; - last_dir = prev_dir; - prev_dir = NULL; - } - } -not_return: - if (pt != NULL) - ifree(pt); -} - -/* - * Free a chunk, and possibly the page it's on, if the page becomes empty. - */ - -/* ARGSUSED */ -static __inline__ void -free_bytes(void *ptr, u_long index, struct pginfo * info) -{ - struct pginfo **mp, **pd; - struct pdinfo *pi; -#ifdef MALLOC_EXTRA_SANITY - u_long pidx; -#endif /* MALLOC_EXTRA_SANITY */ - void *vp; - long i; - (void) index; - - /* Find the chunk number on the page */ - i = ((u_long) ptr & malloc_pagemask) >> info->shift; - - if ((u_long) ptr & ((1UL << (info->shift)) - 1)) { - wrtwarning("modified (chunk-) pointer"); - return; - } - if (info->bits[i / MALLOC_BITS] & (1UL << (i % MALLOC_BITS))) { - wrtwarning("chunk is already free"); - return; - } - if (malloc_junk && info->size != 0) - memset(ptr, SOME_JUNK, (size_t)info->size); - - info->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS); - info->free++; - - if (info->size != 0) - mp = page_dir + info->shift; - else - mp = page_dir; - - if (info->free == 1) { - /* Page became non-full */ - - /* Insert in address order */ - while (*mp != NULL && (*mp)->next != NULL && - (*mp)->next->page < info->page) - mp = &(*mp)->next; - info->next = *mp; - *mp = info; - return; - } - if (info->free != info->total) - return; - - /* Find & remove this page in the queue */ - while (*mp != info) { - mp = &((*mp)->next); -#ifdef MALLOC_EXTRA_SANITY - if (!*mp) { - wrterror("(ES): Not on queue"); - errno = EFAULT; - return; - } -#endif /* MALLOC_EXTRA_SANITY */ - } - *mp = info->next; - - /* Free the page & the info structure if need be */ - pdir_lookup(ptr2index(info->page), &pi); -#ifdef MALLOC_EXTRA_SANITY - pidx = PI_IDX(ptr2index(info->page)); - if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { - wrterror("(ES): mapped pages not found in directory"); - errno = EFAULT; - return; - } -#endif /* MALLOC_EXTRA_SANITY */ - if (pi != last_dir) { - prev_dir = last_dir; - last_dir = pi; - } - pd = pi->base; - pd[PI_OFF(ptr2index(info->page))] = MALLOC_FIRST; - - /* If the page was mprotected, unprotect it before releasing it */ - if (info->size == 0) - mprotect(info->page, malloc_pagesize, PROT_READ | PROT_WRITE); - - vp = info->page; /* Order is important ! */ - if (vp != (void *) info) - ifree(info); - ifree(vp); -} - -static void -ifree(void *ptr) -{ - struct pginfo *info, **pd; - u_long index; -#ifdef MALLOC_EXTRA_SANITY - u_long pidx; -#endif /* MALLOC_EXTRA_SANITY */ - struct pdinfo *pi; - - if (!malloc_started) { - wrtwarning("malloc() has never been called"); - return; - } - /* If we're already sinking, don't make matters any worse. */ - if (suicide) - return; - - if (malloc_ptrguard && PTR_ALIGNED(ptr)) - ptr = (char *) ptr - PTR_GAP; - - index = ptr2index(ptr); - - if (index < malloc_pageshift) { - warnx("(%p)", ptr); - wrtwarning("ifree: junk pointer, too low to make sense"); - return; - } - if (index > last_index) { - warnx("(%p)", ptr); - wrtwarning("ifree: junk pointer, too high to make sense"); - return; - } - pdir_lookup(index, &pi); -#ifdef MALLOC_EXTRA_SANITY - pidx = PI_IDX(index); - if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { - wrterror("(ES): mapped pages not found in directory"); - errno = EFAULT; - return; - } -#endif /* MALLOC_EXTRA_SANITY */ - if (pi != last_dir) { - prev_dir = last_dir; - last_dir = pi; - } - pd = pi->base; - info = pd[PI_OFF(index)]; - - if (info < MALLOC_MAGIC) - free_pages(ptr, index, info); - else - free_bytes(ptr, index, info); - - /* does not matter if malloc_bytes fails */ - if (px == NULL) - px = malloc_bytes(sizeof *px); - - return; -} - -/* - * Common function for handling recursion. Only - * print the error message once, to avoid making the problem - * potentially worse. - */ -static void -malloc_recurse(void) -{ - static int noprint; - - if (noprint == 0) { - noprint = 1; - wrtwarning("recursive call"); - } - malloc_active--; - _MALLOC_UNLOCK(); - errno = EDEADLK; -} - -/* - * These are the public exported interface routines. - */ -void * -malloc(size_t size) -{ - void *r; - - if (!align) - _MALLOC_LOCK(); - malloc_func = " in malloc():"; - if (malloc_active++) { - malloc_recurse(); - return (NULL); - } - r = imalloc(size); - UTRACE(0, size, r); - malloc_active--; - if (!align) - _MALLOC_UNLOCK(); - if (malloc_xmalloc && r == NULL) { - wrterror("out of memory"); - errno = ENOMEM; - } - return (r); -} - -void -free(void *ptr) -{ - /* This is legal. XXX quick path */ - if (ptr == NULL) - return; - - _MALLOC_LOCK(); - malloc_func = " in free():"; - if (malloc_active++) { - malloc_recurse(); - return; - } - ifree(ptr); - UTRACE(ptr, 0, 0); - malloc_active--; - _MALLOC_UNLOCK(); - return; -} - -void * -realloc(void *ptr, size_t size) -{ - void *r; - - _MALLOC_LOCK(); - malloc_func = " in realloc():"; - if (malloc_active++) { - malloc_recurse(); - return (NULL); - } - - if (ptr == NULL) - r = imalloc(size); - else - r = irealloc(ptr, size); - - UTRACE(ptr, size, r); - malloc_active--; - _MALLOC_UNLOCK(); - if (malloc_xmalloc && r == NULL) { - wrterror("out of memory"); - errno = ENOMEM; - } - return (r); -} - -#ifndef SIZE_MAX -//#if defined(__i386__)||defined(__arm__)||defined(__powerpc__) -//#define SIZE_MAX 0xffffffff -//#endif -//#if defined(__x86_64__) -//#define SIZE_MAX 0xffffffffffffffff -//#endif -#define SIZE_MAX SIZE_T_MAX -#endif - -void * -calloc(size_t num, size_t size) -{ - void *p; - - if (num && SIZE_MAX / num < size) { - fprintf(stderr,"OOOOPS"); - errno = ENOMEM; - return NULL; - } - size *= num; - p = malloc(size); - if (p) - memset(p, 0, size); - return(p); -} - -#ifndef BUILDING_FOR_TOR -static int ispowerof2 (size_t a) { - size_t b; - for (b = 1ULL << (sizeof(size_t)*NBBY - 1); b > 1; b >>= 1) - if (b == a) - return 1; - return 0; -} -#endif - -#ifndef BUILDING_FOR_TOR -int posix_memalign(void **memptr, size_t alignment, size_t size) -{ - void *r; - size_t max; - if ((alignment < PTR_SIZE) || (alignment%PTR_SIZE != 0)) return EINVAL; - if (!ispowerof2(alignment)) return EINVAL; - if (alignment < malloc_minsize) alignment = malloc_minsize; - max = alignment > size ? alignment : size; - if (alignment <= malloc_pagesize) - r = malloc(max); - else { - _MALLOC_LOCK(); - align = 1; - g_alignment = alignment; - r = malloc(size); - align=0; - _MALLOC_UNLOCK(); - } - *memptr = r; - if (!r) return ENOMEM; - return 0; -} - -void *memalign(size_t boundary, size_t size) -{ - void *r; - posix_memalign(&r, boundary, size); - return r; -} - -void *valloc(size_t size) -{ - void *r; - posix_memalign(&r, malloc_pagesize, size); - return r; -} -#endif - -size_t malloc_good_size(size_t size) -{ - if (size == 0) { - return 1; - } else if (size <= malloc_maxsize) { - int j; - size_t ii; - /* round up to the nearest power of 2, with same approach - * as malloc_bytes() uses. */ - j = 1; - ii = size - 1; - while (ii >>= 1) - j++; - return ((size_t)1) << j; - } else { - return pageround(size); - } -} diff --git a/src/common/address.c b/src/common/address.c index df26f61f8..2825b123d 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,6 +14,7 @@ #include "address.h" #include "torlog.h" #include "container.h" +#include "sandbox.h" #ifdef _WIN32 #include <process.h> @@ -181,6 +182,16 @@ tor_addr_make_unspec(tor_addr_t *a) a->family = AF_UNSPEC; } +/** Set address <b>a</b> to the null address in address family <b>family</b>. + * The null address for AF_INET is 0.0.0.0. The null address for AF_INET6 is + * [::]. AF_UNSPEC is all null. */ +void +tor_addr_make_null(tor_addr_t *a, sa_family_t family) +{ + memset(a, 0, sizeof(*a)); + a->family = family; +} + /** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set * *<b>addr</b> to the proper IP address and family. The <b>family</b> * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a @@ -224,8 +235,10 @@ tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr) memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; - err = getaddrinfo(name, NULL, &hints, &res); - if (!err) { + err = sandbox_getaddrinfo(name, NULL, &hints, &res); + /* The check for 'res' here shouldn't be necessary, but it makes static + * analysis tools happy. */ + if (!err && res) { best = NULL; for (res_p = res; res_p; res_p = res_p->ai_next) { if (family == AF_UNSPEC) { @@ -305,7 +318,8 @@ tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr) * also treated as internal for now.) */ int -tor_addr_is_internal(const tor_addr_t *addr, int for_listening) +tor_addr_is_internal_(const tor_addr_t *addr, int for_listening, + const char *filename, int lineno) { uint32_t iph4 = 0; uint32_t iph6[4]; @@ -355,8 +369,8 @@ tor_addr_is_internal(const tor_addr_t *addr, int for_listening) /* unknown address family... assume it's not safe for external use */ /* rather than tor_assert(0) */ - log_warn(LD_BUG, "tor_addr_is_internal() called with a non-IP address of " - "type %d", (int)v_family); + log_warn(LD_BUG, "tor_addr_is_internal() called from %s:%d with a " + "non-IP address of type %d", filename, lineno, (int)v_family); tor_fragile_assert(); return 1; } @@ -558,9 +572,22 @@ tor_addr_to_PTR_name(char *out, size_t outlen, * * Return an address family on success, or -1 if an invalid address string is * provided. + * + * If 'flags & TAPMP_EXTENDED_STAR' is false, then the wildcard address '*' + * yield an IPv4 wildcard. + * + * If 'flags & TAPMP_EXTENDED_STAR' is true, then the wildcard address '*' + * yields an AF_UNSPEC wildcard address, and the following change is made + * in the grammar above: + * Address ::= IPv4Address / "[" IPv6Address "]" / "*" / "*4" / "*6" + * with the new "*4" and "*6" productions creating a wildcard to match + * IPv4 or IPv6 addresses. + * */ int -tor_addr_parse_mask_ports(const char *s, tor_addr_t *addr_out, +tor_addr_parse_mask_ports(const char *s, + unsigned flags, + tor_addr_t *addr_out, maskbits_t *maskbits_out, uint16_t *port_min_out, uint16_t *port_max_out) { @@ -617,9 +644,23 @@ tor_addr_parse_mask_ports(const char *s, tor_addr_t *addr_out, memset(addr_out, 0, sizeof(tor_addr_t)); if (!strcmp(address, "*")) { - family = AF_INET; /* AF_UNSPEC ???? XXXX_IP6 */ + if (flags & TAPMP_EXTENDED_STAR) { + family = AF_UNSPEC; + tor_addr_make_unspec(addr_out); + } else { + family = AF_INET; + tor_addr_from_ipv4h(addr_out, 0); + } + any_flag = 1; + } else if (!strcmp(address, "*4") && (flags & TAPMP_EXTENDED_STAR)) { + family = AF_INET; tor_addr_from_ipv4h(addr_out, 0); any_flag = 1; + } else if (!strcmp(address, "*6") && (flags & TAPMP_EXTENDED_STAR)) { + static char nil_bytes[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; + family = AF_INET6; + tor_addr_from_ipv6_bytes(addr_out, nil_bytes); + any_flag = 1; } else if (tor_inet_pton(AF_INET6, address, &in6_tmp) > 0) { family = AF_INET6; tor_addr_from_in6(addr_out, &in6_tmp); @@ -835,6 +876,32 @@ tor_addr_copy(tor_addr_t *dest, const tor_addr_t *src) memcpy(dest, src, sizeof(tor_addr_t)); } +/** Copy a tor_addr_t from <b>src</b> to <b>dest</b>, taking extra case to + * copy only the well-defined portions. Used for computing hashes of + * addresses. + */ +void +tor_addr_copy_tight(tor_addr_t *dest, const tor_addr_t *src) +{ + tor_assert(src != dest); + tor_assert(src); + tor_assert(dest); + memset(dest, 0, sizeof(tor_addr_t)); + dest->family = src->family; + switch (tor_addr_family(src)) + { + case AF_INET: + dest->addr.in_addr.s_addr = src->addr.in_addr.s_addr; + break; + case AF_INET6: + memcpy(dest->addr.in6_addr.s6_addr, src->addr.in6_addr.s6_addr, 16); + case AF_UNSPEC: + break; + default: + tor_fragile_assert(); + } +} + /** Given two addresses <b>addr1</b> and <b>addr2</b>, return 0 if the two * addresses are equivalent under the mask mbits, less than 0 if addr1 * precedes addr2, and greater than 0 otherwise. @@ -956,19 +1023,17 @@ tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2, } } -/** Return a hash code based on the address addr */ -unsigned int +/** Return a hash code based on the address addr. DOCDOC extra */ +uint64_t tor_addr_hash(const tor_addr_t *addr) { switch (tor_addr_family(addr)) { case AF_INET: - return tor_addr_to_ipv4h(addr); + return siphash24g(&addr->addr.in_addr.s_addr, 4); case AF_UNSPEC: return 0x4e4d5342; - case AF_INET6: { - const uint32_t *u = tor_addr_to_in6_addr32(addr); - return u[0] + u[1] + u[2] + u[3]; - } + case AF_INET6: + return siphash24g(&addr->addr.in6_addr.s6_addr, 16); default: tor_fragile_assert(); return 0; @@ -1007,6 +1072,19 @@ fmt_addr_impl(const tor_addr_t *addr, int decorate) return "???"; } +/** Return a string representing the pair <b>addr</b> and <b>port</b>. + * This calls fmt_and_decorate_addr internally, so IPv6 addresses will + * have brackets, and the caveats of fmt_addr_impl apply. + */ +const char * +fmt_addrport(const tor_addr_t *addr, uint16_t port) +{ + /* Add space for a colon and up to 5 digits. */ + static char buf[TOR_ADDR_BUF_LEN + 6]; + tor_snprintf(buf, sizeof(buf), "%s:%u", fmt_and_decorate_addr(addr), port); + return buf; +} + /** Like fmt_addr(), but takes <b>addr</b> as a host-order IPv4 * addresses. Also not thread-safe, also clobbers its return buffer on * repeated calls. */ @@ -1136,6 +1214,8 @@ get_interface_addresses_raw(int severity) result = smartlist_new(); for (i = ifa; i; i = i->ifa_next) { tor_addr_t tmp; + if ((i->ifa_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) + continue; if (!i->ifa_addr) continue; if (i->ifa_addr->sa_family != AF_INET && @@ -1220,14 +1300,14 @@ get_interface_addresses_raw(int severity) /* This interface, AFAICT, only supports AF_INET addresses */ fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { - log(severity, LD_NET, "socket failed: %s", strerror(errno)); + tor_log(severity, LD_NET, "socket failed: %s", strerror(errno)); goto done; } /* Guess how much space we need. */ ifc.ifc_len = sz = 15*1024; ifc.ifc_ifcu.ifcu_req = tor_malloc(sz); if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { - log(severity, LD_NET, "ioctl failed: %s", strerror(errno)); + tor_log(severity, LD_NET, "ioctl failed: %s", strerror(errno)); close(fd); goto done; } @@ -1367,20 +1447,54 @@ get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr) * XXXX024 IPv6 deprecate some of these. */ -/** Return true iff <b>ip</b> (in host order) is an IP reserved to localhost, - * or reserved for local networks by RFC 1918. - */ +/** Given an address of the form "ip:port", try to divide it into its + * ip and port portions, setting *<b>address_out</b> to a newly + * allocated string holding the address portion and *<b>port_out</b> + * to the port. + * + * Don't do DNS lookups and don't allow domain names in the "ip" field. + * + * If <b>default_port</b> is less than 0, don't accept <b>addrport</b> of the + * form "ip" or "ip:0". Otherwise, accept those forms, and set + * *<b>port_out</b> to <b>default_port</b>. + * + * Return 0 on success, -1 on failure. */ int -is_internal_IP(uint32_t ip, int for_listening) +tor_addr_port_parse(int severity, const char *addrport, + tor_addr_t *address_out, uint16_t *port_out, + int default_port) { - tor_addr_t myaddr; - myaddr.family = AF_INET; - myaddr.addr.in_addr.s_addr = htonl(ip); + int retval = -1; + int r; + char *addr_tmp = NULL; + + tor_assert(addrport); + tor_assert(address_out); + tor_assert(port_out); + + r = tor_addr_port_split(severity, addrport, &addr_tmp, port_out); + if (r < 0) + goto done; - return tor_addr_is_internal(&myaddr, for_listening); + if (!*port_out) { + if (default_port >= 0) + *port_out = default_port; + else + goto done; + } + + /* make sure that address_out is an IP address */ + if (tor_addr_parse(address_out, addr_tmp) < 0) + goto done; + + retval = 0; + + done: + tor_free(addr_tmp); + return retval; } -/** Given an address of the form "host:port", try to divide it into its host +/** Given an address of the form "host[:port]", try to divide it into its host * ane port portions, setting *<b>address_out</b> to a newly allocated string * holding the address portion and *<b>port_out</b> to the port (or 0 if no * port is given). Return 0 on success, -1 on failure. */ @@ -1388,9 +1502,18 @@ int tor_addr_port_split(int severity, const char *addrport, char **address_out, uint16_t *port_out) { + tor_addr_t a_tmp; tor_assert(addrport); tor_assert(address_out); tor_assert(port_out); + /* We need to check for IPv6 manually because addr_port_lookup() doesn't + * do a good job on IPv6 addresses that lack a port. */ + if (tor_addr_parse(&a_tmp, addrport) == AF_INET6) { + *port_out = 0; + *address_out = tor_strdup(addrport); + return 0; + } + return addr_port_lookup(severity, addrport, address_out, NULL, port_out); } @@ -1409,17 +1532,17 @@ addr_port_lookup(int severity, const char *addrport, char **address, uint32_t *addr, uint16_t *port_out) { const char *colon; - char *_address = NULL; - int _port; + char *address_ = NULL; + int port_; int ok = 1; tor_assert(addrport); colon = strrchr(addrport, ':'); if (colon) { - _address = tor_strndup(addrport, colon-addrport); - _port = (int) tor_parse_long(colon+1,10,1,65535,NULL,NULL); - if (!_port) { + address_ = tor_strndup(addrport, colon-addrport); + port_ = (int) tor_parse_long(colon+1,10,1,65535,NULL,NULL); + if (!port_) { log_fn(severity, LD_GENERAL, "Port %s out of range", escaped(colon+1)); ok = 0; } @@ -1432,28 +1555,28 @@ addr_port_lookup(int severity, const char *addrport, char **address, ok = 0; } } else { - _address = tor_strdup(addrport); - _port = 0; + address_ = tor_strdup(addrport); + port_ = 0; } if (addr) { /* There's an addr pointer, so we need to resolve the hostname. */ - if (tor_lookup_hostname(_address,addr)) { - log_fn(severity, LD_NET, "Couldn't look up %s", escaped(_address)); + if (tor_lookup_hostname(address_,addr)) { + log_fn(severity, LD_NET, "Couldn't look up %s", escaped(address_)); ok = 0; *addr = 0; } } if (address && ok) { - *address = _address; + *address = address_; } else { if (address) *address = NULL; - tor_free(_address); + tor_free(address_); } if (port_out) - *port_out = ok ? ((uint16_t) _port) : 0; + *port_out = ok ? ((uint16_t) port_) : 0; return ok ? 0 : -1; } @@ -1468,7 +1591,7 @@ addr_mask_get_bits(uint32_t mask) return 0; if (mask == 0xFFFFFFFFu) return 32; - for (i=0; i<=32; ++i) { + for (i=1; i<=32; ++i) { if (mask == (uint32_t) ~((1u<<(32-i))-1)) { return i; } @@ -1476,32 +1599,6 @@ addr_mask_get_bits(uint32_t mask) return -1; } -/** Compare two addresses <b>a1</b> and <b>a2</b> for equality under a - * netmask of <b>mbits</b> bits. Return -1, 0, or 1. - * - * XXXX_IP6 Temporary function to allow masks as bitcounts everywhere. This - * will be replaced with an IPv6-aware version as soon as 32-bit addresses are - * no longer passed around. - */ -int -addr_mask_cmp_bits(uint32_t a1, uint32_t a2, maskbits_t bits) -{ - if (bits > 32) - bits = 32; - else if (bits == 0) - return 0; - - a1 >>= (32-bits); - a2 >>= (32-bits); - - if (a1 < a2) - return -1; - else if (a1 > a2) - return 1; - else - return 0; -} - /** Parse a string <b>s</b> in the format of (*|port(-maxport)?)?, setting the * various *out pointers as appropriate. Return 0 on success, -1 on failure. */ @@ -1554,93 +1651,6 @@ parse_port_range(const char *port, uint16_t *port_min_out, return 0; } -/** Parse a string <b>s</b> in the format of - * (IP(/mask|/mask-bits)?|*)(:(*|port(-maxport))?)?, setting the various - * *out pointers as appropriate. Return 0 on success, -1 on failure. - */ -int -parse_addr_and_port_range(const char *s, uint32_t *addr_out, - maskbits_t *maskbits_out, uint16_t *port_min_out, - uint16_t *port_max_out) -{ - char *address; - char *mask, *port, *endptr; - struct in_addr in; - int bits; - - tor_assert(s); - tor_assert(addr_out); - tor_assert(maskbits_out); - tor_assert(port_min_out); - tor_assert(port_max_out); - - address = tor_strdup(s); - /* Break 'address' into separate strings. - */ - mask = strchr(address,'/'); - port = strchr(mask?mask:address,':'); - if (mask) - *mask++ = '\0'; - if (port) - *port++ = '\0'; - /* Now "address" is the IP|'*' part... - * "mask" is the Mask|Maskbits part... - * and "port" is the *|port|min-max part. - */ - - if (strcmp(address,"*")==0) { - *addr_out = 0; - } else if (tor_inet_aton(address, &in) != 0) { - *addr_out = ntohl(in.s_addr); - } else { - log_warn(LD_GENERAL, "Malformed IP %s in address pattern; rejecting.", - escaped(address)); - goto err; - } - - if (!mask) { - if (strcmp(address,"*")==0) - *maskbits_out = 0; - else - *maskbits_out = 32; - } else { - endptr = NULL; - bits = (int) strtol(mask, &endptr, 10); - if (!*endptr) { - /* strtol handled the whole mask. */ - if (bits < 0 || bits > 32) { - log_warn(LD_GENERAL, - "Bad number of mask bits on address range; rejecting."); - goto err; - } - *maskbits_out = bits; - } else if (tor_inet_aton(mask, &in) != 0) { - bits = addr_mask_get_bits(ntohl(in.s_addr)); - if (bits < 0) { - log_warn(LD_GENERAL, - "Mask %s on address range isn't a prefix; dropping", - escaped(mask)); - goto err; - } - *maskbits_out = bits; - } else { - log_warn(LD_GENERAL, - "Malformed mask %s on address range; rejecting.", - escaped(mask)); - goto err; - } - } - - if (parse_port_range(port, port_min_out, port_max_out)<0) - goto err; - - tor_free(address); - return 0; - err: - tor_free(address); - return -1; -} - /** Given an IPv4 in_addr struct *<b>in</b> (in network order, as usual), * write it as a string into the <b>buf_len</b>-byte buffer in * <b>buf</b>. @@ -1698,3 +1708,15 @@ tor_addr_hostname_is_local(const char *name) !strcasecmpend(name, ".local"); } +/** Return a newly allocated tor_addr_port_t with <b>addr</b> and + <b>port</b> filled in. */ +tor_addr_port_t * +tor_addr_port_new(const tor_addr_t *addr, uint16_t port) +{ + tor_addr_port_t *ap = tor_malloc_zero(sizeof(tor_addr_port_t)); + if (addr) + tor_addr_copy(&ap->addr, addr); + ap->port = port; + return ap; +} + diff --git a/src/common/address.h b/src/common/address.h index c6c126862..8dc63b71c 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -8,8 +8,8 @@ * \brief Headers for address.h **/ -#ifndef _TOR_ADDRESS_H -#define _TOR_ADDRESS_H +#ifndef TOR_ADDRESS_H +#define TOR_ADDRESS_H #include "orconfig.h" #include "torint.h" @@ -40,7 +40,7 @@ typedef struct tor_addr_port_t uint16_t port; } tor_addr_port_t; -#define TOR_ADDR_NULL {AF_UNSPEC, {0}}; +#define TOR_ADDR_NULL {AF_UNSPEC, {0}} static INLINE const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a); static INLINE uint32_t tor_addr_to_ipv4n(const tor_addr_t *a); @@ -55,6 +55,7 @@ socklen_t tor_addr_to_sockaddr(const tor_addr_t *a, uint16_t port, int tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa, uint16_t *port_out); void tor_addr_make_unspec(tor_addr_t *a); +void tor_addr_make_null(tor_addr_t *a, sa_family_t family); char *tor_sockaddr_to_str(const struct sockaddr *sa); /** Return an in6_addr* equivalent to <b>a</b>, or NULL if <b>a</b> is not @@ -145,6 +146,7 @@ char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC; * addresses. */ #define fmt_and_decorate_addr(a) fmt_addr_impl((a), 1) const char *fmt_addr_impl(const tor_addr_t *addr, int decorate); +const char *fmt_addrport(const tor_addr_t *addr, uint16_t port); const char * fmt_addr32(uint32_t addr); int get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr); @@ -165,9 +167,12 @@ int tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2, * "exactly". */ #define tor_addr_eq(a,b) (0==tor_addr_compare((a),(b),CMP_EXACT)) -unsigned int tor_addr_hash(const tor_addr_t *addr); +uint64_t tor_addr_hash(const tor_addr_t *addr); int tor_addr_is_v4(const tor_addr_t *addr); -int tor_addr_is_internal(const tor_addr_t *ip, int for_listening); +int tor_addr_is_internal_(const tor_addr_t *ip, int for_listening, + const char *filename, int lineno); +#define tor_addr_is_internal(addr, for_listening) \ + tor_addr_is_internal_((addr), (for_listening), SHORT_FILE__, __LINE__) /** Longest length that can be required for a reverse lookup name. */ /* 32 nybbles, 32 dots, 8 characters of "ip6.arpa", 1 NUL: 73 characters. */ @@ -179,13 +184,15 @@ int tor_addr_parse_PTR_name(tor_addr_t *result, const char *address, int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out); -int tor_addr_parse_mask_ports(const char *s, +#define TAPMP_EXTENDED_STAR 1 +int tor_addr_parse_mask_ports(const char *s, unsigned flags, tor_addr_t *addr_out, maskbits_t *mask_out, uint16_t *port_min_out, uint16_t *port_max_out); const char * tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate); int tor_addr_parse(tor_addr_t *addr, const char *src); void tor_addr_copy(tor_addr_t *dest, const tor_addr_t *src); +void tor_addr_copy_tight(tor_addr_t *dest, const tor_addr_t *src); void tor_addr_from_ipv4n(tor_addr_t *dest, uint32_t v4addr); /** Set <b>dest</b> to the IPv4 address encoded in <b>v4addr</b> in host * order. */ @@ -202,24 +209,25 @@ int tor_addr_is_loopback(const tor_addr_t *addr); int tor_addr_port_split(int severity, const char *addrport, char **address_out, uint16_t *port_out); +int tor_addr_port_parse(int severity, const char *addrport, + tor_addr_t *address_out, uint16_t *port_out, + int default_port); + int tor_addr_hostname_is_local(const char *name); /* IPv4 helpers */ -int is_internal_IP(uint32_t ip, int for_listening); int addr_port_lookup(int severity, const char *addrport, char **address, uint32_t *addr, uint16_t *port_out); int parse_port_range(const char *port, uint16_t *port_min_out, uint16_t *port_max_out); -int parse_addr_and_port_range(const char *s, uint32_t *addr_out, - maskbits_t *maskbits_out, uint16_t *port_min_out, - uint16_t *port_max_out); int addr_mask_get_bits(uint32_t mask); -int addr_mask_cmp_bits(uint32_t a1, uint32_t a2, maskbits_t bits); /** Length of a buffer to allocate to hold the results of tor_inet_ntoa.*/ #define INET_NTOA_BUF_LEN 16 int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len); char *tor_dup_ip(uint32_t addr) ATTR_MALLOC; int get_interface_address(int severity, uint32_t *addr); +tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port); + #endif diff --git a/src/common/aes.c b/src/common/aes.c index 295a90749..f454a7f7b 100644 --- a/src/common/aes.c +++ b/src/common/aes.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -41,6 +41,7 @@ #include "aes.h" #include "util.h" #include "torlog.h" +#include "di_ops.h" #ifdef ANDROID /* Android's OpenSSL seems to have removed all of its Engine support. */ @@ -134,8 +135,8 @@ int evaluate_evp_for_aes(int force_val) { (void) force_val; - log_notice(LD_CRYPTO, "This version of OpenSSL has a known-good EVP " - "counter-mode implementation. Using it."); + log_info(LD_CRYPTO, "This version of OpenSSL has a known-good EVP " + "counter-mode implementation. Using it."); return 0; } int @@ -212,11 +213,11 @@ evaluate_evp_for_aes(int force_val) e = ENGINE_get_cipher_engine(NID_aes_128_ecb); if (e) { - log_notice(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.", + log_info(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.", ENGINE_get_name(e)); should_use_EVP = 1; } else { - log_notice(LD_CRYPTO, "No AES engine found; using AES_* functions."); + log_info(LD_CRYPTO, "No AES engine found; using AES_* functions."); should_use_EVP = 0; } #endif @@ -257,18 +258,18 @@ evaluate_ctr_for_aes(void) for (i=0; i<16; ++i) AES_ctr128_encrypt(&zero[i], &output[i], 1, &key, ivec, ivec_tmp, &pos); - if (memcmp(output, encrypt_zero, 16)) { + if (fast_memneq(output, encrypt_zero, 16)) { /* Counter mode is buggy */ log_notice(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; " "not using it."); } else { /* Counter mode is okay */ - log_notice(LD_CRYPTO, "This OpenSSL has a good implementation of counter " + log_info(LD_CRYPTO, "This OpenSSL has a good implementation of counter " "mode; using it."); should_use_openssl_CTR = 1; } #else - log_notice(LD_CRYPTO, "This version of OpenSSL has a slow implementation of " + log_info(LD_CRYPTO, "This version of OpenSSL has a slow implementation of " "counter mode; not using it."); #endif return 0; @@ -285,7 +286,7 @@ evaluate_ctr_for_aes(void) * value of the current counter. */ static INLINE void -_aes_fill_buf(aes_cnt_cipher_t *cipher) +aes_fill_buf_(aes_cnt_cipher_t *cipher) { /* We don't currently use OpenSSL's counter mode implementation because: * 1) some versions have known bugs @@ -340,7 +341,7 @@ aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) EVP_EncryptInit(&cipher->key.evp, c, (const unsigned char*)key, NULL); cipher->using_evp = 1; } else { - AES_set_encrypt_key((const unsigned char *)key, key_bits, &cipher->key.aes); + AES_set_encrypt_key((const unsigned char *)key, key_bits,&cipher->key.aes); cipher->using_evp = 0; } @@ -360,7 +361,7 @@ aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) memset(cipher->buf, 0, sizeof(cipher->buf)); else #endif - _aes_fill_buf(cipher); + aes_fill_buf_(cipher); } /** Release storage held by <b>cipher</b> @@ -387,9 +388,10 @@ aes_cipher_free(aes_cnt_cipher_t *cipher) #ifdef CAN_USE_OPENSSL_CTR /* Helper function to use EVP with openssl's counter-mode wrapper. */ -static void evp_block128_fn(const uint8_t in[16], - uint8_t out[16], - const void *key) +static void +evp_block128_fn(const uint8_t in[16], + uint8_t out[16], + const void *key) { EVP_CIPHER_CTX *ctx = (void*)key; int inl=16, outl=16; @@ -429,8 +431,7 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, &cipher->pos); } return; - } - else + } else #endif { int c = cipher->pos; @@ -453,7 +454,7 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, UPDATE_CTR_BUF(cipher, 1); } UPDATE_CTR_BUF(cipher, 0); - _aes_fill_buf(cipher); + aes_fill_buf_(cipher); } } } @@ -469,8 +470,7 @@ aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len) if (should_use_openssl_CTR) { aes_crypt(cipher, data, len, data); return; - } - else + } else #endif { int c = cipher->pos; @@ -493,7 +493,7 @@ aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len) UPDATE_CTR_BUF(cipher, 1); } UPDATE_CTR_BUF(cipher, 0); - _aes_fill_buf(cipher); + aes_fill_buf_(cipher); } } } @@ -515,7 +515,8 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv) #ifdef CAN_USE_OPENSSL_CTR if (!should_use_openssl_CTR) #endif - _aes_fill_buf(cipher); + aes_fill_buf_(cipher); } #endif + diff --git a/src/common/aes.h b/src/common/aes.h index bde567f87..8ff28a762 100644 --- a/src/common/aes.h +++ b/src/common/aes.h @@ -1,12 +1,12 @@ /* Copyright (c) 2003, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Implements a minimal interface to counter-mode AES. */ -#ifndef _TOR_AES_H -#define _TOR_AES_H +#ifndef TOR_AES_H +#define TOR_AES_H /** * \file aes.h diff --git a/src/common/backtrace.c b/src/common/backtrace.c new file mode 100644 index 000000000..3a073a8ff --- /dev/null +++ b/src/common/backtrace.c @@ -0,0 +1,233 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define __USE_GNU +#define _GNU_SOURCE 1 + +#include "orconfig.h" +#include "compat.h" +#include "util.h" +#include "torlog.h" + +#ifdef HAVE_EXECINFO_H +#include <execinfo.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +#ifdef HAVE_CYGWIN_SIGNAL_H +#include <cygwin/signal.h> +#elif defined(HAVE_SYS_UCONTEXT_H) +#include <sys/ucontext.h> +#elif defined(HAVE_UCONTEXT_H) +#include <ucontext.h> +#endif + +#define EXPOSE_CLEAN_BACKTRACE +#include "backtrace.h" + +#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \ + defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION) +#define USE_BACKTRACE +#endif + +#if !defined(USE_BACKTRACE) +#define NO_BACKTRACE_IMPL +#endif + +/** Version of Tor to report in backtrace messages. */ +static char *bt_version = NULL; + +#ifdef USE_BACKTRACE +/** Largest stack depth to try to dump. */ +#define MAX_DEPTH 256 +/** Static allocation of stack to dump. This is static so we avoid stack + * pressure. */ +static void *cb_buf[MAX_DEPTH]; +/** Protects cb_buf from concurrent access */ +static tor_mutex_t cb_buf_mutex; + +/** Change a stacktrace in <b>stack</b> of depth <b>depth</b> so that it will + * log the correct function from which a signal was received with context + * <b>ctx</b>. (When we get a signal, the current function will not have + * called any other function, and will therefore have not pushed its address + * onto the stack. Fortunately, we usually have the program counter in the + * ucontext_t structure. + */ +void +clean_backtrace(void **stack, int depth, const ucontext_t *ctx) +{ +#ifdef PC_FROM_UCONTEXT +#if defined(__linux__) + const int n = 1; +#elif defined(__darwin__) || defined(__APPLE__) || defined(__OpenBSD__) \ + || defined(__FreeBSD__) + const int n = 2; +#else + const int n = 1; +#endif + if (depth <= n) + return; + + stack[n] = (void*) ctx->PC_FROM_UCONTEXT; +#else + (void) depth; + (void) ctx; +#endif +} + +/** Log a message <b>msg</b> at <b>severity</b> in <b>domain</b>, and follow + * that with a backtrace log. */ +void +log_backtrace(int severity, int domain, const char *msg) +{ + int depth; + char **symbols; + int i; + + tor_mutex_acquire(&cb_buf_mutex); + + depth = backtrace(cb_buf, MAX_DEPTH); + symbols = backtrace_symbols(cb_buf, depth); + + tor_log(severity, domain, "%s. Stack trace:", msg); + if (!symbols) { + tor_log(severity, domain, " Unable to generate backtrace."); + goto done; + } + for (i=0; i < depth; ++i) { + tor_log(severity, domain, " %s", symbols[i]); + } + free(symbols); + + done: + tor_mutex_release(&cb_buf_mutex); +} + +static void crash_handler(int sig, siginfo_t *si, void *ctx_) + __attribute__((noreturn)); + +/** Signal handler: write a crash message with a stack trace, and die. */ +static void +crash_handler(int sig, siginfo_t *si, void *ctx_) +{ + char buf[40]; + int depth; + ucontext_t *ctx = (ucontext_t *) ctx_; + int n_fds, i; + const int *fds = NULL; + + (void) si; + + depth = backtrace(cb_buf, MAX_DEPTH); + /* Clean up the top stack frame so we get the real function + * name for the most recently failing function. */ + clean_backtrace(cb_buf, depth, ctx); + + format_dec_number_sigsafe((unsigned)sig, buf, sizeof(buf)); + + tor_log_err_sigsafe(bt_version, " died: Caught signal ", buf, "\n", + NULL); + + n_fds = tor_log_get_sigsafe_err_fds(&fds); + for (i=0; i < n_fds; ++i) + backtrace_symbols_fd(cb_buf, depth, fds[i]); + + abort(); +} + +/** Install signal handlers as needed so that when we crash, we produce a + * useful stack trace. Return 0 on success, -1 on failure. */ +static int +install_bt_handler(void) +{ + int trap_signals[] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS, + SIGIO, -1 }; + int i, rv=0; + + struct sigaction sa; + + tor_mutex_init(&cb_buf_mutex); + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = crash_handler; + sa.sa_flags = SA_SIGINFO; + sigfillset(&sa.sa_mask); + + for (i = 0; trap_signals[i] >= 0; ++i) { + if (sigaction(trap_signals[i], &sa, NULL) == -1) { + log_warn(LD_BUG, "Sigaction failed: %s", strerror(errno)); + rv = -1; + } + } + + { + /* Now, generate (but do not log) a backtrace. This ensures that + * libc has pre-loaded the symbols we need to dump things, so that later + * reads won't be denied by the sandbox code */ + char **symbols; + int depth = backtrace(cb_buf, MAX_DEPTH); + symbols = backtrace_symbols(cb_buf, depth); + if (symbols) + free(symbols); + } + + return rv; +} + +/** Uninstall crash handlers. */ +static void +remove_bt_handler(void) +{ + tor_mutex_uninit(&cb_buf_mutex); +} +#endif + +#ifdef NO_BACKTRACE_IMPL +void +log_backtrace(int severity, int domain, const char *msg) +{ + tor_log(severity, domain, "%s. (Stack trace not available)", msg); +} + +static int +install_bt_handler(void) +{ + return 0; +} + +static void +remove_bt_handler(void) +{ +} +#endif + +/** Set up code to handle generating error messages on crashes. */ +int +configure_backtrace_handler(const char *tor_version) +{ + tor_free(bt_version); + if (!tor_version) + tor_version = ""; + tor_asprintf(&bt_version, "Tor %s", tor_version); + + return install_bt_handler(); +} + +/** Perform end-of-process cleanup for code that generates error messages on + * crashes. */ +void +clean_up_backtrace_handler(void) +{ + remove_bt_handler(); + + tor_free(bt_version); +} + diff --git a/src/common/backtrace.h b/src/common/backtrace.h new file mode 100644 index 000000000..1f4d73339 --- /dev/null +++ b/src/common/backtrace.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_BACKTRACE_H +#define TOR_BACKTRACE_H + +#include "orconfig.h" + +void log_backtrace(int severity, int domain, const char *msg); +int configure_backtrace_handler(const char *tor_version); +void clean_up_backtrace_handler(void); + +#ifdef EXPOSE_CLEAN_BACKTRACE +#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \ + defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION) +void clean_backtrace(void **stack, int depth, const ucontext_t *ctx); +#endif +#endif + +#endif + diff --git a/src/common/ciphers.inc b/src/common/ciphers.inc index 137d78b11..ab4ac4072 100644 --- a/src/common/ciphers.inc +++ b/src/common/ciphers.inc @@ -4,86 +4,51 @@ * * This file was automatically generated by get_mozilla_ciphers.py. */ -#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - CIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -#else - XCIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA - CIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA) -#else - XCIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA - CIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA) -#else - XCIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA - CIPHER(0x0087, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA) +#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + CIPHER(0xc02b, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) #else - XCIPHER(0x0087, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA) + XCIPHER(0xc02b, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) #endif -#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA - CIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + CIPHER(0xc02f, TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256) #else - XCIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) + XCIPHER(0xc02f, TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256) #endif -#ifdef TLS1_TXT_DHE_DSS_WITH_AES_256_SHA - CIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA) +#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + CIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) #else - XCIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA) + XCIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) #endif -#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA - CIPHER(0xc00f, TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA) +#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + CIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) #else - XCIPHER(0xc00f, TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA) + XCIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) #endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA - CIPHER(0xc005, TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA) +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA + CIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA) #else - XCIPHER(0xc005, TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA) + XCIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA) #endif -#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA - CIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA) +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA + CIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA) #else - XCIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA) + XCIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA) #endif -#ifdef TLS1_TXT_RSA_WITH_AES_256_SHA - CIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA) +#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA + CIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA) #else - XCIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA) + XCIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA) #endif #ifdef TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA CIPHER(0xc007, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA) #else XCIPHER(0xc007, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA) #endif -#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - CIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -#else - XCIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -#endif #ifdef TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA CIPHER(0xc011, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA) #else XCIPHER(0xc011, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA) #endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA - CIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA) -#else - XCIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA - CIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA) -#else - XCIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA - CIPHER(0x0044, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA) -#else - XCIPHER(0x0044, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA) -#endif #ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA CIPHER(0x0033, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) #else @@ -94,89 +59,63 @@ #else XCIPHER(0x0032, TLS1_TXT_DHE_DSS_WITH_AES_128_SHA) #endif -#ifdef TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA - CIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA) -#else - XCIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA) -#endif -#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA - CIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA) -#else - XCIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA) -#endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA - CIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA) -#else - XCIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA) -#endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA - CIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA) +#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + CIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA) #else - XCIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA) + XCIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA) #endif -#ifdef TLS1_TXT_RSA_WITH_SEED_SHA - CIPHER(0x0096, TLS1_TXT_RSA_WITH_SEED_SHA) +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA + CIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) #else - XCIPHER(0x0096, TLS1_TXT_RSA_WITH_SEED_SHA) + XCIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) #endif -#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA - CIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA) +#ifdef TLS1_TXT_DHE_DSS_WITH_AES_256_SHA + CIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA) #else - XCIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA) + XCIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA) #endif -#ifdef SSL3_TXT_RSA_RC4_128_MD5 - CIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5) +#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + CIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA) #else - XCIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5) + XCIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA) #endif -#ifdef SSL3_TXT_RSA_RC4_128_SHA - CIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA) +#ifdef SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA + CIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) #else - XCIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA) + XCIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) #endif #ifdef TLS1_TXT_RSA_WITH_AES_128_SHA CIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA) #else XCIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA) #endif -#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA - CIPHER(0xc008, TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA) -#else - XCIPHER(0xc008, TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA) -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA - CIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA) -#else - XCIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA) -#endif -#ifdef SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA - CIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) +#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA + CIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA) #else - XCIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) + XCIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA) #endif -#ifdef SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA - CIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA) +#ifdef TLS1_TXT_RSA_WITH_AES_256_SHA + CIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA) #else - XCIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA) + XCIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA) #endif -#ifdef TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA - CIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA) +#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA + CIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA) #else - XCIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA) + XCIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA) #endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA - CIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA) +#ifdef SSL3_TXT_RSA_DES_192_CBC3_SHA + CIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA) #else - XCIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA) + XCIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA) #endif -/* No openssl macro found for 0xfeff */ -#ifdef SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA - CIPHER(0xfeff, SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA) +#ifdef SSL3_TXT_RSA_RC4_128_SHA + CIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA) #else - XCIPHER(0xfeff, SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA) + XCIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA) #endif -#ifdef SSL3_TXT_RSA_DES_192_CBC3_SHA - CIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA) +#ifdef SSL3_TXT_RSA_RC4_128_MD5 + CIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5) #else - XCIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA) + XCIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5) #endif diff --git a/src/common/compat.c b/src/common/compat.c index 59e3898de..111070cc1 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -18,11 +18,12 @@ /* XXXX024 We should just use AC_USE_SYSTEM_EXTENSIONS in our autoconf, * and get this (and other important stuff!) automatically. Once we do that, * make sure to also change the extern char **environ detection in - * configure.in, because whether that is declared or not depends on whether + * configure.ac, because whether that is declared or not depends on whether * we have _GNU_SOURCE defined! Maybe that means that once we take this out, * we can also take out the configure check. */ #define _GNU_SOURCE +#define COMPAT_PRIVATE #include "compat.h" #ifdef _WIN32 @@ -34,6 +35,15 @@ #ifdef HAVE_UNAME #include <sys/utsname.h> #endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SYSCTL_H +#include <sys/sysctl.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -109,6 +119,7 @@ #include "util.h" #include "container.h" #include "address.h" +#include "sandbox.h" /* Inline the strl functions if the platform doesn't have them. */ #ifndef HAVE_STRLCPY @@ -125,6 +136,7 @@ tor_open_cloexec(const char *path, int flags, unsigned mode) { int fd; #ifdef O_CLOEXEC + path = sandbox_intern_string(path); fd = open(path, flags|O_CLOEXEC, mode); if (fd >= 0) return fd; @@ -135,10 +147,16 @@ tor_open_cloexec(const char *path, int flags, unsigned mode) return -1; #endif + log_debug(LD_FS, "Opening %s with flags %x", path, flags); fd = open(path, flags, mode); #ifdef FD_CLOEXEC - if (fd >= 0) - fcntl(fd, F_SETFD, FD_CLOEXEC); + if (fd >= 0) { + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { + log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno)); + close(fd); + return -1; + } + } #endif return fd; } @@ -150,12 +168,26 @@ tor_fopen_cloexec(const char *path, const char *mode) { FILE *result = fopen(path, mode); #ifdef FD_CLOEXEC - if (result != NULL) - fcntl(fileno(result), F_SETFD, FD_CLOEXEC); + if (result != NULL) { + if (fcntl(fileno(result), F_SETFD, FD_CLOEXEC) == -1) { + log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno)); + fclose(result); + return NULL; + } + } #endif return result; } +/** As rename(), but work correctly with the sandbox. */ +int +tor_rename(const char *path_old, const char *path_new) +{ + log_debug(LD_FS, "Renaming %s to %s", path_old, path_new); + return rename(sandbox_intern_string(path_old), + sandbox_intern_string(path_new)); +} + #if defined(HAVE_SYS_MMAN_H) || defined(RUNNING_DOXYGEN) /** Try to create a memory mapping for <b>filename</b> and return it. On * failure, return NULL. Sets errno properly, using ERANGE to mean @@ -165,9 +197,10 @@ tor_mmap_file(const char *filename) { int fd; /* router file */ char *string; - int page_size; + int page_size, result; tor_mmap_t *res; size_t size, filesize; + struct stat st; tor_assert(filename); @@ -181,9 +214,22 @@ tor_mmap_file(const char *filename) return NULL; } - /* XXXX why not just do fstat here? */ - size = filesize = (size_t) lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); + /* Get the size of the file */ + result = fstat(fd, &st); + if (result != 0) { + int save_errno = errno; + log_warn(LD_FS, + "Couldn't fstat opened descriptor for \"%s\" during mmap: %s", + filename, strerror(errno)); + close(fd); + errno = save_errno; + return NULL; + } + size = filesize = (size_t)(st.st_size); + /* + * Should we check for weird crap like mmapping a named pipe here, + * or just wait for if (!size) below to fail? + */ /* ensure page alignment */ page_size = getpagesize(); size += (size%page_size) ? page_size-(size%page_size) : 0; @@ -214,12 +260,27 @@ tor_mmap_file(const char *filename) return res; } -/** Release storage held for a memory mapping. */ -void +/** Release storage held for a memory mapping; returns 0 on success, + * or -1 on failure (and logs a warning). */ +int tor_munmap_file(tor_mmap_t *handle) { - munmap((char*)handle->data, handle->mapping_size); - tor_free(handle); + int res; + + if (handle == NULL) + return 0; + + res = munmap((char*)handle->data, handle->mapping_size); + if (res == 0) { + /* munmap() succeeded */ + tor_free(handle); + } else { + log_warn(LD_FS, "Failed to munmap() in tor_munmap_file(): %s", + strerror(errno)); + res = -1; + } + + return res; } #elif defined(_WIN32) tor_mmap_t * @@ -301,17 +362,29 @@ tor_mmap_file(const char *filename) tor_munmap_file(res); return NULL; } -void + +/* Unmap the file, and return 0 for success or -1 for failure */ +int tor_munmap_file(tor_mmap_t *handle) { - if (handle->data) + if (handle == NULL) + return 0; + + if (handle->data) { /* This is an ugly cast, but without it, "data" in struct tor_mmap_t would have to be redefined as non-const. */ - UnmapViewOfFile( (LPVOID) handle->data); + BOOL ok = UnmapViewOfFile( (LPVOID) handle->data); + if (!ok) { + log_warn(LD_FS, "Failed to UnmapViewOfFile() in tor_munmap_file(): %d", + (int)GetLastError()); + } + } if (handle->mmap_handle != NULL) CloseHandle(handle->mmap_handle); tor_free(handle); + + return 0; } #else tor_mmap_t * @@ -327,13 +400,25 @@ tor_mmap_file(const char *filename) handle->size = st.st_size; return handle; } -void + +/** Unmap the file mapped with tor_mmap_file(), and return 0 for success + * or -1 for failure. + */ + +int tor_munmap_file(tor_mmap_t *handle) { - char *d = (char*)handle->data; + char *d = NULL; + if (handle == NULL) + return 0; + + d = (char*)handle->data; tor_free(d); memwipe(handle, 0, sizeof(tor_mmap_t)); tor_free(handle); + + /* Can't fail in this mmap()/munmap()-free case */ + return 0; } #endif @@ -425,11 +510,10 @@ tor_vasprintf(char **strp, const char *fmt, va_list args) else *strp = strp_tmp; return r; -#elif defined(_MSC_VER) +#elif defined(HAVE__VSCPRINTF) /* On Windows, _vsnprintf won't tell us the length of the string if it * overflows, so we need to use _vcsprintf to tell how much to allocate */ int len, r; - char *res; len = _vscprintf(fmt, args); if (len < 0) { *strp = NULL; @@ -489,21 +573,29 @@ tor_memmem(const void *_haystack, size_t hlen, #else /* This isn't as fast as the GLIBC implementation, but it doesn't need to * be. */ - const char *p, *end; + const char *p, *last_possible_start; const char *haystack = (const char*)_haystack; const char *needle = (const char*)_needle; char first; tor_assert(nlen); + if (nlen > hlen) + return NULL; + p = haystack; - end = haystack + hlen; + /* Last position at which the needle could start. */ + last_possible_start = haystack + hlen - nlen; first = *(const char*)needle; - while ((p = memchr(p, first, end-p))) { - if (p+nlen > end) - return NULL; + while ((p = memchr(p, first, last_possible_start + 1 - p))) { if (fast_memeq(p, needle, nlen)) return p; - ++p; + if (++p > last_possible_start) { + /* This comparison shouldn't be necessary, since if p was previously + * equal to last_possible_start, the next memchr call would be + * "memchr(p, first, 0)", which will return NULL. But it clarifies the + * logic. */ + return NULL; + } } return NULL; #endif @@ -720,7 +812,7 @@ int replace_file(const char *from, const char *to) { #ifndef _WIN32 - return rename(from,to); + return tor_rename(from, to); #else switch (file_status(to)) { @@ -735,7 +827,7 @@ replace_file(const char *from, const char *to) errno = EISDIR; return -1; } - return rename(from,to); + return tor_rename(from,to); #endif } @@ -861,6 +953,9 @@ tor_lockfile_unlock(tor_lockfile_t *lockfile) /** @{ */ /** Some old versions of Unix didn't define constants for these values, * and instead expect you to say 0, 1, or 2. */ +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif #ifndef SEEK_CUR #define SEEK_CUR 1 #endif @@ -891,6 +986,18 @@ tor_fd_seekend(int fd) #endif } +/** Move <b>fd</b> to position <b>pos</b> in the file. Return -1 on error, 0 + * on success. */ +int +tor_fd_setpos(int fd, off_t pos) +{ +#ifdef _WIN32 + return _lseek(fd, pos, SEEK_SET) < 0 ? -1 : 0; +#else + return lseek(fd, pos, SEEK_SET) < 0 ? -1 : 0; +#endif +} + #undef DEBUG_SOCKET_COUNTING #ifdef DEBUG_SOCKET_COUNTING /** A bitarray of all fds that should be passed to tor_socket_close(). Only @@ -924,24 +1031,40 @@ socket_accounting_unlock(void) } /** As close(), but guaranteed to work for sockets across platforms (including - * Windows, where close()ing a socket doesn't work. Returns 0 on success, -1 - * on failure. */ + * Windows, where close()ing a socket doesn't work. Returns 0 on success and + * the socket error code on failure. */ int -tor_close_socket(tor_socket_t s) +tor_close_socket_simple(tor_socket_t s) { int r = 0; /* On Windows, you have to call close() on fds returned by open(), - * and closesocket() on fds returned by socket(). On Unix, everything - * gets close()'d. We abstract this difference by always using - * tor_close_socket to close sockets, and always using close() on - * files. - */ -#if defined(_WIN32) - r = closesocket(s); -#else - r = close(s); -#endif + * and closesocket() on fds returned by socket(). On Unix, everything + * gets close()'d. We abstract this difference by always using + * tor_close_socket to close sockets, and always using close() on + * files. + */ + #if defined(_WIN32) + r = closesocket(s); + #else + r = close(s); + #endif + + if (r != 0) { + int err = tor_socket_errno(-1); + log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err)); + return err; + } + + return r; +} + +/** As tor_close_socket_simple(), but keeps track of the number + * of open sockets. Returns 0 on success, -1 on failure. */ +int +tor_close_socket(tor_socket_t s) +{ + int r = tor_close_socket_simple(s); socket_accounting_lock(); #ifdef DEBUG_SOCKET_COUNTING @@ -956,13 +1079,11 @@ tor_close_socket(tor_socket_t s) if (r == 0) { --n_sockets_open; } else { - int err = tor_socket_errno(-1); - log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err)); #ifdef _WIN32 - if (err != WSAENOTSOCK) + if (r != WSAENOTSOCK) --n_sockets_open; #else - if (err != EBADF) + if (r != EBADF) --n_sockets_open; #endif r = -1; @@ -1008,26 +1129,62 @@ mark_socket_open(tor_socket_t s) tor_socket_t tor_open_socket(int domain, int type, int protocol) { + return tor_open_socket_with_extensions(domain, type, protocol, 1, 0); +} + +/** As socket(), but creates a nonblocking socket and + * counts the number of open sockets. */ +tor_socket_t +tor_open_socket_nonblocking(int domain, int type, int protocol) +{ + return tor_open_socket_with_extensions(domain, type, protocol, 1, 1); +} + +/** As socket(), but counts the number of open sockets and handles + * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified. + * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate + * if the corresponding extension should be used.*/ +tor_socket_t +tor_open_socket_with_extensions(int domain, int type, int protocol, + int cloexec, int nonblock) +{ tor_socket_t s; -#ifdef SOCK_CLOEXEC - s = socket(domain, type|SOCK_CLOEXEC, protocol); +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) | + (nonblock ? SOCK_NONBLOCK : 0); + s = socket(domain, type|ext_flags, protocol); if (SOCKET_OK(s)) goto socket_ok; /* If we got an error, see if it is EINVAL. EINVAL might indicate that, - * even though we were built on a system with SOCK_CLOEXEC support, we - * are running on one without. */ + * even though we were built on a system with SOCK_CLOEXEC and SOCK_NONBLOCK + * support, we are running on one without. */ if (errno != EINVAL) return s; -#endif /* SOCK_CLOEXEC */ +#endif /* SOCK_CLOEXEC && SOCK_NONBLOCK */ s = socket(domain, type, protocol); if (! SOCKET_OK(s)) return s; #if defined(FD_CLOEXEC) - fcntl(s, F_SETFD, FD_CLOEXEC); + if (cloexec) { + if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { + log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno)); + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } + } +#else + (void)cloexec; #endif + if (nonblock) { + if (set_socket_nonblocking(s) == -1) { + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } + } + goto socket_ok; /* So that socket_ok will not be unused. */ socket_ok: @@ -1038,19 +1195,41 @@ tor_open_socket(int domain, int type, int protocol) return s; } -/** As socket(), but counts the number of open sockets. */ +/** As accept(), but counts the number of open sockets. */ tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len) { + return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 0); +} + +/** As accept(), but returns a nonblocking socket and + * counts the number of open sockets. */ +tor_socket_t +tor_accept_socket_nonblocking(tor_socket_t sockfd, struct sockaddr *addr, + socklen_t *len) +{ + return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 1); +} + +/** As accept(), but counts the number of open sockets and handles + * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified. + * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate + * if the corresponding extension should be used.*/ +tor_socket_t +tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr, + socklen_t *len, int cloexec, int nonblock) +{ tor_socket_t s; -#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - s = accept4(sockfd, addr, len, SOCK_CLOEXEC); +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) | + (nonblock ? SOCK_NONBLOCK : 0); + s = accept4(sockfd, addr, len, ext_flags); if (SOCKET_OK(s)) goto socket_ok; /* If we got an error, see if it is ENOSYS. ENOSYS indicates that, * even though we were built on a system with accept4 support, we * are running on one without. Also, check for EINVAL, which indicates that - * we are missing SOCK_CLOEXEC support. */ + * we are missing SOCK_CLOEXEC/SOCK_NONBLOCK support. */ if (errno != EINVAL && errno != ENOSYS) return s; #endif @@ -1060,9 +1239,24 @@ tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len) return s; #if defined(FD_CLOEXEC) - fcntl(s, F_SETFD, FD_CLOEXEC); + if (cloexec) { + if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { + log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno)); + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } + } +#else + (void)cloexec; #endif + if (nonblock) { + if (set_socket_nonblocking(s) == -1) { + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } + } + goto socket_ok; /* So that socket_ok will not be unused. */ socket_ok: @@ -1084,17 +1278,31 @@ get_n_open_sockets(void) return n; } -/** Turn <b>socket</b> into a nonblocking socket. +/** Turn <b>socket</b> into a nonblocking socket. Return 0 on success, -1 + * on failure. */ -void +int set_socket_nonblocking(tor_socket_t socket) { #if defined(_WIN32) unsigned long nonblocking = 1; ioctlsocket(socket, FIONBIO, (unsigned long*) &nonblocking); #else - fcntl(socket, F_SETFL, O_NONBLOCK); + int flags; + + flags = fcntl(socket, F_GETFL, 0); + if (flags == -1) { + log_warn(LD_NET, "Couldn't get file status flags: %s", strerror(errno)); + return -1; + } + flags |= O_NONBLOCK; + if (fcntl(socket, F_SETFL, flags) == -1) { + log_warn(LD_NET, "Couldn't set file status flags: %s", strerror(errno)); + return -1; + } #endif + + return 0; } /** @@ -1137,10 +1345,22 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) return -errno; #if defined(FD_CLOEXEC) - if (SOCKET_OK(fd[0])) - fcntl(fd[0], F_SETFD, FD_CLOEXEC); - if (SOCKET_OK(fd[1])) - fcntl(fd[1], F_SETFD, FD_CLOEXEC); + if (SOCKET_OK(fd[0])) { + r = fcntl(fd[0], F_SETFD, FD_CLOEXEC); + if (r == -1) { + close(fd[0]); + close(fd[1]); + return -errno; + } + } + if (SOCKET_OK(fd[1])) { + r = fcntl(fd[1], F_SETFD, FD_CLOEXEC); + if (r == -1) { + close(fd[0]); + close(fd[1]); + return -errno; + } + } #endif goto sockets_ok; /* So that sockets_ok will not be unused. */ @@ -1158,17 +1378,29 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) return 0; #else + return tor_ersatz_socketpair(family, type, protocol, fd); +#endif +} + +#ifdef NEED_ERSATZ_SOCKETPAIR +/** + * Helper used to implement socketpair on systems that lack it, by + * making a direct connection to localhost. + */ +STATIC int +tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) +{ /* This socketpair does not work when localhost is down. So * it's really not the same thing at all. But it's close enough * for now, and really, when localhost is down sometimes, we * have other problems too. */ - tor_socket_t listener = -1; - tor_socket_t connector = -1; - tor_socket_t acceptor = -1; + tor_socket_t listener = TOR_INVALID_SOCKET; + tor_socket_t connector = TOR_INVALID_SOCKET; + tor_socket_t acceptor = TOR_INVALID_SOCKET; struct sockaddr_in listen_addr; struct sockaddr_in connect_addr; - int size; + socklen_t size; int saved_errno = -1; if (protocol @@ -1219,7 +1451,6 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) goto tidy_up_and_fail; if (size != sizeof(listen_addr)) goto abort_tidy_up_and_fail; - tor_close_socket(listener); /* Now check we are talking to ourself by matching port and host on the two sockets. */ if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1) @@ -1230,6 +1461,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) || listen_addr.sin_port != connect_addr.sin_port) { goto abort_tidy_up_and_fail; } + tor_close_socket(listener); fd[0] = connector; fd[1] = acceptor; @@ -1244,19 +1476,19 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) tidy_up_and_fail: if (saved_errno < 0) saved_errno = errno; - if (listener != -1) + if (SOCKET_OK(listener)) tor_close_socket(listener); - if (connector != -1) + if (SOCKET_OK(connector)) tor_close_socket(connector); - if (acceptor != -1) + if (SOCKET_OK(acceptor)) tor_close_socket(acceptor); return -saved_errno; -#endif } +#endif /** Number of extra file descriptors to keep in reserve beyond those that we * tell Tor it's allowed to use. */ -#define ULIMIT_BUFFER 32 /* keep 32 extra fd's beyond _ConnLimit */ +#define ULIMIT_BUFFER 32 /* keep 32 extra fd's beyond ConnLimit_ */ /** Learn the maximum allowed number of file descriptors, and tell the system * we want to use up to that number. (Some systems have a low soft limit, and @@ -1470,6 +1702,106 @@ log_credential_status(void) } #endif +#ifndef _WIN32 +/** Cached struct from the last getpwname() call we did successfully. */ +static struct passwd *passwd_cached = NULL; + +/** Helper: copy a struct passwd object. + * + * We only copy the fields pw_uid, pw_gid, pw_name, pw_dir. Tor doesn't use + * any others, and I don't want to run into incompatibilities. + */ +static struct passwd * +tor_passwd_dup(const struct passwd *pw) +{ + struct passwd *new_pw = tor_malloc_zero(sizeof(struct passwd)); + if (pw->pw_name) + new_pw->pw_name = tor_strdup(pw->pw_name); + if (pw->pw_dir) + new_pw->pw_dir = tor_strdup(pw->pw_dir); + new_pw->pw_uid = pw->pw_uid; + new_pw->pw_gid = pw->pw_gid; + + return new_pw; +} + +/** Helper: free one of our cached 'struct passwd' values. */ +static void +tor_passwd_free(struct passwd *pw) +{ + if (!pw) + return; + + tor_free(pw->pw_name); + tor_free(pw->pw_dir); + tor_free(pw); +} + +/** Wrapper around getpwnam() that caches result. Used so that we don't need + * to give the sandbox access to /etc/passwd. + * + * The following fields alone will definitely be copied in the output: pw_uid, + * pw_gid, pw_name, pw_dir. Other fields are not present in cached values. + * + * When called with a NULL argument, this function clears storage associated + * with static variables it uses. + **/ +const struct passwd * +tor_getpwnam(const char *username) +{ + struct passwd *pw; + + if (username == NULL) { + tor_passwd_free(passwd_cached); + passwd_cached = NULL; + return NULL; + } + + if ((pw = getpwnam(username))) { + tor_passwd_free(passwd_cached); + passwd_cached = tor_passwd_dup(pw); + log_notice(LD_GENERAL, "Caching new entry %s for %s", + passwd_cached->pw_name, username); + return pw; + } + + /* Lookup failed */ + if (! passwd_cached || ! passwd_cached->pw_name) + return NULL; + + if (! strcmp(username, passwd_cached->pw_name)) + return passwd_cached; + + return NULL; +} + +/** Wrapper around getpwnam() that can use cached result from + * tor_getpwnam(). Used so that we don't need to give the sandbox access to + * /etc/passwd. + * + * The following fields alone will definitely be copied in the output: pw_uid, + * pw_gid, pw_name, pw_dir. Other fields are not present in cached values. + */ +const struct passwd * +tor_getpwuid(uid_t uid) +{ + struct passwd *pw; + + if ((pw = getpwuid(uid))) { + return pw; + } + + /* Lookup failed */ + if (! passwd_cached) + return NULL; + + if (uid == passwd_cached->pw_uid) + return passwd_cached; + + return NULL; +} +#endif + /** Call setuid and setgid to run as <b>user</b> and switch to their * primary group. Return 0 on success. On failure, log and return -1. */ @@ -1477,7 +1809,7 @@ int switch_id(const char *user) { #ifndef _WIN32 - struct passwd *pw = NULL; + const struct passwd *pw = NULL; uid_t old_uid; gid_t old_gid; static int have_already_switched_id = 0; @@ -1498,7 +1830,7 @@ switch_id(const char *user) old_gid = getgid(); /* Lookup the user and group information, if we have a problem, bail out. */ - pw = getpwnam(user); + pw = tor_getpwnam(user); if (pw == NULL) { log_warn(LD_CONFIG, "Error setting configured user: %s not found", user); return -1; @@ -1669,10 +2001,10 @@ tor_disable_debugger_attach(void) char * get_user_homedir(const char *username) { - struct passwd *pw; + const struct passwd *pw; tor_assert(username); - if (!(pw = getpwnam(username))) { + if (!(pw = tor_getpwnam(username))) { log_err(LD_CONFIG,"User \"%s\" not found.", username); return NULL; } @@ -1684,6 +2016,15 @@ get_user_homedir(const char *username) * actually examine the filesystem; does a purely syntactic modification. * * The parent of the root director is considered to be iteself. + * + * Path separators are the forward slash (/) everywhere and additionally + * the backslash (\) on Win32. + * + * Cuts off any number of trailing path separators but otherwise ignores + * them for purposes of finding the parent directory. + * + * Returns 0 if a parent directory was successfully found, -1 otherwise (fname + * did not have any path separators or only had them at the end). * */ int get_parent_directory(char *fname) @@ -1957,8 +2298,10 @@ tor_inet_pton(int af, const char *src, void *dst) else { unsigned byte1,byte2,byte3,byte4; char more; - for (eow = dot-1; eow >= src && TOR_ISDIGIT(*eow); --eow) + for (eow = dot-1; eow > src && TOR_ISDIGIT(*eow); --eow) ; + if (*eow != ':') + return 0; ++eow; /* We use "scanf" because some platform inet_aton()s are too lax @@ -2060,30 +2403,6 @@ tor_lookup_hostname(const char *name, uint32_t *addr) return -1; } -/** Initialize the insecure libc RNG. */ -void -tor_init_weak_random(unsigned seed) -{ -#ifdef _WIN32 - srand(seed); -#else - srandom(seed); -#endif -} - -/** Return a randomly chosen value in the range 0..TOR_RAND_MAX. This - * entropy will not be cryptographically strong; do not rely on it - * for anything an adversary should not be able to predict. */ -long -tor_weak_random(void) -{ -#ifdef _WIN32 - return rand(); -#else - return random(); -#endif -} - /** Hold the result of our call to <b>uname</b>. */ static char uname_result[256]; /** True iff uname_result is set. */ @@ -2210,6 +2529,12 @@ tor_pthread_helper_fn(void *_data) func(arg); return NULL; } +/** + * A pthread attribute to make threads start detached. + */ +static pthread_attr_t attr_detached; +/** True iff we've called tor_threads_init() */ +static int threads_initialized = 0; #endif /** Minimalist interface to run a void function in the background. On @@ -2233,12 +2558,12 @@ spawn_func(void (*func)(void *), void *data) #elif defined(USE_PTHREADS) pthread_t thread; tor_pthread_data_t *d; + if (PREDICT_UNLIKELY(!threads_initialized)) + tor_threads_init(); d = tor_malloc(sizeof(tor_pthread_data_t)); d->data = data; d->func = func; - if (pthread_create(&thread,NULL,tor_pthread_helper_fn,d)) - return -1; - if (pthread_detach(thread)) + if (pthread_create(&thread,&attr_detached,tor_pthread_helper_fn,d)) return -1; return 0; #else @@ -2290,8 +2615,33 @@ compute_num_cpus_impl(void) return (int)info.dwNumberOfProcessors; else return -1; -#elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) - long cpus = sysconf(_SC_NPROCESSORS_CONF); +#elif defined(HAVE_SYSCONF) +#ifdef _SC_NPROCESSORS_CONF + long cpus_conf = sysconf(_SC_NPROCESSORS_CONF); +#else + long cpus_conf = -1; +#endif +#ifdef _SC_NPROCESSORS_ONLN + long cpus_onln = sysconf(_SC_NPROCESSORS_ONLN); +#else + long cpus_onln = -1; +#endif + long cpus = -1; + + if (cpus_conf > 0 && cpus_onln < 0) { + cpus = cpus_conf; + } else if (cpus_onln > 0 && cpus_conf < 0) { + cpus = cpus_onln; + } else if (cpus_onln > 0 && cpus_conf > 0) { + if (cpus_onln < cpus_conf) { + log_notice(LD_GENERAL, "I think we have %ld CPUS, but only %ld of them " + "are available. Telling Tor to only use %ld. You can over" + "ride this with the NumCPUs option", + cpus_conf, cpus_onln, cpus_onln); + } + cpus = cpus_onln; + } + if (cpus >= 1 && cpus < INT_MAX) return (int)cpus; else @@ -2570,8 +2920,6 @@ tor_get_thread_id(void) * "reentrant" mutexes (i.e., once we can re-lock if we're already holding * them.) */ static pthread_mutexattr_t attr_reentrant; -/** True iff we've called tor_threads_init() */ -static int threads_initialized = 0; /** Initialize <b>mutex</b> so it can be locked. Every mutex must be set * up with tor_mutex_init() or tor_mutex_new(); not both. */ void @@ -2715,6 +3063,8 @@ tor_threads_init(void) if (!threads_initialized) { pthread_mutexattr_init(&attr_reentrant); pthread_mutexattr_settype(&attr_reentrant, PTHREAD_MUTEX_RECURSIVE); + tor_assert(0==pthread_attr_init(&attr_detached)); + tor_assert(0==pthread_attr_setdetachstate(&attr_detached, 1)); threads_initialized = 1; set_main_thread(); } @@ -2759,7 +3109,7 @@ tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex) EnterCriticalSection(&cond->mutex); tor_assert(WaitForSingleObject(event, 0) == WAIT_TIMEOUT); - tor_assert(!smartlist_isin(cond->events, event)); + tor_assert(!smartlist_contains(cond->events, event)); smartlist_add(cond->events, event); LeaveCriticalSection(&cond->mutex); @@ -3090,3 +3440,119 @@ format_win32_error(DWORD err) } #endif +#if defined(HW_PHYSMEM64) +/* This appears to be an OpenBSD thing */ +#define INT64_HW_MEM HW_PHYSMEM64 +#elif defined(HW_MEMSIZE) +/* OSX defines this one */ +#define INT64_HW_MEM HW_MEMSIZE +#endif + +/** + * Helper: try to detect the total system memory, and return it. On failure, + * return 0. + */ +static uint64_t +get_total_system_memory_impl(void) +{ +#if defined(__linux__) + /* On linux, sysctl is deprecated. Because proc is so awesome that you + * shouldn't _want_ to write portable code, I guess? */ + unsigned long long result=0; + int fd = -1; + char *s = NULL; + const char *cp; + size_t file_size=0; + if (-1 == (fd = tor_open_cloexec("/proc/meminfo",O_RDONLY,0))) + return 0; + s = read_file_to_str_until_eof(fd, 65536, &file_size); + if (!s) + goto err; + cp = strstr(s, "MemTotal:"); + if (!cp) + goto err; + /* Use the system sscanf so that space will match a wider number of space */ + if (sscanf(cp, "MemTotal: %llu kB\n", &result) != 1) + goto err; + + close(fd); + tor_free(s); + return result * 1024; + + err: + tor_free(s); + close(fd); + return 0; +#elif defined (_WIN32) + /* Windows has MEMORYSTATUSEX; pretty straightforward. */ + MEMORYSTATUSEX ms; + memset(&ms, 0, sizeof(ms)); + ms.dwLength = sizeof(ms); + if (! GlobalMemoryStatusEx(&ms)) + return 0; + + return ms.ullTotalPhys; + +#elif defined(HAVE_SYSCTL) && defined(INT64_HW_MEM) + /* On many systems, HW_PYHSMEM is clipped to 32 bits; let's use a better + * variant if we know about it. */ + uint64_t memsize = 0; + size_t len = sizeof(memsize); + int mib[2] = {CTL_HW, INT64_HW_MEM}; + if (sysctl(mib,2,&memsize,&len,NULL,0)) + return 0; + + return memsize; + +#elif defined(HAVE_SYSCTL) && defined(HW_PHYSMEM) + /* On some systems (like FreeBSD I hope) you can use a size_t with + * HW_PHYSMEM. */ + size_t memsize=0; + size_t len = sizeof(memsize); + int mib[2] = {CTL_HW, HW_USERMEM}; + if (sysctl(mib,2,&memsize,&len,NULL,0)) + return -1; + + return memsize; + +#else + /* I have no clue. */ + return 0; +#endif +} + +/** + * Try to find out how much physical memory the system has. On success, + * return 0 and set *<b>mem_out</b> to that value. On failure, return -1. + */ +int +get_total_system_memory(size_t *mem_out) +{ + static size_t mem_cached=0; + uint64_t m = get_total_system_memory_impl(); + if (0 == m) { + /* We couldn't find our memory total */ + if (0 == mem_cached) { + /* We have no cached value either */ + *mem_out = 0; + return -1; + } + + *mem_out = mem_cached; + return 0; + } + +#if SIZE_T_MAX != UINT64_MAX + if (m > SIZE_T_MAX) { + /* I think this could happen if we're a 32-bit Tor running on a 64-bit + * system: we could have more system memory than would fit in a + * size_t. */ + m = SIZE_T_MAX; + } +#endif + + *mem_out = mem_cached = (size_t) m; + + return 0; +} + diff --git a/src/common/compat.h b/src/common/compat.h index 42648bb04..683c4d089 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -1,13 +1,14 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -#ifndef _TOR_COMPAT_H -#define _TOR_COMPAT_H +#ifndef TOR_COMPAT_H +#define TOR_COMPAT_H #include "orconfig.h" #include "torint.h" +#include "testsupport.h" #ifdef _WIN32 #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0501 @@ -53,13 +54,13 @@ #endif #include <stdio.h> +#include <errno.h> #if defined (WINCE) #include <fcntl.h> #include <io.h> #include <math.h> #include <projects.h> -#define snprintf _snprintf /* this is not exported as W .... */ #define SHGetPathFromIDListW SHGetPathFromIDList /* wcecompat has vasprintf */ @@ -74,19 +75,29 @@ #error "It seems your platform does not represent NULL as zero. We can't cope." #endif +#ifndef DOUBLE_0_REP_IS_ZERO_BYTES +#error "It seems your platform does not represent 0.0 as zeros. We can't cope." +#endif + #if 'a'!=97 || 'z'!=122 || 'A'!=65 || ' '!=32 #error "It seems that you encode characters in something other than ASCII." #endif /* ===== Compiler compatibility */ -/* GCC can check printf types on arbitrary functions. */ +/* GCC can check printf and scanf types on arbitrary functions. */ #ifdef __GNUC__ #define CHECK_PRINTF(formatIdx, firstArg) \ __attribute__ ((format(printf, formatIdx, firstArg))) #else #define CHECK_PRINTF(formatIdx, firstArg) #endif +#ifdef __GNUC__ +#define CHECK_SCANF(formatIdx, firstArg) \ + __attribute__ ((format(scanf, formatIdx, firstArg))) +#else +#define CHECK_SCANF(formatIdx, firstArg) +#endif /* inline is __inline on windows. */ #ifdef _WIN32 @@ -132,6 +143,16 @@ extern INLINE double U64_TO_DBL(uint64_t x) { #define DBL_TO_U64(x) ((uint64_t) (x)) #endif +#ifdef ENUM_VALS_ARE_SIGNED +#define ENUM_BF(t) unsigned +#else +/** Wrapper for having a bitfield of an enumerated type. Where possible, we + * just use the enumerated type (so the compiler can help us and notice + * problems), but if enumerated types are unsigned, we must use unsigned, + * so that the loss of precision doesn't make large values negative. */ +#define ENUM_BF(t) t +#endif + /* GCC has several useful attributes. */ #if defined(__GNUC__) && __GNUC__ >= 3 #define ATTR_NORETURN __attribute__((noreturn)) @@ -148,6 +169,7 @@ extern INLINE double U64_TO_DBL(uint64_t x) { * * #define ATTR_NONNULL(x) __attribute__((nonnull x)) */ #define ATTR_NONNULL(x) +#define ATTR_UNUSED __attribute__ ((unused)) /** Macro: Evaluates to <b>exp</b> and hints the compiler that the value * of <b>exp</b> will probably be true. @@ -171,6 +193,7 @@ extern INLINE double U64_TO_DBL(uint64_t x) { #define ATTR_MALLOC #define ATTR_NORETURN #define ATTR_NONNULL(x) +#define ATTR_UNUSED #define PREDICT_LIKELY(exp) (exp) #define PREDICT_UNLIKELY(exp) (exp) #endif @@ -239,6 +262,19 @@ size_t strlcpy(char *dst, const char *src, size_t siz) ATTR_NONNULL((1,2)); #define I64_FORMAT "%lld" #endif +#if (SIZEOF_INTPTR_T == SIZEOF_INT) +#define INTPTR_T_FORMAT "%d" +#define INTPTR_PRINTF_ARG(x) ((int)(x)) +#elif (SIZEOF_INTPTR_T == SIZEOF_LONG) +#define INTPTR_T_FORMAT "%ld" +#define INTPTR_PRINTF_ARG(x) ((long)(x)) +#elif (SIZEOF_INTPTR_T == 8) +#define INTPTR_T_FORMAT I64_FORMAT +#define INTPTR_PRINTF_ARG(x) I64_PRINTF_ARG(x) +#else +#error Unknown: SIZEOF_INTPTR_T +#endif + /** Represents an mmaped file. Allocated via tor_mmap_file; freed with * tor_munmap_file. */ typedef struct tor_mmap_t { @@ -256,7 +292,7 @@ typedef struct tor_mmap_t { } tor_mmap_t; tor_mmap_t *tor_mmap_file(const char *filename) ATTR_NONNULL((1)); -void tor_munmap_file(tor_mmap_t *handle) ATTR_NONNULL((1)); +int tor_munmap_file(tor_mmap_t *handle) ATTR_NONNULL((1)); int tor_snprintf(char *str, size_t size, const char *format, ...) CHECK_PRINTF(3,4) ATTR_NONNULL((1,3)); @@ -285,7 +321,7 @@ tor_memstr(const void *haystack, size_t hlen, const char *needle) extern const uint32_t TOR_##name##_TABLE[]; \ static INLINE int TOR_##name(char c) { \ uint8_t u = c; \ - return !!(TOR_##name##_TABLE[(u >> 5) & 7] & (1 << (u & 31))); \ + return !!(TOR_##name##_TABLE[(u >> 5) & 7] & (1u << (u & 31))); \ } DECLARE_CTYPE_FN(ISALPHA) DECLARE_CTYPE_FN(ISALNUM) @@ -308,10 +344,10 @@ char *tor_strtok_r_impl(char *str, const char *sep, char **lasts); #endif #ifdef _WIN32 -#define _SHORT_FILE_ (tor_fix_source_file(__FILE__)) +#define SHORT_FILE__ (tor_fix_source_file(__FILE__)) const char *tor_fix_source_file(const char *fname); #else -#define _SHORT_FILE_ (__FILE__) +#define SHORT_FILE__ (__FILE__) #define tor_fix_source_file(s) (s) #endif @@ -374,6 +410,7 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result); /* ===== File compatibility */ int tor_open_cloexec(const char *path, int flags, unsigned mode); FILE *tor_fopen_cloexec(const char *path, const char *mode); +int tor_rename(const char *path_old, const char *path_new); int replace_file(const char *from, const char *to); int touch_file(const char *fname); @@ -384,6 +421,7 @@ tor_lockfile_t *tor_lockfile_lock(const char *filename, int blocking, void tor_lockfile_unlock(tor_lockfile_t *lockfile); off_t tor_fd_getpos(int fd); +int tor_fd_setpos(int fd, off_t pos); int tor_fd_seekend(int fd); #ifdef _WIN32 @@ -403,21 +441,35 @@ typedef int socklen_t; * any inadvertant checks for the socket being <= 0 or > 0 will probably * still work. */ #define tor_socket_t intptr_t +#define TOR_SOCKET_T_FORMAT INTPTR_T_FORMAT #define SOCKET_OK(s) ((SOCKET)(s) != INVALID_SOCKET) #define TOR_INVALID_SOCKET INVALID_SOCKET #else /** Type used for a network socket. */ #define tor_socket_t int +#define TOR_SOCKET_T_FORMAT "%d" /** Macro: true iff 's' is a possible value for a valid initialized socket. */ #define SOCKET_OK(s) ((s) >= 0) /** Error/uninitialized value for a tor_socket_t. */ #define TOR_INVALID_SOCKET (-1) #endif +int tor_close_socket_simple(tor_socket_t s); int tor_close_socket(tor_socket_t s); +tor_socket_t tor_open_socket_with_extensions( + int domain, int type, int protocol, + int cloexec, int nonblock); tor_socket_t tor_open_socket(int domain, int type, int protocol); +tor_socket_t tor_open_socket_nonblocking(int domain, int type, int protocol); tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len); +tor_socket_t tor_accept_socket_nonblocking(tor_socket_t sockfd, + struct sockaddr *addr, + socklen_t *len); +tor_socket_t tor_accept_socket_with_extensions(tor_socket_t sockfd, + struct sockaddr *addr, + socklen_t *len, + int cloexec, int nonblock); int get_n_open_sockets(void); #define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags) @@ -489,7 +541,7 @@ int tor_inet_aton(const char *cp, struct in_addr *addr) ATTR_NONNULL((1,2)); const char *tor_inet_ntop(int af, const void *src, char *dst, size_t len); int tor_inet_pton(int af, const char *src, void *dst); int tor_lookup_hostname(const char *name, uint32_t *addr) ATTR_NONNULL((1,2)); -void set_socket_nonblocking(tor_socket_t socket); +int set_socket_nonblocking(tor_socket_t socket); int tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]); int network_init(void); @@ -523,10 +575,15 @@ int tor_socket_errno(tor_socket_t sock); const char *tor_socket_strerror(int e); #else #define SOCK_ERRNO(e) e +#if EAGAIN == EWOULDBLOCK #define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN) +#else +#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || (e) == EWOULDBLOCK) +#endif #define ERRNO_IS_EINPROGRESS(e) ((e) == EINPROGRESS) #define ERRNO_IS_CONN_EINPROGRESS(e) ((e) == EINPROGRESS) -#define ERRNO_IS_ACCEPT_EAGAIN(e) ((e) == EAGAIN || (e) == ECONNABORTED) +#define ERRNO_IS_ACCEPT_EAGAIN(e) \ + (ERRNO_IS_EAGAIN(e) || (e) == ECONNABORTED) #define ERRNO_IS_ACCEPT_RESOURCE_LIMIT(e) \ ((e) == EMFILE || (e) == ENFILE || (e) == ENOBUFS || (e) == ENOMEM) #define ERRNO_IS_EADDRINUSE(e) ((e) == EADDRINUSE) @@ -547,11 +604,6 @@ typedef enum { SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED = 0x08, } socks5_reply_status_t; -/* ===== Insecure rng */ -void tor_init_weak_random(unsigned seed); -long tor_weak_random(void); -#define TOR_RAND_MAX (RAND_MAX) - /* ===== OS compatibility */ const char *get_uname(void); @@ -581,11 +633,18 @@ int switch_id(const char *user); char *get_user_homedir(const char *username); #endif +#ifndef _WIN32 +const struct passwd *tor_getpwnam(const char *username); +const struct passwd *tor_getpwuid(uid_t uid); +#endif + int get_parent_directory(char *fname); char *make_path_absolute(char *fname); char **get_environment(void); +int get_total_system_memory(size_t *mem_out); + int spawn_func(void (*func)(void *), void *data); void spawn_exit(void) ATTR_NORETURN; @@ -690,5 +749,13 @@ char *format_win32_error(DWORD err); #endif +#ifdef COMPAT_PRIVATE +#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS) +#define NEED_ERSATZ_SOCKETPAIR +STATIC int tor_ersatz_socketpair(int family, int type, int protocol, + tor_socket_t fd[2]); +#endif +#endif + #endif diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 6655ca87d..74b54bb85 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2012, The Tor Project, Inc. */ +/* Copyright (c) 2009-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -13,6 +13,8 @@ #include "compat.h" #include "compat_libevent.h" +#include "crypto.h" + #include "util.h" #include "torlog.h" @@ -54,7 +56,9 @@ typedef uint32_t le_version_t; * it is. */ #define LE_OTHER V(0,0,99) +#if 0 static le_version_t tor_get_libevent_version(const char **v_out); +#endif #if defined(HAVE_EVENT_SET_LOG_CALLBACK) || defined(RUNNING_DOXYGEN) /** A string which, if it appears in a libevent log, should be ignored. */ @@ -74,19 +78,19 @@ libevent_logging_callback(int severity, const char *msg) } switch (severity) { case _EVENT_LOG_DEBUG: - log(LOG_DEBUG, LD_NOCB|LD_NET, "Message from libevent: %s", buf); + log_debug(LD_NOCB|LD_NET, "Message from libevent: %s", buf); break; case _EVENT_LOG_MSG: - log(LOG_INFO, LD_NOCB|LD_NET, "Message from libevent: %s", buf); + log_info(LD_NOCB|LD_NET, "Message from libevent: %s", buf); break; case _EVENT_LOG_WARN: - log(LOG_WARN, LD_NOCB|LD_GENERAL, "Warning from libevent: %s", buf); + log_warn(LD_NOCB|LD_GENERAL, "Warning from libevent: %s", buf); break; case _EVENT_LOG_ERR: - log(LOG_ERR, LD_NOCB|LD_GENERAL, "Error from libevent: %s", buf); + log_err(LD_NOCB|LD_GENERAL, "Error from libevent: %s", buf); break; default: - log(LOG_WARN, LD_NOCB|LD_GENERAL, "Message [%d] from libevent: %s", + log_warn(LD_NOCB|LD_GENERAL, "Message [%d] from libevent: %s", severity, buf); break; } @@ -185,13 +189,6 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg) /* some paths below don't use torcfg, so avoid unused variable warnings */ (void)torcfg; -#ifdef __APPLE__ - if (MACOSX_KQUEUE_IS_BROKEN || - tor_get_libevent_version(NULL) < V_OLD(1,1,'b')) { - setenv("EVENT_NOKQUEUE","1",1); - } -#endif - #ifdef HAVE_EVENT2_EVENT_H { int attempts = 0; @@ -266,13 +263,13 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg) #if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD) /* Making this a NOTICE for now so we can link bugs to a libevent versions * or methods better. */ - log(LOG_NOTICE, LD_GENERAL, + log_info(LD_GENERAL, "Initialized libevent version %s using method %s. Good.", event_get_version(), tor_libevent_get_method()); #else - log(LOG_NOTICE, LD_GENERAL, + log_notice(LD_GENERAL, "Initialized old libevent (version 1.0b or earlier)."); - log(LOG_WARN, LD_GENERAL, + log_warn(LD_GENERAL, "You have a *VERY* old version of libevent. It is likely to be buggy; " "please build Tor with a more recent version."); #endif @@ -364,6 +361,7 @@ le_versions_compatibility(le_version_t v) return 5; } +#if 0 /** Return the version number of the currently running version of Libevent. * See le_version_t for info on the format. */ @@ -386,6 +384,7 @@ tor_get_libevent_version(const char **v_out) *v_out = v; return r; } +#endif /** Return a string representation of the version of the currently running * version of Libevent. */ @@ -407,77 +406,9 @@ void tor_check_libevent_version(const char *m, int server, const char **badness_out) { - int buggy = 0, iffy = 0, slow = 0, thread_unsafe = 0; - le_version_t version; - const char *v = NULL; - const char *badness = NULL; - const char *sad_os = ""; - - version = tor_get_libevent_version(&v); - - /* It would be better to disable known-buggy methods rather than warning - * about them. But the problem is that with older versions of Libevent, - * it's not trivial to get them to change their methods once they're - * initialized... and with newer versions of Libevent, they aren't actually - * broken. But we should revisit this if we ever find a post-1.4 version - * of Libevent where we need to disable a given method. */ - if (!strcmp(m, "kqueue")) { - if (version < V_OLD(1,1,'b')) - buggy = 1; - } else if (!strcmp(m, "epoll")) { - if (version < V(1,1,0)) - iffy = 1; - } else if (!strcmp(m, "poll")) { - if (version < V_OLD(1,0,'e')) - buggy = 1; - if (version < V(1,1,0)) - slow = 1; - } else if (!strcmp(m, "select")) { - if (version < V(1,1,0)) - slow = 1; - } else if (!strcmp(m, "win32")) { - if (version < V_OLD(1,1,'b')) - buggy = 1; - } - - /* Libevent versions before 1.3b do very badly on operating systems with - * user-space threading implementations. */ -#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) - if (server && version < V_OLD(1,3,'b')) { - thread_unsafe = 1; - sad_os = "BSD variants"; - } -#elif defined(__APPLE__) || defined(__darwin__) - if (server && version < V_OLD(1,3,'b')) { - thread_unsafe = 1; - sad_os = "Mac OS X"; - } -#endif - - if (thread_unsafe) { - log(LOG_WARN, LD_GENERAL, - "Libevent version %s often crashes when running a Tor server with %s. " - "Please use the latest version of libevent (1.3b or later)",v,sad_os); - badness = "BROKEN"; - } else if (buggy) { - log(LOG_WARN, LD_GENERAL, - "There are serious bugs in using %s with libevent %s. " - "Please use the latest version of libevent.", m, v); - badness = "BROKEN"; - } else if (iffy) { - log(LOG_WARN, LD_GENERAL, - "There are minor bugs in using %s with libevent %s. " - "You may want to use the latest version of libevent.", m, v); - badness = "BUGGY"; - } else if (slow && server) { - log(LOG_WARN, LD_GENERAL, - "libevent %s can be very slow with %s. " - "When running a server, please use the latest version of libevent.", - v,m); - badness = "SLOW"; - } - - *badness_out = badness; + (void) m; + (void) server; + *badness_out = NULL; } #if defined(LIBEVENT_VERSION) @@ -486,6 +417,14 @@ tor_check_libevent_version(const char *m, int server, #define HEADER_VERSION _EVENT_VERSION #endif +/** Return a string representation of the version of Libevent that was used +* at compilation time. */ +const char * +tor_libevent_get_header_version_str(void) +{ + return HEADER_VERSION; +} + /** See whether the headers we were built against differ from the library we * linked against so much that we're likely to crash. If so, warn the * user. */ @@ -511,7 +450,7 @@ tor_check_libevent_header_compatibility(void) verybad = compat1 != compat2; - log(verybad ? LOG_WARN : LOG_NOTICE, + tor_log(verybad ? LOG_WARN : LOG_NOTICE, LD_GENERAL, "We were compiled with headers from version %s " "of Libevent, but we're using a Libevent library that says it's " "version %s.", HEADER_VERSION, event_get_version()); @@ -689,7 +628,25 @@ tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev, } #endif -#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,1,1) +int +tor_init_libevent_rng(void) +{ + int rv = 0; +#ifdef HAVE_EVUTIL_SECURE_RNG_INIT + char buf[256]; + if (evutil_secure_rng_init() < 0) { + rv = -1; + } + /* Older libevent -- manually initialize the RNG */ + crypto_rand(buf, 32); + evutil_secure_rng_add_bytes(buf, 32); + evutil_secure_rng_get_bytes(buf, sizeof(buf)); +#endif + return rv; +} + +#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,1,1) \ + && !defined(TOR_UNIT_TESTS) void tor_gettimeofday_cached(struct timeval *tv) { @@ -722,5 +679,45 @@ tor_gettimeofday_cache_clear(void) { cached_time_hires.tv_sec = 0; } + +#ifdef TOR_UNIT_TESTS +/** For testing: force-update the cached time to a given value. */ +void +tor_gettimeofday_cache_set(const struct timeval *tv) +{ + tor_assert(tv); + memcpy(&cached_time_hires, tv, sizeof(*tv)); +} +#endif #endif +/** + * As tor_gettimeofday_cached, but can never move backwards in time. + * + * The returned value may diverge from wall-clock time, since wall-clock time + * can trivially be adjusted backwards, and this can't. Don't mix wall-clock + * time with these values in the same calculation. + * + * Depending on implementation, this function may or may not "smooth out" huge + * jumps forward in wall-clock time. It may or may not keep its results + * advancing forward (as opposed to stalling) if the wall-clock time goes + * backwards. The current implementation does neither of of these. + * + * This function is not thread-safe; do not call it outside the main thread. + * + * In future versions of Tor, this may return a time does not have its + * origin at the Unix epoch. + */ +void +tor_gettimeofday_cached_monotonic(struct timeval *tv) +{ + struct timeval last_tv = { 0, 0 }; + + tor_gettimeofday_cached(tv); + if (timercmp(tv, &last_tv, <)) { + memcpy(tv, &last_tv, sizeof(struct timeval)); + } else { + memcpy(&last_tv, tv, sizeof(struct timeval)); + } +} + diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 56285ef80..9ee7b49cf 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -1,8 +1,8 @@ -/* Copyright (c) 2009-2012, The Tor Project, Inc. */ +/* Copyright (c) 2009-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -#ifndef _TOR_COMPAT_LIBEVENT_H -#define _TOR_COMPAT_LIBEVENT_H +#ifndef TOR_COMPAT_LIBEVENT_H +#define TOR_COMPAT_LIBEVENT_H #include "orconfig.h" @@ -78,6 +78,7 @@ void tor_check_libevent_version(const char *m, int server, const char **badness_out); void tor_check_libevent_header_compatibility(void); const char *tor_libevent_get_version_str(void); +const char *tor_libevent_get_header_version_str(void); #ifdef USE_BUFFEREVENTS const struct timeval *tor_libevent_get_one_tick_timeout(void); @@ -88,8 +89,14 @@ int tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev, struct bufferevent_rate_limit_group *g); #endif +int tor_init_libevent_rng(void); + 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); +#endif +void tor_gettimeofday_cached_monotonic(struct timeval *tv); #endif diff --git a/src/common/container.c b/src/common/container.c index ede98eca5..b937d544f 100644 --- a/src/common/container.c +++ b/src/common/container.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -163,7 +163,7 @@ smartlist_string_remove(smartlist_t *sl, const char *element) /** Return true iff some element E of sl has E==element. */ int -smartlist_isin(const smartlist_t *sl, const void *element) +smartlist_contains(const smartlist_t *sl, const void *element) { int i; for (i=0; i < sl->num_used; i++) @@ -176,7 +176,7 @@ smartlist_isin(const smartlist_t *sl, const void *element) * !strcmp(E,<b>element</b>) */ int -smartlist_string_isin(const smartlist_t *sl, const char *element) +smartlist_contains_string(const smartlist_t *sl, const char *element) { int i; if (!sl) return 0; @@ -203,7 +203,7 @@ smartlist_string_pos(const smartlist_t *sl, const char *element) * !strcasecmp(E,<b>element</b>) */ int -smartlist_string_isin_case(const smartlist_t *sl, const char *element) +smartlist_contains_string_case(const smartlist_t *sl, const char *element) { int i; if (!sl) return 0; @@ -217,11 +217,11 @@ smartlist_string_isin_case(const smartlist_t *sl, const char *element) * to the decimal encoding of <b>num</b>. */ int -smartlist_string_num_isin(const smartlist_t *sl, int num) +smartlist_contains_int_as_string(const smartlist_t *sl, int num) { char buf[32]; /* long enough for 64-bit int, and then some. */ tor_snprintf(buf,sizeof(buf),"%d", num); - return smartlist_string_isin(sl, buf); + return smartlist_contains_string(sl, buf); } /** Return true iff the two lists contain the same strings in the same @@ -243,11 +243,30 @@ smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2) return 1; } +/** Return true iff the two lists contain the same int pointer values in + * the same order, or if they are both NULL. */ +int +smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2) +{ + if (sl1 == NULL) + return sl2 == NULL; + if (sl2 == NULL) + return 0; + if (smartlist_len(sl1) != smartlist_len(sl2)) + return 0; + SMARTLIST_FOREACH(sl1, int *, cp1, { + int *cp2 = smartlist_get(sl2, cp1_sl_idx); + if (*cp1 != *cp2) + return 0; + }); + return 1; +} + /** Return true iff <b>sl</b> has some element E such that * tor_memeq(E,<b>element</b>,DIGEST_LEN) */ int -smartlist_digest_isin(const smartlist_t *sl, const char *element) +smartlist_contains_digest(const smartlist_t *sl, const char *element) { int i; if (!sl) return 0; @@ -257,19 +276,19 @@ smartlist_digest_isin(const smartlist_t *sl, const char *element) return 0; } -/** Return true iff some element E of sl2 has smartlist_isin(sl1,E). +/** Return true iff some element E of sl2 has smartlist_contains(sl1,E). */ int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2) { int i; for (i=0; i < sl2->num_used; i++) - if (smartlist_isin(sl1, sl2->list[i])) + if (smartlist_contains(sl1, sl2->list[i])) return 1; return 0; } -/** Remove every element E of sl1 such that !smartlist_isin(sl2,E). +/** Remove every element E of sl1 such that !smartlist_contains(sl2,E). * Does not preserve the order of sl1. */ void @@ -277,13 +296,13 @@ smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2) { int i; for (i=0; i < sl1->num_used; i++) - if (!smartlist_isin(sl2, sl1->list[i])) { + if (!smartlist_contains(sl2, sl1->list[i])) { sl1->list[i] = sl1->list[--sl1->num_used]; /* swap with the end */ i--; /* so we process the new i'th element */ } } -/** Remove every element E of sl1 such that smartlist_isin(sl2,E). +/** Remove every element E of sl1 such that smartlist_contains(sl2,E). * Does not preserve the order of sl1. */ void @@ -571,59 +590,116 @@ smartlist_bsearch_idx(const smartlist_t *sl, const void *key, int (*compare)(const void *key, const void **member), int *found_out) { - const int len = smartlist_len(sl); - int hi, lo, cmp, mid; + int hi, lo, cmp, mid, len, diff; + + tor_assert(sl); + tor_assert(compare); + tor_assert(found_out); + len = smartlist_len(sl); + + /* Check for the trivial case of a zero-length list */ if (len == 0) { *found_out = 0; + /* We already know smartlist_len(sl) is 0 in this case */ return 0; - } else if (len == 1) { - cmp = compare(key, (const void **) &sl->list[0]); - if (cmp == 0) { - *found_out = 1; - return 0; - } else if (cmp < 0) { - *found_out = 0; - return 0; - } else { - *found_out = 0; - return 1; - } } - hi = smartlist_len(sl) - 1; + /* Okay, we have a real search to do */ + tor_assert(len > 0); lo = 0; + hi = len - 1; + + /* + * These invariants are always true: + * + * For all i such that 0 <= i < lo, sl[i] < key + * For all i such that hi < i <= len, sl[i] > key + */ while (lo <= hi) { - mid = (lo + hi) / 2; + diff = hi - lo; + /* + * We want mid = (lo + hi) / 2, but that could lead to overflow, so + * instead diff = hi - lo (non-negative because of loop condition), and + * then hi = lo + diff, mid = (lo + lo + diff) / 2 = lo + (diff / 2). + */ + mid = lo + (diff / 2); cmp = compare(key, (const void**) &(sl->list[mid])); - if (cmp>0) { /* key > sl[mid] */ - lo = mid+1; - } else if (cmp<0) { /* key < sl[mid] */ - hi = mid-1; - } else { /* key == sl[mid] */ + if (cmp == 0) { + /* sl[mid] == key; we found it */ *found_out = 1; return mid; - } - } - /* lo > hi. */ - { - tor_assert(lo >= 0); - if (lo < smartlist_len(sl)) { - cmp = compare(key, (const void**) &(sl->list[lo])); + } else if (cmp > 0) { + /* + * key > sl[mid] and an index i such that sl[i] == key must + * have i > mid if it exists. + */ + + /* + * Since lo <= mid <= hi, hi can only decrease on each iteration (by + * being set to mid - 1) and hi is initially len - 1, mid < len should + * always hold, and this is not symmetric with the left end of list + * mid > 0 test below. A key greater than the right end of the list + * should eventually lead to lo == hi == mid == len - 1, and then + * we set lo to len below and fall out to the same exit we hit for + * a key in the middle of the list but not matching. Thus, we just + * assert for consistency here rather than handle a mid == len case. + */ + tor_assert(mid < len); + /* Move lo to the element immediately after sl[mid] */ + lo = mid + 1; + } else { + /* This should always be true in this case */ tor_assert(cmp < 0); - } else if (smartlist_len(sl)) { - cmp = compare(key, (const void**) &(sl->list[smartlist_len(sl)-1])); - tor_assert(cmp > 0); + + /* + * key < sl[mid] and an index i such that sl[i] == key must + * have i < mid if it exists. + */ + + if (mid > 0) { + /* Normal case, move hi to the element immediately before sl[mid] */ + hi = mid - 1; + } else { + /* These should always be true in this case */ + tor_assert(mid == lo); + tor_assert(mid == 0); + /* + * We were at the beginning of the list and concluded that every + * element e compares e > key. + */ + *found_out = 0; + return 0; + } } } + + /* + * lo > hi; we have no element matching key but we have elements falling + * on both sides of it. The lo index points to the first element > key. + */ + tor_assert(lo == hi + 1); /* All other cases should have been handled */ + tor_assert(lo >= 0); + tor_assert(lo <= len); + tor_assert(hi >= 0); + tor_assert(hi <= len); + + if (lo < len) { + cmp = compare(key, (const void **) &(sl->list[lo])); + tor_assert(cmp < 0); + } else { + cmp = compare(key, (const void **) &(sl->list[len-1])); + tor_assert(cmp > 0); + } + *found_out = 0; return lo; } /** Helper: compare two const char **s. */ static int -_compare_string_ptrs(const void **_a, const void **_b) +compare_string_ptrs_(const void **_a, const void **_b) { return strcmp((const char*)*_a, (const char*)*_b); } @@ -633,14 +709,14 @@ _compare_string_ptrs(const void **_a, const void **_b) void smartlist_sort_strings(smartlist_t *sl) { - smartlist_sort(sl, _compare_string_ptrs); + smartlist_sort(sl, compare_string_ptrs_); } /** Return the most frequent string in the sorted list <b>sl</b> */ char * smartlist_get_most_frequent_string(smartlist_t *sl) { - return smartlist_get_most_frequent(sl, _compare_string_ptrs); + return smartlist_get_most_frequent(sl, compare_string_ptrs_); } /** Remove duplicate strings from a sorted list, and free them with tor_free(). @@ -648,7 +724,27 @@ smartlist_get_most_frequent_string(smartlist_t *sl) void smartlist_uniq_strings(smartlist_t *sl) { - smartlist_uniq(sl, _compare_string_ptrs, _tor_free); + smartlist_uniq(sl, compare_string_ptrs_, tor_free_); +} + +/** Helper: compare two pointers. */ +static int +compare_ptrs_(const void **_a, const void **_b) +{ + const void *a = *_a, *b = *_b; + if (a<b) + return -1; + else if (a==b) + return 0; + else + return 1; +} + +/** Sort <b>sl</b> in ascending order of the pointers it contains. */ +void +smartlist_sort_pointers(smartlist_t *sl) +{ + smartlist_sort(sl, compare_ptrs_); } /* Heap-based priority queue implementation for O(lg N) insert and remove. @@ -849,7 +945,7 @@ smartlist_pqueue_assert_ok(smartlist_t *sl, /** Helper: compare two DIGEST_LEN digests. */ static int -_compare_digests(const void **_a, const void **_b) +compare_digests_(const void **_a, const void **_b) { return tor_memcmp((const char*)*_a, (const char*)*_b, DIGEST_LEN); } @@ -858,7 +954,7 @@ _compare_digests(const void **_a, const void **_b) void smartlist_sort_digests(smartlist_t *sl) { - smartlist_sort(sl, _compare_digests); + smartlist_sort(sl, compare_digests_); } /** Remove duplicate digests from a sorted list, and free them with tor_free(). @@ -866,12 +962,12 @@ smartlist_sort_digests(smartlist_t *sl) void smartlist_uniq_digests(smartlist_t *sl) { - smartlist_uniq(sl, _compare_digests, _tor_free); + smartlist_uniq(sl, compare_digests_, tor_free_); } /** Helper: compare two DIGEST256_LEN digests. */ static int -_compare_digests256(const void **_a, const void **_b) +compare_digests256_(const void **_a, const void **_b) { return tor_memcmp((const char*)*_a, (const char*)*_b, DIGEST256_LEN); } @@ -880,7 +976,7 @@ _compare_digests256(const void **_a, const void **_b) void smartlist_sort_digests256(smartlist_t *sl) { - smartlist_sort(sl, _compare_digests256); + smartlist_sort(sl, compare_digests256_); } /** Return the most frequent member of the sorted list of DIGEST256_LEN @@ -888,7 +984,7 @@ smartlist_sort_digests256(smartlist_t *sl) char * smartlist_get_most_frequent_digest256(smartlist_t *sl) { - return smartlist_get_most_frequent(sl, _compare_digests256); + return smartlist_get_most_frequent(sl, compare_digests256_); } /** Remove duplicate 256-bit digests from a sorted list, and free them with @@ -897,7 +993,7 @@ smartlist_get_most_frequent_digest256(smartlist_t *sl) void smartlist_uniq_digests256(smartlist_t *sl) { - smartlist_uniq(sl, _compare_digests256, _tor_free); + smartlist_uniq(sl, compare_digests256_, tor_free_); } /** Helper: Declare an entry type and a map type to implement a mapping using @@ -928,7 +1024,7 @@ strmap_entries_eq(const strmap_entry_t *a, const strmap_entry_t *b) static INLINE unsigned int strmap_entry_hash(const strmap_entry_t *a) { - return ht_string_hash(a->key); + return (unsigned) siphash24g(a->key, strlen(a->key)); } /** Helper: compare digestmap_entry_t objects by key value. */ @@ -942,13 +1038,7 @@ digestmap_entries_eq(const digestmap_entry_t *a, const digestmap_entry_t *b) static INLINE unsigned int digestmap_entry_hash(const digestmap_entry_t *a) { -#if SIZEOF_INT != 8 - const uint32_t *p = (const uint32_t*)a->key; - return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4]; -#else - const uint64_t *p = (const uint64_t*)a->key; - return p[0] ^ p[1]; -#endif + return (unsigned) siphash24g(a->key, DIGEST_LEN); } HT_PROTOTYPE(strmap_impl, strmap_entry_t, node, strmap_entry_hash, diff --git a/src/common/container.h b/src/common/container.h index dab3b83f3..0d31f2093 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -1,12 +1,13 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -#ifndef _TOR_CONTAINER_H -#define _TOR_CONTAINER_H +#ifndef TOR_CONTAINER_H +#define TOR_CONTAINER_H #include "util.h" +#include "siphash.h" /** A resizeable list of pointers, with associated helpful functionality. * @@ -35,13 +36,14 @@ void smartlist_remove(smartlist_t *sl, const void *element); void *smartlist_pop_last(smartlist_t *sl); void smartlist_reverse(smartlist_t *sl); void smartlist_string_remove(smartlist_t *sl, const char *element); -int smartlist_isin(const smartlist_t *sl, const void *element); -int smartlist_string_isin(const smartlist_t *sl, const char *element); +int smartlist_contains(const smartlist_t *sl, const void *element); +int smartlist_contains_string(const smartlist_t *sl, const char *element); int smartlist_string_pos(const smartlist_t *, const char *elt); -int smartlist_string_isin_case(const smartlist_t *sl, const char *element); -int smartlist_string_num_isin(const smartlist_t *sl, int num); +int smartlist_contains_string_case(const smartlist_t *sl, const char *element); +int smartlist_contains_int_as_string(const smartlist_t *sl, int num); int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2); -int smartlist_digest_isin(const smartlist_t *sl, const char *element); +int smartlist_contains_digest(const smartlist_t *sl, const char *element); +int smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2); int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2); void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2); void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); @@ -101,6 +103,7 @@ void smartlist_uniq(smartlist_t *sl, void smartlist_sort_strings(smartlist_t *sl); void smartlist_sort_digests(smartlist_t *sl); void smartlist_sort_digests256(smartlist_t *sl); +void smartlist_sort_pointers(smartlist_t *sl); char *smartlist_get_most_frequent_string(smartlist_t *sl); char *smartlist_get_most_frequent_digest256(smartlist_t *sl); @@ -471,64 +474,74 @@ void* strmap_remove_lc(strmap_t *map, const char *key); #define DECLARE_TYPED_DIGESTMAP_FNS(prefix, maptype, valtype) \ typedef struct maptype maptype; \ typedef struct prefix##iter_t prefix##iter_t; \ - static INLINE maptype* prefix##new(void) \ + ATTR_UNUSED static INLINE maptype* \ + prefix##new(void) \ { \ return (maptype*)digestmap_new(); \ } \ - static INLINE digestmap_t* prefix##to_digestmap(maptype *map) \ + ATTR_UNUSED static INLINE digestmap_t* \ + prefix##to_digestmap(maptype *map) \ { \ return (digestmap_t*)map; \ } \ - static INLINE valtype* prefix##get(maptype *map, const char *key) \ + ATTR_UNUSED static INLINE valtype* \ + prefix##get(maptype *map, const char *key) \ { \ return (valtype*)digestmap_get((digestmap_t*)map, key); \ } \ - static INLINE valtype* prefix##set(maptype *map, const char *key, \ - valtype *val) \ + ATTR_UNUSED static INLINE valtype* \ + prefix##set(maptype *map, const char *key, valtype *val) \ { \ return (valtype*)digestmap_set((digestmap_t*)map, key, val); \ } \ - static INLINE valtype* prefix##remove(maptype *map, const char *key) \ + ATTR_UNUSED static INLINE valtype* \ + prefix##remove(maptype *map, const char *key) \ { \ return (valtype*)digestmap_remove((digestmap_t*)map, key); \ } \ - static INLINE void prefix##free(maptype *map, void (*free_val)(void*)) \ + ATTR_UNUSED static INLINE void \ + prefix##free(maptype *map, void (*free_val)(void*)) \ { \ digestmap_free((digestmap_t*)map, free_val); \ } \ - static INLINE int prefix##isempty(maptype *map) \ + ATTR_UNUSED static INLINE int \ + prefix##isempty(maptype *map) \ { \ return digestmap_isempty((digestmap_t*)map); \ } \ - static INLINE int prefix##size(maptype *map) \ + ATTR_UNUSED static INLINE int \ + prefix##size(maptype *map) \ { \ return digestmap_size((digestmap_t*)map); \ } \ - static INLINE prefix##iter_t *prefix##iter_init(maptype *map) \ + ATTR_UNUSED static INLINE \ + prefix##iter_t *prefix##iter_init(maptype *map) \ { \ return (prefix##iter_t*) digestmap_iter_init((digestmap_t*)map); \ } \ - static INLINE prefix##iter_t *prefix##iter_next(maptype *map, \ - prefix##iter_t *iter) \ + ATTR_UNUSED static INLINE \ + prefix##iter_t *prefix##iter_next(maptype *map, prefix##iter_t *iter) \ { \ return (prefix##iter_t*) digestmap_iter_next( \ (digestmap_t*)map, (digestmap_iter_t*)iter); \ } \ - static INLINE prefix##iter_t *prefix##iter_next_rmv(maptype *map, \ - prefix##iter_t *iter) \ + ATTR_UNUSED static INLINE prefix##iter_t* \ + prefix##iter_next_rmv(maptype *map, prefix##iter_t *iter) \ { \ return (prefix##iter_t*) digestmap_iter_next_rmv( \ (digestmap_t*)map, (digestmap_iter_t*)iter); \ } \ - static INLINE void prefix##iter_get(prefix##iter_t *iter, \ - const char **keyp, \ - valtype **valp) \ + ATTR_UNUSED static INLINE void \ + prefix##iter_get(prefix##iter_t *iter, \ + const char **keyp, \ + valtype **valp) \ { \ void *v; \ digestmap_iter_get((digestmap_iter_t*) iter, keyp, &v); \ *valp = v; \ } \ - static INLINE int prefix##iter_done(prefix##iter_t *iter) \ + ATTR_UNUSED static INLINE int \ + prefix##iter_done(prefix##iter_t *iter) \ { \ return digestmap_iter_done((digestmap_iter_t*)iter); \ } @@ -609,11 +622,11 @@ typedef struct { static INLINE void digestset_add(digestset_t *set, const char *digest) { - const uint32_t *p = (const uint32_t *)digest; - const uint32_t d1 = p[0] + (p[1]>>16); - const uint32_t d2 = p[1] + (p[2]>>16); - const uint32_t d3 = p[2] + (p[3]>>16); - const uint32_t d4 = p[3] + (p[0]>>16); + const uint64_t x = siphash24g(digest, 20); + const uint32_t d1 = (uint32_t) x; + const uint32_t d2 = (uint32_t)( (x>>16) + x); + const uint32_t d3 = (uint32_t)( (x>>32) + x); + const uint32_t d4 = (uint32_t)( (x>>48) + x); bitarray_set(set->ba, BIT(d1)); bitarray_set(set->ba, BIT(d2)); bitarray_set(set->ba, BIT(d3)); @@ -623,13 +636,13 @@ digestset_add(digestset_t *set, const char *digest) /** If <b>digest</b> is in <b>set</b>, return nonzero. Otherwise, * <em>probably</em> return zero. */ static INLINE int -digestset_isin(const digestset_t *set, const char *digest) +digestset_contains(const digestset_t *set, const char *digest) { - const uint32_t *p = (const uint32_t *)digest; - const uint32_t d1 = p[0] + (p[1]>>16); - const uint32_t d2 = p[1] + (p[2]>>16); - const uint32_t d3 = p[2] + (p[3]>>16); - const uint32_t d4 = p[3] + (p[0]>>16); + const uint64_t x = siphash24g(digest, 20); + const uint32_t d1 = (uint32_t) x; + const uint32_t d2 = (uint32_t)( (x>>16) + x); + const uint32_t d3 = (uint32_t)( (x>>32) + x); + const uint32_t d4 = (uint32_t)( (x>>48) + x); return bitarray_is_set(set->ba, BIT(d1)) && bitarray_is_set(set->ba, BIT(d2)) && bitarray_is_set(set->ba, BIT(d3)) && @@ -675,11 +688,6 @@ median_int32(int32_t *array, int n_elements) { return find_nth_int32(array, n_elements, (n_elements-1)/2); } -static INLINE long -median_long(long *array, int n_elements) -{ - return find_nth_long(array, n_elements, (n_elements-1)/2); -} #endif diff --git a/src/common/crypto.c b/src/common/crypto.c index 30990ecc8..a247a87d4 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -56,9 +56,10 @@ #include "../common/util.h" #include "container.h" #include "compat.h" +#include "sandbox.h" -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,7) -#error "We require OpenSSL >= 0.9.7" +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) +#error "We require OpenSSL >= 0.9.8" #endif #ifdef ANDROID @@ -69,31 +70,6 @@ /** Longest recognized */ #define MAX_DNS_LABEL_SIZE 63 -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) && \ - !defined(RUNNING_DOXYGEN) -/** @{ */ -/** On OpenSSL versions before 0.9.8, there is no working SHA256 - * implementation, so we use Tom St Denis's nice speedy one, slightly adapted - * to our needs. These macros make it usable by us. */ -#define SHA256_CTX sha256_state -#define SHA256_Init sha256_init -#define SHA256_Update sha256_process -#define LTC_ARGCHK(x) tor_assert(x) -/** @} */ -#include "sha256.c" -#define SHA256_Final(a,b) sha256_done(b,a) - -static unsigned char * -SHA256(const unsigned char *m, size_t len, unsigned char *d) -{ - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, m, len); - SHA256_Final(d, &ctx); - return d; -} -#endif - /** Macro: is k a valid RSA public or private key? */ #define PUBLIC_KEY_OK(k) ((k) && (k)->key && (k)->key->n) /** Macro: is k a valid RSA private key? */ @@ -101,9 +77,9 @@ SHA256(const unsigned char *m, size_t len, unsigned char *d) #ifdef TOR_IS_MULTITHREADED /** A number of preallocated mutexes for use by OpenSSL. */ -static tor_mutex_t **_openssl_mutexes = NULL; +static tor_mutex_t **openssl_mutexes_ = NULL; /** How many mutexes have we allocated for use by OpenSSL? */ -static int _n_openssl_mutexes = 0; +static int n_openssl_mutexes_ = 0; #endif /** A public key, or a public/private key-pair. */ @@ -138,8 +114,7 @@ crypto_get_rsa_padding_overhead(int padding) { switch (padding) { - case RSA_PKCS1_OAEP_PADDING: return 42; - case RSA_PKCS1_PADDING: return 11; + case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD; default: tor_assert(0); return -1; } } @@ -151,14 +126,16 @@ crypto_get_rsa_padding(int padding) { switch (padding) { - case PK_PKCS1_PADDING: return RSA_PKCS1_PADDING; case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING; default: tor_assert(0); return -1; } } /** Boolean: has OpenSSL's crypto been initialized? */ -static int _crypto_global_initialized = 0; +static int crypto_early_initialized_ = 0; + +/** Boolean: has OpenSSL's crypto been initialized? */ +static int crypto_global_initialized_ = 0; /** Log all pending crypto errors at level <b>severity</b>. Use * <b>doing</b> to describe our current activities. @@ -176,10 +153,11 @@ crypto_log_errors(int severity, const char *doing) if (!lib) lib = "(null)"; if (!func) func = "(null)"; if (doing) { - log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)", - doing, msg, lib, func); + tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)", + doing, msg, lib, func); } else { - log(severity, LD_CRYPTO, "crypto error: %s (in %s:%s)", msg, lib, func); + tor_log(severity, LD_CRYPTO, "crypto error: %s (in %s:%s)", + msg, lib, func); } } } @@ -193,10 +171,10 @@ log_engine(const char *fn, ENGINE *e) const char *name, *id; name = ENGINE_get_name(e); id = ENGINE_get_id(e); - log(LOG_NOTICE, LD_CRYPTO, "Using OpenSSL engine %s [%s] for %s", - name?name:"?", id?id:"?", fn); + log_notice(LD_CRYPTO, "Default OpenSSL engine for %s is %s [%s]", + fn, name?name:"?", id?id:"?"); } else { - log(LOG_INFO, LD_CRYPTO, "Using default implementation for %s", fn); + log_info(LD_CRYPTO, "Using default implementation for %s", fn); } } #endif @@ -221,16 +199,136 @@ try_load_engine(const char *path, const char *engine) } #endif +/* Returns a trimmed and human-readable version of an openssl version string +* <b>raw_version</b>. They are usually in the form of 'OpenSSL 1.0.0b 10 +* May 2012' and this will parse them into a form similar to '1.0.0b' */ +static char * +parse_openssl_version_str(const char *raw_version) +{ + const char *end_of_version = NULL; + /* The output should be something like "OpenSSL 1.0.0b 10 May 2012. Let's + trim that down. */ + if (!strcmpstart(raw_version, "OpenSSL ")) { + raw_version += strlen("OpenSSL "); + end_of_version = strchr(raw_version, ' '); + } + + if (end_of_version) + return tor_strndup(raw_version, + end_of_version-raw_version); + else + return tor_strdup(raw_version); +} + +static char *crypto_openssl_version_str = NULL; +/* Return a human-readable version of the run-time openssl version number. */ +const char * +crypto_openssl_get_version_str(void) +{ + if (crypto_openssl_version_str == NULL) { + const char *raw_version = SSLeay_version(SSLEAY_VERSION); + crypto_openssl_version_str = parse_openssl_version_str(raw_version); + } + return crypto_openssl_version_str; +} + +static char *crypto_openssl_header_version_str = NULL; +/* Return a human-readable version of the compile-time openssl version +* number. */ +const char * +crypto_openssl_get_header_version_str(void) +{ + if (crypto_openssl_header_version_str == NULL) { + crypto_openssl_header_version_str = + parse_openssl_version_str(OPENSSL_VERSION_TEXT); + } + return crypto_openssl_header_version_str; +} + +/** Make sure that openssl is using its default PRNG. Return 1 if we had to + * adjust it; 0 otherwise. */ +static int +crypto_force_rand_ssleay(void) +{ + if (RAND_get_rand_method() != RAND_SSLeay()) { + log_notice(LD_CRYPTO, "It appears that one of our engines has provided " + "a replacement the OpenSSL RNG. Resetting it to the default " + "implementation."); + RAND_set_rand_method(RAND_SSLeay()); + return 1; + } + return 0; +} + +/** Set up the siphash key if we haven't already done so. */ +int +crypto_init_siphash_key(void) +{ + static int have_seeded_siphash = 0; + struct sipkey key; + if (have_seeded_siphash) + return 0; + + if (crypto_rand((char*) &key, sizeof(key)) < 0) + return -1; + siphash_set_global_key(&key); + have_seeded_siphash = 1; + return 0; +} + /** Initialize the crypto library. Return 0 on success, -1 on failure. */ int -crypto_global_init(int useAccel, const char *accelName, const char *accelDir) +crypto_early_init(void) { - if (!_crypto_global_initialized) { + if (!crypto_early_initialized_) { + + crypto_early_initialized_ = 1; + ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); - _crypto_global_initialized = 1; + setup_openssl_threading(); + + if (SSLeay() == OPENSSL_VERSION_NUMBER && + !strcmp(SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_TEXT)) { + log_info(LD_CRYPTO, "OpenSSL version matches version from headers " + "(%lx: %s).", SSLeay(), SSLeay_version(SSLEAY_VERSION)); + } else { + log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the " + "version we're running with. If you get weird crashes, that " + "might be why. (Compiled with %lx: %s; running with %lx: %s).", + (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT, + SSLeay(), SSLeay_version(SSLEAY_VERSION)); + } + + if (SSLeay() < OPENSSL_V_SERIES(1,0,0)) { + log_notice(LD_CRYPTO, + "Your OpenSSL version seems to be %s. We recommend 1.0.0 " + "or later.", + crypto_openssl_get_version_str()); + } + + crypto_force_rand_ssleay(); + + if (crypto_seed_rng(1) < 0) + return -1; + if (crypto_init_siphash_key() < 0) + return -1; + } + return 0; +} + +/** Initialize the crypto library. Return 0 on success, -1 on failure. + */ +int +crypto_global_init(int useAccel, const char *accelName, const char *accelDir) +{ + if (!crypto_global_initialized_) { + crypto_early_init(); + + crypto_global_initialized_ = 1; + if (useAccel > 0) { #ifdef DISABLE_ENGINES (void)accelName; @@ -266,21 +364,41 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) " setting default ciphers."); ENGINE_set_default(e, ENGINE_METHOD_ALL); } + /* Log, if available, the intersection of the set of algorithms + used by Tor and the set of algorithms available in the engine */ log_engine("RSA", ENGINE_get_default_RSA()); log_engine("DH", ENGINE_get_default_DH()); + log_engine("ECDH", ENGINE_get_default_ECDH()); + log_engine("ECDSA", ENGINE_get_default_ECDSA()); log_engine("RAND", ENGINE_get_default_RAND()); + log_engine("RAND (which we will not use)", ENGINE_get_default_RAND()); log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1)); - log_engine("3DES", ENGINE_get_cipher_engine(NID_des_ede3_ecb)); - log_engine("AES", ENGINE_get_cipher_engine(NID_aes_128_ecb)); + log_engine("3DES-CBC", ENGINE_get_cipher_engine(NID_des_ede3_cbc)); + log_engine("AES-128-ECB", ENGINE_get_cipher_engine(NID_aes_128_ecb)); + log_engine("AES-128-CBC", ENGINE_get_cipher_engine(NID_aes_128_cbc)); +#ifdef NID_aes_128_ctr + log_engine("AES-128-CTR", ENGINE_get_cipher_engine(NID_aes_128_ctr)); +#endif +#ifdef NID_aes_128_gcm + log_engine("AES-128-GCM", ENGINE_get_cipher_engine(NID_aes_128_gcm)); +#endif + log_engine("AES-256-CBC", ENGINE_get_cipher_engine(NID_aes_256_cbc)); +#ifdef NID_aes_256_gcm + log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm)); +#endif + #endif } else { log_info(LD_CRYPTO, "NOT using OpenSSL engine support."); } + if (crypto_force_rand_ssleay()) { + if (crypto_seed_rng(1) < 0) + return -1; + } + evaluate_evp_for_aes(-1); evaluate_ctr_for_aes(); - - return crypto_seed_rng(1); } return 0; } @@ -294,7 +412,7 @@ crypto_thread_cleanup(void) /** used by tortls.c: wrap an RSA* in a crypto_pk_t. */ crypto_pk_t * -_crypto_new_pk_from_rsa(RSA *rsa) +crypto_new_pk_from_rsa_(RSA *rsa) { crypto_pk_t *env; tor_assert(rsa); @@ -307,7 +425,7 @@ _crypto_new_pk_from_rsa(RSA *rsa) /** Helper, used by tor-checkkey.c and tor-gencert.c. Return the RSA from a * crypto_pk_t. */ RSA * -_crypto_pk_get_rsa(crypto_pk_t *env) +crypto_pk_get_rsa_(crypto_pk_t *env) { return env->key; } @@ -315,7 +433,7 @@ _crypto_pk_get_rsa(crypto_pk_t *env) /** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff * private is set, include the private-key portion of the key. */ EVP_PKEY * -_crypto_pk_get_evp_pkey(crypto_pk_t *env, int private) +crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private) { RSA *key = NULL; EVP_PKEY *pkey = NULL; @@ -343,7 +461,7 @@ _crypto_pk_get_evp_pkey(crypto_pk_t *env, int private) /** Used by tortls.c: Get the DH* from a crypto_dh_t. */ DH * -_crypto_dh_get_dh(crypto_dh_t *dh) +crypto_dh_get_dh_(crypto_dh_t *dh) { return dh->dh; } @@ -358,7 +476,7 @@ crypto_pk_new(void) rsa = RSA_new(); tor_assert(rsa); - return _crypto_new_pk_from_rsa(rsa); + return crypto_new_pk_from_rsa_(rsa); } /** Release a reference to an asymmetric key; when all the references @@ -441,11 +559,7 @@ crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits) if (env->key) RSA_free(env->key); -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) - /* In OpenSSL 0.9.7, RSA_generate_key is all we have. */ - env->key = RSA_generate_key(bits, 65537, NULL, NULL); -#else - /* In OpenSSL 0.9.8, RSA_generate_key is deprecated. */ + { BIGNUM *e = BN_new(); RSA *r = NULL; @@ -463,11 +577,11 @@ crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits) r = NULL; done: if (e) - BN_free(e); + BN_clear_free(e); if (r) RSA_free(r); - } -#endif + } + if (!env->key) { crypto_log_errors(LOG_WARN, "generating RSA key"); return -1; @@ -711,19 +825,23 @@ crypto_pk_public_exponent_ok(crypto_pk_t *env) return BN_is_word(env->key->e, 65537); } -/** Compare the public-key components of a and b. Return -1 if a\<b, 0 - * if a==b, and 1 if a\>b. +/** Compare the public-key components of a and b. Return less than 0 + * if a\<b, 0 if a==b, and greater than 0 if a\>b. A NULL key is + * considered to be less than all non-NULL keys, and equal to itself. + * + * Note that this may leak information about the keys through timing. */ int crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b) { int result; + char a_is_non_null = (a != NULL) && (a->key != NULL); + char b_is_non_null = (b != NULL) && (b->key != NULL); + char an_argument_is_null = !a_is_non_null | !b_is_non_null; - if (!a || !b) - return -1; - - if (!a->key || !b->key) - return -1; + result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null)); + if (an_argument_is_null) + return result; tor_assert(PUBLIC_KEY_OK(a)); tor_assert(PUBLIC_KEY_OK(b)); @@ -733,6 +851,18 @@ crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b) return BN_cmp((a->key)->e, (b->key)->e); } +/** Compare the public-key components of a and b. Return non-zero iff + * a==b. A NULL key is considered to be distinct from all non-NULL + * keys, and equal to itself. + * + * Note that this may leak information about the keys through timing. + */ +int +crypto_pk_eq_keys(crypto_pk_t *a, crypto_pk_t *b) +{ + return (crypto_pk_cmp_keys(a, b) == 0); +} + /** Return the size of the public key modulus in <b>env</b>, in bytes. */ size_t crypto_pk_keysize(crypto_pk_t *env) @@ -791,7 +921,7 @@ crypto_pk_copy_full(crypto_pk_t *env) return NULL; } - return _crypto_new_pk_from_rsa(new_key); + return crypto_new_pk_from_rsa_(new_key); } /** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key @@ -1122,22 +1252,21 @@ int crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len) { int len; - unsigned char *buf, *cp; - len = i2d_RSAPublicKey(pk->key, NULL); - if (len < 0 || (size_t)len > dest_len || dest_len > SIZE_T_CEILING) + unsigned char *buf = NULL; + + len = i2d_RSAPublicKey(pk->key, &buf); + if (len < 0 || buf == NULL) return -1; - cp = buf = tor_malloc(len+1); - len = i2d_RSAPublicKey(pk->key, &cp); - if (len < 0) { - crypto_log_errors(LOG_WARN,"encoding public key"); - tor_free(buf); + + if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) { + OPENSSL_free(buf); return -1; } /* We don't encode directly into 'dest', because that would be illegal * type-punning. (C99 is smarter than me, C99 is smarter than me...) */ memcpy(dest,buf,len); - tor_free(buf); + OPENSSL_free(buf); return len; } @@ -1158,7 +1287,7 @@ crypto_pk_asn1_decode(const char *str, size_t len) crypto_log_errors(LOG_WARN,"decoding public key"); return NULL; } - return _crypto_new_pk_from_rsa(rsa); + return crypto_new_pk_from_rsa_(rsa); } /** Given a private or public key <b>pk</b>, put a SHA1 hash of the @@ -1168,24 +1297,17 @@ crypto_pk_asn1_decode(const char *str, size_t len) int crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out) { - unsigned char *buf, *bufp; + unsigned char *buf = NULL; int len; - len = i2d_RSAPublicKey(pk->key, NULL); - if (len < 0) + len = i2d_RSAPublicKey(pk->key, &buf); + if (len < 0 || buf == NULL) return -1; - buf = bufp = tor_malloc(len+1); - len = i2d_RSAPublicKey(pk->key, &bufp); - if (len < 0) { - crypto_log_errors(LOG_WARN,"encoding public key"); - tor_free(buf); - return -1; - } if (crypto_digest(digest_out, (char*)buf, len) < 0) { - tor_free(buf); + OPENSSL_free(buf); return -1; } - tor_free(buf); + OPENSSL_free(buf); return 0; } @@ -1194,31 +1316,24 @@ crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out) int crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out) { - unsigned char *buf, *bufp; + unsigned char *buf = NULL; int len; - len = i2d_RSAPublicKey(pk->key, NULL); - if (len < 0) + len = i2d_RSAPublicKey(pk->key, &buf); + if (len < 0 || buf == NULL) return -1; - buf = bufp = tor_malloc(len+1); - len = i2d_RSAPublicKey(pk->key, &bufp); - if (len < 0) { - crypto_log_errors(LOG_WARN,"encoding public key"); - tor_free(buf); - return -1; - } if (crypto_digest_all(digests_out, (char*)buf, len) < 0) { - tor_free(buf); + OPENSSL_free(buf); return -1; } - tor_free(buf); + OPENSSL_free(buf); return 0; } /** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces * every four spaces. */ -/* static */ void -add_spaces_to_fp(char *out, size_t outlen, const char *in) +void +crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in) { int n = 0; char *end = out+outlen; @@ -1255,28 +1370,33 @@ crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space) } base16_encode(hexdigest,sizeof(hexdigest),digest,DIGEST_LEN); if (add_space) { - add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest); + crypto_add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest); } else { strncpy(fp_out, hexdigest, HEX_DIGEST_LEN+1); } return 0; } -/** Return true iff <b>s</b> is in the correct format for a fingerprint. +/** Given a private or public key <b>pk</b>, put a hashed fingerprint of + * the public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1 + * bytes of space). Return 0 on success, -1 on failure. + * + * Hashed fingerprints are computed as the SHA1 digest of the SHA1 digest + * of the ASN.1 encoding of the public key, converted to hexadecimal, in + * upper case. */ int -crypto_pk_check_fingerprint_syntax(const char *s) +crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out) { - int i; - for (i = 0; i < FINGERPRINT_LEN; ++i) { - if ((i%5) == 4) { - if (!TOR_ISSPACE(s[i])) return 0; - } else { - if (!TOR_ISXDIGIT(s[i])) return 0; - } + char digest[DIGEST_LEN], hashed_digest[DIGEST_LEN]; + if (crypto_pk_get_digest(pk, digest)) { + return -1; + } + if (crypto_digest(hashed_digest, digest, DIGEST_LEN)) { + return -1; } - if (s[FINGERPRINT_LEN]) return 0; - return 1; + base16_encode(fp_out, FINGERPRINT_LEN + 1, hashed_digest, DIGEST_LEN); + return 0; } /* symmetric crypto */ @@ -1427,7 +1547,7 @@ crypto_digest256(char *digest, const char *m, size_t len, int crypto_digest_all(digests_t *ds_out, const char *m, size_t len) { - digest_algorithm_t i; + int i; tor_assert(ds_out); memset(ds_out, 0, sizeof(*ds_out)); if (crypto_digest(ds_out->d[DIGEST_SHA1], m, len) < 0) @@ -1474,7 +1594,7 @@ struct crypto_digest_t { SHA256_CTX sha2; /**< state for SHA256 */ } d; /**< State for the digest we're using. Only one member of the * union is usable, depending on the value of <b>algorithm</b>. */ - digest_algorithm_t algorithm : 8; /**< Which algorithm is in use? */ + digest_algorithm_bitfield_t algorithm : 8; /**< Which algorithm is in use? */ }; /** Allocate and return a new digest object to compute SHA1 digests. @@ -1599,19 +1719,27 @@ crypto_digest_assign(crypto_digest_t *into, memcpy(into,from,sizeof(crypto_digest_t)); } -/** Compute the HMAC-SHA-1 of the <b>msg_len</b> bytes in <b>msg</b>, using - * the <b>key</b> of length <b>key_len</b>. Store the DIGEST_LEN-byte result - * in <b>hmac_out</b>. - */ +/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest + * at <b>digest_out</b> to the hash of the concatenation of those strings, + * plus the optional string <b>append</b>, computed with the algorithm + * <b>alg</b>. + * <b>out_len</b> must be \<= DIGEST256_LEN. */ void -crypto_hmac_sha1(char *hmac_out, - const char *key, size_t key_len, - const char *msg, size_t msg_len) +crypto_digest_smartlist(char *digest_out, size_t len_out, + const smartlist_t *lst, const char *append, + digest_algorithm_t alg) { - tor_assert(key_len < INT_MAX); - tor_assert(msg_len < INT_MAX); - HMAC(EVP_sha1(), key, (int)key_len, (unsigned char*)msg, (int)msg_len, - (unsigned char*)hmac_out, NULL); + crypto_digest_t *d; + if (alg == DIGEST_SHA1) + d = crypto_digest_new(); + else + d = crypto_digest256_new(alg); + SMARTLIST_FOREACH(lst, const char *, cp, + crypto_digest_add_bytes(d, cp, strlen(cp))); + if (append) + crypto_digest_add_bytes(d, append, strlen(append)); + crypto_digest_get_digest(d, digest_out, len_out); + crypto_digest_free(d); } /** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using @@ -1623,63 +1751,11 @@ crypto_hmac_sha256(char *hmac_out, const char *key, size_t key_len, const char *msg, size_t msg_len) { -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(0,9,8) /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */ tor_assert(key_len < INT_MAX); tor_assert(msg_len < INT_MAX); HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len, (unsigned char*)hmac_out, NULL); -#else - /* OpenSSL doesn't have an EVP implementation for SHA256. We'll need - to do HMAC on our own. - - HMAC isn't so hard: To compute HMAC(key, msg): - 1. If len(key) > blocksize, key = H(key). - 2. If len(key) < blocksize, right-pad key up to blocksize with 0 bytes. - 3. let ipad = key xor 0x363636363636....36 - let opad = key xor 0x5c5c5c5c5c5c....5c - The result is H(opad | H( ipad | msg ) ) - */ -#define BLOCKSIZE 64 -#define DIGESTSIZE 32 - uint8_t k[BLOCKSIZE]; - uint8_t pad[BLOCKSIZE]; - uint8_t d[DIGESTSIZE]; - int i; - SHA256_CTX st; - - tor_assert(key_len < INT_MAX); - tor_assert(msg_len < INT_MAX); - - if (key_len <= BLOCKSIZE) { - memset(k, 0, sizeof(k)); - memcpy(k, key, key_len); /* not time invariant in key_len */ - } else { - SHA256((const uint8_t *)key, key_len, k); - memset(k+DIGESTSIZE, 0, sizeof(k)-DIGESTSIZE); - } - for (i = 0; i < BLOCKSIZE; ++i) - pad[i] = k[i] ^ 0x36; - SHA256_Init(&st); - SHA256_Update(&st, pad, BLOCKSIZE); - SHA256_Update(&st, (uint8_t*)msg, msg_len); - SHA256_Final(d, &st); - - for (i = 0; i < BLOCKSIZE; ++i) - pad[i] = k[i] ^ 0x5c; - SHA256_Init(&st); - SHA256_Update(&st, pad, BLOCKSIZE); - SHA256_Update(&st, d, DIGESTSIZE); - SHA256_Final((uint8_t*)hmac_out, &st); - - /* Now clear everything. */ - memwipe(k, 0, sizeof(k)); - memwipe(pad, 0, sizeof(pad)); - memwipe(d, 0, sizeof(d)); - memwipe(&st, 0, sizeof(st)); -#undef BLOCKSIZE -#undef DIGESTSIZE -#endif } /* DH */ @@ -1734,7 +1810,7 @@ crypto_store_dynamic_dh_modulus(const char *fname) { int len, new_len; DH *dh = NULL; - unsigned char *dh_string_repr = NULL, *cp = NULL; + unsigned char *dh_string_repr = NULL; char *base64_encoded_dh = NULL; char *file_string = NULL; int retval = -1; @@ -1758,15 +1834,8 @@ crypto_store_dynamic_dh_modulus(const char *fname) if (!BN_set_word(dh->g, DH_GENERATOR)) goto done; - len = i2d_DHparams(dh, NULL); - if (len < 0) { - log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (1)."); - goto done; - } - - cp = dh_string_repr = tor_malloc_zero(len+1); - len = i2d_DHparams(dh, &cp); - if ((len < 0) || ((cp - dh_string_repr) != len)) { + len = i2d_DHparams(dh, &dh_string_repr); + if ((len < 0) || (dh_string_repr == NULL)) { log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2)."); goto done; } @@ -1793,7 +1862,8 @@ crypto_store_dynamic_dh_modulus(const char *fname) done: if (dh) DH_free(dh); - tor_free(dh_string_repr); + if (dh_string_repr) + OPENSSL_free(dh_string_repr); tor_free(base64_encoded_dh); tor_free(file_string); @@ -1929,7 +1999,7 @@ crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname) /* If the space is occupied, free the previous TLS DH prime */ if (dh_param_p_tls) { - BN_free(dh_param_p_tls); + BN_clear_free(dh_param_p_tls); dh_param_p_tls = NULL; } @@ -2057,6 +2127,16 @@ crypto_dh_new(int dh_type) return NULL; } +/** Return a copy of <b>dh</b>, sharing its internal state. */ +crypto_dh_t * +crypto_dh_dup(const crypto_dh_t *dh) +{ + crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t)); + dh_new->dh = dh->dh; + DH_up_ref(dh->dh); + return dh_new; +} + /** Return the length of the DH key in <b>dh</b>, in bytes. */ int @@ -2081,8 +2161,8 @@ crypto_dh_generate_public(crypto_dh_t *dh) log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-" "the-universe chances really do happen. Trying again."); /* Free and clear the keys, so OpenSSL will actually try again. */ - BN_free(dh->dh->pub_key); - BN_free(dh->dh->priv_key); + BN_clear_free(dh->dh->pub_key); + BN_clear_free(dh->dh->priv_key); dh->dh->pub_key = dh->dh->priv_key = NULL; goto again; } @@ -2144,10 +2224,10 @@ tor_check_dh_key(int severity, BIGNUM *bn) log_fn(severity, LD_CRYPTO, "DH key must be at most p-2."); goto err; } - BN_free(x); + BN_clear_free(x); return 0; err: - BN_free(x); + BN_clear_free(x); s = BN_bn2hex(bn); log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s); OPENSSL_free(s); @@ -2195,8 +2275,8 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh, goto error; } secret_len = result; - if (crypto_expand_key_material(secret_tmp, secret_len, - secret_out, secret_bytes_out)<0) + if (crypto_expand_key_material_TAP((uint8_t*)secret_tmp, secret_len, + (uint8_t*)secret_out, secret_bytes_out)<0) goto error; secret_len = secret_bytes_out; @@ -2206,7 +2286,7 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh, done: crypto_log_errors(LOG_WARN, "completing DH handshake"); if (pubkey_bn) - BN_free(pubkey_bn); + BN_clear_free(pubkey_bn); if (secret_tmp) { memwipe(secret_tmp, 0, secret_tmp_len); tor_free(secret_tmp); @@ -2222,15 +2302,18 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh, * <b>key_out</b> by taking the first <b>key_out_len</b> bytes of * H(K | [00]) | H(K | [01]) | .... * + * This is the key expansion algorithm used in the "TAP" circuit extension + * mechanism; it shouldn't be used for new protocols. + * * Return 0 on success, -1 on failure. */ int -crypto_expand_key_material(const char *key_in, size_t key_in_len, - char *key_out, size_t key_out_len) +crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len, + uint8_t *key_out, size_t key_out_len) { int i; - char *cp, *tmp = tor_malloc(key_in_len+1); - char digest[DIGEST_LEN]; + uint8_t *cp, *tmp = tor_malloc(key_in_len+1); + uint8_t digest[DIGEST_LEN]; /* If we try to get more than this amount of key data, we'll repeat blocks.*/ tor_assert(key_out_len <= DIGEST_LEN*256); @@ -2239,7 +2322,7 @@ crypto_expand_key_material(const char *key_in, size_t key_in_len, for (cp = key_out, i=0; cp < key_out+key_out_len; ++i, cp += DIGEST_LEN) { tmp[key_in_len] = i; - if (crypto_digest(digest, tmp, key_in_len+1)) + if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1)) goto err; memcpy(cp, digest, MIN(DIGEST_LEN, key_out_len-(cp-key_out))); } @@ -2255,6 +2338,65 @@ crypto_expand_key_material(const char *key_in, size_t key_in_len, return -1; } +/** Expand some secret key material according to RFC5869, using SHA256 as the + * underlying hash. The <b>key_in_len</b> bytes at <b>key_in</b> are the + * secret key material; the <b>salt_in_len</b> bytes at <b>salt_in</b> and the + * <b>info_in_len</b> bytes in <b>info_in_len</b> are the algorithm's "salt" + * and "info" parameters respectively. On success, write <b>key_out_len</b> + * bytes to <b>key_out</b> and return 0. On failure, return -1. + */ +int +crypto_expand_key_material_rfc5869_sha256( + const uint8_t *key_in, size_t key_in_len, + const uint8_t *salt_in, size_t salt_in_len, + const uint8_t *info_in, size_t info_in_len, + uint8_t *key_out, size_t key_out_len) +{ + uint8_t prk[DIGEST256_LEN]; + uint8_t tmp[DIGEST256_LEN + 128 + 1]; + uint8_t mac[DIGEST256_LEN]; + int i; + uint8_t *outp; + size_t tmp_len; + + crypto_hmac_sha256((char*)prk, + (const char*)salt_in, salt_in_len, + (const char*)key_in, key_in_len); + + /* If we try to get more than this amount of key data, we'll repeat blocks.*/ + tor_assert(key_out_len <= DIGEST256_LEN * 256); + tor_assert(info_in_len <= 128); + memset(tmp, 0, sizeof(tmp)); + outp = key_out; + i = 1; + + while (key_out_len) { + size_t n; + if (i > 1) { + memcpy(tmp, mac, DIGEST256_LEN); + memcpy(tmp+DIGEST256_LEN, info_in, info_in_len); + tmp[DIGEST256_LEN+info_in_len] = i; + tmp_len = DIGEST256_LEN + info_in_len + 1; + } else { + memcpy(tmp, info_in, info_in_len); + tmp[info_in_len] = i; + tmp_len = info_in_len + 1; + } + crypto_hmac_sha256((char*)mac, + (const char*)prk, DIGEST256_LEN, + (const char*)tmp, tmp_len); + n = key_out_len < DIGEST256_LEN ? key_out_len : DIGEST256_LEN; + memcpy(outp, mac, n); + key_out_len -= n; + outp += n; + ++i; + } + + memwipe(tmp, 0, sizeof(tmp)); + memwipe(mac, 0, sizeof(mac)); + return 0; +} + /** Free a DH key exchange object. */ void @@ -2282,35 +2424,27 @@ crypto_dh_free(crypto_dh_t *dh) * that fd without checking whether it fit in the fd_set. Thus, if the * system has not just been started up, it is unsafe to call */ #define RAND_POLL_IS_SAFE \ - ((OPENSSL_VERSION_NUMBER >= OPENSSL_V(0,9,7,'j') && \ - OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8)) || \ - OPENSSL_VERSION_NUMBER >= OPENSSL_V(0,9,8,'c')) + (OPENSSL_VERSION_NUMBER >= OPENSSL_V(0,9,8,'c')) /** Set the seed of the weak RNG to a random value. */ -static void -seed_weak_rng(void) +void +crypto_seed_weak_rng(tor_weak_rng_t *rng) { unsigned seed; crypto_rand((void*)&seed, sizeof(seed)); - tor_init_weak_random(seed); + tor_init_weak_random(rng, seed); } -/** Seed OpenSSL's random number generator with bytes from the operating - * system. <b>startup</b> should be true iff we have just started Tor and - * have not yet allocated a bunch of fds. Return 0 on success, -1 on failure. +/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate, + * storing it into <b>out</b>. */ int -crypto_seed_rng(int startup) +crypto_strongest_rand(uint8_t *out, size_t out_len) { - int rand_poll_status = 0; - - /* local variables */ #ifdef _WIN32 - unsigned char buf[ADD_ENTROPY]; static int provider_set = 0; static HCRYPTPROV provider; #else - char buf[ADD_ENTROPY]; static const char *filenames[] = { "/dev/srandom", "/dev/urandom", "/dev/random", NULL }; @@ -2318,63 +2452,83 @@ crypto_seed_rng(int startup) size_t n; #endif - /* OpenSSL has a RAND_poll function that knows about more kinds of - * entropy than we do. We'll try calling that, *and* calling our own entropy - * functions. If one succeeds, we'll accept the RNG as seeded. */ - if (startup || RAND_POLL_IS_SAFE) { - rand_poll_status = RAND_poll(); - if (rand_poll_status == 0) - log_warn(LD_CRYPTO, "RAND_poll() failed."); - } - #ifdef _WIN32 if (!provider_set) { if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { if ((unsigned long)GetLastError() != (unsigned long)NTE_BAD_KEYSET) { log_warn(LD_CRYPTO, "Can't get CryptoAPI provider [1]"); - return rand_poll_status ? 0 : -1; + return -1; } } provider_set = 1; } - if (!CryptGenRandom(provider, sizeof(buf), buf)) { + if (!CryptGenRandom(provider, out_len, out)) { log_warn(LD_CRYPTO, "Can't get entropy from CryptoAPI."); - return rand_poll_status ? 0 : -1; + return -1; } - RAND_seed(buf, sizeof(buf)); - memwipe(buf, 0, sizeof(buf)); - seed_weak_rng(); + return 0; #else for (i = 0; filenames[i]; ++i) { - fd = open(filenames[i], O_RDONLY, 0); + log_debug(LD_FS, "Opening %s for entropy", filenames[i]); + fd = open(sandbox_intern_string(filenames[i]), O_RDONLY, 0); if (fd<0) continue; - log_info(LD_CRYPTO, "Seeding RNG from \"%s\"", filenames[i]); - n = read_all(fd, buf, sizeof(buf), 0); + log_info(LD_CRYPTO, "Reading entropy from \"%s\"", filenames[i]); + n = read_all(fd, (char*)out, out_len, 0); close(fd); - if (n != sizeof(buf)) { + if (n != out_len) { log_warn(LD_CRYPTO, "Error reading from entropy source (read only %lu bytes).", (unsigned long)n); return -1; } - RAND_seed(buf, (int)sizeof(buf)); - memwipe(buf, 0, sizeof(buf)); - seed_weak_rng(); + return 0; } - log_warn(LD_CRYPTO, "Cannot seed RNG -- no entropy source found."); - return rand_poll_status ? 0 : -1; + log_warn(LD_CRYPTO, "Cannot get strong entropy: no entropy source found."); + return -1; #endif } +/** Seed OpenSSL's random number generator with bytes from the operating + * system. <b>startup</b> should be true iff we have just started Tor and + * have not yet allocated a bunch of fds. Return 0 on success, -1 on failure. + */ +int +crypto_seed_rng(int startup) +{ + int rand_poll_ok = 0, load_entropy_ok = 0; + uint8_t buf[ADD_ENTROPY]; + + /* OpenSSL has a RAND_poll function that knows about more kinds of + * entropy than we do. We'll try calling that, *and* calling our own entropy + * functions. If one succeeds, we'll accept the RNG as seeded. */ + if (startup || RAND_POLL_IS_SAFE) { + rand_poll_ok = RAND_poll(); + if (rand_poll_ok == 0) + log_warn(LD_CRYPTO, "RAND_poll() failed."); + } + + load_entropy_ok = !crypto_strongest_rand(buf, sizeof(buf)); + if (load_entropy_ok) { + RAND_seed(buf, sizeof(buf)); + } + + memwipe(buf, 0, sizeof(buf)); + + if (rand_poll_ok || load_entropy_ok) + return 0; + else + return -1; +} + /** Write <b>n</b> bytes of strong random data to <b>to</b>. Return 0 on * success, -1 on failure. */ -int -crypto_rand(char *to, size_t n) +MOCK_IMPL(int, +crypto_rand, (char *to, size_t n)) { int r; tor_assert(n < INT_MAX); @@ -2517,7 +2671,7 @@ smartlist_shuffle(smartlist_t *sl) } } -/** Base-64 encode <b>srclen</b> bytes of data from <b>src</b>. Write +/** Base64 encode <b>srclen</b> bytes of data from <b>src</b>. Write * the result into <b>dest</b>, if it will fit within <b>destlen</b> * bytes. Return the number of bytes written on success; -1 if * destlen is too short, or other failure. @@ -2576,7 +2730,7 @@ static const uint8_t base64_decode_table[256] = { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, }; -/** Base-64 decode <b>srclen</b> bytes of data from <b>src</b>. Write +/** Base64 decode <b>srclen</b> bytes of data from <b>src</b>. Write * the result into <b>dest</b>, if it will fit within <b>destlen</b> * bytes. Return the number of bytes written on success; -1 if * destlen is too short, or other failure. @@ -2683,7 +2837,7 @@ base64_decode(char *dest, size_t destlen, const char *src, size_t srclen) #undef SP #undef PAD -/** Base-64 encode DIGEST_LINE bytes from <b>digest</b>, remove the trailing = +/** Base64 encode DIGEST_LINE bytes from <b>digest</b>, remove the trailing = * and newline characters, and store the nul-terminated result in the first * BASE64_DIGEST_LEN+1 bytes of <b>d64</b>. */ int @@ -2696,7 +2850,7 @@ digest_to_base64(char *d64, const char *digest) return 0; } -/** Given a base-64 encoded, nul-terminated digest in <b>d64</b> (without +/** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without * trailing newline or = characters), decode it and store the result in the * first DIGEST_LEN bytes at <b>digest</b>. */ int @@ -2721,7 +2875,7 @@ digest_from_base64(char *digest, const char *d64) #endif } -/** Base-64 encode DIGEST256_LINE bytes from <b>digest</b>, remove the +/** Base64 encode DIGEST256_LINE bytes from <b>digest</b>, remove the * trailing = and newline characters, and store the nul-terminated result in * the first BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>. */ int @@ -2734,7 +2888,7 @@ digest256_to_base64(char *d64, const char *digest) return 0; } -/** Given a base-64 encoded, nul-terminated digest in <b>d64</b> (without +/** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without * trailing newline or = characters), decode it and store the result in the * first DIGEST256_LEN bytes at <b>digest</b>. */ int @@ -2759,7 +2913,7 @@ digest256_from_base64(char *digest, const char *d64) #endif } -/** Implements base32 encoding as in rfc3548. Limitation: Requires +/** Implements base32 encoding as in RFC 4648. Limitation: Requires * that srclen*8 is a multiple of 5. */ void @@ -2784,7 +2938,7 @@ base32_encode(char *dest, size_t destlen, const char *src, size_t srclen) dest[i] = '\0'; } -/** Implements base32 decoding as in rfc3548. Limitation: Requires +/** Implements base32 decoding as in RFC 4648. Limitation: Requires * that srclen*5 is a multiple of 8. Returns 0 if successful, -1 otherwise. */ int @@ -2937,21 +3091,27 @@ memwipe(void *mem, uint8_t byte, size_t sz) } #ifdef TOR_IS_MULTITHREADED + +#ifndef OPENSSL_THREADS +#error OpenSSL has been built without thread support. Tor requires an \ + OpenSSL library with thread support enabled. +#endif + /** Helper: OpenSSL uses this callback to manipulate mutexes. */ static void -_openssl_locking_cb(int mode, int n, const char *file, int line) +openssl_locking_cb_(int mode, int n, const char *file, int line) { (void)file; (void)line; - if (!_openssl_mutexes) - /* This is not a really good fix for the + if (!openssl_mutexes_) + /* This is not a really good fix for the * "release-freed-lock-from-separate-thread-on-shutdown" problem, but * it can't hurt. */ return; if (mode & CRYPTO_LOCK) - tor_mutex_acquire(_openssl_mutexes[n]); + tor_mutex_acquire(openssl_mutexes_[n]); else - tor_mutex_release(_openssl_mutexes[n]); + tor_mutex_release(openssl_mutexes_[n]); } /** OpenSSL helper type: wraps a Tor mutex so that OpenSSL can use it @@ -2963,7 +3123,7 @@ struct CRYPTO_dynlock_value { /** OpenSSL callback function to allocate a lock: see CRYPTO_set_dynlock_* * documentation in OpenSSL's docs for more info. */ static struct CRYPTO_dynlock_value * -_openssl_dynlock_create_cb(const char *file, int line) +openssl_dynlock_create_cb_(const char *file, int line) { struct CRYPTO_dynlock_value *v; (void)file; @@ -2976,7 +3136,7 @@ _openssl_dynlock_create_cb(const char *file, int line) /** OpenSSL callback function to acquire or release a lock: see * CRYPTO_set_dynlock_* documentation in OpenSSL's docs for more info. */ static void -_openssl_dynlock_lock_cb(int mode, struct CRYPTO_dynlock_value *v, +openssl_dynlock_lock_cb_(int mode, struct CRYPTO_dynlock_value *v, const char *file, int line) { (void)file; @@ -2990,7 +3150,7 @@ _openssl_dynlock_lock_cb(int mode, struct CRYPTO_dynlock_value *v, /** OpenSSL callback function to free a lock: see CRYPTO_set_dynlock_* * documentation in OpenSSL's docs for more info. */ static void -_openssl_dynlock_destroy_cb(struct CRYPTO_dynlock_value *v, +openssl_dynlock_destroy_cb_(struct CRYPTO_dynlock_value *v, const char *file, int line) { (void)file; @@ -3007,15 +3167,15 @@ setup_openssl_threading(void) { int i; int n = CRYPTO_num_locks(); - _n_openssl_mutexes = n; - _openssl_mutexes = tor_malloc(n*sizeof(tor_mutex_t *)); + n_openssl_mutexes_ = n; + openssl_mutexes_ = tor_malloc(n*sizeof(tor_mutex_t *)); for (i=0; i < n; ++i) - _openssl_mutexes[i] = tor_mutex_new(); - CRYPTO_set_locking_callback(_openssl_locking_cb); + openssl_mutexes_[i] = tor_mutex_new(); + CRYPTO_set_locking_callback(openssl_locking_cb_); CRYPTO_set_id_callback(tor_get_thread_id); - CRYPTO_set_dynlock_create_callback(_openssl_dynlock_create_cb); - CRYPTO_set_dynlock_lock_callback(_openssl_dynlock_lock_cb); - CRYPTO_set_dynlock_destroy_callback(_openssl_dynlock_destroy_cb); + CRYPTO_set_dynlock_create_callback(openssl_dynlock_create_cb_); + CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock_cb_); + CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy_cb_); return 0; } #else @@ -3036,11 +3196,11 @@ crypto_global_cleanup(void) ERR_free_strings(); if (dh_param_p) - BN_free(dh_param_p); + BN_clear_free(dh_param_p); if (dh_param_p_tls) - BN_free(dh_param_p_tls); + BN_clear_free(dh_param_p_tls); if (dh_param_g) - BN_free(dh_param_g); + BN_clear_free(dh_param_g); #ifndef DISABLE_ENGINES ENGINE_cleanup(); @@ -3049,18 +3209,20 @@ crypto_global_cleanup(void) CONF_modules_unload(1); CRYPTO_cleanup_all_ex_data(); #ifdef TOR_IS_MULTITHREADED - if (_n_openssl_mutexes) { - int n = _n_openssl_mutexes; - tor_mutex_t **ms = _openssl_mutexes; + if (n_openssl_mutexes_) { + int n = n_openssl_mutexes_; + tor_mutex_t **ms = openssl_mutexes_; int i; - _openssl_mutexes = NULL; - _n_openssl_mutexes = 0; + openssl_mutexes_ = NULL; + n_openssl_mutexes_ = 0; for (i=0;i<n;++i) { tor_mutex_free(ms[i]); } tor_free(ms); } #endif + tor_free(crypto_openssl_version_str); + tor_free(crypto_openssl_header_version_str); return 0; } diff --git a/src/common/crypto.h b/src/common/crypto.h index 7d5627178..aa4271aa3 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -10,11 +10,12 @@ * \brief Headers for crypto.c **/ -#ifndef _TOR_CRYPTO_H -#define _TOR_CRYPTO_H +#ifndef TOR_CRYPTO_H +#define TOR_CRYPTO_H #include <stdio.h> #include "torint.h" +#include "testsupport.h" /* Macro to create an arbitrary OpenSSL version number as used by @@ -51,7 +52,7 @@ /** Length of the output of our message digest. */ #define DIGEST_LEN 20 /** Length of the output of our second (improved) message digests. (For now - * this is just sha256, but any it can be any other 256-byte digest). */ + * this is just sha256, but it could be any other 256-bit digest.) */ #define DIGEST256_LEN 32 /** Length of our symmetric cipher's keys. */ #define CIPHER_KEY_LEN 16 @@ -69,13 +70,9 @@ * signs removed. */ #define BASE64_DIGEST256_LEN 43 -/** Constant used to indicate PKCS1 padding for public-key encryption */ -#define PK_PKCS1_PADDING 60001 /** Constant used to indicate OAEP padding for public-key encryption */ #define PK_PKCS1_OAEP_PADDING 60002 -/** Number of bytes added for PKCS1 padding. */ -#define PKCS1_PADDING_OVERHEAD 11 /** Number of bytes added for PKCS1-OAEP padding. */ #define PKCS1_OAEP_PADDING_OVERHEAD 42 @@ -92,6 +89,7 @@ typedef enum { DIGEST_SHA256 = 1, } digest_algorithm_t; #define N_DIGEST_ALGORITHMS (DIGEST_SHA256+1) +#define digest_algorithm_bitfield_t ENUM_BF(digest_algorithm_t) /** A set of all the digests we know how to compute, taken on a single * string. Any digests that are shorter than 256 bits are right-padded @@ -111,6 +109,9 @@ typedef struct crypto_digest_t crypto_digest_t; typedef struct crypto_dh_t crypto_dh_t; /* global state */ +const char * crypto_openssl_get_version_str(void); +const char * crypto_openssl_get_header_version_str(void); +int crypto_early_init(void); int crypto_global_init(int hardwareAccel, const char *accelName, const char *accelPath); @@ -147,6 +148,7 @@ int crypto_pk_write_private_key_to_filename(crypto_pk_t *env, int crypto_pk_check_key(crypto_pk_t *env); int crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b); +int crypto_pk_eq_keys(crypto_pk_t *a, crypto_pk_t *b); size_t crypto_pk_keysize(crypto_pk_t *env); int crypto_pk_num_bits(crypto_pk_t *env); crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig); @@ -181,7 +183,7 @@ crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len); int crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out); int crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out); int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space); -int crypto_pk_check_fingerprint_syntax(const char *s); +int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out); /* symmetric crypto */ const char *crypto_cipher_get_key(crypto_cipher_t *env); @@ -204,6 +206,10 @@ int crypto_digest(char *digest, const char *m, size_t len); int crypto_digest256(char *digest, const char *m, size_t len, digest_algorithm_t algorithm); int crypto_digest_all(digests_t *ds_out, const char *m, size_t len); +struct smartlist_t; +void crypto_digest_smartlist(char *digest_out, size_t len_out, + const struct smartlist_t *lst, const char *append, + digest_algorithm_t alg); const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg); int crypto_digest_algorithm_parse_name(const char *name); crypto_digest_t *crypto_digest_new(void); @@ -216,9 +222,6 @@ void crypto_digest_get_digest(crypto_digest_t *digest, crypto_digest_t *crypto_digest_dup(const crypto_digest_t *digest); void crypto_digest_assign(crypto_digest_t *into, const crypto_digest_t *from); -void crypto_hmac_sha1(char *hmac_out, - const char *key, size_t key_len, - const char *msg, size_t msg_len); void crypto_hmac_sha256(char *hmac_out, const char *key, size_t key_len, const char *msg, size_t msg_len); @@ -228,6 +231,7 @@ void crypto_hmac_sha256(char *hmac_out, #define DH_TYPE_REND 2 #define DH_TYPE_TLS 3 crypto_dh_t *crypto_dh_new(int dh_type); +crypto_dh_t *crypto_dh_dup(const crypto_dh_t *dh); int crypto_dh_get_bytes(crypto_dh_t *dh); int crypto_dh_generate_public(crypto_dh_t *dh); int crypto_dh_get_public(crypto_dh_t *dh, char *pubkey_out, @@ -236,15 +240,26 @@ ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh, const char *pubkey, size_t pubkey_len, char *secret_out, size_t secret_out_len); void crypto_dh_free(crypto_dh_t *dh); -int crypto_expand_key_material(const char *key_in, size_t in_len, - char *key_out, size_t key_out_len); + +int crypto_expand_key_material_TAP(const uint8_t *key_in, + size_t key_in_len, + uint8_t *key_out, size_t key_out_len); +int crypto_expand_key_material_rfc5869_sha256( + const uint8_t *key_in, size_t key_in_len, + const uint8_t *salt_in, size_t salt_in_len, + const uint8_t *info_in, size_t info_in_len, + uint8_t *key_out, size_t key_out_len); /* random numbers */ int crypto_seed_rng(int startup); -int crypto_rand(char *to, size_t n); +MOCK_DECL(int,crypto_rand,(char *to, size_t n)); +int crypto_strongest_rand(uint8_t *out, size_t out_len); int crypto_rand_int(unsigned int max); uint64_t crypto_rand_uint64(uint64_t max); double crypto_rand_double(void); +struct tor_weak_rng_t; +void crypto_seed_weak_rng(struct tor_weak_rng_t *rng); +int crypto_init_siphash_key(void); char *crypto_random_hostname(int min_rand_len, int max_rand_len, const char *prefix, const char *suffix); @@ -255,7 +270,7 @@ void smartlist_shuffle(struct smartlist_t *sl); int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen); int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen); -/** Characters that can appear (case-insensitively) in a base-32 encoding. */ +/** Characters that can appear (case-insensitively) in a base32 encoding. */ #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567" void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen); int base32_decode(char *dest, size_t destlen, const char *src, size_t srclen); @@ -274,20 +289,18 @@ void secret_to_key(char *key_out, size_t key_out_len, const char *secret, /** OpenSSL-based utility functions. */ void memwipe(void *mem, uint8_t byte, size_t sz); -#ifdef CRYPTO_PRIVATE /* Prototypes for private functions only used by tortls.c, crypto.c, and the * unit tests. */ struct rsa_st; struct evp_pkey_st; struct dh_st; -struct rsa_st *_crypto_pk_get_rsa(crypto_pk_t *env); -crypto_pk_t *_crypto_new_pk_from_rsa(struct rsa_st *rsa); -struct evp_pkey_st *_crypto_pk_get_evp_pkey(crypto_pk_t *env, +struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env); +crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa); +struct evp_pkey_st *crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private); -struct dh_st *_crypto_dh_get_dh(crypto_dh_t *dh); -/* Prototypes for private functions only used by crypto.c and test.c*/ -void add_spaces_to_fp(char *out, size_t outlen, const char *in); -#endif +struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh); + +void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); #endif diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c new file mode 100644 index 000000000..9e83440e1 --- /dev/null +++ b/src/common/crypto_curve25519.c @@ -0,0 +1,191 @@ +/* Copyright (c) 2012-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Wrapper code for a curve25519 implementation. */ + +#define CRYPTO_CURVE25519_PRIVATE +#include "orconfig.h" +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#include "crypto.h" +#include "crypto_curve25519.h" +#include "util.h" +#include "torlog.h" + +/* ============================== + Part 1: wrap a suitable curve25519 implementation as curve25519_impl + ============================== */ + +#ifdef USE_CURVE25519_DONNA +int curve25519_donna(uint8_t *mypublic, + const uint8_t *secret, const uint8_t *basepoint); +#endif +#ifdef USE_CURVE25519_NACL +#ifdef HAVE_CRYPTO_SCALARMULT_CURVE25519_H +#include <crypto_scalarmult_curve25519.h> +#elif defined(HAVE_NACL_CRYPTO_SCALARMULT_CURVE25519_H) +#include <nacl/crypto_scalarmult_curve25519.h> +#endif +#endif + +STATIC int +curve25519_impl(uint8_t *output, const uint8_t *secret, + const uint8_t *basepoint) +{ + uint8_t bp[CURVE25519_PUBKEY_LEN]; + int r; + memcpy(bp, basepoint, CURVE25519_PUBKEY_LEN); + /* Clear the high bit, in case our backend foolishly looks at it. */ + bp[31] &= 0x7f; +#ifdef USE_CURVE25519_DONNA + r = curve25519_donna(output, secret, bp); +#elif defined(USE_CURVE25519_NACL) + r = crypto_scalarmult_curve25519(output, secret, bp); +#else +#error "No implementation of curve25519 is available." +#endif + memwipe(bp, 0, sizeof(bp)); + return r; +} + +/* ============================== + Part 2: Wrap curve25519_impl with some convenience types and functions. + ============================== */ + +/** + * Return true iff a curve25519_public_key_t seems valid. (It's not necessary + * to see if the point is on the curve, since the twist is also secure, but we + * do need to make sure that it isn't the point at infinity.) */ +int +curve25519_public_key_is_ok(const curve25519_public_key_t *key) +{ + return !safe_mem_is_zero(key->public_key, CURVE25519_PUBKEY_LEN); +} + +/** Generate a new keypair and return the secret key. If <b>extra_strong</b> + * is true, this key is possibly going to get used more than once, so + * use a better-than-usual RNG. Return 0 on success, -1 on failure. */ +int +curve25519_secret_key_generate(curve25519_secret_key_t *key_out, + int extra_strong) +{ + uint8_t k_tmp[CURVE25519_SECKEY_LEN]; + + if (crypto_rand((char*)key_out->secret_key, CURVE25519_SECKEY_LEN) < 0) + return -1; + if (extra_strong && !crypto_strongest_rand(k_tmp, CURVE25519_SECKEY_LEN)) { + /* If they asked for extra-strong entropy and we have some, use it as an + * HMAC key to improve not-so-good entropy rather than using it directly, + * just in case the extra-strong entropy is less amazing than we hoped. */ + crypto_hmac_sha256((char *)key_out->secret_key, + (const char *)k_tmp, sizeof(k_tmp), + (const char *)key_out->secret_key, CURVE25519_SECKEY_LEN); + } + memwipe(k_tmp, 0, sizeof(k_tmp)); + key_out->secret_key[0] &= 248; + key_out->secret_key[31] &= 127; + key_out->secret_key[31] |= 64; + + return 0; +} + +void +curve25519_public_key_generate(curve25519_public_key_t *key_out, + const curve25519_secret_key_t *seckey) +{ + static const uint8_t basepoint[32] = {9}; + + curve25519_impl(key_out->public_key, seckey->secret_key, basepoint); +} + +int +curve25519_keypair_generate(curve25519_keypair_t *keypair_out, + int extra_strong) +{ + if (curve25519_secret_key_generate(&keypair_out->seckey, extra_strong) < 0) + return -1; + curve25519_public_key_generate(&keypair_out->pubkey, &keypair_out->seckey); + return 0; +} + +int +curve25519_keypair_write_to_file(const curve25519_keypair_t *keypair, + const char *fname, + const char *tag) +{ + char contents[32 + CURVE25519_SECKEY_LEN + CURVE25519_PUBKEY_LEN]; + int r; + + memset(contents, 0, sizeof(contents)); + tor_snprintf(contents, sizeof(contents), "== c25519v1: %s ==", tag); + tor_assert(strlen(contents) <= 32); + memcpy(contents+32, keypair->seckey.secret_key, CURVE25519_SECKEY_LEN); + memcpy(contents+32+CURVE25519_SECKEY_LEN, + keypair->pubkey.public_key, CURVE25519_PUBKEY_LEN); + + r = write_bytes_to_file(fname, contents, sizeof(contents), 1); + + memwipe(contents, 0, sizeof(contents)); + return r; +} + +int +curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out, + char **tag_out, + const char *fname) +{ + char prefix[33]; + char *content; + struct stat st; + int r = -1; + + *tag_out = NULL; + + st.st_size = 0; + content = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); + if (! content) + goto end; + if (st.st_size != 32 + CURVE25519_SECKEY_LEN + CURVE25519_PUBKEY_LEN) + goto end; + + memcpy(prefix, content, 32); + prefix[32] = '\0'; + if (strcmpstart(prefix, "== c25519v1: ") || + strcmpend(prefix, " ==")) + goto end; + + *tag_out = tor_strndup(prefix+strlen("== c25519v1: "), + strlen(prefix) - strlen("== c25519v1: ==")); + + memcpy(keypair_out->seckey.secret_key, content+32, CURVE25519_SECKEY_LEN); + curve25519_public_key_generate(&keypair_out->pubkey, &keypair_out->seckey); + if (tor_memneq(keypair_out->pubkey.public_key, + content + 32 + CURVE25519_SECKEY_LEN, + CURVE25519_PUBKEY_LEN)) + goto end; + + r = 0; + + end: + if (content) { + memwipe(content, 0, (size_t) st.st_size); + tor_free(content); + } + if (r != 0) { + memset(keypair_out, 0, sizeof(*keypair_out)); + tor_free(*tag_out); + } + return r; +} + +/** Perform the curve25519 ECDH handshake with <b>skey</b> and <b>pkey</b>, + * writing CURVE25519_OUTPUT_LEN bytes of output into <b>output</b>. */ +void +curve25519_handshake(uint8_t *output, + const curve25519_secret_key_t *skey, + const curve25519_public_key_t *pkey) +{ + curve25519_impl(output, skey->secret_key, pkey->public_key); +} + diff --git a/src/common/crypto_curve25519.h b/src/common/crypto_curve25519.h new file mode 100644 index 000000000..57018ac2f --- /dev/null +++ b/src/common/crypto_curve25519.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2012-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_CRYPTO_CURVE25519_H +#define TOR_CRYPTO_CURVE25519_H + +#include "testsupport.h" +#include "torint.h" + +/** Length of a curve25519 public key when encoded. */ +#define CURVE25519_PUBKEY_LEN 32 +/** Length of a curve25519 secret key when encoded. */ +#define CURVE25519_SECKEY_LEN 32 +/** Length of the result of a curve25519 handshake. */ +#define CURVE25519_OUTPUT_LEN 32 + +/** Wrapper type for a curve25519 public key */ +typedef struct curve25519_public_key_t { + uint8_t public_key[CURVE25519_PUBKEY_LEN]; +} curve25519_public_key_t; + +/** Wrapper type for a curve25519 secret key */ +typedef struct curve25519_secret_key_t { + uint8_t secret_key[CURVE25519_SECKEY_LEN]; +} curve25519_secret_key_t; + +/** A paired public and private key for curve25519. **/ +typedef struct curve25519_keypair_t { + curve25519_public_key_t pubkey; + curve25519_secret_key_t seckey; +} curve25519_keypair_t; + +#ifdef CURVE25519_ENABLED +/* These functions require that we actually know how to use curve25519 keys. + * The other data structures and functions in this header let us parse them, + * store them, and move them around. + */ + +int curve25519_public_key_is_ok(const curve25519_public_key_t *); + +int curve25519_secret_key_generate(curve25519_secret_key_t *key_out, + int extra_strong); +void curve25519_public_key_generate(curve25519_public_key_t *key_out, + const curve25519_secret_key_t *seckey); +int curve25519_keypair_generate(curve25519_keypair_t *keypair_out, + int extra_strong); + +void curve25519_handshake(uint8_t *output, + const curve25519_secret_key_t *, + const curve25519_public_key_t *); + +int curve25519_keypair_write_to_file(const curve25519_keypair_t *keypair, + const char *fname, + const char *tag); + +int curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out, + char **tag_out, + const char *fname); + +#ifdef CRYPTO_CURVE25519_PRIVATE +STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret, + const uint8_t *basepoint); +#endif +#endif + +#define CURVE25519_BASE64_PADDED_LEN 44 + +int curve25519_public_from_base64(curve25519_public_key_t *pkey, + const char *input); +int curve25519_public_to_base64(char *output, + const curve25519_public_key_t *pkey); + +#endif + diff --git a/src/common/crypto_format.c b/src/common/crypto_format.c new file mode 100644 index 000000000..be669c8d2 --- /dev/null +++ b/src/common/crypto_format.c @@ -0,0 +1,45 @@ +/* Copyright (c) 2012-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Formatting and parsing code for crypto-related data structures. */ + +#include "orconfig.h" +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#include "crypto.h" +#include "crypto_curve25519.h" +#include "util.h" +#include "torlog.h" + +int +curve25519_public_to_base64(char *output, + const curve25519_public_key_t *pkey) +{ + char buf[128]; + base64_encode(buf, sizeof(buf), + (const char*)pkey->public_key, CURVE25519_PUBKEY_LEN); + buf[CURVE25519_BASE64_PADDED_LEN] = '\0'; + memcpy(output, buf, CURVE25519_BASE64_PADDED_LEN+1); + return 0; +} + +int +curve25519_public_from_base64(curve25519_public_key_t *pkey, + const char *input) +{ + size_t len = strlen(input); + if (len == CURVE25519_BASE64_PADDED_LEN - 1) { + /* not padded */ + return digest256_from_base64((char*)pkey->public_key, input); + } else if (len == CURVE25519_BASE64_PADDED_LEN) { + char buf[128]; + if (base64_decode(buf, sizeof(buf), input, len) != CURVE25519_PUBKEY_LEN) + return -1; + memcpy(pkey->public_key, buf, CURVE25519_PUBKEY_LEN); + return 0; + } else { + return -1; + } +} + diff --git a/src/common/di_ops.c b/src/common/di_ops.c index 7683c59de..14a144340 100644 --- a/src/common/di_ops.c +++ b/src/common/di_ops.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, The Tor Project, Inc. */ +/* Copyright (c) 2011-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -8,6 +8,8 @@ #include "orconfig.h" #include "di_ops.h" +#include "torlog.h" +#include "util.h" /** * Timing-safe version of memcmp. As memcmp, compare the <b>sz</b> bytes at @@ -123,7 +125,7 @@ tor_memeq(const void *a, const void *b, size_t sz) * * If any_difference != 0: * 0 < any_difference < 256, so - * 0 < any_difference - 1 < 255 + * 0 <= any_difference - 1 < 255 * (any_difference - 1) >> 8 == 0 * 1 & ((any_difference - 1) >> 8) == 0 */ @@ -131,3 +133,90 @@ tor_memeq(const void *a, const void *b, size_t sz) return 1 & ((any_difference - 1) >> 8); } +/* Implement di_digest256_map_t as a linked list of entries. */ +struct di_digest256_map_t { + struct di_digest256_map_t *next; + uint8_t key[32]; + void *val; +}; + +/** Release all storage held in <b>map</b>, calling free_fn on each value + * as we go. */ +void +dimap_free(di_digest256_map_t *map, dimap_free_fn free_fn) +{ + while (map) { + di_digest256_map_t *victim = map; + map = map->next; + if (free_fn) + free_fn(victim->val); + tor_free(victim); + } +} + +/** Adjust the map at *<b>map</b>, adding an entry for <b>key</b> -> + * <b>val</b>, where <b>key</b> is a DIGEST256_LEN-byte key. + * + * The caller MUST NOT add a key that already appears in the map. + */ +void +dimap_add_entry(di_digest256_map_t **map, + const uint8_t *key, void *val) +{ + di_digest256_map_t *new_ent; + { + void *old_val = dimap_search(*map, key, NULL); + tor_assert(! old_val); + tor_assert(val); + } + new_ent = tor_malloc_zero(sizeof(di_digest256_map_t)); + new_ent->next = *map; + memcpy(new_ent->key, key, 32); + new_ent->val = val; + *map = new_ent; +} + +/** Search the map at <b>map</b> for an entry whose key is <b>key</b> (a + * DIGEST256_LEN-byte key) returning the corresponding value if we found one, + * and returning <b>dflt_val</b> if the key wasn't found. + * + * This operation takes an amount of time dependent only on the length of + * <b>map</b>, not on the position or presence of <b>key</b> within <b>map</b>. + */ +void * +dimap_search(const di_digest256_map_t *map, const uint8_t *key, + void *dflt_val) +{ + uintptr_t result = (uintptr_t)dflt_val; + + while (map) { + uintptr_t r = (uintptr_t) tor_memeq(map->key, key, 32); + r -= 1; /* Now r is (uintptr_t)-1 if memeq returned false, and + * 0 if memeq returned true. */ + + result &= r; + result |= ((uintptr_t)(map->val)) & ~r; + + map = map->next; + } + + return (void *)result; +} + +/** + * Return true iff the <b>sz</b> bytes at <b>mem</b> are all zero. Runs in + * time independent of the contents of <b>mem</b>. + */ +int +safe_mem_is_zero(const void *mem, size_t sz) +{ + uint32_t total = 0; + const uint8_t *ptr = mem; + + while (sz--) { + total |= *ptr++; + } + + return 1 & ((total - 1) >> 8); +} + diff --git a/src/common/di_ops.h b/src/common/di_ops.h index 8f0bb698f..d93534b69 100644 --- a/src/common/di_ops.h +++ b/src/common/di_ops.h @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -27,5 +27,21 @@ int tor_memeq(const void *a, const void *b, size_t sz); #define fast_memeq(a,b,c) (0==memcmp((a),(b),(c))) #define fast_memneq(a,b,c) (0!=memcmp((a),(b),(c))) +int safe_mem_is_zero(const void *mem, size_t sz); + +/** A type for a map from DIGEST256_LEN-byte blobs to void*, such that + * data lookups take an amount of time proportional only to the size + * of the map, and not to the position or presence of the item in the map. + * + * Not efficient for large maps! */ +typedef struct di_digest256_map_t di_digest256_map_t; +typedef void (*dimap_free_fn)(void *); + +void dimap_free(di_digest256_map_t *map, dimap_free_fn free_fn); +void dimap_add_entry(di_digest256_map_t **map, + const uint8_t *key, void *val); +void *dimap_search(const di_digest256_map_t *map, const uint8_t *key, + void *dflt_val); + #endif diff --git a/src/common/get_mozilla_ciphers.py b/src/common/get_mozilla_ciphers.py deleted file mode 100644 index c7e9a84a0..000000000 --- a/src/common/get_mozilla_ciphers.py +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/python -# coding=utf-8 -# Copyright 2011, The Tor Project, Inc -# original version by Arturo Filastò -# See LICENSE for licensing information - -# This script parses Firefox and OpenSSL sources, and uses this information -# to generate a ciphers.inc file. -# -# It takes two arguments: the location of a firefox source directory, and the -# location of an openssl source directory. - -import os -import re -import sys - -if len(sys.argv) != 3: - print >>sys.stderr, "Syntax: get_mozilla_ciphers.py <firefox-source-dir> <openssl-source-dir>" - sys.exit(1) - -ff_root = sys.argv[1] -ossl_root = sys.argv[2] - -def ff(s): - return os.path.join(ff_root, s) -def ossl(s): - return os.path.join(ossl_root, s) - -##### -# Read the cpp file to understand what Ciphers map to what name : -# Make "ciphers" a map from name used in the javascript to a cipher macro name -fileA = open(ff('security/manager/ssl/src/nsNSSComponent.cpp'),'r') - -# The input format is a file containing exactly one section of the form: -# static CipherPref CipherPrefs[] = { -# {"name", MACRO_NAME}, // comment -# ... -# {NULL, 0} -# } - -inCipherSection = False -cipherLines = [] -for line in fileA: - if line.startswith('static CipherPref CipherPrefs'): - # Get the starting boundary of the Cipher Preferences - inCipherSection = True - elif inCipherSection: - line = line.strip() - if line.startswith('{NULL, 0}'): - # At the ending boundary of the Cipher Prefs - break - else: - cipherLines.append(line) -fileA.close() - -# Parse the lines and put them into a dict -ciphers = {} -cipher_pref = {} -for line in cipherLines: - m = re.search(r'^{\s*\"([^\"]+)\",\s*(\S*)\s*}', line) - if m: - key,value = m.groups() - ciphers[key] = value - cipher_pref[value] = key - -#### -# Now find the correct order for the ciphers -fileC = open(ff('security/nss/lib/ssl/ssl3con.c'), 'r') -firefox_ciphers = [] -inEnum=False -for line in fileC: - if not inEnum: - if "ssl3CipherSuiteCfg cipherSuites[" in line: - inEnum = True - continue - - if line.startswith("};"): - break - - m = re.match(r'^\s*\{\s*([A-Z_0-9]+),', line) - if m: - firefox_ciphers.append(m.group(1)) - -fileC.close() - -##### -# Read the JS file to understand what ciphers are enabled. The format is -# pref("name", true/false); -# Build a map enabled_ciphers from javascript name to "true" or "false", -# and an (unordered!) list of the macro names for those ciphers that are -# enabled. -fileB = open(ff('netwerk/base/public/security-prefs.js'), 'r') - -enabled_ciphers = {} -for line in fileB: - m = re.match(r'pref\(\"([^\"]+)\"\s*,\s*(\S*)\s*\)', line) - if not m: - continue - key, val = m.groups() - if key.startswith("security.ssl3"): - enabled_ciphers[key] = val -fileB.close() - -used_ciphers = [] -for k, v in enabled_ciphers.items(): - if v == "true": - used_ciphers.append(ciphers[k]) - -#oSSLinclude = ('/usr/include/openssl/ssl3.h', '/usr/include/openssl/ssl.h', -# '/usr/include/openssl/ssl2.h', '/usr/include/openssl/ssl23.h', -# '/usr/include/openssl/tls1.h') -oSSLinclude = ('ssl/ssl3.h', 'ssl/ssl.h', - 'ssl/ssl2.h', 'ssl/ssl23.h', - 'ssl/tls1.h') - -##### -# This reads the hex code for the ciphers that are used by firefox. -# sslProtoD is set to a map from macro name to macro value in sslproto.h; -# cipher_codes is set to an (unordered!) list of these hex values. -sslProto = open(ff('security/nss/lib/ssl/sslproto.h'), 'r') -sslProtoD = {} - -for line in sslProto: - m = re.match('#define\s+(\S+)\s+(\S+)', line) - if m: - key, value = m.groups() - sslProtoD[key] = value -sslProto.close() - -cipher_codes = [] -for x in used_ciphers: - cipher_codes.append(sslProtoD[x].lower()) - -#### -# Now read through all the openssl include files, and try to find the openssl -# macro names for those files. -openssl_macro_by_hex = {} -all_openssl_macros = {} -for fl in oSSLinclude: - fp = open(ossl(fl), 'r') - for line in fp.readlines(): - m = re.match('#define\s+(\S+)\s+(\S+)', line) - if m: - value,key = m.groups() - if key.startswith('0x') and "_CK_" in value: - key = key.replace('0x0300','0x').lower() - #print "%s %s" % (key, value) - openssl_macro_by_hex[key] = value - all_openssl_macros[value]=key - fp.close() - -# Now generate the output. -print """\ -/* This is an include file used to define the list of ciphers clients should - * advertise. Before including it, you should define the CIPHER and XCIPHER - * macros. - * - * This file was automatically generated by get_mozilla_ciphers.py. - */""" -# Go in order by the order in CipherPrefs -for firefox_macro in firefox_ciphers: - - try: - js_cipher_name = cipher_pref[firefox_macro] - except KeyError: - # This one has no javascript preference. - continue - - # The cipher needs to be enabled in security-prefs.js - if enabled_ciphers.get(js_cipher_name, 'false') != 'true': - continue - - hexval = sslProtoD[firefox_macro].lower() - - try: - openssl_macro = openssl_macro_by_hex[hexval.lower()] - openssl_macro = openssl_macro.replace("_CK_", "_TXT_") - if openssl_macro not in all_openssl_macros: - raise KeyError() - format = {'hex':hexval, 'macro':openssl_macro, 'note':""} - except KeyError: - # openssl doesn't have a macro for this. - format = {'hex':hexval, 'macro':firefox_macro, - 'note':"/* No openssl macro found for "+hexval+" */\n"} - - res = """\ -%(note)s#ifdef %(macro)s - CIPHER(%(hex)s, %(macro)s) -#else - XCIPHER(%(hex)s, %(macro)s) -#endif""" % format - print res diff --git a/src/common/ht.h b/src/common/ht.h deleted file mode 100644 index 25156c416..000000000 --- a/src/common/ht.h +++ /dev/null @@ -1,490 +0,0 @@ -/* Copyright (c) 2002, Christopher Clark. - * Copyright (c) 2005-2006, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ -/* See license at end. */ - -/* Based on ideas by Christopher Clark and interfaces from Niels Provos. */ - -#ifndef _TOR_HT_H -#define _TOR_HT_H - -#define HT_HEAD(name, type) \ - struct name { \ - /* The hash table itself. */ \ - struct type **hth_table; \ - /* How long is the hash table? */ \ - unsigned hth_table_length; \ - /* How many elements does the table contain? */ \ - unsigned hth_n_entries; \ - /* How many elements will we allow in the table before resizing it? */ \ - unsigned hth_load_limit; \ - /* Position of hth_table_length in the primes table. */ \ - int hth_prime_idx; \ - } - -#define HT_INITIALIZER() \ - { NULL, 0, 0, 0, -1 } - -#ifdef HT_NO_CACHE_HASH_VALUES -#define HT_ENTRY(type) \ - struct { \ - struct type *hte_next; \ - } -#else -#define HT_ENTRY(type) \ - struct { \ - struct type *hte_next; \ - unsigned hte_hash; \ - } -#endif - -#define HT_EMPTY(head) \ - ((head)->hth_n_entries == 0) - -/* How many elements in 'head'? */ -#define HT_SIZE(head) \ - ((head)->hth_n_entries) - -/* Return memory usage for a hashtable (not counting the entries themselves) */ -#define HT_MEM_USAGE(head) \ - (sizeof(*head) + (head)->hth_table_length * sizeof(void*)) - -#define HT_FIND(name, head, elm) name##_HT_FIND((head), (elm)) -#define HT_INSERT(name, head, elm) name##_HT_INSERT((head), (elm)) -#define HT_REPLACE(name, head, elm) name##_HT_REPLACE((head), (elm)) -#define HT_REMOVE(name, head, elm) name##_HT_REMOVE((head), (elm)) -#define HT_START(name, head) name##_HT_START(head) -#define HT_NEXT(name, head, elm) name##_HT_NEXT((head), (elm)) -#define HT_NEXT_RMV(name, head, elm) name##_HT_NEXT_RMV((head), (elm)) -#define HT_CLEAR(name, head) name##_HT_CLEAR(head) -#define HT_INIT(name, head) name##_HT_INIT(head) -/* Helper: */ -static INLINE unsigned -ht_improve_hash(unsigned h) -{ - /* Aim to protect against poor hash functions by adding logic here - * - logic taken from java 1.4 hashtable source */ - h += ~(h << 9); - h ^= ((h >> 14) | (h << 18)); /* >>> */ - h += (h << 4); - h ^= ((h >> 10) | (h << 22)); /* >>> */ - return h; -} - -#if 0 -/** Basic string hash function, from Java standard String.hashCode(). */ -static INLINE unsigned -ht_string_hash(const char *s) -{ - unsigned h = 0; - int m = 1; - while (*s) { - h += ((signed char)*s++)*m; - m = (m<<5)-1; /* m *= 31 */ - } - return h; -} -#endif - -/** Basic string hash function, from Python's str.__hash__() */ -static INLINE unsigned -ht_string_hash(const char *s) -{ - unsigned h; - const unsigned char *cp = (const unsigned char *)s; - h = *cp << 7; - while (*cp) { - h = (1000003*h) ^ *cp++; - } - /* This conversion truncates the length of the string, but that's ok. */ - h ^= (unsigned)(cp-(const unsigned char*)s); - return h; -} - -#ifndef HT_NO_CACHE_HASH_VALUES -#define HT_SET_HASH_(elm, field, hashfn) \ - do { (elm)->field.hte_hash = hashfn(elm); } while (0) -#define HT_SET_HASHVAL_(elm, field, val) \ - do { (elm)->field.hte_hash = (val); } while (0) -#define HT_ELT_HASH_(elm, field, hashfn) \ - ((elm)->field.hte_hash) -#else -#define HT_SET_HASH_(elm, field, hashfn) \ - ((void)0) -#define HT_ELT_HASH_(elm, field, hashfn) \ - (hashfn(elm)) -#define HT_SET_HASHVAL_(elm, field, val) \ - ((void)0) -#endif - -/* Helper: alias for the bucket containing 'elm'. */ -#define HT_BUCKET_(head, field, elm, hashfn) \ - ((head)->hth_table[HT_ELT_HASH_(elm,field,hashfn) \ - % head->hth_table_length]) - -#define HT_FOREACH(x, name, head) \ - for ((x) = HT_START(name, head); \ - (x) != NULL; \ - (x) = HT_NEXT(name, head, x)) - -#define HT_PROTOTYPE(name, type, field, hashfn, eqfn) \ - int name##_HT_GROW(struct name *ht, unsigned min_capacity); \ - void name##_HT_CLEAR(struct name *ht); \ - int name##_HT_REP_IS_BAD_(const struct name *ht); \ - static INLINE void \ - name##_HT_INIT(struct name *head) { \ - head->hth_table_length = 0; \ - head->hth_table = NULL; \ - head->hth_n_entries = 0; \ - head->hth_load_limit = 0; \ - head->hth_prime_idx = -1; \ - } \ - /* Helper: returns a pointer to the right location in the table \ - * 'head' to find or insert the element 'elm'. */ \ - static INLINE struct type ** \ - name##_HT_FIND_P_(struct name *head, struct type *elm) \ - { \ - struct type **p; \ - if (!head->hth_table) \ - return NULL; \ - p = &HT_BUCKET_(head, field, elm, hashfn); \ - while (*p) { \ - if (eqfn(*p, elm)) \ - return p; \ - p = &(*p)->field.hte_next; \ - } \ - return p; \ - } \ - /* Return a pointer to the element in the table 'head' matching 'elm', \ - * or NULL if no such element exists */ \ - static INLINE struct type * \ - name##_HT_FIND(const struct name *head, struct type *elm) \ - { \ - struct type **p; \ - struct name *h = (struct name *) head; \ - HT_SET_HASH_(elm, field, hashfn); \ - p = name##_HT_FIND_P_(h, elm); \ - return p ? *p : NULL; \ - } \ - /* Insert the element 'elm' into the table 'head'. Do not call this \ - * function if the table might already contain a matching element. */ \ - static INLINE void \ - name##_HT_INSERT(struct name *head, struct type *elm) \ - { \ - struct type **p; \ - if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \ - name##_HT_GROW(head, head->hth_n_entries+1); \ - ++head->hth_n_entries; \ - HT_SET_HASH_(elm, field, hashfn); \ - p = &HT_BUCKET_(head, field, elm, hashfn); \ - elm->field.hte_next = *p; \ - *p = elm; \ - } \ - /* Insert the element 'elm' into the table 'head'. If there already \ - * a matching element in the table, replace that element and return \ - * it. */ \ - static INLINE struct type * \ - name##_HT_REPLACE(struct name *head, struct type *elm) \ - { \ - struct type **p, *r; \ - if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \ - name##_HT_GROW(head, head->hth_n_entries+1); \ - HT_SET_HASH_(elm, field, hashfn); \ - p = name##_HT_FIND_P_(head, elm); \ - r = *p; \ - *p = elm; \ - if (r && (r!=elm)) { \ - elm->field.hte_next = r->field.hte_next; \ - r->field.hte_next = NULL; \ - return r; \ - } else { \ - ++head->hth_n_entries; \ - return NULL; \ - } \ - } \ - /* Remove any element matching 'elm' from the table 'head'. If such \ - * an element is found, return it; otherwise return NULL. */ \ - static INLINE struct type * \ - name##_HT_REMOVE(struct name *head, struct type *elm) \ - { \ - struct type **p, *r; \ - HT_SET_HASH_(elm, field, hashfn); \ - p = name##_HT_FIND_P_(head,elm); \ - if (!p || !*p) \ - return NULL; \ - r = *p; \ - *p = r->field.hte_next; \ - r->field.hte_next = NULL; \ - --head->hth_n_entries; \ - return r; \ - } \ - /* Invoke the function 'fn' on every element of the table 'head', \ - * using 'data' as its second argument. If the function returns \ - * nonzero, remove the most recently examined element before invoking \ - * the function again. */ \ - static INLINE void \ - name##_HT_FOREACH_FN(struct name *head, \ - int (*fn)(struct type *, void *), \ - void *data) \ - { \ - unsigned idx; \ - struct type **p, **nextp, *next; \ - if (!head->hth_table) \ - return; \ - for (idx=0; idx < head->hth_table_length; ++idx) { \ - p = &head->hth_table[idx]; \ - while (*p) { \ - nextp = &(*p)->field.hte_next; \ - next = *nextp; \ - if (fn(*p, data)) { \ - --head->hth_n_entries; \ - *p = next; \ - } else { \ - p = nextp; \ - } \ - } \ - } \ - } \ - /* Return a pointer to the first element in the table 'head', under \ - * an arbitrary order. This order is stable under remove operations, \ - * but not under others. If the table is empty, return NULL. */ \ - static INLINE struct type ** \ - name##_HT_START(struct name *head) \ - { \ - unsigned b = 0; \ - while (b < head->hth_table_length) { \ - if (head->hth_table[b]) \ - return &head->hth_table[b]; \ - ++b; \ - } \ - return NULL; \ - } \ - /* Return the next element in 'head' after 'elm', under the arbitrary \ - * order used by HT_START. If there are no more elements, return \ - * NULL. If 'elm' is to be removed from the table, you must call \ - * this function for the next value before you remove it. \ - */ \ - static INLINE struct type ** \ - name##_HT_NEXT(struct name *head, struct type **elm) \ - { \ - if ((*elm)->field.hte_next) { \ - return &(*elm)->field.hte_next; \ - } else { \ - unsigned b = (HT_ELT_HASH_(*elm, field, hashfn) \ - % head->hth_table_length)+1; \ - while (b < head->hth_table_length) { \ - if (head->hth_table[b]) \ - return &head->hth_table[b]; \ - ++b; \ - } \ - return NULL; \ - } \ - } \ - static INLINE struct type ** \ - name##_HT_NEXT_RMV(struct name *head, struct type **elm) \ - { \ - unsigned h = HT_ELT_HASH_(*elm, field, hashfn); \ - *elm = (*elm)->field.hte_next; \ - --head->hth_n_entries; \ - if (*elm) { \ - return elm; \ - } else { \ - unsigned b = (h % head->hth_table_length)+1; \ - while (b < head->hth_table_length) { \ - if (head->hth_table[b]) \ - return &head->hth_table[b]; \ - ++b; \ - } \ - return NULL; \ - } \ - } - -#define HT_GENERATE(name, type, field, hashfn, eqfn, load, mallocfn, \ - reallocfn, freefn) \ - static unsigned name##_PRIMES[] = { \ - 53, 97, 193, 389, \ - 769, 1543, 3079, 6151, \ - 12289, 24593, 49157, 98317, \ - 196613, 393241, 786433, 1572869, \ - 3145739, 6291469, 12582917, 25165843, \ - 50331653, 100663319, 201326611, 402653189, \ - 805306457, 1610612741 \ - }; \ - static unsigned name##_N_PRIMES = \ - (unsigned)(sizeof(name##_PRIMES)/sizeof(name##_PRIMES[0])); \ - /* Expand the internal table of 'head' until it is large enough to \ - * hold 'size' elements. Return 0 on success, -1 on allocation \ - * failure. */ \ - int \ - name##_HT_GROW(struct name *head, unsigned size) \ - { \ - unsigned new_len, new_load_limit; \ - int prime_idx; \ - struct type **new_table; \ - if (head->hth_prime_idx == (int)name##_N_PRIMES - 1) \ - return 0; \ - if (head->hth_load_limit > size) \ - return 0; \ - prime_idx = head->hth_prime_idx; \ - do { \ - new_len = name##_PRIMES[++prime_idx]; \ - new_load_limit = (unsigned)(load*new_len); \ - } while (new_load_limit <= size && \ - prime_idx < (int)name##_N_PRIMES); \ - if ((new_table = mallocfn(new_len*sizeof(struct type*)))) { \ - unsigned b; \ - memset(new_table, 0, new_len*sizeof(struct type*)); \ - for (b = 0; b < head->hth_table_length; ++b) { \ - struct type *elm, *next; \ - unsigned b2; \ - elm = head->hth_table[b]; \ - while (elm) { \ - next = elm->field.hte_next; \ - b2 = HT_ELT_HASH_(elm, field, hashfn) % new_len; \ - elm->field.hte_next = new_table[b2]; \ - new_table[b2] = elm; \ - elm = next; \ - } \ - } \ - if (head->hth_table) \ - freefn(head->hth_table); \ - head->hth_table = new_table; \ - } else { \ - unsigned b, b2; \ - new_table = reallocfn(head->hth_table, new_len*sizeof(struct type*)); \ - if (!new_table) return -1; \ - memset(new_table + head->hth_table_length, 0, \ - (new_len - head->hth_table_length)*sizeof(struct type*)); \ - for (b=0; b < head->hth_table_length; ++b) { \ - struct type *e, **pE; \ - for (pE = &new_table[b], e = *pE; e != NULL; e = *pE) { \ - b2 = HT_ELT_HASH_(e, field, hashfn) % new_len; \ - if (b2 == b) { \ - pE = &e->field.hte_next; \ - } else { \ - *pE = e->field.hte_next; \ - e->field.hte_next = new_table[b2]; \ - new_table[b2] = e; \ - } \ - } \ - } \ - head->hth_table = new_table; \ - } \ - head->hth_table_length = new_len; \ - head->hth_prime_idx = prime_idx; \ - head->hth_load_limit = new_load_limit; \ - return 0; \ - } \ - /* Free all storage held by 'head'. Does not free 'head' itself, or \ - * individual elements. */ \ - void \ - name##_HT_CLEAR(struct name *head) \ - { \ - if (head->hth_table) \ - freefn(head->hth_table); \ - head->hth_table_length = 0; \ - name##_HT_INIT(head); \ - } \ - /* Debugging helper: return false iff the representation of 'head' is \ - * internally consistent. */ \ - int \ - name##_HT_REP_IS_BAD_(const struct name *head) \ - { \ - unsigned n, i; \ - struct type *elm; \ - if (!head->hth_table_length) { \ - if (!head->hth_table && !head->hth_n_entries && \ - !head->hth_load_limit && head->hth_prime_idx == -1) \ - return 0; \ - else \ - return 1; \ - } \ - if (!head->hth_table || head->hth_prime_idx < 0 || \ - !head->hth_load_limit) \ - return 2; \ - if (head->hth_n_entries > head->hth_load_limit) \ - return 3; \ - if (head->hth_table_length != name##_PRIMES[head->hth_prime_idx]) \ - return 4; \ - if (head->hth_load_limit != (unsigned)(load*head->hth_table_length)) \ - return 5; \ - for (n = i = 0; i < head->hth_table_length; ++i) { \ - for (elm = head->hth_table[i]; elm; elm = elm->field.hte_next) { \ - if (HT_ELT_HASH_(elm, field, hashfn) != hashfn(elm)) \ - return 1000 + i; \ - if ((HT_ELT_HASH_(elm, field, hashfn) % head->hth_table_length) != i) \ - return 10000 + i; \ - ++n; \ - } \ - } \ - if (n != head->hth_n_entries) \ - return 6; \ - return 0; \ - } - -/** Implements an over-optimized "find and insert if absent" block; - * not meant for direct usage by typical code, or usage outside the critical - * path.*/ -#define HT_FIND_OR_INSERT_(name, field, hashfn, head, eltype, elm, var, y, n) \ - { \ - struct name *var##_head_ = head; \ - struct eltype **var; \ - if (!var##_head_->hth_table || \ - var##_head_->hth_n_entries >= var##_head_->hth_load_limit) \ - name##_HT_GROW(var##_head_, var##_head_->hth_n_entries+1); \ - HT_SET_HASH_((elm), field, hashfn); \ - var = name##_HT_FIND_P_(var##_head_, (elm)); \ - if (*var) { \ - y; \ - } else { \ - n; \ - } \ - } -#define HT_FOI_INSERT_(field, head, elm, newent, var) \ - { \ - HT_SET_HASHVAL_(newent, field, (elm)->field.hte_hash); \ - newent->field.hte_next = NULL; \ - *var = newent; \ - ++((head)->hth_n_entries); \ - } - -/* - * Copyright 2005, Nick Mathewson. Implementation logic is adapted from code - * by Christopher Clark, retrofit to allow drop-in memory management, and to - * use the same interface as Niels Provos's tree.h. This is probably still - * a derived work, so the original license below still applies. - * - * Copyright (c) 2002, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#endif - diff --git a/src/common/include.am b/src/common/include.am new file mode 100644 index 000000000..61a90cd35 --- /dev/null +++ b/src/common/include.am @@ -0,0 +1,139 @@ + +noinst_LIBRARIES += \ + src/common/libor.a \ + src/common/libor-crypto.a \ + src/common/libor-event.a + +if UNITTESTS_ENABLED +noinst_LIBRARIES += \ + src/common/libor-testing.a \ + src/common/libor-crypto-testing.a \ + src/common/libor-event-testing.a +endif + +EXTRA_DIST+= \ + src/common/common_sha1.i \ + src/common/Makefile.nmake + +#CFLAGS = -Wall -Wpointer-arith -O2 +AM_CPPFLAGS += -I$(srcdir)/src/common -Isrc/common + +if USE_OPENBSD_MALLOC +libor_extra_source=src/ext/OpenBSD_malloc_Linux.c +else +libor_extra_source= +endif + +if USE_MEMPOOLS +libor_mempool_source=src/common/mempool.c +libor_mempool_header=src/common/mempool.h +else +libor_mempool_source= +libor_mempool_header= +endif + +src_common_libcurve25519_donna_a_CFLAGS= + +if BUILD_CURVE25519_DONNA +src_common_libcurve25519_donna_a_SOURCES=\ + src/ext/curve25519_donna/curve25519-donna.c +src_common_libcurve25519_donna_a_CFLAGS+=\ + @F_OMIT_FRAME_POINTER@ +noinst_LIBRARIES+=src/common/libcurve25519_donna.a +LIBDONNA=src/common/libcurve25519_donna.a +else +if BUILD_CURVE25519_DONNA_C64 +src_common_libcurve25519_donna_a_SOURCES=\ + src/ext/curve25519_donna/curve25519-donna-c64.c +noinst_LIBRARIES+=src/common/libcurve25519_donna.a +LIBDONNA=src/common/libcurve25519_donna.a +else +LIBDONNA= +endif +endif + +if CURVE25519_ENABLED +libcrypto_extra_source=src/common/crypto_curve25519.c +endif + +LIBOR_A_SOURCES = \ + src/common/address.c \ + src/common/backtrace.c \ + src/common/compat.c \ + src/common/container.c \ + src/common/di_ops.c \ + src/common/log.c \ + src/common/memarea.c \ + src/common/procmon.c \ + src/common/util.c \ + src/common/util_codedigest.c \ + src/common/sandbox.c \ + src/ext/csiphash.c \ + $(libor_extra_source) \ + $(libor_mempool_source) + +LIBOR_CRYPTO_A_SOURCES = \ + src/common/aes.c \ + src/common/crypto.c \ + src/common/crypto_format.c \ + src/common/torgzip.c \ + src/common/tortls.c \ + $(libcrypto_extra_source) + +LIBOR_EVENT_A_SOURCES = src/common/compat_libevent.c + +src_common_libor_a_SOURCES = $(LIBOR_A_SOURCES) +src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES) +src_common_libor_event_a_SOURCES = $(LIBOR_EVENT_A_SOURCES) + +src_common_libor_testing_a_SOURCES = $(LIBOR_A_SOURCES) +src_common_libor_crypto_testing_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES) +src_common_libor_event_testing_a_SOURCES = $(LIBOR_EVENT_A_SOURCES) + +src_common_libor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_common_libor_crypto_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_common_libor_event_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_common_libor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +src_common_libor_crypto_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +src_common_libor_event_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + + +COMMONHEADERS = \ + src/common/address.h \ + src/common/backtrace.h \ + src/common/aes.h \ + src/common/ciphers.inc \ + src/common/compat.h \ + src/common/compat_libevent.h \ + src/common/container.h \ + src/common/crypto.h \ + src/common/crypto_curve25519.h \ + src/common/di_ops.h \ + src/common/memarea.h \ + src/common/linux_syscalls.inc \ + src/common/procmon.h \ + src/common/sandbox.h \ + src/common/testsupport.h \ + src/common/torgzip.h \ + src/common/torint.h \ + src/common/torlog.h \ + src/common/tortls.h \ + src/common/util.h \ + $(libor_mempool_header) + +noinst_HEADERS+= $(COMMONHEADERS) + +DISTCLEANFILES+= src/common/common_sha1.i + +src/common/common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(COMMONHEADERS) + $(AM_V_GEN)if test "@SHA1SUM@" != none; then \ + (cd "$(srcdir)" && "@SHA1SUM@" $(src_common_libor_SOURCES) $(src_common_libor_crypto_a_SOURCES) $(COMMONHEADERS)) | "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > $@; \ + elif test "@OPENSSL@" != none; then \ + (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_common_libor_SOURCES) $(src_Common_libor_crypto_a_SOURCES) $(COMMONHEADERS)) | "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > $@; \ + else \ + rm $@; \ + touch $@; \ + fi + +src/common/util_codedigest.o: src/common/common_sha1.i + diff --git a/src/common/linux_syscalls.inc b/src/common/linux_syscalls.inc new file mode 100644 index 000000000..cf47c7380 --- /dev/null +++ b/src/common/linux_syscalls.inc @@ -0,0 +1,1153 @@ +/* Automatically generated with + gen_linux_syscalls.pl /usr/include/asm/unistd*.h + Do not edit. + */ +static const struct { + int syscall_num; const char *syscall_name; +} SYSCALLS_BY_NUMBER[] = { +#ifdef __NR__llseek + { __NR__llseek, "_llseek" }, +#endif +#ifdef __NR__newselect + { __NR__newselect, "_newselect" }, +#endif +#ifdef __NR__sysctl + { __NR__sysctl, "_sysctl" }, +#endif +#ifdef __NR_accept + { __NR_accept, "accept" }, +#endif +#ifdef __NR_accept4 + { __NR_accept4, "accept4" }, +#endif +#ifdef __NR_access + { __NR_access, "access" }, +#endif +#ifdef __NR_acct + { __NR_acct, "acct" }, +#endif +#ifdef __NR_add_key + { __NR_add_key, "add_key" }, +#endif +#ifdef __NR_adjtimex + { __NR_adjtimex, "adjtimex" }, +#endif +#ifdef __NR_afs_syscall + { __NR_afs_syscall, "afs_syscall" }, +#endif +#ifdef __NR_alarm + { __NR_alarm, "alarm" }, +#endif +#ifdef __NR_arch_prctl + { __NR_arch_prctl, "arch_prctl" }, +#endif +#ifdef __NR_bdflush + { __NR_bdflush, "bdflush" }, +#endif +#ifdef __NR_bind + { __NR_bind, "bind" }, +#endif +#ifdef __NR_break + { __NR_break, "break" }, +#endif +#ifdef __NR_brk + { __NR_brk, "brk" }, +#endif +#ifdef __NR_capget + { __NR_capget, "capget" }, +#endif +#ifdef __NR_capset + { __NR_capset, "capset" }, +#endif +#ifdef __NR_chdir + { __NR_chdir, "chdir" }, +#endif +#ifdef __NR_chmod + { __NR_chmod, "chmod" }, +#endif +#ifdef __NR_chown + { __NR_chown, "chown" }, +#endif +#ifdef __NR_chown32 + { __NR_chown32, "chown32" }, +#endif +#ifdef __NR_chroot + { __NR_chroot, "chroot" }, +#endif +#ifdef __NR_clock_adjtime + { __NR_clock_adjtime, "clock_adjtime" }, +#endif +#ifdef __NR_clock_getres + { __NR_clock_getres, "clock_getres" }, +#endif +#ifdef __NR_clock_gettime + { __NR_clock_gettime, "clock_gettime" }, +#endif +#ifdef __NR_clock_nanosleep + { __NR_clock_nanosleep, "clock_nanosleep" }, +#endif +#ifdef __NR_clock_settime + { __NR_clock_settime, "clock_settime" }, +#endif +#ifdef __NR_clone + { __NR_clone, "clone" }, +#endif +#ifdef __NR_close + { __NR_close, "close" }, +#endif +#ifdef __NR_connect + { __NR_connect, "connect" }, +#endif +#ifdef __NR_creat + { __NR_creat, "creat" }, +#endif +#ifdef __NR_create_module + { __NR_create_module, "create_module" }, +#endif +#ifdef __NR_delete_module + { __NR_delete_module, "delete_module" }, +#endif +#ifdef __NR_dup + { __NR_dup, "dup" }, +#endif +#ifdef __NR_dup2 + { __NR_dup2, "dup2" }, +#endif +#ifdef __NR_dup3 + { __NR_dup3, "dup3" }, +#endif +#ifdef __NR_epoll_create + { __NR_epoll_create, "epoll_create" }, +#endif +#ifdef __NR_epoll_create1 + { __NR_epoll_create1, "epoll_create1" }, +#endif +#ifdef __NR_epoll_ctl + { __NR_epoll_ctl, "epoll_ctl" }, +#endif +#ifdef __NR_epoll_ctl_old + { __NR_epoll_ctl_old, "epoll_ctl_old" }, +#endif +#ifdef __NR_epoll_pwait + { __NR_epoll_pwait, "epoll_pwait" }, +#endif +#ifdef __NR_epoll_wait + { __NR_epoll_wait, "epoll_wait" }, +#endif +#ifdef __NR_epoll_wait_old + { __NR_epoll_wait_old, "epoll_wait_old" }, +#endif +#ifdef __NR_eventfd + { __NR_eventfd, "eventfd" }, +#endif +#ifdef __NR_eventfd2 + { __NR_eventfd2, "eventfd2" }, +#endif +#ifdef __NR_execve + { __NR_execve, "execve" }, +#endif +#ifdef __NR_exit + { __NR_exit, "exit" }, +#endif +#ifdef __NR_exit_group + { __NR_exit_group, "exit_group" }, +#endif +#ifdef __NR_faccessat + { __NR_faccessat, "faccessat" }, +#endif +#ifdef __NR_fadvise64 + { __NR_fadvise64, "fadvise64" }, +#endif +#ifdef __NR_fadvise64_64 + { __NR_fadvise64_64, "fadvise64_64" }, +#endif +#ifdef __NR_fallocate + { __NR_fallocate, "fallocate" }, +#endif +#ifdef __NR_fanotify_init + { __NR_fanotify_init, "fanotify_init" }, +#endif +#ifdef __NR_fanotify_mark + { __NR_fanotify_mark, "fanotify_mark" }, +#endif +#ifdef __NR_fchdir + { __NR_fchdir, "fchdir" }, +#endif +#ifdef __NR_fchmod + { __NR_fchmod, "fchmod" }, +#endif +#ifdef __NR_fchmodat + { __NR_fchmodat, "fchmodat" }, +#endif +#ifdef __NR_fchown + { __NR_fchown, "fchown" }, +#endif +#ifdef __NR_fchown32 + { __NR_fchown32, "fchown32" }, +#endif +#ifdef __NR_fchownat + { __NR_fchownat, "fchownat" }, +#endif +#ifdef __NR_fcntl + { __NR_fcntl, "fcntl" }, +#endif +#ifdef __NR_fcntl64 + { __NR_fcntl64, "fcntl64" }, +#endif +#ifdef __NR_fdatasync + { __NR_fdatasync, "fdatasync" }, +#endif +#ifdef __NR_fgetxattr + { __NR_fgetxattr, "fgetxattr" }, +#endif +#ifdef __NR_finit_module + { __NR_finit_module, "finit_module" }, +#endif +#ifdef __NR_flistxattr + { __NR_flistxattr, "flistxattr" }, +#endif +#ifdef __NR_flock + { __NR_flock, "flock" }, +#endif +#ifdef __NR_fork + { __NR_fork, "fork" }, +#endif +#ifdef __NR_fremovexattr + { __NR_fremovexattr, "fremovexattr" }, +#endif +#ifdef __NR_fsetxattr + { __NR_fsetxattr, "fsetxattr" }, +#endif +#ifdef __NR_fstat + { __NR_fstat, "fstat" }, +#endif +#ifdef __NR_fstat64 + { __NR_fstat64, "fstat64" }, +#endif +#ifdef __NR_fstatat64 + { __NR_fstatat64, "fstatat64" }, +#endif +#ifdef __NR_fstatfs + { __NR_fstatfs, "fstatfs" }, +#endif +#ifdef __NR_fstatfs64 + { __NR_fstatfs64, "fstatfs64" }, +#endif +#ifdef __NR_fsync + { __NR_fsync, "fsync" }, +#endif +#ifdef __NR_ftime + { __NR_ftime, "ftime" }, +#endif +#ifdef __NR_ftruncate + { __NR_ftruncate, "ftruncate" }, +#endif +#ifdef __NR_ftruncate64 + { __NR_ftruncate64, "ftruncate64" }, +#endif +#ifdef __NR_futex + { __NR_futex, "futex" }, +#endif +#ifdef __NR_futimesat + { __NR_futimesat, "futimesat" }, +#endif +#ifdef __NR_get_kernel_syms + { __NR_get_kernel_syms, "get_kernel_syms" }, +#endif +#ifdef __NR_get_mempolicy + { __NR_get_mempolicy, "get_mempolicy" }, +#endif +#ifdef __NR_get_robust_list + { __NR_get_robust_list, "get_robust_list" }, +#endif +#ifdef __NR_get_thread_area + { __NR_get_thread_area, "get_thread_area" }, +#endif +#ifdef __NR_getcpu + { __NR_getcpu, "getcpu" }, +#endif +#ifdef __NR_getcwd + { __NR_getcwd, "getcwd" }, +#endif +#ifdef __NR_getdents + { __NR_getdents, "getdents" }, +#endif +#ifdef __NR_getdents64 + { __NR_getdents64, "getdents64" }, +#endif +#ifdef __NR_getegid + { __NR_getegid, "getegid" }, +#endif +#ifdef __NR_getegid32 + { __NR_getegid32, "getegid32" }, +#endif +#ifdef __NR_geteuid + { __NR_geteuid, "geteuid" }, +#endif +#ifdef __NR_geteuid32 + { __NR_geteuid32, "geteuid32" }, +#endif +#ifdef __NR_getgid + { __NR_getgid, "getgid" }, +#endif +#ifdef __NR_getgid32 + { __NR_getgid32, "getgid32" }, +#endif +#ifdef __NR_getgroups + { __NR_getgroups, "getgroups" }, +#endif +#ifdef __NR_getgroups32 + { __NR_getgroups32, "getgroups32" }, +#endif +#ifdef __NR_getitimer + { __NR_getitimer, "getitimer" }, +#endif +#ifdef __NR_getpeername + { __NR_getpeername, "getpeername" }, +#endif +#ifdef __NR_getpgid + { __NR_getpgid, "getpgid" }, +#endif +#ifdef __NR_getpgrp + { __NR_getpgrp, "getpgrp" }, +#endif +#ifdef __NR_getpid + { __NR_getpid, "getpid" }, +#endif +#ifdef __NR_getpmsg + { __NR_getpmsg, "getpmsg" }, +#endif +#ifdef __NR_getppid + { __NR_getppid, "getppid" }, +#endif +#ifdef __NR_getpriority + { __NR_getpriority, "getpriority" }, +#endif +#ifdef __NR_getresgid + { __NR_getresgid, "getresgid" }, +#endif +#ifdef __NR_getresgid32 + { __NR_getresgid32, "getresgid32" }, +#endif +#ifdef __NR_getresuid + { __NR_getresuid, "getresuid" }, +#endif +#ifdef __NR_getresuid32 + { __NR_getresuid32, "getresuid32" }, +#endif +#ifdef __NR_getrlimit + { __NR_getrlimit, "getrlimit" }, +#endif +#ifdef __NR_getrusage + { __NR_getrusage, "getrusage" }, +#endif +#ifdef __NR_getsid + { __NR_getsid, "getsid" }, +#endif +#ifdef __NR_getsockname + { __NR_getsockname, "getsockname" }, +#endif +#ifdef __NR_getsockopt + { __NR_getsockopt, "getsockopt" }, +#endif +#ifdef __NR_gettid + { __NR_gettid, "gettid" }, +#endif +#ifdef __NR_gettimeofday + { __NR_gettimeofday, "gettimeofday" }, +#endif +#ifdef __NR_getuid + { __NR_getuid, "getuid" }, +#endif +#ifdef __NR_getuid32 + { __NR_getuid32, "getuid32" }, +#endif +#ifdef __NR_getxattr + { __NR_getxattr, "getxattr" }, +#endif +#ifdef __NR_gtty + { __NR_gtty, "gtty" }, +#endif +#ifdef __NR_idle + { __NR_idle, "idle" }, +#endif +#ifdef __NR_init_module + { __NR_init_module, "init_module" }, +#endif +#ifdef __NR_inotify_add_watch + { __NR_inotify_add_watch, "inotify_add_watch" }, +#endif +#ifdef __NR_inotify_init + { __NR_inotify_init, "inotify_init" }, +#endif +#ifdef __NR_inotify_init1 + { __NR_inotify_init1, "inotify_init1" }, +#endif +#ifdef __NR_inotify_rm_watch + { __NR_inotify_rm_watch, "inotify_rm_watch" }, +#endif +#ifdef __NR_io_cancel + { __NR_io_cancel, "io_cancel" }, +#endif +#ifdef __NR_io_destroy + { __NR_io_destroy, "io_destroy" }, +#endif +#ifdef __NR_io_getevents + { __NR_io_getevents, "io_getevents" }, +#endif +#ifdef __NR_io_setup + { __NR_io_setup, "io_setup" }, +#endif +#ifdef __NR_io_submit + { __NR_io_submit, "io_submit" }, +#endif +#ifdef __NR_ioctl + { __NR_ioctl, "ioctl" }, +#endif +#ifdef __NR_ioperm + { __NR_ioperm, "ioperm" }, +#endif +#ifdef __NR_iopl + { __NR_iopl, "iopl" }, +#endif +#ifdef __NR_ioprio_get + { __NR_ioprio_get, "ioprio_get" }, +#endif +#ifdef __NR_ioprio_set + { __NR_ioprio_set, "ioprio_set" }, +#endif +#ifdef __NR_ipc + { __NR_ipc, "ipc" }, +#endif +#ifdef __NR_kcmp + { __NR_kcmp, "kcmp" }, +#endif +#ifdef __NR_kexec_load + { __NR_kexec_load, "kexec_load" }, +#endif +#ifdef __NR_keyctl + { __NR_keyctl, "keyctl" }, +#endif +#ifdef __NR_kill + { __NR_kill, "kill" }, +#endif +#ifdef __NR_lchown + { __NR_lchown, "lchown" }, +#endif +#ifdef __NR_lchown32 + { __NR_lchown32, "lchown32" }, +#endif +#ifdef __NR_lgetxattr + { __NR_lgetxattr, "lgetxattr" }, +#endif +#ifdef __NR_link + { __NR_link, "link" }, +#endif +#ifdef __NR_linkat + { __NR_linkat, "linkat" }, +#endif +#ifdef __NR_listen + { __NR_listen, "listen" }, +#endif +#ifdef __NR_listxattr + { __NR_listxattr, "listxattr" }, +#endif +#ifdef __NR_llistxattr + { __NR_llistxattr, "llistxattr" }, +#endif +#ifdef __NR_lock + { __NR_lock, "lock" }, +#endif +#ifdef __NR_lookup_dcookie + { __NR_lookup_dcookie, "lookup_dcookie" }, +#endif +#ifdef __NR_lremovexattr + { __NR_lremovexattr, "lremovexattr" }, +#endif +#ifdef __NR_lseek + { __NR_lseek, "lseek" }, +#endif +#ifdef __NR_lsetxattr + { __NR_lsetxattr, "lsetxattr" }, +#endif +#ifdef __NR_lstat + { __NR_lstat, "lstat" }, +#endif +#ifdef __NR_lstat64 + { __NR_lstat64, "lstat64" }, +#endif +#ifdef __NR_madvise + { __NR_madvise, "madvise" }, +#endif +#ifdef __NR_mbind + { __NR_mbind, "mbind" }, +#endif +#ifdef __NR_migrate_pages + { __NR_migrate_pages, "migrate_pages" }, +#endif +#ifdef __NR_mincore + { __NR_mincore, "mincore" }, +#endif +#ifdef __NR_mkdir + { __NR_mkdir, "mkdir" }, +#endif +#ifdef __NR_mkdirat + { __NR_mkdirat, "mkdirat" }, +#endif +#ifdef __NR_mknod + { __NR_mknod, "mknod" }, +#endif +#ifdef __NR_mknodat + { __NR_mknodat, "mknodat" }, +#endif +#ifdef __NR_mlock + { __NR_mlock, "mlock" }, +#endif +#ifdef __NR_mlockall + { __NR_mlockall, "mlockall" }, +#endif +#ifdef __NR_mmap + { __NR_mmap, "mmap" }, +#endif +#ifdef __NR_mmap2 + { __NR_mmap2, "mmap2" }, +#endif +#ifdef __NR_modify_ldt + { __NR_modify_ldt, "modify_ldt" }, +#endif +#ifdef __NR_mount + { __NR_mount, "mount" }, +#endif +#ifdef __NR_move_pages + { __NR_move_pages, "move_pages" }, +#endif +#ifdef __NR_mprotect + { __NR_mprotect, "mprotect" }, +#endif +#ifdef __NR_mpx + { __NR_mpx, "mpx" }, +#endif +#ifdef __NR_mq_getsetattr + { __NR_mq_getsetattr, "mq_getsetattr" }, +#endif +#ifdef __NR_mq_notify + { __NR_mq_notify, "mq_notify" }, +#endif +#ifdef __NR_mq_open + { __NR_mq_open, "mq_open" }, +#endif +#ifdef __NR_mq_timedreceive + { __NR_mq_timedreceive, "mq_timedreceive" }, +#endif +#ifdef __NR_mq_timedsend + { __NR_mq_timedsend, "mq_timedsend" }, +#endif +#ifdef __NR_mq_unlink + { __NR_mq_unlink, "mq_unlink" }, +#endif +#ifdef __NR_mremap + { __NR_mremap, "mremap" }, +#endif +#ifdef __NR_msgctl + { __NR_msgctl, "msgctl" }, +#endif +#ifdef __NR_msgget + { __NR_msgget, "msgget" }, +#endif +#ifdef __NR_msgrcv + { __NR_msgrcv, "msgrcv" }, +#endif +#ifdef __NR_msgsnd + { __NR_msgsnd, "msgsnd" }, +#endif +#ifdef __NR_msync + { __NR_msync, "msync" }, +#endif +#ifdef __NR_munlock + { __NR_munlock, "munlock" }, +#endif +#ifdef __NR_munlockall + { __NR_munlockall, "munlockall" }, +#endif +#ifdef __NR_munmap + { __NR_munmap, "munmap" }, +#endif +#ifdef __NR_name_to_handle_at + { __NR_name_to_handle_at, "name_to_handle_at" }, +#endif +#ifdef __NR_nanosleep + { __NR_nanosleep, "nanosleep" }, +#endif +#ifdef __NR_newfstatat + { __NR_newfstatat, "newfstatat" }, +#endif +#ifdef __NR_nfsservctl + { __NR_nfsservctl, "nfsservctl" }, +#endif +#ifdef __NR_nice + { __NR_nice, "nice" }, +#endif +#ifdef __NR_oldfstat + { __NR_oldfstat, "oldfstat" }, +#endif +#ifdef __NR_oldlstat + { __NR_oldlstat, "oldlstat" }, +#endif +#ifdef __NR_oldolduname + { __NR_oldolduname, "oldolduname" }, +#endif +#ifdef __NR_oldstat + { __NR_oldstat, "oldstat" }, +#endif +#ifdef __NR_olduname + { __NR_olduname, "olduname" }, +#endif +#ifdef __NR_open + { __NR_open, "open" }, +#endif +#ifdef __NR_open_by_handle_at + { __NR_open_by_handle_at, "open_by_handle_at" }, +#endif +#ifdef __NR_openat + { __NR_openat, "openat" }, +#endif +#ifdef __NR_pause + { __NR_pause, "pause" }, +#endif +#ifdef __NR_perf_event_open + { __NR_perf_event_open, "perf_event_open" }, +#endif +#ifdef __NR_personality + { __NR_personality, "personality" }, +#endif +#ifdef __NR_pipe + { __NR_pipe, "pipe" }, +#endif +#ifdef __NR_pipe2 + { __NR_pipe2, "pipe2" }, +#endif +#ifdef __NR_pivot_root + { __NR_pivot_root, "pivot_root" }, +#endif +#ifdef __NR_poll + { __NR_poll, "poll" }, +#endif +#ifdef __NR_ppoll + { __NR_ppoll, "ppoll" }, +#endif +#ifdef __NR_prctl + { __NR_prctl, "prctl" }, +#endif +#ifdef __NR_pread64 + { __NR_pread64, "pread64" }, +#endif +#ifdef __NR_preadv + { __NR_preadv, "preadv" }, +#endif +#ifdef __NR_prlimit64 + { __NR_prlimit64, "prlimit64" }, +#endif +#ifdef __NR_process_vm_readv + { __NR_process_vm_readv, "process_vm_readv" }, +#endif +#ifdef __NR_process_vm_writev + { __NR_process_vm_writev, "process_vm_writev" }, +#endif +#ifdef __NR_prof + { __NR_prof, "prof" }, +#endif +#ifdef __NR_profil + { __NR_profil, "profil" }, +#endif +#ifdef __NR_pselect6 + { __NR_pselect6, "pselect6" }, +#endif +#ifdef __NR_ptrace + { __NR_ptrace, "ptrace" }, +#endif +#ifdef __NR_putpmsg + { __NR_putpmsg, "putpmsg" }, +#endif +#ifdef __NR_pwrite64 + { __NR_pwrite64, "pwrite64" }, +#endif +#ifdef __NR_pwritev + { __NR_pwritev, "pwritev" }, +#endif +#ifdef __NR_query_module + { __NR_query_module, "query_module" }, +#endif +#ifdef __NR_quotactl + { __NR_quotactl, "quotactl" }, +#endif +#ifdef __NR_read + { __NR_read, "read" }, +#endif +#ifdef __NR_readahead + { __NR_readahead, "readahead" }, +#endif +#ifdef __NR_readdir + { __NR_readdir, "readdir" }, +#endif +#ifdef __NR_readlink + { __NR_readlink, "readlink" }, +#endif +#ifdef __NR_readlinkat + { __NR_readlinkat, "readlinkat" }, +#endif +#ifdef __NR_readv + { __NR_readv, "readv" }, +#endif +#ifdef __NR_reboot + { __NR_reboot, "reboot" }, +#endif +#ifdef __NR_recvfrom + { __NR_recvfrom, "recvfrom" }, +#endif +#ifdef __NR_recvmmsg + { __NR_recvmmsg, "recvmmsg" }, +#endif +#ifdef __NR_recvmsg + { __NR_recvmsg, "recvmsg" }, +#endif +#ifdef __NR_remap_file_pages + { __NR_remap_file_pages, "remap_file_pages" }, +#endif +#ifdef __NR_removexattr + { __NR_removexattr, "removexattr" }, +#endif +#ifdef __NR_rename + { __NR_rename, "rename" }, +#endif +#ifdef __NR_renameat + { __NR_renameat, "renameat" }, +#endif +#ifdef __NR_request_key + { __NR_request_key, "request_key" }, +#endif +#ifdef __NR_restart_syscall + { __NR_restart_syscall, "restart_syscall" }, +#endif +#ifdef __NR_rmdir + { __NR_rmdir, "rmdir" }, +#endif +#ifdef __NR_rt_sigaction + { __NR_rt_sigaction, "rt_sigaction" }, +#endif +#ifdef __NR_rt_sigpending + { __NR_rt_sigpending, "rt_sigpending" }, +#endif +#ifdef __NR_rt_sigprocmask + { __NR_rt_sigprocmask, "rt_sigprocmask" }, +#endif +#ifdef __NR_rt_sigqueueinfo + { __NR_rt_sigqueueinfo, "rt_sigqueueinfo" }, +#endif +#ifdef __NR_rt_sigreturn + { __NR_rt_sigreturn, "rt_sigreturn" }, +#endif +#ifdef __NR_rt_sigsuspend + { __NR_rt_sigsuspend, "rt_sigsuspend" }, +#endif +#ifdef __NR_rt_sigtimedwait + { __NR_rt_sigtimedwait, "rt_sigtimedwait" }, +#endif +#ifdef __NR_rt_tgsigqueueinfo + { __NR_rt_tgsigqueueinfo, "rt_tgsigqueueinfo" }, +#endif +#ifdef __NR_sched_get_priority_max + { __NR_sched_get_priority_max, "sched_get_priority_max" }, +#endif +#ifdef __NR_sched_get_priority_min + { __NR_sched_get_priority_min, "sched_get_priority_min" }, +#endif +#ifdef __NR_sched_getaffinity + { __NR_sched_getaffinity, "sched_getaffinity" }, +#endif +#ifdef __NR_sched_getparam + { __NR_sched_getparam, "sched_getparam" }, +#endif +#ifdef __NR_sched_getscheduler + { __NR_sched_getscheduler, "sched_getscheduler" }, +#endif +#ifdef __NR_sched_rr_get_interval + { __NR_sched_rr_get_interval, "sched_rr_get_interval" }, +#endif +#ifdef __NR_sched_setaffinity + { __NR_sched_setaffinity, "sched_setaffinity" }, +#endif +#ifdef __NR_sched_setparam + { __NR_sched_setparam, "sched_setparam" }, +#endif +#ifdef __NR_sched_setscheduler + { __NR_sched_setscheduler, "sched_setscheduler" }, +#endif +#ifdef __NR_sched_yield + { __NR_sched_yield, "sched_yield" }, +#endif +#ifdef __NR_security + { __NR_security, "security" }, +#endif +#ifdef __NR_select + { __NR_select, "select" }, +#endif +#ifdef __NR_semctl + { __NR_semctl, "semctl" }, +#endif +#ifdef __NR_semget + { __NR_semget, "semget" }, +#endif +#ifdef __NR_semop + { __NR_semop, "semop" }, +#endif +#ifdef __NR_semtimedop + { __NR_semtimedop, "semtimedop" }, +#endif +#ifdef __NR_sendfile + { __NR_sendfile, "sendfile" }, +#endif +#ifdef __NR_sendfile64 + { __NR_sendfile64, "sendfile64" }, +#endif +#ifdef __NR_sendmmsg + { __NR_sendmmsg, "sendmmsg" }, +#endif +#ifdef __NR_sendmsg + { __NR_sendmsg, "sendmsg" }, +#endif +#ifdef __NR_sendto + { __NR_sendto, "sendto" }, +#endif +#ifdef __NR_set_mempolicy + { __NR_set_mempolicy, "set_mempolicy" }, +#endif +#ifdef __NR_set_robust_list + { __NR_set_robust_list, "set_robust_list" }, +#endif +#ifdef __NR_set_thread_area + { __NR_set_thread_area, "set_thread_area" }, +#endif +#ifdef __NR_set_tid_address + { __NR_set_tid_address, "set_tid_address" }, +#endif +#ifdef __NR_setdomainname + { __NR_setdomainname, "setdomainname" }, +#endif +#ifdef __NR_setfsgid + { __NR_setfsgid, "setfsgid" }, +#endif +#ifdef __NR_setfsgid32 + { __NR_setfsgid32, "setfsgid32" }, +#endif +#ifdef __NR_setfsuid + { __NR_setfsuid, "setfsuid" }, +#endif +#ifdef __NR_setfsuid32 + { __NR_setfsuid32, "setfsuid32" }, +#endif +#ifdef __NR_setgid + { __NR_setgid, "setgid" }, +#endif +#ifdef __NR_setgid32 + { __NR_setgid32, "setgid32" }, +#endif +#ifdef __NR_setgroups + { __NR_setgroups, "setgroups" }, +#endif +#ifdef __NR_setgroups32 + { __NR_setgroups32, "setgroups32" }, +#endif +#ifdef __NR_sethostname + { __NR_sethostname, "sethostname" }, +#endif +#ifdef __NR_setitimer + { __NR_setitimer, "setitimer" }, +#endif +#ifdef __NR_setns + { __NR_setns, "setns" }, +#endif +#ifdef __NR_setpgid + { __NR_setpgid, "setpgid" }, +#endif +#ifdef __NR_setpriority + { __NR_setpriority, "setpriority" }, +#endif +#ifdef __NR_setregid + { __NR_setregid, "setregid" }, +#endif +#ifdef __NR_setregid32 + { __NR_setregid32, "setregid32" }, +#endif +#ifdef __NR_setresgid + { __NR_setresgid, "setresgid" }, +#endif +#ifdef __NR_setresgid32 + { __NR_setresgid32, "setresgid32" }, +#endif +#ifdef __NR_setresuid + { __NR_setresuid, "setresuid" }, +#endif +#ifdef __NR_setresuid32 + { __NR_setresuid32, "setresuid32" }, +#endif +#ifdef __NR_setreuid + { __NR_setreuid, "setreuid" }, +#endif +#ifdef __NR_setreuid32 + { __NR_setreuid32, "setreuid32" }, +#endif +#ifdef __NR_setrlimit + { __NR_setrlimit, "setrlimit" }, +#endif +#ifdef __NR_setsid + { __NR_setsid, "setsid" }, +#endif +#ifdef __NR_setsockopt + { __NR_setsockopt, "setsockopt" }, +#endif +#ifdef __NR_settimeofday + { __NR_settimeofday, "settimeofday" }, +#endif +#ifdef __NR_setuid + { __NR_setuid, "setuid" }, +#endif +#ifdef __NR_setuid32 + { __NR_setuid32, "setuid32" }, +#endif +#ifdef __NR_setxattr + { __NR_setxattr, "setxattr" }, +#endif +#ifdef __NR_sgetmask + { __NR_sgetmask, "sgetmask" }, +#endif +#ifdef __NR_shmat + { __NR_shmat, "shmat" }, +#endif +#ifdef __NR_shmctl + { __NR_shmctl, "shmctl" }, +#endif +#ifdef __NR_shmdt + { __NR_shmdt, "shmdt" }, +#endif +#ifdef __NR_shmget + { __NR_shmget, "shmget" }, +#endif +#ifdef __NR_shutdown + { __NR_shutdown, "shutdown" }, +#endif +#ifdef __NR_sigaction + { __NR_sigaction, "sigaction" }, +#endif +#ifdef __NR_sigaltstack + { __NR_sigaltstack, "sigaltstack" }, +#endif +#ifdef __NR_signal + { __NR_signal, "signal" }, +#endif +#ifdef __NR_signalfd + { __NR_signalfd, "signalfd" }, +#endif +#ifdef __NR_signalfd4 + { __NR_signalfd4, "signalfd4" }, +#endif +#ifdef __NR_sigpending + { __NR_sigpending, "sigpending" }, +#endif +#ifdef __NR_sigprocmask + { __NR_sigprocmask, "sigprocmask" }, +#endif +#ifdef __NR_sigreturn + { __NR_sigreturn, "sigreturn" }, +#endif +#ifdef __NR_sigsuspend + { __NR_sigsuspend, "sigsuspend" }, +#endif +#ifdef __NR_socket + { __NR_socket, "socket" }, +#endif +#ifdef __NR_socketcall + { __NR_socketcall, "socketcall" }, +#endif +#ifdef __NR_socketpair + { __NR_socketpair, "socketpair" }, +#endif +#ifdef __NR_splice + { __NR_splice, "splice" }, +#endif +#ifdef __NR_ssetmask + { __NR_ssetmask, "ssetmask" }, +#endif +#ifdef __NR_stat + { __NR_stat, "stat" }, +#endif +#ifdef __NR_stat64 + { __NR_stat64, "stat64" }, +#endif +#ifdef __NR_statfs + { __NR_statfs, "statfs" }, +#endif +#ifdef __NR_statfs64 + { __NR_statfs64, "statfs64" }, +#endif +#ifdef __NR_stime + { __NR_stime, "stime" }, +#endif +#ifdef __NR_stty + { __NR_stty, "stty" }, +#endif +#ifdef __NR_swapoff + { __NR_swapoff, "swapoff" }, +#endif +#ifdef __NR_swapon + { __NR_swapon, "swapon" }, +#endif +#ifdef __NR_symlink + { __NR_symlink, "symlink" }, +#endif +#ifdef __NR_symlinkat + { __NR_symlinkat, "symlinkat" }, +#endif +#ifdef __NR_sync + { __NR_sync, "sync" }, +#endif +#ifdef __NR_sync_file_range + { __NR_sync_file_range, "sync_file_range" }, +#endif +#ifdef __NR_syncfs + { __NR_syncfs, "syncfs" }, +#endif +#ifdef __NR_sysfs + { __NR_sysfs, "sysfs" }, +#endif +#ifdef __NR_sysinfo + { __NR_sysinfo, "sysinfo" }, +#endif +#ifdef __NR_syslog + { __NR_syslog, "syslog" }, +#endif +#ifdef __NR_tee + { __NR_tee, "tee" }, +#endif +#ifdef __NR_tgkill + { __NR_tgkill, "tgkill" }, +#endif +#ifdef __NR_time + { __NR_time, "time" }, +#endif +#ifdef __NR_timer_create + { __NR_timer_create, "timer_create" }, +#endif +#ifdef __NR_timer_delete + { __NR_timer_delete, "timer_delete" }, +#endif +#ifdef __NR_timer_getoverrun + { __NR_timer_getoverrun, "timer_getoverrun" }, +#endif +#ifdef __NR_timer_gettime + { __NR_timer_gettime, "timer_gettime" }, +#endif +#ifdef __NR_timer_settime + { __NR_timer_settime, "timer_settime" }, +#endif +#ifdef __NR_timerfd_create + { __NR_timerfd_create, "timerfd_create" }, +#endif +#ifdef __NR_timerfd_gettime + { __NR_timerfd_gettime, "timerfd_gettime" }, +#endif +#ifdef __NR_timerfd_settime + { __NR_timerfd_settime, "timerfd_settime" }, +#endif +#ifdef __NR_times + { __NR_times, "times" }, +#endif +#ifdef __NR_tkill + { __NR_tkill, "tkill" }, +#endif +#ifdef __NR_truncate + { __NR_truncate, "truncate" }, +#endif +#ifdef __NR_truncate64 + { __NR_truncate64, "truncate64" }, +#endif +#ifdef __NR_tuxcall + { __NR_tuxcall, "tuxcall" }, +#endif +#ifdef __NR_ugetrlimit + { __NR_ugetrlimit, "ugetrlimit" }, +#endif +#ifdef __NR_ulimit + { __NR_ulimit, "ulimit" }, +#endif +#ifdef __NR_umask + { __NR_umask, "umask" }, +#endif +#ifdef __NR_umount + { __NR_umount, "umount" }, +#endif +#ifdef __NR_umount2 + { __NR_umount2, "umount2" }, +#endif +#ifdef __NR_uname + { __NR_uname, "uname" }, +#endif +#ifdef __NR_unlink + { __NR_unlink, "unlink" }, +#endif +#ifdef __NR_unlinkat + { __NR_unlinkat, "unlinkat" }, +#endif +#ifdef __NR_unshare + { __NR_unshare, "unshare" }, +#endif +#ifdef __NR_uselib + { __NR_uselib, "uselib" }, +#endif +#ifdef __NR_ustat + { __NR_ustat, "ustat" }, +#endif +#ifdef __NR_utime + { __NR_utime, "utime" }, +#endif +#ifdef __NR_utimensat + { __NR_utimensat, "utimensat" }, +#endif +#ifdef __NR_utimes + { __NR_utimes, "utimes" }, +#endif +#ifdef __NR_vfork + { __NR_vfork, "vfork" }, +#endif +#ifdef __NR_vhangup + { __NR_vhangup, "vhangup" }, +#endif +#ifdef __NR_vm86 + { __NR_vm86, "vm86" }, +#endif +#ifdef __NR_vm86old + { __NR_vm86old, "vm86old" }, +#endif +#ifdef __NR_vmsplice + { __NR_vmsplice, "vmsplice" }, +#endif +#ifdef __NR_vserver + { __NR_vserver, "vserver" }, +#endif +#ifdef __NR_wait4 + { __NR_wait4, "wait4" }, +#endif +#ifdef __NR_waitid + { __NR_waitid, "waitid" }, +#endif +#ifdef __NR_waitpid + { __NR_waitpid, "waitpid" }, +#endif +#ifdef __NR_write + { __NR_write, "write" }, +#endif +#ifdef __NR_writev + { __NR_writev, "writev" }, +#endif + {0, NULL} +}; + diff --git a/src/common/log.c b/src/common/log.c index 5e2e6b5b5..517fa4faa 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -36,6 +36,10 @@ #include "torlog.h" #include "container.h" +/** Given a severity, yields an index into log_severity_list_t.masks to use + * for that severity. */ +#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR) + /** @{ */ /** The string we stick at the end of a log message when it is too long, * and its length. */ @@ -83,12 +87,12 @@ should_log_function_name(log_domain_mask_t domain, int severity) case LOG_DEBUG: case LOG_INFO: /* All debugging messages occur in interesting places. */ - return 1; + return (domain & LD_NOFUNCNAME) == 0; case LOG_NOTICE: case LOG_WARN: case LOG_ERR: /* We care about places where bugs occur. */ - return (domain == LD_BUG); + return (domain & (LD_BUG|LD_NOFUNCNAME)) == LD_BUG; default: /* Call assert, not tor_assert, since tor_assert calls log on failure. */ assert(0); return 0; @@ -131,7 +135,7 @@ static smartlist_t *pending_cb_messages = NULL; /** What's the lowest log level anybody cares about? Checking this lets us * bail out early from log_debug if we aren't debugging. */ -int _log_global_min_severity = LOG_NOTICE; +int log_global_min_severity_ = LOG_NOTICE; static void delete_log(logfile_t *victim); static void close_log(logfile_t *victim); @@ -140,11 +144,9 @@ static char *domain_to_string(log_domain_mask_t domain, char *buf, size_t buflen); static INLINE char *format_msg(char *buf, size_t buf_len, log_domain_mask_t domain, int severity, const char *funcname, + const char *suffix, const char *format, va_list ap, size_t *msg_len_out) - CHECK_PRINTF(6,0); -static void logv(int severity, log_domain_mask_t domain, const char *funcname, - const char *format, va_list ap) - CHECK_PRINTF(4,0); + CHECK_PRINTF(7,0); /** Name of the application: used to generate the message we write at the * start of each new log. */ @@ -177,7 +179,7 @@ set_log_time_granularity(int granularity_msec) * <b>buf_len</b> character buffer in <b>buf</b>. */ static INLINE size_t -_log_prefix(char *buf, size_t buf_len, int severity) +log_prefix_(char *buf, size_t buf_len, int severity) { time_t t; struct timeval now; @@ -230,7 +232,7 @@ log_tor_version(logfile_t *lf, int reset) /* We are resetting, but we aren't at the start of the file; no * need to log again. */ return 0; - n = _log_prefix(buf, sizeof(buf), LOG_NOTICE); + n = log_prefix_(buf, sizeof(buf), LOG_NOTICE); if (appname) { tor_snprintf(buf+n, sizeof(buf)-n, "%s opening %slog file.\n", appname, is_new?"new ":""); @@ -251,6 +253,7 @@ log_tor_version(logfile_t *lf, int reset) static INLINE char * format_msg(char *buf, size_t buf_len, log_domain_mask_t domain, int severity, const char *funcname, + const char *suffix, const char *format, va_list ap, size_t *msg_len_out) { size_t n; @@ -262,7 +265,7 @@ format_msg(char *buf, size_t buf_len, buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */ buf_end = buf+buf_len; /* point *after* the last char we can write to */ - n = _log_prefix(buf, buf_len, severity); + n = log_prefix_(buf, buf_len, severity); end_of_prefix = buf+n; if (log_domains_are_logged) { @@ -312,6 +315,13 @@ format_msg(char *buf, size_t buf_len, n = buf_len; } else { n += r; + if (suffix) { + size_t suffix_len = strlen(suffix); + if (buf_len-n >= suffix_len) { + memcpy(buf+n, suffix, suffix_len); + n += suffix_len; + } + } } buf[n]='\n'; buf[n+1]='\0'; @@ -323,9 +333,9 @@ format_msg(char *buf, size_t buf_len, * <b>severity</b>. If provided, <b>funcname</b> is prepended to the * message. The actual message is derived as from tor_snprintf(format,ap). */ -static void -logv(int severity, log_domain_mask_t domain, const char *funcname, - const char *format, va_list ap) +MOCK_IMPL(STATIC void, +logv,(int severity, log_domain_mask_t domain, const char *funcname, + const char *suffix, const char *format, va_list ap)) { char buf[10024]; size_t msg_len = 0; @@ -361,8 +371,8 @@ logv(int severity, log_domain_mask_t domain, const char *funcname, if (!formatted) { end_of_prefix = - format_msg(buf, sizeof(buf), domain, severity, funcname, format, ap, - &msg_len); + format_msg(buf, sizeof(buf), domain, severity, funcname, suffix, + format, ap, &msg_len); formatted = 1; } @@ -423,102 +433,277 @@ void tor_log(int severity, log_domain_mask_t domain, const char *format, ...) { va_list ap; - if (severity > _log_global_min_severity) + if (severity > log_global_min_severity_) return; va_start(ap,format); - logv(severity, domain, NULL, format, ap); + logv(severity, domain, NULL, NULL, format, ap); va_end(ap); } +/** Maximum number of fds that will get notifications if we crash */ +#define MAX_SIGSAFE_FDS 8 +/** Array of fds to log crash-style warnings to. */ +static int sigsafe_log_fds[MAX_SIGSAFE_FDS] = { STDERR_FILENO }; +/** The number of elements used in sigsafe_log_fds */ +static int n_sigsafe_log_fds = 1; + +/** Write <b>s</b> to each element of sigsafe_log_fds. Return 0 on success, -1 + * on failure. */ +static int +tor_log_err_sigsafe_write(const char *s) +{ + int i; + ssize_t r; + size_t len = strlen(s); + int err = 0; + for (i=0; i < n_sigsafe_log_fds; ++i) { + r = write(sigsafe_log_fds[i], s, len); + err += (r != (ssize_t)len); + } + return err ? -1 : 0; +} + +/** Given a list of string arguments ending with a NULL, writes them + * to our logs and to stderr (if possible). This function is safe to call + * from within a signal handler. */ +void +tor_log_err_sigsafe(const char *m, ...) +{ + va_list ap; + const char *x; + char timebuf[33]; + time_t now = time(NULL); + + if (!m) + return; + if (log_time_granularity >= 2000) { + int g = log_time_granularity / 1000; + now -= now % g; + } + timebuf[0] = now < 0 ? '-' : ' '; + if (now < 0) now = -now; + timebuf[1] = '\0'; + format_dec_number_sigsafe(now, timebuf+1, sizeof(timebuf)-1); + tor_log_err_sigsafe_write("\n==========================================" + "================== T="); + tor_log_err_sigsafe_write(timebuf); + tor_log_err_sigsafe_write("\n"); + tor_log_err_sigsafe_write(m); + va_start(ap, m); + while ((x = va_arg(ap, const char*))) { + tor_log_err_sigsafe_write(x); + } + va_end(ap); +} + +/** Set *<b>out</b> to a pointer to an array of the fds to log errors to from + * inside a signal handler. Return the number of elements in the array. */ +int +tor_log_get_sigsafe_err_fds(const int **out) +{ + *out = sigsafe_log_fds; + return n_sigsafe_log_fds; +} + +/** Helper function; return true iff the <b>n</b>-element array <b>array</b> + * contains <b>item</b>. */ +static int +int_array_contains(const int *array, int n, int item) +{ + int j; + for (j = 0; j < n; ++j) { + if (array[j] == item) + return 1; + } + return 0; +} + +/** Function to call whenever the list of logs changes to get ready to log + * from signal handlers. */ +void +tor_log_update_sigsafe_err_fds(void) +{ + const logfile_t *lf; + int found_real_stderr = 0; + + LOCK_LOGS(); + /* Reserve the first one for stderr. This is safe because when we daemonize, + * we dup2 /dev/null to stderr, */ + sigsafe_log_fds[0] = STDERR_FILENO; + n_sigsafe_log_fds = 1; + + for (lf = logfiles; lf; lf = lf->next) { + /* Don't try callback to the control port, or syslogs: We can't + * do them from a signal handler. Don't try stdout: we always do stderr. + */ + if (lf->is_temporary || lf->is_syslog || + lf->callback || lf->seems_dead || lf->fd < 0) + continue; + if (lf->severities->masks[SEVERITY_MASK_IDX(LOG_ERR)] & + (LD_BUG|LD_GENERAL)) { + if (lf->fd == STDERR_FILENO) + found_real_stderr = 1; + /* Avoid duplicates */ + if (int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, lf->fd)) + continue; + sigsafe_log_fds[n_sigsafe_log_fds++] = lf->fd; + if (n_sigsafe_log_fds == MAX_SIGSAFE_FDS) + break; + } + } + + if (!found_real_stderr && + int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, STDOUT_FILENO)) { + /* Don't use a virtual stderr when we're also logging to stdout. */ + assert(n_sigsafe_log_fds >= 2); /* Don't use assert inside log functions*/ + sigsafe_log_fds[0] = sigsafe_log_fds[--n_sigsafe_log_fds]; + } + + UNLOCK_LOGS(); +} + +/** Add to <b>out</b> a copy of every currently configured log file name. Used + * to enable access to these filenames with the sandbox code. */ +void +tor_log_get_logfile_names(smartlist_t *out) +{ + logfile_t *lf; + tor_assert(out); + + LOCK_LOGS(); + + for (lf = logfiles; lf; lf = lf->next) { + if (lf->is_temporary || lf->is_syslog || lf->callback) + continue; + if (lf->filename == NULL) + continue; + smartlist_add(out, tor_strdup(lf->filename)); + } + + UNLOCK_LOGS(); +} + /** Output a message to the log, prefixed with a function name <b>fn</b>. */ #ifdef __GNUC__ /** GCC-based implementation of the log_fn backend, used when we have * variadic macros. All arguments are as for log_fn, except for * <b>fn</b>, which is the name of the calling functions. */ void -_log_fn(int severity, log_domain_mask_t domain, const char *fn, +log_fn_(int severity, log_domain_mask_t domain, const char *fn, const char *format, ...) { va_list ap; - if (severity > _log_global_min_severity) + if (severity > log_global_min_severity_) return; va_start(ap,format); - logv(severity, domain, fn, format, ap); + logv(severity, domain, fn, NULL, format, ap); va_end(ap); } +void +log_fn_ratelim_(ratelim_t *ratelim, int severity, log_domain_mask_t domain, + const char *fn, const char *format, ...) +{ + va_list ap; + char *m; + if (severity > log_global_min_severity_) + return; + m = rate_limit_log(ratelim, approx_time()); + if (m == NULL) + return; + va_start(ap, format); + logv(severity, domain, fn, m, format, ap); + va_end(ap); + tor_free(m); +} #else /** @{ */ /** Variant implementation of log_fn, log_debug, log_info,... for C compilers * without variadic macros. In this case, the calling function sets - * _log_fn_function_name to the name of the function, then invokes the - * appropriate _log_fn, _log_debug, etc. */ -const char *_log_fn_function_name=NULL; + * log_fn_function_name_ to the name of the function, then invokes the + * appropriate log_fn_, log_debug_, etc. */ +const char *log_fn_function_name_=NULL; void -_log_fn(int severity, log_domain_mask_t domain, const char *format, ...) +log_fn_(int severity, log_domain_mask_t domain, const char *format, ...) { va_list ap; - if (severity > _log_global_min_severity) + if (severity > log_global_min_severity_) return; va_start(ap,format); - logv(severity, domain, _log_fn_function_name, format, ap); + logv(severity, domain, log_fn_function_name_, NULL, format, ap); va_end(ap); - _log_fn_function_name = NULL; + log_fn_function_name_ = NULL; } void -_log_debug(log_domain_mask_t domain, const char *format, ...) +log_fn_ratelim_(ratelim_t *ratelim, int severity, log_domain_mask_t domain, + const char *format, ...) +{ + va_list ap; + char *m; + if (severity > log_global_min_severity_) + return; + m = rate_limit_log(ratelim, approx_time()); + if (m == NULL) + return; + va_start(ap, format); + logv(severity, domain, log_fn_function_name_, m, format, ap); + va_end(ap); + tor_free(m); +} +void +log_debug_(log_domain_mask_t domain, const char *format, ...) { va_list ap; /* For GCC we do this check in the macro. */ - if (PREDICT_LIKELY(LOG_DEBUG > _log_global_min_severity)) + if (PREDICT_LIKELY(LOG_DEBUG > log_global_min_severity_)) return; va_start(ap,format); - logv(LOG_DEBUG, domain, _log_fn_function_name, format, ap); + logv(LOG_DEBUG, domain, log_fn_function_name_, NULL, format, ap); va_end(ap); - _log_fn_function_name = NULL; + log_fn_function_name_ = NULL; } void -_log_info(log_domain_mask_t domain, const char *format, ...) +log_info_(log_domain_mask_t domain, const char *format, ...) { va_list ap; - if (LOG_INFO > _log_global_min_severity) + if (LOG_INFO > log_global_min_severity_) return; va_start(ap,format); - logv(LOG_INFO, domain, _log_fn_function_name, format, ap); + logv(LOG_INFO, domain, log_fn_function_name_, NULL, format, ap); va_end(ap); - _log_fn_function_name = NULL; + log_fn_function_name_ = NULL; } void -_log_notice(log_domain_mask_t domain, const char *format, ...) +log_notice_(log_domain_mask_t domain, const char *format, ...) { va_list ap; - if (LOG_NOTICE > _log_global_min_severity) + if (LOG_NOTICE > log_global_min_severity_) return; va_start(ap,format); - logv(LOG_NOTICE, domain, _log_fn_function_name, format, ap); + logv(LOG_NOTICE, domain, log_fn_function_name_, NULL, format, ap); va_end(ap); - _log_fn_function_name = NULL; + log_fn_function_name_ = NULL; } void -_log_warn(log_domain_mask_t domain, const char *format, ...) +log_warn_(log_domain_mask_t domain, const char *format, ...) { va_list ap; - if (LOG_WARN > _log_global_min_severity) + if (LOG_WARN > log_global_min_severity_) return; va_start(ap,format); - logv(LOG_WARN, domain, _log_fn_function_name, format, ap); + logv(LOG_WARN, domain, log_fn_function_name_, NULL, format, ap); va_end(ap); - _log_fn_function_name = NULL; + log_fn_function_name_ = NULL; } void -_log_err(log_domain_mask_t domain, const char *format, ...) +log_err_(log_domain_mask_t domain, const char *format, ...) { va_list ap; - if (LOG_ERR > _log_global_min_severity) + if (LOG_ERR > log_global_min_severity_) return; va_start(ap,format); - logv(LOG_ERR, domain, _log_fn_function_name, format, ap); + logv(LOG_ERR, domain, log_fn_function_name_, NULL, format, ap); va_end(ap); - _log_fn_function_name = NULL; + log_fn_function_name_ = NULL; } /** @} */ #endif @@ -638,7 +823,7 @@ add_stream_log_impl(const log_severity_list_t *severity, lf->next = logfiles; logfiles = lf; - _log_global_min_severity = get_min_log_level(); + log_global_min_severity_ = get_min_log_level(); } /** Add a log handler named <b>name</b> to send all messages in <b>severity</b> @@ -706,7 +891,7 @@ add_callback_log(const log_severity_list_t *severity, log_callback cb) LOCK_LOGS(); logfiles = lf; - _log_global_min_severity = get_min_log_level(); + log_global_min_severity_ = get_min_log_level(); UNLOCK_LOGS(); return 0; } @@ -726,7 +911,7 @@ change_callback_log_severity(int loglevelMin, int loglevelMax, memcpy(lf->severities, &severities, sizeof(severities)); } } - _log_global_min_severity = get_min_log_level(); + log_global_min_severity_ = get_min_log_level(); UNLOCK_LOGS(); } @@ -792,7 +977,7 @@ close_temp_logs(void) } } - _log_global_min_severity = get_min_log_level(); + log_global_min_severity_ = get_min_log_level(); UNLOCK_LOGS(); } @@ -833,14 +1018,16 @@ add_file_log(const log_severity_list_t *severity, const char *filename) fd = tor_open_cloexec(filename, O_WRONLY|O_CREAT|O_APPEND, 0644); if (fd<0) return -1; - if (tor_fd_seekend(fd)<0) + if (tor_fd_seekend(fd)<0) { + close(fd); return -1; + } LOCK_LOGS(); add_stream_log_impl(severity, filename, fd); logfiles->needs_close = 1; lf = logfiles; - _log_global_min_severity = get_min_log_level(); + log_global_min_severity_ = get_min_log_level(); if (log_tor_version(lf, 0) < 0) { delete_log(lf); @@ -871,7 +1058,7 @@ add_syslog_log(const log_severity_list_t *severity) LOCK_LOGS(); lf->next = logfiles; logfiles = lf; - _log_global_min_severity = get_min_log_level(); + log_global_min_severity_ = get_min_log_level(); UNLOCK_LOGS(); return 0; } @@ -907,7 +1094,7 @@ log_level_to_string(int level) static const char *domain_list[] = { "GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM", "HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV", - "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", NULL + "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", "CHANNEL", NULL }; /** Return a bitmask for the log domain for which <b>domain</b> is the name, @@ -1106,42 +1293,7 @@ switch_logs_debug(void) for (i = LOG_DEBUG; i >= LOG_ERR; --i) lf->severities->masks[SEVERITY_MASK_IDX(i)] = ~0u; } - _log_global_min_severity = get_min_log_level(); + log_global_min_severity_ = get_min_log_level(); UNLOCK_LOGS(); } -#if 0 -static void -dump_log_info(logfile_t *lf) -{ - const char *tp; - - if (lf->filename) { - printf("=== log into \"%s\" (%s-%s) (%stemporary)\n", lf->filename, - sev_to_string(lf->min_loglevel), - sev_to_string(lf->max_loglevel), - lf->is_temporary?"":"not "); - } else if (lf->is_syslog) { - printf("=== syslog (%s-%s) (%stemporary)\n", - sev_to_string(lf->min_loglevel), - sev_to_string(lf->max_loglevel), - lf->is_temporary?"":"not "); - } else { - printf("=== log (%s-%s) (%stemporary)\n", - sev_to_string(lf->min_loglevel), - sev_to_string(lf->max_loglevel), - lf->is_temporary?"":"not "); - } -} - -void -describe_logs(void) -{ - logfile_t *lf; - printf("==== BEGIN LOGS ====\n"); - for (lf = logfiles; lf; lf = lf->next) - dump_log_info(lf); - printf("==== END LOGS ====\n"); -} -#endif - diff --git a/src/common/memarea.c b/src/common/memarea.c index 07bd593cc..bcaea0949 100644 --- a/src/common/memarea.c +++ b/src/common/memarea.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2012, The Tor Project, Inc. */ +/* Copyright (c) 2008-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** \file memarea.c @@ -29,6 +29,13 @@ #error "void* is neither 4 nor 8 bytes long. I don't know how to align stuff." #endif +#if defined(__GNUC__) && defined(FLEXIBLE_ARRAY_MEMBER) +#define USE_ALIGNED_ATTRIBUTE +#define U_MEM mem +#else +#define U_MEM u.mem +#endif + #ifdef USE_SENTINELS /** Magic value that we stick at the end of a memarea so we can make sure * there are no run-off-the-end bugs. */ @@ -39,12 +46,12 @@ * end, set those bytes. */ #define SET_SENTINEL(chunk) \ STMT_BEGIN \ - set_uint32( &(chunk)->u.mem[chunk->mem_size], SENTINEL_VAL ); \ + set_uint32( &(chunk)->U_MEM[chunk->mem_size], SENTINEL_VAL ); \ STMT_END /** Assert that the sentinel on a memarea is set correctly. */ #define CHECK_SENTINEL(chunk) \ STMT_BEGIN \ - uint32_t sent_val = get_uint32(&(chunk)->u.mem[chunk->mem_size]); \ + uint32_t sent_val = get_uint32(&(chunk)->U_MEM[chunk->mem_size]); \ tor_assert(sent_val == SENTINEL_VAL); \ STMT_END #else @@ -71,19 +78,23 @@ realign_pointer(void *ptr) typedef struct memarea_chunk_t { /** Next chunk in this area. Only kept around so we can free it. */ struct memarea_chunk_t *next_chunk; - size_t mem_size; /**< How much RAM is available in u.mem, total? */ - char *next_mem; /**< Next position in u.mem to allocate data at. If it's + size_t mem_size; /**< How much RAM is available in mem, total? */ + char *next_mem; /**< Next position in mem to allocate data at. If it's * greater than or equal to mem+mem_size, this chunk is * full. */ +#ifdef USE_ALIGNED_ATTRIBUTE + char mem[FLEXIBLE_ARRAY_MEMBER] __attribute__((aligned(MEMAREA_ALIGN))); +#else union { char mem[1]; /**< Memory space in this chunk. */ - void *_void_for_alignment; /**< Dummy; used to make sure mem is aligned. */ + void *void_for_alignment_; /**< Dummy; used to make sure mem is aligned. */ } u; +#endif } memarea_chunk_t; /** How many bytes are needed for overhead before we get to the memory part * of a chunk? */ -#define CHUNK_HEADER_SIZE STRUCT_OFFSET(memarea_chunk_t, u) +#define CHUNK_HEADER_SIZE STRUCT_OFFSET(memarea_chunk_t, U_MEM) /** What's the smallest that we'll allocate a chunk? */ #define CHUNK_SIZE 4096 @@ -118,10 +129,10 @@ alloc_chunk(size_t sz, int freelist_ok) size_t chunk_size = freelist_ok ? CHUNK_SIZE : sz; memarea_chunk_t *res; chunk_size += SENTINEL_LEN; - res = tor_malloc_roundup(&chunk_size); + res = tor_malloc(chunk_size); res->next_chunk = NULL; res->mem_size = chunk_size - CHUNK_HEADER_SIZE - SENTINEL_LEN; - res->next_mem = res->u.mem; + res->next_mem = res->U_MEM; tor_assert(res->next_mem+res->mem_size+SENTINEL_LEN == ((char*)res)+chunk_size); tor_assert(realign_pointer(res->next_mem) == res->next_mem); @@ -140,7 +151,7 @@ chunk_free_unchecked(memarea_chunk_t *chunk) ++freelist_len; chunk->next_chunk = freelist; freelist = chunk; - chunk->next_mem = chunk->u.mem; + chunk->next_mem = chunk->U_MEM; } else { tor_free(chunk); } @@ -183,7 +194,7 @@ memarea_clear(memarea_t *area) } area->first->next_chunk = NULL; } - area->first->next_mem = area->first->u.mem; + area->first->next_mem = area->first->U_MEM; } /** Remove all unused memarea chunks from the internal freelist. */ @@ -207,7 +218,7 @@ memarea_owns_ptr(const memarea_t *area, const void *p) memarea_chunk_t *chunk; const char *ptr = p; for (chunk = area->first; chunk; chunk = chunk->next_chunk) { - if (ptr >= chunk->u.mem && ptr < chunk->next_mem) + if (ptr >= chunk->U_MEM && ptr < chunk->next_mem) return 1; } return 0; @@ -226,7 +237,7 @@ memarea_alloc(memarea_t *area, size_t sz) tor_assert(sz < SIZE_T_CEILING); if (sz == 0) sz = 1; - if (chunk->next_mem+sz > chunk->u.mem+chunk->mem_size) { + if (chunk->next_mem+sz > chunk->U_MEM+chunk->mem_size) { if (sz+CHUNK_HEADER_SIZE >= CHUNK_SIZE) { /* This allocation is too big. Stick it in a special chunk, and put * that chunk second in the list. */ @@ -244,8 +255,8 @@ memarea_alloc(memarea_t *area, size_t sz) result = chunk->next_mem; chunk->next_mem = chunk->next_mem + sz; /* Reinstate these if bug 930 ever comes back - tor_assert(chunk->next_mem >= chunk->u.mem); - tor_assert(chunk->next_mem <= chunk->u.mem+chunk->mem_size); + tor_assert(chunk->next_mem >= chunk->U_MEM); + tor_assert(chunk->next_mem <= chunk->U_MEM+chunk->mem_size); */ chunk->next_mem = realign_pointer(chunk->next_mem); return result; @@ -280,14 +291,11 @@ memarea_strdup(memarea_t *area, const char *s) char * memarea_strndup(memarea_t *area, const char *s, size_t n) { - size_t ln; + size_t ln = 0; char *result; - const char *cp, *end = s+n; tor_assert(n < SIZE_T_CEILING); - for (cp = s; cp < end && *cp; ++cp) + for (ln = 0; ln < n && s[ln]; ++ln) ; - /* cp now points to s+n, or to the 0 in the string. */ - ln = cp-s; result = memarea_alloc(area, ln+1); memcpy(result, s, ln); result[ln]='\0'; @@ -304,8 +312,8 @@ memarea_get_stats(memarea_t *area, size_t *allocated_out, size_t *used_out) for (chunk = area->first; chunk; chunk = chunk->next_chunk) { CHECK_SENTINEL(chunk); a += CHUNK_HEADER_SIZE + chunk->mem_size; - tor_assert(chunk->next_mem >= chunk->u.mem); - u += CHUNK_HEADER_SIZE + (chunk->next_mem - chunk->u.mem); + tor_assert(chunk->next_mem >= chunk->U_MEM); + u += CHUNK_HEADER_SIZE + (chunk->next_mem - chunk->U_MEM); } *allocated_out = a; *used_out = u; @@ -320,9 +328,9 @@ memarea_assert_ok(memarea_t *area) for (chunk = area->first; chunk; chunk = chunk->next_chunk) { CHECK_SENTINEL(chunk); - tor_assert(chunk->next_mem >= chunk->u.mem); + tor_assert(chunk->next_mem >= chunk->U_MEM); tor_assert(chunk->next_mem <= - (char*) realign_pointer(chunk->u.mem+chunk->mem_size)); + (char*) realign_pointer(chunk->U_MEM+chunk->mem_size)); } } diff --git a/src/common/memarea.h b/src/common/memarea.h index b3c76d8d0..8b88585d3 100644 --- a/src/common/memarea.h +++ b/src/common/memarea.h @@ -1,9 +1,9 @@ -/* Copyright (c) 2008-2012, The Tor Project, Inc. */ +/* Copyright (c) 2008-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Tor dependencies */ -#ifndef _TOR_MEMAREA_H -#define _TOR_MEMAREA_H +#ifndef TOR_MEMAREA_H +#define TOR_MEMAREA_H typedef struct memarea_t memarea_t; diff --git a/src/common/mempool.c b/src/common/mempool.c index 637f081c8..438988876 100644 --- a/src/common/mempool.c +++ b/src/common/mempool.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2012, The Tor Project, Inc. */ +/* Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #if 1 /* Tor dependencies */ @@ -71,7 +71,6 @@ #define ASSERT(x) tor_assert(x) #undef ALLOC_CAN_RETURN_NULL #define TOR -//#define ALLOC_ROUNDUP(p) tor_malloc_roundup(p) /* End Tor dependencies */ #else /* If you're not building this as part of Tor, you'll want to define the @@ -115,7 +114,7 @@ struct mp_allocated_t { * (Not actual size.) */ char mem[1]; /** An extra element to the union to insure correct alignment. */ - ALIGNMENT_TYPE _dummy; + ALIGNMENT_TYPE dummy_; } u; }; @@ -166,25 +165,16 @@ static mp_chunk_t * mp_chunk_new(mp_pool_t *pool) { size_t sz = pool->new_chunk_capacity * pool->item_alloc_size; -#ifdef ALLOC_ROUNDUP - size_t alloc_size = CHUNK_OVERHEAD + sz; - mp_chunk_t *chunk = ALLOC_ROUNDUP(&alloc_size); -#else mp_chunk_t *chunk = ALLOC(CHUNK_OVERHEAD + sz); -#endif + #ifdef MEMPOOL_STATS ++pool->total_chunks_allocated; #endif CHECK_ALLOC(chunk); memset(chunk, 0, sizeof(mp_chunk_t)); /* Doesn't clear the whole thing. */ chunk->magic = MP_CHUNK_MAGIC; -#ifdef ALLOC_ROUNDUP - chunk->mem_size = alloc_size - CHUNK_OVERHEAD; - chunk->capacity = chunk->mem_size / pool->item_alloc_size; -#else chunk->capacity = pool->new_chunk_capacity; chunk->mem_size = sz; -#endif chunk->next_mem = chunk->mem; chunk->pool = pool; return chunk; diff --git a/src/common/mempool.h b/src/common/mempool.h index d0a7bc2f3..0fc1e4c67 100644 --- a/src/common/mempool.h +++ b/src/common/mempool.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2012, The Tor Project, Inc. */ +/* Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -6,8 +6,8 @@ * \brief Headers for mempool.c **/ -#ifndef _TOR_MEMPOOL_H -#define _TOR_MEMPOOL_H +#ifndef TOR_MEMPOOL_H +#define TOR_MEMPOOL_H /** A memory pool is a context in which a large number of fixed-sized * objects can be allocated efficiently. See mempool.c for implementation @@ -22,6 +22,8 @@ void mp_pool_destroy(mp_pool_t *pool); void mp_pool_assert_ok(mp_pool_t *pool); void mp_pool_log_status(mp_pool_t *pool, int severity); +#define MP_POOL_ITEM_OVERHEAD (sizeof(void*)) + #define MEMPOOL_STATS #ifdef MEMPOOL_PRIVATE diff --git a/src/common/procmon.c b/src/common/procmon.c index 36b1a4855..0a49689e3 100644 --- a/src/common/procmon.c +++ b/src/common/procmon.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, The Tor Project, Inc. */ +/* Copyright (c) 2011-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -25,9 +25,21 @@ #ifdef _WIN32 #include <windows.h> +#endif -/* Windows does not define pid_t, but _getpid() returns an int. */ +#if (0 == SIZEOF_PID_T) && defined(_WIN32) +/* Windows does not define pid_t sometimes, but _getpid() returns an int. + * Everybody else needs to have a pid_t. */ typedef int pid_t; +#define PID_T_FORMAT "%d" +#elif (SIZEOF_PID_T == SIZEOF_INT) || (SIZEOF_PID_T == SIZEOF_SHORT) +#define PID_T_FORMAT "%d" +#elif (SIZEOF_PID_T == SIZEOF_LONG) +#define PID_T_FORMAT "%ld" +#elif (SIZEOF_PID_T == SIZEOF_INT64_T) +#define PID_T_FORMAT I64_FORMAT +#else +#error Unknown: SIZEOF_PID_T #endif /* Define to 1 if process-termination monitors on this OS and Libevent @@ -204,15 +216,17 @@ tor_process_monitor_new(struct event_base *base, if (procmon->hproc != NULL) { procmon->poll_hproc = 1; - log_info(procmon->log_domain, "Successfully opened handle to process %d; " + log_info(procmon->log_domain, "Successfully opened handle to process " + PID_T_FORMAT"; " "monitoring it.", - (int)(procmon->pid)); + procmon->pid); } else { /* If we couldn't get a handle to the process, we'll try again the * first time we poll. */ - log_info(procmon->log_domain, "Failed to open handle to process %d; will " + log_info(procmon->log_domain, "Failed to open handle to process " + PID_T_FORMAT"; will " "try again later.", - (int)(procmon->pid)); + procmon->pid); } #endif @@ -257,7 +271,8 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2, if (!GetExitCodeProcess(procmon->hproc, &exit_code)) { char *errmsg = format_win32_error(GetLastError()); log_warn(procmon->log_domain, "Error \"%s\" occurred while polling " - "handle for monitored process %d; assuming it's dead.", + "handle for monitored process "PID_T_FORMAT"; assuming " + "it's dead.", errmsg, procmon->pid); tor_free(errmsg); its_dead_jim = 1; @@ -273,7 +288,7 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2, if (procmon->hproc != NULL) { log_info(procmon->log_domain, "Successfully opened handle to monitored " - "process %d.", + "process "PID_T_FORMAT".", procmon->pid); its_dead_jim = 0; procmon->poll_hproc = 1; @@ -292,8 +307,8 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2, if (!its_dead_jim) log_info(procmon->log_domain, "Failed to open handle to monitored " - "process %d, and error code %lu (%s) is not 'invalid " - "parameter' -- assuming the process is still alive.", + "process "PID_T_FORMAT", and error code %lu (%s) is not " + "'invalid parameter' -- assuming the process is still alive.", procmon->pid, err_code, errmsg); @@ -306,9 +321,9 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2, its_dead_jim = its_dead_jim && (errno == ESRCH); #endif - log(its_dead_jim ? LOG_NOTICE : LOG_INFO, - procmon->log_domain, "Monitored process %d is %s.", - (int)procmon->pid, + tor_log(its_dead_jim ? LOG_NOTICE : LOG_INFO, + procmon->log_domain, "Monitored process "PID_T_FORMAT" is %s.", + procmon->pid, its_dead_jim ? "dead" : "still alive"); if (its_dead_jim) { diff --git a/src/common/procmon.h b/src/common/procmon.h index 88d64d6a1..b9388e2e9 100644 --- a/src/common/procmon.h +++ b/src/common/procmon.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, The Tor Project, Inc. */ +/* Copyright (c) 2011-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/common/sandbox.c b/src/common/sandbox.c new file mode 100644 index 000000000..4721b8dfc --- /dev/null +++ b/src/common/sandbox.c @@ -0,0 +1,1758 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file sandbox.c + * \brief Code to enable sandboxing. + **/ + +#include "orconfig.h" + +#ifndef _LARGEFILE64_SOURCE +/** + * Temporarily required for O_LARGEFILE flag. Needs to be removed + * with the libevent fix. + */ +#define _LARGEFILE64_SOURCE +#endif + +/** Malloc mprotect limit in bytes. */ +#define MALLOC_MP_LIM 1048576 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "sandbox.h" +#include "container.h" +#include "torlog.h" +#include "torint.h" +#include "util.h" +#include "tor_queue.h" + +#define DEBUGGING_CLOSE + +#if defined(USE_LIBSECCOMP) + +#define _GNU_SOURCE + +#include <sys/mman.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/epoll.h> +#include <sys/prctl.h> +#include <linux/futex.h> +#include <bits/signum.h> + +#include <stdarg.h> +#include <seccomp.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <poll.h> + +#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \ + defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION) +#define USE_BACKTRACE +#define EXPOSE_CLEAN_BACKTRACE +#include "backtrace.h" +#endif + +#ifdef USE_BACKTRACE +#include <execinfo.h> +#endif + +/** + * Linux 32 bit definitions + */ +#if defined(__i386__) + +#define REG_SYSCALL REG_EAX +#define M_SYSCALL gregs[REG_SYSCALL] + +/** + * Linux 64 bit definitions + */ +#elif defined(__x86_64__) + +#define REG_SYSCALL REG_RAX +#define M_SYSCALL gregs[REG_SYSCALL] + +#elif defined(__arm__) + +#define M_SYSCALL arm_r7 + +#endif + +/**Determines if at least one sandbox is active.*/ +static int sandbox_active = 0; +/** Holds the parameter list configuration for the sandbox.*/ +static sandbox_cfg_t *filter_dynamic = NULL; +/** Holds a list of pre-recorded results from getaddrinfo().*/ +static sb_addr_info_t *sb_addr_info = NULL; + +#undef SCMP_CMP +#define SCMP_CMP(a,b,c) ((struct scmp_arg_cmp){(a),(b),(c),0}) +#define SCMP_CMP4(a,b,c,d) ((struct scmp_arg_cmp){(a),(b),(c),(d)}) +/* We use a wrapper here because these masked comparisons seem to be pretty + * verbose. Also, it's important to cast to scmp_datum_t before negating the + * mask, since otherwise the negation might get applied to a 32 bit value, and + * the high bits of the value might get masked out improperly. */ +#define SCMP_CMP_MASKED(a,b,c) \ + SCMP_CMP4((a), SCMP_CMP_MASKED_EQ, ~(scmp_datum_t)(b), (c)) + +/** Variable used for storing all syscall numbers that will be allowed with the + * stage 1 general Tor sandbox. + */ +static int filter_nopar_gen[] = { + SCMP_SYS(access), + SCMP_SYS(brk), + SCMP_SYS(clock_gettime), + SCMP_SYS(close), + SCMP_SYS(clone), + SCMP_SYS(epoll_create), + SCMP_SYS(epoll_wait), + SCMP_SYS(fcntl), + SCMP_SYS(fstat), +#ifdef __NR_fstat64 + SCMP_SYS(fstat64), +#endif + SCMP_SYS(getdents64), + SCMP_SYS(getegid), +#ifdef __NR_getegid32 + SCMP_SYS(getegid32), +#endif + SCMP_SYS(geteuid), +#ifdef __NR_geteuid32 + SCMP_SYS(geteuid32), +#endif + SCMP_SYS(getgid), +#ifdef __NR_getgid32 + SCMP_SYS(getgid32), +#endif +#ifdef __NR_getrlimit + SCMP_SYS(getrlimit), +#endif + SCMP_SYS(gettimeofday), + SCMP_SYS(gettid), + SCMP_SYS(getuid), +#ifdef __NR_getuid32 + SCMP_SYS(getuid32), +#endif + SCMP_SYS(lseek), +#ifdef __NR__llseek + SCMP_SYS(_llseek), +#endif + SCMP_SYS(mkdir), + SCMP_SYS(mlockall), +#ifdef __NR_mmap + /* XXXX restrict this in the same ways as mmap2 */ + SCMP_SYS(mmap), +#endif + SCMP_SYS(munmap), + SCMP_SYS(read), + SCMP_SYS(rt_sigreturn), + SCMP_SYS(sched_getaffinity), + SCMP_SYS(set_robust_list), +#ifdef __NR_sigreturn + SCMP_SYS(sigreturn), +#endif + SCMP_SYS(stat), + SCMP_SYS(uname), + SCMP_SYS(write), + SCMP_SYS(writev), + SCMP_SYS(exit_group), + SCMP_SYS(exit), + + SCMP_SYS(madvise), +#ifdef __NR_stat64 + // getaddrinfo uses this.. + SCMP_SYS(stat64), +#endif + + /* + * These socket syscalls are not required on x86_64 and not supported with + * some libseccomp versions (eg: 1.0.1) + */ +#if defined(__i386) + SCMP_SYS(recv), + SCMP_SYS(send), +#endif + + // socket syscalls + SCMP_SYS(bind), + SCMP_SYS(listen), + SCMP_SYS(connect), + SCMP_SYS(getsockname), + SCMP_SYS(recvmsg), + SCMP_SYS(recvfrom), + SCMP_SYS(sendto), + SCMP_SYS(unlink) +}; + +/* These macros help avoid the error where the number of filters we add on a + * single rule don't match the arg_cnt param. */ +#define seccomp_rule_add_0(ctx,act,call) \ + seccomp_rule_add((ctx),(act),(call),0) +#define seccomp_rule_add_1(ctx,act,call,f1) \ + seccomp_rule_add((ctx),(act),(call),1,(f1)) +#define seccomp_rule_add_2(ctx,act,call,f1,f2) \ + seccomp_rule_add((ctx),(act),(call),2,(f1),(f2)) +#define seccomp_rule_add_3(ctx,act,call,f1,f2,f3) \ + seccomp_rule_add((ctx),(act),(call),3,(f1),(f2),(f3)) +#define seccomp_rule_add_4(ctx,act,call,f1,f2,f3,f4) \ + seccomp_rule_add((ctx),(act),(call),4,(f1),(f2),(f3),(f4)) + +/** + * Function responsible for setting up the rt_sigaction syscall for + * the seccomp filter sandbox. + */ +static int +sb_rt_sigaction(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + unsigned i; + int rc; + int param[] = { SIGINT, SIGTERM, SIGPIPE, SIGUSR1, SIGUSR2, SIGHUP, SIGCHLD, +#ifdef SIGXFSZ + SIGXFSZ +#endif + }; + (void) filter; + + for (i = 0; i < ARRAY_LENGTH(param); i++) { + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), + SCMP_CMP(0, SCMP_CMP_EQ, param[i])); + if (rc) + break; + } + + return rc; +} + +#if 0 +/** + * Function responsible for setting up the execve syscall for + * the seccomp filter sandbox. + */ +static int +sb_execve(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc; + sandbox_cfg_t *elem = NULL; + + // for each dynamic parameter filters + for (elem = filter; elem != NULL; elem = elem->next) { + smp_param_t *param = elem->param; + + if (param != NULL && param->prot == 1 && param->syscall + == SCMP_SYS(execve)) { + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve), + SCMP_CMP(0, SCMP_CMP_EQ, param->value)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add execve syscall, received " + "libseccomp error %d", rc); + return rc; + } + } + } + + return 0; +} +#endif + +/** + * Function responsible for setting up the time syscall for + * the seccomp filter sandbox. + */ +static int +sb_time(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + (void) filter; +#ifdef __NR_time + return seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(time), + SCMP_CMP(0, SCMP_CMP_EQ, 0)); +#else + return 0; +#endif +} + +/** + * Function responsible for setting up the accept4 syscall for + * the seccomp filter sandbox. + */ +static int +sb_accept4(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void)filter; + +#ifdef __i386__ + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketcall), + SCMP_CMP(0, SCMP_CMP_EQ, 18)); + if (rc) { + return rc; + } +#endif + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), + SCMP_CMP_MASKED(3, SOCK_CLOEXEC|SOCK_NONBLOCK, 0)); + if (rc) { + return rc; + } + + return 0; +} + +#ifdef __NR_mmap2 +/** + * Function responsible for setting up the mmap2 syscall for + * the seccomp filter sandbox. + */ +static int +sb_mmap2(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void)filter; + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ), + SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE)); + if (rc) { + return rc; + } + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE), + SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE)); + if (rc) { + return rc; + } + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), + SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS)); + if (rc) { + return rc; + } + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), + SCMP_CMP(3, SCMP_CMP_EQ,MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK)); + if (rc) { + return rc; + } + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), + SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE)); + if (rc) { + return rc; + } + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), + SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS)); + if (rc) { + return rc; + } + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_EXEC), + SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_DENYWRITE)); + if (rc) { + return rc; + } + + return 0; +} +#endif + +/** + * Function responsible for setting up the open syscall for + * the seccomp filter sandbox. + */ +static int +sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc; + sandbox_cfg_t *elem = NULL; + + // for each dynamic parameter filters + for (elem = filter; elem != NULL; elem = elem->next) { + smp_param_t *param = elem->param; + + if (param != NULL && param->prot == 1 && param->syscall + == SCMP_SYS(open)) { + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), + SCMP_CMP(0, SCMP_CMP_EQ, param->value)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add open syscall, received " + "libseccomp error %d", rc); + return rc; + } + } + } + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), + SCMP_CMP_MASKED(1, O_CLOEXEC|O_NONBLOCK|O_NOCTTY, O_RDONLY)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add open syscall, received libseccomp " + "error %d", rc); + return rc; + } + + return 0; +} + +static int +sb__sysctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc; + (void) filter; + (void) ctx; + + rc = seccomp_rule_add_0(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(_sysctl)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add _sysctl syscall, " + "received libseccomp error %d", rc); + return rc; + } + + return 0; +} + +/** + * Function responsible for setting up the rename syscall for + * the seccomp filter sandbox. + */ +static int +sb_rename(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc; + sandbox_cfg_t *elem = NULL; + + // for each dynamic parameter filters + for (elem = filter; elem != NULL; elem = elem->next) { + smp_param_t *param = elem->param; + + if (param != NULL && param->prot == 1 && + param->syscall == SCMP_SYS(rename)) { + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rename), + SCMP_CMP(0, SCMP_CMP_EQ, param->value), + SCMP_CMP(1, SCMP_CMP_EQ, param->value2)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add rename syscall, received " + "libseccomp error %d", rc); + return rc; + } + } + } + + return 0; +} + +/** + * Function responsible for setting up the openat syscall for + * the seccomp filter sandbox. + */ +static int +sb_openat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc; + sandbox_cfg_t *elem = NULL; + + // for each dynamic parameter filters + for (elem = filter; elem != NULL; elem = elem->next) { + smp_param_t *param = elem->param; + + if (param != NULL && param->prot == 1 && param->syscall + == SCMP_SYS(openat)) { + rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), + SCMP_CMP(0, SCMP_CMP_EQ, AT_FDCWD), + SCMP_CMP(1, SCMP_CMP_EQ, param->value), + SCMP_CMP(2, SCMP_CMP_EQ, O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY| + O_CLOEXEC)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add openat syscall, received " + "libseccomp error %d", rc); + return rc; + } + } + } + + return 0; +} + +/** + * Function responsible for setting up the socket syscall for + * the seccomp filter sandbox. + */ +static int +sb_socket(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + int i; + (void) filter; + +#ifdef __i386__ + rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket)); + if (rc) + return rc; +#endif + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), + SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE), + SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_STREAM)); + if (rc) + return rc; + + for (i = 0; i < 2; ++i) { + const int pf = i ? PF_INET : PF_INET6; + + rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), + SCMP_CMP(0, SCMP_CMP_EQ, pf), + SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_STREAM), + SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_TCP)); + if (rc) + return rc; + + rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), + SCMP_CMP(0, SCMP_CMP_EQ, pf), + SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_DGRAM), + SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP)); + if (rc) + return rc; + } + + rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), + SCMP_CMP(0, SCMP_CMP_EQ, PF_NETLINK), + SCMP_CMP(1, SCMP_CMP_EQ, SOCK_RAW), + SCMP_CMP(2, SCMP_CMP_EQ, 0)); + if (rc) + return rc; + + return 0; +} + +/** + * Function responsible for setting up the socketpair syscall for + * the seccomp filter sandbox. + */ +static int +sb_socketpair(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + +#ifdef __i386__ + rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair)); + if (rc) + return rc; +#endif + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair), + SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE), + SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC)); + if (rc) + return rc; + + return 0; +} + +/** + * Function responsible for setting up the setsockopt syscall for + * the seccomp filter sandbox. + */ +static int +sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + +#ifdef __i386__ + rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt)); + if (rc) + return rc; +#endif + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), + SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), + SCMP_CMP(2, SCMP_CMP_EQ, SO_REUSEADDR)); + if (rc) + return rc; + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), + SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), + SCMP_CMP(2, SCMP_CMP_EQ, SO_SNDBUF)); + if (rc) + return rc; + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), + SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), + SCMP_CMP(2, SCMP_CMP_EQ, SO_RCVBUF)); + if (rc) + return rc; + +#ifdef IP_TRANSPARENT + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), + SCMP_CMP(1, SCMP_CMP_EQ, SOL_IP), + SCMP_CMP(2, SCMP_CMP_EQ, IP_TRANSPARENT)); + if (rc) + return rc; +#endif + + return 0; +} + +/** + * Function responsible for setting up the getsockopt syscall for + * the seccomp filter sandbox. + */ +static int +sb_getsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + +#ifdef __i386__ + rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt)); + if (rc) + return rc; +#endif + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), + SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), + SCMP_CMP(2, SCMP_CMP_EQ, SO_ERROR)); + if (rc) + return rc; + + return 0; +} + +#ifdef __NR_fcntl64 +/** + * Function responsible for setting up the fcntl64 syscall for + * the seccomp filter sandbox. + */ +static int +sb_fcntl64(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), + SCMP_CMP(1, SCMP_CMP_EQ, F_GETFL)); + if (rc) + return rc; + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), + SCMP_CMP(1, SCMP_CMP_EQ, F_SETFL), + SCMP_CMP(2, SCMP_CMP_EQ, O_RDWR|O_NONBLOCK)); + if (rc) + return rc; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), + SCMP_CMP(1, SCMP_CMP_EQ, F_GETFD)); + if (rc) + return rc; + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), + SCMP_CMP(1, SCMP_CMP_EQ, F_SETFD), + SCMP_CMP(2, SCMP_CMP_EQ, FD_CLOEXEC)); + if (rc) + return rc; + + return 0; +} +#endif + +/** + * Function responsible for setting up the epoll_ctl syscall for + * the seccomp filter sandbox. + * + * Note: basically allows everything but will keep for now.. + */ +static int +sb_epoll_ctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), + SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_ADD)); + if (rc) + return rc; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), + SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_MOD)); + if (rc) + return rc; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), + SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_DEL)); + if (rc) + return rc; + + return 0; +} + +/** + * Function responsible for setting up the fcntl64 syscall for + * the seccomp filter sandbox. + * + * NOTE: if multiple filters need to be added, the PR_SECCOMP parameter needs + * to be whitelisted in this function. + */ +static int +sb_prctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prctl), + SCMP_CMP(0, SCMP_CMP_EQ, PR_SET_DUMPABLE)); + if (rc) + return rc; + + return 0; +} + +/** + * Function responsible for setting up the fcntl64 syscall for + * the seccomp filter sandbox. + * + * NOTE: does not NEED to be here.. currently only occurs before filter; will + * keep just in case for the future. + */ +static int +sb_mprotect(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ)); + if (rc) + return rc; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE)); + if (rc) + return rc; + + return 0; +} + +/** + * Function responsible for setting up the rt_sigprocmask syscall for + * the seccomp filter sandbox. + */ +static int +sb_rt_sigprocmask(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), + SCMP_CMP(0, SCMP_CMP_EQ, SIG_UNBLOCK)); + if (rc) + return rc; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), + SCMP_CMP(0, SCMP_CMP_EQ, SIG_SETMASK)); + if (rc) + return rc; + + return 0; +} + +/** + * Function responsible for setting up the flock syscall for + * the seccomp filter sandbox. + * + * NOTE: does not need to be here, occurs before filter is applied. + */ +static int +sb_flock(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock), + SCMP_CMP(1, SCMP_CMP_EQ, LOCK_EX|LOCK_NB)); + if (rc) + return rc; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock), + SCMP_CMP(1, SCMP_CMP_EQ, LOCK_UN)); + if (rc) + return rc; + + return 0; +} + +/** + * Function responsible for setting up the futex syscall for + * the seccomp filter sandbox. + */ +static int +sb_futex(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + + // can remove + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), + SCMP_CMP(1, SCMP_CMP_EQ, + FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME)); + if (rc) + return rc; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), + SCMP_CMP(1, SCMP_CMP_EQ, FUTEX_WAKE_PRIVATE)); + if (rc) + return rc; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), + SCMP_CMP(1, SCMP_CMP_EQ, FUTEX_WAIT_PRIVATE)); + if (rc) + return rc; + + return 0; +} + +/** + * Function responsible for setting up the mremap syscall for + * the seccomp filter sandbox. + * + * NOTE: so far only occurs before filter is applied. + */ +static int +sb_mremap(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap), + SCMP_CMP(3, SCMP_CMP_EQ, MREMAP_MAYMOVE)); + if (rc) + return rc; + + return 0; +} + +/** + * Function responsible for setting up the poll syscall for + * the seccomp filter sandbox. + */ +static int +sb_poll(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + (void) filter; + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(poll), + SCMP_CMP(1, SCMP_CMP_EQ, 1), + SCMP_CMP(2, SCMP_CMP_EQ, 10)); + if (rc) + return rc; + + return 0; +} + +#ifdef __NR_stat64 +/** + * Function responsible for setting up the stat64 syscall for + * the seccomp filter sandbox. + */ +static int +sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + sandbox_cfg_t *elem = NULL; + + // for each dynamic parameter filters + for (elem = filter; elem != NULL; elem = elem->next) { + smp_param_t *param = elem->param; + + if (param != NULL && param->prot == 1 && (param->syscall == SCMP_SYS(open) + || param->syscall == SCMP_SYS(stat64))) { + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat64), + SCMP_CMP(0, SCMP_CMP_EQ, param->value)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add open syscall, received " + "libseccomp error %d", rc); + return rc; + } + } + } + + return 0; +} +#endif + +/** + * Array of function pointers responsible for filtering different syscalls at + * a parameter level. + */ +static sandbox_filter_func_t filter_func[] = { + sb_rt_sigaction, + sb_rt_sigprocmask, +#if 0 + sb_execve, +#endif + sb_time, + sb_accept4, +#ifdef __NR_mmap2 + sb_mmap2, +#endif + sb_open, + sb_openat, + sb__sysctl, + sb_rename, +#ifdef __NR_fcntl64 + sb_fcntl64, +#endif + sb_epoll_ctl, + sb_prctl, + sb_mprotect, + sb_flock, + sb_futex, + sb_mremap, + sb_poll, +#ifdef __NR_stat64 + sb_stat64, +#endif + + sb_socket, + sb_setsockopt, + sb_getsockopt, + sb_socketpair +}; + +const char * +sandbox_intern_string(const char *str) +{ + sandbox_cfg_t *elem; + + if (str == NULL) + return NULL; + + for (elem = filter_dynamic; elem != NULL; elem = elem->next) { + smp_param_t *param = elem->param; + + if (param->prot) { + if (!strcmp(str, (char*)(param->value))) { + return (char*)param->value; + } + if (param->value2 && !strcmp(str, (char*)param->value2)) { + return (char*)param->value2; + } + } + } + + if (sandbox_active) + log_warn(LD_BUG, "No interned sandbox parameter found for %s", str); + return str; +} + +/** DOCDOC */ +static int +prot_strings_helper(strmap_t *locations, + char **pr_mem_next_p, + size_t *pr_mem_left_p, + intptr_t *value_p) +{ + char *param_val; + size_t param_size; + void *location; + + if (*value_p == 0) + return 0; + + param_val = (char*) *value_p; + param_size = strlen(param_val) + 1; + location = strmap_get(locations, param_val); + + if (location) { + // We already interned this string. + tor_free(param_val); + *value_p = (intptr_t) location; + return 0; + } else if (*pr_mem_left_p >= param_size) { + // copy to protected + location = *pr_mem_next_p; + memcpy(location, param_val, param_size); + + // re-point el parameter to protected + tor_free(param_val); + *value_p = (intptr_t) location; + + strmap_set(locations, location, location); /* good real estate advice */ + + // move next available protected memory + *pr_mem_next_p += param_size; + *pr_mem_left_p -= param_size; + return 0; + } else { + log_err(LD_BUG,"(Sandbox) insufficient protected memory!"); + return -1; + } +} + +/** + * Protects all the strings in the sandbox's parameter list configuration. It + * works by calculating the total amount of memory required by the parameter + * list, allocating the memory using mmap, and protecting it from writes with + * mprotect(). + */ +static int +prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg) +{ + int ret = 0; + size_t pr_mem_size = 0, pr_mem_left = 0; + char *pr_mem_next = NULL, *pr_mem_base; + sandbox_cfg_t *el = NULL; + strmap_t *locations = NULL; + + // get total number of bytes required to mmap. (Overestimate.) + for (el = cfg; el != NULL; el = el->next) { + pr_mem_size += strlen((char*) el->param->value) + 1; + if (el->param->value2) + pr_mem_size += strlen((char*) el->param->value2) + 1; + } + + // allocate protected memory with MALLOC_MP_LIM canary + pr_mem_base = (char*) mmap(NULL, MALLOC_MP_LIM + pr_mem_size, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (pr_mem_base == MAP_FAILED) { + log_err(LD_BUG,"(Sandbox) failed allocate protected memory! mmap: %s", + strerror(errno)); + ret = -1; + goto out; + } + + pr_mem_next = pr_mem_base + MALLOC_MP_LIM; + pr_mem_left = pr_mem_size; + + locations = strmap_new(); + + // change el value pointer to protected + for (el = cfg; el != NULL; el = el->next) { + if (prot_strings_helper(locations, &pr_mem_next, &pr_mem_left, + &el->param->value) < 0) { + ret = -2; + goto out; + } + if (prot_strings_helper(locations, &pr_mem_next, &pr_mem_left, + &el->param->value2) < 0) { + ret = -2; + goto out; + } + el->param->prot = 1; + } + + // protecting from writes + if (mprotect(pr_mem_base, MALLOC_MP_LIM + pr_mem_size, PROT_READ)) { + log_err(LD_BUG,"(Sandbox) failed to protect memory! mprotect: %s", + strerror(errno)); + ret = -3; + goto out; + } + + /* + * Setting sandbox restrictions so the string memory cannot be tampered with + */ + // no mremap of the protected base address + ret = seccomp_rule_add_1(ctx, SCMP_ACT_KILL, SCMP_SYS(mremap), + SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base)); + if (ret) { + log_err(LD_BUG,"(Sandbox) mremap protected memory filter fail!"); + return ret; + } + + // no munmap of the protected base address + ret = seccomp_rule_add_1(ctx, SCMP_ACT_KILL, SCMP_SYS(munmap), + SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base)); + if (ret) { + log_err(LD_BUG,"(Sandbox) munmap protected memory filter fail!"); + return ret; + } + + /* + * Allow mprotect with PROT_READ|PROT_WRITE because openssl uses it, but + * never over the memory region used by the protected strings. + * + * PROT_READ|PROT_WRITE was originally fully allowed in sb_mprotect(), but + * had to be removed due to limitation of libseccomp regarding intervals. + * + * There is a restriction on how much you can mprotect with R|W up to the + * size of the canary. + */ + ret = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), + SCMP_CMP(0, SCMP_CMP_LT, (intptr_t) pr_mem_base), + SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE)); + if (ret) { + log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (LT)!"); + return ret; + } + + ret = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), + SCMP_CMP(0, SCMP_CMP_GT, (intptr_t) pr_mem_base + pr_mem_size + + MALLOC_MP_LIM), + SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM), + SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE)); + if (ret) { + log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (GT)!"); + return ret; + } + + out: + strmap_free(locations, NULL); + return ret; +} + +/** + * Auxiliary function used in order to allocate a sandbox_cfg_t element and set + * it's values according the the parameter list. All elements are initialised + * with the 'prot' field set to false, as the pointer is not protected at this + * point. + */ +static sandbox_cfg_t* +new_element2(int syscall, intptr_t value, intptr_t value2) +{ + smp_param_t *param = NULL; + + sandbox_cfg_t *elem = tor_malloc_zero(sizeof(sandbox_cfg_t)); + param = elem->param = tor_malloc_zero(sizeof(smp_param_t)); + + param->syscall = syscall; + param->value = value; + param->value2 = value2; + param->prot = 0; + + return elem; +} + +static sandbox_cfg_t* +new_element(int syscall, intptr_t value) +{ + return new_element2(syscall, value, 0); +} + +#ifdef __NR_stat64 +#define SCMP_stat SCMP_SYS(stat64) +#else +#define SCMP_stat SCMP_SYS(stat) +#endif + +int +sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file) +{ + sandbox_cfg_t *elem = NULL; + + elem = new_element(SCMP_stat, (intptr_t)(void*) file); + if (!elem) { + log_err(LD_BUG,"(Sandbox) failed to register parameter!"); + return -1; + } + + elem->next = *cfg; + *cfg = elem; + + return 0; +} + +int +sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...) +{ + int rc = 0; + char *fn = NULL; + + va_list ap; + va_start(ap, cfg); + + while ((fn = va_arg(ap, char*)) != NULL) { + rc = sandbox_cfg_allow_stat_filename(cfg, fn); + if (rc) { + log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_stat_filename_array fail"); + goto end; + } + } + + end: + va_end(ap); + return 0; +} + +int +sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file) +{ + sandbox_cfg_t *elem = NULL; + + elem = new_element(SCMP_SYS(open), (intptr_t)(void *) file); + if (!elem) { + log_err(LD_BUG,"(Sandbox) failed to register parameter!"); + return -1; + } + + elem->next = *cfg; + *cfg = elem; + + return 0; +} + +int +sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2) +{ + sandbox_cfg_t *elem = NULL; + + elem = new_element2(SCMP_SYS(rename), + (intptr_t)(void *) file1, + (intptr_t)(void *) file2); + + if (!elem) { + log_err(LD_BUG,"(Sandbox) failed to register parameter!"); + return -1; + } + + elem->next = *cfg; + *cfg = elem; + + return 0; +} + +int +sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...) +{ + int rc = 0; + char *fn = NULL; + + va_list ap; + va_start(ap, cfg); + + while ((fn = va_arg(ap, char*)) != NULL) { + rc = sandbox_cfg_allow_open_filename(cfg, fn); + if (rc) { + log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_open_filename_array fail"); + goto end; + } + } + + end: + va_end(ap); + return 0; +} + +int +sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file) +{ + sandbox_cfg_t *elem = NULL; + + elem = new_element(SCMP_SYS(openat), (intptr_t)(void *) file); + if (!elem) { + log_err(LD_BUG,"(Sandbox) failed to register parameter!"); + return -1; + } + + elem->next = *cfg; + *cfg = elem; + + return 0; +} + +int +sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...) +{ + int rc = 0; + char *fn = NULL; + + va_list ap; + va_start(ap, cfg); + + while ((fn = va_arg(ap, char*)) != NULL) { + rc = sandbox_cfg_allow_openat_filename(cfg, fn); + if (rc) { + log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_openat_filename_array fail"); + goto end; + } + } + + end: + va_end(ap); + return 0; +} + +#if 0 +int +sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com) +{ + sandbox_cfg_t *elem = NULL; + + elem = new_element(SCMP_SYS(execve), (intptr_t)(void *) com); + if (!elem) { + log_err(LD_BUG,"(Sandbox) failed to register parameter!"); + return -1; + } + + elem->next = *cfg; + *cfg = elem; + + return 0; +} + +int +sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...) +{ + int rc = 0; + char *fn = NULL; + + va_list ap; + va_start(ap, cfg); + + while ((fn = va_arg(ap, char*)) != NULL) { + + rc = sandbox_cfg_allow_execve(cfg, fn); + if (rc) { + log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_execve_array failed"); + goto end; + } + } + + end: + va_end(ap); + return 0; +} +#endif + +int +sandbox_getaddrinfo(const char *name, const char *servname, + const struct addrinfo *hints, + struct addrinfo **res) +{ + sb_addr_info_t *el; + + if (servname != NULL) + return -1; + + *res = NULL; + + for (el = sb_addr_info; el; el = el->next) { + if (!strcmp(el->name, name)) { + *res = tor_malloc(sizeof(struct addrinfo)); + + memcpy(*res, el->info, sizeof(struct addrinfo)); + /* XXXX What if there are multiple items in the list? */ + return 0; + } + } + + if (!sandbox_active) { + if (getaddrinfo(name, NULL, hints, res)) { + log_err(LD_BUG,"(Sandbox) getaddrinfo failed!"); + return -1; + } + + return 0; + } + + // getting here means something went wrong + log_err(LD_BUG,"(Sandbox) failed to get address %s!", name); + if (*res) { + tor_free(*res); + res = NULL; + } + return -1; +} + +int +sandbox_add_addrinfo(const char* name) +{ + int ret; + struct addrinfo hints; + sb_addr_info_t *el = NULL; + + el = tor_malloc(sizeof(sb_addr_info_t)); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + ret = getaddrinfo(name, NULL, &hints, &(el->info)); + if (ret) { + log_err(LD_BUG,"(Sandbox) failed to getaddrinfo"); + ret = -2; + tor_free(el); + goto out; + } + + el->name = tor_strdup(name); + el->next = sb_addr_info; + sb_addr_info = el; + + out: + return ret; +} + +/** + * Function responsible for going through the parameter syscall filters and + * call each function pointer in the list. + */ +static int +add_param_filter(scmp_filter_ctx ctx, sandbox_cfg_t* cfg) +{ + unsigned i; + int rc = 0; + + // function pointer + for (i = 0; i < ARRAY_LENGTH(filter_func); i++) { + if ((filter_func[i])(ctx, cfg)) { + log_err(LD_BUG,"(Sandbox) failed to add syscall %d, received libseccomp " + "error %d", i, rc); + return rc; + } + } + + return 0; +} + +/** + * Function responsible of loading the libseccomp syscall filters which do not + * have parameter filtering. + */ +static int +add_noparam_filter(scmp_filter_ctx ctx) +{ + unsigned i; + int rc = 0; + + // add general filters + for (i = 0; i < ARRAY_LENGTH(filter_nopar_gen); i++) { + rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, filter_nopar_gen[i]); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add syscall index %d (NR=%d), " + "received libseccomp error %d", i, filter_nopar_gen[i], rc); + return rc; + } + } + + return 0; +} + +/** + * Function responsible for setting up and enabling a global syscall filter. + * The function is a prototype developed for stage 1 of sandboxing Tor. + * Returns 0 on success. + */ +static int +install_syscall_filter(sandbox_cfg_t* cfg) +{ + int rc = 0; + scmp_filter_ctx ctx; + + ctx = seccomp_init(SCMP_ACT_TRAP); + if (ctx == NULL) { + log_err(LD_BUG,"(Sandbox) failed to initialise libseccomp context"); + rc = -1; + goto end; + } + + // protectign sandbox parameter strings + if ((rc = prot_strings(ctx, cfg))) { + goto end; + } + + // add parameter filters + if ((rc = add_param_filter(ctx, cfg))) { + log_err(LD_BUG, "(Sandbox) failed to add param filters!"); + goto end; + } + + // adding filters with no parameters + if ((rc = add_noparam_filter(ctx))) { + log_err(LD_BUG, "(Sandbox) failed to add param filters!"); + goto end; + } + + // loading the seccomp2 filter + if ((rc = seccomp_load(ctx))) { + log_err(LD_BUG, "(Sandbox) failed to load: %d (%s)!", rc, + strerror(-rc)); + goto end; + } + + // marking the sandbox as active + sandbox_active = 1; + + end: + seccomp_release(ctx); + return (rc < 0 ? -rc : rc); +} + +#include "linux_syscalls.inc" +static const char * +get_syscall_name(int syscall_num) +{ + int i; + for (i = 0; SYSCALLS_BY_NUMBER[i].syscall_name; ++i) { + if (SYSCALLS_BY_NUMBER[i].syscall_num == syscall_num) + return SYSCALLS_BY_NUMBER[i].syscall_name; + } + + { + static char syscall_name_buf[64]; + format_dec_number_sigsafe(syscall_num, + syscall_name_buf, sizeof(syscall_name_buf)); + return syscall_name_buf; + } +} + +#ifdef USE_BACKTRACE +#define MAX_DEPTH 256 +static void *syscall_cb_buf[MAX_DEPTH]; +#endif + +/** + * Function called when a SIGSYS is caught by the application. It notifies the + * user that an error has occurred and either terminates or allows the + * application to continue execution, based on the DEBUGGING_CLOSE symbol. + */ +static void +sigsys_debugging(int nr, siginfo_t *info, void *void_context) +{ + ucontext_t *ctx = (ucontext_t *) (void_context); + const char *syscall_name; + int syscall; +#ifdef USE_BACKTRACE + int depth; + int n_fds, i; + const int *fds = NULL; +#endif + + (void) nr; + + if (info->si_code != SYS_SECCOMP) + return; + + if (!ctx) + return; + + syscall = (int) ctx->uc_mcontext.M_SYSCALL; + +#ifdef USE_BACKTRACE + depth = backtrace(syscall_cb_buf, MAX_DEPTH); + /* Clean up the top stack frame so we get the real function + * name for the most recently failing function. */ + clean_backtrace(syscall_cb_buf, depth, ctx); +#endif + + syscall_name = get_syscall_name(syscall); + + tor_log_err_sigsafe("(Sandbox) Caught a bad syscall attempt (syscall ", + syscall_name, + ")\n", + NULL); + +#ifdef USE_BACKTRACE + n_fds = tor_log_get_sigsafe_err_fds(&fds); + for (i=0; i < n_fds; ++i) + backtrace_symbols_fd(syscall_cb_buf, depth, fds[i]); +#endif + +#if defined(DEBUGGING_CLOSE) + _exit(1); +#endif // DEBUGGING_CLOSE +} + +/** + * Function that adds a handler for SIGSYS, which is the signal thrown + * when the application is issuing a syscall which is not allowed. The + * main purpose of this function is to help with debugging by identifying + * filtered syscalls. + */ +static int +install_sigsys_debugging(void) +{ + struct sigaction act; + sigset_t mask; + + memset(&act, 0, sizeof(act)); + sigemptyset(&mask); + sigaddset(&mask, SIGSYS); + + act.sa_sigaction = &sigsys_debugging; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGSYS, &act, NULL) < 0) { + log_err(LD_BUG,"(Sandbox) Failed to register SIGSYS signal handler"); + return -1; + } + + if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) { + log_err(LD_BUG,"(Sandbox) Failed call to sigprocmask()"); + return -2; + } + + return 0; +} + +/** + * Function responsible of registering the sandbox_cfg_t list of parameter + * syscall filters to the existing parameter list. This is used for incipient + * multiple-sandbox support. + */ +static int +register_cfg(sandbox_cfg_t* cfg) +{ + sandbox_cfg_t *elem = NULL; + + if (filter_dynamic == NULL) { + filter_dynamic = cfg; + return 0; + } + + for (elem = filter_dynamic; elem->next != NULL; elem = elem->next) + ; + + elem->next = cfg; + + return 0; +} + +#endif // USE_LIBSECCOMP + +#ifdef USE_LIBSECCOMP +/** + * Initialises the syscall sandbox filter for any linux architecture, taking + * into account various available features for different linux flavours. + */ +static int +initialise_libseccomp_sandbox(sandbox_cfg_t* cfg) +{ + if (install_sigsys_debugging()) + return -1; + + if (install_syscall_filter(cfg)) + return -2; + + if (register_cfg(cfg)) + return -3; + + return 0; +} + +int +sandbox_is_active(void) +{ + return sandbox_active != 0; +} +#endif // USE_LIBSECCOMP + +sandbox_cfg_t* +sandbox_cfg_new(void) +{ + return NULL; +} + +int +sandbox_init(sandbox_cfg_t *cfg) +{ +#if defined(USE_LIBSECCOMP) + return initialise_libseccomp_sandbox(cfg); + +#elif defined(__linux__) + (void)cfg; + log_warn(LD_GENERAL, + "This version of Tor was built without support for sandboxing. To " + "build with support for sandboxing on Linux, you must have " + "libseccomp and its necessary header files (e.g. seccomp.h)."); + return 0; + +#else + (void)cfg; + log_warn(LD_GENERAL, + "Currently, sandboxing is only implemented on Linux. The feature " + "is disabled on your platform."); + return 0; +#endif +} + +#ifndef USE_LIBSECCOMP +int +sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file) +{ + (void)cfg; (void)file; + return 0; +} + +int +sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...) +{ + (void)cfg; + return 0; +} + +int +sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file) +{ + (void)cfg; (void)file; + return 0; +} + +int +sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...) +{ + (void)cfg; + return 0; +} + +#if 0 +int +sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com) +{ + (void)cfg; (void)com; + return 0; +} + +int +sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...) +{ + (void)cfg; + return 0; +} +#endif + +int +sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file) +{ + (void)cfg; (void)file; + return 0; +} + +int +sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...) +{ + (void)cfg; + return 0; +} + +int +sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2) +{ + (void)cfg; (void)file1; (void)file2; + return 0; +} + +int +sandbox_is_active(void) +{ + return 0; +} +#endif + diff --git a/src/common/sandbox.h b/src/common/sandbox.h new file mode 100644 index 000000000..c3c676663 --- /dev/null +++ b/src/common/sandbox.h @@ -0,0 +1,222 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file sandbox.h + * \brief Header file for sandbox.c. + **/ + +#ifndef SANDBOX_H_ +#define SANDBOX_H_ + +#include "orconfig.h" +#include "torint.h" + +#ifndef SYS_SECCOMP + +/** + * Used by SIGSYS signal handler to check if the signal was issued due to a + * seccomp2 filter violation. + */ +#define SYS_SECCOMP 1 + +#endif + +#if defined(HAVE_SECCOMP_H) && defined(__linux__) +#define USE_LIBSECCOMP +#endif + +struct sandbox_cfg_elem; + +/** Typedef to structure used to manage a sandbox configuration. */ +typedef struct sandbox_cfg_elem sandbox_cfg_t; + +/** + * Linux definitions + */ +#ifdef USE_LIBSECCOMP + +#ifndef __USE_GNU +#define __USE_GNU +#endif +#include <sys/ucontext.h> +#include <seccomp.h> +#include <netdb.h> + +#define PARAM_PTR 0 +#define PARAM_NUM 1 + +/** + * Enum used to manage the type of the implementation for general purpose. + */ +typedef enum { + /** Libseccomp implementation based on seccomp2*/ + LIBSECCOMP2 = 0 +} SB_IMPL; + +/** + * Configuration parameter structure associated with the LIBSECCOMP2 + * implementation. + */ +typedef struct smp_param { + /** syscall associated with parameter. */ + int syscall; + + /** parameter value. */ + intptr_t value; + /** parameter value, second argument. */ + intptr_t value2; + + /** parameter flag (0 = not protected, 1 = protected). */ + int prot; +} smp_param_t; + +/** + * Structure used to manage a sandbox configuration. + * + * It is implemented as a linked list of parameters. Currently only controls + * parameters for open, openat, execve, stat64. + */ +struct sandbox_cfg_elem { + /** Sandbox implementation which dictates the parameter type. */ + SB_IMPL implem; + + /** Configuration parameter. */ + smp_param_t *param; + + /** Next element of the configuration*/ + struct sandbox_cfg_elem *next; +}; + +/** + * Structure used for keeping a linked list of getaddrinfo pre-recorded + * results. + */ +struct sb_addr_info_el { + /** Name of the address info result. */ + char *name; + /** Pre-recorded getaddrinfo result. */ + struct addrinfo *info; + /** Next element in the list. */ + struct sb_addr_info_el *next; +}; +/** Typedef to structure used to manage an addrinfo list. */ +typedef struct sb_addr_info_el sb_addr_info_t; + +/** Function pointer defining the prototype of a filter function.*/ +typedef int (*sandbox_filter_func_t)(scmp_filter_ctx ctx, + sandbox_cfg_t *filter); + +/** Type that will be used in step 3 in order to manage multiple sandboxes.*/ +typedef struct { + /** function pointers associated with the filter */ + sandbox_filter_func_t *filter_func; + + /** filter function pointer parameters */ + sandbox_cfg_t *filter_dynamic; +} sandbox_t; + +#endif // USE_LIBSECCOMP + +#ifdef USE_LIBSECCOMP +/** Pre-calls getaddrinfo in order to pre-record result. */ +int sandbox_add_addrinfo(const char *addr); + +struct addrinfo; +/** Replacement for getaddrinfo(), using pre-recorded results. */ +int sandbox_getaddrinfo(const char *name, const char *servname, + const struct addrinfo *hints, + struct addrinfo **res); +#else +#define sandbox_getaddrinfo(name, servname, hints, res) \ + getaddrinfo((name),(servname), (hints),(res)) +#define sandbox_add_addrinfo(name) \ + ((void)(name)) +#endif + +#ifdef USE_LIBSECCOMP +/** Returns a registered protected string used with the sandbox, given that + * it matches the parameter. + */ +const char* sandbox_intern_string(const char *param); +#else +#define sandbox_intern_string(s) (s) +#endif + +/** Creates an empty sandbox configuration file.*/ +sandbox_cfg_t * sandbox_cfg_new(void); + +/** + * Function used to add a open allowed filename to a supplied configuration. + * The (char*) specifies the path to the allowed file; we take ownership + * of the pointer. + */ +int sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file); + +/**DOCDOC*/ +int sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2); + +/** Function used to add a series of open allowed filenames to a supplied + * configuration. + * @param cfg sandbox configuration. + * @param ... a list of stealable pointers to permitted files. The last + * one must be NULL. +*/ +int sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...); + +/** + * Function used to add a openat allowed filename to a supplied configuration. + * The (char*) specifies the path to the allowed file; we steal the pointer to + * that file. + */ +int sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file); + +/** Function used to add a series of openat allowed filenames to a supplied + * configuration. + * @param cfg sandbox configuration. + * @param ... a list of stealable pointers to permitted files. The last + * one must be NULL. + */ +int sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...); + +#if 0 +/** + * Function used to add a execve allowed filename to a supplied configuration. + * The (char*) specifies the path to the allowed file; that pointer is stolen. + */ +int sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com); + +/** Function used to add a series of execve allowed filenames to a supplied + * configuration. + * @param cfg sandbox configuration. + * @param ... an array of stealable pointers to permitted files. The last + * one must be NULL. + */ +int sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...); +#endif + +/** + * Function used to add a stat/stat64 allowed filename to a configuration. + * The (char*) specifies the path to the allowed file; that pointer is stolen. + */ +int sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file); + +/** Function used to add a series of stat64 allowed filenames to a supplied + * configuration. + * @param cfg sandbox configuration. + * @param ... an array of stealable pointers to permitted files. The last + * one must be NULL. + */ +int sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...); + +/** Function used to initialise a sandbox configuration.*/ +int sandbox_init(sandbox_cfg_t* cfg); + +/** Return true iff the sandbox is turned on. */ +int sandbox_is_active(void); + +#endif /* SANDBOX_H_ */ + diff --git a/src/common/sha256.c b/src/common/sha256.c deleted file mode 100644 index 813c68d2a..000000000 --- a/src/common/sha256.c +++ /dev/null @@ -1,331 +0,0 @@ -/* Copyright (c) 2009-2012, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ -/* This SHA256 implementation is adapted from the public domain one in - LibTomCrypt, version 1.6. Tor uses it on platforms where OpenSSL doesn't - have a SHA256. */ - - -typedef struct sha256_state { - uint64_t length; - uint32_t state[8], curlen; - unsigned char buf[64]; -} sha256_state; - -#define CRYPT_OK 0 -#define CRYPT_NOP -1 -#define CRYPT_INVALID_ARG -2 - -#define LOAD32H(x,y) STMT_BEGIN x = ntohl(get_uint32((const char*)y)); STMT_END -#define STORE32H(x,y) STMT_BEGIN set_uint32((char*)y, htonl(x)); STMT_END -#define STORE64H(x,y) STMT_BEGIN \ - set_uint32((char*)y, htonl((uint32_t)((x)>>32))); \ - set_uint32(((char*)y)+4, htonl((uint32_t)((x)&0xffffffff))); \ - STMT_END -#define RORc(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) -#ifndef MIN - #define MIN(x, y) ( ((x)<(y))?(x):(y) ) -#endif - - -/* LibTomCrypt, modular cryptographic library -- Tom St Denis - * - * LibTomCrypt is a library that provides various cryptographic - * algorithms in a highly modular and flexible manner. - * - * The library is free for all purposes without any express - * guarantee it works. - * - * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com - */ - -/** - @file sha256.c - SHA256 by Tom St Denis -*/ - - -#ifdef LTC_SMALL_CODE -/* the K array */ -static const uint32_t K[64] = { - 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, - 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, - 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, - 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, - 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, - 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, - 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, - 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, - 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, - 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, - 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, - 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, - 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL -}; -#endif - -/* Various logical functions */ -#define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) -#define S(x, n) RORc((x),(n)) -#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) -#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) -#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) -#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) -#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) - -/* compress 512-bits */ -#ifdef LTC_CLEAN_STACK -static int _sha256_compress(sha256_state * md, unsigned char *buf) -#else -static int sha256_compress(sha256_state * md, unsigned char *buf) -#endif -{ - uint32_t S[8], W[64], t0, t1; -#ifdef LTC_SMALL_CODE - uint32_t t; -#endif - int i; - - /* copy state into S */ - for (i = 0; i < 8; i++) { - S[i] = md->state[i]; - } - - /* copy the state into 512-bits into W[0..15] */ - for (i = 0; i < 16; i++) { - LOAD32H(W[i], buf + (4*i)); - } - - /* fill W[16..63] */ - for (i = 16; i < 64; i++) { - W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; - } - - /* Compress */ -#ifdef LTC_SMALL_CODE -#define RND(a,b,c,d,e,f,g,h,i) \ - t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ - t1 = Sigma0(a) + Maj(a, b, c); \ - d += t0; \ - h = t0 + t1; - - for (i = 0; i < 64; ++i) { - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i); - t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; - S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; - } -#else -#define RND(a,b,c,d,e,f,g,h,i,ki) \ - t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ - t1 = Sigma0(a) + Maj(a, b, c); \ - d += t0; \ - h = t0 + t1; - - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); - -#undef RND - -#endif - - /* feedback */ - for (i = 0; i < 8; i++) { - md->state[i] = md->state[i] + S[i]; - } - return CRYPT_OK; -} - -#ifdef LTC_CLEAN_STACK -static int sha256_compress(sha256_state * md, unsigned char *buf) -{ - int err; - err = _sha256_compress(md, buf); - burn_stack(sizeof(uint32_t) * 74); - return err; -} -#endif - -/** - Initialize the hash state - @param md The hash state you wish to initialize - @return CRYPT_OK if successful -*/ -static int sha256_init(sha256_state * md) -{ - LTC_ARGCHK(md != NULL); - - md->curlen = 0; - md->length = 0; - md->state[0] = 0x6A09E667UL; - md->state[1] = 0xBB67AE85UL; - md->state[2] = 0x3C6EF372UL; - md->state[3] = 0xA54FF53AUL; - md->state[4] = 0x510E527FUL; - md->state[5] = 0x9B05688CUL; - md->state[6] = 0x1F83D9ABUL; - md->state[7] = 0x5BE0CD19UL; - return CRYPT_OK; -} - -/** - Process a block of memory though the hash - @param md The hash state - @param in The data to hash - @param inlen The length of the data (octets) - @return CRYPT_OK if successful -*/ -static int sha256_process (sha256_state * md, const unsigned char *in, unsigned long inlen) -{ - unsigned long n; - int err; - LTC_ARGCHK(md != NULL); - LTC_ARGCHK(in != NULL); - if (md->curlen > sizeof(md->buf)) { - return CRYPT_INVALID_ARG; - } - while (inlen > 0) { - if (md->curlen == 0 && inlen >= 64) { - if ((err = sha256_compress (md, (unsigned char *)in)) != CRYPT_OK) { - return err; - } - md->length += 64 * 8; - in += 64; - inlen -= 64; - } else { - n = MIN(inlen, (64 - md->curlen)); - memcpy(md->buf + md->curlen, in, (size_t)n); - md->curlen += n; - in += n; - inlen -= n; - if (md->curlen == 64) { - if ((err = sha256_compress (md, md->buf)) != CRYPT_OK) { - return err; - } - md->length += 8*64; - md->curlen = 0; - } - } - } - return CRYPT_OK; -} - -/** - Terminate the hash to get the digest - @param md The hash state - @param out [out] The destination of the hash (32 bytes) - @return CRYPT_OK if successful -*/ -static int sha256_done(sha256_state * md, unsigned char *out) -{ - int i; - - LTC_ARGCHK(md != NULL); - LTC_ARGCHK(out != NULL); - - if (md->curlen >= sizeof(md->buf)) { - return CRYPT_INVALID_ARG; - } - - - /* increase the length of the message */ - md->length += md->curlen * 8; - - /* append the '1' bit */ - md->buf[md->curlen++] = (unsigned char)0x80; - - /* if the length is currently above 56 bytes we append zeros - * then compress. Then we can fall back to padding zeros and length - * encoding like normal. - */ - if (md->curlen > 56) { - while (md->curlen < 64) { - md->buf[md->curlen++] = (unsigned char)0; - } - sha256_compress(md, md->buf); - md->curlen = 0; - } - - /* pad upto 56 bytes of zeroes */ - while (md->curlen < 56) { - md->buf[md->curlen++] = (unsigned char)0; - } - - /* store length */ - STORE64H(md->length, md->buf+56); - sha256_compress(md, md->buf); - - /* copy output */ - for (i = 0; i < 8; i++) { - STORE32H(md->state[i], out+(4*i)); - } -#ifdef LTC_CLEAN_STACK - zeromem(md, sizeof(sha256_state)); -#endif - return CRYPT_OK; -} - -/* $Source: /cvs/libtom/libtomcrypt/src/hashes/sha2/sha256.c,v $ */ -/* $Revision: 1.9 $ */ -/* $Date: 2006/11/01 09:28:17 $ */ diff --git a/src/common/strlcat.c b/src/common/strlcat.c deleted file mode 100644 index 316733bcc..000000000 --- a/src/common/strlcat.c +++ /dev/null @@ -1,70 +0,0 @@ -/* $OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $ */ - -/* - * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $"; -#endif /* LIBC_SCCS and not lint */ - -#include <sys/types.h> -#include <string.h> - -/* - * Appends src to string dst of size siz (unlike strncat, siz is the - * full size of dst, not space left). At most siz-1 characters - * will be copied. Always NUL terminates (unless siz <= strlen(dst)). - * Returns strlen(src) + MIN(siz, strlen(initial dst)). - * If retval >= siz, truncation occurred. - */ -size_t -strlcat(char *dst, const char *src, size_t siz) -{ - register char *d = dst; - register const char *s = src; - register size_t n = siz; - size_t dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if (n == 0) - return(dlen + strlen(s)); - while (*s != '\0') { - if (n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return(dlen + (s - src)); /* count does not include NUL */ -} diff --git a/src/common/strlcpy.c b/src/common/strlcpy.c deleted file mode 100644 index 9fc47903a..000000000 --- a/src/common/strlcpy.c +++ /dev/null @@ -1,60 +0,0 @@ -/* $OpenBSD: strlcpy.c,v 1.2 1998/11/06 04:33:16 wvdputte Exp $ */ - -/* - * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: strlcpy.c,v 1.2 1998/11/06 04:33:16 wvdputte Exp $"; -#endif /* LIBC_SCCS and not lint */ - -#include <sys/types.h> -#include <string.h> - -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ -size_t strlcpy(char *dst, const char *src, size_t siz) -{ - register char *d = dst; - register const char *s = src; - register size_t n = siz; - - if (n == 0) - return(strlen(s)); - while (*s != '\0') { - if (n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return(s - src); /* count does not include NUL */ -} diff --git a/src/common/testsupport.h b/src/common/testsupport.h new file mode 100644 index 000000000..4a4f50b69 --- /dev/null +++ b/src/common/testsupport.h @@ -0,0 +1,80 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_TESTSUPPORT_H +#define TOR_TESTSUPPORT_H + +#ifdef TOR_UNIT_TESTS +#define STATIC +#else +#define STATIC static +#endif + +/** Quick and dirty macros to implement test mocking. + * + * To use them, suppose that you have a function you'd like to mock + * with the signature "void writebuf(size_t n, char *buf)". You can then + * declare the function as: + * + * MOCK_DECL(void, writebuf, (size_t n, char *buf)); + * + * and implement it as: + * + * MOCK_IMPL(void + * writebuf,(size_t n, char *buf) + * { + * ... + * } + * + * For the non-testing build, this will expand simply into: + * + * void writebuf(size_t n, char *buf); + * void + * writebuf(size_t n, char *buf) + * { + * ... + * } + * + * But for the testing case, it will expand into: + * + * void writebuf__real(size_t n, char *buf); + * extern void (*writebuf)(size_t n, char *buf); + * + * void (*writebuf)(size_t n, char *buf) = writebuf__real; + * void + * writebuf__real(size_t n, char *buf) + * { + * ... + * } + * + * This is not a great mocking system! It is deliberately "the simplest + * thing that could work", and pays for its simplicity in its lack of + * features, and in its uglification of the Tor code. Replacing it with + * something clever would be a fine thing. + * + * @{ */ +#ifdef TOR_UNIT_TESTS +#define MOCK_DECL(rv, funcname, arglist) \ + rv funcname ##__real arglist; \ + extern rv(*funcname) arglist +#define MOCK_IMPL(rv, funcname, arglist) \ + rv(*funcname) arglist = funcname ##__real; \ + rv funcname ##__real arglist +#define MOCK(func, replacement) \ + do { \ + (func) = (replacement); \ + } while (0) +#define UNMOCK(func) \ + do { \ + func = func ##__real; \ + } while (0) +#else +#define MOCK_DECL(rv, funcname, arglist) \ + rv funcname arglist +#define MOCK_IMPL(rv, funcname, arglist) \ + rv funcname arglist +#endif +/** @} */ + +#endif + diff --git a/src/common/torgzip.c b/src/common/torgzip.c index da4136228..15451ee30 100644 --- a/src/common/torgzip.c +++ b/src/common/torgzip.c @@ -1,6 +1,6 @@ /* Copyright (c) 2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -68,6 +68,22 @@ is_gzip_supported(void) return gzip_is_supported; } +/** Return a string representation of the version of the currently running + * version of zlib. */ +const char * +tor_zlib_get_version_str(void) +{ + return zlibVersion(); +} + +/** Return a string representation of the version of the version of zlib +* used at compilation. */ +const char * +tor_zlib_get_header_version_str(void) +{ + return ZLIB_VERSION; +} + /** Return the 'bits' value to tell zlib to use <b>method</b>.*/ static INLINE int method_bits(compress_method_t method) diff --git a/src/common/torgzip.h b/src/common/torgzip.h index d3ded81f9..5db03fe6e 100644 --- a/src/common/torgzip.h +++ b/src/common/torgzip.h @@ -1,6 +1,6 @@ /* Copyright (c) 2003, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -8,8 +8,8 @@ * \brief Headers for torgzip.h **/ -#ifndef _TOR_TORGZIP_H -#define _TOR_TORGZIP_H +#ifndef TOR_TORGZIP_H +#define TOR_TORGZIP_H /** Enumeration of what kind of compression to use. Only ZLIB_METHOD is * guaranteed to be supported by the compress/uncompress functions here; @@ -32,6 +32,12 @@ tor_gzip_uncompress(char **out, size_t *out_len, int is_gzip_supported(void); +const char * +tor_zlib_get_version_str(void); + +const char * +tor_zlib_get_header_version_str(void); + compress_method_t detect_compression_method(const char *in, size_t in_len); /** Return values from tor_zlib_process; see that function's documentation for diff --git a/src/common/torint.h b/src/common/torint.h index 8771802d7..a993d7649 100644 --- a/src/common/torint.h +++ b/src/common/torint.h @@ -1,6 +1,6 @@ /* Copyright (c) 2003, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -8,8 +8,8 @@ * \brief Header file to define uint32_t and friends **/ -#ifndef _TOR_TORINT_H -#define _TOR_TORINT_H +#ifndef TOR_TORINT_H +#define TOR_TORINT_H #include "orconfig.h" @@ -214,16 +214,20 @@ typedef int32_t ssize_t; #if (SIZEOF_VOID_P > 4 && SIZEOF_VOID_P <= 8) #ifndef HAVE_INTPTR_T typedef int64_t intptr_t; +#define SIZEOF_INTPTR_T 8 #endif #ifndef HAVE_UINTPTR_T typedef uint64_t uintptr_t; +#define SIZEOF_UINTPTR_T 8 #endif #elif (SIZEOF_VOID_P > 2 && SIZEOF_VOID_P <= 4) #ifndef HAVE_INTPTR_T typedef int32_t intptr_t; +#define SIZEOF_INTPTR_T 4 #endif #ifndef HAVE_UINTPTR_T typedef uint32_t uintptr_t; +#define SIZEOF_UINTPTR_T 4 #endif #else #error "void * is either >8 bytes or <= 2. In either case, I am confused." diff --git a/src/common/torlog.h b/src/common/torlog.h index 28890a44a..34f70f3c0 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -10,9 +10,10 @@ * \brief Headers for log.c **/ -#ifndef _TOR_LOG_H +#ifndef TOR_TORLOG_H #include "compat.h" +#include "testsupport.h" #ifdef HAVE_SYSLOG_H #include <syslog.h> @@ -94,12 +95,17 @@ #define LD_HANDSHAKE (1u<<19) /** Heartbeat messages */ #define LD_HEARTBEAT (1u<<20) +/** Abstract channel_t code */ +#define LD_CHANNEL (1u<<21) /** Number of logging domains in the code. */ -#define N_LOGGING_DOMAINS 21 +#define N_LOGGING_DOMAINS 22 /** This log message is not safe to send to a callback-based logger * immediately. Used as a flag, not a log domain. */ #define LD_NOCB (1u<<31) +/** This log message should not include a function name, even if it otherwise + * would. Used as a flag, not a log domain. */ +#define LD_NOFUNCNAME (1u<<30) /** Mask of zero or more log domains, OR'd together. */ typedef uint32_t log_domain_mask_t; @@ -112,12 +118,6 @@ typedef struct log_severity_list_t { log_domain_mask_t masks[LOG_DEBUG-LOG_ERR+1]; } log_severity_list_t; -#ifdef LOG_PRIVATE -/** Given a severity, yields an index into log_severity_list_t.masks to use - * for that severity. */ -#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR) -#endif - /** Callback type used for add_callback_log. */ typedef void (*log_callback)(int severity, uint32_t domain, const char *msg); @@ -151,66 +151,93 @@ void set_log_time_granularity(int granularity_msec); void tor_log(int severity, log_domain_mask_t domain, const char *format, ...) CHECK_PRINTF(3,4); -#define log tor_log /* hack it so we don't conflict with log() as much */ -#if defined(__GNUC__) || defined(RUNNING_DOXYGEN) -extern int _log_global_min_severity; +void tor_log_err_sigsafe(const char *m, ...); +int tor_log_get_sigsafe_err_fds(const int **out); +void tor_log_update_sigsafe_err_fds(void); + +struct smartlist_t; +void tor_log_get_logfile_names(struct smartlist_t *out); + +extern int log_global_min_severity_; -void _log_fn(int severity, log_domain_mask_t domain, +#if defined(__GNUC__) || defined(RUNNING_DOXYGEN) +void log_fn_(int severity, log_domain_mask_t domain, const char *funcname, const char *format, ...) CHECK_PRINTF(4,5); +struct ratelim_t; +void log_fn_ratelim_(struct ratelim_t *ratelim, int severity, + log_domain_mask_t domain, const char *funcname, + const char *format, ...) + CHECK_PRINTF(5,6); /** Log a message at level <b>severity</b>, using a pretty-printed version * of the current function name. */ #define log_fn(severity, domain, args...) \ - _log_fn(severity, domain, __PRETTY_FUNCTION__, args) + log_fn_(severity, domain, __PRETTY_FUNCTION__, args) +/** As log_fn, but use <b>ratelim</b> (an instance of ratelim_t) to control + * the frequency at which messages can appear. + */ +#define log_fn_ratelim(ratelim, severity, domain, args...) \ + log_fn_ratelim_(ratelim, severity, domain, __PRETTY_FUNCTION__, args) #define log_debug(domain, args...) \ STMT_BEGIN \ - if (PREDICT_UNLIKELY(_log_global_min_severity == LOG_DEBUG)) \ - _log_fn(LOG_DEBUG, domain, __PRETTY_FUNCTION__, args); \ + if (PREDICT_UNLIKELY(log_global_min_severity_ == LOG_DEBUG)) \ + log_fn_(LOG_DEBUG, domain, __PRETTY_FUNCTION__, args); \ STMT_END #define log_info(domain, args...) \ - _log_fn(LOG_INFO, domain, __PRETTY_FUNCTION__, args) + log_fn_(LOG_INFO, domain, __PRETTY_FUNCTION__, args) #define log_notice(domain, args...) \ - _log_fn(LOG_NOTICE, domain, __PRETTY_FUNCTION__, args) + log_fn_(LOG_NOTICE, domain, __PRETTY_FUNCTION__, args) #define log_warn(domain, args...) \ - _log_fn(LOG_WARN, domain, __PRETTY_FUNCTION__, args) + log_fn_(LOG_WARN, domain, __PRETTY_FUNCTION__, args) #define log_err(domain, args...) \ - _log_fn(LOG_ERR, domain, __PRETTY_FUNCTION__, args) + log_fn_(LOG_ERR, domain, __PRETTY_FUNCTION__, args) #else /* ! defined(__GNUC__) */ -void _log_fn(int severity, log_domain_mask_t domain, const char *format, ...); -void _log_debug(log_domain_mask_t domain, const char *format, ...); -void _log_info(log_domain_mask_t domain, const char *format, ...); -void _log_notice(log_domain_mask_t domain, const char *format, ...); -void _log_warn(log_domain_mask_t domain, const char *format, ...); -void _log_err(log_domain_mask_t domain, const char *format, ...); +void log_fn_(int severity, log_domain_mask_t domain, const char *format, ...); +struct ratelim_t; +void log_fn_ratelim_(struct ratelim_t *ratelim, int severity, + log_domain_mask_t domain, const char *format, ...); +void log_debug_(log_domain_mask_t domain, const char *format, ...); +void log_info_(log_domain_mask_t domain, const char *format, ...); +void log_notice_(log_domain_mask_t domain, const char *format, ...); +void log_warn_(log_domain_mask_t domain, const char *format, ...); +void log_err_(log_domain_mask_t domain, const char *format, ...); #if defined(_MSC_VER) && _MSC_VER < 1300 /* MSVC 6 and earlier don't have __func__, or even __LINE__. */ -#define log_fn _log_fn -#define log_debug _log_debug -#define log_info _log_info -#define log_notice _log_notice -#define log_warn _log_warn -#define log_err _log_err +#define log_fn log_fn_ +#define log_fn_ratelim log_fn_ratelim_ +#define log_debug log_debug_ +#define log_info log_info_ +#define log_notice log_notice_ +#define log_warn log_warn_ +#define log_err log_err_ #else /* We don't have GCC's varargs macros, so use a global variable to pass the * function name to log_fn */ -extern const char *_log_fn_function_name; +extern const char *log_fn_function_name_; /* We abuse the comma operator here, since we can't use the standard * do {...} while (0) trick to wrap this macro, since the macro can't take * arguments. */ -#define log_fn (_log_fn_function_name=__func__),_log_fn -#define log_debug (_log_fn_function_name=__func__),_log_debug -#define log_info (_log_fn_function_name=__func__),_log_info -#define log_notice (_log_fn_function_name=__func__),_log_notice -#define log_warn (_log_fn_function_name=__func__),_log_warn -#define log_err (_log_fn_function_name=__func__),_log_err +#define log_fn (log_fn_function_name_=__func__),log_fn_ +#define log_fn_ratelim (log_fn_function_name_=__func__),log_fn_ratelim_ +#define log_debug (log_fn_function_name_=__func__),log_debug_ +#define log_info (log_fn_function_name_=__func__),log_info_ +#define log_notice (log_fn_function_name_=__func__),log_notice_ +#define log_warn (log_fn_function_name_=__func__),log_warn_ +#define log_err (log_fn_function_name_=__func__),log_err_ #endif #endif /* !GNUC */ -# define _TOR_LOG_H +#ifdef LOG_PRIVATE +MOCK_DECL(STATIC void, logv, (int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, const char *format, + va_list ap) CHECK_PRINTF(5,0)); +#endif + +# define TOR_TORLOG_H #endif diff --git a/src/common/tortls.c b/src/common/tortls.c index 60aac6492..ea0f21cb2 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -48,9 +48,6 @@ #include "compat_libevent.h" #endif -#define CRYPTO_PRIVATE /* to import prototypes from crypto.h */ -#define TORTLS_PRIVATE - #include "crypto.h" #include "tortls.h" #include "util.h" @@ -58,8 +55,8 @@ #include "container.h" #include <string.h> -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,7) -#error "We require OpenSSL >= 0.9.7" +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) +#error "We require OpenSSL >= 0.9.8" #endif /* Enable the "v2" TLS handshake. @@ -127,8 +124,33 @@ typedef struct tor_tls_context_t { crypto_pk_t *auth_key; } tor_tls_context_t; +/** Return values for tor_tls_classify_client_ciphers. + * + * @{ + */ +/** An error occurred when examining the client ciphers */ +#define CIPHERS_ERR -1 +/** The client cipher list indicates that a v1 handshake was in use. */ +#define CIPHERS_V1 1 +/** The client cipher list indicates that the client is using the v2 or the + * v3 handshake, but that it is (probably!) lying about what ciphers it + * supports */ +#define CIPHERS_V2 2 +/** The client cipher list indicates that the client is using the v2 or the + * v3 handshake, and that it is telling the truth about what ciphers it + * supports */ +#define CIPHERS_UNRESTRICTED 3 +/** @} */ + #define TOR_TLS_MAGIC 0x71571571 +typedef enum { + TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE, + TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE, + TOR_TLS_ST_BUFFEREVENT +} tor_tls_state_t; +#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t) + /** Holds a SSL object and its associated data. Members are only * accessed from within tortls.c. */ @@ -138,12 +160,9 @@ struct tor_tls_t { SSL *ssl; /**< An OpenSSL SSL object. */ int socket; /**< The underlying file descriptor for this TLS connection. */ char *address; /**< An address to log when describing this connection. */ - enum { - TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE, - TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE, - TOR_TLS_ST_BUFFEREVENT - } state : 3; /**< The current SSL state, depending on which operations have - * completed successfully. */ + tor_tls_state_bitfield_t state : 3; /**< The current SSL state, + * depending on which operations + * have completed successfully. */ unsigned int isServer:1; /**< True iff this is a server-side connection */ unsigned int wasV2Handshake:1; /**< True iff the original handshake for * this connection used the updated version @@ -152,6 +171,9 @@ struct tor_tls_t { * one certificate). */ /** True iff we should call negotiated_callback when we're done reading. */ unsigned int got_renegotiate:1; + /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't + * called that function yet. */ + int8_t client_cipher_list_type; /** Incremented every time we start the server side of a handshake. */ uint8_t server_handshake_count; size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last @@ -210,14 +232,16 @@ static X509* tor_tls_create_certificate(crypto_pk_t *rsa, crypto_pk_t *rsa_sign, const char *cname, const char *cname_sign, - unsigned int lifetime); + unsigned int cert_lifetime); static int tor_tls_context_init_one(tor_tls_context_t **ppcontext, crypto_pk_t *identity, unsigned int key_lifetime, + unsigned int flags, int is_client); static tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, + unsigned int flags, int is_client); static int check_cert_lifetime_internal(int severity, const X509 *cert, int past_tolerance, int future_tolerance); @@ -234,8 +258,8 @@ static tor_tls_context_t *client_tls_context = NULL; static int tls_library_is_initialized = 0; /* Module-internal error codes. */ -#define _TOR_TLS_SYSCALL (_MIN_TOR_TLS_ERROR_VAL - 2) -#define _TOR_TLS_ZERORETURN (_MIN_TOR_TLS_ERROR_VAL - 1) +#define TOR_TLS_SYSCALL_ (MIN_TOR_TLS_ERROR_VAL_ - 2) +#define TOR_TLS_ZERORETURN_ (MIN_TOR_TLS_ERROR_VAL_ - 1) /** Write a description of the current state of <b>tls</b> into the * <b>sz</b>-byte buffer at <b>buf</b>. */ @@ -308,11 +332,11 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, if (!lib) lib = "(null)"; if (!func) func = "(null)"; if (doing) { - log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)", + tor_log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)", doing, addr?" with ":"", addr?addr:"", msg, lib, func, state); } else { - log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)", + tor_log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)", addr?" with ":"", addr?addr:"", msg, lib, func, state); } @@ -336,35 +360,19 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) static int tor_errno_to_tls_error(int e) { -#if defined(_WIN32) switch (e) { - case WSAECONNRESET: // most common + case SOCK_ERRNO(ECONNRESET): // most common return TOR_TLS_ERROR_CONNRESET; - case WSAETIMEDOUT: + case SOCK_ERRNO(ETIMEDOUT): return TOR_TLS_ERROR_TIMEOUT; - case WSAENETUNREACH: - case WSAEHOSTUNREACH: + case SOCK_ERRNO(EHOSTUNREACH): + case SOCK_ERRNO(ENETUNREACH): return TOR_TLS_ERROR_NO_ROUTE; - case WSAECONNREFUSED: + case SOCK_ERRNO(ECONNREFUSED): return TOR_TLS_ERROR_CONNREFUSED; // least common default: return TOR_TLS_ERROR_MISC; } -#else - switch (e) { - case ECONNRESET: // most common - return TOR_TLS_ERROR_CONNRESET; - case ETIMEDOUT: - return TOR_TLS_ERROR_TIMEOUT; - case EHOSTUNREACH: - case ENETUNREACH: - return TOR_TLS_ERROR_NO_ROUTE; - case ECONNREFUSED: - return TOR_TLS_ERROR_CONNREFUSED; // least common - default: - return TOR_TLS_ERROR_MISC; - } -#endif } /** Given a TOR_TLS_* error code, return a string equivalent. */ @@ -393,9 +401,9 @@ tor_tls_err_to_string(int err) /** Given a TLS object and the result of an SSL_* call, use * SSL_get_error to determine whether an error has occurred, and if so * which one. Return one of TOR_TLS_{DONE|WANTREAD|WANTWRITE|ERROR}. - * If extra&CATCH_SYSCALL is true, return _TOR_TLS_SYSCALL instead of + * If extra&CATCH_SYSCALL is true, return TOR_TLS_SYSCALL_ instead of * reporting syscall errors. If extra&CATCH_ZERO is true, return - * _TOR_TLS_ZERORETURN instead of reporting zero-return errors. + * TOR_TLS_ZERORETURN_ instead of reporting zero-return errors. * * If an error has occurred, log it at level <b>severity</b> and describe the * current action as <b>doing</b>. @@ -415,14 +423,14 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra, return TOR_TLS_WANTWRITE; case SSL_ERROR_SYSCALL: if (extra&CATCH_SYSCALL) - return _TOR_TLS_SYSCALL; + return TOR_TLS_SYSCALL_; if (r == 0) { - log(severity, LD_NET, "TLS error: unexpected close while %s (%s)", + tor_log(severity, LD_NET, "TLS error: unexpected close while %s (%s)", doing, SSL_state_string_long(tls->ssl)); tor_error = TOR_TLS_ERROR_IO; } else { int e = tor_socket_errno(tls->socket); - log(severity, LD_NET, + tor_log(severity, LD_NET, "TLS error: <syscall error while %s> (errno=%d: %s; state=%s)", doing, e, tor_socket_strerror(e), SSL_state_string_long(tls->ssl)); @@ -432,8 +440,8 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra, return tor_error; case SSL_ERROR_ZERO_RETURN: if (extra&CATCH_ZERO) - return _TOR_TLS_ZERORETURN; - log(severity, LD_NET, "TLS connection closed while %s in state %s", + return TOR_TLS_ZERORETURN_; + tor_log(severity, LD_NET, "TLS connection closed while %s in state %s", doing, SSL_state_string_long(tls->ssl)); tls_log_errors(tls, severity, domain, doing); return TOR_TLS_CLOSE; @@ -478,7 +486,7 @@ tor_tls_init(void) * a test of intelligence and determination. */ if (version > OPENSSL_V(0,9,8,'k') && version <= OPENSSL_V(0,9,8,'l')) { - log_notice(LD_GENERAL, "OpenSSL %s looks like version 0.9.8l, but " + log_info(LD_GENERAL, "OpenSSL %s looks like version 0.9.8l, but " "some vendors have backported renegotiation code from " "0.9.8m without updating the version number. " "I will try SSL3_FLAGS and SSL_OP to enable renegotation.", @@ -486,12 +494,12 @@ tor_tls_init(void) use_unsafe_renegotiation_flag = 1; use_unsafe_renegotiation_op = 1; } else if (version > OPENSSL_V(0,9,8,'l')) { - log_notice(LD_GENERAL, "OpenSSL %s looks like version 0.9.8m or later; " + log_info(LD_GENERAL, "OpenSSL %s looks like version 0.9.8m or later; " "I will try SSL_OP to enable renegotiation", SSLeay_version(SSLEAY_VERSION)); use_unsafe_renegotiation_op = 1; } else if (version <= OPENSSL_V(0,9,8,'k')) { - log_notice(LD_GENERAL, "OpenSSL %s [%lx] looks like it's older than " + log_info(LD_GENERAL, "OpenSSL %s [%lx] looks like it's older than " "0.9.8l, but some vendors have backported 0.9.8l's " "renegotiation code to earlier versions, and some have " "backported the code from 0.9.8m or 0.9.8n. I'll set both " @@ -505,6 +513,37 @@ tor_tls_init(void) SSLeay_version(SSLEAY_VERSION), version); } +#if (SIZEOF_VOID_P >= 8 && \ + !defined(OPENSSL_NO_EC) && \ + OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1)) + if (version >= OPENSSL_V_SERIES(1,0,1)) { + /* Warn if we could *almost* be running with much faster ECDH. + If we're built for a 64-bit target, using OpenSSL 1.0.1, but we + don't have one of the built-in __uint128-based speedups, we are + just one build operation away from an accelerated handshake. + + (We could be looking at OPENSSL_NO_EC_NISTP_64_GCC_128 instead of + doing this test, but that gives compile-time options, not runtime + behavior.) + */ + EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + const EC_GROUP *g = key ? EC_KEY_get0_group(key) : NULL; + const EC_METHOD *m = g ? EC_GROUP_method_of(g) : NULL; + const int warn = (m == EC_GFp_simple_method() || + m == EC_GFp_mont_method() || + m == EC_GFp_nist_method()); + EC_KEY_free(key); + + if (warn) + log_notice(LD_GENERAL, "We were built to run on a 64-bit CPU, with " + "OpenSSL 1.0.1 or later, but with a version of OpenSSL " + "that apparently lacks accelerated support for the NIST " + "P-224 and P-256 groups. Building openssl with such " + "support (using the enable-ec_nistp_64_gcc_128 option " + "when configuring it) would make ECDH much faster."); + } +#endif + tor_tls_allocate_tor_tls_object_ex_data_index(); tls_library_is_initialized = 1; @@ -567,9 +606,10 @@ tor_x509_name_new(const char *cname) /** Generate and sign an X509 certificate with the public key <b>rsa</b>, * signed by the private key <b>rsa_sign</b>. The commonName of the * certificate will be <b>cname</b>; the commonName of the issuer will be - * <b>cname_sign</b>. The cert will be valid for <b>cert_lifetime</b> seconds - * starting from now. Return a certificate on success, NULL on - * failure. + * <b>cname_sign</b>. The cert will be valid for <b>cert_lifetime</b> + * seconds, starting from some time in the past. + * + * Return a certificate on success, NULL on failure. */ static X509 * tor_tls_create_certificate(crypto_pk_t *rsa, @@ -591,15 +631,20 @@ tor_tls_create_certificate(crypto_pk_t *rsa, tor_tls_init(); - start_time = time(NULL); + /* Make sure we're part-way through the certificate lifetime, rather + * than having it start right now. Don't choose quite uniformly, since + * then we might pick a time where we're about to expire. Lastly, be + * sure to start on a day boundary. */ + start_time = time(NULL) - crypto_rand_int(cert_lifetime) + 2*24*3600; + start_time -= start_time % (24*3600); tor_assert(rsa); tor_assert(cname); tor_assert(rsa_sign); tor_assert(cname_sign); - if (!(sign_pkey = _crypto_pk_get_evp_pkey(rsa_sign,1))) + if (!(sign_pkey = crypto_pk_get_evp_pkey_(rsa_sign,1))) goto error; - if (!(pkey = _crypto_pk_get_evp_pkey(rsa,0))) + if (!(pkey = crypto_pk_get_evp_pkey_(rsa,0))) goto error; if (!(x509 = X509_new())) goto error; @@ -647,7 +692,7 @@ tor_tls_create_certificate(crypto_pk_t *rsa, if (pkey) EVP_PKEY_free(pkey); if (serial_number) - BN_free(serial_number); + BN_clear_free(serial_number); if (name) X509_NAME_free(name); if (name_issuer) @@ -657,11 +702,58 @@ tor_tls_create_certificate(crypto_pk_t *rsa, #undef SERIAL_NUMBER_SIZE } -/** List of ciphers that servers should select from.*/ +/** List of ciphers that servers should select from when the client might be + * claiming extra unsupported ciphers in order to avoid fingerprinting. */ #define SERVER_CIPHER_LIST \ (TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" \ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \ SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) + +/** List of ciphers that servers should select from when we actually have + * our choice of what cipher to use. */ +const char UNRESTRICTED_SERVER_CIPHER_LIST[] = + /* This list is autogenerated with the gen_server_ciphers.py script; + * don't hand-edit it. */ +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 + TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 + TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA + TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA + TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 + TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 + TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 ":" +#endif +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 ":" +#endif + /* Required */ + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" + /* Required */ + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" +#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA + TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA ":" +#endif + /* Required */ + SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA; + /* Note: to set up your own private testing network with link crypto * disabled, set your Tors' cipher list to * (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate @@ -728,24 +820,24 @@ tor_cert_new(X509 *x509_cert) tor_cert_t *cert; EVP_PKEY *pkey; RSA *rsa; - int length, length2; - unsigned char *cp; + int length; + unsigned char *buf = NULL; if (!x509_cert) return NULL; - length = i2d_X509(x509_cert, NULL); + length = i2d_X509(x509_cert, &buf); cert = tor_malloc_zero(sizeof(tor_cert_t)); - if (length <= 0) { + if (length <= 0 || buf == NULL) { tor_free(cert); log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate"); X509_free(x509_cert); return NULL; } cert->encoded_len = (size_t) length; - cp = cert->encoded = tor_malloc(length); - length2 = i2d_X509(x509_cert, &cp); - tor_assert(length2 == length); + cert->encoded = tor_malloc(length); + memcpy(cert->encoded, buf, length); + OPENSSL_free(buf); cert->cert = x509_cert; @@ -754,7 +846,7 @@ tor_cert_new(X509 *x509_cert) if ((pkey = X509_get_pubkey(x509_cert)) && (rsa = EVP_PKEY_get1_RSA(pkey))) { - crypto_pk_t *pk = _crypto_new_pk_from_rsa(rsa); + crypto_pk_t *pk = crypto_new_pk_from_rsa_(rsa); crypto_pk_get_all_digests(pk, &cert->pkey_digests); cert->pkey_digests_set = 1; crypto_pk_free(pk); @@ -778,13 +870,8 @@ tor_cert_decode(const uint8_t *certificate, size_t certificate_len) if (certificate_len > INT_MAX) return NULL; -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) - /* This ifdef suppresses a type warning. Take out this case once everybody - * is using OpenSSL 0.9.8 or later. */ - x509 = d2i_X509(NULL, (unsigned char**)&cp, (int)certificate_len); -#else x509 = d2i_X509(NULL, &cp, (int)certificate_len); -#endif + if (!x509) return NULL; /* Couldn't decode */ if (cp - certificate != (int)certificate_len) { @@ -901,36 +988,11 @@ tor_tls_cert_get_key(tor_cert_t *cert) EVP_PKEY_free(pkey); return NULL; } - result = _crypto_new_pk_from_rsa(rsa); + result = crypto_new_pk_from_rsa_(rsa); EVP_PKEY_free(pkey); return result; } -/** Return true iff <b>a</b> and <b>b</b> represent the same public key. */ -static int -pkey_eq(EVP_PKEY *a, EVP_PKEY *b) -{ - /* We'd like to do this, but openssl 0.9.7 doesn't have it: - return EVP_PKEY_cmp(a,b) == 1; - */ - unsigned char *a_enc=NULL, *b_enc=NULL, *a_ptr, *b_ptr; - int a_len1, b_len1, a_len2, b_len2, result; - a_len1 = i2d_PublicKey(a, NULL); - b_len1 = i2d_PublicKey(b, NULL); - if (a_len1 != b_len1) - return 0; - a_ptr = a_enc = tor_malloc(a_len1); - b_ptr = b_enc = tor_malloc(b_len1); - a_len2 = i2d_PublicKey(a, &a_ptr); - b_len2 = i2d_PublicKey(b, &b_ptr); - tor_assert(a_len2 == a_len1); - tor_assert(b_len2 == b_len1); - result = tor_memeq(a_enc, b_enc, a_len1); - tor_free(a_enc); - tor_free(b_enc); - return result; -} - /** Return true iff the other side of <b>tls</b> has authenticated to us, and * the key certified in <b>cert</b> is the same as the key they used to do it. */ @@ -946,7 +1008,7 @@ tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert) link_key = X509_get_pubkey(peercert); cert_key = X509_get_pubkey(cert->cert); - result = link_key && cert_key && pkey_eq(cert_key, link_key); + result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1; X509_free(peercert); if (link_key) @@ -1019,17 +1081,20 @@ tor_tls_context_incref(tor_tls_context_t *ctx) /** Create new global client and server TLS contexts. * * If <b>server_identity</b> is NULL, this will not generate a server - * TLS context. If <b>is_public_server</b> is non-zero, this will use + * TLS context. If TOR_TLS_CTX_IS_PUBLIC_SERVER is set in <b>flags</b>, use * the same TLS context for incoming and outgoing connections, and - * ignore <b>client_identity</b>. */ + * ignore <b>client_identity</b>. If one of TOR_TLS_CTX_USE_ECDHE_P{224,256} + * is set in <b>flags</b>, use that ECDHE group if possible; otherwise use + * the default ECDHE group. */ int -tor_tls_context_init(int is_public_server, +tor_tls_context_init(unsigned flags, crypto_pk_t *client_identity, crypto_pk_t *server_identity, unsigned int key_lifetime) { int rv1 = 0; int rv2 = 0; + const int is_public_server = flags & TOR_TLS_CTX_IS_PUBLIC_SERVER; if (is_public_server) { tor_tls_context_t *new_ctx; @@ -1039,7 +1104,7 @@ tor_tls_context_init(int is_public_server, rv1 = tor_tls_context_init_one(&server_tls_context, server_identity, - key_lifetime, 0); + key_lifetime, flags, 0); if (rv1 >= 0) { new_ctx = server_tls_context; @@ -1056,6 +1121,7 @@ tor_tls_context_init(int is_public_server, rv1 = tor_tls_context_init_one(&server_tls_context, server_identity, key_lifetime, + flags, 0); } else { tor_tls_context_t *old_ctx = server_tls_context; @@ -1069,6 +1135,7 @@ tor_tls_context_init(int is_public_server, rv2 = tor_tls_context_init_one(&client_tls_context, client_identity, key_lifetime, + flags, 1); } @@ -1085,10 +1152,12 @@ static int tor_tls_context_init_one(tor_tls_context_t **ppcontext, crypto_pk_t *identity, unsigned int key_lifetime, + unsigned int flags, int is_client) { tor_tls_context_t *new_ctx = tor_tls_context_new(identity, key_lifetime, + flags, is_client); tor_tls_context_t *old_ctx = *ppcontext; @@ -1112,7 +1181,7 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext, */ static tor_tls_context_t * tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, - int is_client) + unsigned flags, int is_client) { crypto_pk_t *rsa = NULL, *rsa_auth = NULL; EVP_PKEY *pkey = NULL; @@ -1150,7 +1219,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2, key_lifetime); if (!cert || !idcert || !authcert) { - log(LOG_WARN, LD_CRYPTO, "Error creating certificate"); + log_warn(LD_CRYPTO, "Error creating certificate"); goto error; } } @@ -1181,6 +1250,10 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, goto error; SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2); + /* Prefer the server's ordering of ciphers: the client's ordering has + * historically been chosen for fingerprinting resistance. */ + SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + /* Disable TLS1.1 and TLS1.2 if they exist. We need to do this to * workaround a bug present in all OpenSSL 1.0.1 versions (as of 1 * June 2012), wherein renegotiating while using one of these TLS @@ -1189,19 +1262,29 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, * version. Once some version of OpenSSL does TLS1.1 and TLS1.2 * renegotiation properly, we can turn them back on when built with * that version. */ +#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,1,'e') #ifdef SSL_OP_NO_TLSv1_2 SSL_CTX_set_options(result->ctx, SSL_OP_NO_TLSv1_2); #endif #ifdef SSL_OP_NO_TLSv1_1 SSL_CTX_set_options(result->ctx, SSL_OP_NO_TLSv1_1); #endif +#endif + /* Disable TLS tickets if they're supported. We never want to use them; * using them can make our perfect forward secrecy a little worse, *and* * create an opportunity to fingerprint us (since it's unusual to use them * with TLS sessions turned off). + * + * In 0.2.4, clients advertise support for them though, to avoid a TLS + * distinguishability vector. This can give us worse PFS, though, if we + * get a server that doesn't set SSL_OP_NO_TICKET. With luck, there will + * be few such servers by the time 0.2.4 is more stable. */ #ifdef SSL_OP_NO_TICKET - SSL_CTX_set_options(result->ctx, SSL_OP_NO_TICKET); + if (! is_client) { + SSL_CTX_set_options(result->ctx, SSL_OP_NO_TICKET); + } #endif if ( @@ -1222,6 +1305,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, } SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE); #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION SSL_CTX_set_options(result->ctx, @@ -1257,7 +1341,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); if (!is_client) { tor_assert(rsa); - if (!(pkey = _crypto_pk_get_evp_pkey(rsa,1))) + if (!(pkey = crypto_pk_get_evp_pkey_(rsa,1))) goto error; if (!SSL_CTX_use_PrivateKey(result->ctx, pkey)) goto error; @@ -1269,9 +1353,29 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, { crypto_dh_t *dh = crypto_dh_new(DH_TYPE_TLS); tor_assert(dh); - SSL_CTX_set_tmp_dh(result->ctx, _crypto_dh_get_dh(dh)); + SSL_CTX_set_tmp_dh(result->ctx, crypto_dh_get_dh_(dh)); crypto_dh_free(dh); } +#if (!defined(OPENSSL_NO_EC) && \ + OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)) + if (! is_client) { + int nid; + EC_KEY *ec_key; + if (flags & TOR_TLS_CTX_USE_ECDHE_P224) + nid = NID_secp224r1; + else if (flags & TOR_TLS_CTX_USE_ECDHE_P256) + nid = NID_X9_62_prime256v1; + else + nid = NID_X9_62_prime256v1; + /* Use P-256 for ECDHE. */ + ec_key = EC_KEY_new_by_curve_name(nid); + if (ec_key != NULL) /*XXXX Handle errors? */ + SSL_CTX_set_tmp_ecdh(result->ctx, ec_key); + EC_KEY_free(ec_key); + } +#else + (void)flags; +#endif SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER, always_accept_verify_cb); /* let us realloc bufs that we're writing from */ @@ -1307,29 +1411,120 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, return NULL; } +/** Invoked when a TLS state changes: log the change at severity 'debug' */ +static void +tor_tls_debug_state_callback(const SSL *ssl, int type, int val) +{ + log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", + ssl, SSL_state_string_long(ssl), type, val); +} + +/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */ +const char * +tor_tls_get_ciphersuite_name(tor_tls_t *tls) +{ + return SSL_get_cipher(tls->ssl); +} + #ifdef V2_HANDSHAKE_SERVER -/** Return true iff the cipher list suggested by the client for <b>ssl</b> is - * a list that indicates that the client knows how to do the v2 TLS connection - * handshake. */ + +/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to + * 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers + * that it claims to support. We'll prune this list to remove the ciphers + * *we* don't recognize. */ +static uint16_t v2_cipher_list[] = { + 0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */ + 0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */ + 0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */ + 0x0038, /* TLS1_TXT_DHE_DSS_WITH_AES_256_SHA */ + 0xc00f, /* TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA */ + 0xc005, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA */ + 0x0035, /* TLS1_TXT_RSA_WITH_AES_256_SHA */ + 0xc007, /* TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA */ + 0xc009, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA */ + 0xc011, /* TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA */ + 0xc013, /* TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA */ + 0x0033, /* TLS1_TXT_DHE_RSA_WITH_AES_128_SHA */ + 0x0032, /* TLS1_TXT_DHE_DSS_WITH_AES_128_SHA */ + 0xc00c, /* TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA */ + 0xc00e, /* TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA */ + 0xc002, /* TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA */ + 0xc004, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA */ + 0x0004, /* SSL3_TXT_RSA_RC4_128_MD5 */ + 0x0005, /* SSL3_TXT_RSA_RC4_128_SHA */ + 0x002f, /* TLS1_TXT_RSA_WITH_AES_128_SHA */ + 0xc008, /* TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA */ + 0xc012, /* TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA */ + 0x0016, /* SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA */ + 0x0013, /* SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA */ + 0xc00d, /* TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA */ + 0xc003, /* TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA */ + 0xfeff, /* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA */ + 0x000a, /* SSL3_TXT_RSA_DES_192_CBC3_SHA */ + 0 +}; +/** Have we removed the unrecognized ciphers from v2_cipher_list yet? */ +static int v2_cipher_list_pruned = 0; + +/** Remove from v2_cipher_list every cipher that we don't support, so that + * comparing v2_cipher_list to a client's cipher list will give a sensible + * result. */ +static void +prune_v2_cipher_list(void) +{ + uint16_t *inp, *outp; + const SSL_METHOD *m = SSLv23_method(); + + inp = outp = v2_cipher_list; + while (*inp) { + unsigned char cipherid[3]; + const SSL_CIPHER *cipher; + /* Is there no better way to do this? */ + set_uint16(cipherid, htons(*inp)); + cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting + * with a two-byte 'cipherid', it may look for a v2 + * cipher with the appropriate 3 bytes. */ + cipher = m->get_cipher_by_char(cipherid); + if (cipher) { + tor_assert((cipher->id & 0xffff) == *inp); + *outp++ = *inp++; + } else { + inp++; + } + } + *outp = 0; + + v2_cipher_list_pruned = 1; +} + +/** Examine the client cipher list in <b>ssl</b>, and determine what kind of + * client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2, + * CIPHERS_UNRESTRICTED. + **/ static int -tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address) +tor_tls_classify_client_ciphers(const SSL *ssl, + STACK_OF(SSL_CIPHER) *peer_ciphers) { - int i; - SSL_SESSION *session; + int i, res; + tor_tls_t *tor_tls; + if (PREDICT_UNLIKELY(!v2_cipher_list_pruned)) + prune_v2_cipher_list(); + + tor_tls = tor_tls_get_by_ssl(ssl); + if (tor_tls && tor_tls->client_cipher_list_type) + return tor_tls->client_cipher_list_type; + /* If we reached this point, we just got a client hello. See if there is * a cipher list. */ - if (!(session = SSL_get_session((SSL *)ssl))) { - log_info(LD_NET, "No session on TLS?"); - return 0; - } - if (!session->ciphers) { + if (!peer_ciphers) { log_info(LD_NET, "No ciphers on session"); - return 0; + res = CIPHERS_ERR; + goto done; } /* Now we need to see if there are any ciphers whose presence means we're * dealing with an updated Tor. */ - for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) { - SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i); + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); const char *ciphername = SSL_CIPHER_get_name(cipher); if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) && strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) && @@ -1337,34 +1532,67 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address) strcmp(ciphername, "(NONE)")) { log_debug(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername); // return 1; - goto dump_list; + goto v2_or_higher; } } - return 0; - dump_list: + res = CIPHERS_V1; + goto done; + v2_or_higher: + { + const uint16_t *v2_cipher = v2_cipher_list; + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); + uint16_t id = cipher->id & 0xffff; + if (id == 0x00ff) /* extended renegotiation indicator. */ + continue; + if (!id || id != *v2_cipher) { + res = CIPHERS_UNRESTRICTED; + goto dump_ciphers; + } + ++v2_cipher; + } + if (*v2_cipher != 0) { + res = CIPHERS_UNRESTRICTED; + goto dump_ciphers; + } + res = CIPHERS_V2; + } + + dump_ciphers: { smartlist_t *elts = smartlist_new(); char *s; - for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) { - SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i); + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); const char *ciphername = SSL_CIPHER_get_name(cipher); smartlist_add(elts, (char*)ciphername); } s = smartlist_join_strings(elts, ":", 0, NULL); - log_debug(LD_NET, "Got a non-version-1 cipher list from %s. It is: '%s'", - address, s); + log_debug(LD_NET, "Got a %s V2/V3 cipher list from %s. It is: '%s'", + (res == CIPHERS_V2) ? "fictitious" : "real", ADDR(tor_tls), s); tor_free(s); smartlist_free(elts); } - return 1; + done: + if (tor_tls) + return tor_tls->client_cipher_list_type = res; + + return res; } -/** Invoked when a TLS state changes: log the change at severity 'debug' */ -static void -tor_tls_debug_state_callback(const SSL *ssl, int type, int val) +/** Return true iff the cipher list suggested by the client for <b>ssl</b> is + * a list that indicates that the client knows how to do the v2 TLS connection + * handshake. */ +static int +tor_tls_client_is_using_v2_ciphers(const SSL *ssl) { - log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", - ssl, SSL_state_string_long(ssl), type, val); + SSL_SESSION *session; + if (!(session = SSL_get_session((SSL *)ssl))) { + log_info(LD_NET, "No session on TLS?"); + return CIPHERS_ERR; + } + + return tor_tls_classify_client_ciphers(ssl, session->ciphers) >= CIPHERS_V2; } /** Invoked when we're accepting a connection on <b>ssl</b>, and the connection @@ -1400,7 +1628,7 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) } /* Now check the cipher list. */ - if (tor_tls_client_is_using_v2_ciphers(ssl, ADDR(tls))) { + if (tor_tls_client_is_using_v2_ciphers(ssl)) { if (tls->wasV2Handshake) return; /* We already turned this stuff off for the first handshake; * This is a renegotiation. */ @@ -1426,6 +1654,48 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) } #endif +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0) +/** Callback to get invoked on a server after we've read the list of ciphers + * the client supports, but before we pick our own ciphersuite. + * + * We can't abuse an info_cb for this, since by the time one of the + * client_hello info_cbs is called, we've already picked which ciphersuite to + * use. + * + * Technically, this function is an abuse of this callback, since the point of + * a session_secret_cb is to try to set up and/or verify a shared-secret for + * authentication on the fly. But as long as we return 0, we won't actually be + * setting up a shared secret, and all will be fine. + */ +static int +tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + SSL_CIPHER **cipher, void *arg) +{ + (void) secret; + (void) secret_len; + (void) peer_ciphers; + (void) cipher; + (void) arg; + + if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) == + CIPHERS_UNRESTRICTED) { + SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST); + } + + SSL_set_session_secret_cb(ssl, NULL, NULL); + + return 0; +} +static void +tor_tls_setup_session_secret_cb(tor_tls_t *tls) +{ + SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL); +} +#else +#define tor_tls_setup_session_secret_cb(tls) STMT_NIL +#endif + /** Explain which ciphers we're missing. */ static void log_unsupported_ciphers(smartlist_t *unsupported) @@ -1625,6 +1895,9 @@ tor_tls_new(int sock, int isServer) SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback); } + if (isServer) + tor_tls_setup_session_secret_cb(result); + /* Not expected to get called. */ tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object"); return result; @@ -1721,6 +1994,10 @@ tor_tls_free(tor_tls_t *tls) if (!tls) return; tor_assert(tls->ssl); + { + size_t r,w; + tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */ + } #ifdef SSL_set_tlsext_host_name SSL_set_tlsext_host_name(tls->ssl, NULL); #endif @@ -1761,7 +2038,7 @@ tor_tls_read(tor_tls_t *tls, char *cp, size_t len) return r; } err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET); - if (err == _TOR_TLS_ZERORETURN || err == TOR_TLS_CLOSE) { + if (err == TOR_TLS_ZERORETURN_ || err == TOR_TLS_CLOSE) { log_debug(LD_NET,"read returned r=%d; TLS is closed",r); tls->state = TOR_TLS_ST_CLOSED; return TOR_TLS_CLOSE; @@ -1772,6 +2049,13 @@ tor_tls_read(tor_tls_t *tls, char *cp, size_t len) } } +/** Total number of bytes that we've used TLS to send. Used to track TLS + * overhead. */ +static uint64_t total_bytes_written_over_tls = 0; +/** Total number of bytes that TLS has put on the network for us. Used to + * track TLS overhead. */ +static uint64_t total_bytes_written_by_tls = 0; + /** Underlying function for TLS writing. Write up to <b>n</b> * characters from <b>cp</b> onto <b>tls</b>. On success, returns the * number of characters written. On failure, returns TOR_TLS_ERROR, @@ -1798,6 +2082,7 @@ tor_tls_write(tor_tls_t *tls, const char *cp, size_t n) r = SSL_write(tls->ssl, cp, (int)n); err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO, LD_NET); if (err == TOR_TLS_DONE) { + total_bytes_written_over_tls += r; return r; } if (err == TOR_TLS_WANTWRITE || err == TOR_TLS_WANTREAD) { @@ -1866,7 +2151,7 @@ tor_tls_finish_handshake(tor_tls_t *tls) /* There doesn't seem to be a clear OpenSSL API to clear mode flags. */ tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN; #ifdef V2_HANDSHAKE_SERVER - if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) { + if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) { /* This check is redundant, but back when we did it in the callback, * we might have not been able to look up the tor_tls_t if the code * was buggy. Fixing that. */ @@ -1974,7 +2259,7 @@ tor_tls_shutdown(tor_tls_t *tls) } while (r>0); err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down", LOG_INFO, LD_NET); - if (err == _TOR_TLS_ZERORETURN) { + if (err == TOR_TLS_ZERORETURN_) { tls->state = TOR_TLS_ST_GOTCLOSE; /* fall through... */ } else { @@ -1990,11 +2275,11 @@ tor_tls_shutdown(tor_tls_t *tls) } err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down", LOG_INFO, LD_NET); - if (err == _TOR_TLS_SYSCALL) { + if (err == TOR_TLS_SYSCALL_) { /* The underlying TCP connection closed while we were shutting down. */ tls->state = TOR_TLS_ST_CLOSED; return TOR_TLS_DONE; - } else if (err == _TOR_TLS_ZERORETURN) { + } else if (err == TOR_TLS_ZERORETURN_) { /* The TLS connection says that it sent a shutdown record, but * isn't done shutting down yet. Make sure that this hasn't * happened before, then go back to the start of the function @@ -2002,7 +2287,7 @@ tor_tls_shutdown(tor_tls_t *tls) */ if (tls->state == TOR_TLS_ST_GOTCLOSE || tls->state == TOR_TLS_ST_SENTCLOSE) { - log(LOG_WARN, LD_NET, + log_warn(LD_NET, "TLS returned \"half-closed\" value while already half-closed"); return TOR_TLS_ERROR_MISC; } @@ -2050,9 +2335,10 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem) char mytime[33]; time_t now = time(NULL); struct tm tm; + size_t n; if (problem) - log(severity, LD_GENERAL, + tor_log(severity, LD_GENERAL, "Certificate %s. Either their clock is set wrong, or your clock " "is wrong.", problem); @@ -2075,11 +2361,17 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem) BIO_get_mem_ptr(bio, &buf); s2 = tor_strndup(buf->data, buf->length); - strftime(mytime, 32, "%b %d %H:%M:%S %Y GMT", tor_gmtime_r(&now, &tm)); - - log(severity, LD_GENERAL, - "(certificate lifetime runs from %s through %s. Your time is %s.)", - s1,s2,mytime); + n = strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm)); + if (n > 0) { + tor_log(severity, LD_GENERAL, + "(certificate lifetime runs from %s through %s. Your time is %s.)", + s1,s2,mytime); + } else { + tor_log(severity, LD_GENERAL, + "(certificate lifetime runs from %s through %s. " + "Couldn't get your time.)", + s1, s2); + } end: /* Not expected to get invoked */ @@ -2164,7 +2456,7 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key) rsa = EVP_PKEY_get1_RSA(id_pkey); if (!rsa) goto done; - *identity_key = _crypto_new_pk_from_rsa(rsa); + *identity_key = crypto_new_pk_from_rsa_(rsa); r = 0; @@ -2287,18 +2579,31 @@ tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written) "r=%lu, last_read=%lu, w=%lu, last_written=%lu", r, tls->last_read_count, w, tls->last_write_count); } + total_bytes_written_by_tls += *n_written; tls->last_read_count = r; tls->last_write_count = w; } +/** Return a ratio of the bytes that TLS has sent to the bytes that we've told + * it to send. Used to track whether our TLS records are getting too tiny. */ +MOCK_IMPL(double, +tls_get_write_overhead_ratio,(void)) +{ + if (total_bytes_written_over_tls == 0) + return 1.0; + + return U64_TO_DBL(total_bytes_written_by_tls) / + U64_TO_DBL(total_bytes_written_over_tls); +} + /** Implement check_no_tls_errors: If there are any pending OpenSSL * errors, log an error message. */ void -_check_no_tls_errors(const char *fname, int line) +check_no_tls_errors_(const char *fname, int line) { if (ERR_peek_error() == 0) return; - log(LOG_WARN, LD_CRYPTO, "Unhandled OpenSSL errors found at %s:%d: ", + log_warn(LD_CRYPTO, "Unhandled OpenSSL errors found at %s:%d: ", tor_fix_source_file(fname), line); tls_log_errors(NULL, LOG_WARN, LD_NET, NULL); } diff --git a/src/common/tortls.h b/src/common/tortls.h index 491a5419d..a76ba3bc7 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -1,10 +1,10 @@ /* Copyright (c) 2003, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -#ifndef _TOR_TORTLS_H -#define _TOR_TORTLS_H +#ifndef TOR_TORTLS_H +#define TOR_TORTLS_H /** * \file tortls.h @@ -13,6 +13,7 @@ #include "crypto.h" #include "compat.h" +#include "testsupport.h" /* Opaque structure to hold a TLS connection. */ typedef struct tor_tls_t tor_tls_t; @@ -21,7 +22,7 @@ typedef struct tor_tls_t tor_tls_t; typedef struct tor_cert_t tor_cert_t; /* Possible return values for most tor_tls_* functions. */ -#define _MIN_TOR_TLS_ERROR_VAL -9 +#define MIN_TOR_TLS_ERROR_VAL_ -9 #define TOR_TLS_ERROR_MISC -9 /* Rename to unexpected close or something. XXXX */ #define TOR_TLS_ERROR_IO -8 @@ -54,7 +55,12 @@ const char *tor_tls_err_to_string(int err); void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz); void tor_tls_free_all(void); -int tor_tls_context_init(int is_public_server, + +#define TOR_TLS_CTX_IS_PUBLIC_SERVER (1u<<0) +#define TOR_TLS_CTX_USE_ECDHE_P256 (1u<<1) +#define TOR_TLS_CTX_USE_ECDHE_P224 (1u<<2) + +int tor_tls_context_init(unsigned flags, crypto_pk_t *client_identity, crypto_pk_t *server_identity, unsigned int key_lifetime); @@ -90,6 +96,8 @@ void tor_tls_get_buffer_sizes(tor_tls_t *tls, size_t *rbuf_capacity, size_t *rbuf_bytes, size_t *wbuf_capacity, size_t *wbuf_bytes); +MOCK_DECL(double, tls_get_write_overhead_ratio, (void)); + int tor_tls_used_v1_handshake(tor_tls_t *tls); int tor_tls_received_v3_certificate(tor_tls_t *tls); int tor_tls_get_num_server_handshakes(tor_tls_t *tls); @@ -98,9 +106,9 @@ int tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out); /* Log and abort if there are unhandled TLS errors in OpenSSL's error stack. */ -#define check_no_tls_errors() _check_no_tls_errors(__FILE__,__LINE__) +#define check_no_tls_errors() check_no_tls_errors_(__FILE__,__LINE__) -void _check_no_tls_errors(const char *fname, int line); +void check_no_tls_errors_(const char *fname, int line); void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, int severity, int domain, const char *doing); @@ -129,6 +137,7 @@ int tor_tls_cert_is_valid(int severity, const tor_cert_t *cert, const tor_cert_t *signing_cert, int check_rsa_1024); +const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls); #endif diff --git a/src/common/util.c b/src/common/util.c index 6fb597a3a..e27036a84 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-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -20,11 +20,12 @@ #define UTIL_PRIVATE #include "util.h" #include "torlog.h" -#undef log #include "crypto.h" #include "torint.h" #include "container.h" #include "address.h" +#include "sandbox.h" +#include "backtrace.h" #ifdef _WIN32 #include <io.h> @@ -39,8 +40,8 @@ #endif /* math.h needs this on Linux */ -#ifndef __USE_ISOC99 -#define __USE_ISOC99 1 +#ifndef _USE_ISOC99_ +#define _USE_ISOC99_ 1 #endif #include <math.h> #include <stdlib.h> @@ -95,6 +96,23 @@ #endif /* ===== + * Assertion helper. + * ===== */ +/** Helper for tor_assert: report the assertion failure. */ +void +tor_assertion_failed_(const char *fname, unsigned int line, + const char *func, const char *expr) +{ + char buf[256]; + log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.", + fname, line, func, expr); + tor_snprintf(buf, sizeof(buf), + "Assertion %s failed in %s at %s:%u", + expr, func, fname, line); + log_backtrace(LOG_ERR, LD_BUG, buf); +} + +/* ===== * Memory management * ===== */ #ifdef USE_DMALLOC @@ -125,7 +143,7 @@ * ignored otherwise. */ void * -_tor_malloc(size_t size DMALLOC_PARAMS) +tor_malloc_(size_t size DMALLOC_PARAMS) { void *result; @@ -159,7 +177,7 @@ _tor_malloc(size_t size DMALLOC_PARAMS) * the process on error. (Same as calloc(size,1), but never returns NULL.) */ void * -_tor_malloc_zero(size_t size DMALLOC_PARAMS) +tor_malloc_zero_(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 @@ -167,7 +185,7 @@ _tor_malloc_zero(size_t size DMALLOC_PARAMS) * 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 = _tor_malloc(size DMALLOC_FN_ARGS); + void *result = tor_malloc_(size DMALLOC_FN_ARGS); memset(result, 0, size); return result; } @@ -184,7 +202,7 @@ _tor_malloc_zero(size_t size DMALLOC_PARAMS) * smaller than size). Don't do that then. */ void * -_tor_calloc(size_t nmemb, size_t size DMALLOC_PARAMS) +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 @@ -197,7 +215,7 @@ _tor_calloc(size_t nmemb, size_t size DMALLOC_PARAMS) tor_assert(nmemb < max_nmemb); - result = _tor_malloc_zero((nmemb * size) DMALLOC_FN_ARGS); + result = tor_malloc_zero_((nmemb * size) DMALLOC_FN_ARGS); return result; } @@ -206,7 +224,7 @@ _tor_calloc(size_t nmemb, size_t size DMALLOC_PARAMS) * terminate. (Like realloc(ptr,size), but never returns NULL.) */ void * -_tor_realloc(void *ptr, size_t size DMALLOC_PARAMS) +tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS) { void *result; @@ -230,7 +248,7 @@ _tor_realloc(void *ptr, size_t size DMALLOC_PARAMS) * NULL.) */ char * -_tor_strdup(const char *s DMALLOC_PARAMS) +tor_strdup_(const char *s DMALLOC_PARAMS) { char *dup; tor_assert(s); @@ -254,12 +272,12 @@ _tor_strdup(const char *s DMALLOC_PARAMS) * NULL.) */ char * -_tor_strndup(const char *s, size_t n DMALLOC_PARAMS) +tor_strndup_(const char *s, size_t n DMALLOC_PARAMS) { char *dup; tor_assert(s); tor_assert(n < SIZE_T_CEILING); - dup = _tor_malloc((n+1) DMALLOC_FN_ARGS); + dup = tor_malloc_((n+1) DMALLOC_FN_ARGS); /* Performance note: Ordinarily we prefer strlcpy to strncpy. But * this function gets called a whole lot, and platform strncpy is * much faster than strlcpy when strlen(s) is much longer than n. @@ -272,55 +290,38 @@ _tor_strndup(const char *s, size_t n DMALLOC_PARAMS) /** Allocate a chunk of <b>len</b> bytes, with the same contents as the * <b>len</b> bytes starting at <b>mem</b>. */ void * -_tor_memdup(const void *mem, size_t len DMALLOC_PARAMS) +tor_memdup_(const void *mem, size_t len DMALLOC_PARAMS) { char *dup; tor_assert(len < SIZE_T_CEILING); tor_assert(mem); - dup = _tor_malloc(len DMALLOC_FN_ARGS); + dup = tor_malloc_(len DMALLOC_FN_ARGS); + memcpy(dup, mem, len); + return dup; +} + +/** As tor_memdup(), but add an extra 0 byte at the end of the resulting + * memory. */ +void * +tor_memdup_nulterm_(const void *mem, size_t len DMALLOC_PARAMS) +{ + char *dup; + tor_assert(len < SIZE_T_CEILING+1); + tor_assert(mem); + dup = tor_malloc_(len+1 DMALLOC_FN_ARGS); memcpy(dup, mem, len); + dup[len] = '\0'; return dup; } /** Helper for places that need to take a function pointer to the right * spelling of "free()". */ void -_tor_free(void *mem) +tor_free_(void *mem) { tor_free(mem); } -#if defined(HAVE_MALLOC_GOOD_SIZE) && !defined(HAVE_MALLOC_GOOD_SIZE_PROTOTYPE) -/* Some version of Mac OSX have malloc_good_size in their libc, but not - * actually defined in malloc/malloc.h. We detect this and work around it by - * prototyping. - */ -extern size_t malloc_good_size(size_t size); -#endif - -/** Allocate and return a chunk of memory of size at least *<b>size</b>, using - * the same resources we would use to malloc *<b>sizep</b>. Set *<b>sizep</b> - * to the number of usable bytes in the chunk of memory. */ -void * -_tor_malloc_roundup(size_t *sizep DMALLOC_PARAMS) -{ -#ifdef HAVE_MALLOC_GOOD_SIZE - tor_assert(*sizep < SIZE_T_CEILING); - *sizep = malloc_good_size(*sizep); - return _tor_malloc(*sizep DMALLOC_FN_ARGS); -#elif 0 && defined(HAVE_MALLOC_USABLE_SIZE) && !defined(USE_DMALLOC) - /* Never use malloc_usable_size(); it makes valgrind really unhappy, - * and doesn't win much in terms of usable space where it exists. */ - void *result; - tor_assert(*sizep < SIZE_T_CEILING); - result = _tor_malloc(*sizep DMALLOC_FN_ARGS); - *sizep = malloc_usable_size(result); - return result; -#else - return _tor_malloc(*sizep DMALLOC_FN_ARGS); -#endif -} - /** Call the platform malloc info function, and dump the results to the log at * level <b>severity</b>. If no such function exists, do nothing. */ void @@ -354,8 +355,8 @@ tor_log_mallinfo(int severity) * ===== */ /** - * Returns the natural logarithm of d base 2. We define this wrapper here so - * as to make it easier not to conflict with Tor's log() macro. + * Returns the natural logarithm of d base e. We defined this wrapper here so + * to avoid conflicts with old versions of tor_log(), which were named log(). */ double tor_mathlog(double d) @@ -363,9 +364,9 @@ tor_mathlog(double d) return log(d); } -/** Return the long integer closest to d. We define this wrapper here so - * that not all users of math.h need to use the right incancations to get - * the c99 functions. */ +/** Return the long integer closest to <b>d</b>. We define this wrapper + * here so that not all users of math.h need to use the right incantations + * to get the c99 functions. */ long tor_lround(double d) { @@ -378,6 +379,21 @@ tor_lround(double d) #endif } +/** Return the 64-bit integer closest to d. We define this wrapper here so + * that not all users of math.h need to use the right incantations to get the + * c99 functions. */ +int64_t +tor_llround(double d) +{ +#if defined(HAVE_LLROUND) + return (int64_t)llround(d); +#elif defined(HAVE_RINT) + return (int64_t)rint(d); +#else + return (int64_t)(d > 0 ? d + 0.5 : ceil(d - 0.5)); +#endif +} + /** Returns floor(log2(u64)). If u64 is 0, (incorrectly) returns 0. */ int tor_log2(uint64_t u64) @@ -410,12 +426,24 @@ tor_log2(uint64_t u64) return r; } -/** Return the power of 2 closest to <b>u64</b>. */ +/** Return the power of 2 in range [1,UINT64_MAX] closest to <b>u64</b>. If + * there are two powers of 2 equally close, round down. */ uint64_t round_to_power_of_2(uint64_t u64) { - int lg2 = tor_log2(u64); - uint64_t low = U64_LITERAL(1) << lg2, high = U64_LITERAL(1) << (lg2+1); + int lg2; + uint64_t low; + uint64_t high; + if (u64 == 0) + return 1; + + lg2 = tor_log2(u64); + low = U64_LITERAL(1) << lg2; + + if (lg2 == 63) + return low; + + high = U64_LITERAL(1) << (lg2+1); if (high - u64 < u64 - low) return high; else @@ -655,6 +683,16 @@ fast_memcmpstart(const void *mem, size_t memlen, return fast_memcmp(mem, prefix, plen); } +/** Given a nul-terminated string s, set every character before the nul + * to zero. */ +void +tor_strclear(char *s) +{ + while (*s) { + *s++ = '\0'; + } +} + /** Return a pointer to the first char of s that is not whitespace and * not a comment, or to the terminating NUL if no such character exists. */ @@ -860,6 +898,39 @@ tor_digest_is_zero(const char *digest) return tor_memeq(digest, ZERO_DIGEST, DIGEST_LEN); } +/** Return true if <b>string</b> is a valid 'key=[value]' string. + * "value" is optional, to indicate the empty string. Log at logging + * <b>severity</b> if something ugly happens. */ +int +string_is_key_value(int severity, const char *string) +{ + /* position of equal sign in string */ + const char *equal_sign_pos = NULL; + + tor_assert(string); + + if (strlen(string) < 2) { /* "x=" is shortest args string */ + tor_log(severity, LD_GENERAL, "'%s' is too short to be a k=v value.", + escaped(string)); + return 0; + } + + equal_sign_pos = strchr(string, '='); + if (!equal_sign_pos) { + tor_log(severity, LD_GENERAL, "'%s' is not a k=v value.", escaped(string)); + return 0; + } + + /* validate that the '=' is not in the beginning of the string. */ + if (equal_sign_pos == string) { + tor_log(severity, LD_GENERAL, "'%s' is not a valid k=v value.", + escaped(string)); + return 0; + } + + return 1; +} + /** Return true iff the DIGEST256_LEN bytes in digest are all zero. */ int tor_digest256_is_zero(const char *digest) @@ -1013,7 +1084,7 @@ base16_encode(char *dest, size_t destlen, const char *src, size_t srclen) /** Helper: given a hex digit, return its value, or -1 if it isn't hex. */ static INLINE int -_hex_decode_digit(char c) +hex_decode_digit_(char c) { switch (c) { case '0': return 0; @@ -1041,7 +1112,7 @@ _hex_decode_digit(char c) int hex_decode_digit(char c) { - return _hex_decode_digit(c); + return hex_decode_digit_(c); } /** Given a hexadecimal string of <b>srclen</b> bytes in <b>src</b>, decode it @@ -1059,8 +1130,8 @@ base16_decode(char *dest, size_t destlen, const char *src, size_t srclen) return -1; end = src+srclen; while (src<end) { - v1 = _hex_decode_digit(*src); - v2 = _hex_decode_digit(*(src+1)); + v1 = hex_decode_digit_(*src); + v2 = hex_decode_digit_(*(src+1)); if (v1<0||v2<0) return -1; *(uint8_t*)dest = (v1<<4)|v2; @@ -1160,130 +1231,58 @@ esc_for_log(const char *s) const char * escaped(const char *s) { - static char *_escaped_val = NULL; - tor_free(_escaped_val); + static char *escaped_val_ = NULL; + tor_free(escaped_val_); if (s) - _escaped_val = esc_for_log(s); + escaped_val_ = esc_for_log(s); else - _escaped_val = NULL; + escaped_val_ = NULL; - return _escaped_val; + return escaped_val_; } -/** Rudimentary string wrapping code: given a un-wrapped <b>string</b> (no - * newlines!), break the string into newline-terminated lines of no more than - * <b>width</b> characters long (not counting newline) and insert them into - * <b>out</b> in order. Precede the first line with prefix0, and subsequent - * lines with prefixRest. - */ -/* This uses a stupid greedy wrapping algorithm right now: - * - For each line: - * - Try to fit as much stuff as possible, but break on a space. - * - If the first "word" of the line will extend beyond the allowable - * width, break the word at the end of the width. - */ -void -wrap_string(smartlist_t *out, const char *string, size_t width, - const char *prefix0, const char *prefixRest) +/** Return a newly allocated string equal to <b>string</b>, except that every + * character in <b>chars_to_escape</b> is preceded by a backslash. */ +char * +tor_escape_str_for_pt_args(const char *string, const char *chars_to_escape) { - size_t p0Len, pRestLen, pCurLen; - const char *eos, *prefixCur; - tor_assert(out); + char *new_string = NULL; + char *new_cp = NULL; + size_t length, new_length; + tor_assert(string); - tor_assert(width); - if (!prefix0) - prefix0 = ""; - if (!prefixRest) - prefixRest = ""; - - p0Len = strlen(prefix0); - pRestLen = strlen(prefixRest); - tor_assert(width > p0Len && width > pRestLen); - eos = strchr(string, '\0'); - tor_assert(eos); - pCurLen = p0Len; - prefixCur = prefix0; - - while ((eos-string)+pCurLen > width) { - const char *eol = string + width - pCurLen; - while (eol > string && *eol != ' ') - --eol; - /* eol is now the last space that can fit, or the start of the string. */ - if (eol > string) { - size_t line_len = (eol-string) + pCurLen + 2; - char *line = tor_malloc(line_len); - memcpy(line, prefixCur, pCurLen); - memcpy(line+pCurLen, string, eol-string); - line[line_len-2] = '\n'; - line[line_len-1] = '\0'; - smartlist_add(out, line); - string = eol + 1; - } else { - size_t line_len = width + 2; - char *line = tor_malloc(line_len); - memcpy(line, prefixCur, pCurLen); - memcpy(line+pCurLen, string, width - pCurLen); - line[line_len-2] = '\n'; - line[line_len-1] = '\0'; - smartlist_add(out, line); - string += width-pCurLen; - } - prefixCur = prefixRest; - pCurLen = pRestLen; - } - if (string < eos) { - size_t line_len = (eos-string) + pCurLen + 2; - char *line = tor_malloc(line_len); - memcpy(line, prefixCur, pCurLen); - memcpy(line+pCurLen, string, eos-string); - line[line_len-2] = '\n'; - line[line_len-1] = '\0'; - smartlist_add(out, line); + length = strlen(string); + + if (!length) /* If we were given the empty string, return the same. */ + return tor_strdup(""); + /* (new_length > SIZE_MAX) => ((length * 2) + 1 > SIZE_MAX) => + (length*2 > SIZE_MAX - 1) => (length > (SIZE_MAX - 1)/2) */ + if (length > (SIZE_MAX - 1)/2) /* check for overflow */ + return NULL; + + /* this should be enough even if all characters must be escaped */ + new_length = (length * 2) + 1; + + new_string = new_cp = tor_malloc(new_length); + + while (*string) { + if (strchr(chars_to_escape, *string)) + *new_cp++ = '\\'; + + *new_cp++ = *string++; } + + *new_cp = '\0'; /* NUL-terminate the new string */ + + return new_string; } /* ===== * Time * ===== */ -/** - * Converts struct timeval to a double value. - * Preserves microsecond precision, but just barely. - * Error is approx +/- 0.1 usec when dealing with epoch values. - */ -double -tv_to_double(const struct timeval *tv) -{ - double conv = tv->tv_sec; - conv += tv->tv_usec/1000000.0; - return conv; -} - -/** - * Converts timeval to milliseconds. - */ -int64_t -tv_to_msec(const struct timeval *tv) -{ - int64_t conv = ((int64_t)tv->tv_sec)*1000L; - /* Round ghetto-style */ - conv += ((int64_t)tv->tv_usec+500)/1000L; - return conv; -} - -/** - * Converts timeval to microseconds. - */ -int64_t -tv_to_usec(const struct timeval *tv) -{ - int64_t conv = ((int64_t)tv->tv_sec)*1000000L; - conv += tv->tv_usec; - return conv; -} - /** Return the number of microseconds elapsed between *start and *end. */ long @@ -1322,6 +1321,18 @@ tv_mdiff(const struct timeval *start, const struct timeval *end) return mdiff; } +/** + * Converts timeval to milliseconds. + */ +int64_t +tv_to_msec(const struct timeval *tv) +{ + int64_t conv = ((int64_t)tv->tv_sec)*1000L; + /* Round ghetto-style */ + conv += ((int64_t)tv->tv_usec+500)/1000L; + return conv; +} + /** Yield true iff <b>y</b> is a leap-year. */ #define IS_LEAPYEAR(y) (!(y % 4) && ((y % 100) || !(y % 400))) /** Helper: Return the number of leap-days between Jan 1, y1 and Jan 1, y2. */ @@ -1336,7 +1347,7 @@ n_leapdays(int y1, int y2) static const int days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -/** Compute a time_t given a struct tm. The result is given in GMT, and +/** Compute a time_t given a struct tm. The result is given in UTC, and * does not account for leap seconds. Return 0 on success, -1 on failure. */ int @@ -1377,10 +1388,11 @@ static const char *MONTH_NAMES[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -/** Set <b>buf</b> to the RFC1123 encoding of the GMT value of <b>t</b>. +/** Set <b>buf</b> to the RFC1123 encoding of the UTC value of <b>t</b>. * The buffer must be at least RFC1123_TIME_LEN+1 bytes long. * - * (RFC1123 format is Fri, 29 Sep 2006 15:54:20 GMT) + * (RFC1123 format is "Fri, 29 Sep 2006 15:54:20 GMT". Note the "GMT" + * rather than "UTC".) */ void format_rfc1123_time(char *buf, time_t t) @@ -1398,8 +1410,11 @@ format_rfc1123_time(char *buf, time_t t) memcpy(buf+8, MONTH_NAMES[tm.tm_mon], 3); } -/** Parse the RFC1123 encoding of some time (in GMT) from <b>buf</b>, - * and store the result in *<b>t</b>. +/** Parse the (a subset of) the RFC1123 encoding of some time (in UTC) from + * <b>buf</b>, and store the result in *<b>t</b>. + * + * Note that we only accept the subset generated by format_rfc1123_time above, + * not the full range of formats suggested by RFC 1123. * * Return 0 on success, -1 on failure. */ @@ -1501,7 +1516,7 @@ void format_iso_time_nospace_usec(char *buf, const struct timeval *tv) { tor_assert(tv); - format_iso_time_nospace(buf, tv->tv_sec); + format_iso_time_nospace(buf, (time_t)tv->tv_sec); tor_snprintf(buf+ISO_TIME_LEN, 8, ".%06d", (int)tv->tv_usec); } @@ -1815,7 +1830,8 @@ file_status(const char *fname) int r; f = tor_strdup(fname); clean_name_for_stat(f); - r = stat(f, &st); + log_debug(LD_FS, "stat()ing %s", f); + r = stat(sandbox_intern_string(f), &st); tor_free(f); if (r) { if (errno == ENOENT) { @@ -1827,6 +1843,10 @@ file_status(const char *fname) return FN_DIR; else if (st.st_mode & S_IFREG) return FN_FILE; +#ifndef _WIN32 + else if (st.st_mode & S_IFIFO) + return FN_FILE; +#endif else return FN_ERROR; } @@ -1851,7 +1871,7 @@ check_private_dir(const char *dirname, cpd_check_t check, char *f; #ifndef _WIN32 int mask; - struct passwd *pw = NULL; + const struct passwd *pw = NULL; uid_t running_uid; gid_t running_gid; #else @@ -1861,7 +1881,8 @@ check_private_dir(const char *dirname, cpd_check_t check, tor_assert(dirname); f = tor_strdup(dirname); clean_name_for_stat(f); - r = stat(f, &st); + log_debug(LD_FS, "stat()ing %s", f); + r = stat(sandbox_intern_string(f), &st); tor_free(f); if (r) { if (errno != ENOENT) { @@ -1897,7 +1918,7 @@ check_private_dir(const char *dirname, cpd_check_t check, if (effective_user) { /* Look up the user and group information. * If we have a problem, bail out. */ - pw = getpwnam(effective_user); + pw = tor_getpwnam(effective_user); if (pw == NULL) { log_warn(LD_CONFIG, "Error setting configured user: %s not found", effective_user); @@ -1911,13 +1932,13 @@ check_private_dir(const char *dirname, cpd_check_t check, } if (st.st_uid != running_uid) { - struct passwd *pw = NULL; + const struct passwd *pw = NULL; char *process_ownername = NULL; - pw = getpwuid(running_uid); + pw = tor_getpwuid(running_uid); process_ownername = pw ? tor_strdup(pw->pw_name) : tor_strdup("<unknown>"); - pw = getpwuid(st.st_uid); + pw = tor_getpwuid(st.st_uid); log_warn(LD_FS, "%s is not owned by this user (%s, %d) but by " "%s (%d). Perhaps you are running Tor as the wrong user?", @@ -1983,7 +2004,8 @@ write_str_to_file(const char *fname, const char *str, int bin) #ifdef _WIN32 if (!bin && strchr(str, '\r')) { log_warn(LD_BUG, - "We're writing a text string that already contains a CR."); + "We're writing a text string that already contains a CR to %s", + escaped(fname)); } #endif return write_bytes_to_file(fname, str, strlen(str), bin); @@ -2047,8 +2069,10 @@ start_writing_to_file(const char *fname, int open_flags, int mode, open_flags &= ~O_EXCL; new_file->rename_on_close = 1; } +#if O_BINARY != 0 if (open_flags & O_BINARY) new_file->binary = 1; +#endif new_file->fd = tor_open_cloexec(open_name, open_flags, mode); if (new_file->fd < 0) { @@ -2120,6 +2144,7 @@ static int finish_writing_to_file_impl(open_file_t *file_data, int abort_write) { int r = 0; + tor_assert(file_data && file_data->filename); if (file_data->stdio_file) { if (fclose(file_data->stdio_file)) { @@ -2136,7 +2161,13 @@ finish_writing_to_file_impl(open_file_t *file_data, int abort_write) if (file_data->rename_on_close) { tor_assert(file_data->tempname && file_data->filename); if (abort_write) { - unlink(file_data->tempname); + int res = unlink(file_data->tempname); + if (res != 0) { + /* We couldn't unlink and we'll leave a mess behind */ + log_warn(LD_FS, "Failed to unlink %s: %s", + file_data->tempname, strerror(errno)); + r = -1; + } } else { tor_assert(strcmp(file_data->filename, file_data->tempname)); if (replace_file(file_data->tempname, file_data->filename)) { @@ -2202,12 +2233,20 @@ write_chunks_to_file_impl(const char *fname, const smartlist_t *chunks, return -1; } -/** Given a smartlist of sized_chunk_t, write them atomically to a file - * <b>fname</b>, overwriting or creating the file as necessary. */ +/** Given a smartlist of sized_chunk_t, write them to a file + * <b>fname</b>, overwriting or creating the file as necessary. + * If <b>no_tempfile</b> is 0 then the file will be written + * atomically. */ int -write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin) +write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin, + int no_tempfile) { int flags = OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT); + + if (no_tempfile) { + /* O_APPEND stops write_chunks_to_file from using tempfiles */ + flags |= O_APPEND; + } return write_chunks_to_file_impl(fname, chunks, flags); } @@ -2228,9 +2267,9 @@ write_bytes_to_file_impl(const char *fname, const char *str, size_t len, /** As write_str_to_file, but does not assume a NUL-terminated * string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */ -int -write_bytes_to_file(const char *fname, const char *str, size_t len, - int bin) +MOCK_IMPL(int, +write_bytes_to_file,(const char *fname, const char *str, size_t len, + int bin)) { return write_bytes_to_file_impl(fname, str, len, OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT)); @@ -2257,6 +2296,46 @@ write_bytes_to_new_file(const char *fname, const char *str, size_t len, (bin?O_BINARY:O_TEXT)); } +/** + * Read the contents of the open file <b>fd</b> presuming it is a FIFO + * (or similar) file descriptor for which the size of the file isn't + * known ahead of time. Return NULL on failure, and a NUL-terminated + * string on success. On success, set <b>sz_out</b> to the number of + * bytes read. + */ +char * +read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out) +{ + ssize_t r; + size_t pos = 0; + char *string = NULL; + size_t string_max = 0; + + if (max_bytes_to_read+1 >= SIZE_T_CEILING) + return NULL; + + do { + /* XXXX This "add 1K" approach is a little goofy; if we care about + * performance here, we should be doubling. But in practice we shouldn't + * be using this function on big files anyway. */ + string_max = pos + 1024; + if (string_max > max_bytes_to_read) + string_max = max_bytes_to_read + 1; + string = tor_realloc(string, string_max); + r = read(fd, string + pos, string_max - pos - 1); + if (r < 0) { + tor_free(string); + return NULL; + } + + pos += r; + } while (r > 0 && pos < max_bytes_to_read); + + *sz_out = pos; + string[pos] = '\0'; + return string; +} + /** Read the contents of <b>filename</b> into a newly allocated * string; return the string on success or NULL on failure. * @@ -2305,8 +2384,26 @@ read_file_to_str(const char *filename, int flags, struct stat *stat_out) return NULL; } - if ((uint64_t)(statbuf.st_size)+1 >= SIZE_T_CEILING) +#ifndef _WIN32 +/** When we detect that we're reading from a FIFO, don't read more than + * this many bytes. It's insane overkill for most uses. */ +#define FIFO_READ_MAX (1024*1024) + if (S_ISFIFO(statbuf.st_mode)) { + size_t sz = 0; + string = read_file_to_str_until_eof(fd, FIFO_READ_MAX, &sz); + if (string && stat_out) { + statbuf.st_size = sz; + memcpy(stat_out, &statbuf, sizeof(struct stat)); + } + close(fd); + return string; + } +#endif + + if ((uint64_t)(statbuf.st_size)+1 >= SIZE_T_CEILING) { + close(fd); return NULL; + } string = tor_malloc((size_t)(statbuf.st_size+1)); @@ -2466,10 +2563,13 @@ unescape_string(const char *s, char **result, size_t *size_out) * key portion and *<b>value_out</b> to a new string holding the value portion * of the line, and return a pointer to the start of the next line. If we run * out of data, return a pointer to the end of the string. If we encounter an - * error, return NULL. + * error, return NULL and set *<b>err_out</b> (if provided) to an error + * message. */ const char * -parse_config_line_from_str(const char *line, char **key_out, char **value_out) +parse_config_line_from_str_verbose(const char *line, char **key_out, + char **value_out, + const char **err_out) { /* I believe the file format here is supposed to be: FILE = (EMPTYLINE | LINE)* (EMPTYLASTLINE | LASTLINE)? @@ -2543,12 +2643,18 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) /* Find the end of the line. */ if (*line == '\"') { // XXX No continuation handling is done here - if (!(line = unescape_string(line, value_out, NULL))) - return NULL; + if (!(line = unescape_string(line, value_out, NULL))) { + if (err_out) + *err_out = "Invalid escape sequence in quoted string"; + return NULL; + } while (*line == ' ' || *line == '\t') ++line; - if (*line && *line != '#' && *line != '\n') + if (*line && *line != '#' && *line != '\n') { + if (err_out) + *err_out = "Excess data after quoted string"; return NULL; + } } else { /* Look for the end of the line. */ while (*line && *line != '\n' && (*line != '#' || continuation)) { @@ -2683,9 +2789,9 @@ digit_to_num(char d) * success, store the result in <b>out</b>, advance bufp to the next * character, and return 0. On failure, return -1. */ static int -scan_unsigned(const char **bufp, unsigned *out, int width, int base) +scan_unsigned(const char **bufp, unsigned long *out, int width, int base) { - unsigned result = 0; + unsigned long result = 0; int scanned_so_far = 0; const int hex = base==16; tor_assert(base == 10 || base == 16); @@ -2697,8 +2803,8 @@ scan_unsigned(const char **bufp, unsigned *out, int width, int base) while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp)) && scanned_so_far < width) { int digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++); - unsigned new_result = result * base + digit; - if (new_result > UINT32_MAX || new_result < result) + unsigned long new_result = result * base + digit; + if (new_result < result) return -1; /* over/underflow. */ result = new_result; ++scanned_so_far; @@ -2711,6 +2817,89 @@ scan_unsigned(const char **bufp, unsigned *out, int width, int base) return 0; } +/** Helper: Read an signed int from *<b>bufp</b> of up to <b>width</b> + * characters. (Handle arbitrary width if <b>width</b> is less than 0.) On + * success, store the result in <b>out</b>, advance bufp to the next + * character, and return 0. On failure, return -1. */ +static int +scan_signed(const char **bufp, long *out, int width) +{ + int neg = 0; + unsigned long result = 0; + + if (!bufp || !*bufp || !out) + return -1; + if (width<0) + width=MAX_SCANF_WIDTH; + + if (**bufp == '-') { + neg = 1; + ++*bufp; + --width; + } + + if (scan_unsigned(bufp, &result, width, 10) < 0) + return -1; + + if (neg) { + if (result > ((unsigned long)LONG_MAX) + 1) + return -1; /* Underflow */ + *out = -(long)result; + } else { + if (result > LONG_MAX) + return -1; /* Overflow */ + *out = (long)result; + } + + return 0; +} + +/** Helper: Read a decimal-formatted double from *<b>bufp</b> of up to + * <b>width</b> characters. (Handle arbitrary width if <b>width</b> is less + * than 0.) On success, store the result in <b>out</b>, advance bufp to the + * next character, and return 0. On failure, return -1. */ +static int +scan_double(const char **bufp, double *out, int width) +{ + int neg = 0; + double result = 0; + int scanned_so_far = 0; + + if (!bufp || !*bufp || !out) + return -1; + if (width<0) + width=MAX_SCANF_WIDTH; + + if (**bufp == '-') { + neg = 1; + ++*bufp; + } + + while (**bufp && TOR_ISDIGIT(**bufp) && scanned_so_far < width) { + const int digit = digit_to_num(*(*bufp)++); + result = result * 10 + digit; + ++scanned_so_far; + } + if (**bufp == '.') { + double fracval = 0, denominator = 1; + ++*bufp; + ++scanned_so_far; + while (**bufp && TOR_ISDIGIT(**bufp) && scanned_so_far < width) { + const int digit = digit_to_num(*(*bufp)++); + fracval = fracval * 10 + digit; + denominator *= 10; + ++scanned_so_far; + } + result += fracval / denominator; + } + + if (!scanned_so_far) /* No actual digits scanned */ + return -1; + + *out = neg ? -result : result; + return 0; +} + /** Helper: copy up to <b>width</b> non-space characters from <b>bufp</b> to * <b>out</b>. Make sure <b>out</b> is nul-terminated. Advance <b>bufp</b> * to the next non-space character or the EOS. */ @@ -2747,6 +2936,7 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) } } else { int width = -1; + int longmod = 0; ++pattern; if (TOR_ISDIGIT(*pattern)) { width = digit_to_num(*pattern++); @@ -2759,17 +2949,57 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) if (!width) /* No zero-width things. */ return -1; } + if (*pattern == 'l') { + longmod = 1; + ++pattern; + } if (*pattern == 'u' || *pattern == 'x') { - unsigned *u = va_arg(ap, unsigned *); + unsigned long u; const int base = (*pattern == 'u') ? 10 : 16; if (!*buf) return n_matched; - if (scan_unsigned(&buf, u, width, base)<0) + if (scan_unsigned(&buf, &u, width, base)<0) + return n_matched; + if (longmod) { + unsigned long *out = va_arg(ap, unsigned long *); + *out = u; + } else { + unsigned *out = va_arg(ap, unsigned *); + if (u > UINT_MAX) + return n_matched; + *out = (unsigned) u; + } + ++pattern; + ++n_matched; + } else if (*pattern == 'f') { + double *d = va_arg(ap, double *); + if (!longmod) + return -1; /* float not supported */ + if (!*buf) + return n_matched; + if (scan_double(&buf, d, width)<0) return n_matched; ++pattern; ++n_matched; + } else if (*pattern == 'd') { + long lng=0; + if (scan_signed(&buf, &lng, width)<0) + return n_matched; + if (longmod) { + long *out = va_arg(ap, long *); + *out = lng; + } else { + int *out = va_arg(ap, int *); + if (lng < INT_MIN || lng > INT_MAX) + return n_matched; + *out = (int)lng; + } + ++pattern; + ++n_matched; } else if (*pattern == 's') { char *s = va_arg(ap, char *); + if (longmod) + return -1; if (width < 0) return -1; if (scan_string(&buf, s, width)<0) @@ -2778,6 +3008,8 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) ++n_matched; } else if (*pattern == 'c') { char *ch = va_arg(ap, char *); + if (longmod) + return -1; if (width != -1) return -1; if (!*buf) @@ -2788,6 +3020,8 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) } else if (*pattern == '%') { if (*buf != '%') return n_matched; + if (longmod) + return -1; ++buf; ++pattern; } else { @@ -2801,9 +3035,14 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) /** Minimal sscanf replacement: parse <b>buf</b> according to <b>pattern</b> * and store the results in the corresponding argument fields. Differs from - * sscanf in that it: Only handles %u, %x, %c and %Ns. Does not handle - * arbitrarily long widths. %u and %x do not consume any space. Is - * locale-independent. Returns -1 on malformed patterns. + * sscanf in that: + * <ul><li>It only handles %u, %lu, %x, %lx, %[NUM]s, %d, %ld, %lf, and %c. + * <li>It only handles decimal inputs for %lf. (12.3, not 1.23e1) + * <li>It does not handle arbitrarily long widths. + * <li>Numbers do not consume any space characters. + * <li>It is locale-independent. + * <li>%u and %x do not consume any space. + * <li>It returns -1 on malformed patterns.</ul> * * (As with other locale-independent functions, we need this to parse data that * is in ASCII without worrying that the C library's locale-handling will make @@ -2892,9 +3131,10 @@ tor_listdir(const char *dirname) FindClose(handle); tor_free(pattern); #else + const char *prot_dname = sandbox_intern_string(dirname); DIR *d; struct dirent *de; - if (!(d = opendir(dirname))) + if (!(d = opendir(prot_dname))) return NULL; result = smartlist_new(); @@ -3190,14 +3430,59 @@ tor_join_win_cmdline(const char *argv[]) return joined_argv; } +/* As format_{hex,dex}_number_sigsafe, but takes a <b>radix</b> argument + * in range 2..16 inclusive. */ +static int +format_number_sigsafe(unsigned long x, char *buf, int buf_len, + unsigned int radix) +{ + unsigned long tmp; + int len; + char *cp; + + /* NOT tor_assert. This needs to be safe to run from within a signal handler, + * and from within the 'tor_assert() has failed' code. */ + if (radix < 2 || radix > 16) + return 0; + + /* Count how many digits we need. */ + tmp = x; + len = 1; + while (tmp >= radix) { + tmp /= radix; + ++len; + } + + /* Not long enough */ + if (!buf || len >= buf_len) + return 0; + + cp = buf + len; + *cp = '\0'; + do { + unsigned digit = (unsigned) (x % radix); + tor_assert(cp > buf); + --cp; + *cp = "0123456789ABCDEF"[digit]; + x /= radix; + } while (x); + + /* NOT tor_assert; see above. */ + if (cp != buf) { + abort(); + } + + return len; +} + /** - * Helper function to output hex numbers, called by - * format_helper_exit_status(). This writes the hexadecimal digits of x into - * buf, up to max_len digits, and returns the actual number of digits written. - * If there is insufficient space, it will write nothing and return 0. + * Helper function to output hex numbers from within a signal handler. * - * This function DOES NOT add a terminating NUL character to its output: be - * careful! + * Writes the nul-terminated hexadecimal digits of <b>x</b> into a buffer + * <b>buf</b> of size <b>buf_len</b>, and return the actual number of digits + * written, not counting the terminal NUL. + * + * If there is insufficient space, write nothing and return 0. * * This accepts an unsigned int because format_helper_exit_status() needs to * call it with a signed int and an unsigned char, and since the C standard @@ -3212,54 +3497,27 @@ tor_join_win_cmdline(const char *argv[]) * arbitrary C functions. */ int -format_hex_number_for_helper_exit_status(unsigned int x, char *buf, - int max_len) +format_hex_number_sigsafe(unsigned long x, char *buf, int buf_len) { - int len; - unsigned int tmp; - char *cur; - - /* Sanity check */ - if (!buf || max_len <= 0) - return 0; - - /* How many chars do we need for x? */ - if (x > 0) { - len = 0; - tmp = x; - while (tmp > 0) { - tmp >>= 4; - ++len; - } - } else { - len = 1; - } - - /* Bail if we would go past the end of the buffer */ - if (len > max_len) - return 0; - - /* Point to last one */ - cur = buf + len - 1; - - /* Convert x to hex */ - do { - *cur-- = "0123456789ABCDEF"[x & 0xf]; - x >>= 4; - } while (x != 0 && cur >= buf); + return format_number_sigsafe(x, buf, buf_len, 16); +} - /* Return len */ - return len; +/** As format_hex_number_sigsafe, but format the number in base 10. */ +int +format_dec_number_sigsafe(unsigned long x, char *buf, int buf_len) +{ + return format_number_sigsafe(x, buf, buf_len, 10); } +#ifndef _WIN32 /** Format <b>child_state</b> and <b>saved_errno</b> as a hex string placed in * <b>hex_errno</b>. Called between fork and _exit, so must be signal-handler * safe. * - * <b>hex_errno</b> must have at least HEX_ERRNO_SIZE bytes available. + * <b>hex_errno</b> must have at least HEX_ERRNO_SIZE+1 bytes available. * * The format of <b>hex_errno</b> is: "CHILD_STATE/ERRNO\n", left-padded - * with spaces. Note that there is no trailing \0. CHILD_STATE indicates where + * with spaces. CHILD_STATE indicates where * in the processs of starting the child process did the failure occur (see * CHILD_STATE_* macros for definition), and SAVED_ERRNO is the value of * errno when the failure occurred. @@ -3267,7 +3525,7 @@ format_hex_number_for_helper_exit_status(unsigned int x, char *buf, * On success return the number of characters added to hex_errno, not counting * the terminating NUL; return -1 on error. */ -int +STATIC int format_helper_exit_status(unsigned char child_state, int saved_errno, char *hex_errno) { @@ -3294,12 +3552,12 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, * Count how many chars of space we have left, and keep a pointer into the * current point in the buffer. */ - left = HEX_ERRNO_SIZE; + left = HEX_ERRNO_SIZE+1; cur = hex_errno; /* Emit child_state */ - written = format_hex_number_for_helper_exit_status(child_state, - cur, left); + written = format_hex_number_sigsafe(child_state, cur, left); + if (written <= 0) goto err; @@ -3328,8 +3586,7 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, } /* Emit unsigned_errno */ - written = format_hex_number_for_helper_exit_status(unsigned_errno, - cur, left); + written = format_hex_number_sigsafe(unsigned_errno, cur, left); if (written <= 0) goto err; @@ -3338,8 +3595,8 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, left -= written; cur += written; - /* Check that we have enough space left for a newline */ - if (left <= 0) + /* Check that we have enough space left for a newline and a NUL */ + if (left <= 1) goto err; /* Emit the newline and NUL */ @@ -3360,6 +3617,7 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, done: return res; } +#endif /* Maximum number of file descriptors, if we cannot get it via sysconf() */ #define DEFAULT_MAX_FD 256 @@ -3555,7 +3813,7 @@ tor_spawn_background(const char *const filename, const char **argv, TRUE, // handles are inherited /*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess() * work?) */ - 0, // creation flags + CREATE_NO_WINDOW, // creation flags (env==NULL) ? NULL : env->windows_environment_block, NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer @@ -3594,7 +3852,7 @@ tor_spawn_background(const char *const filename, const char **argv, this is used for printing out the error message */ unsigned char child_state = CHILD_STATE_INIT; - char hex_errno[HEX_ERRNO_SIZE]; + char hex_errno[HEX_ERRNO_SIZE + 2]; /* + 1 should be sufficient actually */ static int max_fd = -1; @@ -3630,12 +3888,13 @@ tor_spawn_background(const char *const filename, const char **argv, child_state = CHILD_STATE_MAXFD; #ifdef _SC_OPEN_MAX - if (-1 != max_fd) { + if (-1 == max_fd) { max_fd = (int) sysconf(_SC_OPEN_MAX); - if (max_fd == -1) + if (max_fd == -1) { max_fd = DEFAULT_MAX_FD; log_warn(LD_GENERAL, "Cannot find maximum file descriptor, assuming %d", max_fd); + } } #else max_fd = DEFAULT_MAX_FD; @@ -3775,19 +4034,26 @@ tor_spawn_background(const char *const filename, const char **argv, * <b>process_handle</b>. * If <b>also_terminate_process</b> is true, also terminate the * process of the process handle. */ -void -tor_process_handle_destroy(process_handle_t *process_handle, - int also_terminate_process) +MOCK_IMPL(void, +tor_process_handle_destroy,(process_handle_t *process_handle, + int also_terminate_process)) { if (!process_handle) return; if (also_terminate_process) { if (tor_terminate_process(process_handle) < 0) { - log_notice(LD_GENERAL, "Failed to terminate process with PID '%d'", - tor_process_get_pid(process_handle)); + const char *errstr = +#ifdef _WIN32 + format_win32_error(GetLastError()); +#else + strerror(errno); +#endif + log_notice(LD_GENERAL, "Failed to terminate process with " + "PID '%d' ('%s').", tor_process_get_pid(process_handle), + errstr); } else { - log_info(LD_GENERAL, "Terminated process with PID '%d'", + log_info(LD_GENERAL, "Terminated process with PID '%d'.", tor_process_get_pid(process_handle)); } } @@ -4255,7 +4521,94 @@ tor_split_lines(smartlist_t *sl, char *buf, int len) return smartlist_len(sl); } +/** Return a string corresponding to <b>stream_status</b>. */ +const char * +stream_status_to_string(enum stream_status stream_status) +{ + switch (stream_status) { + case IO_STREAM_OKAY: + return "okay"; + case IO_STREAM_EAGAIN: + return "temporarily unavailable"; + case IO_STREAM_TERM: + return "terminated"; + case IO_STREAM_CLOSED: + return "closed"; + default: + tor_fragile_assert(); + return "unknown"; + } +} + +/* DOCDOC */ +static void +log_portfw_spawn_error_message(const char *buf, + const char *executable, int *child_status) +{ + /* Parse error message */ + int retval, child_state, saved_errno; + retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x", + &child_state, &saved_errno); + if (retval == 2) { + log_warn(LD_GENERAL, + "Failed to start child process \"%s\" in state %d: %s", + executable, child_state, strerror(saved_errno)); + if (child_status) + *child_status = 1; + } else { + /* Failed to parse message from child process, log it as a + warning */ + log_warn(LD_GENERAL, + "Unexpected message from port forwarding helper \"%s\": %s", + executable, buf); + } +} + #ifdef _WIN32 + +/** Return a smartlist containing lines outputted from + * <b>handle</b>. Return NULL on error, and set + * <b>stream_status_out</b> appropriately. */ +MOCK_IMPL(smartlist_t *, +tor_get_lines_from_handle, (HANDLE *handle, + enum stream_status *stream_status_out)) +{ + int pos; + char stdout_buf[600] = {0}; + smartlist_t *lines = NULL; + + tor_assert(stream_status_out); + + *stream_status_out = IO_STREAM_TERM; + + pos = tor_read_all_handle(handle, stdout_buf, sizeof(stdout_buf) - 1, NULL); + if (pos < 0) { + *stream_status_out = IO_STREAM_TERM; + return NULL; + } + if (pos == 0) { + *stream_status_out = IO_STREAM_EAGAIN; + return NULL; + } + + /* End with a null even if there isn't a \r\n at the end */ + /* TODO: What if this is a partial line? */ + stdout_buf[pos] = '\0'; + + /* Split up the buffer */ + lines = smartlist_new(); + tor_split_lines(lines, stdout_buf, pos); + + /* Currently 'lines' is populated with strings residing on the + stack. Replace them with their exact copies on the heap: */ + SMARTLIST_FOREACH(lines, char *, line, + SMARTLIST_REPLACE_CURRENT(lines, line, tor_strdup(line))); + + *stream_status_out = IO_STREAM_OKAY; + + return lines; +} + /** Read from stream, and send lines to log at the specified log level. * Returns -1 if there is a error reading, and 0 otherwise. * If the generated stream is flushed more often than on new lines, or @@ -4303,6 +4656,34 @@ log_from_handle(HANDLE *pipe, int severity) #else +/** Return a smartlist containing lines outputted from + * <b>handle</b>. Return NULL on error, and set + * <b>stream_status_out</b> appropriately. */ +MOCK_IMPL(smartlist_t *, +tor_get_lines_from_handle, (FILE *handle, + enum stream_status *stream_status_out)) +{ + enum stream_status stream_status; + char stdout_buf[400]; + smartlist_t *lines = NULL; + + while (1) { + memset(stdout_buf, 0, sizeof(stdout_buf)); + + stream_status = get_string_from_pipe(handle, + stdout_buf, sizeof(stdout_buf) - 1); + if (stream_status != IO_STREAM_OKAY) + goto done; + + if (!lines) lines = smartlist_new(); + smartlist_add(lines, tor_strdup(stdout_buf)); + } + + done: + *stream_status_out = stream_status; + return lines; +} + /** Read from stream, and send lines to log at the specified log level. * Returns 1 if stream is closed normally, -1 if there is a error reading, and * 0 otherwise. Handles lines from tor-fw-helper and @@ -4330,23 +4711,7 @@ log_from_pipe(FILE *stream, int severity, const char *executable, /* Check if buf starts with SPAWN_ERROR_MESSAGE */ if (strcmpstart(buf, SPAWN_ERROR_MESSAGE) == 0) { - /* Parse error message */ - int retval, child_state, saved_errno; - retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x", - &child_state, &saved_errno); - if (retval == 2) { - log_warn(LD_GENERAL, - "Failed to start child process \"%s\" in state %d: %s", - executable, child_state, strerror(saved_errno)); - if (child_status) - *child_status = 1; - } else { - /* Failed to parse message from child process, log it as a - warning */ - log_warn(LD_GENERAL, - "Unexpected message from port forwarding helper \"%s\": %s", - executable, buf); - } + log_portfw_spawn_error_message(buf, executable, child_status); } else { log_fn(severity, LD_GENERAL, "Port forwarding helper says: %s", buf); } @@ -4421,9 +4786,144 @@ get_string_from_pipe(FILE *stream, char *buf_out, size_t count) return IO_STREAM_TERM; } -/* DOCDOC tor_check_port_forwarding */ +/** Parse a <b>line</b> from tor-fw-helper and issue an appropriate + * log message to our user. */ +static void +handle_fw_helper_line(const char *executable, const char *line) +{ + smartlist_t *tokens = smartlist_new(); + char *message = NULL; + char *message_for_log = NULL; + const char *external_port = NULL; + const char *internal_port = NULL; + const char *result = NULL; + int port = 0; + int success = 0; + + if (strcmpstart(line, SPAWN_ERROR_MESSAGE) == 0) { + /* We need to check for SPAWN_ERROR_MESSAGE again here, since it's + * possible that it got sent after we tried to read it in log_from_pipe. + * + * XXX Ideally, we should be using one of stdout/stderr for the real + * output, and one for the output of the startup code. We used to do that + * before cd05f35d2c. + */ + int child_status; + log_portfw_spawn_error_message(line, executable, &child_status); + goto done; + } + + smartlist_split_string(tokens, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + + if (smartlist_len(tokens) < 5) + goto err; + + if (strcmp(smartlist_get(tokens, 0), "tor-fw-helper") || + strcmp(smartlist_get(tokens, 1), "tcp-forward")) + goto err; + + external_port = smartlist_get(tokens, 2); + internal_port = smartlist_get(tokens, 3); + result = smartlist_get(tokens, 4); + + if (smartlist_len(tokens) > 5) { + /* If there are more than 5 tokens, they are part of [<message>]. + Let's use a second smartlist to form the whole message; + strncat loops suck. */ + int i; + int message_words_n = smartlist_len(tokens) - 5; + smartlist_t *message_sl = smartlist_new(); + for (i = 0; i < message_words_n; i++) + smartlist_add(message_sl, smartlist_get(tokens, 5+i)); + + tor_assert(smartlist_len(message_sl) > 0); + message = smartlist_join_strings(message_sl, " ", 0, NULL); + + /* wrap the message in log-friendly wrapping */ + tor_asprintf(&message_for_log, " ('%s')", message); + + smartlist_free(message_sl); + } + + port = atoi(external_port); + if (port < 1 || port > 65535) + goto err; + + port = atoi(internal_port); + if (port < 1 || port > 65535) + goto err; + + if (!strcmp(result, "SUCCESS")) + success = 1; + else if (!strcmp(result, "FAIL")) + success = 0; + else + goto err; + + if (!success) { + log_warn(LD_GENERAL, "Tor was unable to forward TCP port '%s' to '%s'%s. " + "Please make sure that your router supports port " + "forwarding protocols (like NAT-PMP). Note that if '%s' is " + "your ORPort, your relay will be unable to receive inbound " + "traffic.", external_port, internal_port, + message_for_log ? message_for_log : "", + internal_port); + } else { + log_info(LD_GENERAL, + "Tor successfully forwarded TCP port '%s' to '%s'%s.", + external_port, internal_port, + message_for_log ? message_for_log : ""); + } + + goto done; + + err: + log_warn(LD_GENERAL, "tor-fw-helper sent us a string we could not " + "parse (%s).", line); + + done: + SMARTLIST_FOREACH(tokens, char *, cp, tor_free(cp)); + smartlist_free(tokens); + tor_free(message); + tor_free(message_for_log); +} + +/** Read what tor-fw-helper has to say in its stdout and handle it + * appropriately */ +static int +handle_fw_helper_output(const char *executable, + process_handle_t *process_handle) +{ + smartlist_t *fw_helper_output = NULL; + enum stream_status stream_status = 0; + + fw_helper_output = + tor_get_lines_from_handle(tor_process_get_stdout_pipe(process_handle), + &stream_status); + if (!fw_helper_output) { /* didn't get any output from tor-fw-helper */ + /* if EAGAIN we should retry in the future */ + return (stream_status == IO_STREAM_EAGAIN) ? 0 : -1; + } + + /* Handle the lines we got: */ + SMARTLIST_FOREACH_BEGIN(fw_helper_output, char *, line) { + handle_fw_helper_line(executable, line); + tor_free(line); + } SMARTLIST_FOREACH_END(line); + + smartlist_free(fw_helper_output); + + return 0; +} + +/** Spawn tor-fw-helper and ask it to forward the ports in + * <b>ports_to_forward</b>. <b>ports_to_forward</b> contains strings + * of the form "<external port>:<internal port>", which is the format + * that tor-fw-helper expects. */ void -tor_check_port_forwarding(const char *filename, int dir_port, int or_port, +tor_check_port_forwarding(const char *filename, + smartlist_t *ports_to_forward, time_t now) { /* When fw-helper succeeds, how long do we wait until running it again */ @@ -4437,32 +4937,51 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, static process_handle_t *child_handle=NULL; static time_t time_to_run_helper = 0; - int stdout_status, stderr_status, retval; - const char *argv[10]; - char s_dirport[6], s_orport[6]; + int stderr_status, retval; + int stdout_status = 0; tor_assert(filename); - /* Set up command line for tor-fw-helper */ - snprintf(s_dirport, sizeof s_dirport, "%d", dir_port); - snprintf(s_orport, sizeof s_orport, "%d", or_port); - - /* TODO: Allow different internal and external ports */ - argv[0] = filename; - argv[1] = "--internal-or-port"; - argv[2] = s_orport; - argv[3] = "--external-or-port"; - argv[4] = s_orport; - argv[5] = "--internal-dir-port"; - argv[6] = s_dirport; - argv[7] = "--external-dir-port"; - argv[8] = s_dirport; - argv[9] = NULL; - /* Start the child, if it is not already running */ if ((!child_handle || child_handle->status != PROCESS_STATUS_RUNNING) && time_to_run_helper < now) { - int status; + /*tor-fw-helper cli looks like this: tor_fw_helper -p :5555 -p 4555:1111 */ + const char **argv; /* cli arguments */ + int args_n, status; + int argv_index = 0; /* index inside 'argv' */ + + tor_assert(smartlist_len(ports_to_forward) > 0); + + /* check for overflow during 'argv' allocation: + (len(ports_to_forward)*2 + 2)*sizeof(char*) > SIZE_MAX == + len(ports_to_forward) > (((SIZE_MAX/sizeof(char*)) - 2)/2) */ + if ((size_t) smartlist_len(ports_to_forward) > + (((SIZE_MAX/sizeof(char*)) - 2)/2)) { + log_warn(LD_GENERAL, + "Overflow during argv allocation. This shouldn't happen."); + return; + } + /* check for overflow during 'argv_index' increase: + ((len(ports_to_forward)*2 + 2) > INT_MAX) == + len(ports_to_forward) > (INT_MAX - 2)/2 */ + if (smartlist_len(ports_to_forward) > (INT_MAX - 2)/2) { + log_warn(LD_GENERAL, + "Overflow during argv_index increase. This shouldn't happen."); + return; + } + + /* Calculate number of cli arguments: one for the filename, two + 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_malloc_zero(sizeof(char*)*args_n); + + argv[argv_index++] = filename; + SMARTLIST_FOREACH_BEGIN(ports_to_forward, const char *, port) { + argv[argv_index++] = "-p"; + argv[argv_index++] = port; + } SMARTLIST_FOREACH_END(port); + argv[argv_index] = NULL; /* Assume tor-fw-helper will succeed, start it later*/ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS; @@ -4479,6 +4998,9 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, status = tor_spawn_background(filename, argv, NULL, &child_handle); #endif + tor_free_((void*)argv); + argv=NULL; + if (PROCESS_STATUS_ERROR == status) { log_warn(LD_GENERAL, "Failed to start port forwarding helper %s", filename); @@ -4496,16 +5018,17 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, /* Read from stdout/stderr and log result */ retval = 0; #ifdef _WIN32 - stdout_status = log_from_handle(child_handle->stdout_pipe, LOG_INFO); - stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_WARN); - /* If we got this far (on Windows), the process started */ - retval = 0; + stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_INFO); #else - stdout_status = log_from_pipe(child_handle->stdout_handle, - LOG_INFO, filename, &retval); stderr_status = log_from_pipe(child_handle->stderr_handle, - LOG_WARN, filename, &retval); + LOG_INFO, filename, &retval); #endif + if (handle_fw_helper_output(filename, child_handle) < 0) { + log_warn(LD_GENERAL, "Failed to handle fw helper output."); + stdout_status = -1; + retval = -1; + } + if (retval) { /* There was a problem in the child process */ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL; @@ -4551,3 +5074,45 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, } } +/** Initialize the insecure RNG <b>rng</b> from a seed value <b>seed</b>. */ +void +tor_init_weak_random(tor_weak_rng_t *rng, unsigned seed) +{ + rng->state = (uint32_t)(seed & 0x7fffffff); +} + +/** Return a randomly chosen value in the range 0..TOR_WEAK_RANDOM_MAX based + * on the RNG state of <b>rng</b>. This entropy will not be cryptographically + * strong; do not rely on it for anything an adversary should not be able to + * predict. */ +int32_t +tor_weak_random(tor_weak_rng_t *rng) +{ + /* Here's a linear congruential generator. OpenBSD and glibc use these + * parameters; they aren't too bad, and should have maximal period over the + * range 0..INT32_MAX. We don't want to use the platform rand() or random(), + * since some platforms have bad weak RNGs that only return values in the + * range 0..INT16_MAX, which just isn't enough. */ + rng->state = (rng->state * 1103515245 + 12345) & 0x7fffffff; + return (int32_t) rng->state; +} + +/** Return a random number in the range [0 , <b>top</b>). {That is, the range + * of integers i such that 0 <= i < top.} Chooses uniformly. Requires that + * top is greater than 0. This randomness is not cryptographically strong; do + * not rely on it for anything an adversary should not be able to predict. */ +int32_t +tor_weak_random_range(tor_weak_rng_t *rng, int32_t top) +{ + /* We don't want to just do tor_weak_random() % top, since random() is often + * implemented with an LCG whose modulus is a power of 2, and those are + * cyclic in their low-order bits. */ + int divisor, result; + tor_assert(top > 0); + divisor = TOR_WEAK_RANDOM_MAX / top; + do { + result = (int32_t)(tor_weak_random(rng) / divisor); + } while (result >= top); + return result; +} + diff --git a/src/common/util.h b/src/common/util.h index 8977d273c..18dc20639 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -8,13 +8,14 @@ * \brief Headers for util.c **/ -#ifndef _TOR_UTIL_H -#define _TOR_UTIL_H +#ifndef TOR_UTIL_H +#define TOR_UTIL_H #include "orconfig.h" #include "torint.h" #include "compat.h" #include "di_ops.h" +#include "testsupport.h" #include <stdio.h> #include <stdlib.h> #ifdef _WIN32 @@ -47,13 +48,13 @@ /** Like assert(3), but send assertion failures to the log as well as to * stderr. */ #define tor_assert(expr) STMT_BEGIN \ - if (PREDICT_UNLIKELY(!(expr))) { \ - log_err(LD_BUG, "%s:%d: %s: Assertion %s failed; aborting.", \ - _SHORT_FILE_, __LINE__, __func__, #expr); \ - fprintf(stderr,"%s:%d %s: Assertion %s failed; aborting.\n", \ - _SHORT_FILE_, __LINE__, __func__, #expr); \ - abort(); \ - } STMT_END + if (PREDICT_UNLIKELY(!(expr))) { \ + tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \ + abort(); \ + } STMT_END + +void tor_assertion_failed_(const char *fname, unsigned int line, + const char *func, const char *expr); /* If we're building with dmalloc, we want all of our memory allocation * functions to take an extra file/line pair of arguments. If not, not. @@ -62,7 +63,7 @@ * to calls. */ #ifdef USE_DMALLOC #define DMALLOC_PARAMS , const char *file, const int line -#define DMALLOC_ARGS , _SHORT_FILE_, __LINE__ +#define DMALLOC_ARGS , SHORT_FILE__, __LINE__ #else #define DMALLOC_PARAMS #define DMALLOC_ARGS @@ -74,23 +75,24 @@ #define tor_fragile_assert() /* Memory management */ -void *_tor_malloc(size_t size DMALLOC_PARAMS) ATTR_MALLOC; -void *_tor_malloc_zero(size_t size DMALLOC_PARAMS) ATTR_MALLOC; -void *_tor_malloc_roundup(size_t *size DMALLOC_PARAMS) ATTR_MALLOC; -void *_tor_calloc(size_t nmemb, size_t size DMALLOC_PARAMS) ATTR_MALLOC; -void *_tor_realloc(void *ptr, size_t size DMALLOC_PARAMS); -char *_tor_strdup(const char *s DMALLOC_PARAMS) ATTR_MALLOC ATTR_NONNULL((1)); -char *_tor_strndup(const char *s, size_t n DMALLOC_PARAMS) +void *tor_malloc_(size_t size DMALLOC_PARAMS) ATTR_MALLOC; +void *tor_malloc_zero_(size_t size DMALLOC_PARAMS) ATTR_MALLOC; +void *tor_calloc_(size_t nmemb, size_t size DMALLOC_PARAMS) ATTR_MALLOC; +void *tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS); +char *tor_strdup_(const char *s DMALLOC_PARAMS) ATTR_MALLOC ATTR_NONNULL((1)); +char *tor_strndup_(const char *s, size_t n DMALLOC_PARAMS) + ATTR_MALLOC ATTR_NONNULL((1)); +void *tor_memdup_(const void *mem, size_t len DMALLOC_PARAMS) ATTR_MALLOC ATTR_NONNULL((1)); -void *_tor_memdup(const void *mem, size_t len DMALLOC_PARAMS) +void *tor_memdup_nulterm_(const void *mem, size_t len DMALLOC_PARAMS) ATTR_MALLOC ATTR_NONNULL((1)); -void _tor_free(void *mem); +void tor_free_(void *mem); #ifdef USE_DMALLOC extern int dmalloc_free(const char *file, const int line, void *pnt, const int func_id); #define tor_free(p) STMT_BEGIN \ if (PREDICT_LIKELY((p)!=NULL)) { \ - dmalloc_free(_SHORT_FILE_, __LINE__, (p), 0); \ + dmalloc_free(SHORT_FILE__, __LINE__, (p), 0); \ (p)=NULL; \ } \ STMT_END @@ -100,7 +102,7 @@ extern int dmalloc_free(const char *file, const int line, void *pnt, * and it sets the pointer value to NULL after freeing it. * * This is a macro. If you need a function pointer to release memory from - * tor_malloc(), use _tor_free(). + * tor_malloc(), use tor_free_(). */ #define tor_free(p) STMT_BEGIN \ if (PREDICT_LIKELY((p)!=NULL)) { \ @@ -110,14 +112,14 @@ extern int dmalloc_free(const char *file, const int line, void *pnt, STMT_END #endif -#define tor_malloc(size) _tor_malloc(size DMALLOC_ARGS) -#define tor_malloc_zero(size) _tor_malloc_zero(size DMALLOC_ARGS) -#define tor_calloc(nmemb,size) _tor_calloc(nmemb, size DMALLOC_ARGS) -#define tor_malloc_roundup(szp) _tor_malloc_roundup(szp DMALLOC_ARGS) -#define tor_realloc(ptr, size) _tor_realloc(ptr, size DMALLOC_ARGS) -#define tor_strdup(s) _tor_strdup(s DMALLOC_ARGS) -#define tor_strndup(s, n) _tor_strndup(s, n DMALLOC_ARGS) -#define tor_memdup(s, n) _tor_memdup(s, n DMALLOC_ARGS) +#define tor_malloc(size) tor_malloc_(size DMALLOC_ARGS) +#define tor_malloc_zero(size) tor_malloc_zero_(size DMALLOC_ARGS) +#define tor_calloc(nmemb,size) tor_calloc_(nmemb, size DMALLOC_ARGS) +#define tor_realloc(ptr, size) tor_realloc_(ptr, size DMALLOC_ARGS) +#define tor_strdup(s) tor_strdup_(s DMALLOC_ARGS) +#define tor_strndup(s, n) tor_strndup_(s, n DMALLOC_ARGS) +#define tor_memdup(s, n) tor_memdup_(s, n DMALLOC_ARGS) +#define tor_memdup_nulterm(s, n) tor_memdup_nulterm_(s, n DMALLOC_ARGS) void tor_log_mallinfo(int severity); @@ -161,6 +163,7 @@ void tor_log_mallinfo(int severity); /* Math functions */ double tor_mathlog(double d) ATTR_CONST; long tor_lround(double d) ATTR_CONST; +int64_t tor_llround(double d) ATTR_CONST; int tor_log2(uint64_t u64) ATTR_CONST; uint64_t round_to_power_of_2(uint64_t u64); unsigned round_to_next_multiple_of(unsigned number, unsigned divisor); @@ -173,6 +176,17 @@ int n_bits_set_u8(uint8_t v); * overflow. */ #define CEIL_DIV(a,b) (((a)+(b)-1)/(b)) +/* Return <b>v</b> if it's between <b>min</b> and <b>max</b>. Otherwise + * return <b>min</b> if <b>v</b> is smaller than <b>min</b>, or <b>max</b> if + * <b>b</b> is larger than <b>max</b>. + * + * Requires that <b>min</b> is no more than <b>max</b>. May evaluate any of + * its arguments more than once! */ +#define CLAMP(min,v,max) \ + ( ((v) < (min)) ? (min) : \ + ((v) > (max)) ? (max) : \ + (v) ) + /* String manipulation */ /** Allowable characters in a hexadecimal string. */ @@ -188,6 +202,7 @@ int strcasecmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2)); int strcmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2)); int strcasecmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2)); int fast_memcmpstart(const void *mem, size_t memlen, const char *prefix); +void tor_strclear(char *s); void tor_strstrip(char *s, const char *strip) ATTR_NONNULL((1,2)); long tor_parse_long(const char *s, int base, long min, @@ -208,25 +223,22 @@ const char *find_whitespace_eos(const char *s, const char *eos); const char *find_str_at_start_of_line(const char *haystack, const char *needle); int string_is_C_identifier(const char *string); +int string_is_key_value(int severity, const char *string); int tor_mem_is_zero(const char *mem, size_t len); int tor_digest_is_zero(const char *digest); int tor_digest256_is_zero(const char *digest); char *esc_for_log(const char *string) ATTR_MALLOC; const char *escaped(const char *string); + +char *tor_escape_str_for_pt_args(const char *string, + const char *chars_to_escape); + struct smartlist_t; -void wrap_string(struct smartlist_t *out, const char *string, size_t width, - const char *prefix0, const char *prefixRest); -int tor_vsscanf(const char *buf, const char *pattern, va_list ap) -#ifdef __GNUC__ - __attribute__((format(scanf, 2, 0))) -#endif - ; +int tor_vsscanf(const char *buf, const char *pattern, va_list ap) \ + CHECK_SCANF(2, 0); int tor_sscanf(const char *buf, const char *pattern, ...) -#ifdef __GNUC__ - __attribute__((format(scanf, 2, 3))) -#endif - ; + CHECK_SCANF(2, 3); void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern, ...) CHECK_PRINTF(2, 3); @@ -239,11 +251,9 @@ void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen); int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen); /* Time helpers */ -double tv_to_double(const struct timeval *tv); -int64_t tv_to_msec(const struct timeval *tv); -int64_t tv_to_usec(const struct timeval *tv); long tv_udiff(const struct timeval *start, const struct timeval *end); long tv_mdiff(const struct timeval *start, const struct timeval *end); +int64_t tv_to_msec(const struct timeval *tv); int tor_timegm(const struct tm *tm, time_t *time_out); #define RFC1123_TIME_LEN 29 void format_rfc1123_time(char *buf, time_t t); @@ -283,6 +293,15 @@ void update_approx_time(time_t now); } } </pre> + + As a convenience wrapper for logging, you can replace the above with: + <pre> + if (possibly_very_frequent_event()) { + static ratelim_t warning_limit = RATELIM_INIT(300); + log_fn_ratelim(&warning_limit, LOG_WARN, LD_GENERAL, + "The event occurred!"); + } + </pre> */ typedef struct ratelim_t { int rate; @@ -306,6 +325,8 @@ enum stream_status { IO_STREAM_CLOSED }; +const char *stream_status_to_string(enum stream_status stream_status); + enum stream_status get_string_from_pipe(FILE *stream, char *buf, size_t count); /** Return values from file_status(); see that function's documentation @@ -335,8 +356,9 @@ FILE *fdopen_file(open_file_t *file_data); int finish_writing_to_file(open_file_t *file_data); int abort_writing_to_file(open_file_t *file_data); int write_str_to_file(const char *fname, const char *str, int bin); -int write_bytes_to_file(const char *fname, const char *str, size_t len, - int bin); +MOCK_DECL(int, +write_bytes_to_file,(const char *fname, const char *str, size_t len, + int bin)); /** An ad-hoc type to hold a string of characters and a count; used by * write_chunks_to_file. */ typedef struct sized_chunk_t { @@ -344,7 +366,7 @@ typedef struct sized_chunk_t { size_t len; } sized_chunk_t; int write_chunks_to_file(const char *fname, const struct smartlist_t *chunks, - int bin); + int bin, int no_tempfile); int append_bytes_to_file(const char *fname, const char *str, size_t len, int bin); int write_bytes_to_new_file(const char *fname, const char *str, size_t len, @@ -360,8 +382,14 @@ struct stat; #endif char *read_file_to_str(const char *filename, int flags, struct stat *stat_out) ATTR_MALLOC; -const char *parse_config_line_from_str(const char *line, - char **key_out, char **value_out); +char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, + size_t *sz_out) + ATTR_MALLOC; +const char *parse_config_line_from_str_verbose(const char *line, + char **key_out, char **value_out, + const char **err_out); +#define parse_config_line_from_str(line,key_out,value_out) \ + parse_config_line_from_str_verbose((line),(key_out),(value_out),NULL) char *expand_filename(const char *filename); struct smartlist_t *tor_listdir(const char *dirname); int path_is_relative(const char *filename); @@ -373,7 +401,8 @@ void write_pidfile(char *filename); /* Port forwarding */ void tor_check_port_forwarding(const char *filename, - int dir_port, int or_port, time_t now); + struct smartlist_t *ports_to_forward, + time_t now); typedef struct process_handle_t process_handle_t; typedef struct process_environment_t process_environment_t; @@ -464,16 +493,45 @@ HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle); FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle); #endif -int tor_terminate_process(process_handle_t *process_handle); -void tor_process_handle_destroy(process_handle_t *process_handle, - int also_terminate_process); +#ifdef _WIN32 +MOCK_DECL(struct smartlist_t *, +tor_get_lines_from_handle,(HANDLE *handle, + enum stream_status *stream_status)); +#else +MOCK_DECL(struct smartlist_t *, +tor_get_lines_from_handle,(FILE *handle, + enum stream_status *stream_status)); +#endif + +int +tor_terminate_process(process_handle_t *process_handle); + +MOCK_DECL(void, +tor_process_handle_destroy,(process_handle_t *process_handle, + int also_terminate_process)); + +/* ===== Insecure rng */ +typedef struct tor_weak_rng_t { + uint32_t state; +} tor_weak_rng_t; + +#define TOR_WEAK_RNG_INIT {383745623} +#define TOR_WEAK_RANDOM_MAX (INT_MAX) +void tor_init_weak_random(tor_weak_rng_t *weak_rng, unsigned seed); +int32_t tor_weak_random(tor_weak_rng_t *weak_rng); +int32_t tor_weak_random_range(tor_weak_rng_t *rng, int32_t top); +/** Randomly return true according to <b>rng</b> with probability 1 in + * <b>n</b> */ +#define tor_weak_random_one_in_n(rng, n) (0==tor_weak_random_range((rng),(n))) + +int format_hex_number_sigsafe(unsigned long x, char *buf, int max_len); +int format_dec_number_sigsafe(unsigned long x, char *buf, int max_len); #ifdef UTIL_PRIVATE /* Prototypes for private functions only used by util.c (and unit tests) */ -int format_hex_number_for_helper_exit_status(unsigned int x, char *buf, - int max_len); -int format_helper_exit_status(unsigned char child_state, +#ifndef _WIN32 +STATIC int format_helper_exit_status(unsigned char child_state, int saved_errno, char *hex_errno); /* Space for hex values of child state, a slash, saved_errno (with @@ -482,7 +540,11 @@ int format_helper_exit_status(unsigned char child_state, 1 + sizeof(int) * 2 + 1) #endif +#endif + const char *libor_get_digests(void); +#define ARRAY_LENGTH(x) (sizeof(x)) / sizeof(x[0]) + #endif |