1
/* secmem.c - memory allocation from a secure heap
2
* Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
4
* This file is part of GnuPG.
6
* GnuPG is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* GnuPG is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
28
#if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
30
#include <sys/types.h>
32
#ifdef USE_CAPABILITIES
33
#include <sys/capability.h>
45
#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
46
#define MAP_ANONYMOUS MAP_ANON
48
/* It seems that Slackware 7.1 does not know about EPERM */
49
#if !defined(EPERM) && defined(ENOMEM)
54
#define DEFAULT_POOLSIZE 16384
56
typedef struct memblock_struct MEMBLOCK;
57
struct memblock_struct {
61
PROPERLY_ALIGNED_TYPE aligned;
68
static volatile int pool_okay; /* may be checked in an atexit function */
70
static volatile int pool_is_mmapped;
72
static size_t poolsize; /* allocated length */
73
static size_t poollen; /* used length */
74
static MEMBLOCK *unused_blocks;
75
static unsigned max_alloced;
76
static unsigned cur_alloced;
77
static unsigned max_blocks;
78
static unsigned cur_blocks;
79
static int disable_secmem;
80
static int show_warning;
81
static int no_warning;
82
static int suspend_warning;
90
log_info(_("WARNING: using insecure memory!\n"));
91
log_info(_("please see http://www.gnupg.org/faq.html"
92
" for more information\n"));
98
lock_pool( void *p, size_t n )
100
#if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
103
cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
107
cap_set_proc( cap_from_text("cap_ipc_lock+p") );
111
#ifdef EAGAIN /* OpenBSD returns this */
114
#ifdef ENOSYS /* Some SCOs return this (function not implemented) */
117
#ifdef ENOMEM /* Linux can return this */
121
log_error("can't lock memory: %s\n", strerror(err));
125
#elif defined(HAVE_MLOCK)
131
#ifdef HAVE_BROKEN_MLOCK
132
/* ick. but at least we get secured memory. about to lock
133
entire data segment. */
136
/* The configure for AIX returns broken mlock but the plock has
137
the strange requirement to somehow set the stack limit first.
138
The problem might turn out in indeterministic program behaviour
139
and hanging processes which can somehow be solved when enough
140
processes are clogging up the memory. To get this problem out
141
of the way we simply don't try to lock the memory at all.
146
err = plock( DATLOCK );
150
#else /*!HAVE_PLOCK*/
160
#endif /*!HAVE_PLOCK*/
167
if( uid && !geteuid() ) {
168
/* check that we really dropped the privs.
169
* Note: setuid(0) should always fail */
170
if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
171
log_fatal("failed to reset uid: %s\n", strerror(errno));
176
#ifdef EAGAIN /* OpenBSD returns this */
179
#ifdef ENOSYS /* Some SCOs return this (function not implemented) */
182
#ifdef ENOMEM /* Linux can return this */
186
log_error("can't lock memory: %s\n", strerror(err));
190
#elif defined ( __QNX__ )
191
/* QNX does not page at all, so the whole secure memory stuff does
192
* not make much sense. However it is still of use because it
193
* wipes out the memory on a free().
194
* Therefore it is sufficient to suppress the warning
196
#elif defined (HAVE_DOSISH_SYSTEM) || defined (__CYGWIN__)
197
/* It does not make sense to print such a warning, given the fact that
198
* this whole Windows !@#$% and their user base are inherently insecure
200
#elif defined (__riscos__)
201
/* no virtual memory on RISC OS, so no pages are swapped to disc,
202
* besides we don't have mmap, so we don't use it! ;-)
203
* But don't complain, as explained above.
206
log_info("Please note that you don't have secure memory on this system\n");
219
log_bug("secure memory is disabled");
221
#ifdef HAVE_GETPAGESIZE
222
pgsize = getpagesize();
228
poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
230
pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
231
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
232
#else /* map /dev/zero instead */
235
fd = open("/dev/zero", O_RDWR);
237
log_error("can't open /dev/zero: %s\n", strerror(errno) );
241
pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
246
if( pool == (void*)-1 )
247
log_info("can't mmap pool of %u bytes: %s - using malloc\n",
248
(unsigned)poolsize, strerror(errno));
256
pool = malloc( poolsize );
258
log_fatal("can't allocate memory pool of %u bytes\n",
263
lock_pool( pool, poolsize );
268
/* concatenate unused blocks */
272
/* fixme: we really should do this */
276
secmem_set_flags( unsigned flags )
278
int was_susp = suspend_warning;
280
no_warning = flags & 1;
281
suspend_warning = flags & 2;
283
/* and now issue the warning if it is not longer suspended */
284
if( was_susp && !suspend_warning && show_warning ) {
291
secmem_get_flags(void)
295
flags = no_warning ? 1:0;
296
flags |= suspend_warning ? 2:0;
300
/* Returns 1 if memory was locked, 0 if not. */
302
secmem_init( size_t n )
306
#ifdef USE_CAPABILITIES
307
/* drop all capabilities */
308
cap_set_proc( cap_from_text("all-eip") );
310
#elif !defined(HAVE_DOSISH_SYSTEM)
315
if( uid != geteuid() ) {
316
if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
317
log_fatal("failed to drop setuid\n" );
320
#endif /* !__riscos__ */
323
if( n < DEFAULT_POOLSIZE )
324
n = DEFAULT_POOLSIZE;
328
log_error("Oops, secure memory pool already initialized\n");
331
return !show_warning;
336
secmem_malloc( size_t size )
343
_("operation is not possible without initialized secure memory\n"));
344
log_info(_("(you may have used the wrong program for this task)\n"));
347
if( show_warning && !suspend_warning ) {
352
/* blocks are always a multiple of 32 */
353
size += sizeof(MEMBLOCK);
354
size = ((size + 31) / 32) * 32;
357
/* try to get it from the used blocks */
358
for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
359
if( mb->size >= size ) {
361
mb2->u.next = mb->u.next;
363
unused_blocks = mb->u.next;
366
/* allocate a new block */
367
if( (poollen + size <= poolsize) ) {
368
mb = (void*)((char*)pool + poollen);
372
else if( !compressed ) {
381
cur_alloced += mb->size;
383
if( cur_alloced > max_alloced )
384
max_alloced = cur_alloced;
385
if( cur_blocks > max_blocks )
386
max_blocks = cur_blocks;
388
return &mb->u.aligned.c;
393
secmem_realloc( void *p, size_t newsize )
399
mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
402
return p; /* it is easier not to shrink the memory */
403
a = secmem_malloc( newsize );
406
memset((char*)a+size, 0, newsize-size);
414
secmem_free( void *a )
422
mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
424
/* This does not make much sense: probably this memory is held in the
425
* cache. We do it anyway: */
426
wipememory2(mb, 0xff, size );
427
wipememory2(mb, 0xaa, size );
428
wipememory2(mb, 0x55, size );
429
wipememory2(mb, 0x00, size );
431
mb->u.next = unused_blocks;
438
m_is_secure( const void *p )
440
return p >= pool && p < (void*)((char*)pool+poolsize);
446
* Warning: This code might be called by an interrupt handler
447
* and frankly, there should really be such a handler,
448
* to make sure that the memory is wiped out.
449
* We hope that the OS wipes out mlocked memory after
450
* receiving a SIGKILL - it really should do so, otherwise
451
* there is no chance to get the secure memory cleaned.
459
wipememory2( pool, 0xff, poolsize);
460
wipememory2( pool, 0xaa, poolsize);
461
wipememory2( pool, 0x55, poolsize);
462
wipememory2( pool, 0x00, poolsize);
464
if( pool_is_mmapped )
465
munmap( pool, poolsize );
481
"secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
482
cur_alloced, max_alloced, cur_blocks, max_blocks,
483
(ulong)poollen, (ulong)poolsize );