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