Revert last. It is still wrong.
[gnupg.git] / util / secmem.c
blob6da58b2e630496c133056509968738a3ebc14d0c
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19 * USA.
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <stdarg.h>
28 #include <unistd.h>
29 #if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
30 #include <sys/mman.h>
31 #include <sys/types.h>
32 #include <fcntl.h>
33 #ifdef USE_CAPABILITIES
34 #include <sys/capability.h>
35 #endif
36 #ifdef HAVE_PLOCK
37 #include <sys/lock.h>
38 #endif
39 #endif
41 #include "types.h"
42 #include "memory.h"
43 #include "util.h"
44 #include "i18n.h"
46 /* MinGW doesn't seem to prototype getpagesize, though it does have
47 it. */
48 #if !HAVE_DECL_GETPAGESIZE
49 int getpagesize(void);
50 #endif
52 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
53 #define MAP_ANONYMOUS MAP_ANON
54 #endif
55 /* It seems that Slackware 7.1 does not know about EPERM */
56 #if !defined(EPERM) && defined(ENOMEM)
57 #define EPERM ENOMEM
58 #endif
61 #define DEFAULT_POOLSIZE 16384
63 typedef struct memblock_struct MEMBLOCK;
64 struct memblock_struct {
65 unsigned size;
66 union {
67 MEMBLOCK *next;
68 PROPERLY_ALIGNED_TYPE aligned;
69 } u;
74 static void *pool;
75 static volatile int pool_okay; /* may be checked in an atexit function */
76 #ifdef HAVE_MMAP
77 static volatile int pool_is_mmapped;
78 #endif
79 static size_t poolsize; /* allocated length */
80 static size_t poollen; /* used length */
81 static MEMBLOCK *unused_blocks;
82 static unsigned max_alloced;
83 static unsigned cur_alloced;
84 static unsigned max_blocks;
85 static unsigned cur_blocks;
86 static int disable_secmem;
87 static int show_warning;
88 static int no_warning;
89 static int suspend_warning;
92 static void
93 print_warn(void)
95 if (!no_warning)
97 log_info(_("WARNING: using insecure memory!\n"));
98 log_info(_("please see http://www.gnupg.org/faq.html"
99 " for more information\n"));
104 static void
105 lock_pool( void *p, size_t n )
107 #if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
108 int err;
110 cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
111 err = mlock( p, n );
112 if( err && errno )
113 err = errno;
114 cap_set_proc( cap_from_text("cap_ipc_lock+p") );
116 if( err ) {
117 if( errno != EPERM
118 #ifdef EAGAIN /* OpenBSD returns this */
119 && errno != EAGAIN
120 #endif
121 #ifdef ENOSYS /* Some SCOs return this (function not implemented) */
122 && errno != ENOSYS
123 #endif
124 #ifdef ENOMEM /* Linux can return this */
125 && errno != ENOMEM
126 #endif
128 log_error("can't lock memory: %s\n", strerror(err));
129 show_warning = 1;
132 #elif defined(HAVE_MLOCK)
133 uid_t uid;
134 int err;
136 uid = getuid();
138 #ifdef HAVE_BROKEN_MLOCK
139 /* ick. but at least we get secured memory. about to lock
140 entire data segment. */
141 #ifdef HAVE_PLOCK
142 # ifdef _AIX
143 /* The configure for AIX returns broken mlock but the plock has
144 the strange requirement to somehow set the stack limit first.
145 The problem might turn out in indeterministic program behaviour
146 and hanging processes which can somehow be solved when enough
147 processes are clogging up the memory. To get this problem out
148 of the way we simply don't try to lock the memory at all.
150 errno = EPERM;
151 err = errno;
152 # else /* !_AIX */
153 err = plock( DATLOCK );
154 if( err && errno )
155 err = errno;
156 # endif /*_AIX*/
157 #else /*!HAVE_PLOCK*/
158 if( uid ) {
159 errno = EPERM;
160 err = errno;
162 else {
163 err = mlock( p, n );
164 if( err && errno )
165 err = errno;
167 #endif /*!HAVE_PLOCK*/
168 #else
169 err = mlock( p, n );
170 if( err && errno )
171 err = errno;
172 #endif
174 if( uid && !geteuid() ) {
175 /* check that we really dropped the privs.
176 * Note: setuid(0) should always fail */
177 if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
178 log_fatal("failed to reset uid: %s\n", strerror(errno));
181 if( err ) {
182 if( errno != EPERM
183 #ifdef EAGAIN /* OpenBSD returns this */
184 && errno != EAGAIN
185 #endif
186 #ifdef ENOSYS /* Some SCOs return this (function not implemented) */
187 && errno != ENOSYS
188 #endif
189 #ifdef ENOMEM /* Linux can return this */
190 && errno != ENOMEM
191 #endif
193 log_error("can't lock memory: %s\n", strerror(err));
194 show_warning = 1;
197 #elif defined ( __QNX__ )
198 /* QNX does not page at all, so the whole secure memory stuff does
199 * not make much sense. However it is still of use because it
200 * wipes out the memory on a free().
201 * Therefore it is sufficient to suppress the warning
203 #elif defined (HAVE_DOSISH_SYSTEM) || defined (__CYGWIN__)
204 /* It does not make sense to print such a warning, given the fact that
205 * this whole Windows !@#$% and their user base are inherently insecure
207 #elif defined (__riscos__)
208 /* no virtual memory on RISC OS, so no pages are swapped to disc,
209 * besides we don't have mmap, so we don't use it! ;-)
210 * But don't complain, as explained above.
212 #else
213 log_info("Please note that you don't have secure memory on this system\n");
214 #endif
218 static void
219 init_pool( size_t n)
221 size_t pgsize;
223 poolsize = n;
225 if( disable_secmem )
226 log_bug("secure memory is disabled");
228 #ifdef HAVE_GETPAGESIZE
229 pgsize = getpagesize();
230 #else
231 pgsize = 4096;
232 #endif
234 #ifdef HAVE_MMAP
235 poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
236 #ifdef MAP_ANONYMOUS
237 pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
238 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
239 #else /* map /dev/zero instead */
240 { int fd;
242 fd = open("/dev/zero", O_RDWR);
243 if( fd == -1 ) {
244 log_error("can't open /dev/zero: %s\n", strerror(errno) );
245 pool = (void*)-1;
247 else {
248 pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
249 MAP_PRIVATE, fd, 0);
252 #endif
253 if( pool == (void*)-1 )
254 log_info("can't mmap pool of %u bytes: %s - using malloc\n",
255 (unsigned)poolsize, strerror(errno));
256 else {
257 pool_is_mmapped = 1;
258 pool_okay = 1;
261 #endif
262 if( !pool_okay ) {
263 pool = malloc( poolsize );
264 if( !pool )
265 log_fatal("can't allocate memory pool of %u bytes\n",
266 (unsigned)poolsize);
267 else
268 pool_okay = 1;
270 lock_pool( pool, poolsize );
271 poollen = 0;
275 /* concatenate unused blocks */
276 static void
277 compress_pool(void)
279 /* fixme: we really should do this */
282 void
283 secmem_set_flags( unsigned flags )
285 int was_susp = suspend_warning;
287 no_warning = flags & 1;
288 suspend_warning = flags & 2;
290 /* and now issue the warning if it is not longer suspended */
291 if( was_susp && !suspend_warning && show_warning ) {
292 show_warning = 0;
293 print_warn();
297 unsigned
298 secmem_get_flags(void)
300 unsigned flags;
302 flags = no_warning ? 1:0;
303 flags |= suspend_warning ? 2:0;
304 return flags;
307 /* Returns 1 if memory was locked, 0 if not. */
309 secmem_init( size_t n )
311 if( !n ) {
312 #ifndef __riscos__
313 #ifdef USE_CAPABILITIES
314 /* drop all capabilities */
315 cap_set_proc( cap_from_text("all-eip") );
317 #elif !defined(HAVE_DOSISH_SYSTEM)
318 uid_t uid;
320 disable_secmem=1;
321 uid = getuid();
322 if( uid != geteuid() ) {
323 if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
324 log_fatal("failed to drop setuid\n" );
326 #endif
327 #endif /* !__riscos__ */
329 else {
330 if( n < DEFAULT_POOLSIZE )
331 n = DEFAULT_POOLSIZE;
332 if( !pool_okay )
333 init_pool(n);
334 else
335 log_error("Oops, secure memory pool already initialized\n");
338 return !show_warning;
342 void *
343 secmem_malloc( size_t size )
345 MEMBLOCK *mb, *mb2;
346 int compressed=0;
348 if( !pool_okay ) {
349 log_info(
350 _("operation is not possible without initialized secure memory\n"));
351 log_info(_("(you may have used the wrong program for this task)\n"));
352 exit(2);
354 if( show_warning && !suspend_warning ) {
355 show_warning = 0;
356 print_warn();
359 /* Blocks are always a multiple of 32. Note that we allocate an
360 extra of the size of an entire MEMBLOCK. This is required
361 becuase we do not only need the SIZE info but also extra space
362 to chain up unused memory blocks. */
363 size += sizeof(MEMBLOCK);
364 size = ((size + 31) / 32) * 32;
366 retry:
367 /* try to get it from the used blocks */
368 for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
369 if( mb->size >= size ) {
370 if( mb2 )
371 mb2->u.next = mb->u.next;
372 else
373 unused_blocks = mb->u.next;
374 goto leave;
376 /* allocate a new block */
377 if( (poollen + size <= poolsize) ) {
378 mb = (void*)((char*)pool + poollen);
379 poollen += size;
380 mb->size = size;
382 else if( !compressed ) {
383 compressed=1;
384 compress_pool();
385 goto retry;
387 else
388 return NULL;
390 leave:
391 cur_alloced += mb->size;
392 cur_blocks++;
393 if( cur_alloced > max_alloced )
394 max_alloced = cur_alloced;
395 if( cur_blocks > max_blocks )
396 max_blocks = cur_blocks;
398 return &mb->u.aligned.c;
402 void *
403 secmexrealloc( void *p, size_t newsize )
405 MEMBLOCK *mb;
406 size_t size;
407 void *a;
409 mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
410 size = mb->size;
411 if (size < sizeof(MEMBLOCK))
412 log_bug ("secure memory corrupted at block %p\n", (void *)mb);
413 size -= ((size_t) &((MEMBLOCK*)0)->u.aligned.c);
415 if( newsize <= size )
416 return p; /* It is easier not to shrink the memory. */
417 a = secmem_malloc( newsize );
418 if ( a ) {
419 memcpy(a, p, size);
420 memset((char*)a+size, 0, newsize-size);
421 secmem_free(p);
423 return a;
427 void
428 secmem_free( void *a )
430 MEMBLOCK *mb;
431 size_t size;
433 if( !a )
434 return;
436 mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
437 size = mb->size;
438 /* This does not make much sense: probably this memory is held in the
439 * cache. We do it anyway: */
440 wipememory2(mb, 0xff, size );
441 wipememory2(mb, 0xaa, size );
442 wipememory2(mb, 0x55, size );
443 wipememory2(mb, 0x00, size );
444 mb->size = size;
445 mb->u.next = unused_blocks;
446 unused_blocks = mb;
447 cur_blocks--;
448 cur_alloced -= size;
452 m_is_secure( const void *p )
454 return p >= pool && p < (void*)((char*)pool+poolsize);
459 /****************
460 * Warning: This code might be called by an interrupt handler
461 * and frankly, there should really be such a handler,
462 * to make sure that the memory is wiped out.
463 * We hope that the OS wipes out mlocked memory after
464 * receiving a SIGKILL - it really should do so, otherwise
465 * there is no chance to get the secure memory cleaned.
467 void
468 secmem_term()
470 if( !pool_okay )
471 return;
473 wipememory2( pool, 0xff, poolsize);
474 wipememory2( pool, 0xaa, poolsize);
475 wipememory2( pool, 0x55, poolsize);
476 wipememory2( pool, 0x00, poolsize);
477 #ifdef HAVE_MMAP
478 if( pool_is_mmapped )
479 munmap( pool, poolsize );
480 #endif
481 pool = NULL;
482 pool_okay = 0;
483 poolsize=0;
484 poollen=0;
485 unused_blocks=NULL;
489 void
490 secmem_dump_stats()
492 if( disable_secmem )
493 return;
494 fprintf(stderr,
495 "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
496 cur_alloced, max_alloced, cur_blocks, max_blocks,
497 (ulong)poollen, (ulong)poolsize );