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