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