diff --git a/Makefile.am b/Makefile.am index 1f62939..e11b009 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,123 +1,123 @@ # 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +# 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 \ 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_W32 pinentry_w32 = w32 else pinentry_w32 = endif if BUILD_PINENTRY_FLTK pinentry_fltk = fltk else pinentry_fltk = endif SUBDIRS = m4 secmem pinentry ${pinentry_curses} ${pinentry_tty} \ ${pinentry_emacs} ${pinentry_gtk_2} ${pinentry_gnome_3} \ ${pinentry_qt} ${pinentry_w32} ${pinentry_fltk} 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) dist-hook: gen-ChangeLog echo "$(VERSION)" > $(distdir)/VERSION 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/configure.ac b/configure.ac index ea12d56..3de0882 100644 --- a/configure.ac +++ b/configure.ac @@ -1,718 +1,718 @@ # configure.ac # Copyright (C) 1999 Robert Bihlmeyer # Copyright (C) 2001, 2002, 2003, 2004, 2007, 2015, 2016 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +# along with this program; if not, see . +# SPDX-License-Identifier: GPL-2.0+ # (Process this file with autoconf to produce a configure script.) AC_PREREQ(2.69) min_automake_version="1.14" # To build a release you need to create a tag with the version number # (git tag -s pinentry-n.m.k) and run "./autogen.sh --force". Please # bump the version number immediately after the release, do another # commit, and a push so that the git magic is able to work. m4_define(mym4_version, [1.0.1]) # Below is m4 magic to extract and compute the git revision number, # the decimalized short revision number, a beta version string and a # flag indicating a development version (mym4_isgit). Note that the # m4 processing is done by autoconf and not during the configure run. m4_define([mym4_revision], m4_esyscmd([git branch -v 2>/dev/null \ | awk '/^\* / {printf "%s",$3}'])) m4_define([mym4_revision_dec], m4_esyscmd_s([echo $((0x$(echo ]mym4_revision[|head -c 4)))])) m4_define([mym4_betastring], m4_esyscmd_s([git describe --match 'pinentry-[0-9].[0-9].*[0-9]' \ --long| awk -F- '$3!=0{print"-beta"$3}'])) m4_define([mym4_isgit],m4_if(mym4_betastring,[],[no],[yes])) m4_define([mym4_full_version],[mym4_version[]mym4_betastring]) AC_INIT([pinentry],[mym4_full_version], [https://bugs.gnupg.org]) AC_CONFIG_AUX_DIR([build-aux]) AM_CONFIG_HEADER(config.h) AC_CONFIG_SRCDIR(pinentry/pinentry.h) AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip]) AC_GNU_SOURCE AM_MAINTAINER_MODE AC_CANONICAL_HOST AH_TOP([ #ifndef PINENTRY_CONFIG_H_INCLUDED #define PINENTRY_CONFIG_H_INCLUDED /* Enable gpg-error's strerror macro under W32CE. */ #define GPG_ERR_ENABLE_ERRNO_MACROS 1 ]) AH_BOTTOM([ #endif /*PINENTRY_CONFIG_H_INCLUDED*/ ]) dnl Checks for programs. AC_PROG_MAKE_SET AM_SANITY_CHECK missing_dir=`cd $ac_aux_dir && pwd` AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir) AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir) AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) AC_PROG_CC AC_PROG_CPP AC_PROG_INSTALL AC_PROG_RANLIB # We need to check for cplusplus here because we may not do the test # for Qt and autoconf does does not allow that. AC_PROG_CXX AC_PROG_LN_S PKG_PROG_PKG_CONFIG AC_CHECK_TOOL(WINDRES, windres, :) AC_CHECK_PROGS(GITLOG_TO_CHANGELOG, gitlog-to-changelog, [build-aux/gitlog-to-changelog]) have_dosish_system=no have_w32_system=no have_w32ce_system=no case "${host}" in *-mingw32*) AC_DEFINE(USE_ONLY_8DOT3,1, [Set this to limit filenames to the 8.3 format]) have_dosish_system=yes have_w32_system=yes case "${host}" in *-mingw32ce*) have_w32ce_system=yes ;; *) AC_DEFINE(HAVE_DRIVE_LETTERS,1, [Defined if the OS supports drive letters.]) ;; esac ;; i?86-emx-os2 | i?86-*-os2*emx ) # OS/2 with the EMX environment AC_DEFINE(HAVE_DRIVE_LETTERS) have_dosish_system=yes ;; i?86-*-msdosdjgpp*) # DOS with the DJGPP environment AC_DEFINE(HAVE_DRIVE_LETTERS) have_dosish_system=yes ;; esac if test "$have_dosish_system" = yes; then AC_DEFINE(HAVE_DOSISH_SYSTEM,1, [Defined if we run on some of the PCDOS like systems (DOS, Windoze. OS/2) with special properties like no file modes]) fi AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes) if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system]) if test "$have_w32ce_system" = yes; then AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE]) fi fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes) dnl Checks for compiler features. if test "$GCC" = yes; then # Check whether gcc does not emit a diagnositc for unknown -Wno-* # options. This is the case for gcc >= 4.6 AC_MSG_CHECKING([if gcc ignores unknown -Wno-* options]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6 ) #kickerror #endif]],[])],[_gcc_silent_wno=yes],[_gcc_silent_wno=no]) AC_MSG_RESULT($_gcc_silent_wno) if test "$USE_MAINTAINER_MODE" = "yes"; then CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes" CFLAGS="$CFLAGS -Wformat -Wno-format-y2k -Wformat-security" if test x"$_gcc_silent_wno" = xyes ; then _gcc_warn=yes else AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wno-missing-field-initializers" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [_gcc_warn=yes],[_gcc_warn=no]) AC_MSG_RESULT($_gcc_warn) CFLAGS=$_gcc_cflags_save; fi if test x"$_gcc_warn" = xyes ; then CFLAGS="$CFLAGS -W -Wno-sign-compare -Wno-missing-field-initializers" fi AC_MSG_CHECKING([if gcc supports -Wdeclaration-after-statement]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wdeclaration-after-statement" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_warn=yes,_gcc_warn=no) AC_MSG_RESULT($_gcc_warn) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_warn" = xyes ; then CFLAGS="$CFLAGS -Wdeclaration-after-statement" fi else # Not in maintainer mode: Use standard warnings. CFLAGS="$CFLAGS -Wall" fi CPPFLAGS="$CPPFLAGS -Wall" if test x"$_gcc_silent_wno" = xyes ; then _gcc_warn=yes else AC_MSG_CHECKING([if gcc supports -Wno-pointer-sign]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wno-pointer-sign" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],[_gcc_warn=yes],[_gcc_warn=no]) AC_MSG_RESULT($_gcc_warn) CFLAGS=$_gcc_cflags_save; fi if test x"$_gcc_warn" = xyes ; then CFLAGS="$CFLAGS -Wno-pointer-sign" fi AC_MSG_CHECKING([if gcc supports -Wpointer-arith]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wpointer-arith" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_warn=yes,_gcc_warn=no) AC_MSG_RESULT($_gcc_warn) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_warn" = xyes ; then CFLAGS="$CFLAGS -Wpointer-arith" fi fi # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS(string.h unistd.h langinfo.h termio.h locale.h utime.h wchar.h) dnl Checks for library functions. AC_CHECK_FUNCS(seteuid stpcpy mmap) GNUPG_CHECK_MLOCK dnl Checks for standard types. AC_TYPE_UINT32_T # Common libraries and cflags. COMMON_CFLAGS= COMMON_LIBS= AC_SUBST(COMMON_CFLAGS) AC_SUBST(COMMON_LIBS) dnl Checks for libgpg-error # # libgpg-error is a library with error codes shared between GnuPG # related projects. # NEED_GPG_ERROR_VERSION=1.16 have_gpg_error=no AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION", have_gpg_error=yes,have_gpg_error=no) COMMON_CFLAGS="$GPG_ERROR_CFLAGS $COMMON_CFLAGS" COMMON_LIBS="$GPG_ERROR_LIBS $COMMON_LIBS" AC_DEFINE_UNQUOTED(GPG_ERR_ENABLE_GETTEXT_MACROS, 1, [Under Windows we use the gettext code from libgpg-error]) AC_DEFINE_UNQUOTED(GPG_ERR_ENABLE_ERRNO_MACROS, 1, [Under WindowsCE we use the strerror replacement from libgpg-error.]) dnl Checks for libassuan. # # libassuan is used for IPC # NEED_LIBASSUAN_API=2 NEED_LIBASSUAN_VERSION=2.1.0 have_libassuan=no AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_API:$NEED_LIBASSUAN_VERSION", have_libassuan=yes,have_libassuan=no) if test "$have_libassuan" = "yes"; then AC_DEFINE_UNQUOTED(GNUPG_LIBASSUAN_VERSION, "$libassuan_version", [version of the libassuan library]) fi COMMON_CFLAGS="$LIBASSUAN_CFLAGS $COMMON_CFLAGS" COMMON_LIBS="$LIBASSUAN_LIBS $COMMON_LIBS" dnl Checks for libsecmem. GNUPG_CHECK_TYPEDEF(byte, HAVE_BYTE_TYPEDEF) GNUPG_CHECK_TYPEDEF(ulong, HAVE_ULONG_TYPEDEF) dnl Check for libcap AC_ARG_WITH([libcap], AC_HELP_STRING([--without-libcap], [Disable support for capabilities library])) if test "x$with_libcap" != "xno"; then AC_PATH_PROG(SETCAP, setcap, :, "$PATH:/sbin:/usr/sbin") AC_CHECK_LIB(cap, cap_set_proc, [ AC_DEFINE(USE_CAPABILITIES,1,[The capabilities support library is installed]) LIBCAP=-lcap ]) fi AC_SUBST(LIBCAP) dnl dnl Check for curses pinentry program. dnl AC_ARG_ENABLE(pinentry-curses, AC_HELP_STRING([--enable-pinentry-curses], [build curses pinentry]), pinentry_curses=$enableval, pinentry_curses=maybe) AC_ARG_ENABLE(fallback-curses, AC_HELP_STRING([--enable-fallback-curses], [include curses fallback]), fallback_curses=$enableval, fallback_curses=maybe) dnl dnl Checks for curses libraries. Deal correctly with $pinentry_curses = maybe dnl and $fallback_curses = maybe. dnl if test "$pinentry_curses" != "no" -o "$fallback_curses" != "no"; then IU_LIB_CURSES fi if test "$LIBCURSES"; then if test "$pinentry_curses" != "no"; then pinentry_curses=yes fi if test "$fallback_curses" != "no"; then fallback_curses=yes AC_DEFINE(FALLBACK_CURSES, 1, [The GUI pinentries should fall back to curses if X is not available.]) fi else if test "$pinentry_curses" = "yes" -o "$fallback_curses" = "yes"; then AC_MSG_ERROR([[ *** *** The curses library is required. The latest version of *** ncurses is always available from ftp://ftp.gnu.org/gnu/ncurses/. ***]]) fi pinentry_curses=no fallback_curses=no fi AM_CONDITIONAL(BUILD_LIBPINENTRY_CURSES, test "$pinentry_curses" = "yes" -o "$fallback_curses" = "yes") AM_CONDITIONAL(BUILD_PINENTRY_CURSES, test "$pinentry_curses" = "yes") AM_CONDITIONAL(FALLBACK_CURSES, test "$fallback_curses" = "yes") if test "$pinentry_curses" = "yes"; then AC_DEFINE(PINENTRY_CURSES, 1, [The Curses version of Pinentry is to be build]) fi dnl dnl Check for tty pinentry program. dnl AC_ARG_ENABLE(pinentry-tty, AC_HELP_STRING([--enable-pinentry-tty], [build tty pinentry]), pinentry_tty=$enableval, pinentry_tty=maybe) AM_CONDITIONAL(BUILD_PINENTRY_TTY, test "$pinentry_tty" = "yes") if test "$pinentry_tty" = "yes"; then AC_DEFINE(PINENTRY_TTY, 1, [The TTY version of Pinentry is to be build]) fi dnl dnl Additional checks pinentry Curses. dnl if test "$pinentry_curses" = "yes" \ -o "$fallback_curses" = "yes" ; then AM_ICONV if test "$am_cv_func_iconv" != "yes"; then AC_MSG_ERROR([[ *** *** The iconv function is required. You can specify its location *** using the --with-libiconv-prefix=PREFIX option to configure. ***]]) fi fi dnl dnl Check for emacs pinentry program. dnl AC_ARG_ENABLE(pinentry-emacs, AC_HELP_STRING([--enable-pinentry-emacs], [build emacs pinentry]), pinentry_emacs=$enableval, pinentry_emacs=no) AC_ARG_ENABLE(inside-emacs, AC_HELP_STRING([--enable-inside-emacs], [include emacs hack]), inside_emacs=$enableval, inside_emacs=maybe) if test "$pinentry_emacs" != "no" -o "$inside_emacs" != "no"; then AC_MSG_CHECKING([if Unix domain socket is supported]) AC_TRY_COMPILE([ #include #include ], [int s = socket (AF_UNIX, SOCK_STREAM, 0);], [_unixsock_works=yes], [_unixsock_works=no]) AC_MSG_RESULT($_unixsock_works) if test "$_unixsock_works" = "yes"; then if test "$pinentry_emacs" != "no"; then pinentry_emacs=yes fi if test "$inside_emacs" != "no"; then inside_emacs=yes AC_DEFINE(INSIDE_EMACS, 1, [The GUI pinentries should respect INSIDE_EMACS envvar.]) fi else if test "$pinentry_emacs" = "yes" -o "$inside_emacs" = "yes"; then AC_MSG_ERROR([[ *** *** Support for Unix domain sockets is required. ***]]) fi pinentry_emacs=no inside_emacs=no fi fi AM_CONDITIONAL(BUILD_LIBPINENTRY_EMACS, test "$pinentry_emacs" = "yes" -o "$inside_emacs" = "yes") AM_CONDITIONAL(BUILD_PINENTRY_EMACS, test "$pinentry_emacs" = "yes") AM_CONDITIONAL(INSIDE_EMACS, test "$inside_emacs" = "yes") if test "$pinentry_emacs" = "yes"; then AC_DEFINE(PINENTRY_EMACS, 1, [The Emacs version of Pinentry is to be build]) fi dnl dnl Check for GTK+-2 / GNOME3 pinentry programs. dnl AC_ARG_ENABLE(pinentry-gtk2, AC_HELP_STRING([--enable-pinentry-gtk2], [build GTK+-2 pinentry]), pinentry_gtk_2=$enableval, pinentry_gtk_2=maybe) AC_ARG_ENABLE(pinentry-gnome3, AC_HELP_STRING([--enable-pinentry-gnome3], [build GNOME 3 pinentry]), pinentry_gnome_3=$enableval, pinentry_gnome_3=maybe) dnl check if the module gtk+-2.0 exists if test "$pinentry_gtk_2" != "no"; then PKG_CHECK_MODULES( GTK2, [gtk+-2.0 >= 2.4.0], [ test "$pinentry_gtk_2" != "no" && pinentry_gtk_2=yes test "$pinentry_gnome_3" != "no" && pinentry_gnome_3=yes ], [ AC_MSG_WARN([pkg-config could not find the module gtk+-2.0]) pinentry_gtk_2=no ] ) fi AM_CONDITIONAL(BUILD_PINENTRY_GTK_2, test "$pinentry_gtk_2" = "yes") if test "$pinentry_gnome_3" != "no"; then PKG_CHECK_MODULES( GNOME3, [gcr-3,gcr-base-3], [ pinentry_gnome_3=yes AC_DEFINE(GCR_API_SUBJECT_TO_CHANGE, 1, [Nod nod]) ], [ AC_MSG_WARN([pkg-config could not find the module gcr-3,gcr-base-3]) pinentry_gnome_3=no ] ) fi AM_CONDITIONAL(BUILD_PINENTRY_GNOME_3, test "$pinentry_gnome_3" = "yes") dnl dnl Check for libsecret. dnl AC_ARG_ENABLE(libsecret, AC_HELP_STRING([--enable-libsecret], [optionally cache passphrases using libsecret]), libsecret=$enableval, libsecret=maybe) dnl check if the module libsecret exists if test "$libsecret" != "no"; then PKG_CHECK_MODULES( LIBSECRET, [libsecret-1], [libsecret=yes], [ AC_MSG_WARN([pkg-config could not find the module libsecret-1]) libsecret=no ] ) fi AM_CONDITIONAL(BUILD_WITH_LIBSECRET, test "$libsecret" = "yes") if test "$libsecret" = "yes"; then AC_DEFINE(HAVE_LIBSECRET, 1, [The pinentries should optionally cache the passphrase using libsecret.]) COMMON_CFLAGS="$LIBSECRET_CFLAGS $COMMON_CFLAGS" COMMON_LIBS="$LIBSECRET_LIBS $COMMON_LIBS" fi dnl dnl Check for Qt pinentry program. dnl AC_ARG_ENABLE(pinentry-qt, AC_HELP_STRING([--enable-pinentry-qt], [build qt pinentry]), pinentry_qt=$enableval, pinentry_qt=maybe) dnl dnl Checks for qt libraries. Deal correctly with $pinentry_qt = maybe. dnl Tries to find Qt5, falls back on Qt4 dnl if test "$pinentry_qt" != "no"; then FIND_QT if test "$have_qt4_libs" != "yes" -a "$have_qt5_libs" != "yes"; then if test "$pinentry_qt" = "yes"; then AC_MSG_ERROR([[ *** *** Qt4 (QtCore, QtGui) or Qt5 (Qt5Core, Qt5Gui, Qt5Widgets) is required. ***]]) else pinentry_qt=no fi fi fi AC_SUBST(PINENTRY_QT_CFLAGS) AC_SUBST(PINENTRY_QT_LIBS) AC_SUBST(MOC) dnl If we have come so far, qt pinentry can be build. if test "$pinentry_qt" != "no"; then pinentry_qt=yes fi AM_CONDITIONAL(BUILD_PINENTRY_QT, test "$pinentry_qt" = "yes") if test "$pinentry_qt" = "yes"; then AC_DEFINE(PINENTRY_QT, 1, [The qt version of Pinentry is to be build]) if test "$have_qt4_libs" = "yes"; then pinentry_qt_lib_version="(Qt4)" else pinentry_qt_lib_version="(Qt5)" fi fi # # Check whether we should build the W32 pinentry. This is actually # the simplest check as we do this only for that platform. # pinentry_w32=no test $have_w32_system = yes && pinentry_w32=yes AM_CONDITIONAL(BUILD_PINENTRY_W32, test "$pinentry_w32" = "yes") dnl dnl Check for FLTK pinentry program. dnl AC_ARG_ENABLE(pinentry-fltk, AC_HELP_STRING([--enable-pinentry-fltk], [build FLTK 1.3 pinentry]), pinentry_fltk=$enableval, pinentry_fltk=maybe) dnl check for fltk-config if test "$pinentry_fltk" != "no"; then AC_PATH_PROG(FLTK_CONFIG, fltk-config, no) if test x"${FLTK_CONFIG}" = xno ; then AC_MSG_WARN([fltk-config is not found]) pinentry_fltk=no fi fi dnl check for FLTK libraries and set flags if test "$pinentry_fltk" != "no"; then AC_MSG_CHECKING([for FLTK 1.3]) FLTK_VERSION=`${FLTK_CONFIG} --api-version` if test ${FLTK_VERSION} != "1.3" ; then AC_MSG_RESULT([no]) AC_MSG_WARN([FLTK 1.3 not found (available $FLTK_VERSION)]) pinentry_fltk=no else AC_MSG_RESULT([yes]) FLTKCFLAGS=`${FLTK_CONFIG} --cflags` FLTKCXXFLAGS=`${FLTK_CONFIG} --cxxflags` FLTKLIBS=`${FLTK_CONFIG} --ldflags` AC_SUBST(FLTKCFLAGS) AC_SUBST(FLTKCXXFLAGS) AC_SUBST(FLTKLIBS) pinentry_fltk=yes fi fi AM_CONDITIONAL(BUILD_PINENTRY_FLTK, test "$pinentry_fltk" = "yes") # Figure out the default pinentry. We are very conservative here. # Please change the order only after verifying that the preferred # pinentry really is better (more feature-complete and more secure). if test "$pinentry_gtk_2" = "yes"; then PINENTRY_DEFAULT=pinentry-gtk-2 else if test "$pinentry_qt" = "yes"; then PINENTRY_DEFAULT=pinentry-qt else if test "$pinentry_gnome_3" = "yes"; then PINENTRY_DEFAULT=pinentry-gnome3 else if test "$pinentry_curses" = "yes"; then PINENTRY_DEFAULT=pinentry-curses else if test "$pinentry_tty" = "yes"; then PINENTRY_DEFAULT=pinentry-tty else if test "$pinentry_w32" = "yes"; then PINENTRY_DEFAULT=pinentry-w32 else if test "$pinentry_fltk" = "yes"; then PINENTRY_DEFAULT=pinentry-fltk else AC_MSG_ERROR([[No pinentry enabled.]]) fi fi fi fi fi fi fi AC_SUBST(PINENTRY_DEFAULT) # # Print errors here so that they are visible all # together and the user can acquire them all together. # die=no if test "$have_gpg_error" = "no"; then die=yes AC_MSG_NOTICE([[ *** *** You need libgpg-error to build this program. ** This library is for example available at *** ftp://ftp.gnupg.org/gcrypt/libgpg-error *** (at least version $NEED_GPG_ERROR_VERSION is required.) ***]]) fi if test "$have_libassuan" = "no"; then die=yes AC_MSG_NOTICE([[ *** *** You need libassuan to build this program. *** This library is for example available at *** ftp://ftp.gnupg.org/gcrypt/libassuan/ *** (at least version $NEED_LIBASSUAN_VERSION (API $NEED_LIBASSUAN_API) is required). ***]]) fi if test "$die" = "yes"; then AC_MSG_ERROR([[ *** *** Required libraries not found. Please consult the above messages *** and install them before running configure again. ***]]) fi # # To avoid double inclusion of config.h which might happen at some # places, we add the usual double inclusion protection at the top of # config.h. # AH_TOP([ #ifndef GNUPG_CONFIG_H_INCLUDED #define GNUPG_CONFIG_H_INCLUDED ]) # # Stuff which goes at the bottom of config.h. # AH_BOTTOM([ #ifdef GPG_ERR_SOURCE_DEFAULT # error GPG_ERR_SOURCE_DEFAULT already defined #endif #define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_PINENTRY #endif /*GNUPG_CONFIG_H_INCLUDED*/ ]) AC_CONFIG_FILES([ m4/Makefile secmem/Makefile pinentry/Makefile curses/Makefile tty/Makefile emacs/Makefile gtk+-2/Makefile gnome3/Makefile qt/Makefile w32/Makefile fltk/Makefile doc/Makefile Makefile ]) AC_OUTPUT AC_MSG_NOTICE([ Pinentry v${VERSION} has been configured as follows: Revision: mym4_revision (mym4_revision_dec) Platform: $host Curses Pinentry ..: $pinentry_curses TTY Pinentry .....: $pinentry_tty Emacs Pinentry ...: $pinentry_emacs GTK+-2 Pinentry ..: $pinentry_gtk_2 GNOME 3 Pinentry .: $pinentry_gnome_3 Qt Pinentry ......: $pinentry_qt $pinentry_qt_lib_version W32 Pinentry .....: $pinentry_w32 FLTK Pinentry ....: $pinentry_fltk Fallback to Curses: $fallback_curses Emacs integration : $inside_emacs libsecret ........: $libsecret Default Pinentry .: $PINENTRY_DEFAULT ]) diff --git a/emacs/Makefile.am b/emacs/Makefile.am index ca0a4dc..c75c93d 100644 --- a/emacs/Makefile.am +++ b/emacs/Makefile.am @@ -1,28 +1,28 @@ # Makefile.am - PIN entry emacs frontend. # Copyright (C) 2002, 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +# along with this program; if not, see . +# SPDX-License-Identifier: GPL-2.0+ ## Process this file with automake to produce Makefile.in bin_PROGRAMS = pinentry-emacs AM_CPPFLAGS = $(COMMON_CFLAGS) $(NEMACS_INCLUDE) -I$(top_srcdir)/pinentry LDADD = ../pinentry/libpinentry.a ../secmem/libsecmem.a \ $(COMMON_LIBS) $(LIBCAP) $(LIBEMACS) $(LIBICONV) pinentry_emacs_SOURCES = pinentry-emacs.c diff --git a/emacs/pinentry-emacs.c b/emacs/pinentry-emacs.c index 3c39a96..5750502 100644 --- a/emacs/pinentry-emacs.c +++ b/emacs/pinentry-emacs.c @@ -1,48 +1,49 @@ /* pinentry-emacs.c - A secure emacs dialog for PIN entry, library version - Copyright (C) 2015 Daiki Ueno - - 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 . -*/ + * Copyright (C) 2015 Daiki Ueno + * + * 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+ + */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pinentry.h" #include "pinentry-emacs.h" pinentry_cmd_handler_t pinentry_cmd_handler = emacs_cmd_handler; int main (int argc, char *argv[]) { pinentry_init ("pinentry-emacs"); if (!pinentry_emacs_init ()) return 1; pinentry_parse_opts (argc, argv); if (pinentry_loop ()) return 1; return 0; } diff --git a/fltk/Makefile.am b/fltk/Makefile.am index 54209f9..598c4c4 100644 --- a/fltk/Makefile.am +++ b/fltk/Makefile.am @@ -1,21 +1,22 @@ # Makefile.am - PIN entry FLTK frontend. +# SPDX-License-Identifier: GPL-2.0+ bin_PROGRAMS = pinentry-fltk if FALLBACK_CURSES ncurses_include = $(NCURSES_INCLUDE) libcurses = ../pinentry/libpinentry-curses.a $(LIBCURSES) $(LIBICONV) else ncurses_include = libcurses = endif AM_CPPFLAGS = $(COMMON_CFLAGS) $(FLTKCXXFLAGS) $(ncurses_include) -I$(top_srcdir)/secmem -I$(top_srcdir)/pinentry AM_CXXFLAGS = -std=c++11 LDADD = ../pinentry/libpinentry.a ../secmem/libsecmem.a $(COMMON_LIBS) $(LIBCAP) $(FLTKLIBS) $(libcurses) pinentry_fltk_SOURCES = main.cxx pinwindow.cxx pinwindow.h \ passwindow.cxx passwindow.h \ qualitypasswindow.cxx qualitypasswindow.h EXTRA_DIST = encrypt.xpm icon.xpm diff --git a/fltk/main.cxx b/fltk/main.cxx index 1a72c89..8e7e726 100644 --- a/fltk/main.cxx +++ b/fltk/main.cxx @@ -1,327 +1,328 @@ /* main.cpp - A Fltk based dialog for PIN entry. Copyright (C) 2016 Anatoly madRat L. Berenblit Written by Anatoly madRat L. Berenblit . This program 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. This program 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + SPDX-License-Identifier: GPL-2.0+ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define PGMNAME (PACKAGE_NAME"-fltk") #include #include #include #include #include #include #include "memory.h" #include #include #ifdef FALLBACK_CURSES #include #endif #include #include #include #include #include #include #include "pinwindow.h" #include "passwindow.h" #include "qualitypasswindow.h" #define CONFIRM_STRING "Confirm" #define REPEAT_ERROR_STRING "Texts do not match" #define OK_STRING "OK" #define CANCEL_STRING "Cancel" char *application = NULL; static std::string escape_accel_utf8(const char *s) { std::string result; if (NULL != s) { result.reserve(strlen(s)); for (const char *p = s; *p; ++p) { if ('&' == *p) result.push_back(*p); result.push_back(*p); } } return result; } class cancel_exception { }; static int get_quality(const char *passwd, void *ptr) { if (NULL == passwd || 0 == *passwd) return 0; pinentry_t* pe = reinterpret_cast(ptr); return pinentry_inq_quality(*pe, passwd, strlen(passwd)); } bool is_short(const char *str) { return fl_utf_nb_char(reinterpret_cast(str), strlen(str)) < 16; } bool is_empty(const char *str) { return (NULL == str) || (0 == *str); } static int fltk_cmd_handler(pinentry_t pe) { int ret = -1; try { // TODO: Add parent window to pinentry-fltk window //if (pe->parent_wid){} std::string title = !is_empty(pe->title)?pe->title:PGMNAME; std::string ok = escape_accel_utf8(pe->ok?pe->ok:(pe->default_ok?pe->default_ok:OK_STRING)); std::string cancel = escape_accel_utf8(pe->cancel?pe->cancel:(pe->default_cancel?pe->default_cancel:CANCEL_STRING)); if (!!pe->pin) // password (or confirmation) { std::unique_ptr window; bool isSimple = (NULL == pe->quality_bar) && // pinenty.h: If this is not NULL ... is_empty(pe->error) && is_empty(pe->description) && is_short(pe->prompt); if (isSimple) { assert(NULL == pe->description); window.reset(PinWindow::create()); window->prompt(pe->prompt); } else { PassWindow *pass = NULL; if (pe->quality_bar) // pinenty.h: If this is not NULL ... { QualityPassWindow *p = QualityPassWindow::create(get_quality, &pe); window.reset(p); pass = p; p->quality(pe->quality_bar); } else { pass = PassWindow::create(); window.reset(pass); } if (NULL == pe->description) { pass->description(pe->prompt); pass->prompt(" "); } else { pass->description(pe->description); pass->prompt(escape_accel_utf8(pe->prompt).c_str()); } pass->description(pe->description); pass->prompt(escape_accel_utf8(pe->prompt).c_str()); if (NULL != pe->error) pass->error(pe->error); } window->ok(ok.c_str()); window->cancel(cancel.c_str()); window->title(title.c_str()); window->showModal((NULL != application)?1:0, &application); if (NULL == window->passwd()) throw cancel_exception(); const std::string password = window->passwd(); window.reset(); if (pe->repeat_passphrase) { const char *dont_match = NULL; do { if (NULL == dont_match && is_short(pe->repeat_passphrase)) { window.reset(PinWindow::create()); window->prompt(escape_accel_utf8(pe->repeat_passphrase).c_str()); } else { PassWindow *pass = PassWindow::create(); window.reset(pass); pass->description(pe->repeat_passphrase); pass->prompt(" "); pass->error(dont_match); } window->ok(ok.c_str()); window->cancel(cancel.c_str()); window->title(title.c_str()); window->showModal(); if (NULL == window->passwd()) throw cancel_exception(); if (password == window->passwd()) { pe->repeat_okay = 1; ret = 1; break; } else { dont_match = (NULL!=pe->repeat_error_string)? pe->repeat_error_string:REPEAT_ERROR_STRING; } } while (true); } else ret = 1; pinentry_setbufferlen(pe, password.size()+1); if (pe->pin) { memcpy(pe->pin, password.c_str(), password.size()+1); pe->result = password.size(); ret = password.size(); } } else { // Confirmation or Message Dialog title, desc Fl_Window dummy(0,0, 1,1); dummy.border(0); dummy.show((NULL != application)?1:0, &application); dummy.hide(); fl_message_title(title.c_str()); int result = -1; const char *message = (NULL != pe->description)?pe->description:CONFIRM_STRING; if (pe->one_button) { fl_ok = ok.c_str(); fl_message(message); result = 1; // OK } else if (pe->notok) { switch (fl_choice(message, ok.c_str(), cancel.c_str(), pe->notok)) { case 0: result = 1; break; case 2: result = 0; break; default: case 1: result = -1;break; } } else { switch (fl_choice(message, ok.c_str(), cancel.c_str(), NULL)) { case 0: result = 1; break; default: case 1: result = -1;break; } } // cancel -> pe->canceled = true, 0 // ok/y -> 1 // no -> 0 if (-1 == result) pe->canceled = true; ret = (1 == result); } Fl::check(); } catch (const cancel_exception&) { ret = -1; } catch (...) { ret = -1; } // do_touch_file(pe); only for NCURSES? return ret; } pinentry_cmd_handler_t pinentry_cmd_handler = fltk_cmd_handler; int main(int argc, char *argv[]) { application = *argv; pinentry_init(PGMNAME); #ifdef FALLBACK_CURSES if (!pinentry_have_display(argc, argv)) pinentry_cmd_handler = curses_cmd_handler; else #endif { //FLTK understood only -D (--display) // and should be converted into -di[splay] const static struct option long_options[] = { {"display", required_argument, 0, 'D' }, {NULL, no_argument, 0, 0 } }; for (int i = 0; i < argc-1; ++i) { switch (getopt_long(argc-i, argv+i, "D:", long_options, NULL)) { case 'D': { char* emul[] = {application, (char*)"-display", optarg}; Fl::args(3, emul); i = argc; break; } default: break; } } } pinentry_parse_opts(argc, argv); return pinentry_loop() ?EXIT_FAILURE:EXIT_SUCCESS; } diff --git a/fltk/passwindow.cxx b/fltk/passwindow.cxx index 78b3b2c..a0cbe25 100644 --- a/fltk/passwindow.cxx +++ b/fltk/passwindow.cxx @@ -1,85 +1,86 @@ /* passwindow.cxx - PassWindow is a more complex fltk dialog with more longer desc field and possibility to show some error text. if needed qualitybar - should be used QualityPassWindow. Copyright (C) 2016 Anatoly madRat L. Berenblit Written by Anatoly madRat L. Berenblit . This program 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. This program 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + SPDX-License-Identifier: GPL-2.0+ */ #include "passwindow.h" #include #include #include const char *PassWindow::DESCRIPTION = "Please enter the passphrase:"; PassWindow::PassWindow() : error_(NULL) { } void PassWindow::prompt(const char *name) { set_label(input_, name, PROMPT); } void PassWindow::description(const char *name) { set_label(message_, name, DESCRIPTION); } void PassWindow::error(const char *name) { set_label(error_, name, ""); } int PassWindow::init(const int cx, const int cy) { int y = PinWindow::init(cx, cy); assert(window_ == Fl_Group::current()); // make_window should all add current y = icon_->y(); // move back to icon's const int mx = icon_->x()+icon_->w(); message_->resize(mx, icon_->y(), cx-mx-10, icon_->h()); message_->align(Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_CLIP | FL_ALIGN_WRAP | FL_ALIGN_INSIDE)); description(NULL); y += icon_->h(); input_->resize(130, y+5, cx-150, 25); input_->labeltype(FL_NORMAL_LABEL); prompt(NULL); y = input_->y()+input_->h(); error_ = new Fl_Box(20, y+5, cx-30, 30); error_->labelcolor(FL_RED); error_->align(Fl_Align(FL_ALIGN_CENTER | FL_ALIGN_WRAP | FL_ALIGN_INSIDE)); // if not fit - user can read y = error_->y()+error_->h(); return y; } PassWindow* PassWindow::create() { PassWindow* p = new PassWindow; p->init(460, 185); p->window_->end(); p->input_->take_focus(); return p; } diff --git a/fltk/passwindow.h b/fltk/passwindow.h index 43813cd..28472d5 100644 --- a/fltk/passwindow.h +++ b/fltk/passwindow.h @@ -1,50 +1,51 @@ /* passwindow.h - PassWindow is a more complex fltk dialog with more longer desc field and possibility to show some error text. if needed qualitybar - should be used QualityPassWindow. Copyright (C) 2016 Anatoly madRat L. Berenblit Written by Anatoly madRat L. Berenblit . This program 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. This program 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + SPDX-License-Identifier: GPL-2.0+ */ #ifndef __PASSWINDOW_H__ #define __PASSWINDOW_H__ #include "pinwindow.h" class PassWindow : public PinWindow { protected: static const char *DESCRIPTION; protected: Fl_Box *error_; PassWindow(); public: virtual void prompt(const char *message); virtual void description(const char *desc); virtual void error(const char *err); static PassWindow* create(); protected: virtual int init(const int cx, const int cy); }; #endif //#ifndef __PASSWINDOW_H__ diff --git a/fltk/pinwindow.cxx b/fltk/pinwindow.cxx index ad11e91..c187dd5 100644 --- a/fltk/pinwindow.cxx +++ b/fltk/pinwindow.cxx @@ -1,250 +1,251 @@ /* pinwindow.cxx - PinWindow is a simple fltk dialog for entring password with timeout. if needed description (long text), error message, qualitybar and etc should used PassWindow. Copyright (C) 2016 Anatoly madRat L. Berenblit Written by Anatoly madRat L. Berenblit . This program 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. This program 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include "memory.h" #include "encrypt.xpm" #include "icon.xpm" #include "pinwindow.h" const char *PinWindow::TITLE = "Password"; const char *PinWindow::BUTTON_OK = "OK"; const char *PinWindow::BUTTON_CANCEL = "Cancel"; const char *PinWindow::PROMPT = "Passphrase:"; static const char *timeout_format = "%s(%d)"; static Fl_Pixmap encrypt(encrypt_xpm); static Fl_Pixmap icon(icon_xpm); PinWindow::PinWindow() : window_(NULL) ,message_(NULL) ,input_(NULL) ,ok_(NULL) ,cancel_(NULL) ,cancel_name_(BUTTON_CANCEL) ,passwd_(NULL) ,timeout_(0) { } PinWindow::~PinWindow() { wipe(); release(); delete window_; } void PinWindow::release() { if (NULL != passwd_) { memset(passwd_, 0, strlen(passwd_)); secmem_free(passwd_); } passwd_ = NULL; } void PinWindow::title(const char *name) { set_label(window_, name, TITLE); } void PinWindow::ok(const char* name) { set_label(ok_, name, BUTTON_OK); } void PinWindow::cancel(const char* label) { if (NULL != label && 0 != *label) cancel_name_ = label; else cancel_name_ = BUTTON_CANCEL; update_cancel_label(); } void PinWindow::prompt(const char *name) { set_label(message_, name, PROMPT); } void PinWindow::timeout(unsigned int time) { if (timeout_ == time) return; // A xor B ~ A != B if ( (time>0) != (timeout_>0)) { //enable or disable if (time>0) Fl::add_timeout(1.0, timeout_cb, this); else Fl::remove_timeout(timeout_cb, this); } timeout_=time; update_cancel_label(); --timeout_; } void PinWindow::showModal() { if (NULL != window_) { window_->show(); Fl::run(); } Fl::check(); } void PinWindow::showModal(const int argc, char* argv[]) { if (NULL != window_) { window_->show(argc, argv); Fl::run(); } Fl::check(); } int PinWindow::init(const int cx, const int cy) { assert(NULL == window_); window_ = new Fl_Window(cx, cy, TITLE); Fl_RGB_Image app(&icon); window_->icon(&app); icon_ = new Fl_Box(10, 10, 64, 64); icon_->image(encrypt); message_ = new Fl_Box(79, 5, cx-99, 44, PROMPT); message_->align(Fl_Align(FL_ALIGN_LEFT_TOP | FL_ALIGN_WRAP | FL_ALIGN_INSIDE)); // left input_ = new Fl_Secret_Input(79, 59, cx-99, 25); input_->labeltype(FL_NO_LABEL); const int button_y = cy-40; ok_ = new Fl_Return_Button(cx-300, button_y, 120, 25, BUTTON_OK); ok_->callback(ok_cb, this); cancel_ = new Fl_Button(cx-160, button_y, 120, 25); update_cancel_label(); cancel_->callback(cancel_cb, this); window_->hotspot(input_); window_->set_modal(); return 84; }; void PinWindow::update_cancel_label() { if (timeout_ == 0) { cancel_->label(cancel_name_.c_str()); } else { const size_t len = cancel_name_.size()+strlen(timeout_format)+10+1; char *buf = new char[len]; snprintf(buf, len, timeout_format, cancel_name_.c_str(), timeout_); cancel_->copy_label(buf); delete[] buf; // no way to attach label } } void PinWindow::timeout_cb(void* val) { PinWindow *self = reinterpret_cast(val); if (self->timeout_ == 0) { cancel_cb(self->cancel_, self); } else { self->update_cancel_label(); --self->timeout_; Fl::repeat_timeout(1.0, timeout_cb, val); } } void PinWindow::cancel_cb(Fl_Widget *button, void *val) { PinWindow *self = reinterpret_cast(val); self->wipe(); self->release(); self->window_->hide(); } void PinWindow::ok_cb(Fl_Widget *button, void *val) { PinWindow *self = reinterpret_cast(val); self->release(); const char *passwd = self->input_->value(); size_t len = strlen(passwd)+1; self->passwd_ = reinterpret_cast(secmem_malloc(len)); if (NULL != self->passwd_) memcpy(self->passwd_, passwd, len); self->wipe(); self->window_->hide(); } void PinWindow::wipe() { int len = input_->size(); char* emul = new char[len+1]; for (int i=0; ireplace(0, len, emul, len); delete[] emul; input_->value(TITLE); // hide size too } PinWindow* PinWindow::create() { PinWindow* p = new PinWindow; p->init(410, 140); p->window_->end(); p->input_->take_focus(); return p; } diff --git a/fltk/pinwindow.h b/fltk/pinwindow.h index e1d009e..e1548da 100644 --- a/fltk/pinwindow.h +++ b/fltk/pinwindow.h @@ -1,108 +1,109 @@ /* pinwindow.h - PinWindow is a simple fltk dialog for entring password with timeout. if needed description (long text), error message, qualitybar and etc should used PassWindow. Copyright (C) 2016 Anatoly madRat L. Berenblit Written by Anatoly madRat L. Berenblit . This program 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. This program 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + SPDX-License-Identifier: GPL-2.0+ */ #ifndef __PINWINDOW_H__ #define __PINWINDOW_H__ #include "config.h" class Fl_Window; class Fl_Box; class Fl_Input; class Fl_Button; class Fl_Widget; #include #include class PinWindow { protected: static const char *TITLE; static const char *BUTTON_OK; static const char *BUTTON_CANCEL; static const char *PROMPT; protected: PinWindow(const PinWindow&); PinWindow& operator=(const PinWindow&); Fl_Window *window_; Fl_Box *icon_; Fl_Box *message_; Fl_Input *input_; Fl_Button *ok_, *cancel_; std::string cancel_name_; char *passwd_; // SECURE_MEMORY unsigned int timeout_; // click cancel if timeout public: virtual ~PinWindow(); static PinWindow* create(); inline const char* passwd() const { return passwd_; } virtual void timeout(unsigned int time); // 0 - infinity, seconds virtual void title(const char *title); virtual void ok(const char* ok); virtual void cancel(const char* cancel); virtual void prompt(const char *message); virtual void showModal(); virtual void showModal(const int argc, char* argv[]); protected: PinWindow(); void wipe(); // clear UI memory void release(); // clear secure memory void update_cancel_label(); virtual int init(const int cx, const int cy); //callbacks static void cancel_cb(Fl_Widget *button, void *val); static void ok_cb(Fl_Widget *button, void *val); static void timeout_cb(void*); // ISSUE: Fl_Window component in tinycore works only as Fl_Window::label(...); not Fl_Widget template void set_label(TWidget* widget, const char *label, const char *def) { assert(NULL != widget); // widget must be created if (NULL != widget) { if (NULL != label && 0 != *label) widget->copy_label(label); else widget->label(def); } }; }; #endif //#ifndef __PINWINDOW_H__ diff --git a/fltk/qualitypasswindow.cxx b/fltk/qualitypasswindow.cxx index 6d7f7cc..4529073 100644 --- a/fltk/qualitypasswindow.cxx +++ b/fltk/qualitypasswindow.cxx @@ -1,92 +1,93 @@ /* qualitypasswindow.cxx - QualityPassWindow pin entry with Password QualityBar and etc Copyright (C) 2016 Anatoly madRat L. Berenblit Written by Anatoly madRat L. Berenblit . This program 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. This program 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include "qualitypasswindow.h" const char *QualityPassWindow::QUALITY = "Quality"; QualityPassWindow::QualityPassWindow(QualityPassWindow::GetQualityFn qualify, void* ptr) : get_quality_(qualify) ,get_quality_user_(ptr) ,quality_(NULL) { assert(NULL != qualify); } void QualityPassWindow::input_changed(Fl_Widget *input, void *val) { QualityPassWindow *self = reinterpret_cast(val); assert(NULL != self->get_quality_); // function should be assigned in ctor assert(NULL != self->quality_); // quality progress bar must be created in init if (NULL != self->quality_ && NULL != self->get_quality_) { int result = self->get_quality_(self->input_->value(), self->get_quality_user_); bool isErr = (result <= 0); if (isErr) result = -result; self->quality_->selection_color(isErr?FL_RED:FL_GREEN); self->quality_->value(std::min(result, 100)); } } QualityPassWindow* QualityPassWindow::create(QualityPassWindow::GetQualityFn qualify, void *user) { QualityPassWindow *p = new QualityPassWindow(qualify, user); p->init(460, 215); p->window_->end(); p->input_->take_focus(); return p; } void QualityPassWindow::quality(const char *name) { set_label(quality_, name, QUALITY); } int QualityPassWindow::init(const int cx, const int cy) { int y = PassWindow::init(cx, cy); assert(window_ == Fl_Group::current()); // make_window should all add current input_->when(FL_WHEN_CHANGED); input_->callback(input_changed, this); y = input_->y() + input_->h(); quality_ = new Fl_Progress(input_->x(), y+5, input_->w(), 25, QUALITY); quality_->align(Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_CLIP | FL_ALIGN_WRAP)); quality_->maximum(100.1); quality_->minimum(0.0); y = quality_->y() + quality_->h(); error_->position(error_->x(), y+5); return error_->y() + error_->h(); } diff --git a/fltk/qualitypasswindow.h b/fltk/qualitypasswindow.h index 164c10d..eee5515 100644 --- a/fltk/qualitypasswindow.h +++ b/fltk/qualitypasswindow.h @@ -1,54 +1,55 @@ /* qualitypasswindow.h - QualityPassWindow pin entry with Password QualityBar and etc Copyright (C) 2016 Anatoly madRat L. Berenblit Written by Anatoly madRat L. Berenblit . This program 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. This program 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + SPDX-License-Identifier: GPL-2.0+ */ #ifndef __QUALITYPASSWINDOW_H__ #define __QUALITYPASSWINDOW_H__ #include "passwindow.h" class Fl_Progress; class QualityPassWindow : public PassWindow { protected: static const char *QUALITY; public: typedef int (*GetQualityFn)(const char *passwd, void *ptr); static QualityPassWindow* create(GetQualityFn qualify, void* user); void quality(const char *name); protected: QualityPassWindow(GetQualityFn qualify, void*); const GetQualityFn get_quality_; void* const get_quality_user_; Fl_Progress *quality_; virtual int init(const int cx, const int cy); static void input_changed(Fl_Widget *input, void *val); }; #endif //#ifndef __QUALITYPASSWINDOW_H__ diff --git a/gnome3/Makefile.am b/gnome3/Makefile.am index 9dc487a..6b7064f 100644 --- a/gnome3/Makefile.am +++ b/gnome3/Makefile.am @@ -1,37 +1,37 @@ # Makefile.am - PIN entry GTK+ frontend. # Copyright (C) 2002, 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +# along with this program; if not, see . +# SPDX-License-Identifier: GPL-2.0+ ## Process this file with automake to produce Makefile.in bin_PROGRAMS = pinentry-gnome3 if FALLBACK_CURSES ncurses_include = $(NCURSES_INCLUDE) libcurses = ../pinentry/libpinentry-curses.a $(LIBCURSES) $(LIBICONV) else ncurses_include = libcurses = endif AM_CPPFLAGS = $(COMMON_CFLAGS) $(GNOME3_CFLAGS) \ $(ncurses_include) -I$(top_srcdir)/secmem -I$(top_srcdir)/pinentry LDADD = ../pinentry/libpinentry.a ../secmem/libsecmem.a \ $(COMMON_LIBS) $(LIBCAP) $(GNOME3_LIBS) $(libcurses) pinentry_gnome3_SOURCES = pinentry-gnome3.c diff --git a/gnome3/pinentry-gnome3.c b/gnome3/pinentry-gnome3.c index 27d2132..b395676 100644 --- a/gnome3/pinentry-gnome3.c +++ b/gnome3/pinentry-gnome3.c @@ -1,549 +1,550 @@ /* pinentry-gnome3.c - Copyright (C) 2015 g10 Code GmbH - - pinentry-gnome-3 is a pinentry application for GNOME 3. It tries - to follow the Gnome Human Interface Guide as close as possible. - - This program 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. - - This program 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + * Copyright (C) 2015 g10 Code GmbH + * + * pinentry-gnome-3 is a pinentry application for GNOME 3. It tries + * to follow the Gnome Human Interface Guide as close as possible. + * + * This program 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. + * + * This program 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+ + */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "memory.h" #include "pinentry.h" #ifdef FALLBACK_CURSES #include "pinentry-curses.h" #endif #define PGMNAME "pinentry-gnome3" #ifndef VERSION # define VERSION #endif struct pe_gnome3_run_s { pinentry_t pinentry; GcrPrompt *prompt; GMainLoop *main_loop; int ret; guint timeout_id; int timed_out; }; static void pe_gcr_prompt_password_done (GObject *source_object, GAsyncResult *res, gpointer user_data); static void pe_gcr_prompt_confirm_done (GObject *source_object, GAsyncResult *res, gpointer user_data); static gboolean pe_gcr_timeout_done (gpointer user_data); static gchar * pinentry_utf8_validate (gchar *text) { gchar *result; if (!text) return NULL; if (g_utf8_validate (text, -1, NULL)) return g_strdup (text); /* Failure: Assume that it was encoded in the current locale and convert it to utf-8. */ result = g_locale_to_utf8 (text, -1, NULL, NULL, NULL); if (!result) { gchar *p; result = p = g_strdup (text); while (!g_utf8_validate (p, -1, (const gchar **) &p)) *p = '?'; } return result; } static void _propagate_g_error_to_pinentry (pinentry_t pe, GError *error, gpg_err_code_t code, const char *loc) { char *t; /* We can't return the result of g_strdup_printf directly, because * this needs to be g_free'd, but the users of PE (e.g., * pinentry_reset in pinentry/pinentry.c) use free. */ t = g_strdup_printf ("%d: %s", error->code, error->message); if (t) { /* If strdup fails, then PE->SPECIFIC_ERR_INFO will be NULL, * which is exactly what we want if strdup fails. So, there is * no need to check for failure. */ pe->specific_err_info = strdup (t); g_free (t); } else { pe->specific_err_info = NULL; } pe->specific_err = gpg_error (code); pe->specific_err_loc = loc; } static GcrPrompt * create_prompt (pinentry_t pe, int confirm) { GcrPrompt *prompt; GError *error = NULL; char *msg, *p; char window_id[32]; /* Create the prompt. */ prompt = GCR_PROMPT (gcr_system_prompt_open (pe->timeout ? pe->timeout : -1, NULL, &error)); if (! prompt) { /* this means the timeout elapsed, but no prompt was ever shown. */ if (error->code == GCR_SYSTEM_PROMPT_IN_PROGRESS) { fprintf (stderr, "Timeout: the Gcr system prompter was already in use.\n"); pe->specific_err_info = strdup ("Timeout: the Gcr system prompter was already in use."); /* not using GPG_ERR_TIMEOUT here because the user never saw a prompt: */ pe->specific_err = gpg_error (GPG_ERR_PIN_ENTRY); } else { fprintf (stderr, "couldn't create prompt for gnupg passphrase: %s\n", error->message); _propagate_g_error_to_pinentry (pe, error, GPG_ERR_CONFIGURATION, "gcr_system_prompt_open"); } g_error_free (error); return NULL; } /* Set the messages for the various buttons, etc. */ p = pinentry_get_title (pe); if (p) { msg = pinentry_utf8_validate (p); if (msg) { gcr_prompt_set_title (prompt, msg); g_free (msg); } free (p); } if (pe->description) { msg = pinentry_utf8_validate (pe->description); gcr_prompt_set_description (prompt, msg); g_free (msg); } /* An error occurred during the last prompt. */ if (pe->error) { msg = pinentry_utf8_validate (pe->error); gcr_prompt_set_warning (prompt, msg); g_free (msg); } if (! pe->prompt && confirm) gcr_prompt_set_message (prompt, "Message"); else if (! pe->prompt && ! confirm) gcr_prompt_set_message (prompt, "Enter Passphrase"); else { msg = pinentry_utf8_validate (pe->prompt); gcr_prompt_set_message (prompt, msg); g_free (msg); } if (! confirm) gcr_prompt_set_password_new (prompt, !!pe->repeat_passphrase); if (pe->ok || pe->default_ok) { msg = pinentry_utf8_validate (pe->ok ?: pe->default_ok); gcr_prompt_set_continue_label (prompt, msg); g_free (msg); } /* XXX: Disable this button if pe->one_button is set. */ if (pe->cancel || pe->default_cancel) { msg = pinentry_utf8_validate (pe->cancel ?: pe->default_cancel); gcr_prompt_set_cancel_label (prompt, msg); g_free (msg); } if (confirm && pe->notok) { /* XXX: Add support for the third option. */ } /* gcr expects a string; we have a int. see gcr's ui/frob-system-prompt.c for example conversion using %lu */ snprintf (window_id, sizeof (window_id), "%lu", (long unsigned int)pe->parent_wid); window_id[sizeof (window_id) - 1] = '\0'; gcr_prompt_set_caller_window (prompt, window_id); #ifdef HAVE_LIBSECRET if (! confirm && pe->allow_external_password_cache && pe->keyinfo) { if (pe->default_pwmngr) { msg = pinentry_utf8_validate (pe->default_pwmngr); gcr_prompt_set_choice_label (prompt, msg); g_free (msg); } else gcr_prompt_set_choice_label (prompt, "Automatically unlock this key, whenever I'm logged in"); } #endif return prompt; } static int gnome3_cmd_handler (pinentry_t pe) { struct pe_gnome3_run_s state; state.main_loop = g_main_loop_new (NULL, FALSE); if (!state.main_loop) { pe->specific_err_info = strdup ("Failed to create GMainLoop"); pe->specific_err = gpg_error (GPG_ERR_PIN_ENTRY); pe->specific_err_loc = "g_main_loop_new"; pe->canceled = 1; return -1; } state.pinentry = pe; state.ret = 0; state.timeout_id = 0; state.timed_out = 0; state.prompt = create_prompt (pe, !(pe->pin)); if (!state.prompt) { pe->canceled = 1; return -1; } if (pe->pin) gcr_prompt_password_async (state.prompt, NULL, pe_gcr_prompt_password_done, &state); else gcr_prompt_confirm_async (state.prompt, NULL, pe_gcr_prompt_confirm_done, &state); if (pe->timeout) state.timeout_id = g_timeout_add_seconds (pe->timeout, pe_gcr_timeout_done, &state); g_main_loop_run (state.main_loop); /* clean up state: */ if (state.timeout_id && !state.timed_out) g_source_destroy (g_main_context_find_source_by_id (NULL, state.timeout_id)); g_clear_object (&state.prompt); g_main_loop_unref (state.main_loop); return state.ret; }; static void pe_gcr_prompt_password_done (GObject *source_object, GAsyncResult *res, gpointer user_data) { struct pe_gnome3_run_s *state = user_data; GcrPrompt *prompt = GCR_PROMPT (source_object); if (state && prompt && state->prompt == prompt) { const char *password; GError *error = NULL; pinentry_t pe = state->pinentry; int ret = -1; /* "The returned password is valid until the next time a method is called to display another prompt." */ password = gcr_prompt_password_finish (prompt, res, &error); if ((! password && ! error) || (error && error->code == G_IO_ERROR_CANCELLED)) { /* operation was cancelled or timed out. */ ret = -1; if (state->timed_out) state->pinentry->specific_err = gpg_error (GPG_ERR_TIMEOUT); if (error) g_error_free (error); } else if (error) { _propagate_g_error_to_pinentry (pe, error, GPG_ERR_PIN_ENTRY, "gcr_system_password_finish"); g_error_free (error); ret = -1; } else { pinentry_setbufferlen (pe, strlen (password) + 1); if (pe->pin) strcpy (pe->pin, password); if (pe->repeat_passphrase) pe->repeat_okay = 1; #ifdef HAVE_LIBSECRET if (pe->allow_external_password_cache && pe->keyinfo) pe->may_cache_password = gcr_prompt_get_choice_chosen (prompt); #endif ret = 1; } state->ret = ret; } if (state) g_main_loop_quit (state->main_loop); } static void pe_gcr_prompt_confirm_done (GObject *source_object, GAsyncResult *res, gpointer user_data) { struct pe_gnome3_run_s *state = user_data; GcrPrompt *prompt = GCR_PROMPT (source_object); if (state && prompt && state->prompt == prompt) { GcrPromptReply reply; GError *error = NULL; pinentry_t pe = state->pinentry; int ret = -1; /* XXX: We don't support a third button! */ reply = gcr_prompt_confirm_finish (prompt, res, &error); if (error) { if (error->code == G_IO_ERROR_CANCELLED) { pe->canceled = 1; if (state->timed_out) state->pinentry->specific_err = gpg_error (GPG_ERR_TIMEOUT); } else _propagate_g_error_to_pinentry (state->pinentry, error, GPG_ERR_PIN_ENTRY, "gcr_system_confirm_finish"); g_error_free (error); ret = 0; } else if (reply == GCR_PROMPT_REPLY_CONTINUE /* XXX: Hack since gcr doesn't yet support one button message boxes treat cancel the same as okay. */ || pe->one_button) { /* Confirmation. */ ret = 1; } else /* GCR_PROMPT_REPLY_CANCEL */ { pe->canceled = 1; if (state->timed_out) state->pinentry->specific_err = gpg_error (GPG_ERR_TIMEOUT); ret = 0; } state->ret = ret; } if (state) g_main_loop_quit (state->main_loop); } static gboolean pe_gcr_timeout_done (gpointer user_data) { struct pe_gnome3_run_s *state = user_data; if (!state) return FALSE; state->timed_out = 1; gcr_prompt_close (state->prompt); return FALSE; } pinentry_cmd_handler_t pinentry_cmd_handler = gnome3_cmd_handler; /* Test whether there is a GNOME screensaver running that happens to * be locked. Note that if there is no GNOME screensaver running at * all the answer is still FALSE. */ static gboolean pe_gnome_screen_locked (void) { GDBusConnection *dbus; GError *error = NULL; GVariant *reply, *reply_bool; gboolean ret; dbus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); if (!dbus) { fprintf (stderr, "failed to connect to user session D-Bus (%d): %s", error ? error->code : -1, error ? error->message : ""); if (error) g_error_free (error); return FALSE; } /* this is intended to be the equivalent of: * dbus-send --print-reply=literal --session \ * --dest=org.gnome.ScreenSaver \ * /org/gnome/ScreenSaver \ * org.gnome.ScreenSaver.GetActive */ reply = g_dbus_connection_call_sync (dbus, "org.gnome.ScreenSaver", "/org/gnome/ScreenSaver", "org.gnome.ScreenSaver", "GetActive", NULL, ((const GVariantType *) "(b)"), G_DBUS_CALL_FLAGS_NO_AUTO_START, 0, NULL, &error); g_object_unref(dbus); if (!reply) { /* G_IO_ERROR_TIMED_OUT is the expected response when there is * no gnome screensaver at all, don't be noisy in that case: */ if (!(error && error->code == G_IO_ERROR_TIMED_OUT)) fprintf (stderr, "Failed to get d-bus reply for org.gnome.ScreenSaver.GetActive (%d): %s\n", error ? error->code : -1, error ? error->message : ""); if (error) g_error_free (error); return FALSE; } reply_bool = g_variant_get_child_value (reply, 0); if (!reply_bool) { fprintf (stderr, "Failed to get d-bus boolean from org.gnome.ScreenSaver.GetActive; assuming screensaver is not locked\n"); ret = FALSE; } else { ret = g_variant_get_boolean (reply_bool); g_variant_unref (reply_bool); } g_variant_unref (reply); return ret; } /* Test whether we can create a system prompt or not. This briefly * does create a system prompt, which blocks other tools from making * the same request concurrently, so we just create it to test if it is * available, and quickly close it. */ static int pe_gcr_system_prompt_available (void) { GcrSystemPrompt *prompt; GError *error = NULL; int ret = 0; prompt = GCR_SYSTEM_PROMPT (gcr_system_prompt_open (0, NULL, &error)); if (prompt) { ret = 1; if (!gcr_system_prompt_close (prompt, NULL, &error)) fprintf (stderr, "failed to close test Gcr System Prompt (%d): %s\n", error ? error->code : -1, error ? error->message : ""); g_clear_object (&prompt); } else if (error && error->code == GCR_SYSTEM_PROMPT_IN_PROGRESS) { /* This one particular failure is OK; we're clearly capable of * making a system prompt, even though someone else has the * system prompter right now: */ ret = 1; } if (error) g_error_free (error); return ret; } int main (int argc, char *argv[]) { pinentry_init (PGMNAME); #ifdef FALLBACK_CURSES if (!getenv ("DBUS_SESSION_BUS_ADDRESS")) { fprintf (stderr, "No $DBUS_SESSION_BUS_ADDRESS found," " falling back to curses\n"); pinentry_cmd_handler = curses_cmd_handler; pinentry_set_flavor_flag ("curses"); } else if (!pe_gcr_system_prompt_available ()) { fprintf (stderr, "No Gcr System Prompter available," " falling back to curses\n"); pinentry_cmd_handler = curses_cmd_handler; pinentry_set_flavor_flag ("curses"); } else if (pe_gnome_screen_locked ()) { fprintf (stderr, "GNOME screensaver is locked," " falling back to curses\n"); pinentry_cmd_handler = curses_cmd_handler; pinentry_set_flavor_flag ("curses"); } #endif pinentry_parse_opts (argc, argv); if (pinentry_loop ()) return 1; return 0; } diff --git a/gtk+-2/Makefile.am b/gtk+-2/Makefile.am index e9bff80..a908f81 100644 --- a/gtk+-2/Makefile.am +++ b/gtk+-2/Makefile.am @@ -1,37 +1,37 @@ # Makefile.am - PIN entry GTK+ frontend. # Copyright (C) 2002, 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +# along with this program; if not, see . +# SPDX-License-Identifier: GPL-2.0+ ## Process this file with automake to produce Makefile.in bin_PROGRAMS = pinentry-gtk-2 if FALLBACK_CURSES ncurses_include = $(NCURSES_INCLUDE) libcurses = ../pinentry/libpinentry-curses.a $(LIBCURSES) $(LIBICONV) else ncurses_include = libcurses = endif AM_CPPFLAGS = $(COMMON_CFLAGS) $(GTK2_CFLAGS) $(ncurses_include) \ -I$(top_srcdir)/secmem -I$(top_srcdir)/pinentry LDADD = ../pinentry/libpinentry.a ../secmem/libsecmem.a \ $(COMMON_LIBS) $(LIBCAP) $(GTK2_LIBS) $(libcurses) pinentry_gtk_2_SOURCES = pinentry-gtk-2.c diff --git a/gtk+-2/pinentry-gtk-2.c b/gtk+-2/pinentry-gtk-2.c index 89728c4..c803628 100644 --- a/gtk+-2/pinentry-gtk-2.c +++ b/gtk+-2/pinentry-gtk-2.c @@ -1,973 +1,974 @@ /* pinentry-gtk-2.c - Copyright (C) 1999 Robert Bihlmeyer - Copyright (C) 2001, 2002, 2007, 2015 g10 Code GmbH - Copyright (C) 2004 by Albrecht Dreß - - pinentry-gtk-2 is a pinentry application for the Gtk+-2 widget set. - It tries to follow the Gnome Human Interface Guide as close as - possible. - - This program 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. - - This program 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + * Copyright (C) 1999 Robert Bihlmeyer + * Copyright (C) 2001, 2002, 2007, 2015 g10 Code GmbH + * Copyright (C) 2004 by Albrecht Dreß + * + * pinentry-gtk-2 is a pinentry application for the Gtk+-2 widget set. + * It tries to follow the Gnome Human Interface Guide as close as + * possible. + * + * This program 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. + * + * This program 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+ + */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7 ) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wstrict-prototypes" #endif #include #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7 ) # pragma GCC diagnostic pop #endif #include #include #include #include #include #include #ifdef HAVE_GETOPT_H #include #else #include "getopt.h" #endif /* HAVE_GETOPT_H */ #include "pinentry.h" #ifdef FALLBACK_CURSES #include "pinentry-curses.h" #endif #define PGMNAME "pinentry-gtk2" #ifndef VERSION # define VERSION #endif static pinentry_t pinentry; static int grab_failed; static int passphrase_ok; typedef enum { CONFIRM_CANCEL, CONFIRM_OK, CONFIRM_NOTOK } confirm_value_t; static confirm_value_t confirm_value; static GtkWindow *mainwindow; static GtkWidget *entry; static GtkWidget *repeat_entry; static GtkWidget *error_label; static GtkWidget *qualitybar; #if !GTK_CHECK_VERSION (2, 12, 0) static GtkTooltips *tooltips; #endif static gboolean got_input; static guint timeout_source; static int confirm_mode; /* Gnome hig small and large space in pixels. */ #define HIG_TINY 2 #define HIG_SMALL 6 #define HIG_LARGE 12 /* The text shown in the quality bar when no text is shown. This is not * the empty string, because with an empty string the height of * the quality bar is less than with a non-empty string. This results * in ugly layout changes when the text changes from non-empty to empty * and vice versa. */ #define QUALITYBAR_EMPTY_TEXT " " /* Constrain size of the window the window should not shrink beyond the requisition, and should not grow vertically. */ static void constrain_size (GtkWidget *win, GtkRequisition *req, gpointer data) { static gint width, height; GdkGeometry geo; (void)data; if (req->width == width && req->height == height) return; width = req->width; height = req->height; geo.min_width = width; /* This limit is arbitrary, but INT_MAX breaks other things */ geo.max_width = 10000; geo.min_height = geo.max_height = height; gtk_window_set_geometry_hints (GTK_WINDOW (win), NULL, &geo, GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE); } /* Realize the window as transient. This makes the window a modal dialog to the root window, which helps the window manager. See the following quote from: https://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2512420 Implementing enhanced support for application transient windows If the WM_TRANSIENT_FOR property is set to None or Root window, the window should be treated as a transient for all other windows in the same group. It has been noted that this is a slight ICCCM violation, but as this behavior is pretty standard for many toolkits and window managers, and is extremely unlikely to break anything, it seems reasonable to document it as standard. */ static void make_transient (GtkWidget *win, GdkEvent *event, gpointer data) { GdkScreen *screen; GdkWindow *root; (void)event; (void)data; /* Make window transient for the root window. */ screen = gdk_screen_get_default (); root = gdk_screen_get_root_window (screen); gdk_window_set_transient_for (gtk_widget_get_window (win), root); } /* Convert GdkGrabStatus to string. */ static const char * grab_strerror (GdkGrabStatus status) { switch (status) { case GDK_GRAB_SUCCESS: return "success"; case GDK_GRAB_ALREADY_GRABBED: return "already grabbed"; case GDK_GRAB_INVALID_TIME: return "invalid time"; case GDK_GRAB_NOT_VIEWABLE: return "not viewable"; case GDK_GRAB_FROZEN: return "frozen"; } return "unknown"; } /* Grab the keyboard for maximum security */ static int grab_keyboard (GtkWidget *win, GdkEvent *event, gpointer data) { GdkGrabStatus err; int tries = 0, max_tries = 4096; (void)data; if (! pinentry->grab) return FALSE; do err = gdk_keyboard_grab (gtk_widget_get_window (win), FALSE, gdk_event_get_time (event)); while (tries++ < max_tries && err == GDK_GRAB_NOT_VIEWABLE); if (err) { g_critical ("could not grab keyboard: %s (%d)", grab_strerror (err), err); grab_failed = 1; gtk_main_quit (); } if (tries > 1) g_warning ("it took %d tries to grab the keyboard", tries); return FALSE; } /* Grab the pointer to prevent the user from accidentally locking herself out of her graphical interface. */ static int grab_pointer (GtkWidget *win, GdkEvent *event, gpointer data) { GdkGrabStatus err; GdkCursor *cursor; int tries = 0, max_tries = 4096; (void)data; /* Change the cursor for the duration of the grab to indicate that * something is going on. The fvwm window manager grabs the pointer * for a short time and thus we may end up with the already grabbed * error code. Actually this error code should be used to detect a * malicious grabbing application but with fvwm this renders * Pinentry only unusable. Thus we try again several times also for * that error code. See Debian bug 850708 for details. */ /* XXX: It would be nice to have a key cursor, unfortunately there is none readily available. */ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (win), GDK_DOT); do err = gdk_pointer_grab (gtk_widget_get_window (win), TRUE, 0 /* event mask */, NULL /* confine to */, cursor, gdk_event_get_time (event)); while (tries++ < max_tries && (err == GDK_GRAB_NOT_VIEWABLE || err == GDK_GRAB_ALREADY_GRABBED)); if (err) { g_critical ("could not grab pointer: %s (%d)", grab_strerror (err), err); grab_failed = 1; gtk_main_quit (); } if (tries > 1) g_warning ("it took %d tries to grab the pointer", tries); return FALSE; } /* Remove all grabs and restore the windows transient state. */ static int ungrab_inputs (GtkWidget *win, GdkEvent *event, gpointer data) { (void)data; gdk_keyboard_ungrab (gdk_event_get_time (event)); gdk_pointer_ungrab (gdk_event_get_time (event)); /* Unmake window transient for the root window. */ /* gdk_window_set_transient_for cannot be used with parent = NULL to unset transient hint (unlike gtk_ version which can). Replacement code is taken from gtk_window_transient_parent_unrealized. */ gdk_property_delete (gtk_widget_get_window (win), gdk_atom_intern_static_string ("WM_TRANSIENT_FOR")); return FALSE; } static int delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) { (void)widget; (void)event; (void)data; pinentry->close_button = 1; gtk_main_quit (); return TRUE; } /* A button was clicked. DATA indicates which button was clicked (i.e., the appropriate action) and is either CONFIRM_CANCEL, CONFIRM_OK or CONFIRM_NOTOK. */ static void button_clicked (GtkWidget *widget, gpointer data) { (void)widget; if (confirm_mode) { confirm_value = (confirm_value_t) data; gtk_main_quit (); return; } if (data) { const char *s, *s2; /* Okay button or enter used in text field. */ s = gtk_entry_get_text (GTK_ENTRY (entry)); if (!s) s = ""; if (pinentry->repeat_passphrase && repeat_entry) { s2 = gtk_entry_get_text (GTK_ENTRY (repeat_entry)); if (!s2) s2 = ""; if (strcmp (s, s2)) { gtk_label_set_text (GTK_LABEL (error_label), pinentry->repeat_error_string? pinentry->repeat_error_string: "not correctly repeated"); gtk_widget_grab_focus (entry); return; /* again */ } pinentry->repeat_okay = 1; } passphrase_ok = 1; pinentry_setbufferlen (pinentry, strlen (s) + 1); if (pinentry->pin) strcpy (pinentry->pin, s); } gtk_main_quit (); } static void enter_callback (GtkWidget *widget, GtkWidget *next_widget) { if (next_widget) gtk_widget_grab_focus (next_widget); else button_clicked (widget, (gpointer) CONFIRM_OK); } static void cancel_callback (GtkAccelGroup *acc, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data) { (void)acc; (void)keyval; (void)modifier; (void)data; button_clicked (GTK_WIDGET (accelerable), (gpointer)CONFIRM_CANCEL); } static gchar * pinentry_utf8_validate (gchar *text) { gchar *result; if (!text) return NULL; if (g_utf8_validate (text, -1, NULL)) return g_strdup (text); /* Failure: Assume that it was encoded in the current locale and convert it to utf-8. */ result = g_locale_to_utf8 (text, -1, NULL, NULL, NULL); if (!result) { gchar *p; result = p = g_strdup (text); while (!g_utf8_validate (p, -1, (const gchar **) &p)) *p = '?'; } return result; } /* Handler called for "changed". We use it to update the quality indicator. */ static void changed_text_handler (GtkWidget *widget) { char textbuf[50]; const char *s; int length; int percent; GdkColor color = { 0, 0, 0, 0}; got_input = TRUE; if (pinentry->repeat_passphrase && repeat_entry) { gtk_entry_set_text (GTK_ENTRY (repeat_entry), ""); gtk_label_set_text (GTK_LABEL (error_label), ""); } if (!qualitybar || !pinentry->quality_bar) return; s = gtk_entry_get_text (GTK_ENTRY (widget)); if (!s) s = ""; length = strlen (s); percent = length? pinentry_inq_quality (pinentry, s, length) : 0; if (!length) { strcpy(textbuf, QUALITYBAR_EMPTY_TEXT); color.red = 0xffff; } else if (percent < 0) { snprintf (textbuf, sizeof textbuf, "(%d%%)", -percent); textbuf[sizeof textbuf -1] = 0; color.red = 0xffff; percent = -percent; } else { snprintf (textbuf, sizeof textbuf, "%d%%", percent); textbuf[sizeof textbuf -1] = 0; color.green = 0xffff; } gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (qualitybar), (double)percent/100.0); gtk_progress_bar_set_text (GTK_PROGRESS_BAR (qualitybar), textbuf); gtk_widget_modify_bg (qualitybar, GTK_STATE_PRELIGHT, &color); } #ifdef HAVE_LIBSECRET static void may_save_passphrase_toggled (GtkWidget *widget, gpointer data) { GtkToggleButton *button = GTK_TOGGLE_BUTTON (widget); pinentry_t ctx = (pinentry_t) data; ctx->may_cache_password = gtk_toggle_button_get_active (button); } #endif /* Return TRUE if it is okay to unhide the entry. */ static int confirm_unhiding (void) { const char *s; GtkWidget *dialog; int result; char *message, *show_btn_label; s = gtk_entry_get_text (GTK_ENTRY (entry)); if (!s || !*s) return TRUE; /* Nothing entered - go ahead an unhide. */ message = pinentry_utf8_validate (pinentry->default_cf_visi); if (!message) { message = g_strdup ("Do you really want to make " "your passphrase visible on the screen?"); } show_btn_label = pinentry_utf8_validate (pinentry->default_tt_visi); if (!show_btn_label) { show_btn_label = g_strdup ("Make passphrase visible"); } dialog = gtk_message_dialog_new (GTK_WINDOW (mainwindow), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, "%s", message); gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, show_btn_label, GTK_RESPONSE_OK, NULL); result = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK); gtk_widget_destroy (dialog); g_free (message); g_free (show_btn_label); return result; } static void show_hide_button_toggled (GtkWidget *widget, gpointer data) { GtkToggleButton *button = GTK_TOGGLE_BUTTON (widget); GtkWidget *label = data; const char *text; char *tooltip; gboolean reveal; if (!gtk_toggle_button_get_active (button) || !confirm_unhiding ()) { text = "abc"; tooltip = pinentry_utf8_validate (pinentry->default_tt_visi); if (!tooltip) { tooltip = g_strdup ("Make the passphrase visible"); } gtk_toggle_button_set_active (button, FALSE); reveal = FALSE; } else { text = "***"; tooltip = pinentry_utf8_validate (pinentry->default_tt_hide); if (!tooltip) { tooltip = g_strdup ("Hide the passphrase"); } reveal = TRUE; } gtk_entry_set_visibility (GTK_ENTRY (entry), reveal); if (repeat_entry) { gtk_entry_set_visibility (GTK_ENTRY (repeat_entry), reveal); } gtk_label_set_markup (GTK_LABEL(label), text); if (!pinentry->grab) { gtk_widget_set_tooltip_text (GTK_WIDGET(button), tooltip); } g_free (tooltip); } static gboolean timeout_cb (gpointer data) { pinentry_t pe = (pinentry_t)data; if (!got_input) { gtk_main_quit (); if (pe) pe->specific_err = gpg_error (GPG_ERR_TIMEOUT); } /* Don't run again. */ timeout_source = 0; return FALSE; } static GtkWidget * create_show_hide_button (void) { GtkWidget *button, *label; label = gtk_label_new (NULL); button = gtk_toggle_button_new (); show_hide_button_toggled (button, label); gtk_container_add (GTK_CONTAINER (button), label); g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (show_hide_button_toggled), label); return button; } static GtkWidget * create_window (pinentry_t ctx) { GtkWidget *w; GtkWidget *win, *box; GtkWidget *wvbox, *chbox, *bbox; GtkAccelGroup *acc; gchar *msg; char *p; repeat_entry = NULL; #if !GTK_CHECK_VERSION (2, 12, 0) tooltips = gtk_tooltips_new (); #endif /* FIXME: check the grabbing code against the one we used with the old gpg-agent */ win = gtk_window_new (GTK_WINDOW_TOPLEVEL); mainwindow = GTK_WINDOW (win); acc = gtk_accel_group_new (); g_signal_connect (G_OBJECT (win), "delete_event", G_CALLBACK (delete_event), NULL); #if 0 g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (gtk_main_quit), NULL); #endif g_signal_connect (G_OBJECT (win), "size-request", G_CALLBACK (constrain_size), NULL); g_signal_connect (G_OBJECT (win), "realize", G_CALLBACK (make_transient), NULL); if (!confirm_mode) { /* We need to grab the keyboard when its visible! not when its mapped (there is a difference) */ g_object_set (G_OBJECT(win), "events", GDK_VISIBILITY_NOTIFY_MASK | GDK_STRUCTURE_MASK, NULL); g_signal_connect (G_OBJECT (win), pinentry->grab ? "visibility-notify-event" : "focus-in-event", G_CALLBACK (grab_keyboard), NULL); if (pinentry->grab) g_signal_connect (G_OBJECT (win), "visibility-notify-event", G_CALLBACK (grab_pointer), NULL); g_signal_connect (G_OBJECT (win), pinentry->grab ? "unmap-event" : "focus-out-event", G_CALLBACK (ungrab_inputs), NULL); } gtk_window_add_accel_group (GTK_WINDOW (win), acc); wvbox = gtk_vbox_new (FALSE, HIG_LARGE * 2); gtk_container_add (GTK_CONTAINER (win), wvbox); gtk_container_set_border_width (GTK_CONTAINER (wvbox), HIG_LARGE); chbox = gtk_hbox_new (FALSE, HIG_LARGE); gtk_box_pack_start (GTK_BOX (wvbox), chbox, FALSE, FALSE, 0); w = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION, GTK_ICON_SIZE_DIALOG); gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0); gtk_box_pack_start (GTK_BOX (chbox), w, FALSE, FALSE, 0); box = gtk_vbox_new (FALSE, HIG_SMALL); gtk_box_pack_start (GTK_BOX (chbox), box, TRUE, TRUE, 0); p = pinentry_get_title (pinentry); if (p) { msg = pinentry_utf8_validate (p); if (msg) gtk_window_set_title (GTK_WINDOW(win), msg); g_free (msg); free (p); } if (pinentry->description) { msg = pinentry_utf8_validate (pinentry->description); w = gtk_label_new (msg); g_free (msg); gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5); gtk_label_set_line_wrap (GTK_LABEL (w), TRUE); gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0); } if (!confirm_mode && (pinentry->error || pinentry->repeat_passphrase)) { /* With the repeat passphrase option we need to create the label in any case so that it may later be updated by the error message. */ GdkColor color = { 0, 0xffff, 0, 0 }; if (pinentry->error) msg = pinentry_utf8_validate (pinentry->error); else msg = ""; error_label = gtk_label_new (msg); if (pinentry->error) g_free (msg); gtk_misc_set_alignment (GTK_MISC (error_label), 0.0, 0.5); gtk_label_set_line_wrap (GTK_LABEL (error_label), TRUE); gtk_box_pack_start (GTK_BOX (box), error_label, TRUE, FALSE, 0); gtk_widget_modify_fg (error_label, GTK_STATE_NORMAL, &color); } qualitybar = NULL; if (!confirm_mode) { int nrow; GtkWidget *table, *hbox; nrow = 1; if (pinentry->quality_bar) nrow++; if (pinentry->repeat_passphrase) nrow++; table = gtk_table_new (nrow, 2, FALSE); nrow = 0; gtk_box_pack_start (GTK_BOX (box), table, FALSE, FALSE, 0); if (pinentry->prompt) { msg = pinentry_utf8_validate (pinentry->prompt); w = gtk_label_new_with_mnemonic (msg); g_free (msg); gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), w, 0, 1, nrow, nrow+1, GTK_FILL, GTK_FILL, 4, 0); } entry = gtk_entry_new (); gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE); /* Allow the user to set a narrower invisible character than the large dot currently used by GTK. Examples are "•★Ⓐ" */ if (pinentry->invisible_char) { gunichar *uch; /*""*/ uch = g_utf8_to_ucs4 (pinentry->invisible_char, -1, NULL, NULL, NULL); if (uch) { gtk_entry_set_invisible_char (GTK_ENTRY (entry), *uch); g_free (uch); } } gtk_widget_set_size_request (entry, 200, -1); g_signal_connect (G_OBJECT (entry), "changed", G_CALLBACK (changed_text_handler), entry); hbox = gtk_hbox_new (FALSE, HIG_TINY); gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); /* There was a wish in issue #2139 that this button should not be part of the tab order (focus_order). This should still be added. */ w = create_show_hide_button (); gtk_box_pack_end (GTK_BOX (hbox), w, FALSE, FALSE, 0); gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, nrow, nrow+1, GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0); gtk_widget_show (entry); nrow++; if (pinentry->quality_bar) { msg = pinentry_utf8_validate (pinentry->quality_bar); w = gtk_label_new (msg); g_free (msg); gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), w, 0, 1, nrow, nrow+1, GTK_FILL, GTK_FILL, 4, 0); qualitybar = gtk_progress_bar_new(); gtk_progress_bar_set_text (GTK_PROGRESS_BAR (qualitybar), QUALITYBAR_EMPTY_TEXT); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (qualitybar), 0.0); if (pinentry->quality_bar_tt && !pinentry->grab) { #if !GTK_CHECK_VERSION (2, 12, 0) gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltips), qualitybar, pinentry->quality_bar_tt, ""); #else gtk_widget_set_tooltip_text (qualitybar, pinentry->quality_bar_tt); #endif } gtk_table_attach (GTK_TABLE (table), qualitybar, 1, 2, nrow, nrow+1, GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0); nrow++; } if (pinentry->repeat_passphrase) { msg = pinentry_utf8_validate (pinentry->repeat_passphrase); w = gtk_label_new (msg); g_free (msg); gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), w, 0, 1, nrow, nrow+1, GTK_FILL, GTK_FILL, 4, 0); repeat_entry = gtk_entry_new (); gtk_entry_set_visibility (GTK_ENTRY (repeat_entry), FALSE); gtk_widget_set_size_request (repeat_entry, 200, -1); gtk_table_attach (GTK_TABLE (table), repeat_entry, 1, 2, nrow, nrow+1, GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0); gtk_widget_show (repeat_entry); nrow++; g_signal_connect (G_OBJECT (repeat_entry), "activate", G_CALLBACK (enter_callback), NULL); } /* When the user presses enter in the entry widget, the widget is activated. If we have a repeat entry, send the focus to it. Otherwise, activate the "Ok" button. */ g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (enter_callback), repeat_entry); } bbox = gtk_hbutton_box_new (); gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END); gtk_box_set_spacing (GTK_BOX (bbox), 6); gtk_box_pack_start (GTK_BOX (wvbox), bbox, TRUE, FALSE, 0); #ifdef HAVE_LIBSECRET if (ctx->allow_external_password_cache && ctx->keyinfo) /* Only show this if we can cache passwords and we have a stable key identifier. */ { if (pinentry->default_pwmngr) { msg = pinentry_utf8_validate (pinentry->default_pwmngr); w = gtk_check_button_new_with_mnemonic (msg); g_free (msg); } else w = gtk_check_button_new_with_label ("Save passphrase using libsecret"); /* Make sure it is off by default. */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), FALSE); gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0); gtk_widget_show (w); g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (may_save_passphrase_toggled), (gpointer) ctx); } #endif if (!pinentry->one_button) { if (pinentry->cancel) { msg = pinentry_utf8_validate (pinentry->cancel); w = gtk_button_new_with_mnemonic (msg); g_free (msg); } else if (pinentry->default_cancel) { GtkWidget *image; msg = pinentry_utf8_validate (pinentry->default_cancel); w = gtk_button_new_with_mnemonic (msg); g_free (msg); image = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON); if (image) gtk_button_set_image (GTK_BUTTON (w), image); } else w = gtk_button_new_from_stock (GTK_STOCK_CANCEL); gtk_container_add (GTK_CONTAINER (bbox), w); g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (button_clicked), (gpointer) CONFIRM_CANCEL); gtk_accel_group_connect (acc, GDK_KEY_Escape, 0, 0, g_cclosure_new (G_CALLBACK (cancel_callback), NULL, NULL)); } if (confirm_mode && !pinentry->one_button && pinentry->notok) { msg = pinentry_utf8_validate (pinentry->notok); w = gtk_button_new_with_mnemonic (msg); g_free (msg); gtk_container_add (GTK_CONTAINER (bbox), w); g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (button_clicked), (gpointer) CONFIRM_NOTOK); } if (pinentry->ok) { msg = pinentry_utf8_validate (pinentry->ok); w = gtk_button_new_with_mnemonic (msg); g_free (msg); } else if (pinentry->default_ok) { GtkWidget *image; msg = pinentry_utf8_validate (pinentry->default_ok); w = gtk_button_new_with_mnemonic (msg); g_free (msg); image = gtk_image_new_from_stock (GTK_STOCK_OK, GTK_ICON_SIZE_BUTTON); if (image) gtk_button_set_image (GTK_BUTTON (w), image); } else w = gtk_button_new_from_stock (GTK_STOCK_OK); gtk_container_add (GTK_CONTAINER(bbox), w); if (!confirm_mode) { GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT); gtk_widget_grab_default (w); } g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK(button_clicked), (gpointer) CONFIRM_OK); gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER); gtk_window_set_keep_above (GTK_WINDOW (win), TRUE); gtk_widget_show_all (win); gtk_window_present (GTK_WINDOW (win)); /* Make sure it has the focus. */ if (pinentry->timeout > 0) timeout_source = g_timeout_add (pinentry->timeout*1000, timeout_cb, pinentry); return win; } static int gtk_cmd_handler (pinentry_t pe) { GtkWidget *w; int want_pass = !!pe->pin; got_input = FALSE; pinentry = pe; confirm_value = CONFIRM_CANCEL; passphrase_ok = 0; confirm_mode = want_pass ? 0 : 1; w = create_window (pe); gtk_main (); gtk_widget_destroy (w); while (gtk_events_pending ()) gtk_main_iteration (); if (timeout_source) /* There is a timer running. Cancel it. */ { g_source_remove (timeout_source); timeout_source = 0; } if (confirm_value == CONFIRM_CANCEL || grab_failed) pe->canceled = 1; pinentry = NULL; if (want_pass) { if (passphrase_ok && pe->pin) return strlen (pe->pin); else return -1; } else return (confirm_value == CONFIRM_OK) ? 1 : 0; } pinentry_cmd_handler_t pinentry_cmd_handler = gtk_cmd_handler; int main (int argc, char *argv[]) { pinentry_init (PGMNAME); #ifdef FALLBACK_CURSES if (pinentry_have_display (argc, argv)) { if (! gtk_init_check (&argc, &argv)) { pinentry_cmd_handler = curses_cmd_handler; pinentry_set_flavor_flag ("curses"); } } else { pinentry_cmd_handler = curses_cmd_handler; pinentry_set_flavor_flag ("curses"); } #else gtk_init (&argc, &argv); #endif pinentry_parse_opts (argc, argv); if (pinentry_loop ()) return 1; return 0; } diff --git a/pinentry/Makefile.am b/pinentry/Makefile.am index a99f12e..0866cce 100644 --- a/pinentry/Makefile.am +++ b/pinentry/Makefile.am @@ -1,44 +1,44 @@ # Pinentry support library Makefile # Copyright (C) 2002, 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +# along with this program; if not, see . +# SPDX-License-Identifier: GPL-2.0+ ## Process this file with automake to produce Makefile.in EXTRA_DIST = if BUILD_LIBPINENTRY_CURSES pinentry_curses = libpinentry-curses.a else pinentry_curses = endif if BUILD_LIBPINENTRY_EMACS pinentry_emacs_sources = pinentry-emacs.h pinentry-emacs.c else pinentry_emacs_sources = endif noinst_LIBRARIES = libpinentry.a $(pinentry_curses) LDADD = $(COMMON_LIBS) AM_CPPFLAGS = $(COMMON_CFLAGS) -I$(top_srcdir)/secmem libpinentry_a_SOURCES = pinentry.h pinentry.c argparse.c argparse.h \ password-cache.h password-cache.c $(pinentry_emacs_sources) libpinentry_curses_a_SOURCES = pinentry-curses.h pinentry-curses.c libpinentry_curses_a_CFLAGS = @NCURSES_CFLAGS@ diff --git a/pinentry/argparse.c b/pinentry/argparse.c index f6c160c..ee9c08b 100644 --- a/pinentry/argparse.c +++ b/pinentry/argparse.c @@ -1,1607 +1,1608 @@ /* [argparse.c wk 17.06.97] Argument Parser for option handling * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc. * Copyright (C) 1997-2001, 2006-2008, 2013-2015 Werner Koch * * This file is part of JNLIB, which is a subsystem of GnuPG. * * JNLIB is free software; you can redistribute it and/or modify it * under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - 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. * * or both in parallel, as here. * * JNLIB 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . + * SPDX-License-Identifier: (GPL-2.0+ OR LGPL-3.0+) */ /* This file may be used as part of GnuPG or standalone. A GnuPG build is detected by the presence of the macro GNUPG_MAJOR_VERSION. Some feature are only availalbe in the GnuPG build mode. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #ifdef GNUPG_MAJOR_VERSION # include "libjnlib-config.h" # include "mischelp.h" # include "stringhelp.h" # include "logging.h" # ifdef JNLIB_NEED_UTF8CONV # include "utf8conv.h" # endif #endif /*GNUPG_MAJOR_VERSION*/ #include "argparse.h" /* GnuPG uses GPLv3+ but a standalone version of this defaults to GPLv2+ because that is the license of this file. Change this if you include it in a program which uses GPLv3. If you don't want to set a a copyright string for your usage() you may also hardcode it here. */ #ifndef GNUPG_MAJOR_VERSION # define ARGPARSE_GPL_VERSION 2 # define ARGPARSE_CRIGHT_STR "Copyright (C) YEAR NAME" #else /* Used by GnuPG */ # define ARGPARSE_GPL_VERSION 3 # define ARGPARSE_CRIGHT_STR "Copyright (C) 2015 Free Software Foundation, Inc." #endif /*GNUPG_MAJOR_VERSION*/ /* Replacements for standalone builds. */ #ifndef GNUPG_MAJOR_VERSION # ifndef _ # define _(a) (a) # endif # ifndef DIM # define DIM(v) (sizeof(v)/sizeof((v)[0])) # endif # define jnlib_malloc(a) malloc ((a)) # define jnlib_realloc(a,b) realloc ((a), (b)) # define jnlib_strdup(a) strdup ((a)) # define jnlib_free(a) free ((a)) # define jnlib_log_error my_log_error # define jnlib_log_bug my_log_bug # define trim_spaces(a) my_trim_spaces ((a)) # define map_static_macro_string(a) (a) #endif /*!GNUPG_MAJOR_VERSION*/ #define ARGPARSE_STR(v) #v #define ARGPARSE_STR2(v) ARGPARSE_STR(v) /* Replacements for standalone builds. */ #ifndef GNUPG_MAJOR_VERSION static void my_log_error (const char *fmt, ...) { va_list arg_ptr ; va_start (arg_ptr, fmt); fprintf (stderr, "%s: ", strusage (11)); vfprintf (stderr, fmt, arg_ptr); va_end (arg_ptr); } static void my_log_bug (const char *fmt, ...) { va_list arg_ptr ; va_start (arg_ptr, fmt); fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11)); vfprintf (stderr, fmt, arg_ptr); va_end (arg_ptr); abort (); } static char * my_trim_spaces (char *str) { char *string, *p, *mark; string = str; /* Find first non space character. */ for (p=string; *p && isspace (*(unsigned char*)p) ; p++) ; /* Move characters. */ for ((mark = NULL); (*string = *p); string++, p++) if (isspace (*(unsigned char*)p)) { if (!mark) mark = string; } else mark = NULL; if (mark) *mark = '\0' ; /* Remove trailing spaces. */ return str ; } #endif /*!GNUPG_MAJOR_VERSION*/ /********************************* * @Summary arg_parse * #include "argparse.h" * * typedef struct { * char *argc; pointer to argc (value subject to change) * char ***argv; pointer to argv (value subject to change) * unsigned flags; Global flags (DO NOT CHANGE) * int err; print error about last option * 1 = warning, 2 = abort * int r_opt; return option * int r_type; type of return value (0 = no argument found) * union { * int ret_int; * long ret_long * ulong ret_ulong; * char *ret_str; * } r; Return values * struct { * int idx; * const char *last; * void *aliases; * } internal; DO NOT CHANGE * } ARGPARSE_ARGS; * * typedef struct { * int short_opt; * const char *long_opt; * unsigned flags; * } ARGPARSE_OPTS; * * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts ); * * @Description * This is my replacement for getopt(). See the example for a typical usage. * Global flags are: * Bit 0 : Do not remove options form argv * Bit 1 : Do not stop at last option but return other args * with r_opt set to -1. * Bit 2 : Assume options and real args are mixed. * Bit 3 : Do not use -- to stop option processing. * Bit 4 : Do not skip the first arg. * Bit 5 : allow usage of long option with only one dash * Bit 6 : ignore --version * all other bits must be set to zero, this value is modified by the * function, so assume this is write only. * Local flags (for each option): * Bit 2-0 : 0 = does not take an argument * 1 = takes int argument * 2 = takes string argument * 3 = takes long argument * 4 = takes ulong argument * Bit 3 : argument is optional (r_type will the be set to 0) * Bit 4 : allow 0x etc. prefixed values. * Bit 6 : Ignore this option * Bit 7 : This is a command and not an option * You stop the option processing by setting opts to NULL, the function will * then return 0. * @Return Value * Returns the args.r_opt or 0 if ready * r_opt may be -2/-7 to indicate an unknown option/command. * @See Also * ArgExpand * @Notes * You do not need to process the options 'h', '--help' or '--version' * because this function includes standard help processing; but if you * specify '-h', '--help' or '--version' you have to do it yourself. * The option '--' stops argument processing; if bit 1 is set the function * continues to return normal arguments. * To process float args or unsigned args you must use a string args and do * the conversion yourself. * @Example * * ARGPARSE_OPTS opts[] = { * { 'v', "verbose", 0 }, * { 'd', "debug", 0 }, * { 'o', "output", 2 }, * { 'c', "cross-ref", 2|8 }, * { 'm', "my-option", 1|8 }, * { 300, "ignored-long-option, ARGPARSE_OP_IGNORE}, * { 500, "have-no-short-option-for-this-long-option", 0 }, * {0} }; * ARGPARSE_ARGS pargs = { &argc, &argv, 0 } * * while( ArgParse( &pargs, &opts) ) { * switch( pargs.r_opt ) { * case 'v': opt.verbose++; break; * case 'd': opt.debug++; break; * case 'o': opt.outfile = pargs.r.ret_str; break; * case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break; * case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break; * case 500: opt.a_long_one++; break * default : pargs.err = 1; break; -- force warning output -- * } * } * if( argc > 1 ) * log_fatal( "Too many args"); * */ typedef struct alias_def_s *ALIAS_DEF; struct alias_def_s { ALIAS_DEF next; char *name; /* malloced buffer with name, \0, value */ const char *value; /* ptr into name */ }; /* Object to store the names for the --ignore-invalid-option option. This is a simple linked list. */ typedef struct iio_item_def_s *IIO_ITEM_DEF; struct iio_item_def_s { IIO_ITEM_DEF next; char name[1]; /* String with the long option name. */ }; static const char *(*strusage_handler)( int ) = NULL; static int (*custom_outfnc) (int, const char *); static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s); static void show_help(ARGPARSE_OPTS *opts, unsigned flags); static void show_version(void); static int writestrings (int is_error, const char *string, ...) #if __GNUC__ >= 4 __attribute__ ((sentinel(0))) #endif ; void argparse_register_outfnc (int (*fnc)(int, const char *)) { custom_outfnc = fnc; } /* Write STRING and all following const char * arguments either to stdout or, if IS_ERROR is set, to stderr. The list of strings must be terminated by a NULL. */ static int writestrings (int is_error, const char *string, ...) { va_list arg_ptr; const char *s; int count = 0; if (string) { s = string; va_start (arg_ptr, string); do { if (custom_outfnc) custom_outfnc (is_error? 2:1, s); else fputs (s, is_error? stderr : stdout); count += strlen (s); } while ((s = va_arg (arg_ptr, const char *))); va_end (arg_ptr); } return count; } static void flushstrings (int is_error) { if (custom_outfnc) custom_outfnc (is_error? 2:1, NULL); else fflush (is_error? stderr : stdout); } static void initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno ) { if( !(arg->flags & (1<<15)) ) { /* Initialize this instance. */ arg->internal.idx = 0; arg->internal.last = NULL; arg->internal.inarg = 0; arg->internal.stopped = 0; arg->internal.aliases = NULL; arg->internal.cur_alias = NULL; arg->internal.iio_list = NULL; arg->err = 0; arg->flags |= 1<<15; /* Mark as initialized. */ if ( *arg->argc < 0 ) jnlib_log_bug ("invalid argument for arg_parse\n"); } if (arg->err) { /* Last option was erroneous. */ const char *s; if (filename) { if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) s = _("argument not expected"); else if ( arg->r_opt == ARGPARSE_READ_ERROR ) s = _("read error"); else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG ) s = _("keyword too long"); else if ( arg->r_opt == ARGPARSE_MISSING_ARG ) s = _("missing argument"); else if ( arg->r_opt == ARGPARSE_INVALID_ARG ) s = _("invalid argument"); else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) s = _("invalid command"); else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS ) s = _("invalid alias definition"); else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) s = _("out of core"); else s = _("invalid option"); jnlib_log_error ("%s:%u: %s\n", filename, *lineno, s); } else { s = arg->internal.last? arg->internal.last:"[??]"; if ( arg->r_opt == ARGPARSE_MISSING_ARG ) jnlib_log_error (_("missing argument for option \"%.50s\"\n"), s); else if ( arg->r_opt == ARGPARSE_INVALID_ARG ) jnlib_log_error (_("invalid argument for option \"%.50s\"\n"), s); else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) jnlib_log_error (_("option \"%.50s\" does not expect an " "argument\n"), s ); else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) jnlib_log_error (_("invalid command \"%.50s\"\n"), s); else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION ) jnlib_log_error (_("option \"%.50s\" is ambiguous\n"), s); else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND ) jnlib_log_error (_("command \"%.50s\" is ambiguous\n"),s ); else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) jnlib_log_error ("%s\n", _("out of core\n")); else jnlib_log_error (_("invalid option \"%.50s\"\n"), s); } if (arg->err != ARGPARSE_PRINT_WARNING) exit (2); arg->err = 0; } /* Zero out the return value union. */ arg->r.ret_str = NULL; arg->r.ret_long = 0; } static void store_alias( ARGPARSE_ARGS *arg, char *name, char *value ) { /* TODO: replace this dummy function with a rea one * and fix the probelms IRIX has with (ALIAS_DEV)arg.. * used as lvalue */ (void)arg; (void)name; (void)value; #if 0 ALIAS_DEF a = jnlib_xmalloc( sizeof *a ); a->name = name; a->value = value; a->next = (ALIAS_DEF)arg->internal.aliases; (ALIAS_DEF)arg->internal.aliases = a; #endif } /* Return true if KEYWORD is in the ignore-invalid-option list. */ static int ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword) { IIO_ITEM_DEF item = arg->internal.iio_list; for (; item; item = item->next) if (!strcmp (item->name, keyword)) return 1; return 0; } /* Add the keywords up to the next LF to the list of to be ignored options. After returning FP will either be at EOF or the next character read wll be the first of a new line. The function returns 0 on success or true on malloc failure. */ static int ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp) { IIO_ITEM_DEF item; int c; char name[100]; int namelen = 0; int ready = 0; enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS; while (!ready) { c = getc (fp); if (c == '\n') ready = 1; else if (c == EOF) { c = '\n'; ready = 1; } again: switch (state) { case skipWS: if (!isascii (c) || !isspace(c)) { namelen = 0; state = collectNAME; goto again; } break; case collectNAME: if (isspace (c)) { state = addNAME; goto again; } else if (namelen < DIM(name)-1) name[namelen++] = c; else /* Too long. */ state = skipNAME; break; case skipNAME: if (isspace (c)) { state = skipWS; goto again; } break; case addNAME: name[namelen] = 0; if (!ignore_invalid_option_p (arg, name)) { item = jnlib_malloc (sizeof *item + namelen); if (!item) return 1; strcpy (item->name, name); item->next = (IIO_ITEM_DEF)arg->internal.iio_list; arg->internal.iio_list = item; } state = skipWS; goto again; } } return 0; } /* Clear the entire ignore-invalid-option list. */ static void ignore_invalid_option_clear (ARGPARSE_ARGS *arg) { IIO_ITEM_DEF item, tmpitem; for (item = arg->internal.iio_list; item; item = tmpitem) { tmpitem = item->next; jnlib_free (item); } arg->internal.iio_list = NULL; } /**************** * Get options from a file. * Lines starting with '#' are comment lines. * Syntax is simply a keyword and the argument. * Valid keywords are all keywords from the long_opt list without * the leading dashes. The special keywords "help", "warranty" and "version" * are not valid here. * The special keyword "alias" may be used to store alias definitions, * which are later expanded like long options. * The option * ignore-invalid-option OPTIONNAMEs * is recognized and updates a list of option which should be ignored if they * are not defined. * Caller must free returned strings. * If called with FP set to NULL command line args are parse instead. * * Q: Should we allow the syntax * keyword = value * and accept for boolean options a value of 1/0, yes/no or true/false? * Note: Abbreviation of options is here not allowed. */ int optfile_parse (FILE *fp, const char *filename, unsigned *lineno, ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) { int state, i, c; int idx=0; char keyword[100]; char *buffer = NULL; size_t buflen = 0; int in_alias=0; if (!fp) /* Divert to to arg_parse() in this case. */ return arg_parse (arg, opts); initialize (arg, filename, lineno); /* Find the next keyword. */ state = i = 0; for (;;) { c = getc (fp); if (c == '\n' || c== EOF ) { if ( c != EOF ) ++*lineno; if (state == -1) break; else if (state == 2) { keyword[i] = 0; for (i=0; opts[i].short_opt; i++ ) { if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword)) break; } idx = i; arg->r_opt = opts[idx].short_opt; if ((opts[idx].flags & ARGPARSE_OPT_IGNORE)) { state = i = 0; continue; } else if (!opts[idx].short_opt ) { if (!strcmp (keyword, "ignore-invalid-option")) { /* No argument - ignore this meta option. */ state = i = 0; continue; } else if (ignore_invalid_option_p (arg, keyword)) { /* This invalid option is in the iio list. */ state = i = 0; continue; } arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND) ? ARGPARSE_INVALID_COMMAND : ARGPARSE_INVALID_OPTION); } else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) arg->r_type = 0; /* Does not take an arg. */ else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) ) arg->r_type = 0; /* Arg is optional. */ else arg->r_opt = ARGPARSE_MISSING_ARG; break; } else if (state == 3) { /* No argument found. */ if (in_alias) arg->r_opt = ARGPARSE_MISSING_ARG; else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) arg->r_type = 0; /* Does not take an arg. */ else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL)) arg->r_type = 0; /* No optional argument. */ else arg->r_opt = ARGPARSE_MISSING_ARG; break; } else if (state == 4) { /* Has an argument. */ if (in_alias) { if (!buffer) arg->r_opt = ARGPARSE_UNEXPECTED_ARG; else { char *p; buffer[i] = 0; p = strpbrk (buffer, " \t"); if (p) { *p++ = 0; trim_spaces (p); } if (!p || !*p) { jnlib_free (buffer); arg->r_opt = ARGPARSE_INVALID_ALIAS; } else { store_alias (arg, buffer, p); } } } else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) arg->r_opt = ARGPARSE_UNEXPECTED_ARG; else { char *p; if (!buffer) { keyword[i] = 0; buffer = jnlib_strdup (keyword); if (!buffer) arg->r_opt = ARGPARSE_OUT_OF_CORE; } else buffer[i] = 0; if (buffer) { trim_spaces (buffer); p = buffer; if (*p == '"') { /* Remove quotes. */ p++; if (*p && p[strlen(p)-1] == '\"' ) p[strlen(p)-1] = 0; } if (!set_opt_arg (arg, opts[idx].flags, p)) jnlib_free(buffer); } } break; } else if (c == EOF) { ignore_invalid_option_clear (arg); if (ferror (fp)) arg->r_opt = ARGPARSE_READ_ERROR; else arg->r_opt = 0; /* EOF. */ break; } state = 0; i = 0; } else if (state == -1) ; /* Skip. */ else if (state == 0 && isascii (c) && isspace(c)) ; /* Skip leading white space. */ else if (state == 0 && c == '#' ) state = 1; /* Start of a comment. */ else if (state == 1) ; /* Skip comments. */ else if (state == 2 && isascii (c) && isspace(c)) { /* Check keyword. */ keyword[i] = 0; for (i=0; opts[i].short_opt; i++ ) if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword)) break; idx = i; arg->r_opt = opts[idx].short_opt; if ((opts[idx].flags & ARGPARSE_OPT_IGNORE)) { state = 1; /* Process like a comment. */ } else if (!opts[idx].short_opt) { if (!strcmp (keyword, "alias")) { in_alias = 1; state = 3; } else if (!strcmp (keyword, "ignore-invalid-option")) { if (ignore_invalid_option_add (arg, fp)) { arg->r_opt = ARGPARSE_OUT_OF_CORE; break; } state = i = 0; ++*lineno; } else if (ignore_invalid_option_p (arg, keyword)) state = 1; /* Process like a comment. */ else { arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND) ? ARGPARSE_INVALID_COMMAND : ARGPARSE_INVALID_OPTION); state = -1; /* Skip rest of line and leave. */ } } else state = 3; } else if (state == 3) { /* Skip leading spaces of the argument. */ if (!isascii (c) || !isspace(c)) { i = 0; keyword[i++] = c; state = 4; } } else if (state == 4) { /* Collect the argument. */ if (buffer) { if (i < buflen-1) buffer[i++] = c; else { char *tmp; size_t tmplen = buflen + 50; tmp = jnlib_realloc (buffer, tmplen); if (tmp) { buflen = tmplen; buffer = tmp; buffer[i++] = c; } else { jnlib_free (buffer); arg->r_opt = ARGPARSE_OUT_OF_CORE; break; } } } else if (i < DIM(keyword)-1) keyword[i++] = c; else { size_t tmplen = DIM(keyword) + 50; buffer = jnlib_malloc (tmplen); if (buffer) { buflen = tmplen; memcpy(buffer, keyword, i); buffer[i++] = c; } else { arg->r_opt = ARGPARSE_OUT_OF_CORE; break; } } } else if (i >= DIM(keyword)-1) { arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG; state = -1; /* Skip rest of line and leave. */ } else { keyword[i++] = c; state = 2; } } return arg->r_opt; } static int find_long_option( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts, const char *keyword ) { int i; size_t n; (void)arg; /* Would be better if we can do a binary search, but it is not possible to reorder our option table because we would mess up our help strings - What we can do is: Build a nice option lookup table when this function is first invoked */ if( !*keyword ) return -1; for(i=0; opts[i].short_opt; i++ ) if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) ) return i; #if 0 { ALIAS_DEF a; /* see whether it is an alias */ for( a = args->internal.aliases; a; a = a->next ) { if( !strcmp( a->name, keyword) ) { /* todo: must parse the alias here */ args->internal.cur_alias = a; return -3; /* alias available */ } } } #endif /* not found, see whether it is an abbreviation */ /* aliases may not be abbreviated */ n = strlen( keyword ); for(i=0; opts[i].short_opt; i++ ) { if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) { int j; for(j=i+1; opts[j].short_opt; j++ ) { if( opts[j].long_opt && !strncmp( opts[j].long_opt, keyword, n ) ) return -2; /* abbreviation is ambiguous */ } return i; } } return -1; /* Not found. */ } int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) { int idx; int argc; char **argv; char *s, *s2; int i; initialize( arg, NULL, NULL ); argc = *arg->argc; argv = *arg->argv; idx = arg->internal.idx; if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0)) { /* Skip the first argument. */ argc--; argv++; idx++; } next_one: if (!argc) { /* No more args. */ arg->r_opt = 0; goto leave; /* Ready. */ } s = *argv; arg->internal.last = s; if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL)) { arg->r_opt = ARGPARSE_IS_ARG; /* Not an option but an argument. */ arg->r_type = 2; arg->r.ret_str = s; argc--; argv++; idx++; /* set to next one */ } else if( arg->internal.stopped ) { arg->r_opt = 0; goto leave; /* Ready. */ } else if ( *s == '-' && s[1] == '-' ) { /* Long option. */ char *argpos; arg->internal.inarg = 0; if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP)) { /* Stop option processing. */ arg->internal.stopped = 1; arg->flags |= ARGPARSE_FLAG_STOP_SEEN; argc--; argv++; idx++; goto next_one; } argpos = strchr( s+2, '=' ); if ( argpos ) *argpos = 0; i = find_long_option ( arg, opts, s+2 ); if ( argpos ) *argpos = '='; if ( i < 0 && !strcmp ( "help", s+2) ) show_help (opts, arg->flags); else if ( i < 0 && !strcmp ( "version", s+2) ) { if (!(arg->flags & ARGPARSE_FLAG_NOVERSION)) { show_version (); exit(0); } } else if ( i < 0 && !strcmp( "warranty", s+2)) { writestrings (0, strusage (16), "\n", NULL); exit (0); } else if ( i < 0 && !strcmp( "dump-options", s+2) ) { for (i=0; opts[i].short_opt; i++ ) { if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE)) writestrings (0, "--", opts[i].long_opt, "\n", NULL); } writestrings (0, "--dump-options\n--help\n--version\n--warranty\n", NULL); exit (0); } if ( i == -2 ) arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION; else if ( i == -1 ) { arg->r_opt = ARGPARSE_INVALID_OPTION; arg->r.ret_str = s+2; } else arg->r_opt = opts[i].short_opt; if ( i < 0 ) ; else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) ) { if ( argpos ) { s2 = argpos+1; if ( !*s2 ) s2 = NULL; } else s2 = argv[1]; if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) { arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */ } else if ( !s2 ) { arg->r_opt = ARGPARSE_MISSING_ARG; } else if ( !argpos && *s2 == '-' && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) { /* The argument is optional and the next seems to be an option. We do not check this possible option but assume no argument */ arg->r_type = ARGPARSE_TYPE_NONE; } else { set_opt_arg (arg, opts[i].flags, s2); if ( !argpos ) { argc--; argv++; idx++; /* Skip one. */ } } } else { /* Does not take an argument. */ if ( argpos ) arg->r_type = ARGPARSE_UNEXPECTED_ARG; else arg->r_type = 0; } argc--; argv++; idx++; /* Set to next one. */ } else if ( (*s == '-' && s[1]) || arg->internal.inarg ) { /* Short option. */ int dash_kludge = 0; i = 0; if ( !arg->internal.inarg ) { arg->internal.inarg++; if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) ) { for (i=0; opts[i].short_opt; i++ ) if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1)) { dash_kludge = 1; break; } } } s += arg->internal.inarg; if (!dash_kludge ) { for (i=0; opts[i].short_opt; i++ ) if ( opts[i].short_opt == *s ) break; } if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) ) show_help (opts, arg->flags); arg->r_opt = opts[i].short_opt; if (!opts[i].short_opt ) { arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)? ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION; arg->internal.inarg++; /* Point to the next arg. */ arg->r.ret_str = s; } else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) ) { if ( s[1] && !dash_kludge ) { s2 = s+1; set_opt_arg (arg, opts[i].flags, s2); } else { s2 = argv[1]; if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) { arg->r_type = ARGPARSE_TYPE_NONE; } else if ( !s2 ) { arg->r_opt = ARGPARSE_MISSING_ARG; } else if ( *s2 == '-' && s2[1] && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) { /* The argument is optional and the next seems to be an option. We do not check this possible option but assume no argument. */ arg->r_type = ARGPARSE_TYPE_NONE; } else { set_opt_arg (arg, opts[i].flags, s2); argc--; argv++; idx++; /* Skip one. */ } } s = "x"; /* This is so that !s[1] yields false. */ } else { /* Does not take an argument. */ arg->r_type = ARGPARSE_TYPE_NONE; arg->internal.inarg++; /* Point to the next arg. */ } if ( !s[1] || dash_kludge ) { /* No more concatenated short options. */ arg->internal.inarg = 0; argc--; argv++; idx++; } } else if ( arg->flags & ARGPARSE_FLAG_MIXED ) { arg->r_opt = ARGPARSE_IS_ARG; arg->r_type = 2; arg->r.ret_str = s; argc--; argv++; idx++; /* Set to next one. */ } else { arg->internal.stopped = 1; /* Stop option processing. */ goto next_one; } leave: *arg->argc = argc; *arg->argv = argv; arg->internal.idx = idx; return arg->r_opt; } /* Returns: -1 on error, 0 for an integer type and 1 for a non integer type argument. */ static int set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s) { int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10; long l; switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) ) { case ARGPARSE_TYPE_LONG: case ARGPARSE_TYPE_INT: errno = 0; l = strtol (s, NULL, base); if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) { arg->r_opt = ARGPARSE_INVALID_ARG; return -1; } if (arg->r_type == ARGPARSE_TYPE_LONG) arg->r.ret_long = l; else if ( (l < 0 && l < INT_MIN) || l > INT_MAX ) { arg->r_opt = ARGPARSE_INVALID_ARG; return -1; } else arg->r.ret_int = (int)l; return 0; case ARGPARSE_TYPE_ULONG: while (isascii (*s) && isspace(*s)) s++; if (*s == '-') { arg->r.ret_ulong = 0; arg->r_opt = ARGPARSE_INVALID_ARG; return -1; } errno = 0; arg->r.ret_ulong = strtoul (s, NULL, base); if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE) { arg->r_opt = ARGPARSE_INVALID_ARG; return -1; } return 0; case ARGPARSE_TYPE_STRING: default: arg->r.ret_str = s; return 1; } } static size_t long_opt_strlen( ARGPARSE_OPTS *o ) { size_t n = strlen (o->long_opt); if ( o->description && *o->description == '|' ) { const char *s; #ifdef JNLIB_NEED_UTF8CONV int is_utf8 = is_native_utf8 (); #endif s=o->description+1; if ( *s != '=' ) n++; /* For a (mostly) correct length calculation we exclude continuation bytes (10xxxxxx) if we are on a native utf8 terminal. */ for (; *s && *s != '|'; s++ ) #ifdef JNLIB_NEED_UTF8CONV if ( is_utf8 && (*s&0xc0) != 0x80 ) #endif n++; } return n; } /**************** * Print formatted help. The description string has some special * meanings: * - A description string which is "@" suppresses help output for * this option * - a description,ine which starts with a '@' and is followed by * any other characters is printed as is; this may be used for examples * ans such. * - A description which starts with a '|' outputs the string between this * bar and the next one as arguments of the long option. */ static void show_help (ARGPARSE_OPTS *opts, unsigned int flags) { const char *s; char tmp[2]; show_version (); writestrings (0, "\n", NULL); s = strusage (42); if (s && *s == '1') { s = strusage (40); writestrings (1, s, NULL); if (*s && s[strlen(s)] != '\n') writestrings (1, "\n", NULL); } s = strusage(41); writestrings (0, s, "\n", NULL); if ( opts[0].description ) { /* Auto format the option description. */ int i,j, indent; /* Get max. length of long options. */ for (i=indent=0; opts[i].short_opt; i++ ) { if ( opts[i].long_opt ) if ( !opts[i].description || *opts[i].description != '@' ) if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 ) indent = j; } /* Example: " -v, --verbose Viele Sachen ausgeben" */ indent += 10; if ( *opts[0].description != '@' ) writestrings (0, "Options:", "\n", NULL); for (i=0; opts[i].short_opt; i++ ) { s = map_static_macro_string (_( opts[i].description )); if ( s && *s== '@' && !s[1] ) /* Hide this line. */ continue; if ( s && *s == '@' ) /* Unindented comment only line. */ { for (s++; *s; s++ ) { if ( *s == '\n' ) { if( s[1] ) writestrings (0, "\n", NULL); } else { tmp[0] = *s; tmp[1] = 0; writestrings (0, tmp, NULL); } } writestrings (0, "\n", NULL); continue; } j = 3; if ( opts[i].short_opt < 256 ) { tmp[0] = opts[i].short_opt; tmp[1] = 0; writestrings (0, " -", tmp, NULL ); if ( !opts[i].long_opt ) { if (s && *s == '|' ) { writestrings (0, " ", NULL); j++; for (s++ ; *s && *s != '|'; s++, j++ ) { tmp[0] = *s; tmp[1] = 0; writestrings (0, tmp, NULL); } if ( *s ) s++; } } } else writestrings (0, " ", NULL); if ( opts[i].long_opt ) { tmp[0] = opts[i].short_opt < 256?',':' '; tmp[1] = 0; j += writestrings (0, tmp, " --", opts[i].long_opt, NULL); if (s && *s == '|' ) { if ( *++s != '=' ) { writestrings (0, " ", NULL); j++; } for ( ; *s && *s != '|'; s++, j++ ) { tmp[0] = *s; tmp[1] = 0; writestrings (0, tmp, NULL); } if ( *s ) s++; } writestrings (0, " ", NULL); j += 3; } for (;j < indent; j++ ) writestrings (0, " ", NULL); if ( s ) { if ( *s && j > indent ) { writestrings (0, "\n", NULL); for (j=0;j < indent; j++ ) writestrings (0, " ", NULL); } for (; *s; s++ ) { if ( *s == '\n' ) { if ( s[1] ) { writestrings (0, "\n", NULL); for (j=0; j < indent; j++ ) writestrings (0, " ", NULL); } } else { tmp[0] = *s; tmp[1] = 0; writestrings (0, tmp, NULL); } } } writestrings (0, "\n", NULL); } if ( (flags & ARGPARSE_FLAG_ONEDASH) ) writestrings (0, "\n(A single dash may be used " "instead of the double ones)\n", NULL); } if ( (s=strusage(19)) ) { writestrings (0, "\n", NULL); writestrings (0, s, NULL); } flushstrings (0); exit(0); } static void show_version () { const char *s; int i; /* Version line. */ writestrings (0, strusage (11), NULL); if ((s=strusage (12))) writestrings (0, " (", s, ")", NULL); writestrings (0, " ", strusage (13), "\n", NULL); /* Additional version lines. */ for (i=20; i < 30; i++) if ((s=strusage (i))) writestrings (0, s, "\n", NULL); /* Copyright string. */ if ((s=strusage (14))) writestrings (0, s, "\n", NULL); /* Licence string. */ if( (s=strusage (10)) ) writestrings (0, s, "\n", NULL); /* Copying conditions. */ if ( (s=strusage(15)) ) writestrings (0, s, NULL); /* Thanks. */ if ((s=strusage(18))) writestrings (0, s, NULL); /* Additional program info. */ for (i=30; i < 40; i++ ) if ( (s=strusage (i)) ) writestrings (0, s, NULL); flushstrings (0); } void usage (int level) { const char *p; if (!level) { writestrings (1, strusage(11), " ", strusage(13), "; ", strusage (14), "\n", NULL); flushstrings (1); } else if (level == 1) { p = strusage (40); writestrings (1, p, NULL); if (*p && p[strlen(p)] != '\n') writestrings (1, "\n", NULL); exit (2); } else if (level == 2) { p = strusage (42); if (p && *p == '1') { p = strusage (40); writestrings (1, p, NULL); if (*p && p[strlen(p)] != '\n') writestrings (1, "\n", NULL); } writestrings (0, strusage(41), "\n", NULL); exit (0); } } /* Level * 0: Print copyright string to stderr * 1: Print a short usage hint to stderr and terminate * 2: Print a long usage hint to stdout and terminate * 10: Return license info string * 11: Return the name of the program * 12: Return optional name of package which includes this program. * 13: version string * 14: copyright string * 15: Short copying conditions (with LFs) * 16: Long copying conditions (with LFs) * 17: Optional printable OS name * 18: Optional thanks list (with LFs) * 19: Bug report info *20..29: Additional lib version strings. *30..39: Additional program info (with LFs) * 40: short usage note (with LF) * 41: long usage note (with LF) * 42: Flag string: * First char is '1': * The short usage notes needs to be printed * before the long usage note. */ const char * strusage( int level ) { const char *p = strusage_handler? strusage_handler(level) : NULL; if ( p ) return map_static_macro_string (p); switch ( level ) { case 10: #if ARGPARSE_GPL_VERSION == 3 p = ("License GPLv3+: GNU GPL version 3 or later " ""); #else p = ("License GPLv2+: GNU GPL version 2 or later " ""); #endif break; case 11: p = "foo"; break; case 13: p = "0.0"; break; case 14: p = ARGPARSE_CRIGHT_STR; break; case 15: p = "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n"; break; case 16: p = "This is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation; either version " ARGPARSE_STR2(ARGPARSE_GPL_VERSION) " of the License, or\n" "(at your option) any later version.\n\n" "It is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n\n" "You should have received a copy of the GNU General Public License\n" "along with this software. If not, see .\n"; break; case 40: /* short and long usage */ case 41: p = ""; break; } return p; } /* Set the usage handler. This function is basically a constructor. */ void set_strusage ( const char *(*f)( int ) ) { strusage_handler = f; } #ifdef TEST static struct { int verbose; int debug; char *outfile; char *crf; int myopt; int echo; int a_long_one; } opt; int main(int argc, char **argv) { ARGPARSE_OPTS opts[] = { ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"), ARGPARSE_s_n('e', "echo" , ("Zeile ausgeben, damit wir sehen, " "was wir eingegeben haben")), ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"), ARGPARSE_s_s('o', "output", 0 ), ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ), /* Note that on a non-utf8 terminal the ß might garble the output. */ ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"), ARGPARSE_o_i('m', "my-option", 0), ARGPARSE_s_n(500, "a-long-option", 0 ), ARGPARSE_end() }; ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL | ARGPARSE_FLAG_MIXED | ARGPARSE_FLAG_ONEDASH) }; int i; while (arg_parse (&pargs, opts)) { switch (pargs.r_opt) { case ARGPARSE_IS_ARG : printf ("arg='%s'\n", pargs.r.ret_str); break; case 'v': opt.verbose++; break; case 'e': opt.echo++; break; case 'd': opt.debug++; break; case 'o': opt.outfile = pargs.r.ret_str; break; case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break; case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break; case 500: opt.a_long_one++; break; default : pargs.err = ARGPARSE_PRINT_WARNING; break; } } for (i=0; i < argc; i++ ) printf ("%3d -> (%s)\n", i, argv[i] ); puts ("Options:"); if (opt.verbose) printf (" verbose=%d\n", opt.verbose ); if (opt.debug) printf (" debug=%d\n", opt.debug ); if (opt.outfile) printf (" outfile='%s'\n", opt.outfile ); if (opt.crf) printf (" crffile='%s'\n", opt.crf ); if (opt.myopt) printf (" myopt=%d\n", opt.myopt ); if (opt.a_long_one) printf (" a-long-one=%d\n", opt.a_long_one ); if (opt.echo) printf (" echo=%d\n", opt.echo ); return 0; } #endif /*TEST*/ /**** bottom of file ****/ diff --git a/pinentry/argparse.h b/pinentry/argparse.h index 54d9c5b..5b652eb 100644 --- a/pinentry/argparse.h +++ b/pinentry/argparse.h @@ -1,203 +1,204 @@ /* argparse.h - Argument parser for option handling. * Copyright (C) 1998,1999,2000,2001,2006 Free Software Foundation, Inc. * * This file is part of JNLIB, which is a subsystem of GnuPG. * * JNLIB is free software; you can redistribute it and/or modify it * under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - 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. * * or both in parallel, as here. * * JNLIB 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . + * SPDX-License-Identifier: (GPL-2.0+ OR LGPL-3.0+) */ #ifndef LIBJNLIB_ARGPARSE_H #define LIBJNLIB_ARGPARSE_H #include typedef struct { int *argc; /* Pointer to ARGC (value subject to change). */ char ***argv; /* Pointer to ARGV (value subject to change). */ unsigned int flags; /* Global flags. May be set prior to calling the parser. The parser may change the value. */ int err; /* Print error description for last option. Either 0, ARGPARSE_PRINT_WARNING or ARGPARSE_PRINT_ERROR. */ int r_opt; /* Returns option code. */ int r_type; /* Returns type of option value. */ union { int ret_int; long ret_long; unsigned long ret_ulong; char *ret_str; } r; /* Return values */ struct { int idx; int inarg; int stopped; const char *last; void *aliases; const void *cur_alias; void *iio_list; } internal; /* Private - do not change. */ } ARGPARSE_ARGS; typedef struct { int short_opt; const char *long_opt; unsigned int flags; const char *description; /* Optional option description. */ } ARGPARSE_OPTS; /* Global flags (ARGPARSE_ARGS). */ #define ARGPARSE_FLAG_KEEP 1 /* Do not remove options form argv. */ #define ARGPARSE_FLAG_ALL 2 /* Do not stop at last option but return remaining args with R_OPT set to -1. */ #define ARGPARSE_FLAG_MIXED 4 /* Assume options and args are mixed. */ #define ARGPARSE_FLAG_NOSTOP 8 /* Do not stop processing at "--". */ #define ARGPARSE_FLAG_ARG0 16 /* Do not skip the first arg. */ #define ARGPARSE_FLAG_ONEDASH 32 /* Allow long options with one dash. */ #define ARGPARSE_FLAG_NOVERSION 64 /* No output for "--version". */ #define ARGPARSE_FLAG_STOP_SEEN 256 /* Set to true if a "--" has been seen. */ /* Flags for each option (ARGPARSE_OPTS). The type code may be ORed with the OPT flags. */ #define ARGPARSE_TYPE_NONE 0 /* Does not take an argument. */ #define ARGPARSE_TYPE_INT 1 /* Takes an int argument. */ #define ARGPARSE_TYPE_STRING 2 /* Takes a string argument. */ #define ARGPARSE_TYPE_LONG 3 /* Takes a long argument. */ #define ARGPARSE_TYPE_ULONG 4 /* Takes an unsigned long argument. */ #define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional. */ #define ARGPARSE_OPT_PREFIX (1<<4) /* Allow 0x etc. prefixed values. */ #define ARGPARSE_OPT_IGNORE (1<<6) /* Ignore command or option. */ #define ARGPARSE_OPT_COMMAND (1<<7) /* The argument is a command. */ #define ARGPARSE_TYPE_MASK 7 /* Mask for the type values (internal). */ /* A set of macros to make option definitions easier to read. */ #define ARGPARSE_x(s,l,t,f,d) \ { (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) } #define ARGPARSE_s(s,l,t,d) \ { (s), (l), ARGPARSE_TYPE_ ## t, (d) } #define ARGPARSE_s_n(s,l,d) \ { (s), (l), ARGPARSE_TYPE_NONE, (d) } #define ARGPARSE_s_i(s,l,d) \ { (s), (l), ARGPARSE_TYPE_INT, (d) } #define ARGPARSE_s_s(s,l,d) \ { (s), (l), ARGPARSE_TYPE_STRING, (d) } #define ARGPARSE_s_l(s,l,d) \ { (s), (l), ARGPARSE_TYPE_LONG, (d) } #define ARGPARSE_s_u(s,l,d) \ { (s), (l), ARGPARSE_TYPE_ULONG, (d) } #define ARGPARSE_o(s,l,t,d) \ { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_OPTIONAL), (d) } #define ARGPARSE_o_n(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_OPTIONAL), (d) } #define ARGPARSE_o_i(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_OPTIONAL), (d) } #define ARGPARSE_o_s(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) } #define ARGPARSE_o_l(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_OPTIONAL), (d) } #define ARGPARSE_o_u(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_OPTIONAL), (d) } #define ARGPARSE_p(s,l,t,d) \ { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_p_n(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_p_i(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_p_s(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_p_l(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_p_u(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_op(s,l,t,d) \ { (s), (l), (ARGPARSE_TYPE_ ## t \ | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_op_n(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_NONE \ | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_op_i(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_INT \ | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_op_s(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_STRING \ | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_op_l(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_LONG \ | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_op_u(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_ULONG \ | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } #define ARGPARSE_c(s,l,d) \ { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) } #define ARGPARSE_ignore(s,l) \ { (s), (l), (ARGPARSE_OPT_IGNORE), "@" } #define ARGPARSE_group(s,d) \ { (s), NULL, 0, (d) } #define ARGPARSE_end() { 0, NULL, 0, NULL } /* Other constants. */ #define ARGPARSE_PRINT_WARNING 1 #define ARGPARSE_PRINT_ERROR 2 /* Error values. */ #define ARGPARSE_IS_ARG (-1) #define ARGPARSE_INVALID_OPTION (-2) #define ARGPARSE_MISSING_ARG (-3) #define ARGPARSE_KEYWORD_TOO_LONG (-4) #define ARGPARSE_READ_ERROR (-5) #define ARGPARSE_UNEXPECTED_ARG (-6) #define ARGPARSE_INVALID_COMMAND (-7) #define ARGPARSE_AMBIGUOUS_OPTION (-8) #define ARGPARSE_AMBIGUOUS_COMMAND (-9) #define ARGPARSE_INVALID_ALIAS (-10) #define ARGPARSE_OUT_OF_CORE (-11) #define ARGPARSE_INVALID_ARG (-12) int arg_parse (ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); int optfile_parse (FILE *fp, const char *filename, unsigned *lineno, ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); void usage (int level); const char *strusage (int level); void set_strusage (const char *(*f)( int )); void argparse_register_outfnc (int (*fnc)(int, const char *)); #endif /*LIBJNLIB_ARGPARSE_H*/ diff --git a/pinentry/password-cache.c b/pinentry/password-cache.c index 06c48b4..0da17b5 100644 --- a/pinentry/password-cache.c +++ b/pinentry/password-cache.c @@ -1,170 +1,171 @@ /* password-cache.c - Password cache support. Copyright (C) 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+ */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #ifdef HAVE_LIBSECRET # include #endif #include "password-cache.h" #include "memory.h" #ifdef HAVE_LIBSECRET static const SecretSchema * gpg_schema (void) { static const SecretSchema the_schema = { "org.gnupg.Passphrase", SECRET_SCHEMA_NONE, { { "stored-by", SECRET_SCHEMA_ATTRIBUTE_STRING }, { "keygrip", SECRET_SCHEMA_ATTRIBUTE_STRING }, { "NULL", 0 }, } }; return &the_schema; } static char * keygrip_to_label (const char *keygrip) { char const prefix[] = "GnuPG: "; char *label; label = malloc (sizeof (prefix) + strlen (keygrip)); if (label) { memcpy (label, prefix, sizeof (prefix) - 1); strcpy (&label[sizeof (prefix) - 1], keygrip); } return label; } #endif void password_cache_save (const char *keygrip, const char *password) { #ifdef HAVE_LIBSECRET char *label; GError *error = NULL; if (! *keygrip) return; label = keygrip_to_label (keygrip); if (! label) return; if (! secret_password_store_sync (gpg_schema (), SECRET_COLLECTION_DEFAULT, label, password, NULL, &error, "stored-by", "GnuPG Pinentry", "keygrip", keygrip, NULL)) { fprintf (stderr, "Failed to cache password for key %s with secret service: %s\n", keygrip, error->message); g_error_free (error); } free (label); #else (void) keygrip; (void) password; return; #endif } char * password_cache_lookup (const char *keygrip, int *fatal_error) { #ifdef HAVE_LIBSECRET GError *error = NULL; char *password; char *password2; if (! *keygrip) return NULL; password = secret_password_lookup_nonpageable_sync (gpg_schema (), NULL, &error, "keygrip", keygrip, NULL); if (error != NULL) { if (fatal_error) *fatal_error = 1; fprintf (stderr, "Failed to lookup password for key %s with secret service: %s\n", keygrip, error->message); g_error_free (error); return NULL; } if (! password) /* The password for this key is not cached. Just return NULL. */ return NULL; /* The password needs to be returned in secmem allocated memory. */ password2 = secmem_malloc (strlen (password) + 1); if (password2) strcpy(password2, password); else fprintf (stderr, "secmem_malloc failed: can't copy password!\n"); secret_password_free (password); return password2; #else (void) keygrip; return NULL; #endif } /* Try and remove the cached password for key grip. Returns -1 on error, 0 if the key is not found and 1 if the password was removed. */ int password_cache_clear (const char *keygrip) { #ifdef HAVE_LIBSECRET GError *error = NULL; int removed = secret_password_clear_sync (gpg_schema (), NULL, &error, "keygrip", keygrip, NULL); if (error != NULL) { fprintf (stderr, "Failed to clear password for key %s with secret service: %s\n", keygrip, error->message); g_debug("%s", error->message); g_error_free (error); return -1; } if (removed) return 1; return 0; #else (void) keygrip; return -1; #endif } diff --git a/pinentry/password-cache.h b/pinentry/password-cache.h index 34fd00b..d7ccfee 100644 --- a/pinentry/password-cache.h +++ b/pinentry/password-cache.h @@ -1,29 +1,30 @@ /* password-cache.h - Password cache support interfaces. Copyright (C) 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+ */ #ifndef PASSWORD_CACHE_H #define PASSWORD_CACHE_H void password_cache_save (const char *key_grip, const char *password); char *password_cache_lookup (const char *key_grip, int *fatal_error); int password_cache_clear (const char *keygrip); #endif diff --git a/pinentry/pinentry-curses.c b/pinentry/pinentry-curses.c index 7c82e0f..89bb5b6 100644 --- a/pinentry/pinentry-curses.c +++ b/pinentry/pinentry-curses.c @@ -1,1194 +1,1194 @@ /* pinentry-curses.c - A secure curses dialog for PIN entry, library version - Copyright (C) 2002, 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA */ + * Copyright (C) 2002, 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+ + */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UTIME_H #include #endif /*HAVE_UTIME_H*/ #include #ifdef HAVE_WCHAR_H #include #endif /*HAVE_WCHAR_H*/ #include #include "pinentry.h" #if GPG_ERROR_VERSION_NUMBER < 0x011900 /* 1.25 */ # define GPG_ERR_WINDOW_TOO_SMALL 301 # define GPG_ERR_MISSING_ENVVAR 303 #endif /* FIXME: We should allow configuration of these button labels and in any case use the default_ok, default_cancel values if available. However, I have no clue about curses and localization. */ #define STRING_OK "" #define STRING_NOTOK "" #define STRING_CANCEL "" #define USE_COLORS (has_colors () && COLOR_PAIRS >= 2) static short pinentry_color[] = { -1, -1, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE }; static int init_screen; #ifndef HAVE_DOSISH_SYSTEM static int timed_out; #endif typedef enum { DIALOG_POS_NONE, DIALOG_POS_PIN, DIALOG_POS_OK, DIALOG_POS_NOTOK, DIALOG_POS_CANCEL } dialog_pos_t; struct dialog { dialog_pos_t pos; int pin_y; int pin_x; /* Width of the PIN field. */ int pin_size; /* Cursor location in PIN field. */ int pin_loc; int pin_max; /* Length of PIN. */ int pin_len; int ok_y; int ok_x; char *ok; int cancel_y; int cancel_x; char *cancel; int notok_y; int notok_x; char *notok; pinentry_t pinentry; }; typedef struct dialog *dialog_t; #ifdef HAVE_NCURSESW typedef wchar_t CH; #define STRLEN(x) wcslen (x) #define ADDCH(x) addnwstr (&x, 1); #define CHWIDTH(x) wcwidth (x) #define NULLCH L'\0' #define NLCH L'\n' #define SPCH L' ' #else typedef char CH; #define STRLEN(x) strlen (x) #define ADDCH(x) addch ((unsigned char) x) #define CHWIDTH(x) 1 #define NULLCH '\0' #define NLCH '\n' #define SPCH ' ' #endif /* Return the next line up to MAXLEN columns wide in START and LEN. The first invocation should have 0 as *LEN. If the line ends with a \n, it is a normal line that will be continued. If it is a '\0' the end of the text is reached after this line. In all other cases there is a forced line break. A full line is returned and will be continued in the next line. */ static void collect_line (int maxwidth, CH **start_p, int *len_p) { int last_space = 0; int len = *len_p; int width = 0; CH *end; /* Skip to next line. */ *start_p += len; /* Skip leading space. */ while (**start_p == SPCH) (*start_p)++; end = *start_p; len = 0; while (width < maxwidth - 1 && *end != NULLCH && *end != NLCH) { len++; end++; if (*end == SPCH) last_space = len; width += CHWIDTH (*end); } if (*end != NULLCH && *end != NLCH && last_space != 0) { /* We reached the end of the available space, but still have characters to go in this line. We can break the line into two parts at a space. */ len = last_space; (*start_p)[len] = NLCH; } *len_p = len + 1; } #ifdef HAVE_NCURSESW static CH * utf8_to_local (char *lc_ctype, char *string) { mbstate_t ps; size_t len; char *local; const char *p; wchar_t *wcs = NULL; char *old_ctype = NULL; local = pinentry_utf8_to_local (lc_ctype, string); if (!local) return NULL; old_ctype = strdup (setlocale (LC_CTYPE, NULL)); setlocale (LC_CTYPE, lc_ctype? lc_ctype : ""); p = local; memset (&ps, 0, sizeof(mbstate_t)); len = mbsrtowcs (NULL, &p, strlen (string), &ps); if (len == (size_t)-1) { free (local); goto leave; } wcs = calloc (len + 1, sizeof(wchar_t)); if (!wcs) { free (local); goto leave; } p = local; memset (&ps, 0, sizeof(mbstate_t)); mbsrtowcs (wcs, &p, len, &ps); free (local); leave: if (old_ctype) { setlocale (LC_CTYPE, old_ctype); free (old_ctype); } return wcs; } #else static CH * utf8_to_local (const char *lc_ctype, const char *string) { return pinentry_utf8_to_local (lc_ctype, string); } #endif static int dialog_create (pinentry_t pinentry, dialog_t dialog) { int err = 0; int size_y; int size_x; int y; int x; int ypos; int xpos; int description_x = 0; int error_x = 0; CH *description = NULL; CH *error = NULL; CH *prompt = NULL; dialog->pinentry = pinentry; #define COPY_OUT(what) \ do \ if (pinentry->what) \ { \ what = utf8_to_local (pinentry->lc_ctype, pinentry->what); \ if (!what) \ { \ err = 1; \ pinentry->specific_err = gpg_error (GPG_ERR_LOCALE_PROBLEM); \ pinentry->specific_err_loc = "dialog_create_copy"; \ goto out; \ } \ } \ while (0) COPY_OUT (description); COPY_OUT (error); COPY_OUT (prompt); /* There is no pinentry->default_notok. Map it to pinentry->notok. */ #define default_notok notok #define MAKE_BUTTON(which,default) \ do \ { \ char *new = NULL; \ if (pinentry->default_##which || pinentry->which) \ { \ int len; \ char *msg; \ int i, j; \ \ msg = pinentry->which; \ if (! msg) \ msg = pinentry->default_##which; \ len = strlen (msg); \ \ new = malloc (len + 3); \ if (!new) \ { \ err = 1; \ pinentry->specific_err = gpg_error_from_syserror (); \ pinentry->specific_err_loc = "dialog_create_mk_button"; \ goto out; \ } \ \ new[0] = '<'; \ for (i = 0, j = 1; i < len; i ++, j ++) \ { \ if (msg[i] == '_') \ { \ i ++; \ if (msg[i] == 0) \ /* _ at end of string. */ \ break; \ } \ new[j] = msg[i]; \ } \ \ new[j] = '>'; \ new[j + 1] = 0; \ } \ dialog->which = pinentry_utf8_to_local (pinentry->lc_ctype, \ new ? new : default); \ if (!dialog->which) \ { \ err = 1; \ pinentry->specific_err = gpg_error (GPG_ERR_LOCALE_PROBLEM); \ pinentry->specific_err_loc = "dialog_create_utf8conv"; \ goto out; \ } \ } \ while (0) MAKE_BUTTON (ok, STRING_OK); if (!pinentry->one_button) MAKE_BUTTON (cancel, STRING_CANCEL); else dialog->cancel = NULL; if (!pinentry->one_button && pinentry->notok) MAKE_BUTTON (notok, STRING_NOTOK); else dialog->notok = NULL; getmaxyx (stdscr, size_y, size_x); /* Check if all required lines fit on the screen. */ y = 1; /* Top frame. */ if (description) { CH *start = description; int len = 0; do { collect_line (size_x - 4, &start, &len); if (len > description_x) description_x = len; y++; } while (start[len - 1]); y++; } if (pinentry->pin) { if (error) { CH *p = error; int err_x = 0; while (*p) { if (*(p++) == '\n') { if (err_x > error_x) error_x = err_x; y++; err_x = 0; } else err_x++; } if (err_x > error_x) error_x = err_x; y += 2; /* Error message. */ } y += 2; /* Pin entry field. */ } y += 2; /* OK/Cancel and bottom frame. */ if (y > size_y) { err = 1; pinentry->specific_err = gpg_error (size_y < 0? GPG_ERR_MISSING_ENVVAR /* */ : GPG_ERR_WINDOW_TOO_SMALL); pinentry->specific_err_loc = "dialog_create"; goto out; } /* Check if all required columns fit on the screen. */ x = 0; if (description) { int new_x = description_x; if (new_x > size_x - 4) new_x = size_x - 4; if (new_x > x) x = new_x; } if (pinentry->pin) { #define MIN_PINENTRY_LENGTH 40 int new_x; if (error) { new_x = error_x; if (new_x > size_x - 4) new_x = size_x - 4; if (new_x > x) x = new_x; } new_x = MIN_PINENTRY_LENGTH; if (prompt) { new_x += STRLEN (prompt) + 1; /* One space after prompt. */ } if (new_x > size_x - 4) new_x = size_x - 4; if (new_x > x) x = new_x; } /* We position the buttons after the first, second and third fourth of the width. Account for rounding. */ if (x < 3 * strlen (dialog->ok)) x = 3 * strlen (dialog->ok); if (dialog->cancel) if (x < 3 * strlen (dialog->cancel)) x = 3 * strlen (dialog->cancel); if (dialog->notok) if (x < 3 * strlen (dialog->notok)) x = 3 * strlen (dialog->notok); /* Add the frame. */ x += 4; if (x > size_x) { err = 1; pinentry->specific_err = gpg_error (size_x < 0? GPG_ERR_MISSING_ENVVAR /* */ : GPG_ERR_WINDOW_TOO_SMALL); pinentry->specific_err_loc = "dialog_create"; goto out; } dialog->pos = DIALOG_POS_NONE; dialog->pin_max = pinentry->pin_len; dialog->pin_loc = 0; dialog->pin_len = 0; ypos = (size_y - y) / 2; xpos = (size_x - x) / 2; move (ypos, xpos); addch (ACS_ULCORNER); hline (0, x - 2); move (ypos, xpos + x - 1); addch (ACS_URCORNER); move (ypos + 1, xpos + x - 1); vline (0, y - 2); move (ypos + y - 1, xpos); addch (ACS_LLCORNER); hline (0, x - 2); move (ypos + y - 1, xpos + x - 1); addch (ACS_LRCORNER); ypos++; if (description) { CH *start = description; int len = 0; do { int i; move (ypos, xpos); addch (ACS_VLINE); addch (' '); collect_line (size_x - 4, &start, &len); for (i = 0; i < len - 1; i++) { ADDCH (start[i]); } if (start[len - 1] != NULLCH && start[len - 1] != NLCH) ADDCH (start[len - 1]); ypos++; } while (start[len - 1]); move (ypos, xpos); addch (ACS_VLINE); ypos++; } if (pinentry->pin) { int i; if (error) { CH *p = error; i = 0; while (*p) { move (ypos, xpos); addch (ACS_VLINE); addch (' '); if (USE_COLORS && pinentry->color_so != PINENTRY_COLOR_NONE) { attroff (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0)); attron (COLOR_PAIR (2) | (pinentry->color_so_bright ? A_BOLD : 0)); } else standout (); for (;*p && *p != NLCH; p++) if (i < x - 4) { i++; ADDCH (*p); } if (USE_COLORS && pinentry->color_so != PINENTRY_COLOR_NONE) { attroff (COLOR_PAIR (2) | (pinentry->color_so_bright ? A_BOLD : 0)); attron (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0)); } else standend (); if (*p == '\n') p++; i = 0; ypos++; } move (ypos, xpos); addch (ACS_VLINE); ypos++; } move (ypos, xpos); addch (ACS_VLINE); addch (' '); dialog->pin_y = ypos; dialog->pin_x = xpos + 2; dialog->pin_size = x - 4; if (prompt) { CH *p = prompt; i = STRLEN (prompt); if (i > x - 4 - MIN_PINENTRY_LENGTH) i = x - 4 - MIN_PINENTRY_LENGTH; dialog->pin_x += i + 1; dialog->pin_size -= i + 1; while (i-- > 0) { ADDCH (*(p++)); } addch (' '); } for (i = 0; i < dialog->pin_size; i++) addch ('_'); ypos++; move (ypos, xpos); addch (ACS_VLINE); ypos++; } move (ypos, xpos); addch (ACS_VLINE); if (dialog->cancel || dialog->notok) { dialog->ok_y = ypos; /* Calculating the left edge of the left button, rounding down. */ dialog->ok_x = xpos + 2 + ((x - 4) / 3 - strlen (dialog->ok)) / 2; move (dialog->ok_y, dialog->ok_x); addstr (dialog->ok); if (! pinentry->pin && dialog->notok) { dialog->notok_y = ypos; /* Calculating the left edge of the middle button, rounding up. */ dialog->notok_x = xpos + x / 2 - strlen (dialog->notok) / 2; move (dialog->notok_y, dialog->notok_x); addstr (dialog->notok); } if (dialog->cancel) { dialog->cancel_y = ypos; /* Calculating the left edge of the right button, rounding up. */ dialog->cancel_x = xpos + x - 2 - ((x - 4) / 3 + strlen (dialog->cancel)) / 2; move (dialog->cancel_y, dialog->cancel_x); addstr (dialog->cancel); } } else { dialog->ok_y = ypos; /* Calculating the left edge of the OK button, rounding down. */ dialog->ok_x = xpos + x / 2 - strlen (dialog->ok) / 2; move (dialog->ok_y, dialog->ok_x); addstr (dialog->ok); } out: if (description) free (description); if (error) free (error); if (prompt) free (prompt); return err; } static void set_cursor_state (int on) { static int normal_state = -1; static int on_last; if (normal_state < 0 && !on) { normal_state = curs_set (0); on_last = on; } else if (on != on_last) { curs_set (on ? normal_state : 0); on_last = on; } } static int dialog_switch_pos (dialog_t diag, dialog_pos_t new_pos) { if (new_pos != diag->pos) { switch (diag->pos) { case DIALOG_POS_OK: move (diag->ok_y, diag->ok_x); addstr (diag->ok); break; case DIALOG_POS_NOTOK: if (diag->notok) { move (diag->notok_y, diag->notok_x); addstr (diag->notok); } break; case DIALOG_POS_CANCEL: if (diag->cancel) { move (diag->cancel_y, diag->cancel_x); addstr (diag->cancel); } break; default: break; } diag->pos = new_pos; switch (diag->pos) { case DIALOG_POS_PIN: move (diag->pin_y, diag->pin_x + diag->pin_loc); set_cursor_state (1); break; case DIALOG_POS_OK: set_cursor_state (0); move (diag->ok_y, diag->ok_x); standout (); addstr (diag->ok); standend (); move (diag->ok_y, diag->ok_x); break; case DIALOG_POS_NOTOK: if (diag->notok) { set_cursor_state (0); move (diag->notok_y, diag->notok_x); standout (); addstr (diag->notok); standend (); move (diag->notok_y, diag->notok_x); } break; case DIALOG_POS_CANCEL: if (diag->cancel) { set_cursor_state (0); move (diag->cancel_y, diag->cancel_x); standout (); addstr (diag->cancel); standend (); move (diag->cancel_y, diag->cancel_x); } break; case DIALOG_POS_NONE: set_cursor_state (0); break; } refresh (); } return 0; } /* XXX Assume that field width is at least > 5. */ static void dialog_input (dialog_t diag, int alt, int chr) { int old_loc = diag->pin_loc; assert (diag->pinentry->pin); assert (diag->pos == DIALOG_POS_PIN); if (alt && chr == KEY_BACKSPACE) /* Remap alt-backspace to control-W. */ chr = 'w' - 'a' + 1; switch (chr) { case KEY_BACKSPACE: /* control-h. */ case 'h' - 'a' + 1: /* ASCII DEL. What Mac OS X apparently emits when the "delete" (backspace) key is pressed. */ case 127: if (diag->pin_len > 0) { diag->pin_len--; diag->pin_loc--; if (diag->pin_loc == 0 && diag->pin_len > 0) { diag->pin_loc = diag->pin_size - 5; if (diag->pin_loc > diag->pin_len) diag->pin_loc = diag->pin_len; } } break; case 'l' - 'a' + 1: /* control-l */ /* Refresh the screen. */ endwin (); refresh (); break; case 'u' - 'a' + 1: /* control-u */ /* Erase the whole line. */ if (diag->pin_len > 0) { diag->pin_len = 0; diag->pin_loc = 0; } break; case 'w' - 'a' + 1: /* control-w. */ while (diag->pin_len > 0 && diag->pinentry->pin[diag->pin_len - 1] == ' ') { diag->pin_len --; diag->pin_loc --; if (diag->pin_loc < 0) { diag->pin_loc += diag->pin_size; if (diag->pin_loc > diag->pin_len) diag->pin_loc = diag->pin_len; } } while (diag->pin_len > 0 && diag->pinentry->pin[diag->pin_len - 1] != ' ') { diag->pin_len --; diag->pin_loc --; if (diag->pin_loc < 0) { diag->pin_loc += diag->pin_size; if (diag->pin_loc > diag->pin_len) diag->pin_loc = diag->pin_len; } } break; default: if (chr > 0 && chr < 256 && diag->pin_len < diag->pin_max) { /* Make sure there is enough room for this character and a following NUL byte. */ if (! pinentry_setbufferlen (diag->pinentry, diag->pin_len + 2)) { /* Bail. Here we use a simple approach. It would be better to have a pinentry_bug function. */ assert (!"setbufferlen failed"); abort (); } diag->pinentry->pin[diag->pin_len] = (char) chr; diag->pin_len++; diag->pin_loc++; if (diag->pin_loc == diag->pin_size && diag->pin_len < diag->pin_max) { diag->pin_loc = 5; if (diag->pin_loc < diag->pin_size - (diag->pin_max + 1 - diag->pin_len)) diag->pin_loc = diag->pin_size - (diag->pin_max + 1 - diag->pin_len); } } break; } if (old_loc < diag->pin_loc) { move (diag->pin_y, diag->pin_x + old_loc); while (old_loc++ < diag->pin_loc) addch ('*'); } else if (old_loc > diag->pin_loc) { move (diag->pin_y, diag->pin_x + diag->pin_loc); while (old_loc-- > diag->pin_loc) addch ('_'); } move (diag->pin_y, diag->pin_x + diag->pin_loc); } static int dialog_run (pinentry_t pinentry, const char *tty_name, const char *tty_type) { int confirm_mode = !pinentry->pin; struct dialog diag; FILE *ttyfi = NULL; FILE *ttyfo = NULL; SCREEN *screen = 0; int done = 0; char *pin_utf8; int alt = 0; #ifndef HAVE_DOSISH_SYSTEM int no_input = 1; #endif #ifdef HAVE_NCURSESW char *old_ctype = NULL; if (pinentry->lc_ctype) { old_ctype = strdup (setlocale (LC_CTYPE, NULL)); setlocale (LC_CTYPE, pinentry->lc_ctype); } else setlocale (LC_CTYPE, ""); #endif /* Open the desired terminal if necessary. */ if (tty_name) { ttyfi = fopen (tty_name, "r"); if (!ttyfi) { pinentry->specific_err = gpg_error_from_syserror (); pinentry->specific_err_loc = "open_tty_for_read"; return confirm_mode? 0 : -1; } ttyfo = fopen (tty_name, "w"); if (!ttyfo) { int err = errno; fclose (ttyfi); errno = err; pinentry->specific_err = gpg_error_from_syserror (); pinentry->specific_err_loc = "open_tty_for_write"; return confirm_mode? 0 : -1; } screen = newterm (tty_type, ttyfo, ttyfi); set_term (screen); } else { if (!init_screen) { if (!(isatty(fileno(stdin)) && isatty(fileno(stdout)))) { errno = ENOTTY; pinentry->specific_err = gpg_error_from_syserror (); pinentry->specific_err_loc = "isatty"; return confirm_mode? 0 : -1; } init_screen = 1; initscr (); } else clear (); } keypad (stdscr, TRUE); /* Enable keyboard mapping. */ nonl (); /* Tell curses not to do NL->CR/NL on output. */ cbreak (); /* Take input chars one at a time, no wait for \n. */ noecho (); /* Don't echo input - in color. */ if (pinentry->ttyalert) { if (! strcmp(pinentry->ttyalert, "beep")) beep (); else if (! strcmp(pinentry->ttyalert, "flash")) flash (); } if (has_colors ()) { start_color (); #ifdef NCURSES_VERSION use_default_colors (); #endif if (pinentry->color_so == PINENTRY_COLOR_DEFAULT) { pinentry->color_so = PINENTRY_COLOR_RED; pinentry->color_so_bright = 1; } if (COLOR_PAIRS >= 2) { init_pair (1, pinentry_color[pinentry->color_fg], pinentry_color[pinentry->color_bg]); init_pair (2, pinentry_color[pinentry->color_so], pinentry_color[pinentry->color_bg]); bkgd (COLOR_PAIR (1)); attron (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0)); } } refresh (); /* Create the dialog. */ if (dialog_create (pinentry, &diag)) { /* Note: pinentry->specific_err has already been set. */ endwin (); if (screen) delscreen (screen); #ifdef HAVE_NCURSESW if (old_ctype) { setlocale (LC_CTYPE, old_ctype); free (old_ctype); } #endif if (ttyfi) fclose (ttyfi); if (ttyfo) fclose (ttyfo); return -2; } dialog_switch_pos (&diag, confirm_mode? DIALOG_POS_OK : DIALOG_POS_PIN); #ifndef HAVE_DOSISH_SYSTEM wtimeout (stdscr, 70); #endif do { int c; c = wgetch (stdscr); /* Refresh, accept single keystroke of input. */ #ifndef HAVE_DOSISH_SYSTEM if (timed_out && no_input) { done = -2; pinentry->specific_err = gpg_error (GPG_ERR_TIMEOUT); break; } #endif switch (c) { case ERR: #ifndef HAVE_DOSISH_SYSTEM continue; #else done = -2; break; #endif case 27: /* Alt was pressed. */ alt = 1; /* Get the next key press. */ continue; case KEY_LEFT: case KEY_UP: switch (diag.pos) { case DIALOG_POS_OK: if (!confirm_mode) dialog_switch_pos (&diag, DIALOG_POS_PIN); break; case DIALOG_POS_NOTOK: dialog_switch_pos (&diag, DIALOG_POS_OK); break; case DIALOG_POS_CANCEL: if (diag.notok) dialog_switch_pos (&diag, DIALOG_POS_NOTOK); else dialog_switch_pos (&diag, DIALOG_POS_OK); break; default: break; } break; case KEY_RIGHT: case KEY_DOWN: switch (diag.pos) { case DIALOG_POS_PIN: dialog_switch_pos (&diag, DIALOG_POS_OK); break; case DIALOG_POS_OK: if (diag.notok) dialog_switch_pos (&diag, DIALOG_POS_NOTOK); else dialog_switch_pos (&diag, DIALOG_POS_CANCEL); break; case DIALOG_POS_NOTOK: dialog_switch_pos (&diag, DIALOG_POS_CANCEL); break; default: break; } break; case '\t': switch (diag.pos) { case DIALOG_POS_PIN: dialog_switch_pos (&diag, DIALOG_POS_OK); break; case DIALOG_POS_OK: if (diag.notok) dialog_switch_pos (&diag, DIALOG_POS_NOTOK); else dialog_switch_pos (&diag, DIALOG_POS_CANCEL); break; case DIALOG_POS_NOTOK: dialog_switch_pos (&diag, DIALOG_POS_CANCEL); break; case DIALOG_POS_CANCEL: if (confirm_mode) dialog_switch_pos (&diag, DIALOG_POS_OK); else dialog_switch_pos (&diag, DIALOG_POS_PIN); break; default: break; } break; case '\005': done = -2; break; case '\r': switch (diag.pos) { case DIALOG_POS_PIN: case DIALOG_POS_OK: done = 1; break; case DIALOG_POS_NOTOK: done = -1; break; case DIALOG_POS_CANCEL: done = -2; break; case DIALOG_POS_NONE: break; } break; default: if (diag.pos == DIALOG_POS_PIN) dialog_input (&diag, alt, c); } #ifndef HAVE_DOSISH_SYSTEM no_input = 0; #endif if (c != -1) alt = 0; } while (!done); if (!confirm_mode) { /* NUL terminate the passphrase. dialog_run makes sure there is enough space for the terminating NUL byte. */ diag.pinentry->pin[diag.pin_len] = 0; } set_cursor_state (1); endwin (); if (screen) delscreen (screen); #ifdef HAVE_NCURSESW if (old_ctype) { setlocale (LC_CTYPE, old_ctype); free (old_ctype); } #endif if (ttyfi) fclose (ttyfi); if (ttyfo) fclose (ttyfo); /* XXX Factor out into dialog_release or something. */ free (diag.ok); if (diag.cancel) free (diag.cancel); if (diag.notok) free (diag.notok); if (!confirm_mode) { pinentry->locale_err = 1; pin_utf8 = pinentry_local_to_utf8 (pinentry->lc_ctype, pinentry->pin, 1); if (pin_utf8) { pinentry_setbufferlen (pinentry, strlen (pin_utf8) + 1); if (pinentry->pin) strcpy (pinentry->pin, pin_utf8); secmem_free (pin_utf8); pinentry->locale_err = 0; } } if (done == -2) pinentry->canceled = 1; /* In confirm mode return cancel instead of error. */ if (confirm_mode) return done < 0 ? 0 : 1; return done < 0 ? -1 : diag.pin_len; } /* If a touch has been registered, touch that file. */ static void do_touch_file (pinentry_t pinentry) { #ifdef HAVE_UTIME_H struct stat st; time_t tim; if (!pinentry->touch_file || !*pinentry->touch_file) return; if (stat (pinentry->touch_file, &st)) return; /* Oops. */ /* Make sure that we actually update the mtime. */ while ( (tim = time (NULL)) == st.st_mtime ) sleep (1); /* Update but ignore errors as we can't do anything in that case. Printing error messages may even clubber the display further. */ utime (pinentry->touch_file, NULL); #endif /*HAVE_UTIME_H*/ } #ifndef HAVE_DOSISH_SYSTEM static void catchsig (int sig) { if (sig == SIGALRM) timed_out = 1; } #endif int curses_cmd_handler (pinentry_t pinentry) { int rc; #ifndef HAVE_DOSISH_SYSTEM timed_out = 0; if (pinentry->timeout) { struct sigaction sa; memset (&sa, 0, sizeof(sa)); sa.sa_handler = catchsig; sigaction (SIGALRM, &sa, NULL); alarm (pinentry->timeout); } #endif rc = dialog_run (pinentry, pinentry->ttyname, pinentry->ttytype); do_touch_file (pinentry); return rc; } diff --git a/pinentry/pinentry-curses.h b/pinentry/pinentry-curses.h index e05593f..b33f134 100644 --- a/pinentry/pinentry-curses.h +++ b/pinentry/pinentry-curses.h @@ -1,36 +1,35 @@ /* pinentry-curses.h - A secure curses dialog for PIN entry, library version - Copyright (C) 2002 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA */ - + * Copyright (C) 2002 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+ + */ #ifndef PINENTRY_CURSES_H #define PINENTRY_CURSES_H #include "pinentry.h" #ifdef __cplusplus extern "C" { #endif int curses_cmd_handler (pinentry_t pinentry); #ifdef __cplusplus } #endif #endif /* PINENTRY_CURSES_H */ diff --git a/pinentry/pinentry-emacs.c b/pinentry/pinentry-emacs.c index 2f7693b..190ea9d 100644 --- a/pinentry/pinentry-emacs.c +++ b/pinentry/pinentry-emacs.c @@ -1,704 +1,705 @@ /* pinentry-emacs.c - A secure emacs dialog for PIN entry, library version - Copyright (C) 2015 Daiki Ueno - - 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 . -*/ + * Copyright (C) 2015 Daiki Ueno + * + * 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+ + */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_INTTYPES_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UTIME_H #include #endif /*HAVE_UTIME_H*/ #include #include "pinentry-emacs.h" #include "memory.h" #include "secmem-util.h" /* The communication mechanism is similar to emacsclient, but there are a few differences: - To avoid unnecessary character escaping and encoding conversion, we use a subset of the Pinentry Assuan protocol, instead of the emacsclient protocol. - We only use a Unix domain socket, while emacsclient has an ability to use a TCP socket. The socket file is located at ${TMPDIR-/tmp}/emacs$(id -u)/pinentry (i.e., under the same directory as the socket file used by emacsclient, so the same permission and file owner settings apply). - The server implementation can be found in pinentry.el, which is available in Emacs 25+ or from ELPA. */ #define LINELENGTH ASSUAN_LINELENGTH #define SEND_BUFFER_SIZE 4096 #define INITIAL_TIMEOUT 60 static int initial_timeout = INITIAL_TIMEOUT; #undef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #undef MAX #define MAX(x, y) ((x) < (y) ? (y) : (x)) #ifndef SUN_LEN # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + strlen ((ptr)->sun_path)) #endif /* FIXME: We could use the I/O functions in Assuan directly, once Pinentry links to libassuan. */ static int emacs_socket = -1; static char send_buffer[SEND_BUFFER_SIZE + 1]; static int send_buffer_length; /* Fill pointer for the send buffer. */ static pinentry_cmd_handler_t fallback_cmd_handler; #ifndef HAVE_DOSISH_SYSTEM static int timed_out; #endif static int set_socket (const char *socket_name) { struct sockaddr_un unaddr; struct stat statbuf; const char *tmpdir; char *tmpdir_storage = NULL; char *socket_name_storage = NULL; uid_t uid; unaddr.sun_family = AF_UNIX; /* We assume 32-bit UIDs, which can be represented with 10 decimal digits. */ uid = getuid (); if (uid != (uint32_t) uid) { fprintf (stderr, "UID is too large\n"); return 0; } tmpdir = getenv ("TMPDIR"); if (!tmpdir) { #ifdef _CS_DARWIN_USER_TEMP_DIR size_t n = confstr (_CS_DARWIN_USER_TEMP_DIR, NULL, (size_t) 0); if (n > 0) { tmpdir = tmpdir_storage = malloc (n); if (!tmpdir) { fprintf (stderr, "out of core\n"); return 0; } confstr (_CS_DARWIN_USER_TEMP_DIR, tmpdir_storage, n); } else #endif tmpdir = "/tmp"; } socket_name_storage = malloc (strlen (tmpdir) + strlen ("/emacs") + 10 + strlen ("/") + strlen (socket_name) + 1); if (!socket_name_storage) { fprintf (stderr, "out of core\n"); free (tmpdir_storage); return 0; } sprintf (socket_name_storage, "%s/emacs%u/%s", tmpdir, (uint32_t) uid, socket_name); free (tmpdir_storage); if (strlen (socket_name_storage) >= sizeof (unaddr.sun_path)) { fprintf (stderr, "socket name is too long\n"); free (socket_name_storage); return 0; } strcpy (unaddr.sun_path, socket_name_storage); free (socket_name_storage); /* See if the socket exists, and if it's owned by us. */ if (stat (unaddr.sun_path, &statbuf) == -1) { perror ("stat"); return 0; } if (statbuf.st_uid != geteuid ()) { fprintf (stderr, "socket is not owned by the same user\n"); return 0; } emacs_socket = socket (AF_UNIX, SOCK_STREAM, 0); if (emacs_socket < 0) { perror ("socket"); return 0; } if (connect (emacs_socket, (struct sockaddr *) &unaddr, SUN_LEN (&unaddr)) < 0) { perror ("connect"); close (emacs_socket); emacs_socket = -1; return 0; } return 1; } /* Percent-escape control characters in DATA. Return a newly allocated string. */ static char * escape (const char *data) { char *buffer, *out_p; size_t length, buffer_length; size_t offset; size_t count = 0; length = strlen (data); for (offset = 0; offset < length; offset++) { switch (data[offset]) { case '%': case '\n': case '\r': count++; break; default: break; } } buffer_length = length + count * 2; buffer = malloc (buffer_length + 1); if (!buffer) return NULL; out_p = buffer; for (offset = 0; offset < length; offset++) { int c = data[offset]; switch (c) { case '%': case '\n': case '\r': sprintf (out_p, "%%%02X", c); out_p += 3; break; default: *out_p++ = c; break; } } *out_p = '\0'; return buffer; } /* The inverse of escape. Unlike escape, it removes quoting in string DATA by modifying the string in place, to avoid copying of secret data sent from Emacs. */ static char * unescape (char *data) { char *p = data, *q = data; while (*p) { if (*p == '%' && p[1] && p[2]) { p++; *q++ = xtoi_2 (p); p += 2; } else *q++ = *p++; } *q = 0; return data; } /* Let's send the data to Emacs when either - the data ends in "\n", or - the buffer is full (but this shouldn't happen) Otherwise, we just accumulate it. */ static int send_to_emacs (int s, const char *buffer) { size_t length; length = strlen (buffer); while (*buffer) { size_t part = MIN (length, SEND_BUFFER_SIZE - send_buffer_length); memcpy (&send_buffer[send_buffer_length], buffer, part); buffer += part; send_buffer_length += part; if (send_buffer_length == SEND_BUFFER_SIZE || (send_buffer_length > 0 && send_buffer[send_buffer_length-1] == '\n')) { int sent = send (s, send_buffer, send_buffer_length, 0); if (sent < 0) { fprintf (stderr, "failed to send %d bytes to socket: %s\n", send_buffer_length, strerror (errno)); send_buffer_length = 0; return 0; } if (sent != send_buffer_length) memmove (send_buffer, &send_buffer[sent], send_buffer_length - sent); send_buffer_length -= sent; } length -= part; } return 1; } /* Read a server response. If the response contains data, it will be stored in BUFFER with a terminating NUL byte. BUFFER must be at least as large as CAPACITY. */ static gpg_error_t read_from_emacs (int s, int timeout, char *buffer, size_t capacity) { struct timeval tv; fd_set rfds; int retval; /* Offset in BUFFER. */ size_t offset = 0; int got_response = 0; char read_buffer[LINELENGTH + 1]; /* Offset in READ_BUFFER. */ size_t read_offset = 0; gpg_error_t result = 0; tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO (&rfds); FD_SET (s, &rfds); retval = select (s + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { perror ("select"); return gpg_error (GPG_ERR_ASS_GENERAL); } else if (retval == 0) { timed_out = 1; return gpg_error (GPG_ERR_TIMEOUT); } /* Loop until we get either OK or ERR. */ while (!got_response) { int rl = 0; char *p, *end_p; do { errno = 0; rl = recv (s, read_buffer + read_offset, LINELENGTH - read_offset, 0); } /* If we receive a signal (e.g. SIGWINCH, which we pass through to Emacs), on some OSes we get EINTR and must retry. */ while (rl < 0 && errno == EINTR); if (rl < 0) { perror ("recv"); return gpg_error (GPG_ERR_ASS_GENERAL);; } if (rl == 0) break; read_offset += rl; read_buffer[read_offset] = '\0'; end_p = strchr (read_buffer, '\n'); /* If the buffer is filled without NL, throw away the content and start over the buffering. FIXME: We could return ASSUAN_Line_Too_Long or ASSUAN_Line_Not_Terminated here. */ if (!end_p && read_offset == sizeof (read_buffer) - 1) { read_offset = 0; continue; } /* Loop over all NL-terminated messages. */ for (p = read_buffer; end_p; p = end_p + 1, end_p = strchr (p, '\n')) { *end_p = '\0'; if (!strncmp ("D ", p, 2)) { char *data; size_t data_length; size_t needed_capacity; data = p + 2; data_length = end_p - data; if (data_length > 0) { needed_capacity = offset + data_length + 1; /* Check overflow. This is unrealistic but can happen since OFFSET is cumulative. */ if (needed_capacity < offset) return gpg_error (GPG_ERR_ASS_GENERAL);; if (needed_capacity > capacity) return gpg_error (GPG_ERR_ASS_GENERAL);; memcpy (&buffer[offset], data, data_length); offset += data_length; buffer[offset] = 0; } } else if (!strcmp ("OK", p) || !strncmp ("OK ", p, 3)) { got_response = 1; break; } else if (!strncmp ("ERR ", p, 4)) { unsigned long code = strtoul (p + 4, NULL, 10); if (code == ULONG_MAX && errno == ERANGE) return gpg_error (GPG_ERR_ASS_GENERAL); else result = code; got_response = 1; break; } else if (*p == '#') ; else fprintf (stderr, "invalid response: %s\n", p); } if (!got_response) { size_t length = &read_buffer[read_offset] - p; memmove (read_buffer, p, length); read_offset = length; } } return result; } int set_label (pinentry_t pe, const char *name, const char *value) { char buffer[16], *escaped; gpg_error_t error; int retval; if (!send_to_emacs (emacs_socket, name) || !send_to_emacs (emacs_socket, " ")) return 0; escaped = escape (value); if (!escaped) return 0; retval = send_to_emacs (emacs_socket, escaped) && send_to_emacs (emacs_socket, "\n"); free (escaped); if (!retval) return 0; error = read_from_emacs (emacs_socket, pe->timeout, buffer, sizeof (buffer)); return error == 0; } static void set_labels (pinentry_t pe) { char *p; p = pinentry_get_title (pe); if (p) { set_label (pe, "SETTITLE", p); free (p); } if (pe->description) set_label (pe, "SETDESC", pe->description); if (pe->error) set_label (pe, "SETERROR", pe->error); if (pe->prompt) set_label (pe, "SETPROMPT", pe->prompt); else if (pe->default_prompt) set_label (pe, "SETPROMPT", pe->default_prompt); if (pe->repeat_passphrase) set_label (pe, "SETREPEAT", pe->repeat_passphrase); if (pe->repeat_error_string) set_label (pe, "SETREPEATERROR", pe->repeat_error_string); /* XXX: pe->quality_bar and pe->quality_bar_tt are not supported. */ /* Buttons. */ if (pe->ok) set_label (pe, "SETOK", pe->ok); else if (pe->default_ok) set_label (pe, "SETOK", pe->default_ok); if (pe->cancel) set_label (pe, "SETCANCEL", pe->cancel); else if (pe->default_ok) set_label (pe, "SETCANCEL", pe->default_cancel); if (pe->notok) set_label (pe, "SETNOTOK", pe->notok); } static int do_password (pinentry_t pe) { char *buffer, *password; size_t length = LINELENGTH; gpg_error_t error; set_labels (pe); if (!send_to_emacs (emacs_socket, "GETPIN\n")) return -1; buffer = secmem_malloc (length); if (!buffer) { pe->specific_err = gpg_error (GPG_ERR_ENOMEM); return -1; } error = read_from_emacs (emacs_socket, pe->timeout, buffer, length); if (error != 0) { if (gpg_err_code (error) == GPG_ERR_CANCELED) pe->canceled = 1; secmem_free (buffer); pe->specific_err = error; return -1; } password = unescape (buffer); pinentry_setbufferlen (pe, strlen (password) + 1); if (pe->pin) strcpy (pe->pin, password); secmem_free (buffer); if (pe->repeat_passphrase) pe->repeat_okay = 1; /* XXX: we don't support external password cache (yet). */ return 1; } static int do_confirm (pinentry_t pe) { char buffer[16]; gpg_error_t error; set_labels (pe); if (!send_to_emacs (emacs_socket, "CONFIRM\n")) return 0; error = read_from_emacs (emacs_socket, pe->timeout, buffer, sizeof (buffer)); if (error != 0) { if (gpg_err_code (error) == GPG_ERR_CANCELED) pe->canceled = 1; pe->specific_err = error; return 0; } return 1; } /* If a touch has been registered, touch that file. */ static void do_touch_file (pinentry_t pinentry) { #ifdef HAVE_UTIME_H struct stat st; time_t tim; if (!pinentry->touch_file || !*pinentry->touch_file) return; if (stat (pinentry->touch_file, &st)) return; /* Oops. */ /* Make sure that we actually update the mtime. */ while ( (tim = time (NULL)) == st.st_mtime ) sleep (1); /* Update but ignore errors as we can't do anything in that case. Printing error messages may even clubber the display further. */ utime (pinentry->touch_file, NULL); #endif /*HAVE_UTIME_H*/ } #ifndef HAVE_DOSISH_SYSTEM static void catchsig (int sig) { if (sig == SIGALRM) timed_out = 1; } #endif int emacs_cmd_handler (pinentry_t pe) { int rc; #ifndef HAVE_DOSISH_SYSTEM timed_out = 0; if (pe->timeout) { struct sigaction sa; memset (&sa, 0, sizeof(sa)); sa.sa_handler = catchsig; sigaction (SIGALRM, &sa, NULL); alarm (pe->timeout); } #endif if (pe->pin) rc = do_password (pe); else rc = do_confirm (pe); do_touch_file (pe); return rc; } static int initial_emacs_cmd_handler (pinentry_t pe) { /* Let the select() call in pinentry_emacs_init honor the timeout value set through an Assuan option. */ initial_timeout = pe->timeout; if (emacs_socket < 0) pinentry_emacs_init (); /* If we have successfully connected to Emacs, swap pinentry_cmd_handler to emacs_cmd_handler, so further interactions will be forwarded to Emacs. Otherwise, set it back to the original command handler saved as fallback_cmd_handler. */ if (emacs_socket < 0) pinentry_cmd_handler = fallback_cmd_handler; else { pinentry_cmd_handler = emacs_cmd_handler; pinentry_set_flavor_flag ("emacs"); } return (* pinentry_cmd_handler) (pe); } void pinentry_enable_emacs_cmd_handler (void) { const char *envvar; /* Check if pinentry_cmd_handler is already prepared for Emacs. */ if (pinentry_cmd_handler == initial_emacs_cmd_handler || pinentry_cmd_handler == emacs_cmd_handler) return; /* Check if INSIDE_EMACS envvar is set. */ envvar = getenv ("INSIDE_EMACS"); if (!envvar || !*envvar) return; /* Save the original command handler as fallback_cmd_handler, and swap pinentry_cmd_handler to initial_emacs_cmd_handler. */ fallback_cmd_handler = pinentry_cmd_handler; pinentry_cmd_handler = initial_emacs_cmd_handler; } int pinentry_emacs_init (void) { char buffer[256]; gpg_error_t error; assert (emacs_socket < 0); /* Check if we can connect to the Emacs server socket. */ if (!set_socket ("pinentry")) return 0; /* Check if the server responds. */ error = read_from_emacs (emacs_socket, initial_timeout, buffer, sizeof (buffer)); if (error != 0) { close (emacs_socket); emacs_socket = -1; return 0; } return 1; } diff --git a/pinentry/pinentry-emacs.h b/pinentry/pinentry-emacs.h index 24dabb9..00b4413 100644 --- a/pinentry/pinentry-emacs.h +++ b/pinentry/pinentry-emacs.h @@ -1,43 +1,44 @@ /* pinentry-emacs.c - A secure emacs dialog for PIN entry, library version - Copyright (C) 2015 Daiki Ueno - - 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 . -*/ + * Copyright (C) 2015 Daiki Ueno + * + * 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+ + */ #ifndef PINENTRY_EMACS_H #define PINENTRY_EMACS_H #include "pinentry.h" #ifdef __cplusplus extern "C" { #endif /* Enable pinentry command handler which interacts with Emacs, if INSIDE_EMACS envvar is set. This function shall be called upon receiving an Assuan request "OPTION allow-emacs-prompt". */ void pinentry_enable_emacs_cmd_handler (void); /* Initialize the Emacs interface, return true if success. */ int pinentry_emacs_init (void); int emacs_cmd_handler (pinentry_t pinentry); #ifdef __cplusplus } #endif #endif /* PINENTRY_EMACS_H */ diff --git a/pinentry/pinentry.c b/pinentry/pinentry.c index ac3a323..17b994d 100644 --- a/pinentry/pinentry.c +++ b/pinentry/pinentry.c @@ -1,1896 +1,1897 @@ /* pinentry.c - The PIN entry support library - Copyright (C) 2002, 2003, 2007, 2008, 2010, 2015, 2016 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 . + * Copyright (C) 2002, 2003, 2007, 2008, 2010, 2015, 2016 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+ */ #ifdef HAVE_CONFIG_H #include #endif #ifndef HAVE_W32CE_SYSTEM # include #endif #include #include #include #include #include #ifndef HAVE_W32CE_SYSTEM # include #endif #ifdef HAVE_LANGINFO_H #include #endif #include #ifdef HAVE_W32CE_SYSTEM # include #endif #if defined FALLBACK_CURSES || defined PINENTRY_CURSES || defined PINENTRY_GTK #include #endif #include #include "memory.h" #include "secmem-util.h" #include "argparse.h" #include "pinentry.h" #include "password-cache.h" #ifdef INSIDE_EMACS # include "pinentry-emacs.h" #endif #ifdef FALLBACK_CURSES # include "pinentry-curses.h" #endif #ifdef HAVE_W32CE_SYSTEM #define getpid() GetCurrentProcessId () #endif /* Keep the name of our program here. */ static char this_pgmname[50]; struct pinentry pinentry; static const char *flavor_flag; /* Because gtk_init removes the --display arg from the command lines * and our command line parser is called after gtk_init (so that it * does not see gtk specific options) we don't have a way to get hold * of the --display option. Our solution is to remember --disable in * the call to pinentry_have_display and set it then in our * parser. */ static char *remember_display; static void pinentry_reset (int use_defaults) { /* GPG Agent sets these options once when it starts the pinentry. Don't reset them. */ int grab = pinentry.grab; char *ttyname = pinentry.ttyname; char *ttytype = pinentry.ttytype; char *ttyalert = pinentry.ttyalert; char *lc_ctype = pinentry.lc_ctype; char *lc_messages = pinentry.lc_messages; int allow_external_password_cache = pinentry.allow_external_password_cache; char *default_ok = pinentry.default_ok; char *default_cancel = pinentry.default_cancel; char *default_prompt = pinentry.default_prompt; char *default_pwmngr = pinentry.default_pwmngr; char *default_cf_visi = pinentry.default_cf_visi; char *default_tt_visi = pinentry.default_tt_visi; char *default_tt_hide = pinentry.default_tt_hide; char *touch_file = pinentry.touch_file; unsigned long owner_pid = pinentry.owner_pid; int owner_uid = pinentry.owner_uid; char *owner_host = pinentry.owner_host; /* These options are set from the command line. Don't reset them. */ int debug = pinentry.debug; char *display = pinentry.display; int parent_wid = pinentry.parent_wid; pinentry_color_t color_fg = pinentry.color_fg; int color_fg_bright = pinentry.color_fg_bright; pinentry_color_t color_bg = pinentry.color_bg; pinentry_color_t color_so = pinentry.color_so; int color_so_bright = pinentry.color_so_bright; int timeout = pinentry.timeout; char *invisible_char = pinentry.invisible_char; /* Free any allocated memory. */ if (use_defaults) { free (pinentry.ttyname); free (pinentry.ttytype); free (pinentry.ttyalert); free (pinentry.lc_ctype); free (pinentry.lc_messages); free (pinentry.default_ok); free (pinentry.default_cancel); free (pinentry.default_prompt); free (pinentry.default_pwmngr); free (pinentry.default_cf_visi); free (pinentry.default_tt_visi); free (pinentry.default_tt_hide); free (pinentry.touch_file); free (pinentry.owner_host); free (pinentry.display); } free (pinentry.title); free (pinentry.description); free (pinentry.error); free (pinentry.prompt); free (pinentry.ok); free (pinentry.notok); free (pinentry.cancel); secmem_free (pinentry.pin); free (pinentry.repeat_passphrase); free (pinentry.repeat_error_string); free (pinentry.quality_bar); free (pinentry.quality_bar_tt); free (pinentry.keyinfo); free (pinentry.specific_err_info); /* Reset the pinentry structure. */ memset (&pinentry, 0, sizeof (pinentry)); /* Restore options without a default we want to preserve. */ pinentry.invisible_char = invisible_char; /* Restore other options or set defaults. */ if (use_defaults) { /* Pinentry timeout in seconds. */ pinentry.timeout = 60; /* Global grab. */ pinentry.grab = 1; pinentry.color_fg = PINENTRY_COLOR_DEFAULT; pinentry.color_fg_bright = 0; pinentry.color_bg = PINENTRY_COLOR_DEFAULT; pinentry.color_so = PINENTRY_COLOR_DEFAULT; pinentry.color_so_bright = 0; pinentry.owner_uid = -1; } else /* Restore the options. */ { pinentry.grab = grab; pinentry.ttyname = ttyname; pinentry.ttytype = ttytype; pinentry.ttyalert = ttyalert; pinentry.lc_ctype = lc_ctype; pinentry.lc_messages = lc_messages; pinentry.allow_external_password_cache = allow_external_password_cache; pinentry.default_ok = default_ok; pinentry.default_cancel = default_cancel; pinentry.default_prompt = default_prompt; pinentry.default_pwmngr = default_pwmngr; pinentry.default_cf_visi = default_cf_visi; pinentry.default_tt_visi = default_tt_visi; pinentry.default_tt_hide = default_tt_hide; pinentry.touch_file = touch_file; pinentry.owner_pid = owner_pid; pinentry.owner_uid = owner_uid; pinentry.owner_host = owner_host; pinentry.debug = debug; pinentry.display = display; pinentry.parent_wid = parent_wid; pinentry.color_fg = color_fg; pinentry.color_fg_bright = color_fg_bright; pinentry.color_bg = color_bg; pinentry.color_so = color_so; pinentry.color_so_bright = color_so_bright; pinentry.timeout = timeout; } } static gpg_error_t pinentry_assuan_reset_handler (assuan_context_t ctx, char *line) { (void)ctx; (void)line; pinentry_reset (0); return 0; } static int lc_ctype_unknown_warning = 0; #if defined FALLBACK_CURSES || defined PINENTRY_CURSES || defined PINENTRY_GTK char * pinentry_utf8_to_local (const char *lc_ctype, const char *text) { iconv_t cd; const char *input = text; size_t input_len = strlen (text) + 1; char *output; size_t output_len; char *output_buf; size_t processed; char *old_ctype; char *target_encoding; /* If no locale setting could be determined, simply copy the string. */ if (!lc_ctype) { if (! lc_ctype_unknown_warning) { fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n", this_pgmname); lc_ctype_unknown_warning = 1; } return strdup (text); } old_ctype = strdup (setlocale (LC_CTYPE, NULL)); if (!old_ctype) return NULL; setlocale (LC_CTYPE, lc_ctype); target_encoding = nl_langinfo (CODESET); if (!target_encoding) target_encoding = "?"; setlocale (LC_CTYPE, old_ctype); free (old_ctype); /* This is overkill, but simplifies the iconv invocation greatly. */ output_len = input_len * MB_LEN_MAX; output_buf = output = malloc (output_len); if (!output) return NULL; cd = iconv_open (target_encoding, "UTF-8"); if (cd == (iconv_t) -1) { fprintf (stderr, "%s: can't convert from UTF-8 to %s: %s\n", this_pgmname, target_encoding, strerror (errno)); free (output_buf); return NULL; } processed = iconv (cd, (ICONV_CONST char **)&input, &input_len, &output, &output_len); iconv_close (cd); if (processed == (size_t) -1 || input_len) { fprintf (stderr, "%s: error converting from UTF-8 to %s: %s\n", this_pgmname, target_encoding, strerror (errno)); free (output_buf); return NULL; } return output_buf; } /* Convert TEXT which is encoded according to LC_CTYPE to UTF-8. With SECURE set to true, use secure memory for the returned buffer. Return NULL on error. */ char * pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure) { char *old_ctype; char *source_encoding; iconv_t cd; const char *input = text; size_t input_len = strlen (text) + 1; char *output; size_t output_len; char *output_buf; size_t processed; /* If no locale setting could be determined, simply copy the string. */ if (!lc_ctype) { if (! lc_ctype_unknown_warning) { fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n", this_pgmname); lc_ctype_unknown_warning = 1; } output_buf = secure? secmem_malloc (input_len) : malloc (input_len); if (output_buf) strcpy (output_buf, input); return output_buf; } old_ctype = strdup (setlocale (LC_CTYPE, NULL)); if (!old_ctype) return NULL; setlocale (LC_CTYPE, lc_ctype); source_encoding = nl_langinfo (CODESET); setlocale (LC_CTYPE, old_ctype); free (old_ctype); /* This is overkill, but simplifies the iconv invocation greatly. */ output_len = input_len * MB_LEN_MAX; output_buf = output = secure? secmem_malloc (output_len):malloc (output_len); if (!output) return NULL; cd = iconv_open ("UTF-8", source_encoding); if (cd == (iconv_t) -1) { fprintf (stderr, "%s: can't convert from %s to UTF-8: %s\n", this_pgmname, source_encoding? source_encoding : "?", strerror (errno)); if (secure) secmem_free (output_buf); else free (output_buf); return NULL; } processed = iconv (cd, (ICONV_CONST char **)&input, &input_len, &output, &output_len); iconv_close (cd); if (processed == (size_t) -1 || input_len) { fprintf (stderr, "%s: error converting from %s to UTF-8: %s\n", this_pgmname, source_encoding? source_encoding : "?", strerror (errno)); if (secure) secmem_free (output_buf); else free (output_buf); return NULL; } return output_buf; } #endif /* Copy TEXT or TEXTLEN to BUFFER and escape as required. Return a pointer to the end of the new buffer. Note that BUFFER must be large enough to keep the entire text; allocataing it 3 times of TEXTLEN is sufficient. */ static char * copy_and_escape (char *buffer, const void *text, size_t textlen) { int i; const unsigned char *s = (unsigned char *)text; char *p = buffer; for (i=0; i < textlen; i++) { if (s[i] < ' ' || s[i] == '+') { snprintf (p, 4, "%%%02X", s[i]); p += 3; } else if (s[i] == ' ') *p++ = '+'; else *p++ = s[i]; } return p; } static char * get_cmdline (unsigned long pid) { char buffer[200]; FILE *fp; size_t i, n; snprintf (buffer, sizeof buffer, "/proc/%lu/cmdline", pid); buffer[sizeof buffer - 1] = 0; fp = fopen (buffer, "rb"); if (!fp) return NULL; n = fread (buffer, 1, sizeof buffer - 1, fp); if (n < sizeof buffer -1 && ferror (fp)) { /* Some error occurred. */ fclose (fp); return NULL; } fclose (fp); if (n == 0) return NULL; /* Arguments are delimited by Nuls. We should do proper quoting but * that can be a bit complicated, thus we simply replace the Nuls by * spaces. */ for (i=0; i < n; i++) if (!buffer[i] && i < n-1) buffer[i] = ' '; buffer[i] = 0; /* Make sure the last byte is the string terminator. */ return strdup (buffer); } /* Atomically ask the kernel for information about process PID. * Return a malloc'ed copy of the process name as long as the process * uid matches UID. If it cannot determine that the process has uid * UID, it returns NULL. * This is not as informative as get_cmdline, but it verifies that the * process does belong to the user in question. */ static char * get_pid_name_for_uid (unsigned long pid, int uid) { char buffer[400]; FILE *fp; size_t end, n; char *uidstr; snprintf (buffer, sizeof buffer, "/proc/%lu/status", pid); buffer[sizeof buffer - 1] = 0; fp = fopen (buffer, "rb"); if (!fp) return NULL; n = fread (buffer, 1, sizeof buffer - 1, fp); if (n < sizeof buffer -1 && ferror (fp)) { /* Some error occurred. */ fclose (fp); return NULL; } fclose (fp); if (n == 0) return NULL; if (strncmp (buffer, "Name:\t", 6)) return NULL; end = strcspn (buffer + 6, "\n") + 6; buffer[end] = 0; /* check that uid matches what we expect */ uidstr = strstr (buffer + end + 1, "\nUid:\t"); if (!uidstr) return NULL; if (atoi (uidstr + 6) != uid) return NULL; return strdup (buffer + 6); } /* Return a malloced string with the title. The caller mus free the * string. If no title is available or the title string has an error * NULL is returned. */ char * pinentry_get_title (pinentry_t pe) { char *title; if (pe->title) title = strdup (pe->title); else if (pe->owner_pid) { char buf[200]; struct utsname utsbuf; char *pidname = NULL; char *cmdline = NULL; if (pe->owner_host && !uname (&utsbuf) && utsbuf.nodename && !strcmp (utsbuf.nodename, pe->owner_host)) { pidname = get_pid_name_for_uid (pe->owner_pid, pe->owner_uid); if (pidname) cmdline = get_cmdline (pe->owner_pid); } if (pe->owner_host && (cmdline || pidname)) snprintf (buf, sizeof buf, "[%lu]@%s (%s)", pe->owner_pid, pe->owner_host, cmdline ? cmdline : pidname); else if (pe->owner_host) snprintf (buf, sizeof buf, "[%lu]@%s", pe->owner_pid, pe->owner_host); else snprintf (buf, sizeof buf, "[%lu] ", pe->owner_pid); buf[sizeof buf - 1] = 0; free (pidname); free (cmdline); title = strdup (buf); } else title = strdup (this_pgmname); return title; } /* Run a quality inquiry for PASSPHRASE of LENGTH. (We need LENGTH because not all backends might be able to return a proper C-string.). Returns: A value between -100 and 100 to give an estimate of the passphrase's quality. Negative values are use if the caller won't even accept that passphrase. Note that we expect just one data line which should not be escaped in any represent a numeric signed decimal value. Extra data is currently ignored but should not be send at all. */ int pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length) { assuan_context_t ctx = pin->ctx_assuan; const char prefix[] = "INQUIRE QUALITY "; char *command; char *line; size_t linelen; int gotvalue = 0; int value = 0; int rc; if (!ctx) return 0; /* Can't run the callback. */ if (length > 300) length = 300; /* Limit so that it definitely fits into an Assuan line. */ command = secmem_malloc (strlen (prefix) + 3*length + 1); if (!command) return 0; strcpy (command, prefix); copy_and_escape (command + strlen(command), passphrase, length); rc = assuan_write_line (ctx, command); secmem_free (command); if (rc) { fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc); return 0; } for (;;) { do { rc = assuan_read_line (ctx, &line, &linelen); if (rc) { fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc); return 0; } } while (*line == '#' || !linelen); if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (!line[3] || line[3] == ' ')) break; /* END command received*/ if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N' && (!line[3] || line[3] == ' ')) break; /* CAN command received*/ if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (!line[3] || line[3] == ' ')) break; /* ERR command received*/ if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue) continue; gotvalue = 1; value = atoi (line+2); } if (value < -100) value = -100; else if (value > 100) value = 100; return value; } /* Try to make room for at least LEN bytes in the pinentry. Returns new buffer on success and 0 on failure or when the old buffer is sufficient. */ char * pinentry_setbufferlen (pinentry_t pin, int len) { char *newp; if (pin->pin_len) assert (pin->pin); else assert (!pin->pin); if (len < 2048) len = 2048; if (len <= pin->pin_len) return pin->pin; newp = secmem_realloc (pin->pin, len); if (newp) { pin->pin = newp; pin->pin_len = len; } else { secmem_free (pin->pin); pin->pin = 0; pin->pin_len = 0; } return newp; } static void pinentry_setbuffer_clear (pinentry_t pin) { if (! pin->pin) { assert (pin->pin_len == 0); return; } assert (pin->pin_len > 0); secmem_free (pin->pin); pin->pin = NULL; pin->pin_len = 0; } static void pinentry_setbuffer_init (pinentry_t pin) { pinentry_setbuffer_clear (pin); pinentry_setbufferlen (pin, 0); } /* passphrase better be alloced with secmem_alloc. */ void pinentry_setbuffer_use (pinentry_t pin, char *passphrase, int len) { if (! passphrase) { assert (len == 0); pinentry_setbuffer_clear (pin); return; } if (passphrase && len == 0) len = strlen (passphrase) + 1; if (pin->pin) secmem_free (pin->pin); pin->pin = passphrase; pin->pin_len = len; } static struct assuan_malloc_hooks assuan_malloc_hooks = { secmem_malloc, secmem_realloc, secmem_free }; /* Initialize the secure memory subsystem, drop privileges and return. Must be called early. */ void pinentry_init (const char *pgmname) { /* Store away our name. */ if (strlen (pgmname) > sizeof this_pgmname - 2) abort (); strcpy (this_pgmname, pgmname); gpgrt_check_version (NULL); /* Initialize secure memory. 1 is too small, so the default size will be used. */ secmem_init (1); secmem_set_flags (SECMEM_WARN); drop_privs (); if (atexit (secmem_term)) { /* FIXME: Could not register at-exit function, bail out. */ } assuan_set_malloc_hooks (&assuan_malloc_hooks); } /* Simple test to check whether DISPLAY is set or the option --display was given. Used to decide whether the GUI or curses should be initialized. */ int pinentry_have_display (int argc, char **argv) { int found = 0; for (; argc; argc--, argv++) { if (!strcmp (*argv, "--display")) { if (argv[1] && !remember_display) { remember_display = strdup (argv[1]); if (!remember_display) { #ifndef HAVE_W32CE_SYSTEM fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno)); #endif exit (EXIT_FAILURE); } } found = 1; break; } else if (!strncmp (*argv, "--display=", 10)) { if (!remember_display) { remember_display = strdup (*argv+10); if (!remember_display) { #ifndef HAVE_W32CE_SYSTEM fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno)); #endif exit (EXIT_FAILURE); } } found = 1; break; } } #ifndef HAVE_W32CE_SYSTEM { const char *s; s = getenv ("DISPLAY"); if (s && *s) found = 1; } #endif return found; } /* Print usage information and and provide strings for help. */ static const char * my_strusage( int level ) { const char *p; switch (level) { case 11: p = this_pgmname; break; case 12: p = "pinentry"; break; case 13: p = PACKAGE_VERSION; break; case 14: p = "Copyright (C) 2016 g10 Code GmbH"; break; case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break; case 1: case 40: { static char *str; if (!str) { size_t n = 50 + strlen (this_pgmname); str = malloc (n); if (str) { snprintf (str, n, "Usage: %s [options] (-h for help)", this_pgmname); str[n-1] = 0; } } p = str; } break; case 41: p = "Ask securely for a secret and print it to stdout."; break; case 42: p = "1"; /* Flag print 40 as part of 41. */ break; default: p = NULL; break; } return p; } char * parse_color (char *arg, pinentry_color_t *color_p, int *bright_p) { static struct { const char *name; pinentry_color_t color; } colors[] = { { "none", PINENTRY_COLOR_NONE }, { "default", PINENTRY_COLOR_DEFAULT }, { "black", PINENTRY_COLOR_BLACK }, { "red", PINENTRY_COLOR_RED }, { "green", PINENTRY_COLOR_GREEN }, { "yellow", PINENTRY_COLOR_YELLOW }, { "blue", PINENTRY_COLOR_BLUE }, { "magenta", PINENTRY_COLOR_MAGENTA }, { "cyan", PINENTRY_COLOR_CYAN }, { "white", PINENTRY_COLOR_WHITE } }; int i; char *new_arg; pinentry_color_t color = PINENTRY_COLOR_DEFAULT; if (!arg) return NULL; new_arg = strchr (arg, ','); if (new_arg) new_arg++; if (bright_p) { const char *bname[] = { "bright-", "bright", "bold-", "bold" }; *bright_p = 0; for (i = 0; i < sizeof (bname) / sizeof (bname[0]); i++) if (!strncasecmp (arg, bname[i], strlen (bname[i]))) { *bright_p = 1; arg += strlen (bname[i]); } } for (i = 0; i < sizeof (colors) / sizeof (colors[0]); i++) if (!strncasecmp (arg, colors[i].name, strlen (colors[i].name))) color = colors[i].color; *color_p = color; return new_arg; } /* Parse the command line options. May exit the program if only help or version output is requested. */ void pinentry_parse_opts (int argc, char *argv[]) { static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n('d', "debug", "Turn on debugging output"), ARGPARSE_s_s('D', "display", "|DISPLAY|Set the X display"), ARGPARSE_s_s('T', "ttyname", "|FILE|Set the tty terminal node name"), ARGPARSE_s_s('N', "ttytype", "|NAME|Set the tty terminal type"), ARGPARSE_s_s('C', "lc-ctype", "|STRING|Set the tty LC_CTYPE value"), ARGPARSE_s_s('M', "lc-messages", "|STRING|Set the tty LC_MESSAGES value"), ARGPARSE_s_i('o', "timeout", "|SECS|Timeout waiting for input after this many seconds"), ARGPARSE_s_n('g', "no-global-grab", "Grab keyboard only while window is focused"), ARGPARSE_s_u('W', "parent-wid", "Parent window ID (for positioning)"), ARGPARSE_s_s('c', "colors", "|STRING|Set custom colors for ncurses"), ARGPARSE_s_s('a', "ttyalert", "|STRING|Set the alert mode (none, beep or flash)"), ARGPARSE_end() }; ARGPARSE_ARGS pargs = { &argc, &argv, 0 }; set_strusage (my_strusage); pinentry_reset (1); while (arg_parse (&pargs, opts)) { switch (pargs.r_opt) { case 'd': pinentry.debug = 1; break; case 'g': pinentry.grab = 0; break; case 'D': /* Note, this is currently not used because the GUI engine has already been initialized when parsing these options. */ pinentry.display = strdup (pargs.r.ret_str); if (!pinentry.display) { #ifndef HAVE_W32CE_SYSTEM fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno)); #endif exit (EXIT_FAILURE); } break; case 'T': pinentry.ttyname = strdup (pargs.r.ret_str); if (!pinentry.ttyname) { #ifndef HAVE_W32CE_SYSTEM fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno)); #endif exit (EXIT_FAILURE); } break; case 'N': pinentry.ttytype = strdup (pargs.r.ret_str); if (!pinentry.ttytype) { #ifndef HAVE_W32CE_SYSTEM fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno)); #endif exit (EXIT_FAILURE); } break; case 'C': pinentry.lc_ctype = strdup (pargs.r.ret_str); if (!pinentry.lc_ctype) { #ifndef HAVE_W32CE_SYSTEM fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno)); #endif exit (EXIT_FAILURE); } break; case 'M': pinentry.lc_messages = strdup (pargs.r.ret_str); if (!pinentry.lc_messages) { #ifndef HAVE_W32CE_SYSTEM fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno)); #endif exit (EXIT_FAILURE); } break; case 'W': pinentry.parent_wid = pargs.r.ret_ulong; break; case 'c': { char *tmpstr = pargs.r.ret_str; tmpstr = parse_color (tmpstr, &pinentry.color_fg, &pinentry.color_fg_bright); tmpstr = parse_color (tmpstr, &pinentry.color_bg, NULL); tmpstr = parse_color (tmpstr, &pinentry.color_so, &pinentry.color_so_bright); } break; case 'o': pinentry.timeout = pargs.r.ret_int; break; case 'a': pinentry.ttyalert = strdup (pargs.r.ret_str); if (!pinentry.ttyalert) { #ifndef HAVE_W32CE_SYSTEM fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno)); #endif exit (EXIT_FAILURE); } break; default: pargs.err = ARGPARSE_PRINT_WARNING; break; } } if (!pinentry.display && remember_display) { pinentry.display = remember_display; remember_display = NULL; } } /* Set the optional flag used with getinfo. */ void pinentry_set_flavor_flag (const char *string) { flavor_flag = string; } static gpg_error_t option_handler (assuan_context_t ctx, const char *key, const char *value) { (void)ctx; if (!strcmp (key, "no-grab") && !*value) pinentry.grab = 0; else if (!strcmp (key, "grab") && !*value) pinentry.grab = 1; else if (!strcmp (key, "debug-wait")) { #ifndef HAVE_W32CE_SYSTEM fprintf (stderr, "%s: waiting for debugger - my pid is %u ...\n", this_pgmname, (unsigned int) getpid()); sleep (*value?atoi (value):5); fprintf (stderr, "%s: ... okay\n", this_pgmname); #endif } else if (!strcmp (key, "display")) { if (pinentry.display) free (pinentry.display); pinentry.display = strdup (value); if (!pinentry.display) return gpg_error_from_syserror (); } else if (!strcmp (key, "ttyname")) { if (pinentry.ttyname) free (pinentry.ttyname); pinentry.ttyname = strdup (value); if (!pinentry.ttyname) return gpg_error_from_syserror (); } else if (!strcmp (key, "ttytype")) { if (pinentry.ttytype) free (pinentry.ttytype); pinentry.ttytype = strdup (value); if (!pinentry.ttytype) return gpg_error_from_syserror (); } else if (!strcmp (key, "ttyalert")) { if (pinentry.ttyalert) free (pinentry.ttyalert); pinentry.ttyalert = strdup (value); if (!pinentry.ttyalert) return gpg_error_from_syserror (); } else if (!strcmp (key, "lc-ctype")) { if (pinentry.lc_ctype) free (pinentry.lc_ctype); pinentry.lc_ctype = strdup (value); if (!pinentry.lc_ctype) return gpg_error_from_syserror (); } else if (!strcmp (key, "lc-messages")) { if (pinentry.lc_messages) free (pinentry.lc_messages); pinentry.lc_messages = strdup (value); if (!pinentry.lc_messages) return gpg_error_from_syserror (); } else if (!strcmp (key, "owner")) { long along; char *endp; free (pinentry.owner_host); pinentry.owner_host = NULL; pinentry.owner_uid = -1; pinentry.owner_pid = 0; errno = 0; along = strtol (value, &endp, 10); if (along && !errno) { pinentry.owner_pid = (unsigned long)along; if (*endp) { errno = 0; if (*endp == '/') { /* we have a uid */ endp++; along = strtol (endp, &endp, 10); if (along >= 0 && !errno) pinentry.owner_uid = (int)along; } if (endp) { while (*endp == ' ') endp++; if (*endp) { pinentry.owner_host = strdup (endp); for (endp=pinentry.owner_host; *endp && *endp != ' '; endp++) ; *endp = 0; } } } } } else if (!strcmp (key, "parent-wid")) { pinentry.parent_wid = atoi (value); /* FIXME: Use strtol and add some error handling. */ } else if (!strcmp (key, "touch-file")) { if (pinentry.touch_file) free (pinentry.touch_file); pinentry.touch_file = strdup (value); if (!pinentry.touch_file) return gpg_error_from_syserror (); } else if (!strcmp (key, "default-ok")) { pinentry.default_ok = strdup (value); if (!pinentry.default_ok) return gpg_error_from_syserror (); } else if (!strcmp (key, "default-cancel")) { pinentry.default_cancel = strdup (value); if (!pinentry.default_cancel) return gpg_error_from_syserror (); } else if (!strcmp (key, "default-prompt")) { pinentry.default_prompt = strdup (value); if (!pinentry.default_prompt) return gpg_error_from_syserror (); } else if (!strcmp (key, "default-pwmngr")) { pinentry.default_pwmngr = strdup (value); if (!pinentry.default_pwmngr) return gpg_error_from_syserror (); } else if (!strcmp (key, "default-cf-visi")) { pinentry.default_cf_visi = strdup (value); if (!pinentry.default_cf_visi) return gpg_error_from_syserror (); } else if (!strcmp (key, "default-tt-visi")) { pinentry.default_tt_visi = strdup (value); if (!pinentry.default_tt_visi) return gpg_error_from_syserror (); } else if (!strcmp (key, "default-tt-hide")) { pinentry.default_tt_hide = strdup (value); if (!pinentry.default_tt_hide) return gpg_error_from_syserror (); } else if (!strcmp (key, "allow-external-password-cache") && !*value) { pinentry.allow_external_password_cache = 1; pinentry.tried_password_cache = 0; } else if (!strcmp (key, "allow-emacs-prompt") && !*value) { #ifdef INSIDE_EMACS pinentry_enable_emacs_cmd_handler (); #endif } else if (!strcmp (key, "invisible-char")) { if (pinentry.invisible_char) free (pinentry.invisible_char); pinentry.invisible_char = strdup (value); if (!pinentry.invisible_char) return gpg_error_from_syserror (); } else return gpg_error (GPG_ERR_UNKNOWN_OPTION); return 0; } /* Note, that it is sufficient to allocate the target string D as long as the source string S, i.e.: strlen(s)+1; */ static void strcpy_escaped (char *d, const char *s) { while (*s) { if (*s == '%' && s[1] && s[2]) { s++; *d++ = xtoi_2 ( s); s += 2; } else *d++ = *s++; } *d = 0; } static void write_status_error (assuan_context_t ctx, pinentry_t pe) { char buf[500]; const char *pgm; pgm = strchr (this_pgmname, '-'); if (pgm && pgm[1]) pgm++; else pgm = this_pgmname; snprintf (buf, sizeof buf, "%s.%s %d %s", pgm, pe->specific_err_loc? pe->specific_err_loc : "?", pe->specific_err, pe->specific_err_info? pe->specific_err_info : ""); buf[sizeof buf -1] = 0; assuan_write_status (ctx, "ERROR", buf); } static gpg_error_t cmd_setdesc (assuan_context_t ctx, char *line) { char *newd; (void)ctx; newd = malloc (strlen (line) + 1); if (!newd) return gpg_error_from_syserror (); strcpy_escaped (newd, line); if (pinentry.description) free (pinentry.description); pinentry.description = newd; return 0; } static gpg_error_t cmd_setprompt (assuan_context_t ctx, char *line) { char *newp; (void)ctx; newp = malloc (strlen (line) + 1); if (!newp) return gpg_error_from_syserror (); strcpy_escaped (newp, line); if (pinentry.prompt) free (pinentry.prompt); pinentry.prompt = newp; return 0; } /* The data provided at LINE may be used by pinentry implementations to identify a key for caching strategies of its own. The empty string and --clear mean that the key does not have a stable identifier. */ static gpg_error_t cmd_setkeyinfo (assuan_context_t ctx, char *line) { (void)ctx; if (pinentry.keyinfo) free (pinentry.keyinfo); if (*line && strcmp(line, "--clear") != 0) pinentry.keyinfo = strdup (line); else pinentry.keyinfo = NULL; return 0; } static gpg_error_t cmd_setrepeat (assuan_context_t ctx, char *line) { char *p; (void)ctx; p = malloc (strlen (line) + 1); if (!p) return gpg_error_from_syserror (); strcpy_escaped (p, line); free (pinentry.repeat_passphrase); pinentry.repeat_passphrase = p; return 0; } static gpg_error_t cmd_setrepeaterror (assuan_context_t ctx, char *line) { char *p; (void)ctx; p = malloc (strlen (line) + 1); if (!p) return gpg_error_from_syserror (); strcpy_escaped (p, line); free (pinentry.repeat_error_string); pinentry.repeat_error_string = p; return 0; } static gpg_error_t cmd_seterror (assuan_context_t ctx, char *line) { char *newe; (void)ctx; newe = malloc (strlen (line) + 1); if (!newe) return gpg_error_from_syserror (); strcpy_escaped (newe, line); if (pinentry.error) free (pinentry.error); pinentry.error = newe; return 0; } static gpg_error_t cmd_setok (assuan_context_t ctx, char *line) { char *newo; (void)ctx; newo = malloc (strlen (line) + 1); if (!newo) return gpg_error_from_syserror (); strcpy_escaped (newo, line); if (pinentry.ok) free (pinentry.ok); pinentry.ok = newo; return 0; } static gpg_error_t cmd_setnotok (assuan_context_t ctx, char *line) { char *newo; (void)ctx; newo = malloc (strlen (line) + 1); if (!newo) return gpg_error_from_syserror (); strcpy_escaped (newo, line); if (pinentry.notok) free (pinentry.notok); pinentry.notok = newo; return 0; } static gpg_error_t cmd_setcancel (assuan_context_t ctx, char *line) { char *newc; (void)ctx; newc = malloc (strlen (line) + 1); if (!newc) return gpg_error_from_syserror (); strcpy_escaped (newc, line); if (pinentry.cancel) free (pinentry.cancel); pinentry.cancel = newc; return 0; } static gpg_error_t cmd_settimeout (assuan_context_t ctx, char *line) { (void)ctx; if (line && *line) pinentry.timeout = atoi (line); return 0; } static gpg_error_t cmd_settitle (assuan_context_t ctx, char *line) { char *newt; (void)ctx; newt = malloc (strlen (line) + 1); if (!newt) return gpg_error_from_syserror (); strcpy_escaped (newt, line); if (pinentry.title) free (pinentry.title); pinentry.title = newt; return 0; } static gpg_error_t cmd_setqualitybar (assuan_context_t ctx, char *line) { char *newval; (void)ctx; if (!*line) line = "Quality:"; newval = malloc (strlen (line) + 1); if (!newval) return gpg_error_from_syserror (); strcpy_escaped (newval, line); if (pinentry.quality_bar) free (pinentry.quality_bar); pinentry.quality_bar = newval; return 0; } /* Set the tooltip to be used for a quality bar. */ static gpg_error_t cmd_setqualitybar_tt (assuan_context_t ctx, char *line) { char *newval; (void)ctx; if (*line) { newval = malloc (strlen (line) + 1); if (!newval) return gpg_error_from_syserror (); strcpy_escaped (newval, line); } else newval = NULL; if (pinentry.quality_bar_tt) free (pinentry.quality_bar_tt); pinentry.quality_bar_tt = newval; return 0; } static gpg_error_t cmd_getpin (assuan_context_t ctx, char *line) { int result; int set_prompt = 0; int just_read_password_from_cache = 0; (void)line; pinentry_setbuffer_init (&pinentry); if (!pinentry.pin) return gpg_error (GPG_ERR_ENOMEM); /* Try reading from the password cache. */ if (/* If repeat passphrase is set, then we don't want to read from the cache. */ ! pinentry.repeat_passphrase /* Are we allowed to read from the cache? */ && pinentry.allow_external_password_cache && pinentry.keyinfo /* Only read from the cache if we haven't already tried it. */ && ! pinentry.tried_password_cache /* If the last read resulted in an error, then don't read from the cache. */ && ! pinentry.error) { char *password; int give_up_on_password_store = 0; pinentry.tried_password_cache = 1; password = password_cache_lookup (pinentry.keyinfo, &give_up_on_password_store); if (give_up_on_password_store) pinentry.allow_external_password_cache = 0; if (password) /* There is a cached password. Try it. */ { int len = strlen(password) + 1; if (len > pinentry.pin_len) len = pinentry.pin_len; memcpy (pinentry.pin, password, len); pinentry.pin[len] = '\0'; secmem_free (password); pinentry.pin_from_cache = 1; assuan_write_status (ctx, "PASSWORD_FROM_CACHE", ""); /* Result is the length of the password not including the NUL terminator. */ result = len - 1; just_read_password_from_cache = 1; goto out; } } /* The password was not cached (or we are not allowed to / cannot use the cache). Prompt the user. */ pinentry.pin_from_cache = 0; if (!pinentry.prompt) { pinentry.prompt = pinentry.default_prompt?pinentry.default_prompt:"PIN:"; set_prompt = 1; } pinentry.locale_err = 0; pinentry.specific_err = 0; pinentry.specific_err_loc = NULL; free (pinentry.specific_err_info); pinentry.specific_err_info = NULL; pinentry.close_button = 0; pinentry.repeat_okay = 0; pinentry.one_button = 0; pinentry.ctx_assuan = ctx; result = (*pinentry_cmd_handler) (&pinentry); pinentry.ctx_assuan = NULL; if (pinentry.error) { free (pinentry.error); pinentry.error = NULL; } if (pinentry.repeat_passphrase) { free (pinentry.repeat_passphrase); pinentry.repeat_passphrase = NULL; } if (set_prompt) pinentry.prompt = NULL; pinentry.quality_bar = 0; /* Reset it after the command. */ if (pinentry.close_button) assuan_write_status (ctx, "BUTTON_INFO", "close"); if (result < 0) { pinentry_setbuffer_clear (&pinentry); if (pinentry.specific_err) { write_status_error (ctx, &pinentry); return pinentry.specific_err; } return (pinentry.locale_err ? gpg_error (GPG_ERR_LOCALE_PROBLEM) : gpg_error (GPG_ERR_CANCELED)); } out: if (result) { if (pinentry.repeat_okay) assuan_write_status (ctx, "PIN_REPEATED", ""); result = assuan_send_data (ctx, pinentry.pin, strlen(pinentry.pin)); if (!result) result = assuan_send_data (ctx, NULL, 0); if (/* GPG Agent says it's okay. */ pinentry.allow_external_password_cache && pinentry.keyinfo /* We didn't just read it from the cache. */ && ! just_read_password_from_cache /* And the user said it's okay. */ && pinentry.may_cache_password) /* Cache the password. */ password_cache_save (pinentry.keyinfo, pinentry.pin); } pinentry_setbuffer_clear (&pinentry); return result; } /* Note that the option --one-button is a hack to allow the use of old pinentries while the caller is ignoring the result. Given that options have never been used or flagged as an error the new option is an easy way to enable the messsage mode while not requiring to update pinentry or to have the caller test for the message command. New applications which are free to require an updated pinentry should use MESSAGE instead. */ static gpg_error_t cmd_confirm (assuan_context_t ctx, char *line) { int result; pinentry.one_button = !!strstr (line, "--one-button"); pinentry.quality_bar = 0; pinentry.close_button = 0; pinentry.locale_err = 0; pinentry.specific_err = 0; pinentry.specific_err_loc = NULL; free (pinentry.specific_err_info); pinentry.specific_err_info = NULL; pinentry.canceled = 0; pinentry_setbuffer_clear (&pinentry); result = (*pinentry_cmd_handler) (&pinentry); if (pinentry.error) { free (pinentry.error); pinentry.error = NULL; } if (pinentry.close_button) assuan_write_status (ctx, "BUTTON_INFO", "close"); if (result > 0) return 0; /* OK */ if (pinentry.specific_err) { write_status_error (ctx, &pinentry); return pinentry.specific_err; } if (pinentry.locale_err) return gpg_error (GPG_ERR_LOCALE_PROBLEM); if (pinentry.one_button) return 0; /* OK */ if (pinentry.canceled) return gpg_error (GPG_ERR_CANCELED); return gpg_error (GPG_ERR_NOT_CONFIRMED); } static gpg_error_t cmd_message (assuan_context_t ctx, char *line) { (void)line; return cmd_confirm (ctx, "--one-button"); } /* GETINFO Multipurpose function to return a variety of information. Supported values for WHAT are: version - Return the version of the program. pid - Return the process id of the server. flavor - Return information about the used pinentry flavor ttyinfo - Return DISPLAY and ttyinfo. */ static gpg_error_t cmd_getinfo (assuan_context_t ctx, char *line) { int rc; const char *s; char buffer[100]; if (!strcmp (line, "version")) { s = VERSION; rc = assuan_send_data (ctx, s, strlen (s)); } else if (!strcmp (line, "pid")) { snprintf (buffer, sizeof buffer, "%lu", (unsigned long)getpid ()); buffer[sizeof buffer -1] = 0; rc = assuan_send_data (ctx, buffer, strlen (buffer)); } else if (!strcmp (line, "flavor")) { if (!strncmp (this_pgmname, "pinentry-", 9) && this_pgmname[9]) s = this_pgmname + 9; else s = this_pgmname; snprintf (buffer, sizeof buffer, "%s%s%s", s, flavor_flag? ":":"", flavor_flag? flavor_flag : ""); buffer[sizeof buffer -1] = 0; rc = assuan_send_data (ctx, buffer, strlen (buffer)); /* if (!rc) */ /* rc = assuan_write_status (ctx, "FEATURES", "tabbing foo bar"); */ } else if (!strcmp (line, "ttyinfo")) { snprintf (buffer, sizeof buffer, "%s %s %s", pinentry.ttyname? pinentry.ttyname : "-", pinentry.ttytype? pinentry.ttytype : "-", pinentry.display? pinentry.display : "-" ); buffer[sizeof buffer -1] = 0; rc = assuan_send_data (ctx, buffer, strlen (buffer)); } else rc = gpg_error (GPG_ERR_ASS_PARAMETER); return rc; } /* CLEARPASSPHRASE Clear the cache passphrase associated with the key identified by cacheid. */ static gpg_error_t cmd_clear_passphrase (assuan_context_t ctx, char *line) { (void)ctx; if (! line) return gpg_error (GPG_ERR_ASS_INV_VALUE); /* Remove leading and trailing white space. */ while (*line == ' ') line ++; while (line[strlen (line) - 1] == ' ') line[strlen (line) - 1] = 0; switch (password_cache_clear (line)) { case 1: return 0; case 0: return gpg_error (GPG_ERR_ASS_INV_VALUE); default: return gpg_error (GPG_ERR_ASS_GENERAL); } } /* Tell the assuan library about our commands. */ static gpg_error_t register_commands (assuan_context_t ctx) { static struct { const char *name; gpg_error_t (*handler) (assuan_context_t, char *line); } table[] = { { "SETDESC", cmd_setdesc }, { "SETPROMPT", cmd_setprompt }, { "SETKEYINFO", cmd_setkeyinfo }, { "SETREPEAT", cmd_setrepeat }, { "SETREPEATERROR", cmd_setrepeaterror }, { "SETERROR", cmd_seterror }, { "SETOK", cmd_setok }, { "SETNOTOK", cmd_setnotok }, { "SETCANCEL", cmd_setcancel }, { "GETPIN", cmd_getpin }, { "CONFIRM", cmd_confirm }, { "MESSAGE", cmd_message }, { "SETQUALITYBAR", cmd_setqualitybar }, { "SETQUALITYBAR_TT", cmd_setqualitybar_tt }, { "GETINFO", cmd_getinfo }, { "SETTITLE", cmd_settitle }, { "SETTIMEOUT", cmd_settimeout }, { "CLEARPASSPHRASE", cmd_clear_passphrase }, { NULL } }; int i, j; gpg_error_t rc; for (i = j = 0; table[i].name; i++) { rc = assuan_register_command (ctx, table[i].name, table[i].handler, NULL); if (rc) return rc; } return 0; } int pinentry_loop2 (int infd, int outfd) { gpg_error_t rc; assuan_fd_t filedes[2]; assuan_context_t ctx; /* Extra check to make sure we have dropped privs. */ #ifndef HAVE_DOSISH_SYSTEM if (getuid() != geteuid()) abort (); #endif rc = assuan_new (&ctx); if (rc) { fprintf (stderr, "server context creation failed: %s\n", gpg_strerror (rc)); return -1; } /* For now we use a simple pipe based server so that we can work from scripts. We will later add options to run as a daemon and wait for requests on a Unix domain socket. */ filedes[0] = assuan_fdopen (infd); filedes[1] = assuan_fdopen (outfd); rc = assuan_init_pipe_server (ctx, filedes); if (rc) { fprintf (stderr, "%s: failed to initialize the server: %s\n", this_pgmname, gpg_strerror (rc)); return -1; } rc = register_commands (ctx); if (rc) { fprintf (stderr, "%s: failed to the register commands with Assuan: %s\n", this_pgmname, gpg_strerror (rc)); return -1; } assuan_register_option_handler (ctx, option_handler); #if 0 assuan_set_log_stream (ctx, stderr); #endif assuan_register_reset_notify (ctx, pinentry_assuan_reset_handler); for (;;) { rc = assuan_accept (ctx); if (rc == -1) break; else if (rc) { fprintf (stderr, "%s: Assuan accept problem: %s\n", this_pgmname, gpg_strerror (rc)); break; } rc = assuan_process (ctx); if (rc) { fprintf (stderr, "%s: Assuan processing failed: %s\n", this_pgmname, gpg_strerror (rc)); continue; } } assuan_release (ctx); return 0; } /* Start the pinentry event loop. The program will start to process Assuan commands until it is finished or an error occurs. If an error occurs, -1 is returned. Otherwise, 0 is returned. */ int pinentry_loop (void) { return pinentry_loop2 (STDIN_FILENO, STDOUT_FILENO); } diff --git a/pinentry/pinentry.h b/pinentry/pinentry.h index 128331c..009e884 100644 --- a/pinentry/pinentry.h +++ b/pinentry/pinentry.h @@ -1,320 +1,321 @@ /* pinentry.h - The interface for the PIN entry support library. - Copyright (C) 2002, 2003, 2010, 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 . + * Copyright (C) 2002, 2003, 2010, 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+ */ #ifndef PINENTRY_H #define PINENTRY_H #ifdef __cplusplus extern "C" { #if 0 } #endif #endif typedef enum { PINENTRY_COLOR_NONE, PINENTRY_COLOR_DEFAULT, PINENTRY_COLOR_BLACK, PINENTRY_COLOR_RED, PINENTRY_COLOR_GREEN, PINENTRY_COLOR_YELLOW, PINENTRY_COLOR_BLUE, PINENTRY_COLOR_MAGENTA, PINENTRY_COLOR_CYAN, PINENTRY_COLOR_WHITE } pinentry_color_t; struct pinentry { /* The window title, or NULL. (Assuan: "SETTITLE TITLE".) */ char *title; /* The description to display, or NULL. (Assuan: "SETDESC DESC".) */ char *description; /* The error message to display, or NULL. (Assuan: "SETERROR MESSAGE".) */ char *error; /* The prompt to display, or NULL. (Assuan: "SETPROMPT prompt".) */ char *prompt; /* The OK button text to display, or NULL. (Assuan: "SETOK OK".) */ char *ok; /* The Not-OK button text to display, or NULL. This is the text for the alternative option shown by the third button. (Assuan: "SETNOTOK NOTOK".) */ char *notok; /* The Cancel button text to display, or NULL. (Assuan: "SETCANCEL CANCEL".) */ char *cancel; /* The buffer to store the secret into. */ char *pin; /* The length of the buffer. */ int pin_len; /* Whether the pin was read from an external cache (1) or entered by the user (0). */ int pin_from_cache; /* The name of the X display to use if X is available and supported. (Assuan: "OPTION display DISPLAY".) */ char *display; /* The name of the terminal node to open if X not available or supported. (Assuan: "OPTION ttyname TTYNAME".) */ char *ttyname; /* The type of the terminal. (Assuan: "OPTION ttytype TTYTYPE".) */ char *ttytype; /* Set the alert mode (none, beep or flash). */ char *ttyalert; /* The LC_CTYPE value for the terminal. (Assuan: "OPTION lc-ctype LC_CTYPE".) */ char *lc_ctype; /* The LC_MESSAGES value for the terminal. (Assuan: "OPTION lc-messages LC_MESSAGES".) */ char *lc_messages; /* True if debug mode is requested. */ int debug; /* The number of seconds before giving up while waiting for user input. */ int timeout; /* True if caller should grab the keyboard. (Assuan: "OPTION grab" or "OPTION no-grab".) */ int grab; /* The PID of the owner or 0 if not known. The owner is the process * which actually triggered the the pinentry. For example gpg. */ unsigned long owner_pid; /* The numeric uid (user ID) of the owner process or -1 if not * known. */ int owner_uid; /* The malloced hostname of the owner or NULL. */ char *owner_host; /* The window ID of the parent window over which the pinentry window should be displayed. (Assuan: "OPTION parent-wid WID".) */ int parent_wid; /* The name of an optional file which will be touched after a curses entry has been displayed. (Assuan: "OPTION touch-file FILENAME".) */ char *touch_file; /* The frontend should set this to -1 if the user canceled the request, and to the length of the PIN stored in pin otherwise. */ int result; /* The frontend should set this if the NOTOK button was pressed. */ int canceled; /* The frontend should set this to true if an error with the local conversion occurred. */ int locale_err; /* The frontend should set this to a gpg-error so that commands are able to return specific error codes. This is an ugly hack due to the fact that pinentry_cmd_handler_t returns the length of the passphrase or a negative error code. */ int specific_err; /* The frontend may store a string with the error location here. */ const char *specific_err_loc; /* The frontend may store a malloced string here to emit an ERROR * status code with this extra info along with SPECIFIC_ERR. */ char *specific_err_info; /* The frontend should set this to true if the window close button has been used. This flag is used in addition to a regular return value. */ int close_button; /* The caller should set this to true if only one button is required. This is useful for notification dialogs where only a dismiss button is required. */ int one_button; /* If true a second prompt for the passphrase is shown and the user is expected to enter the same passphrase again. Pinentry checks that both match. (Assuan: "SETREPEAT".) */ char *repeat_passphrase; /* The string to show if a repeated passphrase does not match. (Assuan: "SETREPEATERROR ERROR".) */ char *repeat_error_string; /* Set to true if the passphrase has been entered a second time and matches the first passphrase. */ int repeat_okay; /* If this is not NULL, a passphrase quality indicator is shown. There will also be an inquiry back to the caller to get an indication of the quality for the passphrase entered so far. The string is used as a label for the quality bar. (Assuan: "SETQUALITYBAR LABEL".) */ char *quality_bar; /* The tooltip to be show for the qualitybar. Malloced or NULL. (Assuan: "SETQUALITYBAR_TT TOOLTIP".) */ char *quality_bar_tt; /* For the curses pinentry, the color of error messages. */ pinentry_color_t color_fg; int color_fg_bright; pinentry_color_t color_bg; pinentry_color_t color_so; int color_so_bright; /* Malloced and i18ned default strings or NULL. These strings may include an underscore character to indicate an accelerator key. A double underscore represents a plain one. */ /* (Assuan: "OPTION default-ok OK"). */ char *default_ok; /* (Assuan: "OPTION default-cancel CANCEL"). */ char *default_cancel; /* (Assuan: "OPTION default-prompt PROMPT"). */ char *default_prompt; /* (Assuan: "OPTION default-pwmngr SAVE_PASSWORD_WITH_PASSWORD_MANAGER?"). */ char *default_pwmngr; /* (Assuan: "OPTION default-cf-visi Do you really want to make your passphrase visible?"). */ char *default_cf_visi; /* (Assuan: "OPTION default-tt-visi Make passphrase visible?"). */ char *default_tt_visi; /* (Assuan: "OPTION default-tt-hide Hide passphrase"). */ char *default_tt_hide; /* Whether we are allowed to read the password from an external cache. (Assuan: "OPTION allow-external-password-cache") */ int allow_external_password_cache; /* We only try the cache once. */ int tried_password_cache; /* A stable identifier for the key. (Assuan: "SETKEYINFO KEYINFO".) */ char *keyinfo; /* Whether we may cache the password (according to the user). */ int may_cache_password; /* NOTE: If you add any additional fields to this structure, be sure to update the initializer in pinentry/pinentry.c!!! */ /* For the quality indicator we need to do an inquiry. Thus we need to save the assuan ctx. */ void *ctx_assuan; /* An UTF-8 string with an invisible character used to override the default in some pinentries. Only the first character is used. */ char *invisible_char; }; typedef struct pinentry *pinentry_t; /* The pinentry command handler type processes the pinentry request PIN. If PIN->pin is zero, request a confirmation, otherwise a PIN entry. On confirmation, the function should return TRUE if confirmed, and FALSE otherwise. On PIN entry, the function should return -1 if an error occurred or the user cancelled the operation and 1 otherwise. */ typedef int (*pinentry_cmd_handler_t) (pinentry_t pin); /* Start the pinentry event loop. The program will start to process Assuan commands until it is finished or an error occurs. If an error occurs, -1 is returned and errno indicates the type of an error. Otherwise, 0 is returned. */ int pinentry_loop (void); /* The same as above but allows to specify the i/o descriptors. * infd and outfd will be duplicated in this function so the caller * still has to close them if necessary. */ int pinentry_loop2 (int infd, int outfd); /* Convert the UTF-8 encoded string TEXT to the encoding given in LC_CTYPE. Return NULL on error. */ char *pinentry_utf8_to_local (const char *lc_ctype, const char *text); /* Convert TEXT which is encoded according to LC_CTYPE to UTF-8. With SECURE set to true, use secure memory for the returned buffer. Return NULL on error. */ char *pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure); char *pinentry_get_title (pinentry_t pe); /* Run a quality inquiry for PASSPHRASE of LENGTH. */ int pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length); /* Try to make room for at least LEN bytes for the pin in the pinentry PIN. Returns new buffer on success and 0 on failure. */ char *pinentry_setbufferlen (pinentry_t pin, int len); /* Use the buffer at BUFFER for PIN->PIN. BUFFER must be NULL or allocated using secmem_alloc. LEN is the size of the buffer. If it is unknown, but BUFFER is a NUL terminated string, you pass 0 to just use strlen(buffer)+1. */ void pinentry_setbuffer_use (pinentry_t pin, char *buffer, int len); /* Initialize the secure memory subsystem, drop privileges and return. Must be called early. */ void pinentry_init (const char *pgmname); /* Return true if either DISPLAY is set or ARGV contains the string "--display". */ int pinentry_have_display (int argc, char **argv); /* Parse the command line options. May exit the program if only help or version output is requested. */ void pinentry_parse_opts (int argc, char *argv[]); /* Set the optional flag used with getinfo. */ void pinentry_set_flavor_flag (const char *string); /* The caller must define this variable to process assuan commands. */ extern pinentry_cmd_handler_t pinentry_cmd_handler; #ifdef HAVE_W32_SYSTEM /* Windows declares sleep as obsolete, but provides a definition for _sleep but non for the still existing sleep. */ #define sleep(a) _sleep ((a)) #endif /*HAVE_W32_SYSTEM*/ #if 0 { #endif #ifdef __cplusplus } #endif #endif /* PINENTRY_H */ diff --git a/qt/Makefile.am b/qt/Makefile.am index 6659007..698005e 100644 --- a/qt/Makefile.am +++ b/qt/Makefile.am @@ -1,58 +1,58 @@ # Makefile.am # Copyright (C) 2002 g10 Code GmbH, Klarälvdalens Datakonsult AB # Copyright (C) 2008, 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +# along with this program; if not, see . +# SPDX-License-Identifier: GPL-2.0+ ## Process this file with automake to produce Makefile.in bin_PROGRAMS = pinentry-qt EXTRA_DIST = document-encrypt.png pinentry.qrc if FALLBACK_CURSES ncurses_include = $(NCURSES_INCLUDE) libcurses = ../pinentry/libpinentry-curses.a $(LIBCURSES) $(LIBICONV) else ncurses_include = libcurses = endif AM_CPPFLAGS = $(COMMON_CFLAGS) \ -I$(top_srcdir) -I$(top_srcdir)/secmem \ $(ncurses_include) -I$(top_srcdir)/pinentry AM_CXXFLAGS = $(PINENTRY_QT_CFLAGS) pinentry_qt_LDADD = \ ../pinentry/libpinentry.a $(top_builddir)/secmem/libsecmem.a \ $(COMMON_LIBS) $(PINENTRY_QT_LIBS) $(libcurses) $(LIBCAP) BUILT_SOURCES = \ pinentryconfirm.moc pinentrydialog.moc CLEANFILES = \ pinentryconfirm.moc pinentrydialog.moc pinentry_qt_SOURCES = pinentrydialog.h pinentrydialog.cpp \ main.cpp qrc_pinentry.cpp pinentryconfirm.cpp pinentryconfirm.h nodist_pinentry_qt_SOURCES = \ pinentryconfirm.moc pinentrydialog.moc .h.moc: $(MOC) `test -f '$<' || echo '$(srcdir)/'`$< -o $@ diff --git a/qt/main.cpp b/qt/main.cpp index 7d1a264..70a009d 100644 --- a/qt/main.cpp +++ b/qt/main.cpp @@ -1,365 +1,364 @@ /* main.cpp - A Qt dialog for PIN entry. - - Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB) - Copyright (C) 2003 g10 Code GmbH - Copyright 2007 Ingo Klöcker - - Written by Steffen Hansen . - Modified by Marcus Brinkmann . - Modified by Marc Mutz - - This program 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. - - This program 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB) + * Copyright (C) 2003 g10 Code GmbH + * Copyright 2007 Ingo Klöcker + * + * Written by Steffen Hansen . + * Modified by Marcus Brinkmann . + * Modified by Marc Mutz + * + * This program 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. + * + * This program 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+ + */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pinentryconfirm.h" #include "pinentrydialog.h" #include "pinentry.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef FALLBACK_CURSES #include #endif #if QT_VERSION >= 0x050000 && defined(QT_STATIC) #include #ifdef Q_OS_WIN #include #include Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) #elif defined(Q_OS_MAC) Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin) #else Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) #endif #endif static QString escape_accel(const QString &s) { QString result; result.reserve(s.size()); bool afterUnderscore = false; for (unsigned int i = 0, end = s.size() ; i != end ; ++i) { const QChar ch = s[i]; if (ch == QLatin1Char('_')) { if (afterUnderscore) { // escaped _ result += QLatin1Char('_'); afterUnderscore = false; } else { // accel afterUnderscore = true; } } else { if (afterUnderscore || // accel ch == QLatin1Char('&')) { // escape & from being interpreted by Qt result += QLatin1Char('&'); } result += ch; afterUnderscore = false; } } if (afterUnderscore) // trailing single underscore: shouldn't happen, but deal with it robustly: { result += QLatin1Char('_'); } return result; } /* Hack for creating a QWidget with a "foreign" window ID */ class ForeignWidget : public QWidget { public: explicit ForeignWidget(WId wid) : QWidget(0) { QWidget::destroy(); create(wid, false, false); } ~ForeignWidget() { destroy(false, false); } }; namespace { class InvalidUtf8 : public std::invalid_argument { public: InvalidUtf8() : std::invalid_argument("invalid utf8") {} ~InvalidUtf8() throw() {} }; } static const bool GPG_AGENT_IS_PORTED_TO_ONLY_SEND_UTF8 = false; static QString from_utf8(const char *s) { const QString result = QString::fromUtf8(s); if (result.contains(QChar::ReplacementCharacter)) { if (GPG_AGENT_IS_PORTED_TO_ONLY_SEND_UTF8) { throw InvalidUtf8(); } else { return QString::fromLocal8Bit(s); } } return result; } static int qt_cmd_handler(pinentry_t pe) { QWidget *parent = 0; char *str; /* FIXME: Add parent window ID to pinentry and GTK. */ if (pe->parent_wid) { parent = new ForeignWidget((WId) pe->parent_wid); } int want_pass = !!pe->pin; const QString ok = pe->ok ? escape_accel(from_utf8(pe->ok)) : pe->default_ok ? escape_accel(from_utf8(pe->default_ok)) : /* else */ QLatin1String("&OK") ; const QString cancel = pe->cancel ? escape_accel(from_utf8(pe->cancel)) : pe->default_cancel ? escape_accel(from_utf8(pe->default_cancel)) : /* else */ QLatin1String("&Cancel") ; str = pinentry_get_title (pe); const QString title = str ? from_utf8(str) : /* else */ QLatin1String("pinentry-qt") ; free (str); const QString repeatError = pe->repeat_error_string ? from_utf8(pe->repeat_error_string) : QLatin1String("Passphrases do not match"); const QString repeatString = pe->repeat_passphrase ? from_utf8(pe->repeat_passphrase) : QString(); const QString visibilityTT = pe->default_tt_visi ? from_utf8(pe->default_tt_visi) : QLatin1String("Show passphrase"); const QString hideTT = pe->default_tt_hide ? from_utf8(pe->default_tt_hide) : QLatin1String("Hide passphrase"); if (want_pass) { char *str; PinEntryDialog pinentry(parent, 0, pe->timeout, true, !!pe->quality_bar, repeatString, visibilityTT, hideTT); pinentry.setPinentryInfo(pe); pinentry.setPrompt(escape_accel(from_utf8(pe->prompt))); pinentry.setDescription(from_utf8(pe->description)); pinentry.setRepeatErrorText(repeatError); str = pinentry_get_title (pe); if (str) { pinentry.setWindowTitle(from_utf8(str)); free (str); } /* If we reuse the same dialog window. */ pinentry.setPin(QString()); pinentry.setOkText(ok); pinentry.setCancelText(cancel); if (pe->error) { pinentry.setError(from_utf8(pe->error)); } if (pe->quality_bar) { pinentry.setQualityBar(from_utf8(pe->quality_bar)); } if (pe->quality_bar_tt) { pinentry.setQualityBarTT(from_utf8(pe->quality_bar_tt)); } bool ret = pinentry.exec(); if (!ret) { if (pinentry.timedOut()) pe->specific_err = gpg_error (GPG_ERR_TIMEOUT); return -1; } const QString pinStr = pinentry.pin(); QByteArray pin = pinStr.toUtf8(); if (!!pe->repeat_passphrase) { /* Should not have been possible to accept the dialog in that case but we do a safety check here */ pe->repeat_okay = (pinStr == pinentry.repeatedPin()); } int len = strlen(pin.constData()); if (len >= 0) { pinentry_setbufferlen(pe, len + 1); if (pe->pin) { strcpy(pe->pin, pin.constData()); return len; } } return -1; } else { const QString desc = pe->description ? from_utf8(pe->description) : QString(); const QString notok = pe->notok ? escape_accel(from_utf8(pe->notok)) : QString(); const QMessageBox::StandardButtons buttons = pe->one_button ? QMessageBox::Ok : pe->notok ? QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel : /* else */ QMessageBox::Ok | QMessageBox::Cancel ; PinentryConfirm box(QMessageBox::Information, pe->timeout, title, desc, buttons, parent); const struct { QMessageBox::StandardButton button; QString label; } buttonLabels[] = { { QMessageBox::Ok, ok }, { QMessageBox::Yes, ok }, { QMessageBox::No, notok }, { QMessageBox::Cancel, cancel }, }; for (size_t i = 0 ; i < sizeof buttonLabels / sizeof * buttonLabels ; ++i) if ((buttons & buttonLabels[i].button) && !buttonLabels[i].label.isEmpty()) { box.button(buttonLabels[i].button)->setText(buttonLabels[i].label); #ifndef QT_NO_ACCESSIBILITY box.button(buttonLabels[i].button)->setAccessibleDescription(buttonLabels[i].label); #endif } box.setIconPixmap(icon()); if (!pe->one_button) { box.setDefaultButton(QMessageBox::Cancel); } box.show(); raiseWindow(&box); const int rc = box.exec(); if (rc == QMessageBox::Cancel) { pe->canceled = true; } if (box.timedOut()) { pe->specific_err = gpg_error (GPG_ERR_TIMEOUT); } return rc == QMessageBox::Ok || rc == QMessageBox::Yes ; } } static int qt_cmd_handler_ex(pinentry_t pe) { try { return qt_cmd_handler(pe); } catch (const InvalidUtf8 &) { pe->locale_err = true; return pe->pin ? -1 : false ; } catch (...) { pe->canceled = true; return pe->pin ? -1 : false ; } } pinentry_cmd_handler_t pinentry_cmd_handler = qt_cmd_handler_ex; int main(int argc, char *argv[]) { pinentry_init("pinentry-qt"); QApplication *app = NULL; #ifdef FALLBACK_CURSES if (!pinentry_have_display(argc, argv)) { pinentry_cmd_handler = curses_cmd_handler; pinentry_set_flavor_flag ("curses"); } else #endif { /* Qt does only understand -display but not --display; thus we are fixing that here. The code is pretty simply and may get confused if an argument is called "--display". */ char **new_argv, *p; size_t n; int i, done; for (n = 0, i = 0; i < argc; i++) { n += strlen(argv[i]) + 1; } n++; new_argv = (char **)calloc(argc + 1, sizeof * new_argv); if (new_argv) { *new_argv = (char *)malloc(n); } if (!new_argv || !*new_argv) { fprintf(stderr, "pinentry-qt: can't fixup argument list: %s\n", strerror(errno)); exit(EXIT_FAILURE); } for (done = 0, p = *new_argv, i = 0; i < argc; i++) if (!done && !strcmp(argv[i], "--display")) { new_argv[i] = strcpy(p, argv[i] + 1); p += strlen(argv[i] + 1) + 1; done = 1; } else { new_argv[i] = strcpy(p, argv[i]); p += strlen(argv[i]) + 1; } i = argc; app = new QApplication(i, new_argv); app->setWindowIcon(QIcon(QLatin1String(":/document-encrypt.png"))); } pinentry_parse_opts(argc, argv); int rc = pinentry_loop(); delete app; return rc ? EXIT_FAILURE : EXIT_SUCCESS ; } diff --git a/qt/pinentryconfirm.cpp b/qt/pinentryconfirm.cpp index 50c19a4..65b191c 100644 --- a/qt/pinentryconfirm.cpp +++ b/qt/pinentryconfirm.cpp @@ -1,76 +1,77 @@ /* pinentryconfirm.cpp - A QMessageBox with a timeout + * + * Copyright (C) 2011 Ben Kibbey + * + * This program 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. + * + * This program 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+ + */ - Copyright (C) 2011 Ben Kibbey - - This program 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. - - This program 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ #include "pinentryconfirm.h" #include "pinentrydialog.h" #include #include #include #include PinentryConfirm::PinentryConfirm(Icon icon, int timeout, const QString &title, const QString &desc, StandardButtons buttons, QWidget *parent) : QMessageBox(icon, title, desc, buttons, parent) { _timed_out = false; if (timeout > 0) { _timer = new QTimer(this); connect(_timer, SIGNAL(timeout()), this, SLOT(slotTimeout())); _timer->start(timeout * 1000); } #ifndef QT_NO_ACCESSIBILITY setAccessibleDescription(desc); setAccessibleName(title); #endif raiseWindow(this); } bool PinentryConfirm::timedOut() const { return _timed_out; } void PinentryConfirm::showEvent(QShowEvent *event) { static bool resized; if (!resized) { QGridLayout* lay = dynamic_cast (layout()); if (lay) { QSize textSize = fontMetrics().size(Qt::TextExpandTabs, text(), fontMetrics().maxWidth()); QSpacerItem* horizontalSpacer = new QSpacerItem(textSize.width() + iconPixmap().width(), 0, QSizePolicy::Minimum, QSizePolicy::Expanding); lay->addItem(horizontalSpacer, lay->rowCount(), 1, 1, lay->columnCount() - 1); } resized = true; } QDialog::showEvent(event); raiseWindow(this); } void PinentryConfirm::slotTimeout() { QAbstractButton *b = button(QMessageBox::Cancel); _timed_out = true; if (b) { b->animateClick(0); } } #include "pinentryconfirm.moc" diff --git a/qt/pinentryconfirm.h b/qt/pinentryconfirm.h index 21b91c5..ec116ac 100644 --- a/qt/pinentryconfirm.h +++ b/qt/pinentryconfirm.h @@ -1,45 +1,46 @@ /* pinentryconfirm.h - A QMessageBox with a timeout + * + * Copyright (C) 2011 Ben Kibbey + * + * This program 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. + * + * This program 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+ + */ - Copyright (C) 2011 Ben Kibbey - - This program 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. - - This program 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ #ifndef PINENTRYCONFIRM_H #define PINENTRYCONFIRM_H #include #include class PinentryConfirm : public QMessageBox { Q_OBJECT public: PinentryConfirm(Icon, int timeout, const QString &title, const QString &desc, StandardButtons buttons, QWidget *parent); bool timedOut() const; private slots: void slotTimeout(); private: QTimer *_timer; bool _timed_out; protected: /* reimp */ void showEvent(QShowEvent *event); }; #endif diff --git a/qt/pinentrydialog.cpp b/qt/pinentrydialog.cpp index 92cf19e..cb52d7c 100644 --- a/qt/pinentrydialog.cpp +++ b/qt/pinentrydialog.cpp @@ -1,477 +1,476 @@ /* pinentrydialog.cpp - A (not yet) secure Qt 4 dialog for PIN entry. - - Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB) - Copyright 2007 Ingo Klöcker - Copyright 2016 Intevation GmbH - - Written by Steffen Hansen . - Modified by Andre Heinecke - - This program 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. - - This program 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB) + * Copyright 2007 Ingo Klöcker + * Copyright 2016 Intevation GmbH + * + * Written by Steffen Hansen . + * Modified by Andre Heinecke + * + * This program 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. + * + * This program 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 "pinentrydialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include #endif /* I [wk] have no idea for what this code was supposed to do. Foregrounding a window is heavily restricted by modern Windows versions. This is the reason why gpg-agent employs its AllowSetForegroundWindow callback machinery to ask the supposed to be be calling process to allow a pinentry to go into the foreground. [ah] This is a Hack to workaround the fact that Foregrounding a Window is so restricted that it AllowSetForegroundWindow does not always work (e.g. when the ForegroundWindow timeout has not expired. */ #ifdef Q_OS_WIN WINBOOL SetForegroundWindowEx(HWND hWnd) { //Attach foreground window thread to our thread const DWORD ForeGroundID = GetWindowThreadProcessId(::GetForegroundWindow(), NULL); const DWORD CurrentID = GetCurrentThreadId(); WINBOOL retval; AttachThreadInput(ForeGroundID, CurrentID, TRUE); //Do our stuff here HWND hLastActivePopupWnd = GetLastActivePopup(hWnd); retval = SetForegroundWindow(hLastActivePopupWnd); //Detach the attached thread AttachThreadInput(ForeGroundID, CurrentID, FALSE); return retval; }// End SetForegroundWindowEx #endif void raiseWindow(QWidget *w) { /* Maybe Qt will become aggressive enough one day that * this is enough on windows too*/ w->raise(); #ifdef Q_OS_WIN HWND wid = (HWND)w->effectiveWinId(); /* In the meantime we do our own attention grabbing */ if (!SetForegroundWindow(wid) && !SetForegroundWindowEx(wid)) { OutputDebugString("SetForegroundWindow (ex) failed"); /* Yet another fallback which will not work on some * versions and is not recommended by msdn */ if (!ShowWindow(wid, SW_SHOWNORMAL)) { OutputDebugString("ShowWindow failed."); } } /* Even if SetForgeoundWindow / SetForegroundWinowEx don't fail * we sometimes are still not in the foreground. So we try yet * another hack by using SetWindowPos */ if (!SetWindowPos(wid, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW)) { OutputDebugString("SetWindowPos failed."); } else { /* Without moving back to NOTOPMOST we just stay on top. * Even if the user changes focus. */ SetWindowPos(wid, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } #endif } QPixmap icon(QStyle::StandardPixmap which) { QPixmap pm = qApp->windowIcon().pixmap(48, 48); if (which != QStyle::SP_CustomBase) { const QIcon ic = qApp->style()->standardIcon(which); QPainter painter(&pm); const int emblemSize = 22; painter.drawPixmap(pm.width() - emblemSize, 0, ic.pixmap(emblemSize, emblemSize)); } return pm; } void PinEntryDialog::slotTimeout() { _timed_out = true; reject(); } PinEntryDialog::PinEntryDialog(QWidget *parent, const char *name, int timeout, bool modal, bool enable_quality_bar, const QString &repeatString, const QString &visibilityTT, const QString &hideTT) : QDialog(parent, Qt::WindowStaysOnTopHint), mRepeat(NULL), _grabbed(false), mVisibilityTT(visibilityTT), mHideTT(hideTT), mVisiActionEdit(NULL), mVisiCB(NULL) { _timed_out = false; setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); if (modal) { setWindowModality(Qt::ApplicationModal); } _icon = new QLabel(this); _icon->setPixmap(icon()); _error = new QLabel(this); QPalette pal; pal.setColor(QPalette::WindowText, Qt::red); _error->setPalette(pal); _error->hide(); _desc = new QLabel(this); _desc->hide(); _prompt = new QLabel(this); _prompt->hide(); _edit = new QLineEdit(this); _edit->setMaxLength(256); _edit->setEchoMode(QLineEdit::Password); _prompt->setBuddy(_edit); if (enable_quality_bar) { _quality_bar_label = new QLabel(this); _quality_bar_label->setAlignment(Qt::AlignVCenter); _quality_bar = new QProgressBar(this); _quality_bar->setAlignment(Qt::AlignCenter); _have_quality_bar = true; } else { _have_quality_bar = false; } QDialogButtonBox *const buttons = new QDialogButtonBox(this); buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); _ok = buttons->button(QDialogButtonBox::Ok); _cancel = buttons->button(QDialogButtonBox::Cancel); _ok->setDefault(true); if (style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons)) { _ok->setIcon(style()->standardIcon(QStyle::SP_DialogOkButton)); _cancel->setIcon(style()->standardIcon(QStyle::SP_DialogCancelButton)); } if (timeout > 0) { _timer = new QTimer(this); connect(_timer, SIGNAL(timeout()), this, SLOT(slotTimeout())); _timer->start(timeout * 1000); } else { _timer = NULL; } connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); connect(_edit, SIGNAL(textChanged(QString)), this, SLOT(updateQuality(QString))); connect(_edit, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString))); _edit->setFocus(); QGridLayout *const grid = new QGridLayout(this); int row = 1; grid->addWidget(_error, row++, 1, 1, 2); grid->addWidget(_desc, row++, 1, 1, 2); //grid->addItem( new QSpacerItem( 0, _edit->height() / 10, QSizePolicy::Minimum, QSizePolicy::Fixed ), 1, 1 ); grid->addWidget(_prompt, row, 1); grid->addWidget(_edit, row++, 2); if (!repeatString.isNull()) { mRepeat = new QLineEdit; mRepeat->setMaxLength(256); mRepeat->setEchoMode(QLineEdit::Password); connect(mRepeat, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString))); QLabel *repeatLabel = new QLabel(repeatString); repeatLabel->setBuddy(mRepeat); grid->addWidget(repeatLabel, row, 1); grid->addWidget(mRepeat, row++, 2); setTabOrder(_edit, mRepeat); setTabOrder(mRepeat, _ok); } if (enable_quality_bar) { grid->addWidget(_quality_bar_label, row, 1); grid->addWidget(_quality_bar, row++, 2); } /* Set up the show password action */ const QIcon visibilityIcon = QIcon::fromTheme(QLatin1String("visibility")); const QIcon hideIcon = QIcon::fromTheme(QLatin1String("hint")); #if QT_VERSION >= 0x050200 if (!visibilityIcon.isNull() && !hideIcon.isNull()) { mVisiActionEdit = _edit->addAction(visibilityIcon, QLineEdit::TrailingPosition); mVisiActionEdit->setVisible(false); mVisiActionEdit->setToolTip(mVisibilityTT); connect(mVisiActionEdit, SIGNAL(triggered()), this, SLOT(toggleVisibility())); } else #endif { if (!mVisibilityTT.isNull()) { mVisiCB = new QCheckBox(mVisibilityTT); connect(mVisiCB, SIGNAL(toggled(bool)), this, SLOT(toggleVisibility())); grid->addWidget(mVisiCB, row++, 1, 1, 2, Qt::AlignLeft); } } grid->addWidget(buttons, ++row, 0, 1, 3); grid->addWidget(_icon, 0, 0, row - 1, 1, Qt::AlignVCenter | Qt::AlignLeft); grid->setSizeConstraint(QLayout::SetFixedSize); connect(qApp, SIGNAL(focusChanged(QWidget *, QWidget *)), this, SLOT(focusChanged(QWidget *, QWidget *))); } void PinEntryDialog::showEvent(QShowEvent *event) { QDialog::showEvent(event); raiseWindow(this); } void PinEntryDialog::setDescription(const QString &txt) { _desc->setVisible(!txt.isEmpty()); _desc->setText(txt); #ifndef QT_NO_ACCESSIBILITY _desc->setAccessibleDescription(txt); #endif _icon->setPixmap(icon()); setError(QString::null); } QString PinEntryDialog::description() const { return _desc->text(); } void PinEntryDialog::setError(const QString &txt) { if (!txt.isNull()) { _icon->setPixmap(icon(QStyle::SP_MessageBoxCritical)); } _error->setText(txt); #ifndef QT_NO_ACCESSIBILITY _error->setAccessibleDescription(txt); #endif _error->setVisible(!txt.isEmpty()); } QString PinEntryDialog::error() const { return _error->text(); } void PinEntryDialog::setPin(const QString &txt) { _edit->setText(txt); } QString PinEntryDialog::pin() const { return _edit->text(); } void PinEntryDialog::setPrompt(const QString &txt) { _prompt->setText(txt); _prompt->setVisible(!txt.isEmpty()); } QString PinEntryDialog::prompt() const { return _prompt->text(); } void PinEntryDialog::setOkText(const QString &txt) { _ok->setText(txt); #ifndef QT_NO_ACCESSIBILITY _ok->setAccessibleDescription(txt); #endif _ok->setVisible(!txt.isEmpty()); } void PinEntryDialog::setCancelText(const QString &txt) { _cancel->setText(txt); #ifndef QT_NO_ACCESSIBILITY _cancel->setAccessibleDescription(txt); #endif _cancel->setVisible(!txt.isEmpty()); } void PinEntryDialog::setQualityBar(const QString &txt) { if (_have_quality_bar) { _quality_bar_label->setText(txt); #ifndef QT_NO_ACCESSIBILITY _quality_bar_label->setAccessibleDescription(txt); #endif } } void PinEntryDialog::setQualityBarTT(const QString &txt) { if (_have_quality_bar) { _quality_bar->setToolTip(txt); } } void PinEntryDialog::updateQuality(const QString &txt) { int length; int percent; QPalette pal; if (_timer) { _timer->stop(); } if (!_have_quality_bar || !_pinentry_info) { return; } const QByteArray utf8_pin = txt.toUtf8(); const char *pin = utf8_pin.constData(); length = strlen(pin); percent = length ? pinentry_inq_quality(_pinentry_info, pin, length) : 0; if (!length) { _quality_bar->reset(); } else { pal = _quality_bar->palette(); if (percent < 0) { pal.setColor(QPalette::Highlight, QColor("red")); percent = -percent; } else { pal.setColor(QPalette::Highlight, QColor("green")); } _quality_bar->setPalette(pal); _quality_bar->setValue(percent); } } void PinEntryDialog::setPinentryInfo(pinentry_t peinfo) { _pinentry_info = peinfo; } void PinEntryDialog::focusChanged(QWidget *old, QWidget *now) { // Grab keyboard. It might be a little weird to do it here, but it works! // Previously this code was in showEvent, but that did not work in Qt4. if (!_pinentry_info || _pinentry_info->grab) { if (_grabbed && old && (old == _edit || old == mRepeat)) { old->releaseKeyboard(); _grabbed = false; } if (!_grabbed && now && (now == _edit || now == mRepeat)) { now->grabKeyboard(); _grabbed = true; } } } void PinEntryDialog::textChanged(const QString &text) { Q_UNUSED(text); if (mRepeat && mRepeat->text() == _edit->text()) { _ok->setEnabled(true); _ok->setToolTip(QString()); } else if (mRepeat) { _ok->setEnabled(false); _ok->setToolTip(mRepeatError); } if (mVisiActionEdit && sender() == _edit) { mVisiActionEdit->setVisible(!_edit->text().isEmpty()); } } void PinEntryDialog::toggleVisibility() { if (sender() == mVisiActionEdit) { if (_edit->echoMode() == QLineEdit::Password) { mVisiActionEdit->setIcon(QIcon::fromTheme(QLatin1String("hint"))); mVisiActionEdit->setToolTip(mHideTT); _edit->setEchoMode(QLineEdit::Normal); if (mRepeat) { mRepeat->setEchoMode(QLineEdit::Normal); } } else { mVisiActionEdit->setIcon(QIcon::fromTheme(QLatin1String("visibility"))); mVisiActionEdit->setToolTip(mVisibilityTT); _edit->setEchoMode(QLineEdit::Password); if (mRepeat) { mRepeat->setEchoMode(QLineEdit::Password); } } } if (sender() == mVisiCB) { if (mVisiCB->isChecked()) { if (mRepeat) { mRepeat->setEchoMode(QLineEdit::Normal); } _edit->setEchoMode(QLineEdit::Normal); } else { if (mRepeat) { mRepeat->setEchoMode(QLineEdit::Password); } _edit->setEchoMode(QLineEdit::Password); } } } QString PinEntryDialog::repeatedPin() const { if (mRepeat) { return mRepeat->text(); } return QString(); } bool PinEntryDialog::timedOut() const { return _timed_out; } void PinEntryDialog::setRepeatErrorText(const QString &err) { mRepeatError = err; } #include "pinentrydialog.moc" diff --git a/qt/pinentrydialog.h b/qt/pinentrydialog.h index 905be0f..52b7343 100644 --- a/qt/pinentrydialog.h +++ b/qt/pinentrydialog.h @@ -1,120 +1,119 @@ /* pinentrydialog.h - A (not yet) secure Qt 4 dialog for PIN entry. - - Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB) - Copyright 2007 Ingo Klöcker - Copyright 2016 Intevation GmbH - - Written by Steffen Hansen . - Modified by Andre Heinecke - - This program 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. - - This program 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB) + * Copyright 2007 Ingo Klöcker + * Copyright 2016 Intevation GmbH + * + * Written by Steffen Hansen . + * Modified by Andre Heinecke + * + * This program 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. + * + * This program 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+ + */ #ifndef __PINENTRYDIALOG_H__ #define __PINENTRYDIALOG_H__ #include #include #include #include "pinentry.h" class QLabel; class QPushButton; class QLineEdit; class QString; class QProgressBar; class QCheckBox; class QAction; QPixmap icon(QStyle::StandardPixmap which = QStyle::SP_CustomBase); void raiseWindow(QWidget *w); class PinEntryDialog : public QDialog { Q_OBJECT Q_PROPERTY(QString description READ description WRITE setDescription) Q_PROPERTY(QString error READ error WRITE setError) Q_PROPERTY(QString pin READ pin WRITE setPin) Q_PROPERTY(QString prompt READ prompt WRITE setPrompt) public: explicit PinEntryDialog(QWidget *parent = 0, const char *name = 0, int timeout = 0, bool modal = false, bool enable_quality_bar = false, const QString &repeatString = QString(), const QString &visibiltyTT = QString(), const QString &hideTT = QString()); void setDescription(const QString &); QString description() const; void setError(const QString &); QString error() const; void setPin(const QString &); QString pin() const; QString repeatedPin() const; void setRepeatErrorText(const QString &); void setPrompt(const QString &); QString prompt() const; void setOkText(const QString &); void setCancelText(const QString &); void setQualityBar(const QString &); void setQualityBarTT(const QString &); void setPinentryInfo(pinentry_t); bool timedOut() const; protected slots: void updateQuality(const QString &); void slotTimeout(); void textChanged(const QString &); void focusChanged(QWidget *old, QWidget *now); void toggleVisibility(); protected: /* reimp */ void showEvent(QShowEvent *event); private: QLabel *_icon; QLabel *_desc; QLabel *_error; QLabel *_prompt; QLabel *_quality_bar_label; QProgressBar *_quality_bar; QLineEdit *_edit; QLineEdit *mRepeat; QPushButton *_ok; QPushButton *_cancel; bool _grabbed; bool _have_quality_bar; bool _timed_out; pinentry_t _pinentry_info; QTimer *_timer; QString mRepeatError, mVisibilityTT, mHideTT; QAction *mVisiActionEdit; QCheckBox *mVisiCB; }; #endif // __PINENTRYDIALOG_H__ diff --git a/secmem/Makefile.am b/secmem/Makefile.am index e5e0711..9e176e7 100644 --- a/secmem/Makefile.am +++ b/secmem/Makefile.am @@ -1,30 +1,30 @@ # Secure Memory Makefile # Copyright (C) 2002 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +# along with this program; if not, see . +# SPDX-License-Identifier: GPL-2.0+ ## Process this file with automake to produce Makefile.in noinst_LIBRARIES = libsecmem.a libsecmem_a_SOURCES = \ memory.h \ secmem-util.h \ util.h \ secmem.c \ util.c \ secmem++.h diff --git a/secmem/memory.h b/secmem/memory.h index 354c6c9..75fe708 100644 --- a/secmem/memory.h +++ b/secmem/memory.h @@ -1,55 +1,55 @@ /* Quintuple Agent secure memory allocation * Copyright (C) 1998,1999 Free Software Foundation, Inc. * Copyright (C) 1999,2000 Robert Bihlmeyer * - * This program 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. + * This program 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. * - * This program 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. + * This program 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * 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+ */ #ifndef _MEMORY_H #define _MEMORY_H #include #ifdef __cplusplus extern "C" { -#if 0 +#if 0 } #endif #endif /* values for flags, hardcoded in secmem.c */ #define SECMEM_WARN 0 #define SECMEM_DONT_WARN 1 #define SECMEM_SUSPEND_WARN 2 void secmem_init( size_t npool ); void secmem_term( void ); void *secmem_malloc( size_t size ); void *secmem_realloc( void *a, size_t newsize ); void secmem_free( void *a ); int m_is_secure( const void *p ); void secmem_dump_stats(void); void secmem_set_flags( unsigned flags ); unsigned secmem_get_flags(void); size_t secmem_get_max_size (void); -#if 0 +#if 0 { #endif #ifdef __cplusplus } #endif #endif /* _MEMORY_H */ diff --git a/secmem/secmem++.h b/secmem/secmem++.h index 88a5d45..b0aa9e9 100644 --- a/secmem/secmem++.h +++ b/secmem/secmem++.h @@ -1,91 +1,91 @@ /* STL allocator for secmem * Copyright (C) 2008 Marc Mutz * - * This program 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. + * This program 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. * - * This program 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. + * This program 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * 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+ */ #ifndef __SECMEM_SECMEMPP_H__ #define __SECMEM_SECMEMPP_H__ #include "secmem/memory.h" #include namespace secmem { template class alloc { public: // type definitions: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; // rebind template struct rebind { typedef alloc other; }; // address pointer address( reference value ) const { return &value; } const_pointer address( const_reference value ) const { return &value; } // (trivial) ctors and dtors alloc() {} alloc( const alloc & ) {} template alloc( const alloc & ) {} // copy ctor is ok ~alloc() {} // de/allocation size_type max_size() const { return secmem_get_max_size(); } pointer allocate( size_type n, void * =0 ) { return static_cast( secmem_malloc( n * sizeof(T) ) ); } void deallocate( pointer p, size_type ) { secmem_free( p ); } // de/construct void construct( pointer p, const T & value ) { void * loc = p; new (loc)T(value); } void destruct( pointer p ) { p->~T(); } }; // equality comparison template bool operator==( const alloc &, const alloc & ) { return true; } template bool operator!=( const alloc &, const alloc & ) { return false; } } #endif /* __SECMEM_SECMEMPP_H__ */ diff --git a/secmem/secmem.c b/secmem/secmem.c index 7a3bd4b..3abcc4a 100644 --- a/secmem/secmem.c +++ b/secmem/secmem.c @@ -1,463 +1,463 @@ /* secmem.c - memory allocation from a secure heap - * Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc. - * Copyright (C) 2015 g10 Code GmbH + * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * 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 +void log_info(char *template, ...) { va_list args; - + va_start(args, template); vfprintf(stderr, template, args); va_end(args); } -void +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 */ static int pool_is_mmapped; 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 log_info("Please note that you don't have secure memory on this system\n"); #endif } static void init_pool( size_t n) { size_t pgsize; poolsize = n; if( disable_secmem ) log_bug("secure memory is disabled"); #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 +size_t secmem_get_max_size (void) { return poolsize; } diff --git a/secmem/util.c b/secmem/util.c index 31bea01..44609cf 100644 --- a/secmem/util.c +++ b/secmem/util.c @@ -1,116 +1,116 @@ /* Quintuple Agent * Copyright (C) 1999 Robert Bihlmeyer * - * This program 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. + * This program 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. * - * This program 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. + * This program 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * 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+ */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE 1 #include #ifndef HAVE_W32CE_SYSTEM # include #endif #include #include #include #include #include #include "util.h" #ifndef HAVE_DOSISH_SYSTEM static int uid_set = 0; static uid_t real_uid, file_uid; #endif /*!HAVE_DOSISH_SYSTEM*/ /* Write DATA of size BYTES to FD, until all is written or an error occurs. */ ssize_t xwrite(int fd, const void *data, size_t bytes) { char *ptr; size_t todo; ssize_t written = 0; for (ptr = (char *)data, todo = bytes; todo; ptr += written, todo -= written) { do written = write (fd, ptr, todo); while ( #ifdef HAVE_W32CE_SYSTEM 0 #else written == -1 && errno == EINTR #endif ); if (written < 0) break; } return written; } #if 0 extern int debug; int debugmsg(const char *fmt, ...) { va_list va; int ret; if (debug) { va_start(va, fmt); fprintf(stderr, "\e[4m"); ret = vfprintf(stderr, fmt, va); fprintf(stderr, "\e[24m"); va_end(va); return ret; } else return 0; } #endif /* initialize uid variables */ #ifndef HAVE_DOSISH_SYSTEM static void init_uids(void) { real_uid = getuid(); file_uid = geteuid(); uid_set = 1; } #endif /* drop all additional privileges */ void drop_privs() { #ifndef HAVE_DOSISH_SYSTEM if (!uid_set) init_uids(); if (real_uid != file_uid) { if (setuid(real_uid) < 0) { perror("dropping privileges failed"); exit(EXIT_FAILURE); } file_uid = real_uid; } #endif } diff --git a/secmem/util.h b/secmem/util.h index 7986c99..233ab13 100644 --- a/secmem/util.h +++ b/secmem/util.h @@ -1,66 +1,66 @@ /* Quintuple Agent utilities * Copyright (C) 1999 Robert Bihlmeyer * Copyright (C) 2003 g10 Code GmbH * - * This program 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. + * This program 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. * - * This program 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. + * This program 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * 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+ */ #ifndef _UTIL_H #define _UTIL_H #include #ifndef HAVE_BYTE_TYPEDEF -# undef byte +# undef byte # ifdef __riscos__ /* Norcroft treats char == unsigned char but char* != unsigned char* */ typedef char byte; -# else +# else typedef unsigned char byte; -# endif +# endif # define HAVE_BYTE_TYPEDEF #endif #ifndef HAVE_ULONG_TYPEDEF -# undef ulong +# undef ulong typedef unsigned long ulong; # define HAVE_ULONG_TYPEDEF #endif ssize_t xwrite(int, const void *, size_t); /* write until finished */ int debugmsg(const char *, ...); /* output a debug message if debugging==on */ void drop_privs(void); /* finally drop privileges */ /* To avoid that a compiler optimizes certain memset calls away, these macros may be used instead. */ #define wipememory2(_ptr,_set,_len) do { \ volatile char *_vptr=(volatile char *)(_ptr); \ size_t _vlen=(_len); \ while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ } while(0) #define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) #define wipe(_ptr,_len) wipememory2(_ptr,0,_len) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) #endif diff --git a/tty/Makefile.am b/tty/Makefile.am index 1e8ffb5..7844bcb 100644 --- a/tty/Makefile.am +++ b/tty/Makefile.am @@ -1,27 +1,28 @@ # Makefile.am - PIN entry tty frontend. # Copyright (C) 2002, 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 bin_PROGRAMS = pinentry-tty AM_CPPFLAGS = $(COMMON_CFLAGS) -I$(top_srcdir)/secmem -I$(top_srcdir)/pinentry LDADD = ../pinentry/libpinentry.a ../secmem/libsecmem.a \ $(COMMON_LIBS) $(LIBCAP) $(LIBICONV) pinentry_tty_SOURCES = pinentry-tty.c diff --git a/tty/pinentry-tty.c b/tty/pinentry-tty.c index 0e9ab4b..700b0f1 100644 --- a/tty/pinentry-tty.c +++ b/tty/pinentry-tty.c @@ -1,579 +1,580 @@ /* pinentry-tty.c - A minimalist dumb terminal mechanism for PIN entry - Copyright (C) 2014 Serge Voilokov - Copyright (C) 2015 Daniel Kahn Gillmor - Copyright (C) 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 . + * Copyright (C) 2014 Serge Voilokov + * Copyright (C) 2015 Daniel Kahn Gillmor + * Copyright (C) 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+ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_UTIME_H #include #endif /*HAVE_UTIME_H*/ #include #include #include #include #include "pinentry.h" #include "memory.h" #ifndef HAVE_DOSISH_SYSTEM static int timed_out; #endif static struct termios n_term; static struct termios o_term; static int cbreak (int fd) { if ((tcgetattr(fd, &o_term)) == -1) return -1; n_term = o_term; n_term.c_lflag = n_term.c_lflag & ~(ECHO|ICANON); n_term.c_cc[VMIN] = 1; n_term.c_cc[VTIME]= 0; if ((tcsetattr(fd, TCSAFLUSH, &n_term)) == -1) return -1; return 1; } #define UNDERLINE_START "\033[4m" /* Bold, red. */ #define ALERT_START "\033[1;31m" #define NORMAL_RESTORE "\033[0m" static char button (char *text, char *default_text, FILE *ttyfo) { char *highlight; if (! text) return 0; /* Skip any leading white space. */ while (*text == ' ') text ++; highlight = text; while ((highlight = strchr (highlight, '_'))) { highlight = highlight + 1; if (*highlight == '_') { /* Escaped underscore. Skip both characters. */ highlight++; continue; } if (!isalnum (*highlight)) /* Unusable accelerator. */ continue; break; } if (! highlight) /* Not accelerator. Take the first alpha-numeric character. */ { highlight = text; while (*highlight && !isalnum (*highlight)) highlight ++; } if (! *highlight) /* Hmm, no alpha-numeric characters. */ { if (! default_text) return 0; text = highlight = default_text; } fputs (" ", ttyfo); for (; *text; text ++) { /* Skip accelerator prefix. */ if (*text == '_') { text ++; if (! *text) break; } if (text == highlight) fputs (UNDERLINE_START, ttyfo); fputc (*text, ttyfo); if (text == highlight) fputs (NORMAL_RESTORE, ttyfo); } fputc ('\n', ttyfo); return tolower (*highlight); } static void dump_error_text (FILE *ttyfo, const char *text) { int lines = 0; if (! text || ! *text) return; for (;;) { const char *eol = strchr (text, '\n'); if (! eol) eol = text + strlen (text); lines ++; fwrite ("\n *** ", 6, 1, ttyfo); fputs (ALERT_START, ttyfo); fwrite (text, (size_t) (eol - text), 1, ttyfo); fputs (NORMAL_RESTORE, ttyfo); if (! *eol) break; text = eol + 1; } if (lines > 1) fputc ('\n', ttyfo); else fwrite (" ***\n", 5, 1, ttyfo); fputc ('\n', ttyfo); } static int confirm (pinentry_t pinentry, FILE *ttyfi, FILE *ttyfo) { char *msg; char *msgbuffer = NULL; char ok = 0; char notok = 0; char cancel = 0; int ret; dump_error_text (ttyfo, pinentry->error); msg = pinentry->description; if (! msg) { /* If there is no description, fallback to the title. */ msg = msgbuffer = pinentry_get_title (pinentry); } if (! msg) msg = "Confirm:"; if (msg) { fputs (msg, ttyfo); fputc ('\n', ttyfo); } free (msgbuffer); fflush (ttyfo); if (pinentry->ok) ok = button (pinentry->ok, "OK", ttyfo); else if (pinentry->default_ok) ok = button (pinentry->default_ok, "OK", ttyfo); else ok = button ("OK", NULL, ttyfo); if (! pinentry->one_button) { if (pinentry->cancel) cancel = button (pinentry->cancel, "Cancel", ttyfo); else if (pinentry->default_cancel) cancel = button (pinentry->default_cancel, "Cancel", ttyfo); if (pinentry->notok) notok = button (pinentry->notok, "No", ttyfo); } if (cbreak (fileno (ttyfi)) == -1) { int err = errno; fprintf (stderr, "cbreak failure, exiting\n"); errno = err; return -1; } while (1) { int input; if (pinentry->one_button) fprintf (ttyfo, "Press any key to continue."); else { fputc ('[', ttyfo); if (ok) fputc (ok, ttyfo); if (cancel) fputc (cancel, ttyfo); if (notok) fputc (notok, ttyfo); fputs("]? ", ttyfo); } fflush (ttyfo); input = fgetc (ttyfi); fprintf (ttyfo, "%c\n", input); input = tolower (input); if (input == EOF || input == 0x4) /* End of file or control-d (= end of file). */ { pinentry->close_button = 1; pinentry->canceled = 1; ret = 0; break; } if (pinentry->one_button) { ret = 1; break; } if (cancel && input == cancel) { pinentry->canceled = 1; ret = 0; break; } else if (notok && input == notok) { ret = 0; break; } else if (ok && input == ok) { ret = 1; break; } else { fprintf (ttyfo, "Invalid selection.\n"); } } #ifndef HAVE_DOSISH_SYSTEM if (timed_out) pinentry->specific_err = gpg_error (GPG_ERR_TIMEOUT); #endif tcsetattr (fileno(ttyfi), TCSANOW, &o_term); return ret; } static char * read_password (FILE *ttyfi, FILE *ttyfo) { int done = 0; int len = 128; int count = 0; char *buffer; (void) ttyfo; if (cbreak (fileno (ttyfi)) == -1) { int err = errno; fprintf (stderr, "cbreak failure, exiting\n"); errno = err; return NULL; } buffer = secmem_malloc (len); if (! buffer) return NULL; while (!done) { int c; if (count == len - 1) /* Double the buffer's size. Note: we check if count is len - 1 and not len so that we always have space for the NUL character. */ { int new_len = 2 * len; char *tmp = secmem_realloc (buffer, new_len); if (! tmp) { secmem_free (tmp); return NULL; } buffer = tmp; len = new_len; } c = fgetc (ttyfi); switch (c) { case 0x4: case EOF: /* Control-d (i.e., end of file) or a real EOF. */ done = -1; break; case '\n': done = 1; break; case 0x7f: /* Backspace. */ if (count > 0) count --; break; default: buffer[count ++] = c; break; } } buffer[count] = '\0'; tcsetattr (fileno(ttyfi), TCSANOW, &o_term); if (done == -1) { secmem_free (buffer); return NULL; } return buffer; } static int password (pinentry_t pinentry, FILE *ttyfi, FILE *ttyfo) { char *msg; char *msgbuffer = NULL; int done = 0; msg = pinentry->description; if (! msg) msg = msgbuffer = pinentry_get_title (pinentry); if (! msg) msg = "Enter your passphrase."; dump_error_text (ttyfo, pinentry->error); fprintf (ttyfo, "%s\n", msg); free (msgbuffer); while (! done) { char *passphrase; char *prompt = pinentry->prompt; if (! prompt || !*prompt) prompt = "PIN"; fprintf (ttyfo, "%s%s ", prompt, /* Make sure the prompt ends in a : or a question mark. */ (prompt[strlen(prompt) - 1] == ':' || prompt[strlen(prompt) - 1] == '?') ? "" : ":"); fflush (ttyfo); passphrase = read_password (ttyfi, ttyfo); fputc ('\n', ttyfo); if (! passphrase) { done = -1; break; } if (! pinentry->repeat_passphrase) done = 1; else { char *passphrase2; prompt = pinentry->repeat_passphrase; fprintf (ttyfo, "%s%s ", prompt, /* Make sure the prompt ends in a : or a question mark. */ (prompt[strlen(prompt) - 1] == ':' || prompt[strlen(prompt) - 1] == '?') ? "" : ":"); fflush (ttyfo); passphrase2 = read_password (ttyfi, ttyfo); fputc ('\n', ttyfo); if (! passphrase2) { done = -1; break; } if (strcmp (passphrase, passphrase2) == 0) { pinentry->repeat_okay = 1; done = 1; } else dump_error_text (ttyfo, pinentry->repeat_error_string ?: "Passphrases don't match."); secmem_free (passphrase2); } if (done == 1) pinentry_setbuffer_use (pinentry, passphrase, 0); else secmem_free (passphrase); } #ifndef HAVE_DOSISH_SYSTEM if (timed_out) pinentry->specific_err = gpg_error (GPG_ERR_TIMEOUT); #endif return done; } /* If a touch has been registered, touch that file. */ static void do_touch_file(pinentry_t pinentry) { #ifdef HAVE_UTIME_H struct stat st; time_t tim; if (!pinentry->touch_file || !*pinentry->touch_file) return; if (stat(pinentry->touch_file, &st)) return; /* Oops. */ /* Make sure that we actually update the mtime. */ while ((tim = time(NULL)) == st.st_mtime) sleep(1); /* Update but ignore errors as we can't do anything in that case. Printing error messages may even clubber the display further. */ utime (pinentry->touch_file, NULL); #endif /*HAVE_UTIME_H*/ } #ifndef HAVE_DOSISH_SYSTEM static void catchsig(int sig) { if (sig == SIGALRM) timed_out = 1; } #endif int tty_cmd_handler(pinentry_t pinentry) { int rc = 0; FILE *ttyfi = stdin; FILE *ttyfo = stdout; #ifndef HAVE_DOSISH_SYSTEM timed_out = 0; if (pinentry->timeout) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = catchsig; sigaction(SIGALRM, &sa, NULL); alarm(pinentry->timeout); } #endif if (pinentry->ttyname) { ttyfi = fopen (pinentry->ttyname, "r"); if (!ttyfi) rc = -1; else { ttyfo = fopen (pinentry->ttyname, "w"); if (!ttyfo) { int err = errno; fclose (ttyfi); errno = err; rc = -1; } } } if (! rc) { if (pinentry->pin) rc = password (pinentry, ttyfi, ttyfo); else rc = confirm (pinentry, ttyfi, ttyfo); } do_touch_file (pinentry); if (pinentry->ttyname) { fclose (ttyfi); fclose (ttyfo); } return rc; } pinentry_cmd_handler_t pinentry_cmd_handler = tty_cmd_handler; int main (int argc, char *argv[]) { pinentry_init ("pinentry-tty"); /* Consumes all arguments. */ pinentry_parse_opts(argc, argv); if (pinentry_loop ()) return 1; return 0; } diff --git a/w32/Makefile.am b/w32/Makefile.am index 160d375..12c66db 100644 --- a/w32/Makefile.am +++ b/w32/Makefile.am @@ -1,42 +1,42 @@ # Makefile.am - PIN entry W32 # Copyright (C) 2004 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +# along with this program; if not, see . +# SPDX-License-Identifier: GPL-2.0+ ## Process this file with automake to produce Makefile.in logos = logo-32.bmp logo-48.bmp logo-64.bmp logo-96.bmp logo-128.bmp EXTRA_DIST = $(logos) bin_PROGRAMS = pinentry-w32 AM_CPPFLAGS = $(COMMON_CFLAGS) -I$(top_srcdir)/secmem \ -I$(top_srcdir)/pinentry pinentry_w32_SOURCES = main.c pinentry-w32.rc resource.h # Note: For testing you should add -mconsole to LDFLAGS. pinentry_w32_LDFLAGS = -mwindows pinentry_w32_LDADD = pinentry-w32.o \ ../pinentry/libpinentry.a ../secmem/libsecmem.a \ $(COMMON_LIBS) pinentry-w32.o: pinentry-w32.rc resource.h $(logos) $(WINDRES) -I.. -v -o $@ $< diff --git a/w32/main.c b/w32/main.c index b35879d..0b3d702 100644 --- a/w32/main.c +++ b/w32/main.c @@ -1,720 +1,720 @@ /* main.c - Secure W32 dialog for PIN entry. - Copyright (C) 2004, 2007 g10 Code GmbH - - This program 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. - - This program 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA */ + * Copyright (C) 2004, 2007 g10 Code GmbH + * + * This program 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. + * + * This program 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 #if WINVER < 0x0403 # define WINVER 0x0403 /* Required for SendInput. */ #endif #include #ifdef HAVE_W32CE_SYSTEM # include # include #endif #include "pinentry.h" #include "memory.h" #include "resource.h" /* #include "msgcodes.h" */ #define PGMNAME "pinentry-w32" #ifndef LSFW_LOCK # define LSFW_LOCK 1 # define LSFW_UNLOCK 2 #endif #ifndef debugfp #define debugfp stderr #endif /* This function pointer gets initialized in main. */ #ifndef HAVE_W32CE_SYSTEM static BOOL WINAPI (*lock_set_foreground_window)(UINT); #endif static int w32_cmd_handler (pinentry_t pe); static void ok_button_clicked (HWND dlg, pinentry_t pe); /* We use global variables for the state, because there should never ever be a second instance. */ static HWND dialog_handle; static int confirm_mode; static int passphrase_ok; static int confirm_yes; /* The file descriptors for the loop. */ static int w32_infd; static int w32_outfd; /* Connect this module to the pinentry framework. */ pinentry_cmd_handler_t pinentry_cmd_handler = w32_cmd_handler; const char * w32_strerror (int ec) { static char strerr[256]; if (ec == -1) ec = (int)GetLastError (); #ifdef HAVE_W32CE_SYSTEM /* There is only a wchar_t FormatMessage. It does not make much sense to play the conversion game; we print only the code. */ snprintf (strerr, sizeof strerr, "ec=%d", ec); strerr[sizeof strerr -1] = 0; #else FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), strerr, sizeof strerr - 1, NULL); #endif return strerr; } #ifdef HAVE_W32CE_SYSTEM /* Create a pipe. WRITE_END shall have the opposite value of the one pssed to _assuan_w32ce_prepare_pipe; see there for more details. */ #define GPGCEDEV_IOCTL_MAKE_PIPE \ CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS) static HANDLE w32ce_finish_pipe (int rvid, int write_end) { HANDLE hd; hd = CreateFile (L"GPG1:", write_end? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL); if (hd != INVALID_HANDLE_VALUE) { if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_MAKE_PIPE, &rvid, sizeof rvid, NULL, 0, NULL, NULL)) { DWORD lastrc = GetLastError (); CloseHandle (hd); hd = INVALID_HANDLE_VALUE; SetLastError (lastrc); } } return hd; } #endif /*HAVE_W32CE_SYSTEM*/ /* static HWND */ /* show_window_hierarchy (HWND parent, int level) */ /* { */ /* HWND child; */ /* child = GetWindow (parent, GW_CHILD); */ /* while (child) */ /* { */ /* char buf[1024+1]; */ /* char name[200]; */ /* int nname; */ /* char *pname; */ /* memset (buf, 0, sizeof (buf)); */ /* GetWindowText (child, buf, sizeof (buf)-1); */ /* nname = GetClassName (child, name, sizeof (name)-1); */ /* if (nname) */ /* pname = name; */ /* else */ /* pname = NULL; */ /* fprintf (debugfp, "### %*shwnd=%p (%s) `%s'\n", level*2, "", child, */ /* pname? pname:"", buf); */ /* show_window_hierarchy (child, level+1); */ /* child = GetNextWindow (child, GW_HWNDNEXT); */ /* } */ /* return NULL; */ /* } */ /* Convert a wchar to UTF8. Caller needs to release the string. Returns NULL on error. */ static char * wchar_to_utf8 (const wchar_t *string, size_t len, int secure) { int n; char *result; /* Note, that CP_UTF8 is not defined in Windows versions earlier than NT. */ n = WideCharToMultiByte (CP_UTF8, 0, string, len, NULL, 0, NULL, NULL); if (n < 0) return NULL; result = secure? secmem_malloc (n+1) : malloc (n+1); if (!result) return NULL; n = WideCharToMultiByte (CP_UTF8, 0, string, len, result, n, NULL, NULL); if (n < 0) { if (secure) secmem_free (result); else free (result); return NULL; } return result; } /* Convert a UTF8 string to wchar. Returns NULL on error. Caller needs to free the returned value. */ wchar_t * utf8_to_wchar (const char *string) { int n; wchar_t *result; size_t len = strlen (string); n = MultiByteToWideChar (CP_UTF8, 0, string, len, NULL, 0); if (n < 0) return NULL; result = calloc ((n+1), sizeof *result); if (!result) return NULL; n = MultiByteToWideChar (CP_UTF8, 0, string, len, result, n); if (n < 0) { free (result); return NULL; } result[n] = 0; return result; } /* Raise the software input panel. */ static void raise_sip (HWND dlg) { #ifdef HAVE_W32CE_SYSTEM SIPINFO si; SetForegroundWindow (dlg); memset (&si, 0, sizeof si); si.cbSize = sizeof si; if (SipGetInfo (&si)) { si.fdwFlags |= SIPF_ON; SipSetInfo (&si); } #else (void)dlg; #endif } /* Center the window CHILDWND with the desktop as its parent window. STYLE is passed as second arg to SetWindowPos.*/ static void center_window (HWND childwnd, HWND style) { #ifndef HAVE_W32CE_SYSTEM HWND parwnd; RECT rchild, rparent; HDC hdc; int wchild, hchild, wparent, hparent; int wscreen, hscreen, xnew, ynew; int flags = SWP_NOSIZE | SWP_NOZORDER; parwnd = GetDesktopWindow (); GetWindowRect (childwnd, &rchild); wchild = rchild.right - rchild.left; hchild = rchild.bottom - rchild.top; GetWindowRect (parwnd, &rparent); wparent = rparent.right - rparent.left; hparent = rparent.bottom - rparent.top; hdc = GetDC (childwnd); wscreen = GetDeviceCaps (hdc, HORZRES); hscreen = GetDeviceCaps (hdc, VERTRES); ReleaseDC (childwnd, hdc); xnew = rparent.left + ((wparent - wchild) / 2); if (xnew < 0) xnew = 0; else if ((xnew+wchild) > wscreen) xnew = wscreen - wchild; ynew = rparent.top + ((hparent - hchild) / 2); if (ynew < 0) ynew = 0; else if ((ynew+hchild) > hscreen) ynew = hscreen - hchild; if (style == HWND_TOPMOST || style == HWND_NOTOPMOST) flags = SWP_NOMOVE | SWP_NOSIZE; SetWindowPos (childwnd, style? style : NULL, xnew, ynew, 0, 0, flags); #endif } static void move_mouse_and_click (HWND hwnd) { #ifndef HAVE_W32CE_SYSTEM RECT rect; HDC hdc; int wscreen, hscreen, x, y, normx, normy; INPUT inp[3]; int idx; hdc = GetDC (hwnd); wscreen = GetDeviceCaps (hdc, HORZRES); hscreen = GetDeviceCaps (hdc, VERTRES); ReleaseDC (hwnd, hdc); if (wscreen < 10 || hscreen < 10) return; GetWindowRect (hwnd, &rect); x = rect.left; y = rect.bottom; normx = x * (65535 / wscreen); if (normx < 0 || normx > 65535) return; normy = y * (65535 / hscreen); if (normy < 0 || normy > 65535) return; for (idx=0; idx < 3; idx++) memset (&inp[idx], 0, sizeof inp[idx]); idx=0; inp[idx].type = INPUT_MOUSE; inp[idx].mi.dx = normx; inp[idx].mi.dy = normy; inp[idx].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; idx++; inp[idx].type = INPUT_MOUSE; inp[idx].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; idx++; inp[idx].type = INPUT_MOUSE; inp[idx].mi.dwFlags = MOUSEEVENTF_LEFTUP; idx++; if ( (SendInput (idx, inp, sizeof (INPUT)) != idx) && debugfp) fprintf (debugfp, "SendInput failed: %s\n", w32_strerror (-1)); #endif } /* Resize the button so that STRING fits into it. */ static void resize_button (HWND hwnd, const char *string) { if (!hwnd) return; /* FIXME: Need to figure out how to convert dialog coorddnates to screen coordinates and how buttons should be placed. */ /* SetWindowPos (hbutton, NULL, */ /* 10, 180, */ /* strlen (string+2), 14, */ /* (SWP_NOZORDER)); */ } /* Call SetDlgItemTextW with an UTF8 string. */ static void set_dlg_item_text (HWND dlg, int item, const char *string) { if (!string || !*string) SetDlgItemTextW (dlg, item, L""); else { wchar_t *wbuf; wbuf = utf8_to_wchar (string); if (!wbuf) SetDlgItemTextW (dlg, item, L"[out of core]"); else { SetDlgItemTextW (dlg, item, wbuf); free (wbuf); } } } /* Load our butmapped icon from the resource and display it. */ static void set_bitmap (HWND dlg, int item) { HWND hwnd; HBITMAP bitmap; RECT rect; int resid; hwnd = GetDlgItem (dlg, item); if (!hwnd) return; rect.left = 0; rect.top = 0; rect.right = 32; rect.bottom = 32; if (!MapDialogRect (dlg, &rect)) { fprintf (stderr, "MapDialogRect failed: %s\n", w32_strerror (-1)); return; } /* fprintf (stderr, "MapDialogRect: %d/%d\n", rect.right, rect.bottom); */ switch (rect.right) { case 32: resid = IDB_ICON_32; break; case 48: resid = IDB_ICON_48; break; case 64: resid = IDB_ICON_64; break; case 96: resid = IDB_ICON_96; break; default: resid = IDB_ICON_128;break; } bitmap = LoadImage (GetModuleHandle (NULL), MAKEINTRESOURCE (resid), IMAGE_BITMAP, rect.right, rect.bottom, (LR_SHARED | LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS)); if (!bitmap) { fprintf (stderr, "LoadImage failed: %s\n", w32_strerror (-1)); return; } SendMessage(hwnd, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)bitmap); } /* Dialog processing loop. */ static BOOL CALLBACK dlg_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam) { static pinentry_t pe; /* static int item; */ /* { */ /* int idx; */ /* for (idx=0; msgcodes[idx].string; idx++) */ /* if (msg == msgcodes[idx].msg) */ /* break; */ /* if (msgcodes[idx].string) */ /* fprintf (debugfp, "received %s\n", msgcodes[idx].string); */ /* else */ /* fprintf (debugfp, "received WM_%u\n", msg); */ /* } */ switch (msg) { case WM_INITDIALOG: dialog_handle = dlg; pe = (pinentry_t)lparam; if (!pe) abort (); set_dlg_item_text (dlg, IDC_PINENT_PROMPT, pe->prompt); set_dlg_item_text (dlg, IDC_PINENT_DESC, pe->description); set_dlg_item_text (dlg, IDC_PINENT_TEXT, ""); set_bitmap (dlg, IDC_PINENT_ICON); if (pe->ok) { set_dlg_item_text (dlg, IDOK, pe->ok); resize_button (GetDlgItem (dlg, IDOK), pe->ok); } if (pe->cancel) { set_dlg_item_text (dlg, IDCANCEL, pe->cancel); resize_button (GetDlgItem (dlg, IDCANCEL), pe->cancel); } if (pe->error) set_dlg_item_text (dlg, IDC_PINENT_ERR, pe->error); if (confirm_mode) { EnableWindow (GetDlgItem (dlg, IDC_PINENT_TEXT), FALSE); SetWindowPos (GetDlgItem (dlg, IDC_PINENT_TEXT), NULL, 0, 0, 0, 0, (SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_HIDEWINDOW)); /* item = IDOK; */ } /* else */ /* item = IDC_PINENT_TEXT; */ center_window (dlg, HWND_TOP); /* Unfortunately we can't use SetForegroundWindow because there is no easy eay to have all the calling processes do an AllowSetForegroundWindow. What we do instead is to bad hack by simulating a click to the Window. */ /* if (SetForegroundWindow (dlg) && lock_set_foreground_window) */ /* { */ /* lock_set_foreground_window (LSFW_LOCK); */ /* } */ /* show_window_hierarchy (GetDesktopWindow (), 0); */ ShowWindow (dlg, SW_SHOW); move_mouse_and_click ( GetDlgItem (dlg, IDC_PINENT_PROMPT) ); raise_sip (dlg); break; case WM_COMMAND: switch (LOWORD (wparam)) { case IDOK: if (confirm_mode) confirm_yes = 1; else ok_button_clicked (dlg, pe); EndDialog (dlg, TRUE); break; case IDCANCEL: pe->result = -1; EndDialog (dlg, FALSE); break; } break; case WM_KEYDOWN: if (wparam == VK_RETURN) { if (confirm_mode) confirm_yes = 1; else ok_button_clicked (dlg, pe); EndDialog (dlg, TRUE); } break; case WM_CTLCOLORSTATIC: if ((HWND)lparam == GetDlgItem (dlg, IDC_PINENT_ERR)) { /* Display the error prompt in red. */ SetTextColor ((HDC)wparam, RGB (255, 0, 0)); SetBkMode ((HDC)wparam, TRANSPARENT); return (BOOL)GetStockObject (NULL_BRUSH); } break; } return FALSE; } /* The okay button has been clicked or the enter enter key in the text field. */ static void ok_button_clicked (HWND dlg, pinentry_t pe) { char *s_utf8; wchar_t *w_buffer; size_t w_buffer_size = 255; unsigned int nchar; pe->locale_err = 1; w_buffer = secmem_malloc ((w_buffer_size + 1) * sizeof *w_buffer); if (!w_buffer) return; nchar = GetDlgItemTextW (dlg, IDC_PINENT_TEXT, w_buffer, w_buffer_size); s_utf8 = wchar_to_utf8 (w_buffer, nchar, 1); secmem_free (w_buffer); if (s_utf8) { passphrase_ok = 1; pinentry_setbufferlen (pe, strlen (s_utf8) + 1); if (pe->pin) strcpy (pe->pin, s_utf8); secmem_free (s_utf8); pe->locale_err = 0; pe->result = pe->pin? strlen (pe->pin) : 0; } } static int w32_cmd_handler (pinentry_t pe) { /* HWND lastwindow = GetForegroundWindow (); */ confirm_mode = !pe->pin; passphrase_ok = confirm_yes = 0; dialog_handle = NULL; DialogBoxParam (GetModuleHandle (NULL), MAKEINTRESOURCE (IDD_PINENT), GetDesktopWindow (), dlg_proc, (LPARAM)pe); if (dialog_handle) { /* if (lock_set_foreground_window) */ /* lock_set_foreground_window (LSFW_UNLOCK); */ /* if (lastwindow) */ /* SetForegroundWindow (lastwindow); */ } else return -1; if (confirm_mode) return confirm_yes; else if (passphrase_ok && pe->pin) return strlen (pe->pin); else return -1; } /* WindowsCE uses a very strange way of handling the standard streams. There is a function SetStdioPath to associate a standard stream with a file or a device but what we really want is to use pipes as standard streams. Despite that we implement pipes using a device, we would have some limitations on the number of open pipes due to the 3 character limit of device file name. Thus we don't take this path. Another option would be to install a file system driver with support for pipes; this would allow us to get rid of the device name length limitation. However, with GnuPG we can get away be redefining the standard streams and passing the handles to be used on the command line. This has also the advantage that it makes creating a process much easier and does not require the SetStdioPath set and restore game. The caller needs to pass the rendezvous ids using up to three options: -&S0= -&S1= -&S2= They are all optional but they must be the first arguments on the command line. Parsing stops as soon as an invalid option is found. These rendezvous ids are then used to finish the pipe creation.*/ #ifdef HAVE_W32CE_SYSTEM static void parse_std_file_handles (int *argcp, char ***argvp) { int argc = *argcp; char **argv = *argvp; const char *s; int fd; int i; int fixup = 0; if (!argc) return; for (argc--, argv++; argc; argc--, argv++) { s = *argv; if (*s == '-' && s[1] == '&' && s[2] == 'S' && (s[3] == '0' || s[3] == '1' || s[3] == '2') && s[4] == '=' && (strchr ("-01234567890", s[5]) || !strcmp (s+5, "null"))) { if (s[5] == 'n') fd = (int)(-1); else fd = (int)w32ce_finish_pipe (atoi (s+5), s[3] != '0'); if (s[3] == '0' && fd != -1) w32_infd = fd; else if (s[3] == '1' && fd != -1) w32_outfd = fd; fixup++; } else break; } if (fixup) { argc = *argcp; argc -= fixup; *argcp = argc; argv = *argvp; for (i=1; i < argc; i++) argv[i] = argv[i + fixup]; for (; i < argc + fixup; i++) argv[i] = NULL; } } #endif /*HAVE_W32CE_SYSTEM*/ int main (int argc, char **argv) { #ifndef HAVE_W32CE_SYSTEM void *handle; #endif w32_infd = STDIN_FILENO; w32_outfd = STDOUT_FILENO; #ifdef HAVE_W32CE_SYSTEM parse_std_file_handles (&argc, &argv); #endif pinentry_init (PGMNAME); pinentry_parse_opts (argc, argv); /* debugfp = fopen ("pinentry.log", "w"); */ /* if (!debugfp) */ /* debugfp = stderr; */ /* We need to load a function because that one is only available since W2000 but not in older NTs. */ #ifndef HAVE_W32CE_SYSTEM handle = LoadLibrary ("user32.dll"); if (handle) { void *foo; foo = GetProcAddress (handle, "LockSetForegroundWindow"); if (foo) lock_set_foreground_window = foo; else CloseHandle (handle); } #endif if (pinentry_loop2 (w32_infd, w32_outfd)) return 1; #ifdef HAVE_W32CE_SYSTEM Sleep (400); #endif return 0; } diff --git a/w32/pinentry-w32.rc b/w32/pinentry-w32.rc index e46ad74..bc3dacb 100755 --- a/w32/pinentry-w32.rc +++ b/w32/pinentry-w32.rc @@ -1,74 +1,74 @@ /* dialog.rc - Resource definitions -*- c -*- - Copyright (C) 2004, 2010 g10 Code GmbH - - This program 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. - - This program 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA */ + * Copyright (C) 2004, 2010 g10 Code GmbH + * + * This program 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. + * + * This program 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 #include "resource.h" /* * Main dialog */ #ifdef HAVE_W32CE_SYSTEM IDD_PINENT DIALOG DISCARDABLE 0, 0, 150, 100 STYLE DS_MODALFRAME | DS_SYSMODAL | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Pinentry" FONT 8, "MS Shell Dlg" BEGIN LTEXT "", IDC_PINENT_DESC, 2, 2, 140, 24 LTEXT "", IDC_PINENT_ERR, 2, 30, 140, 12 LTEXT "", IDC_PINENT_PROMPT, 2, 56, 20, 12 EDITTEXT IDC_PINENT_TEXT, 30, 56, 100, 12, ES_PASSWORD | ES_AUTOHSCROLL DEFPUSHBUTTON "O&K", IDOK, 2, 70, 50, 14 PUSHBUTTON "&Cancel", IDCANCEL, 60, 70, 50, 14 END #else /* Standard Windows. */ IDB_ICON_32 BITMAP DISCARDABLE "logo-32.bmp" IDB_ICON_48 BITMAP DISCARDABLE "logo-48.bmp" IDB_ICON_64 BITMAP DISCARDABLE "logo-64.bmp" IDB_ICON_96 BITMAP DISCARDABLE "logo-96.bmp" IDB_ICON_128 BITMAP DISCARDABLE "logo-128.bmp" IDD_PINENT DIALOG DISCARDABLE 0, 0, 186, 116 STYLE DS_MODALFRAME | DS_SYSMODAL | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Pinentry" FONT 10, "MS Sans Serif" BEGIN CONTROL "", IDC_PINENT_ICON, "Static", SS_BITMAP|SS_CENTERIMAGE, 4, 6, 32, 32 LTEXT "", IDC_PINENT_DESC, 40, 6, 140, 50 RTEXT "", IDC_PINENT_PROMPT, 6, 60, 60, 12 EDITTEXT IDC_PINENT_TEXT, 70, 59, 110, 12, ES_PASSWORD | ES_AUTOHSCROLL CTEXT "", IDC_PINENT_ERR, 6, 76, 174, 12 DEFPUSHBUTTON "O&K", IDOK, 74, 96, 50, 14 PUSHBUTTON "&Cancel", IDCANCEL, 130, 96, 50, 14 END #endif /* Standard Windows. */ diff --git a/w32/resource.h b/w32/resource.h index a8a285e..4311bf3 100644 --- a/w32/resource.h +++ b/w32/resource.h @@ -1,24 +1,25 @@ /* resource.h * Copyright 2004 g10 Code GmbH * * This file is free software; as a special exception the author gives * unlimited permission to copy and/or distribute it, with or without * modifications, as long as this notice is preserved. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * SPDX-License-Identifier: FSFULLR */ #define IDD_PINENT 101 #define IDC_PINENT_TEXT 292 #define IDC_PINENT_DESC 1000 #define IDC_PINENT_ERR 1001 #define IDC_PINENT_PROMPT 1002 #define IDC_PINENT_ICON 1003 #define IDB_ICON_32 3101 #define IDB_ICON_48 3102 #define IDB_ICON_64 3103 #define IDB_ICON_96 3104 #define IDB_ICON_128 3105