summaryrefslogtreecommitdiffabout
path: root/pwmanager/libcrypt/crypt/secmem.c
authorzautrix <zautrix>2004-10-19 20:16:14 (UTC)
committer zautrix <zautrix>2004-10-19 20:16:14 (UTC)
commiteca49bb06a71980ef61d078904573f25890fc7f2 (patch) (unidiff)
treec5338e3b12430248979a9ac2c1c7e6646ea9ecdf /pwmanager/libcrypt/crypt/secmem.c
parent53cc32b6e7b1f672bf91b2baf2df6c1e8baf3e0a (diff)
downloadkdepimpi-eca49bb06a71980ef61d078904573f25890fc7f2.zip
kdepimpi-eca49bb06a71980ef61d078904573f25890fc7f2.tar.gz
kdepimpi-eca49bb06a71980ef61d078904573f25890fc7f2.tar.bz2
Initial revision
Diffstat (limited to 'pwmanager/libcrypt/crypt/secmem.c') (more/less context) (ignore whitespace changes)
-rw-r--r--pwmanager/libcrypt/crypt/secmem.c653
1 files changed, 653 insertions, 0 deletions
diff --git a/pwmanager/libcrypt/crypt/secmem.c b/pwmanager/libcrypt/crypt/secmem.c
new file mode 100644
index 0000000..163bf20
--- a/dev/null
+++ b/pwmanager/libcrypt/crypt/secmem.c
@@ -0,0 +1,653 @@
1 /* secmem.c -memory allocation from a secure heap
2 * Copyright (C) 1998, 1999, 2000, 2001, 2002,
3 * 2003 Free Software Foundation, Inc.
4 *
5 * This file is part of Libgcrypt.
6 *
7 * Libgcrypt is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser general Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * Libgcrypt is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20 */
21
22#include <config.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <errno.h>
27#include <stdarg.h>
28#include <unistd.h>
29#include <stddef.h>
30
31#if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
32#include <sys/mman.h>
33#include <sys/types.h>
34#include <fcntl.h>
35#ifdef USE_CAPABILITIES
36#include <sys/capability.h>
37#endif
38#endif
39
40#include "ath.h"
41#include "g10lib.h"
42#include "secmem.h"
43
44#if defined (MAP_ANON) && ! defined (MAP_ANONYMOUS)
45#define MAP_ANONYMOUS MAP_ANON
46#endif
47
48#define DEFAULT_POOL_SIZE 16384
49#define DEFAULT_PAGE_SIZE 4096
50
51typedef struct memblock
52{
53 unsigned size; /* Size of the memory available to the
54 user. */
55 int flags; /* See below. */
56 PROPERLY_ALIGNED_TYPE aligned;
57} memblock_t;
58
59/* This flag specifies that the memory block is in use. */
60#define MB_FLAG_ACTIVE 1 << 0
61
62/* The pool of secure memory. */
63static void *pool;
64
65/* Size of POOL in bytes. */
66static size_t pool_size;
67
68/* True, if the memory pool is ready for use. May be checked in an
69 atexit function. */
70static volatile int pool_okay;
71
72/* True, if the memory pool is mmapped. */
73static volatile int pool_is_mmapped;
74
75/* FIXME? */
76static int disable_secmem;
77static int show_warning;
78static int no_warning;
79static int suspend_warning;
80
81/* Stats. */
82static unsigned int cur_alloced, cur_blocks;
83
84/* Lock protecting accesses to the memory pool. */
85static ath_mutex_t secmem_lock;
86
87/* Convenient macros. */
88#define SECMEM_LOCK ath_mutex_lock (&secmem_lock)
89#define SECMEM_UNLOCK ath_mutex_unlock (&secmem_lock)
90
91/* The size of the memblock structure; this does not include the
92 memory that is available to the user. */
93#define BLOCK_HEAD_SIZE \
94 offsetof (memblock_t, aligned)
95
96/* Convert an address into the according memory block structure. */
97#define ADDR_TO_BLOCK(addr) \
98 (memblock_t *) ((char *) addr - BLOCK_HEAD_SIZE)
99
100/* Check wether MB is a valid block. */
101#define BLOCK_VALID(mb) \
102 (((char *) mb - (char *) pool) < pool_size)
103
104/* Update the stats. */
105static void
106stats_update (size_t add, size_t sub)
107{
108 if (add)
109 {
110 cur_alloced += add;
111 cur_blocks++;
112 }
113 if (sub)
114 {
115 cur_alloced -= sub;
116 cur_blocks--;
117 }
118}
119
120/* Return the block following MB or NULL, if MB is the last block. */
121static memblock_t *
122mb_get_next (memblock_t *mb)
123{
124 memblock_t *mb_next;
125
126 mb_next = (memblock_t *) ((char *) mb + BLOCK_HEAD_SIZE + mb->size);
127
128 if (! BLOCK_VALID (mb_next))
129 mb_next = NULL;
130
131 return mb_next;
132}
133
134/* Return the block preceeding MB or NULL, if MB is the first
135 block. */
136static memblock_t *
137mb_get_prev (memblock_t *mb)
138{
139 memblock_t *mb_prev, *mb_next;
140
141 if (mb == pool)
142 mb_prev = NULL;
143 else
144 {
145 mb_prev = (memblock_t *) pool;
146 while (1)
147 {
148 mb_next = mb_get_next (mb_prev);
149 if (mb_next == mb)
150 break;
151 else
152 mb_prev = mb_next;
153 }
154 }
155
156 return mb_prev;
157}
158
159/* If the preceeding block of MB and/or the following block of MB
160 exist and are not active, merge them to form a bigger block. */
161static void
162mb_merge (memblock_t *mb)
163{
164 memblock_t *mb_prev, *mb_next;
165
166 mb_prev = mb_get_prev (mb);
167 mb_next = mb_get_next (mb);
168
169 if (mb_prev && (! (mb_prev->flags & MB_FLAG_ACTIVE)))
170 {
171 mb_prev->size += BLOCK_HEAD_SIZE + mb->size;
172 mb = mb_prev;
173 }
174 if (mb_next && (! (mb_next->flags & MB_FLAG_ACTIVE)))
175 mb->size += BLOCK_HEAD_SIZE + mb_next->size;
176}
177
178/* Return a new block, which can hold SIZE bytes. */
179static memblock_t *
180mb_get_new (memblock_t *block, size_t size)
181{
182 memblock_t *mb, *mb_split;
183
184 for (mb = block; BLOCK_VALID (mb); mb = mb_get_next (mb))
185 if (! (mb->flags & MB_FLAG_ACTIVE) && mb->size >= size)
186 {
187 /* Found a free block. */
188 mb->flags |= MB_FLAG_ACTIVE;
189
190 if (mb->size - size > BLOCK_HEAD_SIZE)
191 {
192 /* Split block. */
193
194 mb_split = (memblock_t *) (((char *) mb) + BLOCK_HEAD_SIZE + size);
195 mb_split->size = mb->size - size - BLOCK_HEAD_SIZE;
196 mb_split->flags = 0;
197
198 mb->size = size;
199
200 mb_merge (mb_split);
201
202 }
203
204 break;
205 }
206
207 if (! BLOCK_VALID (mb))
208 mb = NULL;
209
210 return mb;
211}
212
213/* Print a warning message. */
214static void
215print_warn (void)
216{
217 if (!no_warning)
218 log_info (_("Warning: using insecure memory!\n"));
219}
220
221/* Lock the memory pages into core and drop privileges. */
222static void
223lock_pool (void *p, size_t n)
224{
225#if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
226 int err;
227
228 cap_set_proc (cap_from_text ("cap_ipc_lock+ep"));
229 err = mlock (p, n);
230 if (err && errno)
231 err = errno;
232 cap_set_proc (cap_from_text ("cap_ipc_lock+p"));
233
234 if (err)
235 {
236 if (errno != EPERM
237 #ifdef EAGAIN/* OpenBSD returns this */
238 && errno != EAGAIN
239#endif
240 #ifdef ENOSYS/* Some SCOs return this (function not implemented) */
241 && errno != ENOSYS
242#endif
243#ifdef ENOMEM /* Linux might return this. */
244 && errno != ENOMEM
245#endif
246 )
247 log_error ("can't lock memory: %s\n", strerror (err));
248 show_warning = 1;
249 }
250
251#elif defined(HAVE_MLOCK)
252 uid_t uid;
253 int err;
254
255 uid = getuid ();
256
257#ifdef HAVE_BROKEN_MLOCK
258 /* Under HP/UX mlock segfaults if called by non-root. Note, we have
259 noch checked whether mlock does really work under AIX where we
260 also detected a broken nlock. Note further, that using plock ()
261 is not a good idea under AIX. */
262 if (uid)
263 {
264 errno = EPERM;
265 err = errno;
266 }
267 else
268 {
269 err = mlock (p, n);
270 if (err && errno)
271 err = errno;
272 }
273#else /* !HAVE_BROKEN_MLOCK */
274 err = mlock (p, n);
275 if (err && errno)
276 err = errno;
277#endif /* !HAVE_BROKEN_MLOCK */
278
279 if (uid && ! geteuid ())
280 {
281 /* check that we really dropped the privs.
282 * Note: setuid(0) should always fail */
283 if (setuid (uid) || getuid () != geteuid () || !setuid (0))
284 log_fatal ("failed to reset uid: %s\n", strerror (errno));
285 }
286
287 if (err)
288 {
289 if (errno != EPERM
290 #ifdef EAGAIN/* OpenBSD returns this. */
291 && errno != EAGAIN
292#endif
293 #ifdef ENOSYS/* Some SCOs return this (function not implemented). */
294 && errno != ENOSYS
295#endif
296#ifdef ENOMEM /* Linux might return this. */
297 && errno != ENOMEM
298#endif
299 )
300 log_error ("can't lock memory: %s\n", strerror (err));
301 show_warning = 1;
302 }
303
304#elif defined ( __QNX__ )
305 /* QNX does not page at all, so the whole secure memory stuff does
306 * not make much sense. However it is still of use because it
307 * wipes out the memory on a free().
308 * Therefore it is sufficient to suppress the warning
309 */
310#elif defined (HAVE_DOSISH_SYSTEM) || defined (__CYGWIN__)
311 /* It does not make sense to print such a warning, given the fact that
312 * this whole Windows !@#$% and their user base are inherently insecure
313 */
314#elif defined (__riscos__)
315 /* no virtual memory on RISC OS, so no pages are swapped to disc,
316 * besides we don't have mmap, so we don't use it! ;-)
317 * But don't complain, as explained above.
318 */
319#else
320 log_info ("Please note that you don't have secure memory on this system\n");
321#endif
322}
323
324/* Initialize POOL. */
325static void
326init_pool (size_t n)
327{
328 size_t pgsize;
329 memblock_t *mb;
330
331 pool_size = n;
332
333 if (disable_secmem)
334 log_bug ("secure memory is disabled");
335
336#ifdef HAVE_GETPAGESIZE
337 pgsize = getpagesize ();
338#else
339 pgsize = DEFAULT_PAGE_SIZE;
340#endif
341
342#if HAVE_MMAP
343 pool_size = (pool_size + pgsize - 1) & ~(pgsize - 1);
344#ifdef MAP_ANONYMOUS
345 pool = mmap (0, pool_size, PROT_READ | PROT_WRITE,
346 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
347#else /* map /dev/zero instead */
348 {
349 int fd;
350
351 fd = open ("/dev/zero", O_RDWR);
352 if (fd == -1)
353 {
354 log_error ("can't open /dev/zero: %s\n", strerror (errno));
355 pool = (void *) -1;
356 }
357 else
358 {
359 pool = mmap (0, pool_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
360 }
361 }
362#endif
363 if (pool == (void *) -1)
364 log_info ("can't mmap pool of %u bytes: %s - using malloc\n",
365 (unsigned) pool_size, strerror (errno));
366 else
367 {
368 pool_is_mmapped = 1;
369 pool_okay = 1;
370 }
371
372#endif
373 if (!pool_okay)
374 {
375 pool = malloc (pool_size);
376 if (!pool)
377 log_fatal ("can't allocate memory pool of %u bytes\n",
378 (unsigned) pool_size);
379 else
380 pool_okay = 1;
381 }
382
383 /* Initialize first memory block. */
384 mb = (memblock_t *) pool;
385 mb->size = pool_size;
386 mb->flags = 0;
387}
388
389void
390_gcry_secmem_set_flags (unsigned flags)
391{
392 int was_susp;
393
394 SECMEM_LOCK;
395
396 was_susp = suspend_warning;
397 no_warning = flags & GCRY_SECMEM_FLAG_NO_WARNING;
398 suspend_warning = flags & GCRY_SECMEM_FLAG_SUSPEND_WARNING;
399
400 /* and now issue the warning if it is not longer suspended */
401 if (was_susp && !suspend_warning && show_warning)
402 {
403 show_warning = 0;
404 print_warn ();
405 }
406
407 SECMEM_UNLOCK;
408}
409
410unsigned
411_gcry_secmem_get_flags (void)
412{
413 unsigned flags;
414
415 SECMEM_LOCK;
416
417 flags = no_warning ? GCRY_SECMEM_FLAG_NO_WARNING : 0;
418 flags |= suspend_warning ? GCRY_SECMEM_FLAG_SUSPEND_WARNING : 0;
419
420 SECMEM_UNLOCK;
421
422 return flags;
423}
424
425/* Initialize the secure memory system. If running with the necessary
426 privileges, the secure memory pool will be locked into the core in
427 order to prevent page-outs of the data. Furthermore allocated
428 secure memory will be wiped out when released. */
429void
430_gcry_secmem_init (size_t n)
431{
432 SECMEM_LOCK;
433
434 if (!n)
435 {
436#ifdef USE_CAPABILITIES
437 /* drop all capabilities */
438 cap_set_proc (cap_from_text ("all-eip"));
439
440#elif !defined(HAVE_DOSISH_SYSTEM)
441 uid_t uid;
442
443 disable_secmem = 1;
444 uid = getuid ();
445 if (uid != geteuid ())
446 {
447 if (setuid (uid) || getuid () != geteuid () || !setuid (0))
448 log_fatal ("failed to drop setuid\n");
449 }
450#endif
451 }
452 else
453 {
454 if (n < DEFAULT_POOL_SIZE)
455 n = DEFAULT_POOL_SIZE;
456 if (! pool_okay)
457 {
458 init_pool (n);
459 if (! geteuid ())
460 lock_pool (pool, n);
461 else if (!no_warning)
462 log_info ("Secure memory is not locked into core\n");
463 }
464 else
465 log_error ("Oops, secure memory pool already initialized\n");
466 }
467
468 SECMEM_UNLOCK;
469}
470
471
472static void *
473_gcry_secmem_malloc_internal (size_t size)
474{
475 memblock_t *mb;
476
477 if (!pool_okay)
478 {
479 log_info (_
480 ("operation is not possible without initialized secure memory\n"));
481 exit (2);
482 }
483 if (show_warning && !suspend_warning)
484 {
485 show_warning = 0;
486 print_warn ();
487 }
488
489 /* Blocks are always a multiple of 32. */
490 size = ((size + 31) / 32) * 32;
491
492 mb = mb_get_new ((memblock_t *) pool, size);
493 if (mb)
494 stats_update (size, 0);
495
496 return mb ? &mb->aligned.c : NULL;
497}
498
499void *
500_gcry_secmem_malloc (size_t size)
501{
502 void *p;
503
504 SECMEM_LOCK;
505 p = _gcry_secmem_malloc_internal (size);
506 SECMEM_UNLOCK;
507
508 return p;
509}
510
511static void
512_gcry_secmem_free_internal (void *a)
513{
514 memblock_t *mb;
515 int size;
516
517 if (!a)
518 return;
519
520 mb = ADDR_TO_BLOCK (a);
521 size = mb->size;
522
523 /* This does not make much sense: probably this memory is held in the
524 * cache. We do it anyway: */
525#define MB_WIPE_OUT(byte) \
526 memset ((memblock_t *) ((char *) mb + BLOCK_HEAD_SIZE), (byte), size);
527
528 MB_WIPE_OUT (0xff);
529 MB_WIPE_OUT (0xaa);
530 MB_WIPE_OUT (0x55);
531 MB_WIPE_OUT (0x00);
532
533 stats_update (0, size);
534
535 mb->flags &= ~MB_FLAG_ACTIVE;
536
537 /* Update stats. */
538
539 mb_merge (mb);
540}
541
542/* Wipe out and release memory. */
543void
544_gcry_secmem_free (void *a)
545{
546 SECMEM_LOCK;
547 _gcry_secmem_free_internal (a);
548 SECMEM_UNLOCK;
549}
550
551/* Realloc memory. */
552void *
553_gcry_secmem_realloc (void *p, size_t newsize)
554{
555 memblock_t *mb;
556 size_t size;
557 void *a;
558
559 SECMEM_LOCK;
560
561 mb = (memblock_t *) ((char *) p - ((size_t) &((memblock_t *) 0)->aligned.c));
562 size = mb->size;
563 if (newsize < size)
564 {
565 /* It is easier to not shrink the memory. */
566 a = p;
567 }
568 else
569 {
570 a = _gcry_secmem_malloc_internal (newsize);
571 if (a)
572 {
573 memcpy (a, p, size);
574 memset ((char *) a + size, 0, newsize - size);
575 _gcry_secmem_free_internal (p);
576 }
577 }
578
579 SECMEM_UNLOCK;
580
581 return a;
582}
583
584int
585_gcry_private_is_secure (const void *p)
586{
587 int ret = 0;
588
589 SECMEM_LOCK;
590
591 if (pool_okay && BLOCK_VALID (ADDR_TO_BLOCK (p)))
592 ret = 1;
593
594 SECMEM_UNLOCK;
595
596 return ret;
597}
598
599
600/****************
601 * Warning: This code might be called by an interrupt handler
602 * and frankly, there should really be such a handler,
603 * to make sure that the memory is wiped out.
604 * We hope that the OS wipes out mlocked memory after
605 * receiving a SIGKILL - it really should do so, otherwise
606 * there is no chance to get the secure memory cleaned.
607 */
608void
609_gcry_secmem_term ()
610{
611 if (!pool_okay)
612 return;
613
614 wipememory2 (pool, 0xff, pool_size);
615 wipememory2 (pool, 0xaa, pool_size);
616 wipememory2 (pool, 0x55, pool_size);
617 wipememory2 (pool, 0x00, pool_size);
618#if HAVE_MMAP
619 if (pool_is_mmapped)
620 munmap (pool, pool_size);
621#endif
622 pool = NULL;
623 pool_okay = 0;
624 pool_size = 0;
625}
626
627
628void
629_gcry_secmem_dump_stats ()
630{
631#if 1
632 SECMEM_LOCK;
633
634 if (pool_okay)
635 log_info ("secmem usage: %u/%lu bytes in %u blocks\n",
636 cur_alloced, (unsigned long)pool_size, cur_blocks);
637 SECMEM_UNLOCK;
638#else
639 memblock_t *mb;
640 int i;
641
642 SECMEM_LOCK;
643
644 for (i = 0, mb = (memblock_t *) pool;
645 BLOCK_VALID (mb);
646 mb = mb_get_next (mb), i++)
647 log_info ("SECMEM: [%s] block: %i; size: %i\n",
648 (mb->flags & MB_FLAG_ACTIVE) ? "used" : "free",
649 i,
650 mb->size);
651 SECMEM_UNLOCK;
652#endif
653}