2006-07-29 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / util / secmem.c
1 /* secmem.c  -  memory allocation from a secure heap
2  *      Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
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.
10  *
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.
15  *
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.
20  */
21
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
40
41 #include "types.h"
42 #include "memory.h"
43 #include "util.h"
44 #include "i18n.h"
45
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
51
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
59
60
61 #define DEFAULT_POOLSIZE 16384
62
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;
70 };
71
72
73
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;
90
91
92 static void
93 print_warn(void)
94 {
95   if (!no_warning)
96     {
97       log_info(_("WARNING: using insecure memory!\n"));
98       log_info(_("please see http://www.gnupg.org/faq.html"
99                  " for more information\n"));
100     }
101 }
102
103
104 static void
105 lock_pool( void *p, size_t n )
106 {
107 #if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
108     int err;
109
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") );
115
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
127           )
128             log_error("can't lock memory: %s\n", strerror(err));
129         show_warning = 1;
130     }
131
132 #elif defined(HAVE_MLOCK)
133     uid_t uid;
134     int err;
135
136     uid = getuid();
137
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.
149        */    
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;
161     }
162     else {
163         err = mlock( p, n );
164         if( err && errno )
165             err = errno;
166     }
167 #endif /*!HAVE_PLOCK*/
168 #else
169     err = mlock( p, n );
170     if( err && errno )
171         err = errno;
172 #endif
173
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));
179     }
180
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
192           )
193             log_error("can't lock memory: %s\n", strerror(err));
194         show_warning = 1;
195     }
196
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
202      */
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
206      */
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.
211      */
212 #else
213     log_info("Please note that you don't have secure memory on this system\n");
214 #endif
215 }
216
217
218 static void
219 init_pool( size_t n)
220 {
221     size_t pgsize;
222
223     poolsize = n;
224
225     if( disable_secmem )
226         log_bug("secure memory is disabled");
227
228 #ifdef HAVE_GETPAGESIZE
229     pgsize = getpagesize();
230 #else
231     pgsize = 4096;
232 #endif
233
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;
241
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;
246         }
247         else {
248             pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
249                                       MAP_PRIVATE, fd, 0);
250             close (fd);
251         }
252     }
253 #endif
254     if( pool == (void*)-1 )
255         log_info("can't mmap pool of %u bytes: %s - using malloc\n",
256                             (unsigned)poolsize, strerror(errno));
257     else {
258         pool_is_mmapped = 1;
259         pool_okay = 1;
260     }
261
262 #endif
263     if( !pool_okay ) {
264         pool = malloc( poolsize );
265         if( !pool )
266             log_fatal("can't allocate memory pool of %u bytes\n",
267                                                        (unsigned)poolsize);
268         else
269             pool_okay = 1;
270     }
271     lock_pool( pool, poolsize );
272     poollen = 0;
273 }
274
275
276 /* concatenate unused blocks */
277 static void
278 compress_pool(void)
279 {
280     /* fixme: we really should do this */
281 }
282
283 void
284 secmem_set_flags( unsigned flags )
285 {
286     int was_susp = suspend_warning;
287
288     no_warning = flags & 1;
289     suspend_warning = flags & 2;
290
291     /* and now issue the warning if it is not longer suspended */
292     if( was_susp && !suspend_warning && show_warning ) {
293         show_warning = 0;
294         print_warn();
295     }
296 }
297
298 unsigned
299 secmem_get_flags(void)
300 {
301     unsigned flags;
302
303     flags  = no_warning      ? 1:0;
304     flags |= suspend_warning ? 2:0;
305     return flags;
306 }
307
308 /* Returns 1 if memory was locked, 0 if not. */
309 int
310 secmem_init( size_t n )
311 {
312     if( !n ) {
313 #ifndef __riscos__
314 #ifdef USE_CAPABILITIES
315         /* drop all capabilities */
316         cap_set_proc( cap_from_text("all-eip") );
317
318 #elif !defined(HAVE_DOSISH_SYSTEM)
319         uid_t uid;
320
321         disable_secmem=1;
322         uid = getuid();
323         if( uid != geteuid() ) {
324             if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
325                 log_fatal("failed to drop setuid\n" );
326         }
327 #endif
328 #endif /* !__riscos__ */
329     }
330     else {
331         if( n < DEFAULT_POOLSIZE )
332             n = DEFAULT_POOLSIZE;
333         if( !pool_okay )
334             init_pool(n);
335         else
336             log_error("Oops, secure memory pool already initialized\n");
337     }
338
339     return !show_warning;
340 }
341
342
343 void *
344 secmem_malloc( size_t size )
345 {
346     MEMBLOCK *mb, *mb2;
347     int compressed=0;
348
349     if( !pool_okay ) {
350         log_info(
351          _("operation is not possible without initialized secure memory\n"));
352         log_info(_("(you may have used the wrong program for this task)\n"));
353         exit(2);
354     }
355     if( show_warning && !suspend_warning ) {
356         show_warning = 0;
357         print_warn();
358     }
359
360     /* Blocks are always a multiple of 32.  Note that we allocate an
361        extra of the size of an entire MEMBLOCK.  This is required
362        becuase we do not only need the SIZE info but also extra space
363        to chain up unused memory blocks.  */
364     size += sizeof(MEMBLOCK);
365     size = ((size + 31) / 32) * 32;
366
367   retry:
368     /* try to get it from the used blocks */
369     for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
370         if( mb->size >= size ) {
371             if( mb2 )
372                 mb2->u.next = mb->u.next;
373             else
374                 unused_blocks = mb->u.next;
375             goto leave;
376         }
377     /* allocate a new block */
378     if( (poollen + size <= poolsize) ) {
379         mb = (void*)((char*)pool + poollen);
380         poollen += size;
381         mb->size = size;
382     }
383     else if( !compressed ) {
384         compressed=1;
385         compress_pool();
386         goto retry;
387     }
388     else
389         return NULL;
390
391   leave:
392     cur_alloced += mb->size;
393     cur_blocks++;
394     if( cur_alloced > max_alloced )
395         max_alloced = cur_alloced;
396     if( cur_blocks > max_blocks )
397         max_blocks = cur_blocks;
398
399     return &mb->u.aligned.c;
400 }
401
402
403 void *
404 secmexrealloc( void *p, size_t newsize )
405 {
406     MEMBLOCK *mb;
407     size_t size;
408     void *a;
409
410     mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
411     size = mb->size;
412     if (size < sizeof(MEMBLOCK))
413       log_bug ("secure memory corrupted at block %p\n", (void *)mb);
414     size -= ((size_t) &((MEMBLOCK*)0)->u.aligned.c);
415
416     if( newsize <= size )
417         return p; /* It is easier not to shrink the memory.  */
418     a = secmem_malloc( newsize );
419     if ( a ) {
420         memcpy(a, p, size);
421         memset((char*)a+size, 0, newsize-size);
422         secmem_free(p);
423     }
424     return a;
425 }
426
427
428 void
429 secmem_free( void *a )
430 {
431     MEMBLOCK *mb;
432     size_t size;
433
434     if( !a )
435         return;
436
437     mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
438     size = mb->size;
439     /* This does not make much sense: probably this memory is held in the
440      * cache. We do it anyway: */
441     wipememory2(mb, 0xff, size );
442     wipememory2(mb, 0xaa, size );
443     wipememory2(mb, 0x55, size );
444     wipememory2(mb, 0x00, size );
445     mb->size = size;
446     mb->u.next = unused_blocks;
447     unused_blocks = mb;
448     cur_blocks--;
449     cur_alloced -= size;
450 }
451
452 int
453 m_is_secure( const void *p )
454 {
455     return p >= pool && p < (void*)((char*)pool+poolsize);
456 }
457
458
459
460 /****************
461  * Warning:  This code might be called by an interrupt handler
462  *           and frankly, there should really be such a handler,
463  *           to make sure that the memory is wiped out.
464  *           We hope that the OS wipes out mlocked memory after
465  *           receiving a SIGKILL - it really should do so, otherwise
466  *           there is no chance to get the secure memory cleaned.
467  */
468 void
469 secmem_term()
470 {
471     if( !pool_okay )
472         return;
473
474     wipememory2( pool, 0xff, poolsize);
475     wipememory2( pool, 0xaa, poolsize);
476     wipememory2( pool, 0x55, poolsize);
477     wipememory2( pool, 0x00, poolsize);
478 #ifdef HAVE_MMAP
479     if( pool_is_mmapped )
480         munmap( pool, poolsize );
481 #endif
482     pool = NULL;
483     pool_okay = 0;
484     poolsize=0;
485     poollen=0;
486     unused_blocks=NULL;
487 }
488
489
490 void
491 secmem_dump_stats()
492 {
493     if( disable_secmem )
494         return;
495     fprintf(stderr,
496                 "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
497                 cur_alloced, max_alloced, cur_blocks, max_blocks,
498                 (ulong)poollen, (ulong)poolsize );
499 }