Obsolete option --no-sig-create-check.
[gnupg.git] / util / secmem.c
index 30396f3..553175b 100644 (file)
@@ -1,11 +1,12 @@
 /* secmem.c  - memory allocation from a secure heap
- *     Copyright (C) 1998 Free Software Foundation, Inc.
+ *     Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ *                    2007 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * 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
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,8 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdarg.h>
 #include <unistd.h>
 #if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
-  #include <sys/mman.h>
-  #include <sys/types.h>
-  #include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#ifdef USE_CAPABILITIES
+#include <sys/capability.h>
+#endif
+#ifdef HAVE_PLOCK
+#include <sys/lock.h>
+#endif
 #endif
 
 #include "types.h"
 #include "util.h"
 #include "i18n.h"
 
+/* MinGW doesn't seem to prototype getpagesize, though it does have
+   it. */
+#if !HAVE_DECL_GETPAGESIZE
+int getpagesize(void);
+#endif
+
 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
-  #define MAP_ANONYMOUS MAP_ANON
+#define MAP_ANONYMOUS MAP_ANON
 #endif
+/* It seems that Slackware 7.1 does not know about EPERM */
+#if !defined(EPERM) && defined(ENOMEM)
+#define EPERM  ENOMEM
+#endif
+
 
-#define DEFAULT_POOLSIZE 8196
+#define DEFAULT_POOLSIZE 16384
 
 typedef struct memblock_struct MEMBLOCK;
 struct memblock_struct {
     unsigned size;
     union {
        MEMBLOCK *next;
-       long align_dummy;
-       char d[1];
+       PROPERLY_ALIGNED_TYPE aligned;
     } u;
 };
 
 
 
 static void  *pool;
-static int   pool_okay;
-static int   pool_is_mmapped;
+static volatile int pool_okay; /* may be checked in an atexit function */
+#ifdef HAVE_MMAP
+static volatile int pool_is_mmapped;
+#endif
 static size_t poolsize; /* allocated length */
 static size_t poollen; /* used length */
 static MEMBLOCK *unused_blocks;
@@ -71,23 +89,71 @@ static int suspend_warning;
 
 
 static void
