e9bc05abd7e8fdb832314afd9220f5677ca81348
[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 #endif
33
34 #include "types.h"
35 #include "memory.h"
36 #include "util.h"
37 #include "i18n.h"
38
39 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
40   #define MAP_ANONYMOUS MAP_ANON
41 #endif
42
43 #define DEFAULT_POOLSIZE 8196
44
45 typedef struct memblock_struct MEMBLOCK;
46 struct memblock_struct {
47     unsigned size;
48     union {
49         MEMBLOCK *next;
50         long align_dummy;
51         char d[1];
52     } u;
53 };
54
55
56
57 static void  *pool;
58 static int   pool_okay;
59 static int   pool_is_mmapped;
60 static size_t poolsize; /* allocated length */
61 static size_t poollen;  /* used length */
62 static MEMBLOCK *unused_blocks;
63 static unsigned max_alloced;
64 static unsigned cur_alloced;
65 static unsigned max_blocks;
66 static unsigned cur_blocks;
67 static int disable_secmem;
68 static int show_warning;
69 static int no_warning;
70 static int suspend_warning;
71
72
73 static void
74 print_warn()
75 {
76     if( !no_warning )
77         log_info(_("Warning: using insecure memory!\n"));
78 }
79
80
81 static void
82 lock_pool( void *p, size_t n )
83 {
84   #ifdef HAVE_MLOCK
85     uid_t uid;
86     int err;
87
88     uid = getuid();
89
90   #ifdef HAVE_BROKEN_MLOCK
91     if( uid ) {
92         errno = EPERM;
93         err = errno;
94     }
95     else {
96         err = mlock( p, n );
97         if( err && errno )
98             err = errno;
99     }
100   #else
101     err = mlock( p, n );
102     if( err && errno )
103         err = errno;
104   #endif
105
106     if( uid && !geteuid() ) {
107         if( setuid( uid ) || getuid() != geteuid()  )
108             log_fatal("failed to reset uid: %s\n", strerror(errno));
109     }
110
111     if( err ) {
112         if( errno != EPERM
113           #ifdef EAGAIN  /* OpenBSD returns this */
114             && errno != EAGAIN
115           #endif
116           )
117             log_error("canĀ“t lock memory: %s\n", strerror(err));
118         show_warning = 1;
119     }
120
121   #else
122     log_info("Please note that you don't have secure memory on this system\n");
123   #endif
124 }
125
126
127 static void
128 init_pool( size_t n)
129 {
130     size_t pgsize;
131
132     poolsize = n;
133
134     if( disable_secmem )
135         log_bug("secure memory is disabled");
136
137   #ifdef HAVE_GETPAGESIZE
138     pgsize = getpagesize();
139   #else
140     pgsize = 4096;
141   #endif
142
143   #if HAVE_MMAP
144     poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
145     #ifdef MAP_ANONYMOUS
146        pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
147                                  MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
148     #else /* map /dev/zero instead */
149     {   int fd;
150
151         fd = open("/dev/zero", O_RDWR);
152         if( fd == -1 ) {
153             log_error("can't open /dev/zero: %s\n", strerror(errno) );
154             pool = (void*)-1;
155         }
156         else {
157             pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
158                                       MAP_PRIVATE, fd, 0);
159         }
160     }
161     #endif
162     if( pool == (void*)-1 )
163         log_info("can't mmap pool of %u bytes: %s - using malloc\n",
164                             (unsigned)poolsize, strerror(errno));
165     else {
166         pool_is_mmapped = 1;
167         pool_okay = 1;
168     }
169
170   #endif
171     if( !pool_okay ) {
172         pool = malloc( poolsize );
173         if( !pool )
174             log_fatal("can't allocate memory pool of %u bytes\n",
175                                                        (unsigned)poolsize);
176         else
177             pool_okay = 1;
178     }
179     lock_pool( pool, poolsize );
180     poollen = 0;
181 }
182
183
184 /* concatenate unused blocks */
185 static void
186 compress_pool(void)
187 {
188
189 }
190
191 void
192 secmem_set_flags( unsigned flags )
193 {
194     int was_susp = suspend_warning;
195
196     no_warning = flags & 1;
197     suspend_warning = flags & 2;
198
199     /* and now issue the warning if it is not longer suspended */
200     if( was_susp && !suspend_warning && show_warning ) {
201         show_warning = 0;
202         print_warn();
203     }
204 }
205
206 unsigned
207 secmem_get_flags(void)
208 {
209     unsigned flags;
210
211     flags  = no_warning      ? 1:0;
212     flags |= suspend_warning ? 2:0;
213     return flags;
214 }
215
216 void
217 secmem_init( size_t n )
218 {
219     if( !n ) {
220       #ifndef __MINGW32__
221         uid_t uid;
222
223         disable_secmem=1;
224         uid = getuid();
225         if( uid != geteuid() ) {
226             if( setuid( uid ) || getuid() != geteuid() )
227                 log_fatal("failed to drop setuid\n" );
228         }
229       #endif
230     }
231     else {
232         if( n < DEFAULT_POOLSIZE )
233             n = DEFAULT_POOLSIZE;
234         if( !pool_okay )
235             init_pool(n);
236         else
237             log_error("Oops, secure memory pool already initialized\n");
238     }
239 }
240
241
242 void *
243 secmem_malloc( size_t size )
244 {
245     MEMBLOCK *mb, *mb2;
246     int compressed=0;
247
248     if( !pool_okay )
249         log_bug("secmem not initialized\n");
250     if( show_warning && !suspend_warning ) {
251         show_warning = 0;
252         print_warn();
253     }
254
255     /* blocks are always a multiple of 32 */
256     size += sizeof(MEMBLOCK);
257     size = ((size + 31) / 32) * 32;
258
259   retry:
260     /* try to get it from the used blocks */
261     for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
262         if( mb->size >= size ) {
263             if( mb2 )
264                 mb2->u.next = mb->u.next;
265             else
266                 unused_blocks = mb->u.next;
267             goto leave;
268         }
269     /* allocate a new block */
270     if( (poollen + size <= poolsize) ) {
271         mb = (void*)((char*)pool + poollen);
272         poollen += size;
273         mb->size = size;
274     }
275     else if( !compressed ) {
276         compressed=1;
277         compress_pool();
278         goto retry;
279     }
280     else
281         return NULL;
282
283   leave:
284     cur_alloced += mb->size;
285     cur_blocks++;
286     if( cur_alloced > max_alloced )
287         max_alloced = cur_alloced;
288     if( cur_blocks > max_blocks )
289         max_blocks = cur_blocks;
290     return &mb->u.d;
291 }
292
293
294 void *
295 secmem_realloc( void *p, size_t newsize )
296 {
297     MEMBLOCK *mb;
298     size_t size;
299     void *a;
300
301     mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.d));
302     size = mb->size;
303     if( newsize < size )
304         return p; /* it is easier not to shrink the memory */
305     a = secmem_malloc( newsize );
306     memcpy(a, p, size);
307     memset((char*)a+size, 0, newsize-size);
308     secmem_free(p);
309     return a;
310 }
311
312
313 void
314 secmem_free( void *a )
315 {
316     MEMBLOCK *mb;
317     size_t size;
318
319     if( !a )
320         return;
321
322     mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.d));
323     size = mb->size;
324     memset(mb, 0xff, size );
325     memset(mb, 0xaa, size );
326     memset(mb, 0x55, size );
327     memset(mb, 0x00, size );
328     mb->size = size;
329     mb->u.next = unused_blocks;
330     unused_blocks = mb;
331     cur_blocks--;
332     cur_alloced -= size;
333 }
334
335 int
336 m_is_secure( const void *p )
337 {
338     return p >= pool && p < (void*)((char*)pool+poolsize);
339 }
340
341 void
342 secmem_term()
343 {
344     if( !pool_okay )
345         return;
346
347     memset( pool, 0xff, poolsize);
348     memset( pool, 0xaa, poolsize);
349     memset( pool, 0x55, poolsize);
350     memset( pool, 0x00, poolsize);
351   #if HAVE_MMAP
352     if( pool_is_mmapped )
353         munmap( pool, poolsize );
354   #endif
355     pool = NULL;
356     pool_okay = 0;
357     poolsize=0;
358     poollen=0;
359     unused_blocks=NULL;
360 }
361
362
363 void
364 secmem_dump_stats()
365 {
366     if( disable_secmem )
367         return;
368     fprintf(stderr,
369                 "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
370                 cur_alloced, max_alloced, cur_blocks, max_blocks,
371                 (ulong)poollen, (ulong)poolsize );
372 }
373