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