See ChangeLog: Mon May 31 19:41:10 CEST 1999 Werner Koch
[gnupg.git] / util / secmem.c
index ab4503d..f48b0ed 100644 (file)
@@ -1,14 +1,14 @@
 /* secmem.c  - memory allocation from a secure heap
- *     Copyright (C) 1998 Free Software Foundation, Inc.
+ *     Copyright (C) 1998,1999 Free Software Foundation, Inc.
  *
- * This file is part of GNUPG.
+ * This file is part of GnuPG.
  *
- * GNUPG is free software; you can redistribute it and/or modify
+ * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- * GNUPG is distributed in the hope that it will be useful,
+ * GnuPG is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
 #include <string.h>
 #include <errno.h>
 #include <stdarg.h>
+#include <unistd.h>
 #if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
-  #include <unistd.h>
   #include <sys/mman.h>
   #include <sys/types.h>
+  #include <fcntl.h>
 #endif
 
 #include "types.h"
@@ -35,6 +36,9 @@
 #include "util.h"
 #include "i18n.h"
 
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+  #define MAP_ANONYMOUS MAP_ANON
+#endif
 
 #define DEFAULT_POOLSIZE 8196
 
@@ -43,8 +47,7 @@ struct memblock_struct {
     unsigned size;
     union {
        MEMBLOCK *next;
-       long align_dummy;
-       char d[1];
+       PROPERLY_ALIGNED_TYPE aligned;
     } u;
 };
 
@@ -63,6 +66,16 @@ static unsigned cur_blocks;
 static int disable_secmem;
 static int show_warning;
 static int no_warning;
+static int suspend_warning;
+
+
+static void
+print_warn(void)
+{
+    if( !no_warning )
+       log_info(_("Warning: using insecure memory!\n"));
+}
+
 
 static void
 lock_pool( void *p, size_t n )
@@ -71,13 +84,26 @@ lock_pool( void *p, size_t n )
     uid_t uid;
     int err;
 
-    err = -1; mlock( p, n );
+    uid = getuid();
+
+  #ifdef HAVE_BROKEN_MLOCK
+    if( uid ) {
+       errno = EPERM;
+       err = errno;
+    }
+    else {
+       err = mlock( p, n );
+       if( err && errno )
+           err = errno;
+    }
+  #else
+    err = mlock( p, n );
     if( err && errno )
        err = errno;
+  #endif
 
-    uid = getuid();
     if( uid && !geteuid() ) {
-       if( setuid( uid ) )
+       if( setuid( uid ) || getuid() != geteuid()  )
            log_fatal("failed to reset uid: %s\n", strerror(errno));
     }
 
@@ -100,17 +126,40 @@ lock_pool( void *p, size_t n )
 static void
 init_pool( size_t n)
 {
+    size_t pgsize;
+
     poolsize = n;
 
     if( disable_secmem )
        log_bug("secure memory is disabled");
 
-  #if HAVE_MMAP && defined(MAP_ANONYMOUS)
-    poolsize = (poolsize + 4095) & ~4095;
-    pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
-                             MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+  #ifdef HAVE_GETPAGESIZE
+    pgsize = getpagesize();
+  #else
+    pgsize = 4096;
+  #endif
+
+  #if HAVE_MMAP
+    poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
+    #ifdef MAP_ANONYMOUS
+       pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
+                                MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+    #else /* map /dev/zero instead */
+    {  int fd;
+
+       fd = open("/dev/zero", O_RDWR);
+       if( fd == -1 ) {
+           log_error("can't open /dev/zero: %s\n", strerror(errno) );
+           pool = (void*)-1;
+       }
+       else {
+           pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
+                                     MAP_PRIVATE, fd, 0);
+       }
+    }
+    #endif
     if( pool == (void*)-1 )
-       log_error("can't mmap pool of %u bytes: %s - using malloc\n",
+       log_info("can't mmap pool of %u bytes: %s - using malloc\n",
                            (unsigned)poolsize, strerror(errno));
     else {
        pool_is_mmapped = 1;
@@ -141,20 +190,43 @@ compress_pool(void)
 void
 secmem_set_flags( unsigned flags )
 {
+    int was_susp = suspend_warning;
+
     no_warning = flags & 1;
+    suspend_warning = flags & 2;
+
+    /* and now issue the warning if it is not longer suspended */
+    if( was_susp && !suspend_warning && show_warning ) {
+       show_warning = 0;
+       print_warn();
+    }
 }
 
 unsigned
 secmem_get_flags(void)
 {
-    return no_warning ? 1:0;
+    unsigned flags;
+
+    flags  = no_warning      ? 1:0;
+    flags |= suspend_warning ? 2:0;
+    return flags;
 }
 
 void
 secmem_init( size_t n )
 {
-    if( !n )
+    if( !n ) {
+      #ifndef HAVE_DOSISH_SYSTEM
+       uid_t uid;
+
        disable_secmem=1;
+       uid = getuid();
+       if( uid != geteuid() ) {
+           if( setuid( uid ) || getuid() != geteuid() )
+               log_fatal("failed to drop setuid\n" );
+       }
+      #endif
+    }
     else {
        if( n < DEFAULT_POOLSIZE )
            n = DEFAULT_POOLSIZE;
@@ -172,12 +244,15 @@ secmem_malloc( size_t size )
     MEMBLOCK *mb, *mb2;
     int compressed=0;
 
-    if( !pool_okay )
-       log_bug("secmem not initialized\n");
-    if( show_warning ) {
+    if( !pool_okay ) {
+       log_info(
+        _("operation is not possible without initialized secure memory\n"));
+       log_info(_("(you may have used the wrong program for this task)\n"));
+       exit(2);
+    }
+    if( show_warning && !suspend_warning ) {
        show_warning = 0;
-       if( !no_warning )
-           log_info(_("Warning: using insecure memory!\n"));
+       print_warn();
     }
 
     /* blocks are always a multiple of 32 */
@@ -196,7 +271,7 @@ secmem_malloc( size_t size )
        }
     /* allocate a new block */
     if( (poollen + size <= poolsize) ) {
-       mb = pool + poollen;
+       mb = (void*)((char*)pool + poollen);
        poollen += size;
        mb->size = size;
     }
@@ -215,7 +290,7 @@ secmem_malloc( size_t size )
        max_alloced = cur_alloced;
     if( cur_blocks > max_blocks )
        max_blocks = cur_blocks;
-    return &mb->u.d;
+    return &mb->u.aligned.c;
 }
 
 
@@ -226,13 +301,13 @@ secmem_realloc( void *p, size_t newsize )
     size_t size;
     void *a;
 
-    mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.d));
+    mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
     size = mb->size;
     if( newsize < size )
        return p; /* it is easier not to shrink the memory */
     a = secmem_malloc( newsize );
     memcpy(a, p, size);
-    memset(a+size, 0, newsize-size);
+    memset((char*)a+size, 0, newsize-size);
     secmem_free(p);
     return a;
 }
@@ -247,8 +322,10 @@ secmem_free( void *a )
     if( !a )
        return;
 
-    mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.d));
+    mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
     size = mb->size;
+    /* This does not make much sense: probably this memory is held in the
+     * cache. We do it anyway: */
     memset(mb, 0xff, size );
     memset(mb, 0xaa, size );
     memset(mb, 0x55, size );
@@ -263,7 +340,7 @@ secmem_free( void *a )
 int
 m_is_secure( const void *p )
 {
-    return p >= pool && p < (pool+poolsize);
+    return p >= pool && p < (void*)((char*)pool+poolsize);
 }
 
 void