chiark / gitweb /
Add some more vectors, and a whinge about how Skipjack test vectors are.
[catacomb] / lmem.c
1 /* -*-c-*-
2  *
3  * $Id: lmem.c,v 1.3 2000/07/29 21:58:15 mdw Exp $
4  *
5  * Locked memory allocation (Unix-specific)
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Catacomb.
13  *
14  * Catacomb is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * Catacomb is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with Catacomb; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: lmem.c,v $
33  * Revision 1.3  2000/07/29 21:58:15  mdw
34  * (l_destroy): New function for destroying locked memory blocks.
35  *
36  * Revision 1.2  2000/06/17 11:29:20  mdw
37  * Add arena support.
38  *
39  * Revision 1.1  1999/12/22 16:02:52  mdw
40  * Interface to allocating `locked' memory (which isn't paged out).
41  *
42  */
43
44 /*----- Header files ------------------------------------------------------*/
45
46 #include "config.h"
47
48 #include <assert.h>
49 #include <errno.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53
54 #include <sys/types.h>
55 #include <unistd.h>
56
57 #ifdef HAVE_MLOCK
58 #  include <sys/mman.h>
59 #endif
60
61 #include <mLib/arena.h>
62 #include <mLib/dstr.h>
63 #include <mLib/sub.h>
64
65 #include "lmem.h"
66
67 /*----- Arena operations --------------------------------------------------*/
68
69 static void *aalloc(arena *a, size_t sz) { return l_alloc((lmem *)a, sz); }
70 static void afree(arena *a, void *p) { l_free((lmem *)a, p); }
71 static void apurge(arena *a) { l_purge((lmem *)a); }
72
73 static arena_ops l_ops = { aalloc, arena_fakerealloc, afree, apurge };
74
75 /*----- Main code ---------------------------------------------------------*/
76
77 /* --- @l_init@ --- *
78  *
79  * Arguments:   @lmem *lm@ = pointer to locked memory descriptor
80  *              @size_t sz@ = size of locked memory area requested
81  *
82  * Returns:     Zero if everything is fine, @+1@ if some insecure memory was
83  *              allocated, and @-1@ if everything went horribly wrong.
84  *
85  * Use:         Initializes the locked memory manager.  This function is safe
86  *              to call in a privileged program; privileges should usually be
87  *              dropped after allocating the locked memory block.
88  *
89  *              You must call @sub_init@ before allocating locked memory
90  *              buffers.
91  */
92
93 int l_init(lmem *lm, size_t sz)
94 {
95   char *p;
96   int rc = 0;
97   l_node *l;
98
99   /* --- Preliminaries --- */
100
101   lm->a.ops = &l_ops;
102   lm->err = 0;
103   lm->f = 0;
104
105   /* --- Try making a secure locked passphrase buffer --- *
106    *
107    * Drop privileges before emitting diagnostic messages.
108    */
109
110 #ifdef HAVE_MLOCK
111
112   /* --- Memory-map a page from somewhere --- */
113
114 #  ifdef MAP_ANON
115   p = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
116 #  else
117   {
118     int fd;
119     if ((fd = open("/dev/zero", O_RDWR)) >= 0) {
120       p = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
121       close(fd);
122     }
123   }
124 #  endif
125
126   /* --- Lock the page in memory --- *
127    *
128    * Why does @mmap@ return such a stupid result if it fails?
129    */
130
131   if (p == 0 || p == MAP_FAILED) {
132     lm->emsg = "couldn't map locked memory area: %s";
133     lm->err = errno;
134     p = 0;
135   } else if (mlock(p, sz)) {
136     lm->emsg = "error locking memory area: %s";
137     lm->err = errno;
138     munmap(p, sz);
139     p = 0;
140   } else
141     lm->f |= LF_LOCKED;
142
143 #endif
144
145   /* --- Make a standard passphrase buffer --- */
146
147 #ifdef HAVE_MLOCK
148   if (!p)
149 #else
150   ll->err = 0;
151   ll->emsg = "locked memory not available on this system";
152 #endif
153   {
154     if ((p = malloc(sz)) == 0) {
155       lm->emsg = "not enough standard memory!";
156       lm->err = ENOMEM;
157       return (-1);
158     }
159     rc = +1;
160   }
161
162   /* --- Initialize the buffer --- */
163
164   lm->sz = lm->free = sz;
165   lm->p = p;
166
167   /* --- Initialize the free list --- */
168
169   l = CREATE(l_node);
170   l->next = 0;
171   l->p = p;
172   l->sz = sz;
173   l->f = 0;
174   lm->l = l;
175
176   /* --- Done --- */
177
178   return (rc);
179 }
180
181 /* --- @l_alloc@ --- *
182  *
183  * Arguments:   @lmem *lm@ = pointer to locked memory descriptor
184  *              @size_t sz@ = size requested
185  *
186  * Returns:     Pointer to allocated memory.
187  *
188  * Use:         Allocates @sz@ bytes of locked memory.
189  */
190
191 void *l_alloc(lmem *lm, size_t sz)
192 {
193   l_node *l;
194
195   sz = (sz + 3u) & ~3u;
196   for (l = lm->l; l; l = l->next) {
197     if (l->f & LF_ALLOC)
198       continue;
199     if (l->sz < sz)
200       continue;
201     l->f |= LF_ALLOC;
202     if (l->sz > sz) {
203       l_node *n = CREATE(l_node);
204       n->next = l->next;
205       n->p = l->p + sz;
206       n->sz = l->sz - sz;
207       l->sz = sz;
208       n->f = 0;
209       l->next = n;
210     }
211     assert(((void)"Locked buffer space has vanished", lm->free >= sz));
212     lm->free -= sz;
213     return (l->p);
214   }
215   return (0);
216 }
217
218 /* --- @l_free@ --- *
219  *
220  * Arguments:   @lmem *lm@ = pointer to locked memory descriptor
221  *              @void *p@ = pointer to block
222  *
223  * Returns:     ---
224  *
225  * Use:         Releases a block of locked memory.
226  */
227
228 void l_free(lmem *lm, void *p)
229 {
230   l_node *l;
231   l_node *ll = 0;
232
233   for (l = lm->l; l; l = l->next) {
234     size_t sz;
235
236     /* --- If this isn't the block, skip it --- */
237
238     if (l->p != p) {
239       ll = l;
240       continue;
241     }
242     assert(((void)"Block is already free", l->f & LF_ALLOC));
243
244     /* --- Coalesce with adjacent free blocks --- */
245
246     l->f &= ~LF_ALLOC;
247     sz = l->sz;
248     memset(p, 0, sz);
249
250     if (ll && !(ll->f & LF_ALLOC)) {
251       assert(((void)"Previous block doesn't fit", ll->p + ll->sz == p));
252       ll->sz += sz;
253       ll->next = l->next;
254       DESTROY(l);
255       l = ll;
256     }
257
258     ll = l->next;
259     if (ll && !(ll->f & LF_ALLOC)) {
260       assert(((void)"Next block doesn't fit", ll->p == l->p + l->sz));
261       l->sz += ll->sz;
262       l->next = ll->next;
263       DESTROY(ll);
264     }
265
266     lm->free += sz;
267     assert(((void)"Free lunch", lm->free <= lm->sz));
268     return;
269   }
270   assert(((void)"Not a locked block", 0));
271 }
272
273 /* --- @l_purge@ --- *
274  *
275  * Arguments:   @lmem *lm@ = pointer to locked memory descriptor
276  *
277  * Returns:     ---
278  *
279  * Use:         Purges all the free blocks in the buffer, and clears all of
280  *              the locked memory.  Memory is not freed back to the system.
281  */
282
283 void l_purge(lmem *lm)
284 {
285   l_node *l;
286
287   l = lm->l;
288   while (l) {
289     l_node *ll = l->next;
290     DESTROY(l);
291     l = ll;
292   }
293   memset(lm->p, 0, lm->sz);
294   l = CREATE(l_node);
295   l->next = 0;
296   l->p = lm->p;
297   l->sz = lm->sz;
298   l->f = 0;
299   lm->l = l;
300   lm->free = l->sz;
301 }
302
303 /* --- @l_destroy@ --- *
304  *
305  * Arguments:   @lmem *lm@ = pointer to locked memory descriptor
306  *
307  * Returns:     ---
308  *
309  * Use:         Disposes of a locked memory arena permanently.
310  */
311
312 void l_destroy(lmem *lm)
313 {
314   l_node *l;
315
316   l = lm->l;
317   while (l) {
318     l_node *ll = l->next;
319     DESTROY(l);
320     l = ll;
321   }
322   memset(lm->p, 0, lm->sz);
323
324   if (lm->f & LF_LOCKED)
325     munmap(lm->p, lm->sz);
326   else
327     free(lm->p);
328 }
329
330 /* --- @l_report@ --- *
331  *
332  * Arguments:   @lmem *lm@ = pointer to locked memory descriptor
333  *              @dstr *d@ = string to write the error message on
334  *
335  * Returns:     Zero if the buffer is fine, @+1@ if there was a problem
336  *              getting locked memory but insecure stuff could be allocated,
337  *              and @-1@ if not even insecure memory could be found.
338  *
339  * Use:         Returns a user-digestable explanation for the state of a
340  *              locked memory buffer.  If the return code is zero, no message
341  *              is emitted to the string @d@.
342  */
343
344 int l_report(lmem *lm, dstr *d)
345 {
346   int rc;
347   if (lm->err)
348     dstr_putf(d, lm->emsg, strerror(lm->err));
349   if (!lm->p)
350     rc = -1;
351   else if (lm->err)
352     rc = +1;
353   else
354     rc = 0;
355   return (rc);
356 }
357
358 /*----- That's all, folks -------------------------------------------------*/