See ChangeLog: Tue Jun 29 21:44:25 CEST 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 int   pool_okay;
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           )
101             log_error("can´t lock memory: %s\n", strerror(err));
102         show_warning = 1;
103     }
104
105   #elif defined(HAVE_MLOCK)
106     uid_t uid;
107     int err;
108
109     uid = getuid();
110
111   #ifdef HAVE_BROKEN_MLOCK
112     if( uid ) {
113         errno = EPERM;
114         err = errno;
115     }
116     else {
117         err = mlock( p, n );
118         if( err && errno )
119             err = errno;
120     }
121   #else
122     err = mlock( p, n );
123     if( err && errno )
124         err = errno;
125   #endif
126
127     if( uid && !geteuid() ) {
128         if( setuid( uid ) || getuid() != geteuid()  )
129             log_fatal("failed to reset uid: %s\n", strerror(errno));
130     }
131
132     if( err ) {
133         if( errno != EPERM
134           #ifdef EAGAIN  /* OpenBSD returns this */
135             && errno != EAGAIN
136           #endif
137           )
138             log_error("can´t lock memory: %s\n", strerror(err));
139         show_warning = 1;
140     }
141
142   #else
143     log_info("Please note that you don't have secure memory on this system\n");
144   #endif
145 }
146
147
148 static void
149 init_pool( size_t n)
150 {
151     size_t pgsize;
152
153     poolsize = n;
154
155     if( disable_secmem )
156         log_bug("secure memory is disabled");
157
158   #ifdef HAVE_GETPAGESIZE
159     pgsize = getpagesize();
160   #else
161     pgsize = 4096;
162   #endif
163
164   #if HAVE_MMAP
165     poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
166     #ifdef MAP_ANONYMOUS
167        pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
168                                  MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
169     #else /* map /dev/zero instead */
170     {   int fd;
171
172         fd = open("/dev/zero", O_RDWR);
173         if( fd == -1 ) {
174             log_error("can't open /dev/zero: %s\n", strerror(errno) );
175             pool = (void*)-1;
176         }
177         else {
178             pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
179                                       MAP_PRIVATE, fd, 0);
180         }
181     }
182     #endif
183     if( pool == (void*)-1 )
184         log_info("can't mmap pool of %u bytes: %s - using malloc\n",
185                             (unsigned)poolsize, strerror(errno));
186     else {
187         pool_is_mmapped = 1;
188         pool_okay = 1;
189     }
190
191   #endif
192     if( !pool_okay ) {
193         pool = malloc( poolsize );
194         if( !pool )
195             log_fatal("can't allocate memory pool of %u bytes\n",
196                                                        (unsigned)poolsize);
197         else
198             pool_okay = 1;
199     }
200     lock_pool( pool, poolsize );
201     poollen = 0;
202 }
203
204
205 /* concatenate unused blocks */
206 static void
207 compress_pool(void)
208 {
209     /* fixme: we really should do this */
210 }
211
212 void
213 secmem_set_flags( unsigned flags )
214 {
215     int was_susp = suspend_warning;
216
217     no_warning = flags & 1;
218     suspend_warning = flags & 2;
219
220     /* and now issue the warning if it is not longer suspended */
221     if( was_susp && !suspend_warning && show_warning ) {
222         show_warning = 0;
223         print_warn();
224     }
225 }
226
227 unsigned
228 secmem_get_flags(void)
229 {
230     unsigned flags;
231
232     flags  = no_warning      ? 1:0;
233     flags |= suspend_warning ? 2:0;
234     return flags;
235 }
236
237 void
238 secmem_init( size_t n )
239 {
240     if( !n ) {
241       #ifdef USE_CAPABILITIES
242         /* drop all capabilities */
243         cap_set_proc( cap_from_text("all-eip") );
244
245       #elif !defined(HAVE_DOSISH_SYSTEM)
246         uid_t uid;
247
248         disable_secmem=1;
249         uid = getuid();
250         if( uid != geteuid() ) {
251             if( setuid( uid ) || getuid() != geteuid() )
252                 log_fatal("failed to drop setuid\n" );
253         }
254       #endif
255     }
256     else {
257         if( n < DEFAULT_POOLSIZE )
258             n = DEFAULT_POOLSIZE;
259         if( !pool_okay )
260             init_pool(n);
261         else
262             log_error("Oops, secure memory pool already initialized\n");
263     }
264 }
265
266
267 void *
268 secmem_malloc( size_t size )
269 {
270     MEMBLOCK *mb, *mb2;
271     int compressed=0;
272
273     if( !pool_okay ) {
274         log_info(
275          _("operation is not possible without initialized secure memory\n"));
276         log_info(_("(you may have used the wrong program for this task)\n"));
277         exit(2);
278     }
279     if( show_warning && !suspend_warning ) {
280         show_warning = 0;
281         print_warn();
282     }
283
284     /* blocks are always a multiple of 32 */
285     size += sizeof(MEMBLOCK);
286     size = ((size + 31) / 32) * 32;
287
288   retry:
289     /* try to get it from the used blocks */
290     for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
291         if( mb->size >= size ) {
292             if( mb2 )
293                 mb2->u.next = mb->u.next;
294             else
295                 unused_blocks = mb->u.next;
296             goto leave;
297         }
298     /* allocate a new block */
299     if( (poollen + size <= poolsize) ) {
300         mb = (void*)((char*)pool + poollen);
301         poollen += size;
302         mb->size = size;
303     }
304     else if( !compressed ) {
305         compressed=1;
306         compress_pool();
307         goto retry;
308     }
309     else
310         return NULL;
311
312   leave:
313     cur_alloced += mb->size;
314     cur_blocks++;
315     if( cur_alloced > max_alloced )
316         max_alloced = cur_alloced;
317     if( cur_blocks > max_blocks )
318         max_blocks = cur_blocks;
319
320     return &mb->u.aligned.c;
321 }
322
323
324 void *
325 secmem_realloc( void *p, size_t newsize )
326 {
327     MEMBLOCK *mb;
328     size_t size;
329     void *a;
330
331     mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
332     size = mb->size;
333     if( newsize < size )
334         return p; /* it is easier not to shrink the memory */
335     a = secmem_malloc( newsize );
336     memcpy(a, p, size);
337     memset((char*)a+size, 0, newsize-size);
338     secmem_free(p);
339     return a;
340 }
341
342
343 void
344 secmem_free( void *a )
345 {
346     MEMBLOCK *mb;
347     size_t size;
348
349     if( !a )
350         return;
351
352     mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
353     size = mb->size;
354     /* This does not make much sense: probably this memory is held in the
355      * cache. We do it anyway: */
356     memset(mb, 0xff, size );
357     memset(mb, 0xaa, size );
358     memset(mb, 0x55, size );
359     memset(mb, 0x00, size );
360     mb->size = size;
361     mb->u.next = unused_blocks;
362     unused_blocks = mb;
363     cur_blocks--;
364     cur_alloced -= size;
365 }
366
367 int
368 m_is_secure( const void *p )
369 {
370     return p >= pool && p < (void*)((char*)pool+poolsize);
371 }
372
373 void
374 secmem_term()
375 {
376     if( !pool_okay )
377         return;
378
379     memset( pool, 0xff, poolsize);
380     memset( pool, 0xaa, poolsize);
381     memset( pool, 0x55, poolsize);
382     memset( pool, 0x00, poolsize);
383   #if HAVE_MMAP
384     if( pool_is_mmapped )
385         munmap( pool, poolsize );
386   #endif
387     pool = NULL;
388     pool_okay = 0;
389     poolsize=0;
390     poollen=0;
391     unused_blocks=NULL;
392 }
393
394
395 void
396 secmem_dump_stats()
397 {
398     if( disable_secmem )
399         return;
400     fprintf(stderr,
401                 "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
402                 cur_alloced, max_alloced, cur_blocks, max_blocks,
403                 (ulong)poollen, (ulong)poolsize );
404 }
405