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