-print_warn()
+print_warn(void)
 {
-    if( !no_warning )
-       log_info(_("Warning: using insecure memory!\n"));
+  if (!no_warning)
+    {
+      log_info(_("WARNING: using insecure memory!\n"));
+      log_info(_("please see http://www.gnupg.org/documentation/faqs.html"
+                " for more information\n"));
+    }
 }
 
 
 static void
 lock_pool( void *p, size_t n )
 {
-  #ifdef HAVE_MLOCK
+#if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
+    int err;
+
+    cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
+    err = mlock( p, n );
+    if( err && errno )
+       err = errno;
+    cap_set_proc( cap_from_text("cap_ipc_lock+p") );
+
+    if( err ) {
+       if( errno != EPERM
+#ifdef EAGAIN  /* OpenBSD returns this */
+           && errno != EAGAIN
+#endif
+#ifdef ENOSYS  /* Some SCOs return this (function not implemented) */
+           && errno != ENOSYS
+#endif
+#ifdef ENOMEM  /* Linux can return this */
+            && errno != ENOMEM
+#endif
+         )
+           log_error("can't lock memory: %s\n", strerror(err));
+       show_warning = 1;
+    }
+
+#elif defined(HAVE_MLOCK)
     uid_t uid;
     int err;
 
     uid = getuid();
 
-  #ifdef HAVE_BROKEN_MLOCK
+#ifdef HAVE_BROKEN_MLOCK
+    /* ick. but at least we get secured memory. about to lock
+       entire data segment. */
+#ifdef HAVE_PLOCK
+# ifdef _AIX
+    /* The configure for AIX returns broken mlock but the plock has
+       the strange requirement to somehow set the stack limit first.
+       The problem might turn out in indeterministic program behaviour
+       and hanging processes which can somehow be solved when enough
+       processes are clogging up the memory.  To get this problem out
+       of the way we simply don't try to lock the memory at all.
+       */    
+    errno = EPERM;
+    err = errno;
+# else /* !_AIX */
+    err = plock( DATLOCK );
+    if( err && errno )
+        err = errno;
+# endif /*_AIX*/
+#else /*!HAVE_PLOCK*/
     if( uid ) {
        errno = EPERM;
        err = errno;
@@ -97,36 +163,65 @@ lock_pool( void *p, size_t n )
        if( err && errno )
            err = errno;
     }
-  #else
+#endif /*!HAVE_PLOCK*/
+#else
     err = mlock( p, n );
     if( err && errno )
        err = errno;
-  #endif
+#endif
 
     if( uid && !geteuid() ) {
-       if( setuid( uid ) )
+       /* check that we really dropped the privs.
+        * Note: setuid(0) should always fail */
+       if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
            log_fatal("failed to reset uid: %s\n", strerror(errno));
     }
 
     if( err ) {
        if( errno != EPERM
-         #ifdef EAGAIN  /* OpenBSD returns this */
+#ifdef EAGAIN  /* OpenBSD returns this */
            && errno != EAGAIN
-         #endif
+#endif
+#ifdef ENOSYS  /* Some SCOs return this (function not implemented) */
+           && errno != ENOSYS
+#endif
+#ifdef ENOMEM  /* Linux can return this */
+            && errno != ENOMEM
+#endif
          )
-           log_error("canĀ“t lock memory: %s\n", strerror(err));
+#ifdef __VMS
+           log_warning ("can't lock memory: %s\n", strerror(err));
+#else
+           log_error ("can't lock memory: %s\n", strerror(err));
+#endif
        show_warning = 1;
     }
 
-  #else
+#elif defined ( __QNX__ )
+    /* QNX does not page at all, so the whole secure memory stuff does
+     * not make much sense.  However it is still of use because it
+     * wipes out the memory on a free().
+     * Therefore it is sufficient to suppress the warning
+     */
+#elif defined (HAVE_DOSISH_SYSTEM) || defined (__CYGWIN__)
+    /* It does not make sense to print such a warning, given the fact that 
+     * this whole Windows !@#$% and their user base are inherently insecure
+     */
+#elif defined (__riscos__)
+    /* no virtual memory on RISC OS, so no pages are swapped to disc,
+     * besides we don't have mmap, so we don't use it! ;-)
+     * But don't complain, as explained above.
+     */
+#else
     log_info("Please note that you don't have secure memory on this system\n");
-  #endif
+#endif
 }
 
 
 static void
 init_pool( size_t n)
 {
+    long int pgsize_val;
     size_t pgsize;
 
     poolsize = n;
@@ -134,18 +229,22 @@ init_pool( size_t n)
     if( disable_secmem )
        log_bug("secure memory is disabled");
 
-  #ifdef HAVE_GETPAGESIZE
-    pgsize = getpagesize();
-  #else
-    pgsize = 4096;
-  #endif
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+    pgsize_val = sysconf (_SC_PAGESIZE);
+#elif defined(HAVE_GETPAGESIZE)
+    pgsize_val = getpagesize ();
+#else
+    pgsize_val = -1;
+#endif
+    pgsize = (pgsize_val != -1 && pgsize_val > 0)? pgsize_val : 4096;
+
 
-  #if HAVE_MMAP
+#ifdef HAVE_MMAP
     poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
-    #ifdef MAP_ANONYMOUS
+#ifdef MAP_ANONYMOUS
        pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
                                 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
-    #else /* map /dev/zero instead */
+#else /* map /dev/zero instead */
     {  int fd;
 
        fd = open("/dev/zero", O_RDWR);
@@ -156,9 +255,10 @@ init_pool( size_t n)
        else {
            pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
                                      MAP_PRIVATE, fd, 0);
+           close (fd);
        }
     }
-    #endif
+#endif
     if( pool == (void*)-1 )
        log_info("can't mmap pool of %u bytes: %s - using malloc\n",
                            (unsigned)poolsize, strerror(errno));
@@ -167,7 +267,7 @@ init_pool( size_t n)
        pool_okay = 1;
     }
 
-  #endif
+#endif
     if( !pool_okay ) {
        pool = malloc( poolsize );
        if( !pool )
@@ -185,7 +285,7 @@ init_pool( size_t n)
 static void
 compress_pool(void)
 {
-
+    /* fixme: we really should do this */
 }
 
 void
@@ -213,20 +313,27 @@ secmem_get_flags(void)
     return flags;
 }
 
-void
+/* Returns 1 if memory was locked, 0 if not. */
+int
 secmem_init( size_t n )
 {
     if( !n ) {
-      #ifndef __MINGW32__
+#ifndef __riscos__
+#ifdef USE_CAPABILITIES
+       /* drop all capabilities */
+       cap_set_proc( cap_from_text("all-eip") );
+
+#elif !defined(HAVE_DOSISH_SYSTEM)
        uid_t uid;
 
        disable_secmem=1;
        uid = getuid();
        if( uid != geteuid() ) {
-           if( setuid( uid ) )
+           if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
                log_fatal("failed to drop setuid\n" );
        }
-      #endif
+#endif
+#endif /* !__riscos__ */
     }
     else {
        if( n < DEFAULT_POOLSIZE )
@@ -236,6 +343,8 @@ secmem_init( size_t n )
        else
            log_error("Oops, secure memory pool already initialized\n");
     }
+
+    return !show_warning;
 }
 
 
