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