460 lines · 9.1 KB · C
RawDownload
/* secmem.c  -	memory allocation from a secure heap *	Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc. *      Copyright (C) 2015 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include <stdio.h>#include <stdlib.h>#include <errno.h>#include <stdarg.h>#include <unistd.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#endif#include <string.h> #include "memory.h" #ifdef ORIGINAL_GPG_VERSION#include "types.h"#include "util.h"#else /* ORIGINAL_GPG_VERSION */ #include "util.h" typedef union {    int a;    short b;    char c[1];    long d;#ifdef HAVE_U64_TYPEDEF    u64 e;#endif    float f;    double g;} PROPERLY_ALIGNED_TYPE; #define log_error log_info#define log_bug log_fatal void log_info(char *template, ...){  va_list args;    va_start(args, template);  vfprintf(stderr, template, args);  va_end(args);} void log_fatal(char *template, ...){  va_list args;    va_start(args, template);  vfprintf(stderr, template, args);  va_end(args);  exit(EXIT_FAILURE);} #endif /* ORIGINAL_GPG_VERSION */ #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)#  define MAP_ANONYMOUS MAP_ANON#endif #define DEFAULT_POOLSIZE 16384 typedef struct memblock_struct MEMBLOCK;struct memblock_struct {    unsigned size;    union {	MEMBLOCK *next;	PROPERLY_ALIGNED_TYPE aligned;    } u;};   static void  *pool;static volatile int pool_okay; /* may be checked in an atexit function */static 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;static int disable_secmem;static int show_warning;static int no_warning;static int suspend_warning;  static voidprint_warn(void){    if( !no_warning )	log_info("Warning: using insecure memory!\n");}  static voidlock_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	  )	    log_error("can't lock memory: %s\n", strerror(err));	show_warning = 1;    } #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 )	    err = errno;    }#else    err = mlock( p, n );    if( err && errno )	err = errno;#endif     if( uid && !geteuid() ) {	if( setuid( uid ) || getuid() != geteuid()  )	    log_fatal("failed to reset uid: %s\n", strerror(errno));    }     if( err ) {	if( errno != EPERM#ifdef EAGAIN  /* OpenBSD returns this */	    && errno != EAGAIN#endif	  )	    log_error("can't lock memory: %s\n", strerror(err));	show_warning = 1;    } #else    log_info("Please note that you don't have secure memory on this system\n");#endif}  static voidinit_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 = 4096;#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);	    close (fd);	}    }# 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;	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;}  /* concatenate unused blocks */static voidcompress_pool(void){    /* fixme: we really should do this */} voidsecmem_set_flags( unsigned flags ){    int was_susp = suspend_warning;     no_warning = flags & 1;    suspend_warning = flags & 2;     /* and now issue the warning if it is not longer suspended */    if( was_susp && !suspend_warning && show_warning ) {	show_warning = 0;	print_warn();    }} unsignedsecmem_get_flags(void){    unsigned flags;     flags  = no_warning      ? 1:0;    flags |= suspend_warning ? 2:0;    return flags;} voidsecmem_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() )		log_fatal("failed to drop setuid\n" );	}#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");    }}  void *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();    }     /* 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;    }    else if( !compressed ) {	compressed=1;	compress_pool();	goto retry;    }    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;     memset (&mb->u.aligned.c, 0,	    size - (size_t) &((struct memblock_struct *) 0)->u.aligned.c);     return &mb->u.aligned.c;}  void *secmem_realloc( void *p, size_t newsize ){    MEMBLOCK *mb;    size_t size;    void *a;     if (! p)      return secmem_malloc(newsize);     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 = secmem_malloc( newsize );    memcpy(a, p, size);    memset((char*)a+size, 0, newsize-size);    secmem_free(p);    return a;}  voidsecmem_free( void *a ){    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: */    wipememory2(mb, 0xff, size );    wipememory2(mb, 0xaa, size );    wipememory2(mb, 0x55, size );    wipememory2(mb, 0x00, size );    mb->size = size;    mb->u.next = unused_blocks;    unused_blocks = mb;    cur_blocks--;    cur_alloced -= size;} intm_is_secure( const void *p ){    return p >= pool && p < (void*)((char*)pool+poolsize);} voidsecmem_term(){    if( !pool_okay )	return;     wipememory2( pool, 0xff, poolsize);    wipememory2( pool, 0xaa, poolsize);    wipememory2( pool, 0x55, poolsize);    wipememory2( 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;}  voidsecmem_dump_stats(){    if( disable_secmem )	return;    fprintf(stderr,		"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 );}  size_t secmem_get_max_size (void){  return poolsize;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t