91cd3eb99d2c807430f6f9c57bd5ca8da717d76a
[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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <stdarg.h>
27 #include <unistd.h>
28 #if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
29 #include <sys/mman.h>
30 #include <sys/types.h>
31 #include <fcntl.h>
32 #ifdef USE_CAPABILITIES
33 #include <sys/capability.h>
34 #endif
35 #ifdef HAVE_PLOCK
36 #include <sys/lock.h>
37 #endif
38 #endif
39
40 #include "types.h"
41 #include "memory.h"
42 #include "util.h"
43 #include "i18n.h"
44
45 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
46 #define MAP_ANONYMOUS MAP_ANON
47 #endif
48 /* It seems that Slackware 7.1 does not know about EPERM */
49 #if !defined(EPERM) && defined(ENOMEM)
50 #define EPERM  ENOMEM
51 #endif
52
53
54 #define DEFAULT_POOLSIZE 16384
55
56 typedef struct memblock_struct MEMBLOCK;
57 struct memblock_struct {
58     unsigned size;
59     union {
60         MEMBLOCK *next;
61         PROPERLY_ALIGNED_TYPE aligned;
62     } u;
63 };
64
65
66
67 static void  *pool;
68 static volatile int pool_okay; /* may be checked in an atexit function */
69 #ifdef HAVE_MMAP
70 static volatile int pool_is_mmapped;
71 #endif
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;
83
84
85 static void
86 print_warn(void)
87 {
88   if (!no_warning)
89     {
90       log_info(_("WARNING: using insecure memory!\n"));
91       log_info(_("please see http://www.gnupg.org/faq.html "
92                  "for more information\n"));
93     }
94 }
95
96
97 static void
98 lock_pool( void *p, size_t n )
99 {
100 #if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
101     int err;
102
103     cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
104     err = mlock( p, n );
105     if( err && errno )
106         err = errno;
107     cap_set_proc( cap_from_text("cap_ipc_lock+p") );
108
109     if( err ) {
110         if( errno != EPERM
111 #ifdef EAGAIN  /* OpenBSD returns this */
112             && errno != EAGAIN
113 #endif
114 #ifdef ENOSYS  /* Some SCOs return this (function not implemented) */
115             && errno != ENOSYS
116 #endif
117 #ifdef ENOMEM  /* Linux can return this */
118             && errno != ENOMEM
119 #endif
120           )
121             log_error("can't lock memory: %s\n", strerror(err));
122         show_warning = 1;
123     }
124
125 #elif defined(HAVE_MLOCK)
126     uid_t uid;
127     int err;
128
129     uid = getuid();
130
131 #ifdef HAVE_BROKEN_MLOCK
132     /* ick. but at least we get secured memory. about to lock
133        entire data segment. */
134 #ifdef HAVE_PLOCK
135 # ifdef _AIX
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.
142        */    
143     errno = EPERM;
144     err = errno;
145 # else /* !_AIX */
146     err = plock( DATLOCK );
147     if( err && errno )
148         err = errno;
149 # endif /*_AIX*/
150 #else /*!HAVE_PLOCK*/
151     if( uid ) {
152         errno = EPERM;
153         err = errno;
154     }
155     else {
156         err = mlock( p, n );
157         if( err && errno )
158             err = errno;
159     }
160 #endif /*!HAVE_PLOCK*/
161 #else
162     err = mlock( p, n );
163     if( err && errno )
164         err = errno;
165 #endif
166
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));
172     }
173
174     if( err ) {
175         if( errno != EPERM
176 #ifdef EAGAIN  /* OpenBSD returns this */
177             && errno != EAGAIN
178 #endif
179 #ifdef ENOSYS  /* Some SCOs return this (function not implemented) */
180             && errno != ENOSYS
181 #endif
182 #ifdef ENOMEM  /* Linux can return this */
183             && errno != ENOMEM
184 #endif
185           )
186             log_error("can't lock memory: %s\n", strerror(err));
187         show_warning = 1;
188     }
189
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
195      */
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
199      */
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.
204      */
205 #else
206     log_info("Please note that you don't have secure memory on this system\n");
207 #endif
208 }
209
210
211 static void
212 init_pool( size_t n)
213 {
214     size_t pgsize;
215
216     poolsize = n;
217
218     if( disable_secmem )
219         log_bug("secure memory is disabled");
220
221 #ifdef HAVE_GETPAGESIZE
222     pgsize = getpagesize();
223 #else
224     pgsize = 4096;
225 #endif
226
227 #ifdef HAVE_MMAP
228     poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
229 #ifdef MAP_ANONYMOUS
230        pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
231                                  MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
232 #else /* map /dev/zero instead */
233     {   int fd;
234
235         fd = open("/dev/zero", O_RDWR);
236         if( fd == -1 ) {
237             log_error("can't open /dev/zero: %s\n", strerror(errno) );
238             pool = (void*)-1;
239         }
240         else {
241             pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
242                                       MAP_PRIVATE, fd, 0);
243         }
244     }
245 #endif
246     if( pool == (void*)-1 )
247         log_info("can't mmap pool of %u bytes: %s - using malloc\n",
248                             (unsigned)poolsize, strerror(errno));
249     else {
250         pool_is_mmapped = 1;
251         pool_okay = 1;
252     }
253
254 #endif
255     if( !pool_okay ) {
256         pool = malloc( poolsize );
257         if( !pool )
258             log_fatal("can't allocate memory pool of %u bytes\n",
259                                                        (unsigned)poolsize);
260         else
261             pool_okay = 1;
262     }
263     lock_pool( pool, poolsize );
264     poollen = 0;
265 }
266
267
268 /* concatenate unused blocks */
269 static void
270 compress_pool(void)
271 {
272     /* fixme: we really should do this */
273 }
274
275 void
276 secmem_set_flags( unsigned flags )
277 {
278     int was_susp = suspend_warning;
279
280     no_warning = flags & 1;
281     suspend_warning = flags & 2;
282
283     /* and now issue the warning if it is not longer suspended */
284     if( was_susp && !suspend_warning && show_warning ) {
285         show_warning = 0;
286         print_warn();
287     }
288 }
289
290 unsigned
291 secmem_get_flags(void)
292 {
293     unsigned flags;
294
295     flags  = no_warning      ? 1:0;
296     flags |= suspend_warning ? 2:0;
297     return flags;
298 }
299
300 void
301 secmem_init( size_t n )
302 {
303     if( !n ) {
304 #ifndef __riscos__
305 #ifdef USE_CAPABILITIES
306         /* drop all capabilities */
307         cap_set_proc( cap_from_text("all-eip") );
308
309 #elif !defined(HAVE_DOSISH_SYSTEM)
310         uid_t uid;
311
312         disable_secmem=1;
313         uid = getuid();
314         if( uid != geteuid() ) {
315             if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
316                 log_fatal("failed to drop setuid\n" );
317         }
318 #endif
319 #endif /* !__riscos__ */
320     }
321     else {
322         if( n < DEFAULT_POOLSIZE )
323             n = DEFAULT_POOLSIZE;
324         if( !pool_okay )
325             init_pool(n);
326         else
327             log_error("Oops, secure memory pool already initialized\n");
328     }
329 }
330
331
332 void *
333 secmem_malloc( size_t size )
334 {
335     MEMBLOCK *mb, *mb2;
336     int compressed=0;
337
338     if( !pool_okay ) {
339         log_info(
340          _("operation is not possible without initialized secure memory\n"));
341         log_info(_("(you may have used the wrong program for this task)\n"));
342         exit(2);
343     }
344     if( show_warning && !suspend_warning ) {
345         show_warning = 0;
346         print_warn();
347     }
348
349     /* blocks are always a multiple of 32 */
350     size += sizeof(MEMBLOCK);
351     size = ((size + 31) / 32) * 32;
352
353   retry:
354     /* try to get it from the used blocks */
355     for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
356         if( mb->size >= size ) {
357             if( mb2 )
358                 mb2->u.next = mb->u.next;
359             else
360                 unused_blocks = mb->u.next;
361             goto leave;
362         }
363     /* allocate a new block */
364     if( (poollen + size <= poolsize) ) {
365         mb = (void*)((char*)pool + poollen);
366         poollen += size;
367         mb->size = size;
368     }
369     else if( !compressed ) {
370         compressed=1;
371         compress_pool();
372         goto retry;
373     }
374     else
375         return NULL;
376
377   leave:
378     cur_alloced += mb->size;
379     cur_blocks++;
380     if( cur_alloced > max_alloced )
381         max_alloced = cur_alloced;
382     if( cur_blocks > max_blocks )
383         max_blocks = cur_blocks;
384
385     return &mb->u.aligned.c;
386 }
387
388
389 void *
390 secmem_realloc( void *p, size_t newsize )
391 {
392     MEMBLOCK *mb;
393     size_t size;
394     void *a;
395
396     mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
397     size = mb->size;
398     if( newsize < size )
399         return p; /* it is easier not to shrink the memory */
400     a = secmem_malloc( newsize );
401     if ( a ) {
402         memcpy(a, p, size);
403         memset((char*)a+size, 0, newsize-size);
404         secmem_free(p);
405     }
406     return a;
407 }
408
409
410 void
411 secmem_free( void *a )
412 {
413     MEMBLOCK *mb;
414     size_t size;
415
416     if( !a )
417         return;
418
419     mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
420     size = mb->size;
421     /* This does not make much sense: probably this memory is held in the
422      * cache. We do it anyway: */
423     wipememory2(mb, 0xff, size );
424     wipememory2(mb, 0xaa, size );
425     wipememory2(mb, 0x55, size );
426     wipememory2(mb, 0x00, size );
427     mb->size = size;
428     mb->u.next = unused_blocks;
429     unused_blocks = mb;
430     cur_blocks--;
431     cur_alloced -= size;
432 }
433
434 int
435 m_is_secure( const void *p )
436 {
437     return p >= pool && p < (void*)((char*)pool+poolsize);
438 }
439
440
441
442 /****************
443  * Warning:  This code might be called by an interrupt handler
444  *           and frankly, there should really be such a handler,
445  *           to make sure that the memory is wiped out.
446  *           We hope that the OS wipes out mlocked memory after
447  *           receiving a SIGKILL - it really should do so, otherwise
448  *           there is no chance to get the secure memory cleaned.
449  */
450 void
451 secmem_term()
452 {
453     if( !pool_okay )
454         return;
455
456     wipememory2( pool, 0xff, poolsize);
457     wipememory2( pool, 0xaa, poolsize);
458     wipememory2( pool, 0x55, poolsize);
459     wipememory2( pool, 0x00, poolsize);
460 #ifdef HAVE_MMAP
461     if( pool_is_mmapped )
462         munmap( pool, poolsize );
463 #endif
464     pool = NULL;
465     pool_okay = 0;
466     poolsize=0;
467     poollen=0;
468     unused_blocks=NULL;
469 }
470
471
472 void
473 secmem_dump_stats()
474 {
475     if( disable_secmem )
476         return;
477     fprintf(stderr,
478                 "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
479                 cur_alloced, max_alloced, cur_blocks, max_blocks,
480                 (ulong)poollen, (ulong)poolsize );
481 }