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