@@ -245,14 +354,21 @@ secmem_malloc( size_t size )
     MEMBLOCK *mb, *mb2;
     int compressed=0;
 
-    if( !pool_okay )
-       log_bug("secmem not initialized\n");
+    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;
        print_warn();
     }
 
-    /* blocks are always a multiple of 32 */
+    /* Blocks are always a multiple of 32.  Note that we allocate an
+       extra of the size of an entire MEMBLOCK.  This is required
+       becuase we do not only need the SIZE info but also extra space
+       to chain up unused memory blocks.  */
     size += sizeof(MEMBLOCK);
     size = ((size + 31) / 32) * 32;
 
@@ -287,25 +403,32 @@ 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;
 }
 
 
 void *
-secmem_realloc( void *p, size_t newsize )
+secmexrealloc( void *p, size_t newsize )
 {
     MEMBLOCK *mb;
     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 */
+    if (size < sizeof(MEMBLOCK))
+      log_bug ("secure memory corrupted at block %p\n", (void *)mb);
+    size -= ((size_t) &((MEMBLOCK*)0)->u.aligned.c);
+
+    if( newsize <= size )
+       return p; /* It is easier not to shrink the memory.  */
     a = secmem_malloc( newsize );
-    memcpy(a, p, size);
-    memset((char*)a+size, 0, newsize-size);
-    secmem_free(p);
+    if ( a ) {
+        memcpy(a, p, size);
+        memset((char*)a+size, 0, newsize-size);
+        secmem_free(p);
+    }
     return a;
 }
 
@@ -319,12 +442,14 @@ 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;
-    memset(mb, 0xff, size );
-    memset(mb, 0xaa, size );
-    memset(mb, 0x55, size );
-    memset(mb, 0x00, size );
+    /* This does not make much sense: probably this memory is held in the
+     * cache. We do it anyway: */
+    wipememory2(mb, 0xff, size );
+    wipememory2(mb, 0xaa, size );
+    wipememory2(mb, 0x55, size );
+    wipememory2(mb, 0x00, size );
     mb->size = size;
     mb->u.next = unused_blocks;
     unused_blocks = mb;
@@ -332,26 +457,53 @@ secmem_free( void *a )
     cur_alloced -= size;
 }
 
+
+/* Check whether P points into the pool.  */
+static int
+ptr_into_pool_p (const void *p)
+{
+  /* We need to convert pointers to addresses.  This is required by
+     C-99 6.5.8 to avoid undefined behaviour.  Using size_t is at
+     least only implementation defined.  See also
+     http://lists.gnupg.org/pipermail/gcrypt-devel/2007-February/001102.html
+  */
+  size_t p_addr = (size_t)p;
+  size_t pool_addr = (size_t)pool;
+
+  return p_addr >= pool_addr && p_addr <  pool_addr+poolsize;
+}
+
+
 int
 m_is_secure( const void *p )
 {
-    return p >= pool && p < (void*)((char*)pool+poolsize);
+  return pool_okay && ptr_into_pool_p (p);
 }
 
+
+
+/****************
+ * Warning:  This code might be called by an interrupt handler
+ *          and frankly, there should really be such a handler,
+ *          to make sure that the memory is wiped out.
+ *          We hope that the OS wipes out mlocked memory after
+ *          receiving a SIGKILL - it really should do so, otherwise
+ *          there is no chance to get the secure memory cleaned.
+ */
 void
 secmem_term()
 {
     if( !pool_okay )
        return;
 
-    memset( pool, 0xff, poolsize);
-    memset( pool, 0xaa, poolsize);
-    memset( pool, 0x55, poolsize);
-    memset( pool, 0x00, poolsize);
-  #if HAVE_MMAP
+    wipememory2( pool, 0xff, poolsize);
+    wipememory2( pool, 0xaa, poolsize);
+    wipememory2( pool, 0x55, poolsize);
+    wipememory2( pool, 0x00, poolsize);
+#ifdef HAVE_MMAP
     if( pool_is_mmapped )
        munmap( pool, poolsize );
-  #endif
+#endif
     pool = NULL;
     pool_okay = 0;
     poolsize=0;
@@ -370,4 +522,3 @@ secmem_dump_stats()
                cur_alloced, max_alloced, cur_blocks, max_blocks,
                (ulong)poollen, (ulong)poolsize );
 }
-