diff --git a/configure.ac b/configure.ac index b4223e9a..1b607e0b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,907 +1,908 @@ # configure.ac for GPGME # Copyright (C) 2000 Werner Koch (dd9jn) # Copyright (C) 2001-2018 g10 Code GmbH # # This file is part of GPGME. # # GPGME is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2.1 of the # License, or (at your option) any later version. # # GPGME 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 Lesser General # Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, see . # SPDX-License-Identifier: LGPL-2.1-or-later # (Process this file with autoconf to produce a configure script.) AC_PREREQ(2.59) min_automake_version="1.14" # To build a release you need to create a tag with the version number # (git tag -s gpgme-n.m.k) and run "./autogen.sh --force". Please # bump the version number immediately after the release and do another # commit and push so that the git magic is able to work. See below # for the LT versions. m4_define([mym4_package],[gpgme]) m4_define([mym4_major], [1]) m4_define([mym4_minor], [12]) m4_define([mym4_micro], [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_isbeta). Note that the # m4 processing is done by autoconf and not during the configure run. m4_define([mym4_verslist], m4_split(m4_esyscmd([./autogen.sh --find-version] \ mym4_package mym4_major mym4_minor mym4_micro),[:])) m4_define([mym4_isbeta], m4_argn(2, mym4_verslist)) m4_define([mym4_version], m4_argn(4, mym4_verslist)) m4_define([mym4_revision], m4_argn(7, mym4_verslist)) m4_define([mym4_revision_dec], m4_argn(8, mym4_verslist)) m4_esyscmd([echo ]mym4_version[>VERSION]) AC_INIT([mym4_package],[mym4_version], [https://bugs.gnupg.org]) # LT Version numbers, remember to change them just *before* a release. # (Code changed: REVISION++) # (Interfaces added/removed/changed: CURRENT++, REVISION=0) # (Interfaces added: AGE++) # (Interfaces removed: AGE=0) # LIBGPGME_LT_CURRENT=32 LIBGPGME_LT_AGE=21 LIBGPGME_LT_REVISION=0 # If there is an ABI break in gpgmepp or qgpgme also bump the # version in IMPORTED_LOCATION in the GpgmeppConfig-w32.cmake.in.in LIBGPGMEPP_LT_CURRENT=14 LIBGPGMEPP_LT_AGE=8 LIBGPGMEPP_LT_REVISION=0 LIBQGPGME_LT_CURRENT=10 LIBQGPGME_LT_AGE=3 LIBQGPGME_LT_REVISION=2 ################################################ AC_SUBST(LIBGPGME_LT_CURRENT) AC_SUBST(LIBGPGME_LT_AGE) AC_SUBST(LIBGPGME_LT_REVISION) AC_SUBST(LIBGPGMEPP_LT_CURRENT) AC_SUBST(LIBGPGMEPP_LT_AGE) AC_SUBST(LIBGPGMEPP_LT_REVISION) AC_SUBST(LIBQGPGME_LT_CURRENT) AC_SUBST(LIBQGPGME_LT_AGE) AC_SUBST(LIBQGPGME_LT_REVISION) # If the API is changed in an incompatible way: increment the next counter. GPGME_CONFIG_API_VERSION=1 ############################################## NEED_GPG_ERROR_VERSION=1.24 NEED_LIBASSUAN_API=2 NEED_LIBASSUAN_VERSION=2.4.2 VERSION_MAJOR=mym4_major VERSION_MINOR=mym4_minor VERSION_MICRO=mym4_micro AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR(src/gpgme.h.in) AC_CONFIG_HEADER(conf/config.h) AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip]) AM_MAINTAINER_MODE AC_CANONICAL_HOST AM_SILENT_RULES AC_ARG_VAR(SYSROOT,[locate config scripts also below that directory]) # Enable GNU extensions on systems that have them. AC_GNU_SOURCE AH_VERBATIM([_REENTRANT], [/* To allow the use of GPGME in multithreaded programs we have to use special features from the library. IMPORTANT: gpgme is not yet fully reentrant and you should use it only from one thread. */ #ifndef _REENTRANT # define _REENTRANT 1 #endif]) AC_PROG_CC AC_PROG_CPP AC_PROG_CXX # Note: A suitable gitlog-to-changelog script can be found in GnuPG master. AC_CHECK_PROGS(GITLOG_TO_CHANGELOG, gitlog-to-changelog, [gitlog-to-changelog]) AC_SUBST(VERSION_MAJOR) AC_SUBST(VERSION_MINOR) AC_SUBST(VERSION_MICRO) VERSION_NUMBER=m4_esyscmd(printf "0x%02x%02x%02x" mym4_major \ mym4_minor mym4_micro) AC_SUBST(VERSION_NUMBER) # We need to compile and run a program on the build machine. A # comment in libgpg-error says that the AC_PROG_CC_FOR_BUILD macro in # the AC archive is broken for autoconf 2.57. Given that there is no # newer version of that macro, we assume that it is also broken for # autoconf 2.61 and thus we use a simple but usually sufficient # approach. AC_MSG_CHECKING(for cc for build) if test "$cross_compiling" = "yes"; then CC_FOR_BUILD="${CC_FOR_BUILD-cc}" else CC_FOR_BUILD="${CC_FOR_BUILD-$CC}" fi AC_MSG_RESULT($CC_FOR_BUILD) AC_ARG_VAR(CC_FOR_BUILD,[build system C compiler]) # Don't default to build static libs. LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) # For now we hardcode the use of version scripts. It would be better # to write a test for this or even implement this within libtool. have_ld_version_script=no case "${host}" in *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; *-apple-darwin*) AC_DEFINE(_DARWIN_C_SOURCE, 900000L, Expose all libc features (__DARWIN_C_FULL).) AC_DEFINE(_XOPEN_SOURCE, 500, Activate POSIX interface on MacOS X) ;; esac AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") GPG_DEFAULT=no GPGSM_DEFAULT=no GPGCONF_DEFAULT=no G13_DEFAULT=no component_system=None have_dosish_system=no have_android_system=no have_w32_system=no have_w64_system=no have_macos_system=no build_w32_glib=no build_w32_qt=no available_languages="cl cpp python qt" default_languages="cl cpp python qt" case "${host}" in x86_64-*mingw32*) have_w64_system=yes ;; *-linux-androideabi) have_android_system=yes ;; *-apple-darwin*) have_macos_system=yes ;; esac case "${host}" in *-mingw32*) have_dosish_system=yes have_w32_system=yes GPG_DEFAULT='c:\\gnupg\\gpg.exe' GPGSM_DEFAULT='c:\\gnupg\\gpgsm.exe' GPGCONF_DEFAULT='c:\\gnupg\\gpgconf.exe' G13_DEFAULT='c:\\gnupg\\g13.exe' #component_system='COM+' AM_PATH_GLIB_2_0 AC_ARG_ENABLE(w32-glib, AC_HELP_STRING([--enable-w32-glib], [build GPGME Glib for W32]), build_w32_glib=$enableval) ;; *) # XXX: Probably use exec-prefix here? # GPG_DEFAULT='/usr/bin/gpg' # GPGSM_DEFAULT='/usr/bin/gpgsm' # GPGCONF_DEFAULT='/usr/bin/gpgconf' # G13_DEFAULT='/usr/bin/g13' ;; 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 any kind of W32 API based system]) fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) if test "$have_w64_system" = yes; then AC_DEFINE(HAVE_W64_SYSTEM,1, [Defined if we run on a 64 bit W32 API based system]) fi AM_CONDITIONAL(HAVE_W64_SYSTEM, test "$have_w64_system" = yes) if test "$have_android_system" = yes; then AC_DEFINE(HAVE_ANDROID_SYSTEM,1, [Defined if we build for an Android system]) fi AM_CONDITIONAL(HAVE_ANDROID_SYSTEM, test "$have_android_system" = yes) if test "$have_macos_system" = yes; then AC_DEFINE(HAVE_MACOS_SYSTEM,1, [Defined if we build for an MacOS system]) fi AM_CONDITIONAL(HAVE_MACOS_SYSTEM, test "$have_macos_system" = yes) AM_CONDITIONAL(BUILD_W32_GLIB, test "$build_w32_glib" = yes) AC_ARG_ENABLE([fixed-path], AC_HELP_STRING([--enable-fixed-path=PATH], [locate binaries only via this PATH]), [fixed_search_path="$enableval"], [fixed_search_path=""]) if test x$fixed_search_path != x ; then AC_DEFINE_UNQUOTED(FIXED_SEARCH_PATH, "$fixed_search_path", [Locate binaries only via this PATH]) fi # Note: You need to declare all possible languages also in # lang/Makefile.am's DIST_SUBDIRS. AC_ARG_ENABLE([languages], AC_HELP_STRING([--enable-languages=languages], [enable only specific language bindings]), [enabled_languages=`echo $enableval | \ tr ',:' ' ' | tr '[A-Z]' '[a-z]' | \ sed 's/c++/cpp/'`], [enabled_languages="maybe"]) if test "x$enabled_languages" = "x" \ -o "$enabled_languages" = "no"; then enabled_languages= fi # If languages are explicitly set missing requirements # for the languages are treated as errors otherwise # there will be a warning. explicit_languages=1 if test "x$enabled_languages" = "xmaybe"; then explicit_languages=0 enabled_languages="$default_languages" fi for language in $enabled_languages; do LIST_MEMBER($language, $available_languages) if test "$found" = "0"; then AC_MSG_ERROR([unsupported language binding specified]) fi done # Enable C++ 11 if cpp language is requested LIST_MEMBER("cpp", $enabled_languages) if test "$found" = "1"; then AX_CXX_COMPILE_STDCXX(11, noext, optional) if test "$HAVE_CXX11" != "1"; then if test "$explicit_languages" = "1"; then AC_MSG_ERROR([[ *** *** A compiler with c++11 support is required for the c++ binding. ***]]) else enabled_languages=$(echo $enabled_languages | sed 's/cpp//') enabled_languages=$(echo $enabled_languages | sed 's/qt//') AC_MSG_WARN([[ *** *** No c++11 support detected. C++ and Qt bindings will be disabled. ***]]) fi fi fi # Check that if qt is enabled cpp also is enabled LIST_MEMBER("qt", $enabled_languages) if test "$found" = "1"; then # We need to ensure that in the language order qt comes after cpp # so we remove qt first and explicitly add it as last list member. enabled_languages=$(echo $enabled_languages | sed 's/qt//') LIST_MEMBER("cpp", $enabled_languages) if test "$found" = "0"; then AC_MSG_ERROR([[ *** *** Qt language binding depends on cpp binding. ***]]) fi FIND_QT if test "$have_qt5_libs" != "yes"; then if test "$explicit_languages" = "1"; then AC_MSG_ERROR([[ *** *** Qt5 (Qt5Core) is required for Qt binding. ***]]) else AC_MSG_WARN([[ *** *** Qt5 (Qt5Core) not found Qt Binding will be disabled. ***]]) fi else enabled_languages=`echo $enabled_languages qt` AC_CHECK_PROGS([DOXYGEN], [doxygen]) if test -z "$DOXYGEN"; # This is not highlighted because it's not really important. then AC_MSG_WARN([Doxygen not found - Qt binding doc will not be built.]) fi AC_CHECK_PROGS([GRAPHVIZ], [dot]) if test -z "$GRAPHVIZ"; then AC_MSG_WARN([Graphviz not found - Qt binding doc will not have diagrams.]) fi fi fi AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) if test -n "$GRAPHVIZ"; then HAVE_DOT="YES" else HAVE_DOT="NO" fi AC_SUBST(HAVE_DOT) # Python bindings. LIST_MEMBER("python", $enabled_languages) found_py=$found if test "$found_py" = "1"; then AX_PKG_SWIG if test -z "$SWIG"; then if test "$explicit_languages" = "1"; then AC_MSG_ERROR([[ *** *** You need SWIG to build the Python bindings. ***]]) else enabled_languages=$(echo $enabled_languages | sed 's/python//') fi else # Reset the version collecting vars. PYTHONS= PYTHON_VERSIONS= if test "$found_py" = "1" -o "$found_py3" = "1"; then # Reset everything, so that we can look for another Python. m4_foreach([mym4pythonver], [[2.7],[3.4],[3.5],[3.6],[3.7],[3.8],[all]], [unset PYTHON unset PYTHON_VERSION unset PYTHON_CPPFLAGS unset PYTHON_LDFLAGS unset PYTHON_SITE_PKG unset PYTHON_EXTRA_LIBS unset PYTHON_EXTRA_LDFLAGS unset ac_cv_path_PYTHON unset am_cv_pathless_PYTHON unset am_cv_python_version unset am_cv_python_platform unset am_cv_python_pythondir unset am_cv_python_pyexecdir AM_PATH_PYTHON(mym4pythonver, [ AX_PYTHON_DEVEL if test "$PYTHON_VERSION"; then PYTHONS="$(echo $PYTHONS $PYTHON)" PYTHON_VERSIONS="$(echo $PYTHON_VERSIONS $PYTHON_VERSION)" fi ], :, m4_if([mym4pythonver],[all],[],[python]mym4pythonver)) ]) fi # Recover some values lost in the second attempt to find Python. PYTHON="$(echo $PYTHONS | cut -d ' ' -f 1)" PYTHON_VERSION="$(echo $PYTHON_VERSIONS | cut -d ' ' -f 1)" # Remove duplicates. PYTHONS="$(echo $PYTHONS | tr '[[:space:]]' '\n' | sort | uniq | tr '\n' ' ' | sed -e 's/ $//')" PYTHON_VERSIONS="$(echo $PYTHON_VERSIONS | tr '[[:space:]]' '\n' | sort | uniq | tr '\n' ' ' | sed -e 's/ $//')" if test "$PYTHON_VERSIONS"; then enabled_languages_v=$(echo $enabled_languages | sed -Ee "s/python[[23]]?/python ($PYTHON_VERSIONS)/") enabled_languages=$(echo $enabled_languages | sed -Ee "s/python[[23]]?/python/") else if test "$explicit_languages" = "1"; then AC_MSG_ERROR([[ *** *** Please install the python development packages. ***]]) else enabled_languages=$(echo $enabled_languages | sed 's/python//') fi fi AC_SUBST(PYTHONS, $PYTHONS) fi fi AC_SUBST(ENABLED_LANGUAGES, $enabled_languages) # # Provide information about the build. # BUILD_REVISION="mym4_revision" AC_SUBST(BUILD_REVISION) AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION", [GIT commit id revision used to build this package]) changequote(,)dnl BUILD_VERSION=`echo "$PACKAGE_VERSION" | sed 's/\([0-9.]*\).*/\1./'` changequote([,])dnl BUILD_VERSION="${BUILD_VERSION}mym4_revision_dec" BUILD_FILEVERSION=`echo "${BUILD_VERSION}" | tr . ,` AC_SUBST(BUILD_VERSION) AC_SUBST(BUILD_FILEVERSION) AC_ARG_ENABLE([build-timestamp], AC_HELP_STRING([--enable-build-timestamp], [set an explicit build timestamp for reproducibility. (default is the current time in ISO-8601 format)]), [if test "$enableval" = "yes"; then BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date` else BUILD_TIMESTAMP="$enableval" fi], [BUILD_TIMESTAMP=""]) AC_SUBST(BUILD_TIMESTAMP) AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP", [The time this package was configured for a build]) # # Options to disable some regression tests # run_gpgconf_test="yes" AC_ARG_ENABLE(gpgconf-test, AC_HELP_STRING([--disable-gpgconf-test], [disable GPGCONF regression test]), run_gpgconf_test=$enableval) AM_CONDITIONAL(RUN_GPGCONF_TESTS, test "$run_gpgconf_test" = "yes") run_gpg_test="yes" AC_ARG_ENABLE(gpg-test, AC_HELP_STRING([--disable-gpg-test], [disable GPG regression test]), run_gpg_test=$enableval) AM_CONDITIONAL(RUN_GPG_TESTS, test "$run_gpg_test" = "yes") run_gpgsm_test="yes" AC_ARG_ENABLE(gpgsm-test, AC_HELP_STRING([--disable-gpgsm-test], [disable GPGSM regression test]), run_gpgsm_test=$enableval) AM_CONDITIONAL(RUN_GPGSM_TESTS, test "$run_gpgsm_test" = "yes") run_g13_test="yes" AC_ARG_ENABLE(g13-test, AC_HELP_STRING([--disable-g13-test], [disable G13 regression test]), run_g13_test=$enableval) AM_CONDITIONAL(RUN_G13_TESTS, test "$run_g13_test" = "yes") # Checks for header files. AC_CHECK_HEADERS_ONCE([locale.h sys/select.h sys/uio.h argp.h stdint.h unistd.h sys/time.h sys/types.h sys/stat.h]) # Type checks. AC_C_INLINE AC_CHECK_SIZEOF(unsigned int) AC_SYS_LARGEFILE AC_TYPE_OFF_T AC_TYPE_UINTPTR_T # We require uint64_t if test "$ac_cv_header_stdint_h" != yes; then AC_MSG_ERROR([[ *** *** No stdint.h and thus no uint64_t type. Can't build this library. ***]]) fi # A simple compile time check in gpgme.h for GNU/Linux systems that # prevents a file offset bits mismatch between gpgme and the application. NEED__FILE_OFFSET_BITS=0 if test "$have_w32_system" != yes; then case "$ac_cv_sys_file_offset_bits" in "" | no | unknown) ;; *) NEED__FILE_OFFSET_BITS=$ac_cv_sys_file_offset_bits ;; esac fi AC_SUBST(NEED__FILE_OFFSET_BITS) # Figure out platform dependent typedefs for gpgme.h if test "$have_w32_system" = yes; then INSERT__TYPEDEFS_FOR_GPGME_H=" #ifdef _WIN64 # include typedef int64_t gpgme_off_t; typedef int64_t gpgme_ssize_t; #else /* _WIN32 */ typedef long gpgme_off_t; typedef long gpgme_ssize_t; #endif /* _WIN32 */" API__OFF_T="gpgme_off_t" API__SSIZE_T="gpgme_ssize_t" else INSERT__TYPEDEFS_FOR_GPGME_H=" #include typedef off_t gpgme_off_t; typedef ssize_t gpgme_ssize_t;" API__OFF_T="off_t" API__SSIZE_T="ssize_t" fi AC_SUBST(INSERT__TYPEDEFS_FOR_GPGME_H) AM_SUBST_NOTMAKE(INSERT__TYPEDEFS_FOR_GPGME_H) AC_SUBST(API__OFF_T) AM_SUBST_NOTMAKE(API__OFF_T) AC_SUBST(API__SSIZE_T) AM_SUBST_NOTMAKE(API__SSIZE_T) # Checks for compiler features. if test "$GCC" = yes; then CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes" if test "$USE_MAINTAINER_MODE" = "yes"; then CFLAGS="$CFLAGS -Wformat -Wno-format-y2k -Wformat-security" # If -Wno-missing-field-initializers is supported we can enable a # a bunch of really useful warnings. 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_wopt=yes,_gcc_wopt=no) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -W -Wextra -Wbad-function-cast" CFLAGS="$CFLAGS -Wwrite-strings" CFLAGS="$CFLAGS -Wdeclaration-after-statement" CFLAGS="$CFLAGS -Wno-missing-field-initializers" CFLAGS="$CFLAGS -Wno-sign-compare" + CFLAGS="$CFLAGS -Wno-format-zero-length" fi CXXFLAGS="$CXXFLAGS -Wall -Wextra -Wno-shadow" AC_MSG_CHECKING([if gcc supports -Wpointer-arith]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wpointer-arith" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -Wpointer-arith" fi fi if test "$have_w32_system" = yes; then CFLAGS="$CFLAGS -mms-bitfields" fi fi # Only used for debugging, so no serious test needed (for actual # functionality you have to test libc as well, this only tests the # compiler). AC_CACHE_CHECK([for __thread],[gpgme_cv_tls_works], AC_COMPILE_IFELSE([AC_LANG_PROGRAM([__thread int foo;])], gpgme_cv_tls_works=yes,gpgme_cv_tls_works=no)) if test "$gpgme_cv_tls_works" = yes; then AC_DEFINE(HAVE_TLS, [1], [Define if __thread is supported]) fi # Checks for library functions. AC_MSG_NOTICE([checking for libraries]) AC_FUNC_FSEEKO # Try to find a thread-safe version of ttyname(). gnupg_REPLACE_TTYNAME_R if test "$ac_cv_func_ttyname_r" != yes; then AC_MSG_WARN([ *** *** ttyname() is not thread-safe and ttyname_r() does not exist ***]) fi # Try to find a thread-safe version of getenv(). have_thread_safe_getenv=no jm_GLIBC21 if test $GLIBC21 = yes -o $have_w32_system = yes; then have_thread_safe_getenv=yes fi if test $have_thread_safe_getenv = yes; then AC_DEFINE(HAVE_THREAD_SAFE_GETENV, [1], [Define if getenv() is thread-safe]) fi have_getenv_r=no AC_CHECK_FUNCS(getenv_r, have_getenv_r=yes) if test $have_getenv_r = no && test $have_thread_safe_getenv = no; then AC_MSG_WARN([ *** *** getenv() is not thread-safe and getenv_r() does not exist ***]) fi # For converting time strings to seconds since Epoch, we need the timegm # function. AC_CHECK_FUNCS(timegm) if test "$ac_cv_func_timegm" != yes; then AC_MSG_WARN([ *** *** timegm() not available - a non-thread-safe kludge will be used *** and the TZ variable might be changed at runtime. ***]) fi AC_CHECK_FUNCS(setlocale) # Checking for libgpg-error. have_gpg_error=no AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION", have_gpg_error=yes, have_gpg_error=no) AC_DEFINE(GPG_ERR_SOURCE_DEFAULT, GPG_ERR_SOURCE_GPGME, [The default error source for GPGME.]) # And for libassuan. 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(GPGME_LIBASSUAN_VERSION, "$libassuan_version", [version of the libassuan library]) fi # # Other checks # # Check for getgid etc AC_CHECK_FUNCS(getgid getegid closefrom) # Replacement functions. AC_REPLACE_FUNCS(stpcpy) AC_REPLACE_FUNCS(setenv) # Assuan check for descriptor passing. AC_CHECK_MEMBER(struct cmsghdr.cmsg_len, [supports_descriptor_passing=yes], [supports_descriptor_passing=no AC_MSG_WARN([ *** *** Data structure for sending ancillary data missing. *** Descriptor passing won't work. ***])],[ #include #include #include #include #include #include #if HAVE_SYS_UIO_H #include #endif #include ]) use_descriptor_passing=yes AC_ARG_ENABLE(fd-passing, AC_HELP_STRING([--disable-fd-passing], [do not use FD passing]), use_descriptor_passing=$enableval) if test "$supports_descriptor_passing" != "yes"; then use_descriptor_passing=no fi if test "$use_descriptor_passing" = "yes"; then AC_DEFINE(USE_DESCRIPTOR_PASSING,1, [Defined if descriptor passing is enabled and supported]) fi AM_CONDITIONAL(USE_DESCRIPTOR_PASSING, test "$use_descriptor_passing" = "yes") uiserver=no if test "$use_descriptor_passing" = "yes" && test "$have_libassuan" = "yes"; then uiserver=yes fi if test "$uiserver" != "no"; then AC_DEFINE(ENABLE_UISERVER, 1, [Defined if we are building with uiserver support.]) fi AM_CONDITIONAL(HAVE_UISERVER, test "$uiserver" != "no") # Option --disable-linux-getdents # # By default we use SYS_getdents on Linux to optimize fd closing # before an exec. This option allows to switch this optimization off. use_linux_getdents=yes AC_ARG_ENABLE(linux-getdents, AC_HELP_STRING([--disable-linux-getdents], [do not use SYS_getdents on Linux]), use_linux_getdents=$enableval) if test "$use_linux_getdents" = "yes"; then case "${host}" in *-*-linux*) AC_DEFINE(USE_LINUX_GETDENTS,1, [Defined if SYS_getdents can be used on Linux]) ;; esac fi # # Add a few constants to help porting to W32 # AH_VERBATIM([SEPCONSTANTS], [ /* Separators as used in $PATH and file name. */ #ifdef HAVE_DOSISH_SYSTEM #define PATHSEP_C ';' #define DIRSEP_C '\\' #define DIRSEP_S "\\" #else #define PATHSEP_C ':' #define DIRSEP_C '/' #define DIRSEP_S "/" #endif ]) AH_BOTTOM([ /* Definition of GCC specific attributes. */ #if __GNUC__ > 2 # define GPGME_GCC_A_PURE __attribute__ ((__pure__)) #else # define GPGME_GCC_A_PURE #endif /* Under WindowsCE we need gpg-error's strerror macro. */ #define GPG_ERR_ENABLE_ERRNO_MACROS 1 #define CRIGHTBLURB "Copyright (C) 2000 Werner Koch\n" \ "Copyright (C) 2001--2018 g10 Code GmbH\n" ]) # Substitution used for gpgme-config GPGME_CONFIG_LIBS="-lgpgme" GPGME_CONFIG_CFLAGS="" GPGME_CONFIG_HOST="$host" GPGME_CONFIG_AVAIL_LANG="$enabled_languages" AC_SUBST(GPGME_CONFIG_API_VERSION) AC_SUBST(GPGME_CONFIG_LIBS) AC_SUBST(GPGME_CONFIG_CFLAGS) AC_SUBST(GPGME_CONFIG_HOST) AC_SUBST(GPGME_CONFIG_AVAIL_LANG) # Frob'da Variables LTLIBOBJS=`echo "$LIB@&t@OBJS" | sed 's,\.[[^.]]* ,.lo ,g;s,\.[[^.]]*$,.lo,'` AC_SUBST(LTLIBOBJS) # Some checks for gpgme-tool # Done at top: AC_CHECK_HEADER([argp.h]) AC_CHECK_TYPES([error_t], [], [AC_DEFINE([error_t], [int], [Define to a type to use for `error_t' if it is not otherwise available.])], [#include ]) # A substitution to set generated files in a Emacs buffer to read-only. AC_SUBST(emacs_local_vars_begin, [['Local][ ][Variables:']]) AC_SUBST(emacs_local_vars_read_only, ['buffer-read-only: t']) AC_SUBST(emacs_local_vars_end, ['End:']) # Last check. 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 *** https://www.gnupg.org/ftp/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 *** https://www.gnupg.org/ftp/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 # # Create config files AC_CONFIG_FILES(Makefile src/Makefile tests/Makefile tests/gpg/Makefile tests/gpgsm/Makefile tests/opassuan/Makefile tests/json/Makefile doc/Makefile src/versioninfo.rc src/gpgme.pc src/gpgme-glib.pc src/gpgme.h) AC_CONFIG_FILES(src/gpgme-config, chmod +x src/gpgme-config) AC_CONFIG_FILES(lang/cpp/Makefile lang/cpp/src/Makefile) AC_CONFIG_FILES(lang/cpp/tests/Makefile) AC_CONFIG_FILES(lang/cpp/src/GpgmeppConfig-w32.cmake.in) AC_CONFIG_FILES(lang/cpp/src/GpgmeppConfig.cmake.in) AC_CONFIG_FILES(lang/cpp/src/GpgmeppConfigVersion.cmake) AC_CONFIG_FILES(lang/cpp/src/gpgmepp_version.h) AC_CONFIG_FILES(lang/qt/Makefile lang/qt/src/Makefile) AC_CONFIG_FILES(lang/qt/src/QGpgmeConfig-w32.cmake.in) AC_CONFIG_FILES(lang/qt/src/QGpgmeConfig.cmake.in) AC_CONFIG_FILES(lang/qt/src/QGpgmeConfigVersion.cmake) AC_CONFIG_FILES(lang/qt/tests/Makefile) AC_CONFIG_FILES(lang/qt/src/qgpgme_version.h) AC_CONFIG_FILES([lang/Makefile lang/cl/Makefile lang/cl/gpgme.asd]) AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([lang/qt/doc/Doxyfile])]) AC_CONFIG_FILES([lang/js/Makefile lang/js/src/Makefile lang/js/BrowserTestExtension/Makefile lang/js/DemoExtension/Makefile]) AC_CONFIG_FILES(lang/qt/doc/Makefile) AC_CONFIG_FILES([lang/python/Makefile lang/python/version.py lang/python/tests/Makefile]) AC_CONFIG_FILES([lang/python/setup.py], [chmod a+x lang/python/setup.py]) AC_OUTPUT echo " GPGME v${VERSION} has been configured as follows: Revision: mym4_revision (mym4_revision_dec) Platform: $host UI Server: $uiserver FD Passing: $use_descriptor_passing Language bindings: ${enabled_languages_v:-$enabled_languages} " if test "x${gpg_config_script_warn}" != x; then cat <. * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #include #include "data.h" #include "util.h" #include "debug.h" /* Create a new data buffer filled with LENGTH bytes starting from OFFSET within the file FNAME or stream STREAM (exactly one must be non-zero). */ gpgme_error_t gpgme_data_new_from_filepart (gpgme_data_t *r_dh, const char *fname, FILE *stream, gpgme_off_t offset, size_t length) { gpgme_error_t err; char *buf = NULL; int res; - TRACE_BEG4 (DEBUG_DATA, "gpgme_data_new_from_filepart", r_dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_filepart", r_dh, "file_name=%s, stream=%p, offset=%lli, length=%u", fname, stream, offset, length); if (stream && fname) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (fname) stream = fopen (fname, "rb"); if (!stream) return TRACE_ERR (gpg_error_from_syserror ()); #ifdef HAVE_FSEEKO res = fseeko (stream, offset, SEEK_SET); #else /* FIXME: Check for overflow, or at least bail at compilation. */ res = fseek (stream, offset, SEEK_SET); #endif if (res) { int saved_err = gpg_error_from_syserror (); if (fname) fclose (stream); return TRACE_ERR (saved_err); } buf = malloc (length); if (!buf) { int saved_err = gpg_error_from_syserror (); if (fname) fclose (stream); return TRACE_ERR (saved_err); } while (fread (buf, length, 1, stream) < 1 && ferror (stream) && errno == EINTR); if (ferror (stream)) { int saved_err = gpg_error_from_syserror (); if (buf) free (buf); if (fname) fclose (stream); return TRACE_ERR (saved_err); } if (fname) fclose (stream); err = gpgme_data_new (r_dh); if (err) { if (buf) free (buf); return err; } (*r_dh)->data.mem.buffer = buf; (*r_dh)->data.mem.size = length; (*r_dh)->data.mem.length = length; - return TRACE_SUC1 ("r_dh=%p", *r_dh); + return TRACE_SUC ("r_dh=%p", *r_dh); } /* Create a new data buffer filled with the content of file FNAME. COPY must be non-zero (delayed reads are not supported yet). */ gpgme_error_t gpgme_data_new_from_file (gpgme_data_t *r_dh, const char *fname, int copy) { gpgme_error_t err; struct stat statbuf; - TRACE_BEG3 (DEBUG_DATA, "gpgme_data_new_from_file", r_dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_file", r_dh, "file_name=%s, copy=%i (%s)", fname, copy, copy ? "yes" : "no"); if (!fname || !copy) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (stat (fname, &statbuf) < 0) return TRACE_ERR (gpg_error_from_syserror ()); err = gpgme_data_new_from_filepart (r_dh, fname, NULL, 0, statbuf.st_size); return TRACE_ERR (err); } static int gpgme_error_to_errno (gpgme_error_t err) { int res = gpg_err_code_to_errno (gpg_err_code (err)); if (!err) { switch (gpg_err_code (err)) { case GPG_ERR_EOF: res = 0; break; case GPG_ERR_INV_VALUE: res = EINVAL; break; case GPG_ERR_NOT_SUPPORTED: res = ENOSYS; break; default: /* FIXME: Yeah, well. */ res = EINVAL; break; } } - TRACE3 (DEBUG_DATA, "gpgme:gpgme_error_to_errno", 0, + TRACE (DEBUG_DATA, "gpgme:gpgme_error_to_errno", 0, "mapping %s <%s> to: %s", gpgme_strerror (err), gpgme_strsource (err), strerror (res)); gpg_err_set_errno (res); return res ? -1 : 0; } static gpgme_ssize_t old_user_read (gpgme_data_t dh, void *buffer, size_t size) { gpgme_error_t err; size_t amt; - TRACE_BEG2 (DEBUG_DATA, "gpgme:old_user_read", dh, + TRACE_BEG (DEBUG_DATA, "gpgme:old_user_read", dh, "buffer=%p, size=%u", buffer, size); err = (*dh->data.old_user.cb) (dh->data.old_user.handle, buffer, size, &amt); if (err) return TRACE_SYSRES (gpgme_error_to_errno (err)); return TRACE_SYSRES ((gpgme_ssize_t)amt); } static gpgme_off_t old_user_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_DATA, "gpgme:old_user_seek", dh, + TRACE_BEG (DEBUG_DATA, "gpgme:old_user_seek", dh, "offset=%llu, whence=%i", offset, whence); if (whence != SEEK_SET || offset) { gpg_err_set_errno (EINVAL); return TRACE_SYSRES (-1); } err = (*dh->data.old_user.cb) (dh->data.old_user.handle, NULL, 0, NULL); if (err) return TRACE_SYSRES (gpgme_error_to_errno (err)); return TRACE_SYSRES (0); } static struct _gpgme_data_cbs old_user_cbs = { old_user_read, NULL, old_user_seek, NULL }; /* Create a new data buffer which retrieves the data from the callback function READ_CB. */ gpgme_error_t gpgme_data_new_with_read_cb (gpgme_data_t *r_dh, int (*read_cb) (void *, char *, size_t, size_t *), void *read_cb_value) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_DATA, "gpgme_data_new_with_read_cb", r_dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_new_with_read_cb", r_dh, "read_cb=%p/%p", read_cb, read_cb_value); err = _gpgme_data_new (r_dh, &old_user_cbs); if (err) return TRACE_ERR (err); (*r_dh)->data.old_user.cb = read_cb; (*r_dh)->data.old_user.handle = read_cb_value; return TRACE_ERR (0); } diff --git a/src/data-estream.c b/src/data-estream.c index fa11d664..1f1a64eb 100644 --- a/src/data-estream.c +++ b/src/data-estream.c @@ -1,99 +1,99 @@ /* data-stream.c - A stream based data object. * Copyright (C) 2002, 2004, 2018 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #if HAVE_CONFIG_H #include #endif #include #ifdef HAVE_SYS_TYPES_H # include #endif #include "debug.h" #include "data.h" static gpgme_ssize_t stream_es_read (gpgme_data_t dh, void *buffer, size_t size) { size_t amt = gpgrt_fread (buffer, 1, size, dh->data.e_stream); if (amt > 0) return amt; return gpgrt_ferror (dh->data.e_stream) ? -1 : 0; } static gpgme_ssize_t stream_es_write (gpgme_data_t dh, const void *buffer, size_t size) { size_t amt = gpgrt_fwrite (buffer, 1, size, dh->data.e_stream); if (amt > 0) return amt; return gpgrt_ferror (dh->data.e_stream) ? -1 : 0; } static gpgme_off_t stream_es_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { int err; err = gpgrt_fseeko (dh->data.e_stream, offset, whence); if (err) return -1; return gpgrt_ftello (dh->data.e_stream); } static int stream_es_get_fd (gpgme_data_t dh) { gpgrt_fflush (dh->data.e_stream); return gpgrt_fileno (dh->data.e_stream); } static struct _gpgme_data_cbs stream_es_cbs = { stream_es_read, stream_es_write, stream_es_seek, NULL, stream_es_get_fd }; gpgme_error_t gpgme_data_new_from_estream (gpgme_data_t *r_dh, gpgrt_stream_t stream) { gpgme_error_t err; - TRACE_BEG1 (DEBUG_DATA, "gpgme_data_new_from_estream", r_dh, "estream=%p", + TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_estream", r_dh, "estream=%p", stream); err = _gpgme_data_new (r_dh, &stream_es_cbs); if (err) return TRACE_ERR (err); (*r_dh)->data.e_stream = stream; - return TRACE_SUC1 ("dh=%p", *r_dh); + return TRACE_SUC ("dh=%p", *r_dh); } diff --git a/src/data-fd.c b/src/data-fd.c index b8739f2c..6a915fc0 100644 --- a/src/data-fd.c +++ b/src/data-fd.c @@ -1,86 +1,86 @@ /* data-fd.c - A file descriptor based data object. * Copyright (C) 2002, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include "debug.h" #include "data.h" static gpgme_ssize_t fd_read (gpgme_data_t dh, void *buffer, size_t size) { return read (dh->data.fd, buffer, size); } static gpgme_ssize_t fd_write (gpgme_data_t dh, const void *buffer, size_t size) { return write (dh->data.fd, buffer, size); } static gpgme_off_t fd_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { return lseek (dh->data.fd, offset, whence); } static int fd_get_fd (gpgme_data_t dh) { return (dh->data.fd); } static struct _gpgme_data_cbs fd_cbs = { fd_read, fd_write, fd_seek, NULL, fd_get_fd }; gpgme_error_t gpgme_data_new_from_fd (gpgme_data_t *r_dh, int fd) { gpgme_error_t err; - TRACE_BEG1 (DEBUG_DATA, "gpgme_data_new_from_fd", r_dh, "fd=0x%x", fd); + TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_fd", r_dh, "fd=0x%x", fd); err = _gpgme_data_new (r_dh, &fd_cbs); if (err) return TRACE_ERR (err); (*r_dh)->data.fd = fd; - return TRACE_SUC1 ("dh=%p", *r_dh); + return TRACE_SUC ("dh=%p", *r_dh); } diff --git a/src/data-mem.c b/src/data-mem.c index 2d19a99c..c461a606 100644 --- a/src/data-mem.c +++ b/src/data-mem.c @@ -1,305 +1,305 @@ /* data-mem.c - A memory based data object. * Copyright (C) 2002, 2003, 2004, 2007 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include "data.h" #include "util.h" #include "debug.h" static gpgme_ssize_t mem_read (gpgme_data_t dh, void *buffer, size_t size) { size_t amt = dh->data.mem.length - dh->data.mem.offset; const char *src; if (!amt) return 0; if (size < amt) amt = size; src = dh->data.mem.buffer ? dh->data.mem.buffer : dh->data.mem.orig_buffer; memcpy (buffer, src + dh->data.mem.offset, amt); dh->data.mem.offset += amt; return amt; } static gpgme_ssize_t mem_write (gpgme_data_t dh, const void *buffer, size_t size) { size_t unused; if (!dh->data.mem.buffer && dh->data.mem.orig_buffer) { size_t new_size = dh->data.mem.size; char *new_buffer; if (new_size < dh->data.mem.offset + size) new_size = dh->data.mem.offset + size; new_buffer = malloc (new_size); if (!new_buffer) return -1; memcpy (new_buffer, dh->data.mem.orig_buffer, dh->data.mem.length); dh->data.mem.buffer = new_buffer; dh->data.mem.size = new_size; } unused = dh->data.mem.size - dh->data.mem.offset; if (unused < size) { /* Allocate a large enough buffer with exponential backoff. */ #define INITIAL_ALLOC 512 size_t new_size = dh->data.mem.size ? (2 * dh->data.mem.size) : INITIAL_ALLOC; char *new_buffer; if (new_size < dh->data.mem.offset + size) new_size = dh->data.mem.offset + size; new_buffer = realloc (dh->data.mem.buffer, new_size); if (!new_buffer && new_size > dh->data.mem.offset + size) { /* Maybe we were too greedy, try again. */ new_size = dh->data.mem.offset + size; new_buffer = realloc (dh->data.mem.buffer, new_size); } if (!new_buffer) return -1; dh->data.mem.buffer = new_buffer; dh->data.mem.size = new_size; } memcpy (dh->data.mem.buffer + dh->data.mem.offset, buffer, size); dh->data.mem.offset += size; if (dh->data.mem.length < dh->data.mem.offset) dh->data.mem.length = dh->data.mem.offset; return size; } static gpgme_off_t mem_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { switch (whence) { case SEEK_SET: if (offset < 0 || offset > dh->data.mem.length) { gpg_err_set_errno (EINVAL); return -1; } dh->data.mem.offset = offset; break; case SEEK_CUR: if ((offset > 0 && dh->data.mem.length - dh->data.mem.offset < offset) || (offset < 0 && dh->data.mem.offset < -offset)) { gpg_err_set_errno (EINVAL); return -1; } dh->data.mem.offset += offset; break; case SEEK_END: if (offset > 0 || -offset > dh->data.mem.length) { gpg_err_set_errno (EINVAL); return -1; } dh->data.mem.offset = dh->data.mem.length + offset; break; default: gpg_err_set_errno (EINVAL); return -1; } return dh->data.mem.offset; } static void mem_release (gpgme_data_t dh) { if (dh->data.mem.buffer) free (dh->data.mem.buffer); } static struct _gpgme_data_cbs mem_cbs = { mem_read, mem_write, mem_seek, mem_release, NULL }; /* Create a new data buffer and return it in R_DH. */ gpgme_error_t gpgme_data_new (gpgme_data_t *r_dh) { gpgme_error_t err; - TRACE_BEG (DEBUG_DATA, "gpgme_data_new", r_dh); + TRACE_BEG (DEBUG_DATA, "gpgme_data_new", r_dh, ""); err = _gpgme_data_new (r_dh, &mem_cbs); if (err) return TRACE_ERR (err); - return TRACE_SUC1 ("dh=%p", *r_dh); + return TRACE_SUC ("dh=%p", *r_dh); } /* Create a new data buffer filled with SIZE bytes starting from BUFFER. If COPY is zero, copying is delayed until necessary, and the data is taken from the original location when needed. */ gpgme_error_t gpgme_data_new_from_mem (gpgme_data_t *r_dh, const char *buffer, size_t size, int copy) { gpgme_error_t err; - TRACE_BEG4 (DEBUG_DATA, "gpgme_data_new_from_mem", r_dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_mem", r_dh, "buffer=%p, size=%u, copy=%i (%s)", buffer, size, copy, copy ? "yes" : "no"); err = _gpgme_data_new (r_dh, &mem_cbs); if (err) return TRACE_ERR (err); if (copy) { char *bufcpy = malloc (size); if (!bufcpy) { int saved_err = gpg_error_from_syserror (); _gpgme_data_release (*r_dh); return TRACE_ERR (saved_err); } memcpy (bufcpy, buffer, size); (*r_dh)->data.mem.buffer = bufcpy; } else (*r_dh)->data.mem.orig_buffer = buffer; (*r_dh)->data.mem.size = size; (*r_dh)->data.mem.length = size; - return TRACE_SUC1 ("dh=%p", *r_dh); + return TRACE_SUC ("dh=%p", *r_dh); } /* Destroy the data buffer DH and return a pointer to its content. The memory has be to released with gpgme_free() by the user. It's size is returned in R_LEN. */ char * gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len) { gpg_error_t err; char *str = NULL; size_t len; int blankout; - TRACE_BEG1 (DEBUG_DATA, "gpgme_data_release_and_get_mem", dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_release_and_get_mem", dh, "r_len=%p", r_len); if (!dh || dh->cbs != &mem_cbs) { gpgme_data_release (dh); TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); return NULL; } err = _gpgme_data_get_prop (dh, 0, DATA_PROP_BLANKOUT, &blankout); if (err) { gpgme_data_release (dh); TRACE_ERR (err); return NULL; } str = dh->data.mem.buffer; len = dh->data.mem.length; if (blankout && len) len = 1; if (!str && dh->data.mem.orig_buffer) { str = malloc (len); if (!str) { int saved_err = gpg_error_from_syserror (); gpgme_data_release (dh); TRACE_ERR (saved_err); return NULL; } if (blankout) memset (str, 0, len); else memcpy (str, dh->data.mem.orig_buffer, len); } else { if (blankout && len) *str = 0; /* Prevent mem_release from releasing the buffer memory. We * must not fail from this point. */ dh->data.mem.buffer = NULL; } if (r_len) *r_len = len; gpgme_data_release (dh); if (r_len) { - TRACE_SUC2 ("buffer=%p, len=%u", str, *r_len); + TRACE_SUC ("buffer=%p, len=%u", str, *r_len); } else { - TRACE_SUC1 ("buffer=%p", str); + TRACE_SUC ("buffer=%p", str); } return str; } /* Release the memory returned by gpgme_data_release_and_get_mem() and some other functions. */ void gpgme_free (void *buffer) { - TRACE (DEBUG_DATA, "gpgme_free", buffer); + TRACE (DEBUG_DATA, "gpgme_free", buffer, ""); if (buffer) free (buffer); } diff --git a/src/data-stream.c b/src/data-stream.c index 8961d079..19a5a079 100644 --- a/src/data-stream.c +++ b/src/data-stream.c @@ -1,108 +1,108 @@ /* data-stream.c - A stream based data object. * Copyright (C) 2002, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #ifdef HAVE_SYS_TYPES_H # include #endif #include "debug.h" #include "data.h" static gpgme_ssize_t stream_read (gpgme_data_t dh, void *buffer, size_t size) { size_t amt = fread (buffer, 1, size, dh->data.stream); if (amt > 0) return amt; return ferror (dh->data.stream) ? -1 : 0; } static gpgme_ssize_t stream_write (gpgme_data_t dh, const void *buffer, size_t size) { size_t amt = fwrite (buffer, 1, size, dh->data.stream); if (amt > 0) return amt; return ferror (dh->data.stream) ? -1 : 0; } static gpgme_off_t stream_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { int err; #ifdef HAVE_FSEEKO err = fseeko (dh->data.stream, offset, whence); #else /* FIXME: Check for overflow, or at least bail at compilation. */ err = fseek (dh->data.stream, offset, whence); #endif if (err) return -1; #ifdef HAVE_FSEEKO return ftello (dh->data.stream); #else return ftell (dh->data.stream); #endif } static int stream_get_fd (gpgme_data_t dh) { fflush (dh->data.stream); return fileno (dh->data.stream); } static struct _gpgme_data_cbs stream_cbs = { stream_read, stream_write, stream_seek, NULL, stream_get_fd }; gpgme_error_t gpgme_data_new_from_stream (gpgme_data_t *r_dh, FILE *stream) { gpgme_error_t err; - TRACE_BEG1 (DEBUG_DATA, "gpgme_data_new_from_stream", r_dh, "stream=%p", + TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_stream", r_dh, "stream=%p", stream); err = _gpgme_data_new (r_dh, &stream_cbs); if (err) return TRACE_ERR (err); (*r_dh)->data.stream = stream; - return TRACE_SUC1 ("dh=%p", *r_dh); + return TRACE_SUC ("dh=%p", *r_dh); } diff --git a/src/data-user.c b/src/data-user.c index d1d85250..f5bd2efb 100644 --- a/src/data-user.c +++ b/src/data-user.c @@ -1,104 +1,104 @@ /* data-user.c - A user callback based data object. * Copyright (C) 2002, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #include "debug.h" #include "data.h" static gpgme_ssize_t user_read (gpgme_data_t dh, void *buffer, size_t size) { if (!dh->data.user.cbs->read) { gpg_err_set_errno (EBADF); return -1; } return (*dh->data.user.cbs->read) (dh->data.user.handle, buffer, size); } static gpgme_ssize_t user_write (gpgme_data_t dh, const void *buffer, size_t size) { if (!dh->data.user.cbs->write) { gpg_err_set_errno (EBADF); return -1; } return (*dh->data.user.cbs->write) (dh->data.user.handle, buffer, size); } static gpgme_off_t user_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { if (!dh->data.user.cbs->seek) { gpg_err_set_errno (EBADF); return -1; } return (*dh->data.user.cbs->seek) (dh->data.user.handle, offset, whence); } static void user_release (gpgme_data_t dh) { if (dh->data.user.cbs->release) (*dh->data.user.cbs->release) (dh->data.user.handle); } static struct _gpgme_data_cbs user_cbs = { user_read, user_write, user_seek, user_release, NULL }; gpgme_error_t gpgme_data_new_from_cbs (gpgme_data_t *r_dh, gpgme_data_cbs_t cbs, void *handle) { gpgme_error_t err; - TRACE_BEG1 (DEBUG_DATA, "gpgme_data_new_from_cbs", r_dh, "handle=%p", handle); + TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_cbs", r_dh, "handle=%p", handle); err = _gpgme_data_new (r_dh, &user_cbs); if (err) return TRACE_ERR (err); (*r_dh)->data.user.cbs = cbs; (*r_dh)->data.user.handle = handle; - return TRACE_SUC1 ("dh=%p", *r_dh); + return TRACE_SUC ("dh=%p", *r_dh); } diff --git a/src/data.c b/src/data.c index a786c7c7..f633e08c 100644 --- a/src/data.c +++ b/src/data.c @@ -1,663 +1,663 @@ /* data.c - An abstraction for data objects. * Copyright (C) 2002, 2003, 2004, 2005, 2007 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include "gpgme.h" #include "data.h" #include "util.h" #include "ops.h" #include "priv-io.h" #include "debug.h" /* The property table which has an entry for each active data object. * The data object itself uses an index into this table and the table * has a pointer back to the data object. All access to that table is * controlled by the property_table_lock. * * We use a separate table instead of linking all data objects * together for faster locating properties of the data object using * the data objects serial number. We use 64 bit for the serial * number which is good enough to create a new data object every * nanosecond for more than 500 years. Thus no wrap around will ever * happen. */ struct property_s { gpgme_data_t dh; /* The data objcet or NULL if the slot is not used. */ uint64_t dserial; /* The serial number of the data object. */ struct { unsigned int blankout : 1; /* Void the held data. */ } flags; }; typedef struct property_s *property_t; static property_t property_table; static unsigned int property_table_size; DEFINE_STATIC_LOCK (property_table_lock); #define PROPERTY_TABLE_ALLOCATION_CHUNK 32 /* Insert the newly created data object DH into the property table and * store the index of it at R_IDX. An error code is returned on error * and the table is not changed. */ static gpg_error_t insert_into_property_table (gpgme_data_t dh, unsigned int *r_idx) { static uint64_t last_dserial; gpg_error_t err; unsigned int idx; LOCK (property_table_lock); if (!property_table) { property_table_size = PROPERTY_TABLE_ALLOCATION_CHUNK; property_table = calloc (property_table_size, sizeof *property_table); if (!property_table) { err = gpg_error_from_syserror (); goto leave; } } /* Find an empty slot. */ for (idx = 0; idx < property_table_size; idx++) if (!property_table[idx].dh) break; if (!(idx < property_table_size)) { /* No empty slot found. Enlarge the table. */ property_t newtbl; unsigned int newsize; newsize = property_table_size + PROPERTY_TABLE_ALLOCATION_CHUNK;; if ((newsize * sizeof *property_table) < (property_table_size * sizeof *property_table)) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } newtbl = realloc (property_table, newsize * sizeof *property_table); if (!newtbl) { err = gpg_error_from_syserror (); goto leave; } property_table = newtbl; for (idx = property_table_size; idx < newsize; idx++) property_table[idx].dh = NULL; idx = property_table_size; property_table_size = newsize; } /* Slot found. */ property_table[idx].dh = dh; property_table[idx].dserial = ++last_dserial; memset (&property_table[idx].flags, 0, sizeof property_table[idx].flags); *r_idx = idx; err = 0; leave: UNLOCK (property_table_lock); return err; } /* Remove the data object at PROPIDX from the table. DH is only used * for cross checking. */ static void remove_from_property_table (gpgme_data_t dh, unsigned int propidx) { LOCK (property_table_lock); assert (property_table); assert (propidx < property_table_size); assert (property_table[propidx].dh == dh); property_table[propidx].dh = NULL; UNLOCK (property_table_lock); } /* Return the data object's serial number for handle DH. This is a * unique serial number for each created data object. */ uint64_t _gpgme_data_get_dserial (gpgme_data_t dh) { uint64_t dserial; unsigned int idx; if (!dh) return 0; idx = dh->propidx; LOCK (property_table_lock); assert (property_table); assert (idx < property_table_size); assert (property_table[idx].dh == dh); dserial = property_table[idx].dserial; UNLOCK (property_table_lock); return dserial; } /* Set an internal property of a data object. The data object may * either be identified by the usual DH or by using the data serial * number DSERIAL. */ gpg_error_t _gpgme_data_set_prop (gpgme_data_t dh, uint64_t dserial, data_prop_t name, int value) { gpg_error_t err = 0; int idx; - TRACE_BEG3 (DEBUG_DATA, "gpgme_data_set_prop", dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_set_prop", dh, "dserial=%llu %lu=%d", (unsigned long long)dserial, (unsigned long)name, value); LOCK (property_table_lock); if ((!dh && !dserial) || (dh && dserial)) { err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } if (dh) /* Lookup via handle. */ { idx = dh->propidx; assert (property_table); assert (idx < property_table_size); assert (property_table[idx].dh == dh); } else /* Lookup via DSERIAL. */ { if (!property_table) { err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } for (idx = 0; idx < property_table_size; idx++) if (property_table[idx].dh && property_table[idx].dserial == dserial) break; if (!(idx < property_table_size)) { err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } } switch (name) { case DATA_PROP_NONE: /* Nothing to to do. */ break; case DATA_PROP_BLANKOUT: property_table[idx].flags.blankout = !!value; break; default: err = gpg_error (GPG_ERR_UNKNOWN_NAME); break; } leave: UNLOCK (property_table_lock); return TRACE_ERR (err); } /* Get an internal property of a data object. This is the counter * part to _gpgme_data_set_property. The value of the property is * stored at R_VALUE. On error 0 is stored at R_VALUE. */ gpg_error_t _gpgme_data_get_prop (gpgme_data_t dh, uint64_t dserial, data_prop_t name, int *r_value) { gpg_error_t err = 0; int idx; - TRACE_BEG2 (DEBUG_DATA, "gpgme_data_get_prop", dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_get_prop", dh, "dserial=%llu %lu", (unsigned long long)dserial, (unsigned long)name); *r_value = 0; LOCK (property_table_lock); if ((!dh && !dserial) || (dh && dserial)) { err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } if (dh) /* Lookup via handle. */ { idx = dh->propidx; assert (property_table); assert (idx < property_table_size); assert (property_table[idx].dh == dh); } else /* Lookup via DSERIAL. */ { if (!property_table) { err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } for (idx = 0; idx < property_table_size; idx++) if (property_table[idx].dh && property_table[idx].dserial == dserial) break; if (!(idx < property_table_size)) { err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } } switch (name) { case DATA_PROP_NONE: /* Nothing to to do. */ break; case DATA_PROP_BLANKOUT: *r_value = property_table[idx].flags.blankout; break; default: err = gpg_error (GPG_ERR_UNKNOWN_NAME); break; } leave: UNLOCK (property_table_lock); return TRACE_ERR (err); } gpgme_error_t _gpgme_data_new (gpgme_data_t *r_dh, struct _gpgme_data_cbs *cbs) { gpgme_error_t err; gpgme_data_t dh; if (!r_dh) return gpg_error (GPG_ERR_INV_VALUE); *r_dh = NULL; if (_gpgme_selftest) return _gpgme_selftest; dh = calloc (1, sizeof (*dh)); if (!dh) return gpg_error_from_syserror (); dh->cbs = cbs; err = insert_into_property_table (dh, &dh->propidx); if (err) { free (dh); return err; } *r_dh = dh; return 0; } void _gpgme_data_release (gpgme_data_t dh) { if (!dh) return; remove_from_property_table (dh, dh->propidx); if (dh->file_name) free (dh->file_name); free (dh); } /* Read up to SIZE bytes into buffer BUFFER from the data object with the handle DH. Return the number of characters read, 0 on EOF and -1 on error. If an error occurs, errno is set. */ gpgme_ssize_t gpgme_data_read (gpgme_data_t dh, void *buffer, size_t size) { gpgme_ssize_t res; int blankout; - TRACE_BEG2 (DEBUG_DATA, "gpgme_data_read", dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_read", dh, "buffer=%p, size=%u", buffer, size); if (!dh) { gpg_err_set_errno (EINVAL); return TRACE_SYSRES (-1); } if (!dh->cbs->read) { gpg_err_set_errno (ENOSYS); return TRACE_SYSRES (-1); } if (_gpgme_data_get_prop (dh, 0, DATA_PROP_BLANKOUT, &blankout) || blankout) res = 0; else { do res = (*dh->cbs->read) (dh, buffer, size); while (res < 0 && errno == EINTR); } return TRACE_SYSRES (res); } /* Write up to SIZE bytes from buffer BUFFER to the data object with the handle DH. Return the number of characters written, or -1 on error. If an error occurs, errno is set. */ gpgme_ssize_t gpgme_data_write (gpgme_data_t dh, const void *buffer, size_t size) { gpgme_ssize_t res; - TRACE_BEG2 (DEBUG_DATA, "gpgme_data_write", dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_write", dh, "buffer=%p, size=%u", buffer, size); if (!dh) { gpg_err_set_errno (EINVAL); return TRACE_SYSRES (-1); } if (!dh->cbs->write) { gpg_err_set_errno (ENOSYS); return TRACE_SYSRES (-1); } do res = (*dh->cbs->write) (dh, buffer, size); while (res < 0 && errno == EINTR); return TRACE_SYSRES (res); } /* Set the current position from where the next read or write starts in the data object with the handle DH to OFFSET, relative to WHENCE. */ gpgme_off_t gpgme_data_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { - TRACE_BEG2 (DEBUG_DATA, "gpgme_data_seek", dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_seek", dh, "offset=%lli, whence=%i", offset, whence); if (!dh) { gpg_err_set_errno (EINVAL); return TRACE_SYSRES (-1); } if (!dh->cbs->seek) { gpg_err_set_errno (ENOSYS); return TRACE_SYSRES (-1); } /* For relative movement, we must take into account the actual position of the read counter. */ if (whence == SEEK_CUR) offset -= dh->pending_len; offset = (*dh->cbs->seek) (dh, offset, whence); if (offset >= 0) dh->pending_len = 0; return TRACE_SYSRES (offset); } /* Convenience function to do a gpgme_data_seek (dh, 0, SEEK_SET). */ gpgme_error_t gpgme_data_rewind (gpgme_data_t dh) { gpgme_error_t err; - TRACE_BEG (DEBUG_DATA, "gpgme_data_rewind", dh); + TRACE_BEG (DEBUG_DATA, "gpgme_data_rewind", dh, ""); err = ((gpgme_data_seek (dh, 0, SEEK_SET) == -1) ? gpg_error_from_syserror () : 0); return TRACE_ERR (err); } /* Release the data object with the handle DH. */ void gpgme_data_release (gpgme_data_t dh) { - TRACE (DEBUG_DATA, "gpgme_data_release", dh); + TRACE (DEBUG_DATA, "gpgme_data_release", dh, ""); if (!dh) return; if (dh->cbs->release) (*dh->cbs->release) (dh); _gpgme_data_release (dh); } /* Get the current encoding meta information for the data object with handle DH. */ gpgme_data_encoding_t gpgme_data_get_encoding (gpgme_data_t dh) { - TRACE1 (DEBUG_DATA, "gpgme_data_get_encoding", dh, - "dh->encoding=%i", dh ? dh->encoding : GPGME_DATA_ENCODING_NONE); + TRACE (DEBUG_DATA, "gpgme_data_get_encoding", dh, + "dh->encoding=%i", dh ? dh->encoding : GPGME_DATA_ENCODING_NONE); return dh ? dh->encoding : GPGME_DATA_ENCODING_NONE; } /* Set the encoding meta information for the data object with handle DH to ENC. */ gpgme_error_t gpgme_data_set_encoding (gpgme_data_t dh, gpgme_data_encoding_t enc) { - TRACE_BEG1 (DEBUG_DATA, "gpgme_data_set_encoding", dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_set_encoding", dh, "encoding=%i", enc); if (!dh) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (enc < 0 || enc > GPGME_DATA_ENCODING_MIME) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); dh->encoding = enc; return TRACE_ERR (0); } /* Set the file name associated with the data object with handle DH to FILE_NAME. */ gpgme_error_t gpgme_data_set_file_name (gpgme_data_t dh, const char *file_name) { - TRACE_BEG1 (DEBUG_DATA, "gpgme_data_set_file_name", dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_set_file_name", dh, "file_name=%s", file_name); if (!dh) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (dh->file_name) free (dh->file_name); if (file_name) { dh->file_name = strdup (file_name); if (!dh->file_name) return TRACE_ERR (gpg_error_from_syserror ()); } else dh->file_name = 0; return TRACE_ERR (0); } /* Get the file name associated with the data object with handle DH, or NULL if there is none. */ char * gpgme_data_get_file_name (gpgme_data_t dh) { if (!dh) { - TRACE (DEBUG_DATA, "gpgme_data_get_file_name", dh); + TRACE (DEBUG_DATA, "gpgme_data_get_file_name", dh, ""); return NULL; } - TRACE1 (DEBUG_DATA, "gpgme_data_get_file_name", dh, - "dh->file_name=%s", dh->file_name); + TRACE (DEBUG_DATA, "gpgme_data_get_file_name", dh, + "dh->file_name=%s", dh->file_name); return dh->file_name; } /* Set a flag for the data object DH. See the manual for details. */ gpg_error_t gpgme_data_set_flag (gpgme_data_t dh, const char *name, const char *value) { - TRACE_BEG2 (DEBUG_DATA, "gpgme_data_set_flag", dh, + TRACE_BEG (DEBUG_DATA, "gpgme_data_set_flag", dh, "%s=%s", name, value); if (!dh) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (!strcmp (name, "size-hint")) { dh->size_hint= value? _gpgme_string_to_off (value) : 0; } else return gpg_error (GPG_ERR_UNKNOWN_NAME); return 0; } /* Functions to support the wait interface. */ gpgme_error_t _gpgme_data_inbound_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; gpgme_data_t dh = (gpgme_data_t) data->handler_value; char buffer[BUFFER_SIZE]; char *bufp = buffer; gpgme_ssize_t buflen; - TRACE_BEG1 (DEBUG_CTX, "_gpgme_data_inbound_handler", dh, + TRACE_BEG (DEBUG_CTX, "_gpgme_data_inbound_handler", dh, "fd=0x%x", fd); buflen = _gpgme_io_read (fd, buffer, BUFFER_SIZE); if (buflen < 0) return gpg_error_from_syserror (); if (buflen == 0) { _gpgme_io_close (fd); return TRACE_ERR (0); } do { gpgme_ssize_t amt = gpgme_data_write (dh, bufp, buflen); if (amt == 0 || (amt < 0 && errno != EINTR)) return TRACE_ERR (gpg_error_from_syserror ()); bufp += amt; buflen -= amt; } while (buflen > 0); return TRACE_ERR (0); } gpgme_error_t _gpgme_data_outbound_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; gpgme_data_t dh = (gpgme_data_t) data->handler_value; gpgme_ssize_t nwritten; - TRACE_BEG1 (DEBUG_CTX, "_gpgme_data_outbound_handler", dh, + TRACE_BEG (DEBUG_CTX, "_gpgme_data_outbound_handler", dh, "fd=0x%x", fd); if (!dh->pending_len) { gpgme_ssize_t amt = gpgme_data_read (dh, dh->pending, BUFFER_SIZE); if (amt < 0) return TRACE_ERR (gpg_error_from_syserror ()); if (amt == 0) { _gpgme_io_close (fd); return TRACE_ERR (0); } dh->pending_len = amt; } nwritten = _gpgme_io_write (fd, dh->pending, dh->pending_len); if (nwritten == -1 && errno == EAGAIN) return TRACE_ERR (0); if (nwritten == -1 && errno == EPIPE) { /* Not much we can do. The other end closed the pipe, but we still have data. This should only ever happen if the other end is going to tell us what happened on some other channel. Silently close our end. */ _gpgme_io_close (fd); return TRACE_ERR (0); } if (nwritten <= 0) return TRACE_ERR (gpg_error_from_syserror ()); if (nwritten < dh->pending_len) memmove (dh->pending, dh->pending + nwritten, dh->pending_len - nwritten); dh->pending_len -= nwritten; return TRACE_ERR (0); } /* Get the file descriptor associated with DH, if possible. Otherwise return -1. */ int _gpgme_data_get_fd (gpgme_data_t dh) { if (!dh || !dh->cbs->get_fd) return -1; return (*dh->cbs->get_fd) (dh); } /* Get the size-hint value for DH or 0 if not available. */ gpgme_off_t _gpgme_data_get_size_hint (gpgme_data_t dh) { return dh ? dh->size_hint : 0; } diff --git a/src/debug.c b/src/debug.c index 364a5767..a2f0e694 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,400 +1,485 @@ /* debug.c - helpful output in desperate situations * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #ifndef HAVE_DOSISH_SYSTEM # ifdef HAVE_SYS_TYPES_H # include # endif # ifdef HAVE_SYS_STAT_H # include # endif # include #endif #include #include "util.h" #include "ath.h" #include "sema.h" #include "sys-util.h" #include "debug.h" /* Lock to serialize initialization of the debug output subsystem and output of actual debug messages. */ DEFINE_STATIC_LOCK (debug_lock); /* The amount of detail requested by the user, per environment variable GPGME_DEBUG. */ static int debug_level; /* The output stream for the debug messages. */ static FILE *errfp; /* If not NULL, this malloced string is used instead of the GPGME_DEBUG envvar. It must have been set before the debug subsystem has been initialized. Using it later may or may not have any effect. */ static char *envvar_override; #ifdef HAVE_TLS #define FRAME_NR static __thread int frame_nr = 0; #endif void _gpgme_debug_frame_begin (void) { #ifdef FRAME_NR frame_nr++; #endif } int _gpgme_debug_frame_end (void) { #ifdef FRAME_NR frame_nr--; #endif return 0; } /* Remove leading and trailing white spaces. */ static char * 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; } /* This is an internal function to set debug info. The caller must assure that this function is called only by one thread at a time. The function may have no effect if called after the debug system has been initialized. Returns 0 on success. */ int _gpgme_debug_set_debug_envvar (const char *value) { free (envvar_override); envvar_override = strdup (value); return !envvar_override; } static void debug_init (void) { static int initialized; LOCK (debug_lock); if (!initialized) { gpgme_error_t err; char *e; const char *s1, *s2;; if (envvar_override) { e = strdup (envvar_override); free (envvar_override); envvar_override = NULL; } else { err = _gpgme_getenv ("GPGME_DEBUG", &e); if (err) { UNLOCK (debug_lock); return; } } initialized = 1; errfp = stderr; if (e) { debug_level = atoi (e); s1 = strchr (e, PATHSEP_C); if (s1) { #ifndef HAVE_DOSISH_SYSTEM if (getuid () == geteuid () #if defined(HAVE_GETGID) && defined(HAVE_GETEGID) && getgid () == getegid () #endif ) { #endif char *p; FILE *fp; s1++; if (!(s2 = strchr (s1, PATHSEP_C))) s2 = s1 + strlen (s1); p = malloc (s2 - s1 + 1); if (p) { memcpy (p, s1, s2 - s1); p[s2-s1] = 0; trim_spaces (p); fp = fopen (p,"a"); if (fp) { setvbuf (fp, NULL, _IOLBF, 0); errfp = fp; } free (p); } #ifndef HAVE_DOSISH_SYSTEM } #endif } free (e); } } UNLOCK (debug_lock); if (debug_level > 0) { _gpgme_debug (DEBUG_INIT, "gpgme_debug: level=%d\n", debug_level); #ifdef HAVE_W32_SYSTEM { const char *name = _gpgme_get_inst_dir (); _gpgme_debug (DEBUG_INIT, "gpgme_debug: gpgme='%s'\n", name? name: "?"); } #endif } } /* This should be called as soon as the locks are initialized. It is required so that the assuan logging gets conncted to the gpgme log stream as early as possible. */ void _gpgme_debug_subsystem_init (void) { debug_init (); } /* Log the formatted string FORMAT at debug level LEVEL or higher. * * Returns: 0 * * Note that we always return 0 because the old TRACE macro evaluated * to 0 which issues a warning with newer gcc version about an unused * values. By using a return value of this function this can be * avoided. Fixme: It might be useful to check whether the return * value from the TRACE macros are actually used somewhere. */ int _gpgme_debug (int level, const char *format, ...) { va_list arg_ptr; int saved_errno; saved_errno = errno; if (debug_level < level) return 0; va_start (arg_ptr, format); LOCK (debug_lock); { struct tm *tp; time_t atime = time (NULL); tp = localtime (&atime); fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx> ", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, (unsigned long long) ath_self ()); } #ifdef FRAME_NR { int indent; indent = frame_nr > 0? (2 * (frame_nr - 1)):0; fprintf (errfp, "%*s", indent < 40? indent : 40, ""); } #endif vfprintf (errfp, format, arg_ptr); va_end (arg_ptr); if(format && *format && format[strlen (format) - 1] != '\n') putc ('\n', errfp); UNLOCK (debug_lock); fflush (errfp); gpg_err_set_errno (saved_errno); return 0; } +/* Log the formatted string FORMAT prefixed with additional info + * depending on MODE: + * + * -1 = Do not print any additional args. + * 0 = standalone (used by macro TRACE) + * 1 = enter a function (used by macro TRACE_BEG) + * 2 = debug a function (used by macro TRACE_LOG) + * 3 = leave a function (used by macro TRACE_SUC) + * + * Returns: 0 + * + * Note that we always return 0 because the old TRACE macro evaluated + * to 0 which issues a warning with newer gcc version about an unused + * values. By using a return value of this function this can be + * avoided. Fixme: It might be useful to check whether the return + * value from the TRACE macros are actually used somewhere. + */ +int +_gpgme_debugf (int level, int mode, const char *func, const char *tagname, + const char *tagvalue, const char *format, ...) +{ + va_list arg_ptr; + int saved_errno; + + saved_errno = errno; + if (debug_level < level) + return 0; + + va_start (arg_ptr, format); + LOCK (debug_lock); + { + struct tm *tp; + time_t atime = time (NULL); + + tp = localtime (&atime); + fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx> ", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec, + (unsigned long long) ath_self ()); + } +#ifdef FRAME_NR + { + int indent; + + indent = frame_nr > 0? (2 * (frame_nr - 1)):0; + fprintf (errfp, "%*s", indent < 40? indent : 40, ""); + } +#endif + + switch (mode) + { + case -1: /* Do nothing. */ + break; + case 0: + fprintf (errfp, "%s: call: %s=%p ", func, tagname, tagvalue); + break; + case 1: + fprintf (errfp, "%s: enter: %s=%p ", func, tagname, tagvalue); + break; + case 2: + fprintf (errfp, "%s: check: %s=%p ", func, tagname, tagvalue); + break; + case 3: + if (tagname) + fprintf (errfp, "%s: leave: %s=%p ", func, tagname, tagvalue); + else + fprintf (errfp, "%s: leave: ", func); + break; + default: + fprintf (errfp, "%s: m=%d: %s=%p ", func, mode, tagname, tagvalue); + break; + } + + vfprintf (errfp, format, arg_ptr); + va_end (arg_ptr); + if(format && *format && format[strlen (format) - 1] != '\n') + putc ('\n', errfp); + UNLOCK (debug_lock); + fflush (errfp); + + gpg_err_set_errno (saved_errno); + return 0; +} + + /* Start a new debug line in *LINE, logged at level LEVEL or higher, and starting with the formatted string FORMAT. */ void _gpgme_debug_begin (void **line, int level, const char *format, ...) { va_list arg_ptr; int res; if (debug_level < level) { /* Disable logging of this line. */ *line = NULL; return; } va_start (arg_ptr, format); res = gpgrt_vasprintf ((char **) line, format, arg_ptr); va_end (arg_ptr); if (res < 0) *line = NULL; } /* Add the formatted string FORMAT to the debug line *LINE. */ void _gpgme_debug_add (void **line, const char *format, ...) { va_list arg_ptr; char *toadd; char *result; int res; if (!*line) return; va_start (arg_ptr, format); res = gpgrt_vasprintf (&toadd, format, arg_ptr); va_end (arg_ptr); if (res < 0) { gpgrt_free (*line); *line = NULL; } res = gpgrt_asprintf (&result, "%s%s", *(char **) line, toadd); gpgrt_free (toadd); gpgrt_free (*line); if (res < 0) *line = NULL; else *line = result; } /* Finish construction of *LINE and send it to the debug output stream. */ void _gpgme_debug_end (void **line) { if (!*line) return; /* The smallest possible level is 1, so force logging here by using that. */ _gpgme_debug (1, "%s", *line); gpgrt_free (*line); *line = NULL; } #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a')) void _gpgme_debug_buffer (int lvl, const char *const fmt, const char *const func, const char *const buffer, size_t len) { int idx = 0; int j; if (!_gpgme_debug_trace ()) return; if (!buffer) return; while (idx < len) { char str[51]; char *strp = str; char *strp2 = &str[34]; for (j = 0; j < 16; j++) { unsigned char val; if (idx < len) { val = buffer[idx++]; *(strp++) = TOHEX (val >> 4); *(strp++) = TOHEX (val % 16); *(strp2++) = isprint (val) ? val : '.'; } else { *(strp++) = ' '; *(strp++) = ' '; } if (j == 7) *(strp++) = ' '; } *(strp++) = ' '; *(strp2) = '\0'; _gpgme_debug (lvl, fmt, func, str); } } diff --git a/src/debug.h b/src/debug.h index 77fec559..9f9fd5df 100644 --- a/src/debug.h +++ b/src/debug.h @@ -1,299 +1,194 @@ /* debug.h - interface to debugging functions Copyright (C) 2002, 2004, 2005, 2007 g10 Code GmbH This file is part of GPGME. GPGME is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. GPGME 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser 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. */ #ifndef DEBUG_H #define DEBUG_H #include #ifdef HAVE_STDINT_H #include #endif #include "gpgme.h" /* Required for gpgme_error stuff. */ /* Indirect stringification, requires __STDC__ to work. */ #define STRINGIFY(v) #v #define XSTRINGIFY(v) STRINGIFY(v) /* * The debug levels. * * Note that TRACE_LOGBUFX uses the current debug level + 1. */ #define DEBUG_INIT 1 #define DEBUG_GLOBAL 2 #define DEBUG_CTX 3 #define DEBUG_ENGINE 4 #define DEBUG_DATA 5 #define DEBUG_ASSUAN 6 #define DEBUG_SYSIO 7 /* Remove path components from filenames (i.e. __FILE__) for cleaner logs. */ static inline const char *_gpgme_debug_srcname (const char *file) GPGME_GCC_A_PURE; static inline const char * _gpgme_debug_srcname (const char *file) { const char *s = strrchr (file, '/'); return s? s+1:file; } /* Initialization helper function; see debug.c. */ int _gpgme_debug_set_debug_envvar (const char *value); /* Called early to initialize the logging. */ void _gpgme_debug_subsystem_init (void); /* Log the formatted string FORMAT at debug level LEVEL or higher. */ int _gpgme_debug (int level, const char *format, ...); +int _gpgme_debugf (int level, int mode, + const char *func, const char *tagname, const char *tagvalue, + const char *format, ...) GPGRT_ATTR_PRINTF(6,7); + /* Start a new debug line in *LINE, logged at level LEVEL or higher, and starting with the formatted string FORMAT. */ void _gpgme_debug_begin (void **helper, int level, const char *format, ...); /* Add the formatted string FORMAT to the debug line *LINE. */ void _gpgme_debug_add (void **helper, const char *format, ...); /* Finish construction of *LINE and send it to the debug output stream. */ void _gpgme_debug_end (void **helper); void _gpgme_debug_buffer (int lvl, const char *const fmt, const char *const func, const char *const buffer, size_t len); void _gpgme_debug_frame_begin (void); int _gpgme_debug_frame_end (void); static inline gpgme_error_t _gpgme_trace_gpgme_error (gpgme_error_t err, const char *file, int line) { _gpgme_debug (DEBUG_ENGINE, "%s:%d: returning error: %s\n", _gpgme_debug_srcname (file), line, gpgme_strerror (err)); return err; } /* Trace support. */ /* FIXME: For now. */ #define _gpgme_debug_trace() 1 #define _TRACE(lvl, name, tag) \ int _gpgme_trace_level = lvl; \ const char *const _gpgme_trace_func = name; \ const char *const _gpgme_trace_tagname = STRINGIFY (tag); \ void *_gpgme_trace_tag = (void *) (uintptr_t) tag; \ _gpgme_debug_frame_begin () -#define TRACE_BEG(lvl, name, tag) \ - _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag) -#define TRACE_BEG0(lvl, name, tag, fmt) \ - _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag) -#define TRACE_BEG1(lvl, name, tag, fmt, arg1) \ +#define TRACE_BEG(lvl, name, tag, ...) \ _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1) -#define TRACE_BEG2(lvl, name, tag, fmt, arg1, arg2) \ - _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2) -#define TRACE_BEG3(lvl, name, tag, fmt, arg1, arg2, arg3) \ - _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2, arg3) -#define TRACE_BEG4(lvl, name, tag, fmt, arg1, arg2, arg3, arg4) \ - _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2, arg3, arg4) -#define TRACE_BEG5(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, arg5) \ - _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2, arg3, arg4, arg5) -#define TRACE_BEG7(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, \ - arg5, arg6, arg7) \ - _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2, arg3, arg4, arg5, arg6, arg7) -#define TRACE_BEG8(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, \ - arg5, arg6, arg7, arg8) \ - _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - -#define TRACE(lvl, name, tag) \ - _gpgme_debug_frame_begin (), \ - _gpgme_debug (lvl, "%s: call: %s=%p\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag), \ - _gpgme_debug_frame_end () -#define TRACE0(lvl, name, tag, fmt) \ - _gpgme_debug_frame_begin (), \ - _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag), \ - _gpgme_debug_frame_end () -#define TRACE1(lvl, name, tag, fmt, arg1) \ - _gpgme_debug_frame_begin (), \ - _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1), \ - _gpgme_debug_frame_end () -#define TRACE2(lvl, name, tag, fmt, arg1, arg2) \ - _gpgme_debug_frame_begin (), \ - _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ - arg2), _gpgme_debug_frame_end () -#define TRACE3(lvl, name, tag, fmt, arg1, arg2, arg3) \ - _gpgme_debug_frame_begin (), \ - _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ - arg2, arg3), _gpgme_debug_frame_end () -#define TRACE6(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, arg5, arg6) \ + _gpgme_debugf (_gpgme_trace_level, 1, \ + _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ + __VA_ARGS__) + +#define TRACE(lvl, name, tag, ...) \ _gpgme_debug_frame_begin (), \ - _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ - arg2, arg3, arg4, arg5, arg6), \ - _gpgme_debug_frame_end () + _gpgme_debugf (lvl, 0, \ + name, STRINGIFY (tag), (void *) (uintptr_t) tag, \ + __VA_ARGS__), \ + _gpgme_debug_frame_end () #define TRACE_ERR(err) \ - err == 0 ? (TRACE_SUC ()) : \ + err == 0 ? (TRACE_SUC ("")) : \ (_gpgme_debug (_gpgme_trace_level, "%s:%d: error: %s <%s>\n", \ _gpgme_trace_func, __LINE__, gpgme_strerror (err), \ gpgme_strsource (err)), _gpgme_debug_frame_end (), (err)) + + /* The cast to void suppresses GCC warnings. */ #define TRACE_SYSRES(res) \ - res >= 0 ? ((void) (TRACE_SUC1 ("result=%i", res)), (res)) : \ - (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \ - _gpgme_trace_func, strerror (errno)), _gpgme_debug_frame_end (), (res)) + res >= 0 ? ((void) (TRACE_SUC ("result=%i", res)), (res)) : \ + (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \ + _gpgme_trace_func, strerror (errno)), \ + _gpgme_debug_frame_end (), (res)) #define TRACE_SYSERR(res) \ - res == 0 ? ((void) (TRACE_SUC1 ("result=%i", res)), (res)) : \ + res == 0 ? ((void) (TRACE_SUC ("result=%i", res)), (res)) : \ (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \ _gpgme_trace_func, strerror (res)), \ _gpgme_debug_frame_end (), (res)) #define TRACE_SYSERR_NR(res) \ - do { res == 0 ? ((void) (TRACE_SUC1 ("result=%i", res)), (res)) : \ + do { res == 0 ? ((void) (TRACE_SUC ("result=%i", res)), (res)) : \ (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \ _gpgme_trace_func, strerror (res)), \ _gpgme_debug_frame_end ()); } while (0) -#define TRACE_SUC() \ - _gpgme_debug (_gpgme_trace_level, "%s: leave\n", \ - _gpgme_trace_func), _gpgme_debug_frame_end () -#define TRACE_SUC0(fmt) \ - _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ - _gpgme_trace_func), _gpgme_debug_frame_end () -#define TRACE_SUC1(fmt, arg1) \ - _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ - _gpgme_trace_func, arg1), _gpgme_debug_frame_end () -#define TRACE_SUC2(fmt, arg1, arg2) \ - _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ - _gpgme_trace_func, arg1, arg2), _gpgme_debug_frame_end () -#define TRACE_SUC3(fmt, arg1, arg2, arg3) \ - _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ - _gpgme_trace_func, arg1, arg2, arg3), _gpgme_debug_frame_end () -#define TRACE_SUC4(fmt, arg1, arg2, arg3, arg4) \ - _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ - _gpgme_trace_func, arg1, arg2, arg3, arg4), \ - _gpgme_debug_frame_end () -#define TRACE_SUC5(fmt, arg1, arg2, arg3, arg4, arg5) \ - _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ - _gpgme_trace_func, arg1, arg2, arg3, arg4, arg5), \ - _gpgme_debug_frame_end () -#define TRACE_SUC6(fmt, arg1, arg2, arg3, arg4, arg5, arg6) \ - _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ - _gpgme_trace_func, arg1, arg2, arg3, arg4, arg5, arg6), \ - _gpgme_debug_frame_end () +#define TRACE_SUC(...) \ + _gpgme_debugf (_gpgme_trace_level, 3, _gpgme_trace_func, NULL, NULL, \ + __VA_ARGS__), _gpgme_debug_frame_end () -#define TRACE_LOG(fmt) \ - _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag) -#define TRACE_LOG1(fmt, arg1) \ - _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1) -#define TRACE_LOG2(fmt, arg1, arg2) \ - _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2) -#define TRACE_LOG3(fmt, arg1, arg2, arg3) \ - _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2, arg3) -#define TRACE_LOG4(fmt, arg1, arg2, arg3, arg4) \ - _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2, arg3, arg4) -#define TRACE_LOG5(fmt, arg1, arg2, arg3, arg4, arg5) \ - _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2, arg3, arg4, arg5) -#define TRACE_LOG6(fmt, arg1, arg2, arg3, arg4, arg5, arg6) \ - _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - arg1, arg2, arg3, arg4, arg5, arg6) +#define TRACE_LOG(...) \ + _gpgme_debugf (_gpgme_trace_level, 2, \ + _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ + __VA_ARGS__) #define TRACE_LOGBUF(buf, len) \ _gpgme_debug_buffer (_gpgme_trace_level, "%s: check: %s", \ _gpgme_trace_func, buf, len) #define TRACE_LOGBUFX(buf, len) \ _gpgme_debug_buffer (_gpgme_trace_level+1, "%s: check: %s", \ _gpgme_trace_func, buf, len) #define TRACE_SEQ(hlp,fmt) \ _gpgme_debug_begin (&(hlp), _gpgme_trace_level, \ "%s: check: %s=%p, " fmt, _gpgme_trace_func, \ _gpgme_trace_tagname, _gpgme_trace_tag) #define TRACE_ADD0(hlp,fmt) \ _gpgme_debug_add (&(hlp), fmt) #define TRACE_ADD1(hlp,fmt,a) \ _gpgme_debug_add (&(hlp), fmt, (a)) #define TRACE_ADD2(hlp,fmt,a,b) \ _gpgme_debug_add (&(hlp), fmt, (a), (b)) #define TRACE_ADD3(hlp,fmt,a,b,c) \ _gpgme_debug_add (&(hlp), fmt, (a), (b), (c)) #define TRACE_END(hlp,fmt) \ _gpgme_debug_add (&(hlp), fmt); \ _gpgme_debug_end (&(hlp)) #define TRACE_ENABLED(hlp) (!!(hlp)) /* And finally a simple macro to trace the location of an error code. This macro is independent of the other trace macros and may be used without any preconditions. */ #define trace_gpg_error(e) \ _gpgme_trace_gpgme_error (gpg_error (e), __FILE__, __LINE__) #endif /* DEBUG_H */ diff --git a/src/decrypt-verify.c b/src/decrypt-verify.c index b9c3103a..b63318f2 100644 --- a/src/decrypt-verify.c +++ b/src/decrypt-verify.c @@ -1,183 +1,183 @@ /* decrypt-verify.c - Decrypt and verify function. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "debug.h" #include "gpgme.h" #include "ops.h" static gpgme_error_t decrypt_verify_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_decrypt_status_handler (priv, code, args); if (!err) err = _gpgme_verify_status_handler (priv, code, args); return err; } static gpgme_error_t decrypt_verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_decrypt_flags_t flags, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; assert ((flags & GPGME_DECRYPT_VERIFY)); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_decrypt_init_result (ctx, plain); if (err) return err; err = _gpgme_op_verify_init_result (ctx); if (err) return err; if (!cipher) return gpg_error (GPG_ERR_NO_DATA); if (!plain) return gpg_error (GPG_ERR_INV_VALUE); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, decrypt_verify_status_handler, ctx); return _gpgme_engine_op_decrypt (ctx->engine, flags, cipher, plain, ctx->export_session_keys, ctx->override_session_key, ctx->auto_key_retrieve); } /* Decrypt ciphertext CIPHER and make a signature verification within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_verify_start (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_verify_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_verify_start", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = decrypt_verify_start (ctx, 0, GPGME_DECRYPT_VERIFY, cipher, plain); return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER and make a signature verification within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_verify (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_verify", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_verify", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = decrypt_verify_start (ctx, 1, GPGME_DECRYPT_VERIFY, cipher, plain); if (!err) err = _gpgme_wait_one (ctx); ctx->ignore_mdc_error = 0; /* Always reset. */ return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_ext_start (gpgme_ctx_t ctx, gpgme_decrypt_flags_t flags, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_ext_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_ext_start", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if ((flags & GPGME_DECRYPT_VERIFY)) err = decrypt_verify_start (ctx, 0, flags, cipher, plain); else err = _gpgme_decrypt_start (ctx, 0, flags, cipher, plain); return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_ext (gpgme_ctx_t ctx, gpgme_decrypt_flags_t flags, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_ext", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_ext", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if ((flags & GPGME_DECRYPT_VERIFY)) err = decrypt_verify_start (ctx, 1, flags, cipher, plain); else err = _gpgme_decrypt_start (ctx, 1, flags, cipher, plain); if (!err) err = _gpgme_wait_one (ctx); ctx->ignore_mdc_error = 0; /* Always reset. */ return TRACE_ERR (err); } diff --git a/src/decrypt.c b/src/decrypt.c index 77c45627..f8847996 100644 --- a/src/decrypt.c +++ b/src/decrypt.c @@ -1,619 +1,619 @@ /* decrypt.c - Decrypt function. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2017 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "debug.h" #include "gpgme.h" #include "util.h" #include "context.h" #include "ops.h" #include "data.h" typedef struct { struct _gpgme_op_decrypt_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; int okay; /* A flag telling that the a decryption failed and an optional error * code to further specify the failure. */ int failed; gpg_error_t pkdecrypt_failed; /* At least one secret key is not available. gpg issues NO_SECKEY * status lines for each key the message has been encrypted to but * that secret key is not available. This can't be done for hidden * recipients, though. We track it here to allow for a better error * message than the general DECRYPTION_FAILED. */ int any_no_seckey; /* If the engine emits a DECRYPTION_INFO status and that does not * indicate that an integrity protection mode is active, this flag * is set. */ int not_integrity_protected; /* The error code from the first ERROR line. This is in some cases * used to return a better matching error code to the caller. */ gpg_error_t first_status_error; /* A pointer to the next pointer of the last recipient in the list. This makes appending new invalid signers painless while preserving the order. */ gpgme_recipient_t *last_recipient_p; /* The data object serial number of the plaintext. */ uint64_t plaintext_dserial; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_recipient_t recipient = opd->result.recipients; free (opd->result.unsupported_algorithm); free (opd->result.file_name); free (opd->result.session_key); free (opd->result.symkey_algo); while (recipient) { gpgme_recipient_t next = recipient->next; free (recipient); recipient = next; } } gpgme_decrypt_result_t gpgme_op_decrypt_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; - TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx, ""); ctx->ignore_mdc_error = 0; /* Always reset this flag. */ err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL); opd = hook; if (err || !opd) { - TRACE_SUC0 ("result=(null)"); + TRACE_SUC ("result=(null)"); return NULL; } /* Make sure that SYMKEY_ALGO has a value. */ if (!opd->result.symkey_algo) { opd->result.symkey_algo = strdup ("?.?"); if (!opd->result.symkey_algo) { - TRACE_SUC0 ("result=(null)"); + TRACE_SUC ("result=(null)"); return NULL; } } if (_gpgme_debug_trace ()) { gpgme_recipient_t rcp; if (opd->result.unsupported_algorithm) { - TRACE_LOG1 ("result: unsupported_algorithm: %s", + TRACE_LOG ("result: unsupported_algorithm: %s", opd->result.unsupported_algorithm); } if (opd->result.wrong_key_usage) { TRACE_LOG ("result: wrong key usage"); } rcp = opd->result.recipients; while (rcp) { - TRACE_LOG3 ("result: recipient: keyid=%s, pubkey_algo=%i, " + TRACE_LOG ("result: recipient: keyid=%s, pubkey_algo=%i, " "status=%s", rcp->keyid, rcp->pubkey_algo, gpg_strerror (rcp->status)); rcp = rcp->next; } if (opd->result.file_name) { - TRACE_LOG1 ("result: original file name: %s", opd->result.file_name); + TRACE_LOG ("result: original file name: %s", opd->result.file_name); } } - TRACE_SUC1 ("result=%p", &opd->result); + TRACE_SUC ("result=%p", &opd->result); return &opd->result; } /* Parse the ARGS of an error status line and record some error * conditions at OPD. Returns 0 on success. */ static gpgme_error_t parse_status_error (char *args, op_data_t opd) { gpgme_error_t err; char *field[3]; int nfields; char *args2; if (!args) return trace_gpg_error (GPG_ERR_INV_ENGINE); args2 = strdup (args); /* Split modifies the input string. */ nfields = _gpgme_split_fields (args2, field, DIM (field)); if (nfields < 1) { free (args2); return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required arg missing. */ } err = nfields < 2 ? 0 : atoi (field[1]); if (!strcmp (field[0], "decrypt.algorithm")) { if (gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM && nfields > 2 && strcmp (field[2], "?")) { opd->result.unsupported_algorithm = strdup (field[2]); if (!opd->result.unsupported_algorithm) { free (args2); return gpg_error_from_syserror (); } } } else if (!strcmp (field[0], "decrypt.keyusage")) { if (gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE) opd->result.wrong_key_usage = 1; } else if (!strcmp (field[0], "pkdecrypt_failed")) { switch (gpg_err_code (err)) { case GPG_ERR_CANCELED: case GPG_ERR_FULLY_CANCELED: /* It is better to return with a cancel error code than the * general decryption failed error code. */ opd->pkdecrypt_failed = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED); break; case GPG_ERR_BAD_PASSPHRASE: /* A bad passphrase is severe enough that we return this * error code. */ opd->pkdecrypt_failed = err; break; default: /* For now all other error codes are ignored and the * standard DECRYPT_FAILED is returned. */ break; } } else if (!strcmp (field[0], "nomdc_with_legacy_cipher")) { opd->result.legacy_cipher_nomdc = 1; opd->not_integrity_protected = 1; } /* Record the first error code. */ if (err && !opd->first_status_error) opd->first_status_error = err; free (args2); return 0; } static gpgme_error_t parse_enc_to (char *args, gpgme_recipient_t *recp, gpgme_protocol_t protocol) { gpgme_recipient_t rec; char *tail; int i; rec = malloc (sizeof (*rec)); if (!rec) return gpg_error_from_syserror (); rec->next = NULL; rec->keyid = rec->_keyid; rec->status = 0; for (i = 0; i < sizeof (rec->_keyid) - 1; i++) { if (args[i] == '\0' || args[i] == ' ') break; rec->_keyid[i] = args[i]; } rec->_keyid[i] = '\0'; args = &args[i]; if (*args != '\0' && *args != ' ') { free (rec); return trace_gpg_error (GPG_ERR_INV_ENGINE); } while (*args == ' ') args++; if (*args) { gpg_err_set_errno (0); rec->pubkey_algo = _gpgme_map_pk_algo (strtol (args, &tail, 0), protocol); if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (rec); return trace_gpg_error (GPG_ERR_INV_ENGINE); } } /* FIXME: The key length is always 0 right now, so no need to parse it. */ *recp = rec; return 0; } /* Parse the ARGS of a * DECRYPTION_INFO [] * status. Returns 0 on success and updates the OPD. */ static gpgme_error_t parse_decryption_info (char *args, op_data_t opd, gpgme_protocol_t protocol) { char *field[3]; int nfields; char *args2; int mdc, aead_algo; const char *algostr, *modestr; if (!args) return trace_gpg_error (GPG_ERR_INV_ENGINE); args2 = strdup (args); /* Split modifies the input string. */ nfields = _gpgme_split_fields (args2, field, DIM (field)); if (nfields < 2) { free (args2); return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required arg missing. */ } mdc = atoi (field[0]); algostr = _gpgme_cipher_algo_name (atoi (field[1]), protocol); aead_algo = nfields < 3? 0 : atoi (field[2]); modestr = _gpgme_cipher_mode_name (aead_algo, protocol); free (args2); free (opd->result.symkey_algo); if (!aead_algo && mdc != 2) opd->result.symkey_algo = _gpgme_strconcat (algostr, ".PGPCFB", NULL); else opd->result.symkey_algo = _gpgme_strconcat (algostr, ".", modestr, NULL); if (!opd->result.symkey_algo) return gpg_error_from_syserror (); if (!mdc && !aead_algo) opd->not_integrity_protected = 1; return 0; } gpgme_error_t _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_passphrase_status_handler (priv, code, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: /* We force an encryption failure if we know that integrity * protection is missing. For modern version of gpg using * modern cipher algorithms this is not required because gpg * will issue a failure anyway. However older gpg versions emit * only a warning. * Fixme: These error values should probably be attributed to * the underlying crypto engine (as error source). */ if (opd->failed) { /* This comes from a specialized ERROR status line. */ if (opd->pkdecrypt_failed) return opd->pkdecrypt_failed; /* For an integrity failure return just DECRYPTION_FAILED; * the actual cause can be taken from an already set * decryption result flag. */ if ((opd->not_integrity_protected && !ctx->ignore_mdc_error)) return gpg_error (GPG_ERR_DECRYPT_FAILED); /* If we have any other ERROR code we prefer that over * NO_SECKEY because it is probably the better matching * code. For example a garbled message with multiple * plaintext will return BAD_DATA here but may also have * indicated a NO_SECKEY. */ if (opd->first_status_error) return opd->first_status_error; /* No secret key is pretty common reason. */ if (opd->any_no_seckey) return gpg_error (GPG_ERR_NO_SECKEY); /* Generic decryption failed error code. */ return gpg_error (GPG_ERR_DECRYPT_FAILED); } else if (!opd->okay) { /* No data was found. */ return gpg_error (GPG_ERR_NO_DATA); } else if (opd->failure_code) { /* The engine returned failure code at program exit. */ return opd->failure_code; } break; case GPGME_STATUS_DECRYPTION_INFO: err = parse_decryption_info (args, opd, ctx->protocol); if (err) return err; break; case GPGME_STATUS_DECRYPTION_OKAY: opd->okay = 1; break; case GPGME_STATUS_DECRYPTION_FAILED: opd->failed = 1; /* Tell the data object that it shall not return any data. We * use the serial number because the data object may be owned by * another thread. We also don't check for an error because it * is possible that the data object has already been destroyed * and we are then not interested in returning an error. */ if (!ctx->ignore_mdc_error) _gpgme_data_set_prop (NULL, opd->plaintext_dserial, DATA_PROP_BLANKOUT, 1); break; case GPGME_STATUS_ERROR: /* Note that this is an informational status code which should * not lead to an error return unless it is something not * related to the backend. However, it is used to return a * better matching final error code. */ err = parse_status_error (args, opd); if (err) return err; break; case GPGME_STATUS_ENC_TO: err = parse_enc_to (args, opd->last_recipient_p, ctx->protocol); if (err) return err; opd->last_recipient_p = &(*opd->last_recipient_p)->next; break; case GPGME_STATUS_SESSION_KEY: if (opd->result.session_key) free (opd->result.session_key); opd->result.session_key = strdup(args); break; case GPGME_STATUS_NO_SECKEY: { gpgme_recipient_t rec = opd->result.recipients; while (rec) { if (!strcmp (rec->keyid, args)) { rec->status = gpg_error (GPG_ERR_NO_SECKEY); break; } rec = rec->next; } /* FIXME: Is this ok? */ if (!rec) return trace_gpg_error (GPG_ERR_INV_ENGINE); opd->any_no_seckey = 1; } break; case GPGME_STATUS_PLAINTEXT: { int mime = 0; err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime); if (err) return err; opd->result.is_mime = !!mime; } break; case GPGME_STATUS_INQUIRE_MAXLEN: if (ctx->status_cb && !ctx->full_status) { err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args); if (err) return err; } break; case GPGME_STATUS_DECRYPTION_COMPLIANCE_MODE: PARSE_COMPLIANCE_FLAGS (args, &opd->result); break; default: break; } return 0; } static gpgme_error_t decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_decrypt_status_handler (priv, code, args); return err; } gpgme_error_t _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx, gpgme_data_t plaintext) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->last_recipient_p = &opd->result.recipients; opd->plaintext_dserial = _gpgme_data_get_dserial (plaintext); return 0; } gpgme_error_t _gpgme_decrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_decrypt_flags_t flags, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; assert (!(flags & GPGME_DECRYPT_VERIFY)); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_decrypt_init_result (ctx, plain); if (err) return err; if (!cipher) return gpg_error (GPG_ERR_NO_DATA); if (!plain) return gpg_error (GPG_ERR_INV_VALUE); if (err) return err; if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx); return _gpgme_engine_op_decrypt (ctx->engine, flags, cipher, plain, ctx->export_session_keys, ctx->override_session_key, ctx->auto_key_retrieve); } gpgme_error_t gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_start", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_decrypt_start (ctx, 0, 0, cipher, plain); return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_decrypt_start (ctx, 1, 0, cipher, plain); if (!err) err = _gpgme_wait_one (ctx); ctx->ignore_mdc_error = 0; /* Always reset. */ return TRACE_ERR (err); } diff --git a/src/delete.c b/src/delete.c index 1bf1cb48..cd7ab570 100644 --- a/src/delete.c +++ b/src/delete.c @@ -1,209 +1,209 @@ /* delete.c - Delete a key. Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH This file is part of GPGME. GPGME is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. GPGME 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser 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. */ #if HAVE_CONFIG_H #include #endif #include #include #include "debug.h" #include "gpgme.h" #include "context.h" #include "ops.h" static gpgme_error_t delete_status_handler (void *priv, gpgme_status_code_t code, char *args) { (void)priv; if (code == GPGME_STATUS_DELETE_PROBLEM) { enum delete_problem { DELETE_No_Problem = 0, DELETE_No_Such_Key = 1, DELETE_Must_Delete_Secret_Key = 2, DELETE_Ambiguous_Specification = 3 }; long problem; char *tail; gpg_err_set_errno (0); problem = strtol (args, &tail, 0); if (errno || (*tail && *tail != ' ')) return trace_gpg_error (GPG_ERR_INV_ENGINE); switch (problem) { case DELETE_No_Problem: break; case DELETE_No_Such_Key: return gpg_error (GPG_ERR_NO_PUBKEY); case DELETE_Must_Delete_Secret_Key: return gpg_error (GPG_ERR_CONFLICT); case DELETE_Ambiguous_Specification: return gpg_error (GPG_ERR_AMBIGUOUS_NAME); } return gpg_error (GPG_ERR_GENERAL); } else if (code == GPGME_STATUS_ERROR) { /* Some error stati are informational, so we don't return an error code if we are not ready to process this status. */ gpgme_error_t err; char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else return trace_gpg_error (GPG_ERR_INV_ENGINE); err = atoi (which); if (!strcmp (where, "delete_key.secret") && (gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)) { /* This indicates a user cancellation on the confirmation dialog. */ return gpg_error (gpg_err_code (err)); } } return 0; } static gpgme_error_t delete_start (gpgme_ctx_t ctx, int synchronous, const gpgme_key_t key, unsigned int flags) { gpgme_error_t err; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, delete_status_handler, ctx); return _gpgme_engine_op_delete (ctx->engine, key, flags); } /* Delete KEY from the keyring. If ALLOW_SECRET is non-zero, secret keys are also deleted. */ gpgme_error_t gpgme_op_delete_start (gpgme_ctx_t ctx, const gpgme_key_t key, int allow_secret) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_delete_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_delete_start", ctx, "key=%p (%s), allow_secret=%i", key, (key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", allow_secret); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = delete_start (ctx, 0, key, allow_secret ? GPGME_DELETE_ALLOW_SECRET : 0); return TRACE_ERR (err); } /* Delete KEY from the keyring. If ALLOW_SECRET is non-zero, secret keys are also deleted. */ gpgme_error_t gpgme_op_delete (gpgme_ctx_t ctx, const gpgme_key_t key, int allow_secret) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_delete", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_delete", ctx, "key=%p (%s), allow_secret=%i", key, (key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", allow_secret); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = delete_start (ctx, 1, key, allow_secret ? GPGME_DELETE_ALLOW_SECRET : 0); if (!err) err = _gpgme_wait_one (ctx); return err; } /* Delete KEY from the keyring. */ gpgme_error_t gpgme_op_delete_ext_start (gpgme_ctx_t ctx, const gpgme_key_t key, unsigned int flags) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_delete_ext_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_delete_ext_start", ctx, "key=%p (%s), flags=0x%x", key, (key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = delete_start (ctx, 0, key, flags); return TRACE_ERR (err); } /* Delete KEY from the keyring. */ gpgme_error_t gpgme_op_delete_ext (gpgme_ctx_t ctx, const gpgme_key_t key, unsigned int flags) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_delete_ext", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_delete_ext", ctx, "key=%p (%s), flags=0x%x", key, (key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = delete_start (ctx, 1, key, flags); if (!err) err = _gpgme_wait_one (ctx); return err; } diff --git a/src/edit.c b/src/edit.c index feb7cf66..a7d2bb51 100644 --- a/src/edit.c +++ b/src/edit.c @@ -1,314 +1,314 @@ /* edit.c - Key edit function. * Copyright (C) 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { /* The user callback function and its hook value. */ gpgme_interact_cb_t fnc; gpgme_edit_cb_t fnc_old; void *fnc_value; } *op_data_t; static gpgme_error_t edit_status_handler (void *priv, gpgme_status_code_t status, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_passphrase_status_handler (priv, status, args); if (err) return err; err = _gpgme_progress_status_handler (priv, status, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, -1, NULL); opd = hook; if (err) return err; if (opd->fnc_old) return (*opd->fnc_old) (opd->fnc_value, status, args, -1); return (*opd->fnc) (opd->fnc_value, _gpgme_status_to_string (status), args, -1); } static gpgme_error_t command_handler (void *priv, gpgme_status_code_t status, const char *args, int fd, int *processed_r) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; int processed = 0; if (ctx->passphrase_cb) { err = _gpgme_passphrase_command_handler (ctx, status, args, fd, &processed); if (err) return err; } else err = 0; if (!processed) { void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, -1, NULL); opd = hook; if (err) return err; if (opd->fnc_old) err = (*opd->fnc_old) (opd->fnc_value, status, args, fd); else err = (*opd->fnc) (opd->fnc_value, _gpgme_status_to_string (status), args, fd); if (gpg_err_code (err) == GPG_ERR_FALSE) err = 0; else processed = 1; } *processed_r = processed; return err; } static gpgme_error_t interact_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, unsigned int flags, gpgme_interact_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (!fnc || !out) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, sizeof (*opd), NULL); opd = hook; if (err) return err; opd->fnc = fnc; opd->fnc_old = NULL; opd->fnc_value = fnc_value; err = _gpgme_engine_set_command_handler (ctx->engine, command_handler, ctx); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, edit_status_handler, ctx); return _gpgme_engine_op_edit (ctx->engine, (flags & GPGME_INTERACT_CARD)? 1: 0, key, out, ctx); } gpgme_error_t gpgme_op_interact_start (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags, gpgme_interact_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; - TRACE_BEG5 (DEBUG_CTX, "gpgme_op_interact_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_interact_start", ctx, "key=%p flags=0x%x fnc=%p fnc_value=%p, out=%p", key, flags,fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = interact_start (ctx, 0, key, flags, fnc, fnc_value, out); return err; } gpgme_error_t gpgme_op_interact (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags, gpgme_interact_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; - TRACE_BEG5 (DEBUG_CTX, "gpgme_op_interact", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_interact", ctx, "key=%p flags=0x%x fnc=%p fnc_value=%p, out=%p", key, flags,fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = interact_start (ctx, 1, key, flags, fnc, fnc_value, out); if (!err) err = _gpgme_wait_one (ctx); return err; } /* The deprecated interface. */ static gpgme_error_t edit_start (gpgme_ctx_t ctx, int synchronous, int type, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (!fnc || !out) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, sizeof (*opd), NULL); opd = hook; if (err) return err; opd->fnc = NULL; opd->fnc_old = fnc; opd->fnc_value = fnc_value; err = _gpgme_engine_set_command_handler (ctx->engine, command_handler, ctx); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, edit_status_handler, ctx); return _gpgme_engine_op_edit (ctx->engine, type, key, out, ctx); } gpgme_error_t gpgme_op_edit_start (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; - TRACE_BEG5 (DEBUG_CTX, "gpgme_op_edit_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_edit_start", ctx, "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key, (key && key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = edit_start (ctx, 0, 0, key, fnc, fnc_value, out); return err; } /* Edit the key KEY. Send status and command requests to FNC and output of edit commands to OUT. */ gpgme_error_t gpgme_op_edit (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; - TRACE_BEG5 (DEBUG_CTX, "gpgme_op_edit", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_edit", ctx, "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key, (key && key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = edit_start (ctx, 1, 0, key, fnc, fnc_value, out); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } gpgme_error_t gpgme_op_card_edit_start (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; - TRACE_BEG5 (DEBUG_CTX, "gpgme_op_card_edit_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_card_edit_start", ctx, "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key, (key && key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = edit_start (ctx, 0, 1, key, fnc, fnc_value, out); return err; } /* Edit the card for the key KEY. Send status and command requests to FNC and output of edit commands to OUT. */ gpgme_error_t gpgme_op_card_edit (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out) { gpgme_error_t err; - TRACE_BEG5 (DEBUG_CTX, "gpgme_op_card_edit", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_card_edit", ctx, "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key, (key && key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid", fnc, fnc_value, out); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = edit_start (ctx, 1, 1, key, fnc, fnc_value, out); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } diff --git a/src/encrypt-sign.c b/src/encrypt-sign.c index acc1a05d..e8272d8e 100644 --- a/src/encrypt-sign.c +++ b/src/encrypt-sign.c @@ -1,218 +1,218 @@ /* encrypt-sign.c - encrypt and verify functions * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" static gpgme_error_t encrypt_sign_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_encrypt_status_handler (priv, code, args); if (!err) err = _gpgme_sign_status_handler (priv, code, args); return err; } static gpgme_error_t encrypt_sym_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_sign_status_handler (priv, code, args); if (!err) err = _gpgme_passphrase_status_handler (priv, code, args); return err; } static gpgme_error_t encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; int symmetric; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; symmetric = (!recp && !recpstring) || (flags & GPGME_ENCRYPT_SYMMETRIC); if (!plain) return gpg_error (GPG_ERR_NO_DATA); if (!cipher) return gpg_error (GPG_ERR_INV_VALUE); if (recp && !*recp) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_op_encrypt_init_result (ctx); if (err) return err; err = _gpgme_op_sign_init_result (ctx); if (err) return err; if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, symmetric ? encrypt_sym_status_handler : encrypt_sign_status_handler, ctx); return _gpgme_engine_op_encrypt_sign (ctx->engine, recp, recpstring, flags, plain, cipher, ctx->use_armor, ctx /* FIXME */); } /* Old version of gpgme_op_encrypt_sign_ext_start w/o RECPSTRING. */ gpgme_error_t gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { return gpgme_op_encrypt_sign_ext_start (ctx, recp, NULL, flags, plain, cipher); } /* Old version of gpgme_op_encrypt_sign_ext w/o RECPSTRING. */ gpgme_error_t gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { return gpgme_op_encrypt_sign_ext (ctx, recp, NULL, flags, plain, cipher); } /* Encrypt plaintext PLAIN within CTX for the recipients RECP and * store the resulting ciphertext in CIPHER. Also sign the ciphertext * with the signers in CTX. */ gpgme_error_t gpgme_op_encrypt_sign_ext (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_sign", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_sign", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && (recp || recpstring)) { if (recp) { int i = 0; while (recp[i]) { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + TRACE_LOG ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? recp[i]->subkeys->fpr : "invalid"); i++; } } else { - TRACE_LOG1 ("recipients = '%s'", recpstring); + TRACE_LOG ("recipients = '%s'", recpstring); } } err = encrypt_sign_start (ctx, 1, recp, recpstring, flags, plain, cipher); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } /* Encrypt plaintext PLAIN within CTX for the recipients RECP and store the resulting ciphertext in CIPHER. Also sign the ciphertext with the signers in CTX. */ gpgme_error_t gpgme_op_encrypt_sign_ext_start (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_sign_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_sign_start", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && (recp || recpstring)) { if (recp) { int i = 0; while (recp[i]) { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + TRACE_LOG ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? recp[i]->subkeys->fpr : "invalid"); i++; } } else { - TRACE_LOG1 ("recipients = '%s'", recpstring); + TRACE_LOG ("recipients = '%s'", recpstring); } } err = encrypt_sign_start (ctx, 0, recp, recpstring, flags, plain, cipher); return err; } diff --git a/src/encrypt.c b/src/encrypt.c index b23d20d6..c5f668ea 100644 --- a/src/encrypt.c +++ b/src/encrypt.c @@ -1,363 +1,363 @@ /* encrypt.c - Encrypt function. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" typedef struct { struct _gpgme_op_encrypt_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The fingerprint from the last KEY_CONSIDERED status line. */ char *kc_fpr; /* The flags from the last KEY_CONSIDERED status line. */ unsigned int kc_flags; /* A pointer to the next pointer of the last invalid recipient in the list. This makes appending new invalid recipients painless while preserving the order. */ gpgme_invalid_key_t *lastp; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_invalid_key_t invalid_recipient = opd->result.invalid_recipients; while (invalid_recipient) { gpgme_invalid_key_t next = invalid_recipient->next; if (invalid_recipient->fpr) free (invalid_recipient->fpr); free (invalid_recipient); invalid_recipient = next; } free (opd->kc_fpr); } gpgme_encrypt_result_t gpgme_op_encrypt_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; - TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_result", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, -1, NULL); opd = hook; if (err || !opd) { - TRACE_SUC0 ("result=(null)"); + TRACE_SUC ("result=(null)"); return NULL; } if (_gpgme_debug_trace ()) { gpgme_invalid_key_t invkeys = opd->result.invalid_recipients; int i = 0; while (invkeys) { - TRACE_LOG3 ("invalid_recipients[%i] = %s (%s)", + TRACE_LOG ("invalid_recipients[%i] = %s (%s)", i, invkeys->fpr ? invkeys->fpr : "(null)", gpg_strerror (invkeys->reason)); invkeys = invkeys->next; i++; } } - TRACE_SUC1 ("result=%p", &opd->result); + TRACE_SUC ("result=%p", &opd->result); return &opd->result; } gpgme_error_t _gpgme_encrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: if (opd->result.invalid_recipients) return gpg_error (GPG_ERR_UNUSABLE_PUBKEY); if (opd->failure_code) return opd->failure_code; break; case GPGME_STATUS_KEY_CONSIDERED: /* This is emitted during gpg's key lookup to give information * about the lookup results. We store the last one so it can be * used in connection with INV_RECP. */ free (opd->kc_fpr); opd->kc_fpr = NULL; err = _gpgme_parse_key_considered (args, &opd->kc_fpr, &opd->kc_flags); if (err) return err; break; case GPGME_STATUS_INV_RECP: err = _gpgme_parse_inv_recp (args, 0, opd->kc_fpr, opd->kc_flags, opd->lastp); if (err) return err; opd->lastp = &(*opd->lastp)->next; free (opd->kc_fpr); opd->kc_fpr = NULL; break; case GPGME_STATUS_NO_RECP: /* Should not happen, because we require at least one recipient. */ return gpg_error (GPG_ERR_GENERAL); default: break; } return 0; } static gpgme_error_t encrypt_sym_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_passphrase_status_handler (priv, code, args); return err; } static gpgme_error_t encrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_encrypt_status_handler (priv, code, args); return err; } gpgme_error_t _gpgme_op_encrypt_init_result (gpgme_ctx_t ctx) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->lastp = &opd->result.invalid_recipients; return 0; } static gpgme_error_t encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; int symmetric = 0; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_encrypt_init_result (ctx); if (err) return err; symmetric = (!recp && !recpstring) || (flags & GPGME_ENCRYPT_SYMMETRIC); if (!plain) return gpg_error (GPG_ERR_NO_DATA); if (!cipher) return gpg_error (GPG_ERR_INV_VALUE); if (recp && !*recp) return gpg_error (GPG_ERR_INV_VALUE); if (symmetric && ctx->passphrase_cb) { /* Symmetric encryption requires a passphrase. */ err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, symmetric ? encrypt_sym_status_handler : encrypt_status_handler, ctx); return _gpgme_engine_op_encrypt (ctx->engine, recp, recpstring, flags, plain, cipher, ctx->use_armor); } /* Old version of gpgme_op_encrypt_ext without RECPSTRING. */ gpgme_error_t gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { return gpgme_op_encrypt_ext (ctx, recp, NULL, flags, plain, cipher); } /* Old version of gpgme_op_encrypt_ext_start without RECPSTRING. */ gpgme_error_t gpgme_op_encrypt_start (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { return gpgme_op_encrypt_ext_start (ctx, recp, NULL, flags, plain, cipher); } /* Encrypt plaintext PLAIN within CTX for the recipients RECP and * store the resulting ciphertext in CIPHER. RECPSTRING can be used * instead of the RECP array to directly specify recipients as LF * delimited strings; these may be any kind of recipient specification * patterns as supported by the backend. */ gpgme_error_t gpgme_op_encrypt_ext (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && (recp || recpstring)) { if (recp) { int i = 0; while (recp[i]) { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + TRACE_LOG ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? recp[i]->subkeys->fpr : "invalid"); i++; } } else { - TRACE_LOG1 ("recipients = '%s'", recpstring); + TRACE_LOG ("recipients = '%s'", recpstring); } } err = encrypt_start (ctx, 1, recp, recpstring, flags, plain, cipher); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } gpgme_error_t gpgme_op_encrypt_ext_start (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_start", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && (recp || recpstring)) { if (recp) { int i = 0; while (recp[i]) { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + TRACE_LOG ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? recp[i]->subkeys->fpr : "invalid"); i++; } } else { - TRACE_LOG1 ("recipients = '%s'", recpstring); + TRACE_LOG ("recipients = '%s'", recpstring); } } err = encrypt_start (ctx, 0, recp, recpstring, flags, plain, cipher); return TRACE_ERR (err); } diff --git a/src/engine-assuan.c b/src/engine-assuan.c index 83c0ca9f..79e826e0 100644 --- a/src/engine-assuan.c +++ b/src/engine-assuan.c @@ -1,844 +1,844 @@ /* engine-assuan.c - Low-level Assuan protocol engine * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ /* Note: This engine requires a modern Assuan server which uses gpg-error codes. In particular there is no backward compatible mapping of old Assuan error codes implemented. */ #if HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LOCALE_H #include #endif #include #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "priv-io.h" #include "sema.h" #include "assuan.h" #include "debug.h" #include "engine-backend.h" typedef struct { int fd; /* FD we talk about. */ int server_fd;/* Server FD for this connection. */ int dir; /* Inbound/Outbound, maybe given implicit? */ void *data; /* Handler-specific data. */ void *tag; /* ID from the user for gpgme_remove_io_callback. */ } iocb_data_t; /* Engine instance data. */ struct engine_llass { assuan_context_t assuan_ctx; int lc_ctype_set; int lc_messages_set; iocb_data_t status_cb; struct gpgme_io_cbs io_cbs; /* Hack for old opassuan.c interface, see there the result struct. */ gpg_error_t last_op_err; /* User provided callbacks. */ struct { gpgme_assuan_data_cb_t data_cb; void *data_cb_value; gpgme_assuan_inquire_cb_t inq_cb; void *inq_cb_value; gpgme_assuan_status_cb_t status_cb; void *status_cb_value; } user; /* Option flags. */ struct { int gpg_agent:1; /* Assume this is a gpg-agent connection. */ } opt; char request_origin[10]; /* Copy from the CTX. */ }; typedef struct engine_llass *engine_llass_t; gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine) { engine_llass_t llass = engine; return llass->last_op_err; } /* Prototypes. */ static void llass_io_event (void *engine, gpgme_event_io_t type, void *type_data); /* return the default home directory. */ static const char * llass_get_home_dir (void) { /* For this engine the home directory is not a filename but a string used to convey options. The exclamation mark is a marker to show that this is not a directory name. Options are strings delimited by a space. The only option defined for now is GPG_AGENT to enable GPG_AGENT specific commands to send to the server at connection startup. */ return "!GPG_AGENT"; } static char * llass_get_version (const char *file_name) { (void)file_name; return NULL; } static const char * llass_get_req_version (void) { return NULL; } static void close_notify_handler (int fd, void *opaque) { engine_llass_t llass = opaque; assert (fd != -1); if (llass->status_cb.fd == fd) { if (llass->status_cb.tag) llass->io_cbs.remove (llass->status_cb.tag); llass->status_cb.fd = -1; llass->status_cb.tag = NULL; } } static gpgme_error_t llass_cancel (void *engine) { engine_llass_t llass = engine; if (!llass) return gpg_error (GPG_ERR_INV_VALUE); if (llass->status_cb.fd != -1) _gpgme_io_close (llass->status_cb.fd); if (llass->assuan_ctx) { assuan_release (llass->assuan_ctx); llass->assuan_ctx = NULL; } return 0; } static gpgme_error_t llass_cancel_op (void *engine) { engine_llass_t llass = engine; if (!llass) return gpg_error (GPG_ERR_INV_VALUE); if (llass->status_cb.fd != -1) _gpgme_io_close (llass->status_cb.fd); return 0; } static void llass_release (void *engine) { engine_llass_t llass = engine; if (!llass) return; llass_cancel (engine); free (llass); } /* Create a new instance. If HOME_DIR is NULL standard options for use with gpg-agent are issued. */ static gpgme_error_t llass_new (void **engine, const char *file_name, const char *home_dir, const char *version) { gpgme_error_t err = 0; engine_llass_t llass; char *optstr; char *env_tty = NULL; (void)version; /* Not yet used. */ llass = calloc (1, sizeof *llass); if (!llass) return gpg_error_from_syserror (); llass->status_cb.fd = -1; llass->status_cb.dir = 1; llass->status_cb.tag = 0; llass->status_cb.data = llass; /* Parse_options. */ if (home_dir && *home_dir == '!') { home_dir++; /* Very simple parser only working for the one option we support. */ /* Note that wk promised to write a regression test if this parser will be extended. */ if (!strncmp (home_dir, "GPG_AGENT", 9) && (!home_dir[9] || home_dir[9] == ' ')) llass->opt.gpg_agent = 1; } err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME, &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, NULL); if (err) goto leave; assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks); assuan_set_flag (llass->assuan_ctx, ASSUAN_CONVEY_COMMENTS, 1); err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 0); if (err) goto leave; if (llass->opt.gpg_agent) { char *dft_display = NULL; err = _gpgme_getenv ("DISPLAY", &dft_display); if (err) goto leave; if (dft_display) { if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0) { err = gpg_error_from_syserror (); free (dft_display); goto leave; } free (dft_display); err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; } } if (llass->opt.gpg_agent) err = _gpgme_getenv ("GPG_TTY", &env_tty); if (llass->opt.gpg_agent && (isatty (1) || env_tty || err)) { int rc = 0; char dft_ttyname[64]; char *dft_ttytype = NULL; if (err) goto leave; else if (env_tty) { snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty); free (env_tty); } else rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); /* Even though isatty() returns 1, ttyname_r() may fail in many ways, e.g., when /dev/pts is not accessible under chroot. */ if (!rc) { if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; err = _gpgme_getenv ("TERM", &dft_ttytype); if (err) goto leave; if (dft_ttytype) { if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0) { err = gpg_error_from_syserror (); free (dft_ttytype); goto leave; } free (dft_ttytype); err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; } } } #ifdef HAVE_W32_SYSTEM /* Under Windows we need to use AllowSetForegroundWindow. Tell llass to tell us when it needs it. */ if (!err && llass->opt.gpg_agent) { err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This work only with recent gpg-agents. */ } #endif /*HAVE_W32_SYSTEM*/ leave: /* Close the server ends of the pipes (because of this, we must use the stored server_fd_str in the function start). Our ends are closed in llass_release(). */ if (err) llass_release (llass); else *engine = llass; return err; } /* Copy flags from CTX into the engine object. */ static void llass_set_engine_flags (void *engine, const gpgme_ctx_t ctx) { engine_llass_t llass = engine; if (ctx->request_origin) { if (strlen (ctx->request_origin) + 1 > sizeof llass->request_origin) strcpy (llass->request_origin, "xxx"); /* Too long - force error */ else strcpy (llass->request_origin, ctx->request_origin); } else *llass->request_origin = 0; } static gpgme_error_t llass_set_locale (void *engine, int category, const char *value) { gpgme_error_t err; engine_llass_t llass = engine; char *optstr; const char *catstr; if (!llass->opt.gpg_agent) return 0; /* FIXME: If value is NULL, we need to reset the option to default. But we can't do this. So we error out here. gpg-agent needs support for this. */ if (0) ; #ifdef LC_CTYPE else if (category == LC_CTYPE) { catstr = "lc-ctype"; if (!value && llass->lc_ctype_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) llass->lc_ctype_set = 1; } #endif #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { catstr = "lc-messages"; if (!value && llass->lc_messages_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) llass->lc_messages_set = 1; } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: Reset value to default. */ if (!value) return 0; if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0) err = gpg_error_from_syserror (); else { err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); } return err; } /* This is the inquiry callback. It handles stuff which ee need to handle here and passes everything on to the user callback. */ static gpgme_error_t inquire_cb (engine_llass_t llass, const char *keyword, const char *args) { gpg_error_t err; if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED")) { _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10)); } if (llass->user.inq_cb) { gpgme_data_t data = NULL; err = llass->user.inq_cb (llass->user.inq_cb_value, keyword, args, &data); if (!err && data) { /* FIXME: Returning data is not yet implemented. However we need to allow the caller to cleanup his data object. Thus we run the callback in finish mode immediately. */ err = llass->user.inq_cb (llass->user.inq_cb_value, NULL, NULL, &data); } } else err = 0; return err; } static gpgme_error_t llass_status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_llass_t llass = (engine_llass_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (llass->assuan_ctx, &line, &linelen); if (err) { /* Reading a full line may not be possible when communicating over a socket in nonblocking mode. In this case, we are done for now. */ if (gpg_err_code (err) == GPG_ERR_EAGAIN) { - TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass, + TRACE (DEBUG_CTX, "gpgme:llass_status_handler", llass, "fd 0x%x: EAGAIN reading assuan line (ignored)", fd); err = 0; continue; } - TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass, + TRACE (DEBUG_CTX, "gpgme:llass_status_handler", llass, "fd 0x%x: error reading assuan line: %s", fd, gpg_strerror (err)); } else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ') { char *src = line + 2; char *end = line + linelen; char *dst = src; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; if (linelen && llass->user.data_cb) err = llass->user.data_cb (llass->user.data_cb_value, src, linelen); - TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass, + TRACE (DEBUG_CTX, "gpgme:llass_status_handler", llass, "fd 0x%x: D inlinedata; status from cb: %s", fd, (llass->user.data_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (line[3] == '\0' || line[3] == ' ')) { /* END received. Tell the data callback. */ if (llass->user.data_cb) err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0); - TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass, + TRACE (DEBUG_CTX, "gpgme:llass_status_handler", llass, "fd 0x%x: END line; status from cb: %s", fd, (llass->user.data_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { char *args; char *src; for (src=line+2; *src == ' '; src++) ; args = strchr (src, ' '); if (!args) args = line + linelen; /* Let it point to an empty string. */ else *(args++) = 0; while (*args == ' ') args++; if (llass->user.status_cb) err = llass->user.status_cb (llass->user.status_cb_value, src, args); - TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass, + TRACE (DEBUG_CTX, "gpgme:llass_status_handler", llass, "fd 0x%x: S line (%s) - status from cb: %s", fd, line+2, (llass->user.status_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *src; char *args; for (src=line+7; *src == ' '; src++) ; args = strchr (src, ' '); if (!args) args = line + linelen; /* Let it point to an empty string. */ else *(args++) = 0; while (*args == ' ') args++; err = inquire_cb (llass, src, args); if (!err) { /* Flush and send END. */ err = assuan_send_data (llass->assuan_ctx, NULL, 0); } else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED) { /* Flush and send CANcel. */ err = assuan_send_data (llass->assuan_ctx, NULL, 1); } } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (line+4); else err = gpg_error (GPG_ERR_GENERAL); - TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass, + TRACE (DEBUG_CTX, "gpgme:llass_status_handler", llass, "fd 0x%x: ERR line: %s", fd, err ? gpg_strerror (err) : "ok"); /* Command execution errors are not fatal, as we use a session based protocol. */ data->op_err = err; llass->last_op_err = err; /* The caller will do the rest (namely, call cancel_op, which closes status_fd). */ return 0; } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { - TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass, + TRACE (DEBUG_CTX, "gpgme:llass_status_handler", llass, "fd 0x%x: OK line", fd); llass->last_op_err = 0; _gpgme_io_close (llass->status_cb.fd); return 0; } else { /* Comment line or invalid line. */ } } while (!err && assuan_pending_line (llass->assuan_ctx)); return err; } static gpgme_error_t add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass, + TRACE_BEG (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass, "fd %d, dir %d", iocbd->fd, iocbd->dir); err = (*llass->io_cbs.add) (llass->io_cbs.add_priv, iocbd->fd, iocbd->dir, handler, iocbd->data, &iocbd->tag); if (err) return TRACE_ERR (err); if (!iocbd->dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (iocbd->fd); return TRACE_ERR (err); } static gpgme_error_t start (engine_llass_t llass, const char *command) { gpgme_error_t err; assuan_fd_t afdlist[5]; int fdlist[5]; int nfds; int i; if (*llass->request_origin && llass->opt.gpg_agent) { char *cmd; cmd = _gpgme_strconcat ("OPTION pretend-request-origin=", llass->request_origin, NULL); if (!cmd) return gpg_error_from_syserror (); err = assuan_transact (llass->assuan_ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL); free (cmd); if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION) return err; } /* We need to know the fd used by assuan for reads. We do this by using the assumption that the first returned fd from assuan_get_active_fds() is always this one. */ nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */, afdlist, DIM (afdlist)); if (nfds < 1) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ /* For now... */ for (i = 0; i < nfds; i++) fdlist[i] = (int) afdlist[i]; /* We "duplicate" the file descriptor, so we can close it here (we can't close fdlist[0], as that is closed by libassuan, and closing it here might cause libassuan to close some unrelated FD later). Alternatively, we could special case status_fd and register/unregister it manually as needed, but this increases code duplication and is more complicated as we can not use the close notifications etc. A third alternative would be to let Assuan know that we closed the FD, but that complicates the Assuan interface. */ llass->status_cb.fd = _gpgme_io_dup (fdlist[0]); if (llass->status_cb.fd < 0) return gpg_error_from_syserror (); if (_gpgme_io_set_close_notify (llass->status_cb.fd, close_notify_handler, llass)) { _gpgme_io_close (llass->status_cb.fd); llass->status_cb.fd = -1; return gpg_error (GPG_ERR_GENERAL); } err = add_io_cb (llass, &llass->status_cb, llass_status_handler); if (!err) err = assuan_write_line (llass->assuan_ctx, command); /* FIXME: If *command == '#' no answer is expected. */ if (!err) llass_io_event (llass, GPGME_EVENT_START, NULL); return err; } static gpgme_error_t llass_transact (void *engine, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { engine_llass_t llass = engine; gpgme_error_t err; if (!llass || !command || !*command) return gpg_error (GPG_ERR_INV_VALUE); llass->user.data_cb = data_cb; llass->user.data_cb_value = data_cb_value; llass->user.inq_cb = inq_cb; llass->user.inq_cb_value = inq_cb_value; llass->user.status_cb = status_cb; llass->user.status_cb_value = status_cb_value; err = start (llass, command); return err; } static void llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_llass_t llass = engine; llass->io_cbs = *io_cbs; } static void llass_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_llass_t llass = engine; - TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass, + TRACE (DEBUG_ENGINE, "gpgme:llass_io_event", llass, "event %p, type %d, type_data %p", llass->io_cbs.event, type, type_data); if (llass->io_cbs.event) (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data); } struct engine_ops _gpgme_engine_ops_assuan = { /* Static functions. */ _gpgme_get_default_agent_socket, llass_get_home_dir, llass_get_version, llass_get_req_version, llass_new, /* Member functions. */ llass_release, NULL, /* reset */ NULL, /* set_status_cb */ NULL, /* set_status_handler */ NULL, /* set_command_handler */ NULL, /* set_colon_line_handler */ llass_set_locale, NULL, /* set_protocol */ llass_set_engine_flags, NULL, /* decrypt */ NULL, /* delete */ NULL, /* edit */ NULL, /* encrypt */ NULL, /* encrypt_sign */ NULL, /* export */ NULL, /* export_ext */ NULL, /* genkey */ NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ NULL, /* keylist_data */ NULL, /* keysign */ NULL, /* tofu_policy */ NULL, /* sign */ NULL, /* trustlist */ NULL, /* verify */ NULL, /* getauditlog */ llass_transact, /* opassuan_transact */ NULL, /* conf_load */ NULL, /* conf_save */ NULL, /* conf_dir */ NULL, /* query_swdb */ llass_set_io_cbs, llass_io_event, llass_cancel, llass_cancel_op, NULL, /* passwd */ NULL, /* set_pinentry_mode */ NULL /* opspawn */ }; diff --git a/src/engine-g13.c b/src/engine-g13.c index 90f8e7ea..edb8d54f 100644 --- a/src/engine-g13.c +++ b/src/engine-g13.c @@ -1,824 +1,824 @@ /* engine-g13.c - G13 engine. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LOCALE_H #include #endif #include /* FIXME */ #include #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "priv-io.h" #include "sema.h" #include "assuan.h" #include "debug.h" #include "engine-backend.h" typedef struct { int fd; /* FD we talk about. */ int server_fd;/* Server FD for this connection. */ int dir; /* Inbound/Outbound, maybe given implicit? */ void *data; /* Handler-specific data. */ void *tag; /* ID from the user for gpgme_remove_io_callback. */ char server_fd_str[15]; /* Same as SERVER_FD but as a string. We need this because _gpgme_io_fd2str can't be used on a closed descriptor. */ } iocb_data_t; struct engine_g13 { assuan_context_t assuan_ctx; int lc_ctype_set; int lc_messages_set; iocb_data_t status_cb; struct gpgme_io_cbs io_cbs; /* User provided callbacks. */ struct { gpgme_assuan_data_cb_t data_cb; void *data_cb_value; gpgme_assuan_inquire_cb_t inq_cb; void *inq_cb_value; gpgme_assuan_status_cb_t status_cb; void *status_cb_value; } user; }; typedef struct engine_g13 *engine_g13_t; static void g13_io_event (void *engine, gpgme_event_io_t type, void *type_data); static char * g13_get_version (const char *file_name) { return _gpgme_get_program_version (file_name ? file_name : _gpgme_get_default_g13_name ()); } static const char * g13_get_req_version (void) { return "2.1.0"; } static void close_notify_handler (int fd, void *opaque) { engine_g13_t g13 = opaque; assert (fd != -1); if (g13->status_cb.fd == fd) { if (g13->status_cb.tag) (*g13->io_cbs.remove) (g13->status_cb.tag); g13->status_cb.fd = -1; g13->status_cb.tag = NULL; } } /* This is the default inquiry callback. We use it to handle the Pinentry notifications. */ static gpgme_error_t default_inq_cb (engine_g13_t g13, const char *keyword, const char *args) { gpg_error_t err; if (!strcmp (keyword, "PINENTRY_LAUNCHED")) { _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10)); } if (g13->user.inq_cb) { gpgme_data_t data = NULL; err = g13->user.inq_cb (g13->user.inq_cb_value, keyword, args, &data); if (!err && data) { /* FIXME: Returning data is not yet implemented. However we need to allow the caller to cleanup his data object. Thus we run the callback in finish mode immediately. */ err = g13->user.inq_cb (g13->user.inq_cb_value, NULL, NULL, &data); } } else err = 0; return err; } static gpgme_error_t g13_cancel (void *engine) { engine_g13_t g13 = engine; if (!g13) return gpg_error (GPG_ERR_INV_VALUE); if (g13->status_cb.fd != -1) _gpgme_io_close (g13->status_cb.fd); if (g13->assuan_ctx) { assuan_release (g13->assuan_ctx); g13->assuan_ctx = NULL; } return 0; } static gpgme_error_t g13_cancel_op (void *engine) { engine_g13_t g13 = engine; if (!g13) return gpg_error (GPG_ERR_INV_VALUE); if (g13->status_cb.fd != -1) _gpgme_io_close (g13->status_cb.fd); return 0; } static void g13_release (void *engine) { engine_g13_t g13 = engine; if (!g13) return; g13_cancel (engine); free (g13); } static gpgme_error_t g13_new (void **engine, const char *file_name, const char *home_dir, const char *version) { gpgme_error_t err = 0; engine_g13_t g13; const char *pgmname; int argc; const char *argv[5]; char *dft_display = NULL; char dft_ttyname[64]; char *env_tty = NULL; char *dft_ttytype = NULL; char *optstr; (void)version; /* Not yet used. */ g13 = calloc (1, sizeof *g13); if (!g13) return gpg_error_from_syserror (); g13->status_cb.fd = -1; g13->status_cb.dir = 1; g13->status_cb.tag = 0; g13->status_cb.data = g13; pgmname = file_name ? file_name : _gpgme_get_default_g13_name (); argc = 0; argv[argc++] = _gpgme_get_basename (pgmname); if (home_dir) { argv[argc++] = "--homedir"; argv[argc++] = home_dir; } argv[argc++] = "--server"; argv[argc++] = NULL; err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME, &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, NULL); if (err) goto leave; assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks); #if USE_DESCRIPTOR_PASSING err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING); #else err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv, NULL, NULL, NULL, 0); #endif if (err) goto leave; err = _gpgme_getenv ("DISPLAY", &dft_display); if (err) goto leave; if (dft_display) { if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0) { free (dft_display); err = gpg_error_from_syserror (); goto leave; } free (dft_display); err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; } err = _gpgme_getenv ("GPG_TTY", &env_tty); if (isatty (1) || env_tty || err) { int rc = 0; if (err) goto leave; else if (env_tty) { snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty); free (env_tty); } else rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); /* Even though isatty() returns 1, ttyname_r() may fail in many ways, e.g., when /dev/pts is not accessible under chroot. */ if (!rc) { if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; err = _gpgme_getenv ("TERM", &dft_ttytype); if (err) goto leave; if (dft_ttytype) { if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0) { free (dft_ttytype); err = gpg_error_from_syserror (); goto leave; } free (dft_ttytype); err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; } } } #ifdef HAVE_W32_SYSTEM /* Under Windows we need to use AllowSetForegroundWindow. Tell g13 to tell us when it needs it. */ if (!err) { err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is a new feature of g13. */ } #endif /*HAVE_W32_SYSTEM*/ leave: if (err) g13_release (g13); else *engine = g13; return err; } static gpgme_error_t g13_set_locale (void *engine, int category, const char *value) { engine_g13_t g13 = engine; gpgme_error_t err; char *optstr; const char *catstr; /* FIXME: If value is NULL, we need to reset the option to default. But we can't do this. So we error out here. G13 needs support for this. */ if (0) ; #ifdef LC_CTYPE else if (category == LC_CTYPE) { catstr = "lc-ctype"; if (!value && g13->lc_ctype_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) g13->lc_ctype_set = 1; } #endif #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { catstr = "lc-messages"; if (!value && g13->lc_messages_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) g13->lc_messages_set = 1; } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: Reset value to default. */ if (!value) return 0; if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0) err = gpg_error_from_syserror (); else { err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); } return err; } #if USE_DESCRIPTOR_PASSING static gpgme_error_t g13_assuan_simple_command (assuan_context_t ctx, const char *cmd, engine_status_handler_t status_fnc, void *status_fnc_value) { gpg_error_t err; char *line; size_t linelen; (void)status_fnc; (void)status_fnc_value; err = assuan_write_line (ctx, cmd); if (err) return err; do { err = assuan_read_line (ctx, &line, &linelen); if (err) return err; if (*line == '#' || !linelen) continue; if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) return 0; else if (linelen >= 4 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && line[3] == ' ') err = atoi (&line[4]); else if (linelen >= 2 && line[0] == 'S' && line[1] == ' ') { char *rest; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; /* Nothing to do with status lines. */ } else err = gpg_error (GPG_ERR_GENERAL); } while (!err); return err; } #endif static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_g13_t g13 = (engine_g13_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (g13->assuan_ctx, &line, &linelen); if (err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (g13->assuan_ctx, "BYE"); */ - TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13, + TRACE (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: error reading assuan line: %s", fd, gpg_strerror (err)); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (&line[4]); if (! err) err = gpg_error (GPG_ERR_GENERAL); - TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13, + TRACE (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: ERR line: %s", fd, err ? gpg_strerror (err) : "ok"); /* Command execution errors are not fatal, as we use a session based protocol. */ data->op_err = err; /* The caller will do the rest (namely, call cancel_op, which closes status_fd). */ return 0; } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { - TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13, + TRACE (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: OK line", fd); _gpgme_io_close (g13->status_cb.fd); return 0; } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ') { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst = src; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; if (linelen && g13->user.data_cb) err = g13->user.data_cb (g13->user.data_cb_value, src, linelen); else err = 0; - TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13, + TRACE (DEBUG_CTX, "gpgme:g13_status_handler", g13, "fd 0x%x: D inlinedata; status from cb: %s", fd, (g13->user.data_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { char *src; char *args; src = line + 2; while (*src == ' ') src++; args = strchr (line + 2, ' '); if (!args) args = line + linelen; /* set to an empty string */ else *(args++) = 0; while (*args == ' ') args++; if (g13->user.status_cb) err = g13->user.status_cb (g13->user.status_cb_value, src, args); else err = 0; - TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13, + TRACE (DEBUG_CTX, "gpgme:g13_status_handler", g13, "fd 0x%x: S line (%s) - status from cb: %s", fd, line+2, (g13->user.status_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *src; char *args; for (src=line+7; *src == ' '; src++) ; args = strchr (src, ' '); if (!args) args = line + linelen; /* Let it point to an empty string. */ else *(args++) = 0; while (*args == ' ') args++; err = default_inq_cb (g13, src, args); if (!err) { /* Flush and send END. */ err = assuan_send_data (g13->assuan_ctx, NULL, 0); } else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED) { /* Flush and send CANcel. */ err = assuan_send_data (g13->assuan_ctx, NULL, 1); } assuan_write_line (g13->assuan_ctx, "END"); } } while (!err && assuan_pending_line (g13->assuan_ctx)); return err; } static gpgme_error_t add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13, + TRACE_BEG (DEBUG_ENGINE, "engine-g13:add_io_cb", g13, "fd %d, dir %d", iocbd->fd, iocbd->dir); err = (*g13->io_cbs.add) (g13->io_cbs.add_priv, iocbd->fd, iocbd->dir, handler, iocbd->data, &iocbd->tag); if (err) return TRACE_ERR (err); if (!iocbd->dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (iocbd->fd); return TRACE_ERR (err); } static gpgme_error_t start (engine_g13_t g13, const char *command) { gpgme_error_t err; assuan_fd_t afdlist[5]; int fdlist[5]; int nfds; int i; /* We need to know the fd used by assuan for reads. We do this by using the assumption that the first returned fd from assuan_get_active_fds() is always this one. */ nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */, afdlist, DIM (afdlist)); if (nfds < 1) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ /* For now... */ for (i = 0; i < nfds; i++) fdlist[i] = (int) afdlist[i]; /* We "duplicate" the file descriptor, so we can close it here (we can't close fdlist[0], as that is closed by libassuan, and closing it here might cause libassuan to close some unrelated FD later). Alternatively, we could special case status_fd and register/unregister it manually as needed, but this increases code duplication and is more complicated as we can not use the close notifications etc. A third alternative would be to let Assuan know that we closed the FD, but that complicates the Assuan interface. */ g13->status_cb.fd = _gpgme_io_dup (fdlist[0]); if (g13->status_cb.fd < 0) return gpg_error_from_syserror (); if (_gpgme_io_set_close_notify (g13->status_cb.fd, close_notify_handler, g13)) { _gpgme_io_close (g13->status_cb.fd); g13->status_cb.fd = -1; return gpg_error (GPG_ERR_GENERAL); } err = add_io_cb (g13, &g13->status_cb, status_handler); if (!err) err = assuan_write_line (g13->assuan_ctx, command); if (!err) g13_io_event (g13, GPGME_EVENT_START, NULL); return err; } #if USE_DESCRIPTOR_PASSING static gpgme_error_t g13_reset (void *engine) { engine_g13_t g13 = engine; /* We must send a reset because we need to reset the list of signers. Note that RESET does not reset OPTION commands. */ return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL); } #endif static gpgme_error_t g13_transact (void *engine, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { engine_g13_t g13 = engine; gpgme_error_t err; if (!g13 || !command || !*command) return gpg_error (GPG_ERR_INV_VALUE); g13->user.data_cb = data_cb; g13->user.data_cb_value = data_cb_value; g13->user.inq_cb = inq_cb; g13->user.inq_cb_value = inq_cb_value; g13->user.status_cb = status_cb; g13->user.status_cb_value = status_cb_value; err = start (g13, command); return err; } static void g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_g13_t g13 = engine; g13->io_cbs = *io_cbs; } static void g13_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_g13_t g13 = engine; - TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13, + TRACE (DEBUG_ENGINE, "gpgme:g13_io_event", g13, "event %p, type %d, type_data %p", g13->io_cbs.event, type, type_data); if (g13->io_cbs.event) (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data); } struct engine_ops _gpgme_engine_ops_g13 = { /* Static functions. */ _gpgme_get_default_g13_name, NULL, g13_get_version, g13_get_req_version, g13_new, /* Member functions. */ g13_release, #if USE_DESCRIPTOR_PASSING g13_reset, #else NULL, /* reset */ #endif NULL, /* set_status_cb */ NULL, /* set_status_handler */ NULL, /* set_command_handler */ NULL, /* set_colon_line_handler */ g13_set_locale, NULL, /* set_protocol */ NULL, /* set_engine_flags */ NULL, /* decrypt */ NULL, /* delete */ NULL, /* edit */ NULL, /* encrypt */ NULL, /* encrypt_sign */ NULL, /* export */ NULL, /* export_ext */ NULL, /* genkey */ NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ NULL, /* keylist_data */ NULL, /* keysign */ NULL, /* tofu_policy */ NULL, /* sign */ NULL, /* trustlist */ NULL, /* verify */ NULL, /* getauditlog */ g13_transact, NULL, /* conf_load */ NULL, /* conf_save */ NULL, /* conf_dir */ NULL, /* query_swdb */ g13_set_io_cbs, g13_io_event, g13_cancel, g13_cancel_op, NULL, /* passwd */ NULL, /* set_pinentry_mode */ NULL /* opspawn */ }; diff --git a/src/engine-gpg.c b/src/engine-gpg.c index c88e970a..31d219a3 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -1,3419 +1,3419 @@ /* engine-gpg.c - Gpg Engine. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2009, 2010, 2012, 2013 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LOCALE_H #include #endif #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "context.h" /*temp hack until we have GpmeData methods to do I/O */ #include "priv-io.h" #include "sema.h" #include "debug.h" #include "data.h" #include "mbox-util.h" #include "engine-backend.h" /* This type is used to build a list of gpg arguments and data sources/sinks. */ struct arg_and_data_s { struct arg_and_data_s *next; gpgme_data_t data; /* If this is not NULL, use arg below. */ int inbound; /* True if this is used for reading from gpg. */ int dup_to; int print_fd; /* Print the fd number and not the special form of it. */ int *arg_locp; /* Write back the argv idx of this argument when building command line to this location. */ char arg[1]; /* Used if data above is not used. */ }; struct fd_data_map_s { gpgme_data_t data; int inbound; /* true if this is used for reading from gpg */ int dup_to; int fd; /* the fd to use */ int peer_fd; /* the other side of the pipe */ int arg_loc; /* The index into the argv for translation purposes. */ void *tag; }; /* NB.: R_LINE is allocated an gpgrt function and thus gpgrt_free * shall be used to release it. This takes care of custom memory * allocators and avoids problems on Windows with different runtimes * used for libgpg-error/gpgrt and gpgme. */ typedef gpgme_error_t (*colon_preprocessor_t) (char *line, char **rline); struct engine_gpg { char *file_name; char *version; char *lc_messages; char *lc_ctype; struct arg_and_data_s *arglist; struct arg_and_data_s **argtail; struct { int fd[2]; int arg_loc; size_t bufsize; char *buffer; size_t readpos; int eof; engine_status_handler_t fnc; void *fnc_value; gpgme_status_cb_t mon_cb; void *mon_cb_value; void *tag; } status; /* This is a kludge - see the comment at colon_line_handler. */ struct { int fd[2]; int arg_loc; size_t bufsize; char *buffer; size_t readpos; int eof; engine_colon_line_handler_t fnc; /* this indicate use of this structrue */ void *fnc_value; void *tag; colon_preprocessor_t preprocess_fnc; } colon; char **argv; struct fd_data_map_s *fd_data_map; /* stuff needed for interactive (command) mode */ struct { int used; int fd; void *cb_data; int idx; /* Index in fd_data_map */ gpgme_status_code_t code; /* last code */ char *keyword; /* what has been requested (malloced) */ engine_command_handler_t fnc; void *fnc_value; } cmd; struct gpgme_io_cbs io_cbs; gpgme_pinentry_mode_t pinentry_mode; char request_origin[10]; char *auto_key_locate; char *trust_model; struct { unsigned int no_symkey_cache : 1; unsigned int offline : 1; unsigned int ignore_mdc_error : 1; } flags; /* NULL or the data object fed to --override_session_key-fd. */ gpgme_data_t override_session_key; /* Memory data containing diagnostics (--logger-fd) of gpg */ gpgme_data_t diagnostics; }; typedef struct engine_gpg *engine_gpg_t; static void gpg_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_gpg_t gpg = engine; - TRACE3 (DEBUG_ENGINE, "gpgme:gpg_io_event", gpg, + TRACE (DEBUG_ENGINE, "gpgme:gpg_io_event", gpg, "event %p, type %d, type_data %p", gpg->io_cbs.event, type, type_data); if (gpg->io_cbs.event) (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data); } static void close_notify_handler (int fd, void *opaque) { engine_gpg_t gpg = opaque; assert (fd != -1); if (gpg->status.fd[0] == fd) { if (gpg->status.tag) (*gpg->io_cbs.remove) (gpg->status.tag); gpg->status.fd[0] = -1; } else if (gpg->status.fd[1] == fd) gpg->status.fd[1] = -1; else if (gpg->colon.fd[0] == fd) { if (gpg->colon.tag) (*gpg->io_cbs.remove) (gpg->colon.tag); gpg->colon.fd[0] = -1; } else if (gpg->colon.fd[1] == fd) gpg->colon.fd[1] = -1; else if (gpg->cmd.fd == fd) gpg->cmd.fd = -1; else if (gpg->fd_data_map) { int i; for (i = 0; gpg->fd_data_map[i].data; i++) { if (gpg->fd_data_map[i].fd == fd) { if (gpg->fd_data_map[i].tag) (*gpg->io_cbs.remove) (gpg->fd_data_map[i].tag); gpg->fd_data_map[i].fd = -1; break; } if (gpg->fd_data_map[i].peer_fd == fd) { gpg->fd_data_map[i].peer_fd = -1; break; } } } } /* If FRONT is true, push at the front of the list. Use this for options added late in the process. */ static gpgme_error_t _add_arg (engine_gpg_t gpg, const char *prefix, const char *arg, size_t arglen, int front, int *arg_locp) { struct arg_and_data_s *a; size_t prefixlen = prefix? strlen (prefix) : 0; assert (gpg); assert (arg); a = malloc (sizeof *a + prefixlen + arglen); if (!a) return gpg_error_from_syserror (); a->data = NULL; a->dup_to = -1; a->arg_locp = arg_locp; if (prefixlen) memcpy (a->arg, prefix, prefixlen); memcpy (a->arg + prefixlen, arg, arglen); a->arg[prefixlen + arglen] = 0; if (front) { a->next = gpg->arglist; if (!gpg->arglist) { /* If this is the first argument, we need to update the tail pointer. */ gpg->argtail = &a->next; } gpg->arglist = a; } else { a->next = NULL; *gpg->argtail = a; gpg->argtail = &a->next; } return 0; } static gpgme_error_t add_arg_ext (engine_gpg_t gpg, const char *arg, int front) { return _add_arg (gpg, NULL, arg, strlen (arg), front, NULL); } static gpgme_error_t add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp) { return _add_arg (gpg, NULL, arg, strlen (arg), 0, locp); } static gpgme_error_t add_arg (engine_gpg_t gpg, const char *arg) { return _add_arg (gpg, NULL, arg, strlen (arg), 0, NULL); } static gpgme_error_t add_arg_pfx (engine_gpg_t gpg, const char *prefix, const char *arg) { return _add_arg (gpg, prefix, arg, strlen (arg), 0, NULL); } static gpgme_error_t add_arg_len (engine_gpg_t gpg, const char *prefix, const char *arg, size_t arglen) { return _add_arg (gpg, prefix, arg, arglen, 0, NULL); } static gpgme_error_t add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound) { struct arg_and_data_s *a; assert (gpg); assert (data); a = malloc (sizeof *a - 1); if (!a) return gpg_error_from_syserror (); a->next = NULL; a->data = data; a->inbound = inbound; a->arg_locp = NULL; if (dup_to == -2) { a->print_fd = 1; a->dup_to = -1; } else { a->print_fd = 0; a->dup_to = dup_to; } *gpg->argtail = a; gpg->argtail = &a->next; return 0; } /* Return true if the engine's version is at least VERSION. */ static int have_gpg_version (engine_gpg_t gpg, const char *version) { return _gpgme_compare_versions (gpg->version, version); } static char * gpg_get_version (const char *file_name) { return _gpgme_get_program_version (file_name ? file_name : _gpgme_get_default_gpg_name ()); } static const char * gpg_get_req_version (void) { return "1.4.0"; } static void free_argv (char **argv) { int i; for (i = 0; argv[i]; i++) free (argv[i]); free (argv); } static void free_fd_data_map (struct fd_data_map_s *fd_data_map) { int i; if (!fd_data_map) return; for (i = 0; fd_data_map[i].data; i++) { if (fd_data_map[i].fd != -1) _gpgme_io_close (fd_data_map[i].fd); if (fd_data_map[i].peer_fd != -1) _gpgme_io_close (fd_data_map[i].peer_fd); /* Don't release data because this is only a reference. */ } free (fd_data_map); } static gpgme_error_t gpg_cancel (void *engine) { engine_gpg_t gpg = engine; if (!gpg) return gpg_error (GPG_ERR_INV_VALUE); /* If gpg may be waiting for a cmd, close the cmd fd first. On Windows, close operations block on the reader/writer thread. */ if (gpg->cmd.used) { if (gpg->cmd.fd != -1) _gpgme_io_close (gpg->cmd.fd); else if (gpg->fd_data_map && gpg->fd_data_map[gpg->cmd.idx].fd != -1) _gpgme_io_close (gpg->fd_data_map[gpg->cmd.idx].fd); } if (gpg->status.fd[0] != -1) _gpgme_io_close (gpg->status.fd[0]); if (gpg->status.fd[1] != -1) _gpgme_io_close (gpg->status.fd[1]); if (gpg->colon.fd[0] != -1) _gpgme_io_close (gpg->colon.fd[0]); if (gpg->colon.fd[1] != -1) _gpgme_io_close (gpg->colon.fd[1]); if (gpg->fd_data_map) { free_fd_data_map (gpg->fd_data_map); gpg->fd_data_map = NULL; } return 0; } static void gpg_release (void *engine) { engine_gpg_t gpg = engine; if (!gpg) return; gpg_cancel (engine); if (gpg->file_name) free (gpg->file_name); if (gpg->version) free (gpg->version); if (gpg->lc_messages) free (gpg->lc_messages); if (gpg->lc_ctype) free (gpg->lc_ctype); while (gpg->arglist) { struct arg_and_data_s *next = gpg->arglist->next; free (gpg->arglist); gpg->arglist = next; } if (gpg->status.buffer) free (gpg->status.buffer); if (gpg->colon.buffer) free (gpg->colon.buffer); if (gpg->argv) free_argv (gpg->argv); if (gpg->cmd.keyword) free (gpg->cmd.keyword); free (gpg->auto_key_locate); free (gpg->trust_model); gpgme_data_release (gpg->override_session_key); gpgme_data_release (gpg->diagnostics); free (gpg); } static gpgme_error_t gpg_new (void **engine, const char *file_name, const char *home_dir, const char *version) { engine_gpg_t gpg; gpgme_error_t rc = 0; char *dft_display = NULL; char dft_ttyname[64]; char *dft_ttytype = NULL; char *env_tty = NULL; gpg = calloc (1, sizeof *gpg); if (!gpg) return gpg_error_from_syserror (); if (file_name) { gpg->file_name = strdup (file_name); if (!gpg->file_name) { rc = gpg_error_from_syserror (); goto leave; } } if (version) { gpg->version = strdup (version); if (!gpg->version) { rc = gpg_error_from_syserror (); goto leave; } } gpg->argtail = &gpg->arglist; gpg->status.fd[0] = -1; gpg->status.fd[1] = -1; gpg->colon.fd[0] = -1; gpg->colon.fd[1] = -1; gpg->cmd.fd = -1; gpg->cmd.idx = -1; /* Allocate the read buffer for the status pipe. */ gpg->status.bufsize = 1024; gpg->status.readpos = 0; gpg->status.buffer = malloc (gpg->status.bufsize); if (!gpg->status.buffer) { rc = gpg_error_from_syserror (); goto leave; } /* In any case we need a status pipe - create it right here and don't handle it with our generic gpgme_data_t mechanism. */ if (_gpgme_io_pipe (gpg->status.fd, 1) == -1) { rc = gpg_error_from_syserror (); goto leave; } if (_gpgme_io_set_close_notify (gpg->status.fd[0], close_notify_handler, gpg) || _gpgme_io_set_close_notify (gpg->status.fd[1], close_notify_handler, gpg)) { rc = gpg_error (GPG_ERR_GENERAL); goto leave; } gpg->status.eof = 0; if (home_dir) { rc = add_arg (gpg, "--homedir"); if (!rc) rc = add_arg (gpg, home_dir); if (rc) goto leave; } rc = add_arg (gpg, "--status-fd"); if (rc) goto leave; { char buf[25]; _gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]); rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc); if (rc) goto leave; } rc = add_arg (gpg, "--no-tty"); if (!rc) rc = add_arg (gpg, "--charset"); if (!rc) rc = add_arg (gpg, "utf8"); if (!rc) rc = add_arg (gpg, "--enable-progress-filter"); if (!rc && have_gpg_version (gpg, "2.1.11")) rc = add_arg (gpg, "--exit-on-status-write-error"); if (rc) goto leave; rc = _gpgme_getenv ("DISPLAY", &dft_display); if (rc) goto leave; if (dft_display) { rc = add_arg (gpg, "--display"); if (!rc) rc = add_arg (gpg, dft_display); free (dft_display); if (rc) goto leave; } rc = _gpgme_getenv ("GPG_TTY", &env_tty); if (isatty (1) || env_tty || rc) { int err = 0; if (rc) goto leave; else if (env_tty) { snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty); free (env_tty); } else err = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); /* Even though isatty() returns 1, ttyname_r() may fail in many ways, e.g., when /dev/pts is not accessible under chroot. */ if (!err) { if (*dft_ttyname) { rc = add_arg (gpg, "--ttyname"); if (!rc) rc = add_arg (gpg, dft_ttyname); } else rc = 0; if (!rc) { rc = _gpgme_getenv ("TERM", &dft_ttytype); if (rc) goto leave; if (dft_ttytype) { rc = add_arg (gpg, "--ttytype"); if (!rc) rc = add_arg (gpg, dft_ttytype); } free (dft_ttytype); } if (rc) goto leave; } } rc = gpgme_data_new (&gpg->diagnostics); if (rc) goto leave; rc = add_arg (gpg, "--logger-fd"); if (rc) goto leave; rc = add_data (gpg, gpg->diagnostics, -2, 1); leave: if (rc) gpg_release (gpg); else *engine = gpg; return rc; } /* Copy flags from CTX into the engine object. */ static void gpg_set_engine_flags (void *engine, const gpgme_ctx_t ctx) { engine_gpg_t gpg = engine; if (ctx->request_origin && have_gpg_version (gpg, "2.2.6")) { if (strlen (ctx->request_origin) + 1 > sizeof gpg->request_origin) strcpy (gpg->request_origin, "xxx"); /* Too long - force error */ else strcpy (gpg->request_origin, ctx->request_origin); } else *gpg->request_origin = 0; if (ctx->auto_key_locate && have_gpg_version (gpg, "2.1.18")) { if (gpg->auto_key_locate) free (gpg->auto_key_locate); gpg->auto_key_locate = _gpgme_strconcat ("--auto-key-locate=", ctx->auto_key_locate, NULL); } if (ctx->trust_model && strlen (ctx->trust_model)) { if (gpg->trust_model) free (gpg->trust_model); gpg->trust_model = _gpgme_strconcat ("--trust-model=", ctx->trust_model, NULL); } gpg->flags.no_symkey_cache = (ctx->no_symkey_cache && have_gpg_version (gpg, "2.2.7")); gpg->flags.offline = (ctx->offline && have_gpg_version (gpg, "2.1.23")); gpg->flags.ignore_mdc_error = !!ctx->ignore_mdc_error; } static gpgme_error_t gpg_set_locale (void *engine, int category, const char *value) { engine_gpg_t gpg = engine; if (0) ; #ifdef LC_CTYPE else if (category == LC_CTYPE) { if (gpg->lc_ctype) { free (gpg->lc_ctype); gpg->lc_ctype = NULL; } if (value) { gpg->lc_ctype = strdup (value); if (!gpg->lc_ctype) return gpg_error_from_syserror (); } } #endif #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { if (gpg->lc_messages) { free (gpg->lc_messages); gpg->lc_messages = NULL; } if (value) { gpg->lc_messages = strdup (value); if (!gpg->lc_messages) return gpg_error_from_syserror (); } } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); return 0; } /* This sets a status callback for monitoring status lines before they * are passed to a caller set handler. */ static void gpg_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value) { engine_gpg_t gpg = engine; gpg->status.mon_cb = cb; gpg->status.mon_cb_value = cb_value; } /* Note, that the status_handler is allowed to modify the args value. */ static void gpg_set_status_handler (void *engine, engine_status_handler_t fnc, void *fnc_value) { engine_gpg_t gpg = engine; gpg->status.fnc = fnc; gpg->status.fnc_value = fnc_value; } /* Kludge to process --with-colon output. */ static gpgme_error_t gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc, void *fnc_value) { engine_gpg_t gpg = engine; gpg->colon.bufsize = 1024; gpg->colon.readpos = 0; gpg->colon.buffer = malloc (gpg->colon.bufsize); if (!gpg->colon.buffer) return gpg_error_from_syserror (); if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) { int saved_err = gpg_error_from_syserror (); free (gpg->colon.buffer); gpg->colon.buffer = NULL; return saved_err; } if (_gpgme_io_set_close_notify (gpg->colon.fd[0], close_notify_handler, gpg) || _gpgme_io_set_close_notify (gpg->colon.fd[1], close_notify_handler, gpg)) return gpg_error (GPG_ERR_GENERAL); gpg->colon.eof = 0; gpg->colon.fnc = fnc; gpg->colon.fnc_value = fnc_value; return 0; } static gpgme_error_t command_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpg_t gpg = (engine_gpg_t) data->handler_value; gpgme_error_t err; int processed = 0; assert (gpg->cmd.used); assert (gpg->cmd.code); assert (gpg->cmd.fnc); err = gpg->cmd.fnc (gpg->cmd.fnc_value, gpg->cmd.code, gpg->cmd.keyword, fd, &processed); gpg->cmd.code = 0; /* And sleep again until read_status will wake us up again. */ /* XXX We must check if there are any more fds active after removing this one. */ (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag); gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd; gpg->fd_data_map[gpg->cmd.idx].fd = -1; if (err) return err; /* We always need to send at least a newline character. */ if (!processed) _gpgme_io_write (fd, "\n", 1); return 0; } /* The FNC will be called to get a value for one of the commands with * a key KEY. If the code passed to FNC is 0, the function may * release resources associated with the returned value from another * call. To match such a second call to a first call, the returned * value from the first call is passed as keyword. */ static gpgme_error_t gpg_set_command_handler (void *engine, engine_command_handler_t fnc, void *fnc_value) { engine_gpg_t gpg = engine; gpgme_error_t rc; rc = add_arg (gpg, "--command-fd"); if (rc) return rc; /* This is a hack. We don't have a real data object. The only thing that matters is that we use something unique, so we use the address of the cmd structure in the gpg object. */ rc = add_data (gpg, (void *) &gpg->cmd, -2, 0); if (rc) return rc; gpg->cmd.fnc = fnc; gpg->cmd.cb_data = (void *) &gpg->cmd; gpg->cmd.fnc_value = fnc_value; gpg->cmd.used = 1; return 0; } static gpgme_error_t build_argv (engine_gpg_t gpg, const char *pgmname) { gpgme_error_t err; struct arg_and_data_s *a; struct fd_data_map_s *fd_data_map; size_t datac=0, argc=0; char **argv; int need_special = 0; int use_agent = 0; char *p; if (_gpgme_in_gpg_one_mode ()) { /* In GnuPG-1 mode we don't want to use the agent with a malformed environment variable. This is only a very basic test but sufficient to make our life in the regression tests easier. With GnuPG-2 the agent is anyway required and on modern installations GPG_AGENT_INFO is optional. */ err = _gpgme_getenv ("GPG_AGENT_INFO", &p); if (err) return err; use_agent = (p && strchr (p, ':')); if (p) free (p); } if (gpg->argv) { free_argv (gpg->argv); gpg->argv = NULL; } if (gpg->fd_data_map) { free_fd_data_map (gpg->fd_data_map); gpg->fd_data_map = NULL; } argc++; /* For argv[0]. */ for (a = gpg->arglist; a; a = a->next) { argc++; if (a->data) { /*fprintf (stderr, "build_argv: data\n" );*/ datac++; if (a->dup_to == -1 && !a->print_fd) need_special = 1; } else { /* fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/ } } if (need_special) argc++; if (use_agent) argc++; if (gpg->pinentry_mode) argc++; if (!gpg->cmd.used) argc++; /* --batch */ argc += 4; /* --no-sk-comments, --request-origin, --no-symkey-cache */ /* --disable-dirmngr */ argv = calloc (argc + 1, sizeof *argv); if (!argv) return gpg_error_from_syserror (); fd_data_map = calloc (datac + 1, sizeof *fd_data_map); if (!fd_data_map) { int saved_err = gpg_error_from_syserror (); free_argv (argv); return saved_err; } argc = datac = 0; argv[argc] = strdup (_gpgme_get_basename (pgmname)); /* argv[0] */ if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; if (need_special) { argv[argc] = strdup ("--enable-special-filenames"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } if (use_agent) { argv[argc] = strdup ("--use-agent"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } if (*gpg->request_origin) { argv[argc] = _gpgme_strconcat ("--request-origin=", gpg->request_origin, NULL); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } if (gpg->auto_key_locate) { argv[argc] = strdup (gpg->auto_key_locate); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } if (gpg->trust_model) { argv[argc] = strdup (gpg->trust_model); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } if (gpg->flags.no_symkey_cache) { argv[argc] = strdup ("--no-symkey-cache"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } if (gpg->flags.ignore_mdc_error) { argv[argc] = strdup ("--ignore-mdc-error"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } if (gpg->flags.offline) { argv[argc] = strdup ("--disable-dirmngr"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } if (gpg->pinentry_mode && have_gpg_version (gpg, "2.1.0")) { const char *s = NULL; switch (gpg->pinentry_mode) { case GPGME_PINENTRY_MODE_DEFAULT: break; case GPGME_PINENTRY_MODE_ASK: s = "--pinentry-mode=ask"; break; case GPGME_PINENTRY_MODE_CANCEL: s = "--pinentry-mode=cancel"; break; case GPGME_PINENTRY_MODE_ERROR: s = "--pinentry-mode=error"; break; case GPGME_PINENTRY_MODE_LOOPBACK:s = "--pinentry-mode=loopback"; break; } if (s) { argv[argc] = strdup (s); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } } if (!gpg->cmd.used) { argv[argc] = strdup ("--batch"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } argv[argc] = strdup ("--no-sk-comments"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; for (a = gpg->arglist; a; a = a->next) { if (a->arg_locp) *(a->arg_locp) = argc; if (a->data) { /* Create a pipe to pass it down to gpg. */ fd_data_map[datac].inbound = a->inbound; /* Create a pipe. */ { int fds[2]; if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound ? 1 : 0) == -1) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, gpg) || _gpgme_io_set_close_notify (fds[1], close_notify_handler, gpg)) { /* We leak fd_data_map and the fds. This is not easy to avoid and given that we reach this here only after a malloc failure for a small object, it is probably better not to do anything. */ return gpg_error (GPG_ERR_GENERAL); } /* If the data_type is FD, we have to do a dup2 here. */ if (fd_data_map[datac].inbound) { fd_data_map[datac].fd = fds[0]; fd_data_map[datac].peer_fd = fds[1]; } else { fd_data_map[datac].fd = fds[1]; fd_data_map[datac].peer_fd = fds[0]; } } /* Hack to get hands on the fd later. */ if (gpg->cmd.used) { if (gpg->cmd.cb_data == a->data) { assert (gpg->cmd.idx == -1); gpg->cmd.idx = datac; } } fd_data_map[datac].data = a->data; fd_data_map[datac].dup_to = a->dup_to; if (a->dup_to == -1) { char *ptr; int buflen = 25; argv[argc] = malloc (buflen); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } ptr = argv[argc]; if (!a->print_fd) { *(ptr++) = '-'; *(ptr++) = '&'; buflen -= 2; } _gpgme_io_fd2str (ptr, buflen, fd_data_map[datac].peer_fd); fd_data_map[datac].arg_loc = argc; argc++; } datac++; } else { argv[argc] = strdup (a->arg); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } } gpg->argv = argv; gpg->fd_data_map = fd_data_map; return 0; } static gpgme_error_t add_io_cb (engine_gpg_t gpg, int fd, int dir, gpgme_io_cb_t handler, void *data, void **tag) { gpgme_error_t err; err = (*gpg->io_cbs.add) (gpg->io_cbs.add_priv, fd, dir, handler, data, tag); if (err) return err; if (!dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (fd); return err; } /* Handle the status output of GnuPG. This function does read entire lines and passes them as C strings to the callback function (we can use C Strings because the status output is always UTF-8 encoded). Of course we have to buffer the lines to cope with long lines e.g. with a large user ID. Note: We can optimize this to only cope with status line code we know about and skip all other stuff without buffering (i.e. without extending the buffer). */ static gpgme_error_t read_status (engine_gpg_t gpg) { char *p; int nread; size_t bufsize = gpg->status.bufsize; char *buffer = gpg->status.buffer; size_t readpos = gpg->status.readpos; gpgme_error_t err; assert (buffer); if (bufsize - readpos < 256) { /* Need more room for the read. */ bufsize += 1024; buffer = realloc (buffer, bufsize); if (!buffer) return gpg_error_from_syserror (); } nread = _gpgme_io_read (gpg->status.fd[0], buffer + readpos, bufsize-readpos); if (nread == -1) return gpg_error_from_syserror (); if (!nread) { err = 0; gpg->status.eof = 1; if (gpg->status.mon_cb) err = gpg->status.mon_cb (gpg->status.mon_cb_value, "", ""); if (gpg->status.fnc) { char emptystring[1] = {0}; err = gpg->status.fnc (gpg->status.fnc_value, GPGME_STATUS_EOF, emptystring); if (gpg_err_code (err) == GPG_ERR_FALSE) err = 0; /* Drop special error code. */ } return err; } while (nread > 0) { for (p = buffer + readpos; nread; nread--, p++) { if (*p == '\n') { /* (we require that the last line is terminated by a LF) */ if (p > buffer && p[-1] == '\r') p[-1] = 0; *p = 0; if (!strncmp (buffer, "[GNUPG:] ", 9) && buffer[9] >= 'A' && buffer[9] <= 'Z') { char *rest; gpgme_status_code_t r; rest = strchr (buffer + 9, ' '); if (!rest) rest = p; /* Set to an empty string. */ else *rest++ = 0; r = _gpgme_parse_status (buffer + 9); if (gpg->status.mon_cb && r != GPGME_STATUS_PROGRESS) { /* Note that we call the monitor even if we do * not know the status code (r < 0). */ err = gpg->status.mon_cb (gpg->status.mon_cb_value, buffer + 9, rest); if (err) return err; } if (r >= 0) { if (gpg->cmd.used && (r == GPGME_STATUS_GET_BOOL || r == GPGME_STATUS_GET_LINE || r == GPGME_STATUS_GET_HIDDEN)) { gpg->cmd.code = r; if (gpg->cmd.keyword) free (gpg->cmd.keyword); gpg->cmd.keyword = strdup (rest); if (!gpg->cmd.keyword) return gpg_error_from_syserror (); /* This should be the last thing we have received and the next thing will be that the command handler does its action. */ if (nread > 1) - TRACE0 (DEBUG_CTX, "gpgme:read_status", 0, + TRACE (DEBUG_CTX, "gpgme:read_status", 0, "error: unexpected data"); add_io_cb (gpg, gpg->cmd.fd, 0, command_handler, gpg, &gpg->fd_data_map[gpg->cmd.idx].tag); gpg->fd_data_map[gpg->cmd.idx].fd = gpg->cmd.fd; gpg->cmd.fd = -1; } else if (gpg->status.fnc) { err = gpg->status.fnc (gpg->status.fnc_value, r, rest); if (gpg_err_code (err) == GPG_ERR_FALSE) err = 0; /* Drop special error code. */ if (err) return err; } } } /* To reuse the buffer for the next line we have to shift the remaining data to the buffer start and restart the loop Hmmm: We can optimize this function by looking forward in the buffer to see whether a second complete line is available and in this case avoid the memmove for this line. */ nread--; p++; if (nread) memmove (buffer, p, nread); readpos = 0; break; /* the for loop */ } else readpos++; } } /* Update the gpg object. */ gpg->status.bufsize = bufsize; gpg->status.buffer = buffer; gpg->status.readpos = readpos; return 0; } static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpg_t gpg = (engine_gpg_t) data->handler_value; int err; assert (fd == gpg->status.fd[0]); err = read_status (gpg); if (err) return err; if (gpg->status.eof) _gpgme_io_close (fd); return 0; } static gpgme_error_t read_colon_line (engine_gpg_t gpg) { char *p; int nread; size_t bufsize = gpg->colon.bufsize; char *buffer = gpg->colon.buffer; size_t readpos = gpg->colon.readpos; assert (buffer); if (bufsize - readpos < 256) { /* Need more room for the read. */ bufsize += 1024; buffer = realloc (buffer, bufsize); if (!buffer) return gpg_error_from_syserror (); } nread = _gpgme_io_read (gpg->colon.fd[0], buffer+readpos, bufsize-readpos); if (nread == -1) return gpg_error_from_syserror (); if (!nread) { gpg->colon.eof = 1; assert (gpg->colon.fnc); gpg->colon.fnc (gpg->colon.fnc_value, NULL); return 0; } while (nread > 0) { for (p = buffer + readpos; nread; nread--, p++) { if ( *p == '\n' ) { /* (we require that the last line is terminated by a LF) and we skip empty lines. Note: we use UTF8 encoding and escaping of special characters. We require at least one colon to cope with some other printed information. */ *p = 0; if (*buffer && strchr (buffer, ':')) { char *line = NULL; if (gpg->colon.preprocess_fnc) { gpgme_error_t err; err = gpg->colon.preprocess_fnc (buffer, &line); if (err) return err; } assert (gpg->colon.fnc); if (line) { char *linep = line; char *endp; do { endp = strchr (linep, '\n'); if (endp) *endp++ = 0; gpg->colon.fnc (gpg->colon.fnc_value, linep); linep = endp; } while (linep && *linep); gpgrt_free (line); } else gpg->colon.fnc (gpg->colon.fnc_value, buffer); } /* To reuse the buffer for the next line we have to shift the remaining data to the buffer start and restart the loop Hmmm: We can optimize this function by looking forward in the buffer to see whether a second complete line is available and in this case avoid the memmove for this line. */ nread--; p++; if (nread) memmove (buffer, p, nread); readpos = 0; break; /* The for loop. */ } else readpos++; } } /* Update the gpg object. */ gpg->colon.bufsize = bufsize; gpg->colon.buffer = buffer; gpg->colon.readpos = readpos; return 0; } /* This colonline handler thing is not the clean way to do it. It might be better to enhance the gpgme_data_t object to act as a wrapper for a callback. Same goes for the status thing. For now we use this thing here because it is easier to implement. */ static gpgme_error_t colon_line_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpg_t gpg = (engine_gpg_t) data->handler_value; gpgme_error_t rc = 0; assert (fd == gpg->colon.fd[0]); rc = read_colon_line (gpg); if (rc) return rc; if (gpg->colon.eof) _gpgme_io_close (fd); return 0; } static gpgme_error_t start (engine_gpg_t gpg) { gpgme_error_t rc; int i, n; int status; struct spawn_fd_item_s *fd_list; pid_t pid; const char *pgmname; if (!gpg) return gpg_error (GPG_ERR_INV_VALUE); if (!gpg->file_name && !_gpgme_get_default_gpg_name ()) return trace_gpg_error (GPG_ERR_INV_ENGINE); if (gpg->lc_ctype) { rc = add_arg_ext (gpg, gpg->lc_ctype, 1); if (!rc) rc = add_arg_ext (gpg, "--lc-ctype", 1); if (rc) return rc; } if (gpg->lc_messages) { rc = add_arg_ext (gpg, gpg->lc_messages, 1); if (!rc) rc = add_arg_ext (gpg, "--lc-messages", 1); if (rc) return rc; } pgmname = gpg->file_name ? gpg->file_name : _gpgme_get_default_gpg_name (); rc = build_argv (gpg, pgmname); if (rc) return rc; /* status_fd, colon_fd and end of list. */ n = 3; for (i = 0; gpg->fd_data_map[i].data; i++) n++; fd_list = calloc (n, sizeof *fd_list); if (! fd_list) return gpg_error_from_syserror (); /* Build the fd list for the child. */ n = 0; fd_list[n].fd = gpg->status.fd[1]; fd_list[n].dup_to = -1; fd_list[n].arg_loc = gpg->status.arg_loc; n++; if (gpg->colon.fnc) { fd_list[n].fd = gpg->colon.fd[1]; fd_list[n].dup_to = 1; n++; } for (i = 0; gpg->fd_data_map[i].data; i++) { fd_list[n].fd = gpg->fd_data_map[i].peer_fd; fd_list[n].dup_to = gpg->fd_data_map[i].dup_to; fd_list[n].arg_loc = gpg->fd_data_map[i].arg_loc; n++; } fd_list[n].fd = -1; fd_list[n].dup_to = -1; status = _gpgme_io_spawn (pgmname, gpg->argv, (IOSPAWN_FLAG_DETACHED |IOSPAWN_FLAG_ALLOW_SET_FG), fd_list, NULL, NULL, &pid); { int saved_err = gpg_error_from_syserror (); free (fd_list); if (status == -1) return saved_err; } /*_gpgme_register_term_handler ( closure, closure_value, pid );*/ rc = add_io_cb (gpg, gpg->status.fd[0], 1, status_handler, gpg, &gpg->status.tag); if (rc) /* FIXME: kill the child */ return rc; if (gpg->colon.fnc) { assert (gpg->colon.fd[0] != -1); rc = add_io_cb (gpg, gpg->colon.fd[0], 1, colon_line_handler, gpg, &gpg->colon.tag); if (rc) /* FIXME: kill the child */ return rc; } for (i = 0; gpg->fd_data_map[i].data; i++) { if (gpg->cmd.used && i == gpg->cmd.idx) { /* Park the cmd fd. */ gpg->cmd.fd = gpg->fd_data_map[i].fd; gpg->fd_data_map[i].fd = -1; } else { rc = add_io_cb (gpg, gpg->fd_data_map[i].fd, gpg->fd_data_map[i].inbound, gpg->fd_data_map[i].inbound ? _gpgme_data_inbound_handler : _gpgme_data_outbound_handler, gpg->fd_data_map[i].data, &gpg->fd_data_map[i].tag); if (rc) /* FIXME: kill the child */ return rc; } } gpg_io_event (gpg, GPGME_EVENT_START, NULL); /* fixme: check what data we can release here */ return 0; } /* Add the --input-size-hint option if requested. */ static gpgme_error_t add_input_size_hint (engine_gpg_t gpg, gpgme_data_t data) { gpgme_error_t err; gpgme_off_t value = _gpgme_data_get_size_hint (data); char numbuf[50]; /* Large enough for even 2^128 in base-10. */ char *p; if (!value || !have_gpg_version (gpg, "2.1.15")) return 0; err = add_arg (gpg, "--input-size-hint"); if (!err) { p = numbuf + sizeof numbuf; *--p = 0; do { *--p = '0' + (value % 10); value /= 10; } while (value); err = add_arg (gpg, p); } return err; } static gpgme_error_t gpg_decrypt (void *engine, gpgme_decrypt_flags_t flags, gpgme_data_t ciph, gpgme_data_t plain, int export_session_key, const char *override_session_key, int auto_key_retrieve) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, "--decrypt"); if (!err && (flags & GPGME_DECRYPT_UNWRAP)) { if (!have_gpg_version (gpg, "2.1.12")) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else err = add_arg (gpg, "--unwrap"); } if (!err && export_session_key) err = add_arg (gpg, "--show-session-key"); if (!err && auto_key_retrieve) err = add_arg (gpg, "--auto-key-retrieve"); if (!err && override_session_key && *override_session_key) { if (have_gpg_version (gpg, "2.1.16")) { gpgme_data_release (gpg->override_session_key); - TRACE2 (DEBUG_ENGINE, "override", gpg, "seskey='%s' len=%zu\n", + TRACE (DEBUG_ENGINE, "override", gpg, "seskey='%s' len=%zu\n", override_session_key, strlen (override_session_key)); err = gpgme_data_new_from_mem (&gpg->override_session_key, override_session_key, strlen (override_session_key), 1); if (!err) { /* We add --no-keyring because a keyring is not required * when we are overriding the session key. It would * work without that option but --no-keyring avoids that * gpg return a failure due to a missing key log_error() * diagnostic. --no-keyring is supported since 2.1.14. */ err = add_arg (gpg, "--no-keyring"); if (!err) err = add_arg (gpg, "--override-session-key-fd"); if (!err) err = add_data (gpg, gpg->override_session_key, -2, 0); } } else { /* Using that option may leak the session key via ps(1). */ err = add_arg (gpg, "--override-session-key"); if (!err) err = add_arg (gpg, override_session_key); } } /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, plain, 1, 1); if (!err) err = add_input_size_hint (gpg, ciph); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, ciph, -1, 0); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_delete (void *engine, gpgme_key_t key, unsigned int flags) { engine_gpg_t gpg = engine; gpgme_error_t err = 0; int allow_secret = flags & GPGME_DELETE_ALLOW_SECRET; int force = flags & GPGME_DELETE_FORCE; if (force) err = add_arg (gpg, "--yes"); if (!err) err = add_arg (gpg, allow_secret ? "--delete-secret-and-public-key" : "--delete-key"); if (!err) err = add_arg (gpg, "--"); if (!err) { if (!key->subkeys || !key->subkeys->fpr) return gpg_error (GPG_ERR_INV_VALUE); else err = add_arg (gpg, key->subkeys->fpr); } if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_passwd (void *engine, gpgme_key_t key, unsigned int flags) { engine_gpg_t gpg = engine; gpgme_error_t err; (void)flags; if (!key || !key->subkeys || !key->subkeys->fpr) return gpg_error (GPG_ERR_INV_CERT_OBJ); err = add_arg (gpg, "--passwd"); if (!err) err = add_arg (gpg, key->subkeys->fpr); if (!err) err = start (gpg); return err; } static gpgme_error_t append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */) { gpgme_error_t err = 0; int i; gpgme_key_t key; for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++) { const char *s = key->subkeys ? key->subkeys->keyid : NULL; if (s) { if (!err) err = add_arg (gpg, "-u"); if (!err) err = add_arg (gpg, s); } gpgme_key_unref (key); if (err) break; } return err; } static gpgme_error_t append_args_from_sender (engine_gpg_t gpg, gpgme_ctx_t ctx) { gpgme_error_t err; if (ctx->sender && have_gpg_version (gpg, "2.1.15")) { err = add_arg (gpg, "--sender"); if (!err) err = add_arg (gpg, ctx->sender); } else err = 0; return err; } static gpgme_error_t append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */) { gpgme_error_t err = 0; gpgme_sig_notation_t notation; notation = gpgme_sig_notation_get (ctx); while (!err && notation) { if (notation->name && !(notation->flags & GPGME_SIG_NOTATION_HUMAN_READABLE)) err = gpg_error (GPG_ERR_INV_VALUE); else if (notation->name) { char *arg; /* Maximum space needed is one byte for the "critical" flag, the name, one byte for '=', the value, and a terminating '\0'. */ arg = malloc (1 + notation->name_len + 1 + notation->value_len + 1); if (!arg) err = gpg_error_from_syserror (); if (!err) { char *argp = arg; if (notation->critical) *(argp++) = '!'; memcpy (argp, notation->name, notation->name_len); argp += notation->name_len; *(argp++) = '='; /* We know that notation->name is '\0' terminated. */ strcpy (argp, notation->value); } if (!err) err = add_arg (gpg, "--sig-notation"); if (!err) err = add_arg (gpg, arg); if (arg) free (arg); } else { /* This is a policy URL. */ char *value; if (notation->critical) { value = malloc (1 + notation->value_len + 1); if (!value) err = gpg_error_from_syserror (); else { value[0] = '!'; /* We know that notation->value is '\0' terminated. */ strcpy (&value[1], notation->value); } } else value = notation->value; if (!err) err = add_arg (gpg, "--sig-policy-url"); if (!err) err = add_arg (gpg, value); if (value != notation->value) free (value); } notation = notation->next; } return err; } static gpgme_error_t gpg_edit (void *engine, int type, gpgme_key_t key, gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, "--with-colons"); if (!err) err = append_args_from_signers (gpg, ctx); if (!err) err = add_arg (gpg, type == 0 ? "--edit-key" : "--card-edit"); if (!err) err = add_data (gpg, out, 1, 1); if (!err) err = add_arg (gpg, "--"); if (!err && type == 0) { const char *s = key->subkeys ? key->subkeys->fpr : NULL; if (!s) err = gpg_error (GPG_ERR_INV_VALUE); else err = add_arg (gpg, s); } if (!err) err = start (gpg); return err; } /* Add a single argument from a key to an -r option. */ static gpg_error_t add_arg_recipient (engine_gpg_t gpg, gpgme_encrypt_flags_t flags, gpgme_key_t key) { gpg_error_t err; if ((flags & GPGME_ENCRYPT_WANT_ADDRESS)) { /* We have no way to figure out which mail address was * requested. FIXME: It would be possible to figure this out by * consulting the SENDER property of the context. */ err = gpg_error (GPG_ERR_INV_USER_ID); } else err = add_arg (gpg, key->subkeys->fpr); return err; } /* Add a single argument from a USERID string to an -r option. */ static gpg_error_t add_arg_recipient_string (engine_gpg_t gpg, gpgme_encrypt_flags_t flags, const char *userid, int useridlen) { gpg_error_t err; if ((flags & GPGME_ENCRYPT_WANT_ADDRESS)) { char *tmpstr, *mbox; tmpstr = malloc (useridlen + 1); if (!tmpstr) err = gpg_error_from_syserror (); else { memcpy (tmpstr, userid, useridlen); tmpstr[useridlen] = 0; mbox = _gpgme_mailbox_from_userid (tmpstr); if (!mbox) { err = gpg_error_from_syserror (); if (gpg_err_code (err) == GPG_ERR_EINVAL) err = gpg_error (GPG_ERR_INV_USER_ID); } else err = add_arg (gpg, mbox); free (mbox); free (tmpstr); } } else err = add_arg_len (gpg, NULL, userid, useridlen); return err; } static gpgme_error_t append_args_from_recipients (engine_gpg_t gpg, gpgme_encrypt_flags_t flags, gpgme_key_t recp[]) { gpgme_error_t err = 0; int i = 0; while (recp[i]) { if (!recp[i]->subkeys || !recp[i]->subkeys->fpr) err = gpg_error (GPG_ERR_INV_VALUE); if (!err) err = add_arg (gpg, "-r"); if (!err) err = add_arg_recipient (gpg, flags, recp[i]); if (err) break; i++; } return err; } /* Take recipients from the LF delimited STRING and add -r args. */ static gpg_error_t append_args_from_recipients_string (engine_gpg_t gpg, gpgme_encrypt_flags_t flags, const char *string) { gpg_error_t err = 0; gpgme_encrypt_flags_t orig_flags = flags; int any = 0; int ignore = 0; int hidden = 0; int file = 0; const char *s; int n; do { /* Skip leading white space */ while (*string == ' ' || *string == '\t') string++; if (!*string) break; /* Look for the LF. */ s = strchr (string, '\n'); if (s) n = s - string; else n = strlen (string); while (n && (string[n-1] == ' ' || string[n-1] == '\t')) n--; if (!ignore && n == 2 && !memcmp (string, "--", 2)) ignore = 1; else if (!ignore && n == 8 && !memcmp (string, "--hidden", 8)) hidden = 1; else if (!ignore && n == 11 && !memcmp (string, "--no-hidden", 11)) hidden = 0; else if (!ignore && n == 6 && !memcmp (string, "--file", 6)) { file = 1; /* Because the key is used as is we need to ignore this flag: */ flags &= ~GPGME_ENCRYPT_WANT_ADDRESS; } else if (!ignore && n == 9 && !memcmp (string, "--no-file", 9)) { file = 0; flags = orig_flags; } else if (n) /* Not empty - use it. */ { err = add_arg (gpg, file? (hidden? "-F":"-f") : (hidden? "-R":"-r")); if (!err) err = add_arg_recipient_string (gpg, flags, string, n); if (!err) any = 1; } string += n + !!s; } while (!err); if (!err && !any) err = gpg_error (GPG_ERR_MISSING_KEY); return err; } static gpgme_error_t gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err = 0; if (recp || recpstring) err = add_arg (gpg, "--encrypt"); if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || (!recp && !recpstring))) err = add_arg (gpg, "--symmetric"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err && (flags & GPGME_ENCRYPT_WRAP)) { /* gpg is current not able to detect already compressed * packets. Thus when using * gpg --unwrap -d | gpg --no-literal -e * the encryption would add an additional compression layer. * We better suppress that. */ flags |= GPGME_ENCRYPT_NO_COMPRESS; err = add_arg (gpg, "--no-literal"); } if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS)) err = add_arg (gpg, "--compress-algo=none"); if (!err && (flags & GPGME_ENCRYPT_THROW_KEYIDS)) err = add_arg (gpg, "--throw-keyids"); if (gpgme_data_get_encoding (plain) == GPGME_DATA_ENCODING_MIME && have_gpg_version (gpg, "2.1.14")) err = add_arg (gpg, "--mimemode"); if (recp || recpstring) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST)) err = add_arg (gpg, "--always-trust"); if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) err = add_arg (gpg, "--no-encrypt-to"); if (!err && !recp && recpstring) err = append_args_from_recipients_string (gpg, flags, recpstring); else if (!err) err = append_args_from_recipients (gpg, flags, recp); } /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, ciph, 1, 1); if (gpgme_data_get_file_name (plain)) { if (!err) err = add_arg (gpg, "--set-filename"); if (!err) err = add_arg (gpg, gpgme_data_get_file_name (plain)); } if (!err) err = add_input_size_hint (gpg, plain); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, plain, -1, 0); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_encrypt_sign (void *engine, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */) { engine_gpg_t gpg = engine; gpgme_error_t err = 0; if (recp || recpstring) err = add_arg (gpg, "--encrypt"); if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || (!recp && !recpstring))) err = add_arg (gpg, "--symmetric"); if (!err) err = add_arg (gpg, "--sign"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS)) err = add_arg (gpg, "--compress-algo=none"); if (!err && (flags & GPGME_ENCRYPT_THROW_KEYIDS)) err = add_arg (gpg, "--throw-keyids"); if (gpgme_data_get_encoding (plain) == GPGME_DATA_ENCODING_MIME && have_gpg_version (gpg, "2.1.14")) err = add_arg (gpg, "--mimemode"); if (recp || recpstring) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST)) err = add_arg (gpg, "--always-trust"); if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) err = add_arg (gpg, "--no-encrypt-to"); if (!err && !recp && recpstring) err = append_args_from_recipients_string (gpg, flags, recpstring); else if (!err) err = append_args_from_recipients (gpg, flags, recp); } if (!err) err = append_args_from_signers (gpg, ctx); if (!err) err = append_args_from_sender (gpg, ctx); if (!err) err = append_args_from_sig_notations (gpg, ctx); /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, ciph, 1, 1); if (gpgme_data_get_file_name (plain)) { if (!err) err = add_arg (gpg, "--set-filename"); if (!err) err = add_arg (gpg, gpgme_data_get_file_name (plain)); } if (!err) err = add_input_size_hint (gpg, plain); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, plain, -1, 0); if (!err) err = start (gpg); return err; } static gpgme_error_t export_common (engine_gpg_t gpg, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { gpgme_error_t err = 0; if ((mode & ~(GPGME_EXPORT_MODE_EXTERN |GPGME_EXPORT_MODE_MINIMAL |GPGME_EXPORT_MODE_SECRET))) return gpg_error (GPG_ERR_NOT_SUPPORTED); if ((mode & GPGME_EXPORT_MODE_MINIMAL)) { if ((mode & GPGME_EXPORT_MODE_NOUID)) err = add_arg (gpg, "--export-options=export-minimal,export-drop-uids"); else err = add_arg (gpg, "--export-options=export-minimal"); } else if ((mode & GPGME_EXPORT_MODE_NOUID)) err = add_arg (gpg, "--export-options=export-drop-uids"); if (err) ; else if ((mode & GPGME_EXPORT_MODE_EXTERN)) { err = add_arg (gpg, "--send-keys"); if (!err && (mode & GPGME_EXPORT_MODE_NOUID)) err = add_arg (gpg, "--keyserver-options=export-drop-uids"); } else { if ((mode & GPGME_EXPORT_MODE_SECRET)) err = add_arg (gpg, "--export-secret-keys"); else err = add_arg (gpg, "--export"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err) err = add_data (gpg, keydata, 1, 1); } if (!err) err = add_arg (gpg, "--"); return err; } static gpgme_error_t gpg_export (void *engine, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err; err = export_common (gpg, mode, keydata, use_armor); if (!err && pattern && *pattern) err = add_arg (gpg, pattern); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err; err = export_common (gpg, mode, keydata, use_armor); if (pattern) { while (!err && *pattern && **pattern) err = add_arg (gpg, *(pattern++)); } if (!err) err = start (gpg); return err; } /* Helper to add algo, usage, and expire to the list of args. */ static gpgme_error_t gpg_add_algo_usage_expire (engine_gpg_t gpg, const char *algo, unsigned long expires, unsigned int flags) { gpg_error_t err; /* This condition is only required to allow the use of gpg < 2.1.16 */ if (algo || (flags & (GPGME_CREATE_SIGN | GPGME_CREATE_ENCR | GPGME_CREATE_CERT | GPGME_CREATE_AUTH | GPGME_CREATE_NOEXPIRE)) || expires) { err = add_arg (gpg, algo? algo : "default"); if (!err) { char tmpbuf[5*4+1]; snprintf (tmpbuf, sizeof tmpbuf, "%s%s%s%s", (flags & GPGME_CREATE_SIGN)? " sign":"", (flags & GPGME_CREATE_ENCR)? " encr":"", (flags & GPGME_CREATE_CERT)? " cert":"", (flags & GPGME_CREATE_AUTH)? " auth":""); err = add_arg (gpg, *tmpbuf? tmpbuf : "default"); } if (!err) { if ((flags & GPGME_CREATE_NOEXPIRE)) err = add_arg (gpg, "never"); else if (expires == 0) err = add_arg (gpg, "-"); else { char tmpbuf[8+20]; snprintf (tmpbuf, sizeof tmpbuf, "seconds=%lu", expires); err = add_arg (gpg, tmpbuf); } } } else err = 0; return err; } static gpgme_error_t gpg_createkey_from_param (engine_gpg_t gpg, gpgme_data_t help_data, unsigned int extraflags) { gpgme_error_t err; err = add_arg (gpg, "--gen-key"); if (!err && (extraflags & GENKEY_EXTRAFLAG_ARMOR)) err = add_arg (gpg, "--armor"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, help_data, -1, 0); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_createkey (engine_gpg_t gpg, const char *userid, const char *algo, unsigned long expires, unsigned int flags, unsigned int extraflags) { gpgme_error_t err; err = add_arg (gpg, "--quick-gen-key"); if (!err && (extraflags & GENKEY_EXTRAFLAG_ARMOR)) err = add_arg (gpg, "--armor"); if (!err && (flags & GPGME_CREATE_NOPASSWD)) { err = add_arg (gpg, "--passphrase"); if (!err) err = add_arg (gpg, ""); if (!err) err = add_arg (gpg, "--batch"); } if (!err && (flags & GPGME_CREATE_FORCE)) err = add_arg (gpg, "--yes"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_arg (gpg, userid); if (!err) err = gpg_add_algo_usage_expire (gpg, algo, expires, flags); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_addkey (engine_gpg_t gpg, const char *algo, unsigned long expires, gpgme_key_t key, unsigned int flags, unsigned int extraflags) { gpgme_error_t err; if (!key || !key->fpr) return gpg_error (GPG_ERR_INV_ARG); err = add_arg (gpg, "--quick-addkey"); if (!err && (extraflags & GENKEY_EXTRAFLAG_ARMOR)) err = add_arg (gpg, "--armor"); if (!err && (flags & GPGME_CREATE_NOPASSWD)) { err = add_arg (gpg, "--passphrase"); if (!err) err = add_arg (gpg, ""); if (!err) err = add_arg (gpg, "--batch"); } if (!err) err = add_arg (gpg, "--"); if (!err) err = add_arg (gpg, key->fpr); if (!err) err = gpg_add_algo_usage_expire (gpg, algo, expires, flags); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_adduid (engine_gpg_t gpg, gpgme_key_t key, const char *userid, unsigned int extraflags) { gpgme_error_t err; if (!key || !key->fpr || !userid) return gpg_error (GPG_ERR_INV_ARG); if ((extraflags & GENKEY_EXTRAFLAG_SETPRIMARY)) { if (!have_gpg_version (gpg, "2.1.20")) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else err = add_arg (gpg, "--quick-set-primary-uid"); } else if ((extraflags & GENKEY_EXTRAFLAG_REVOKE)) err = add_arg (gpg, "--quick-revuid"); else err = add_arg (gpg, "--quick-adduid"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_arg (gpg, key->fpr); if (!err) err = add_arg (gpg, userid); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_genkey (void *engine, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t key, unsigned int flags, gpgme_data_t help_data, unsigned int extraflags, gpgme_data_t pubkey, gpgme_data_t seckey) { engine_gpg_t gpg = engine; gpgme_error_t err; (void)reserved; if (!gpg) return gpg_error (GPG_ERR_INV_VALUE); /* If HELP_DATA is given the use of the old interface * (gpgme_op_genkey) has been requested. The other modes are: * * USERID && !KEY - Create a new keyblock. * !USERID && KEY - Add a new subkey to KEY (gpg >= 2.1.14) * USERID && KEY && !ALGO - Add a new user id to KEY (gpg >= 2.1.14). * or set a flag on a user id. */ if (help_data) { /* We need a special mechanism to get the fd of a pipe here, so that we can use this for the %pubring and %secring parameters. We don't have this yet, so we implement only the adding to the standard keyrings. */ if (pubkey || seckey) err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); else err = gpg_createkey_from_param (gpg, help_data, extraflags); } else if (!have_gpg_version (gpg, "2.1.13")) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else if (userid && !key) err = gpg_createkey (gpg, userid, algo, expires, flags, extraflags); else if (!userid && key) err = gpg_addkey (gpg, algo, expires, key, flags, extraflags); else if (userid && key && !algo) err = gpg_adduid (gpg, key, userid, extraflags); else err = gpg_error (GPG_ERR_INV_VALUE); return err; } /* Return the next DELIM delimited string from DATA as a C-string. The caller needs to provide the address of a pointer variable which he has to set to NULL before the first call. After the last call to this function, this function needs to be called once more with DATA set to NULL so that the function can release its internal state. After that the pointer variable is free for use again. Note that we use a delimiter and thus a trailing delimiter is not required. DELIM may not be changed after the first call. */ static const char * string_from_data (gpgme_data_t data, int delim, void **helpptr, gpgme_error_t *r_err) { #define MYBUFLEN 2000 /* Fixme: We don't support URLs longer than that. */ struct { int eof_seen; int nbytes; /* Length of the last returned string including the delimiter. */ int buflen; /* Valid length of BUF. */ char buf[MYBUFLEN+1]; /* Buffer with one byte extra space. */ } *self; char *p; int nread; *r_err = 0; if (!data) { if (*helpptr) { free (*helpptr); *helpptr = NULL; } return NULL; } if (*helpptr) self = *helpptr; else { self = malloc (sizeof *self); if (!self) { *r_err = gpg_error_from_syserror (); return NULL; } *helpptr = self; self->eof_seen = 0; self->nbytes = 0; self->buflen = 0; } if (self->eof_seen) return NULL; assert (self->nbytes <= self->buflen); memmove (self->buf, self->buf + self->nbytes, self->buflen - self->nbytes); self->buflen -= self->nbytes; self->nbytes = 0; do { /* Fixme: This is fairly infective scanning because we may scan the buffer several times. */ p = memchr (self->buf, delim, self->buflen); if (p) { *p = 0; self->nbytes = p - self->buf + 1; return self->buf; } if ( !(MYBUFLEN - self->buflen) ) { /* Not enough space - URL too long. */ *r_err = gpg_error (GPG_ERR_TOO_LARGE); return NULL; } nread = gpgme_data_read (data, self->buf + self->buflen, MYBUFLEN - self->buflen); if (nread < 0) { *r_err = gpg_error_from_syserror (); return NULL; } self->buflen += nread; } while (nread); /* EOF reached. If we have anything in the buffer, append a Nul and return it. */ self->eof_seen = 1; if (self->buflen) { self->buf[self->buflen] = 0; /* (we allocated one extra byte) */ return self->buf; } return NULL; #undef MYBUFLEN } static gpgme_error_t gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { engine_gpg_t gpg = engine; gpgme_error_t err; int idx; gpgme_data_encoding_t dataenc; if (keydata && keyarray) return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */ dataenc = gpgme_data_get_encoding (keydata); if (keyarray) { err = add_arg (gpg, "--recv-keys"); if (!err) err = add_arg (gpg, "--"); for (idx=0; !err && keyarray[idx]; idx++) { if (keyarray[idx]->protocol != GPGME_PROTOCOL_OpenPGP) ; else if (!keyarray[idx]->subkeys) ; else if (keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) err = add_arg (gpg, keyarray[idx]->subkeys->fpr); else if (*keyarray[idx]->subkeys->keyid) err = add_arg (gpg, keyarray[idx]->subkeys->keyid); } } else if (dataenc == GPGME_DATA_ENCODING_URL || dataenc == GPGME_DATA_ENCODING_URL0) { void *helpptr; const char *string; gpgme_error_t xerr; int delim = (dataenc == GPGME_DATA_ENCODING_URL)? '\n': 0; /* FIXME: --fetch-keys is probably not correct because it can't grok all kinds of URLs. On Unix it should just work but on Windows we will build the command line and that may fail for some embedded control characters. It is anyway limited to the maximum size of the command line. We need another command which can take its input from a file. Maybe we should use an option to gpg to modify such commands (ala --multifile). */ err = add_arg (gpg, "--fetch-keys"); if (!err) err = add_arg (gpg, "--"); helpptr = NULL; while (!err && (string = string_from_data (keydata, delim, &helpptr, &xerr))) err = add_arg (gpg, string); if (!err) err = xerr; string_from_data (NULL, delim, &helpptr, &xerr); } else if (dataenc == GPGME_DATA_ENCODING_URLESC) { /* Already escaped URLs are not yet supported. */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); } else { err = add_arg (gpg, "--import"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, keydata, -1, 0); } if (!err) err = start (gpg); return err; } /* The output for external keylistings in GnuPG is different from all the other key listings. We catch this here with a special preprocessor that reformats the colon handler lines. */ static gpgme_error_t gpg_keylist_preprocess (char *line, char **r_line) { enum { RT_NONE, RT_INFO, RT_PUB, RT_UID } rectype = RT_NONE; #define NR_FIELDS 16 char *field[NR_FIELDS]; int fields = 0; size_t n; *r_line = NULL; while (line && fields < NR_FIELDS) { field[fields++] = line; line = strchr (line, ':'); if (line) *(line++) = '\0'; } if (!strcmp (field[0], "info")) rectype = RT_INFO; else if (!strcmp (field[0], "pub")) rectype = RT_PUB; else if (!strcmp (field[0], "uid")) rectype = RT_UID; else rectype = RT_NONE; switch (rectype) { case RT_INFO: /* FIXME: Eventually, check the version number at least. */ return 0; case RT_PUB: if (fields < 7) return 0; /* The format is: pub:::::: as defined in 5.2. Machine Readable Indexes of the OpenPGP HTTP Keyserver Protocol (draft). Modern versions of the SKS keyserver return the fingerprint instead of the keyid. We detect this here and use the v4 fingerprint format to convert it to a key id. We want: pub:o::::::::::::: */ n = strlen (field[1]); if (n > 16) { if (gpgrt_asprintf (r_line, "pub:o%s:%s:%s:%s:%s:%s::::::::\n" "fpr:::::::::%s:", field[6], field[3], field[2], field[1] + n - 16, field[4], field[5], field[1]) < 0) return gpg_error_from_syserror (); } else { if (gpgrt_asprintf (r_line, "pub:o%s:%s:%s:%s:%s:%s::::::::", field[6], field[3], field[2], field[1], field[4], field[5]) < 0) return gpg_error_from_syserror (); } return 0; case RT_UID: /* The format is: uid:::: as defined in 5.2. Machine Readable Indexes of the OpenPGP HTTP Keyserver Protocol (draft). For an ldap keyserver the format is: uid: We want: uid:o::::::::: */ { /* The user ID is percent escaped, but we want c-coded. Because we have to replace each '%HL' by '\xHL', we need at most 4/3 th the number of bytes. But because we also need to escape the backslashes we allocate twice as much. */ char *uid = malloc (2 * strlen (field[1]) + 1); char *src; char *dst; if (! uid) return gpg_error_from_syserror (); src = field[1]; dst = uid; while (*src) { if (*src == '%') { *(dst++) = '\\'; *(dst++) = 'x'; src++; /* Copy the next two bytes unconditionally. */ if (*src) *(dst++) = *(src++); if (*src) *(dst++) = *(src++); } else if (*src == '\\') { *dst++ = '\\'; *dst++ = '\\'; src++; } else *(dst++) = *(src++); } *dst = '\0'; if (fields < 4) { if (gpgrt_asprintf (r_line, "uid:o::::::::%s:", uid) < 0) return gpg_error_from_syserror (); } else { if (gpgrt_asprintf (r_line, "uid:o%s::::%s:%s:::%s:", field[4], field[2], field[3], uid) < 0) return gpg_error_from_syserror (); } } return 0; case RT_NONE: /* Unknown record. */ break; } return 0; } static gpg_error_t gpg_keylist_build_options (engine_gpg_t gpg, int secret_only, gpgme_keylist_mode_t mode) { gpg_error_t err; err = add_arg (gpg, "--with-colons"); /* Since gpg 2.1.15 fingerprints are always printed, thus there is * no more need to explicitly request them. */ if (!have_gpg_version (gpg, "2.1.15")) { if (!err) err = add_arg (gpg, "--fixed-list-mode"); if (!err) err = add_arg (gpg, "--with-fingerprint"); if (!err) err = add_arg (gpg, "--with-fingerprint"); } if (!err && (mode & GPGME_KEYLIST_MODE_WITH_TOFU) && have_gpg_version (gpg, "2.1.16")) err = add_arg (gpg, "--with-tofu-info"); if (!err && (mode & GPGME_KEYLIST_MODE_WITH_SECRET)) err = add_arg (gpg, "--with-secret"); if (!err && (mode & GPGME_KEYLIST_MODE_SIGS) && (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS)) { err = add_arg (gpg, "--list-options"); if (!err) err = add_arg (gpg, "show-sig-subpackets=\"20,26\""); } if (!err) { if ( (mode & GPGME_KEYLIST_MODE_EXTERN) ) { if (secret_only) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else if ( (mode & GPGME_KEYLIST_MODE_LOCAL)) { /* The local+extern mode is special. It works only with gpg >= 2.0.10. FIXME: We should check that we have such a version to that we can return a proper error code. The problem is that we don't know the context here and thus can't access the cached version number for the engine info structure. */ err = add_arg (gpg, "--locate-keys"); if ((mode & GPGME_KEYLIST_MODE_SIGS)) err = add_arg (gpg, "--with-sig-check"); } else { err = add_arg (gpg, "--search-keys"); gpg->colon.preprocess_fnc = gpg_keylist_preprocess; } } else { err = add_arg (gpg, secret_only ? "--list-secret-keys" : ((mode & GPGME_KEYLIST_MODE_SIGS) ? "--check-sigs" : "--list-keys")); } } if (!err) err = add_arg (gpg, "--"); return err; } static gpgme_error_t gpg_keylist (void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode, int engine_flags) { engine_gpg_t gpg = engine; gpgme_error_t err; (void)engine_flags; err = gpg_keylist_build_options (gpg, secret_only, mode); if (!err && pattern && *pattern) err = add_arg (gpg, pattern); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_keylist_ext (void *engine, const char *pattern[], int secret_only, int reserved, gpgme_keylist_mode_t mode, int engine_flags) { engine_gpg_t gpg = engine; gpgme_error_t err; (void)engine_flags; if (reserved) return gpg_error (GPG_ERR_INV_VALUE); err = gpg_keylist_build_options (gpg, secret_only, mode); if (pattern) { while (!err && *pattern && **pattern) err = add_arg (gpg, *(pattern++)); } if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_keylist_data (void *engine, gpgme_data_t data) { engine_gpg_t gpg = engine; gpgme_error_t err; if (!have_gpg_version (gpg, "2.1.14")) return gpg_error (GPG_ERR_NOT_SUPPORTED); err = add_arg (gpg, "--with-colons"); if (!err) err = add_arg (gpg, "--with-fingerprint"); if (!err) err = add_arg (gpg, "--import-options"); if (!err) err = add_arg (gpg, "import-show"); if (!err) err = add_arg (gpg, "--dry-run"); if (!err) err = add_arg (gpg, "--import"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, data, -1, 0); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_keysign (void *engine, gpgme_key_t key, const char *userid, unsigned long expire, unsigned int flags, gpgme_ctx_t ctx) { engine_gpg_t gpg = engine; gpgme_error_t err; const char *s; if (!key || !key->fpr) return gpg_error (GPG_ERR_INV_ARG); if (!have_gpg_version (gpg, "2.1.12")) return gpg_error (GPG_ERR_NOT_SUPPORTED); if ((flags & GPGME_KEYSIGN_LOCAL)) err = add_arg (gpg, "--quick-lsign-key"); else err = add_arg (gpg, "--quick-sign-key"); if (!err) err = append_args_from_signers (gpg, ctx); /* If an expiration time has been given use that. If none has been * given the default from gpg.conf is used. To make sure not to set * an expiration time at all the flag GPGME_KEYSIGN_NOEXPIRE can be * used. */ if (!err && (expire || (flags & GPGME_KEYSIGN_NOEXPIRE))) { char tmpbuf[8+20]; if ((flags & GPGME_KEYSIGN_NOEXPIRE)) expire = 0; snprintf (tmpbuf, sizeof tmpbuf, "seconds=%lu", expire); err = add_arg (gpg, "--default-cert-expire"); if (!err) err = add_arg (gpg, tmpbuf); } if (!err) err = add_arg (gpg, "--"); if (!err) err = add_arg (gpg, key->fpr); if (!err && userid) { if ((flags & GPGME_KEYSIGN_LFSEP)) { for (; !err && (s = strchr (userid, '\n')); userid = s + 1) if ((s - userid)) err = add_arg_len (gpg, "=", userid, s - userid); if (!err && *userid) err = add_arg_pfx (gpg, "=", userid); } else err = add_arg_pfx (gpg, "=", userid); } if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_tofu_policy (void *engine, gpgme_key_t key, gpgme_tofu_policy_t policy) { engine_gpg_t gpg = engine; gpgme_error_t err; const char *policystr = NULL; if (!key || !key->fpr) return gpg_error (GPG_ERR_INV_ARG); switch (policy) { case GPGME_TOFU_POLICY_NONE: break; case GPGME_TOFU_POLICY_AUTO: policystr = "auto"; break; case GPGME_TOFU_POLICY_GOOD: policystr = "good"; break; case GPGME_TOFU_POLICY_BAD: policystr = "bad"; break; case GPGME_TOFU_POLICY_ASK: policystr = "ask"; break; case GPGME_TOFU_POLICY_UNKNOWN: policystr = "unknown"; break; } if (!policystr) return gpg_error (GPG_ERR_INV_VALUE); if (!have_gpg_version (gpg, "2.1.10")) return gpg_error (GPG_ERR_NOT_SUPPORTED); err = add_arg (gpg, "--tofu-policy"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_arg (gpg, policystr); if (!err) err = add_arg (gpg, key->fpr); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_gpg_t gpg = engine; gpgme_error_t err; (void)include_certs; if (mode == GPGME_SIG_MODE_CLEAR) err = add_arg (gpg, "--clearsign"); else { err = add_arg (gpg, "--sign"); if (!err && mode == GPGME_SIG_MODE_DETACH) err = add_arg (gpg, "--detach"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err) { if (gpgme_data_get_encoding (in) == GPGME_DATA_ENCODING_MIME && have_gpg_version (gpg, "2.1.14")) err = add_arg (gpg, "--mimemode"); else if (use_textmode) err = add_arg (gpg, "--textmode"); } } if (!err) err = append_args_from_signers (gpg, ctx); if (!err) err = append_args_from_sender (gpg, ctx); if (!err) err = append_args_from_sig_notations (gpg, ctx); if (gpgme_data_get_file_name (in)) { if (!err) err = add_arg (gpg, "--set-filename"); if (!err) err = add_arg (gpg, gpgme_data_get_file_name (in)); } /* Tell the gpg object about the data. */ if (!err) err = add_input_size_hint (gpg, in); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, in, -1, 0); if (!err) err = add_data (gpg, out, 1, 1); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_trustlist (void *engine, const char *pattern) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, "--with-colons"); if (!err) err = add_arg (gpg, "--list-trust-path"); /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--"); if (!err) err = add_arg (gpg, pattern); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext, gpgme_ctx_t ctx) { engine_gpg_t gpg = engine; gpgme_error_t err; err = append_args_from_sender (gpg, ctx); if (!err && ctx->auto_key_retrieve) err = add_arg (gpg, "--auto-key-retrieve"); if (err) ; else if (plaintext) { /* Normal or cleartext signature. */ err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_input_size_hint (gpg, sig); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, sig, -1, 0); if (!err) err = add_data (gpg, plaintext, 1, 1); } else { err = add_arg (gpg, "--verify"); if (!err) err = add_input_size_hint (gpg, signed_text); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, sig, -1, 0); if (!err && signed_text) err = add_data (gpg, signed_text, -1, 0); } if (!err) err = start (gpg); return err; } static void gpg_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_gpg_t gpg = engine; gpg->io_cbs = *io_cbs; } static gpgme_error_t gpg_set_pinentry_mode (void *engine, gpgme_pinentry_mode_t mode) { engine_gpg_t gpg = engine; gpg->pinentry_mode = mode; return 0; } static gpgme_error_t gpg_getauditlog (void *engine, gpgme_data_t output, unsigned int flags) { engine_gpg_t gpg = engine; #define MYBUFLEN 4096 char buf[MYBUFLEN]; int nread; int any_written = 0; if (!(flags & GPGME_AUDITLOG_DIAG)) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } if (!gpg || !output) { return gpg_error (GPG_ERR_INV_VALUE); } if (!gpg->diagnostics) { return gpg_error (GPG_ERR_GENERAL); } gpgme_data_rewind (gpg->diagnostics); while ((nread = gpgme_data_read (gpg->diagnostics, buf, MYBUFLEN)) > 0) { any_written = 1; if (gpgme_data_write (output, buf, nread) == -1) return gpg_error_from_syserror (); } if (!any_written) { return gpg_error (GPG_ERR_NO_DATA); } if (nread == -1) return gpg_error_from_syserror (); gpgme_data_rewind (output); return 0; #undef MYBUFLEN } struct engine_ops _gpgme_engine_ops_gpg = { /* Static functions. */ _gpgme_get_default_gpg_name, NULL, gpg_get_version, gpg_get_req_version, gpg_new, /* Member functions. */ gpg_release, NULL, /* reset */ gpg_set_status_cb, gpg_set_status_handler, gpg_set_command_handler, gpg_set_colon_line_handler, gpg_set_locale, NULL, /* set_protocol */ gpg_set_engine_flags, /* set_engine_flags */ gpg_decrypt, gpg_delete, gpg_edit, gpg_encrypt, gpg_encrypt_sign, gpg_export, gpg_export_ext, gpg_genkey, gpg_import, gpg_keylist, gpg_keylist_ext, gpg_keylist_data, gpg_keysign, gpg_tofu_policy, /* tofu_policy */ gpg_sign, gpg_trustlist, gpg_verify, gpg_getauditlog, NULL, /* opassuan_transact */ NULL, /* conf_load */ NULL, /* conf_save */ NULL, /* conf_dir */ NULL, /* query_swdb */ gpg_set_io_cbs, gpg_io_event, gpg_cancel, NULL, /* cancel_op */ gpg_passwd, gpg_set_pinentry_mode, NULL /* opspawn */ }; diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index a13af7fb..324c92a7 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -1,2240 +1,2240 @@ /* engine-gpgsm.c - GpgSM engine. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009, * 2010 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LOCALE_H #include #endif #include /* FIXME */ #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "priv-io.h" #include "sema.h" #include "data.h" #include "assuan.h" #include "debug.h" #include "engine-backend.h" typedef struct { int fd; /* FD we talk about. */ int server_fd;/* Server FD for this connection. */ int dir; /* Inbound/Outbound, maybe given implicit? */ void *data; /* Handler-specific data. */ void *tag; /* ID from the user for gpgme_remove_io_callback. */ char server_fd_str[15]; /* Same as SERVER_FD but as a string. We need this because _gpgme_io_fd2str can't be used on a closed descriptor. */ } iocb_data_t; struct engine_gpgsm { assuan_context_t assuan_ctx; int lc_ctype_set; int lc_messages_set; iocb_data_t status_cb; /* Input, output etc are from the servers perspective. */ iocb_data_t input_cb; gpgme_data_t input_helper_data; /* Input helper data object. */ void *input_helper_memory; /* Input helper memory block. */ iocb_data_t output_cb; iocb_data_t message_cb; struct { engine_status_handler_t fnc; void *fnc_value; gpgme_status_cb_t mon_cb; void *mon_cb_value; } status; struct { engine_colon_line_handler_t fnc; void *fnc_value; struct { char *line; int linesize; int linelen; } attic; int any; /* any data line seen */ } colon; gpgme_data_t inline_data; /* Used to collect D lines. */ char request_origin[10]; struct gpgme_io_cbs io_cbs; }; typedef struct engine_gpgsm *engine_gpgsm_t; static void gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data); static char * gpgsm_get_version (const char *file_name) { return _gpgme_get_program_version (file_name ? file_name : _gpgme_get_default_gpgsm_name ()); } static const char * gpgsm_get_req_version (void) { return "2.0.4"; } static void close_notify_handler (int fd, void *opaque) { engine_gpgsm_t gpgsm = opaque; assert (fd != -1); if (gpgsm->status_cb.fd == fd) { if (gpgsm->status_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag); gpgsm->status_cb.fd = -1; gpgsm->status_cb.tag = NULL; } else if (gpgsm->input_cb.fd == fd) { if (gpgsm->input_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag); gpgsm->input_cb.fd = -1; gpgsm->input_cb.tag = NULL; if (gpgsm->input_helper_data) { gpgme_data_release (gpgsm->input_helper_data); gpgsm->input_helper_data = NULL; } if (gpgsm->input_helper_memory) { free (gpgsm->input_helper_memory); gpgsm->input_helper_memory = NULL; } } else if (gpgsm->output_cb.fd == fd) { if (gpgsm->output_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag); gpgsm->output_cb.fd = -1; gpgsm->output_cb.tag = NULL; } else if (gpgsm->message_cb.fd == fd) { if (gpgsm->message_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag); gpgsm->message_cb.fd = -1; gpgsm->message_cb.tag = NULL; } } /* This is the default inquiry callback. We use it to handle the Pinentry notifications. */ static gpgme_error_t default_inq_cb (engine_gpgsm_t gpgsm, const char *line) { (void)gpgsm; if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) { _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10)); } return 0; } static gpgme_error_t gpgsm_cancel (void *engine) { engine_gpgsm_t gpgsm = engine; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (gpgsm->status_cb.fd != -1) _gpgme_io_close (gpgsm->status_cb.fd); if (gpgsm->input_cb.fd != -1) _gpgme_io_close (gpgsm->input_cb.fd); if (gpgsm->output_cb.fd != -1) _gpgme_io_close (gpgsm->output_cb.fd); if (gpgsm->message_cb.fd != -1) _gpgme_io_close (gpgsm->message_cb.fd); if (gpgsm->assuan_ctx) { assuan_release (gpgsm->assuan_ctx); gpgsm->assuan_ctx = NULL; } return 0; } static void gpgsm_release (void *engine) { engine_gpgsm_t gpgsm = engine; if (!gpgsm) return; gpgsm_cancel (engine); free (gpgsm->colon.attic.line); free (gpgsm); } static gpgme_error_t gpgsm_new (void **engine, const char *file_name, const char *home_dir, const char *version) { gpgme_error_t err = 0; engine_gpgsm_t gpgsm; const char *pgmname; const char *argv[5]; int argc; #if !USE_DESCRIPTOR_PASSING int fds[2]; int child_fds[4]; #endif char *dft_display = NULL; char dft_ttyname[64]; char *env_tty = NULL; char *dft_ttytype = NULL; char *optstr; (void)version; /* Not yet used. */ gpgsm = calloc (1, sizeof *gpgsm); if (!gpgsm) return gpg_error_from_syserror (); gpgsm->status_cb.fd = -1; gpgsm->status_cb.dir = 1; gpgsm->status_cb.tag = 0; gpgsm->status_cb.data = gpgsm; gpgsm->input_cb.fd = -1; gpgsm->input_cb.dir = 0; gpgsm->input_cb.tag = 0; gpgsm->input_cb.server_fd = -1; *gpgsm->input_cb.server_fd_str = 0; gpgsm->output_cb.fd = -1; gpgsm->output_cb.dir = 1; gpgsm->output_cb.tag = 0; gpgsm->output_cb.server_fd = -1; *gpgsm->output_cb.server_fd_str = 0; gpgsm->message_cb.fd = -1; gpgsm->message_cb.dir = 0; gpgsm->message_cb.tag = 0; gpgsm->message_cb.server_fd = -1; *gpgsm->message_cb.server_fd_str = 0; gpgsm->status.fnc = 0; gpgsm->colon.fnc = 0; gpgsm->colon.attic.line = 0; gpgsm->colon.attic.linesize = 0; gpgsm->colon.attic.linelen = 0; gpgsm->colon.any = 0; gpgsm->inline_data = NULL; gpgsm->io_cbs.add = NULL; gpgsm->io_cbs.add_priv = NULL; gpgsm->io_cbs.remove = NULL; gpgsm->io_cbs.event = NULL; gpgsm->io_cbs.event_priv = NULL; #if !USE_DESCRIPTOR_PASSING if (_gpgme_io_pipe (fds, 0) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->input_cb.fd = fds[1]; gpgsm->input_cb.server_fd = fds[0]; if (_gpgme_io_pipe (fds, 1) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->output_cb.fd = fds[0]; gpgsm->output_cb.server_fd = fds[1]; if (_gpgme_io_pipe (fds, 0) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->message_cb.fd = fds[1]; gpgsm->message_cb.server_fd = fds[0]; child_fds[0] = gpgsm->input_cb.server_fd; child_fds[1] = gpgsm->output_cb.server_fd; child_fds[2] = gpgsm->message_cb.server_fd; child_fds[3] = -1; #endif pgmname = file_name ? file_name : _gpgme_get_default_gpgsm_name (); argc = 0; argv[argc++] = _gpgme_get_basename (pgmname); if (home_dir) { argv[argc++] = "--homedir"; argv[argc++] = home_dir; } argv[argc++] = "--server"; argv[argc++] = NULL; err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME, &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, NULL); if (err) goto leave; assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks); #if USE_DESCRIPTOR_PASSING err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING); #else { assuan_fd_t achild_fds[4]; int i; /* For now... */ for (i = 0; i < 4; i++) achild_fds[i] = (assuan_fd_t) child_fds[i]; err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv, achild_fds, NULL, NULL, 0); /* For now... */ for (i = 0; i < 4; i++) child_fds[i] = (int) achild_fds[i]; } /* On Windows, handles are inserted in the spawned process with DuplicateHandle, and child_fds contains the server-local names for the inserted handles when assuan_pipe_connect returns. */ if (!err) { /* Note: We don't use _gpgme_io_fd2str here. On W32 the returned handles are real W32 system handles, not whatever GPGME uses internally (which may be a system handle, a C library handle or a GLib/Qt channel. Confusing, yes, but remember these are server-local names, so they are not part of GPGME at all. */ snprintf (gpgsm->input_cb.server_fd_str, sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]); snprintf (gpgsm->output_cb.server_fd_str, sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]); snprintf (gpgsm->message_cb.server_fd_str, sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]); } #endif if (err) goto leave; err = _gpgme_getenv ("DISPLAY", &dft_display); if (err) goto leave; if (dft_display) { if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0) { free (dft_display); err = gpg_error_from_syserror (); goto leave; } free (dft_display); err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; } err = _gpgme_getenv ("GPG_TTY", &env_tty); if (isatty (1) || env_tty || err) { int rc = 0; if (err) goto leave; else if (env_tty) { snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty); free (env_tty); } else rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); /* Even though isatty() returns 1, ttyname_r() may fail in many ways, e.g., when /dev/pts is not accessible under chroot. */ if (!rc) { if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; err = _gpgme_getenv ("TERM", &dft_ttytype); if (err) goto leave; if (dft_ttytype) { if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0) { free (dft_ttytype); err = gpg_error_from_syserror (); goto leave; } free (dft_ttytype); err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; } } } /* Ask gpgsm to enable the audit log support. */ if (!err) { err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is an optional feature of gpgsm. */ } #ifdef HAVE_W32_SYSTEM /* Under Windows we need to use AllowSetForegroundWindow. Tell gpgsm to tell us when it needs it. */ if (!err) { err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is a new feature of gpgsm. */ } #endif /*HAVE_W32_SYSTEM*/ #if !USE_DESCRIPTOR_PASSING if (!err && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd, close_notify_handler, gpgsm) || _gpgme_io_set_close_notify (gpgsm->output_cb.fd, close_notify_handler, gpgsm) || _gpgme_io_set_close_notify (gpgsm->message_cb.fd, close_notify_handler, gpgsm))) { err = gpg_error (GPG_ERR_GENERAL); goto leave; } #endif leave: /* Close the server ends of the pipes (because of this, we must use the stored server_fd_str in the function start). Our ends are closed in gpgsm_release(). */ #if !USE_DESCRIPTOR_PASSING if (gpgsm->input_cb.server_fd != -1) _gpgme_io_close (gpgsm->input_cb.server_fd); if (gpgsm->output_cb.server_fd != -1) _gpgme_io_close (gpgsm->output_cb.server_fd); if (gpgsm->message_cb.server_fd != -1) _gpgme_io_close (gpgsm->message_cb.server_fd); #endif if (err) gpgsm_release (gpgsm); else *engine = gpgsm; return err; } /* Copy flags from CTX into the engine object. */ static void gpgsm_set_engine_flags (void *engine, const gpgme_ctx_t ctx) { engine_gpgsm_t gpgsm = engine; if (ctx->request_origin) { if (strlen (ctx->request_origin) + 1 > sizeof gpgsm->request_origin) strcpy (gpgsm->request_origin, "xxx"); /* Too long - force error */ else strcpy (gpgsm->request_origin, ctx->request_origin); } else *gpgsm->request_origin = 0; } static gpgme_error_t gpgsm_set_locale (void *engine, int category, const char *value) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *optstr; const char *catstr; /* FIXME: If value is NULL, we need to reset the option to default. But we can't do this. So we error out here. GPGSM needs support for this. */ if (0) ; #ifdef LC_CTYPE else if (category == LC_CTYPE) { catstr = "lc-ctype"; if (!value && gpgsm->lc_ctype_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) gpgsm->lc_ctype_set = 1; } #endif #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { catstr = "lc-messages"; if (!value && gpgsm->lc_messages_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) gpgsm->lc_messages_set = 1; } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: Reset value to default. */ if (!value) return 0; if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0) err = gpg_error_from_syserror (); else { err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); } return err; } static gpgme_error_t gpgsm_assuan_simple_command (engine_gpgsm_t gpgsm, const char *cmd, engine_status_handler_t status_fnc, void *status_fnc_value) { assuan_context_t ctx = gpgsm->assuan_ctx; gpg_error_t err, cb_err; char *line; size_t linelen; err = assuan_write_line (ctx, cmd); if (err) return err; cb_err = 0; do { err = assuan_read_line (ctx, &line, &linelen); if (err) break; if (*line == '#' || !linelen) continue; if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) break; else if (linelen >= 4 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && line[3] == ' ') { /* We prefer a callback generated error because that one is more related to gpgme and thus probably more important than the error returned by the engine. */ err = cb_err? cb_err : atoi (&line[4]); cb_err = 0; } else if (linelen >= 2 && line[0] == 'S' && line[1] == ' ') { /* After an error from a status callback we skip all further status lines. */ if (!cb_err) { char *rest; gpgme_status_code_t r; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; r = _gpgme_parse_status (line + 2); if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS) { /* Note that we call the monitor even if we do * not know the status code (r < 0). */ cb_err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value, line + 2, rest); } if (r >= 0 && status_fnc && !cb_err) cb_err = status_fnc (status_fnc_value, r, rest); } } else { /* Invalid line or INQUIRY. We can't do anything else than to stop. As with ERR we prefer a status callback generated error code, though. */ err = cb_err ? cb_err : gpg_error (GPG_ERR_GENERAL); cb_err = 0; } } while (!err); /* We only want the first error from the status handler, thus we * take the one saved in CB_ERR. */ if (!err && cb_err) err = cb_err; return err; } typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t; static void gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type) { #if !USE_DESCRIPTOR_PASSING switch (fd_type) { case INPUT_FD: _gpgme_io_close (gpgsm->input_cb.fd); break; case OUTPUT_FD: _gpgme_io_close (gpgsm->output_cb.fd); break; case MESSAGE_FD: _gpgme_io_close (gpgsm->message_cb.fd); break; } #else (void)gpgsm; (void)fd_type; #endif } #define COMMANDLINELEN 40 static gpgme_error_t gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt) { gpg_error_t err = 0; char line[COMMANDLINELEN]; const char *which; iocb_data_t *iocb_data; #if USE_DESCRIPTOR_PASSING int dir; #endif switch (fd_type) { case INPUT_FD: which = "INPUT"; iocb_data = &gpgsm->input_cb; break; case OUTPUT_FD: which = "OUTPUT"; iocb_data = &gpgsm->output_cb; break; case MESSAGE_FD: which = "MESSAGE"; iocb_data = &gpgsm->message_cb; break; default: return gpg_error (GPG_ERR_INV_VALUE); } #if USE_DESCRIPTOR_PASSING dir = iocb_data->dir; /* We try to short-cut the communication by giving GPGSM direct access to the file descriptor, rather than using a pipe. */ iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data); if (iocb_data->server_fd < 0) { int fds[2]; if (_gpgme_io_pipe (fds, dir) < 0) return gpg_error_from_syserror (); iocb_data->fd = dir ? fds[0] : fds[1]; iocb_data->server_fd = dir ? fds[1] : fds[0]; if (_gpgme_io_set_close_notify (iocb_data->fd, close_notify_handler, gpgsm)) { err = gpg_error (GPG_ERR_GENERAL); goto leave_set_fd; } } err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd); if (err) goto leave_set_fd; _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; if (opt) snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt); else snprintf (line, COMMANDLINELEN, "%s FD", which); #else if (opt) snprintf (line, COMMANDLINELEN, "%s FD=%s %s", which, iocb_data->server_fd_str, opt); else snprintf (line, COMMANDLINELEN, "%s FD=%s", which, iocb_data->server_fd_str); #endif err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL); #if USE_DESCRIPTOR_PASSING leave_set_fd: if (err) { _gpgme_io_close (iocb_data->fd); iocb_data->fd = -1; if (iocb_data->server_fd != -1) { _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; } } #endif return err; } static const char * map_data_enc (gpgme_data_t d) { switch (gpgme_data_get_encoding (d)) { case GPGME_DATA_ENCODING_NONE: break; case GPGME_DATA_ENCODING_BINARY: return "--binary"; case GPGME_DATA_ENCODING_BASE64: return "--base64"; case GPGME_DATA_ENCODING_ARMOR: return "--armor"; default: break; } return NULL; } static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen); if (err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ - TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm, + TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: error from assuan (%d) getting status line : %s", fd, err, gpg_strerror (err)); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (&line[4]); if (! err) err = gpg_error (GPG_ERR_GENERAL); - TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, + TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: ERR line - mapped to: %s", fd, err ? gpg_strerror (err) : "ok"); /* Try our best to terminate the connection friendly. */ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { if (gpgsm->status.fnc) { char emptystring[1] = {0}; err = gpgsm->status.fnc (gpgsm->status.fnc_value, GPGME_STATUS_EOF, emptystring); if (gpg_err_code (err) == GPG_ERR_FALSE) err = 0; /* Drop special error code. */ } if (!err && gpgsm->colon.fnc && gpgsm->colon.any) { /* We must tell a colon function about the EOF. We do this only when we have seen any data lines. Note that this inlined use of colon data lines will eventually be changed into using a regular data channel. */ gpgsm->colon.any = 0; err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL); } - TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, + TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: OK line - final status: %s", fd, err ? gpg_strerror (err) : "ok"); _gpgme_io_close (gpgsm->status_cb.fd); return err; } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && gpgsm->colon.fnc) { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst; char **aline = &gpgsm->colon.attic.line; int *alinelen = &gpgsm->colon.attic.linelen; if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1) { char *newline = realloc (*aline, *alinelen + linelen + 1); if (!newline) err = gpg_error_from_syserror (); else { *aline = newline; gpgsm->colon.attic.linesize = *alinelen + linelen + 1; } } if (!err) { dst = *aline + *alinelen; while (!err && src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst = _gpgme_hextobyte (src); (*alinelen)++; src += 2; } else { *dst = *src++; (*alinelen)++; } if (*dst == '\n') { /* Terminate the pending line, pass it to the colon handler and reset it. */ gpgsm->colon.any = 1; if (*alinelen > 1 && *(dst - 1) == '\r') dst--; *dst = '\0'; /* FIXME How should we handle the return code? */ err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline); if (!err) { dst = *aline; *alinelen = 0; } } else dst++; } } - TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, + TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: D line; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && gpgsm->inline_data) { char *src = line + 2; char *end = line + linelen; char *dst = src; gpgme_ssize_t nwritten; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; while (linelen > 0) { nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen); if (nwritten <= 0 || nwritten > linelen) { err = gpg_error_from_syserror (); break; } src += nwritten; linelen -= nwritten; } - TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, + TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: D inlinedata; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { char *rest; gpgme_status_code_t r; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; r = _gpgme_parse_status (line + 2); if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS) { /* Note that we call the monitor even if we do * not know the status code (r < 0). */ err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value, line + 2, rest); } else err = 0; if (r >= 0 && !err) { if (gpgsm->status.fnc) { err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest); if (gpg_err_code (err) == GPG_ERR_FALSE) err = 0; /* Drop special error code. */ } } else fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest); - TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm, + TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: S line (%s) - final status: %s", fd, line+2, err? gpg_strerror (err):"ok"); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *keyword = line+7; while (*keyword == ' ') keyword++;; default_inq_cb (gpgsm, keyword); assuan_write_line (gpgsm->assuan_ctx, "END"); } } while (!err && assuan_pending_line (gpgsm->assuan_ctx)); return err; } static gpgme_error_t add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm, + TRACE_BEG (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm, "fd %d, dir %d", iocbd->fd, iocbd->dir); err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv, iocbd->fd, iocbd->dir, handler, iocbd->data, &iocbd->tag); if (err) return TRACE_ERR (err); if (!iocbd->dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (iocbd->fd); return TRACE_ERR (err); } static gpgme_error_t start (engine_gpgsm_t gpgsm, const char *command) { gpgme_error_t err; assuan_fd_t afdlist[5]; int fdlist[5]; int nfds; int i; if (*gpgsm->request_origin) { char *cmd; cmd = _gpgme_strconcat ("OPTION request-origin=", gpgsm->request_origin, NULL); if (!cmd) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm, cmd, NULL, NULL); free (cmd); if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION) return err; } /* We need to know the fd used by assuan for reads. We do this by using the assumption that the first returned fd from assuan_get_active_fds() is always this one. */ nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */, afdlist, DIM (afdlist)); if (nfds < 1) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ /* For now... */ for (i = 0; i < nfds; i++) fdlist[i] = (int) afdlist[i]; /* We "duplicate" the file descriptor, so we can close it here (we can't close fdlist[0], as that is closed by libassuan, and closing it here might cause libassuan to close some unrelated FD later). Alternatively, we could special case status_fd and register/unregister it manually as needed, but this increases code duplication and is more complicated as we can not use the close notifications etc. A third alternative would be to let Assuan know that we closed the FD, but that complicates the Assuan interface. */ gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]); if (gpgsm->status_cb.fd < 0) return gpg_error_from_syserror (); if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd, close_notify_handler, gpgsm)) { _gpgme_io_close (gpgsm->status_cb.fd); gpgsm->status_cb.fd = -1; return gpg_error (GPG_ERR_GENERAL); } err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler); if (!err && gpgsm->input_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler); if (!err && gpgsm->output_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler); if (!err && gpgsm->message_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler); if (!err) err = assuan_write_line (gpgsm->assuan_ctx, command); if (!err) gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL); return err; } #if USE_DESCRIPTOR_PASSING static gpgme_error_t gpgsm_reset (void *engine) { engine_gpgsm_t gpgsm = engine; /* IF we have an active connection we must send a reset because we need to reset the list of signers. Note that RESET does not reset OPTION commands. */ return (gpgsm->assuan_ctx ? gpgsm_assuan_simple_command (gpgsm, "RESET", NULL, NULL) : 0); } #endif static gpgme_error_t gpgsm_decrypt (void *engine, gpgme_decrypt_flags_t flags, gpgme_data_t ciph, gpgme_data_t plain, int export_session_key, const char *override_session_key, int auto_key_retrieve) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; (void)flags; /* gpgsm is not capable of exporting session keys right now, so we * will ignore this if requested. */ (void)export_session_key; (void)override_session_key; /* --auto-key-retrieve is also not supported. */ (void)auto_key_retrieve; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); gpgsm->input_cb.data = ciph; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ gpgsm->output_cb.data = plain; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); if (err) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (engine, "DECRYPT"); return err; } static gpgme_error_t gpgsm_delete (void *engine, gpgme_key_t key, unsigned int flags) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *fpr = key->subkeys ? key->subkeys->fpr : NULL; char *linep = fpr; char *line; int length = 8; /* "DELKEYS " */ (void)flags; if (!fpr) return gpg_error (GPG_ERR_INV_VALUE); while (*linep) { length++; if (*linep == '%' || *linep == ' ' || *linep == '+') length += 2; linep++; } length++; line = malloc (length); if (!line) return gpg_error_from_syserror (); strcpy (line, "DELKEYS "); linep = &line[8]; while (*fpr) { switch (*fpr) { case '%': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '5'; break; case ' ': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '0'; break; case '+': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = 'B'; break; default: *(linep++) = *fpr; break; } fpr++; } *linep = '\0'; gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[]) { gpgme_error_t err = 0; char *line; int linelen; int invalid_recipients = 0; int i; linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */ line = malloc (10 + 40 + 1); if (!line) return gpg_error_from_syserror (); strcpy (line, "RECIPIENT "); for (i =0; !err && recp[i]; i++) { char *fpr; int newlen; if (!recp[i]->subkeys || !recp[i]->subkeys->fpr) { invalid_recipients++; continue; } fpr = recp[i]->subkeys->fpr; newlen = 11 + strlen (fpr); if (linelen < newlen) { char *newline = realloc (line, newlen); if (! newline) { int saved_err = gpg_error_from_syserror (); free (line); return saved_err; } line = newline; linelen = newlen; } strcpy (&line[10], fpr); err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc, gpgsm->status.fnc_value); /* FIXME: This requires more work. */ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) invalid_recipients++; else if (err) { free (line); return err; } } free (line); return gpg_error (invalid_recipients ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR); } /* Take recipients from the LF delimited STRING and send RECIPIENT * commands to gpgsm. */ static gpgme_error_t set_recipients_from_string (engine_gpgsm_t gpgsm, const char *string) { gpg_error_t err = 0; char *line = NULL; int no_pubkey = 0; const char *s; int n; for (;;) { while (*string == ' ' || *string == '\t') string++; if (!*string) break; s = strchr (string, '\n'); if (s) n = s - string; else n = strlen (string); while (n && (string[n-1] == ' ' || string[n-1] == '\t')) n--; gpgrt_free (line); if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0) { err = gpg_error_from_syserror (); break; } string += n + !!s; err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc, gpgsm->status.fnc_value); /* Fixme: Improve error reporting. */ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) no_pubkey++; else if (err) break; } gpgrt_free (line); return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0; } static gpgme_error_t gpgsm_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (!recp) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); if ((flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) { err = gpgsm_assuan_simple_command (gpgsm, "OPTION no-encrypt-to", NULL, NULL); if (err) return err; } gpgsm->input_cb.data = plain; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = ciph; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; if (!recp && recpstring) err = set_recipients_from_string (gpgsm, recpstring); else err = set_recipients (gpgsm, recp); if (!err) err = start (gpgsm, "ENCRYPT"); return err; } static gpgme_error_t gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; char *cmd; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (!pattern) pattern = ""; cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1); if (!cmd) return gpg_error_from_syserror (); strcpy (cmd, "EXPORT "); if ((mode & GPGME_EXPORT_MODE_SECRET)) { strcat (cmd, "--secret "); if ((mode & GPGME_EXPORT_MODE_RAW)) strcat (cmd, "--raw "); else if ((mode & GPGME_EXPORT_MODE_PKCS12)) strcat (cmd, "--pkcs12 "); } strcat (cmd, pattern); gpgsm->output_cb.data = keydata; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, cmd); free (cmd); return err; } static gpgme_error_t gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; char *line; /* Length is "EXPORT " + "--secret " + "--pkcs12 " + p + '\0'. */ int length = 7 + 9 + 9 + 1; char *linep; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (pattern && *pattern) { const char **pat = pattern; while (*pat) { const char *patlet = *pat; while (*patlet) { length++; if (*patlet == '%' || *patlet == ' ' || *patlet == '+') length += 2; patlet++; } pat++; length++; } } line = malloc (length); if (!line) return gpg_error_from_syserror (); strcpy (line, "EXPORT "); if ((mode & GPGME_EXPORT_MODE_SECRET)) { strcat (line, "--secret "); if ((mode & GPGME_EXPORT_MODE_RAW)) strcat (line, "--raw "); else if ((mode & GPGME_EXPORT_MODE_PKCS12)) strcat (line, "--pkcs12 "); } linep = &line[strlen (line)]; if (pattern && *pattern) { while (*pattern) { const char *patlet = *pattern; while (*patlet) { switch (*patlet) { case '%': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '5'; break; case ' ': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '0'; break; case '+': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = 'B'; break; default: *(linep++) = *patlet; break; } patlet++; } pattern++; if (*pattern) *linep++ = ' '; } } *linep = '\0'; gpgsm->output_cb.data = keydata; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t gpgsm_genkey (void *engine, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t key, unsigned int flags, gpgme_data_t help_data, unsigned int extraflags, gpgme_data_t pubkey, gpgme_data_t seckey) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; (void)reserved; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (help_data) { if (!pubkey || seckey) return gpg_error (GPG_ERR_INV_VALUE); gpgsm->input_cb.data = help_data; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = pubkey; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, (extraflags & GENKEY_EXTRAFLAG_ARMOR)? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, "GENKEY"); return err; } (void)userid; (void)algo; (void)expires; (void)key; (void)flags; /* The new interface has not yet been implemented, */ return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpgme_error_t gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; gpgme_data_encoding_t dataenc; int idx; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (keydata && keyarray) return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */ dataenc = gpgme_data_get_encoding (keydata); if (keyarray) { size_t buflen; char *buffer, *p; /* Fist check whether the engine already features the --re-import option. */ err = gpgsm_assuan_simple_command (gpgsm, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL); if (err) return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Create an internal data object with a list of all fingerprints. The data object and its memory (to avoid an extra copy by gpgme_data_new_from_mem) are stored in two variables which are released by the close_notify_handler. */ for (idx=0, buflen=0; keyarray[idx]; idx++) { if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS && keyarray[idx]->subkeys && keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) buflen += strlen (keyarray[idx]->subkeys->fpr) + 1; } /* Allocate a buffer with extra space for the trailing Nul introduced by the use of stpcpy. */ buffer = malloc (buflen+1); if (!buffer) return gpg_error_from_syserror (); for (idx=0, p = buffer; keyarray[idx]; idx++) { if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS && keyarray[idx]->subkeys && keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n"); } err = gpgme_data_new_from_mem (&gpgsm->input_helper_data, buffer, buflen, 0); if (err) { free (buffer); return err; } gpgsm->input_helper_memory = buffer; gpgsm->input_cb.data = gpgsm->input_helper_data; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) { gpgme_data_release (gpgsm->input_helper_data); gpgsm->input_helper_data = NULL; free (gpgsm->input_helper_memory); gpgsm->input_helper_memory = NULL; return err; } gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; return start (gpgsm, "IMPORT --re-import"); } else if (dataenc == GPGME_DATA_ENCODING_URL || dataenc == GPGME_DATA_ENCODING_URL0 || dataenc == GPGME_DATA_ENCODING_URLESC) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } else { gpgsm->input_cb.data = keydata; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; return start (gpgsm, "IMPORT"); } } static gpgme_error_t gpgsm_keylist (void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode, int engine_flags) { engine_gpgsm_t gpgsm = engine; char *line; gpgme_error_t err; int list_mode = 0; if (mode & GPGME_KEYLIST_MODE_LOCAL) list_mode |= 1; if (mode & GPGME_KEYLIST_MODE_EXTERN) list_mode |= 2; if (!pattern) pattern = ""; /* Hack to make sure that the agent is started. Only if the agent has been started an application may connect to the agent via GPGME_PROTOCOL_ASSUAN - for example to look for smartcards. We do this only if a secret key listing has been requested. In general this is not needed because a secret key listing starts the agent. However on a fresh installation no public keys are available and thus there is no need for gpgsm to ask the agent whether a secret key exists for the public key. */ if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET)) gpgsm_assuan_simple_command (gpgsm, "GETINFO agent-check", NULL, NULL); /* Always send list-mode option because RESET does not reset it. */ if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL); gpgrt_free (line); if (err) return err; /* Always send key validation because RESET does not reset it. */ /* Use the validation mode if requested. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm, (mode & GPGME_KEYLIST_MODE_VALIDATE)? "OPTION with-validation=1": "OPTION with-validation=0" , NULL, NULL); /* Include the ephemeral keys if requested. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm, (mode & GPGME_KEYLIST_MODE_EPHEMERAL)? "OPTION with-ephemeral-keys=1": "OPTION with-ephemeral-keys=0" , NULL, NULL); gpgsm_assuan_simple_command (gpgsm, (mode & GPGME_KEYLIST_MODE_WITH_SECRET)? "OPTION with-secret=1": "OPTION with-secret=0" , NULL, NULL); gpgsm_assuan_simple_command (gpgsm, (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)? "OPTION offline=1": "OPTION offline=0" , NULL, NULL); /* Length is "LISTSECRETKEYS " + p + '\0'. */ line = malloc (15 + strlen (pattern) + 1); if (!line) return gpg_error_from_syserror (); if (secret_only) { strcpy (line, "LISTSECRETKEYS "); strcpy (&line[15], pattern); } else { strcpy (line, "LISTKEYS "); strcpy (&line[9], pattern); } gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only, int reserved, gpgme_keylist_mode_t mode, int engine_flags) { engine_gpgsm_t gpgsm = engine; char *line; gpgme_error_t err; /* Length is "LISTSECRETKEYS " + p + '\0'. */ int length = 15 + 1; char *linep; int any_pattern = 0; int list_mode = 0; if (reserved) return gpg_error (GPG_ERR_INV_VALUE); if (mode & GPGME_KEYLIST_MODE_LOCAL) list_mode |= 1; if (mode & GPGME_KEYLIST_MODE_EXTERN) list_mode |= 2; /* Always send list-mode option because RESET does not reset it. */ if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL); gpgrt_free (line); if (err) return err; /* Always send key validation because RESET does not reset it. */ /* Use the validation mode if required. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm, (mode & GPGME_KEYLIST_MODE_VALIDATE)? "OPTION with-validation=1": "OPTION with-validation=0" , NULL, NULL); gpgsm_assuan_simple_command (gpgsm, (mode & GPGME_KEYLIST_MODE_WITH_SECRET)? "OPTION with-secret=1": "OPTION with-secret=0" , NULL, NULL); gpgsm_assuan_simple_command (gpgsm, (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)? "OPTION offline=1": "OPTION offline=0" , NULL, NULL); if (pattern && *pattern) { const char **pat = pattern; while (*pat) { const char *patlet = *pat; while (*patlet) { length++; if (*patlet == '%' || *patlet == ' ' || *patlet == '+') length += 2; patlet++; } pat++; length++; } } line = malloc (length); if (!line) return gpg_error_from_syserror (); if (secret_only) { strcpy (line, "LISTSECRETKEYS "); linep = &line[15]; } else { strcpy (line, "LISTKEYS "); linep = &line[9]; } if (pattern && *pattern) { while (*pattern) { const char *patlet = *pattern; while (*patlet) { switch (*patlet) { case '%': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '5'; break; case ' ': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '0'; break; case '+': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = 'B'; break; default: *(linep++) = *patlet; break; } patlet++; } any_pattern = 1; *linep++ = ' '; pattern++; } } if (any_pattern) linep--; *linep = '\0'; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *assuan_cmd; int i; gpgme_key_t key; (void)use_textmode; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: This does not work as RESET does not reset it so we can't revert back to default. */ if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT) { /* FIXME: Make sure that if we run multiple operations, that we can reset any previously set value in case the default is requested. */ if (gpgrt_asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm, assuan_cmd, NULL, NULL); gpgrt_free (assuan_cmd); if (err) return err; } for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++) { const char *s = key->subkeys ? key->subkeys->fpr : NULL; if (s && strlen (s) < 80) { char buf[100]; strcpy (stpcpy (buf, "SIGNER "), s); err = gpgsm_assuan_simple_command (gpgsm, buf, gpgsm->status.fnc, gpgsm->status.fnc_value); } else err = gpg_error (GPG_ERR_INV_VALUE); gpgme_key_unref (key); if (err) return err; } gpgsm->input_cb.data = in; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = out; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH ? "SIGN --detached" : "SIGN"); return err; } static gpgme_error_t gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext, gpgme_ctx_t ctx) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; (void)ctx; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); gpgsm->input_cb.data = sig; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; if (!signed_text) { /* Normal or cleartext signature. */ if (plaintext) { gpgsm->output_cb.data = plaintext; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); } else { /* No output requested. */ gpgsm_clear_fd (gpgsm, OUTPUT_FD); } gpgsm_clear_fd (gpgsm, MESSAGE_FD); } else { /* Detached signature. */ gpgsm->message_cb.data = signed_text; err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0); gpgsm_clear_fd (gpgsm, OUTPUT_FD); } gpgsm->inline_data = NULL; if (!err) err = start (gpgsm, "VERIFY"); return err; } /* Send the GETAUDITLOG command. The result is saved to a gpgme data object. */ static gpgme_error_t gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; if (!gpgsm || !output) return gpg_error (GPG_ERR_INV_VALUE); if ((flags & GPGME_AUDITLOG_DIAG)) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); #if USE_DESCRIPTOR_PASSING gpgsm->output_cb.data = output; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); if (err) return err; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; # define CMD "GETAUDITLOG" #else gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = output; # define CMD "GETAUDITLOG --data" #endif err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD); return err; } /* This sets a status callback for monitoring status lines before they * are passed to a caller set handler. */ static void gpgsm_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value) { engine_gpgsm_t gpgsm = engine; gpgsm->status.mon_cb = cb; gpgsm->status.mon_cb_value = cb_value; } static void gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc, void *fnc_value) { engine_gpgsm_t gpgsm = engine; gpgsm->status.fnc = fnc; gpgsm->status.fnc_value = fnc_value; } static gpgme_error_t gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc, void *fnc_value) { engine_gpgsm_t gpgsm = engine; gpgsm->colon.fnc = fnc; gpgsm->colon.fnc_value = fnc_value; gpgsm->colon.any = 0; return 0; } static void gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_gpgsm_t gpgsm = engine; gpgsm->io_cbs = *io_cbs; } static void gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_gpgsm_t gpgsm = engine; - TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm, + TRACE (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm, "event %p, type %d, type_data %p", gpgsm->io_cbs.event, type, type_data); if (gpgsm->io_cbs.event) (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data); } static gpgme_error_t gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *line; (void)flags; if (!key || !key->subkeys || !key->subkeys->fpr) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (gpgrt_asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0) return gpg_error_from_syserror (); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); gpgrt_free (line); return err; } struct engine_ops _gpgme_engine_ops_gpgsm = { /* Static functions. */ _gpgme_get_default_gpgsm_name, NULL, gpgsm_get_version, gpgsm_get_req_version, gpgsm_new, /* Member functions. */ gpgsm_release, #if USE_DESCRIPTOR_PASSING gpgsm_reset, #else NULL, /* reset */ #endif gpgsm_set_status_cb, gpgsm_set_status_handler, NULL, /* set_command_handler */ gpgsm_set_colon_line_handler, gpgsm_set_locale, NULL, /* set_protocol */ gpgsm_set_engine_flags, gpgsm_decrypt, gpgsm_delete, /* decrypt_verify */ NULL, /* edit */ gpgsm_encrypt, NULL, /* encrypt_sign */ gpgsm_export, gpgsm_export_ext, gpgsm_genkey, gpgsm_import, gpgsm_keylist, gpgsm_keylist_ext, NULL, /* keylist_data */ NULL, /* keysign */ NULL, /* tofu_policy */ gpgsm_sign, NULL, /* trustlist */ gpgsm_verify, gpgsm_getauditlog, NULL, /* opassuan_transact */ NULL, /* conf_load */ NULL, /* conf_save */ NULL, /* conf_dir */ NULL, /* query_swdb */ gpgsm_set_io_cbs, gpgsm_io_event, gpgsm_cancel, NULL, /* cancel_op */ gpgsm_passwd, NULL, /* set_pinentry_mode */ NULL /* opspawn */ }; diff --git a/src/engine-spawn.c b/src/engine-spawn.c index 6f57a7d3..296d7f25 100644 --- a/src/engine-spawn.c +++ b/src/engine-spawn.c @@ -1,484 +1,484 @@ /* engine-spawn.c - Run an arbitrary program * Copyright (C) 2014 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LOCALE_H #include #endif #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "context.h" /*temp hack until we have GpmeData methods to do I/O */ #include "priv-io.h" #include "sema.h" #include "debug.h" #include "engine-backend.h" /* This type is used to build a list of data sources/sinks. */ struct datalist_s { struct datalist_s *next; gpgme_data_t data; /* The data object. */ int inbound; /* True if this is used for reading from the peer. */ int dup_to; /* The fd used by the peer. */ }; struct fd_data_map_s { gpgme_data_t data; int inbound; /* True if this is used for reading from the peer. */ int dup_to; /* Dup the fd to that one. */ int fd; /* The fd to use. */ int peer_fd; /* The other side of the pipe. */ void *tag; /* Tag used by the I/O callback. */ }; struct engine_spawn { struct datalist_s *arglist; struct datalist_s **argtail; struct fd_data_map_s *fd_data_map; struct gpgme_io_cbs io_cbs; }; typedef struct engine_spawn *engine_spawn_t; static void engspawn_io_event (void *engine, gpgme_event_io_t type, void *type_data); static gpgme_error_t engspawn_cancel (void *engine); static void close_notify_handler (int fd, void *opaque) { engine_spawn_t esp = opaque; int i; assert (fd != -1); if (esp->fd_data_map) { for (i = 0; esp->fd_data_map[i].data; i++) { if (esp->fd_data_map[i].fd == fd) { if (esp->fd_data_map[i].tag) (*esp->io_cbs.remove) (esp->fd_data_map[i].tag); esp->fd_data_map[i].fd = -1; break; } if (esp->fd_data_map[i].peer_fd == fd) { esp->fd_data_map[i].peer_fd = -1; break; } } } } static gpgme_error_t add_data (engine_spawn_t esp, gpgme_data_t data, int dup_to, int inbound) { struct datalist_s *a; assert (esp); assert (data); a = malloc (sizeof *a); if (!a) return gpg_error_from_syserror (); a->next = NULL; a->data = data; a->inbound = inbound; a->dup_to = dup_to; *esp->argtail = a; esp->argtail = &a->next; return 0; } static void free_fd_data_map (struct fd_data_map_s *fd_data_map) { int i; if (!fd_data_map) return; for (i = 0; fd_data_map[i].data; i++) { if (fd_data_map[i].fd != -1) _gpgme_io_close (fd_data_map[i].fd); if (fd_data_map[i].peer_fd != -1) _gpgme_io_close (fd_data_map[i].peer_fd); /* Don't release data because this is only a reference. */ } free (fd_data_map); } static gpgme_error_t build_fd_data_map (engine_spawn_t esp) { struct datalist_s *a; size_t datac; int fds[2]; for (datac = 0, a = esp->arglist; a; a = a->next) if (a->data) datac++; free_fd_data_map (esp->fd_data_map); esp->fd_data_map = calloc (datac + 1, sizeof *esp->fd_data_map); if (!esp->fd_data_map) return gpg_error_from_syserror (); for (datac = 0, a = esp->arglist; a; a = a->next) { assert (a->data); if (_gpgme_io_pipe (fds, a->inbound ? 1 : 0) == -1) { free (esp->fd_data_map); esp->fd_data_map = NULL; return gpg_error_from_syserror (); } if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, esp) || _gpgme_io_set_close_notify (fds[1], close_notify_handler, esp)) { /* FIXME: Need error cleanup. */ return gpg_error (GPG_ERR_GENERAL); } esp->fd_data_map[datac].inbound = a->inbound; if (a->inbound) { esp->fd_data_map[datac].fd = fds[0]; esp->fd_data_map[datac].peer_fd = fds[1]; } else { esp->fd_data_map[datac].fd = fds[1]; esp->fd_data_map[datac].peer_fd = fds[0]; } esp->fd_data_map[datac].data = a->data; esp->fd_data_map[datac].dup_to = a->dup_to; datac++; } return 0; } static gpgme_error_t add_io_cb (engine_spawn_t esp, int fd, int dir, gpgme_io_cb_t handler, void *data, void **tag) { gpgme_error_t err; err = (*esp->io_cbs.add) (esp->io_cbs.add_priv, fd, dir, handler, data, tag); if (err) return err; if (!dir) /* Fixme: Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (fd); return err; } static gpgme_error_t engspawn_start (engine_spawn_t esp, const char *file, const char *argv[], unsigned int flags) { gpgme_error_t err; int i, n; int status; struct spawn_fd_item_s *fd_list; pid_t pid; unsigned int spflags; const char *save_argv0 = NULL; if (!esp || !file || !argv || !argv[0]) return gpg_error (GPG_ERR_INV_VALUE); spflags = 0; if ((flags & GPGME_SPAWN_DETACHED)) spflags |= IOSPAWN_FLAG_DETACHED; if ((flags & GPGME_SPAWN_ALLOW_SET_FG)) spflags |= IOSPAWN_FLAG_ALLOW_SET_FG; if ((flags & GPGME_SPAWN_SHOW_WINDOW)) spflags |= IOSPAWN_FLAG_SHOW_WINDOW; err = build_fd_data_map (esp); if (err) return err; n = 0; for (i = 0; esp->fd_data_map[i].data; i++) n++; fd_list = calloc (n+1, sizeof *fd_list); if (!fd_list) return gpg_error_from_syserror (); /* Build the fd list for the child. */ n = 0; for (i = 0; esp->fd_data_map[i].data; i++) { fd_list[n].fd = esp->fd_data_map[i].peer_fd; fd_list[n].dup_to = esp->fd_data_map[i].dup_to; n++; } fd_list[n].fd = -1; fd_list[n].dup_to = -1; if (argv[0] && !*argv[0]) { save_argv0 = argv[0]; argv[0] = _gpgme_get_basename (file); } status = _gpgme_io_spawn (file, (char * const *)argv, spflags, fd_list, NULL, NULL, &pid); if (save_argv0) argv[0] = save_argv0; free (fd_list); if (status == -1) return gpg_error_from_syserror (); for (i = 0; esp->fd_data_map[i].data; i++) { err = add_io_cb (esp, esp->fd_data_map[i].fd, esp->fd_data_map[i].inbound, esp->fd_data_map[i].inbound ? _gpgme_data_inbound_handler : _gpgme_data_outbound_handler, esp->fd_data_map[i].data, &esp->fd_data_map[i].tag); if (err) return err; /* FIXME: kill the child */ } engspawn_io_event (esp, GPGME_EVENT_START, NULL); return 0; } /* Public functions */ static const char * engspawn_get_file_name (void) { return "/nonexistent"; } static char * engspawn_get_version (const char *file_name) { (void)file_name; return NULL; } static const char * engspawn_get_req_version (void) { return NULL; } static gpgme_error_t engspawn_new (void **engine, const char *file_name, const char *home_dir, const char *version) { engine_spawn_t esp; (void)file_name; (void)home_dir; (void)version; esp = calloc (1, sizeof *esp); if (!esp) return gpg_error_from_syserror (); esp->argtail = &esp->arglist; *engine = esp; return 0; } static void engspawn_release (void *engine) { engine_spawn_t esp = engine; if (!esp) return; engspawn_cancel (engine); while (esp->arglist) { struct datalist_s *next = esp->arglist->next; free (esp->arglist); esp->arglist = next; } free (esp); } static void engspawn_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_spawn_t esp = engine; esp->io_cbs = *io_cbs; } static void engspawn_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_spawn_t esp = engine; - TRACE3 (DEBUG_ENGINE, "gpgme:engspawn_io_event", esp, + TRACE (DEBUG_ENGINE, "gpgme:engspawn_io_event", esp, "event %p, type %d, type_data %p", esp->io_cbs.event, type, type_data); if (esp->io_cbs.event) (*esp->io_cbs.event) (esp->io_cbs.event_priv, type, type_data); } static gpgme_error_t engspawn_cancel (void *engine) { engine_spawn_t esp = engine; if (!esp) return gpg_error (GPG_ERR_INV_VALUE); if (esp->fd_data_map) { free_fd_data_map (esp->fd_data_map); esp->fd_data_map = NULL; } return 0; } static gpgme_error_t engspawn_op_spawn (void *engine, const char *file, const char *argv[], gpgme_data_t datain, gpgme_data_t dataout, gpgme_data_t dataerr, unsigned int flags) { engine_spawn_t esp = engine; gpgme_error_t err = 0; if (datain) err = add_data (esp, datain, 0, 0); if (!err && dataout) err = add_data (esp, dataout, 1, 1); if (!err && dataerr) err = add_data (esp, dataerr, 2, 1); if (!err) err = engspawn_start (esp, file, argv, flags); return err; } struct engine_ops _gpgme_engine_ops_spawn = { /* Static functions. */ engspawn_get_file_name, NULL, /* get_home_dir */ engspawn_get_version, engspawn_get_req_version, engspawn_new, /* Member functions. */ engspawn_release, NULL, /* reset */ NULL, /* set_status_cb */ NULL, /* set_status_handler */ NULL, /* set_command_handler */ NULL, /* set_colon_line_handler */ NULL, /* set_locale */ NULL, /* set_protocol */ NULL, /* set_engine_flags */ NULL, /* decrypt */ NULL, /* delete */ NULL, /* edit */ NULL, /* encrypt */ NULL, /* encrypt_sign */ NULL, /* export */ NULL, /* export_ext */ NULL, /* genkey */ NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ NULL, /* keylist_data */ NULL, /* keysign */ NULL, /* tofu_policy */ NULL, /* sign */ NULL, /* trustlist */ NULL, /* verify */ NULL, /* getauditlog */ NULL, /* opassuan_transact */ NULL, /* conf_load */ NULL, /* conf_save */ NULL, /* conf_dir */ NULL, /* query_swdb */ engspawn_set_io_cbs, engspawn_io_event, /* io_event */ engspawn_cancel, /* cancel */ NULL, /* cancel_op */ NULL, /* passwd */ NULL, /* set_pinentry_mode */ engspawn_op_spawn /* opspawn */ }; diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c index a79b3b13..62c4e5b6 100644 --- a/src/engine-uiserver.c +++ b/src/engine-uiserver.c @@ -1,1455 +1,1455 @@ /* engine-uiserver.c - Uiserver engine. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ /* Peculiar: Use special keys from email address for recipient and signer (==sender). Use no data objects with encryption for prep_encrypt. */ #if HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include /* FIXME */ #include #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "priv-io.h" #include "sema.h" #include "data.h" #include "assuan.h" #include "debug.h" #include "engine-backend.h" typedef struct { int fd; /* FD we talk about. */ int server_fd;/* Server FD for this connection. */ int dir; /* Inbound/Outbound, maybe given implicit? */ void *data; /* Handler-specific data. */ void *tag; /* ID from the user for gpgme_remove_io_callback. */ char server_fd_str[15]; /* Same as SERVER_FD but as a string. We need this because _gpgme_io_fd2str can't be used on a closed descriptor. */ } iocb_data_t; struct engine_uiserver { assuan_context_t assuan_ctx; int lc_ctype_set; int lc_messages_set; gpgme_protocol_t protocol; iocb_data_t status_cb; /* Input, output etc are from the servers perspective. */ iocb_data_t input_cb; gpgme_data_t input_helper_data; /* Input helper data object. */ void *input_helper_memory; /* Input helper memory block. */ iocb_data_t output_cb; iocb_data_t message_cb; struct { engine_status_handler_t fnc; void *fnc_value; gpgme_status_cb_t mon_cb; void *mon_cb_value; } status; struct { engine_colon_line_handler_t fnc; void *fnc_value; struct { char *line; int linesize; int linelen; } attic; int any; /* any data line seen */ } colon; gpgme_data_t inline_data; /* Used to collect D lines. */ struct gpgme_io_cbs io_cbs; }; typedef struct engine_uiserver *engine_uiserver_t; static void uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data); static char * uiserver_get_version (const char *file_name) { (void)file_name; return NULL; } static const char * uiserver_get_req_version (void) { return NULL; } static void close_notify_handler (int fd, void *opaque) { engine_uiserver_t uiserver = opaque; assert (fd != -1); if (uiserver->status_cb.fd == fd) { if (uiserver->status_cb.tag) (*uiserver->io_cbs.remove) (uiserver->status_cb.tag); uiserver->status_cb.fd = -1; uiserver->status_cb.tag = NULL; } else if (uiserver->input_cb.fd == fd) { if (uiserver->input_cb.tag) (*uiserver->io_cbs.remove) (uiserver->input_cb.tag); uiserver->input_cb.fd = -1; uiserver->input_cb.tag = NULL; if (uiserver->input_helper_data) { gpgme_data_release (uiserver->input_helper_data); uiserver->input_helper_data = NULL; } if (uiserver->input_helper_memory) { free (uiserver->input_helper_memory); uiserver->input_helper_memory = NULL; } } else if (uiserver->output_cb.fd == fd) { if (uiserver->output_cb.tag) (*uiserver->io_cbs.remove) (uiserver->output_cb.tag); uiserver->output_cb.fd = -1; uiserver->output_cb.tag = NULL; } else if (uiserver->message_cb.fd == fd) { if (uiserver->message_cb.tag) (*uiserver->io_cbs.remove) (uiserver->message_cb.tag); uiserver->message_cb.fd = -1; uiserver->message_cb.tag = NULL; } } /* This is the default inquiry callback. We use it to handle the Pinentry notifications. */ static gpgme_error_t default_inq_cb (engine_uiserver_t uiserver, const char *line) { (void)uiserver; if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) { _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10)); } return 0; } static gpgme_error_t uiserver_cancel (void *engine) { engine_uiserver_t uiserver = engine; if (!uiserver) return gpg_error (GPG_ERR_INV_VALUE); if (uiserver->status_cb.fd != -1) _gpgme_io_close (uiserver->status_cb.fd); if (uiserver->input_cb.fd != -1) _gpgme_io_close (uiserver->input_cb.fd); if (uiserver->output_cb.fd != -1) _gpgme_io_close (uiserver->output_cb.fd); if (uiserver->message_cb.fd != -1) _gpgme_io_close (uiserver->message_cb.fd); if (uiserver->assuan_ctx) { assuan_release (uiserver->assuan_ctx); uiserver->assuan_ctx = NULL; } return 0; } static void uiserver_release (void *engine) { engine_uiserver_t uiserver = engine; if (!uiserver) return; uiserver_cancel (engine); free (uiserver->colon.attic.line); free (uiserver); } static gpgme_error_t uiserver_new (void **engine, const char *file_name, const char *home_dir, const char *version) { gpgme_error_t err = 0; engine_uiserver_t uiserver; char *dft_display = NULL; char dft_ttyname[64]; char *env_tty = NULL; char *dft_ttytype = NULL; char *optstr; (void)home_dir; (void)version; /* Not yet used. */ uiserver = calloc (1, sizeof *uiserver); if (!uiserver) return gpg_error_from_syserror (); uiserver->protocol = GPGME_PROTOCOL_DEFAULT; uiserver->status_cb.fd = -1; uiserver->status_cb.dir = 1; uiserver->status_cb.tag = 0; uiserver->status_cb.data = uiserver; uiserver->input_cb.fd = -1; uiserver->input_cb.dir = 0; uiserver->input_cb.tag = 0; uiserver->input_cb.server_fd = -1; *uiserver->input_cb.server_fd_str = 0; uiserver->output_cb.fd = -1; uiserver->output_cb.dir = 1; uiserver->output_cb.tag = 0; uiserver->output_cb.server_fd = -1; *uiserver->output_cb.server_fd_str = 0; uiserver->message_cb.fd = -1; uiserver->message_cb.dir = 0; uiserver->message_cb.tag = 0; uiserver->message_cb.server_fd = -1; *uiserver->message_cb.server_fd_str = 0; uiserver->status.fnc = 0; uiserver->colon.fnc = 0; uiserver->colon.attic.line = 0; uiserver->colon.attic.linesize = 0; uiserver->colon.attic.linelen = 0; uiserver->colon.any = 0; uiserver->inline_data = NULL; uiserver->io_cbs.add = NULL; uiserver->io_cbs.add_priv = NULL; uiserver->io_cbs.remove = NULL; uiserver->io_cbs.event = NULL; uiserver->io_cbs.event_priv = NULL; err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME, &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, NULL); if (err) goto leave; assuan_ctx_set_system_hooks (uiserver->assuan_ctx, &_gpgme_assuan_system_hooks); err = assuan_socket_connect (uiserver->assuan_ctx, file_name ? file_name : _gpgme_get_default_uisrv_socket (), 0, ASSUAN_SOCKET_SERVER_FDPASSING); if (err) goto leave; err = _gpgme_getenv ("DISPLAY", &dft_display); if (err) goto leave; if (dft_display) { if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0) { err = gpg_error_from_syserror (); free (dft_display); goto leave; } free (dft_display); err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; } err = _gpgme_getenv ("GPG_TTY", &env_tty); if (isatty (1) || env_tty || err) { int rc = 0; if (err) goto leave; else if (env_tty) { snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty); free (env_tty); } else rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); /* Even though isatty() returns 1, ttyname_r() may fail in many ways, e.g., when /dev/pts is not accessible under chroot. */ if (!rc) { if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; err = _gpgme_getenv ("TERM", &dft_ttytype); if (err) goto leave; if (dft_ttytype) { if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0) { err = gpg_error_from_syserror (); free (dft_ttytype); goto leave; } free (dft_ttytype); err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); if (err) goto leave; } } } #ifdef HAVE_W32_SYSTEM /* Under Windows we need to use AllowSetForegroundWindow. Tell uiserver to tell us when it needs it. */ if (!err) { err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is a new feature of uiserver. */ } #endif /*HAVE_W32_SYSTEM*/ leave: if (err) uiserver_release (uiserver); else *engine = uiserver; return err; } static gpgme_error_t uiserver_set_locale (void *engine, int category, const char *value) { engine_uiserver_t uiserver = engine; gpgme_error_t err; char *optstr; const char *catstr; /* FIXME: If value is NULL, we need to reset the option to default. But we can't do this. So we error out here. UISERVER needs support for this. */ if (category == LC_CTYPE) { catstr = "lc-ctype"; if (!value && uiserver->lc_ctype_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) uiserver->lc_ctype_set = 1; } #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { catstr = "lc-messages"; if (!value && uiserver->lc_messages_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) uiserver->lc_messages_set = 1; } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: Reset value to default. */ if (!value) return 0; if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0) err = gpg_error_from_syserror (); else { err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); gpgrt_free (optstr); } return err; } static gpgme_error_t uiserver_set_protocol (void *engine, gpgme_protocol_t protocol) { engine_uiserver_t uiserver = engine; if (protocol != GPGME_PROTOCOL_OpenPGP && protocol != GPGME_PROTOCOL_CMS && protocol != GPGME_PROTOCOL_DEFAULT) return gpg_error (GPG_ERR_INV_VALUE); uiserver->protocol = protocol; return 0; } static gpgme_error_t uiserver_assuan_simple_command (engine_uiserver_t uiserver, const char *cmd, engine_status_handler_t status_fnc, void *status_fnc_value) { assuan_context_t ctx = uiserver->assuan_ctx; gpg_error_t err; char *line; size_t linelen; err = assuan_write_line (ctx, cmd); if (err) return err; do { err = assuan_read_line (ctx, &line, &linelen); if (err) return err; if (*line == '#' || !linelen) continue; if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) return 0; else if (linelen >= 4 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && line[3] == ' ') err = atoi (&line[4]); else if (linelen >= 2 && line[0] == 'S' && line[1] == ' ') { char *rest; gpgme_status_code_t r; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; r = _gpgme_parse_status (line + 2); if (uiserver->status.mon_cb && r != GPGME_STATUS_PROGRESS) { /* Note that we call the monitor even if we do * not know the status code (r < 0). */ err = uiserver->status.mon_cb (uiserver->status.mon_cb_value, line + 2, rest); } if (err) ; else if (r >= 0 && status_fnc) err = status_fnc (status_fnc_value, r, rest); else err = gpg_error (GPG_ERR_GENERAL); } else err = gpg_error (GPG_ERR_GENERAL); } while (!err); return err; } typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t; #define COMMANDLINELEN 40 static gpgme_error_t uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt) { gpg_error_t err = 0; char line[COMMANDLINELEN]; const char *which; iocb_data_t *iocb_data; int dir; switch (fd_type) { case INPUT_FD: which = "INPUT"; iocb_data = &uiserver->input_cb; break; case OUTPUT_FD: which = "OUTPUT"; iocb_data = &uiserver->output_cb; break; case MESSAGE_FD: which = "MESSAGE"; iocb_data = &uiserver->message_cb; break; default: return gpg_error (GPG_ERR_INV_VALUE); } dir = iocb_data->dir; /* We try to short-cut the communication by giving UISERVER direct access to the file descriptor, rather than using a pipe. */ iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data); if (iocb_data->server_fd < 0) { int fds[2]; if (_gpgme_io_pipe (fds, 0) < 0) return gpg_error_from_syserror (); iocb_data->fd = dir ? fds[0] : fds[1]; iocb_data->server_fd = dir ? fds[1] : fds[0]; if (_gpgme_io_set_close_notify (iocb_data->fd, close_notify_handler, uiserver)) { err = gpg_error (GPG_ERR_GENERAL); goto leave_set_fd; } } err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd); if (err) goto leave_set_fd; _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; if (opt) snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt); else snprintf (line, COMMANDLINELEN, "%s FD", which); err = uiserver_assuan_simple_command (uiserver, line, NULL, NULL); leave_set_fd: if (err) { _gpgme_io_close (iocb_data->fd); iocb_data->fd = -1; if (iocb_data->server_fd != -1) { _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; } } return err; } static const char * map_data_enc (gpgme_data_t d) { switch (gpgme_data_get_encoding (d)) { case GPGME_DATA_ENCODING_NONE: break; case GPGME_DATA_ENCODING_BINARY: return "--binary"; case GPGME_DATA_ENCODING_BASE64: return "--base64"; case GPGME_DATA_ENCODING_ARMOR: return "--armor"; default: break; } return NULL; } static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen); if (err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */ - TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver, + TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: error from assuan (%d) getting status line : %s", fd, err, gpg_strerror (err)); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (&line[4]); if (! err) err = gpg_error (GPG_ERR_GENERAL); - TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, + TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: ERR line - mapped to: %s", fd, err ? gpg_strerror (err) : "ok"); /* Try our best to terminate the connection friendly. */ /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */ } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { if (uiserver->status.fnc) { char emptystring[1] = {0}; err = uiserver->status.fnc (uiserver->status.fnc_value, GPGME_STATUS_EOF, emptystring); if (gpg_err_code (err) == GPG_ERR_FALSE) err = 0; /* Drop special error code. */ } if (!err && uiserver->colon.fnc && uiserver->colon.any) { /* We must tell a colon function about the EOF. We do this only when we have seen any data lines. Note that this inlined use of colon data lines will eventually be changed into using a regular data channel. */ uiserver->colon.any = 0; err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL); } - TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, + TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: OK line - final status: %s", fd, err ? gpg_strerror (err) : "ok"); _gpgme_io_close (uiserver->status_cb.fd); return err; } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && uiserver->colon.fnc) { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst; char **aline = &uiserver->colon.attic.line; int *alinelen = &uiserver->colon.attic.linelen; if (uiserver->colon.attic.linesize < *alinelen + linelen + 1) { char *newline = realloc (*aline, *alinelen + linelen + 1); if (!newline) err = gpg_error_from_syserror (); else { *aline = newline; uiserver->colon.attic.linesize = *alinelen + linelen + 1; } } if (!err) { dst = *aline + *alinelen; while (!err && src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst = _gpgme_hextobyte (src); (*alinelen)++; src += 2; } else { *dst = *src++; (*alinelen)++; } if (*dst == '\n') { /* Terminate the pending line, pass it to the colon handler and reset it. */ uiserver->colon.any = 1; if (*alinelen > 1 && *(dst - 1) == '\r') dst--; *dst = '\0'; /* FIXME How should we handle the return code? */ err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline); if (!err) { dst = *aline; *alinelen = 0; } } else dst++; } } - TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, + TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: D line; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && uiserver->inline_data) { char *src = line + 2; char *end = line + linelen; char *dst = src; gpgme_ssize_t nwritten; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; while (linelen > 0) { nwritten = gpgme_data_write (uiserver->inline_data, src, linelen); if (!nwritten || (nwritten < 0 && errno != EINTR) || nwritten > linelen) { err = gpg_error_from_syserror (); break; } src += nwritten; linelen -= nwritten; } - TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, + TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: D inlinedata; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { char *rest; gpgme_status_code_t r; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; r = _gpgme_parse_status (line + 2); if (r >= 0) { if (uiserver->status.fnc) { err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest); if (gpg_err_code (err) == GPG_ERR_FALSE) err = 0; /* Drop special error code. */ } } else fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest); - TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver, + TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: S line (%s) - final status: %s", fd, line+2, err? gpg_strerror (err):"ok"); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *keyword = line+7; while (*keyword == ' ') keyword++;; default_inq_cb (uiserver, keyword); assuan_write_line (uiserver->assuan_ctx, "END"); } } while (!err && assuan_pending_line (uiserver->assuan_ctx)); return err; } static gpgme_error_t add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver, + TRACE_BEG (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver, "fd %d, dir %d", iocbd->fd, iocbd->dir); err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv, iocbd->fd, iocbd->dir, handler, iocbd->data, &iocbd->tag); if (err) return TRACE_ERR (err); if (!iocbd->dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (iocbd->fd); return TRACE_ERR (err); } static gpgme_error_t start (engine_uiserver_t uiserver, const char *command) { gpgme_error_t err; int fdlist[5]; int nfds; /* We need to know the fd used by assuan for reads. We do this by using the assumption that the first returned fd from assuan_get_active_fds() is always this one. */ nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */, fdlist, DIM (fdlist)); if (nfds < 1) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ /* We "duplicate" the file descriptor, so we can close it here (we can't close fdlist[0], as that is closed by libassuan, and closing it here might cause libassuan to close some unrelated FD later). Alternatively, we could special case status_fd and register/unregister it manually as needed, but this increases code duplication and is more complicated as we can not use the close notifications etc. A third alternative would be to let Assuan know that we closed the FD, but that complicates the Assuan interface. */ uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]); if (uiserver->status_cb.fd < 0) return gpg_error_from_syserror (); if (_gpgme_io_set_close_notify (uiserver->status_cb.fd, close_notify_handler, uiserver)) { _gpgme_io_close (uiserver->status_cb.fd); uiserver->status_cb.fd = -1; return gpg_error (GPG_ERR_GENERAL); } err = add_io_cb (uiserver, &uiserver->status_cb, status_handler); if (!err && uiserver->input_cb.fd != -1) err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler); if (!err && uiserver->output_cb.fd != -1) err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler); if (!err && uiserver->message_cb.fd != -1) err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler); if (!err) err = assuan_write_line (uiserver->assuan_ctx, command); if (!err) uiserver_io_event (uiserver, GPGME_EVENT_START, NULL); return err; } static gpgme_error_t uiserver_reset (void *engine) { engine_uiserver_t uiserver = engine; /* We must send a reset because we need to reset the list of signers. Note that RESET does not reset OPTION commands. */ return uiserver_assuan_simple_command (uiserver, "RESET", NULL, NULL); } static gpgme_error_t uiserver_decrypt (void *engine, gpgme_decrypt_flags_t flags, gpgme_data_t ciph, gpgme_data_t plain, int export_session_key, const char *override_session_key, int auto_key_retrieve) { engine_uiserver_t uiserver = engine; gpgme_error_t err; const char *protocol; char *cmd; int verify = !!(flags & GPGME_DECRYPT_VERIFY); (void)override_session_key; /* Fixme: We need to see now to add this * to the UI server protocol */ (void)auto_key_retrieve; /* Not yet supported. */ if (!uiserver) return gpg_error (GPG_ERR_INV_VALUE); if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) protocol = ""; else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) protocol = " --protocol=OpenPGP"; else if (uiserver->protocol == GPGME_PROTOCOL_CMS) protocol = " --protocol=CMS"; else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (gpgrt_asprintf (&cmd, "DECRYPT%s%s%s", protocol, verify ? "" : " --no-verify", export_session_key ? " --export-session-key" : "") < 0) return gpg_error_from_syserror (); uiserver->input_cb.data = ciph; err = uiserver_set_fd (uiserver, INPUT_FD, map_data_enc (uiserver->input_cb.data)); if (err) { gpgrt_free (cmd); return gpg_error (GPG_ERR_GENERAL); /* FIXME */ } uiserver->output_cb.data = plain; err = uiserver_set_fd (uiserver, OUTPUT_FD, 0); if (err) { gpgrt_free (cmd); return gpg_error (GPG_ERR_GENERAL); /* FIXME */ } uiserver->inline_data = NULL; err = start (engine, cmd); gpgrt_free (cmd); return err; } static gpgme_error_t set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[]) { gpgme_error_t err = 0; char *line; int linelen; int invalid_recipients = 0; int i; linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */ line = malloc (10 + 40 + 1); if (!line) return gpg_error_from_syserror (); strcpy (line, "RECIPIENT "); for (i=0; !err && recp[i]; i++) { char *uid; int newlen; /* We use only the first user ID of the key. */ if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid) { invalid_recipients++; continue; } newlen = 11 + strlen (uid); if (linelen < newlen) { char *newline = realloc (line, newlen); if (! newline) { int saved_err = gpg_error_from_syserror (); free (line); return saved_err; } line = newline; linelen = newlen; } /* FIXME: need to do proper escaping */ strcpy (&line[10], uid); err = uiserver_assuan_simple_command (uiserver, line, uiserver->status.fnc, uiserver->status.fnc_value); /* FIXME: This might requires more work. */ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) invalid_recipients++; else if (err) { free (line); return err; } } free (line); return gpg_error (invalid_recipients ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR); } /* Take recipients from the LF delimited STRING and send RECIPIENT * commands to gpgsm. */ static gpgme_error_t set_recipients_from_string (engine_uiserver_t uiserver, const char *string) { gpg_error_t err = 0; char *line = NULL; int no_pubkey = 0; const char *s; int n; for (;;) { while (*string == ' ' || *string == '\t') string++; if (!*string) break; s = strchr (string, '\n'); if (s) n = s - string; else n = strlen (string); while (n && (string[n-1] == ' ' || string[n-1] == '\t')) n--; gpgrt_free (line); if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0) { err = gpg_error_from_syserror (); break; } string += n + !!s; err = uiserver_assuan_simple_command (uiserver, line, uiserver->status.fnc, uiserver->status.fnc_value); /* Fixme: Improve error reporting. */ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) no_pubkey++; else if (err) break; } gpgrt_free (line); return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0; } static gpgme_error_t uiserver_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_uiserver_t uiserver = engine; gpgme_error_t err; const char *protocol; char *cmd; if (!uiserver) return gpg_error (GPG_ERR_INV_VALUE); if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) protocol = ""; else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) protocol = " --protocol=OpenPGP"; else if (uiserver->protocol == GPGME_PROTOCOL_CMS) protocol = " --protocol=CMS"; else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (flags & GPGME_ENCRYPT_PREPARE) { if (!recp || plain || ciph) return gpg_error (GPG_ERR_INV_VALUE); if (gpgrt_asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol, (flags & GPGME_ENCRYPT_EXPECT_SIGN) ? " --expect-sign" : "") < 0) return gpg_error_from_syserror (); } else { if (!plain || !ciph) return gpg_error (GPG_ERR_INV_VALUE); if (gpgrt_asprintf (&cmd, "ENCRYPT%s", protocol) < 0) return gpg_error_from_syserror (); } if (plain) { uiserver->input_cb.data = plain; err = uiserver_set_fd (uiserver, INPUT_FD, map_data_enc (uiserver->input_cb.data)); if (err) { gpgrt_free (cmd); return err; } } if (ciph) { uiserver->output_cb.data = ciph; err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (uiserver->output_cb.data)); if (err) { gpgrt_free (cmd); return err; } } uiserver->inline_data = NULL; if (recp || recpstring) { if (recp) err = set_recipients (uiserver, recp); else err = set_recipients_from_string (uiserver, recpstring); if (err) { gpgrt_free (cmd); return err; } } err = start (uiserver, cmd); gpgrt_free (cmd); return err; } static gpgme_error_t uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_uiserver_t uiserver = engine; gpgme_error_t err = 0; const char *protocol; char *cmd; gpgme_key_t key; (void)use_textmode; (void)include_certs; if (!uiserver || !in || !out) return gpg_error (GPG_ERR_INV_VALUE); if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) protocol = ""; else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) protocol = " --protocol=OpenPGP"; else if (uiserver->protocol == GPGME_PROTOCOL_CMS) protocol = " --protocol=CMS"; else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (gpgrt_asprintf (&cmd, "SIGN%s%s", protocol, (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0) return gpg_error_from_syserror (); key = gpgme_signers_enum (ctx, 0); if (key) { const char *s = NULL; if (key && key->uids) s = key->uids->email; if (s && strlen (s) < 80) { char buf[100]; strcpy (stpcpy (buf, "SENDER --info "), s); err = uiserver_assuan_simple_command (uiserver, buf, uiserver->status.fnc, uiserver->status.fnc_value); } else err = gpg_error (GPG_ERR_INV_VALUE); gpgme_key_unref (key); if (err) { gpgrt_free (cmd); return err; } } uiserver->input_cb.data = in; err = uiserver_set_fd (uiserver, INPUT_FD, map_data_enc (uiserver->input_cb.data)); if (err) { gpgrt_free (cmd); return err; } uiserver->output_cb.data = out; err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (uiserver->output_cb.data)); if (err) { gpgrt_free (cmd); return err; } uiserver->inline_data = NULL; err = start (uiserver, cmd); gpgrt_free (cmd); return err; } /* FIXME: Missing a way to specify --silent. */ static gpgme_error_t uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext, gpgme_ctx_t ctx) { engine_uiserver_t uiserver = engine; gpgme_error_t err; const char *protocol; char *cmd; (void)ctx; /* FIXME: We should to add a --sender option to the * UISever protocol. */ if (!uiserver) return gpg_error (GPG_ERR_INV_VALUE); if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) protocol = ""; else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) protocol = " --protocol=OpenPGP"; else if (uiserver->protocol == GPGME_PROTOCOL_CMS) protocol = " --protocol=CMS"; else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (gpgrt_asprintf (&cmd, "VERIFY%s", protocol) < 0) return gpg_error_from_syserror (); uiserver->input_cb.data = sig; err = uiserver_set_fd (uiserver, INPUT_FD, map_data_enc (uiserver->input_cb.data)); if (err) { gpgrt_free (cmd); return err; } if (plaintext) { /* Normal or cleartext signature. */ uiserver->output_cb.data = plaintext; err = uiserver_set_fd (uiserver, OUTPUT_FD, 0); } else { /* Detached signature. */ uiserver->message_cb.data = signed_text; err = uiserver_set_fd (uiserver, MESSAGE_FD, 0); } uiserver->inline_data = NULL; if (!err) err = start (uiserver, cmd); gpgrt_free (cmd); return err; } /* This sets a status callback for monitoring status lines before they * are passed to a caller set handler. */ static void uiserver_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value) { engine_uiserver_t uiserver = engine; uiserver->status.mon_cb = cb; uiserver->status.mon_cb_value = cb_value; } static void uiserver_set_status_handler (void *engine, engine_status_handler_t fnc, void *fnc_value) { engine_uiserver_t uiserver = engine; uiserver->status.fnc = fnc; uiserver->status.fnc_value = fnc_value; } static gpgme_error_t uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc, void *fnc_value) { engine_uiserver_t uiserver = engine; uiserver->colon.fnc = fnc; uiserver->colon.fnc_value = fnc_value; uiserver->colon.any = 0; return 0; } static void uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_uiserver_t uiserver = engine; uiserver->io_cbs = *io_cbs; } static void uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_uiserver_t uiserver = engine; - TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver, + TRACE (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver, "event %p, type %d, type_data %p", uiserver->io_cbs.event, type, type_data); if (uiserver->io_cbs.event) (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data); } struct engine_ops _gpgme_engine_ops_uiserver = { /* Static functions. */ _gpgme_get_default_uisrv_socket, NULL, uiserver_get_version, uiserver_get_req_version, uiserver_new, /* Member functions. */ uiserver_release, uiserver_reset, uiserver_set_status_cb, uiserver_set_status_handler, NULL, /* set_command_handler */ uiserver_set_colon_line_handler, uiserver_set_locale, uiserver_set_protocol, NULL, /* set_engine_flags */ uiserver_decrypt, NULL, /* delete */ NULL, /* edit */ uiserver_encrypt, NULL, /* encrypt_sign */ NULL, /* export */ NULL, /* export_ext */ NULL, /* genkey */ NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ NULL, /* keylist_data */ NULL, /* keysign */ NULL, /* tofu_policy */ uiserver_sign, NULL, /* trustlist */ uiserver_verify, NULL, /* getauditlog */ NULL, /* opassuan_transact */ NULL, /* conf_load */ NULL, /* conf_save */ NULL, /* conf_dir */ NULL, /* query_swdb */ uiserver_set_io_cbs, uiserver_io_event, uiserver_cancel, NULL, /* cancel_op */ NULL, /* passwd */ NULL, /* set_pinentry_mode */ NULL /* opspawn */ }; diff --git a/src/export.c b/src/export.c index 0cc1099f..abf260ad 100644 --- a/src/export.c +++ b/src/export.c @@ -1,481 +1,481 @@ /* export.c - Export a key. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001-2004, 2010, 2014 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include "gpgme.h" #include "util.h" #include "debug.h" #include "context.h" #include "ops.h" /* Local operation data. */ typedef struct { gpg_error_t err; /* Error encountered during the export. */ } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; (void)opd; /* Nothing to release here. */ } /* Parse an error status line. Return the error location and the error code. The function may modify ARGS. */ static char * parse_error (char *args, gpg_error_t *r_err) { char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else { *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE); return NULL; } *r_err = atoi (which); return where; } static gpgme_error_t export_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; const char *loc; err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_ERROR: loc = parse_error (args, &err); if (!loc) return err; else if (opd->err) ; /* We only want to report the first error. */ else if (!strcmp (loc, "keyserver_send")) opd->err = err; break; default: break; } return 0; } static gpgme_error_t export_start (gpgme_ctx_t ctx, int synchronous, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; void *hook; op_data_t opd; if ((mode & ~(GPGME_EXPORT_MODE_EXTERN |GPGME_EXPORT_MODE_MINIMAL |GPGME_EXPORT_MODE_SECRET |GPGME_EXPORT_MODE_RAW |GPGME_EXPORT_MODE_NOUID |GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */ if ((mode & GPGME_EXPORT_MODE_SECRET)) { if ((mode & GPGME_EXPORT_MODE_EXTERN)) return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ if ((mode & GPGME_EXPORT_MODE_RAW) && (mode & GPGME_EXPORT_MODE_PKCS12)) return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ if (ctx->protocol != GPGME_PROTOCOL_CMS && (mode & (GPGME_EXPORT_MODE_RAW|GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_FLAG); /* Only supported for X.509. */ } if ((mode & GPGME_EXPORT_MODE_EXTERN)) { if (keydata) return gpg_error (GPG_ERR_INV_VALUE); } else { if (!keydata) return gpg_error (GPG_ERR_INV_VALUE); } err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx); return _gpgme_engine_op_export (ctx->engine, pattern, mode, keydata, ctx->use_armor); } /* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_export_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_export_start", ctx, "pattern=%s, mode=0x%x, keydata=%p", pattern, mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = export_start (ctx, 0, pattern, mode, keydata); return TRACE_ERR (err); } /* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export (gpgme_ctx_t ctx, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_export", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_export", ctx, "pattern=%s, mode=0x%x, keydata=%p", pattern, mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = export_start (ctx, 1, pattern, mode, keydata); if (!err) err = _gpgme_wait_one (ctx); return err; } static gpgme_error_t export_ext_start (gpgme_ctx_t ctx, int synchronous, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; void *hook; op_data_t opd; if ((mode & ~(GPGME_EXPORT_MODE_EXTERN |GPGME_EXPORT_MODE_MINIMAL |GPGME_EXPORT_MODE_SECRET |GPGME_EXPORT_MODE_RAW |GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */ if ((mode & GPGME_EXPORT_MODE_SECRET)) { if ((mode & GPGME_EXPORT_MODE_EXTERN)) return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ if ((mode & GPGME_EXPORT_MODE_RAW) && (mode & GPGME_EXPORT_MODE_PKCS12)) return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ if (ctx->protocol != GPGME_PROTOCOL_CMS && (mode & (GPGME_EXPORT_MODE_RAW|GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_FLAG); /* Only supported for X.509. */ } if ((mode & GPGME_EXPORT_MODE_EXTERN)) { if (keydata) return gpg_error (GPG_ERR_INV_VALUE); } else { if (!keydata) return gpg_error (GPG_ERR_INV_VALUE); } err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx); return _gpgme_engine_op_export_ext (ctx->engine, pattern, mode, keydata, ctx->use_armor); } /* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_ext_start (gpgme_ctx_t ctx, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_ext_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_export_ext_start", ctx, "mode=0x%x, keydata=%p", mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && pattern) { int i = 0; while (pattern[i]) { - TRACE_LOG2 ("pattern[%i] = %s", i, pattern[i]); + TRACE_LOG ("pattern[%i] = %s", i, pattern[i]); i++; } } err = export_ext_start (ctx, 0, pattern, mode, keydata); return TRACE_ERR (err); } /* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_ext (gpgme_ctx_t ctx, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_ext_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_export_ext_start", ctx, "mode=0x%x, keydata=%p", mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && pattern) { int i = 0; while (pattern[i]) { - TRACE_LOG2 ("pattern[%i] = %s", i, pattern[i]); + TRACE_LOG ("pattern[%i] = %s", i, pattern[i]); i++; } } err = export_ext_start (ctx, 1, pattern, mode, keydata); if (!err) { err = _gpgme_wait_one (ctx); if (!err) { /* For this synchronous operation we check for operational errors and return them. For asynchronous operations there is currently no way to do this - we need to add a gpgme_op_export_result function to fix that. */ void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL); opd = hook; if (!err) err = opd->err; } } return TRACE_ERR (err); } static gpgme_error_t export_keys_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t keys[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; int nkeys, idx; char **pattern; if (!keys) return gpg_error (GPG_ERR_INV_VALUE); /* Create a list of pattern from the keys. */ for (idx=nkeys=0; keys[idx]; idx++) if (keys[idx]->protocol == ctx->protocol) nkeys++; if (!nkeys) return gpg_error (GPG_ERR_NO_DATA); pattern = calloc (nkeys+1, sizeof *pattern); if (!pattern) return gpg_error_from_syserror (); for (idx=nkeys=0; keys[idx]; idx++) if (keys[idx]->protocol == ctx->protocol && keys[idx]->subkeys && keys[idx]->subkeys->fpr && *keys[idx]->subkeys->fpr) { pattern[nkeys] = strdup (keys[idx]->subkeys->fpr); if (!pattern[nkeys]) { err = gpg_error_from_syserror (); goto leave; } nkeys++; } /* Pass on to the regular function. */ err = export_ext_start (ctx, synchronous, (const char**)pattern, mode, keydata); leave: for (idx=0; pattern[idx]; idx++) free (pattern[idx]); free (pattern); return err; } /* Export the keys from the array KEYS into KEYDATA. Only keys of the current protocol are exported and only those which have a fingerprint set; that is keys received with some external search methods are silently skipped. */ gpgme_error_t gpgme_op_export_keys_start (gpgme_ctx_t ctx, gpgme_key_t keys[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpg_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_keys_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_export_keys_start", ctx, "mode=0x%x, keydata=%p", mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && keys) { int i = 0; while (keys[i]) { - TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i], + TRACE_LOG ("keys[%i] = %p (%s)", i, keys[i], (keys[i]->subkeys && keys[i]->subkeys->fpr) ? keys[i]->subkeys->fpr : "invalid"); i++; } } err = export_keys_start (ctx, 0, keys, mode, keydata); return TRACE_ERR (err); } gpgme_error_t gpgme_op_export_keys (gpgme_ctx_t ctx, gpgme_key_t keys[], gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_keys", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_export_keys", ctx, "mode=0x%x, keydata=%p", mode, keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && keys) { int i = 0; while (keys[i]) { - TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i], + TRACE_LOG ("keys[%i] = %p (%s)", i, keys[i], (keys[i]->subkeys && keys[i]->subkeys->fpr) ? keys[i]->subkeys->fpr : "invalid"); i++; } } err = export_keys_start (ctx, 1, keys, mode, keydata); if (!err) { err = _gpgme_wait_one (ctx); if (!err) { /* For this synchronous operation we check for operational errors and return them. For asynchronous operations there is currently no way to do this - we need to add a gpgme_op_export_result function to fix that. */ void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL); opd = hook; if (!err) err = opd->err; } } return TRACE_ERR (err); } diff --git a/src/genkey.c b/src/genkey.c index f76b7d88..b51dbf04 100644 --- a/src/genkey.c +++ b/src/genkey.c @@ -1,665 +1,665 @@ /* genkey.c - Key generation. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2016 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { struct _gpgme_op_genkey_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The error code from certain ERROR status lines or 0. */ gpg_error_t error_code; /* Flag to indicate that a UID is to be added. */ gpg_error_t uidmode; /* The key parameters passed to the crypto engine. */ gpgme_data_t key_parameter; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; if (opd->result.fpr) free (opd->result.fpr); if (opd->key_parameter) gpgme_data_release (opd->key_parameter); } gpgme_genkey_result_t gpgme_op_genkey_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; - TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_result", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL); opd = hook; if (err || !opd) { - TRACE_SUC0 ("result=(null)"); + TRACE_SUC ("result=(null)"); return NULL; } - TRACE_LOG3 ("fpr = %s, %s, %s", opd->result.fpr, + TRACE_LOG ("fpr = %s, %s, %s", opd->result.fpr, opd->result.primary ? "primary" : "no primary", opd->result.sub ? "sub" : "no sub"); - TRACE_SUC1 ("result=%p", &opd->result); + TRACE_SUC ("result=%p", &opd->result); return &opd->result; } /* Parse an error status line. Return the error location and the error code. The function may modify ARGS. */ static char * parse_error (char *args, gpg_error_t *r_err) { char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else { *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE); return NULL; } *r_err = atoi (which); return where; } static gpgme_error_t genkey_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; char *loc; /* Pipe the status code through the progress status handler. */ err = _gpgme_progress_status_handler (ctx, code, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_KEY_CREATED: if (args && *args) { if (*args == 'B' || *args == 'P') { opd->result.primary = 1; opd->result.uid = 1; } if (*args == 'B' || *args == 'S') opd->result.sub = 1; if (args[1] == ' ') { if (opd->result.fpr) free (opd->result.fpr); opd->result.fpr = strdup (&args[2]); if (!opd->result.fpr) return gpg_error_from_syserror (); } } break; case GPGME_STATUS_ERROR: loc = parse_error (args, &err); if (!loc) return err; if (!opd->error_code) opd->error_code = err; break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: if (opd->error_code) return opd->error_code; else if (!opd->uidmode && !opd->result.primary && !opd->result.sub) return gpg_error (GPG_ERR_GENERAL); else if (opd->failure_code) return opd->failure_code; else if (opd->uidmode == 1) opd->result.uid = 1; /* We have no status line, thus this hack. */ break; case GPGME_STATUS_INQUIRE_MAXLEN: if (ctx->status_cb && !ctx->full_status) { err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args); if (err) return err; } break; default: break; } return 0; } static gpgme_error_t get_key_parameter (const char *parms, gpgme_data_t *key_parameter) { const char *content; const char *attrib; const char *endtag; /* Extract the key parameter from the XML structure. */ parms = strstr (parms, "'); if (!content) return gpg_error (GPG_ERR_INV_VALUE); content++; attrib = strstr (parms, "format=\"internal\""); if (!attrib || attrib >= content) return gpg_error (GPG_ERR_INV_VALUE); endtag = strstr (content, ""); if (!endtag) endtag = content + strlen (content); /* FIXME: Check that there are no control statements inside. */ while (content < endtag && (content[0] == '\n' || (content[0] == '\r' && content[1] == '\n'))) content++; return gpgme_data_new_from_mem (key_parameter, content, endtag - content, 1); } static gpgme_error_t genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; err = get_key_parameter (parms, &opd->key_parameter); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_genkey (ctx->engine, NULL, NULL, 0, 0, NULL, 0, opd->key_parameter, ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0, pubkey, seckey); } /* Generate a new keypair and add it to the keyring. PUBKEY and SECKEY should be null for now. PARMS specifies what keys should be generated. */ gpgme_error_t gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_start", ctx, "pubkey=%p, seckey=%p", pubkey, seckey); TRACE_LOGBUF (parms, parms? strlen (parms):0); if (!ctx || parms) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = genkey_start (ctx, 0, parms, pubkey, seckey); return TRACE_ERR (err); } /* Generate a new keypair and add it to the keyring. PUBKEY and SECKEY should be null for now. PARMS specifies what keys should be generated. */ gpgme_error_t gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey", ctx, "pubkey=%p, seckey=%p", pubkey, seckey); TRACE_LOGBUF (parms, parms? strlen (parms):0); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = genkey_start (ctx, 1, parms, pubkey, seckey); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } static gpgme_error_t createkey_start (gpgme_ctx_t ctx, int synchronous, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t anchorkey, unsigned int flags) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (reserved || anchorkey || !userid) return gpg_error (GPG_ERR_INV_ARG); err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_genkey (ctx->engine, userid, algo, reserved, expires, anchorkey, flags, NULL, ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0, NULL, NULL); } gpgme_error_t gpgme_op_createkey_start (gpgme_ctx_t ctx, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t anchorkey, unsigned int flags) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_createkey_start", ctx, "userid='%s', algo='%s' flags=0x%x", userid, algo, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = createkey_start (ctx, 0, userid, algo, reserved, expires, anchorkey, flags); return TRACE_ERR (err); } gpgme_error_t gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t anchorkey, unsigned int flags) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_createkey", ctx, "userid='%s', algo='%s' flags=0x%x", userid, algo, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = createkey_start (ctx, 1, userid, algo, reserved, expires, anchorkey, flags); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } static gpgme_error_t createsubkey_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, const char *algo, unsigned long reserved, unsigned long expires, unsigned int flags) { gpgme_error_t err; void *hook; op_data_t opd; if (ctx->protocol != GPGME_PROTOCOL_OPENPGP) return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (reserved || !key) return gpg_error (GPG_ERR_INV_ARG); err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_genkey (ctx->engine, NULL, algo, reserved, expires, key, flags, NULL, ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0, NULL, NULL); } /* Add a subkey to an existing KEY. */ gpgme_error_t gpgme_op_createsubkey_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo, unsigned long reserved, unsigned long expires, unsigned int flags) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createsubkey_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_createsubkey_start", ctx, "key=%p, algo='%s' flags=0x%x", key, algo, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = createsubkey_start (ctx, 0, key, algo, reserved, expires, flags); return TRACE_ERR (err); } gpgme_error_t gpgme_op_createsubkey (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo, unsigned long reserved, unsigned long expires, unsigned int flags) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createsubkey", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_createsubkey", ctx, "key=%p, algo='%s' flags=0x%x", key, algo, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = createsubkey_start (ctx, 1, key, algo, reserved, expires, flags); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } static gpgme_error_t addrevuid_start (gpgme_ctx_t ctx, int synchronous, int extraflags, gpgme_key_t key, const char *userid, unsigned int flags) { gpgme_error_t err; void *hook; op_data_t opd; if (ctx->protocol != GPGME_PROTOCOL_OPENPGP) return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (!key || !userid) return gpg_error (GPG_ERR_INV_ARG); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->uidmode = extraflags? 2 : 1; _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_genkey (ctx->engine, userid, NULL, 0, 0, key, flags, NULL, extraflags, NULL, NULL); } /* Add USERID to an existing KEY. */ gpgme_error_t gpgme_op_adduid_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned int flags) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_adduid_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_adduid_start", ctx, "uid='%s' flags=0x%x", userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = addrevuid_start (ctx, 0, 0, key, userid, flags); return TRACE_ERR (err); } gpgme_error_t gpgme_op_adduid (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned int flags) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_adduid", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_adduid", ctx, "uid='%s' flags=0x%x", userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = addrevuid_start (ctx, 1, 0, key, userid, flags); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } /* Revoke USERID from KEY. */ gpgme_error_t gpgme_op_revuid_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned int flags) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_revuid_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_revuid_start", ctx, "uid='%s' flags=0x%x", userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = addrevuid_start (ctx, 0, GENKEY_EXTRAFLAG_REVOKE, key, userid, flags); return TRACE_ERR (err); } gpgme_error_t gpgme_op_revuid (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned int flags) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_revuid", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_revuid", ctx, "uid='%s' flags=0x%x", userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = addrevuid_start (ctx, 1, GENKEY_EXTRAFLAG_REVOKE, key, userid, flags); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } /* Set a flag on the USERID of KEY. The only supported flag right now * is "primary" to mark the primary key. */ static gpg_error_t set_uid_flag (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, const char *userid, const char *name, const char *value) { gpgme_error_t err; - TRACE_BEG4 (DEBUG_CTX, "gpgme_op_set_uid_flag", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_set_uid_flag", ctx, "%d uid='%s' '%s'='%s'", synchronous, userid, name, value); if (!ctx || !name || !key || !userid) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); if (!strcmp (name, "primary")) { if (value) err = gpg_error (GPG_ERR_INV_ARG); else err = addrevuid_start (ctx, synchronous, GENKEY_EXTRAFLAG_SETPRIMARY, key, userid, 0); } else return err = gpg_error (GPG_ERR_UNKNOWN_NAME); if (synchronous && !err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } /* See set_uid_flag. */ gpgme_error_t gpgme_op_set_uid_flag_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, const char *name, const char *value) { return set_uid_flag (ctx, 0, key, userid, name, value); } /* See set_uid_flag. This is the synchronous variant. */ gpgme_error_t gpgme_op_set_uid_flag (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, const char *name, const char *value) { return set_uid_flag (ctx, 1, key, userid, name, value); } diff --git a/src/getauditlog.c b/src/getauditlog.c index 2d3f7f99..13473873 100644 --- a/src/getauditlog.c +++ b/src/getauditlog.c @@ -1,105 +1,105 @@ /* getauditlog.c - Retrieve the audit log. * Copyright (C) 2007 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" static gpgme_error_t getauditlog_status_handler (void *priv, gpgme_status_code_t code, char *args) { (void)priv; (void)code; (void)args; return 0; } static gpgme_error_t getauditlog_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t output, unsigned int flags) { gpgme_error_t err; if (!output) return gpg_error (GPG_ERR_INV_VALUE); if (!(flags & GPGME_AUDITLOG_DIAG)) { err = _gpgme_op_reset (ctx, ((synchronous&255) | 256) ); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, getauditlog_status_handler, ctx); return _gpgme_engine_op_getauditlog (ctx->engine, output, flags); } /* Return the auditlog for the current session. This may be called after a successful or failed operation. If no audit log is available GPG_ERR_NO_DATA is returned. This is the asynchronous variant. */ gpgme_error_t gpgme_op_getauditlog_start (gpgme_ctx_t ctx, gpgme_data_t output, unsigned int flags) { gpg_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_getauditlog_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_getauditlog_start", ctx, "output=%p, flags=0x%x", output, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = getauditlog_start (ctx, 0, output, flags); return TRACE_ERR (err); } /* Return the auditlog for the current session. This may be called after a successful or failed operation. If no audit log is available GPG_ERR_NO_DATA is returned. This is the synchronous variant. */ gpgme_error_t gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output, unsigned int flags) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_getauditlog", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_getauditlog", ctx, "output=%p, flags=0x%x", output, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = getauditlog_start (ctx, 1, output, flags); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } diff --git a/src/gpgme.c b/src/gpgme.c index 4dbec0c7..ac98bd59 100644 --- a/src/gpgme.c +++ b/src/gpgme.c @@ -1,1294 +1,1294 @@ /* gpgme.c - GnuPG Made Easy. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2012, * 2014, 2015 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include "util.h" #include "context.h" #include "ops.h" #include "wait.h" #include "debug.h" #include "priv-io.h" #include "sys-util.h" #include "mbox-util.h" /* The default locale. */ DEFINE_STATIC_LOCK (def_lc_lock); static char *def_lc_ctype; static char *def_lc_messages; gpgme_error_t _gpgme_selftest = GPG_ERR_NOT_OPERATIONAL; /* Protects all reference counters in result structures. All other accesses to a result structure are read only. */ DEFINE_STATIC_LOCK (result_ref_lock); /* Set the global flag NAME to VALUE. Return 0 on success. Note that this function does not use gpgme_error and thus a non-zero return value merely means "error". Certain flags may be set before gpgme_check_version is called. See the manual for a description of supported flags. The caller must assure that this function is called only by one thread at a time. */ int gpgme_set_global_flag (const char *name, const char *value) { if (!name || !value) return -1; else if (!strcmp (name, "debug")) return _gpgme_debug_set_debug_envvar (value); else if (!strcmp (name, "disable-gpgconf")) { _gpgme_dirinfo_disable_gpgconf (); return 0; } else if (!strcmp (name, "require-gnupg")) return _gpgme_set_engine_minimal_version (value); else if (!strcmp (name, "gpgconf-name")) return _gpgme_set_default_gpgconf_name (value); else if (!strcmp (name, "gpg-name")) return _gpgme_set_default_gpg_name (value); else if (!strcmp (name, "w32-inst-dir")) return _gpgme_set_override_inst_dir (value); else return -1; } /* Create a new context as an environment for GPGME crypto operations. */ gpgme_error_t gpgme_new (gpgme_ctx_t *r_ctx) { gpgme_error_t err; gpgme_ctx_t ctx; - TRACE_BEG (DEBUG_CTX, "gpgme_new", r_ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_new", r_ctx, ""); if (_gpgme_selftest) return TRACE_ERR (_gpgme_selftest); if (!r_ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); ctx = calloc (1, sizeof *ctx); if (!ctx) return TRACE_ERR (gpg_error_from_syserror ()); INIT_LOCK (ctx->lock); err = _gpgme_engine_info_copy (&ctx->engine_info); if (!err && !ctx->engine_info) err = gpg_error (GPG_ERR_NO_ENGINE); if (err) { free (ctx); return TRACE_ERR (err); } ctx->keylist_mode = GPGME_KEYLIST_MODE_LOCAL; ctx->include_certs = GPGME_INCLUDE_CERTS_DEFAULT; ctx->protocol = GPGME_PROTOCOL_OpenPGP; ctx->sub_protocol = GPGME_PROTOCOL_DEFAULT; _gpgme_fd_table_init (&ctx->fdt); LOCK (def_lc_lock); if (def_lc_ctype) { ctx->lc_ctype = strdup (def_lc_ctype); if (!ctx->lc_ctype) { int saved_err = gpg_error_from_syserror (); UNLOCK (def_lc_lock); _gpgme_engine_info_release (ctx->engine_info); free (ctx); return TRACE_ERR (saved_err); } } else def_lc_ctype = NULL; if (def_lc_messages) { ctx->lc_messages = strdup (def_lc_messages); if (!ctx->lc_messages) { int saved_err = gpg_error_from_syserror (); UNLOCK (def_lc_lock); if (ctx->lc_ctype) free (ctx->lc_ctype); _gpgme_engine_info_release (ctx->engine_info); free (ctx); return TRACE_ERR (saved_err); } } else def_lc_messages = NULL; UNLOCK (def_lc_lock); *r_ctx = ctx; - return TRACE_SUC1 ("ctx=%p", ctx); + return TRACE_SUC ("ctx=%p", ctx); } gpgme_error_t _gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err, gpg_error_t op_err) { gpgme_error_t err; struct gpgme_io_event_done_data data; - TRACE_BEG2 (DEBUG_CTX, "_gpgme_cancel_with_err", ctx, "ctx_err=%i, op_err=%i", + TRACE_BEG (DEBUG_CTX, "_gpgme_cancel_with_err", ctx, "ctx_err=%i, op_err=%i", ctx_err, op_err); if (ctx_err) { err = _gpgme_engine_cancel (ctx->engine); if (err) return TRACE_ERR (err); } else { err = _gpgme_engine_cancel_op (ctx->engine); if (err) return TRACE_ERR (err); } data.err = ctx_err; data.op_err = op_err; _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data); return TRACE_ERR (0); } /* Cancel a pending asynchronous operation. */ gpgme_error_t gpgme_cancel (gpgme_ctx_t ctx) { gpg_error_t err; - TRACE_BEG (DEBUG_CTX, "gpgme_cancel", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_cancel", ctx, ""); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_cancel_with_err (ctx, gpg_error (GPG_ERR_CANCELED), 0); return TRACE_ERR (err); } /* Cancel a pending operation asynchronously. */ gpgme_error_t gpgme_cancel_async (gpgme_ctx_t ctx) { - TRACE_BEG (DEBUG_CTX, "gpgme_cancel_async", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_cancel_async", ctx, ""); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); LOCK (ctx->lock); ctx->canceled = 1; UNLOCK (ctx->lock); return TRACE_ERR (0); } /* Release all resources associated with the given context. */ void gpgme_release (gpgme_ctx_t ctx) { - TRACE (DEBUG_CTX, "gpgme_release", ctx); + TRACE (DEBUG_CTX, "gpgme_release", ctx, ""); if (!ctx) return; _gpgme_engine_release (ctx->engine); ctx->engine = NULL; _gpgme_fd_table_deinit (&ctx->fdt); _gpgme_release_result (ctx); _gpgme_signers_clear (ctx); _gpgme_sig_notation_clear (ctx); free (ctx->sender); free (ctx->signers); free (ctx->lc_ctype); free (ctx->lc_messages); free (ctx->override_session_key); free (ctx->request_origin); free (ctx->auto_key_locate); free (ctx->trust_model); _gpgme_engine_info_release (ctx->engine_info); ctx->engine_info = NULL; DESTROY_LOCK (ctx->lock); free (ctx); } void gpgme_result_ref (void *result) { struct ctx_op_data *data; if (! result) return; data = (void*)((char*)result - sizeof (struct ctx_op_data)); assert (data->magic == CTX_OP_DATA_MAGIC); LOCK (result_ref_lock); data->references++; UNLOCK (result_ref_lock); } void gpgme_result_unref (void *result) { struct ctx_op_data *data; if (! result) return; data = (void*)((char*)result - sizeof (struct ctx_op_data)); assert (data->magic == CTX_OP_DATA_MAGIC); LOCK (result_ref_lock); if (--data->references) { UNLOCK (result_ref_lock); return; } UNLOCK (result_ref_lock); if (data->cleanup) (*data->cleanup) (data->hook); free (data); } void _gpgme_release_result (gpgme_ctx_t ctx) { struct ctx_op_data *data = ctx->op_data; while (data) { struct ctx_op_data *next_data = data->next; data->next = NULL; gpgme_result_unref (data->hook); data = next_data; } ctx->op_data = NULL; } gpgme_error_t gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol) { - TRACE_BEG2 (DEBUG_CTX, "gpgme_set_protocol", ctx, "protocol=%i (%s)", + TRACE_BEG (DEBUG_CTX, "gpgme_set_protocol", ctx, "protocol=%i (%s)", protocol, gpgme_get_protocol_name (protocol) ? gpgme_get_protocol_name (protocol) : "invalid"); if (protocol != GPGME_PROTOCOL_OpenPGP && protocol != GPGME_PROTOCOL_CMS && protocol != GPGME_PROTOCOL_GPGCONF && protocol != GPGME_PROTOCOL_ASSUAN && protocol != GPGME_PROTOCOL_G13 && protocol != GPGME_PROTOCOL_UISERVER && protocol != GPGME_PROTOCOL_SPAWN) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (ctx->protocol != protocol) { /* Shut down the engine when switching protocols. */ if (ctx->engine) { - TRACE_LOG1 ("releasing ctx->engine=%p", ctx->engine); + TRACE_LOG ("releasing ctx->engine=%p", ctx->engine); _gpgme_engine_release (ctx->engine); ctx->engine = NULL; } ctx->protocol = protocol; } return TRACE_ERR (0); } gpgme_protocol_t gpgme_get_protocol (gpgme_ctx_t ctx) { - TRACE2 (DEBUG_CTX, "gpgme_get_protocol", ctx, + TRACE (DEBUG_CTX, "gpgme_get_protocol", ctx, "ctx->protocol=%i (%s)", ctx->protocol, gpgme_get_protocol_name (ctx->protocol) ? gpgme_get_protocol_name (ctx->protocol) : "invalid"); return ctx->protocol; } gpgme_error_t gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol) { - TRACE2 (DEBUG_CTX, "gpgme_set_sub_protocol", ctx, "protocol=%i (%s)", + TRACE (DEBUG_CTX, "gpgme_set_sub_protocol", ctx, "protocol=%i (%s)", protocol, gpgme_get_protocol_name (protocol) ? gpgme_get_protocol_name (protocol) : "invalid"); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); ctx->sub_protocol = protocol; return 0; } gpgme_protocol_t gpgme_get_sub_protocol (gpgme_ctx_t ctx) { - TRACE2 (DEBUG_CTX, "gpgme_get_sub_protocol", ctx, + TRACE (DEBUG_CTX, "gpgme_get_sub_protocol", ctx, "ctx->sub_protocol=%i (%s)", ctx->sub_protocol, gpgme_get_protocol_name (ctx->sub_protocol) ? gpgme_get_protocol_name (ctx->sub_protocol) : "invalid"); return ctx->sub_protocol; } const char * gpgme_get_protocol_name (gpgme_protocol_t protocol) { switch (protocol) { case GPGME_PROTOCOL_OpenPGP: return "OpenPGP"; case GPGME_PROTOCOL_CMS: return "CMS"; case GPGME_PROTOCOL_GPGCONF: return "GPGCONF"; case GPGME_PROTOCOL_ASSUAN: return "Assuan"; case GPGME_PROTOCOL_G13: return "G13"; case GPGME_PROTOCOL_UISERVER: return "UIServer"; case GPGME_PROTOCOL_SPAWN: return "Spawn"; case GPGME_PROTOCOL_DEFAULT: return "default"; case GPGME_PROTOCOL_UNKNOWN: return "unknown"; default: return NULL; } } /* Store the sender's address in the context. ADDRESS is addr-spec of * mailbox but my also be a complete mailbox, in which case this * function extracts the addr-spec from it. Returns 0 on success or * an error code if no valid addr-spec could be extracted from * ADDRESS. */ gpgme_error_t gpgme_set_sender (gpgme_ctx_t ctx, const char *address) { char *p = NULL; - TRACE_BEG1 (DEBUG_CTX, "gpgme_set_sender", ctx, "sender='%s'", + TRACE_BEG (DEBUG_CTX, "gpgme_set_sender", ctx, "sender='%s'", address?address:"(null)"); if (!ctx || (address && !(p = _gpgme_mailbox_from_userid (address)))) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); free (ctx->sender); ctx->sender = p; return TRACE_ERR (0); } /* Return the sender's address (addr-spec part) from the context or * NULL if none was set. The returned value is valid as long as the * CTX is valid and gpgme_set_sender has not been used. */ const char * gpgme_get_sender (gpgme_ctx_t ctx) { - TRACE1 (DEBUG_CTX, "gpgme_get_sender", ctx, "sender='%s'", + TRACE (DEBUG_CTX, "gpgme_get_sender", ctx, "sender='%s'", ctx?ctx->sender:""); return ctx->sender; } /* Enable or disable the use of an ascii armor for all output. */ void gpgme_set_armor (gpgme_ctx_t ctx, int use_armor) { - TRACE2 (DEBUG_CTX, "gpgme_set_armor", ctx, "use_armor=%i (%s)", + TRACE (DEBUG_CTX, "gpgme_set_armor", ctx, "use_armor=%i (%s)", use_armor, use_armor ? "yes" : "no"); if (!ctx) return; ctx->use_armor = !!use_armor; } /* Return the state of the armor flag. */ int gpgme_get_armor (gpgme_ctx_t ctx) { - TRACE2 (DEBUG_CTX, "gpgme_get_armor", ctx, "ctx->use_armor=%i (%s)", + TRACE (DEBUG_CTX, "gpgme_get_armor", ctx, "ctx->use_armor=%i (%s)", ctx->use_armor, ctx->use_armor ? "yes" : "no"); return ctx->use_armor; } /* Set the flag NAME for CTX to VALUE. Please consult the manual for * a description of the flags. */ gpgme_error_t gpgme_set_ctx_flag (gpgme_ctx_t ctx, const char *name, const char *value) { gpgme_error_t err = 0; int abool; - TRACE2 (DEBUG_CTX, "gpgme_set_ctx_flag", ctx, + TRACE (DEBUG_CTX, "gpgme_set_ctx_flag", ctx, "name='%s' value='%s'", name? name:"(null)", value?value:"(null)"); abool = (value && *value)? !!atoi (value) : 0; if (!ctx || !name || !value) err = gpg_error (GPG_ERR_INV_VALUE); else if (!strcmp (name, "redraw")) { ctx->redraw_suggested = abool; } else if (!strcmp (name, "full-status")) { ctx->full_status = abool; } else if (!strcmp (name, "raw-description")) { ctx->raw_description = abool; } else if (!strcmp (name, "export-session-key")) { ctx->export_session_keys = abool; } else if (!strcmp (name, "override-session-key")) { free (ctx->override_session_key); ctx->override_session_key = strdup (value); if (!ctx->override_session_key) err = gpg_error_from_syserror (); } else if (!strcmp (name, "auto-key-retrieve")) { ctx->auto_key_retrieve = abool; } else if (!strcmp (name, "request-origin")) { free (ctx->request_origin); ctx->request_origin = strdup (value); if (!ctx->request_origin) err = gpg_error_from_syserror (); } else if (!strcmp (name, "no-symkey-cache")) { ctx->no_symkey_cache = abool; } else if (!strcmp (name, "ignore-mdc-error")) { ctx->ignore_mdc_error = abool; } else if (!strcmp (name, "auto-key-locate")) { free (ctx->auto_key_locate); ctx->auto_key_locate = strdup (value); if (!ctx->auto_key_locate) err = gpg_error_from_syserror (); } else if (!strcmp (name, "trust-model")) { free (ctx->trust_model); ctx->trust_model = strdup (value); if (!ctx->trust_model) err = gpg_error_from_syserror (); } else err = gpg_error (GPG_ERR_UNKNOWN_NAME); return err; } /* Get the context flag named NAME. See gpgme_set_ctx_flag for a list * of valid names. If the NAME is unknown NULL is returned. For a * boolean flag an empty string is returned for False and the string * "1" for True; thus either atoi or a simple string test can be * used. */ const char * gpgme_get_ctx_flag (gpgme_ctx_t ctx, const char *name) { if (!ctx || !name) return NULL; else if (!strcmp (name, "redraw")) { return ctx->redraw_suggested? "1":""; } else if (!strcmp (name, "full-status")) { return ctx->full_status? "1":""; } else if (!strcmp (name, "raw-description")) { return ctx->raw_description? "1":""; } else if (!strcmp (name, "export-session-key")) { return ctx->export_session_keys? "1":""; } else if (!strcmp (name, "override-session-key")) { return ctx->override_session_key? ctx->override_session_key : ""; } else if (!strcmp (name, "auto-key-retrieve")) { return ctx->auto_key_retrieve? "1":""; } else if (!strcmp (name, "request-origin")) { return ctx->request_origin? ctx->request_origin : ""; } else if (!strcmp (name, "no-symkey-cache")) { return ctx->no_symkey_cache? "1":""; } else if (!strcmp (name, "ignore-mdc-error")) { return ctx->ignore_mdc_error? "1":""; } else if (!strcmp (name, "auto-key-locate")) { return ctx->auto_key_locate? ctx->auto_key_locate : ""; } else return NULL; } /* Enable or disable the use of the special textmode. Textmode is for example used for the RFC2015 signatures; note that the updated RFC 3156 mandates that the MUA does some preparations so that textmode is not needed anymore. */ void gpgme_set_textmode (gpgme_ctx_t ctx, int use_textmode) { - TRACE2 (DEBUG_CTX, "gpgme_set_textmode", ctx, "use_textmode=%i (%s)", + TRACE (DEBUG_CTX, "gpgme_set_textmode", ctx, "use_textmode=%i (%s)", use_textmode, use_textmode ? "yes" : "no"); if (!ctx) return; ctx->use_textmode = !!use_textmode; } /* Return the state of the textmode flag. */ int gpgme_get_textmode (gpgme_ctx_t ctx) { - TRACE2 (DEBUG_CTX, "gpgme_get_textmode", ctx, "ctx->use_textmode=%i (%s)", + TRACE (DEBUG_CTX, "gpgme_get_textmode", ctx, "ctx->use_textmode=%i (%s)", ctx->use_textmode, ctx->use_textmode ? "yes" : "no"); return ctx->use_textmode; } /* Enable offline mode for this context. In offline mode dirmngr will be disabled. */ void gpgme_set_offline (gpgme_ctx_t ctx, int offline) { - TRACE2 (DEBUG_CTX, "gpgme_set_offline", ctx, "offline=%i (%s)", + TRACE (DEBUG_CTX, "gpgme_set_offline", ctx, "offline=%i (%s)", offline, offline ? "yes" : "no"); if (!ctx) return; ctx->offline = !!offline; } /* Return the state of the offline flag. */ int gpgme_get_offline (gpgme_ctx_t ctx) { - TRACE2 (DEBUG_CTX, "gpgme_get_offline", ctx, "ctx->offline=%i (%s)", + TRACE (DEBUG_CTX, "gpgme_get_offline", ctx, "ctx->offline=%i (%s)", ctx->offline, ctx->offline ? "yes" : "no"); return ctx->offline; } /* Set the number of certifications to include in an S/MIME message. The default is GPGME_INCLUDE_CERTS_DEFAULT. -1 means all certs, and -2 means all certs except the root cert. */ void gpgme_set_include_certs (gpgme_ctx_t ctx, int nr_of_certs) { if (!ctx) return; if (nr_of_certs == GPGME_INCLUDE_CERTS_DEFAULT) ctx->include_certs = GPGME_INCLUDE_CERTS_DEFAULT; else if (nr_of_certs < -2) ctx->include_certs = -2; else ctx->include_certs = nr_of_certs; - TRACE2 (DEBUG_CTX, "gpgme_set_include_certs", ctx, "nr_of_certs=%i%s", + TRACE (DEBUG_CTX, "gpgme_set_include_certs", ctx, "nr_of_certs=%i%s", nr_of_certs, nr_of_certs == ctx->include_certs ? "" : " (-2)"); } /* Get the number of certifications to include in an S/MIME message. */ int gpgme_get_include_certs (gpgme_ctx_t ctx) { - TRACE1 (DEBUG_CTX, "gpgme_get_include_certs", ctx, "ctx->include_certs=%i", + TRACE (DEBUG_CTX, "gpgme_get_include_certs", ctx, "ctx->include_certs=%i", ctx->include_certs); return ctx->include_certs; } /* This function changes the default behaviour of the keylisting functions. MODE is a bitwise-OR of the GPGME_KEYLIST_* flags. The default mode is GPGME_KEYLIST_MODE_LOCAL. */ gpgme_error_t gpgme_set_keylist_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode) { - TRACE1 (DEBUG_CTX, "gpgme_set_keylist_mode", ctx, "keylist_mode=0x%x", + TRACE (DEBUG_CTX, "gpgme_set_keylist_mode", ctx, "keylist_mode=0x%x", mode); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); ctx->keylist_mode = mode; return 0; } /* This function returns the default behaviour of the keylisting functions. */ gpgme_keylist_mode_t gpgme_get_keylist_mode (gpgme_ctx_t ctx) { - TRACE1 (DEBUG_CTX, "gpgme_get_keylist_mode", ctx, + TRACE (DEBUG_CTX, "gpgme_get_keylist_mode", ctx, "ctx->keylist_mode=0x%x", ctx->keylist_mode); return ctx->keylist_mode; } /* Set the pinentry mode for CTX to MODE. */ gpgme_error_t gpgme_set_pinentry_mode (gpgme_ctx_t ctx, gpgme_pinentry_mode_t mode) { - TRACE1 (DEBUG_CTX, "gpgme_set_pinentry_mode", ctx, "pinentry_mode=%u", + TRACE (DEBUG_CTX, "gpgme_set_pinentry_mode", ctx, "pinentry_mode=%u", (unsigned int)mode); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); switch (mode) { case GPGME_PINENTRY_MODE_DEFAULT: case GPGME_PINENTRY_MODE_ASK: case GPGME_PINENTRY_MODE_CANCEL: case GPGME_PINENTRY_MODE_ERROR: case GPGME_PINENTRY_MODE_LOOPBACK: break; default: return gpg_error (GPG_ERR_INV_VALUE); } ctx->pinentry_mode = mode; return 0; } /* Get the pinentry mode of CTX. */ gpgme_pinentry_mode_t gpgme_get_pinentry_mode (gpgme_ctx_t ctx) { - TRACE1 (DEBUG_CTX, "gpgme_get_pinentry_mode", ctx, + TRACE (DEBUG_CTX, "gpgme_get_pinentry_mode", ctx, "ctx->pinentry_mode=%u", (unsigned int)ctx->pinentry_mode); return ctx->pinentry_mode; } /* This function sets a callback function to be used to pass a passphrase to gpg. */ void gpgme_set_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, void *cb_value) { - TRACE2 (DEBUG_CTX, "gpgme_set_passphrase_cb", ctx, + TRACE (DEBUG_CTX, "gpgme_set_passphrase_cb", ctx, "passphrase_cb=%p/%p", cb, cb_value); if (!ctx) return; ctx->passphrase_cb = cb; ctx->passphrase_cb_value = cb_value; } /* This function returns the callback function to be used to pass a passphrase to the crypto engine. */ void gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *r_cb, void **r_cb_value) { - TRACE2 (DEBUG_CTX, "gpgme_get_passphrase_cb", ctx, + TRACE (DEBUG_CTX, "gpgme_get_passphrase_cb", ctx, "ctx->passphrase_cb=%p/%p", ctx->passphrase_cb, ctx->passphrase_cb_value); if (r_cb) *r_cb = ctx->passphrase_cb; if (r_cb_value) *r_cb_value = ctx->passphrase_cb_value; } /* This function sets a callback function to be used as a progress indicator. */ void gpgme_set_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t cb, void *cb_value) { - TRACE2 (DEBUG_CTX, "gpgme_set_progress_cb", ctx, "progress_cb=%p/%p", + TRACE (DEBUG_CTX, "gpgme_set_progress_cb", ctx, "progress_cb=%p/%p", cb, cb_value); if (!ctx) return; ctx->progress_cb = cb; ctx->progress_cb_value = cb_value; } /* This function returns the callback function to be used as a progress indicator. */ void gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *r_cb, void **r_cb_value) { - TRACE2 (DEBUG_CTX, "gpgme_get_progress_cb", ctx, "ctx->progress_cb=%p/%p", + TRACE (DEBUG_CTX, "gpgme_get_progress_cb", ctx, "ctx->progress_cb=%p/%p", ctx->progress_cb, ctx->progress_cb_value); if (r_cb) *r_cb = ctx->progress_cb; if (r_cb_value) *r_cb_value = ctx->progress_cb_value; } /* This function sets a callback function to be used as a status message forwarder. */ void gpgme_set_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t cb, void *cb_value) { - TRACE2 (DEBUG_CTX, "gpgme_set_status_cb", ctx, "status_cb=%p/%p", + TRACE (DEBUG_CTX, "gpgme_set_status_cb", ctx, "status_cb=%p/%p", cb, cb_value); if (!ctx) return; ctx->status_cb = cb; ctx->status_cb_value = cb_value; } /* This function returns the callback function to be used as a status message forwarder. */ void gpgme_get_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t *r_cb, void **r_cb_value) { - TRACE2 (DEBUG_CTX, "gpgme_get_status_cb", ctx, "ctx->status_cb=%p/%p", + TRACE (DEBUG_CTX, "gpgme_get_status_cb", ctx, "ctx->status_cb=%p/%p", ctx ? ctx->status_cb : NULL, ctx ? ctx->status_cb_value : NULL); if (r_cb) *r_cb = NULL; if (r_cb_value) *r_cb_value = NULL; if (!ctx || !ctx->status_cb) return; if (r_cb) *r_cb = ctx->status_cb; if (r_cb_value) *r_cb_value = ctx->status_cb_value; } /* Set the I/O callback functions for CTX to IO_CBS. */ void gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs) { if (!ctx) return; if (io_cbs) { - TRACE6 (DEBUG_CTX, "gpgme_set_io_cbs", ctx, + TRACE (DEBUG_CTX, "gpgme_set_io_cbs", ctx, "io_cbs=%p (add=%p/%p, remove=%p, event=%p/%p", io_cbs, io_cbs->add, io_cbs->add_priv, io_cbs->remove, io_cbs->event, io_cbs->event_priv); ctx->io_cbs = *io_cbs; } else { - TRACE1 (DEBUG_CTX, "gpgme_set_io_cbs", ctx, + TRACE (DEBUG_CTX, "gpgme_set_io_cbs", ctx, "io_cbs=%p (default)", io_cbs); ctx->io_cbs.add = NULL; ctx->io_cbs.add_priv = NULL; ctx->io_cbs.remove = NULL; ctx->io_cbs.event = NULL; ctx->io_cbs.event_priv = NULL; } } /* This function provides access to the internal read function; it is normally not used. */ gpgme_ssize_t gpgme_io_read (int fd, void *buffer, size_t count) { int ret; - TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_read", fd, + TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_read", fd, "buffer=%p, count=%u", buffer, count); ret = _gpgme_io_read (fd, buffer, count); return TRACE_SYSRES (ret); } /* This function provides access to the internal write function. It is to be used by user callbacks to return data to gpgme. See gpgme_passphrase_cb_t and gpgme_edit_cb_t. */ gpgme_ssize_t gpgme_io_write (int fd, const void *buffer, size_t count) { int ret; - TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_write", fd, + TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_write", fd, "buffer=%p, count=%u", buffer, count); ret = _gpgme_io_write (fd, buffer, count); return TRACE_SYSRES (ret); } /* This function provides access to the internal write function. It is to be used by user callbacks to return data to gpgme. See gpgme_passphrase_cb_t and gpgme_edit_cb_t. Note that this is a variant of gpgme_io_write which guarantees that all COUNT bytes are written or an error is return. Returns: 0 on success or -1 on error and the sets errno. */ int gpgme_io_writen (int fd, const void *buffer_arg, size_t count) { const char *buffer = buffer_arg; int ret = 0; - TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_writen", fd, + TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_writen", fd, "buffer=%p, count=%u", buffer, count); while (count) { ret = _gpgme_io_write (fd, buffer, count); if (ret < 0) break; buffer += ret; count -= ret; ret = 0; } return TRACE_SYSRES (ret); } /* This function returns the callback function for I/O. */ void gpgme_get_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs) { - TRACE6 (DEBUG_CTX, "gpgme_get_io_cbs", ctx, + TRACE (DEBUG_CTX, "gpgme_get_io_cbs", ctx, "io_cbs=%p, ctx->io_cbs.add=%p/%p, .remove=%p, .event=%p/%p", io_cbs, io_cbs->add, io_cbs->add_priv, io_cbs->remove, io_cbs->event, io_cbs->event_priv); *io_cbs = ctx->io_cbs; } /* This function sets the locale for the context CTX, or the default locale if CTX is a null pointer. */ gpgme_error_t gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value) { int failed = 0; char *new_lc_ctype = NULL; char *new_lc_messages = NULL; - TRACE_BEG2 (DEBUG_CTX, "gpgme_set_locale", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_set_locale", ctx, "category=%i, value=%s", category, value ? value : "(null)"); #define PREPARE_ONE_LOCALE(lcat, ucat) \ if (!failed && value \ && (category == LC_ALL || category == LC_ ## ucat)) \ { \ new_lc_ ## lcat = strdup (value); \ if (!new_lc_ ## lcat) \ failed = 1; \ } #ifdef LC_CTYPE PREPARE_ONE_LOCALE (ctype, CTYPE); #endif #ifdef LC_MESSAGES PREPARE_ONE_LOCALE (messages, MESSAGES); #endif if (failed) { int saved_err = gpg_error_from_syserror (); if (new_lc_ctype) free (new_lc_ctype); if (new_lc_messages) free (new_lc_messages); return TRACE_ERR (saved_err); } #define SET_ONE_LOCALE(lcat, ucat) \ if (category == LC_ALL || category == LC_ ## ucat) \ { \ if (ctx) \ { \ if (ctx->lc_ ## lcat) \ free (ctx->lc_ ## lcat); \ ctx->lc_ ## lcat = new_lc_ ## lcat; \ } \ else \ { \ if (def_lc_ ## lcat) \ free (def_lc_ ## lcat); \ def_lc_ ## lcat = new_lc_ ## lcat; \ } \ } if (!ctx) LOCK (def_lc_lock); #ifdef LC_CTYPE SET_ONE_LOCALE (ctype, CTYPE); #endif #ifdef LC_MESSAGES SET_ONE_LOCALE (messages, MESSAGES); #endif if (!ctx) UNLOCK (def_lc_lock); return TRACE_ERR (0); } /* Get the information about the configured engines. A pointer to the first engine in the statically allocated linked list is returned. The returned data is valid until the next gpgme_ctx_set_engine_info. */ gpgme_engine_info_t gpgme_ctx_get_engine_info (gpgme_ctx_t ctx) { - TRACE1 (DEBUG_CTX, "gpgme_ctx_get_engine_info", ctx, + TRACE (DEBUG_CTX, "gpgme_ctx_get_engine_info", ctx, "ctx->engine_info=%p", ctx->engine_info); return ctx->engine_info; } /* Set the engine info for the context CTX, protocol PROTO, to the file name FILE_NAME and the home directory HOME_DIR. */ gpgme_error_t gpgme_ctx_set_engine_info (gpgme_ctx_t ctx, gpgme_protocol_t proto, const char *file_name, const char *home_dir) { gpgme_error_t err; - TRACE_BEG4 (DEBUG_CTX, "gpgme_ctx_set_engine_info", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_ctx_set_engine_info", ctx, "protocol=%i (%s), file_name=%s, home_dir=%s", proto, gpgme_get_protocol_name (proto) ? gpgme_get_protocol_name (proto) : "unknown", file_name ? file_name : "(default)", home_dir ? home_dir : "(default)"); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); /* Shut down the engine when changing engine info. */ if (ctx->engine) { - TRACE_LOG1 ("releasing ctx->engine=%p", ctx->engine); + TRACE_LOG ("releasing ctx->engine=%p", ctx->engine); _gpgme_engine_release (ctx->engine); ctx->engine = NULL; } err = _gpgme_set_engine_info (ctx->engine_info, proto, file_name, home_dir); return TRACE_ERR (err); } /* Clear all notation data from the context. */ void _gpgme_sig_notation_clear (gpgme_ctx_t ctx) { gpgme_sig_notation_t notation; if (!ctx) return; notation = ctx->sig_notations; while (notation) { gpgme_sig_notation_t next_notation = notation->next; _gpgme_sig_notation_free (notation); notation = next_notation; } ctx->sig_notations = NULL; } void gpgme_sig_notation_clear (gpgme_ctx_t ctx) { - TRACE (DEBUG_CTX, "gpgme_sig_notation_clear", ctx); + TRACE (DEBUG_CTX, "gpgme_sig_notation_clear", ctx, ""); if (!ctx) return; _gpgme_sig_notation_clear (ctx); } /* Add the human-readable notation data with name NAME and value VALUE to the context CTX, using the flags FLAGS. If NAME is NULL, then VALUE should be a policy URL. The flag GPGME_SIG_NOTATION_HUMAN_READABLE is forced to be true for notation data, and false for policy URLs. */ gpgme_error_t gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name, const char *value, gpgme_sig_notation_flags_t flags) { gpgme_error_t err; gpgme_sig_notation_t notation; gpgme_sig_notation_t *lastp; - TRACE_BEG3 (DEBUG_CTX, "gpgme_sig_notation_add", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_sig_notation_add", ctx, "name=%s, value=%s, flags=0x%x", name ? name : "(null)", value ? value : "(null)", flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (name) flags |= GPGME_SIG_NOTATION_HUMAN_READABLE; else flags &= ~GPGME_SIG_NOTATION_HUMAN_READABLE; err = _gpgme_sig_notation_create (¬ation, name, name ? strlen (name) : 0, value, value ? strlen (value) : 0, flags); if (err) return TRACE_ERR (err); lastp = &ctx->sig_notations; while (*lastp) lastp = &(*lastp)->next; *lastp = notation; return TRACE_ERR (0); } /* Get the sig notations for this context. */ gpgme_sig_notation_t gpgme_sig_notation_get (gpgme_ctx_t ctx) { if (!ctx) { - TRACE (DEBUG_CTX, "gpgme_sig_notation_get", ctx); + TRACE (DEBUG_CTX, "gpgme_sig_notation_get", ctx, ""); return NULL; } - TRACE1 (DEBUG_CTX, "gpgme_sig_notation_get", ctx, + TRACE (DEBUG_CTX, "gpgme_sig_notation_get", ctx, "ctx->sig_notations=%p", ctx->sig_notations); return ctx->sig_notations; } /* Return a public key algorithm string made of the algorithm and size or the curve name. May return NULL on error. Caller must free the result using gpgme_free. */ char * gpgme_pubkey_algo_string (gpgme_subkey_t subkey) { const char *prefix = NULL; char *result; if (!subkey) { gpg_err_set_errno (EINVAL); return NULL; } switch (subkey->pubkey_algo) { case GPGME_PK_RSA: case GPGME_PK_RSA_E: case GPGME_PK_RSA_S: prefix = "rsa"; break; case GPGME_PK_ELG_E: prefix = "elg"; break; case GPGME_PK_DSA: prefix = "dsa"; break; case GPGME_PK_ELG: prefix = "xxx"; break; case GPGME_PK_ECC: case GPGME_PK_ECDH: case GPGME_PK_ECDSA: case GPGME_PK_EDDSA: prefix = ""; break; } if (prefix && *prefix) { char buffer[40]; snprintf (buffer, sizeof buffer, "%s%u", prefix, subkey->length); result = strdup (buffer); } else if (prefix && subkey->curve && *subkey->curve) result = strdup (subkey->curve); else if (prefix) result = strdup ("E_error"); else result = strdup ("unknown"); return result; } const char * gpgme_pubkey_algo_name (gpgme_pubkey_algo_t algo) { switch (algo) { case GPGME_PK_RSA: return "RSA"; case GPGME_PK_RSA_E: return "RSA-E"; case GPGME_PK_RSA_S: return "RSA-S"; case GPGME_PK_ELG_E: return "ELG-E"; case GPGME_PK_DSA: return "DSA"; case GPGME_PK_ECC: return "ECC"; case GPGME_PK_ELG: return "ELG"; case GPGME_PK_ECDSA: return "ECDSA"; case GPGME_PK_ECDH: return "ECDH"; case GPGME_PK_EDDSA: return "EdDSA"; default: return NULL; } } const char * gpgme_hash_algo_name (gpgme_hash_algo_t algo) { switch (algo) { case GPGME_MD_MD5: return "MD5"; case GPGME_MD_SHA1: return "SHA1"; case GPGME_MD_RMD160: return "RIPEMD160"; case GPGME_MD_MD2: return "MD2"; case GPGME_MD_TIGER: return "TIGER192"; case GPGME_MD_HAVAL: return "HAVAL"; case GPGME_MD_SHA256: return "SHA256"; case GPGME_MD_SHA384: return "SHA384"; case GPGME_MD_SHA512: return "SHA512"; case GPGME_MD_SHA224: return "SHA224"; case GPGME_MD_MD4: return "MD4"; case GPGME_MD_CRC32: return "CRC32"; case GPGME_MD_CRC32_RFC1510: return "CRC32RFC1510"; case GPGME_MD_CRC24_RFC2440: return "CRC24RFC2440"; default: return NULL; } } diff --git a/src/import.c b/src/import.c index 33779e96..3d49146f 100644 --- a/src/import.c +++ b/src/import.c @@ -1,451 +1,451 @@ /* import.c - Import a key. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { struct _gpgme_op_import_result result; /* A pointer to the next pointer of the last import status in the list. This makes appending new imports painless while preserving the order. */ gpgme_import_status_t *lastp; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_import_status_t import = opd->result.imports; while (import) { gpgme_import_status_t next = import->next; free (import->fpr); free (import); import = next; } } gpgme_import_result_t gpgme_op_import_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; - TRACE_BEG (DEBUG_CTX, "gpgme_op_import_result", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_import_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL); opd = hook; if (err || !opd) { - TRACE_SUC0 ("result=(null)"); + TRACE_SUC ("result=(null)"); return NULL; } if (_gpgme_debug_trace ()) { gpgme_import_status_t impstat; int i; - TRACE_LOG5 ("%i considered, %i no UID, %i imported, %i imported RSA, " + TRACE_LOG ("%i considered, %i no UID, %i imported, %i imported RSA, " "%i unchanged", opd->result.considered, opd->result.no_user_id, opd->result.imported, opd->result.imported_rsa, opd->result.unchanged); - TRACE_LOG4 ("%i new UIDs, %i new sub keys, %i new signatures, " + TRACE_LOG ("%i new UIDs, %i new sub keys, %i new signatures, " "%i new revocations", opd->result.new_user_ids, opd->result.new_sub_keys, opd->result.new_signatures, opd->result.new_revocations); - TRACE_LOG3 ("%i secret keys, %i imported, %i unchanged", + TRACE_LOG ("%i secret keys, %i imported, %i unchanged", opd->result.secret_read, opd->result.secret_imported, opd->result.secret_unchanged); - TRACE_LOG3 ("%i skipped new keys, %i not imported, %i v3 skipped", + TRACE_LOG ("%i skipped new keys, %i not imported, %i v3 skipped", opd->result.skipped_new_keys, opd->result.not_imported, opd->result.skipped_v3_keys); impstat = opd->result.imports; i = 0; while (impstat) { - TRACE_LOG4 ("import[%i] for %s = 0x%x (%s)", + TRACE_LOG ("import[%i] for %s = 0x%x (%s)", i, impstat->fpr, impstat->status, impstat->result); impstat = impstat->next; i++; } } - TRACE_SUC1 ("result=%p", &opd->result); + TRACE_SUC ("result=%p", &opd->result); return &opd->result; } static gpgme_error_t parse_import (char *args, gpgme_import_status_t *import_status, int problem) { gpgme_import_status_t import; char *tail; long int nr; import = malloc (sizeof (*import)); if (!import) return gpg_error_from_syserror (); import->next = NULL; gpg_err_set_errno (0); nr = strtol (args, &tail, 0); if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (import); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args = tail; if (problem) { switch (nr) { case 0: case 4: default: import->result = gpg_error (GPG_ERR_GENERAL); break; case 1: import->result = gpg_error (GPG_ERR_BAD_CERT); break; case 2: import->result = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); break; case 3: import->result = gpg_error (GPG_ERR_BAD_CERT_CHAIN); break; } import->status = 0; } else { import->result = gpg_error (GPG_ERR_NO_ERROR); import->status = nr; } while (*args == ' ') args++; tail = strchr (args, ' '); if (tail) *tail = '\0'; import->fpr = strdup (args); if (!import->fpr) { free (import); return gpg_error_from_syserror (); } *import_status = import; return 0; } gpgme_error_t parse_import_res (char *args, gpgme_import_result_t result) { char *tail; gpg_err_set_errno (0); #define PARSE_NEXT(x) \ (x) = strtol (args, &tail, 0); \ if (errno || args == tail || !(*tail == ' ' || !*tail)) \ /* The crypto backend does not behave. */ \ return trace_gpg_error (GPG_ERR_INV_ENGINE); \ args = tail; PARSE_NEXT (result->considered); PARSE_NEXT (result->no_user_id); PARSE_NEXT (result->imported); PARSE_NEXT (result->imported_rsa); PARSE_NEXT (result->unchanged); PARSE_NEXT (result->new_user_ids); PARSE_NEXT (result->new_sub_keys); PARSE_NEXT (result->new_signatures); PARSE_NEXT (result->new_revocations); PARSE_NEXT (result->secret_read); PARSE_NEXT (result->secret_imported); PARSE_NEXT (result->secret_unchanged); PARSE_NEXT (result->skipped_new_keys); PARSE_NEXT (result->not_imported); if (args && *args) { PARSE_NEXT (result->skipped_v3_keys); } return 0; } static gpgme_error_t import_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_IMPORT_OK: case GPGME_STATUS_IMPORT_PROBLEM: err = parse_import (args, opd->lastp, code == GPGME_STATUS_IMPORT_OK ? 0 : 1); if (err) return err; opd->lastp = &(*opd->lastp)->next; break; case GPGME_STATUS_IMPORT_RES: err = parse_import_res (args, &opd->result); break; default: break; } return err; } static gpgme_error_t _gpgme_op_import_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t keydata) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->lastp = &opd->result.imports; if (!keydata) return gpg_error (GPG_ERR_NO_DATA); _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx); return _gpgme_engine_op_import (ctx->engine, keydata, NULL); } gpgme_error_t gpgme_op_import_start (gpgme_ctx_t ctx, gpgme_data_t keydata) { gpg_error_t err; - TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_import_start", ctx, "keydata=%p", keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_import_start (ctx, 0, keydata); return TRACE_ERR (err); } /* Import the key in KEYDATA into the keyring. */ gpgme_error_t gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata) { gpgme_error_t err; - TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_import", ctx, "keydata=%p", keydata); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_import_start (ctx, 1, keydata); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } static gpgme_error_t _gpgme_op_import_keys_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t *keys) { gpgme_error_t err; void *hook; op_data_t opd; int idx, firstidx, nkeys; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->lastp = &opd->result.imports; if (!keys) return gpg_error (GPG_ERR_NO_DATA); for (idx=nkeys=0, firstidx=-1; keys[idx]; idx++) { /* We only consider keys of the current protocol. */ if (keys[idx]->protocol != ctx->protocol) continue; if (firstidx == -1) firstidx = idx; /* If a key has been found using a different key listing mode, we bail out. This makes the processing easier. Fixme: To allow a mix of keys we would need to sort them by key listing mode and start two import operations one after the other. */ if (keys[idx]->keylist_mode != keys[firstidx]->keylist_mode) return gpg_error (GPG_ERR_CONFLICT); nkeys++; } if (!nkeys) return gpg_error (GPG_ERR_NO_DATA); _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx); return _gpgme_engine_op_import (ctx->engine, NULL, keys); } /* Asynchronous version of gpgme_op_import_key. */ gpgme_error_t gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t *keys) { gpg_error_t err; - TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys_start", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys_start", ctx, ""); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && keys) { int i = 0; while (keys[i]) { - TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i], + TRACE_LOG ("keys[%i] = %p (%s)", i, keys[i], (keys[i]->subkeys && keys[i]->subkeys->fpr) ? keys[i]->subkeys->fpr : "invalid"); i++; } } err = _gpgme_op_import_keys_start (ctx, 0, keys); return TRACE_ERR (err); } /* Import the keys from the array KEYS into the keyring. In particular it is used to actually import keys retrieved from an external source (i.e. using GPGME_KEYLIST_MODE_EXTERN). It replaces the old workaround of exporting and then importing a key as used to make an X.509 key permanent. This function automagically does the right thing. KEYS is a NULL terminated array of gpgme key objects. The result is the usual import result structure. Only keys matching the current protocol are imported; other keys are ignored. */ gpgme_error_t gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys) { gpgme_error_t err; - TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys", ctx, ""); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && keys) { int i = 0; while (keys[i]) { - TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i], + TRACE_LOG ("keys[%i] = %p (%s)", i, keys[i], (keys[i]->subkeys && keys[i]->subkeys->fpr) ? keys[i]->subkeys->fpr : "invalid"); i++; } } err = _gpgme_op_import_keys_start (ctx, 1, keys); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } /* Deprecated interface. */ gpgme_error_t gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr) { gpgme_error_t err = gpgme_op_import (ctx, keydata); if (!err && nr) { gpgme_import_result_t result = gpgme_op_import_result (ctx); *nr = result->considered; } return err; } diff --git a/src/keylist.c b/src/keylist.c index a0fa841b..7ea7b267 100644 --- a/src/keylist.c +++ b/src/keylist.c @@ -1,1343 +1,1343 @@ /* keylist.c - Listing keys. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, * 2008, 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #ifdef HAVE_SYS_TYPES_H /* Solaris 8 needs sys/types.h before time.h. */ # include #endif #include #include #include #include #include /* Suppress warning for accessing deprecated member "class". */ #define _GPGME_IN_GPGME #include "gpgme.h" #include "util.h" #include "context.h" #include "ops.h" #include "debug.h" struct key_queue_item_s { struct key_queue_item_s *next; gpgme_key_t key; }; typedef struct { struct _gpgme_op_keylist_result result; /* The error code from ERROR keydb_search. */ gpgme_error_t keydb_search_err; gpgme_key_t tmp_key; /* This points to the last uid in tmp_key. */ gpgme_user_id_t tmp_uid; /* This points to the last sig in tmp_uid. */ gpgme_key_sig_t tmp_keysig; /* Something new is available. */ int key_cond; struct key_queue_item_s *key_queue; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; struct key_queue_item_s *key = opd->key_queue; if (opd->tmp_key) gpgme_key_unref (opd->tmp_key); /* opd->tmp_uid and opd->tmp_keysig are actually part of opd->tmp_key, so we do not need to release them here. */ while (key) { struct key_queue_item_s *next = key->next; gpgme_key_unref (key->key); key = next; } } gpgme_keylist_result_t gpgme_op_keylist_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; - TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_result", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err || !opd) { - TRACE_SUC0 ("result=(null)"); + TRACE_SUC ("result=(null)"); return NULL; } - TRACE_LOG1 ("truncated = %i", opd->result.truncated); + TRACE_LOG ("truncated = %i", opd->result.truncated); - TRACE_SUC1 ("result=%p", &opd->result); + TRACE_SUC ("result=%p", &opd->result); return &opd->result; } static gpgme_error_t keylist_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; (void)args; err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_TRUNCATED: opd->result.truncated = 1; break; case GPGME_STATUS_ERROR: err = _gpgme_parse_failure (args); if (!opd->keydb_search_err && !strcmp (args, "keydb_search")) opd->keydb_search_err = err; err = 0; break; default: break; } return err; } static void set_subkey_trust_info (gpgme_subkey_t subkey, const char *src) { while (*src && !isdigit (*src)) { switch (*src) { case 'e': subkey->expired = 1; break; case 'r': subkey->revoked = 1; break; case 'd': /* Note that gpg 1.3 won't print that anymore but only uses the capabilities field. */ subkey->disabled = 1; break; case 'i': subkey->invalid = 1; break; } src++; } } static void set_mainkey_trust_info (gpgme_key_t key, const char *src) { /* First set the trust info of the main key (the first subkey). */ set_subkey_trust_info (key->subkeys, src); /* Now set the summarized trust info. */ while (*src && !isdigit (*src)) { switch (*src) { case 'e': key->expired = 1; break; case 'r': key->revoked = 1; break; case 'd': /* Note that gpg 1.3 won't print that anymore but only uses the capabilities field. However, it is still used for external key listings. */ key->disabled = 1; break; case 'i': key->invalid = 1; break; } src++; } } static void set_userid_flags (gpgme_key_t key, const char *src) { gpgme_user_id_t uid = key->_last_uid; assert (uid); /* Look at letters and stop at the first digit. */ while (*src && !isdigit (*src)) { switch (*src) { case 'r': uid->revoked = 1; break; case 'i': uid->invalid = 1; break; case 'n': uid->validity = GPGME_VALIDITY_NEVER; break; case 'm': uid->validity = GPGME_VALIDITY_MARGINAL; break; case 'f': uid->validity = GPGME_VALIDITY_FULL; break; case 'u': uid->validity = GPGME_VALIDITY_ULTIMATE; break; } src++; } } static void set_subkey_capability (gpgme_subkey_t subkey, const char *src) { while (*src) { switch (*src) { case 'e': subkey->can_encrypt = 1; break; case 's': subkey->can_sign = 1; break; case 'c': subkey->can_certify = 1; break; case 'a': subkey->can_authenticate = 1; break; case 'q': subkey->is_qualified = 1; break; case 'd': subkey->disabled = 1; break; } src++; } } static void set_mainkey_capability (gpgme_key_t key, const char *src) { /* First set the capabilities of the main key (the first subkey). */ set_subkey_capability (key->subkeys, src); while (*src) { switch (*src) { case 'd': case 'D': /* Note, that this flag is also set using the key validity field for backward compatibility with gpg 1.2. We use d and D, so that a future gpg version will be able to disable certain subkeys. Currently it is expected that gpg sets this for the primary key. */ key->disabled = 1; break; case 'e': case 'E': key->can_encrypt = 1; break; case 's': case 'S': key->can_sign = 1; break; case 'c': case 'C': key->can_certify = 1; break; case 'a': case 'A': key->can_authenticate = 1; break; case 'q': case 'Q': key->is_qualified = 1; break; } src++; } } static void set_ownertrust (gpgme_key_t key, const char *src) { /* Look at letters and stop at the first digit. */ while (*src && !isdigit (*src)) { switch (*src) { case 'n': key->owner_trust = GPGME_VALIDITY_NEVER; break; case 'm': key->owner_trust = GPGME_VALIDITY_MARGINAL; break; case 'f': key->owner_trust = GPGME_VALIDITY_FULL; break; case 'u': key->owner_trust = GPGME_VALIDITY_ULTIMATE; break; default: key->owner_trust = GPGME_VALIDITY_UNKNOWN; break; } src++; } } static gpgme_keyorg_t parse_keyorg (const char *string) { switch (atoi (string)) { case 0: return GPGME_KEYORG_UNKNOWN; case 1: case 2: return GPGME_KEYORG_KS; case 3: return GPGME_KEYORG_DANE; case 4: return GPGME_KEYORG_WKD; case 5: return GPGME_KEYORG_URL; case 6: return GPGME_KEYORG_FILE; case 7: return GPGME_KEYORG_SELF; default: return GPGME_KEYORG_OTHER; } } /* Parse field 15 of a secret key or subkey. This fields holds a reference to smartcards. FIELD is the content of the field and we are allowed to modify it. */ static gpg_error_t parse_sec_field15 (gpgme_key_t key, gpgme_subkey_t subkey, char *field) { if (!*field) ; /* Empty. */ else if (*field == '#') { /* This is a stub for an offline key. We reset the SECRET flag of the subkey here. Note that the secret flag of the entire key will be true even then. We even explicitly set key->secret to make it works for GPGME_KEYLIST_MODE_WITH_SECRET. */ subkey->secret = 0; key->secret = 1; } else if (strchr ("01234567890ABCDEFabcdef", *field)) { /* Fields starts with a hex digit; thus it is a serial number. */ key->secret = 1; subkey->is_cardkey = 1; subkey->card_number = strdup (field); if (!subkey->card_number) return gpg_error_from_syserror (); } else if (*field == '+') { key->secret = 1; subkey->secret = 1; } else { /* RFU. */ } return 0; } /* Parse a tfs record. */ static gpg_error_t parse_tfs_record (gpgme_user_id_t uid, char **field, int nfield) { gpg_error_t err; gpgme_tofu_info_t ti; unsigned long uval; /* We add only the first TOFU record in case future versions emit * several. */ if (uid->tofu) return 0; /* Check that we have enough fields and that the version is supported. */ if (nfield < 8 || atoi(field[1]) != 1) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti = calloc (1, sizeof *ti); if (!ti) return gpg_error_from_syserror (); /* Note that we allow a value of up to 7 which is what we can store * in the ti->validity. */ err = _gpgme_strtoul_field (field[2], &uval); if (err || uval > 7) goto inv_engine; ti->validity = uval; /* Parse the sign-count. */ err = _gpgme_strtoul_field (field[3], &uval); if (err) goto inv_engine; if (uval > USHRT_MAX) uval = USHRT_MAX; ti->signcount = uval; /* Parse the encr-count. */ err = _gpgme_strtoul_field (field[4], &uval); if (err) goto inv_engine; if (uval > USHRT_MAX) uval = USHRT_MAX; ti->encrcount = uval; /* Parse the policy. */ if (!strcmp (field[5], "none")) ti->policy = GPGME_TOFU_POLICY_NONE; else if (!strcmp (field[5], "auto")) ti->policy = GPGME_TOFU_POLICY_AUTO; else if (!strcmp (field[5], "good")) ti->policy = GPGME_TOFU_POLICY_GOOD; else if (!strcmp (field[5], "bad")) ti->policy = GPGME_TOFU_POLICY_BAD; else if (!strcmp (field[5], "ask")) ti->policy = GPGME_TOFU_POLICY_ASK; else /* "unknown" and invalid policy strings. */ ti->policy = GPGME_TOFU_POLICY_UNKNOWN; /* Parse first and last seen timestamps. */ err = _gpgme_strtoul_field (field[6], &uval); if (err) goto inv_engine; ti->signfirst = uval; err = _gpgme_strtoul_field (field[7], &uval); if (err) goto inv_engine; ti->signlast = uval; if (nfield > 9) { /* This condition is only to allow for gpg 2.1.15 - can * eventually be removed. */ err = _gpgme_strtoul_field (field[8], &uval); if (err) goto inv_engine; ti->encrfirst = uval; err = _gpgme_strtoul_field (field[9], &uval); if (err) goto inv_engine; ti->encrlast = uval; } /* Ready. */ uid->tofu = ti; return 0; inv_engine: free (ti); return trace_gpg_error (GPG_ERR_INV_ENGINE); } /* We have read an entire key into tmp_key and should now finish it. It is assumed that this releases tmp_key. */ static void finish_key (gpgme_ctx_t ctx, op_data_t opd) { gpgme_key_t key = opd->tmp_key; opd->tmp_key = NULL; opd->tmp_uid = NULL; opd->tmp_keysig = NULL; if (key) _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key); } /* Note: We are allowed to modify LINE. */ static gpgme_error_t keylist_colon_handler (void *priv, char *line) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; enum { RT_NONE, RT_SIG, RT_UID, RT_TFS, RT_SUB, RT_PUB, RT_FPR, RT_GRP, RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK } rectype = RT_NONE; #define NR_FIELDS 20 char *field[NR_FIELDS]; int fields = 0; void *hook; op_data_t opd; gpgme_error_t err; gpgme_key_t key; gpgme_subkey_t subkey = NULL; gpgme_key_sig_t keysig = NULL; err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return err; key = opd->tmp_key; - TRACE2 (DEBUG_CTX, "gpgme:keylist_colon_handler", ctx, + TRACE (DEBUG_CTX, "gpgme:keylist_colon_handler", ctx, "key = %p, line = %s", key, line ? line : "(null)"); if (!line) { /* End Of File. */ finish_key (ctx, opd); return 0; } while (line && fields < NR_FIELDS) { field[fields++] = line; line = strchr (line, ':'); if (line) *(line++) = '\0'; } if (!strcmp (field[0], "sig")) rectype = RT_SIG; else if (!strcmp (field[0], "rev")) rectype = RT_REV; else if (!strcmp (field[0], "pub")) rectype = RT_PUB; else if (!strcmp (field[0], "sec")) rectype = RT_SEC; else if (!strcmp (field[0], "crt")) rectype = RT_CRT; else if (!strcmp (field[0], "crs")) rectype = RT_CRS; else if (!strcmp (field[0], "fpr") && key) rectype = RT_FPR; else if (!strcmp (field[0], "grp") && key) rectype = RT_GRP; else if (!strcmp (field[0], "uid") && key) rectype = RT_UID; else if (!strcmp (field[0], "tfs") && key) rectype = RT_TFS; else if (!strcmp (field[0], "sub") && key) rectype = RT_SUB; else if (!strcmp (field[0], "ssb") && key) rectype = RT_SSB; else if (!strcmp (field[0], "spk") && key) rectype = RT_SPK; else rectype = RT_NONE; /* Only look at signature and trust info records immediately following a user ID. For this, clear the user ID pointer when encountering anything but a signature or trust record. */ if (rectype != RT_SIG && rectype != RT_REV && rectype != RT_TFS) opd->tmp_uid = NULL; /* Only look at subpackets immediately following a signature. For this, clear the signature pointer when encountering anything but a subpacket. */ if (rectype != RT_SPK) opd->tmp_keysig = NULL; switch (rectype) { case RT_PUB: case RT_SEC: case RT_CRT: case RT_CRS: /* Start a new keyblock. */ err = _gpgme_key_new (&key); if (err) return err; key->keylist_mode = ctx->keylist_mode; err = _gpgme_key_add_subkey (key, &subkey); if (err) { gpgme_key_unref (key); return err; } if (rectype == RT_SEC || rectype == RT_CRS) key->secret = subkey->secret = 1; if (rectype == RT_CRT || rectype == RT_CRS) key->protocol = GPGME_PROTOCOL_CMS; finish_key (ctx, opd); opd->tmp_key = key; /* Field 2 has the trust info. */ if (fields >= 2) set_mainkey_trust_info (key, field[1]); /* Field 3 has the key length. */ if (fields >= 3) { int i = atoi (field[2]); /* Ignore invalid values. */ if (i > 1) subkey->length = i; } /* Field 4 has the public key algorithm. */ if (fields >= 4) { int i = atoi (field[3]); if (i >= 1 && i < 128) subkey->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol); } /* Field 5 has the long keyid. Allow short key IDs for the output of an external keyserver listing. */ if (fields >= 5 && strlen (field[4]) <= DIM(subkey->_keyid) - 1) strcpy (subkey->_keyid, field[4]); /* Field 6 has the timestamp (seconds). */ if (fields >= 6) subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL); /* Field 7 has the expiration time (seconds). */ if (fields >= 7) subkey->expires = _gpgme_parse_timestamp (field[6], NULL); /* Field 8 has the X.509 serial number. */ if (fields >= 8 && (rectype == RT_CRT || rectype == RT_CRS)) { key->issuer_serial = strdup (field[7]); if (!key->issuer_serial) return gpg_error_from_syserror (); } /* Field 9 has the ownertrust. */ if (fields >= 9) set_ownertrust (key, field[8]); /* Field 10 is not used for gpg due to --fixed-list-mode option but GPGSM stores the issuer name. */ if (fields >= 10 && (rectype == RT_CRT || rectype == RT_CRS)) if (_gpgme_decode_c_string (field[9], &key->issuer_name, 0)) return gpg_error (GPG_ERR_ENOMEM); /* FIXME */ /* Field 11 has the signature class. */ /* Field 12 has the capabilities. */ if (fields >= 12) set_mainkey_capability (key, field[11]); /* Field 15 carries special flags of a secret key. */ if (fields >= 15 && (key->secret || (ctx->keylist_mode & GPGME_KEYLIST_MODE_WITH_SECRET))) { err = parse_sec_field15 (key, subkey, field[14]); if (err) return err; } /* Field 17 has the curve name for ECC. */ if (fields >= 17 && *field[16]) { subkey->curve = strdup (field[16]); if (!subkey->curve) return gpg_error_from_syserror (); } /* Field 18 has the compliance flags. */ if (fields >= 17 && *field[17]) PARSE_COMPLIANCE_FLAGS (field[17], subkey); if (fields >= 20) { key->last_update = _gpgme_parse_timestamp_ul (field[18]); key->origin = parse_keyorg (field[19]); } break; case RT_SUB: case RT_SSB: /* Start a new subkey. */ err = _gpgme_key_add_subkey (key, &subkey); if (err) return err; if (rectype == RT_SSB) subkey->secret = 1; /* Field 2 has the trust info. */ if (fields >= 2) set_subkey_trust_info (subkey, field[1]); /* Field 3 has the key length. */ if (fields >= 3) { int i = atoi (field[2]); /* Ignore invalid values. */ if (i > 1) subkey->length = i; } /* Field 4 has the public key algorithm. */ if (fields >= 4) { int i = atoi (field[3]); if (i >= 1 && i < 128) subkey->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol); } /* Field 5 has the long keyid. */ if (fields >= 5 && strlen (field[4]) == DIM(subkey->_keyid) - 1) strcpy (subkey->_keyid, field[4]); /* Field 6 has the timestamp (seconds). */ if (fields >= 6) subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL); /* Field 7 has the expiration time (seconds). */ if (fields >= 7) subkey->expires = _gpgme_parse_timestamp (field[6], NULL); /* Field 8 is reserved (LID). */ /* Field 9 has the ownertrust. */ /* Field 10, the user ID, is n/a for a subkey. */ /* Field 11 has the signature class. */ /* Field 12 has the capabilities. */ if (fields >= 12) set_subkey_capability (subkey, field[11]); /* Field 15 carries special flags of a secret key. */ if (fields >= 15 && (key->secret || (ctx->keylist_mode & GPGME_KEYLIST_MODE_WITH_SECRET))) { err = parse_sec_field15 (key, subkey, field[14]); if (err) return err; } /* Field 17 has the curve name for ECC. */ if (fields >= 17 && *field[16]) { subkey->curve = strdup (field[16]); if (!subkey->curve) return gpg_error_from_syserror (); } /* Field 18 has the compliance flags. */ if (fields >= 17 && *field[17]) PARSE_COMPLIANCE_FLAGS (field[17], subkey); break; case RT_UID: /* Field 2 has the trust info, and field 10 has the user ID. */ if (fields >= 10) { if (_gpgme_key_append_name (key, field[9], 1)) return gpg_error (GPG_ERR_ENOMEM); /* FIXME */ if (field[1]) set_userid_flags (key, field[1]); opd->tmp_uid = key->_last_uid; if (fields >= 20) { opd->tmp_uid->last_update = _gpgme_parse_timestamp_ul (field[18]); opd->tmp_uid->origin = parse_keyorg (field[19]); } } break; case RT_TFS: if (opd->tmp_uid) { err = parse_tfs_record (opd->tmp_uid, field, fields); if (err) return err; } break; case RT_FPR: /* Field 10 has the fingerprint (take only the first one). */ if (fields >= 10 && field[9] && *field[9]) { /* Need to apply it to the last subkey because all subkeys do have fingerprints. */ subkey = key->_last_subkey; if (!subkey->fpr) { subkey->fpr = strdup (field[9]); if (!subkey->fpr) return gpg_error_from_syserror (); } /* If this is the first subkey, store the fingerprint also in the KEY object. */ if (subkey == key->subkeys) { if (key->fpr && strcmp (key->fpr, subkey->fpr)) { /* FPR already set but mismatch: Should never happen. */ return trace_gpg_error (GPG_ERR_INTERNAL); } if (!key->fpr) { key->fpr = strdup (subkey->fpr); if (!key->fpr) return gpg_error_from_syserror (); } } } /* Field 13 has the gpgsm chain ID (take only the first one). */ if (fields >= 13 && !key->chain_id && *field[12]) { key->chain_id = strdup (field[12]); if (!key->chain_id) return gpg_error_from_syserror (); } break; case RT_GRP: /* Field 10 has the keygrip. */ if (fields >= 10 && field[9] && *field[9]) { /* Need to apply it to the last subkey because all subkeys have a keygrip. */ subkey = key->_last_subkey; if (!subkey->keygrip) { subkey->keygrip = strdup (field[9]); if (!subkey->keygrip) return gpg_error_from_syserror (); } } break; case RT_SIG: case RT_REV: if (!opd->tmp_uid) return 0; /* Start a new (revoked) signature. */ assert (opd->tmp_uid == key->_last_uid); keysig = _gpgme_key_add_sig (key, (fields >= 10) ? field[9] : NULL); if (!keysig) return gpg_error (GPG_ERR_ENOMEM); /* FIXME */ /* Field 2 has the calculated trust ('!', '-', '?', '%'). */ if (fields >= 2) switch (field[1][0]) { case '!': keysig->status = gpg_error (GPG_ERR_NO_ERROR); break; case '-': keysig->status = gpg_error (GPG_ERR_BAD_SIGNATURE); break; case '?': keysig->status = gpg_error (GPG_ERR_NO_PUBKEY); break; case '%': keysig->status = gpg_error (GPG_ERR_GENERAL); break; default: keysig->status = gpg_error (GPG_ERR_NO_ERROR); break; } /* Field 4 has the public key algorithm. */ if (fields >= 4) { int i = atoi (field[3]); if (i >= 1 && i < 128) keysig->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol); } /* Field 5 has the long keyid. */ if (fields >= 5 && strlen (field[4]) == DIM(keysig->_keyid) - 1) strcpy (keysig->_keyid, field[4]); /* Field 6 has the timestamp (seconds). */ if (fields >= 6) keysig->timestamp = _gpgme_parse_timestamp (field[5], NULL); /* Field 7 has the expiration time (seconds). */ if (fields >= 7) keysig->expires = _gpgme_parse_timestamp (field[6], NULL); /* Field 11 has the signature class (eg, 0x30 means revoked). */ if (fields >= 11) if (field[10][0] && field[10][1]) { int sig_class = _gpgme_hextobyte (field[10]); if (sig_class >= 0) { keysig->sig_class = sig_class; keysig->class = keysig->sig_class; if (sig_class == 0x30) keysig->revoked = 1; } if (field[10][2] == 'x') keysig->exportable = 1; } opd->tmp_keysig = keysig; break; case RT_SPK: if (!opd->tmp_keysig) return 0; assert (opd->tmp_keysig == key->_last_uid->_last_keysig); if (fields >= 4) { /* Field 2 has the subpacket type. */ int type = atoi (field[1]); /* Field 3 has the flags. */ int flags = atoi (field[2]); /* Field 4 has the length. */ int len = atoi (field[3]); /* Field 5 has the data. */ char *data = field[4]; /* Type 20: Notation data. */ /* Type 26: Policy URL. */ if (type == 20 || type == 26) { gpgme_sig_notation_t notation; keysig = opd->tmp_keysig; /* At this time, any error is serious. */ err = _gpgme_parse_notation (¬ation, type, flags, len, data); if (err) return err; /* Add a new notation. FIXME: Could be factored out. */ if (!keysig->notations) keysig->notations = notation; if (keysig->_last_notation) keysig->_last_notation->next = notation; keysig->_last_notation = notation; } } case RT_NONE: /* Unknown record. */ break; } return 0; } void _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type, void *type_data) { gpgme_error_t err; gpgme_ctx_t ctx = (gpgme_ctx_t) data; gpgme_key_t key = (gpgme_key_t) type_data; void *hook; op_data_t opd; struct key_queue_item_s *q, *q2; assert (type == GPGME_EVENT_NEXT_KEY); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return; q = malloc (sizeof *q); if (!q) { gpgme_key_unref (key); /* FIXME return GPGME_Out_Of_Core; */ return; } q->key = key; q->next = NULL; /* FIXME: Use a tail pointer? */ if (!(q2 = opd->key_queue)) opd->key_queue = q; else { for (; q2->next; q2 = q2->next) ; q2->next = q; } opd->key_cond = 1; } /* Start a keylist operation within CTX, searching for keys which match PATTERN. If SECRET_ONLY is true, only secret keys are returned. */ gpgme_error_t gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only) { gpgme_error_t err; void *hook; op_data_t opd; int flags = 0; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_keylist_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_start", ctx, "pattern=%s, secret_only=%i", pattern, secret_only); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, keylist_colon_handler, ctx); if (err) return TRACE_ERR (err); if (ctx->offline) flags |= GPGME_ENGINE_FLAG_OFFLINE; err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only, ctx->keylist_mode, flags); return TRACE_ERR (err); } /* Start a keylist operation within CTX, searching for keys which match PATTERN. If SECRET_ONLY is true, only secret keys are returned. */ gpgme_error_t gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[], int secret_only, int reserved) { gpgme_error_t err; void *hook; op_data_t opd; int flags = 0; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_keylist_ext_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_ext_start", ctx, "secret_only=%i, reserved=0x%x", secret_only, reserved); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, keylist_colon_handler, ctx); if (err) return TRACE_ERR (err); if (ctx->offline) flags |= GPGME_ENGINE_FLAG_OFFLINE; err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only, reserved, ctx->keylist_mode, flags); return TRACE_ERR (err); } /* Start a keylist operation within CTX to show keys contained * in DATA. */ gpgme_error_t gpgme_op_keylist_from_data_start (gpgme_ctx_t ctx, gpgme_data_t data, int reserved) { gpgme_error_t err; void *hook; op_data_t opd; - TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_from_data_start", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_from_data_start", ctx, ""); if (!ctx || !data || reserved) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, keylist_colon_handler, ctx); if (err) return TRACE_ERR (err); err = _gpgme_engine_op_keylist_data (ctx->engine, data); return TRACE_ERR (err); } /* Return the next key from the keylist in R_KEY. */ gpgme_error_t gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key) { gpgme_error_t err; struct key_queue_item_s *queue_item; void *hook; op_data_t opd; - TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_next", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_next", ctx, ""); if (!ctx || !r_key) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); *r_key = NULL; if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return TRACE_ERR (err); if (opd == NULL) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (!opd->key_queue) { err = _gpgme_wait_on_condition (ctx, &opd->key_cond, NULL); if (err) return TRACE_ERR (err); if (!opd->key_cond) return TRACE_ERR (opd->keydb_search_err? opd->keydb_search_err /**/ : gpg_error (GPG_ERR_EOF)); opd->key_cond = 0; assert (opd->key_queue); } queue_item = opd->key_queue; opd->key_queue = queue_item->next; if (!opd->key_queue) opd->key_cond = 0; *r_key = queue_item->key; free (queue_item); - return TRACE_SUC2 ("key=%p (%s)", *r_key, + return TRACE_SUC ("key=%p (%s)", *r_key, ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ? (*r_key)->subkeys->fpr : "invalid"); } /* Terminate a pending keylist operation within CTX. */ gpgme_error_t gpgme_op_keylist_end (gpgme_ctx_t ctx) { - TRACE (DEBUG_CTX, "gpgme_op_keylist_end", ctx); + TRACE (DEBUG_CTX, "gpgme_op_keylist_end", ctx, ""); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); return 0; } /* Get the key with the fingerprint FPR from the crypto backend. If SECRET is true, get the secret key. */ gpgme_error_t gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key, int secret) { gpgme_ctx_t listctx; gpgme_error_t err; gpgme_key_t result, key; - TRACE_BEG2 (DEBUG_CTX, "gpgme_get_key", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_get_key", ctx, "fpr=%s, secret=%i", fpr, secret); if (r_key) *r_key = NULL; if (!ctx || !r_key || !fpr) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (strlen (fpr) < 8) /* We have at least a key ID. */ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); /* FIXME: We use our own context because we have to avoid the user's I/O callback handlers. */ err = gpgme_new (&listctx); if (err) return TRACE_ERR (err); { gpgme_protocol_t proto; gpgme_engine_info_t info; /* Clone the relevant state. */ proto = gpgme_get_protocol (ctx); gpgme_set_protocol (listctx, proto); gpgme_set_keylist_mode (listctx, gpgme_get_keylist_mode (ctx)); info = gpgme_ctx_get_engine_info (ctx); while (info && info->protocol != proto) info = info->next; if (info) gpgme_ctx_set_engine_info (listctx, proto, info->file_name, info->home_dir); } err = gpgme_op_keylist_start (listctx, fpr, secret); if (!err) err = gpgme_op_keylist_next (listctx, &result); if (!err) { try_next_key: err = gpgme_op_keylist_next (listctx, &key); if (gpgme_err_code (err) == GPG_ERR_EOF) err = 0; else { if (!err && result && result->subkeys && result->subkeys->fpr && key && key->subkeys && key->subkeys->fpr && !strcmp (result->subkeys->fpr, key->subkeys->fpr)) { /* The fingerprint is identical. We assume that this is the same key and don't mark it as an ambiguous. This problem may occur with corrupted keyrings and has been noticed often with gpgsm. In fact gpgsm uses a similar hack to sort out such duplicates but it can't do that while listing keys. */ gpgme_key_unref (key); goto try_next_key; } if (!err) { gpgme_key_unref (key); err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } gpgme_key_unref (result); result = NULL; } } gpgme_release (listctx); if (! err) { *r_key = result; - TRACE_LOG2 ("key=%p (%s)", *r_key, + TRACE_LOG ("key=%p (%s)", *r_key, ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ? (*r_key)->subkeys->fpr : "invalid"); } return TRACE_ERR (err); } diff --git a/src/keysign.c b/src/keysign.c index 5cb47881..44ab5ff2 100644 --- a/src/keysign.c +++ b/src/keysign.c @@ -1,219 +1,219 @@ /* keysign.c - OpenPGP key signing * Copyright (C) 2016 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The error code from certain ERROR status lines or 0. */ gpg_error_t error_code; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; (void)opd; } /* Parse an error status line. Return the error location and the error code. The function may modify ARGS. */ static char * parse_error (char *args, gpg_error_t *r_err) { char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else { *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE); return NULL; } *r_err = atoi (which); return where; } static gpgme_error_t keysign_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; char *loc; /* Pipe the status code through the progress status handler. */ err = _gpgme_progress_status_handler (ctx, code, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_KEYSIGN, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_ERROR: loc = parse_error (args, &err); if (!loc) return err; if (!opd->error_code) opd->error_code = err; break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: if (opd->error_code) return opd->error_code; else if (opd->failure_code) return opd->failure_code; break; case GPGME_STATUS_INQUIRE_MAXLEN: if (ctx->status_cb && !ctx->full_status) { err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args); if (err) return err; } break; default: break; } return 0; } /* Sign the USERID of KEY using the current set of signers. If USERID * is NULL, sign all user ids. To put several user ids into USERID, * separate them by LF and set the flag GPGME_KEYSIGN_LFSEP. */ static gpgme_error_t keysign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, const char *userid, unsigned long expires, unsigned int flags) { gpgme_error_t err; void *hook; op_data_t opd; if (ctx->protocol != GPGME_PROTOCOL_OPENPGP) return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (!key) return gpg_error (GPG_ERR_INV_ARG); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYSIGN, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, keysign_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_keysign (ctx->engine, key, userid, expires, flags, ctx); } /* Sign the USERID of KEY using the current set of signers. */ gpgme_error_t gpgme_op_keysign_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned long expires, unsigned int flags) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_keysign_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_keysign_start", ctx, "key=%p, uid='%s' flags=0x%x", key, userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = keysign_start (ctx, 0, key, userid, expires, flags); return TRACE_ERR (err); } gpgme_error_t gpgme_op_keysign (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid, unsigned long expires, unsigned int flags) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_keysign", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_keysign", ctx, "key=%p, uid='%s' flags=0x%x", key, userid, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG)); err = keysign_start (ctx, 1, key, userid, expires, flags); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } diff --git a/src/opassuan.c b/src/opassuan.c index c50ae557..bfb06869 100644 --- a/src/opassuan.c +++ b/src/opassuan.c @@ -1,231 +1,231 @@ /* opassuan.c - Low-level Assuan operations. * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif /* Suppress warning for accessing deprecated member "err". */ #define _GPGME_IN_GPGME 1 #include "gpgme.h" #include "context.h" #include "ops.h" #include "util.h" #include "debug.h" /* LEGACY: Remove this when removing the deprecated result structure. */ typedef struct { struct _gpgme_op_assuan_result result; } *op_data_t; static gpgme_error_t opassuan_start (gpgme_ctx_t ctx, int synchronous, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpgme_error_t err; if (!command || !*command) return gpg_error (GPG_ERR_INV_VALUE); /* The flag value 256 is used to suppress an engine reset. This is required to keep the connection running. */ err = _gpgme_op_reset (ctx, ((synchronous&255) | 256)); if (err) return err; { /* LEGACY: Remove this when removing the deprecated result structure. */ void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, sizeof (*opd), NULL); if (err) return err; } return _gpgme_engine_op_assuan_transact (ctx->engine, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } /* XXXX. This is the asynchronous variant. */ gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpg_error_t err; - TRACE_BEG7 (DEBUG_CTX, "gpgme_op_assuan_transact_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_assuan_transact_start", ctx, "command=%s, data_cb=%p/%p, inq_cb=%p/%p, status_cb=%p/%p", command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = opassuan_start (ctx, 0, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); return TRACE_ERR (err); } /* XXXX. This is the synchronous variant. */ gpgme_error_t gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value, gpgme_error_t *op_err_p) { gpgme_error_t err; gpgme_error_t op_err; - TRACE_BEG8 (DEBUG_CTX, "gpgme_op_assuan_transact", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_assuan_transact", ctx, "command=%s, data_cb=%p/%p, inq_cb=%p/%p, status_cb=%p/%p, " "op_err=%p", command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value, op_err_p); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = opassuan_start (ctx, 1, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); if (err) goto out; err = _gpgme_wait_one_ext (ctx, &op_err); if (op_err) { - TRACE_LOG2 ("op_err = %s <%s>", gpgme_strerror (op_err), + TRACE_LOG ("op_err = %s <%s>", gpgme_strerror (op_err), gpgme_strsource (op_err)); if (! op_err_p) { TRACE_LOG ("warning: operational error ignored by user"); } } if (op_err_p) *op_err_p = op_err; out: return TRACE_ERR (err); } /* Compatibility code for old interface. */ /* Evil hack breaking abstractions for the purpose of localizing our other hack. This is copied from engine.c. */ struct engine { struct engine_ops *ops; void *engine; }; gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine); gpgme_assuan_result_t gpgme_op_assuan_result (gpgme_ctx_t ctx) { gpgme_error_t err; void *hook; op_data_t opd; - TRACE_BEG (DEBUG_CTX, "gpgme_op_assuan_result", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_assuan_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, -1, NULL); opd = hook; /* Check in case this function is used without having run a command before. */ if (err || !opd) { - TRACE_SUC0 ("result=(null)"); + TRACE_SUC ("result=(null)"); return NULL; } /* All of this is a hack for the old style interface. The new style interface returns op errors directly. */ opd->result.err = _gpgme_engine_assuan_last_op_err (ctx->engine->engine); if (opd->result.err) { - TRACE_LOG1 ("err = %s", gpg_strerror (0)); + TRACE_LOG ("err = %s", gpg_strerror (0)); } else { - TRACE_LOG2 ("err = %s <%s>", gpg_strerror (opd->result.err), + TRACE_LOG ("err = %s <%s>", gpg_strerror (opd->result.err), gpg_strsource (opd->result.err)); } - TRACE_SUC1 ("result=%p", &opd->result); + TRACE_SUC ("result=%p", &opd->result); return &opd->result; } gpgme_error_t gpgme_op_assuan_transact (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpgme_error_t err; - TRACE (DEBUG_CTX, "gpgme_op_assuan_transact", ctx); + TRACE (DEBUG_CTX, "gpgme_op_assuan_transact", ctx, ""); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); /* Users of the old-style session based interfaces need to look at the result structure. */ err = gpgme_op_assuan_transact_ext (ctx, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value, NULL); return err; } diff --git a/src/passwd.c b/src/passwd.c index 154ae287..0ba8c08d 100644 --- a/src/passwd.c +++ b/src/passwd.c @@ -1,205 +1,205 @@ /* passwd.c - Passphrase changing function * Copyright (C) 2010 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" typedef struct { /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; int success_seen; int error_seen; } *op_data_t; /* Parse an error status line and return the error code. */ static gpgme_error_t parse_error (char *args) { gpgme_error_t err; char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else return trace_gpg_error (GPG_ERR_INV_ENGINE); err = atoi (which); if (!strcmp (where, "keyedit.passwd")) return err; return 0; } static gpgme_error_t passwd_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_PASSWD, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_ERROR: err = parse_error (args); if (err) opd->error_seen = 1; break; case GPGME_STATUS_SUCCESS: opd->success_seen = 1; break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: /* In case the OpenPGP engine does not properly implement the passwd command we won't get a success status back and thus we conclude that this operation is not supported. This is for example the case for GnuPG < 2.0.16. Note that this test is obsolete for assuan based engines because they will properly return an error for an unknown command. */ if (ctx->protocol == GPGME_PROTOCOL_OpenPGP && !opd->error_seen && !opd->success_seen) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else if (opd->failure_code) err = opd->failure_code; break; default: break; } return err; } static gpgme_error_t passwd_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, unsigned int flags) { gpgme_error_t err; void *hook; op_data_t opd; if (!key) return gpg_error (GPG_ERR_INV_VALUE); if (flags) return gpg_error (GPG_ERR_INV_FLAG); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_PASSWD, &hook, sizeof (*opd), NULL); opd = hook; if (err) return err; opd->success_seen = 0; opd->error_seen = 0; _gpgme_engine_set_status_handler (ctx->engine, passwd_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } return _gpgme_engine_op_passwd (ctx->engine, key, flags); } /* Change the passphrase for KEY. FLAGS is reserved for future use and must be passed as 0. The engine is expected to present a user interface to enter the old and the new passphrase. This is the asynchronous variant. Note that if ever the need arises to supply a passphrase we can do this with a flag value and the passphrase callback feature. */ gpgme_error_t gpgme_op_passwd_start (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags) { gpg_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_passwd_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_passwd_start", ctx, "key=%p, flags=0x%x", key, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = passwd_start (ctx, 0, key, flags); return TRACE_ERR (err); } /* Change the passphrase for KEY. FLAGS is reserved for future use and must be passed as 0. This is the synchronous variant. */ gpgme_error_t gpgme_op_passwd (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_passwd", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_passwd", ctx, "key=%p, flags=0x%x", key, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = passwd_start (ctx, 1, key, flags); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } diff --git a/src/posix-io.c b/src/posix-io.c index 67c6e392..78f523b1 100644 --- a/src/posix-io.c +++ b/src/posix-io.c @@ -1,876 +1,876 @@ /* posix-io.c - Posix I/O functions * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2004, 2005, 2007, 2010 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_STDINT_H # include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_SYS_UIO_H # include #endif #include #include #ifdef USE_LINUX_GETDENTS # include # include # include #endif /*USE_LINUX_GETDENTS*/ #include "util.h" #include "priv-io.h" #include "sema.h" #include "ath.h" #include "debug.h" void _gpgme_io_subsystem_init (void) { struct sigaction act; sigaction (SIGPIPE, NULL, &act); if (act.sa_handler == SIG_DFL) { act.sa_handler = SIG_IGN; sigemptyset (&act.sa_mask); act.sa_flags = 0; sigaction (SIGPIPE, &act, NULL); } } /* Write the printable version of FD to the buffer BUF of length BUFLEN. The printable version is the representation on the command line that the child process expects. */ int _gpgme_io_fd2str (char *buf, int buflen, int fd) { return snprintf (buf, buflen, "%d", fd); } /* The table to hold notification handlers. We use a linear search and extend the table as needed. */ struct notify_table_item_s { int fd; /* -1 indicates an unused entry. */ _gpgme_close_notify_handler_t handler; void *value; }; typedef struct notify_table_item_s *notify_table_item_t; static notify_table_item_t notify_table; static size_t notify_table_size; DEFINE_STATIC_LOCK (notify_table_lock); int _gpgme_io_read (int fd, void *buffer, size_t count) { int nread; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_read", fd, "buffer=%p, count=%u", buffer, count); do { nread = _gpgme_ath_read (fd, buffer, count); } while (nread == -1 && errno == EINTR); TRACE_LOGBUFX (buffer, nread); return TRACE_SYSRES (nread); } int _gpgme_io_write (int fd, const void *buffer, size_t count) { int nwritten; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_write", fd, "buffer=%p, count=%u", buffer, count); TRACE_LOGBUFX (buffer, count); do { nwritten = _gpgme_ath_write (fd, buffer, count); } while (nwritten == -1 && errno == EINTR); return TRACE_SYSRES (nwritten); } int _gpgme_io_pipe (int filedes[2], int inherit_idx) { int saved_errno; int err; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, "inherit_idx=%i (GPGME uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); err = pipe (filedes); if (err < 0) return TRACE_SYSRES (err); /* FIXME: Should get the old flags first. */ err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC); saved_errno = errno; if (err < 0) { close (filedes[0]); close (filedes[1]); } errno = saved_errno; if (err) return TRACE_SYSRES (err); - return TRACE_SUC2 ("read=0x%x, write=0x%x", filedes[0], filedes[1]); + return TRACE_SUC ("read=0x%x, write=0x%x", filedes[0], filedes[1]); } int _gpgme_io_close (int fd) { int res; _gpgme_close_notify_handler_t handler = NULL; void *handler_value; int idx; - TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd); + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd, ""); if (fd == -1) { errno = EINVAL; return TRACE_SYSRES (-1); } /* First call the notify handler. */ LOCK (notify_table_lock); for (idx=0; idx < notify_table_size; idx++) { if (notify_table[idx].fd == fd) { handler = notify_table[idx].handler; handler_value = notify_table[idx].value; notify_table[idx].handler = NULL; notify_table[idx].value = NULL; notify_table[idx].fd = -1; /* Mark slot as free. */ break; } } UNLOCK (notify_table_lock); if (handler) { - TRACE_LOG2 ("invoking close handler %p/%p", handler, handler_value); + TRACE_LOG ("invoking close handler %p/%p", handler, handler_value); handler (fd, handler_value); } /* Then do the close. */ res = close (fd); return TRACE_SYSRES (res); } int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler, void *value) { int res = 0; int idx; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, "close_handler=%p/%p", handler, value); assert (fd != -1); LOCK (notify_table_lock); for (idx=0; idx < notify_table_size; idx++) if (notify_table[idx].fd == -1) break; if (idx == notify_table_size) { /* We need to increase the size of the table. The approach we take is straightforward to minimize the risk of bugs. */ notify_table_item_t newtbl; size_t newsize = notify_table_size + 64; newtbl = calloc (newsize, sizeof *newtbl); if (!newtbl) { res = -1; goto leave; } for (idx=0; idx < notify_table_size; idx++) newtbl[idx] = notify_table[idx]; for (; idx < newsize; idx++) { newtbl[idx].fd = -1; newtbl[idx].handler = NULL; newtbl[idx].value = NULL; } free (notify_table); notify_table = newtbl; idx = notify_table_size; notify_table_size = newsize; } notify_table[idx].fd = fd; notify_table[idx].handler = handler; notify_table[idx].value = value; leave: UNLOCK (notify_table_lock); return TRACE_SYSRES (res); } int _gpgme_io_set_nonblocking (int fd) { int flags; int res; - TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd); + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd, ""); flags = fcntl (fd, F_GETFL, 0); if (flags == -1) return TRACE_SYSRES (-1); flags |= O_NONBLOCK; res = fcntl (fd, F_SETFL, flags); return TRACE_SYSRES (res); } #ifdef USE_LINUX_GETDENTS /* This is not declared in public headers; getdents64(2) says that we must * define it ourselves. */ struct linux_dirent64 { ino64_t d_ino; off64_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[]; }; # define DIR_BUF_SIZE 1024 #endif /*USE_LINUX_GETDENTS*/ static long int get_max_fds (void) { const char *source = NULL; long int fds = -1; int rc; /* Under Linux we can figure out the highest used file descriptor by * reading /proc/self/fd. This is in the common cases much faster * than for example doing 4096 close calls where almost all of them * will fail. * * We can't use the normal opendir/readdir/closedir interface between * fork and exec in a multi-threaded process because opendir uses * malloc and thus a mutex which may deadlock with a malloc in another * thread. However, the underlying getdents system call is safe. */ #ifdef USE_LINUX_GETDENTS { int dir_fd; char dir_buf[DIR_BUF_SIZE]; struct linux_dirent64 *dir_entry; int r, pos; const char *s; int x; dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY); if (dir_fd != -1) { for (;;) { r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE); if (r == -1) { /* Fall back to other methods. */ fds = -1; break; } if (r == 0) break; for (pos = 0; pos < r; pos += dir_entry->d_reclen) { dir_entry = (struct linux_dirent64 *) (dir_buf + pos); s = dir_entry->d_name; if (*s < '0' || *s > '9') continue; /* atoi is not guaranteed to be async-signal-safe. */ for (x = 0; *s >= '0' && *s <= '9'; s++) x = x * 10 + (*s - '0'); if (!*s && x > fds && x != dir_fd) fds = x; } } close (dir_fd); } if (fds != -1) { fds++; source = "/proc"; } } #endif /*USE_LINUX_GETDENTS*/ #ifdef RLIMIT_NOFILE if (fds == -1) { struct rlimit rl; rc = getrlimit (RLIMIT_NOFILE, &rl); if (rc == 0) { source = "RLIMIT_NOFILE"; fds = rl.rlim_max; } } #endif #ifdef RLIMIT_OFILE if (fds == -1) { struct rlimit rl; rc = getrlimit (RLIMIT_OFILE, &rl); if (rc == 0) { source = "RLIMIT_OFILE"; fds = rl.rlim_max; } } #endif #ifdef _SC_OPEN_MAX if (fds == -1) { long int scres; scres = sysconf (_SC_OPEN_MAX); if (scres >= 0) { source = "_SC_OPEN_MAX"; return scres; } } #endif #ifdef OPEN_MAX if (fds == -1) { source = "OPEN_MAX"; fds = OPEN_MAX; } #endif #if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \ && !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX) #warning "No known way to get the maximum number of file descriptors." #endif if (fds == -1) { source = "arbitrary"; /* Arbitrary limit. */ fds = 1024; } /* AIX returns INT32_MAX instead of a proper value. We assume that * this is always an error and use a more reasonable limit. */ #ifdef INT32_MAX if (fds == INT32_MAX) { source = "aix-fix"; fds = 1024; } #endif - TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", 0, "max fds=%i (%s)", fds, source); + TRACE (DEBUG_SYSIO, "gpgme:max_fds", 0, "max fds=%i (%s)", fds, source); return fds; } int _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal) { int status; pid_t ret; *r_status = 0; *r_signal = 0; do ret = _gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG); while (ret == (pid_t)(-1) && errno == EINTR); if (ret == pid) { if (WIFSIGNALED (status)) { *r_status = 4; /* Need some value here. */ *r_signal = WTERMSIG (status); } else if (WIFEXITED (status)) *r_status = WEXITSTATUS (status); else *r_status = 4; /* Oops. */ return 1; } return 0; } /* Returns 0 on success, -1 on error. */ int _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, struct spawn_fd_item_s *fd_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, pid_t *r_pid) { pid_t pid; int i; int status; int signo; - TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_spawn", path, "path=%s", path); i = 0; while (argv[i]) { - TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]); + TRACE_LOG ("argv[%2i] = %s", i, argv[i]); i++; } for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].dup_to == -1) - TRACE_LOG2 ("fd[%i] = 0x%x", i, fd_list[i].fd); + TRACE_LOG ("fd[%i] = 0x%x", i, fd_list[i].fd); else - TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to); + TRACE_LOG ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to); pid = fork (); if (pid == -1) return TRACE_SYSRES (-1); if (!pid) { /* Intermediate child to prevent zombie processes. */ if ((pid = fork ()) == 0) { /* Child. */ int max_fds = -1; int fd; int seen_stdin = 0; int seen_stdout = 0; int seen_stderr = 0; if (atfork) atfork (atforkvalue, 0); /* First close all fds which will not be inherited. If we * have closefrom(2) we first figure out the highest fd we * do not want to close, then call closefrom, and on success * use the regular code to close all fds up to the start * point of closefrom. Note that Solaris' and FreeBSD's closefrom do * not return errors. */ #ifdef HAVE_CLOSEFROM { fd = -1; for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].fd > fd) fd = fd_list[i].fd; fd++; #if defined(__sun) || defined(__FreeBSD__) closefrom (fd); max_fds = fd; #else /*!__sun */ while ((i = closefrom (fd)) && errno == EINTR) ; if (!i || errno == EBADF) max_fds = fd; #endif /*!__sun*/ } #endif /*HAVE_CLOSEFROM*/ if (max_fds == -1) max_fds = get_max_fds (); for (fd = 0; fd < max_fds; fd++) { for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].fd == fd) break; if (fd_list[i].fd == -1) close (fd); } /* And now dup and close those to be duplicated. */ for (i = 0; fd_list[i].fd != -1; i++) { int child_fd; int res; if (fd_list[i].dup_to != -1) child_fd = fd_list[i].dup_to; else child_fd = fd_list[i].fd; if (child_fd == 0) seen_stdin = 1; else if (child_fd == 1) seen_stdout = 1; else if (child_fd == 2) seen_stderr = 1; if (fd_list[i].dup_to == -1) continue; res = dup2 (fd_list[i].fd, fd_list[i].dup_to); if (res < 0) { #if 0 /* FIXME: The debug file descriptor is not dup'ed anyway, so we can't see this. */ - TRACE_LOG1 ("dup2 failed in child: %s\n", + TRACE_LOG ("dup2 failed in child: %s\n", strerror (errno)); #endif _exit (8); } close (fd_list[i].fd); } if (! seen_stdin || ! seen_stdout || !seen_stderr) { fd = open ("/dev/null", O_RDWR); if (fd == -1) { /* The debug file descriptor is not dup'ed, so we can't do a trace output. */ _exit (8); } /* Make sure that the process has connected stdin. */ if (! seen_stdin && fd != 0) { if (dup2 (fd, 0) == -1) _exit (8); } if (! seen_stdout && fd != 1) { if (dup2 (fd, 1) == -1) _exit (8); } if (! seen_stderr && fd != 2) { if (dup2 (fd, 2) == -1) _exit (8); } if (fd != 0 && fd != 1 && fd != 2) close (fd); } execv (path, (char *const *) argv); /* Hmm: in that case we could write a special status code to the status-pipe. */ _exit (8); /* End child. */ } if (pid == -1) _exit (1); else _exit (0); } - TRACE_LOG1 ("waiting for child process pid=%i", pid); + TRACE_LOG ("waiting for child process pid=%i", pid); _gpgme_io_waitpid (pid, 1, &status, &signo); if (status) return TRACE_SYSRES (-1); for (i = 0; fd_list[i].fd != -1; i++) { if (! (flags & IOSPAWN_FLAG_NOCLOSE)) _gpgme_io_close (fd_list[i].fd); /* No handle translation. */ fd_list[i].peer_name = fd_list[i].fd; } if (r_pid) *r_pid = pid; return TRACE_SYSRES (0); } /* Select on the list of fds. Returns: -1 = error, 0 = timeout or nothing to select, > 0 = number of signaled fds. */ int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) { fd_set readfds; fd_set writefds; unsigned int i; int any; int max_fd; int n; int count; /* Use a 1s timeout. */ struct timeval timeout = { 1, 0 }; void *dbg_help = NULL; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", fds, "nfds=%u, nonblock=%u", nfds, nonblock); FD_ZERO (&readfds); FD_ZERO (&writefds); max_fd = 0; if (nonblock) timeout.tv_sec = 0; TRACE_SEQ (dbg_help, "select on [ "); any = 0; for (i = 0; i < nfds; i++) { if (fds[i].fd == -1) continue; if (fds[i].for_read) { if (fds[i].fd >= FD_SETSIZE) { TRACE_END (dbg_help, " -BAD- ]"); gpg_err_set_errno (EMFILE); return TRACE_SYSRES (-1); } assert (!FD_ISSET (fds[i].fd, &readfds)); FD_SET (fds[i].fd, &readfds); if (fds[i].fd > max_fd) max_fd = fds[i].fd; TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd); any = 1; } else if (fds[i].for_write) { if (fds[i].fd >= FD_SETSIZE) { TRACE_END (dbg_help, " -BAD- ]"); gpg_err_set_errno (EMFILE); return TRACE_SYSRES (-1); } assert (!FD_ISSET (fds[i].fd, &writefds)); FD_SET (fds[i].fd, &writefds); if (fds[i].fd > max_fd) max_fd = fds[i].fd; TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd); any = 1; } fds[i].signaled = 0; } TRACE_END (dbg_help, "]"); if (!any) return TRACE_SYSRES (0); do { count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL, &timeout); } while (count < 0 && errno == EINTR); if (count < 0) return TRACE_SYSRES (-1); TRACE_SEQ (dbg_help, "select OK [ "); if (TRACE_ENABLED (dbg_help)) { for (i = 0; i <= max_fd; i++) { if (FD_ISSET (i, &readfds)) TRACE_ADD1 (dbg_help, "r0x%x ", i); if (FD_ISSET (i, &writefds)) TRACE_ADD1 (dbg_help, "w0x%x ", i); } TRACE_END (dbg_help, "]"); } /* The variable N is used to optimize it a little bit. */ for (n = count, i = 0; i < nfds && n; i++) { if (fds[i].fd == -1) ; else if (fds[i].for_read) { if (FD_ISSET (fds[i].fd, &readfds)) { fds[i].signaled = 1; n--; } } else if (fds[i].for_write) { if (FD_ISSET (fds[i].fd, &writefds)) { fds[i].signaled = 1; n--; } } } return TRACE_SYSRES (count); } int _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags) { int nread; int saved_errno; struct iovec *iov; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd, "msg=%p, flags=%i", msg, flags); nread = 0; iov = msg->msg_iov; while (iov < msg->msg_iov + msg->msg_iovlen) { nread += iov->iov_len; iov++; } - TRACE_LOG1 ("about to receive %d bytes", nread); + TRACE_LOG ("about to receive %d bytes", nread); do { nread = _gpgme_ath_recvmsg (fd, msg, flags); } while (nread == -1 && errno == EINTR); saved_errno = errno; if (nread > 0) { int nr = nread; iov = msg->msg_iov; while (nr > 0) { int len = nr > iov->iov_len ? iov->iov_len : nr; TRACE_LOGBUFX (msg->msg_iov->iov_base, len); iov++; nr -= len; } } errno = saved_errno; return TRACE_SYSRES (nread); } int _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags) { int nwritten; struct iovec *iov; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd, "msg=%p, flags=%i", msg, flags); nwritten = 0; iov = msg->msg_iov; while (iov < msg->msg_iov + msg->msg_iovlen) { nwritten += iov->iov_len; iov++; } - TRACE_LOG1 ("about to receive %d bytes", nwritten); + TRACE_LOG ("about to receive %d bytes", nwritten); iov = msg->msg_iov; while (nwritten > 0) { int len = nwritten > iov->iov_len ? iov->iov_len : nwritten; TRACE_LOGBUFX (msg->msg_iov->iov_base, len); iov++; nwritten -= len; } do { nwritten = _gpgme_ath_sendmsg (fd, msg, flags); } while (nwritten == -1 && errno == EINTR); return TRACE_SYSRES (nwritten); } int _gpgme_io_dup (int fd) { int new_fd; do new_fd = dup (fd); while (new_fd == -1 && errno == EINTR); - TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd); + TRACE (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd); return new_fd; } int _gpgme_io_socket (int domain, int type, int proto) { int res; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_socket", domain, "type=%i, proto=%i", type, proto); res = socket (domain, type, proto); return TRACE_SYSRES (res); } int _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen) { int res; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_connect", fd, "addr=%p, addrlen=%i", addr, addrlen); do res = ath_connect (fd, addr, addrlen); while (res == -1 && errno == EINTR); return TRACE_SYSRES (res); } diff --git a/src/queryswdb.c b/src/queryswdb.c index 537de39d..e72dabbc 100644 --- a/src/queryswdb.c +++ b/src/queryswdb.c @@ -1,122 +1,122 @@ /* queryswdb.c - Access to the SWDB file * Copyright (C) 2016 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" typedef struct { struct _gpgme_op_query_swdb_result result; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_query_swdb_result_t result = &opd->result; assert (!result->next); free (result->name); free (result->iversion); free (result->version); } gpgme_query_swdb_result_t gpgme_op_query_swdb_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; - TRACE_BEG (DEBUG_CTX, "gpgme_op_query_swdb_result", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_query_swdb_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_QUERY_SWDB, &hook, -1, NULL); opd = hook; if (err || !opd) { - TRACE_SUC0 ("result=(null)"); + TRACE_SUC ("result=(null)"); return NULL; } - TRACE_SUC1 ("result=%p", &opd->result); + TRACE_SUC ("result=%p", &opd->result); return &opd->result; } /* Query the swdb for software package NAME and check against the * installed version given by IVERSION. If IVERSION is NULL a check * is only done if GPGME can figure out the version by itself * (e.g. for "gpgme" or "gnupg"). RESERVED should be 0. * * Note that we only implemented the synchronous variant of this * function but the API is prepared for an asynchronous variant. */ gpgme_error_t gpgme_op_query_swdb (gpgme_ctx_t ctx, const char *name, const char *iversion, unsigned int reserved) { gpgme_error_t err; void *hook; op_data_t opd; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_query_swdb", ctx, - "name=%s, iversion=%a", name, iversion); + TRACE_BEG (DEBUG_CTX, "gpgme_op_query_swdb", ctx, + "name=%s, iversion=%", name, iversion); if (!ctx || reserved) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (ctx->protocol != GPGME_PROTOCOL_GPGCONF) return TRACE_ERR (gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL)); if (!name) name = "gpgme"; if (!iversion && !strcmp (name, "gpgme")) iversion = VERSION; err = _gpgme_op_reset (ctx, 1); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_QUERY_SWDB, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return TRACE_ERR (err); err = _gpgme_engine_op_query_swdb (ctx->engine, name, iversion, &opd->result); return TRACE_ERR (err); } diff --git a/src/sign.c b/src/sign.c index 49eac44c..31081aea 100644 --- a/src/sign.c +++ b/src/sign.c @@ -1,500 +1,500 @@ /* sign.c - Signing function. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include /* Suppress warning for accessing deprecated member "class". */ #define _GPGME_IN_GPGME 1 #include "gpgme.h" #include "context.h" #include "ops.h" #include "util.h" #include "debug.h" typedef struct { struct _gpgme_op_sign_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The fingerprint from the last KEY_CONSIDERED status line. */ char *kc_fpr; /* The flags from the last KEY_CONSIDERED status line. */ unsigned int kc_flags; /* A pointer to the next pointer of the last invalid signer in the list. This makes appending new invalid signers painless while preserving the order. */ gpgme_invalid_key_t *last_signer_p; /* Likewise for signature information. */ gpgme_new_signature_t *last_sig_p; /* Flags used while processing the status lines. */ unsigned int ignore_inv_recp:1; unsigned int inv_sgnr_seen:1; unsigned int sig_created_seen:1; } *op_data_t; static void release_signatures (gpgme_new_signature_t sig) { while (sig) { gpgme_new_signature_t next = sig->next; free (sig->fpr); free (sig); sig = next; } } static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_invalid_key_t invalid_signer = opd->result.invalid_signers; while (invalid_signer) { gpgme_invalid_key_t next = invalid_signer->next; if (invalid_signer->fpr) free (invalid_signer->fpr); free (invalid_signer); invalid_signer = next; } release_signatures (opd->result.signatures); free (opd->kc_fpr); } gpgme_sign_result_t gpgme_op_sign_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; gpgme_invalid_key_t inv_key, key; gpgme_new_signature_t sig; unsigned int inv_signers = 0; unsigned int signatures = 0; - TRACE_BEG (DEBUG_CTX, "gpgme_op_sign_result", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_sign_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook, -1, NULL); opd = hook; if (err || !opd) { - TRACE_SUC0 ("result=(null)"); + TRACE_SUC ("result=(null)"); return NULL; } for (inv_key = opd->result.invalid_signers; inv_key; inv_key = inv_key->next) inv_signers++; for (sig = opd->result.signatures; sig; sig = sig->next) signatures++; if (gpgme_signers_count (ctx) && signatures + inv_signers != gpgme_signers_count (ctx)) { /* In this case at least one signatures was not created perhaps due to a bad passphrase etc. Thus the entire message is broken and should not be used. We add the already created signatures to the invalid signers list and thus this case can be detected. */ - TRACE_LOG3 ("result: invalid signers: %u, signatures: %u, count: %u", + TRACE_LOG ("result: invalid signers: %u, signatures: %u, count: %u", inv_signers, signatures, gpgme_signers_count (ctx)); for (sig = opd->result.signatures; sig; sig = sig->next) { key = calloc (1, sizeof *key); if (!key) { - TRACE_SUC0 ("out of core; result=(null)"); + TRACE_SUC ("out of core; result=(null)"); return NULL; } if (sig->fpr) { key->fpr = strdup (sig->fpr); if (!key->fpr) { free (key); - TRACE_SUC0 ("out of core; result=(null)"); + TRACE_SUC ("out of core; result=(null)"); return NULL; } } key->reason = GPG_ERR_GENERAL; inv_key = opd->result.invalid_signers; if (inv_key) { for (; inv_key->next; inv_key = inv_key->next) ; inv_key->next = key; } else opd->result.invalid_signers = key; } release_signatures (opd->result.signatures); opd->result.signatures = NULL; } if (_gpgme_debug_trace()) { - TRACE_LOG2 ("result: invalid signers: %i, signatures: %i", + TRACE_LOG ("result: invalid signers: %i, signatures: %i", inv_signers, signatures); for (inv_key=opd->result.invalid_signers; inv_key; inv_key=inv_key->next) { - TRACE_LOG3 ("result: invalid signer: fpr=%s, reason=%s <%s>", + TRACE_LOG ("result: invalid signer: fpr=%s, reason=%s <%s>", inv_key->fpr, gpgme_strerror (inv_key->reason), gpgme_strsource (inv_key->reason)); } for (sig = opd->result.signatures; sig; sig = sig->next) { - TRACE_LOG6 ("result: signature: type=%i, pubkey_algo=%i, " + TRACE_LOG ("result: signature: type=%i, pubkey_algo=%i, " "hash_algo=%i, timestamp=%li, fpr=%s, sig_class=%i", sig->type, sig->pubkey_algo, sig->hash_algo, sig->timestamp, sig->fpr, sig->sig_class); } } - TRACE_SUC1 ("result=%p", &opd->result); + TRACE_SUC ("result=%p", &opd->result); return &opd->result; } static gpgme_error_t parse_sig_created (char *args, gpgme_new_signature_t *sigp, gpgme_protocol_t protocol) { gpgme_new_signature_t sig; char *tail; sig = malloc (sizeof (*sig)); if (!sig) return gpg_error_from_syserror (); sig->next = NULL; switch (*args) { case 'S': sig->type = GPGME_SIG_MODE_NORMAL; break; case 'D': sig->type = GPGME_SIG_MODE_DETACH; break; case 'C': sig->type = GPGME_SIG_MODE_CLEAR; break; default: /* The backend engine is not behaving. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args++; if (*args != ' ') { free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } gpg_err_set_errno (0); sig->pubkey_algo = _gpgme_map_pk_algo (strtol (args, &tail, 0), protocol); if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args = tail; sig->hash_algo = strtol (args, &tail, 0); if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args = tail; sig->sig_class = strtol (args, &tail, 0); sig->class = sig->sig_class; sig->_obsolete_class = sig->sig_class; if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args = tail; sig->timestamp = _gpgme_parse_timestamp (args, &tail); if (sig->timestamp == -1 || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } args = tail; while (*args == ' ') args++; if (!*args) { /* The crypto backend does not behave. */ free (sig); return trace_gpg_error (GPG_ERR_INV_ENGINE); } tail = strchr (args, ' '); if (tail) *tail = '\0'; sig->fpr = strdup (args); if (!sig->fpr) { free (sig); return gpg_error_from_syserror (); } *sigp = sig; return 0; } gpgme_error_t _gpgme_sign_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_passphrase_status_handler (priv, code, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_SIG_CREATED: opd->sig_created_seen = 1; err = parse_sig_created (args, opd->last_sig_p, ctx->protocol); if (err) return err; opd->last_sig_p = &(*opd->last_sig_p)->next; break; case GPGME_STATUS_KEY_CONSIDERED: /* This is emitted during gpg's key lookup to give information * about the lookup results. We store the last one so it can be * used in connection with INV_RECP. */ free (opd->kc_fpr); opd->kc_fpr = NULL; err = _gpgme_parse_key_considered (args, &opd->kc_fpr, &opd->kc_flags); if (err) return err; break; case GPGME_STATUS_INV_RECP: if (opd->inv_sgnr_seen && opd->ignore_inv_recp) break; /* FALLTHROUGH */ case GPGME_STATUS_INV_SGNR: if (code == GPGME_STATUS_INV_SGNR) opd->inv_sgnr_seen = 1; free (opd->kc_fpr); opd->kc_fpr = NULL; err = _gpgme_parse_inv_recp (args, 1, opd->kc_fpr, opd->kc_flags, opd->last_signer_p); if (err) return err; opd->last_signer_p = &(*opd->last_signer_p)->next; free (opd->kc_fpr); opd->kc_fpr = NULL; break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: /* The UI server does not send information about the created signature. This is irrelevant for this protocol and thus we should not check for that. */ if (opd->result.invalid_signers) err = gpg_error (GPG_ERR_UNUSABLE_SECKEY); else if (!opd->sig_created_seen && ctx->protocol != GPGME_PROTOCOL_UISERVER) err = opd->failure_code? opd->failure_code:gpg_error (GPG_ERR_GENERAL); break; case GPGME_STATUS_INQUIRE_MAXLEN: if (ctx->status_cb && !ctx->full_status) err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args); break; default: break; } return err; } static gpgme_error_t sign_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_sign_status_handler (priv, code, args); return err; } static gpgme_error_t sign_init_result (gpgme_ctx_t ctx, int ignore_inv_recp) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->failure_code = 0; opd->last_signer_p = &opd->result.invalid_signers; opd->last_sig_p = &opd->result.signatures; opd->ignore_inv_recp = !!ignore_inv_recp; opd->inv_sgnr_seen = 0; opd->sig_created_seen = 0; return 0; } gpgme_error_t _gpgme_op_sign_init_result (gpgme_ctx_t ctx) { return sign_init_result (ctx, 0); } static gpgme_error_t sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode) { gpgme_error_t err; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; /* If we are using the CMS protocol, we ignore the INV_RECP status code if a newer GPGSM is in use. GPGMS does not support combined sign+encrypt and thus this can't harm. */ err = sign_init_result (ctx, (ctx->protocol == GPGME_PROTOCOL_CMS)); if (err) return err; if (mode != GPGME_SIG_MODE_NORMAL && mode != GPGME_SIG_MODE_DETACH && mode != GPGME_SIG_MODE_CLEAR) return gpg_error (GPG_ERR_INV_VALUE); if (!plain) return gpg_error (GPG_ERR_NO_DATA); if (!sig) return gpg_error (GPG_ERR_INV_VALUE); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, sign_status_handler, ctx); return _gpgme_engine_op_sign (ctx->engine, plain, sig, mode, ctx->use_armor, ctx->use_textmode, ctx->include_certs, ctx /* FIXME */); } /* Sign the plaintext PLAIN and store the signature in SIG. */ gpgme_error_t gpgme_op_sign_start (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode) { gpg_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_sign_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_sign_start", ctx, "plain=%p, sig=%p, mode=%i", plain, sig, mode); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = sign_start (ctx, 0, plain, sig, mode); return TRACE_ERR (err); } /* Sign the plaintext PLAIN and store the signature in SIG. */ gpgme_error_t gpgme_op_sign (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_sign", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_sign", ctx, "plain=%p, sig=%p, mode=%i", plain, sig, mode); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = sign_start (ctx, 1, plain, sig, mode); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } diff --git a/src/signers.c b/src/signers.c index fe2d6786..6a5ccd22 100644 --- a/src/signers.c +++ b/src/signers.c @@ -1,118 +1,118 @@ /* signers.c - Maintain signer sets. * Copyright (C) 2001 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "gpgme.h" #include "util.h" #include "context.h" #include "debug.h" /* Delete all signers from CTX. */ void _gpgme_signers_clear (gpgme_ctx_t ctx) { unsigned int i; if (!ctx || !ctx->signers) return; for (i = 0; i < ctx->signers_len; i++) { assert (ctx->signers[i]); gpgme_key_unref (ctx->signers[i]); ctx->signers[i] = NULL; } ctx->signers_len = 0; } void gpgme_signers_clear (gpgme_ctx_t ctx) { - TRACE (DEBUG_CTX, "gpgme_signers_clear", ctx); + TRACE (DEBUG_CTX, "gpgme_signers_clear", ctx, ""); _gpgme_signers_clear (ctx); } /* Add KEY to list of signers in CTX. */ gpgme_error_t gpgme_signers_add (gpgme_ctx_t ctx, const gpgme_key_t key) { - TRACE_BEG2 (DEBUG_CTX, "gpgme_signers_add", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_signers_add", ctx, "key=%p (%s)", key, (key && key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid"); if (!ctx || !key) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (ctx->signers_len == ctx->signers_size) { gpgme_key_t *newarr; int n = ctx->signers_size + 5; int j; newarr = realloc (ctx->signers, n * sizeof (*newarr)); if (!newarr) return TRACE_ERR (gpg_error_from_syserror ()); for (j = ctx->signers_size; j < n; j++) newarr[j] = NULL; ctx->signers = newarr; ctx->signers_size = n; } gpgme_key_ref (key); ctx->signers[ctx->signers_len++] = key; - return TRACE_SUC (); + return TRACE_SUC (""); } /* Return the number of signers in CTX. */ unsigned int gpgme_signers_count (const gpgme_ctx_t ctx) { return ctx? ctx->signers_len : 0; } /* Return the SEQth signer's key in CTX with one reference. */ gpgme_key_t gpgme_signers_enum (const gpgme_ctx_t ctx, int seq) { unsigned int seqno; if (!ctx || seq < 0) return NULL; seqno = (unsigned int) seq; if (seqno >= ctx->signers_len) return NULL; gpgme_key_ref (ctx->signers[seqno]); return ctx->signers[seqno]; } diff --git a/src/spawn.c b/src/spawn.c index a4d5e8de..c5431b14 100644 --- a/src/spawn.c +++ b/src/spawn.c @@ -1,106 +1,106 @@ /* spawn.c - Run an arbitrary command with callbacks. * Copyright (C) 2014 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "util.h" #include "ops.h" static gpgme_error_t spawn_start (gpgme_ctx_t ctx, int synchronous, const char *file, const char *argv[], gpgme_data_t datain, gpgme_data_t dataout, gpgme_data_t dataerr, unsigned int flags) { gpgme_error_t err; const char *tmp_argv[2]; if (ctx->protocol != GPGME_PROTOCOL_SPAWN) return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; if (!argv) { tmp_argv[0] = _gpgme_get_basename (file); tmp_argv[1] = NULL; argv = tmp_argv; } return _gpgme_engine_op_spawn (ctx->engine, file, argv, datain, dataout, dataerr, flags); } /* Run the command FILE with the arguments in ARGV. Connect stdin to DATAIN, stdout to DATAOUT, and STDERR to DATAERR. If one the data streams is NULL, connect to /dev/null instead. */ gpgme_error_t gpgme_op_spawn_start (gpgme_ctx_t ctx, const char *file, const char *argv[], gpgme_data_t datain, gpgme_data_t dataout, gpgme_data_t dataerr, unsigned int flags) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_spawn_start", ctx, "file=(%s) flaggs=%x", + TRACE_BEG (DEBUG_CTX, "gpgme_op_spawn_start", ctx, "file=(%s) flaggs=%x", file, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = spawn_start (ctx, 0, file, argv, datain, dataout, dataerr, flags); return err; } /* Run the command FILE with the arguments in ARGV. Connect stdin to DATAIN, stdout to DATAOUT, and STDERR to DATAERR. If one the data streams is NULL, connect to /dev/null instead. Synchronous variant. */ gpgme_error_t gpgme_op_spawn (gpgme_ctx_t ctx, const char *file, const char *argv[], gpgme_data_t datain, gpgme_data_t dataout, gpgme_data_t dataerr, unsigned int flags) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_spawn", ctx, "file=(%s) flags=%x", + TRACE_BEG (DEBUG_CTX, "gpgme_op_spawn", ctx, "file=(%s) flags=%x", file, flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = spawn_start (ctx, 1, file, argv, datain, dataout, dataerr, flags); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } diff --git a/src/tofupolicy.c b/src/tofupolicy.c index 3ac9b8a6..79593186 100644 --- a/src/tofupolicy.c +++ b/src/tofupolicy.c @@ -1,185 +1,185 @@ /* tofupolicy.c - Tofu policy helpers. * Copyright (C) 2016 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" typedef struct { /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The error code from an ERROR status line or 0. */ gpg_error_t error_code; } *op_data_t; /* Parse an error status line. Return the error location and the error code. The function may modify ARGS. */ static char * parse_error (char *args, gpg_error_t *r_err) { char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else { *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE); return NULL; } *r_err = atoi (which); return where; } static gpgme_error_t tofu_policy_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; char *loc; err = _gpgme_op_data_lookup (ctx, OPDATA_TOFU_POLICY, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_ERROR: loc = parse_error (args, &err); if (!loc) return err; if (!opd->error_code) opd->error_code = err; break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: if (opd->error_code) err = opd->error_code; else if (opd->failure_code) err = opd->failure_code; break; default: break; } return err; } /* Set the TOFU policy for KEY to POLICY. */ static gpgme_error_t tofu_policy_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key, gpgme_tofu_policy_t policy) { gpgme_error_t err; void *hook; op_data_t opd; if (ctx->protocol != GPGME_PROTOCOL_OPENPGP) return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (!key) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_TOFU_POLICY, &hook, sizeof (*opd), NULL); opd = hook; if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, tofu_policy_status_handler, ctx); return _gpgme_engine_op_tofu_policy (ctx->engine, key, policy); } /* Set the TOFU policy of KEY to POLCIY. This is the asynchronous * variant. */ gpgme_error_t gpgme_op_tofu_policy_start (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_tofu_policy_t policy) { gpg_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_tofu_policy_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_tofu_policy_start", ctx, "key=%p, policy=%u", key, (unsigned int)policy); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = tofu_policy_start (ctx, 0, key, policy); return TRACE_ERR (err); } /* This is the synchronous variant of gpgme_op_tofu_policy_start. */ gpgme_error_t gpgme_op_tofu_policy (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_tofu_policy_t policy) { gpgme_error_t err; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_tofu_policy", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_tofu_policy", ctx, "key=%p, policy=%u", key, (unsigned int)policy); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = tofu_policy_start (ctx, 1, key, policy); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } diff --git a/src/trustlist.c b/src/trustlist.c index 69f565a0..c7778379 100644 --- a/src/trustlist.c +++ b/src/trustlist.c @@ -1,279 +1,279 @@ /* trustlist.c - Trust item listing. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "gpgme.h" #include "debug.h" #include "util.h" #include "context.h" #include "ops.h" struct trust_queue_item_s { struct trust_queue_item_s *next; gpgme_trust_item_t item; }; typedef struct { /* Something new is available. */ int trust_cond; struct trust_queue_item_s *trust_queue; } *op_data_t; static gpgme_error_t trustlist_status_handler (void *priv, gpgme_status_code_t code, char *args) { (void)priv; (void)code; (void)args; return 0; } /* This handler is used to parse the output of --list-trust-path: Format: level:keyid:type:recno:ot:val:mc:cc:name: With TYPE = U for a user ID K for a key The RECNO is either the one of the dir record or the one of the uid record. OT is the the usual trust letter and only available on K lines. VAL is the calculated validity MC is the marginal trust counter and only available on U lines CC is the same for the complete count NAME ist the username and only printed on U lines. */ static gpgme_error_t trustlist_colon_handler (void *priv, char *line) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; char *p, *pend; int field = 0; gpgme_trust_item_t item = NULL; if (!line) return 0; /* EOF */ for (p = line; p; p = pend) { field++; pend = strchr (p, ':'); if (pend) *pend++ = 0; switch (field) { case 1: /* level */ err = _gpgme_trust_item_new (&item); if (err) return err; item->level = atoi (p); break; case 2: /* long keyid */ if (strlen (p) == DIM(item->keyid) - 1) strcpy (item->keyid, p); break; case 3: /* type */ item->type = *p == 'K'? 1 : *p == 'U'? 2 : 0; break; case 5: /* owner trust */ item->_owner_trust[0] = *p; break; case 6: /* validity */ item->_validity[0] = *p; break; case 9: /* user ID */ item->name = strdup (p); if (!item->name) { int saved_err = gpg_error_from_syserror (); gpgme_trust_item_unref (item); return saved_err; } break; } } if (item) _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_TRUSTITEM, item); return 0; } void _gpgme_op_trustlist_event_cb (void *data, gpgme_event_io_t type, void *type_data) { gpgme_ctx_t ctx = (gpgme_ctx_t) data; gpgme_error_t err; void *hook; op_data_t opd; gpgme_trust_item_t item = (gpgme_trust_item_t) type_data; struct trust_queue_item_s *q, *q2; assert (type == GPGME_EVENT_NEXT_TRUSTITEM); err = _gpgme_op_data_lookup (ctx, OPDATA_TRUSTLIST, &hook, -1, NULL); opd = hook; if (err) return; q = malloc (sizeof *q); if (!q) { gpgme_trust_item_unref (item); /* FIXME: GPGME_Out_Of_Core; */ return; } q->item = item; q->next = NULL; /* FIXME: Use a tail pointer */ q2 = opd->trust_queue; if (!q2) opd->trust_queue = q; else { while (q2->next) q2 = q2->next; q2->next = q; } /* FIXME: unlock queue */ opd->trust_cond = 1; } gpgme_error_t gpgme_op_trustlist_start (gpgme_ctx_t ctx, const char *pattern, int max_level) { gpgme_error_t err = 0; void *hook; op_data_t opd; - TRACE_BEG2 (DEBUG_CTX, "gpgme_op_trustlist_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_trustlist_start", ctx, "pattern=%s, max_level=%i", pattern, max_level); if (!ctx || !pattern || !*pattern) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_TRUSTLIST, &hook, sizeof (*opd), NULL); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, trustlist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, trustlist_colon_handler, ctx); if (err) return TRACE_ERR (err); err = _gpgme_engine_op_trustlist (ctx->engine, pattern); return TRACE_ERR (err); } gpgme_error_t gpgme_op_trustlist_next (gpgme_ctx_t ctx, gpgme_trust_item_t *r_item) { gpgme_error_t err; void *hook; op_data_t opd; struct trust_queue_item_s *q; - TRACE_BEG (DEBUG_CTX, "gpgme_op_trustlist_next", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_trustlist_next", ctx, ""); if (!ctx || !r_item) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); *r_item = NULL; if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_data_lookup (ctx, OPDATA_TRUSTLIST, &hook, -1, NULL); opd = hook; if (err) return TRACE_ERR (err); if (opd == NULL) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (!opd->trust_queue) { err = _gpgme_wait_on_condition (ctx, &opd->trust_cond, NULL); if (err) return TRACE_ERR (err); if (!opd->trust_cond) return TRACE_ERR (gpg_error (GPG_ERR_EOF)); opd->trust_cond = 0; assert (opd->trust_queue); } q = opd->trust_queue; opd->trust_queue = q->next; *r_item = q->item; free (q); if ((*r_item)->type == 1) { - TRACE_SUC5 ("trust_item=%p: %s: owner trust %s with level %i " + TRACE_SUC ("trust_item=%p: %s: owner trust %s with level %i " "and validity 0x%x", *r_item, (*r_item)->keyid, (*r_item)->owner_trust, (*r_item)->level, (*r_item)->validity); } else if ((*r_item)->type == 2) { - TRACE_SUC5 ("trust_item=%p: %s: UID %s with level %i " + TRACE_SUC ("trust_item=%p: %s: UID %s with level %i " "and validity 0x%x", *r_item, (*r_item)->keyid, (*r_item)->name, (*r_item)->level, (*r_item)->validity); } else { - TRACE_SUC5 ("trust_item=%p: %s: unknown type %i with level %i " + TRACE_SUC ("trust_item=%p: %s: unknown type %i with level %i " "and validity 0x%x", *r_item, (*r_item)->keyid, (*r_item)->type, (*r_item)->level, (*r_item)->validity); } return 0; } /* Terminate a pending trustlist operation within CTX. */ gpgme_error_t gpgme_op_trustlist_end (gpgme_ctx_t ctx) { - TRACE (DEBUG_CTX, "gpgme_op_trustlist_end", ctx); + TRACE (DEBUG_CTX, "gpgme_op_trustlist_end", ctx, ""); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); return 0; } diff --git a/src/verify.c b/src/verify.c index a80563a0..78e549ed 100644 --- a/src/verify.c +++ b/src/verify.c @@ -1,1394 +1,1394 @@ /* verify.c - Signature verification. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "gpgme.h" #include "debug.h" #include "util.h" #include "context.h" #include "ops.h" typedef struct { struct _gpgme_op_verify_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; gpgme_signature_t current_sig; int did_prepare_new_sig; int only_newsig_seen; int plaintext_seen; int conflict_user_seen; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_signature_t sig = opd->result.signatures; while (sig) { gpgme_signature_t next = sig->next; gpgme_sig_notation_t notation = sig->notations; while (notation) { gpgme_sig_notation_t next_nota = notation->next; _gpgme_sig_notation_free (notation); notation = next_nota; } if (sig->fpr) free (sig->fpr); if (sig->pka_address) free (sig->pka_address); if (sig->key) gpgme_key_unref (sig->key); free (sig); sig = next; } if (opd->result.file_name) free (opd->result.file_name); } gpgme_verify_result_t gpgme_op_verify_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; gpgme_signature_t sig; - TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx); + TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL); opd = hook; if (err || !opd) { - TRACE_SUC0 ("result=(null)"); + TRACE_SUC ("result=(null)"); return NULL; } /* It is possible that we saw a new signature only followed by an ERROR line for that. In particular a missing X.509 key triggers this. In this case it is surprising that the summary field has not been updated. We fix it here by explicitly looking for this case. The real fix would be to have GPGME emit ERRSIG. */ for (sig = opd->result.signatures; sig; sig = sig->next) { if (!sig->summary) { switch (gpg_err_code (sig->status)) { case GPG_ERR_KEY_EXPIRED: sig->summary |= GPGME_SIGSUM_KEY_EXPIRED; break; case GPG_ERR_NO_PUBKEY: sig->summary |= GPGME_SIGSUM_KEY_MISSING; break; default: break; } } } /* Now for some tracing stuff. */ if (_gpgme_debug_trace ()) { int i; for (sig = opd->result.signatures, i = 0; sig; sig = sig->next, i++) { - TRACE_LOG4 ("sig[%i] = fpr %s, summary 0x%x, status %s", + TRACE_LOG ("sig[%i] = fpr %s, summary 0x%x, status %s", i, sig->fpr, sig->summary, gpg_strerror (sig->status)); - TRACE_LOG6 ("sig[%i] = timestamps 0x%x/0x%x flags:%s%s%s", + TRACE_LOG ("sig[%i] = timestamps 0x%x/0x%x flags:%s%s%s", i, sig->timestamp, sig->exp_timestamp, sig->wrong_key_usage ? "wrong key usage" : "", sig->pka_trust == 1 ? "pka bad" : (sig->pka_trust == 2 ? "pka_okay" : "pka RFU"), sig->chain_model ? "chain model" : ""); - TRACE_LOG5 ("sig[%i] = validity 0x%x (%s), algos %s/%s", + TRACE_LOG ("sig[%i] = validity 0x%x (%s), algos %s/%s", i, sig->validity, gpg_strerror (sig->validity_reason), gpgme_pubkey_algo_name (sig->pubkey_algo), gpgme_hash_algo_name (sig->hash_algo)); if (sig->pka_address) { - TRACE_LOG2 ("sig[%i] = PKA address %s", i, sig->pka_address); + TRACE_LOG ("sig[%i] = PKA address %s", i, sig->pka_address); } if (sig->notations) { - TRACE_LOG1 ("sig[%i] = has notations (not shown)", i); + TRACE_LOG ("sig[%i] = has notations (not shown)", i); } } } - TRACE_SUC1 ("result=%p", &opd->result); + TRACE_SUC ("result=%p", &opd->result); return &opd->result; } /* Build a summary vector from RESULT. */ static void calc_sig_summary (gpgme_signature_t sig) { unsigned long sum = 0; /* Calculate the red/green flag. */ if (sig->validity == GPGME_VALIDITY_FULL || sig->validity == GPGME_VALIDITY_ULTIMATE) { if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED) sum |= GPGME_SIGSUM_GREEN; } else if (sig->validity == GPGME_VALIDITY_NEVER) { if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED) sum |= GPGME_SIGSUM_RED; } else if (gpg_err_code (sig->status) == GPG_ERR_BAD_SIGNATURE) sum |= GPGME_SIGSUM_RED; /* FIXME: handle the case when key and message are expired. */ switch (gpg_err_code (sig->status)) { case GPG_ERR_SIG_EXPIRED: sum |= GPGME_SIGSUM_SIG_EXPIRED; break; case GPG_ERR_KEY_EXPIRED: sum |= GPGME_SIGSUM_KEY_EXPIRED; break; case GPG_ERR_NO_PUBKEY: sum |= GPGME_SIGSUM_KEY_MISSING; break; case GPG_ERR_CERT_REVOKED: sum |= GPGME_SIGSUM_KEY_REVOKED; break; case GPG_ERR_BAD_SIGNATURE: case GPG_ERR_NO_ERROR: break; default: sum |= GPGME_SIGSUM_SYS_ERROR; break; } /* Now look at the certain reason codes. */ switch (gpg_err_code (sig->validity_reason)) { case GPG_ERR_CRL_TOO_OLD: if (sig->validity == GPGME_VALIDITY_UNKNOWN) sum |= GPGME_SIGSUM_CRL_TOO_OLD; break; case GPG_ERR_CERT_REVOKED: /* Note that this is a second way to set this flag. It may also have been set due to a sig->status of STATUS_REVKEYSIG from parse_new_sig. */ sum |= GPGME_SIGSUM_KEY_REVOKED; break; default: break; } /* Check other flags. */ if (sig->wrong_key_usage) sum |= GPGME_SIGSUM_BAD_POLICY; /* Set the valid flag when the signature is unquestionable valid. (The test is identical to if(sum == GPGME_SIGSUM_GREEN)). */ if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN)) sum |= GPGME_SIGSUM_VALID; sig->summary = sum; } static gpgme_error_t prepare_new_sig (op_data_t opd) { gpgme_signature_t sig; if (opd->only_newsig_seen && opd->current_sig) { /* We have only seen the NEWSIG status and nothing else - we better skip this signature therefore and reuse it for the next possible signature. */ sig = opd->current_sig; memset (sig, 0, sizeof *sig); assert (opd->result.signatures == sig); } else { sig = calloc (1, sizeof (*sig)); if (!sig) return gpg_error_from_syserror (); if (!opd->result.signatures) opd->result.signatures = sig; if (opd->current_sig) opd->current_sig->next = sig; opd->current_sig = sig; } opd->did_prepare_new_sig = 1; opd->only_newsig_seen = 0; return 0; } static gpgme_error_t parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args, gpgme_protocol_t protocol) { gpgme_signature_t sig; char *end = strchr (args, ' '); char *tail; int got_fpr = 0; if (end) { *end = '\0'; end++; } if (!opd->did_prepare_new_sig) { gpg_error_t err; err = prepare_new_sig (opd); if (err) return err; } assert (opd->did_prepare_new_sig); opd->did_prepare_new_sig = 0; assert (opd->current_sig); sig = opd->current_sig; /* FIXME: We should set the source of the state. */ switch (code) { case GPGME_STATUS_GOODSIG: sig->status = gpg_error (GPG_ERR_NO_ERROR); break; case GPGME_STATUS_EXPSIG: sig->status = gpg_error (GPG_ERR_SIG_EXPIRED); break; case GPGME_STATUS_EXPKEYSIG: sig->status = gpg_error (GPG_ERR_KEY_EXPIRED); break; case GPGME_STATUS_BADSIG: sig->status = gpg_error (GPG_ERR_BAD_SIGNATURE); break; case GPGME_STATUS_REVKEYSIG: sig->status = gpg_error (GPG_ERR_CERT_REVOKED); break; case GPGME_STATUS_ERRSIG: /* Parse the pubkey algo. */ if (!end) goto parse_err_sig_fail; gpg_err_set_errno (0); sig->pubkey_algo = _gpgme_map_pk_algo (strtol (end, &tail, 0), protocol); if (errno || end == tail || *tail != ' ') goto parse_err_sig_fail; end = tail; while (*end == ' ') end++; /* Parse the hash algo. */ if (!*end) goto parse_err_sig_fail; gpg_err_set_errno (0); sig->hash_algo = strtol (end, &tail, 0); if (errno || end == tail || *tail != ' ') goto parse_err_sig_fail; end = tail; while (*end == ' ') end++; /* Skip the sig class. */ end = strchr (end, ' '); if (!end) goto parse_err_sig_fail; while (*end == ' ') end++; /* Parse the timestamp. */ sig->timestamp = _gpgme_parse_timestamp (end, &tail); if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' ')) return trace_gpg_error (GPG_ERR_INV_ENGINE); end = tail; while (*end == ' ') end++; /* Parse the return code. */ if (!*end) goto parse_err_sig_fail; gpg_err_set_errno (0); sig->status = strtoul (end, &tail, 10); if (errno || end == tail || (*tail && *tail != ' ')) goto parse_err_sig_fail; if (!*tail) goto parse_err_sig_ok; end = tail; while (*end == ' ') end++; /* Parse the new fingerprint (from the ISSUER_FPR subpacket). */ if (!*end || (*end == '-' && (end[1] == ' ' || !end[1]))) goto parse_err_sig_ok; /* Okay (just trailing spaces). */ sig->fpr = strdup (end); if (!sig->fpr) return gpg_error_from_syserror (); got_fpr = 1; goto parse_err_sig_ok; parse_err_sig_fail: sig->status = gpg_error (GPG_ERR_GENERAL); parse_err_sig_ok: break; default: return gpg_error (GPG_ERR_GENERAL); } if (*args && !got_fpr) { sig->fpr = strdup (args); if (!sig->fpr) return gpg_error_from_syserror (); } return 0; } static gpgme_error_t parse_valid_sig (gpgme_signature_t sig, char *args, gpgme_protocol_t protocol) { char *end = strchr (args, ' '); if (end) { *end = '\0'; end++; } if (!*args) /* We require at least the fingerprint. */ return gpg_error (GPG_ERR_GENERAL); if (sig->fpr) free (sig->fpr); sig->fpr = strdup (args); if (!sig->fpr) return gpg_error_from_syserror (); /* Skip the creation date. */ end = strchr (end, ' '); if (end) { char *tail; sig->timestamp = _gpgme_parse_timestamp (end, &tail); if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' ')) return trace_gpg_error (GPG_ERR_INV_ENGINE); end = tail; sig->exp_timestamp = _gpgme_parse_timestamp (end, &tail); if (sig->exp_timestamp == -1 || end == tail || (*tail && *tail != ' ')) return trace_gpg_error (GPG_ERR_INV_ENGINE); end = tail; while (*end == ' ') end++; /* Skip the signature version. */ end = strchr (end, ' '); if (end) { while (*end == ' ') end++; /* Skip the reserved field. */ end = strchr (end, ' '); if (end) { /* Parse the pubkey algo. */ gpg_err_set_errno (0); sig->pubkey_algo = _gpgme_map_pk_algo (strtol (end, &tail, 0), protocol); if (errno || end == tail || *tail != ' ') return trace_gpg_error (GPG_ERR_INV_ENGINE); end = tail; while (*end == ' ') end++; if (*end) { /* Parse the hash algo. */ gpg_err_set_errno (0); sig->hash_algo = strtol (end, &tail, 0); if (errno || end == tail || *tail != ' ') return trace_gpg_error (GPG_ERR_INV_ENGINE); end = tail; } } } } return 0; } static gpgme_error_t parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args) { gpgme_error_t err; gpgme_sig_notation_t *lastp = &sig->notations; gpgme_sig_notation_t notation = sig->notations; char *p; if (code == GPGME_STATUS_NOTATION_NAME || code == GPGME_STATUS_POLICY_URL) { p = strchr (args, ' '); if (p) *p = '\0'; /* FIXME: We could keep a pointer to the last notation in the list. */ while (notation && notation->value) { lastp = ¬ation->next; notation = notation->next; } if (notation) /* There is another notation name without data for the previous one. The crypto backend misbehaves. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); err = _gpgme_sig_notation_create (¬ation, NULL, 0, NULL, 0, 0); if (err) return err; if (code == GPGME_STATUS_NOTATION_NAME) { err = _gpgme_decode_percent_string (args, ¬ation->name, 0, 0); if (err) { _gpgme_sig_notation_free (notation); return err; } notation->name_len = strlen (notation->name); /* Set default flags for use with older gpg versions which * do not emit a NOTATIONS_FLAG line. */ notation->flags = GPGME_SIG_NOTATION_HUMAN_READABLE; notation->human_readable = 1; } else { /* This is a policy URL. */ err = _gpgme_decode_percent_string (args, ¬ation->value, 0, 0); if (err) { _gpgme_sig_notation_free (notation); return err; } notation->value_len = strlen (notation->value); } *lastp = notation; } else if (code == GPGME_STATUS_NOTATION_FLAGS) { char *field[2]; while (notation && notation->next) { lastp = ¬ation->next; notation = notation->next; } if (!notation || !notation->name) { /* There are notation flags without a previous notation name. * The crypto backend misbehaves. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); } if (_gpgme_split_fields (args, field, DIM (field)) < 2) { /* Required args missing. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); } notation->flags = 0; if (atoi (field[0])) { notation->flags |= GPGME_SIG_NOTATION_CRITICAL; notation->critical = 1; } if (atoi (field[1])) { notation->flags |= GPGME_SIG_NOTATION_HUMAN_READABLE; notation->human_readable = 1; } } else if (code == GPGME_STATUS_NOTATION_DATA) { int len = strlen (args) + 1; char *dest; /* FIXME: We could keep a pointer to the last notation in the list. */ while (notation && notation->next) { lastp = ¬ation->next; notation = notation->next; } if (!notation || !notation->name) /* There is notation data without a previous notation name. The crypto backend misbehaves. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); if (!notation->value) { dest = notation->value = malloc (len); if (!dest) return gpg_error_from_syserror (); } else { int cur_len = strlen (notation->value); dest = realloc (notation->value, len + strlen (notation->value)); if (!dest) return gpg_error_from_syserror (); notation->value = dest; dest += cur_len; } err = _gpgme_decode_percent_string (args, &dest, len, 0); if (err) return err; notation->value_len += strlen (dest); } else return trace_gpg_error (GPG_ERR_INV_ENGINE); return 0; } static gpgme_error_t parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args) { char *end = strchr (args, ' '); if (end) *end = '\0'; switch (code) { case GPGME_STATUS_TRUST_UNDEFINED: default: sig->validity = GPGME_VALIDITY_UNKNOWN; break; case GPGME_STATUS_TRUST_NEVER: sig->validity = GPGME_VALIDITY_NEVER; break; case GPGME_STATUS_TRUST_MARGINAL: sig->validity = GPGME_VALIDITY_MARGINAL; break; case GPGME_STATUS_TRUST_FULLY: case GPGME_STATUS_TRUST_ULTIMATE: sig->validity = GPGME_VALIDITY_FULL; break; } sig->validity_reason = 0; sig->chain_model = 0; if (*args) { sig->validity_reason = atoi (args); while (*args && *args != ' ') args++; if (*args) { while (*args == ' ') args++; if (!strncmp (args, "chain", 2) && (args[2] == ' ' || !args[2])) sig->chain_model = 1; } } return 0; } /* Parse a TOFU_USER line and put the info into SIG. */ static gpgme_error_t parse_tofu_user (gpgme_signature_t sig, char *args, gpgme_protocol_t protocol) { gpg_error_t err; char *tail; gpgme_user_id_t uid; gpgme_tofu_info_t ti; char *fpr = NULL; char *address = NULL; tail = strchr (args, ' '); if (!tail || tail == args) { err = trace_gpg_error (GPG_ERR_INV_ENGINE); /* No fingerprint. */ goto leave; } *tail++ = 0; fpr = strdup (args); if (!fpr) { err = gpg_error_from_syserror (); goto leave; } if (sig->key && sig->key->fpr && strcmp (sig->key->fpr, fpr)) { /* GnuPG since 2.1.17 emits multiple TOFU_USER lines with different fingerprints in case of conflicts for a signature. */ err = gpg_error (GPG_ERR_DUP_VALUE); goto leave; } args = tail; tail = strchr (args, ' '); if (tail == args) { err = trace_gpg_error (GPG_ERR_INV_ENGINE); /* No addr-spec. */ goto leave; } if (tail) *tail = 0; err = _gpgme_decode_percent_string (args, &address, 0, 0); if (err) goto leave; if (!sig->key) { err = _gpgme_key_new (&sig->key); if (err) goto leave; sig->key->fpr = fpr; sig->key->protocol = protocol; fpr = NULL; } else if (!sig->key->fpr) { err = trace_gpg_error (GPG_ERR_INTERNAL); goto leave; } err = _gpgme_key_append_name (sig->key, address, 0); if (err) goto leave; uid = sig->key->_last_uid; assert (uid); ti = calloc (1, sizeof *ti); if (!ti) { err = gpg_error_from_syserror (); goto leave; } uid->tofu = ti; leave: free (fpr); free (address); return err; } /* Parse a TOFU_STATS line and store it in the last tofu info of SIG. * * TOFU_STATS \ * [ [ ]] */ static gpgme_error_t parse_tofu_stats (gpgme_signature_t sig, char *args) { gpgme_error_t err; gpgme_tofu_info_t ti; char *field[8]; int nfields; unsigned long uval; if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu)) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */ if (ti->signfirst || ti->signcount || ti->validity || ti->policy) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set. */ nfields = _gpgme_split_fields (args, field, DIM (field)); if (nfields < 3) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required args missing. */ /* Note that we allow a value of up to 7 which is what we can store * in the ti->validity. */ err = _gpgme_strtoul_field (field[0], &uval); if (err || uval > 7) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti->validity = uval; /* Parse the sign-count. */ err = _gpgme_strtoul_field (field[1], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); if (uval > USHRT_MAX) uval = USHRT_MAX; ti->signcount = uval; /* Parse the encr-count. */ err = _gpgme_strtoul_field (field[2], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); if (uval > USHRT_MAX) uval = USHRT_MAX; ti->encrcount = uval; if (nfields == 3) return 0; /* All mandatory fields parsed. */ /* Parse the policy. */ if (!strcmp (field[3], "none")) ti->policy = GPGME_TOFU_POLICY_NONE; else if (!strcmp (field[3], "auto")) ti->policy = GPGME_TOFU_POLICY_AUTO; else if (!strcmp (field[3], "good")) ti->policy = GPGME_TOFU_POLICY_GOOD; else if (!strcmp (field[3], "bad")) ti->policy = GPGME_TOFU_POLICY_BAD; else if (!strcmp (field[3], "ask")) ti->policy = GPGME_TOFU_POLICY_ASK; else /* "unknown" and invalid policy strings. */ ti->policy = GPGME_TOFU_POLICY_UNKNOWN; if (nfields == 4) return 0; /* No more optional fields. */ /* Parse first and last seen timestamps (none or both are required). */ if (nfields < 6) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* "tm2" missing. */ err = _gpgme_strtoul_field (field[4], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti->signfirst = uval; err = _gpgme_strtoul_field (field[5], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti->signlast = uval; if (nfields > 7) { /* This condition is only to allow for gpg 2.1.15 - can * eventually be removed. */ err = _gpgme_strtoul_field (field[6], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti->encrfirst = uval; err = _gpgme_strtoul_field (field[7], &uval); if (err) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti->encrlast = uval; } return 0; } /* Parse a TOFU_STATS_LONG line and store it in the last tofu info of SIG. */ static gpgme_error_t parse_tofu_stats_long (gpgme_signature_t sig, char *args, int raw) { gpgme_error_t err; gpgme_tofu_info_t ti; char *p; if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu)) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */ if (ti->description) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set. */ err = _gpgme_decode_percent_string (args, &ti->description, 0, 0); if (err) return err; /* Remove the non-breaking spaces. */ if (!raw) { for (p = ti->description; *p; p++) if (*p == '~') *p = ' '; } return 0; } /* Parse an error status line and if SET_STATUS is true update the result status as appropriate. With SET_STATUS being false, only check for an error. */ static gpgme_error_t parse_error (gpgme_signature_t sig, char *args, int set_status) { gpgme_error_t err; char *where = strchr (args, ' '); char *which; if (where) { *where = '\0'; which = where + 1; where = strchr (which, ' '); if (where) *where = '\0'; where = args; } else return trace_gpg_error (GPG_ERR_INV_ENGINE); err = atoi (which); if (!strcmp (where, "proc_pkt.plaintext") && gpg_err_code (err) == GPG_ERR_BAD_DATA) { /* This indicates a double plaintext. The only solid way to handle this is by failing the oepration. */ return gpg_error (GPG_ERR_BAD_DATA); } else if (!set_status) ; else if (!strcmp (where, "verify.findkey")) sig->status = err; else if (!strcmp (where, "verify.keyusage") && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE) sig->wrong_key_usage = 1; return 0; } gpgme_error_t _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; gpgme_signature_t sig; char *end; err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL); opd = hook; if (err) return err; sig = opd->current_sig; switch (code) { case GPGME_STATUS_NEWSIG: if (sig) calc_sig_summary (sig); err = prepare_new_sig (opd); opd->only_newsig_seen = 1; opd->conflict_user_seen = 0; return err; case GPGME_STATUS_GOODSIG: case GPGME_STATUS_EXPSIG: case GPGME_STATUS_EXPKEYSIG: case GPGME_STATUS_BADSIG: case GPGME_STATUS_ERRSIG: case GPGME_STATUS_REVKEYSIG: if (sig && !opd->did_prepare_new_sig) calc_sig_summary (sig); opd->only_newsig_seen = 0; return parse_new_sig (opd, code, args, ctx->protocol); case GPGME_STATUS_VALIDSIG: opd->only_newsig_seen = 0; return sig ? parse_valid_sig (sig, args, ctx->protocol) : trace_gpg_error (GPG_ERR_INV_ENGINE); case GPGME_STATUS_NODATA: opd->only_newsig_seen = 0; if (!sig) return gpg_error (GPG_ERR_NO_DATA); sig->status = gpg_error (GPG_ERR_NO_DATA); break; case GPGME_STATUS_UNEXPECTED: opd->only_newsig_seen = 0; if (!sig) return gpg_error (GPG_ERR_GENERAL); sig->status = gpg_error (GPG_ERR_NO_DATA); break; case GPGME_STATUS_NOTATION_NAME: case GPGME_STATUS_NOTATION_FLAGS: case GPGME_STATUS_NOTATION_DATA: case GPGME_STATUS_POLICY_URL: opd->only_newsig_seen = 0; return sig ? parse_notation (sig, code, args) : trace_gpg_error (GPG_ERR_INV_ENGINE); case GPGME_STATUS_TRUST_UNDEFINED: case GPGME_STATUS_TRUST_NEVER: case GPGME_STATUS_TRUST_MARGINAL: case GPGME_STATUS_TRUST_FULLY: case GPGME_STATUS_TRUST_ULTIMATE: opd->only_newsig_seen = 0; return sig ? parse_trust (sig, code, args) : trace_gpg_error (GPG_ERR_INV_ENGINE); case GPGME_STATUS_PKA_TRUST_BAD: case GPGME_STATUS_PKA_TRUST_GOOD: opd->only_newsig_seen = 0; /* Check that we only get one of these status codes per signature; if not the crypto backend misbehaves. */ if (!sig || sig->pka_trust || sig->pka_address) return trace_gpg_error (GPG_ERR_INV_ENGINE); sig->pka_trust = code == GPGME_STATUS_PKA_TRUST_GOOD? 2 : 1; end = strchr (args, ' '); if (end) *end = 0; sig->pka_address = strdup (args); break; case GPGME_STATUS_TOFU_USER: opd->only_newsig_seen = 0; if (!sig) return trace_gpg_error (GPG_ERR_INV_ENGINE); err = parse_tofu_user (sig, args, ctx->protocol); /* gpg emits TOFU User lines for each conflicting key. * GPGME does not expose this to have a clean API and * a GPGME user can do a keylisting with the address * normalisation. * So when a duplicated TOFU_USER line is encountered * we ignore the conflicting tofu stats emitted afterwards. */ if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) { opd->conflict_user_seen = 1; break; } opd->conflict_user_seen = 0; return trace_gpg_error (err); case GPGME_STATUS_TOFU_STATS: opd->only_newsig_seen = 0; if (opd->conflict_user_seen) break; return sig ? parse_tofu_stats (sig, args) /* */ : trace_gpg_error (GPG_ERR_INV_ENGINE); case GPGME_STATUS_TOFU_STATS_LONG: opd->only_newsig_seen = 0; if (opd->conflict_user_seen) break; return sig ? parse_tofu_stats_long (sig, args, ctx->raw_description) /* */ : trace_gpg_error (GPG_ERR_INV_ENGINE); case GPGME_STATUS_ERROR: opd->only_newsig_seen = 0; /* Some error stati are informational, so we don't return an error code if we are not ready to process this status. */ return parse_error (sig, args, !!sig ); case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: if (sig && !opd->did_prepare_new_sig) calc_sig_summary (sig); if (opd->only_newsig_seen && sig) { gpgme_signature_t sig2; /* The last signature has no valid information - remove it from the list. */ assert (!sig->next); if (sig == opd->result.signatures) opd->result.signatures = NULL; else { for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next) if (sig2->next == sig) { sig2->next = NULL; break; } } /* Note that there is no need to release the members of SIG because we won't be here if they have been set. */ free (sig); opd->current_sig = NULL; } opd->only_newsig_seen = 0; if (opd->failure_code) return opd->failure_code; break; case GPGME_STATUS_PLAINTEXT: if (++opd->plaintext_seen > 1) return gpg_error (GPG_ERR_BAD_DATA); { int mime = 0; err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime); if (err) return err; opd->result.is_mime = !!mime; } break; case GPGME_STATUS_VERIFICATION_COMPLIANCE_MODE: PARSE_COMPLIANCE_FLAGS (args, opd->current_sig); break; default: break; } return 0; } static gpgme_error_t verify_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_verify_status_handler (priv, code, args); return err; } gpgme_error_t _gpgme_op_verify_init_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, sizeof (*opd), release_op_data); } static gpgme_error_t verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { gpgme_error_t err; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_verify_init_result (ctx); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx); if (!sig) return gpg_error (GPG_ERR_NO_DATA); return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext, ctx); } /* Decrypt ciphertext CIPHER and make a signature verification within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { gpg_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_verify_start", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_start", ctx, "sig=%p, signed_text=%p, plaintext=%p", sig, signed_text, plaintext); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = verify_start (ctx, 0, sig, signed_text, plaintext); return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER and make a signature verification within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_verify", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_verify", ctx, "sig=%p, signed_text=%p, plaintext=%p", sig, signed_text, plaintext); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = verify_start (ctx, 1, sig, signed_text, plaintext); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } /* Compatibility interfaces. */ /* Get the key used to create signature IDX in CTX and return it in R_KEY. */ gpgme_error_t gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key) { gpgme_verify_result_t result; gpgme_signature_t sig; if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); result = gpgme_op_verify_result (ctx); sig = result->signatures; while (sig && idx) { sig = sig->next; idx--; } if (!sig || idx) return gpg_error (GPG_ERR_EOF); return gpgme_get_key (ctx, sig->fpr, r_key, 0); } /* Retrieve the signature status of signature IDX in CTX after a successful verify operation in R_STAT (if non-null). The creation time stamp of the signature is returned in R_CREATED (if non-null). The function returns a string containing the fingerprint. */ const char * gpgme_get_sig_status (gpgme_ctx_t ctx, int idx, _gpgme_sig_stat_t *r_stat, time_t *r_created) { gpgme_verify_result_t result; gpgme_signature_t sig; result = gpgme_op_verify_result (ctx); sig = result->signatures; while (sig && idx) { sig = sig->next; idx--; } if (!sig || idx) return NULL; if (r_stat) { switch (gpg_err_code (sig->status)) { case GPG_ERR_NO_ERROR: *r_stat = GPGME_SIG_STAT_GOOD; break; case GPG_ERR_BAD_SIGNATURE: *r_stat = GPGME_SIG_STAT_BAD; break; case GPG_ERR_NO_PUBKEY: *r_stat = GPGME_SIG_STAT_NOKEY; break; case GPG_ERR_NO_DATA: *r_stat = GPGME_SIG_STAT_NOSIG; break; case GPG_ERR_SIG_EXPIRED: *r_stat = GPGME_SIG_STAT_GOOD_EXP; break; case GPG_ERR_KEY_EXPIRED: *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY; break; default: *r_stat = GPGME_SIG_STAT_ERROR; break; } } if (r_created) *r_created = sig->timestamp; return sig->fpr; } /* Retrieve certain attributes of a signature. IDX is the index number of the signature after a successful verify operation. WHAT is an attribute where GPGME_ATTR_EXPIRE is probably the most useful one. WHATIDX is to be passed as 0 for most attributes . */ unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx, _gpgme_attr_t what, int whatidx) { gpgme_verify_result_t result; gpgme_signature_t sig; (void)whatidx; result = gpgme_op_verify_result (ctx); sig = result->signatures; while (sig && idx) { sig = sig->next; idx--; } if (!sig || idx) return 0; switch (what) { case GPGME_ATTR_CREATED: return sig->timestamp; case GPGME_ATTR_EXPIRE: return sig->exp_timestamp; case GPGME_ATTR_VALIDITY: return (unsigned long) sig->validity; case GPGME_ATTR_SIG_STATUS: switch (gpg_err_code (sig->status)) { case GPG_ERR_NO_ERROR: return GPGME_SIG_STAT_GOOD; case GPG_ERR_BAD_SIGNATURE: return GPGME_SIG_STAT_BAD; case GPG_ERR_NO_PUBKEY: return GPGME_SIG_STAT_NOKEY; case GPG_ERR_NO_DATA: return GPGME_SIG_STAT_NOSIG; case GPG_ERR_SIG_EXPIRED: return GPGME_SIG_STAT_GOOD_EXP; case GPG_ERR_KEY_EXPIRED: return GPGME_SIG_STAT_GOOD_EXPKEY; default: return GPGME_SIG_STAT_ERROR; } case GPGME_ATTR_SIG_SUMMARY: return sig->summary; default: break; } return 0; } const char * gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx, _gpgme_attr_t what, int whatidx) { gpgme_verify_result_t result; gpgme_signature_t sig; result = gpgme_op_verify_result (ctx); sig = result->signatures; while (sig && idx) { sig = sig->next; idx--; } if (!sig || idx) return NULL; switch (what) { case GPGME_ATTR_FPR: return sig->fpr; case GPGME_ATTR_ERRTOK: if (whatidx == 1) return sig->wrong_key_usage ? "Wrong_Key_Usage" : ""; else return ""; default: break; } return NULL; } diff --git a/src/version.c b/src/version.c index e1d8e592..8c35ed0e 100644 --- a/src/version.c +++ b/src/version.c @@ -1,370 +1,370 @@ /* version.c - Version check routines. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_W32_SYSTEM #include #endif #include "gpgme.h" #include "priv-io.h" #include "debug.h" #include "context.h" /* For _gpgme_sema_subsystem_init and _gpgme_status_init. */ #include "sema.h" #include "util.h" #ifdef HAVE_ASSUAN_H #include "assuan.h" #endif #ifdef HAVE_W32_SYSTEM #include "windows.h" #endif /* We implement this function, so we have to disable the overriding macro. */ #undef gpgme_check_version /* Bootstrap the subsystems needed for concurrent operation. This must be done once at startup. We can not guarantee this using a lock, though, because the semaphore subsystem needs to be initialized itself before it can be used. So we expect that the user performs the necessary synchronization. */ static void do_subsystem_inits (void) { static int done = 0; if (done) return; #ifdef HAVE_W32_SYSTEM /* We need to make sure that the sockets are initialized. */ { WSADATA wsadat; WSAStartup (0x202, &wsadat); } #endif _gpgme_debug_subsystem_init (); _gpgme_io_subsystem_init (); _gpgme_status_init (); done = 1; } /* Put vesion information into the binary. */ static const char * cright_blurb (void) { static const char blurb[] = "\n\n" "This is GPGME " PACKAGE_VERSION " - The GnuPG Made Easy library\n" CRIGHTBLURB "\n" "(" BUILD_REVISION " " BUILD_TIMESTAMP ")\n" "\n\n"; return blurb; } /* Read the next number in the version string STR and return it in *NUMBER. Return a pointer to the tail of STR after parsing, or *NULL if the version string was invalid. */ static const char * parse_version_number (const char *str, int *number) { #define MAXVAL ((INT_MAX - 10) / 10) int val = 0; /* Leading zeros are not allowed. */ if (*str == '0' && isdigit(str[1])) return NULL; while (isdigit (*str) && val <= MAXVAL) { val *= 10; val += *(str++) - '0'; } *number = val; return val > MAXVAL ? NULL : str; } /* Parse the version string STR in the format MAJOR.MINOR.MICRO (for example, 9.3.2) and return the components in MAJOR, MINOR and MICRO as integers. The function returns the tail of the string that follows the version number. This might be the empty string if there is nothing following the version number, or a patchlevel. The function returns NULL if the version string is not valid. */ static const char * parse_version_string (const char *str, int *major, int *minor, int *micro) { str = parse_version_number (str, major); if (!str || *str != '.') return NULL; str++; str = parse_version_number (str, minor); if (!str || *str != '.') return NULL; str++; str = parse_version_number (str, micro); if (!str) return NULL; /* A patchlevel might follow. */ return str; } /* Return true if MY_VERSION is at least REQ_VERSION, and false otherwise. */ int _gpgme_compare_versions (const char *my_version, const char *rq_version) { int my_major, my_minor, my_micro; int rq_major, rq_minor, rq_micro; const char *my_plvl, *rq_plvl; if (!rq_version) return 1; if (!my_version) return 0; my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro); if (!my_plvl) return 0; rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro); if (!rq_plvl) return 0; if (my_major > rq_major || (my_major == rq_major && my_minor > rq_minor) || (my_major == rq_major && my_minor == rq_minor && my_micro > rq_micro) || (my_major == rq_major && my_minor == rq_minor && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0)) return 1; return 0; } /* Check that the the version of the library is at minimum the requested one and return the version string; return NULL if the condition is not met. If a NULL is passed to this function, no check is done and the version string is simply returned. This function must be run once at startup, as it also initializes some subsystems. Its invocation must be synchronized against calling any of the other functions in a multi-threaded environments. */ const char * gpgme_check_version (const char *req_version) { const char *result; do_subsystem_inits (); /* Catch-22: We need to get at least the debug subsystem ready before using the trace facility. If we won't the trace would automagically initialize the debug system with out the locks being initialized and missing the assuan log level setting. */ - TRACE2 (DEBUG_INIT, "gpgme_check_version", 0, + TRACE (DEBUG_INIT, "gpgme_check_version", 0, "req_version=%s, VERSION=%s", req_version? req_version:"(null)", VERSION); result = _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL; if (result != NULL) _gpgme_selftest = 0; return result; } /* Check the version and also at runtime if the struct layout of the library matches the one of the user. This is particular useful for Windows targets (-mms-bitfields). */ const char * gpgme_check_version_internal (const char *req_version, size_t offset_sig_validity) { const char *result; if (req_version && req_version[0] == 1 && req_version[1] == 1) return cright_blurb (); result = gpgme_check_version (req_version); if (result == NULL) return result; /* Catch-22, see above. */ - TRACE2 (DEBUG_INIT, "gpgme_check_version_internal", 0, + TRACE (DEBUG_INIT, "gpgme_check_version_internal", 0, "req_version=%s, offset_sig_validity=%i", req_version ? req_version : "(null)", offset_sig_validity); if (offset_sig_validity != offsetof (struct _gpgme_signature, validity)) { - TRACE1 (DEBUG_INIT, "gpgme_check_version_internal", 0, + TRACE (DEBUG_INIT, "gpgme_check_version_internal", 0, "offset_sig_validity mismatch: expected %i", offsetof (struct _gpgme_signature, validity)); _gpgme_selftest = GPG_ERR_SELFTEST_FAILED; } return result; } #define LINELENGTH 80 /* Extract the version string of a program from STRING. The version number is expected to be in GNU style format: foo 1.2.3 foo (bar system) 1.2.3 foo 1.2.3 cruft foo (bar system) 1.2.3 cruft. Spaces and tabs are skipped and used as delimiters, a term in (nested) parenthesis before the version string is skipped, the version string may consist of any non-space and non-tab characters but needs to bstart with a digit. */ static const char * extract_version_string (const char *string, size_t *r_len) { const char *s; int count, len; for (s=string; *s; s++) if (*s == ' ' || *s == '\t') break; while (*s == ' ' || *s == '\t') s++; if (*s == '(') { for (count=1, s++; count && *s; s++) if (*s == '(') count++; else if (*s == ')') count--; } /* For robustness we look for a digit. */ while ( *s && !(*s >= '0' && *s <= '9') ) s++; if (*s >= '0' && *s <= '9') { for (len=0; s[len]; len++) if (s[len] == ' ' || s[len] == '\t') break; } else len = 0; *r_len = len; return s; } /* Retrieve the version number from the --version output of the program FILE_NAME. */ char * _gpgme_get_program_version (const char *const file_name) { char line[LINELENGTH] = ""; int linelen = 0; char *mark = NULL; int rp[2]; int nread; char *argv[] = {NULL /* file_name */, (char*)"--version", 0}; struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0}, {-1, -1} }; int status; if (!file_name) return NULL; argv[0] = (char *) file_name; if (_gpgme_io_pipe (rp, 1) < 0) return NULL; cfd[0].fd = rp[1]; status = _gpgme_io_spawn (file_name, argv, IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL); if (status < 0) { _gpgme_io_close (rp[0]); _gpgme_io_close (rp[1]); return NULL; } do { nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1); if (nread > 0) { line[linelen + nread] = '\0'; mark = strchr (&line[linelen], '\n'); if (mark) { if (mark > &line[0] && mark[-1] == '\r') mark--; *mark = '\0'; break; } linelen += nread; } } while (nread > 0 && linelen < LINELENGTH - 1); _gpgme_io_close (rp[0]); if (mark) { size_t len; const char *s; s = extract_version_string (line, &len); if (!len) return NULL; mark = malloc (len + 1); if (!mark) return NULL; memcpy (mark, s, len); mark[len] = 0; return mark; } return NULL; } diff --git a/src/vfs-create.c b/src/vfs-create.c index 1e23106a..51b8307c 100644 --- a/src/vfs-create.c +++ b/src/vfs-create.c @@ -1,205 +1,205 @@ /* vfs-create.c - vfs create support in GPGME * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" static gpgme_error_t vfs_start (gpgme_ctx_t ctx, int synchronous, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpgme_error_t err; if (!command || !*command) return gpg_error (GPG_ERR_INV_VALUE); /* The flag value 256 is used to suppress an engine reset. This is required to keep the connection running. */ err = _gpgme_op_reset (ctx, ((synchronous & 255) | 256)); if (err) return err; return _gpgme_engine_op_assuan_transact (ctx->engine, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } #if 0 /* XXXX. This is the asynchronous variant. */ static gpgme_error_t gpgme_op_vfs_transact_start (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { return vfs_start (ctx, 0, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } #endif /* XXXX. This is the synchronous variant. */ static gpgme_error_t gpgme_op_vfs_transact (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value, gpgme_error_t *op_err) { gpgme_error_t err; if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); err = vfs_start (ctx, 1, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); if (!err) err = _gpgme_wait_one_ext (ctx, op_err); return err; } /* The actual exported interface follows. */ /* The container is automatically uncreateed when the context is reset or destroyed. This is a synchronous convenience interface, which automatically returns an operation error if there is no transmission error. */ static gpgme_error_t _gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *container_file, unsigned int flags, gpgme_error_t *op_err) { gpg_error_t err; char *cmd; char *container_file_esc = NULL; int i; (void)flags; /* We want to encourage people to check error values, so not getting them is discouraged here. Also makes our code easier. */ if (! op_err) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_encode_percent_string (container_file, &container_file_esc, 0); if (err) return err; i = 0; while (!err && recp[i]) { if (!recp[i]->subkeys || !recp[i]->subkeys->fpr) { free (container_file_esc); return gpg_error (GPG_ERR_UNUSABLE_PUBKEY); } if (gpgrt_asprintf (&cmd, "RECIPIENT %s", recp[i]->subkeys->fpr) < 0) { err = gpg_error_from_syserror (); free (container_file_esc); return err; } err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL, op_err); gpgrt_free (cmd); if (err || *op_err) { free (container_file_esc); return err; } recp++; } if (gpgrt_asprintf (&cmd, "CREATE -- %s", container_file_esc) < 0) { err = gpg_error_from_syserror (); free (container_file_esc); return err; } free (container_file_esc); err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL, op_err); gpgrt_free (cmd); return err; } gpgme_error_t gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *container_file, unsigned int flags, gpgme_error_t *op_err) { gpg_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_vfs_create", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_vfs_create", ctx, "container_file=%s, flags=0x%x, op_err=%p", container_file, flags, op_err); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (_gpgme_debug_trace () && recp) { int i = 0; while (recp[i]) { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + TRACE_LOG ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? recp[i]->subkeys->fpr : "invalid"); i++; } } err = _gpgme_op_vfs_create (ctx, recp, container_file, flags, op_err); return TRACE_ERR (err); } diff --git a/src/vfs-mount.c b/src/vfs-mount.c index bb5f056c..c6ee7c97 100644 --- a/src/vfs-mount.c +++ b/src/vfs-mount.c @@ -1,247 +1,247 @@ /* vfs-mount.c - vfs mount support in GPGME * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { struct _gpgme_op_vfs_mount_result result; } *op_data_t; gpgme_vfs_mount_result_t gpgme_op_vfs_mount_result (gpgme_ctx_t ctx) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_VFS_MOUNT, &hook, -1, NULL); opd = hook; /* Check in case this function is used without having run a command before. */ if (err || !opd) return NULL; return &opd->result; } static gpgme_error_t _gpgme_vfs_mount_status_handler (void *priv, const char *code, const char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_VFS_MOUNT, &hook, -1, NULL); opd = hook; if (err) return err; if (! strcasecmp ("MOUNTPOINT", code)) { if (opd->result.mount_dir) free (opd->result.mount_dir); opd->result.mount_dir = strdup (args); } return 0; } static gpgme_error_t vfs_start (gpgme_ctx_t ctx, int synchronous, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpgme_error_t err; void *hook; op_data_t opd; if (!command || !*command) return gpg_error (GPG_ERR_INV_VALUE); /* The flag value 256 is used to suppress an engine reset. This is required to keep the connection running. */ err = _gpgme_op_reset (ctx, ((synchronous & 255) | 256)); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_VFS_MOUNT, &hook, sizeof (*opd), NULL); opd = hook; if (err) return err; return _gpgme_engine_op_assuan_transact (ctx->engine, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } #if 0 /* XXXX. This is the asynchronous variant. */ static gpgme_error_t gpgme_op_vfs_transact_start (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { return vfs_start (ctx, 0, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } #endif /* XXXX. This is the synchronous variant. */ static gpgme_error_t gpgme_op_vfs_transact (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value, gpgme_error_t *op_err) { gpgme_error_t err; if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); err = vfs_start (ctx, 1, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); if (!err) err = _gpgme_wait_one_ext (ctx, op_err); return err; } /* The actual exported interface follows. */ /* The container is automatically unmounted when the context is reset or destroyed. This is a synchronous convenience interface, which automatically returns an operation error if there is no transmission error. */ static gpgme_error_t _gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file, const char *mount_dir, int flags, gpgme_error_t *op_err) { gpg_error_t err; char *cmd; char *container_file_esc = NULL; (void)flags; /* We want to encourage people to check error values, so not getting them is discouraged here. Also makes our code easier. */ if (! op_err) return gpg_error (GPG_ERR_INV_VALUE); err = _gpgme_encode_percent_string (container_file, &container_file_esc, 0); if (err) return err; if (gpgrt_asprintf (&cmd, "OPEN -- %s", container_file_esc) < 0) { err = gpg_error_from_syserror (); free (container_file_esc); return err; } free (container_file_esc); err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL, op_err); gpgrt_free (cmd); if (err || *op_err) return err; if (mount_dir) { char *mount_dir_esc = NULL; err = _gpgme_encode_percent_string (mount_dir, &mount_dir_esc, 0); if (err) return err; if (gpgrt_asprintf (&cmd, "MOUNT -- %s", mount_dir_esc) < 0) { err = gpg_error_from_syserror (); free (mount_dir_esc); return err; } free (mount_dir_esc); } else { if (gpgrt_asprintf (&cmd, "MOUNT") < 0) return gpg_error_from_syserror (); } err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL, _gpgme_vfs_mount_status_handler, ctx, op_err); gpgrt_free (cmd); return err; } gpgme_error_t gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file, const char *mount_dir, unsigned int flags, gpgme_error_t *op_err) { gpg_error_t err; - TRACE_BEG4 (DEBUG_CTX, "gpgme_op_vfs_mount", ctx, + TRACE_BEG (DEBUG_CTX, "gpgme_op_vfs_mount", ctx, "container=%s, mount_dir=%s, flags=0x%x, op_err=%p", container_file, mount_dir, flags, op_err); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_vfs_mount (ctx, container_file, mount_dir, flags, op_err); return TRACE_ERR (err); } diff --git a/src/w32-glib-io.c b/src/w32-glib-io.c index abd8045e..8c8722ce 100644 --- a/src/w32-glib-io.c +++ b/src/w32-glib-io.c @@ -1,1085 +1,1085 @@ /* w32-glib-io.c - W32 Glib I/O functions * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2004, 2005 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #include #include #include "util.h" #include "priv-io.h" #include "sema.h" #include "debug.h" #ifndef O_BINARY #ifdef _O_BINARY #define O_BINARY _O_BINARY #else #define O_BINARY 0 #endif #endif /* This file is an ugly hack to get GPGME working with glib on Windows targets. On Windows, you can not select() on file descriptors. The only way to check if there is something to read is to read something. This means that GPGME can not let glib check for data without letting glib also handle the data on Windows targets. The ugly consequence is that we need to work on GIOChannels in GPGME, creating a glib dependency. Also, we need to export an interface for the application to get at GPGME's GIOChannel. There is no good way to abstract all this with callbacks, because the whole thing is also interconnected with the creation of pipes and child processes. The following rule applies only to this I/O backend: * ALL operations must use the user defined event loop. GPGME can not anymore provide its own event loop. This is mostly a sanity requirement: Although we have in theory all information we need to make the GPGME W32 code for select still work, it would be a big complication and require changes throughout GPGME. Eventually, we probably have to bite the bullet and make some really nice callback interfaces to let the user control all this at a per-context level. */ #define MAX_SLAFD 256 static struct { int used; /* If this is not -1, then it's a libc file descriptor. */ int fd; /* If fd is -1, this is the Windows socket handle. */ int socket; GIOChannel *chan; /* The boolean PRIMARY is true if this file descriptor caused the allocation of CHAN. Only then should CHAN be destroyed when this FD is closed. This, together with the fact that dup'ed file descriptors are closed before the file descriptors from which they are dup'ed are closed, ensures that CHAN is always valid, and shared among all file descriptors referring to the same underlying object. The logic behind this is that there is only one reason for us to dup file descriptors anyway: to allow simpler book-keeping of file descriptors shared between GPGME and libassuan, which both want to close something. Using the same channel for these duplicates works just fine (and in fact, using different channels does not work because the W32 backend in glib does not support that: One would end up with several competing reader/writer threads. */ int primary; } giochannel_table[MAX_SLAFD]; static GIOChannel * find_channel (int fd) { if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used) return NULL; return giochannel_table[fd].chan; } /* Returns the FD or -1 on resource limit. */ int new_dummy_channel_from_fd (int cfd) { int idx; for (idx = 0; idx < MAX_SLAFD; idx++) if (! giochannel_table[idx].used) break; if (idx == MAX_SLAFD) { errno = EIO; return -1; } giochannel_table[idx].used = 1; giochannel_table[idx].chan = NULL; giochannel_table[idx].fd = cfd; giochannel_table[idx].socket = INVALID_SOCKET; giochannel_table[idx].primary = 1; return idx; } /* Returns the FD or -1 on resource limit. */ int new_channel_from_fd (int cfd) { int idx; for (idx = 0; idx < MAX_SLAFD; idx++) if (! giochannel_table[idx].used) break; if (idx == MAX_SLAFD) { errno = EIO; return -1; } giochannel_table[idx].used = 1; giochannel_table[idx].chan = g_io_channel_win32_new_fd (cfd); giochannel_table[idx].fd = cfd; giochannel_table[idx].socket = INVALID_SOCKET; giochannel_table[idx].primary = 1; g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL); g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE); return idx; } /* Returns the FD or -1 on resource limit. */ int new_channel_from_socket (int sock) { int idx; for (idx = 0; idx < MAX_SLAFD; idx++) if (! giochannel_table[idx].used) break; if (idx == MAX_SLAFD) { errno = EIO; return -1; } giochannel_table[idx].used = 1; giochannel_table[idx].chan = g_io_channel_win32_new_socket (sock); giochannel_table[idx].fd = -1; giochannel_table[idx].socket = sock; giochannel_table[idx].primary = 1; g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL); g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE); return idx; } /* Compatibility interface. Obsolete. */ void * gpgme_get_giochannel (int fd) { return find_channel (fd); } /* Look up the giochannel for "file descriptor" FD. */ void * gpgme_get_fdptr (int fd) { return find_channel (fd); } /* Write the printable version of FD to the buffer BUF of length BUFLEN. The printable version is the representation on the command line that the child process expects. */ int _gpgme_io_fd2str (char *buf, int buflen, int fd) { HANDLE hndl; - TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_fd2str", fd, "fd=%d", fd); + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_fd2str", fd, "fd=%d", fd); if (giochannel_table[fd].fd != -1) hndl = (HANDLE) _get_osfhandle (giochannel_table[fd].fd); else hndl = (HANDLE) giochannel_table[fd].socket; - TRACE_SUC1 ("syshd=%p", hndl); + TRACE_SUC ("syshd=%p", hndl); return snprintf (buf, buflen, "%d", (int) hndl); } void _gpgme_io_subsystem_init (void) { } static struct { _gpgme_close_notify_handler_t handler; void *value; } notify_table[MAX_SLAFD]; int _gpgme_io_read (int fd, void *buffer, size_t count) { int saved_errno = 0; gsize nread; GIOChannel *chan; GIOStatus status; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_read", fd, "buffer=%p, count=%u", buffer, count); chan = find_channel (fd); if (!chan) { TRACE_LOG ("no channel registered"); errno = EINVAL; return TRACE_SYSRES (-1); } - TRACE_LOG1 ("channel %p", chan); + TRACE_LOG ("channel %p", chan); { GError *err = NULL; status = g_io_channel_read_chars (chan, (gchar *) buffer, count, &nread, &err); if (err) { - TRACE_LOG2 ("status %i, err %s", status, err->message); + TRACE_LOG ("status %i, err %s", status, err->message); g_error_free (err); } } if (status == G_IO_STATUS_EOF) nread = 0; else if (status == G_IO_STATUS_AGAIN) { nread = -1; saved_errno = EAGAIN; } else if (status != G_IO_STATUS_NORMAL) { - TRACE_LOG1 ("status %d", status); + TRACE_LOG ("status %d", status); nread = -1; saved_errno = EIO; } if (nread != 0 && nread != -1) TRACE_LOGBUFX (buffer, nread); errno = saved_errno; return TRACE_SYSRES (nread); } int _gpgme_io_write (int fd, const void *buffer, size_t count) { int saved_errno = 0; gsize nwritten; GIOChannel *chan; GIOStatus status; GError *err = NULL; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_write", fd, "buffer=%p, count=%u", buffer, count); TRACE_LOGBUFX (buffer, count); chan = find_channel (fd); if (!chan) { TRACE_LOG ("fd %d: no channel registered"); errno = EINVAL; return -1; } status = g_io_channel_write_chars (chan, (gchar *) buffer, count, &nwritten, &err); if (err) { - TRACE_LOG1 ("write error: %s", err->message); + TRACE_LOG ("write error: %s", err->message); g_error_free (err); } if (status == G_IO_STATUS_AGAIN) { nwritten = -1; saved_errno = EAGAIN; } else if (status != G_IO_STATUS_NORMAL) { nwritten = -1; saved_errno = EIO; } errno = saved_errno; return TRACE_SYSRES (nwritten); } int _gpgme_io_pipe (int filedes[2], int inherit_idx) { int fds[2]; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, "inherit_idx=%i (GPGME uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); #define PIPEBUF_SIZE 4096 if (_pipe (fds, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1) return TRACE_SYSRES (-1); /* Make one end inheritable. */ if (inherit_idx == 0) { int new_read; new_read = _dup (fds[0]); _close (fds[0]); fds[0] = new_read; if (new_read < 0) { _close (fds[1]); return TRACE_SYSRES (-1); } } else if (inherit_idx == 1) { int new_write; new_write = _dup (fds[1]); _close (fds[1]); fds[1] = new_write; if (new_write < 0) { _close (fds[0]); return TRACE_SYSRES (-1); } } /* For _gpgme_io_close. */ filedes[inherit_idx] = new_dummy_channel_from_fd (fds[inherit_idx]); if (filedes[inherit_idx] < 0) { int saved_errno = errno; _close (fds[0]); _close (fds[1]); errno = saved_errno; return TRACE_SYSRES (-1); } /* Now we have a pipe with the correct end inheritable. The other end should have a giochannel. */ filedes[1 - inherit_idx] = new_channel_from_fd (fds[1 - inherit_idx]); if (filedes[1 - inherit_idx] < 0) { int saved_errno = errno; _gpgme_io_close (fds[inherit_idx]); _close (fds[1 - inherit_idx]); errno = saved_errno; return TRACE_SYSRES (-1); } - return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p", + return TRACE_SUC ("read=0x%x/%p, write=0x%x/%p, channel=%p", filedes[0], (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd), filedes[1], (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd), giochannel_table[1 - inherit_idx].chan); } int _gpgme_io_close (int fd) { - TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd); + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd, ""); if (fd < 0 || fd >= MAX_SLAFD) { errno = EBADF; return TRACE_SYSRES (-1); } assert (giochannel_table[fd].used); /* First call the notify handler. */ if (notify_table[fd].handler) { notify_table[fd].handler (fd, notify_table[fd].value); notify_table[fd].handler = NULL; notify_table[fd].value = NULL; } /* Then do the close. */ if (giochannel_table[fd].chan) { if (giochannel_table[fd].primary) g_io_channel_shutdown (giochannel_table[fd].chan, 1, NULL); g_io_channel_unref (giochannel_table[fd].chan); } else { /* Dummy entry, just close. */ assert (giochannel_table[fd].fd != -1); _close (giochannel_table[fd].fd); } giochannel_table[fd].used = 0; giochannel_table[fd].fd = -1; giochannel_table[fd].socket = INVALID_SOCKET; giochannel_table[fd].chan = NULL; giochannel_table[fd].primary = 0; - TRACE_SUC (); + TRACE_SUC (""); return 0; } int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler, void *value) { - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, "close_handler=%p/%p", handler, value); assert (fd != -1); if (fd < 0 || fd >= (int) DIM (notify_table)) { errno = EINVAL; return TRACE_SYSRES (-1); } notify_table[fd].handler = handler; notify_table[fd].value = value; return TRACE_SYSRES (0); } int _gpgme_io_set_nonblocking (int fd) { GIOChannel *chan; GIOStatus status; - TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd); + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd, ""); chan = find_channel (fd); if (!chan) { errno = EIO; return TRACE_SYSRES (-1); } status = g_io_channel_set_flags (chan, g_io_channel_get_flags (chan) | G_IO_FLAG_NONBLOCK, NULL); if (status != G_IO_STATUS_NORMAL) { #if 0 /* glib 1.9.2 does not implement set_flags and returns an error. */ errno = EIO; return TRACE_SYSRES (-1); #else - TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)", + TRACE_LOG ("g_io_channel_set_flags failed: status=%d (ignored)", status); #endif } return TRACE_SYSRES (0); } static char * build_commandline (char **argv) { int i; int n = 0; char *buf; char *p; /* We have to quote some things because under Windows the program parses the commandline and does some unquoting. We enclose the whole argument in double-quotes, and escape literal double-quotes as well as backslashes with a backslash. We end up with a trailing space at the end of the line, but that is harmless. */ for (i = 0; argv[i]; i++) { p = argv[i]; /* The leading double-quote. */ n++; while (*p) { /* An extra one for each literal that must be escaped. */ if (*p == '\\' || *p == '"') n++; n++; p++; } /* The trailing double-quote and the delimiter. */ n += 2; } /* And a trailing zero. */ n++; buf = p = malloc (n); if (!buf) return NULL; for (i = 0; argv[i]; i++) { char *argvp = argv[i]; *(p++) = '"'; while (*argvp) { if (*argvp == '\\' || *argvp == '"') *(p++) = '\\'; *(p++) = *(argvp++); } *(p++) = '"'; *(p++) = ' '; } *(p++) = 0; return buf; } int _gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags, struct spawn_fd_item_s *fd_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, pid_t *r_pid) { SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, /* returns process handle */ 0, /* returns primary thread handle */ 0, /* returns pid */ 0 /* returns tid */ }; STARTUPINFO si; int cr_flags = (CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (GetCurrentProcess ())); int i; char **args; char *arg_string; /* FIXME. */ int debug_me = 0; int tmp_fd; char *tmp_name; - TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_spawn", path, "path=%s", path); i = 0; while (argv[i]) { - TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]); + TRACE_LOG ("argv[%2i] = %s", i, argv[i]); i++; } /* We do not inherit any handles by default, and just insert those handles we want the child to have afterwards. But some handle values occur on the command line, and we need to move stdin/out/err to the right location. So we use a wrapper program which gets the information from a temporary file. */ if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0) { - TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno)); + TRACE_LOG ("_gpgme_mkstemp failed: %s", strerror (errno)); return TRACE_SYSRES (-1); } - TRACE_LOG1 ("tmp_name = %s", tmp_name); + TRACE_LOG ("tmp_name = %s", tmp_name); args = calloc (2 + i + 1, sizeof (*args)); args[0] = (char *) _gpgme_get_w32spawn_path (); args[1] = tmp_name; args[2] = path; memcpy (&args[3], &argv[1], i * sizeof (*args)); memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; arg_string = build_commandline (args); free (args); if (!arg_string) { close (tmp_fd); DeleteFile (tmp_name); return TRACE_SYSRES (-1); } memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE; si.hStdInput = INVALID_HANDLE_VALUE; si.hStdOutput = INVALID_HANDLE_VALUE; si.hStdError = INVALID_HANDLE_VALUE; cr_flags |= CREATE_SUSPENDED; if ((flags & IOSPAWN_FLAG_DETACHED)) cr_flags |= DETACHED_PROCESS; if (!CreateProcessA (_gpgme_get_w32spawn_path (), arg_string, &sec_attr, /* process security attributes */ &sec_attr, /* thread security attributes */ FALSE, /* inherit handles */ cr_flags, /* creation flags */ NULL, /* environment */ NULL, /* use current drive/directory */ &si, /* startup information */ &pi)) /* returns process information */ { - TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("CreateProcess failed: ec=%d", (int) GetLastError ()); free (arg_string); close (tmp_fd); DeleteFile (tmp_name); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } free (arg_string); if (flags & IOSPAWN_FLAG_ALLOW_SET_FG) _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId); /* Insert the inherited handles. */ for (i = 0; fd_list[i].fd != -1; i++) { HANDLE hd; /* Make it inheritable for the wrapper process. */ if (!DuplicateHandle (GetCurrentProcess(), _get_osfhandle (giochannel_table[fd_list[i].fd].fd), pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { - TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); TerminateProcess (pi.hProcess, 0); /* Just in case TerminateProcess didn't work, let the process fail on its own. */ ResumeThread (pi.hThread); CloseHandle (pi.hThread); CloseHandle (pi.hProcess); close (tmp_fd); DeleteFile (tmp_name); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } /* Return the child name of this handle. */ fd_list[i].peer_name = (int) hd; } /* Write the handle translation information to the temporary file. */ { /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex notation: "0xFEDCBA9876543210" with an extra white space after every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead for a time when a HANDLE is 64 bit. */ #define BUFFER_MAX 800 char line[BUFFER_MAX + 1]; int res; int written; size_t len; if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG)) strcpy (line, "~1 \n"); else strcpy (line, "\n"); for (i = 0; fd_list[i].fd != -1; i++) { /* Strip the newline. */ len = strlen (line) - 1; /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */ snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n", fd_list[i].fd, fd_list[i].dup_to, fd_list[i].peer_name, fd_list[i].arg_loc); /* Rather safe than sorry. */ line[BUFFER_MAX - 1] = '\n'; line[BUFFER_MAX] = '\0'; } len = strlen (line); written = 0; do { res = write (tmp_fd, &line[written], len - written); if (res > 0) written += res; } while (res > 0 || (res < 0 && errno == EAGAIN)); } close (tmp_fd); /* The temporary file is deleted by the gpgme-w32spawn process (hopefully). */ - TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, " + TRACE_LOG ("CreateProcess ready: hProcess=%p, hThread=%p, " "dwProcessID=%d, dwThreadId=%d", pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId); if (r_pid) *r_pid = (pid_t)pi.dwProcessId; if (ResumeThread (pi.hThread) < 0) - TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("ResumeThread failed: ec=%d", (int) GetLastError ()); if (!CloseHandle (pi.hThread)) - TRACE_LOG1 ("CloseHandle of thread failed: ec=%d", + TRACE_LOG ("CloseHandle of thread failed: ec=%d", (int) GetLastError ()); - TRACE_LOG1 ("process=%p", pi.hProcess); + TRACE_LOG ("process=%p", pi.hProcess); /* We don't need to wait for the process. */ if (!CloseHandle (pi.hProcess)) - TRACE_LOG1 ("CloseHandle of process failed: ec=%d", + TRACE_LOG ("CloseHandle of process failed: ec=%d", (int) GetLastError ()); if (! (flags & IOSPAWN_FLAG_NOCLOSE)) { for (i = 0; fd_list[i].fd != -1; i++) _gpgme_io_close (fd_list[i].fd); } for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].dup_to == -1) - TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, + TRACE_LOG ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].peer_name); else - TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd, + TRACE_LOG ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd, fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" : ((fd_list[i].dup_to == 1) ? "out" : "err")); return TRACE_SYSRES (0); } /* Select on the list of fds. Returns: -1 = error, 0 = timeout or nothing to select, > 0 = number of signaled fds. */ int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) { int npollfds; GPollFD *pollfds; int *pollfds_map; int i; int j; int any; int n; int count; /* Use a 1s timeout. */ int timeout = 1000; void *dbg_help = NULL; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", fds, "nfds=%u, nonblock=%u", nfds, nonblock); if (nonblock) timeout = 0; pollfds = calloc (nfds, sizeof *pollfds); if (!pollfds) return -1; pollfds_map = calloc (nfds, sizeof *pollfds_map); if (!pollfds_map) { free (pollfds); return -1; } npollfds = 0; TRACE_SEQ (dbg_help, "select on [ "); any = 0; for (i = 0; i < nfds; i++) { GIOChannel *chan = NULL; if (fds[i].fd == -1) continue; if ((fds[i].for_read || fds[i].for_write) && !(chan = find_channel (fds[i].fd))) { TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd); TRACE_END (dbg_help, "]"); assert (!"see log file"); } else if (fds[i].for_read ) { assert(chan); g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds); pollfds_map[npollfds] = i; TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd); npollfds++; any = 1; } else if (fds[i].for_write) { assert(chan); g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds); pollfds_map[npollfds] = i; TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd); npollfds++; any = 1; } fds[i].signaled = 0; } TRACE_END (dbg_help, "]"); if (!any) { count = 0; goto leave; } count = g_io_channel_win32_poll (pollfds, npollfds, timeout); if (count < 0) { int saved_errno = errno; errno = saved_errno; goto leave; } TRACE_SEQ (dbg_help, "select OK [ "); if (TRACE_ENABLED (dbg_help)) { for (i = 0; i < npollfds; i++) { if ((pollfds[i].revents & G_IO_IN)) TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd); if ((pollfds[i].revents & G_IO_OUT)) TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd); } TRACE_END (dbg_help, "]"); } /* COUNT is used to stop the lop as soon as possible. */ for (n = count, i = 0; i < npollfds && n; i++) { j = pollfds_map[i]; assert (j >= 0 && j < nfds); if (fds[j].fd == -1) ; else if (fds[j].for_read) { if ((pollfds[i].revents & G_IO_IN)) { fds[j].signaled = 1; n--; } } else if (fds[j].for_write) { if ((pollfds[i].revents & G_IO_OUT)) { fds[j].signaled = 1; n--; } } } leave: free (pollfds); free (pollfds_map); return TRACE_SYSRES (count); } int _gpgme_io_dup (int fd) { int newfd; GIOChannel *chan; - TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd); + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd, ""); if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used) { errno = EINVAL; return TRACE_SYSRES (-1); } for (newfd = 0; newfd < MAX_SLAFD; newfd++) if (! giochannel_table[newfd].used) break; if (newfd == MAX_SLAFD) { errno = EIO; return TRACE_SYSRES (-1); } chan = giochannel_table[fd].chan; g_io_channel_ref (chan); giochannel_table[newfd].used = 1; giochannel_table[newfd].chan = chan; giochannel_table[newfd].fd = -1; giochannel_table[newfd].socket = INVALID_SOCKET; giochannel_table[newfd].primary = 0; return TRACE_SYSRES (newfd); } static int wsa2errno (int err) { switch (err) { case WSAENOTSOCK: return EINVAL; case WSAEWOULDBLOCK: return EAGAIN; case ERROR_BROKEN_PIPE: return EPIPE; case WSANOTINITIALISED: return ENOSYS; default: return EIO; } } int _gpgme_io_socket (int domain, int type, int proto) { int res; int fd; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_socket", domain, "type=%i, protp=%i", type, proto); res = socket (domain, type, proto); if (res == INVALID_SOCKET) { errno = wsa2errno (WSAGetLastError ()); return TRACE_SYSRES (-1); } fd = new_channel_from_socket (res); if (fd < 0) { int saved_errno = errno; closesocket (res); errno = saved_errno; return TRACE_SYSRES (-1); } - TRACE_SUC2 ("fd=%i, socket=0x%x", fd, res); + TRACE_SUC ("fd=%i, socket=0x%x", fd, res); return fd; } int _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen) { GIOChannel *chan; int sockfd; int res; GIOFlags flags; GIOStatus status; GError *err = NULL; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_connect", fd, "addr=%p, addrlen=%i", addr, addrlen); chan = find_channel (fd); if (! chan) { errno = EINVAL; return TRACE_SYSRES (-1); } flags = g_io_channel_get_flags (chan); if (flags & G_IO_FLAG_NONBLOCK) { status = g_io_channel_set_flags (chan, flags & ~G_IO_FLAG_NONBLOCK, &err); if (err) { - TRACE_LOG1 ("setting flags error: %s", err->message); + TRACE_LOG ("setting flags error: %s", err->message); g_error_free (err); err = NULL; } if (status != G_IO_STATUS_NORMAL) { errno = EIO; return TRACE_SYSRES (-1); } } sockfd = giochannel_table[fd].socket; if (sockfd == INVALID_SOCKET) { errno = EINVAL; return TRACE_SYSRES (-1); } - TRACE_LOG1 ("connect sockfd=0x%x", sockfd); + TRACE_LOG ("connect sockfd=0x%x", sockfd); res = connect (sockfd, addr, addrlen); /* FIXME: Error ignored here. */ if (! (flags & G_IO_FLAG_NONBLOCK)) g_io_channel_set_flags (chan, flags, NULL); if (res) { - TRACE_LOG2 ("connect failed: %i %i", res, WSAGetLastError ()); + TRACE_LOG ("connect failed: %i %i", res, WSAGetLastError ()); errno = wsa2errno (WSAGetLastError ()); return TRACE_SYSRES (-1); } - return TRACE_SUC (); + return TRACE_SUC (""); } diff --git a/src/w32-io.c b/src/w32-io.c index b4a03448..24e7e2ba 100644 --- a/src/w32-io.c +++ b/src/w32-io.c @@ -1,1975 +1,1975 @@ /* w32-io.c - W32 API I/O functions. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001-2004, 2007, 2010, 2018 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #include "util.h" #include "sema.h" #include "priv-io.h" #include "debug.h" #include "sys-util.h" /* The number of entries in our file table. We may eventually use a * lower value and dynamically resize the table. */ #define MAX_SLAFD 512 #define handle_to_fd(a) ((int)(a)) #define READBUF_SIZE 4096 #define WRITEBUF_SIZE 4096 #define PIPEBUF_SIZE 4096 /* An object to store handles or sockets. */ struct hddesc_s { HANDLE hd; SOCKET sock; int refcount; }; typedef struct hddesc_s *hddesc_t; /* The context used by a reader thread. */ struct reader_context_s { hddesc_t hdd; HANDLE thread_hd; int refcount; /* Bumped if the FD has been duped and thus we have * another FD referencing this context. */ DECLARE_LOCK (mutex); int stop_me; int eof; int eof_shortcut; int error; int error_code; /* This is manually reset. */ HANDLE have_data_ev; /* This is automatically reset. */ HANDLE have_space_ev; /* This is manually reset but actually only triggered once. */ HANDLE close_ev; size_t readpos, writepos; char buffer[READBUF_SIZE]; }; /* The context used by a writer thread. */ struct writer_context_s { hddesc_t hdd; HANDLE thread_hd; int refcount; DECLARE_LOCK (mutex); int stop_me; int error; int error_code; /* This is manually reset. */ HANDLE have_data; HANDLE is_empty; HANDLE close_ev; size_t nbytes; char buffer[WRITEBUF_SIZE]; }; /* An object to keep track of HANDLEs and sockets and map them to an * integer similar to what we use in Unix. Note that despite this * integer is often named "fd", it is not a file descriptor but really * only an index into this table. Never ever pass such an fd to any * other function except for those implemented here. */ static struct { int used; /* The handle descriptor. */ hddesc_t hdd; /* DUP_FROM is just a debug helper to show from which fd this fd was * dup-ed. */ int dup_from; /* Two flags to indicate whether a reader or writer (or both) are * needed. This is so that we can delay the actual thread creation * until they are needed. */ unsigned int want_reader:1; unsigned int want_writer:1; /* The context of an associated reader object or NULL. */ struct reader_context_s *reader; /* The context of an associated writer object or NULL. */ struct writer_context_s *writer; /* A notification handler. Noet that we current support only one * callback per fd. */ struct { _gpgme_close_notify_handler_t handler; void *value; } notify; } fd_table[MAX_SLAFD]; static size_t fd_table_size = MAX_SLAFD; DEFINE_STATIC_LOCK (fd_table_lock); /* We use a single global lock for all hddesc_t objects. */ DEFINE_STATIC_LOCK (hddesc_lock); /* Wrapper around CloseHandle to print an error. */ #define close_handle(hd) _close_handle ((hd), __LINE__); static void _close_handle (HANDLE hd, int line) { if (!CloseHandle (hd)) { - TRACE2 (DEBUG_INIT, "w32-io", hd, "CloseHandle failed at line %d: ec=%d", + TRACE (DEBUG_INIT, "w32-io", hd, "CloseHandle failed at line %d: ec=%d", line, (int) GetLastError ()); } } /* Wrapper around WaitForSingleObject to print an error. */ #define wait_for_single_object(hd,msec) \ _wait_for_single_object ((hd), (msec), __LINE__) static DWORD _wait_for_single_object (HANDLE hd, DWORD msec, int line) { DWORD res; res = WaitForSingleObject (hd, msec); if (res == WAIT_FAILED) { - TRACE2 (DEBUG_INIT, "w32-io", hd, + TRACE (DEBUG_INIT, "w32-io", hd, "WFSO failed at line %d: ec=%d", line, (int) GetLastError ()); } return res; } /* Create a new handle descriptor object. */ static hddesc_t new_hddesc (void) { hddesc_t hdd; hdd = malloc (sizeof *hdd); if (!hdd) return NULL; hdd->hd = INVALID_HANDLE_VALUE; hdd->sock = INVALID_SOCKET; hdd->refcount = 0; return hdd; } static hddesc_t ref_hddesc (hddesc_t hdd) { LOCK (hddesc_lock); hdd->refcount++; UNLOCK (hddesc_lock); return hdd; } /* Release a handle descriptor object and close its handle or socket * if needed. */ static void release_hddesc (hddesc_t hdd) { if (!hdd) return; LOCK (hddesc_lock); hdd->refcount--; if (hdd->refcount < 1) { /* Holds a valid handle or was never initialized (in which case * REFCOUNT would be -1 here). */ - TRACE_BEG3 (DEBUG_SYSIO, "gpgme:release_hddesc", hdd, + TRACE_BEG (DEBUG_SYSIO, "gpgme:release_hddesc", hdd, "hd=%p, sock=%d, refcount=%d", hdd->hd, hdd->sock, hdd->refcount); if (hdd->hd != INVALID_HANDLE_VALUE) close_handle (hdd->hd); if (hdd->sock != INVALID_SOCKET) { - TRACE_LOG1 ("closing socket %d", hdd->sock); + TRACE_LOG ("closing socket %d", hdd->sock); if (closesocket (hdd->sock)) { - TRACE_LOG1 ("closesocket failed: ec=%d", (int)WSAGetLastError ()); + TRACE_LOG ("closesocket failed: ec=%d", (int)WSAGetLastError ()); } } free (hdd); - TRACE_SUC (); + TRACE_SUC (""); } UNLOCK (hddesc_lock); } /* Returns our FD or -1 on resource limit. The returned integer * references a new object which has not been initialized but can be * release with release_fd. */ static int new_fd (void) { int idx; LOCK (fd_table_lock); for (idx = 0; idx < fd_table_size; idx++) if (! fd_table[idx].used) break; if (idx == fd_table_size) { gpg_err_set_errno (EIO); idx = -1; } else { fd_table[idx].used = 1; fd_table[idx].hdd = NULL; fd_table[idx].dup_from = -1; fd_table[idx].want_reader = 0; fd_table[idx].want_writer = 0; fd_table[idx].reader = NULL; fd_table[idx].writer = NULL; fd_table[idx].notify.handler = NULL; fd_table[idx].notify.value = NULL; } UNLOCK (fd_table_lock); return idx; } /* Releases our FD but it this is just this entry. No close operation * is involved here; it must be done prior to calling this * function. */ static void release_fd (int fd) { if (fd < 0 || fd >= fd_table_size) return; LOCK (fd_table_lock); if (fd_table[fd].used) { release_hddesc (fd_table[fd].hdd); fd_table[fd].used = 0; fd_table[fd].hdd = NULL; fd_table[fd].dup_from = -1; fd_table[fd].want_reader = 0; fd_table[fd].want_writer = 0; fd_table[fd].reader = NULL; fd_table[fd].writer = NULL; fd_table[fd].notify.handler = NULL; fd_table[fd].notify.value = NULL; } UNLOCK (fd_table_lock); } static int get_desired_thread_priority (void) { int value; if (!_gpgme_get_conf_int ("IOThreadPriority", &value)) { value = THREAD_PRIORITY_HIGHEST; - TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0, + TRACE (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0, "%d (default)", value); } else { - TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0, + TRACE (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0, "%d (configured)", value); } return value; } /* The reader thread. Created on the fly by gpgme_io_read and * destroyed by destroy_reader. Note that this functions works with a * copy of the value of the HANDLE variable frm the FS_TABLE. */ static DWORD CALLBACK reader (void *arg) { struct reader_context_s *ctx = arg; int nbytes; DWORD nread; int sock; - TRACE_BEG4 (DEBUG_SYSIO, "gpgme:reader", ctx->hdd, + TRACE_BEG (DEBUG_SYSIO, "gpgme:reader", ctx->hdd, "hd=%p, sock=%d, thread=%p, refcount=%d", ctx->hdd->hd, ctx->hdd->sock, ctx->thread_hd, ctx->refcount); if (ctx->hdd->hd != INVALID_HANDLE_VALUE) sock = 0; else sock = 1; for (;;) { LOCK (ctx->mutex); /* Leave a 1 byte gap so that we can see whether it is empty or full. */ if ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos) { /* Wait for space. */ if (!ResetEvent (ctx->have_space_ev)) { - TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("ResetEvent failed: ec=%d", (int) GetLastError ()); } UNLOCK (ctx->mutex); - TRACE_LOG1 ("waiting for space (refcnt=%d)", ctx->refcount); + TRACE_LOG ("waiting for space (refcnt=%d)", ctx->refcount); wait_for_single_object (ctx->have_space_ev, INFINITE); TRACE_LOG ("got space"); LOCK (ctx->mutex); } if (ctx->stop_me) { UNLOCK (ctx->mutex); break; } nbytes = (ctx->readpos + READBUF_SIZE - ctx->writepos - 1) % READBUF_SIZE; if (nbytes > READBUF_SIZE - ctx->writepos) nbytes = READBUF_SIZE - ctx->writepos; UNLOCK (ctx->mutex); - TRACE_LOG2 ("%s %d bytes", sock? "receiving":"reading", nbytes); + TRACE_LOG ("%s %d bytes", sock? "receiving":"reading", nbytes); if (sock) { int n; n = recv (ctx->hdd->sock, ctx->buffer + ctx->writepos, nbytes, 0); if (n < 0) { ctx->error_code = (int) WSAGetLastError (); if (ctx->error_code == ERROR_BROKEN_PIPE) { ctx->eof = 1; TRACE_LOG ("got EOF (broken connection)"); } else { /* Check whether the shutdown triggered the error - no need to to print a warning in this case. */ if ( ctx->error_code == WSAECONNABORTED || ctx->error_code == WSAECONNRESET) { LOCK (ctx->mutex); if (ctx->stop_me) { UNLOCK (ctx->mutex); TRACE_LOG ("got shutdown"); break; } UNLOCK (ctx->mutex); } ctx->error = 1; - TRACE_LOG1 ("recv error: ec=%d", ctx->error_code); + TRACE_LOG ("recv error: ec=%d", ctx->error_code); } break; } nread = n; } else { if (!ReadFile (ctx->hdd->hd, ctx->buffer + ctx->writepos, nbytes, &nread, NULL)) { ctx->error_code = (int) GetLastError (); if (ctx->error_code == ERROR_BROKEN_PIPE) { ctx->eof = 1; TRACE_LOG ("got EOF (broken pipe)"); } else if (ctx->error_code == ERROR_OPERATION_ABORTED) { ctx->eof = 1; TRACE_LOG ("got EOF (closed by us)"); } else { ctx->error = 1; - TRACE_LOG1 ("read error: ec=%d", ctx->error_code); + TRACE_LOG ("read error: ec=%d", ctx->error_code); } break; } } LOCK (ctx->mutex); if (ctx->stop_me) { UNLOCK (ctx->mutex); break; } if (!nread) { ctx->eof = 1; TRACE_LOG ("got eof"); UNLOCK (ctx->mutex); break; } - TRACE_LOG2 ("got %u bytes (refcnt=%d)", nread, ctx->refcount); + TRACE_LOG ("got %u bytes (refcnt=%d)", nread, ctx->refcount); ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE; if (!SetEvent (ctx->have_data_ev)) { - TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev, + TRACE_LOG ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev, (int) GetLastError ()); } UNLOCK (ctx->mutex); } /* Indicate that we have an error or EOF. */ if (!SetEvent (ctx->have_data_ev)) { - TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev, + TRACE_LOG ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev, (int) GetLastError ()); } TRACE_LOG ("waiting for close"); wait_for_single_object (ctx->close_ev, INFINITE); release_hddesc (ctx->hdd); close_handle (ctx->close_ev); close_handle (ctx->have_data_ev); close_handle (ctx->have_space_ev); close_handle (ctx->thread_hd); DESTROY_LOCK (ctx->mutex); free (ctx); - return TRACE_SUC (); + return TRACE_SUC (""); } /* Create a new reader thread and return its context object. The * input is the handle descriptor HDD. This function may not call any * fd based functions because the caller already holds a lock on the * fd_table. */ static struct reader_context_s * create_reader (hddesc_t hdd) { struct reader_context_s *ctx; SECURITY_ATTRIBUTES sec_attr; DWORD tid; - TRACE_BEG3 (DEBUG_SYSIO, "gpgme:create_reader", hdd, + TRACE_BEG (DEBUG_SYSIO, "gpgme:create_reader", hdd, "handle=%p sock=%d refhdd=%d", hdd->hd, hdd->sock, hdd->refcount); memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; ctx = calloc (1, sizeof *ctx); if (!ctx) { TRACE_SYSERR_NR (errno); return NULL; } ctx->hdd = ref_hddesc (hdd); ctx->refcount = 1; ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL); if (ctx->have_data_ev) ctx->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL); if (ctx->have_space_ev) ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL); if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->close_ev) { - TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("CreateEvent failed: ec=%d", (int) GetLastError ()); if (ctx->have_data_ev) close_handle (ctx->have_data_ev); if (ctx->have_space_ev) close_handle (ctx->have_space_ev); if (ctx->close_ev) close_handle (ctx->close_ev); release_hddesc (ctx->hdd); free (ctx); TRACE_SYSERR_NR (EIO); return NULL; } INIT_LOCK (ctx->mutex); ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid); if (!ctx->thread_hd) { - TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("CreateThread failed: ec=%d", (int) GetLastError ()); DESTROY_LOCK (ctx->mutex); if (ctx->have_data_ev) close_handle (ctx->have_data_ev); if (ctx->have_space_ev) close_handle (ctx->have_space_ev); if (ctx->close_ev) close_handle (ctx->close_ev); release_hddesc (ctx->hdd); free (ctx); TRACE_SYSERR_NR (EIO); return NULL; } else { /* We set the priority of the thread higher because we know that it only runs for a short time. This greatly helps to increase the performance of the I/O. */ SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ()); } - TRACE_SUC (); + TRACE_SUC (""); return ctx; } /* Prepare destruction of the reader thread for CTX. Returns 0 if a call to this function is sufficient and destroy_reader_finish shall not be called. */ static void destroy_reader (struct reader_context_s *ctx) { LOCK (ctx->mutex); ctx->refcount--; if (ctx->refcount != 0) { - TRACE2 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx, + TRACE (DEBUG_SYSIO, "gpgme:destroy_reader", ctx, "hdd=%p refcount now %d", ctx->hdd, ctx->refcount); UNLOCK (ctx->mutex); return; } ctx->stop_me = 1; if (ctx->have_space_ev) SetEvent (ctx->have_space_ev); - TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx, + TRACE (DEBUG_SYSIO, "gpgme:destroy_reader", ctx, "hdd=%p close triggered", ctx->hdd); UNLOCK (ctx->mutex); /* The reader thread is usually blocking in recv or ReadFile. If the peer does not send an EOF or breaks the pipe the WFSO might get stuck waiting for the termination of the reader thread. This happens quite often with sockets, thus we definitely need to get out of the recv. A shutdown does this nicely. For handles (i.e. pipes) it would also be nice to cancel the operation, but such a feature is only available since Vista. Thus we need to dlopen that syscall. */ assert (ctx->hdd); if (ctx->hdd && ctx->hdd->hd != INVALID_HANDLE_VALUE) { _gpgme_w32_cancel_synchronous_io (ctx->thread_hd); } else if (ctx->hdd && ctx->hdd->sock != INVALID_SOCKET) { if (shutdown (ctx->hdd->sock, 2)) - TRACE2 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx, + TRACE (DEBUG_SYSIO, "gpgme:destroy_reader", ctx, "shutdown socket %d failed: %s", ctx->hdd->sock, (int) WSAGetLastError ()); } /* After setting this event CTX is void. */ SetEvent (ctx->close_ev); } /* Find a reader context or create a new one. Note that the reader * context will last until a _gpgme_io_close. NULL is returned for a * bad FD or for other errors. */ static struct reader_context_s * find_reader (int fd) { struct reader_context_s *rd = NULL; TRACE_BEG0 (DEBUG_SYSIO, "gpgme:find_reader", fd, ""); LOCK (fd_table_lock); if (fd < 0 || fd >= fd_table_size || !fd_table[fd].used) { UNLOCK (fd_table_lock); gpg_err_set_errno (EBADF); - TRACE_SUC0 ("EBADF"); + TRACE_SUC ("EBADF"); return NULL; } rd = fd_table[fd].reader; if (rd) { UNLOCK (fd_table_lock); - TRACE_SUC1 ("rd=%p", rd); + TRACE_SUC ("rd=%p", rd); return rd; /* Return already initialized reader thread object. */ } /* Create a new reader thread. */ - TRACE_LOG3 ("fd=%d -> hdd=%p dupfrom=%d creating reader", + TRACE_LOG ("fd=%d -> hdd=%p dupfrom=%d creating reader", fd, fd_table[fd].hdd, fd_table[fd].dup_from); rd = create_reader (fd_table[fd].hdd); if (!rd) gpg_err_set_errno (EIO); else fd_table[fd].reader = rd; UNLOCK (fd_table_lock); - TRACE_SUC1 ("rd=%p (new)", rd); + TRACE_SUC ("rd=%p (new)", rd); return rd; } int _gpgme_io_read (int fd, void *buffer, size_t count) { int nread; struct reader_context_s *ctx; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_read", fd, "buffer=%p, count=%u", buffer, count); ctx = find_reader (fd); if (!ctx) return TRACE_SYSRES (-1); if (ctx->eof_shortcut) return TRACE_SYSRES (0); LOCK (ctx->mutex); if (ctx->readpos == ctx->writepos && !ctx->error) { /* No data available. */ UNLOCK (ctx->mutex); - TRACE_LOG1 ("waiting for data from thread %p", ctx->thread_hd); + TRACE_LOG ("waiting for data from thread %p", ctx->thread_hd); wait_for_single_object (ctx->have_data_ev, INFINITE); - TRACE_LOG1 ("data from thread %p available", ctx->thread_hd); + TRACE_LOG ("data from thread %p available", ctx->thread_hd); LOCK (ctx->mutex); } if (ctx->readpos == ctx->writepos || ctx->error) { UNLOCK (ctx->mutex); ctx->eof_shortcut = 1; if (ctx->eof) return TRACE_SYSRES (0); if (!ctx->error) { TRACE_LOG ("EOF but ctx->eof flag not set"); return 0; } gpg_err_set_errno (ctx->error_code); return TRACE_SYSRES (-1); } nread = ctx->readpos < ctx->writepos ? ctx->writepos - ctx->readpos : READBUF_SIZE - ctx->readpos; if (nread > count) nread = count; memcpy (buffer, ctx->buffer + ctx->readpos, nread); ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE; if (ctx->readpos == ctx->writepos && !ctx->eof) { if (!ResetEvent (ctx->have_data_ev)) { - TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("ResetEvent failed: ec=%d", (int) GetLastError ()); UNLOCK (ctx->mutex); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } } if (!SetEvent (ctx->have_space_ev)) { - TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", + TRACE_LOG ("SetEvent (0x%x) failed: ec=%d", ctx->have_space_ev, (int) GetLastError ()); UNLOCK (ctx->mutex); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } UNLOCK (ctx->mutex); TRACE_LOGBUFX (buffer, nread); return TRACE_SYSRES (nread); } /* The writer does use a simple buffering strategy so that we are informed about write errors as soon as possible (i. e. with the the next call to the write function. */ static DWORD CALLBACK writer (void *arg) { struct writer_context_s *ctx = arg; DWORD nwritten; int sock; - TRACE_BEG4 (DEBUG_SYSIO, "gpgme:writer", ctx->hdd, + TRACE_BEG (DEBUG_SYSIO, "gpgme:writer", ctx->hdd, "hd=%p, sock=%d, thread=%p, refcount=%d", ctx->hdd->hd, ctx->hdd->sock, ctx->thread_hd, ctx->refcount); if (ctx->hdd->hd != INVALID_HANDLE_VALUE) sock = 0; else sock = 1; for (;;) { LOCK (ctx->mutex); if (ctx->stop_me && !ctx->nbytes) { UNLOCK (ctx->mutex); break; } if (!ctx->nbytes) { if (!SetEvent (ctx->is_empty)) - TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("SetEvent failed: ec=%d", (int) GetLastError ()); if (!ResetEvent (ctx->have_data)) - TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("ResetEvent failed: ec=%d", (int) GetLastError ()); UNLOCK (ctx->mutex); TRACE_LOG ("idle"); wait_for_single_object (ctx->have_data, INFINITE); TRACE_LOG ("got data to send"); LOCK (ctx->mutex); } if (ctx->stop_me && !ctx->nbytes) { UNLOCK (ctx->mutex); break; } UNLOCK (ctx->mutex); - TRACE_LOG2 ("%s %d bytes", sock?"sending":"writing", ctx->nbytes); + TRACE_LOG ("%s %d bytes", sock?"sending":"writing", ctx->nbytes); /* Note that CTX->nbytes is not zero at this point, because _gpgme_io_write always writes at least 1 byte before waking us up, unless CTX->stop_me is true, which we catch above. */ if (sock) { /* We need to try send first because a socket handle can't be used with WriteFile. */ int n; n = send (ctx->hdd->sock, ctx->buffer, ctx->nbytes, 0); if (n < 0) { ctx->error_code = (int) WSAGetLastError (); ctx->error = 1; - TRACE_LOG1 ("send error: ec=%d", ctx->error_code); + TRACE_LOG ("send error: ec=%d", ctx->error_code); break; } nwritten = n; } else { if (!WriteFile (ctx->hdd->hd, ctx->buffer, ctx->nbytes, &nwritten, NULL)) { if (GetLastError () == ERROR_BUSY) { /* Probably stop_me is set now. */ TRACE_LOG ("pipe busy (unblocked?)"); continue; } ctx->error_code = (int) GetLastError (); ctx->error = 1; - TRACE_LOG1 ("write error: ec=%d", ctx->error_code); + TRACE_LOG ("write error: ec=%d", ctx->error_code); break; } } - TRACE_LOG1 ("wrote %d bytes", (int) nwritten); + TRACE_LOG ("wrote %d bytes", (int) nwritten); LOCK (ctx->mutex); ctx->nbytes -= nwritten; UNLOCK (ctx->mutex); } /* Indicate that we have an error. */ if (!SetEvent (ctx->is_empty)) - TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("SetEvent failed: ec=%d", (int) GetLastError ()); TRACE_LOG ("waiting for close"); wait_for_single_object (ctx->close_ev, INFINITE); if (ctx->nbytes) - TRACE_LOG1 ("still %d bytes in buffer at close time", ctx->nbytes); + TRACE_LOG ("still %d bytes in buffer at close time", ctx->nbytes); release_hddesc (ctx->hdd); close_handle (ctx->close_ev); close_handle (ctx->have_data); close_handle (ctx->is_empty); close_handle (ctx->thread_hd); DESTROY_LOCK (ctx->mutex); free (ctx); - return TRACE_SUC (); + return TRACE_SUC (""); } static struct writer_context_s * create_writer (hddesc_t hdd) { struct writer_context_s *ctx; SECURITY_ATTRIBUTES sec_attr; DWORD tid; -TRACE_BEG3 (DEBUG_SYSIO, "gpgme:create_writer", hdd, +TRACE_BEG (DEBUG_SYSIO, "gpgme:create_writer", hdd, "handle=%p sock=%d refhdd=%d", hdd->hd, hdd->sock, hdd->refcount); memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; ctx = calloc (1, sizeof *ctx); if (!ctx) { TRACE_SYSERR_NR (errno); return NULL; } ctx->hdd = ref_hddesc (hdd); ctx->refcount = 1; ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL); if (ctx->have_data) ctx->is_empty = CreateEvent (&sec_attr, TRUE, TRUE, NULL); if (ctx->is_empty) ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL); if (!ctx->have_data || !ctx->is_empty || !ctx->close_ev) { - TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("CreateEvent failed: ec=%d", (int) GetLastError ()); if (ctx->have_data) close_handle (ctx->have_data); if (ctx->is_empty) close_handle (ctx->is_empty); if (ctx->close_ev) close_handle (ctx->close_ev); release_hddesc (ctx->hdd); free (ctx); TRACE_SYSERR_NR (EIO); return NULL; } INIT_LOCK (ctx->mutex); ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid ); if (!ctx->thread_hd) { - TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("CreateThread failed: ec=%d", (int) GetLastError ()); DESTROY_LOCK (ctx->mutex); if (ctx->have_data) close_handle (ctx->have_data); if (ctx->is_empty) close_handle (ctx->is_empty); if (ctx->close_ev) close_handle (ctx->close_ev); release_hddesc (ctx->hdd); free (ctx); TRACE_SYSERR_NR (EIO); return NULL; } else { /* We set the priority of the thread higher because we know that it only runs for a short time. This greatly helps to increase the performance of the I/O. */ SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ()); } - TRACE_SUC (); + TRACE_SUC (""); return ctx; } static void destroy_writer (struct writer_context_s *ctx) { LOCK (ctx->mutex); ctx->refcount--; if (ctx->refcount != 0) { - TRACE2 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx, + TRACE (DEBUG_SYSIO, "gpgme:destroy_writer", ctx, "hdd=%p refcount now %d", ctx->hdd, ctx->refcount); UNLOCK (ctx->mutex); return; } ctx->stop_me = 1; if (ctx->have_data) SetEvent (ctx->have_data); - TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx, + TRACE (DEBUG_SYSIO, "gpgme:destroy_writer", ctx, "hdd=%p close triggered", ctx->hdd); UNLOCK (ctx->mutex); /* Give the writer a chance to flush the buffer. */ wait_for_single_object (ctx->is_empty, INFINITE); /* After setting this event CTX is void. */ SetEvent (ctx->close_ev); } /* Find a writer context or create a new one. Note that the writer * context will last until a _gpgme_io_close. NULL is returned for a * bad FD or for other errors. */ static struct writer_context_s * find_writer (int fd) { struct writer_context_s *wt = NULL; TRACE_BEG0 (DEBUG_SYSIO, "gpgme:find_writer", fd, ""); LOCK (fd_table_lock); if (fd < 0 || fd >= fd_table_size || !fd_table[fd].used) { UNLOCK (fd_table_lock); gpg_err_set_errno (EBADF); - TRACE_SUC0 ("EBADF"); + TRACE_SUC ("EBADF"); return NULL; } wt = fd_table[fd].writer; if (wt) { UNLOCK (fd_table_lock); - TRACE_SUC1 ("wt=%p", wt); + TRACE_SUC ("wt=%p", wt); return wt; /* Return already initialized writer thread object. */ } /* Create a new writer thread. */ - TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d creating writer", + TRACE_LOG ("fd=%d -> handle=%p socket=%d dupfrom=%d creating writer", fd, fd_table[fd].hdd->hd, fd_table[fd].hdd->sock, fd_table[fd].dup_from); wt = create_writer (fd_table[fd].hdd); if (!wt) gpg_err_set_errno (EIO); else fd_table[fd].writer = wt; UNLOCK (fd_table_lock); - TRACE_SUC1 ("wt=%p (new)", wt); + TRACE_SUC ("wt=%p (new)", wt); return wt; } int _gpgme_io_write (int fd, const void *buffer, size_t count) { struct writer_context_s *ctx; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_write", fd, "buffer=%p, count=%u", buffer, count); TRACE_LOGBUFX (buffer, count); if (count == 0) return TRACE_SYSRES (0); ctx = find_writer (fd); if (!ctx) return TRACE_SYSRES (-1); LOCK (ctx->mutex); if (!ctx->error && ctx->nbytes) { /* Bytes are pending for send. */ /* Reset the is_empty event. Better safe than sorry. */ if (!ResetEvent (ctx->is_empty)) { - TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("ResetEvent failed: ec=%d", (int) GetLastError ()); UNLOCK (ctx->mutex); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } UNLOCK (ctx->mutex); - TRACE_LOG1 ("waiting for empty buffer in thread %p", ctx->thread_hd); + TRACE_LOG ("waiting for empty buffer in thread %p", ctx->thread_hd); wait_for_single_object (ctx->is_empty, INFINITE); - TRACE_LOG1 ("thread %p buffer is empty", ctx->thread_hd); + TRACE_LOG ("thread %p buffer is empty", ctx->thread_hd); LOCK (ctx->mutex); } if (ctx->error) { UNLOCK (ctx->mutex); if (ctx->error_code == ERROR_NO_DATA) gpg_err_set_errno (EPIPE); else gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } /* If no error occurred, the number of bytes in the buffer must be zero. */ assert (!ctx->nbytes); if (count > WRITEBUF_SIZE) count = WRITEBUF_SIZE; memcpy (ctx->buffer, buffer, count); ctx->nbytes = count; /* We have to reset the is_empty event early, because it is also * used by the select() implementation to probe the channel. */ if (!ResetEvent (ctx->is_empty)) { - TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("ResetEvent failed: ec=%d", (int) GetLastError ()); UNLOCK (ctx->mutex); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } if (!SetEvent (ctx->have_data)) { - TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("SetEvent failed: ec=%d", (int) GetLastError ()); UNLOCK (ctx->mutex); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } UNLOCK (ctx->mutex); return TRACE_SYSRES ((int) count); } int _gpgme_io_pipe (int filedes[2], int inherit_idx) { int rfd; int wfd; HANDLE rh; HANDLE wh; hddesc_t rhdesc; hddesc_t whdesc; SECURITY_ATTRIBUTES sec_attr; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, "inherit_idx=%i (GPGME uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); /* Get a new empty file descriptor. */ rfd = new_fd (); if (rfd == -1) return TRACE_SYSRES (-1); wfd = new_fd (); if (wfd == -1) { release_fd (rfd); return TRACE_SYSRES (-1); } rhdesc = new_hddesc (); if (!rhdesc) { release_fd (rfd); release_fd (wfd); return TRACE_SYSRES (-1); } whdesc = new_hddesc (); if (!whdesc) { release_fd (rfd); release_fd (wfd); release_hddesc (rhdesc); return TRACE_SYSRES (-1); } /* Create a pipe. */ memset (&sec_attr, 0, sizeof (sec_attr)); sec_attr.nLength = sizeof (sec_attr); sec_attr.bInheritHandle = FALSE; if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE)) { - TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("CreatePipe failed: ec=%d", (int) GetLastError ()); release_fd (rfd); release_fd (wfd); release_hddesc (rhdesc); release_hddesc (whdesc); gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } /* Make one end inheritable. */ if (inherit_idx == 0) { HANDLE hd; if (!DuplicateHandle (GetCurrentProcess(), rh, GetCurrentProcess(), &hd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { - TRACE_LOG1 ("DuplicateHandle failed: ec=%d", + TRACE_LOG ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); release_fd (rfd); release_fd (wfd); close_handle (rh); close_handle (wh); release_hddesc (rhdesc); release_hddesc (whdesc); gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } close_handle (rh); rh = hd; } else if (inherit_idx == 1) { HANDLE hd; if (!DuplicateHandle( GetCurrentProcess(), wh, GetCurrentProcess(), &hd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { - TRACE_LOG1 ("DuplicateHandle failed: ec=%d", + TRACE_LOG ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); release_fd (rfd); release_fd (wfd); close_handle (rh); close_handle (wh); release_hddesc (rhdesc); release_hddesc (whdesc); gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } close_handle (wh); wh = hd; } /* Put the HANDLEs of the new pipe into the file descriptor table. * Note that we don't need to lock the table because we have just * acquired these two fresh fds and they are not known by any other * thread. */ fd_table[rfd].want_reader = 1; ref_hddesc (rhdesc)->hd = rh; fd_table[rfd].hdd = rhdesc; fd_table[wfd].want_writer = 1; ref_hddesc (whdesc)->hd = wh; fd_table[wfd].hdd = whdesc; filedes[0] = rfd; filedes[1] = wfd; - return TRACE_SUC6 ("read=0x%x (hdd=%p,hd=%p), write=0x%x (hdd=%p,hd=%p)", + return TRACE_SUC ("read=0x%x (hdd=%p,hd=%p), write=0x%x (hdd=%p,hd=%p)", rfd, fd_table[rfd].hdd, fd_table[rfd].hdd->hd, wfd, fd_table[wfd].hdd, fd_table[wfd].hdd->hd); } /* Close out File descriptor FD. */ int _gpgme_io_close (int fd) { _gpgme_close_notify_handler_t handler = NULL; void *value = NULL; - TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd); + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd, ""); if (fd < 0) { gpg_err_set_errno (EBADF); return TRACE_SYSRES (-1); } LOCK (fd_table_lock); /* Check the size in the locked state because we may eventually add * code to change that size. */ if (fd >= fd_table_size || !fd_table[fd].used) { UNLOCK (fd_table_lock); gpg_err_set_errno (EBADF); return TRACE_SYSRES (-1); } - TRACE_LOG2 ("hdd=%p dupfrom=%d", fd_table[fd].hdd, fd_table[fd].dup_from); + TRACE_LOG ("hdd=%p dupfrom=%d", fd_table[fd].hdd, fd_table[fd].dup_from); if (fd_table[fd].reader) { - TRACE_LOG1 ("destroying reader %p", fd_table[fd].reader); + TRACE_LOG ("destroying reader %p", fd_table[fd].reader); destroy_reader (fd_table[fd].reader); fd_table[fd].reader = NULL; } if (fd_table[fd].writer) { - TRACE_LOG1 ("destroying writer %p", fd_table[fd].writer); + TRACE_LOG ("destroying writer %p", fd_table[fd].writer); destroy_writer (fd_table[fd].writer); fd_table[fd].writer = NULL; } /* The handler may not use any fd function because the table is * locked. Can we avoid this? */ handler = fd_table[fd].notify.handler; value = fd_table[fd].notify.value; /* Release our reference to the handle descriptor. Note that if no * reader or writer threads were used this release will also take * care that the handle descriptor is closed * (i.e. CloseHandle(hdd->hd) is called). */ release_hddesc (fd_table[fd].hdd); fd_table[fd].hdd = NULL; UNLOCK (fd_table_lock); /* Run the notification callback. */ if (handler) handler (fd, value); release_fd (fd); /* FIXME: We should have a release_fd_locked () */ return TRACE_SYSRES (0); } /* Set a close notification callback which is called right after FD * has been closed but before its slot (ie. the FD number) is being * released. The HANDLER may thus use the provided value of the FD * but it may not pass it to any I/O functions. Note: Only the last * handler set for an FD is used. */ int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler, void *value) { - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, "close_handler=%p/%p", handler, value); LOCK (fd_table_lock); if (fd < 0 || fd >= fd_table_size || !fd_table[fd].used) { UNLOCK (fd_table_lock); gpg_err_set_errno (EBADF); return TRACE_SYSRES (-1);; } fd_table[fd].notify.handler = handler; fd_table[fd].notify.value = value; UNLOCK (fd_table_lock); return TRACE_SYSRES (0); } int _gpgme_io_set_nonblocking (int fd) { - TRACE (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd); + TRACE (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd, ""); return 0; } static char * build_commandline (char **argv) { int i; int n = 0; char *buf; char *p; /* We have to quote some things because under Windows the program parses the commandline and does some unquoting. We enclose the whole argument in double-quotes, and escape literal double-quotes as well as backslashes with a backslash. We end up with a trailing space at the end of the line, but that is harmless. */ for (i = 0; argv[i]; i++) { p = argv[i]; /* The leading double-quote. */ n++; while (*p) { /* An extra one for each literal that must be escaped. */ if (*p == '\\' || *p == '"') n++; n++; p++; } /* The trailing double-quote and the delimiter. */ n += 2; } /* And a trailing zero. */ n++; buf = p = malloc (n); if (!buf) return NULL; for (i = 0; argv[i]; i++) { char *argvp = argv[i]; *(p++) = '"'; while (*argvp) { if (*argvp == '\\' || *argvp == '"') *(p++) = '\\'; *(p++) = *(argvp++); } *(p++) = '"'; *(p++) = ' '; } *(p++) = 0; return buf; } int _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, struct spawn_fd_item_s *fd_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, pid_t *r_pid) { PROCESS_INFORMATION pi = { NULL, /* returns process handle */ 0, /* returns primary thread handle */ 0, /* returns pid */ 0 /* returns tid */ }; int i; SECURITY_ATTRIBUTES sec_attr; STARTUPINFOA si; int cr_flags = CREATE_DEFAULT_ERROR_MODE; char **args; char *arg_string; /* FIXME. */ int debug_me = 0; int tmp_fd; char *tmp_name; const char *spawnhelper; - TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_spawn", path, "path=%s", path); (void)atfork; (void)atforkvalue; i = 0; while (argv[i]) { - TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]); + TRACE_LOG ("argv[%2i] = %s", i, argv[i]); i++; } /* We do not inherit any handles by default, and just insert those handles we want the child to have afterwards. But some handle values occur on the command line, and we need to move stdin/out/err to the right location. So we use a wrapper program which gets the information from a temporary file. */ if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0) { - TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno)); + TRACE_LOG ("_gpgme_mkstemp failed: %s", strerror (errno)); return TRACE_SYSRES (-1); } - TRACE_LOG1 ("tmp_name = %s", tmp_name); + TRACE_LOG ("tmp_name = %s", tmp_name); args = calloc (2 + i + 1, sizeof (*args)); args[0] = (char *) _gpgme_get_w32spawn_path (); args[1] = tmp_name; args[2] = (char *)path; memcpy (&args[3], &argv[1], i * sizeof (*args)); memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; arg_string = build_commandline (args); free (args); if (!arg_string) { close (tmp_fd); DeleteFileA (tmp_name); free (tmp_name); return TRACE_SYSRES (-1); } memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE; si.hStdInput = INVALID_HANDLE_VALUE; si.hStdOutput = INVALID_HANDLE_VALUE; si.hStdError = INVALID_HANDLE_VALUE; cr_flags |= CREATE_SUSPENDED; if ((flags & IOSPAWN_FLAG_DETACHED)) cr_flags |= DETACHED_PROCESS; cr_flags |= GetPriorityClass (GetCurrentProcess ()); spawnhelper = _gpgme_get_w32spawn_path (); if (!spawnhelper) { /* This is a common mistake for new users of gpgme not to include gpgme-w32spawn.exe with their binary. So we want to make this transparent to developers. If users have somehow messed up their installation this should also be properly communicated as otherwise calls to gnupg will result in unsupported protocol errors that do not explain a lot. */ char *msg; gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the " "detected installation directory of GpgME" "\n\t\"%s\"\n\n" "Crypto operations will not work.\n\n" "If you see this it indicates a problem " "with your installation.\n" "Please report the problem to your " "distributor of GpgME.\n\n" "Developer's Note: The install dir can be " "manually set with: gpgme_set_global_flag", _gpgme_get_inst_dir ()); MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK); gpgrt_free (msg); gpg_err_set_errno (EIO); close (tmp_fd); DeleteFileA (tmp_name); free (tmp_name); return TRACE_SYSRES (-1); } if (!CreateProcessA (spawnhelper, arg_string, &sec_attr, /* process security attributes */ &sec_attr, /* thread security attributes */ FALSE, /* inherit handles */ cr_flags, /* creation flags */ NULL, /* environment */ NULL, /* use current drive/directory */ &si, /* startup information */ &pi)) /* returns process information */ { int lasterr = (int)GetLastError (); - TRACE_LOG1 ("CreateProcess failed: ec=%d", lasterr); + TRACE_LOG ("CreateProcess failed: ec=%d", lasterr); free (arg_string); close (tmp_fd); DeleteFileA (tmp_name); free (tmp_name); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } if (flags & IOSPAWN_FLAG_ALLOW_SET_FG) _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId); /* Insert the inherited handles. */ LOCK (fd_table_lock); for (i = 0; fd_list[i].fd != -1; i++) { int fd = fd_list[i].fd; HANDLE ohd = INVALID_HANDLE_VALUE; HANDLE hd = INVALID_HANDLE_VALUE; /* Make it inheritable for the wrapper process. */ if (fd >= 0 && fd < fd_table_size && fd_table[fd].used && fd_table[fd].hdd) ohd = fd_table[fd].hdd->hd; if (!DuplicateHandle (GetCurrentProcess(), ohd, pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { - TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); TerminateProcess (pi.hProcess, 0); /* Just in case TerminateProcess didn't work, let the process fail on its own. */ ResumeThread (pi.hThread); close_handle (pi.hThread); close_handle (pi.hProcess); close (tmp_fd); DeleteFileA (tmp_name); free (tmp_name); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); UNLOCK (fd_table_lock); return TRACE_SYSRES (-1); } /* Return the child name of this handle. */ fd_list[i].peer_name = handle_to_fd (hd); } /* Write the handle translation information to the temporary file. */ { /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex notation: "0xFEDCBA9876543210" with an extra white space after every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead for a time when a HANDLE is 64 bit. */ #define BUFFER_MAX 810 char line[BUFFER_MAX + 1]; int res; int written; size_t len; if (flags) snprintf (line, BUFFER_MAX, "~%i \n", flags); else strcpy (line, "\n"); for (i = 0; fd_list[i].fd != -1; i++) { /* Strip the newline. */ len = strlen (line) - 1; /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */ snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n", fd_list[i].fd, fd_list[i].dup_to, fd_list[i].peer_name, fd_list[i].arg_loc); /* Rather safe than sorry. */ line[BUFFER_MAX - 1] = '\n'; line[BUFFER_MAX] = '\0'; } len = strlen (line); written = 0; do { res = write (tmp_fd, &line[written], len - written); if (res > 0) written += res; } while (res > 0 || (res < 0 && errno == EAGAIN)); } close (tmp_fd); /* The temporary file is deleted by the gpgme-w32spawn process (hopefully). */ free (tmp_name); free (arg_string); UNLOCK (fd_table_lock); - TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, " + TRACE_LOG ("CreateProcess ready: hProcess=%p, hThread=%p, " "dwProcessID=%d, dwThreadId=%d", pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId); if (r_pid) *r_pid = (pid_t)pi.dwProcessId; if (ResumeThread (pi.hThread) == (DWORD)(-1)) - TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ()); + TRACE_LOG ("ResumeThread failed: ec=%d", (int) GetLastError ()); close_handle (pi.hThread); - TRACE_LOG1 ("process=%p", pi.hProcess); + TRACE_LOG ("process=%p", pi.hProcess); /* We don't need to wait for the process. */ close_handle (pi.hProcess); if (! (flags & IOSPAWN_FLAG_NOCLOSE)) { for (i = 0; fd_list[i].fd != -1; i++) _gpgme_io_close (fd_list[i].fd); } for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].dup_to == -1) - TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, + TRACE_LOG ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].peer_name); else - TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd, + TRACE_LOG ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd, fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" : ((fd_list[i].dup_to == 1) ? "out" : "err")); return TRACE_SYSRES (0); } /* Select on the list of fds. Returns: -1 = error, 0 = timeout or nothing to select, > 0 = number of signaled fds. */ int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) { HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS]; int waitidx[MAXIMUM_WAIT_OBJECTS]; int code; int nwait; int i; int any; int count; void *dbg_help; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", fds, "nfds=%u, nonblock=%u", nfds, nonblock); #if 0 restart: #endif TRACE_SEQ (dbg_help, "select on [ "); any = 0; nwait = 0; count = 0; for (i=0; i < nfds; i++) { if (fds[i].fd == -1) continue; fds[i].signaled = 0; if (fds[i].for_read || fds[i].for_write) { if (fds[i].for_read) { /* FIXME: A find_reader_locked() along with separate * lock calls might be a better appaoched here. */ struct reader_context_s *ctx = find_reader (fds[i].fd); if (!ctx) - TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)", + TRACE_LOG ("error: no reader for FD 0x%x (ignored)", fds[i].fd); else { if (nwait >= DIM (waitbuf)) { TRACE_END (dbg_help, "oops ]"); TRACE_LOG ("Too many objects for WFMO!"); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } waitidx[nwait] = i; waitbuf[nwait++] = ctx->have_data_ev; } TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd); any = 1; } else if (fds[i].for_write) { struct writer_context_s *ctx = find_writer (fds[i].fd); if (!ctx) - TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)", + TRACE_LOG ("error: no writer for FD 0x%x (ignored)", fds[i].fd); else { if (nwait >= DIM (waitbuf)) { TRACE_END (dbg_help, "oops ]"); TRACE_LOG ("Too many objects for WFMO!"); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); } waitidx[nwait] = i; waitbuf[nwait++] = ctx->is_empty; } TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd); any = 1; } } } TRACE_END (dbg_help, "]"); if (!any) return TRACE_SYSRES (0); code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000); if (code < WAIT_OBJECT_0 + nwait) { /* The WFMO is a really silly function: It does return either the index of the signaled object or if 2 objects have been signalled at the same time, the index of the object with the lowest object is returned - so and how do we find out how many objects have been signaled?. The only solution I can imagine is to test each object starting with the returned index individually - how dull. */ any = 0; for (i = code - WAIT_OBJECT_0; i < nwait; i++) { if (wait_for_single_object (waitbuf[i], 0) == WAIT_OBJECT_0) { assert (waitidx[i] >=0 && waitidx[i] < nfds); fds[waitidx[i]].signaled = 1; any = 1; count++; } } if (!any) { TRACE_LOG ("no signaled objects found after WFMO"); count = -1; } } else if (code == WAIT_TIMEOUT) TRACE_LOG ("WFMO timed out"); else if (code == WAIT_FAILED) { int le = (int) GetLastError (); #if 0 if (le == ERROR_INVALID_HANDLE) { int k; int j = handle_to_fd (waitbuf[i]); - TRACE_LOG1 ("WFMO invalid handle %d removed", j); + TRACE_LOG ("WFMO invalid handle %d removed", j); for (k = 0 ; k < nfds; k++) { if (fds[k].fd == j) { fds[k].for_read = fds[k].for_write = 0; goto restart; } } TRACE_LOG (" oops, or not???"); } #endif - TRACE_LOG1 ("WFMO failed: %d", le); + TRACE_LOG ("WFMO failed: %d", le); count = -1; } else { - TRACE_LOG1 ("WFMO returned %d", code); + TRACE_LOG ("WFMO returned %d", code); count = -1; } if (count > 0) { TRACE_SEQ (dbg_help, "select OK [ "); for (i = 0; i < nfds; i++) { if (fds[i].fd == -1) continue; if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled) TRACE_ADD2 (dbg_help, "%c0x%x ", fds[i].for_read ? 'r' : 'w', fds[i].fd); } TRACE_END (dbg_help, "]"); } if (count < 0) { /* FIXME: Should determine a proper error code. */ gpg_err_set_errno (EIO); } return TRACE_SYSRES (count); } void _gpgme_io_subsystem_init (void) { /* Nothing to do. */ } /* Write the printable version of FD to BUFFER which has an allocated * length of BUFLEN. The printable version is the representation on * the command line that the child process expects. Note that this * works closely together with the gpgme-32spawn wrapper process which * translates these command line args to the real handles. */ int _gpgme_io_fd2str (char *buffer, int buflen, int fd) { return snprintf (buffer, buflen, "%d", fd); } int _gpgme_io_dup (int fd) { int newfd; struct reader_context_s *rd_ctx; struct writer_context_s *wt_ctx; int want_reader, want_writer; - TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd); + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd, ""); LOCK (fd_table_lock); if (fd < 0 || fd >= fd_table_size || !fd_table[fd].used) { UNLOCK (fd_table_lock); gpg_err_set_errno (EBADF); return TRACE_SYSRES (-1); } newfd = new_fd(); if (newfd == -1) { UNLOCK (fd_table_lock); gpg_err_set_errno (EMFILE); return TRACE_SYSRES (-1); } fd_table[newfd].hdd = ref_hddesc (fd_table[fd].hdd); fd_table[newfd].dup_from = fd; want_reader = fd_table[fd].want_reader; want_writer = fd_table[fd].want_writer; UNLOCK (fd_table_lock); rd_ctx = want_reader? find_reader (fd) : NULL; if (rd_ctx) { /* NEWFD initializes a freshly allocated slot and does not need * to be locked. */ LOCK (rd_ctx->mutex); rd_ctx->refcount++; UNLOCK (rd_ctx->mutex); fd_table[newfd].reader = rd_ctx; } wt_ctx = want_writer? find_writer (fd) : NULL; if (wt_ctx) { LOCK (wt_ctx->mutex); wt_ctx->refcount++; UNLOCK (wt_ctx->mutex); fd_table[newfd].writer = wt_ctx; } return TRACE_SYSRES (newfd); } /* The following interface is only useful for GPGME Glib and Qt. */ /* Compatibility interface, obsolete. */ void * gpgme_get_giochannel (int fd) { (void)fd; return NULL; } /* Look up the giochannel or qiodevice for file descriptor FD. */ void * gpgme_get_fdptr (int fd) { (void)fd; return NULL; } static int wsa2errno (int err) { switch (err) { case WSAENOTSOCK: return EINVAL; case WSAEWOULDBLOCK: return EAGAIN; case ERROR_BROKEN_PIPE: return EPIPE; case WSANOTINITIALISED: return ENOSYS; default: return EIO; } } int _gpgme_io_socket (int domain, int type, int proto) { int res; int fd; hddesc_t hdd; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_socket", domain, "type=%i, protp=%i", type, proto); fd = new_fd(); if (fd == -1) return TRACE_SYSRES (-1); hdd = new_hddesc (); if (!hdd) { UNLOCK (fd_table_lock); release_fd (fd); gpg_err_set_errno (ENOMEM); return TRACE_SYSRES (-1); } res = socket (domain, type, proto); if (res == INVALID_SOCKET) { release_fd (fd); gpg_err_set_errno (wsa2errno (WSAGetLastError ())); return TRACE_SYSRES (-1); } ref_hddesc (hdd)->sock = res; fd_table[fd].hdd = hdd; fd_table[fd].want_reader = 1; fd_table[fd].want_writer = 1; - TRACE_SUC3 ("hdd=%p, socket=0x%x (0x%x)", hdd, fd, hdd->sock); + TRACE_SUC ("hdd=%p, socket=0x%x (0x%x)", hdd, fd, hdd->sock); return fd; } int _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen) { int res; int sock; - TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd, + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_connect", fd, "addr=%p, addrlen=%i", addr, addrlen); LOCK (fd_table_lock); if (fd < 0 || fd >= fd_table_size || !fd_table[fd].used || !fd_table[fd].hdd) { gpg_err_set_errno (EBADF); UNLOCK (fd_table_lock); return TRACE_SYSRES (-1); } sock = fd_table[fd].hdd->sock; UNLOCK (fd_table_lock); res = connect (sock, addr, addrlen); if (res) { gpg_err_set_errno (wsa2errno (WSAGetLastError ())); return TRACE_SYSRES (-1); } - return TRACE_SUC (); + return TRACE_SUC (""); } diff --git a/src/w32-util.c b/src/w32-util.c index 7992b618..1f8ef6dc 100644 --- a/src/w32-util.c +++ b/src/w32-util.c @@ -1,795 +1,795 @@ /* w32-util.c - Utility functions for the W32 API * Copyright (C) 1999 Free Software Foundation, Inc * Copyright (C) 2001 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2007, 2013 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #include #if __MINGW64_VERSION_MAJOR >= 2 # define _WIN32_IE 0x0501 /* Required by mingw64 toolkit. */ #else # define _WIN32_IE 0x0400 /* Required for SHGetSpecialFolderPathA. */ #endif /* We need to include the windows stuff here prior to shlobj.h so that we get the right winsock version. This is usually done in util.h but that header also redefines some Windows functions which we need to avoid unless having included shlobj.h. */ #include #include #include #include #include "util.h" #include "ath.h" #include "sema.h" #include "debug.h" #include "sys-util.h" #define HAVE_ALLOW_SET_FOREGROUND_WINDOW 1 #ifndef F_OK # define F_OK 0 #endif /* The Registry key used by GNUPG. */ #ifdef _WIN64 # define GNUPG_REGKEY_2 "Software\\Wow6432Node\\GNU\\GnuPG" #else # define GNUPG_REGKEY_2 "Software\\GNU\\GnuPG" #endif #ifdef _WIN64 # define GNUPG_REGKEY_3 "Software\\Wow6432Node\\GnuPG" #else # define GNUPG_REGKEY_3 "Software\\GnuPG" #endif DEFINE_STATIC_LOCK (get_path_lock); /* The module handle of this DLL. If we are linked statically, dllmain does not exists and thus the value of my_hmodule will be NULL. The effect is that a GetModuleFileName always returns the file name of the DLL or executable which contains the gpgme code. */ static HMODULE my_hmodule; /* These variables store the malloced name of alternative default binaries. The are set only once by gpgme_set_global_flag. */ static char *default_gpg_name; static char *default_gpgconf_name; /* If this variable is not NULL the value is assumed to be the installation directory. The variable may only be set once by gpgme_set_global_flag and accessed by _gpgme_get_inst_dir. */ static char *override_inst_dir; #define RTLD_LAZY 0 static GPG_ERR_INLINE void * dlopen (const char * name, int flag) { void * hd = LoadLibrary (name); (void)flag; return hd; } static GPG_ERR_INLINE void * dlsym (void * hd, const char * sym) { if (hd && sym) { void * fnc = GetProcAddress (hd, sym); if (!fnc) return NULL; return fnc; } return NULL; } static GPG_ERR_INLINE int dlclose (void * hd) { if (hd) { FreeLibrary (hd); return 0; } return -1; } /* Return a malloced string encoded in UTF-8 from the wide char input string STRING. Caller must free this value. Returns NULL and sets ERRNO on failure. Calling this function with STRING set to NULL is not defined. */ static char * wchar_to_utf8 (const wchar_t *string) { int n; char *result; n = WideCharToMultiByte (CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL); if (n < 0) { gpg_err_set_errno (EINVAL); return NULL; } result = malloc (n+1); if (!result) return NULL; n = WideCharToMultiByte (CP_UTF8, 0, string, -1, result, n, NULL, NULL); if (n < 0) { free (result); gpg_err_set_errno (EINVAL); result = NULL; } return result; } /* Replace all forward slashes by backslashes. */ static void replace_slashes (char *string) { for (; *string; string++) if (*string == '/') *string = '\\'; } /* Get the base name of NAME. Returns a pointer into NAME right after the last slash or backslash or to NAME if no slash or backslash exists. */ static const char * get_basename (const char *name) { const char *mark, *s; for (mark=NULL, s=name; *s; s++) if (*s == '/' || *s == '\\') mark = s; return mark? mark+1 : name; } void _gpgme_allow_set_foreground_window (pid_t pid) { #ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW static int initialized; static BOOL (WINAPI * func)(DWORD); void *handle; if (!initialized) { /* Available since W2000; thus we dynload it. */ initialized = 1; handle = dlopen ("user32.dll", RTLD_LAZY); if (handle) { func = dlsym (handle, "AllowSetForegroundWindow"); if (!func) { dlclose (handle); handle = NULL; } } } if (!pid || pid == (pid_t)(-1)) { - TRACE1 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, + TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, "no action for pid %d", (int)pid); } else if (func) { int rc = func (pid); - TRACE2 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, + TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, "called for pid %d; result=%d", (int)pid, rc); } else { - TRACE0 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, + TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, "function not available"); } #endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */ } /* Wrapper around CancelSynchronousIo which is only available since * Vista. */ void _gpgme_w32_cancel_synchronous_io (HANDLE thread) { static int initialized; static BOOL (WINAPI * func)(DWORD); void *handle; if (!initialized) { /* Available since Vista; thus we dynload it. */ initialized = 1; handle = dlopen ("kernel32.dll", RTLD_LAZY); if (handle) { func = dlsym (handle, "CancelSynchronousIo"); if (!func) { dlclose (handle); handle = NULL; } } } if (func) { if (!func (thread) && GetLastError() != ERROR_NOT_FOUND) { - TRACE2 (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", 0, + TRACE (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", 0, "called for thread %p: ec=%d", thread, GetLastError ()); } } else { - TRACE0 (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", 0, + TRACE (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", 0, "function not available"); } } /* Return a string from the W32 Registry or NULL in case of error. Caller must release the return value. A NULL for root is an alias for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */ static char * read_w32_registry_string (const char *root, const char *dir, const char *name) { HKEY root_key, key_handle; DWORD n1, nbytes, type; char *result = NULL; if (!root) root_key = HKEY_CURRENT_USER; else if (!strcmp( root, "HKEY_CLASSES_ROOT")) root_key = HKEY_CLASSES_ROOT; else if (!strcmp( root, "HKEY_CURRENT_USER")) root_key = HKEY_CURRENT_USER; else if (!strcmp( root, "HKEY_LOCAL_MACHINE")) root_key = HKEY_LOCAL_MACHINE; else if (!strcmp( root, "HKEY_USERS")) root_key = HKEY_USERS; else if (!strcmp( root, "HKEY_PERFORMANCE_DATA")) root_key = HKEY_PERFORMANCE_DATA; else if (!strcmp( root, "HKEY_CURRENT_CONFIG")) root_key = HKEY_CURRENT_CONFIG; else return NULL; if (RegOpenKeyExA (root_key, dir, 0, KEY_READ, &key_handle)) { if (root) return NULL; /* no need for a RegClose, so return direct */ /* It seems to be common practise to fall back to HKLM. */ if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) return NULL; /* still no need for a RegClose, so return direct */ } nbytes = 1; if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes)) { if (root) goto leave; /* Try to fallback to HKLM also vor a missing value. */ RegCloseKey (key_handle); if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) return NULL; /* Nope. */ if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes)) goto leave; } n1 = nbytes + 1; result = malloc (n1); if (!result) goto leave; if (RegQueryValueExA (key_handle, name, 0, &type, (LPBYTE) result, &n1)) { free (result); result = NULL; goto leave; } result[nbytes] = 0; /* Make sure it is really a string. */ leave: RegCloseKey (key_handle); return result; } /* Return the name of the directory with the gpgme DLL or the EXE (if statically linked). May return NULL on severe errors. */ const char * _gpgme_get_inst_dir (void) { static char *inst_dir; if (override_inst_dir) return override_inst_dir; LOCK (get_path_lock); if (!inst_dir) { wchar_t *moddir; moddir = malloc ((MAX_PATH+5) * sizeof *moddir); if (moddir) { if (!GetModuleFileNameW (my_hmodule, moddir, MAX_PATH)) *moddir = 0; if (!*moddir) gpg_err_set_errno (ENOENT); else { inst_dir = wchar_to_utf8 (moddir); if (inst_dir) { char *p = strrchr (inst_dir, '\\'); if (p) *p = 0; } } free (moddir); } } UNLOCK (get_path_lock); return inst_dir; } static char * find_program_in_dir (const char *dir, const char *name) { char *result; result = _gpgme_strconcat (dir, "\\", name, NULL); if (!result) return NULL; if (access (result, F_OK)) { free (result); return NULL; } return result; } static char * find_program_at_standard_place (const char *name) { char path[MAX_PATH]; char *result = NULL; /* See https://wiki.tcl-lang.org/page/Getting+Windows+%22special+folders%22+with+Ffidl for details on compatibility. We First try the generic place and then fallback to the x86 (i.e. 32 bit) place. This will prefer a 64 bit of the program over a 32 bit version on 64 bit Windows if installed. */ if (SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILES, 0)) { result = _gpgme_strconcat (path, "\\", name, NULL); if (result && access (result, F_OK)) { free (result); result = NULL; } } if (!result && SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILESX86, 0)) { result = _gpgme_strconcat (path, "\\", name, NULL); if (result && access (result, F_OK)) { free (result); result = NULL; } } return result; } /* Set the default name for the gpg binary. This function may only be called by gpgme_set_global_flag. Returns 0 on success. */ int _gpgme_set_default_gpg_name (const char *name) { if (!default_gpg_name) { default_gpg_name = _gpgme_strconcat (name, ".exe", NULL); if (default_gpg_name) replace_slashes (default_gpg_name); } return !default_gpg_name; } /* Set the default name for the gpgconf binary. This function may only be called by gpgme_set_global_flag. Returns 0 on success. */ int _gpgme_set_default_gpgconf_name (const char *name) { if (!default_gpgconf_name) { default_gpgconf_name = _gpgme_strconcat (name, ".exe", NULL); if (default_gpgconf_name) replace_slashes (default_gpgconf_name); } return !default_gpgconf_name; } /* Set the override installation directory. This function may only be called by gpgme_set_global_flag. Returns 0 on success. */ int _gpgme_set_override_inst_dir (const char *dir) { if (!override_inst_dir) { override_inst_dir = strdup (dir); if (override_inst_dir) { replace_slashes (override_inst_dir); /* Remove a trailing slash. */ if (*override_inst_dir && override_inst_dir[strlen (override_inst_dir)-1] == '\\') override_inst_dir[strlen (override_inst_dir)-1] = 0; } } return !override_inst_dir; } /* Return the full file name of the GPG binary. This function is used iff gpgconf was not found and thus it can be assumed that gpg2 is not installed. This function is only called by get_gpgconf_item and may not be called concurrently. */ char * _gpgme_get_gpg_path (void) { char *gpg = NULL; const char *name, *inst_dir; name = default_gpg_name? get_basename (default_gpg_name) : "gpg.exe"; /* 1. Try to find gpg.exe in the installation directory of gpgme. */ inst_dir = _gpgme_get_inst_dir (); if (inst_dir) { gpg = find_program_in_dir (inst_dir, name); } /* 2. Try to find gpg.exe using that ancient registry key. */ if (!gpg) { char *dir; dir = read_w32_registry_string ("HKEY_LOCAL_MACHINE", GNUPG_REGKEY_2, "Install Directory"); if (dir) { gpg = find_program_in_dir (dir, name); free (dir); } } /* 3. Try to find gpg.exe below CSIDL_PROGRAM_FILES. */ if (!gpg) { name = default_gpg_name? default_gpg_name : "GNU\\GnuPG\\gpg.exe"; gpg = find_program_at_standard_place (name); } /* 4. Print a debug message if not found. */ if (!gpg) _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpg_path: '%s' not found", name); return gpg; } /* This function is only called by get_gpgconf_item and may not be called concurrently. */ char * _gpgme_get_gpgconf_path (void) { char *gpgconf = NULL; const char *inst_dir, *name; name = default_gpgconf_name? get_basename(default_gpgconf_name):"gpgconf.exe"; /* 1. Try to find gpgconf.exe in the installation directory of gpgme. */ inst_dir = _gpgme_get_inst_dir (); if (inst_dir) { gpgconf = find_program_in_dir (inst_dir, name); } /* 2. Try to find gpgconf.exe from GnuPG >= 2.1 below CSIDL_PROGRAM_FILES. */ if (!gpgconf) { const char *name2 = (default_gpgconf_name ? default_gpgconf_name /**/ : "GnuPG\\bin\\gpgconf.exe"); gpgconf = find_program_at_standard_place (name2); } /* 3. Try to find gpgconf.exe using the Windows registry. */ if (!gpgconf) { char *dir; dir = read_w32_registry_string (NULL, GNUPG_REGKEY_2, "Install Directory"); if (!dir) { char *tmp = read_w32_registry_string (NULL, GNUPG_REGKEY_3, "Install Directory"); if (tmp) { dir = _gpgme_strconcat (tmp, "\\bin", NULL); free (tmp); if (!dir) return NULL; } } if (dir) { gpgconf = find_program_in_dir (dir, name); free (dir); } } /* 4. Try to find gpgconf.exe from Gpg4win below CSIDL_PROGRAM_FILES. */ if (!gpgconf) { gpgconf = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe"); } /* 5. Try to find gpgconf.exe relative to us. */ if (!gpgconf && inst_dir) { char *dir = _gpgme_strconcat (inst_dir, "\\..\\..\\GnuPG\\bin"); gpgconf = find_program_in_dir (dir, name); free (dir); } /* 5. Print a debug message if not found. */ if (!gpgconf) _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpgconf_path: '%s' not found",name); return gpgconf; } const char * _gpgme_get_w32spawn_path (void) { static char *w32spawn_program; const char *inst_dir; inst_dir = _gpgme_get_inst_dir (); LOCK (get_path_lock); if (!w32spawn_program) w32spawn_program = find_program_in_dir (inst_dir, "gpgme-w32spawn.exe"); UNLOCK (get_path_lock); return w32spawn_program; } /* Return an integer value from gpgme specific configuration entries. VALUE receives that value; function returns true if a value has been configured and false if not. */ int _gpgme_get_conf_int (const char *key, int *value) { char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key); if (!tmp) return 0; *value = atoi (tmp); free (tmp); return 1; } /* mkstemp extracted from libc/sysdeps/posix/tempname.c. Copyright (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. */ static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; /* Generate a temporary file name based on TMPL. TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed does not exist at the time of the call to mkstemp. TMPL is overwritten with the result. */ static int my_mkstemp (char *tmpl) { int len; char *XXXXXX; static uint64_t value; uint64_t random_time_bits; unsigned int count; int fd = -1; int save_errno = errno; /* A lower bound on the number of temporary files to attempt to generate. The maximum total number of temporary file names that can exist for a given template is 62**6. It should never be necessary to try all these combinations. Instead if a reasonable number of names is tried (we define reasonable as 62**3) fail to give the system administrator the chance to remove the problems. */ #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX unsigned int attempts = TMP_MAX; #else unsigned int attempts = ATTEMPTS_MIN; #endif len = strlen (tmpl); if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) { gpg_err_set_errno (EINVAL); return -1; } /* This is where the Xs start. */ XXXXXX = &tmpl[len - 6]; /* Get some more or less random data. */ { FILETIME ft; GetSystemTimeAsFileTime (&ft); random_time_bits = (((uint64_t)ft.dwHighDateTime << 32) | (uint64_t)ft.dwLowDateTime); } value += random_time_bits ^ ath_self (); for (count = 0; count < attempts; value += 7777, ++count) { uint64_t v = value; /* Fill in the random bits. */ XXXXXX[0] = letters[v % 62]; v /= 62; XXXXXX[1] = letters[v % 62]; v /= 62; XXXXXX[2] = letters[v % 62]; v /= 62; XXXXXX[3] = letters[v % 62]; v /= 62; XXXXXX[4] = letters[v % 62]; v /= 62; XXXXXX[5] = letters[v % 62]; fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd >= 0) { gpg_err_set_errno (save_errno); return fd; } else if (errno != EEXIST) return -1; } /* We got out of the loop because we ran out of combinations to try. */ gpg_err_set_errno (EEXIST); return -1; } int _gpgme_mkstemp (int *fd, char **name) { char tmp[MAX_PATH + 2]; char *tmpname; int err; *fd = -1; *name = NULL; err = GetTempPathA (MAX_PATH + 1, tmp); if (err == 0 || err > MAX_PATH + 1) strcpy (tmp,"c:\\windows\\temp"); else { int len = strlen(tmp); /* GetTempPath may return with \ on the end */ while(len > 0 && tmp[len - 1] == '\\') { tmp[len-1] = '\0'; len--; } } tmpname = _gpgme_strconcat (tmp, "\\gpgme-XXXXXX", NULL); if (!tmpname) return -1; *fd = my_mkstemp (tmpname); if (*fd < 0) { free (tmpname); return -1; } *name = tmpname; return 0; } /* Entry point called by the DLL loader. */ #ifdef DLL_EXPORT int WINAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved) { (void)reserved; if (reason == DLL_PROCESS_ATTACH) my_hmodule = hinst; return TRUE; } #endif /*DLL_EXPORT*/ diff --git a/src/wait.c b/src/wait.c index a011b123..1da9e93d 100644 --- a/src/wait.c +++ b/src/wait.c @@ -1,222 +1,222 @@ /* wait.c * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include "util.h" #include "context.h" #include "ops.h" #include "wait.h" #include "sema.h" #include "priv-io.h" #include "engine.h" #include "debug.h" void _gpgme_fd_table_init (fd_table_t fdt) { fdt->fds = NULL; fdt->size = 0; } void _gpgme_fd_table_deinit (fd_table_t fdt) { if (fdt->fds) free (fdt->fds); } /* XXX We should keep a marker and roll over for speed. */ static gpgme_error_t fd_table_put (fd_table_t fdt, int fd, int dir, void *opaque, int *idx) { unsigned int i, j; struct io_select_fd_s *new_fds; for (i = 0; i < fdt->size; i++) { if (fdt->fds[i].fd == -1) break; } if (i == fdt->size) { #define FDT_ALLOCSIZE 10 new_fds = realloc (fdt->fds, (fdt->size + FDT_ALLOCSIZE) * sizeof (*new_fds)); if (!new_fds) return gpg_error_from_syserror (); fdt->fds = new_fds; fdt->size += FDT_ALLOCSIZE; for (j = 0; j < FDT_ALLOCSIZE; j++) fdt->fds[i + j].fd = -1; } fdt->fds[i].fd = fd; fdt->fds[i].for_read = (dir == 1); fdt->fds[i].for_write = (dir == 0); fdt->fds[i].signaled = 0; fdt->fds[i].opaque = opaque; *idx = i; return 0; } /* Register the file descriptor FD with the handler FNC (which gets FNC_DATA as its first argument) for the direction DIR. DATA should be the context for which the fd is added. R_TAG will hold the tag that can be used to remove the fd. */ gpgme_error_t _gpgme_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc, void *fnc_data, void **r_tag) { gpgme_error_t err; gpgme_ctx_t ctx = (gpgme_ctx_t) data; fd_table_t fdt; struct wait_item_s *item; struct tag *tag; assert (fnc); assert (ctx); fdt = &ctx->fdt; assert (fdt); tag = malloc (sizeof *tag); if (!tag) return gpg_error_from_syserror (); tag->ctx = ctx; /* Allocate a structure to hold information about the handler. */ item = calloc (1, sizeof *item); if (!item) { free (tag); return gpg_error_from_syserror (); } item->ctx = ctx; item->dir = dir; item->handler = fnc; item->handler_value = fnc_data; err = fd_table_put (fdt, fd, dir, item, &tag->idx); if (err) { free (tag); free (item); return err; } - TRACE3 (DEBUG_CTX, "_gpgme_add_io_cb", ctx, + TRACE (DEBUG_CTX, "_gpgme_add_io_cb", ctx, "fd %d, dir=%d -> tag=%p", fd, dir, tag); *r_tag = tag; return 0; } void _gpgme_remove_io_cb (void *data) { struct tag *tag = data; gpgme_ctx_t ctx; fd_table_t fdt; int idx; assert (tag); ctx = tag->ctx; assert (ctx); fdt = &ctx->fdt; assert (fdt); idx = tag->idx; - TRACE2 (DEBUG_CTX, "_gpgme_remove_io_cb", data, + TRACE (DEBUG_CTX, "_gpgme_remove_io_cb", data, "setting fd 0x%x (item=%p) done", fdt->fds[idx].fd, fdt->fds[idx].opaque); free (fdt->fds[idx].opaque); free (tag); /* Free the table entry. */ fdt->fds[idx].fd = -1; fdt->fds[idx].for_read = 0; fdt->fds[idx].for_write = 0; fdt->fds[idx].opaque = NULL; } /* This is slightly embarrassing. The problem is that running an I/O callback _may_ influence the status of other file descriptors. Our own event loops could compensate for that, but the external event loops cannot. FIXME: We may still want to optimize this a bit when we are called from our own event loops. So if CHECKED is 1, the check is skipped. */ gpgme_error_t _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked, gpgme_error_t *op_err) { struct wait_item_s *item; struct io_cb_data iocb_data; gpgme_error_t err; item = (struct wait_item_s *) an_fds->opaque; assert (item); if (!checked) { int nr; struct io_select_fd_s fds; - TRACE0 (DEBUG_CTX, "_gpgme_run_io_cb", item, "need to check"); + TRACE (DEBUG_CTX, "_gpgme_run_io_cb", item, "need to check"); fds = *an_fds; fds.signaled = 0; /* Just give it a quick poll. */ nr = _gpgme_io_select (&fds, 1, 1); assert (nr <= 1); if (nr < 0) return errno; else if (nr == 0) /* The status changed in the meantime, there is nothing left to do. */ return 0; } - TRACE2 (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)", + TRACE (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)", item->handler_value, an_fds->fd); iocb_data.handler_value = item->handler_value; iocb_data.op_err = 0; err = item->handler (&iocb_data, an_fds->fd); *op_err = iocb_data.op_err; return err; }