diff options
| author | Moritz Schulte <mo@g10code.com> | 2003-05-13 10:10:41 +0000 |
|---|---|---|
| committer | Moritz Schulte <mo@g10code.com> | 2003-05-13 10:10:41 +0000 |
| commit | 4a0a7c8db5576e40a051e9ba46b62b93dd72e7bd (patch) | |
| tree | a0d17115f9e7af66fc9b524a43ecb01f545a89db /src/secmem.c | |
| parent | d0c0e132fe30212b8cbe97b7a94e8dd1449288de (diff) | |
| download | libgcrypt-4a0a7c8db5576e40a051e9ba46b62b93dd72e7bd.tar.gz | |
2003-05-13 Moritz Schulte <moritz@g10code.com>
* secmem.c (stats_update): New function.
(BLOCK_HEAD_SIZE): New symbol.
(MB_FLAG_ACTIVE): New symbol.
(ADDR_TO_BLOCK, BLOCK_VALID): New macros.
(mb_get_next): New function.
(mb_get_prev): New function.
(mb_merge): New function.
(mb_get_new): New function.
(unused_blocks): Removed variable.
(init_pool): Initialize new memory pool.
(_gcry_secmem_malloc): Use new heap management code.
(_gcry_secmem_free): Likewise.
(_gcry_secmem_realloc): Likewise.
Renamed type MEMBLOCK to memblock_t.
Diffstat (limited to 'src/secmem.c')
| -rw-r--r-- | src/secmem.c | 734 |
1 files changed, 441 insertions, 293 deletions
diff --git a/src/secmem.c b/src/secmem.c index 7d0b6819..7f1a6a34 100644 --- a/src/secmem.c +++ b/src/secmem.c @@ -25,371 +25,510 @@ #include <errno.h> #include <stdarg.h> #include <unistd.h> +#include <stddef.h> + #if defined(HAVE_MLOCK) || defined(HAVE_MMAP) - #include <sys/mman.h> - #include <sys/types.h> - #include <fcntl.h> - #ifdef USE_CAPABILITIES - #include <sys/capability.h> - #endif +# include <sys/mman.h> +# include <sys/types.h> +# include <fcntl.h> +# ifdef USE_CAPABILITIES +# include <sys/capability.h> +# endif #endif #include "g10lib.h" #include "secmem.h" -#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) - #define MAP_ANONYMOUS MAP_ANON +#if defined (MAP_ANON) && ! defined (MAP_ANONYMOUS) +# define MAP_ANONYMOUS MAP_ANON #endif -#define DEFAULT_POOLSIZE 16384 -#define DEFAULT_PAGESIZE 4096 +#define DEFAULT_POOL_SIZE 16384 +#define DEFAULT_PAGE_SIZE 4096 + +typedef struct memblock +{ + unsigned size; /* Size of the memory available to the + user. */ + int flags; /* See below. */ + PROPERLY_ALIGNED_TYPE aligned; +} memblock_t; + +/* This flag specifies that the memory block is in use. */ +#define MB_FLAG_ACTIVE 1 << 0 -typedef struct memblock_struct MEMBLOCK; -struct memblock_struct { - unsigned size; - union { - MEMBLOCK *next; - PROPERLY_ALIGNED_TYPE aligned; - } u; -}; +/* The pool of secure memory. */ +static void *pool; +/* Size of POOL in bytes. */ +static size_t pool_size; +/* True, if the memory pool is ready for use. May be checked in an + atexit function. */ +static volatile int pool_okay; -static void *pool; -static volatile int pool_okay; /* may be checked in an atexit function */ +/* True, if the memory pool is mmapped. */ static volatile int pool_is_mmapped; -static size_t poolsize; /* allocated length */ -static size_t poollen; /* used length */ -static MEMBLOCK *unused_blocks; -static unsigned max_alloced; -static unsigned cur_alloced; -static unsigned max_blocks; -static unsigned cur_blocks; + +/* FIXME? */ static int disable_secmem; static int show_warning; static int no_warning; static int suspend_warning; +/* Stats. */ +static unsigned int cur_alloced, cur_blocks; + +/* The size of the memblock structure; this does not include the + memory that is available to the user. */ +#define BLOCK_HEAD_SIZE \ + offsetof (memblock_t, aligned) +/* Convert an address into the according memory block structure. */ +#define ADDR_TO_BLOCK(addr) \ + (memblock_t *) ((char *) addr - BLOCK_HEAD_SIZE) + +/* Check wether MB is a valid block. */ +#define BLOCK_VALID(mb) \ + (((char *) mb - (char *) pool) < pool_size) + +/* Update the stats. */ static void -print_warn(void) +stats_update (size_t add, size_t sub) +{ + if (add) + { + cur_alloced += add; + cur_blocks++; + } + if (sub) + { + cur_alloced -= sub; + cur_blocks--; + } +} + +/* Return the block following MB or NULL, if MB is the last block. */ +static memblock_t * +mb_get_next (memblock_t *mb) { - if( !no_warning ) - log_info(_("Warning: using insecure memory!\n")); + memblock_t *mb_next; + + mb_next = (memblock_t *) ((char *) mb + BLOCK_HEAD_SIZE + mb->size); + + if (! BLOCK_VALID (mb_next)) + mb_next = NULL; + + return mb_next; } +/* Return the block preceeding MB or NULL, if MB is the first + block. */ +static memblock_t * +mb_get_prev (memblock_t *mb) +{ + memblock_t *mb_prev, *mb_next; + + if (mb == pool) + mb_prev = NULL; + else + { + mb_prev = (memblock_t *) pool; + while (1) + { + mb_next = mb_get_next (mb_prev); + if (mb_next == mb) + break; + else + mb_prev = mb_next; + } + } + + return mb_prev; +} +/* If the preceeding block of MB and/or the following block of MB + exist and are not active, merge them to form a bigger block. */ static void -lock_pool( void *p, size_t n ) +mb_merge (memblock_t *mb) { - #if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK) - int err; + memblock_t *mb_prev, *mb_next; - cap_set_proc( cap_from_text("cap_ipc_lock+ep") ); - err = mlock( p, n ); - if( err && errno ) - err = errno; - cap_set_proc( cap_from_text("cap_ipc_lock+p") ); - - if( err ) { - if( errno != EPERM - #ifdef EAGAIN /* OpenBSD returns this */ - && errno != EAGAIN - #endif - #ifdef ENOSYS /* Some SCOs return this (function not implemented) */ - && errno != ENOSYS - #endif - ) - log_error("can't lock memory: %s\n", strerror(err)); - show_warning = 1; + mb_prev = mb_get_prev (mb); + mb_next = mb_get_next (mb); + + if (mb_prev && (! (mb_prev->flags & MB_FLAG_ACTIVE))) + { + mb_prev->size += BLOCK_HEAD_SIZE + mb->size; + mb = mb_prev; } + if (mb_next && (! (mb_next->flags & MB_FLAG_ACTIVE))) + mb->size += BLOCK_HEAD_SIZE + mb_next->size; +} - #elif defined(HAVE_MLOCK) - uid_t uid; - int err; +/* Return a new block, which can hold SIZE bytes. */ +static memblock_t * +mb_get_new (memblock_t *pool, size_t size) +{ + memblock_t *mb, *mb_split; + + for (mb = pool; BLOCK_VALID (mb); mb = mb_get_next (mb)) + if (! (mb->flags & MB_FLAG_ACTIVE) && mb->size >= size) + { + /* Found a free block. */ + mb->flags |= MB_FLAG_ACTIVE; - uid = getuid(); + if (mb->size - size > BLOCK_HEAD_SIZE) + { + /* Split block. */ + + mb_split = (memblock_t *) (((char *) mb) + BLOCK_HEAD_SIZE + size); + mb_split->size = mb->size - size - BLOCK_HEAD_SIZE; + mb_split->flags = 0; - #ifdef HAVE_BROKEN_MLOCK - if( uid ) { - errno = EPERM; - err = errno; + mb->size = size; + + mb_merge (mb_split); + + } + + break; + } + + if (! BLOCK_VALID (mb)) + mb = NULL; + + return mb; +} + +/* Print a warning message. */ +static void +print_warn (void) +{ + if (!no_warning) + log_info (_("Warning: using insecure memory!\n")); +} + +/* Lock the memory pages into core and drop privileges. */ +static void +lock_pool (void *p, size_t n) +{ +#if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK) + int err; + + cap_set_proc (cap_from_text ("cap_ipc_lock+ep")); + err = mlock (p, n); + if (err && errno) + err = errno; + cap_set_proc (cap_from_text ("cap_ipc_lock+p")); + + if (err) + { + if (errno != EPERM +#ifdef EAGAIN /* OpenBSD returns this */ + && errno != EAGAIN +#endif +#ifdef ENOSYS /* Some SCOs return this (function not implemented) */ + && errno != ENOSYS +#endif + ) + log_error ("can't lock memory: %s\n", strerror (err)); + show_warning = 1; } - else { - err = mlock( p, n ); - if( err && errno ) - err = errno; + +#elif defined(HAVE_MLOCK) + uid_t uid; + int err; + + uid = getuid (); + +#ifdef HAVE_BROKEN_MLOCK + if (uid) + { + errno = EPERM; + err = errno; } - #else - err = mlock( p, n ); - if( err && errno ) + else + { + err = mlock (p, n); + if (err && errno) err = errno; - #endif + } +#else + err = mlock (p, n); + if (err && errno) + err = errno; +#endif - if( uid && !geteuid() ) { - /* check that we really dropped the privs. - * Note: setuid(0) should always fail */ - if( setuid( uid ) || getuid() != geteuid() || !setuid(0) ) - log_fatal("failed to reset uid: %s\n", strerror(errno)); + if (uid && ! geteuid ()) + { + /* check that we really dropped the privs. + * Note: setuid(0) should always fail */ + if (setuid (uid) || getuid () != geteuid () || !setuid (0)) + log_fatal ("failed to reset uid: %s\n", strerror (errno)); } - if( err ) { - if( errno != EPERM - #ifdef EAGAIN /* OpenBSD returns this */ - && errno != EAGAIN - #endif - #ifdef ENOSYS /* Some SCOs return this (function not implemented) */ - && errno != ENOSYS - #endif + if (err) + { + if (errno != EPERM +#ifdef EAGAIN /* OpenBSD returns this */ + && errno != EAGAIN +#endif +#ifdef ENOSYS /* Some SCOs return this (function not implemented) */ + && errno != ENOSYS +#endif ) - log_error("can't lock memory: %s\n", strerror(err)); - show_warning = 1; + log_error ("can't lock memory: %s\n", strerror (err)); + show_warning = 1; } - #elif defined ( __QNX__ ) - /* QNX does not page at all, so the whole secure memory stuff does - * not make much sense. However it is still of use because it - * wipes out the memory on a free(). - * Therefore it is sufficient to suppress the warning - */ - #else - log_info("Please note that you don't have secure memory on this system\n"); - #endif +#elif defined ( __QNX__ ) + /* QNX does not page at all, so the whole secure memory stuff does + * not make much sense. However it is still of use because it + * wipes out the memory on a free(). + * Therefore it is sufficient to suppress the warning + */ +#else + log_info ("Please note that you don't have secure memory on this system\n"); +#endif } - +/* Initialize POOL. */ static void -init_pool( size_t n) +init_pool (size_t n) { - size_t pgsize; - - poolsize = n; - - if( disable_secmem ) - log_bug("secure memory is disabled"); - - #ifdef HAVE_GETPAGESIZE - pgsize = getpagesize(); - #else - pgsize = DEFAULT_PAGESIZE; - #endif - - #if HAVE_MMAP - poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1); - #ifdef MAP_ANONYMOUS - pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - #else /* map /dev/zero instead */ - { int fd; - - fd = open("/dev/zero", O_RDWR); - if( fd == -1 ) { - log_error("can't open /dev/zero: %s\n", strerror(errno) ); - pool = (void*)-1; - } - else { - pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE, - MAP_PRIVATE, fd, 0); - } + size_t pgsize; + memblock_t *mb; + + pool_size = n; + + if (disable_secmem) + log_bug ("secure memory is disabled"); + +#ifdef HAVE_GETPAGESIZE + pgsize = getpagesize (); +#else + pgsize = DEFAULT_PAGE_SIZE; +#endif + +#if HAVE_MMAP + pool_size = (pool_size + pgsize - 1) & ~(pgsize - 1); +#ifdef MAP_ANONYMOUS + pool = mmap (0, pool_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#else /* map /dev/zero instead */ + { + int fd; + + fd = open ("/dev/zero", O_RDWR); + if (fd == -1) + { + log_error ("can't open /dev/zero: %s\n", strerror (errno)); + pool = (void *) -1; + } + else + { + pool = mmap (0, pool_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + } + } +#endif + if (pool == (void *) -1) + log_info ("can't mmap pool of %u bytes: %s - using malloc\n", + (unsigned) pool_size, strerror (errno)); + else + { + pool_is_mmapped = 1; + pool_okay = 1; } - #endif - if( pool == (void*)-1 ) - log_info("can't mmap pool of %u bytes: %s - using malloc\n", - (unsigned)poolsize, strerror(errno)); - else { - pool_is_mmapped = 1; + +#endif + if (!pool_okay) + { + pool = malloc (pool_size); + if (!pool) + log_fatal ("can't allocate memory pool of %u bytes\n", + (unsigned) pool_size); + else pool_okay = 1; } - #endif - if( !pool_okay ) { - pool = malloc( poolsize ); - if( !pool ) - log_fatal("can't allocate memory pool of %u bytes\n", - (unsigned)poolsize); - else - pool_okay = 1; - } - lock_pool( pool, poolsize ); - poollen = 0; + /* Initialize first memory block. */ + mb = (memblock_t *) pool; + mb->size = pool_size; + mb->flags = 0; } - /* concatenate unused blocks */ static void -compress_pool(void) +compress_pool (void) { - /* fixme: we really should do this */ + /* fixme: we really should do this */ } void -_gcry_secmem_set_flags( unsigned flags ) +_gcry_secmem_set_flags (unsigned flags) { - int was_susp = suspend_warning; + int was_susp = suspend_warning; - no_warning = flags & GCRY_SECMEM_FLAG_NO_WARNING; - suspend_warning = flags & GCRY_SECMEM_FLAG_SUSPEND_WARNING; + no_warning = flags & GCRY_SECMEM_FLAG_NO_WARNING; + suspend_warning = flags & GCRY_SECMEM_FLAG_SUSPEND_WARNING; - /* and now issue the warning if it is not longer suspended */ - if( was_susp && !suspend_warning && show_warning ) { - show_warning = 0; - print_warn(); + /* and now issue the warning if it is not longer suspended */ + if (was_susp && !suspend_warning && show_warning) + { + show_warning = 0; + print_warn (); } } unsigned -_gcry_secmem_get_flags(void) +_gcry_secmem_get_flags (void) { - unsigned flags; + unsigned flags; - flags = no_warning ? GCRY_SECMEM_FLAG_NO_WARNING : 0; - flags |= suspend_warning ? GCRY_SECMEM_FLAG_SUSPEND_WARNING : 0; - return flags; + flags = no_warning ? GCRY_SECMEM_FLAG_NO_WARNING : 0; + flags |= suspend_warning ? GCRY_SECMEM_FLAG_SUSPEND_WARNING : 0; + return flags; } +/* Initialize the secure memory system. If running with the necessary + privileges, the secure memory pool will be locked into the core in + order to prevent page-outs of the data. Furthermore allocated + secure memory will be wiped out when released. */ void -_gcry_secmem_init( size_t n ) +_gcry_secmem_init (size_t n) { - if( !n ) { - #ifdef USE_CAPABILITIES - /* drop all capabilities */ - cap_set_proc( cap_from_text("all-eip") ); - - #elif !defined(HAVE_DOSISH_SYSTEM) - uid_t uid; - - disable_secmem=1; - uid = getuid(); - if( uid != geteuid() ) { - if( setuid( uid ) || getuid() != geteuid() || !setuid(0) ) - log_fatal("failed to drop setuid\n" ); + if (!n) + { +#ifdef USE_CAPABILITIES + /* drop all capabilities */ + cap_set_proc (cap_from_text ("all-eip")); + +#elif !defined(HAVE_DOSISH_SYSTEM) + uid_t uid; + + disable_secmem = 1; + uid = getuid (); + if (uid != geteuid ()) + { + if (setuid (uid) || getuid () != geteuid () || !setuid (0)) + log_fatal ("failed to drop setuid\n"); } - #endif +#endif } - else { - if( n < DEFAULT_POOLSIZE ) - n = DEFAULT_POOLSIZE; - if( !pool_okay ) - init_pool(n); - else - log_error("Oops, secure memory pool already initialized\n"); + else + { + if (n < DEFAULT_POOL_SIZE) + n = DEFAULT_POOL_SIZE; + if (! pool_okay) + { + init_pool (n); + if (! geteuid ()) + lock_pool (pool, n); + else + log_info ("Secure memory is not locked into core\n"); + } + else + log_error ("Oops, secure memory pool already initialized\n"); } } void * -_gcry_secmem_malloc( size_t size ) +_gcry_secmem_malloc (size_t size) { - MEMBLOCK *mb, *mb2; - int compressed=0; - - if( !pool_okay ) { - log_info( - _("operation is not possible without initialized secure memory\n")); - log_info(_("(you may have used the wrong program for this task)\n")); - exit(2); - } - if( show_warning && !suspend_warning ) { - show_warning = 0; - print_warn(); - } + int compressed = 0; + memblock_t *mb; - /* blocks are always a multiple of 32 */ - size += sizeof(MEMBLOCK); - size = ((size + 31) / 32) * 32; - - retry: - /* try to get it from the used blocks */ - for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next ) - if( mb->size >= size ) { - if( mb2 ) - mb2->u.next = mb->u.next; - else - unused_blocks = mb->u.next; - goto leave; - } - /* allocate a new block */ - if( (poollen + size <= poolsize) ) { - mb = (void*)((char*)pool + poollen); - poollen += size; - mb->size = size; + if (!pool_okay) + { + log_info (_ + ("operation is not possible without initialized secure memory\n")); + log_info (_("(you may have used the wrong program for this task)\n")); + exit (2); } - else if( !compressed ) { - compressed=1; - compress_pool(); - goto retry; + if (show_warning && !suspend_warning) + { + show_warning = 0; + print_warn (); } - else - return NULL; - leave: - cur_alloced += mb->size; - cur_blocks++; - if( cur_alloced > max_alloced ) - max_alloced = cur_alloced; - if( cur_blocks > max_blocks ) - max_blocks = cur_blocks; + /* blocks are always a multiple of 32 */ + size = ((size + 31) / 32) * 32; - return &mb->u.aligned.c; + mb = mb_get_new ((memblock_t *) pool, size); + if (mb) + stats_update (size, 0); + + return mb ? &mb->aligned.c : NULL; } +/* Wipe out and release memory. */ +void +_gcry_secmem_free (void *a) +{ + memblock_t *mb; + int size; + + if (!a) + return; + + mb = ADDR_TO_BLOCK (a); + size = mb->size; + + /* This does not make much sense: probably this memory is held in the + * cache. We do it anyway: */ +#define MB_WIPE_OUT(byte) \ + memset ((memblock_t *) ((char *) mb + BLOCK_HEAD_SIZE), (byte), size); + + MB_WIPE_OUT (0xff); + MB_WIPE_OUT (0xaa); + MB_WIPE_OUT (0x55); + MB_WIPE_OUT (0x00); + + stats_update (0, size); + + mb->flags &= ~MB_FLAG_ACTIVE; + + /* Update stats. */ + + mb_merge (mb); +} +/* Realloc memory. */ void * -_gcry_secmem_realloc( void *p, size_t newsize ) +_gcry_secmem_realloc (void *p, size_t newsize) { - MEMBLOCK *mb; - size_t size; - void *a; - - mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c)); - size = mb->size; - if( newsize < size ) - return p; /* it is easier not to shrink the memory */ - a = _gcry_secmem_malloc( newsize ); - if ( a ) { - memcpy(a, p, size); - memset((char*)a+size, 0, newsize-size); - _gcry_secmem_free(p); + memblock_t *mb; + size_t size; + void *a; + + mb = (memblock_t *) ((char *) p - ((size_t) & ((memblock_t *) 0)->aligned.c)); + size = mb->size; + if (newsize < size) + return p; /* it is easier not to shrink the memory */ + a = _gcry_secmem_malloc (newsize); + if (a) + { + memcpy (a, p, size); + memset ((char *) a + size, 0, newsize - size); + _gcry_secmem_free (p); } - return a; + return a; } - -void -_gcry_secmem_free( void *a ) +int +_gcry_private_is_secure (const void *p) { - MEMBLOCK *mb; - size_t size; - - if( !a ) - return; - - mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c)); - size = mb->size; - /* This does not make much sense: probably this memory is held in the - * cache. We do it anyway: */ - memset(mb, 0xff, size ); - memset(mb, 0xaa, size ); - memset(mb, 0x55, size ); - memset(mb, 0x00, size ); - mb->size = size; - mb->u.next = unused_blocks; - unused_blocks = mb; - cur_blocks--; - cur_alloced -= size; -} + int ret = 0; + if (pool_okay && BLOCK_VALID (ADDR_TO_BLOCK (p))) + ret = 1; -int -_gcry_private_is_secure( const void *p ) -{ - if (!pool_okay) - return 0; - return p >= pool && p < (void*)((char*)pool+poolsize); + return ret; } - /**************** * Warning: This code might be called by an interrupt handler * and frankly, there should really be such a handler, @@ -399,34 +538,43 @@ _gcry_private_is_secure( const void *p ) * there is no chance to get the secure memory cleaned. */ void -_gcry_secmem_term() +_gcry_secmem_term () { - if( !pool_okay ) - return; - - memset( pool, 0xff, poolsize); - memset( pool, 0xaa, poolsize); - memset( pool, 0x55, poolsize); - memset( pool, 0x00, poolsize); - #if HAVE_MMAP - if( pool_is_mmapped ) - munmap( pool, poolsize ); - #endif - pool = NULL; - pool_okay = 0; - poolsize=0; - poollen=0; - unused_blocks=NULL; + if (!pool_okay) + return; + + memset (pool, 0xff, pool_size); + memset (pool, 0xaa, pool_size); + memset (pool, 0x55, pool_size); + memset (pool, 0x00, pool_size); +#if HAVE_MMAP + if (pool_is_mmapped) + munmap (pool, pool_size); +#endif + pool = NULL; + pool_okay = 0; + pool_size = 0; } void -_gcry_secmem_dump_stats() +_gcry_secmem_dump_stats () { - if( disable_secmem ) - return; - log_info ("secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n", - cur_alloced, max_alloced, cur_blocks, max_blocks, - (ulong)poollen, (ulong)poolsize ); + memblock_t *mb; + int i; + +#if 1 + if (pool_okay) + log_info ("secmem usage: %u/%u bytes in %u blocks\n", + cur_alloced, pool_size, cur_blocks); +#else + + for (i = 0, mb = (memblock_t *) pool; + BLOCK_VALID (mb); + mb = mb_get_next (mb), i++) + log_info ("SECMEM: [%s] block: %i; size: %i\n", + (mb->flags & MB_FLAG_ACTIVE) ? "used" : "free", + i, + mb->size); +#endif } - |
