diff --git a/Makefile.am b/Makefile.am index f55dbaf..dfe58c2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,141 +1,141 @@ # Makefile.am # Copyright (C) 2002, 2012, 2015 g10 Code GmbH # # This file is part of PINENTRY. # # PINENTRY 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. # # PINENTRY 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. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # SPDX-License-Identifier: GPL-2.0+ ## Process this file with automake to produce Makefile.in ACLOCAL_AMFLAGS = -I m4 DISTCHECK_CONFIGURE_FLAGS = --disable-pinentry-qt --enable-pinentry-emacs GITLOG_TO_CHANGELOG=gitlog-to-changelog EXTRA_DIST = autogen.sh autogen.rc README.GIT ChangeLog-old VERSION \ build-aux/gitlog-to-changelog \ build-aux/git-log-fix build-aux/git-log-footer if BUILD_PINENTRY_CURSES pinentry_curses = curses else pinentry_curses = endif if BUILD_PINENTRY_TTY pinentry_tty = tty else pinentry_tty = endif if BUILD_PINENTRY_EMACS pinentry_emacs = emacs else pinentry_emacs = endif if BUILD_PINENTRY_GTK_2 pinentry_gtk_2 = gtk+-2 else pinentry_gtk_2 = endif if BUILD_PINENTRY_GNOME_3 pinentry_gnome_3 = gnome3 else pinentry_gnome_3 = endif if BUILD_PINENTRY_QT pinentry_qt = qt else pinentry_qt = endif if BUILD_PINENTRY_TQT pinentry_tqt = tqt else pinentry_tqt = endif if BUILD_PINENTRY_W32 pinentry_w32 = w32 else pinentry_w32 = endif if BUILD_PINENTRY_FLTK pinentry_fltk = fltk else pinentry_fltk = endif if BUILD_PINENTRY_EFL pinentry_efl = efl else pinentry_efl = endif if BUILD_DOC doc = doc else doc = endif SUBDIRS = m4 secmem pinentry ${pinentry_curses} ${pinentry_tty} \ ${pinentry_emacs} ${pinentry_gtk_2} ${pinentry_gnome_3} \ ${pinentry_qt} ${pinentry_tqt} ${pinentry_w32} \ ${pinentry_fltk} ${pinentry_efl} ${doc} install-exec-local: @list='$(bin_PROGRAMS)'; for p in $$list; do \ echo " $(SETCAP) cap_ipc_lock+p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ $(SETCAP) cap_ipc_lock+p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'` || true; \ done (cd $(DESTDIR)$(bindir); \ - rm -f pinentry; \ - $(LN_S) $(PINENTRY_DEFAULT)$(EXEEXT) pinentry) + rm -f pinentry$(EXEEXT); \ + $(LN_S) $(PINENTRY_DEFAULT)$(EXEEXT) pinentry$(EXEEXT)) dist-hook: gen-ChangeLog distcheck-hook: set -e; ( \ pref="#+macro: pinentry_" ;\ reldate="$$(date -u +%Y-%m-%d)" ;\ echo "$${pref}ver $(PACKAGE_VERSION)" ;\ echo "$${pref}date $${reldate}" ;\ list='$(DIST_ARCHIVES)'; for i in $$list; do \ case "$$i" in *.tar.bz2) \ echo "$${pref}size $$(wc -c <$$i|awk '{print int($$1/1024)}')k" ;\ echo "$${pref}sha1 $$(sha1sum <$$i|cut -d' ' -f1)" ;\ echo "$${pref}sha2 $$(sha256sum <$$i|cut -d' ' -f1)" ;;\ esac;\ done ) | tee $(distdir).swdb gen_start_date = 2012-08-08T00:00:00 .PHONY: gen-ChangeLog gen-ChangeLog: if test -d $(top_srcdir)/.git; then \ (cd $(top_srcdir) && \ $(GITLOG_TO_CHANGELOG) --append-dot --tear-off \ --amend=build-aux/git-log-fix \ --since=$(gen_start_date) ) > $(distdir)/cl-t; \ cat $(top_srcdir)/build-aux/git-log-footer >> $(distdir)/cl-t;\ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi diff --git a/secmem/secmem.c b/secmem/secmem.c index 3abcc4a..0220e0f 100644 --- a/secmem/secmem.c +++ b/secmem/secmem.c @@ -1,463 +1,469 @@ /* secmem.c - memory allocation from a secure heap * Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc. * Copyright (C) 2015 g10 Code GmbH * * 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 * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #ifndef HAVE_W32CE_SYSTEM #include #endif #include #include #if defined(HAVE_MLOCK) || defined(HAVE_MMAP) # include # include # include # ifdef USE_CAPABILITIES # include # endif #endif #include #include "memory.h" #ifdef ORIGINAL_GPG_VERSION #include "types.h" #include "util.h" #else /* ORIGINAL_GPG_VERSION */ #include "util.h" typedef union { int a; short b; char c[1]; long d; #ifdef HAVE_U64_TYPEDEF u64 e; #endif float f; double g; } PROPERLY_ALIGNED_TYPE; #define log_error log_info #define log_bug log_fatal void log_info(char *template, ...) { va_list args; va_start(args, template); vfprintf(stderr, template, args); va_end(args); } void log_fatal(char *template, ...) { va_list args; va_start(args, template); vfprintf(stderr, template, args); va_end(args); exit(EXIT_FAILURE); } #endif /* ORIGINAL_GPG_VERSION */ #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) # define MAP_ANONYMOUS MAP_ANON #endif #define DEFAULT_POOLSIZE 16384 typedef struct memblock_struct MEMBLOCK; struct memblock_struct { unsigned size; union { MEMBLOCK *next; PROPERLY_ALIGNED_TYPE aligned; } u; }; static void *pool; static volatile int pool_okay; /* may be checked in an atexit function */ +#if HAVE_MMAP static int pool_is_mmapped; +#endif static size_t poolsize; /* allocated length */ static size_t poollen; /* used length */ static MEMBLOCK *unused_blocks; static unsigned max_alloced; static unsigned cur_alloced; static unsigned max_blocks; 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 ) { #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 ) 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 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 if( uid && !geteuid() ) { if( setuid( uid ) || getuid() != geteuid() ) log_fatal("failed to reset uid: %s\n", strerror(errno)); } if( err ) { if( errno != EPERM #ifdef EAGAIN /* OpenBSD returns this */ && errno != EAGAIN #endif ) log_error("can't lock memory: %s\n", strerror(err)); show_warning = 1; } #else + (void)p; + (void)n; log_info("Please note that you don't have secure memory on this system\n"); #endif } static void init_pool( size_t n) { +#if HAVE_MMAP size_t pgsize; +#endif poolsize = n; if( disable_secmem ) log_bug("secure memory is disabled"); +#if HAVE_MMAP #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); close (fd); } } # endif if( pool == (void*)-1 ) log_info("can't mmap pool of %u bytes: %s - using malloc\n", (unsigned)poolsize, strerror(errno)); else { pool_is_mmapped = 1; pool_okay = 1; } #endif if( !pool_okay ) { pool = malloc( poolsize ); if( !pool ) log_fatal("can't allocate memory pool of %u bytes\n", (unsigned)poolsize); else pool_okay = 1; } lock_pool( pool, poolsize ); poollen = 0; } /* concatenate unused blocks */ static void compress_pool(void) { /* fixme: we really should do this */ } 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) { unsigned flags; flags = no_warning ? 1:0; flags |= suspend_warning ? 2:0; return flags; } void secmem_init( size_t n ) { if( !n ) { #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 ) || getuid() != geteuid() ) log_fatal("failed to drop setuid\n" ); } #endif } else { if( n < DEFAULT_POOLSIZE ) n = DEFAULT_POOLSIZE; if( !pool_okay ) init_pool(n); else log_error("Oops, secure memory pool already initialized\n"); } } void * secmem_malloc( size_t size ) { MEMBLOCK *mb, *mb2; int compressed=0; 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 */ size += sizeof(MEMBLOCK); size = ((size + 31) / 32) * 32; retry: /* try to get it from the used blocks */ for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next ) if( mb->size >= size ) { if( mb2 ) mb2->u.next = mb->u.next; else unused_blocks = mb->u.next; goto leave; } /* allocate a new block */ if( (poollen + size <= poolsize) ) { mb = (void*)((char*)pool + poollen); poollen += size; mb->size = size; } else if( !compressed ) { compressed=1; compress_pool(); goto retry; } else return NULL; leave: cur_alloced += mb->size; cur_blocks++; if( cur_alloced > max_alloced ) max_alloced = cur_alloced; if( cur_blocks > max_blocks ) max_blocks = cur_blocks; memset (&mb->u.aligned.c, 0, size - (size_t) &((struct memblock_struct *) 0)->u.aligned.c); return &mb->u.aligned.c; } void * secmem_realloc( void *p, size_t newsize ) { MEMBLOCK *mb; size_t size; void *a; if (! p) return secmem_malloc(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 */ a = secmem_malloc( newsize ); memcpy(a, p, size); memset((char*)a+size, 0, newsize-size); secmem_free(p); return a; } void secmem_free( void *a ) { MEMBLOCK *mb; size_t size; if( !a ) return; 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: */ 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; cur_blocks--; cur_alloced -= size; } int m_is_secure( const void *p ) { return p >= pool && p < (void*)((char*)pool+poolsize); } void secmem_term() { if( !pool_okay ) return; wipememory2( pool, 0xff, poolsize); wipememory2( pool, 0xaa, poolsize); wipememory2( pool, 0x55, poolsize); wipememory2( pool, 0x00, poolsize); #if HAVE_MMAP if( pool_is_mmapped ) munmap( pool, poolsize ); #endif pool = NULL; pool_okay = 0; poolsize=0; poollen=0; unused_blocks=NULL; } void secmem_dump_stats() { if( disable_secmem ) return; fprintf(stderr, "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n", cur_alloced, max_alloced, cur_blocks, max_blocks, (ulong)poollen, (ulong)poolsize ); } size_t secmem_get_max_size (void) { return poolsize; }