Add debug code for W32CE
[pinentry-qt.git] / secmem.c
1 /* secmem.c  -  memory allocation from a secure heap
2  *      Copyright (C) 1998, 1999, 2003 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 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #ifndef HAVE_W32CE_SYSTEM
27 #include <unistd.h>
28 #include <errno.h>
29 #endif
30 #include <stdarg.h>
31 #if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
32 # include <sys/mman.h>
33 # include <sys/types.h>
34 # include <fcntl.h>
35 # ifdef USE_CAPABILITIES
36 #  include <sys/capability.h>
37 # endif
38 #endif
39 #include <string.h>
40
41 #include "memory.h"
42
43 #ifdef ORIGINAL_GPG_VERSION
44 #include "types.h"
45 #include "util.h"
46 #else /* ORIGINAL_GPG_VERSION */
47
48 #include "secmem-util.h"
49
50 typedef union {
51     int a;
52     short b;
53     char c[1];
54     long d;
55 #ifdef HAVE_U64_TYPEDEF
56     u64 e;
57 #endif
58     float f;
59     double g;
60 } PROPERLY_ALIGNED_TYPE;
61
62 #define log_error log_info
63 #define log_bug log_fatal
64
65 void 
66 log_info(char *template, ...)
67 {
68   va_list args;
69   
70   va_start(args, template);
71   vfprintf(stderr, template, args);
72   va_end(args);
73 }
74
75 void 
76 log_fatal(char *template, ...)
77 {
78   va_list args;
79   
80   va_start(args, template);
81   vfprintf(stderr, template, args);
82   va_end(args);
83   exit(EXIT_FAILURE);
84 }
85
86 #endif /* ORIGINAL_GPG_VERSION */
87
88 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
89 #  define MAP_ANONYMOUS MAP_ANON
90 #endif
91
92 #define DEFAULT_POOLSIZE 16384
93
94 typedef struct memblock_struct MEMBLOCK;
95 struct memblock_struct {
96     unsigned size;
97     union {
98         MEMBLOCK *next;
99         PROPERLY_ALIGNED_TYPE aligned;
100     } u;
101 };
102
103
104
105 static void  *pool;
106 static volatile int pool_okay; /* may be checked in an atexit function */
107 static int   pool_is_mmapped;
108 static size_t poolsize; /* allocated length */
109 static size_t poollen;  /* used length */
110 static MEMBLOCK *unused_blocks;
111 static unsigned max_alloced;
112 static unsigned cur_alloced;
113 static unsigned max_blocks;
114 static unsigned cur_blocks;
115 static int disable_secmem;
116 static int show_warning;
117 static int no_warning;
118 static int suspend_warning;
119
120
121 static void
122 print_warn(void)
123 {
124     if( !no_warning )
125         log_info("Warning: using insecure memory!\n");
126 }
127
128
129 static void
130 lock_pool( void *p, size_t n )
131 {
132 #if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
133     int err;
134
135     cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
136     err = mlock( p, n );
137     if( err && errno )
138         err = errno;
139     cap_set_proc( cap_from_text("cap_ipc_lock+p") );
140
141     if( err ) {
142         if( errno != EPERM
143           #ifdef EAGAIN  /* OpenBSD returns this */
144             && errno != EAGAIN
145           #endif
146           )
147             log_error("can't lock memory: %s\n", strerror(err));
148         show_warning = 1;
149     }
150
151 #elif defined(HAVE_MLOCK)
152     uid_t uid;
153     int err;
154
155     uid = getuid();
156
157 #ifdef HAVE_BROKEN_MLOCK
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 #else
168     err = mlock( p, n );
169     if( err && errno )
170         err = errno;
171 #endif
172
173     if( uid && !geteuid() ) {
174         if( setuid( uid ) || getuid() != geteuid()  )
175             log_fatal("failed to reset uid: %s\n", strerror(errno));
176     }
177
178     if( err ) {
179         if( errno != EPERM
180 #ifdef EAGAIN  /* OpenBSD returns this */
181             && errno != EAGAIN
182 #endif
183           )
184             log_error("can't lock memory: %s\n", strerror(err));
185         show_warning = 1;
186     }
187 #elif HAVE_W32CE_SYSTEM
188     /* No secure memory - and it does not make sense anyway.  */
189 #else
190     log_info("Please note that you don't have secure memory on this system\n");
191 #endif
192 }
193
194
195 static void
196 init_pool( size_t n)
197 {
198     size_t pgsize;
199
200     poolsize = n;
201
202     if( disable_secmem )
203         log_bug("secure memory is disabled");
204
205 #ifdef HAVE_GETPAGESIZE
206     pgsize = getpagesize();
207 #else
208     pgsize = 4096;
209 #endif
210
211 #if HAVE_MMAP
212     poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
213 # ifdef MAP_ANONYMOUS
214        pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
215                                  MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
216 # else /* map /dev/zero instead */
217     {   int fd;
218
219         fd = open("/dev/zero", O_RDWR);
220         if( fd == -1 ) {
221             log_error("can't open /dev/zero: %s\n", strerror(errno) );
222             pool = (void*)-1;
223         }
224         else {
225             pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
226                                       MAP_PRIVATE, fd, 0);
227             close (fd);
228         }
229     }
230 # endif
231     if( pool == (void*)-1 )
232         log_info("can't mmap pool of %u bytes: %s - using malloc\n",
233                             (unsigned)poolsize, strerror(errno));
234     else {
235         pool_is_mmapped = 1;
236         pool_okay = 1;
237     }
238
239 #endif
240     if( !pool_okay ) {
241         pool = malloc( poolsize );
242         if( !pool )
243             log_fatal("can't allocate memory pool of %u bytes\n",
244                                                        (unsigned)poolsize);
245         else
246             pool_okay = 1;
247     }
248     lock_pool( pool, poolsize );
249     poollen = 0;
250 }
251
252
253 /* concatenate unused blocks */
254 static void
255 compress_pool(void)
256 {
257     /* fixme: we really should do this */
258 }
259
260 void
261 secmem_set_flags( unsigned flags )
262 {
263     int was_susp = suspend_warning;
264
265     no_warning = flags & 1;
266     suspend_warning = flags & 2;
267
268     /* and now issue the warning if it is not longer suspended */
269     if( was_susp && !suspend_warning && show_warning ) {
270         show_warning = 0;
271         print_warn();
272     }
273 }
274
275 unsigned
276 secmem_get_flags(void)
277 {
278     unsigned flags;
279
280     flags  = no_warning      ? 1:0;
281     flags |= suspend_warning ? 2:0;
282     return flags;
283 }
284
285 void
286 secmem_init( size_t n )
287 {
288     if( !n ) {
289 #ifdef USE_CAPABILITIES
290         /* drop all capabilities */
291         cap_set_proc( cap_from_text("all-eip") );
292
293 #elif !defined(HAVE_DOSISH_SYSTEM)
294         uid_t uid;
295
296         disable_secmem=1;
297         uid = getuid();
298         if( uid != geteuid() ) {
299             if( setuid( uid ) || getuid() != geteuid() )
300                 log_fatal("failed to drop setuid\n" );
301         }
302 #endif
303     }
304     else {
305         if( n < DEFAULT_POOLSIZE )
306             n = DEFAULT_POOLSIZE;
307         if( !pool_okay )
308             init_pool(n);
309         else
310             log_error("Oops, secure memory pool already initialized\n");
311     }
312 }
313
314
315 void *
316 secmem_malloc( size_t size )
317 {
318     MEMBLOCK *mb, *mb2;
319     int compressed=0;
320
321     if( !pool_okay ) {
322         log_info(
323         "operation is not possible without initialized secure memory\n");
324         log_info("(you may have used the wrong program for this task)\n");
325         exit(2);
326     }
327     if( show_warning && !suspend_warning ) {
328         show_warning = 0;
329         print_warn();
330     }
331
332     /* blocks are always a multiple of 32 */
333     size += sizeof(MEMBLOCK);
334     size = ((size + 31) / 32) * 32;
335
336   retry:
337     /* try to get it from the used blocks */
338     for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
339         if( mb->size >= size ) {
340             if( mb2 )
341                 mb2->u.next = mb->u.next;
342             else
343                 unused_blocks = mb->u.next;
344             goto leave;
345         }
346     /* allocate a new block */
347     if( (poollen + size <= poolsize) ) {
348         mb = (void*)((char*)pool + poollen);
349         poollen += size;
350         mb->size = size;
351     }
352     else if( !compressed ) {
353         compressed=1;
354         compress_pool();
355         goto retry;
356     }
357     else
358         return NULL;
359
360   leave:
361     cur_alloced += mb->size;
362     cur_blocks++;
363     if( cur_alloced > max_alloced )
364         max_alloced = cur_alloced;
365     if( cur_blocks > max_blocks )
366         max_blocks = cur_blocks;
367
368     return &mb->u.aligned.c;
369 }
370
371
372 void *
373 secmem_realloc( void *p, size_t newsize )
374 {
375     MEMBLOCK *mb;
376     size_t size;
377     void *a;
378
379     mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
380     size = mb->size;
381     if( newsize < size )
382         return p; /* it is easier not to shrink the memory */
383     a = secmem_malloc( newsize );
384     memcpy(a, p, size);
385     memset((char*)a+size, 0, newsize-size);
386     secmem_free(p);
387     return a;
388 }
389
390
391 void
392 secmem_free( void *a )
393 {
394     MEMBLOCK *mb;
395     size_t size;
396
397     if( !a )
398         return;
399
400     mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
401     size = mb->size;
402     /* This does not make much sense: probably this memory is held in the
403      * cache. We do it anyway: */
404     wipememory2(mb, 0xff, size );
405     wipememory2(mb, 0xaa, size );
406     wipememory2(mb, 0x55, size );
407     wipememory2(mb, 0x00, size );
408     mb->size = size;
409     mb->u.next = unused_blocks;
410     unused_blocks = mb;
411     cur_blocks--;
412     cur_alloced -= size;
413 }
414
415 int
416 m_is_secure( const void *p )
417 {
418     return p >= pool && p < (void*)((char*)pool+poolsize);
419 }
420
421 void
422 secmem_term()
423 {
424     if( !pool_okay )
425         return;
426
427     wipememory2( pool, 0xff, poolsize);
428     wipememory2( pool, 0xaa, poolsize);
429     wipememory2( pool, 0x55, poolsize);
430     wipememory2( pool, 0x00, poolsize);
431 #if HAVE_MMAP
432     if( pool_is_mmapped )
433         munmap( pool, poolsize );
434 #endif
435     pool = NULL;
436     pool_okay = 0;
437     poolsize=0;
438     poollen=0;
439     unused_blocks=NULL;
440 }
441
442
443 void
444 secmem_dump_stats()
445 {
446     if( disable_secmem )
447         return;
448     fprintf(stderr,
449                 "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
450                 cur_alloced, max_alloced, cur_blocks, max_blocks,
451                 (ulong)poollen, (ulong)poolsize );
452 }
453
454
455 size_t 
456 secmem_get_max_size (void)
457 {
458   return poolsize;
459 }