From 58cc3326be8fa3b841d0596987090d7cb356e101 Mon Sep 17 00:00:00 2001 From: Guido Guenther Date: Sun, 8 Jun 2008 17:28:55 +0200 Subject: Imported Upstream version 0.7.git30891fc --- secmem/Makefile.am | 31 ++++ secmem/Manifest | 7 + secmem/memory.h | 40 +++++ secmem/secmem-util.h | 3 + secmem/secmem.c | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++ secmem/util.c | 139 ++++++++++++++++ secmem/util.h | 66 ++++++++ 7 files changed, 734 insertions(+) create mode 100644 secmem/Makefile.am create mode 100644 secmem/Manifest create mode 100644 secmem/memory.h create mode 100644 secmem/secmem-util.h create mode 100644 secmem/secmem.c create mode 100644 secmem/util.c create mode 100644 secmem/util.h (limited to 'secmem') diff --git a/secmem/Makefile.am b/secmem/Makefile.am new file mode 100644 index 0000000..331530f --- /dev/null +++ b/secmem/Makefile.am @@ -0,0 +1,31 @@ +# Secure Memory Makefile +# Copyright (C) 2002 g10 Code GmbH +# +# This file is part of PINENTRY. +# +# PINENTRY 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. +# +# PINENTRY 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 + +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = Manifest + +noinst_LIBRARIES = libsecmem.a + +libsecmem_a_SOURCES = \ + memory.h \ + secmem-util.h \ + util.h \ + secmem.c \ + util.c diff --git a/secmem/Manifest b/secmem/Manifest new file mode 100644 index 0000000..a37366d --- /dev/null +++ b/secmem/Manifest @@ -0,0 +1,7 @@ +Makefile.am +memory.h +secmem-util.h +secmem.c +util.c +util.h +$names$ iQCVAwUAP+f/RDEAnp832S/7AQIbRQQAzR7UvGOTMl8AWyVgHGQjW5A5fGzRlaEABl+5UpGmzoFGFdP9upHv3Tj0MKETHNRkdOAA5k5QzamDypAr5RINz9rdZPkNPIAtg4csN7Yb6ITJZaH7yLDJcBmhM49a8ZNpDpQeImzpE05cM6TuGVO6NSIrlt9OBhaHfbkpzgr1tI0==6Tyw diff --git a/secmem/memory.h b/secmem/memory.h new file mode 100644 index 0000000..c6d04b8 --- /dev/null +++ b/secmem/memory.h @@ -0,0 +1,40 @@ +/* Quintuple Agent secure memory allocation + * Copyright (C) 1998,1999 Free Software Foundation, Inc. + * Copyright (C) 1999,2000 Robert Bihlmeyer + * + * This program 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. + * + * This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MEMORY_H +#define _MEMORY_H + +#include + +/* values for flags, hardcoded in secmem.c */ +#define SECMEM_WARN 0 +#define SECMEM_DONT_WARN 1 +#define SECMEM_SUSPEND_WARN 2 + +void secmem_init( size_t npool ); +void secmem_term( void ); +void *secmem_malloc( size_t size ); +void *secmem_realloc( void *a, size_t newsize ); +void secmem_free( void *a ); +int m_is_secure( const void *p ); +void secmem_dump_stats(void); +void secmem_set_flags( unsigned flags ); +unsigned secmem_get_flags(void); + +#endif /* _MEMORY_H */ diff --git a/secmem/secmem-util.h b/secmem/secmem-util.h new file mode 100644 index 0000000..b422182 --- /dev/null +++ b/secmem/secmem-util.h @@ -0,0 +1,3 @@ +/* This file exists because "util.h" is such a generic name that it is + likely to clash with other such files. */ +#include "util.h" diff --git a/secmem/secmem.c b/secmem/secmem.c new file mode 100644 index 0000000..796108d --- /dev/null +++ b/secmem/secmem.c @@ -0,0 +1,448 @@ +/* secmem.c - memory allocation from a secure heap + * Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc. + * + * 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 +#include +#include +#include +#include +#include +#if defined(HAVE_MLOCK) || defined(HAVE_MMAP) +# include +# include +# include +# ifdef USE_CAPABILITIES +# include +# endif +#endif +#include + +#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 void +print_warn(void) +{ + if( !no_warning ) + log_info("Warning: using insecure memory!\n"); +} + + +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 + ) + 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 void +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 = 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 void +compress_pool(void) +{ + /* fixme: we really should do this */ +} + +void +secmem_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(); + } +} + +unsigned +secmem_get_flags(void) +{ + unsigned flags; + + flags = no_warning ? 1:0; + flags |= suspend_warning ? 2:0; + return flags; +} + +void +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() ) + 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; + + return &mb->u.aligned.c; +} + + +void * +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 = secmem_malloc( newsize ); + memcpy(a, p, size); + memset((char*)a+size, 0, newsize-size); + secmem_free(p); + return a; +} + + +void +secmem_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; +} + +int +m_is_secure( const void *p ) +{ + return p >= pool && p < (void*)((char*)pool+poolsize); +} + +void +secmem_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; +} + + +void +secmem_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 ); +} + diff --git a/secmem/util.c b/secmem/util.c new file mode 100644 index 0000000..580fd34 --- /dev/null +++ b/secmem/util.c @@ -0,0 +1,139 @@ +/* Quintuple Agent + * Copyright (C) 1999 Robert Bihlmeyer + * + * This program 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. + * + * This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "util.h" + +#ifndef TEMP_FAILURE_RETRY +#define TEMP_FAILURE_RETRY(expression) \ + (__extension__ \ + ({ long int __result; \ + do __result = (long int) (expression); \ + while (__result == -1L && errno == EINTR); \ + __result; })) +#endif + +#ifndef HAVE_DOSISH_SYSTEM +static int uid_set = 0; +static uid_t real_uid, file_uid; +#endif /*!HAVE_DOSISH_SYSTEM*/ + +/* write DATA of size BYTES to FD, until all is written or an error occurs */ +ssize_t xwrite(int fd, const void *data, size_t bytes) +{ + char *ptr; + size_t todo; + ssize_t written = 0; + + for (ptr = (char *)data, todo = bytes; todo; ptr += written, todo -= written) + if ((written = TEMP_FAILURE_RETRY(write(fd, ptr, todo))) < 0) + break; + return written; +} + +#if 0 +extern int debug; + +int debugmsg(const char *fmt, ...) +{ + va_list va; + int ret; + + if (debug) { + va_start(va, fmt); + fprintf(stderr, "\e[4m"); + ret = vfprintf(stderr, fmt, va); + fprintf(stderr, "\e[24m"); + va_end(va); + return ret; + } else + return 0; +} +#endif + +/* initialize uid variables */ +#ifndef HAVE_DOSISH_SYSTEM +static void init_uids(void) +{ + real_uid = getuid(); + file_uid = geteuid(); + uid_set = 1; +} +#endif + + +#if 0 /* Not used. */ +/* lower privileges to the real user's */ +void lower_privs() +{ + if (!uid_set) + init_uids(); + if (real_uid != file_uid) { +#ifdef HAVE_SETEUID + if (seteuid(real_uid) < 0) { + perror("lowering privileges failed"); + exit(EXIT_FAILURE); + } +#else + fprintf(stderr, _("Warning: running q-agent setuid on this system is dangerous\n")); +#endif /* HAVE_SETEUID */ + } +} +#endif /* if 0 */ + +#if 0 /* Not used. */ +/* raise privileges to the effective user's */ +void raise_privs() +{ + assert(real_uid >= 0); /* lower_privs() must be called before this */ +#ifdef HAVE_SETEUID + if (real_uid != file_uid && seteuid(file_uid) < 0) { + perror("Warning: raising privileges failed"); + } +#endif /* HAVE_SETEUID */ +} +#endif /* if 0 */ + +/* drop all additional privileges */ +void drop_privs() +{ +#ifndef HAVE_DOSISH_SYSTEM + if (!uid_set) + init_uids(); + if (real_uid != file_uid) { + if (setuid(real_uid) < 0) { + perror("dropping privileges failed"); + exit(EXIT_FAILURE); + } + file_uid = real_uid; + } +#endif +} diff --git a/secmem/util.h b/secmem/util.h new file mode 100644 index 0000000..7986c99 --- /dev/null +++ b/secmem/util.h @@ -0,0 +1,66 @@ +/* Quintuple Agent utilities + * Copyright (C) 1999 Robert Bihlmeyer + * Copyright (C) 2003 g10 Code GmbH + * + * This program 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. + * + * This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _UTIL_H +#define _UTIL_H + +#include + +#ifndef HAVE_BYTE_TYPEDEF +# undef byte +# ifdef __riscos__ + /* Norcroft treats char == unsigned char but char* != unsigned char* */ + typedef char byte; +# else + typedef unsigned char byte; +# endif +# define HAVE_BYTE_TYPEDEF +#endif + +#ifndef HAVE_ULONG_TYPEDEF +# undef ulong + typedef unsigned long ulong; +# define HAVE_ULONG_TYPEDEF +#endif + + +ssize_t xwrite(int, const void *, size_t); /* write until finished */ +int debugmsg(const char *, ...); /* output a debug message if debugging==on */ +void drop_privs(void); /* finally drop privileges */ + + +/* To avoid that a compiler optimizes certain memset calls away, these + macros may be used instead. */ +#define wipememory2(_ptr,_set,_len) do { \ + volatile char *_vptr=(volatile char *)(_ptr); \ + size_t _vlen=(_len); \ + while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ + } while(0) +#define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) +#define wipe(_ptr,_len) wipememory2(_ptr,0,_len) + + + + +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) + + +#endif -- cgit