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