release 0.2.3
[gnupg.git] / util / secmem.c
1 /* secmem.c  -  memory allocation from a secure heap
2  *      Copyright (c) 1997 by Werner Koch (dd9jn)
3  *
4  * This file is part of G10.
5  *
6  * G10 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  * G10 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
65 static void
66 lock_pool( void *p, size_t n )
67 {
68   #ifdef HAVE_MLOCK
69     uid_t uid;
70     int err;
71
72     err = mlock( p, n );
73     if( err && errno )
74         err = errno;
75
76     uid = getuid();
77     if( uid && !geteuid() ) {
78         if( setuid( uid ) )
79             log_fatal("failed to reset uid: %s\n", strerror(errno));
80     }
81
82     if( err ) {
83         if( errno != EPERM )
84             log_error("can´t lock memory: %s\n", strerror(err));
85         log_info(_("Warning: using insecure memory!\n"));
86     }
87
88   #else
89     log_info("Please note that you don´t have secure memory on this system\n");
90   #endif
91 }
92
93
94 static void
95 init_pool( size_t n)
96 {
97     poolsize = n;
98
99     if( disable_secmem )
100         log_bug("secure memory is disabled");
101
102   #if HAVE_MMAP && defined(MAP_ANONYMOUS)
103     poolsize = (poolsize + 4095) & ~4095;
104     pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
105                               MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
106     if( pool == (void*)-1 )
107         log_error("can't mmap pool of %u bytes: %s - using malloc\n",
108                             (unsigned)poolsize, strerror(errno));
109     else {
110         pool_is_mmapped = 1;
111         pool_okay = 1;
112     }
113
114   #endif
115     if( !pool_okay ) {
116         pool = malloc( poolsize );
117         if( !pool )
118             log_fatal("can't allocate memory pool of %u bytes\n",
119                                                        (unsigned)poolsize);
120         else
121             pool_okay = 1;
122     }
123     lock_pool( pool, poolsize );
124     poollen = 0;
125 }
126
127
128 /* concatenate unused blocks */
129 static void
130 compress_pool(void)
131 {
132
133 }
134
135
136 void
137 secmem_init( size_t n )
138 {
139     if( !n )
140         disable_secmem=1;
141     else {
142         if( n < DEFAULT_POOLSIZE )
143             n = DEFAULT_POOLSIZE;
144         if( !pool_okay )
145             init_pool(n);
146         else
147             log_error("Oops, secure memory pool already initialized\n");
148     }
149 }
150
151
152 void *
153 secmem_malloc( size_t size )
154 {
155     MEMBLOCK *mb, *mb2;
156     int compressed=0;
157
158     if( !pool_okay )
159         init_pool(DEFAULT_POOLSIZE);
160
161     /* blocks are always a multiple of 32 */
162     size += sizeof(MEMBLOCK);
163     size = ((size + 31) / 32) * 32;
164
165   retry:
166     /* try to get it from the used blocks */
167     for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
168         if( mb->size >= size ) {
169             if( mb2 )
170                 mb2->u.next = mb->u.next;
171             else
172                 unused_blocks = mb->u.next;
173             goto leave;
174         }
175     /* allocate a new block */
176     if( (poollen + size <= poolsize) ) {
177         mb = pool + poollen;
178         poollen += size;
179         mb->size = size;
180     }
181     else if( !compressed ) {
182         compressed=1;
183         compress_pool();
184         goto retry;
185     }
186     else
187         return NULL;
188
189   leave:
190     cur_alloced += mb->size;
191     cur_blocks++;
192     if( cur_alloced > max_alloced )
193         max_alloced = cur_alloced;
194     if( cur_blocks > max_blocks )
195         max_blocks = cur_blocks;
196     return &mb->u.d;
197 }
198
199
200 void
201 secmem_free( void *a )
202 {
203     MEMBLOCK *mb;
204     size_t size;
205
206     if( !a )
207         return;
208
209     mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.d));
210     size = mb->size;
211     memset(mb, 0xff, size );
212     memset(mb, 0xaa, size );
213     memset(mb, 0x55, size );
214     memset(mb, 0x00, size );
215     mb->size = size;
216     mb->u.next = unused_blocks;
217     unused_blocks = mb;
218     cur_blocks--;
219     cur_alloced -= size;
220 }
221
222 void
223 secmem_term()
224 {
225     if( !pool_okay )
226         return;
227
228     memset( pool, 0xff, poolsize);
229     memset( pool, 0xaa, poolsize);
230     memset( pool, 0x55, poolsize);
231     memset( pool, 0x00, poolsize);
232   #if HAVE_MMAP
233     if( pool_is_mmapped )
234         munmap( pool, poolsize );
235   #endif
236     pool = NULL;
237     pool_okay = 0;
238     poolsize=0;
239     poollen=0;
240     unused_blocks=NULL;
241 }
242
243
244 void
245 secmem_dump_stats()
246 {
247     if( disable_secmem )
248         return;
249     fprintf(stderr,
250                 "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
251                 cur_alloced, max_alloced, cur_blocks, max_blocks,
252                 (ulong)poollen, (ulong)poolsize );
253 }
254