Obsolete option --no-sig-create-check.
[gnupg.git] / util / secmem.c
index 6813296..553175b 100644 (file)
@@ -1,11 +1,12 @@
 /* secmem.c  - memory allocation from a secure heap
- *     Copyright (C) 1998, 1999, 2000 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>
-  #ifdef USE_CAPABILITIES
-    #include <sys/capability.h>
-  #endif
-  #ifdef HAVE_PLOCK
-    #include <sys/lock.h>
-  #endif
+#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
+#define EPERM  ENOMEM
 #endif
 
 
@@ -88,8 +94,8 @@ print_warn(void)
   if (!no_warning)
     {
       log_info(_("WARNING: using insecure memory!\n"));
-      log_info(_("please see http://www.gnupg.org/faq.html "
-                "for more information\n"));
+      log_info(_("please see http://www.gnupg.org/documentation/faqs.html"
+                " for more information\n"));
     }
 }
 
@@ -97,7 +103,7 @@ print_warn(void)
 static void
 lock_pool( void *p, size_t n )
 {
-  #if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
+#if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
     int err;
 
     cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
@@ -108,33 +114,45 @@ lock_pool( void *p, size_t n )
 
     if( err ) {
        if( errno != EPERM
-         #ifdef EAGAIN  /* OpenBSD returns this */
+#ifdef EAGAIN  /* OpenBSD returns this */
            && errno != EAGAIN
-         #endif
-         #ifdef ENOSYS  /* Some SCOs return this (function not implemented) */
+#endif
+#ifdef ENOSYS  /* Some SCOs return this (function not implemented) */
            && errno != ENOSYS
-         #endif
-          #ifdef ENOMEM  /* Linux can return this */
+#endif
+#ifdef ENOMEM  /* Linux can return this */
             && errno != ENOMEM
-          #endif
+#endif
          )
            log_error("can't lock memory: %s\n", strerror(err));
        show_warning = 1;
     }
 
-  #elif defined(HAVE_MLOCK)
+#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 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;
@@ -145,12 +163,12 @@ lock_pool( void *p, size_t n )
        if( err && errno )
            err = errno;
     }
-  #endif /*!HAVE_PLOCK*/
-  #else
+#endif /*!HAVE_PLOCK*/
+#else
     err = mlock( p, n );
     if( err && errno )
        err = errno;
-  #endif
+#endif
 
     if( uid && !geteuid() ) {
        /* check that we really dropped the privs.
@@ -161,44 +179,49 @@ lock_pool( void *p, size_t n )
 
     if( err ) {
        if( errno != EPERM
-         #ifdef EAGAIN  /* OpenBSD returns this */
+#ifdef EAGAIN  /* OpenBSD returns this */
            && errno != EAGAIN
-         #endif
-         #ifdef ENOSYS  /* Some SCOs return this (function not implemented) */
+#endif
+#ifdef ENOSYS  /* Some SCOs return this (function not implemented) */
            && errno != ENOSYS
-         #endif
-          #ifdef ENOMEM  /* Linux can return this */
+#endif
+#ifdef ENOMEM  /* Linux can return this */
             && errno != ENOMEM
-          #endif
+#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;
     }
 
-  #elif defined ( __QNX__ )
+#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__)
+#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__)
+#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
+#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;
@@ -206,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;
+
 
-  #ifdef 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);
@@ -228,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));
@@ -239,7 +267,7 @@ init_pool( size_t n)
        pool_okay = 1;
     }
 
-  #endif
+#endif
     if( !pool_okay ) {
        pool = malloc( poolsize );
        if( !pool )
@@ -285,16 +313,17 @@ 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 __riscos__
-      #ifdef USE_CAPABILITIES
+#ifdef USE_CAPABILITIES
        /* drop all capabilities */
        cap_set_proc( cap_from_text("all-eip") );
 
-      #elif !defined(HAVE_DOSISH_SYSTEM)
+#elif !defined(HAVE_DOSISH_SYSTEM)
        uid_t uid;
 
        disable_secmem=1;
@@ -303,7 +332,7 @@ secmem_init( size_t n )
            if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
                log_fatal("failed to drop setuid\n" );
        }
-      #endif
+#endif
 #endif /* !__riscos__ */
     }
     else {
@@ -314,6 +343,8 @@ secmem_init( size_t n )
        else
            log_error("Oops, secure memory pool already initialized\n");
     }
+
+    return !show_warning;
 }
 
 
@@ -334,7 +365,10 @@ secmem_malloc( size_t size )
        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;
 
@@ -375,7 +409,7 @@ secmem_malloc( size_t size )
 
 
 void *
-secmem_realloc( void *p, size_t newsize )
+secmexrealloc( void *p, size_t newsize )
 {
     MEMBLOCK *mb;
     size_t size;
@@ -383,8 +417,12 @@ secmem_realloc( void *p, size_t newsize )
 
     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 );
     if ( a ) {
         memcpy(a, p, size);
@@ -419,10 +457,27 @@ 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);
 }
 
 
@@ -445,10 +500,10 @@ secmem_term()
     wipememory2( pool, 0xaa, poolsize);
     wipememory2( pool, 0x55, poolsize);
     wipememory2( pool, 0x00, poolsize);
-  #ifdef HAVE_MMAP
+#ifdef HAVE_MMAP
     if( pool_is_mmapped )
        munmap( pool, poolsize );
-  #endif
+#endif
     pool = NULL;
     pool_okay = 0;
     poolsize=0;
@@ -467,4 +522,3 @@ secmem_dump_stats()
                cur_alloced, max_alloced, cur_blocks, max_blocks,
                (ulong)poollen, (ulong)poolsize );
 }
-