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