Fix various build warnings reported by Joe Vender on MinGW.
[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         }
251     }
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;
259     }
260
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;
269     }
270     lock_pool( pool, poolsize );
271     poollen = 0;
272 }
273
274
275 /* concatenate unused blocks */
276 static void
277 compress_pool(void)
278 {
279     /* fixme: we really should do this */
280 }
281
282 void
283 secmem_set_flags( unsigned flags )
284 {
285     int was_susp = suspend_warning;
286
287     no_warning = flags & 1;
288     suspend_warning = flags & 2;
289
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();
294     }
295 }
296
297 unsigned
298 secmem_get_flags(void)
299 {
300     unsigned flags;
301
302     flags  = no_warning      ? 1:0;
303     flags |= suspend_warning ? 2:0;
304     return flags;
305 }
306
307 /* Returns 1 if memory was locked, 0 if not. */
308 int
309 secmem_init( size_t n )
310 {
311     if( !n ) {
312 #ifndef __riscos__
313 #ifdef USE_CAPABILITIES
314         /* drop all capabilities */
315         cap_set_proc( cap_from_text("all-eip") );
316
317 #elif !defined(HAVE_DOSISH_SYSTEM)
318         uid_t uid;
319
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" );
325         }
326 #endif
327 #endif /* !__riscos__ */
328     }
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");
336     }
337
338     return !show_warning;
339 }
340
341
342 void *
343 secmem_malloc( size_t size )
344 {
345     MEMBLOCK *mb, *mb2;
346     int compressed=0;
347
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);
353     }
354     if( show_warning && !suspend_warning ) {
355         show_warning = 0;
356         print_warn();
357     }
358
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;
365
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;
375         }
376     /* allocate a new block */
377     if( (poollen + size <= poolsize) ) {
378         mb = (void*)((char*)pool + poollen);
379         poollen += size;
380         mb->size = size;
381     }
382     else if( !compressed ) {
383         compressed=1;
384         compress_pool();
385         goto retry;
386     }
387     else
388         return NULL;
389
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;
397
398     return &mb->u.aligned.c;
399 }
400
401
402 void *
403 secmexrealloc( void *p, size_t newsize )
404 {
405     MEMBLOCK *mb;
406     size_t size;
407     void *a;
408
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", mb);
413     size -= ((size_t) &((MEMBLOCK*)0)->u.aligned.c);
414
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);
422     }
423     return a;
424 }
425
426
427 void
428 secmem_free( void *a )
429 {
430     MEMBLOCK *mb;
431     size_t size;
432
433     if( !a )
434         return;
435
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;
449 }
450
451 int
452 m_is_secure( const void *p )
453 {
454     return p >= pool && p < (void*)((char*)pool+poolsize);
455 }
456
457
458
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.
466  */
467 void
468 secmem_term()
469 {
470     if( !pool_okay )
471         return;
472
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;
486 }
487
488
489 void
490 secmem_dump_stats()
491 {
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 );
498 }