diff --git a/Makefile.am b/Makefile.am index 4ffb113..ba01f60 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,75 +1,76 @@ # Assuan top level Makefile # Copyright (C) 2001, 2002, 2003, 2007, 2011 Free Software Foundation, Inc. # # This file is part of Assuan. # # Assuan 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. # # Assuan 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+ ## Process this file with automake to produce Makefile.in ACLOCAL_AMFLAGS = -I m4 DISTCHECK_CONFIGURE_FLAGS = --enable-doc # (A suitable gitlog-to-changelog script can be found in GnuPG master.) GITLOG_TO_CHANGELOG=gitlog-to-changelog EXTRA_DIST = autogen.sh autogen.rc README.GIT \ ChangeLog-2011 doc/ChangeLog-2011 src/ChangeLog-2011 \ tests/ChangeLog-2011 contrib/ChangeLog-2011 \ build-aux/git-log-footer build-aux/git-log-fix if BUILD_DOC doc = doc else doc = endif SUBDIRS = m4 src $(doc) tests dist-hook: gen-ChangeLog echo "$(VERSION)" > $(distdir)/VERSION distcheck-hook: set -e; ( \ pref="#+macro: $$(echo $(PACKAGE_NAME)|tr '-' '_')_" ;\ reldate="$$(date -u +%Y-%m-%d)" ;\ echo "$${pref}ver $(PACKAGE_VERSION)" ;\ echo "$${pref}date $${reldate}" ;\ list='$(DIST_ARCHIVES)'; for i in $$list; do \ case "$$i" in *.tar.bz2) \ echo "$${pref}size $$(wc -c <$$i|awk '{print int($$1/1024)}')k" ;\ echo "$${pref}sha1 $$(sha1sum <$$i|cut -d' ' -f1)" ;\ echo "$${pref}sha2 $$(sha256sum <$$i|cut -d' ' -f1)" ;;\ esac;\ done ) | tee $(distdir).swdb gen_start_date = 2011-12-01T00:00:00 .PHONY: gen-ChangeLog gen-ChangeLog: set -e; \ if test -d $(top_srcdir)/.git; then \ (cd $(top_srcdir) && \ $(GITLOG_TO_CHANGELOG) --append-dot --tear-off \ --amend=build-aux/git-log-fix --tear-off \ --since=$(gen_start_date) ) > $(distdir)/cl-t; \ cat $(top_srcdir)/build-aux/git-log-footer >> $(distdir)/cl-t;\ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi stowinstall: $(MAKE) $(AM_MAKEFLAGS) install prefix=/usr/local/stow/libassuan diff --git a/configure.ac b/configure.ac index 118e047..731449d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,494 +1,495 @@ # configure.ac - for libassuan # Copyright (C) 2001, 2002, 2003, 2006, 2007, 2009, # 2011, 2012, 2013 Free Software Foundation, Inc. # Copyright (C) 2013, 2014, 2015 g10 Code GmbH # # This file is part of Assuan. # # Assuan 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. # # Assuan 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+ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) min_automake_version="1.14" # To build a release you need to create a tag with the version number # (git tag -s libassuan-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],[libassuan]) m4_define([mym4_major], [2]) m4_define([mym4_minor], [4]) m4_define([mym4_micro], [4]) # To start a new development series, i.e a new major or minor number # you need to mark an arbitrary commit before the first beta release # with an annotated tag. For example a 2.1 branch starts off with # the tag "foo-2.1-base". This is used as the base for counting # beta numbers before the first release of a series. # 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)) AC_INIT([mym4_package],[mym4_version], [http://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/changed: AGE=0) # LIBASSUAN_LT_CURRENT=7 LIBASSUAN_LT_AGE=7 LIBASSUAN_LT_REVISION=3 # If the API is changed in an incompatible way: increment the next counter. LIBASSUAN_CONFIG_API_VERSION=2 ############################################## AC_SUBST(LIBASSUAN_LT_CURRENT) AC_SUBST(LIBASSUAN_LT_AGE) AC_SUBST(LIBASSUAN_LT_REVISION) PACKAGE=$PACKAGE_NAME VERSION=$PACKAGE_VERSION AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip]) AM_MAINTAINER_MODE AC_CONFIG_SRCDIR(src/assuan.h.in) AC_CONFIG_MACRO_DIR(m4) AM_CONFIG_HEADER(config.h) AC_CANONICAL_HOST AM_SILENT_RULES AB_INIT AC_GNU_SOURCE AC_SUBST(PACKAGE) AC_SUBST(VERSION) AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package]) AC_DEFINE_UNQUOTED(PACKAGE_BUGREPORT, "$PACKAGE_BUGREPORT",[Bug report address]) VERSION_NUMBER=m4_esyscmd(printf "0x%02x%02x%02x" mym4_major \ mym4_minor mym4_micro) AC_SUBST(VERSION_NUMBER) # 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 check_descriptor_passing=yes case "${host}" in *-*-cygwin*) check_descriptor_passing=no ;; *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; *-apple-darwin*) AC_DEFINE(_XOPEN_SOURCE, 500, Activate POSIX interface on MacOS X) AC_DEFINE(_DARWIN_C_SOURCE, 900000L, Expose all libc features (__DARWIN_C_FULL)) ;; esac AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") AH_TOP([ #ifndef _ASSUAN_CONFIG_H_INCLUDED #define _ASSUAN_CONFIG_H_INCLUDED /* Enable gpg-error's strerror macro under W32CE. */ #define GPG_ERR_ENABLE_ERRNO_MACROS 1 /* Provide the es_ macro for estream. */ #define GPGRT_ENABLE_ES_MACROS 1 ]) AH_BOTTOM([ #endif /*_ASSUAN_CONFIG_H_INCLUDED*/ ]) # Checks for programs. missing_dir=`cd $ac_aux_dir && pwd` AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir) AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir) AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) AC_PROG_AWK AC_PROG_CC AC_PROG_CPP AM_PROG_CC_C_O if test "x$ac_cv_prog_cc_c89" = "xno" ; then AC_MSG_ERROR([[No C-89 compiler found]]) fi AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET #AC_ARG_PROGRAM # 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]) if test "$GCC" = yes; then CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes" 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 # # Options depending on the host OS. # have_dosish_system=no have_w32_system=no have_w64_system=no have_w32ce_system=no case "${host}" in *-linux*) if test "$GCC" = yes; then CFLAGS="$CFLAGS -fPIC -DPIC" fi ;; x86_64-*mingw32*) have_dosish_system=yes have_w32_system=yes have_w64_system=yes ;; *-mingw32ce*) have_dosish_system=yes have_w32_system=yes have_w32ce_system=yes ;; *-mingw32*) have_dosish_system=yes have_w32_system=yes ;; *-solaris*) AC_DEFINE(_XOPEN_SOURCE, 500, Activate extensions on Solaris) AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, Activate extensions on Solaris) AC_DEFINE(__EXTENSIONS__, 1, Activate extensions on Solaris) ;; 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 if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1,[Defined if we run on a W32 API based system]) if test "$have_w64_system" = yes; then AC_DEFINE(HAVE_W64_SYSTEM,1, [Defined if we run on a 64 bit W32 API based system]) fi if test "$have_w32ce_system" = yes; then AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE]) fi fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes) AM_CONDITIONAL(HAVE_W64_SYSTEM, test "$have_w64_system" = yes) # # 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_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'` changequote([,])dnl BUILD_FILEVERSION="${BUILD_FILEVERSION}mym4_revision_dec" 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]) # # Check for network libraries. They are needed for tests. # AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt, [NETLIBS="-lsocket $NETLIBS"])) AC_SUBST(NETLIBS) if test "$have_w32_system" = yes; then if test "$have_w32ce_system" = yes; then NETLIBS="-lws2 $NETLIBS" else # FIXME: Check why we need to use ws2_32 and document that. NETLIBS="-lws2_32 $NETLIBS" fi fi # # Provide info for src/libassuan-config.in # LIBASSUAN_CONFIG_LIB="-lassuan" LIBASSUAN_CONFIG_CFLAGS="" LIBASSUAN_CONFIG_HOST="$host" LIBASSUAN_CONFIG_EXTRA_LIBS= if test x"$NETLIBS" != x; then LIBASSUAN_CONFIG_EXTRA_LIBS="$LIBASSUAN_CONFIG_EXTRA_LIBS $NETLIBS" fi AC_SUBST(LIBASSUAN_CONFIG_LIB) AC_SUBST(LIBASSUAN_CONFIG_CFLAGS) AC_SUBST(LIBASSUAN_CONFIG_HOST) AC_SUBST(LIBASSUAN_CONFIG_API_VERSION) AC_SUBST(LIBASSUAN_CONFIG_EXTRA_LIBS) # # Checks for header files. # AC_HEADER_STDC AC_CHECK_HEADERS([string.h locale.h sys/uio.h stdint.h inttypes.h \ sys/types.h sys/stat.h unistd.h sys/time.h fcntl.h \ sys/select.h ]) AC_TYPE_UINTPTR_T AC_TYPE_UINT16_T # # Checks for typedefs, structures, and compiler characteristics. # AC_C_CONST AC_C_INLINE AC_TYPE_SIZE_T AC_TYPE_SIGNAL AC_DECL_SYS_SIGLIST gl_HEADER_SYS_SOCKET gl_TYPE_SOCKLEN_T if test $check_descriptor_passing != yes; then use_descriptor_passing=no else AC_CHECK_MEMBER(struct cmsghdr.cmsg_len, [use_descriptor_passing=yes], [use_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 ]) fi if test "$use_descriptor_passing" = "yes"; then AC_DEFINE(USE_DESCRIPTOR_PASSING, 1, [Defined if descriptor passing is supported]) fi AM_CONDITIONAL(USE_DESCRIPTOR_PASSING, test "$use_descriptor_passing" = "yes") # Checking for libgpg-error. AM_PATH_GPG_ERROR(1.17,, AC_MSG_ERROR([libgpg-error was not found])) # # Checks for library functions. # AC_CHECK_FUNCS([flockfile funlockfile inet_pton stat getaddrinfo \ getrlimit ]) # If we didn't find inet_pton, it might be in -lsocket (which might # require -lnsl) if test X"$ac_cv_func_inet_pton" != X"yes" ; then AC_SEARCH_LIBS([inet_pton],[socket],[],[],[-lnsl]) if test X"$ac_cv_search_inet_pton" != X"no" ; then AC_DEFINE([HAVE_INET_PTON],1,[Define to 1 if you have `inet_pton'.]) fi fi # On some systems (e.g. Solaris) nanosleep requires linking to librl. # Given that we use nanosleep only as an optimization over a select # based wait function we want it only if it is available in libc. _save_libs="$LIBS" AC_SEARCH_LIBS([nanosleep], [], [AC_DEFINE(HAVE_NANOSLEEP,1, [Define to 1 if you have the `nanosleep' function in libc.])]) LIBS="$_save_libs" # Check for funopen AC_CHECK_FUNCS(funopen) if test $ac_cv_func_funopen != yes; then # No funopen but we can implement that in terms of fopencookie. AC_CHECK_FUNCS(fopencookie) if test $ac_cv_func_fopencookie = yes; then AC_LIBOBJ([funopen]) else AC_MSG_WARN([ *** *** No implementation of fopencookie or funopen available. *** The assuan_get_data_fp function won't work; see the *** manual for details. GnuPG does not require this feature. ***]) fi fi AC_REPLACE_FUNCS(isascii) AC_REPLACE_FUNCS(putc_unlocked) AC_REPLACE_FUNCS(memrchr) AC_REPLACE_FUNCS(stpcpy) AC_CHECK_HEADERS(unistd.h) AC_REPLACE_FUNCS(setenv) # # Check for the getsockopt SO_PEERCRED # AC_MSG_CHECKING(for SO_PEERCRED) AC_CACHE_VAL(assuan_cv_sys_so_peercred, [AC_TRY_COMPILE([#include ], [struct ucred cr; int cl = sizeof cr; getsockopt (1, SOL_SOCKET, SO_PEERCRED, &cr, &cl);], assuan_cv_sys_so_peercred=yes, assuan_cv_sys_so_peercred=no) ]) AC_MSG_RESULT($assuan_cv_sys_so_peercred) if test $assuan_cv_sys_so_peercred = yes; then AC_DEFINE(HAVE_SO_PEERCRED, 1, [Defined if SO_PEERCRED is supported (Linux specific)]) else # Check for the getsockopt LOCAL_PEEREID (NetBSD) AC_MSG_CHECKING(for LOCAL_PEEREID) AC_CACHE_VAL(assuan_cv_sys_so_local_peereid, [AC_TRY_COMPILE([#include #include ], [struct unpcbid unp; int unpl = sizeof unp; getsockopt (1, SOL_SOCKET, LOCAL_PEEREID, &unp, &unpl);], assuan_cv_sys_so_local_peereid=yes, assuan_cv_sys_so_local_peereid=no) ]) AC_MSG_RESULT($assuan_cv_sys_so_local_peereid) if test $assuan_cv_sys_so_local_peereid = yes; then AC_DEFINE(HAVE_LOCAL_PEEREID, 1, [Defined if LOCAL_PEEREID is supported (NetBSD specific)]) else # (Open)Solaris AC_CHECK_FUNCS([getpeerucred], AC_CHECK_HEADERS([ucred.h])) if test $ac_cv_func_getpeerucred != yes; then # FreeBSD AC_CHECK_FUNCS([getpeereid]) fi fi fi # # Extra features # build_doc=yes AC_ARG_ENABLE([doc], AC_HELP_STRING([--disable-doc], [do not build the documentation]), build_doc=$enableval, build_doc=yes) AM_CONDITIONAL([BUILD_DOC], [test "x$build_doc" != xno]) # # Create the config files. # AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([m4/Makefile]) AC_CONFIG_FILES([src/Makefile]) AC_CONFIG_FILES([doc/Makefile]) AC_CONFIG_FILES([tests/Makefile]) AC_CONFIG_FILES([src/libassuan-config], [chmod +x src/libassuan-config]) AC_CONFIG_FILES([src/versioninfo.rc]) AC_OUTPUT echo " Libassuan v${VERSION} has been configured as follows: Revision: mym4_revision (mym4_revision_dec) Platform: $host " diff --git a/src/Makefile.am b/src/Makefile.am index 8feef3a..f353e29 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,152 +1,154 @@ # Assuan Makefile # Copyright (C) 2001, 2002, 2003, 2009, 2010 Free Software Foundation, Inc. # # This file is part of Assuan. # # Assuan 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. # # Assuan 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+ ## Process this file with automake to produce Makefile.in EXTRA_DIST = libassuan-config.in libassuan.m4 libassuan.vers \ versioninfo.rc.in libassuan.def mkheader.c gpgcedev.def AM_CPPFLAGS = -I.. bin_SCRIPTS = libassuan-config m4datadir = $(datadir)/aclocal m4data_DATA = libassuan.m4 lib_LTLIBRARIES = libassuan.la if HAVE_W32CE_SYSTEM lib_LTLIBRARIES += libgpgcedev.la bin_PROGRAMS = gpgcemgr endif nodist_include_HEADERS = assuan.h if HAVE_LD_VERSION_SCRIPT libassuan_version_script_cmd = -Wl,--version-script=$(srcdir)/libassuan.vers else libassuan_version_script_cmd = endif CLEANFILES = mkheader assuan.h BUILT_SOURCES = assuan.h parts_of_assuan_h = \ posix-includes.inc.h w32-includes.inc.h \ posix-types.inc.h w32-types.inc.h \ posix-fd-t.inc.h w32-fd-t.inc.h w32ce-fd-t.inc.h \ posix-sock-nonce.inc.h w32-sock-nonce.inc.h \ posix-sys-pth-impl.h w32-sys-pth-impl.h \ w32ce-add.h common_sources = \ assuan.h.in $(parts_of_assuan_h) \ assuan-defs.h \ assuan.c context.c system.c \ debug.c debug.h conversion.c sysutils.c \ client.c server.c \ assuan-error.c \ assuan-buffer.c \ assuan-handler.c \ assuan-inquire.c \ assuan-listen.c \ assuan-pipe-server.c \ assuan-socket-server.c \ assuan-pipe-connect.c \ assuan-socket-connect.c \ assuan-uds.c \ assuan-logging.c \ assuan-socket.c if HAVE_W32_SYSTEM if HAVE_W32CE_SYSTEM common_sources += system-w32ce.c else common_sources += system-w32.c endif else common_sources += system-posix.c endif if HAVE_W32_SYSTEM LTRCCOMPILE = $(LIBTOOL) --mode=compile $(RC) \ `echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \ sed -e 's/-I/--include-dir /g;s/-D/--define /g'` SUFFIXES: .rc .lo .rc.lo: $(LTRCCOMPILE) -i $< -o $@ libassuan_res = versioninfo.lo libassuan_res_ldflag = -Wl,.libs/versioninfo.o no_undefined = -no-undefined export_symbols = -export-symbols $(srcdir)/libassuan.def extra_ltoptions = -XCClinker -static-libgcc install-def-file: $(INSTALL) $(srcdir)/libassuan.def $(DESTDIR)$(libdir)/libassuan.def uninstall-def-file: -rm $(DESTDIR)$(libdir)/libassuan.def libassuan_deps = $(libassuan_res) libassuan.def else libassuan_res = libassuan_res_ldflag = no_undefined = export_symbols = extra_ltoptions = install-def-file: uninstall-def-file: libassuan_deps = endif libassuan_la_SOURCES = $(common_sources) assuan-io.c nodist_libassuan_la_SOURCES = assuan.h libassuan_la_CPPFLAGS = $(AM_CPPFLAGS) @GPG_ERROR_CFLAGS@ libassuan_la_LDFLAGS = $(libassuan_res_ldflag) $(no_undefined) \ $(extra_ltoptions) \ $(export_symbols) $(libassuan_version_script_cmd) -version-info \ @LIBASSUAN_LT_CURRENT@:@LIBASSUAN_LT_REVISION@:@LIBASSUAN_LT_AGE@ libassuan_la_DEPENDENCIES = @LTLIBOBJS@ \ $(srcdir)/libassuan.vers $(libassuan_deps) libassuan_la_LIBADD = @LTLIBOBJS@ @NETLIBS@ @GPG_ERROR_LIBS@ if HAVE_W32CE_SYSTEM libgpgcedev_la_SOURCES = gpgcedev.c libgpgcedev_la_CPPFLAGS = $(AM_CPPFLAGS) libgpgcedev_la_LDFLAGS = $(no_undefined) -export-symbols $(srcdir)/gpgcedev.def libgpgcedev_la_DEPENDENCIES = gpgcedev.def gpgcemgr_SOURCES = gpgcemgr.c gpgcemgr_CPPFLAGS = $(AM_CPPFLAGS) install-exec-hook: mv -f $(DESTDIR)$(bindir)/libgpgcedev-0.dll \ $(DESTDIR)$(bindir)/gpgcedev.dll endif mkheader: mkheader.c Makefile $(CC_FOR_BUILD) -I. -I$(srcdir) -o $@ $(srcdir)/mkheader.c assuan.h: assuan.h.in mkheader $(parts_of_assuan_h) ./mkheader $(host_os) $(srcdir)/assuan.h.in \ @VERSION@ @VERSION_NUMBER@ >$@ diff --git a/src/assuan-buffer.c b/src/assuan-buffer.c index ef9ba41..be87280 100644 --- a/src/assuan-buffer.c +++ b/src/assuan-buffer.c @@ -1,548 +1,550 @@ /* assuan-buffer.c - read and send data - Copyright (C) 2001-2004, 2006, 2009, 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2001-2004, 2006, 2009, 2010 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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+ */ #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #ifdef HAVE_W32_SYSTEM #ifndef HAVE_W32CE_SYSTEM # include #endif #endif #include "assuan-defs.h" /* Extended version of write(2) to guarantee that all bytes are written. Returns 0 on success or -1 and ERRNO on failure. NOTE: This function does not return the number of bytes written, so any error must be treated as fatal for this connection as the state of the receiver is unknown. This works best if blocking is allowed (so EAGAIN cannot occur). */ static int writen (assuan_context_t ctx, const char *buffer, size_t length) { while (length) { ssize_t nwritten = ctx->engine.writefnc (ctx, buffer, length); if (nwritten < 0) { if (errno == EINTR) continue; return -1; /* write error */ } length -= nwritten; buffer += nwritten; } return 0; /* okay */ } /* Read an entire line. Returns 0 on success or -1 and ERRNO on failure. EOF is indictated by setting the integer at address R_EOF. Note: BUF, R_NREAD and R_EOF contain a valid result even if an error is returned. */ static int readline (assuan_context_t ctx, char *buf, size_t buflen, int *r_nread, int *r_eof) { size_t nleft = buflen; char *p; *r_eof = 0; *r_nread = 0; while (nleft > 0) { ssize_t n = ctx->engine.readfnc (ctx, buf, nleft); if (n < 0) { if (errno == EINTR) continue; #ifdef HAVE_W32_SYSTEM if (errno == EPIPE) { /* Under Windows we get EPIPE (actually ECONNRESET) after termination of the client. Assume an EOF. */ *r_eof = 1; break; /* allow incomplete lines */ } #endif /*HAVE_W32_SYSTEM*/ return -1; /* read error */ } else if (!n) { *r_eof = 1; break; /* allow incomplete lines */ } p = buf; nleft -= n; buf += n; *r_nread += n; p = memrchr (p, '\n', n); if (p) break; /* at least one full line available - that's enough for now */ } return 0; } /* Read a line with buffering of partial lines. Function returns an Assuan error. */ gpg_error_t _assuan_read_line (assuan_context_t ctx) { gpg_error_t rc = 0; char *line = ctx->inbound.line; int nread, atticlen; char *endp = 0; if (ctx->inbound.eof) return _assuan_error (ctx, GPG_ERR_EOF); atticlen = ctx->inbound.attic.linelen; if (atticlen) { memcpy (line, ctx->inbound.attic.line, atticlen); ctx->inbound.attic.linelen = 0; endp = memchr (line, '\n', atticlen); if (endp) { /* Found another line in the attic. */ nread = atticlen; atticlen = 0; } else { /* There is pending data but not a full line. */ assert (atticlen < LINELENGTH); rc = readline (ctx, line + atticlen, LINELENGTH - atticlen, &nread, &ctx->inbound.eof); } } else /* No pending data. */ rc = readline (ctx, line, LINELENGTH, &nread, &ctx->inbound.eof); if (rc) { int saved_errno = errno; char buf[100]; snprintf (buf, sizeof buf, "error: %s", strerror (errno)); _assuan_log_control_channel (ctx, 0, buf, NULL, 0, NULL, 0); if (saved_errno == EAGAIN) { /* We have to save a partial line. Due to readline's behaviour, we know that this is not a complete line yet (no newline). So we don't set PENDING to true. */ memcpy (ctx->inbound.attic.line, line, atticlen + nread); ctx->inbound.attic.pending = 0; ctx->inbound.attic.linelen = atticlen + nread; } gpg_err_set_errno (saved_errno); return _assuan_error (ctx, gpg_err_code_from_syserror ()); } if (!nread) { assert (ctx->inbound.eof); _assuan_log_control_channel (ctx, 0, "eof", NULL, 0, NULL, 0); return _assuan_error (ctx, GPG_ERR_EOF); } ctx->inbound.attic.pending = 0; nread += atticlen; if (! endp) endp = memchr (line, '\n', nread); if (endp) { unsigned monitor_result; int n = endp - line + 1; if (n < nread) /* LINE contains more than one line. We copy it to the attic now as handlers are allowed to modify the passed buffer. */ { int len = nread - n; memcpy (ctx->inbound.attic.line, endp + 1, len); ctx->inbound.attic.pending = memrchr (endp + 1, '\n', len) ? 1 : 0; ctx->inbound.attic.linelen = len; } if (endp != line && endp[-1] == '\r') endp --; *endp = 0; ctx->inbound.linelen = endp - line; monitor_result = 0; if (ctx->io_monitor) monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 0, ctx->inbound.line, ctx->inbound.linelen); if (monitor_result & ASSUAN_IO_MONITOR_IGNORE) ctx->inbound.linelen = 0; if ( !(monitor_result & ASSUAN_IO_MONITOR_NOLOG)) _assuan_log_control_channel (ctx, 0, NULL, ctx->inbound.line, ctx->inbound.linelen, NULL, 0); return 0; } else { _assuan_log_control_channel (ctx, 0, "invalid line", NULL, 0, NULL, 0); *line = 0; ctx->inbound.linelen = 0; return _assuan_error (ctx, ctx->inbound.eof ? GPG_ERR_ASS_INCOMPLETE_LINE : GPG_ERR_ASS_LINE_TOO_LONG); } } /* Read the next line from the client or server and return a pointer in *LINE to a buffer holding the line. LINELEN is the length of *LINE. The buffer is valid until the next read operation on it. The caller may modify the buffer. The buffer is invalid (i.e. must not be used) if an error is returned. Returns 0 on success or an assuan error code. See also: assuan_pending_line(). */ gpg_error_t assuan_read_line (assuan_context_t ctx, char **line, size_t *linelen) { gpg_error_t err; if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); do { err = _assuan_read_line (ctx); } while (_assuan_error_is_eagain (ctx, err)); *line = ctx->inbound.line; *linelen = ctx->inbound.linelen; return err; } /* Return true if a full line is buffered (i.e. an entire line may be read without any I/O). */ int assuan_pending_line (assuan_context_t ctx) { return ctx && ctx->inbound.attic.pending; } gpg_error_t _assuan_write_line (assuan_context_t ctx, const char *prefix, const char *line, size_t len) { gpg_error_t rc = 0; size_t prefixlen = prefix? strlen (prefix):0; unsigned int monitor_result; /* Make sure that the line is short enough. */ if (len + prefixlen + 2 > ASSUAN_LINELENGTH) { _assuan_log_control_channel (ctx, 1, "supplied line too long - truncated", NULL, 0, NULL, 0); if (prefixlen > 5) prefixlen = 5; if (len > ASSUAN_LINELENGTH - prefixlen - 2) len = ASSUAN_LINELENGTH - prefixlen - 2 - 1; } monitor_result = 0; if (ctx->io_monitor) monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 1, line, len); /* Fixme: we should do some kind of line buffering. */ if (!(monitor_result & ASSUAN_IO_MONITOR_NOLOG)) _assuan_log_control_channel (ctx, 1, NULL, prefixlen? prefix:NULL, prefixlen, line, len); if (prefixlen && !(monitor_result & ASSUAN_IO_MONITOR_IGNORE)) { rc = writen (ctx, prefix, prefixlen); if (rc) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); } if (!rc && !(monitor_result & ASSUAN_IO_MONITOR_IGNORE)) { rc = writen (ctx, line, len); if (rc) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); if (!rc) { rc = writen (ctx, "\n", 1); if (rc) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); } } return rc; } gpg_error_t assuan_write_line (assuan_context_t ctx, const char *line) { size_t len; const char *str; if (! ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); /* Make sure that we never take a LF from the user - this might violate the protocol. */ str = strchr (line, '\n'); len = str ? (str - line) : strlen (line); if (str) _assuan_log_control_channel (ctx, 1, "supplied line with LF - truncated", NULL, 0, NULL, 0); return _assuan_write_line (ctx, NULL, line, len); } /* Write out the data in buffer as datalines with line wrapping and percent escaping. This function is used for GNU's custom streams. */ int _assuan_cookie_write_data (void *cookie, const char *buffer, size_t orig_size) { assuan_context_t ctx = cookie; size_t size = orig_size; char *line; size_t linelen; if (ctx->outbound.data.error) return 0; line = ctx->outbound.data.line; linelen = ctx->outbound.data.linelen; line += linelen; while (size) { unsigned int monitor_result; /* Insert data line header. */ if (!linelen) { *line++ = 'D'; *line++ = ' '; linelen += 2; } /* Copy data, keep space for the CRLF and to escape one character. */ while (size && linelen < LINELENGTH-2-2) { if (*buffer == '%' || *buffer == '\r' || *buffer == '\n') { sprintf (line, "%%%02X", *(unsigned char*)buffer); line += 3; linelen += 3; buffer++; } else { *line++ = *buffer++; linelen++; } size--; } monitor_result = 0; if (ctx->io_monitor) monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 1, ctx->outbound.data.line, linelen); if (linelen >= LINELENGTH-2-2) { if (!(monitor_result & ASSUAN_IO_MONITOR_NOLOG)) _assuan_log_control_channel (ctx, 1, NULL, ctx->outbound.data.line, linelen, NULL, 0); *line++ = '\n'; linelen++; if ( !(monitor_result & ASSUAN_IO_MONITOR_IGNORE) && writen (ctx, ctx->outbound.data.line, linelen)) { ctx->outbound.data.error = gpg_err_code_from_syserror (); return 0; } line = ctx->outbound.data.line; linelen = 0; } } ctx->outbound.data.linelen = linelen; return (int) orig_size; } /* Write out any buffered data This function is used for GNU's custom streams */ int _assuan_cookie_write_flush (void *cookie) { assuan_context_t ctx = cookie; char *line; size_t linelen; unsigned int monitor_result; if (ctx->outbound.data.error) return 0; line = ctx->outbound.data.line; linelen = ctx->outbound.data.linelen; line += linelen; monitor_result = 0; if (ctx->io_monitor) monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 1, ctx->outbound.data.line, linelen); if (linelen) { if (!(monitor_result & ASSUAN_IO_MONITOR_NOLOG)) _assuan_log_control_channel (ctx, 1, NULL, ctx->outbound.data.line, linelen, NULL, 0); *line++ = '\n'; linelen++; if (! (monitor_result & ASSUAN_IO_MONITOR_IGNORE) && writen (ctx, ctx->outbound.data.line, linelen)) { ctx->outbound.data.error = gpg_err_code_from_syserror (); return 0; } ctx->outbound.data.linelen = 0; } return 0; } /** * assuan_send_data: * @ctx: An assuan context * @buffer: Data to send or NULL to flush * @length: length of the data to send/ * * This function may be used by the server or the client to send data * lines. The data will be escaped as required by the Assuan protocol * and may get buffered until a line is full. To force sending the * data out @buffer may be passed as NULL (in which case @length must * also be 0); however when used by a client this flush operation does * also send the terminating "END" command to terminate the response on * a INQUIRE response. However, when assuan_transact() is used, this * function takes care of sending END itself. * * If BUFFER is NULL and LENGTH is 1 and we are a client, a "CAN" is * send instead of an "END". * * Return value: 0 on success or an error code **/ gpg_error_t assuan_send_data (assuan_context_t ctx, const void *buffer, size_t length) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!buffer && length > 1) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!buffer) { /* flush what we have */ _assuan_cookie_write_flush (ctx); if (ctx->outbound.data.error) return ctx->outbound.data.error; if (!ctx->is_server) return assuan_write_line (ctx, length == 1? "CAN":"END"); } else { _assuan_cookie_write_data (ctx, buffer, length); if (ctx->outbound.data.error) return ctx->outbound.data.error; } return 0; } gpg_error_t assuan_sendfd (assuan_context_t ctx, assuan_fd_t fd) { /* It is explicitly allowed to use (NULL, -1) as a runtime test to check whether descriptor passing is available. */ if (!ctx && fd == ASSUAN_INVALID_FD) #ifdef USE_DESCRIPTOR_PASSING return 0; #else return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED); #endif if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (! ctx->engine.sendfd) return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, "server does not support sending and receiving " "of file descriptors"); return ctx->engine.sendfd (ctx, fd); } gpg_error_t assuan_receivefd (assuan_context_t ctx, assuan_fd_t *fd) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (! ctx->engine.receivefd) return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, "server does not support sending and receiving " "of file descriptors"); return ctx->engine.receivefd (ctx, fd); } diff --git a/src/assuan-defs.h b/src/assuan-defs.h index 97b053d..6688198 100644 --- a/src/assuan-defs.h +++ b/src/assuan-defs.h @@ -1,434 +1,435 @@ /* assuan-defs.h - Internal definitions to Assuan - Copyright (C) 2001, 2002, 2004, 2005, 2007, 2008, - 2009, 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2001, 2002, 2004, 2005, 2007, 2008, + * 2009, 2010 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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+ */ #ifndef ASSUAN_DEFS_H #define ASSUAN_DEFS_H #ifdef HAVE_SYS_TYPES_H # include #endif #ifndef HAVE_W32_SYSTEM # include # include #else # ifdef HAVE_WINSOCK2_H # /* Avoid inclusion of winsock.h via windows.h. */ # include # endif # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "assuan.h" #if __GNUC__ > 2 # define ASSUAN_GCC_A_PURE __attribute__ ((__pure__)) #else # define ASSUAN_GCC_A_PURE #endif #ifndef HAVE_W32_SYSTEM #define DIRSEP_C '/' #else #define DIRSEP_C '\\' #endif #define LINELENGTH ASSUAN_LINELENGTH struct cmdtbl_s { const char *name; assuan_handler_t handler; const char *helpstr; }; /* The context we use with most functions. */ struct assuan_context_s { /* Members managed by the generic routines in assuan.c. */ /* The error source for errors generated from this context. */ gpg_err_source_t err_source; #ifdef HAVE_W32_SYSTEM /* The per-context w32 error string. */ char w32_strerror[256]; #endif /* The allocation hooks. */ struct assuan_malloc_hooks malloc_hooks; /* Logging callback handler. */ assuan_log_cb_t log_cb; void *log_cb_data; void *user_pointer; /* Context specific flags (cf. assuan_flag_t). */ struct { unsigned int no_waitpid : 1; unsigned int confidential : 1; unsigned int no_fixsignals : 1; unsigned int convey_comments : 1; unsigned int no_logging : 1; unsigned int force_close : 1; } flags; /* If set, this is called right before logging an I/O line. */ assuan_io_monitor_t io_monitor; void *io_monitor_data; /* Callback handlers replacing system I/O functions. */ struct assuan_system_hooks system; int peercred_valid; /* Whether this structure has valid information. */ struct _assuan_peercred peercred; /* Now come the members specific to subsystems or engines. FIXME: This is not developed yet. See below for the legacy members. */ struct { void (*release) (assuan_context_t ctx); /* Routine to read from input_fd. Sets errno on failure. */ ssize_t (*readfnc) (assuan_context_t, void *, size_t); /* Routine to write to output_fd. Sets errno on failure. */ ssize_t (*writefnc) (assuan_context_t, const void *, size_t); /* Send a file descriptor. */ gpg_error_t (*sendfd) (assuan_context_t, assuan_fd_t); /* Receive a file descriptor. */ gpg_error_t (*receivefd) (assuan_context_t, assuan_fd_t *); } engine; /* Engine specific or other subsystem members. */ /* assuan-logging.c. Does not require deallocation from us. */ FILE *log_fp; /* assuan-util.c */ gpg_error_t err_no; const char *err_str; int is_server; /* Set if this is context belongs to a server */ int in_inquire; int in_process_next; int process_complete; int in_command; /* The following members are used by assuan_inquire_ext. */ gpg_error_t (*inquire_cb) (void *cb_data, gpg_error_t rc, unsigned char *buf, size_t len); void *inquire_cb_data; void *inquire_membuf; char *hello_line; char *okay_line; /* See assuan_set_okay_line() */ struct { assuan_fd_t fd; int eof; char line[LINELENGTH]; int linelen; /* w/o CR, LF - might not be the same as strlen(line) due to embedded nuls. However a nul is always written at this pos. */ struct { char line[LINELENGTH]; int linelen ; int pending; /* i.e. at least one line is available in the attic */ } attic; } inbound; struct { assuan_fd_t fd; struct { FILE *fp; char line[LINELENGTH]; int linelen; int error; } data; } outbound; int max_accepts; /* If we can not handle more than one connection, set this to 1, otherwise to -1. */ pid_t pid; /* The pid of the peer. */ assuan_fd_t listen_fd; /* The fd we are listening on (used by socket servers) */ assuan_sock_nonce_t listen_nonce; /* Used with LISTEN_FD. */ assuan_fd_t connected_fd; /* helper */ /* Used for Unix domain sockets. */ struct sockaddr_un myaddr; struct sockaddr_un serveraddr; /* Structure used for unix domain sockets. */ struct { assuan_fd_t pendingfds[5]; /* Array to save received descriptors. */ int pendingfdscount; /* Number of received descriptors. */ } uds; gpg_error_t (*accept_handler)(assuan_context_t); void (*finish_handler)(assuan_context_t); struct cmdtbl_s *cmdtbl; size_t cmdtbl_used; /* used entries */ size_t cmdtbl_size; /* allocated size of table */ /* The name of the command currently processed by a command handler. This is a pointer into CMDTBL. NULL if not in a command handler. */ const char *current_cmd_name; assuan_handler_t bye_notify_fnc; assuan_handler_t reset_notify_fnc; assuan_handler_t cancel_notify_fnc; gpg_error_t (*option_handler_fnc)(assuan_context_t,const char*, const char*); assuan_handler_t input_notify_fnc; assuan_handler_t output_notify_fnc; /* This function is called right before a command handler is called. */ gpg_error_t (*pre_cmd_notify_fnc)(assuan_context_t, const char *cmd); /* This function is called right after a command has been processed. It may be used to command related cleanup. */ void (*post_cmd_notify_fnc)(assuan_context_t, gpg_error_t); assuan_fd_t input_fd; /* Set by the INPUT command. */ assuan_fd_t output_fd; /* Set by the OUTPUT command. */ }; /* Generate an error code specific to a context. */ static GPG_ERR_INLINE gpg_error_t _assuan_error (assuan_context_t ctx, gpg_err_code_t errcode) { return gpg_err_make (ctx?ctx->err_source: GPG_ERR_SOURCE_ASSUAN, errcode); } /* Release all resources associated with an engine operation. */ void _assuan_reset (assuan_context_t ctx); /* Default log handler. */ int _assuan_log_handler (assuan_context_t ctx, void *hook, unsigned int cat, const char *msg); /* Manage memory specific to a context. */ void *_assuan_malloc (assuan_context_t ctx, size_t cnt); void *_assuan_realloc (assuan_context_t ctx, void *ptr, size_t cnt); void *_assuan_calloc (assuan_context_t ctx, size_t cnt, size_t elsize); void _assuan_free (assuan_context_t ctx, void *ptr); /* System hooks. */ void _assuan_usleep (assuan_context_t ctx, unsigned int usec); int _assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx); int _assuan_close (assuan_context_t ctx, assuan_fd_t fd); int _assuan_close_inheritable (assuan_context_t ctx, assuan_fd_t fd); ssize_t _assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size); ssize_t _assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size); int _assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags); int _assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags); int _assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char *argv[], assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags); pid_t _assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, int *status, int options); int _assuan_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]); int _assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol); int _assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr, socklen_t length); extern struct assuan_system_hooks _assuan_system_hooks; /* Copy the system hooks struct, paying attention to version differences. SRC is usually from the user, DST MUST be from the library. */ void _assuan_system_hooks_copy (assuan_system_hooks_t dst, assuan_system_hooks_t src); /*-- assuan-pipe-server.c --*/ void _assuan_release_context (assuan_context_t ctx); /*-- assuan-uds.c --*/ void _assuan_uds_close_fds (assuan_context_t ctx); void _assuan_uds_deinit (assuan_context_t ctx); void _assuan_init_uds_io (assuan_context_t ctx); /*-- assuan-handler.c --*/ gpg_error_t _assuan_register_std_commands (assuan_context_t ctx); /*-- assuan-buffer.c --*/ gpg_error_t _assuan_read_line (assuan_context_t ctx); int _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size); int _assuan_cookie_write_flush (void *cookie); gpg_error_t _assuan_write_line (assuan_context_t ctx, const char *prefix, const char *line, size_t len); /*-- client.c --*/ gpg_error_t _assuan_read_from_server (assuan_context_t ctx, assuan_response_t *okay, int *off, int convey_comments); /*-- assuan-error.c --*/ /*-- assuan-inquire.c --*/ gpg_error_t _assuan_inquire_ext_cb (assuan_context_t ctx); void _assuan_inquire_release (assuan_context_t ctx); /* Check if ERR means EAGAIN. */ int _assuan_error_is_eagain (assuan_context_t ctx, gpg_error_t err); #define set_error(c,e,t) \ assuan_set_error ((c), _assuan_error (c,e), (t)) #ifdef HAVE_W32_SYSTEM char *_assuan_w32_strerror (assuan_context_t ctx, int ec); #endif /*HAVE_W32_SYSTEM*/ /*-- assuan-logging.c --*/ void _assuan_init_log_envvars (void); void _assuan_log_control_channel (assuan_context_t ctx, int outbound, const char *string, const void *buffer1, size_t length1, const void *buffer2, size_t length2); /*-- assuan-io.c --*/ ssize_t _assuan_simple_read (assuan_context_t ctx, void *buffer, size_t size); ssize_t _assuan_simple_write (assuan_context_t ctx, const void *buffer, size_t size); /*-- assuan-socket.c --*/ assuan_fd_t _assuan_sock_new (assuan_context_t ctx, int domain, int type, int proto); int _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd, struct sockaddr *addr, int addrlen); int _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd, struct sockaddr *addr, int addrlen); int _assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr, int *r_redirected); int _assuan_sock_get_nonce (assuan_context_t ctx, struct sockaddr *addr, int addrlen, assuan_sock_nonce_t *nonce); int _assuan_sock_check_nonce (assuan_context_t ctx, assuan_fd_t fd, assuan_sock_nonce_t *nonce); #ifdef HAVE_W32_SYSTEM int _assuan_sock_wsa2errno (int err); #endif #ifdef HAVE_FOPENCOOKIE /* We have to implement funopen in terms of glibc's fopencookie. */ FILE *_assuan_funopen(void *cookie, cookie_read_function_t *readfn, cookie_write_function_t *writefn, cookie_seek_function_t *seekfn, cookie_close_function_t *closefn); #define funopen(a,r,w,s,c) _assuan_funopen ((a), (r), (w), (s), (c)) #endif /*HAVE_FOPENCOOKIE*/ /*-- sysutils.c --*/ const char *_assuan_sysutils_blurb (void); #ifdef HAVE_W32CE_SYSTEM #define getpid() GetCurrentProcessId () char *_assuan_getenv (const char *name); #define getenv(a) _assuan_getenv ((a)) #endif /*HAVE_W32CE_SYSTEM*/ /* Prototypes for replacement functions. */ #ifndef HAVE_MEMRCHR void *memrchr (const void *block, int c, size_t size); #endif #ifndef HAVE_STPCPY char *stpcpy (char *dest, const char *src); #endif #ifndef HAVE_SETENV #define setenv _assuan_setenv #define unsetenv _assuan_unsetenv #define clearenv _assuan_clearenv int setenv (const char *name, const char *value, int replace); #endif #ifndef HAVE_PUTC_UNLOCKED int putc_unlocked (int c, FILE *stream); #endif #define DIM(v) (sizeof(v)/sizeof((v)[0])) /* To avoid that a compiler optimizes memset calls away, these macros can be used. */ #define wipememory2(_ptr,_set,_len) do { \ volatile char *_vptr=(volatile char *)(_ptr); \ size_t _vlen=(_len); \ while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ } while(0) #define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) #if HAVE_W64_SYSTEM # define SOCKET2HANDLE(s) ((void *)(s)) # define HANDLE2SOCKET(h) ((uintptr_t)(h)) #elif HAVE_W32_SYSTEM # define SOCKET2HANDLE(s) ((void *)(s)) # define HANDLE2SOCKET(h) ((unsigned int)(h)) #else # define SOCKET2HANDLE(s) (s) # define HANDLE2SOCKET(h) (h) #endif void _assuan_client_finish (assuan_context_t ctx); void _assuan_client_release (assuan_context_t ctx); void _assuan_server_finish (assuan_context_t ctx); void _assuan_server_release (assuan_context_t ctx); /* Encode the C formatted string SRC and return the malloc'ed result. */ char *_assuan_encode_c_string (assuan_context_t ctx, const char *src); #endif /*ASSUAN_DEFS_H*/ diff --git a/src/assuan-error.c b/src/assuan-error.c index cc1ff5c..8799203 100644 --- a/src/assuan-error.c +++ b/src/assuan-error.c @@ -1,67 +1,68 @@ /* assuan-error.c - Copyright (C) 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #undef _ASSUAN_IN_LIBASSUAN /* undef to get all error codes. */ #include "assuan.h" #include "assuan-defs.h" /* A small helper function to treat EAGAIN transparently to the caller. */ int _assuan_error_is_eagain (assuan_context_t ctx, gpg_error_t err) { if (gpg_err_code (err) == GPG_ERR_EAGAIN) { /* Avoid spinning by sleeping for one tenth of a second. */ _assuan_usleep (ctx, 100000); return 1; } else return 0; } #ifdef HAVE_W32_SYSTEM char * _assuan_w32_strerror (assuan_context_t ctx, int ec) { if (ec == -1) ec = (int)GetLastError (); #ifdef HAVE_W32CE_SYSTEM snprintf (ctx->w32_strerror, sizeof (ctx->w32_strerror) - 1, "ec=%d", (int)GetLastError ()); #else FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), ctx->w32_strerror, sizeof (ctx->w32_strerror) - 1, NULL); #endif return ctx->w32_strerror; } #endif diff --git a/src/assuan-handler.c b/src/assuan-handler.c index dec0f1b..2299fcd 100644 --- a/src/assuan-handler.c +++ b/src/assuan-handler.c @@ -1,1068 +1,1069 @@ /* assuan-handler.c - dispatch commands - Copyright (C) 2001, 2002, 2003, 2007, 2009, - 2011 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2001, 2002, 2003, 2007, 2009, + * 2011 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 "assuan-defs.h" #include "debug.h" #define spacep(p) (*(p) == ' ' || *(p) == '\t') #define digitp(a) ((a) >= '0' && (a) <= '9') static int my_strcasecmp (const char *a, const char *b); #define PROCESS_DONE(ctx, rc) \ ((ctx)->in_process_next ? assuan_process_done ((ctx), (rc)) : (rc)) static gpg_error_t dummy_handler (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASSUAN_SERVER_FAULT, "no handler registered")); } static const char std_help_nop[] = "NOP\n" "\n" "No operation. Returns OK without any action."; static gpg_error_t std_handler_nop (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, 0); /* okay */ } static const char std_help_cancel[] = "CANCEL\n" "\n" "Run the server's cancel handler if one has been registered."; static gpg_error_t std_handler_cancel (assuan_context_t ctx, char *line) { if (ctx->cancel_notify_fnc) /* Return value ignored. */ ctx->cancel_notify_fnc (ctx, line); return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL)); } static const char std_help_option[] = "OPTION [ [=] ]\n" "\n" "Set option to configure server operation. Leading and\n" "trailing spaces around and are allowed but should be\n" "ignored. For compatibility reasons, may be prefixed with two\n" "dashes. The use of the equal sign is optional but suggested if\n" " is given."; static gpg_error_t std_handler_option (assuan_context_t ctx, char *line) { char *key, *value, *p; for (key=line; spacep (key); key++) ; if (!*key) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "argument required")); if (*key == '=') return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "no option name given")); for (value=key; *value && !spacep (value) && *value != '='; value++) ; if (*value) { if (spacep (value)) *value++ = 0; /* terminate key */ for (; spacep (value); value++) ; if (*value == '=') { *value++ = 0; /* terminate key */ for (; spacep (value); value++) ; if (!*value) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "option argument expected")); } if (*value) { for (p = value + strlen(value) - 1; p > value && spacep (p); p--) ; if (p > value) *++p = 0; /* strip trailing spaces */ } } if (*key == '-' && key[1] == '-' && key[2]) key += 2; /* the double dashes are optional */ if (*key == '-') return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "option should not begin with one dash")); if (ctx->option_handler_fnc) return PROCESS_DONE (ctx, ctx->option_handler_fnc (ctx, key, value)); return PROCESS_DONE (ctx, 0); } static const char std_help_bye[] = "BYE\n" "\n" "Close the connection. The server will reply with OK."; static gpg_error_t std_handler_bye (assuan_context_t ctx, char *line) { if (ctx->bye_notify_fnc) /* Return value ignored. */ ctx->bye_notify_fnc (ctx, line); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); /* pretty simple :-) */ ctx->process_complete = 1; return PROCESS_DONE (ctx, 0); } static const char std_help_auth[] = "AUTH\n" "\n" "Reserved for future extensions."; static gpg_error_t std_handler_auth (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL)); } static const char std_help_reset[] = "RESET\n" "\n" "Reset the connection but not any existing authentication. The server\n" "should release all resources associated with the connection."; static gpg_error_t std_handler_reset (assuan_context_t ctx, char *line) { gpg_error_t err = 0; if (ctx->reset_notify_fnc) err = ctx->reset_notify_fnc (ctx, line); if (! err) { assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); _assuan_uds_close_fds (ctx); } return PROCESS_DONE (ctx, err); } static const char std_help_help[] = "HELP []\n" "\n" "Lists all commands that the server understands as comment lines on\n" "the status channel. If is given, list detailed help for\n" "that command."; static gpg_error_t std_handler_help (assuan_context_t ctx, char *line) { unsigned int i; char buf[ASSUAN_LINELENGTH]; const char *helpstr; size_t n; n = strcspn (line, " \t\n"); if (!n) { /* Print all commands. If a help string is available and that starts with the command name, print the first line of the help string. */ for (i = 0; i < ctx->cmdtbl_used; i++) { n = strlen (ctx->cmdtbl[i].name); helpstr = ctx->cmdtbl[i].helpstr; if (helpstr && !strncmp (ctx->cmdtbl[i].name, helpstr, n) && (!helpstr[n] || helpstr[n] == '\n' || helpstr[n] == ' ') && (n = strcspn (helpstr, "\n")) ) snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr); else snprintf (buf, sizeof (buf), "# %s", ctx->cmdtbl[i].name); buf[ASSUAN_LINELENGTH - 1] = '\0'; assuan_write_line (ctx, buf); } } else { /* Print the help for the given command. */ int c = line[n]; line[n] = 0; for (i=0; ctx->cmdtbl[i].name; i++) if (!my_strcasecmp (line, ctx->cmdtbl[i].name)) break; line[n] = c; if (!ctx->cmdtbl[i].name) return PROCESS_DONE (ctx, set_error (ctx,GPG_ERR_UNKNOWN_COMMAND,NULL)); helpstr = ctx->cmdtbl[i].helpstr; if (!helpstr) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_FOUND, NULL)); do { n = strcspn (helpstr, "\n"); snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr); helpstr += n; if (*helpstr == '\n') helpstr++; buf[ASSUAN_LINELENGTH - 1] = '\0'; assuan_write_line (ctx, buf); } while (*helpstr); } return PROCESS_DONE (ctx, 0); } static const char std_help_end[] = "END\n" "\n" "Used by a client to mark the end of raw data."; static gpg_error_t std_handler_end (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL)); } gpg_error_t assuan_command_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd) { char *endp; if ((strncmp (line, "FD", 2) && strncmp (line, "fd", 2)) || (line[2] != '=' && line[2] != '\0' && !spacep(&line[2]))) return set_error (ctx, GPG_ERR_ASS_SYNTAX, "FD[=] expected"); line += 2; if (*line == '=') { line ++; if (!digitp (*line)) return set_error (ctx, GPG_ERR_ASS_SYNTAX, "number required"); #if HAVE_W64_SYSTEM *rfd = (void*)strtoull (line, &endp, 10); #elif HAVE_W32_SYSTEM *rfd = (void*)strtoul (line, &endp, 10); #else *rfd = strtoul (line, &endp, 10); #endif /* Remove that argument so that a notify handler won't see it. */ memset (line, ' ', endp? (endp-line):strlen(line)); if (*rfd == ctx->inbound.fd) return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as inbound fd"); if (*rfd == ctx->outbound.fd) return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as outbound fd"); return 0; } else /* Our peer has sent the file descriptor. */ return assuan_receivefd (ctx, rfd); } static const char std_help_input[] = "INPUT FD[=]\n" "\n" "Used by a client to pass an input file descriptor to the server.\n" "The server opens as a local file descriptor. Without , the\n" "server opens the file descriptor just sent by the client using\n" "assuan_sendfd."; static gpg_error_t std_handler_input (assuan_context_t ctx, char *line) { gpg_error_t rc; assuan_fd_t fd, oldfd; rc = assuan_command_parse_fd (ctx, line, &fd); if (rc) return PROCESS_DONE (ctx, rc); #ifdef HAVE_W32CE_SYSTEM oldfd = fd; fd = _assuan_w32ce_finish_pipe ((int)fd, 0); if (fd == INVALID_HANDLE_VALUE) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_PARAMETER, "rvid conversion failed")); TRACE2 (ctx, ASSUAN_LOG_SYSIO, "std_handler_input", ctx, "turned RVID 0x%x into handle 0x%x", oldfd, fd); #endif if (ctx->input_notify_fnc) { oldfd = ctx->input_fd; ctx->input_fd = fd; rc = ctx->input_notify_fnc (ctx, line); if (rc) ctx->input_fd = oldfd; } else if (!rc) ctx->input_fd = fd; return PROCESS_DONE (ctx, rc); } static const char std_help_output[] = "OUTPUT FD[=]\n" "\n" "Used by a client to pass an output file descriptor to the server.\n" "The server opens as a local file descriptor. Without , the\n" "server opens the file descriptor just sent by the client using\n" "assuan_sendfd."; static gpg_error_t std_handler_output (assuan_context_t ctx, char *line) { gpg_error_t rc; assuan_fd_t fd, oldfd; rc = assuan_command_parse_fd (ctx, line, &fd); if (rc) return PROCESS_DONE (ctx, rc); #ifdef HAVE_W32CE_SYSTEM oldfd = fd; fd = _assuan_w32ce_finish_pipe ((int)fd, 1); if (fd == INVALID_HANDLE_VALUE) return PROCESS_DONE (ctx, set_error (ctx, gpg_err_code_from_syserror (), "rvid conversion failed")); TRACE2 (ctx, ASSUAN_LOG_SYSIO, "std_handler_output", ctx, "turned RVID 0x%x into handle 0x%x", oldfd, fd); #endif if (ctx->output_notify_fnc) { oldfd = ctx->output_fd; ctx->output_fd = fd; rc = ctx->output_notify_fnc (ctx, line); if (rc) ctx->output_fd = oldfd; } else if (!rc) ctx->output_fd = fd; return PROCESS_DONE (ctx, rc); } /* This is a table with the standard commands and handler for them. The table is used to initialize a new context and associate strings with default handlers */ static struct { const char *name; gpg_error_t (*handler)(assuan_context_t, char *line); const char *help; int always; /* always initialize this command */ } std_cmd_table[] = { { "NOP", std_handler_nop, std_help_nop, 1 }, { "CANCEL", std_handler_cancel, std_help_cancel, 1 }, { "OPTION", std_handler_option, std_help_option, 1 }, { "BYE", std_handler_bye, std_help_bye, 1 }, { "AUTH", std_handler_auth, std_help_auth, 1 }, { "RESET", std_handler_reset, std_help_reset, 1 }, { "END", std_handler_end, std_help_end, 1 }, { "HELP", std_handler_help, std_help_help, 1 }, { "INPUT", std_handler_input, std_help_input, 0 }, { "OUTPUT", std_handler_output, std_help_output, 0 }, { } }; /** * assuan_register_command: * @ctx: the server context * @cmd_name: A string with the command name * @handler: The handler function to be called or NULL to use a default * handler. * HELPSTRING * * Register a handler to be used for a given command. Note that * several default handlers are already regsitered with a new context. * This function however allows to override them. * * Return value: 0 on success or an error code **/ gpg_error_t assuan_register_command (assuan_context_t ctx, const char *cmd_name, assuan_handler_t handler, const char *help_string) { int i, cmd_index = -1; const char *s; if (cmd_name && !*cmd_name) cmd_name = NULL; if (!cmd_name) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!handler) { /* find a default handler. */ for (i=0; (s=std_cmd_table[i].name) && strcmp (cmd_name, s); i++) ; if (!s) { /* Try again but case insensitive. */ for (i=0; (s=std_cmd_table[i].name) && my_strcasecmp (cmd_name, s); i++) ; } if (s) handler = std_cmd_table[i].handler; if (!handler) handler = dummy_handler; /* Last resort is the dummy handler. */ } if (!ctx->cmdtbl) { ctx->cmdtbl_size = 50; ctx->cmdtbl = _assuan_calloc (ctx, ctx->cmdtbl_size, sizeof *ctx->cmdtbl); if (!ctx->cmdtbl) return _assuan_error (ctx, gpg_err_code_from_syserror ()); ctx->cmdtbl_used = 0; } else if (ctx->cmdtbl_used >= ctx->cmdtbl_size) { struct cmdtbl_s *x; x = _assuan_realloc (ctx, ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x); if (!x) return _assuan_error (ctx, gpg_err_code_from_syserror ()); ctx->cmdtbl = x; ctx->cmdtbl_size += 50; } for (i=0; icmdtbl_used; i++) { if (!my_strcasecmp (cmd_name, ctx->cmdtbl[i].name)) { cmd_index = i; break; } } if (cmd_index == -1) cmd_index = ctx->cmdtbl_used++; ctx->cmdtbl[cmd_index].name = cmd_name; ctx->cmdtbl[cmd_index].handler = handler; ctx->cmdtbl[cmd_index].helpstr = help_string; return 0; } /* Return the name of the command currently processed by a handler. The string returned is valid until the next call to an assuan function on the same context. Returns NULL if no handler is executed or the command is not known. */ const char * assuan_get_command_name (assuan_context_t ctx) { return ctx? ctx->current_cmd_name : NULL; } gpg_error_t assuan_register_pre_cmd_notify (assuan_context_t ctx, gpg_error_t (*fnc)(assuan_context_t, const char *cmd)) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->pre_cmd_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_post_cmd_notify (assuan_context_t ctx, void (*fnc)(assuan_context_t, gpg_error_t)) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->post_cmd_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_bye_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->bye_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_reset_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->reset_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_cancel_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->cancel_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_option_handler (assuan_context_t ctx, gpg_error_t (*fnc)(assuan_context_t, const char*, const char*)) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->option_handler_fnc = fnc; return 0; } gpg_error_t assuan_register_input_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->input_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_output_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->output_notify_fnc = fnc; return 0; } /* Helper to register the standards commands */ gpg_error_t _assuan_register_std_commands (assuan_context_t ctx) { gpg_error_t rc; int i; for (i = 0; std_cmd_table[i].name; i++) { if (std_cmd_table[i].always) { rc = assuan_register_command (ctx, std_cmd_table[i].name, NULL, NULL); if (rc) return rc; } } return 0; } /* Process the special data lines. The "D " has already been removed from the line. As all handlers this function may modify the line. */ static gpg_error_t handle_data_line (assuan_context_t ctx, char *line, int linelen) { return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL); } /* like ascii_strcasecmp but assume that B is already uppercase */ static int my_strcasecmp (const char *a, const char *b) { if (a == b) return 0; for (; *a && *b; a++, b++) { if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b) break; } return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b); } /* Parse the line, break out the command, find it in the command table, remove leading and white spaces from the arguments, call the handler with the argument line and return the error. */ static gpg_error_t dispatch_command (assuan_context_t ctx, char *line, int linelen) { gpg_error_t err; char *p; const char *s; int shift, i; /* Note that as this function is invoked by assuan_process_next as well, we need to hide non-critical errors with PROCESS_DONE. */ if (*line == 'D' && line[1] == ' ') /* divert to special handler */ /* FIXME: Depending on the final implementation of handle_data_line, this may be wrong here. For example, if a user callback is invoked, and that callback is responsible for calling assuan_process_done, then this is wrong. */ return PROCESS_DONE (ctx, handle_data_line (ctx, line+2, linelen-2)); for (p=line; *p && *p != ' ' && *p != '\t'; p++) ; if (p==line) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "leading white-space")); if (*p) { /* Skip over leading WS after the keyword */ *p++ = 0; while ( *p == ' ' || *p == '\t') p++; } shift = p - line; for (i=0; (s=ctx->cmdtbl[i].name); i++) { if (!strcmp (line, s)) break; } if (!s) { /* and try case insensitive */ for (i=0; (s=ctx->cmdtbl[i].name); i++) { if (!my_strcasecmp (line, s)) break; } } if (!s) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_UNKNOWN_CMD, NULL)); line += shift; /* linelen -= shift; -- not needed. */ if (ctx->pre_cmd_notify_fnc) { err = ctx->pre_cmd_notify_fnc(ctx, ctx->cmdtbl[i].name); if (err) return PROCESS_DONE(ctx, err); } /* fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */ ctx->current_cmd_name = ctx->cmdtbl[i].name; err = ctx->cmdtbl[i].handler (ctx, line); ctx->current_cmd_name = NULL; return err; } /* Call this to acknowledge the current command. */ gpg_error_t assuan_process_done (assuan_context_t ctx, gpg_error_t rc) { if (!ctx->in_command) return _assuan_error (ctx, GPG_ERR_ASS_GENERAL); if (ctx->flags.force_close) ctx->process_complete = 1; ctx->in_command = 0; /* Check for data write errors. */ if (ctx->outbound.data.fp) { /* Flush the data lines. */ fclose (ctx->outbound.data.fp); ctx->outbound.data.fp = NULL; if (!rc && ctx->outbound.data.error) rc = ctx->outbound.data.error; } else { /* Flush any data send without using the data FP. */ assuan_send_data (ctx, NULL, 0); if (!rc && ctx->outbound.data.error) rc = ctx->outbound.data.error; } /* Error handling. */ if (!rc) { if (ctx->process_complete) { /* No error checking because the peer may have already disconnect. */ assuan_write_line (ctx, "OK closing connection"); ctx->finish_handler (ctx); } else rc = assuan_write_line (ctx, ctx->okay_line ? ctx->okay_line : "OK"); } else { char errline[300]; const char *text = ctx->err_no == rc ? ctx->err_str : NULL; char ebuf[50]; if (ctx->flags.force_close) text = "[closing connection]"; gpg_strerror_r (rc, ebuf, sizeof (ebuf)); snprintf (errline, sizeof errline, "ERR %d %.50s <%.30s>%s%.100s", rc, ebuf, gpg_strsource (rc), text? " - ":"", text?text:""); rc = assuan_write_line (ctx, errline); if (ctx->flags.force_close) ctx->finish_handler (ctx); } if (ctx->post_cmd_notify_fnc) ctx->post_cmd_notify_fnc (ctx, rc); ctx->flags.confidential = 0; if (ctx->okay_line) { _assuan_free (ctx, ctx->okay_line); ctx->okay_line = NULL; } return rc; } static gpg_error_t process_next (assuan_context_t ctx) { gpg_error_t rc; /* What the next thing to do is depends on the current state. However, we will always first read the next line. The client is required to write full lines without blocking long after starting a partial line. */ rc = _assuan_read_line (ctx); if (_assuan_error_is_eagain (ctx, rc)) return 0; if (gpg_err_code (rc) == GPG_ERR_EOF) { ctx->process_complete = 1; return 0; } if (rc) return rc; if (*ctx->inbound.line == '#' || !ctx->inbound.linelen) /* Comment lines are ignored. */ return 0; /* Now we have a line that really means something. It could be one of the following things: First, if we are not in a command already, it is the next command to dispatch. Second, if we are in a command, it can only be the response to an INQUIRE reply. */ if (!ctx->in_command) { ctx->in_command = 1; ctx->outbound.data.error = 0; ctx->outbound.data.linelen = 0; /* Dispatch command and return reply. */ ctx->in_process_next = 1; rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen); ctx->in_process_next = 0; } else if (ctx->in_inquire) { /* FIXME: Pick up the continuation. */ rc = _assuan_inquire_ext_cb (ctx); } else { /* Should not happen. The client is sending data while we are in a command and not waiting for an inquire. We log an error and discard it. */ TRACE0 (ctx, ASSUAN_LOG_DATA, "process_next", ctx, "unexpected client data"); rc = 0; } return rc; } /* This function should be invoked when the assuan connected FD is ready for reading. If the equivalent to EWOULDBLOCK is returned (this should be done by the command handler), assuan_process_next should be invoked the next time the connected FD is readable. Eventually, the caller will finish by invoking assuan_process_done. DONE is set to 1 if the connection has ended. */ gpg_error_t assuan_process_next (assuan_context_t ctx, int *done) { gpg_error_t rc; if (done) *done = 0; ctx->process_complete = 0; do { rc = process_next (ctx); } while (!rc && !ctx->process_complete && assuan_pending_line (ctx)); if (done) *done = !!ctx->process_complete; return rc; } static gpg_error_t process_request (assuan_context_t ctx) { gpg_error_t rc; if (ctx->in_inquire) return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS); do { rc = _assuan_read_line (ctx); } while (_assuan_error_is_eagain (ctx, rc)); if (gpg_err_code (rc) == GPG_ERR_EOF) { ctx->process_complete = 1; return 0; } if (rc) return rc; if (*ctx->inbound.line == '#' || !ctx->inbound.linelen) return 0; /* comment line - ignore */ ctx->in_command = 1; ctx->outbound.data.error = 0; ctx->outbound.data.linelen = 0; /* dispatch command and return reply */ rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen); return assuan_process_done (ctx, rc); } /** * assuan_process: * @ctx: assuan context * * This function is used to handle the assuan protocol after a * connection has been established using assuan_accept(). This is the * main protocol handler. * * Return value: 0 on success or an error code if the assuan operation * failed. Note, that no error is returned for operational errors. **/ gpg_error_t assuan_process (assuan_context_t ctx) { gpg_error_t rc; ctx->process_complete = 0; do { rc = process_request (ctx); } while (!rc && !ctx->process_complete); return rc; } /** * assuan_get_active_fds: * @ctx: Assuan context * @what: 0 for read fds, 1 for write fds * @fdarray: Caller supplied array to store the FDs * @fdarraysize: size of that array * * Return all active filedescriptors for the given context. This * function can be used to select on the fds and call * assuan_process_next() if there is an active one. The first fd in * the array is the one used for the command connection. * * Note, that write FDs are not yet supported. * * Return value: number of FDs active and put into @fdarray or -1 on * error which is most likely a too small fdarray. **/ int assuan_get_active_fds (assuan_context_t ctx, int what, assuan_fd_t *fdarray, int fdarraysize) { int n = 0; if (!ctx || fdarraysize < 2 || what < 0 || what > 1) return -1; if (!what) { if (ctx->inbound.fd != ASSUAN_INVALID_FD) fdarray[n++] = ctx->inbound.fd; } else { if (ctx->outbound.fd != ASSUAN_INVALID_FD) fdarray[n++] = ctx->outbound.fd; if (ctx->outbound.data.fp) #if defined(HAVE_W32CE_SYSTEM) fdarray[n++] = (void*)fileno (ctx->outbound.data.fp); #elif defined(HAVE_W32_SYSTEM) fdarray[n++] = (void*)_get_osfhandle (fileno (ctx->outbound.data.fp)); #else fdarray[n++] = fileno (ctx->outbound.data.fp); #endif } return n; } /* Two simple wrappers to make the expected function types match. */ #ifdef HAVE_FUNOPEN static int fun1_cookie_write (void *cookie, const char *buffer, int orig_size) { return _assuan_cookie_write_data (cookie, buffer, orig_size); } #endif /*HAVE_FUNOPEN*/ #ifdef HAVE_FOPENCOOKIE static ssize_t fun2_cookie_write (void *cookie, const char *buffer, size_t orig_size) { return _assuan_cookie_write_data (cookie, buffer, orig_size); } #endif /*HAVE_FOPENCOOKIE*/ /* Return a FP to be used for data output. The FILE pointer is valid until the end of a handler. So a close is not needed. Assuan does all the buffering needed to insert the status line as well as the required line wappping and quoting for data lines. We use GNU's custom streams here. There should be an alternative implementaion for systems w/o a glibc, a simple implementation could use a child process */ FILE * assuan_get_data_fp (assuan_context_t ctx) { #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN) if (ctx->outbound.data.fp) return ctx->outbound.data.fp; #ifdef HAVE_FUNOPEN ctx->outbound.data.fp = funopen (ctx, 0, fun1_cookie_write, 0, _assuan_cookie_write_flush); #else ctx->outbound.data.fp = funopen (ctx, 0, fun2_cookie_write, 0, _assuan_cookie_write_flush); #endif ctx->outbound.data.error = 0; return ctx->outbound.data.fp; #else gpg_err_set_errno (ENOSYS); return NULL; #endif } /* Set the text used for the next OK response. This string is automatically reset to NULL after the next command. */ gpg_error_t assuan_set_okay_line (assuan_context_t ctx, const char *line) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!line) { _assuan_free (ctx, ctx->okay_line); ctx->okay_line = NULL; } else { /* FIXME: we need to use gcry_is_secure() to test whether we should allocate the entire line in secure memory */ char *buf = _assuan_malloc (ctx, 3 + strlen(line) + 1); if (!buf) return _assuan_error (ctx, gpg_err_code_from_syserror ()); strcpy (buf, "OK "); strcpy (buf+3, line); _assuan_free (ctx, ctx->okay_line); ctx->okay_line = buf; } return 0; } gpg_error_t assuan_write_status (assuan_context_t ctx, const char *keyword, const char *text) { char buffer[256]; char *helpbuf; size_t n; gpg_error_t ae; if ( !ctx || !keyword) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!text) text = ""; n = 2 + strlen (keyword) + 1 + strlen (text) + 1; if (n < sizeof (buffer)) { strcpy (buffer, "S "); strcat (buffer, keyword); if (*text) { strcat (buffer, " "); strcat (buffer, text); } ae = assuan_write_line (ctx, buffer); } else if ( (helpbuf = _assuan_malloc (ctx, n)) ) { strcpy (helpbuf, "S "); strcat (helpbuf, keyword); if (*text) { strcat (helpbuf, " "); strcat (helpbuf, text); } ae = assuan_write_line (ctx, helpbuf); _assuan_free (ctx, helpbuf); } else ae = 0; return ae; } diff --git a/src/assuan-inquire.c b/src/assuan-inquire.c index f863935..fa227a6 100644 --- a/src/assuan-inquire.c +++ b/src/assuan-inquire.c @@ -1,414 +1,415 @@ /* assuan-inquire.c - handle inquire stuff - Copyright (C) 2001-2003, 2005, 2007, 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2001-2003, 2005, 2007, 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 "assuan-defs.h" #define digitp(a) ((a) >= '0' && (a) <= '9') #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) struct membuf { size_t len; size_t size; char *buf; int out_of_core; int too_large; size_t maxlen; }; /* A simple implementation of a dynamic buffer. Use init_membuf() to create a buffer, put_membuf to append bytes and get_membuf to release and return the buffer. Allocation errors are detected but only returned at the final get_membuf(), this helps not to clutter the code with out of core checks. */ static void init_membuf (assuan_context_t ctx, struct membuf *mb, int initiallen, size_t maxlen) { mb->len = 0; mb->size = initiallen; mb->out_of_core = 0; mb->too_large = 0; mb->maxlen = maxlen; /* we need to allocate one byte more for get_membuf */ mb->buf = _assuan_malloc (ctx, initiallen + 1); if (!mb->buf) mb->out_of_core = 1; } static void put_membuf (assuan_context_t ctx, struct membuf *mb, const void *buf, size_t len) { if (mb->out_of_core || mb->too_large) return; if (mb->maxlen && mb->len + len > mb->maxlen) { mb->too_large = 1; return; } if (mb->len + len >= mb->size) { char *p; mb->size += len + 1024; /* we need to allocate one byte more for get_membuf */ p = _assuan_realloc (ctx, mb->buf, mb->size + 1); if (!p) { mb->out_of_core = 1; return; } mb->buf = p; } memcpy (mb->buf + mb->len, buf, len); mb->len += len; } static void * get_membuf (assuan_context_t ctx, struct membuf *mb, size_t *len) { char *p; if (mb->out_of_core || mb->too_large) { _assuan_free (ctx, mb->buf); mb->buf = NULL; return NULL; } mb->buf[mb->len] = 0; /* there is enough space for the hidden eos */ p = mb->buf; *len = mb->len; mb->buf = NULL; mb->out_of_core = 1; /* don't allow a reuse */ return p; } static void free_membuf (assuan_context_t ctx, struct membuf *mb) { _assuan_free (ctx, mb->buf); mb->buf = NULL; } /** * assuan_inquire: * @ctx: An assuan context * @keyword: The keyword used for the inquire * @r_buffer: Returns an allocated buffer * @r_length: Returns the length of this buffer * @maxlen: If not 0, the size limit of the inquired data. * * A server may use this to send an inquire. r_buffer, r_length and * maxlen may all be NULL/0 to indicate that no real data is expected. * The returned buffer is guaranteed to have an extra 0-byte after the * length. Thus it can be used as a string if embedded 0 bytes are * not an issue. * * Return value: 0 on success or an ASSUAN error code **/ gpg_error_t assuan_inquire (assuan_context_t ctx, const char *keyword, unsigned char **r_buffer, size_t *r_length, size_t maxlen) { gpg_error_t rc; struct membuf mb; char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */ unsigned char *line, *p; int linelen; int nodataexpected; if (r_buffer) *r_buffer = NULL; if (r_length) *r_length = 0; if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); nodataexpected = !r_buffer && !r_length && !maxlen; if (!nodataexpected && (!r_buffer || !r_length)) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!ctx->is_server) return _assuan_error (ctx, GPG_ERR_ASS_NOT_A_SERVER); if (ctx->in_inquire) return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS); ctx->in_inquire = 1; if (nodataexpected) memset (&mb, 0, sizeof mb); /* avoid compiler warnings */ else init_membuf (ctx, &mb, maxlen? maxlen:1024, maxlen); strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword); rc = assuan_write_line (ctx, cmdbuf); if (rc) goto out; for (;;) { do { do rc = _assuan_read_line (ctx); while (_assuan_error_is_eagain (ctx, rc)); if (rc) goto out; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; } while (*line == '#' || !linelen); /* Note: As a convenience for manual testing we allow case insensitive keywords. */ if ((line[0] == 'E'||line[0] == 'e') && (line[1] == 'N' || line[1] == 'n') && (line[2] == 'D' || line[2] == 'd') && (!line[3] || line[3] == ' ')) break; /* END command received*/ if ((line[0] == 'C' || line[0] == 'c') && (line[1] == 'A' || line[1] == 'a') && (line[2] == 'N' || line[2] == 'n')) { rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED); goto out; } if ((line[0] != 'D' && line[0] != 'd') || line[1] != ' ' || nodataexpected) { rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD); goto out; } if (linelen < 3) continue; line += 2; linelen -= 2; if (mb.too_large) continue; /* Need to read up the remaining data. */ p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (ctx, &mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (ctx, &mb, tmp, 1); } line = p; } } if (!nodataexpected) { if (mb.too_large) rc = _assuan_error (ctx, GPG_ERR_ASS_TOO_MUCH_DATA); else { *r_buffer = get_membuf (ctx, &mb, r_length); if (!*r_buffer) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); } } out: if (!nodataexpected) free_membuf (ctx, &mb); ctx->in_inquire = 0; return rc; } void _assuan_inquire_release (assuan_context_t ctx) { if (ctx->in_inquire) { if (ctx->inquire_membuf) { free_membuf (ctx, ctx->inquire_membuf); free (ctx->inquire_membuf); } ctx->in_inquire = 0; } } gpg_error_t _assuan_inquire_ext_cb (assuan_context_t ctx) { gpg_error_t rc; unsigned char *line; int linelen; struct membuf *mb; unsigned char *p; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; mb = ctx->inquire_membuf; if ((line[0] == 'C' || line[0] == 'c') && (line[1] == 'A' || line[1] == 'a') && (line[2] == 'N' || line[2] == 'n')) { rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED); goto out; } if ((line[0] == 'E'||line[0] == 'e') && (line[1] == 'N' || line[1] == 'n') && (line[2] == 'D' || line[2] == 'd') && (!line[3] || line[3] == ' ')) { rc = 0; goto out; } if ((line[0] != 'D' && line[0] != 'd') || line[1] != ' ' || mb == NULL) { rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD); goto out; } if (linelen < 3) return 0; line += 2; linelen -= 2; p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (ctx, mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (ctx, mb, tmp, 1); } line = p; } if (mb->too_large) { rc = _assuan_error (ctx, GPG_ERR_ASS_TOO_MUCH_DATA); goto out; } return 0; out: { size_t buf_len = 0; unsigned char *buf = NULL; if (mb) { buf = get_membuf (ctx, mb, &buf_len); if (!buf) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); free_membuf (ctx, mb); free (mb); ctx->inquire_membuf = NULL; } ctx->in_inquire = 0; rc = (ctx->inquire_cb) (ctx->inquire_cb_data, rc, buf, buf_len); } return rc; } /** * assuan_inquire_ext: * @ctx: An assuan context * @keyword: The keyword used for the inquire * @maxlen: If not 0, the size limit of the inquired data. * @cb: A callback handler which is invoked after the operation completed. * @cb_data: A user-provided value passed to the callback handler. * * A server may use this to send an inquire. R_BUFFER, R_LENGTH and * MAXLEN may all be NULL/0 to indicate that no real data is expected. * * Return value: 0 on success or an ASSUAN error code **/ gpg_error_t assuan_inquire_ext (assuan_context_t ctx, const char *keyword, size_t maxlen, gpg_error_t (*cb) (void *cb_data, gpg_error_t rc, unsigned char *buf, size_t len), void *cb_data) { gpg_error_t rc; struct membuf *mb = NULL; char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */ if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!ctx->is_server) return _assuan_error (ctx, GPG_ERR_ASS_NOT_A_SERVER); if (ctx->in_inquire) return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS); mb = malloc (sizeof (struct membuf)); if (!mb) return _assuan_error (ctx, gpg_err_code_from_syserror ()); init_membuf (ctx, mb, maxlen ? maxlen : 1024, maxlen); strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword); rc = assuan_write_line (ctx, cmdbuf); if (rc) { free_membuf (ctx, mb); free (mb); return rc; } ctx->in_inquire = 1; /* Set up the continuation. */ ctx->inquire_cb = cb; ctx->inquire_cb_data = cb_data; ctx->inquire_membuf = mb; return 0; } diff --git a/src/assuan-io.c b/src/assuan-io.c index 8bb8ed9..b3b22fd 100644 --- a/src/assuan-io.c +++ b/src/assuan-io.c @@ -1,61 +1,62 @@ /* assuan-io.c - Wraps the read and write functions. - Copyright (C) 2002, 2004, 2006-2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2002, 2004, 2006-2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include -# endif +# endif # include #else # include #endif #include "assuan-defs.h" ssize_t _assuan_simple_read (assuan_context_t ctx, void *buffer, size_t size) { return _assuan_read (ctx, ctx->inbound.fd, buffer, size); } ssize_t _assuan_simple_write (assuan_context_t ctx, const void *buffer, size_t size) { return _assuan_write (ctx, ctx->outbound.fd, buffer, size); } diff --git a/src/assuan-listen.c b/src/assuan-listen.c index 2c589ab..6755d59 100644 --- a/src/assuan-listen.c +++ b/src/assuan-listen.c @@ -1,175 +1,176 @@ -/* assuan-listen.c - Wait for a connection (server) - Copyright (C) 2001, 2002, 2004, 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . +/* assuan-listen.c - Wait for a connection (server) + * Copyright (C) 2001, 2002, 2004, 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #ifdef HAVE_UNISTD_H # include #endif #include #include "assuan-defs.h" gpg_error_t assuan_set_hello_line (assuan_context_t ctx, const char *line) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!line) { _assuan_free (ctx, ctx->hello_line); ctx->hello_line = NULL; } else { char *buf = _assuan_malloc (ctx, 3 + strlen (line) + 1); if (!buf) return _assuan_error (ctx, gpg_err_code_from_syserror ()); if (strchr (line, '\n')) strcpy (buf, line); else { strcpy (buf, "OK "); strcpy (buf+3, line); } _assuan_free (ctx, ctx->hello_line); ctx->hello_line = buf; } return 0; } /** * assuan_accept: * @ctx: context - * + * * Cancel any existing connection and wait for a connection from a * client. The initial handshake is performed which may include an * initial authentication or encryption negotiation. - * + * * Return value: 0 on success or an error if the connection could for * some reason not be established. **/ gpg_error_t assuan_accept (assuan_context_t ctx) { gpg_error_t rc = 0; const char *p, *pend; if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (ctx->max_accepts != -1) { if (ctx->max_accepts-- == 0) return -1; /* second invocation for pipemode -> terminate */ } if (ctx->accept_handler) { /* FIXME: This should be superfluous, if everything else is correct. */ ctx->finish_handler (ctx); rc = ctx->accept_handler (ctx); if (rc) return rc; } /* Send the hello. */ p = ctx->hello_line; if (p && (pend = strchr (p, '\n'))) { /* This is a multi line hello. Send all but the last line as comments. */ do { rc = _assuan_write_line (ctx, "# ", p, pend - p); if (rc) return rc; p = pend + 1; pend = strchr (p, '\n'); } while (pend); rc = _assuan_write_line (ctx, "OK ", p, strlen (p)); } else if (p) rc = assuan_write_line (ctx, p); else { static char const okstr[] = "OK Pleased to meet you"; pid_t apid = assuan_get_pid (ctx); if (apid != ASSUAN_INVALID_PID) { char tmpbuf[50]; snprintf (tmpbuf, sizeof tmpbuf, "%s, process %i", okstr, (int)apid); rc = assuan_write_line (ctx, tmpbuf); } else rc = assuan_write_line (ctx, okstr); } if (rc) return rc; - + return 0; } assuan_fd_t assuan_get_input_fd (assuan_context_t ctx) { return ctx ? ctx->input_fd : ASSUAN_INVALID_FD; } assuan_fd_t assuan_get_output_fd (assuan_context_t ctx) { return ctx ? ctx->output_fd : ASSUAN_INVALID_FD; } /* Close the fd descriptor set by the command INPUT FD=n. We handle this fd inside assuan so that we can do some initial checks */ gpg_error_t assuan_close_input_fd (assuan_context_t ctx) { if (!ctx || ctx->input_fd == ASSUAN_INVALID_FD) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); _assuan_close (ctx, ctx->input_fd); ctx->input_fd = ASSUAN_INVALID_FD; return 0; } /* Close the fd descriptor set by the command OUTPUT FD=n. We handle this fd inside assuan so that we can do some initial checks */ gpg_error_t assuan_close_output_fd (assuan_context_t ctx) { if (!ctx || ctx->output_fd == ASSUAN_INVALID_FD) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); _assuan_close (ctx, ctx->output_fd); ctx->output_fd = ASSUAN_INVALID_FD; return 0; } diff --git a/src/assuan-logging.c b/src/assuan-logging.c index fd47582..8ee6aa2 100644 --- a/src/assuan-logging.c +++ b/src/assuan-logging.c @@ -1,304 +1,305 @@ /* assuan-logging.c - Default logging function. - Copyright (C) 2002, 2003, 2004, 2007, 2009, - 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2002, 2003, 2004, 2007, 2009, + * 2010 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include #endif /*HAVE_W32_SYSTEM*/ #include #include #include "assuan-defs.h" /* The default log handler is useful for global logging, but it should only be used by one user of libassuan at a time. Libraries that use libassuan can register their own log handler. */ /* A common prefix for all log messages. */ static char prefix_buffer[80]; /* A global flag read from the environment to check if to enable full logging of buffer data. This is also used by custom log handlers. */ static int full_logging; /* A bitfield that specifies the categories to log. */ static int log_cats; #define TEST_LOG_CAT(x) (!! (log_cats & (1 << (x - 1)))) static FILE *_assuan_log; void _assuan_init_log_envvars (void) { char *flagstr; full_logging = !!getenv ("ASSUAN_FULL_LOGGING"); flagstr = getenv ("ASSUAN_DEBUG"); if (flagstr) log_cats = atoi (flagstr); else /* Default to log the control channel. */ log_cats = (1 << (ASSUAN_LOG_CONTROL - 1)); _assuan_sysutils_blurb (); /* Make sure this code gets linked in. */ } void assuan_set_assuan_log_stream (FILE *fp) { _assuan_log = fp; _assuan_init_log_envvars (); } /* Set the per context log stream. Also enable the default log stream if it has not been set. */ void assuan_set_log_stream (assuan_context_t ctx, FILE *fp) { if (ctx) { if (ctx->log_fp) fflush (ctx->log_fp); ctx->log_fp = fp; if (! _assuan_log) assuan_set_assuan_log_stream (fp); } } /* Set the prefix to be used for logging to TEXT or resets it to the default if TEXT is NULL. */ void assuan_set_assuan_log_prefix (const char *text) { if (text) { strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1); prefix_buffer[sizeof (prefix_buffer)-1] = 0; } else *prefix_buffer = 0; } /* Get the prefix to be used for logging. */ const char * assuan_get_assuan_log_prefix (void) { return prefix_buffer; } /* Default log handler. */ int _assuan_log_handler (assuan_context_t ctx, void *hook, unsigned int cat, const char *msg) { FILE *fp; const char *prf; int saved_errno = errno; /* For now. */ if (msg == NULL) return TEST_LOG_CAT (cat); if (! TEST_LOG_CAT (cat)) return 0; fp = ctx->log_fp ? ctx->log_fp : _assuan_log; if (!fp) return 0; prf = assuan_get_assuan_log_prefix (); if (*prf) fprintf (fp, "%s[%u]: ", prf, (unsigned int)getpid ()); fprintf (fp, "%s", msg); /* If the log stream is a file, the output would be buffered. This is bad for debugging, thus we flush the stream if FORMAT ends with a LF. */ if (msg && *msg && msg[strlen (msg) - 1] == '\n') fflush (fp); gpg_err_set_errno (saved_errno); return 0; } /* Log a control channel message. This is either a STRING with a diagnostic or actual data in (BUFFER1,LENGTH1) and (BUFFER2,LENGTH2). If OUTBOUND is true the data is intended for the peer. */ void _assuan_log_control_channel (assuan_context_t ctx, int outbound, const char *string, const void *buffer1, size_t length1, const void *buffer2, size_t length2) { int res; char *outbuf; int saved_errno; /* Check whether logging is enabled and do a quick check to see whether the callback supports our category. */ if (!ctx || !ctx->log_cb || ctx->flags.no_logging || !(*ctx->log_cb) (ctx, ctx->log_cb_data, ASSUAN_LOG_CONTROL, NULL)) return; saved_errno = errno; /* Note that we use the inbound channel fd as the printed channel number for both directions. */ #ifdef HAVE_W32_SYSTEM # define CHANNEL_FMT "%p" #else # define CHANNEL_FMT "%d" #endif #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a')) if (!buffer1 && buffer2) { buffer1 = buffer2; length1 = length2; buffer2 = NULL; length2 = 0; } if (ctx->flags.confidential && !string && buffer1) string = "[Confidential data not shown]"; if (string) { /* Print the diagnostic. */ res = gpgrt_asprintf (&outbuf, "chan_" CHANNEL_FMT " %s [%s]\n", ctx->inbound.fd, outbound? "->":"<-", string); } else if (buffer1) { /* Print the control channel data. */ const unsigned char *s; unsigned int n, x; for (n = length1, s = buffer1; n; n--, s++) if ((!isascii (*s) || iscntrl (*s) || !isprint (*s) || !*s) && !(*s >= 0x80)) break; if (!n && buffer2) { for (n = length2, s = buffer2; n; n--, s++) if ((!isascii (*s) || iscntrl (*s) || !isprint (*s) || !*s) && !(*s >= 0x80)) break; } if (!buffer2) length2 = 0; if (!n && (length1 && *(const char*)buffer1 != '[')) { /* No control characters and not starting with our error message indicator. Log it verbatim. */ res = gpgrt_asprintf (&outbuf, "chan_" CHANNEL_FMT " %s %.*s%.*s\n", ctx->inbound.fd, outbound? "->":"<-", (int)length1, (const char*)buffer1, (int)length2, buffer2? (const char*)buffer2:""); } else { /* The buffer contains control characters - do a hex dump. Even in full logging mode we limit the line length - however this is no real limit because the provided buffers will never be larger than the maximum assuan line length. */ char *hp; unsigned int nbytes; unsigned int maxbytes = full_logging? (2*LINELENGTH) : 16; nbytes = length1 + length2; if (nbytes > maxbytes) nbytes = maxbytes; if (!(outbuf = malloc (50 + 3*nbytes + 60 + 3 + 1))) res = -1; else { res = 0; hp = outbuf; snprintf (hp, 50, "chan_" CHANNEL_FMT " %s [", ctx->inbound.fd, outbound? "->":"<-"); hp += strlen (hp); n = 0; for (s = buffer1, x = 0; x < length1 && n < nbytes; x++, n++) { *hp++ = ' '; *hp++ = TOHEX (*s >> 4); *hp++ = TOHEX (*s & 0x0f); s++; } for (s = buffer2, x = 0; x < length2 && n < nbytes; x++, n++) { *hp++ = ' '; *hp++ = TOHEX (*s >> 4); *hp++ = TOHEX (*s & 0x0f); s++; } if (nbytes < length1 + length2) { snprintf (hp, 60, " ...(%u byte(s) skipped)", (unsigned int)((length1+length2) - nbytes)); hp += strlen (hp); } strcpy (hp, " ]\n"); } } } else { res = 0; outbuf = NULL; } if (res < 0) ctx->log_cb (ctx, ctx->log_cb_data, ASSUAN_LOG_CONTROL, "[libassuan failed to format the log message]"); else if (outbuf) { ctx->log_cb (ctx, ctx->log_cb_data, ASSUAN_LOG_CONTROL, outbuf); gpgrt_free (outbuf); } #undef TOHEX #undef CHANNEL_FMT gpg_err_set_errno (saved_errno); } diff --git a/src/assuan-pipe-connect.c b/src/assuan-pipe-connect.c index a657c94..1589d79 100644 --- a/src/assuan-pipe-connect.c +++ b/src/assuan-pipe-connect.c @@ -1,428 +1,429 @@ /* assuan-pipe-connect.c - Establish a pipe connection (client) - Copyright (C) 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, - 2011 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, + * 2011 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 /* On Windows systems signal.h is not needed and even not supported on WindowsCE. */ #ifndef HAVE_DOSISH_SYSTEM # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifndef HAVE_W32_SYSTEM # include #else # ifdef HAVE_WINSOCK2_H # include # endif # include #endif #include "assuan-defs.h" #include "debug.h" /* Hacks for Slowaris. */ #ifndef PF_LOCAL # ifdef PF_UNIX # define PF_LOCAL PF_UNIX # else # define PF_LOCAL AF_UNIX # endif #endif #ifndef AF_LOCAL # define AF_LOCAL AF_UNIX #endif /* This should be called to make sure that SIGPIPE gets ignored. */ static void fix_signals (void) { #ifndef HAVE_DOSISH_SYSTEM /* No SIGPIPE for these systems. */ static int fixed_signals; if (!fixed_signals) { 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); } fixed_signals = 1; /* FIXME: This is not MT safe */ } #endif /*HAVE_DOSISH_SYSTEM*/ } /* Helper for pipe_connect. */ static gpg_error_t initial_handshake (assuan_context_t ctx) { assuan_response_t response; int off; gpg_error_t err; err = _assuan_read_from_server (ctx, &response, &off, 0); if (err) TRACE1 (ctx, ASSUAN_LOG_SYSIO, "initial_handshake", ctx, "can't connect server: %s", gpg_strerror (err)); else if (response != ASSUAN_RESPONSE_OK) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "initial_handshake", ctx, "can't connect server: `%s'", ctx->inbound.line); err = _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } return err; } struct at_pipe_fork { void (*user_atfork) (void *opaque, int reserved); void *user_atforkvalue; pid_t parent_pid; }; static void at_pipe_fork_cb (void *opaque, int reserved) { struct at_pipe_fork *atp = opaque; if (atp->user_atfork) atp->user_atfork (atp->user_atforkvalue, reserved); #ifndef HAVE_W32_SYSTEM { char mypidstr[50]; /* We store our parents pid in the environment so that the execed assuan server is able to read the actual pid of the client. The server can't use getppid because it might have been double forked before the assuan server has been initialized. */ sprintf (mypidstr, "%lu", (unsigned long) atp->parent_pid); setenv ("_assuan_pipe_connect_pid", mypidstr, 1); /* Make sure that we never pass a connection fd variable when using a simple pipe. */ unsetenv ("_assuan_connection_fd"); } #endif } static gpg_error_t pipe_connect (assuan_context_t ctx, const char *name, const char **argv, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { gpg_error_t rc; assuan_fd_t rp[2]; assuan_fd_t wp[2]; pid_t pid; int res; struct at_pipe_fork atp; unsigned int spawn_flags; atp.user_atfork = atfork; atp.user_atforkvalue = atforkvalue; atp.parent_pid = getpid (); if (!ctx || !name || !argv || !argv[0]) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (! ctx->flags.no_fixsignals) fix_signals (); if (_assuan_pipe (ctx, rp, 1) < 0) return _assuan_error (ctx, gpg_err_code_from_syserror ()); if (_assuan_pipe (ctx, wp, 0) < 0) { _assuan_close (ctx, rp[0]); _assuan_close_inheritable (ctx, rp[1]); return _assuan_error (ctx, gpg_err_code_from_syserror ()); } spawn_flags = 0; if (flags & ASSUAN_PIPE_CONNECT_DETACHED) spawn_flags |= ASSUAN_SPAWN_DETACHED; /* FIXME: Use atfork handler that closes child fds on Unix. */ res = _assuan_spawn (ctx, &pid, name, argv, wp[0], rp[1], fd_child_list, at_pipe_fork_cb, &atp, spawn_flags); if (res < 0) { rc = gpg_err_code_from_syserror (); _assuan_close (ctx, rp[0]); _assuan_close_inheritable (ctx, rp[1]); _assuan_close_inheritable (ctx, wp[0]); _assuan_close (ctx, wp[1]); return _assuan_error (ctx, rc); } /* Close the stdin/stdout child fds in the parent. */ _assuan_close_inheritable (ctx, rp[1]); _assuan_close_inheritable (ctx, wp[0]); ctx->engine.release = _assuan_client_release; ctx->engine.readfnc = _assuan_simple_read; ctx->engine.writefnc = _assuan_simple_write; ctx->engine.sendfd = NULL; ctx->engine.receivefd = NULL; ctx->finish_handler = _assuan_client_finish; ctx->max_accepts = 1; ctx->accept_handler = NULL; ctx->inbound.fd = rp[0]; /* Our inbound is read end of read pipe. */ ctx->outbound.fd = wp[1]; /* Our outbound is write end of write pipe. */ ctx->pid = pid; rc = initial_handshake (ctx); if (rc) _assuan_reset (ctx); return rc; } /* FIXME: For socketpair_connect, use spawn function and add atfork handler to do the right thing. Instead of stdin and stdout, we extend the fd_child_list by fds[1]. */ #ifndef HAVE_W32_SYSTEM struct at_socketpair_fork { assuan_fd_t peer_fd; void (*user_atfork) (void *opaque, int reserved); void *user_atforkvalue; pid_t parent_pid; }; static void at_socketpair_fork_cb (void *opaque, int reserved) { struct at_socketpair_fork *atp = opaque; if (atp->user_atfork) atp->user_atfork (atp->user_atforkvalue, reserved); #ifndef HAVE_W32_SYSTEM { char mypidstr[50]; /* We store our parents pid in the environment so that the execed assuan server is able to read the actual pid of the client. The server can't use getppid because it might have been double forked before the assuan server has been initialized. */ sprintf (mypidstr, "%lu", (unsigned long) atp->parent_pid); setenv ("_assuan_pipe_connect_pid", mypidstr, 1); /* Now set the environment variable used to convey the connection's file descriptor. */ sprintf (mypidstr, "%d", atp->peer_fd); if (setenv ("_assuan_connection_fd", mypidstr, 1)) _exit (4); } #endif } /* This function is similar to pipe_connect but uses a socketpair and sets the I/O up to use sendmsg/recvmsg. */ static gpg_error_t socketpair_connect (assuan_context_t ctx, const char *name, const char **argv, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue) { gpg_error_t err; int idx; int fds[2]; char mypidstr[50]; pid_t pid; int *child_fds = NULL; int child_fds_cnt = 0; struct at_socketpair_fork atp; int rc; TRACE_BEG3 (ctx, ASSUAN_LOG_CTX, "socketpair_connect", ctx, "name=%s,atfork=%p,atforkvalue=%p", name ? name : "(null)", atfork, atforkvalue); atp.user_atfork = atfork; atp.user_atforkvalue = atforkvalue; atp.parent_pid = getpid (); if (!ctx || (name && (!argv || !argv[0])) || (!name && !argv)) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (! ctx->flags.no_fixsignals) fix_signals (); sprintf (mypidstr, "%lu", (unsigned long)getpid ()); if (fd_child_list) while (fd_child_list[child_fds_cnt] != ASSUAN_INVALID_FD) child_fds_cnt++; child_fds = _assuan_malloc (ctx, (child_fds_cnt + 2) * sizeof (int)); if (! child_fds) return TRACE_ERR (gpg_err_code_from_syserror ()); child_fds[1] = ASSUAN_INVALID_FD; if (fd_child_list) memcpy (&child_fds[1], fd_child_list, (child_fds_cnt + 1) * sizeof (int)); if (_assuan_socketpair (ctx, AF_LOCAL, SOCK_STREAM, 0, fds)) { TRACE_LOG1 ("socketpair failed: %s", strerror (errno)); _assuan_free (ctx, child_fds); return TRACE_ERR (GPG_ERR_ASS_GENERAL); } atp.peer_fd = fds[1]; child_fds[0] = fds[1]; rc = _assuan_spawn (ctx, &pid, name, argv, ASSUAN_INVALID_FD, ASSUAN_INVALID_FD, child_fds, at_socketpair_fork_cb, &atp, 0); if (rc < 0) { err = gpg_err_code_from_syserror (); _assuan_close (ctx, fds[0]); _assuan_close (ctx, fds[1]); _assuan_free (ctx, child_fds); return TRACE_ERR (err); } /* For W32, the user needs to know the server-local names of the inherited handles. Return them here. Note that the translation of the peer socketpair fd (fd_child_list[0]) must be done by the wrapper program based on the environment variable _assuan_connection_fd. */ if (fd_child_list) { for (idx = 0; fd_child_list[idx] != -1; idx++) /* We add 1 to skip over the socketpair end. */ fd_child_list[idx] = child_fds[idx + 1]; } _assuan_free (ctx, child_fds); /* If this is the server child process, exit early. */ if (! name && (*argv)[0] == 's') { _assuan_close (ctx, fds[0]); return 0; } _assuan_close (ctx, fds[1]); ctx->engine.release = _assuan_client_release; ctx->finish_handler = _assuan_client_finish; ctx->max_accepts = 1; ctx->inbound.fd = fds[0]; ctx->outbound.fd = fds[0]; _assuan_init_uds_io (ctx); err = initial_handshake (ctx); if (err) _assuan_reset (ctx); return err; } #endif /*!HAVE_W32_SYSTEM*/ /* Connect to a server over a full-duplex socket (i.e. created by socketpair), creating the assuan context and returning it in CTX. The server filename is NAME, the argument vector in ARGV. FD_CHILD_LIST is a -1 terminated list of file descriptors not to close in the child. ATFORK is called in the child right after the fork; ATFORKVALUE is passed as the first argument and 0 is passed as the second argument. The ATFORK function should only act if the second value is 0. FLAGS is a bit vector and controls how the function acts: Bit 0: If cleared a simple pipe based server is expected and the function behaves similar to `assuan_pipe_connect'. If set a server based on full-duplex pipes is expected. Such pipes are usually created using the `socketpair' function. It also enables features only available with such servers. Bit 7: If set and there is a need to start the server it will be started as a background process. This flag is useful under W32 systems, so that no new console is created and pops up a console window when starting the server If NAME is NULL, no exec is done but the same process is continued. However all file descriptors are closed and some special environment variables are set. To let the caller detect whether the child or the parent continues, the child returns "client" or "server" in *ARGV (but it is sufficient to check only the first character). This feature is only available on POSIX platforms. */ gpg_error_t assuan_pipe_connect (assuan_context_t ctx, const char *name, const char *argv[], assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_pipe_connect", ctx, "name=%s, flags=0x%x", name ? name : "(null)", flags); if (flags & ASSUAN_PIPE_CONNECT_FDPASSING) { #ifdef HAVE_W32_SYSTEM return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED); #else return socketpair_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue); #endif } else return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue, flags); } diff --git a/src/assuan-pipe-server.c b/src/assuan-pipe-server.c index 017dc7b..c78abcd 100644 --- a/src/assuan-pipe-server.c +++ b/src/assuan-pipe-server.c @@ -1,134 +1,135 @@ -/* assuan-pipe-server.c - Assuan server working over a pipe - Copyright (C) 2001, 2002, 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . +/* assuan-pipe-server.c - Assuan server working over a pipe + * Copyright (C) 2001, 2002, 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include -# endif +# endif # include #ifdef HAVE_FCNTL_H # include #endif #endif #include "assuan-defs.h" #include "debug.h" /* Returns true if atoi(S) denotes a valid socket. */ #ifndef HAVE_W32_SYSTEM static int is_valid_socket (const char *s) { struct stat buf; if ( fstat (atoi (s), &buf ) ) return 0; return S_ISSOCK (buf.st_mode); } #endif /*!HAVE_W32_SYSTEM*/ /* This actually is a int file descriptor (and not assuan_fd_t) as _get_osfhandle is called on W32 systems. */ gpg_error_t assuan_init_pipe_server (assuan_context_t ctx, assuan_fd_t filedes[2]) { const char *s; unsigned long ul; gpg_error_t rc; assuan_fd_t infd = ASSUAN_INVALID_FD; assuan_fd_t outfd = ASSUAN_INVALID_FD; int is_usd = 0; TRACE_BEG (ctx, ASSUAN_LOG_CTX, "assuan_init_pipe_server", ctx); if (filedes) { TRACE_LOG2 ("fd[0]=0x%x, fd[1]=0x%x", filedes[0], filedes[1]); } - + rc = _assuan_register_std_commands (ctx); if (rc) return TRACE_ERR (rc); #ifdef HAVE_W32_SYSTEM infd = filedes[0]; outfd = filedes[1]; #else s = getenv ("_assuan_connection_fd"); if (s && *s && is_valid_socket (s)) { /* Well, we are called with an bi-directional file descriptor. Prepare for using sendmsg/recvmsg. In this case we ignore the passed file descriptors. */ infd = atoi (s); outfd = atoi (s); is_usd = 1; } - else if (filedes && filedes[0] != ASSUAN_INVALID_FD + else if (filedes && filedes[0] != ASSUAN_INVALID_FD && filedes[1] != ASSUAN_INVALID_FD ) { /* Standard pipe server. */ infd = filedes[0]; outfd = filedes[1]; } else { rc = _assuan_error (ctx, GPG_ERR_ASS_SERVER_START); return TRACE_ERR (rc); } #endif ctx->is_server = 1; ctx->engine.release = _assuan_server_release; ctx->engine.readfnc = _assuan_simple_read; ctx->engine.writefnc = _assuan_simple_write; ctx->engine.sendfd = NULL; ctx->engine.receivefd = NULL; ctx->max_accepts = 1; s = getenv ("_assuan_pipe_connect_pid"); if (s && (ul=strtoul (s, NULL, 10)) && ul) ctx->pid = (pid_t)ul; else ctx->pid = (pid_t)-1; ctx->accept_handler = NULL; ctx->finish_handler = _assuan_server_finish; ctx->inbound.fd = infd; ctx->outbound.fd = outfd; if (is_usd) _assuan_init_uds_io (ctx); return TRACE_SUC(); } diff --git a/src/assuan-socket-connect.c b/src/assuan-socket-connect.c index 3d3176e..c54c44d 100644 --- a/src/assuan-socket-connect.c +++ b/src/assuan-socket-connect.c @@ -1,349 +1,350 @@ /* assuan-socket-connect.c - Assuan socket based client - Copyright (C) 2002, 2003, 2004, 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2002, 2003, 2004, 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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+ */ #include #include #include #include #include #include #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include #else # include # include # include # include #endif #include "assuan-defs.h" #include "debug.h" /* Hacks for Slowaris. */ #ifndef PF_LOCAL # ifdef PF_UNIX # define PF_LOCAL PF_UNIX # else # define PF_LOCAL AF_UNIX # endif #endif #ifndef AF_LOCAL # define AF_LOCAL AF_UNIX #endif #ifndef INADDR_NONE #define INADDR_NONE ((unsigned long)(-1)) #endif /*INADDR_NONE*/ #ifndef SUN_LEN # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + strlen ((ptr)->sun_path)) #endif #undef WITH_IPV6 #if defined (AF_INET6) && defined(PF_INET) \ && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON) # define WITH_IPV6 1 #endif /* Returns true if STR represents a valid port number in decimal notation and no garbage is following. */ static int parse_portno (const char *str, uint16_t *r_port) { unsigned int value; for (value=0; *str && (*str >= '0' && *str <= '9'); str++) { value = value * 10 + (*str - '0'); if (value > 65535) return 0; } if (*str || !value) return 0; *r_port = value; return 1; } static gpg_error_t _assuan_connect_finalize (assuan_context_t ctx, assuan_fd_t fd, unsigned int flags) { gpg_error_t err; ctx->engine.release = _assuan_client_release; ctx->engine.readfnc = _assuan_simple_read; ctx->engine.writefnc = _assuan_simple_write; ctx->engine.sendfd = NULL; ctx->engine.receivefd = NULL; ctx->finish_handler = _assuan_client_finish; ctx->inbound.fd = fd; ctx->outbound.fd = fd; ctx->max_accepts = -1; if (flags & ASSUAN_SOCKET_CONNECT_FDPASSING) _assuan_init_uds_io (ctx); /* initial handshake */ { assuan_response_t response; int off; err = _assuan_read_from_server (ctx, &response, &off, 0); if (err) TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to server: %s\n", gpg_strerror (err)); else if (response != ASSUAN_RESPONSE_OK) { char *sname = _assuan_encode_c_string (ctx, ctx->inbound.line); if (sname) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to server: %s", sname); _assuan_free (ctx, sname); } err = _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } } return err; } /* Attach an existing connected file descriptor FD to an allocated handle CTX * and initialize the connection. */ gpg_error_t assuan_socket_connect_fd (assuan_context_t ctx, int fd, unsigned int flags) { gpg_error_t err; assuan_fd_t afd; if (!ctx || fd < 0) return GPG_ERR_INV_ARG; afd = assuan_fd_from_posix_fd (fd); if (afd == ASSUAN_INVALID_FD) return GPG_ERR_INV_ARG; err = _assuan_connect_finalize(ctx, afd, flags); if (err) _assuan_reset (ctx); return err; } /* Make a connection to the Unix domain socket NAME and return a new Assuan context in CTX. SERVER_PID is currently not used but may become handy in the future. Defined flag bits are: ASSUAN_SOCKET_CONNECT_FDPASSING sendmsg and recvmsg are used. NAME must either start with a slash and optional with a drive prefix ("c:") or use one of these URL schemata: file:// This is the same as the default just with an explicit schemata. assuan://: assuan://[]: Connect using TCP to PORT of the server with the numerical IPADDR. Note that '[' and ']' are literal characters. */ gpg_error_t assuan_socket_connect (assuan_context_t ctx, const char *name, pid_t server_pid, unsigned int flags) { gpg_error_t err = 0; assuan_fd_t fd; #ifdef WITH_IPV6 struct sockaddr_in6 srvr_addr_in6; #endif struct sockaddr_un srvr_addr_un; struct sockaddr_in srvr_addr_in; struct sockaddr *srvr_addr = NULL; uint16_t port = 0; size_t len = 0; const char *s; int af = AF_LOCAL; int pf = PF_LOCAL; TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_socket_connect", ctx, "name=%s, flags=0x%x", name ? name : "(null)", flags); if (!ctx || !name) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!strncmp (name, "file://", 7) && name[7]) name += 7; else if (!strncmp (name, "assuan://", 9) && name[9]) { name += 9; af = AF_INET; pf = PF_INET; } else /* Default. */ { /* We require that the name starts with a slash if no URL schemata is used. To make things easier we allow an optional drive prefix. */ s = name; if (*s && s[1] == ':') s += 2; if (*s != DIRSEP_C && *s != '/') return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); } if (af == AF_LOCAL) { int redirected; if (_assuan_sock_set_sockaddr_un (name, (struct sockaddr *)&srvr_addr_un, &redirected)) return _assuan_error (ctx, gpg_err_code_from_syserror ()); len = SUN_LEN (&srvr_addr_un); srvr_addr = (struct sockaddr *)&srvr_addr_un; } else { char *addrstr, *p; #ifdef HAVE_INET_PTON void *addrbuf = NULL; #endif addrstr = _assuan_malloc (ctx, strlen (name) + 1); if (!addrstr) return _assuan_error (ctx, gpg_err_code_from_syserror ()); if (*name == '[') { strcpy (addrstr, name+1); p = strchr (addrstr, ']'); if (!p || p[1] != ':' || !parse_portno (p+2, &port)) err = _assuan_error (ctx, GPG_ERR_BAD_URI); else { *p = 0; #ifdef WITH_IPV6 af = AF_INET6; pf = PF_INET6; memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); srvr_addr_in6.sin6_family = af; srvr_addr_in6.sin6_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in6.sin6_addr; #endif srvr_addr = (struct sockaddr *)&srvr_addr_in6; len = sizeof srvr_addr_in6; #else err = _assuan_error (ctx, GPG_ERR_EAFNOSUPPORT); #endif } } else { strcpy (addrstr, name); p = strchr (addrstr, ':'); if (!p || !parse_portno (p+1, &port)) err = _assuan_error (ctx, GPG_ERR_BAD_URI); else { *p = 0; memset (&srvr_addr_in, 0, sizeof srvr_addr_in); srvr_addr_in.sin_family = af; srvr_addr_in.sin_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in.sin_addr; #endif srvr_addr = (struct sockaddr *)&srvr_addr_in; len = sizeof srvr_addr_in; } } if (!err) { #ifdef HAVE_INET_PTON switch (inet_pton (af, addrstr, addrbuf)) { case 1: break; case 0: err = _assuan_error (ctx, GPG_ERR_BAD_URI); break; default: err = _assuan_error (ctx, gpg_err_code_from_syserror ()); } #else /*!HAVE_INET_PTON*/ /* We need to use the old function. If we are here v6 support isn't enabled anyway and thus we can do fine without. Note that Windows as a compatible inet_pton function named inetPton, but only since Vista. */ srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) err = _assuan_error (ctx, GPG_ERR_BAD_URI); #endif /*!HAVE_INET_PTON*/ } _assuan_free (ctx, addrstr); if (err) return err; } fd = _assuan_sock_new (ctx, pf, SOCK_STREAM, 0); if (fd == ASSUAN_INVALID_FD) { err = _assuan_error (ctx, gpg_err_code_from_syserror ()); TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't create socket: %s", strerror (errno)); return err; } if (_assuan_sock_connect (ctx, fd, srvr_addr, len) == -1) { TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to `%s': %s\n", name, strerror (errno)); _assuan_close (ctx, fd); return _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } err = _assuan_connect_finalize (ctx, fd, flags); if (err) _assuan_reset (ctx); return err; } diff --git a/src/assuan-socket-server.c b/src/assuan-socket-server.c index 964720b..a5b7fd7 100644 --- a/src/assuan-socket-server.c +++ b/src/assuan-socket-server.c @@ -1,227 +1,228 @@ /* assuan-socket-server.c - Assuan socket based server - Copyright (C) 2002, 2007, 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2002, 2007, 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_UCRED_H #include #endif #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include -# endif +# endif # include # if HAVE_SYS_SOCKET_H # include # elif HAVE_WS2TCPIP_H # include # endif #else # include # include #endif #include "debug.h" #include "assuan-defs.h" static gpg_error_t accept_connection_bottom (assuan_context_t ctx) { assuan_fd_t fd = ctx->connected_fd; TRACE (ctx, ASSUAN_LOG_SYSIO, "accept_connection_bottom", ctx); ctx->peercred_valid = 0; #ifdef HAVE_SO_PEERCRED { struct ucred cr; socklen_t cl = sizeof cr; if ( !getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)) { ctx->peercred.pid = cr.pid; ctx->peercred.uid = cr.uid; ctx->peercred.gid = cr.gid; ctx->peercred_valid = 1; /* This overrides any already set PID if the function returns a valid one. */ if (cr.pid != ASSUAN_INVALID_PID && cr.pid) ctx->pid = cr.pid; } } #elif defined (HAVE_GETPEERUCRED) { ucred_t *ucred = NULL; if (getpeerucred (fd, &ucred) != -1) { ctx->peercred.uid = ucred_geteuid (ucred); ctx->peercred.gid = ucred_getegid (ucred); ctx->peercred.pid = ucred_getpid (ucred); ctx->peercred_valid = 1; ucred_free (ucred); } } #elif defined (HAVE_LOCAL_PEEREID) { struct unpcbid unp; socklen_t unpl = sizeof unp; if (getsockopt (fd, 0, LOCAL_PEEREID, &unp, &unpl) != -1) { ctx->peercred.pid = unp.unp_pid; ctx->peercred.uid = unp.unp_euid; ctx->peercred.gid = unp.unp_egid; ctx->peercred_valid = 1; } } #elif defined(HAVE_GETPEEREID) { if (getpeereid (fd, &ctx->peercred.uid, &ctx->peercred.gid) != -1) { ctx->peercred.pid = ASSUAN_INVALID_PID; ctx->peercred_valid = 1; } } #endif ctx->inbound.fd = fd; ctx->inbound.eof = 0; ctx->inbound.linelen = 0; ctx->inbound.attic.linelen = 0; ctx->inbound.attic.pending = 0; ctx->outbound.fd = fd; ctx->outbound.data.linelen = 0; ctx->outbound.data.error = 0; - + ctx->flags.confidential = 0; return 0; } static gpg_error_t accept_connection (assuan_context_t ctx) { assuan_fd_t fd; struct sockaddr_un clnt_addr; socklen_t len = sizeof clnt_addr; - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "accept_connection", ctx, + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "accept_connection", ctx, "listen_fd=0x%x", ctx->listen_fd); - fd = SOCKET2HANDLE(accept (HANDLE2SOCKET(ctx->listen_fd), + fd = SOCKET2HANDLE(accept (HANDLE2SOCKET(ctx->listen_fd), (struct sockaddr*)&clnt_addr, &len )); if (fd == ASSUAN_INVALID_FD) { return _assuan_error (ctx, gpg_err_code_from_syserror ()); } - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "accept_connection", ctx, + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "accept_connection", ctx, "fd->0x%x", fd); if (_assuan_sock_check_nonce (ctx, fd, &ctx->listen_nonce)) { _assuan_close (ctx, fd); return _assuan_error (ctx, GPG_ERR_ASS_ACCEPT_FAILED); } ctx->connected_fd = fd; return accept_connection_bottom (ctx); } -/* +/* Flag bits: 0 - use sendmsg/recvmsg to allow descriptor passing 1 - FD has already been accepted. */ gpg_error_t assuan_init_socket_server (assuan_context_t ctx, assuan_fd_t fd, unsigned int flags) { gpg_error_t rc; TRACE_BEG2 (ctx, ASSUAN_LOG_CTX, "assuan_init_socket_server", ctx, "fd=0x%x, flags=0x%x", fd, flags); - + rc = _assuan_register_std_commands (ctx); if (rc) return TRACE_ERR (rc); ctx->engine.release = _assuan_server_release; ctx->engine.readfnc = _assuan_simple_read; ctx->engine.writefnc = _assuan_simple_write; ctx->engine.sendfd = NULL; ctx->engine.receivefd = NULL; ctx->is_server = 1; if (flags & ASSUAN_SOCKET_SERVER_ACCEPTED) /* We want a second accept to indicate EOF. */ ctx->max_accepts = 1; else ctx->max_accepts = -1; ctx->input_fd = ASSUAN_INVALID_FD; ctx->output_fd = ASSUAN_INVALID_FD; ctx->inbound.fd = ASSUAN_INVALID_FD; ctx->outbound.fd = ASSUAN_INVALID_FD; if (flags & ASSUAN_SOCKET_SERVER_ACCEPTED) { ctx->listen_fd = ASSUAN_INVALID_FD; ctx->connected_fd = fd; } else { ctx->listen_fd = fd; ctx->connected_fd = ASSUAN_INVALID_FD; } ctx->accept_handler = ((flags & ASSUAN_SOCKET_SERVER_ACCEPTED) - ? accept_connection_bottom + ? accept_connection_bottom : accept_connection); ctx->finish_handler = _assuan_server_finish; if (flags & ASSUAN_SOCKET_SERVER_FDPASSING) _assuan_init_uds_io (ctx); rc = _assuan_register_std_commands (ctx); if (rc) _assuan_reset (ctx); return TRACE_ERR (rc); } /* Save a copy of NONCE in context CTX. This should be used to register the server's nonce with an context established by assuan_init_socket_server. */ void assuan_set_sock_nonce (assuan_context_t ctx, assuan_sock_nonce_t *nonce) { if (ctx && nonce) ctx->listen_nonce = *nonce; } diff --git a/src/assuan-socket.c b/src/assuan-socket.c index 147ec2a..adcd40a 100644 --- a/src/assuan-socket.c +++ b/src/assuan-socket.c @@ -1,1496 +1,1497 @@ /* assuan-socket.c - Socket wrapper - Copyright (C) 2004, 2005, 2009 Free Software Foundation, Inc. - Copyright (C) 2001-2015 g10 Code GmbH - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2004, 2005, 2009 Free Software Foundation, Inc. + * Copyright (C) 2001-2015 g10 Code GmbH + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #ifdef HAVE_W32_SYSTEM # define WIN32_LEAN_AND_MEAN # include # include #ifndef HAVE_W32CE_SYSTEM # include #endif #else # include # include # include # include #endif #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_FCNTL_H #include #endif #include #include "assuan-defs.h" #include "debug.h" /* Hacks for Slowaris. */ #ifndef PF_LOCAL # ifdef PF_UNIX # define PF_LOCAL PF_UNIX # else # define PF_LOCAL AF_UNIX # endif #endif #ifndef AF_LOCAL # define AF_LOCAL AF_UNIX #endif #ifdef HAVE_W32_SYSTEM #ifndef S_IRUSR # define S_IRUSR 0 # define S_IWUSR 0 #endif #ifndef S_IRGRP # define S_IRGRP 0 # define S_IWGRP 0 #endif #ifndef ENOTSUP #define ENOTSUP 129 #endif #ifndef EPROTO #define EPROTO 134 #endif #ifndef EPROTONOSUPPORT #define EPROTONOSUPPORT 135 #endif #ifndef ENETDOWN #define ENETDOWN 116 #endif #ifndef ENETUNREACH #define ENETUNREACH 118 #endif #ifndef EHOSTUNREACH #define EHOSTUNREACH 110 #endif #ifndef ECONNREFUSED #define ECONNREFUSED 107 #endif #ifndef ETIMEDOUT #define ETIMEDOUT 138 #endif #endif #ifndef ENAMETOOLONG # define ENAMETOOLONG EINVAL #endif #ifndef SUN_LEN # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + strlen ((ptr)->sun_path)) #endif #ifndef INADDR_LOOPBACK # define INADDR_LOOPBACK ((in_addr_t) 0x7f000001) /* 127.0.0.1. */ #endif /* The standard SOCKS and TOR port. */ #define SOCKS_PORT 1080 #define TOR_PORT 9050 #define TOR_PORT2 9150 /* The Tor browser is listening there. */ /* In the future, we can allow access to sock_ctx, if that context's hook functions need to be overridden. There can only be one global assuan_sock_* user (one library or one application) with this convenience interface, if non-standard hook functions are needed. */ static assuan_context_t sock_ctx; /* This global flag can be set using assuan_sock_set_flag to enable TOR or SOCKS mode for all sockets. It may not be reset. The value is the port to be used. */ static unsigned short tor_mode; #ifdef HAVE_W32_SYSTEM /* A table of active Cygwin connections. This is only used for listening socket which should be only a few. We do not enter sockets after a connect into this table. */ static assuan_fd_t cygwin_fdtable[16]; /* A critical section to guard access to the table of Cygwin connections. */ static CRITICAL_SECTION cygwin_fdtable_cs; /* Return true if SOCKFD is listed as Cygwin socket. */ static int is_cygwin_fd (assuan_fd_t sockfd) { int ret = 0; int i; EnterCriticalSection (&cygwin_fdtable_cs); for (i=0; i < DIM(cygwin_fdtable); i++) { if (cygwin_fdtable[i] == sockfd) { ret = 1; break; } } LeaveCriticalSection (&cygwin_fdtable_cs); return ret; } /* Insert SOCKFD into the table of Cygwin sockets. Return 0 on success or -1 on error. */ static int insert_cygwin_fd (assuan_fd_t sockfd) { int ret = 0; int mark = -1; int i; EnterCriticalSection (&cygwin_fdtable_cs); for (i=0; i < DIM(cygwin_fdtable); i++) { if (cygwin_fdtable[i] == sockfd) goto leave; /* Already in table. */ else if (cygwin_fdtable[i] == ASSUAN_INVALID_FD) mark = i; } if (mark == -1) { gpg_err_set_errno (EMFILE); ret = -1; } else cygwin_fdtable[mark] = sockfd; leave: LeaveCriticalSection (&cygwin_fdtable_cs); return ret; } /* Delete SOCKFD from the table of Cygwin sockets. */ static void delete_cygwin_fd (assuan_fd_t sockfd) { int i; EnterCriticalSection (&cygwin_fdtable_cs); for (i=0; i < DIM(cygwin_fdtable); i++) { if (cygwin_fdtable[i] == sockfd) { cygwin_fdtable[i] = ASSUAN_INVALID_FD; break; } } LeaveCriticalSection (&cygwin_fdtable_cs); return; } #ifdef HAVE_W32CE_SYSTEM static wchar_t * utf8_to_wchar (const char *string) { int n; size_t nbytes; wchar_t *result; if (!string) return NULL; n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0); if (n < 0) return NULL; nbytes = (size_t)(n+1) * sizeof(*result); if (nbytes / sizeof(*result) != (n+1)) { SetLastError (ERROR_INVALID_PARAMETER); return NULL; } result = malloc (nbytes); if (!result) return NULL; n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n); if (n < 0) { n = GetLastError (); free (result); result = NULL; SetLastError (n); } return result; } static HANDLE MyCreateFile (LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwSharedMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { wchar_t *filename; HANDLE result; int err; filename = utf8_to_wchar (lpFileName); if (!filename) return INVALID_HANDLE_VALUE; result = CreateFileW (filename, dwDesiredAccess, dwSharedMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); err = GetLastError (); free (filename); SetLastError (err); return result; } static int MyDeleteFile (LPCSTR lpFileName) { wchar_t *filename; int result, err; filename = utf8_to_wchar (lpFileName); if (!filename) return 0; result = DeleteFileW (filename); err = GetLastError (); free (filename); SetLastError (err); return result; } #else /*!HAVE_W32CE_SYSTEM*/ #define MyCreateFile CreateFileA #define MyDeleteFile DeleteFileA #endif /*!HAVE_W32CE_SYSTEM*/ int _assuan_sock_wsa2errno (int err) { switch (err) { case WSAENOTSOCK: return EINVAL; case WSAEWOULDBLOCK: return EAGAIN; case ERROR_BROKEN_PIPE: return EPIPE; case WSANOTINITIALISED: return ENOSYS; case WSAECONNREFUSED: return ECONNREFUSED; default: return EIO; } } /* W32: Fill BUFFER with LENGTH bytes of random. Returns -1 on failure, 0 on success. Sets errno on failure. */ static int get_nonce (char *buffer, size_t nbytes) { HCRYPTPROV prov; int ret = -1; if (!CryptAcquireContext (&prov, NULL, NULL, PROV_RSA_FULL, (CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) ) gpg_err_set_errno (ENODEV); else { if (!CryptGenRandom (prov, nbytes, (unsigned char *) buffer)) gpg_err_set_errno (ENODEV); else ret = 0; CryptReleaseContext (prov, 0); } return ret; } /* W32: The buffer for NONCE needs to be at least 16 bytes. Returns 0 on success and sets errno on failure. If FNAME has a Cygwin socket descriptor True is stored at CYGWIN. */ static int read_port_and_nonce (const char *fname, unsigned short *port, char *nonce, int *cygwin) { FILE *fp; char buffer[50], *p; size_t nread; int aval; *cygwin = 0; fp = fopen (fname, "rb"); if (!fp) return -1; nread = fread (buffer, 1, sizeof buffer - 1, fp); fclose (fp); if (!nread) { gpg_err_set_errno (ENOENT); return -1; } buffer[nread] = 0; if (!strncmp (buffer, "!", 10)) { /* This is the Cygwin compatible socket emulation. The format * of the file is: * * "!%u %c %08x-%08x-%08x-%08x\x00" * * %d for port number, %c for kind of socket (s for STREAM), and * we have 16-byte random bytes for nonce. We only support * stream mode. */ unsigned int u0; int narr[4]; if (sscanf (buffer+10, "%u s %08x-%08x-%08x-%08x", &u0, narr+0, narr+1, narr+2, narr+3) != 5 || u0 < 1 || u0 > 65535) { gpg_err_set_errno (EINVAL); return -1; } *port = u0; memcpy (nonce, narr, 16); *cygwin = 1; } else { /* This is our own socket emulation. */ aval = atoi (buffer); if (aval < 1 || aval > 65535) { gpg_err_set_errno (EINVAL); return -1; } *port = (unsigned int)aval; for (p=buffer; nread && *p != '\n'; p++, nread--) ; if (*p != '\n' || nread != 17) { gpg_err_set_errno (EINVAL); return -1; } p++; nread--; memcpy (nonce, p, 16); } return 0; } #endif /*HAVE_W32_SYSTEM*/ #ifndef HAVE_W32_SYSTEM /* Find a redirected socket name for fname and return a malloced setup filled sockaddr. If this does not work out NULL is returned and ERRNO is set. If the file seems to be a redirect True is stored at R_REDIRECT. Note that this function uses the standard malloc and not the assuan wrapped one. The format of the file is: %Assuan% socket=NAME where NAME is the actual socket to use. No white spaces are allowed, both lines must be terminated by a single LF, extra lines are not allowed. Environment variables are interpreted in NAME if given in "${VAR} notation; no escape characters are defined, if "${" shall be used verbatim, you need to use an environment variable with that content. The use of an absolute NAME is strongly suggested. The length of the file is limited to 511 bytes which is more than sufficient for that common value of 107 for sun_path. */ static struct sockaddr_un * eval_redirection (const char *fname, int *r_redirect) { FILE *fp; char buffer[512], *name; size_t n; struct sockaddr_un *addr; char *p, *pend; const char *s; *r_redirect = 0; fp = fopen (fname, "rb"); if (!fp) return NULL; n = fread (buffer, 1, sizeof buffer - 1, fp); fclose (fp); if (!n) { gpg_err_set_errno (ENOENT); return NULL; } buffer[n] = 0; /* Check that it is a redirection file. We also check that the first byte of the name is not a LF because that would lead to an zero length name. */ if (n < 17 || buffer[n-1] != '\n' || memcmp (buffer, "%Assuan%\nsocket=", 16) || buffer[16] == '\n') { gpg_err_set_errno (EINVAL); return NULL; } buffer[n-1] = 0; name = buffer + 16; *r_redirect = 1; addr = calloc (1, sizeof *addr); if (!addr) return NULL; addr->sun_family = AF_LOCAL; n = 0; for (p=name; *p; p++) { if (*p == '$' && p[1] == '{') { p += 2; pend = strchr (p, '}'); if (!pend) { free (addr); gpg_err_set_errno (EINVAL); return NULL; } *pend = 0; if (*p && (s = getenv (p))) { for (; *s; s++) { if (n < sizeof addr->sun_path - 1) addr->sun_path[n++] = *s; else { free (addr); gpg_err_set_errno (ENAMETOOLONG); return NULL; } } } p = pend; } else if (*p == '\n') break; /* Be nice and stop at the first LF. */ else if (n < sizeof addr->sun_path - 1) addr->sun_path[n++] = *p; else { free (addr); gpg_err_set_errno (ENAMETOOLONG); return NULL; } } return addr; } #endif /*!HAVE_W32_SYSTEM*/ /* Return a new socket. Note that under W32 we consider a socket the same as an System Handle; all functions using such a handle know about this dual use and act accordingly. */ assuan_fd_t _assuan_sock_new (assuan_context_t ctx, int domain, int type, int proto) { #ifdef HAVE_W32_SYSTEM assuan_fd_t res; if (domain == AF_UNIX || domain == AF_LOCAL) domain = AF_INET; res = SOCKET2HANDLE(_assuan_socket (ctx, domain, type, proto)); return res; #else return _assuan_socket (ctx, domain, type, proto); #endif } int _assuan_sock_set_flag (assuan_context_t ctx, assuan_fd_t sockfd, const char *name, int value) { (void)ctx; if (!strcmp (name, "cygwin")) { #ifdef HAVE_W32_SYSTEM if (!value) delete_cygwin_fd (sockfd); else if (insert_cygwin_fd (sockfd)) return -1; #else /* Setting the Cygwin flag on non-Windows is ignored. */ #endif } else if (!strcmp (name, "tor-mode") || !strcmp (name, "socks")) { /* If SOCKFD is ASSUAN_INVALID_FD this controls global flag to switch AF_INET and AF_INET6 into TOR mode by using a SOCKS5 proxy on localhost:9050. It may only be switched on and this needs to be done before any new threads are started. Once TOR mode has been enabled, TOR mode can be disabled for a specific socket by using SOCKFD with a VALUE of 0. */ if (sockfd == ASSUAN_INVALID_FD) { if (tor_mode && !value) { gpg_err_set_errno (EPERM); return -1; /* Clearing the global flag is not allowed. */ } else if (value) { if (*name == 's') tor_mode = SOCKS_PORT; else tor_mode = TOR_PORT; } } else if (tor_mode && sockfd != ASSUAN_INVALID_FD) { /* Fixme: Disable/enable tormode for the given context. */ } else { gpg_err_set_errno (EINVAL); return -1; } } else { gpg_err_set_errno (EINVAL); return -1; } return 0; } int _assuan_sock_get_flag (assuan_context_t ctx, assuan_fd_t sockfd, const char *name, int *r_value) { (void)ctx; if (!strcmp (name, "cygwin")) { #ifdef HAVE_W32_SYSTEM *r_value = is_cygwin_fd (sockfd); #else *r_value = 0; #endif } else if (!strcmp (name, "tor-mode")) { /* FIXME: Find tor-mode for the given socket. */ *r_value = tor_mode == TOR_PORT; } else if (!strcmp (name, "socks")) { *r_value = tor_mode == SOCKS_PORT; } else { gpg_err_set_errno (EINVAL); return -1; } return 0; } /* Read NBYTES from SOCKFD into BUFFER. Return 0 on success. Handle EAGAIN and EINTR. */ static int do_readn (assuan_context_t ctx, assuan_fd_t sockfd, void *buffer, size_t nbytes) { char *p = buffer; ssize_t n; while (nbytes) { n = _assuan_read (ctx, sockfd, p, nbytes); if (n < 0 && errno == EINTR) ; else if (n < 0 && errno == EAGAIN) _assuan_usleep (ctx, 100000); /* 100ms */ else if (n < 0) return -1; else if (!n) { gpg_err_set_errno (EIO); return -1; } else { p += n; nbytes -= n; } } return 0; } /* Write NBYTES from BUFFER to SOCKFD. Return 0 on success; on error return -1 and set ERRNO. */ static int do_writen (assuan_context_t ctx, assuan_fd_t sockfd, const void *buffer, size_t nbytes) { int ret; ret = _assuan_write (ctx, sockfd, buffer, nbytes); if (ret >= 0 && ret != nbytes) { gpg_err_set_errno (EIO); ret = -1; } else if (ret >= 0) ret = 0; return ret; } /* Connect using the SOCKS5 protocol. */ static int socks5_connect (assuan_context_t ctx, assuan_fd_t sock, unsigned short socksport, const char *credentials, const char *hostname, unsigned short hostport, struct sockaddr *addr, socklen_t length) { int ret; /* struct sockaddr_in6 proxyaddr_in6; */ struct sockaddr_in proxyaddr_in; struct sockaddr *proxyaddr; size_t proxyaddrlen; struct sockaddr_in6 *addr_in6; struct sockaddr_in *addr_in; unsigned char buffer[22+512]; /* The extra 512 gives enough space for username/password or the hostname. */ size_t buflen, hostnamelen; int method; /* memset (&proxyaddr_in6, 0, sizeof proxyaddr_in6); */ memset (&proxyaddr_in, 0, sizeof proxyaddr_in); /* Either HOSTNAME or ADDR may be given. */ if (hostname && addr) { gpg_err_set_errno (EINVAL); return -1; } /* If a hostname is given it must fit into our buffer and it must be less than 256 so that its length can be encoded in one byte. */ hostnamelen = hostname? strlen (hostname) : 0; if (hostnamelen > 255) { gpg_err_set_errno (ENAMETOOLONG); return -1; } /* Connect to local host. */ /* Fixme: First try to use IPv6 but note that _assuan_sock_connect_byname created the socket with AF_INET. */ proxyaddr_in.sin_family = AF_INET; proxyaddr_in.sin_port = htons (socksport); proxyaddr_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK); proxyaddr = (struct sockaddr *)&proxyaddr_in; proxyaddrlen = sizeof proxyaddr_in; ret = _assuan_connect (ctx, HANDLE2SOCKET (sock), proxyaddr, proxyaddrlen); if (ret && socksport == TOR_PORT && errno == ECONNREFUSED) { /* Standard Tor port failed - try the Tor browser port. */ proxyaddr_in.sin_port = htons (TOR_PORT2); ret = _assuan_connect (ctx, HANDLE2SOCKET (sock), proxyaddr, proxyaddrlen); } /* If we get an EINPROGRESS here the caller is trying to do a * non-blocking connect (e.g. for custom time out handling) which * fails here. The easiest fix would be to allow the client to tell * us the timeout value and we do the timeout handling later on in the * Socks protocol. */ if (ret) return ret; buffer[0] = 5; /* RFC-1928 VER field. */ buffer[1] = 1; /* NMETHODS */ if (credentials) method = 2; /* Method: username/password authentication. */ else method = 0; /* Method: No authentication required. */ buffer[2] = method; /* Negotiate method. */ ret = do_writen (ctx, sock, buffer, 3); if (ret) return ret; ret = do_readn (ctx, sock, buffer, 2); if (ret) return ret; if (buffer[0] != 5 || buffer[1] != method ) { /* Socks server returned wrong version or does not support our requested method. */ gpg_err_set_errno (ENOTSUP); /* Fixme: Is there a better errno? */ return -1; } if (credentials) { const char *password; int ulen, plen; password = strchr (credentials, ':'); if (!password) { gpg_err_set_errno (EINVAL); /* No password given. */ return -1; } ulen = password - credentials; password++; plen = strlen (password); if (!ulen || ulen > 255 || !plen || plen > 255) { gpg_err_set_errno (EINVAL); return -1; } buffer[0] = 1; /* VER of the sub-negotiation. */ buffer[1] = ulen; buflen = 2; memcpy (buffer+buflen, credentials, ulen); buflen += ulen; buffer[buflen++] = plen; memcpy (buffer+buflen, password, plen); buflen += plen; ret = do_writen (ctx, sock, buffer, buflen); wipememory (buffer, buflen); if (ret) return ret; ret = do_readn (ctx, sock, buffer, 2); if (ret) return ret; if (buffer[0] != 1) { /* SOCKS server returned wrong version. */ gpg_err_set_errno (EPROTONOSUPPORT); return -1; } if (buffer[1]) { /* SOCKS server denied access. */ gpg_err_set_errno (EACCES); return -1; } } if (hostname && !*hostname && !hostport) { /* Empty hostname given. Stop right here to allow the caller to do the actual proxy request. */ return 0; } /* Send request details (rfc-1928, 4). */ buffer[0] = 5; /* VER */ buffer[1] = 1; /* CMD = CONNECT */ buffer[2] = 0; /* RSV */ if (hostname) { buffer[3] = 3; /* ATYP = DOMAINNAME */ buflen = 4; buffer[buflen++] = hostnamelen; memcpy (buffer+buflen, hostname, hostnamelen); buflen += hostnamelen; buffer[buflen++] = (hostport >> 8); /* DST.PORT */ buffer[buflen++] = hostport; } else if (addr->sa_family == AF_INET6) { addr_in6 = (struct sockaddr_in6 *)addr; buffer[3] = 4; /* ATYP = IPv6 */ memcpy (buffer+ 4, &addr_in6->sin6_addr.s6_addr, 16); /* DST.ADDR */ memcpy (buffer+20, &addr_in6->sin6_port, 2); /* DST.PORT */ buflen = 22; } else { addr_in = (struct sockaddr_in *)addr; buffer[3] = 1; /* ATYP = IPv4 */ memcpy (buffer+4, &addr_in->sin_addr.s_addr, 4); /* DST.ADDR */ memcpy (buffer+8, &addr_in->sin_port, 2); /* DST.PORT */ buflen = 10; } ret = do_writen (ctx, sock, buffer, buflen); if (ret) return ret; ret = do_readn (ctx, sock, buffer, 10 /* Length for IPv4 */); if (ret) return ret; if (buffer[0] != 5 || buffer[2] != 0 ) { /* Socks server returned wrong version or the reserved field is not zero. */ gpg_err_set_errno (EPROTONOSUPPORT); return -1; } if (buffer[1]) { switch (buffer[1]) { case 0x01: /* General SOCKS server failure. */ gpg_err_set_errno (ENETDOWN); break; case 0x02: /* Connection not allowed by ruleset. */ gpg_err_set_errno (EACCES); break; case 0x03: /* Network unreachable */ gpg_err_set_errno (ENETUNREACH); break; case 0x04: /* Host unreachable */ gpg_err_set_errno (EHOSTUNREACH); break; case 0x05: /* Connection refused */ gpg_err_set_errno (ECONNREFUSED); break; case 0x06: /* TTL expired */ gpg_err_set_errno (ETIMEDOUT); break; case 0x08: /* Address type not supported */ gpg_err_set_errno (EPROTONOSUPPORT); break; case 0x07: /* Command not supported */ default: gpg_err_set_errno (ENOTSUP); /* Fixme: Is there a better errno? */ } return -1; } if (buffer[3] == 4) { /* ATYP indicates a v6 address. We need to read the remaining 12 bytes. */ ret = do_readn (ctx, sock, buffer+10, 12); if (ret) return ret; } /* FIXME: We have not way to store the actual address used by the server. */ return 0; } /* Return true if SOCKS shall be used. This is the case if tor_mode is enabled and the desired address is not the loopback address. */ static int use_socks (struct sockaddr *addr) { if (!tor_mode) return 0; else if (addr->sa_family == AF_INET6) { struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr; const unsigned char *s; int i; s = (unsigned char *)&addr_in6->sin6_addr.s6_addr; if (s[15] != 1) return 1; /* Last octet is not 1 - not the loopback address. */ for (i=0; i < 15; i++, s++) if (*s) return 1; /* Non-zero octet found - not the loopback address. */ return 0; /* This is the loopback address. */ } else if (addr->sa_family == AF_INET) { struct sockaddr_in *addr_in = (struct sockaddr_in *)addr; if (*(unsigned char*)&addr_in->sin_addr.s_addr == 127) return 0; /* Loopback (127.0.0.0/8) */ return 1; } else return 0; } int _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) { #ifdef HAVE_W32_SYSTEM if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX) { struct sockaddr_in myaddr; struct sockaddr_un *unaddr; unsigned short port; char nonce[16]; int cygwin; int ret; unaddr = (struct sockaddr_un *)addr; if (read_port_and_nonce (unaddr->sun_path, &port, nonce, &cygwin)) return -1; myaddr.sin_family = AF_INET; myaddr.sin_port = htons (port); myaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); /* Set return values. */ unaddr->sun_family = myaddr.sin_family; unaddr->sun_port = myaddr.sin_port; unaddr->sun_addr.s_addr = myaddr.sin_addr.s_addr; ret = _assuan_connect (ctx, HANDLE2SOCKET(sockfd), (struct sockaddr *)&myaddr, sizeof myaddr); if (!ret) { /* Send the nonce. */ ret = do_writen (ctx, sockfd, nonce, 16); if (!ret && cygwin) { char buffer[16]; /* The client sends the nonce back - not useful. We do a dummy read. */ ret = do_readn (ctx, sockfd, buffer, 16); if (!ret) { /* Send our credentials. */ int n = getpid (); memcpy (buffer, &n, 4); memset (buffer+4, 0, 4); /* uid = gid = 0 */ ret = do_writen (ctx, sockfd, buffer, 8); if (!ret) { /* Receive credentials. We don't need them. */ ret = do_readn (ctx, sockfd, buffer, 8); } } } } return ret; } else if (use_socks (addr)) { return socks5_connect (ctx, sockfd, tor_mode, NULL, NULL, 0, addr, addrlen); } else { return _assuan_connect (ctx, HANDLE2SOCKET (sockfd), addr, addrlen); } #else # if HAVE_STAT if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX) { struct sockaddr_un *unaddr; struct stat statbuf; int redirect, res; unaddr = (struct sockaddr_un *)addr; if (!stat (unaddr->sun_path, &statbuf) && !S_ISSOCK (statbuf.st_mode) && S_ISREG (statbuf.st_mode)) { /* The given socket file is not a socket but a regular file. We use the content of that file to redirect to another socket file. This can be used to use sockets on file systems which do not support sockets or if for example a home directory is shared by several machines. */ unaddr = eval_redirection (unaddr->sun_path, &redirect); if (unaddr) { res = _assuan_connect (ctx, sockfd, (struct sockaddr *)unaddr, SUN_LEN (unaddr)); free (unaddr); return res; } if (redirect) return -1; /* Continue using the standard connect. */ } } # endif /*HAVE_STAT*/ if (use_socks (addr)) { return socks5_connect (ctx, sockfd, tor_mode, NULL, NULL, 0, addr, addrlen); } else { return _assuan_connect (ctx, sockfd, addr, addrlen); } #endif } /* Connect to HOST specified as host name on PORT. The current implementation requires that either the flags ASSUAN_SOCK_SOCKS or ASSUAN_SOCK_TOR are given in FLAGS. On success a new socket is returned; on error ASSUAN_INVALID_FD is returned and ERRNO set. If CREDENTIALS is not NULL, it is a string used for password based authentication. Username and password are separated by a colon. RESERVED must be 0. By passing HOST and PORT as 0 the function can be used to check for proxy availability: If the proxy is available a socket will be returned which the caller should then close. */ assuan_fd_t _assuan_sock_connect_byname (assuan_context_t ctx, const char *host, unsigned short port, int reserved, const char *credentials, unsigned int flags) { assuan_fd_t fd; unsigned short socksport; if ((flags & ASSUAN_SOCK_TOR)) socksport = TOR_PORT; else if ((flags & ASSUAN_SOCK_SOCKS)) socksport = SOCKS_PORT; else { gpg_err_set_errno (ENOTSUP); return ASSUAN_INVALID_FD; } if (host && !*host) { /* Error out early on an empty host name. See below. */ gpg_err_set_errno (EINVAL); return ASSUAN_INVALID_FD; } fd = _assuan_sock_new (ctx, AF_INET, SOCK_STREAM, 0); if (fd == ASSUAN_INVALID_FD) return fd; /* For HOST being NULL we pass an empty string which indicates to socks5_connect to stop midway during the proxy negotiation. Note that we can't pass NULL directly as this indicates IP address mode to the called function. */ if (socks5_connect (ctx, fd, socksport, credentials, host? host:"", port, NULL, 0)) { int save_errno = errno; assuan_sock_close (fd); gpg_err_set_errno (save_errno); return ASSUAN_INVALID_FD; } return fd; } int _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) { #ifdef HAVE_W32_SYSTEM if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX) { struct sockaddr_in myaddr; struct sockaddr_un *unaddr; HANDLE filehd; int len = sizeof myaddr; int rc; union { char data[16]; int aint[4]; } nonce; char tmpbuf[50+16]; DWORD nwritten; if (get_nonce (nonce.data, 16)) return -1; unaddr = (struct sockaddr_un *)addr; myaddr.sin_port = 0; myaddr.sin_family = AF_INET; myaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); filehd = MyCreateFile (unaddr->sun_path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (filehd == INVALID_HANDLE_VALUE) { if (GetLastError () == ERROR_FILE_EXISTS) gpg_err_set_errno (EADDRINUSE); return -1; } rc = bind (HANDLE2SOCKET (sockfd), (struct sockaddr *)&myaddr, len); if (!rc) rc = getsockname (HANDLE2SOCKET (sockfd), (struct sockaddr *)&myaddr, &len); if (rc) { int save_e = errno; CloseHandle (filehd); MyDeleteFile (unaddr->sun_path); gpg_err_set_errno (save_e); return rc; } if (is_cygwin_fd (sockfd)) { snprintf (tmpbuf, sizeof tmpbuf, "!%d s %08x-%08x-%08x-%08x", ntohs (myaddr.sin_port), nonce.aint[0], nonce.aint[1], nonce.aint[2], nonce.aint[3]); len = strlen (tmpbuf) + 1; } else { snprintf (tmpbuf, sizeof tmpbuf-16, "%d\n", ntohs (myaddr.sin_port)); len = strlen (tmpbuf); memcpy (tmpbuf+len, nonce.data,16); len += 16; } if (!WriteFile (filehd, tmpbuf, len, &nwritten, NULL)) { CloseHandle (filehd); MyDeleteFile (unaddr->sun_path); gpg_err_set_errno (EIO); return -1; } CloseHandle (filehd); return 0; } else { int res = bind (HANDLE2SOCKET(sockfd), addr, addrlen); if (res < 0) gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ())); return res; } #else return bind (sockfd, addr, addrlen); #endif } /* Setup the ADDR structure for a Unix domain socket with the socket name FNAME. If this is a redirected socket and R_REDIRECTED is not NULL, it will be setup for the real socket. Returns 0 on success and stores 1 at R_REDIRECTED if it is a redirected socket. On error -1 is returned and ERRNO will be set. */ int _assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr, int *r_redirected) { struct sockaddr_un *unaddr = (struct sockaddr_un *)addr; #if !defined(HAVE_W32_SYSTEM) && defined(HAVE_STAT) struct stat statbuf; #endif if (r_redirected) *r_redirected = 0; #if !defined(HAVE_W32_SYSTEM) && defined(HAVE_STAT) if (r_redirected && !stat (fname, &statbuf) && !S_ISSOCK (statbuf.st_mode) && S_ISREG (statbuf.st_mode)) { /* The given socket file is not a socket but a regular file. We use the content of that file to redirect to another socket file. This can be used to use sockets on file systems which do not support sockets or if for example a home directory is shared by several machines. */ struct sockaddr_un *unaddr_new; int redirect; unaddr_new = eval_redirection (fname, &redirect); if (unaddr_new) { memcpy (unaddr, unaddr_new, sizeof *unaddr); free (unaddr_new); *r_redirected = 1; return 0; } if (redirect) { *r_redirected = 1; return -1; /* Error. */ } /* Fallback to standard setup. */ } #endif /*!HAVE_W32_SYSTEM && HAVE_STAT*/ if (strlen (fname)+1 >= sizeof unaddr->sun_path) { gpg_err_set_errno (ENAMETOOLONG); return -1; } memset (unaddr, 0, sizeof *unaddr); unaddr->sun_family = AF_LOCAL; strncpy (unaddr->sun_path, fname, sizeof unaddr->sun_path - 1); unaddr->sun_path[sizeof unaddr->sun_path - 1] = 0; return 0; } int _assuan_sock_get_nonce (assuan_context_t ctx, struct sockaddr *addr, int addrlen, assuan_sock_nonce_t *nonce) { #ifdef HAVE_W32_SYSTEM if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX) { struct sockaddr_un *unaddr; unsigned short port; int dummy; if (sizeof nonce->nonce != 16) { gpg_err_set_errno (EINVAL); return -1; } nonce->length = 16; unaddr = (struct sockaddr_un *)addr; if (read_port_and_nonce (unaddr->sun_path, &port, nonce->nonce, &dummy)) return -1; } else { nonce->length = 42; /* Arbitrary value to detect unitialized nonce. */ nonce->nonce[0] = 42; } #else (void)addr; (void)addrlen; nonce->length = 0; #endif return 0; } int _assuan_sock_check_nonce (assuan_context_t ctx, assuan_fd_t fd, assuan_sock_nonce_t *nonce) { #ifdef HAVE_W32_SYSTEM char buffer[16]; int n; if (sizeof nonce->nonce != 16) { gpg_err_set_errno (EINVAL); return -1; } if (nonce->length == 42 && nonce->nonce[0] == 42) return 0; /* Not a Unix domain socket. */ if (nonce->length != 16) { gpg_err_set_errno (EINVAL); return -1; } if (do_readn (ctx, fd, buffer, 16)) return -1; if (memcmp (buffer, nonce->nonce, 16)) { gpg_err_set_errno (EACCES); return -1; } if (is_cygwin_fd (fd)) { /* Send the nonce back to the client. */ if (do_writen (ctx, fd, buffer, 16)) return -1; /* Read the credentials. Cygwin uses the struct ucred { pid_t pid; uid_t uid; gid_t gid; }; with pid_t being an int (4 bytes) and uid_t and gid_t being shorts (2 bytes). Thus we need to read 8 bytes. However we we ignore the values because they are not kernel controlled. */ if (do_readn (ctx, fd, buffer, 8)) return -1; /* Send our credentials: We use the uid and gid we received but our own pid. */ n = getpid (); memcpy (buffer, &n, 4); if (do_writen (ctx, fd, buffer, 8)) return -1; } #else (void)fd; (void)nonce; #endif return 0; } /* Public API. */ gpg_error_t assuan_sock_init () { gpg_error_t err; #ifdef HAVE_W32_SYSTEM WSADATA wsadat; #endif if (sock_ctx != NULL) return 0; #ifdef HAVE_W32_SYSTEM InitializeCriticalSection (&cygwin_fdtable_cs); #endif err = assuan_new (&sock_ctx); #ifdef HAVE_W32_SYSTEM if (! err) WSAStartup (0x202, &wsadat); #endif return err; } void assuan_sock_deinit () { if (sock_ctx == NULL) return; #ifdef HAVE_W32_SYSTEM WSACleanup (); #endif assuan_release (sock_ctx); sock_ctx = NULL; #ifdef HAVE_W32_SYSTEM DeleteCriticalSection (&cygwin_fdtable_cs); #endif } int assuan_sock_close (assuan_fd_t fd) { #ifdef HAVE_W32_SYSTEM if (fd != ASSUAN_INVALID_FD) delete_cygwin_fd (fd); #endif return _assuan_close (sock_ctx, fd); } assuan_fd_t assuan_sock_new (int domain, int type, int proto) { return _assuan_sock_new (sock_ctx, domain, type, proto); } int assuan_sock_set_flag (assuan_fd_t sockfd, const char *name, int value) { return _assuan_sock_set_flag (sock_ctx, sockfd, name, value); } int assuan_sock_get_flag (assuan_fd_t sockfd, const char *name, int *r_value) { return _assuan_sock_get_flag (sock_ctx, sockfd, name, r_value); } int assuan_sock_connect (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) { return _assuan_sock_connect (sock_ctx, sockfd, addr, addrlen); } assuan_fd_t assuan_sock_connect_byname (const char *host, unsigned short port, int reserved, const char *credentials, unsigned int flags) { return _assuan_sock_connect_byname (sock_ctx, host, port, reserved, credentials, flags); } int assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) { return _assuan_sock_bind (sock_ctx, sockfd, addr, addrlen); } int assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr, int *r_redirected) { return _assuan_sock_set_sockaddr_un (fname, addr, r_redirected); } int assuan_sock_get_nonce (struct sockaddr *addr, int addrlen, assuan_sock_nonce_t *nonce) { return _assuan_sock_get_nonce (sock_ctx, addr, addrlen, nonce); } int assuan_sock_check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce) { return _assuan_sock_check_nonce (sock_ctx, fd, nonce); } diff --git a/src/assuan-uds.c b/src/assuan-uds.c index 59f3a6b..8712b02 100644 --- a/src/assuan-uds.c +++ b/src/assuan-uds.c @@ -1,300 +1,301 @@ /* assuan-uds.c - Assuan unix domain socket utilities - Copyright (C) 2006, 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2006, 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #ifdef HAVE_SYS_TYPES_H # include #endif #ifndef HAVE_W32_SYSTEM # include # include #else # ifdef HAVE_WINSOCK2_H # include # endif # include #endif #if HAVE_SYS_UIO_H #include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_FCNTL_H #include #endif #include #include #include "assuan-defs.h" #include "debug.h" #ifdef USE_DESCRIPTOR_PASSING /* Provide replacement for missing CMSG maccros. We assume that size_t matches the alignment requirement. NOTE: This is not true on Mac OS X, so be extra careful to define _DARWIN_C_SOURCE to get those definitions instead of using these. */ #define MY_ALIGN(n) ((((n))+ sizeof(size_t)-1) & (size_t)~(sizeof(size_t)-1)) #ifndef CMSG_SPACE #define CMSG_SPACE(n) (MY_ALIGN(sizeof(struct cmsghdr)) + MY_ALIGN((n))) #endif #ifndef CMSG_LEN #define CMSG_LEN(n) (MY_ALIGN(sizeof(struct cmsghdr)) + (n)) #endif #ifndef CMSG_FIRSTHDR #define CMSG_FIRSTHDR(mhdr) \ ((size_t)(mhdr)->msg_controllen >= sizeof (struct cmsghdr) \ ? (struct cmsghdr*) (mhdr)->msg_control : (struct cmsghdr*)NULL) #endif #ifndef CMSG_DATA #define CMSG_DATA(cmsg) ((unsigned char*)((struct cmsghdr*)(cmsg)+1)) #endif #endif /*USE_DESCRIPTOR_PASSING*/ /* Read from a unix domain socket using sendmsg. */ static ssize_t uds_reader (assuan_context_t ctx, void *buf, size_t buflen) { #ifndef HAVE_W32_SYSTEM int len = 0; /* This loop should be OK. As FDs are followed by data, the readable status of the socket does not change and no new select/event-loop round is necessary. */ while (!len) /* No data is buffered. */ { struct msghdr msg; struct iovec iovec; #ifdef USE_DESCRIPTOR_PASSING union { struct cmsghdr cm; char control[CMSG_SPACE(sizeof (int))]; } control_u; struct cmsghdr *cmptr; #endif /*USE_DESCRIPTOR_PASSING*/ memset (&msg, 0, sizeof (msg)); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iovec; msg.msg_iovlen = 1; iovec.iov_base = buf; iovec.iov_len = buflen; #ifdef USE_DESCRIPTOR_PASSING msg.msg_control = control_u.control; msg.msg_controllen = sizeof (control_u.control); #endif len = _assuan_recvmsg (ctx, ctx->inbound.fd, &msg, 0); if (len < 0) return -1; if (len == 0) return 0; #ifdef USE_DESCRIPTOR_PASSING cmptr = CMSG_FIRSTHDR (&msg); if (cmptr && cmptr->cmsg_len == CMSG_LEN (sizeof(int))) { if (cmptr->cmsg_level != SOL_SOCKET || cmptr->cmsg_type != SCM_RIGHTS) TRACE0 (ctx, ASSUAN_LOG_SYSIO, "uds_reader", ctx, "unexpected ancillary data received"); else { int fd; memcpy (&fd, CMSG_DATA (cmptr), sizeof (fd)); if (ctx->uds.pendingfdscount >= DIM (ctx->uds.pendingfds)) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "uds_reader", ctx, "too many descriptors pending - " "closing received descriptor %d", fd); _assuan_close (ctx, fd); } else ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = fd; } } #endif /*USE_DESCRIPTOR_PASSING*/ } return len; #else /*HAVE_W32_SYSTEM*/ int res = recvfrom (HANDLE2SOCKET(ctx->inbound.fd), buf, buflen, 0, NULL, NULL); if (res < 0) gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ())); return res; #endif /*HAVE_W32_SYSTEM*/ } /* Write to the domain server. */ static ssize_t uds_writer (assuan_context_t ctx, const void *buf, size_t buflen) { #ifndef HAVE_W32_SYSTEM struct msghdr msg; struct iovec iovec; ssize_t len; memset (&msg, 0, sizeof (msg)); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iovlen = 1; msg.msg_iov = &iovec; iovec.iov_base = (void*)buf; iovec.iov_len = buflen; len = _assuan_sendmsg (ctx, ctx->outbound.fd, &msg, 0); return len; #else /*HAVE_W32_SYSTEM*/ int res = sendto (HANDLE2SOCKET(ctx->outbound.fd), buf, buflen, 0, (struct sockaddr *)&ctx->serveraddr, sizeof (struct sockaddr_in)); if (res < 0) gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ())); return res; #endif /*HAVE_W32_SYSTEM*/ } static gpg_error_t uds_sendfd (assuan_context_t ctx, assuan_fd_t fd) { #ifdef USE_DESCRIPTOR_PASSING struct msghdr msg; struct iovec iovec; union { struct cmsghdr cm; char control[CMSG_SPACE(sizeof (int))]; } control_u; struct cmsghdr *cmptr; int len; char buffer[80]; /* We need to send some real data so that a read won't return 0 which will be taken as an EOF. It also helps with debugging. */ snprintf (buffer, sizeof(buffer)-1, "# descriptor %d is in flight\n", fd); buffer[sizeof(buffer)-1] = 0; memset (&msg, 0, sizeof (msg)); memset (&control_u, 0, sizeof (control_u)); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iovlen = 1; msg.msg_iov = &iovec; iovec.iov_base = buffer; iovec.iov_len = strlen (buffer); msg.msg_control = control_u.control; msg.msg_controllen = sizeof (control_u.control); cmptr = CMSG_FIRSTHDR (&msg); cmptr->cmsg_len = CMSG_LEN(sizeof(int)); cmptr->cmsg_level = SOL_SOCKET; cmptr->cmsg_type = SCM_RIGHTS; memcpy (CMSG_DATA (cmptr), &fd, sizeof (fd)); len = _assuan_sendmsg (ctx, ctx->outbound.fd, &msg, 0); if (len < 0) { int saved_errno = errno; TRACE1 (ctx, ASSUAN_LOG_SYSIO, "uds_sendfd", ctx, "uds_sendfd: %s", strerror (errno)); errno = saved_errno; return _assuan_error (ctx, gpg_err_code_from_syserror ()); } else return 0; #else return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED); #endif } static gpg_error_t uds_receivefd (assuan_context_t ctx, assuan_fd_t *fd) { #ifdef USE_DESCRIPTOR_PASSING int i; if (!ctx->uds.pendingfdscount) { TRACE0 (ctx, ASSUAN_LOG_SYSIO, "uds_receivefd", ctx, "no pending file descriptors"); return _assuan_error (ctx, GPG_ERR_ASS_GENERAL); } assert (ctx->uds.pendingfdscount <= DIM(ctx->uds.pendingfds)); *fd = ctx->uds.pendingfds[0]; for (i=1; i < ctx->uds.pendingfdscount; i++) ctx->uds.pendingfds[i-1] = ctx->uds.pendingfds[i]; ctx->uds.pendingfdscount--; return 0; #else return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED); #endif } /* Close all pending fds. */ void _assuan_uds_close_fds (assuan_context_t ctx) { int i; for (i = 0; i < ctx->uds.pendingfdscount; i++) _assuan_close (ctx, ctx->uds.pendingfds[i]); ctx->uds.pendingfdscount = 0; } /* Deinitialize the unix domain socket I/O functions. */ void _assuan_uds_deinit (assuan_context_t ctx) { _assuan_uds_close_fds (ctx); } /* Helper function to initialize a context for domain I/O. */ void _assuan_init_uds_io (assuan_context_t ctx) { ctx->engine.readfnc = uds_reader; ctx->engine.writefnc = uds_writer; ctx->engine.sendfd = uds_sendfd; ctx->engine.receivefd = uds_receivefd; ctx->uds.pendingfdscount = 0; } diff --git a/src/assuan.c b/src/assuan.c index d4c4b56..e59df65 100644 --- a/src/assuan.c +++ b/src/assuan.c @@ -1,284 +1,285 @@ /* assuan.c - Global interface (not specific to context). - Copyright (C) 2009 Free Software Foundation, Inc. - Copyright (C) 2001, 2002, 2012, 2013 g10 Code GmbH - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2009 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2012, 2013 g10 Code GmbH + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 "assuan-defs.h" #include "debug.h" #define digitp(a) ((a) >= '0' && (a) <= '9') /* Global default state. */ /* The default error source gor generated error codes. */ static gpg_err_source_t _assuan_default_err_source = GPG_ERR_SOURCE_USER_1; /* The default memory management functions. */ static struct assuan_malloc_hooks _assuan_default_malloc_hooks = { malloc, realloc, free }; /* The default logging handler. */ static assuan_log_cb_t _assuan_default_log_cb = _assuan_log_handler; static void *_assuan_default_log_cb_data = NULL; /* Set the default gpg error source. */ void assuan_set_gpg_err_source (gpg_err_source_t errsource) { _assuan_default_err_source = errsource; } /* Get the default gpg error source. */ gpg_err_source_t assuan_get_gpg_err_source (void) { return _assuan_default_err_source; } /* Set the default malloc hooks. */ void assuan_set_malloc_hooks (assuan_malloc_hooks_t malloc_hooks) { _assuan_default_malloc_hooks = *malloc_hooks; } /* Get the default malloc hooks. */ assuan_malloc_hooks_t assuan_get_malloc_hooks (void) { return &_assuan_default_malloc_hooks; } /* Set the default log callback handler. */ void assuan_set_log_cb (assuan_log_cb_t log_cb, void *log_cb_data) { _assuan_default_log_cb = log_cb; _assuan_default_log_cb_data = log_cb_data; _assuan_init_log_envvars (); } /* Get the default log callback handler. */ void assuan_get_log_cb (assuan_log_cb_t *log_cb, void **log_cb_data) { *log_cb = _assuan_default_log_cb; *log_cb_data = _assuan_default_log_cb_data; } void assuan_set_system_hooks (assuan_system_hooks_t system_hooks) { _assuan_system_hooks_copy (&_assuan_system_hooks, system_hooks); } /* Create a new Assuan context. The initial parameters are all needed in the creation of the context. */ gpg_error_t assuan_new_ext (assuan_context_t *r_ctx, gpg_err_source_t err_source, assuan_malloc_hooks_t malloc_hooks, assuan_log_cb_t log_cb, void *log_cb_data) { struct assuan_context_s wctx; assuan_context_t ctx; /* Set up a working context so we can use standard functions. */ memset (&wctx, 0, sizeof (wctx)); wctx.err_source = err_source; wctx.malloc_hooks = *malloc_hooks; wctx.log_cb = log_cb; wctx.log_cb_data = log_cb_data; /* Need a new block for the trace macros to work. */ { TRACE_BEG8 (&wctx, ASSUAN_LOG_CTX, "assuan_new_ext", r_ctx, "err_source = %i (%s), malloc_hooks = %p (%p, %p, %p), " "log_cb = %p, log_cb_data = %p", err_source, gpg_strsource (err_source), malloc_hooks, malloc_hooks->malloc, malloc_hooks->realloc, malloc_hooks->free, log_cb, log_cb_data); *r_ctx = NULL; ctx = _assuan_malloc (&wctx, sizeof (*ctx)); if (!ctx) return TRACE_ERR (gpg_err_code_from_syserror ()); memcpy (ctx, &wctx, sizeof (*ctx)); ctx->system = _assuan_system_hooks; /* FIXME: Delegate to subsystems/engines, as the FDs are not our responsibility (we don't deallocate them, for example). */ ctx->input_fd = ASSUAN_INVALID_FD; ctx->output_fd = ASSUAN_INVALID_FD; ctx->inbound.fd = ASSUAN_INVALID_FD; ctx->outbound.fd = ASSUAN_INVALID_FD; ctx->listen_fd = ASSUAN_INVALID_FD; *r_ctx = ctx; return TRACE_SUC1 ("ctx=%p", ctx); } } /* Create a new context with default arguments. */ gpg_error_t assuan_new (assuan_context_t *r_ctx) { return assuan_new_ext (r_ctx, _assuan_default_err_source, &_assuan_default_malloc_hooks, _assuan_default_log_cb, _assuan_default_log_cb_data); } /* Release all resources associated with an engine operation. */ void _assuan_reset (assuan_context_t ctx) { if (ctx->engine.release) { (*ctx->engine.release) (ctx); ctx->engine.release = NULL; } /* FIXME: Clean standard commands */ } /* Release all resources associated with the given context. */ void assuan_release (assuan_context_t ctx) { if (! ctx) return; TRACE (ctx, ASSUAN_LOG_CTX, "assuan_release", ctx); _assuan_reset (ctx); /* None of the members that are our responsibility requires deallocation. To avoid sensitive data in the line buffers we wipe them out, though. Note that we can't wipe the entire context because it also has a pointer to the actual free(). */ wipememory (&ctx->inbound, sizeof ctx->inbound); wipememory (&ctx->outbound, sizeof ctx->outbound); _assuan_free (ctx, ctx); } /* Version number stuff. */ static const char* parse_version_number (const char *s, int *number) { int val = 0; if (*s == '0' && digitp (s[1])) return NULL; /* Leading zeros are not allowed. */ for (; digitp (*s); s++) { val *= 10; val += *s - '0'; } *number = val; return val < 0 ? NULL : s; } static const char * parse_version_string (const char *s, int *major, int *minor, int *micro) { s = parse_version_number (s, major); if (!s || *s != '.') return NULL; s++; s = parse_version_number (s, minor); if (!s || *s != '.') return NULL; s++; s = parse_version_number (s, micro); if (!s) return NULL; return s; /* Patchlevel. */ } static const char * compare_versions (const char *my_version, const char *req_version) { int my_major, my_minor, my_micro; int rq_major, rq_minor, rq_micro; const char *my_plvl, *rq_plvl; if (!req_version) return my_version; if (!my_version) return NULL; my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro); if (!my_plvl) return NULL; /* Very strange: our own version is bogus. */ rq_plvl = parse_version_string(req_version, &rq_major, &rq_minor, &rq_micro); if (!rq_plvl) return NULL; /* Requested version string is invalid. */ 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)) { return my_version; } return NULL; } /* * Check that the the version of the library is at minimum REQ_VERSION * and return the actual version string; return NULL if the condition * is not met. If NULL is passed to this function, no check is done * and the version string is simply returned. */ const char * assuan_check_version (const char *req_version) { return compare_versions (PACKAGE_VERSION, req_version); } diff --git a/src/assuan.h.in b/src/assuan.h.in index da1af25..acd4f45 100644 --- a/src/assuan.h.in +++ b/src/assuan.h.in @@ -1,595 +1,596 @@ /* assuan.h - Definitions for the Assuan IPC library -*- c -*- - Copyright (C) 2001-2013 Free Software Foundation, Inc. - Copyright (C) 2001-2015 g10 Code GmbH - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . - - @configure_input@ + * Copyright (C) 2001-2013 Free Software Foundation, Inc. + * Copyright (C) 2001-2015 g10 Code GmbH + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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+ + * + * @configure_input@ */ #ifndef ASSUAN_H #define ASSUAN_H #include @include:sys/types.h@ @include:unistd.h@ #include #ifndef _ASSUAN_NO_SOCKET_WRAPPER @include:includes@ #endif /*!_ASSUAN_NO_SOCKET_WRAPPER*/ @include:types@ #include /* Compile time configuration: #define _ASSUAN_NO_SOCKET_WRAPPER Do not include the definitions for the socket wrapper feature. */ #ifdef __cplusplus extern "C" { #if 0 } #endif #endif /* The version of this header should match the one of the library. Do not use this symbol in your application; use assuan_check_version instead. */ #define ASSUAN_VERSION @version@ /* The version number of this header. It may be used to handle minor API incompatibilities. */ #define ASSUAN_VERSION_NUMBER @version-number@ /* Check for compiler features. */ #if __GNUC__ #define _ASSUAN_GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) #if _ASSUAN_GCC_VERSION > 30100 #define _ASSUAN_DEPRECATED __attribute__ ((__deprecated__)) #endif #endif #ifndef _ASSUAN_DEPRECATED #define _ASSUAN_DEPRECATED #endif #define ASSUAN_LINELENGTH 1002 /* 1000 + [CR,]LF */ struct assuan_context_s; typedef struct assuan_context_s *assuan_context_t; @include:fd-t@ assuan_fd_t assuan_fdopen (int fd); @include:sock-nonce@ /* Global interface. */ struct assuan_malloc_hooks { void *(*malloc) (size_t cnt); void *(*realloc) (void *ptr, size_t cnt); void (*free) (void *ptr); }; typedef struct assuan_malloc_hooks *assuan_malloc_hooks_t; /* Categories for log messages. */ #define ASSUAN_LOG_INIT 1 #define ASSUAN_LOG_CTX 2 #define ASSUAN_LOG_ENGINE 3 #define ASSUAN_LOG_DATA 4 #define ASSUAN_LOG_SYSIO 5 #define ASSUAN_LOG_CONTROL 8 /* If MSG is NULL, return true/false depending on if this category is logged. This is used to probe before expensive log message generation (buffer dumps). */ typedef int (*assuan_log_cb_t) (assuan_context_t ctx, void *hook, unsigned int cat, const char *msg); /* Return or check the version number. */ const char *assuan_check_version (const char *req_version); /* Set the default gpg error source. */ void assuan_set_gpg_err_source (gpg_err_source_t errsource); /* Get the default gpg error source. */ gpg_err_source_t assuan_get_gpg_err_source (void); /* Set the default malloc hooks. */ void assuan_set_malloc_hooks (assuan_malloc_hooks_t malloc_hooks); /* Get the default malloc hooks. */ assuan_malloc_hooks_t assuan_get_malloc_hooks (void); /* Set the default log callback handler. */ void assuan_set_log_cb (assuan_log_cb_t log_cb, void *log_cb_data); /* Get the default log callback handler. */ void assuan_get_log_cb (assuan_log_cb_t *log_cb, void **log_cb_data); /* Create a new Assuan context. The initial parameters are all needed in the creation of the context. */ gpg_error_t assuan_new_ext (assuan_context_t *ctx, gpg_err_source_t errsource, assuan_malloc_hooks_t malloc_hooks, assuan_log_cb_t log_cb, void *log_cb_data); /* Create a new context with default arguments. */ gpg_error_t assuan_new (assuan_context_t *ctx); /* Release all resources associated with the given context. */ void assuan_release (assuan_context_t ctx); /* Release the memory at PTR using the allocation handler of the context CTX. This is a convenience function. */ void assuan_free (assuan_context_t ctx, void *ptr); /* Set user-data in a context. */ void assuan_set_pointer (assuan_context_t ctx, void *pointer); /* Get user-data in a context. */ void *assuan_get_pointer (assuan_context_t ctx); /* Definitions of flags for assuan_set_flag(). */ typedef unsigned int assuan_flag_t; /* When using a pipe server, by default Assuan will wait for the forked process to die in assuan_release. In certain cases this is not desirable. By setting this flag, the waitpid will be skipped and the caller is responsible to cleanup a forked process. */ #define ASSUAN_NO_WAITPID 1 /* This flag indicates whether Assuan logging is in confidential mode. You can use assuan_{begin,end}_condidential to change the mode. */ #define ASSUAN_CONFIDENTIAL 2 /* This flag suppresses fix up of signal handlers for pipes. */ #define ASSUAN_NO_FIXSIGNALS 3 /* This flag changes assuan_transact to return comment lines via the status callback. The default is to skip comment lines. */ #define ASSUAN_CONVEY_COMMENTS 4 /* This flag disables logging for one context. */ #define ASSUAN_NO_LOGGING 5 /* This flag forces a connection close. */ #define ASSUAN_FORCE_CLOSE 6 /* For context CTX, set the flag FLAG to VALUE. Values for flags are usually 1 or 0 but certain flags might allow for other values; see the description of the type assuan_flag_t for details. */ void assuan_set_flag (assuan_context_t ctx, assuan_flag_t flag, int value); /* Return the VALUE of FLAG in context CTX. */ int assuan_get_flag (assuan_context_t ctx, assuan_flag_t flag); /* Same as assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 1). */ void assuan_begin_confidential (assuan_context_t ctx); /* Same as assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 0). */ void assuan_end_confidential (assuan_context_t ctx); /* Direction values for assuan_set_io_monitor. */ #define ASSUAN_IO_FROM_PEER 0 #define ASSUAN_IO_TO_PEER 1 /* Return flags of I/O monitor. */ #define ASSUAN_IO_MONITOR_NOLOG 1 #define ASSUAN_IO_MONITOR_IGNORE 2 /* The IO monitor gets to see all I/O on the context, and can return ASSUAN_IO_MONITOR_* bits to control actions on it. */ typedef unsigned int (*assuan_io_monitor_t) (assuan_context_t ctx, void *hook, int inout, const char *line, size_t linelen); /* Set the IO monitor function. */ void assuan_set_io_monitor (assuan_context_t ctx, assuan_io_monitor_t io_monitor, void *hook_data); #define ASSUAN_SYSTEM_HOOKS_VERSION 2 #define ASSUAN_SPAWN_DETACHED 128 struct assuan_system_hooks { /* Always set to ASSUAN_SYTEM_HOOKS_VERSION. */ int version; /* Sleep for the given number of microseconds. */ void (*usleep) (assuan_context_t ctx, unsigned int usec); /* Create a pipe with an inheritable end. */ int (*pipe) (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx); /* Close the given file descriptor, created with _assuan_pipe or one of the socket functions. */ int (*close) (assuan_context_t ctx, assuan_fd_t fd); ssize_t (*read) (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size); ssize_t (*write) (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size); int (*recvmsg) (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags); int (*sendmsg) (assuan_context_t ctx, assuan_fd_t fd, const assuan_msghdr_t msg, int flags); /* If NAME is NULL, don't exec, just fork. FD_CHILD_LIST is modified to reflect the value of the FD in the peer process (on Windows). */ int (*spawn) (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags); /* If action is 0, like waitpid. If action is 1, just release the PID? */ pid_t (*waitpid) (assuan_context_t ctx, pid_t pid, int action, int *status, int options); int (*socketpair) (assuan_context_t ctx, int _namespace, int style, int protocol, assuan_fd_t filedes[2]); int (*socket) (assuan_context_t ctx, int _namespace, int style, int protocol); int (*connect) (assuan_context_t ctx, int sock, struct sockaddr *addr, socklen_t length); }; typedef struct assuan_system_hooks *assuan_system_hooks_t; /* Configuration of the default log handler. */ /* Set the prefix to be used at the start of a line emitted by assuan on the log stream. The default is the empty string. Note, that this function is not thread-safe and should in general be used right at startup. */ void assuan_set_assuan_log_prefix (const char *text); /* Return a prefix to be used at the start of a line emitted by assuan on the log stream. The default implementation returns the empty string, i.e. "" */ const char *assuan_get_assuan_log_prefix (void); /* Global default log stream. */ void assuan_set_assuan_log_stream (FILE *fp); /* Set the per context log stream for the default log handler. */ void assuan_set_log_stream (assuan_context_t ctx, FILE *fp); typedef gpg_error_t (*assuan_handler_t) (assuan_context_t, char *); /*-- assuan-handler.c --*/ gpg_error_t assuan_register_command (assuan_context_t ctx, const char *cmd_string, assuan_handler_t handler, const char *help_string); gpg_error_t assuan_register_pre_cmd_notify (assuan_context_t ctx, gpg_error_t (*fnc)(assuan_context_t, const char *cmd)); gpg_error_t assuan_register_post_cmd_notify (assuan_context_t ctx, void (*fnc)(assuan_context_t, gpg_error_t)); gpg_error_t assuan_register_bye_notify (assuan_context_t ctx, assuan_handler_t handler); gpg_error_t assuan_register_reset_notify (assuan_context_t ctx, assuan_handler_t handler); gpg_error_t assuan_register_cancel_notify (assuan_context_t ctx, assuan_handler_t handler); gpg_error_t assuan_register_input_notify (assuan_context_t ctx, assuan_handler_t handler); gpg_error_t assuan_register_output_notify (assuan_context_t ctx, assuan_handler_t handler); gpg_error_t assuan_register_option_handler (assuan_context_t ctx, gpg_error_t (*fnc)(assuan_context_t, const char*, const char*)); gpg_error_t assuan_process (assuan_context_t ctx); gpg_error_t assuan_process_next (assuan_context_t ctx, int *done); gpg_error_t assuan_process_done (assuan_context_t ctx, gpg_error_t rc); int assuan_get_active_fds (assuan_context_t ctx, int what, assuan_fd_t *fdarray, int fdarraysize); const char *assuan_get_command_name (assuan_context_t ctx); FILE *assuan_get_data_fp (assuan_context_t ctx); gpg_error_t assuan_set_okay_line (assuan_context_t ctx, const char *line); gpg_error_t assuan_write_status (assuan_context_t ctx, const char *keyword, const char *text); /* Negotiate a file descriptor. If LINE contains "FD=N", returns N assuming a local file descriptor. If LINE contains "FD" reads a file descriptor via CTX and stores it in *RDF (the CTX must be capable of passing file descriptors). Under W32 the returned FD is a libc-type one. */ gpg_error_t assuan_command_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd); /*-- assuan-listen.c --*/ gpg_error_t assuan_set_hello_line (assuan_context_t ctx, const char *line); gpg_error_t assuan_accept (assuan_context_t ctx); assuan_fd_t assuan_get_input_fd (assuan_context_t ctx); assuan_fd_t assuan_get_output_fd (assuan_context_t ctx); gpg_error_t assuan_close_input_fd (assuan_context_t ctx); gpg_error_t assuan_close_output_fd (assuan_context_t ctx); /*-- assuan-pipe-server.c --*/ gpg_error_t assuan_init_pipe_server (assuan_context_t ctx, assuan_fd_t filedes[2]); /*-- assuan-socket-server.c --*/ #define ASSUAN_SOCKET_SERVER_FDPASSING 1 #define ASSUAN_SOCKET_SERVER_ACCEPTED 2 gpg_error_t assuan_init_socket_server (assuan_context_t ctx, assuan_fd_t listen_fd, unsigned int flags); void assuan_set_sock_nonce (assuan_context_t ctx, assuan_sock_nonce_t *nonce); /*-- assuan-pipe-connect.c --*/ #define ASSUAN_PIPE_CONNECT_FDPASSING 1 #define ASSUAN_PIPE_CONNECT_DETACHED 128 gpg_error_t assuan_pipe_connect (assuan_context_t ctx, const char *name, const char *argv[], assuan_fd_t *fd_child_list, void (*atfork) (void *, int), void *atforkvalue, unsigned int flags); /*-- assuan-socket-connect.c --*/ #define ASSUAN_SOCKET_CONNECT_FDPASSING 1 gpg_error_t assuan_socket_connect (assuan_context_t ctx, const char *name, pid_t server_pid, unsigned int flags); /*-- assuan-socket-connect.c --*/ gpg_error_t assuan_socket_connect_fd (assuan_context_t ctx, int fd, unsigned int flags); /*-- context.c --*/ pid_t assuan_get_pid (assuan_context_t ctx); struct _assuan_peercred { #ifdef _WIN32 /* Empty struct not allowed on some compilers. */ unsigned int _dummy; #else pid_t pid; uid_t uid; gid_t gid; #endif }; typedef struct _assuan_peercred *assuan_peercred_t; gpg_error_t assuan_get_peercred (assuan_context_t ctx, assuan_peercred_t *peercred); /* Client interface. */ #define ASSUAN_RESPONSE_ERROR 0 #define ASSUAN_RESPONSE_OK 1 #define ASSUAN_RESPONSE_DATA 2 #define ASSUAN_RESPONSE_INQUIRE 3 #define ASSUAN_RESPONSE_STATUS 4 #define ASSUAN_RESPONSE_END 5 #define ASSUAN_RESPONSE_COMMENT 6 typedef int assuan_response_t; /* This already de-escapes data lines. */ gpg_error_t assuan_client_read_response (assuan_context_t ctx, char **line, int *linelen); gpg_error_t assuan_client_parse_response (assuan_context_t ctx, char *line, int linelen, assuan_response_t *response, int *off); /*-- assuan-client.c --*/ gpg_error_t assuan_transact (assuan_context_t ctx, const char *command, gpg_error_t (*data_cb)(void *, const void *, size_t), void *data_cb_arg, gpg_error_t (*inquire_cb)(void*, const char *), void *inquire_cb_arg, gpg_error_t (*status_cb)(void*, const char *), void *status_cb_arg); /*-- assuan-inquire.c --*/ gpg_error_t assuan_inquire (assuan_context_t ctx, const char *keyword, unsigned char **r_buffer, size_t *r_length, size_t maxlen); gpg_error_t assuan_inquire_ext (assuan_context_t ctx, const char *keyword, size_t maxlen, gpg_error_t (*cb) (void *cb_data, gpg_error_t rc, unsigned char *buf, size_t buf_len), void *cb_data); /*-- assuan-buffer.c --*/ gpg_error_t assuan_read_line (assuan_context_t ctx, char **line, size_t *linelen); int assuan_pending_line (assuan_context_t ctx); gpg_error_t assuan_write_line (assuan_context_t ctx, const char *line); gpg_error_t assuan_send_data (assuan_context_t ctx, const void *buffer, size_t length); /* The file descriptor must be pending before assuan_receivefd is called. This means that assuan_sendfd should be called *before* the trigger is sent (normally via assuan_write_line ("INPUT FD")). */ gpg_error_t assuan_sendfd (assuan_context_t ctx, assuan_fd_t fd); gpg_error_t assuan_receivefd (assuan_context_t ctx, assuan_fd_t *fd); /*-- assuan-util.c --*/ gpg_error_t assuan_set_error (assuan_context_t ctx, gpg_error_t err, const char *text); /*-- assuan-socket.c --*/ /* This flag is used with assuan_sock_connect_byname to connect via SOCKS. */ #define ASSUAN_SOCK_SOCKS 1 /* This flag is used with assuan_sock_connect_byname to force a connection via Tor even if the socket subsystem has not been swicthed into Tor mode. This flags overrides ASSUAN_SOCK_SOCKS. */ #define ASSUAN_SOCK_TOR 2 /* These are socket wrapper functions to support an emulation of Unix domain sockets on Windows W32. */ gpg_error_t assuan_sock_init (void); void assuan_sock_deinit (void); int assuan_sock_close (assuan_fd_t fd); assuan_fd_t assuan_sock_new (int domain, int type, int proto); int assuan_sock_set_flag (assuan_fd_t sockfd, const char *name, int value); int assuan_sock_get_flag (assuan_fd_t sockfd, const char *name, int *r_value); int assuan_sock_connect (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen); assuan_fd_t assuan_sock_connect_byname (const char *host, unsigned short port, int reserved, const char *credentials, unsigned int flags); int assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen); int assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr, int *r_redirected); int assuan_sock_get_nonce (struct sockaddr *addr, int addrlen, assuan_sock_nonce_t *nonce); int assuan_sock_check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce); /* Set the default or per context system callbacks. This is irreversible. */ void assuan_set_system_hooks (assuan_system_hooks_t system_hooks); void assuan_ctx_set_system_hooks (assuan_context_t ctx, assuan_system_hooks_t system_hooks); void __assuan_usleep (assuan_context_t ctx, unsigned int usec); int __assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx); int __assuan_close (assuan_context_t ctx, assuan_fd_t fd); int __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags); int __assuan_socketpair (assuan_context_t ctx, int _namespace, int style, int protocol, assuan_fd_t filedes[2]); int __assuan_socket (assuan_context_t ctx, int _namespace, int style, int protocol); int __assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr, socklen_t length); ssize_t __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size); ssize_t __assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size); int __assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags); int __assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, const assuan_msghdr_t msg, int flags); pid_t __assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, int *status, int options); #define ASSUAN_SYSTEM_PTH_IMPL \ static void _assuan_pth_usleep (assuan_context_t ctx, unsigned int usec) \ { (void) ctx; pth_usleep (usec); } \ static ssize_t _assuan_pth_read (assuan_context_t ctx, assuan_fd_t fd, \ void *buffer, size_t size) \ { (void) ctx; return pth_read (fd, buffer, size); } \ static ssize_t _assuan_pth_write (assuan_context_t ctx, assuan_fd_t fd, \ const void *buffer, size_t size) \ { (void) ctx; return pth_write (fd, buffer, size); } \ @include:sys-pth-impl@ static pid_t _assuan_pth_waitpid (assuan_context_t ctx, pid_t pid, \ int nowait, int *status, int options) \ { (void) ctx; \ if (!nowait) return pth_waitpid (pid, status, options); \ else return 0; } \ \ struct assuan_system_hooks _assuan_system_pth = \ { ASSUAN_SYSTEM_HOOKS_VERSION, _assuan_pth_usleep, __assuan_pipe, \ __assuan_close, _assuan_pth_read, _assuan_pth_write, \ _assuan_pth_recvmsg, _assuan_pth_sendmsg, \ __assuan_spawn, _assuan_pth_waitpid, __assuan_socketpair, \ __assuan_socket, __assuan_connect } extern struct assuan_system_hooks _assuan_system_pth; #define ASSUAN_SYSTEM_PTH &_assuan_system_pth #define ASSUAN_SYSTEM_NPTH_IMPL \ static void _assuan_npth_usleep (assuan_context_t ctx, unsigned int usec) \ { (void) ctx; npth_usleep (usec); } \ static ssize_t _assuan_npth_read (assuan_context_t ctx, assuan_fd_t fd, \ void *buffer, size_t size) \ { ssize_t res; (void) ctx; npth_unprotect(); \ res = __assuan_read (ctx, fd, buffer, size); \ npth_protect(); return res; } \ static ssize_t _assuan_npth_write (assuan_context_t ctx, assuan_fd_t fd, \ const void *buffer, size_t size) \ { ssize_t res; (void) ctx; npth_unprotect(); \ res = __assuan_write (ctx, fd, buffer, size); \ npth_protect(); return res; } \ static int _assuan_npth_recvmsg (assuan_context_t ctx, assuan_fd_t fd, \ assuan_msghdr_t msg, int flags) \ { int res; (void) ctx; npth_unprotect(); \ res = __assuan_recvmsg (ctx, fd, msg, flags); \ npth_protect(); return res; } \ static int _assuan_npth_sendmsg (assuan_context_t ctx, assuan_fd_t fd, \ const assuan_msghdr_t msg, int flags) \ { int res; (void) ctx; npth_unprotect(); \ res = __assuan_sendmsg (ctx, fd, msg, flags); \ npth_protect(); return res; } \ static pid_t _assuan_npth_waitpid (assuan_context_t ctx, pid_t pid, \ int nowait, int *status, int options) \ { pid_t res; (void) ctx; npth_unprotect(); \ res = __assuan_waitpid (ctx, pid, nowait, status, options); \ npth_protect(); return res; } \ static int _assuan_npth_connect (assuan_context_t ctx, int sock, \ struct sockaddr *addr, socklen_t len)\ { int res; npth_unprotect(); \ res = __assuan_connect (ctx, sock, addr, len); \ npth_protect(); return res; } \ \ struct assuan_system_hooks _assuan_system_npth = \ { ASSUAN_SYSTEM_HOOKS_VERSION, _assuan_npth_usleep, __assuan_pipe, \ __assuan_close, _assuan_npth_read, _assuan_npth_write, \ _assuan_npth_recvmsg, _assuan_npth_sendmsg, \ __assuan_spawn, _assuan_npth_waitpid, __assuan_socketpair, \ __assuan_socket, _assuan_npth_connect } extern struct assuan_system_hooks _assuan_system_npth; #define ASSUAN_SYSTEM_NPTH &_assuan_system_npth @include:w32ce-add@ #ifdef __cplusplus } #endif #endif /* ASSUAN_H */ diff --git a/src/client.c b/src/client.c index de0e88b..8b357e6 100644 --- a/src/client.c +++ b/src/client.c @@ -1,335 +1,336 @@ /* client.c - Functions common to all clients. - Copyright (C) 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 "assuan-defs.h" #include "debug.h" #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) void _assuan_client_finish (assuan_context_t ctx) { if (ctx->inbound.fd != ASSUAN_INVALID_FD) { _assuan_close (ctx, ctx->inbound.fd); if (ctx->inbound.fd == ctx->outbound.fd) ctx->outbound.fd = ASSUAN_INVALID_FD; ctx->inbound.fd = ASSUAN_INVALID_FD; } if (ctx->outbound.fd != ASSUAN_INVALID_FD) { _assuan_close (ctx, ctx->outbound.fd); ctx->outbound.fd = ASSUAN_INVALID_FD; } if (ctx->pid != ASSUAN_INVALID_PID && ctx->pid) { _assuan_waitpid (ctx, ctx->pid, ctx->flags.no_waitpid, NULL, 0); ctx->pid = ASSUAN_INVALID_PID; } _assuan_uds_deinit (ctx); } /* Disconnect and release the context CTX. */ void _assuan_client_release (assuan_context_t ctx) { assuan_write_line (ctx, "BYE"); _assuan_client_finish (ctx); } /* This function also does deescaping for data lines. */ gpg_error_t assuan_client_read_response (assuan_context_t ctx, char **line_r, int *linelen_r) { gpg_error_t rc; char *line = NULL; int linelen = 0; *line_r = NULL; *linelen_r = 0; do { do { rc = _assuan_read_line (ctx); } while (_assuan_error_is_eagain (ctx, rc)); if (rc) return rc; line = ctx->inbound.line; linelen = ctx->inbound.linelen; } while (!linelen); /* For data lines, we deescape immediately. The user will never have to worry about it. */ if (linelen >= 1 && line[0] == 'D' && line[1] == ' ') { char *s, *d; for (s=d=line; linelen; linelen--) { if (*s == '%' && linelen > 2) { /* handle escaping */ s++; *d++ = xtoi_2 (s); s += 2; linelen -= 2; } else *d++ = *s++; } *d = 0; /* add a hidden string terminator */ linelen = d - line; ctx->inbound.linelen = linelen; } *line_r = line; *linelen_r = linelen; return 0; } gpg_error_t assuan_client_parse_response (assuan_context_t ctx, char *line, int linelen, assuan_response_t *response, int *off) { *response = ASSUAN_RESPONSE_ERROR; *off = 0; if (linelen >= 1 && line[0] == 'D' && line[1] == ' ') { *response = ASSUAN_RESPONSE_DATA; /* data line */ *off = 2; } else if (linelen >= 1 && line[0] == 'S' && (line[1] == '\0' || line[1] == ' ')) { *response = ASSUAN_RESPONSE_STATUS; *off = 1; while (line[*off] == ' ') ++*off; } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { *response = ASSUAN_RESPONSE_OK; *off = 2; while (line[*off] == ' ') ++*off; } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { *response = ASSUAN_RESPONSE_ERROR; *off = 3; while (line[*off] == ' ') ++*off; } 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] == ' ')) { *response = ASSUAN_RESPONSE_INQUIRE; *off = 7; while (line[*off] == ' ') ++*off; } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (line[3] == '\0' || line[3] == ' ')) { *response = ASSUAN_RESPONSE_END; *off = 3; } else if (linelen >= 1 && line[0] == '#') { *response = ASSUAN_RESPONSE_COMMENT; *off = 1; } else return _assuan_error (ctx, GPG_ERR_ASS_INV_RESPONSE); return 0; } gpg_error_t _assuan_read_from_server (assuan_context_t ctx, assuan_response_t *response, int *off, int convey_comments) { gpg_error_t rc; char *line; int linelen; do { *response = ASSUAN_RESPONSE_ERROR; *off = 0; rc = assuan_client_read_response (ctx, &line, &linelen); if (!rc) rc = assuan_client_parse_response (ctx, line, linelen, response, off); } while (!rc && *response == ASSUAN_RESPONSE_COMMENT && !convey_comments); return rc; } /** * assuan_transact: * @ctx: The Assuan context * @command: Command line to be send to the server * @data_cb: Callback function for data lines * @data_cb_arg: first argument passed to @data_cb * @inquire_cb: Callback function for a inquire response * @inquire_cb_arg: first argument passed to @inquire_cb * @status_cb: Callback function for a status response * @status_cb_arg: first argument passed to @status_cb * * FIXME: Write documentation * * Return value: 0 on success or an error code. The error code may be * the one one returned by the server via error lines or from the * callback functions. Take care: If a callback returns an error * this function returns immediately with this error. **/ gpg_error_t assuan_transact (assuan_context_t ctx, const char *command, gpg_error_t (*data_cb)(void *, const void *, size_t), void *data_cb_arg, gpg_error_t (*inquire_cb)(void*, const char *), void *inquire_cb_arg, gpg_error_t (*status_cb)(void*, const char *), void *status_cb_arg) { gpg_error_t rc; assuan_response_t response; int off; char *line; int linelen; rc = assuan_write_line (ctx, command); if (rc) return rc; if (*command == '#' || !*command) return 0; /* Don't expect a response for a comment line. */ again: rc = _assuan_read_from_server (ctx, &response, &off, ctx->flags.convey_comments); if (rc) return rc; /* error reading from server */ line = ctx->inbound.line + off; linelen = ctx->inbound.linelen - off; if (response == ASSUAN_RESPONSE_ERROR) rc = atoi (line); else if (response == ASSUAN_RESPONSE_DATA) { if (!data_cb) rc = _assuan_error (ctx, GPG_ERR_ASS_NO_DATA_CB); else { rc = data_cb (data_cb_arg, line, linelen); if (!rc) goto again; } } else if (response == ASSUAN_RESPONSE_INQUIRE) { if (!inquire_cb) { assuan_write_line (ctx, "END"); /* get out of inquire mode */ _assuan_read_from_server (ctx, &response, &off, 0); /* dummy read */ rc = _assuan_error (ctx, GPG_ERR_ASS_NO_INQUIRE_CB); } else { rc = inquire_cb (inquire_cb_arg, line); if (!rc) rc = assuan_send_data (ctx, NULL, 0); /* flush and send END */ else { /* Flush and send CAN. */ /* Note that in this error case we don't want to return an error code from sending the cancel. The dummy read is to remove the response from the server which we are not interested in. */ assuan_send_data (ctx, NULL, 1); _assuan_read_from_server (ctx, &response, &off, 0); } if (!rc) goto again; } } else if (response == ASSUAN_RESPONSE_STATUS) { if (status_cb) rc = status_cb (status_cb_arg, line); if (!rc) goto again; } else if (response == ASSUAN_RESPONSE_COMMENT && ctx->flags.convey_comments) { line -= off; /* Send line with the comment marker. */ if (status_cb) rc = status_cb (status_cb_arg, line); if (!rc) goto again; } else if (response == ASSUAN_RESPONSE_END) { if (!data_cb) rc = _assuan_error (ctx, GPG_ERR_ASS_NO_DATA_CB); else { rc = data_cb (data_cb_arg, NULL, 0); if (!rc) goto again; } } return rc; } diff --git a/src/context.c b/src/context.c index 147edae..82166bb 100644 --- a/src/context.c +++ b/src/context.c @@ -1,229 +1,230 @@ /* context.c - Context specific interface. - Copyright (C) 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 "assuan-defs.h" #include "debug.h" /* Set user-data in a context. */ void assuan_set_pointer (assuan_context_t ctx, void *user_pointer) { TRACE1 (ctx, ASSUAN_LOG_CTX, "assuan_set_pointer", ctx, "user_pointer=%p", user_pointer); if (ctx) ctx->user_pointer = user_pointer; } /* Get user-data in a context. */ void * assuan_get_pointer (assuan_context_t ctx) { #if 0 /* This is called often. */ TRACE1 (ctx, ASSUAN_LOG_CTX, "assuan_get_pointer", ctx, "ctx->user_pointer=%p", ctx ? ctx->user_pointer : NULL); #endif if (! ctx) return NULL; return ctx->user_pointer; } /* For context CTX, set the flag FLAG to VALUE. Values for flags are usually 1 or 0 but certain flags might allow for other values; see the description of the type assuan_flag_t for details. */ void assuan_set_flag (assuan_context_t ctx, assuan_flag_t flag, int value) { TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_set_flag", ctx, "flag=%i,value=%i", flag, value); if (!ctx) return; switch (flag) { case ASSUAN_NO_WAITPID: ctx->flags.no_waitpid = value; break; case ASSUAN_CONFIDENTIAL: ctx->flags.confidential = value; break; case ASSUAN_NO_FIXSIGNALS: ctx->flags.no_fixsignals = value; break; case ASSUAN_CONVEY_COMMENTS: ctx->flags.convey_comments = value; break; case ASSUAN_NO_LOGGING: ctx->flags.no_logging = value; break; case ASSUAN_FORCE_CLOSE: ctx->flags.force_close = 1; break; } } /* Return the VALUE of FLAG in context CTX. */ int assuan_get_flag (assuan_context_t ctx, assuan_flag_t flag) { int res = 0; TRACE_BEG1 (ctx, ASSUAN_LOG_CTX, "assuan_get_flag", ctx, "flag=%i", flag); if (! ctx) return 0; switch (flag) { case ASSUAN_NO_WAITPID: res = ctx->flags.no_waitpid; break; case ASSUAN_CONFIDENTIAL: res = ctx->flags.confidential; break; case ASSUAN_NO_FIXSIGNALS: res = ctx->flags.no_fixsignals; break; case ASSUAN_CONVEY_COMMENTS: res = ctx->flags.convey_comments; break; case ASSUAN_NO_LOGGING: res = ctx->flags.no_logging; break; case ASSUAN_FORCE_CLOSE: res = ctx->flags.force_close; break; } return TRACE_SUC1 ("flag_value=%i", res); } /* Same as assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 1). */ void assuan_begin_confidential (assuan_context_t ctx) { assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 1); } /* Same as assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 0). */ void assuan_end_confidential (assuan_context_t ctx) { assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 0); } /* Set the system callbacks. */ void assuan_ctx_set_system_hooks (assuan_context_t ctx, assuan_system_hooks_t system_hooks) { TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_set_system_hooks", ctx, "system_hooks=%p (version %i)", system_hooks, system_hooks->version); _assuan_system_hooks_copy (&ctx->system, system_hooks); } /* Set the IO monitor function. */ void assuan_set_io_monitor (assuan_context_t ctx, assuan_io_monitor_t io_monitor, void *hook_data) { TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_set_io_monitor", ctx, "io_monitor=%p,hook_data=%p", io_monitor, hook_data); if (! ctx) return; ctx->io_monitor = io_monitor; ctx->io_monitor_data = hook_data; } /* Store the error in the context so that the error sending function can take out a descriptive text. Inside the assuan code, use the macro set_error instead of this function. */ gpg_error_t assuan_set_error (assuan_context_t ctx, gpg_error_t err, const char *text) { TRACE4 (ctx, ASSUAN_LOG_CTX, "assuan_set_error", ctx, "err=%i (%s,%s),text=%s", err, gpg_strsource (err), gpg_strerror (err), text?text:"(none)"); ctx->err_no = err; ctx->err_str = text; return err; } /* Return the PID of the peer or ASSUAN_INVALID_PID if not known. This function works in some situations where assuan_get_ucred fails. */ pid_t assuan_get_pid (assuan_context_t ctx) { TRACE1 (ctx, ASSUAN_LOG_CTX, "assuan_get_pid", ctx, "pid=%i", ctx ? ctx->pid : -1); return (ctx && ctx->pid) ? ctx->pid : ASSUAN_INVALID_PID; } /* Return user credentials. For getting the pid of the peer the assuan_get_pid is usually better suited. */ gpg_error_t assuan_get_peercred (assuan_context_t ctx, assuan_peercred_t *peercred) { TRACE (ctx, ASSUAN_LOG_CTX, "assuan_get_peercred", ctx); if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!ctx->peercred_valid) return _assuan_error (ctx, GPG_ERR_ASS_GENERAL); *peercred = &ctx->peercred; return 0; } diff --git a/src/conversion.c b/src/conversion.c index 88a7fd0..a86e5a9 100644 --- a/src/conversion.c +++ b/src/conversion.c @@ -1,113 +1,113 @@ /* conversion.c - String conversion helper functions. - Copyright (C) 2000 Werner Koch (dd9jn) - Copyright (C) 2001, 2002, 2003, 2004, 2007, 2009 g10 Code GmbH - - This file is part of Assuan. - - Assuan 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. - - Assuan 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. */ + * Copyright (C) 2000 Werner Koch (dd9jn) + * Copyright (C) 2001, 2002, 2003, 2004, 2007, 2009 g10 Code GmbH + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #include #include #include #include "assuan-defs.h" #include "debug.h" /* Convert the number NR to a hexadecimal string. Returns the tail pointer. */ static char * _assuan_bytetohex (int nr, char *str) { static char hexdigits[] = "0123456789abcdef"; int i; #define NROFHEXDIGITS 2 for (i = 0; i < NROFHEXDIGITS; i++) { int digit = (nr >> (i << 2)) & 0xf; *(str++) = hexdigits[digit]; } return str; } /* Encode the C formatted string SRC and return the malloc'ed result. */ char * _assuan_encode_c_string (assuan_context_t ctx, const char *src) { const unsigned char *istr; char *res; char *ostr; - + ostr = _assuan_malloc (ctx, 4 * strlen (src) + 1); if (! *ostr) return NULL; res = ostr; for (istr = (const unsigned char *) src; *istr; istr++) { int c = 0; switch (*istr) { case '\r': c = 'r'; break; case '\n': c = 'n'; break; case '\f': c = 'f'; break; case '\v': c = 'v'; break; case '\b': c = 'b'; break; default: if ((isascii (*istr) && isprint (*istr)) || (*istr >= 0x80)) *(ostr++) = *istr; else { *(ostr++) = '\\'; *(ostr++) = 'x'; ostr = _assuan_bytetohex (*istr, ostr); } } if (c) { *(ostr++) = '\\'; *(ostr++) = c; } } *(ostr) = '\0'; return res; } diff --git a/src/debug.h b/src/debug.h index 5ce0f07..78bbc23 100644 --- a/src/debug.h +++ b/src/debug.h @@ -1,276 +1,276 @@ /* debug.h - interface to debugging functions - Copyright (C) 2002, 2004, 2005, 2007 g10 Code GmbH - - This file is part of Assuan. - - Assuan 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. - - Assuan 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. */ + * Copyright (C) 2002, 2004, 2005, 2007 g10 Code GmbH + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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+ + */ #ifndef DEBUG_H #define DEBUG_H #include #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_INTTYPES_H #include #endif #include "assuan-defs.h" /* Indirect stringification, requires __STDC__ to work. */ #define STRINGIFY(v) #v #define XSTRINGIFY(v) STRINGIFY(v) /* Remove path components from filenames (i.e. __FILE__) for cleaner logs. */ static GPG_ERR_INLINE const char *_assuan_debug_srcname (const char *file) ASSUAN_GCC_A_PURE; static GPG_ERR_INLINE const char * _assuan_debug_srcname (const char *file) { const char *s = strrchr (file, '/'); return s ? s + 1 : file; } /* Called early to initialize the logging. */ void _assuan_debug_subsystem_init (void); /* Log the formatted string FORMAT at debug level LEVEL or higher. */ void _assuan_debug (assuan_context_t ctx, unsigned int cat, const char *format, ...); /* Start a new debug line in *LINE, logged at level LEVEL or higher, and starting with the formatted string FORMAT. */ void _assuan_debug_begin (assuan_context_t ctx, void **helper, unsigned int cat, const char *format, ...); /* Add the formatted string FORMAT to the debug line *LINE. */ void _assuan_debug_add (assuan_context_t ctx, void **helper, const char *format, ...); /* Finish construction of *LINE and send it to the debug output stream. */ void _assuan_debug_end (assuan_context_t ctx, void **helper, unsigned int cat); void _assuan_debug_buffer (assuan_context_t ctx, unsigned int cat, const char *const fmt, const char *const func, const char *const tagname, void *tag, const char *const buffer, size_t len); /* Trace support. */ #define _TRACE(ctx, lvl, name, tag) \ assuan_context_t _assuan_trace_context = ctx; \ int _assuan_trace_level = lvl; \ const char *const _assuan_trace_func = name; \ const char *const _assuan_trace_tagname = STRINGIFY (tag); \ void *_assuan_trace_tag = (void *) (uintptr_t) tag #define TRACE_BEG(ctx,lvl, name, tag) \ _TRACE (ctx, lvl, name, tag); \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): enter\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag) #define TRACE_BEG0(ctx, lvl, name, tag, fmt) \ _TRACE (ctx, lvl, name, tag); \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): enter: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag) #define TRACE_BEG1(ctx, lvl, name, tag, fmt, arg1) \ _TRACE (ctx, lvl, name, tag); \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): enter: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1) #define TRACE_BEG2(ctx, lvl, name, tag, fmt, arg1, arg2) \ _TRACE (ctx, lvl, name, tag); \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): enter: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2) #define TRACE_BEG3(ctx, lvl, name, tag, fmt, arg1, arg2, arg3) \ _TRACE (ctx, lvl, name, tag); \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): enter: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2, arg3) #define TRACE_BEG4(ctx, lvl, name, tag, fmt, arg1, arg2, arg3, arg4) \ _TRACE (ctx, lvl, name, tag); \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): enter: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2, arg3, arg4) #define TRACE_BEG6(ctx, lvl, name, tag, fmt, arg1, arg2, arg3, arg4,arg5,arg6) \ _TRACE (ctx, lvl, name, tag); \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): enter: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2, arg3, arg4, arg5, arg6) #define TRACE_BEG8(ctx, lvl, name, tag, fmt, arg1, arg2, arg3, arg4, \ arg5, arg6, arg7, arg8) \ _TRACE (ctx, lvl, name, tag); \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): enter: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2, arg3, arg4, \ arg5, arg6, arg7, arg8) #define TRACE(ctx, lvl, name, tag) \ _assuan_debug (ctx, lvl, "%s (%s=%p): call\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag) #define TRACE0(ctx, lvl, name, tag, fmt) \ _assuan_debug (ctx, lvl, "%s (%s=%p): call: " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag) #define TRACE1(ctx, lvl, name, tag, fmt, arg1) \ _assuan_debug (ctx, lvl, "%s (%s=%p): call: " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1) #define TRACE2(ctx, lvl, name, tag, fmt, arg1, arg2) \ _assuan_debug (ctx, lvl, "%s (%s=%p): call: " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ arg2) #define TRACE3(ctx, lvl, name, tag, fmt, arg1, arg2, arg3) \ _assuan_debug (ctx, lvl, "%s (%s=%p): call: " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ arg2, arg3) #define TRACE4(ctx, lvl, name, tag, fmt, arg1, arg2, arg3, arg4) \ _assuan_debug (ctx, lvl, "%s (%s=%p): call: " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ arg2, arg3, arg4) #define TRACE6(ctx, lvl, name, tag, fmt, arg1, arg2, arg3, arg4, arg5, arg6) \ _assuan_debug (ctx, lvl, "%s (%s=%p): call: " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ arg2, arg3, arg4, arg5, arg6) #define TRACE_ERR(err) \ err == 0 ? (TRACE_SUC ()) : \ (_assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): error: %s <%s>\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, gpg_strerror (err), \ ctx?gpg_strsource (ctx->err_source):""), \ _assuan_error (ctx, err)) /* The cast to void suppresses GCC warnings. */ #define TRACE_SYSRES(res) \ res >= 0 ? ((void) (TRACE_SUC1 ("result=%i", res)), (res)) : \ (_assuan_debug (_assuan_trace_context, _assuan_trace_level, "%s (%s=%p): error: %s\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, strerror (errno)), (res)) #define TRACE_SYSERR(res) \ res == 0 ? ((void) (TRACE_SUC1 ("result=%i", res)), (res)) : \ (_assuan_debug (_assuan_trace_context, _assuan_trace_level, "%s (%s=%p): error: %s\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, strerror (res)), (res)) #define TRACE_SUC() \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): leave\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag), 0 #define TRACE_SUC0(fmt) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): leave: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag), 0 #define TRACE_SUC1(fmt, arg1) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): leave: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1), 0 #define TRACE_SUC2(fmt, arg1, arg2) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): leave: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2), 0 #define TRACE_SUC5(fmt, arg1, arg2, arg3, arg4, arg5) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): leave: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2, arg3, arg4, arg5), 0 #define TRACE_LOG(fmt) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): check: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag) #define TRACE_LOG1(fmt, arg1) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): check: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1) #define TRACE_LOG2(fmt, arg1, arg2) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): check: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2) #define TRACE_LOG3(fmt, arg1, arg2, arg3) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): check: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2, arg3) #define TRACE_LOG4(fmt, arg1, arg2, arg3, arg4) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): check: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2, arg3, arg4) #define TRACE_LOG5(fmt, arg1, arg2, arg3, arg4, arg5) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): check: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2, arg3, arg4, arg5) #define TRACE_LOG6(fmt, arg1, arg2, arg3, arg4, arg5, arg6) \ _assuan_debug (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): check: " fmt "\n", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, arg1, arg2, arg3, arg4, arg5, \ arg6) #define TRACE_LOGBUF(buf, len) \ _assuan_debug_buffer (_assuan_trace_context, _assuan_trace_level, \ "%s (%s=%p): check: %s", \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag, buf, len) #define TRACE_SEQ(hlp,fmt) \ _assuan_debug_begin (_assuan_trace_context, &(hlp), \ "%s (%s=%p): check: " fmt, \ _assuan_trace_func, _assuan_trace_tagname, \ _assuan_trace_tag) #define TRACE_ADD0(hlp,fmt) \ _assuan_debug_add (_assuan_trace_context, &(hlp), fmt) #define TRACE_ADD1(hlp,fmt,a) \ _assuan_debug_add (_assuan_trace_context, &(hlp), fmt, (a)) #define TRACE_ADD2(hlp,fmt,a,b) \ _assuan_debug_add (_assuan_trace_context, &(hlp), fmt, (a), (b)) #define TRACE_ADD3(hlp,fmt,a,b,c) \ _assuan_debug_add (_assuan_trace_context, &(hlp), fmt, (a), (b), (c)) #define TRACE_END(hlp,fmt) \ _assuan_debug_add (_assuan_trace_context, &(hlp), fmt); \ _assuan_debug_end (_assuan_trace_context, &(hlp), _assuan_trace_level) #define TRACE_ENABLED(hlp) (!!(hlp)) #endif /* DEBUG_H */ diff --git a/src/funopen.c b/src/funopen.c index e6d77e3..466d9aa 100644 --- a/src/funopen.c +++ b/src/funopen.c @@ -1,62 +1,63 @@ /* funopen.c - Replacement for funopen. * Copyright (C) 2003, 2005 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan 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. * * Assuan 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 /* Replacement for the *BSD function: FILE *funopen (void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int), fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *)); The functions to provide my either be NULL if not required or similar to the unistd function with the exception of using the cookie instead of the file descriptor. */ #ifdef HAVE_FOPENCOOKIE FILE * _assuan_funopen(void *cookie, cookie_read_function_t *readfn, cookie_write_function_t *writefn, cookie_seek_function_t *seekfn, cookie_close_function_t *closefn) { cookie_io_functions_t io; io.read = readfn; io.write = writefn; io.seek = seekfn; io.close = closefn; return fopencookie (cookie, readfn ? ( writefn ? "rw" : "r" ) : ( writefn ? "w" : ""), io); } #else #error No known way to implement funopen. #endif diff --git a/src/gpgcedev.c b/src/gpgcedev.c index bff4655..c841ec2 100644 --- a/src/gpgcedev.c +++ b/src/gpgcedev.c @@ -1,1640 +1,1640 @@ /* gpgcedrv.c - WindowsCE device driver to implement pipe and syslog. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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 3 of - the License, or (at your option) any later version. - - Assuan 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 . + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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 3 of + * the License, or (at your option) any later version. + * + * Assuan 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-3.0+ */ - #include #include #include #include #include #include /* FIXME Cancel not handled. */ #define DBGFILENAME "\\gpgcedev.dbg" #define LOGFILENAME L"\\gpgcedev.log" #define GPGCEDEV_KEY_NAME L"Drivers\\GnuPG_Device" #define GPGCEDEV_KEY_NAME2 L"Drivers\\GnuPG_Log" /* Missing IOCTLs in the current mingw32ce. */ #ifndef IOCTL_PSL_NOTIFY # define FILE_DEVICE_PSL 259 # define IOCTL_PSL_NOTIFY \ CTL_CODE (259, 255, METHOD_NEITHER, FILE_ANY_ACCESS) #endif /*IOCTL_PSL_NOTIFY*/ /* The IOCTL to return the rendezvous id of the handle. The required outbuf parameter is the address of a variable to store the rendezvous ID, which is a LONG value. */ #define GPGCEDEV_IOCTL_GET_RVID \ CTL_CODE (FILE_DEVICE_STREAMS, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS) -/* The IOCTL used to create the pipe. +/* The IOCTL used to create the pipe. The caller sends this IOCTL to the read or the write handle. The required inbuf parameter is address of a variable holding the rendezvous id of the pipe's other end. There is one possible problem with the code: If a pipe is kept in non-rendezvous state until after the rendezvous ids overflow, it is possible that the wrong end will be used. However this is not a realistic scenario. */ #define GPGCEDEV_IOCTL_MAKE_PIPE \ CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS) /* The IOCTL used to unblock a blocking thread. The caller sends this IOCTL to the read or the write handle. No parameter is required. The effect is that a reader or writer blocked on the same handle is unblocked (and will return ERROR_BUSY). Note that the operation can be repeated, if so desired. The state of the pipe itself will not be affected in any way. */ #define GPGCEDEV_IOCTL_UNBLOCK \ CTL_CODE (FILE_DEVICE_STREAMS, 2050, METHOD_BUFFERED, FILE_ANY_ACCESS) /* The IOCTL to assign a rendezvous id to a process. The required inbuf parameters are the rendezvous ID to assign and the process ID of the process receiving the RVID. The handle on which this is called is not really used at all! */ #define GPGCEDEV_IOCTL_ASSIGN_RVID \ CTL_CODE (FILE_DEVICE_STREAMS, 2051, METHOD_BUFFERED, FILE_ANY_ACCESS) /* An object to describe a pipe. */ struct pipeimpl_s { CRITICAL_SECTION critsect; /* Lock for all members. */ int refcnt; - char *buffer; + char *buffer; size_t buffer_size; size_t buffer_len; /* The valid length of the bufer. */ size_t buffer_pos; /* The actual read position. */ #define PIPE_FLAG_NO_READER 1 #define PIPE_FLAG_NO_WRITER 2 #define PIPE_FLAG_UNBLOCK_READER 4 #define PIPE_FLAG_UNBLOCK_WRITER 8 #define PIPE_FLAG_HALT_MONITOR 16 int flags; HANDLE space_available; /* Set if space is available. */ - HANDLE data_available; /* Set if data is available. */ + HANDLE data_available; /* Set if data is available. */ /* For the monitor thread started by ASSIGN_RVID. */ HANDLE monitor_proc; int monitor_access; LONG monitor_rvid; }; typedef struct pipeimpl_s *pipeimpl_t; /* An object to describe a logging context. We can't write directly to the log stream because we want to do line buffering and thus we need to store data until we see LF. */ struct logimpl_s; typedef struct logimpl_s *logimpl_t; struct logimpl_s { unsigned long logid; /* An identifier for a log source. */ unsigned long dsec; /* Tenth of a second since system start. */ char *line; /* Malloced line buffer. */ size_t linesize; /* Allocated size of LINE. */ size_t linelen; /* Used length of the line. */ int truncated; /* Indicates a truncated log line. */ }; /* An object to store information pertaining to an open-context. */ struct opnctx_s; typedef struct opnctx_s *opnctx_t; struct opnctx_s { int inuse; /* True if this object has valid data. */ int is_log; /* True if this describes a log device. */ LONG rvid; /* The unique rendezvous identifier. */ DWORD access_code;/* Value from OpenFile. */ DWORD share_mode; /* Value from OpenFile. */ /* The state shared by all pipe users. Only used if IS_LOG is false. */ pipeimpl_t pipeimpl; /* The state used to implement a log stream. Only used if IS_LOG is true. */ logimpl_t logimpl; }; /* A malloced table of open-context and the number of allocated slots. */ static opnctx_t opnctx_table; static size_t opnctx_table_size; /* The macros make sure that 0 is never a valid openctx_arg. */ #define OPNCTX_TO_IDX(ctx_arg) (((ctx_arg) - opnctx_table) + 1) #define OPNCTX_FROM_IDX(idx) (&opnctx_table[(idx) - 1]) #define OPNCTX_VALID_IDX_P(idx) ((idx) > 0 && (idx) <= opnctx_table_size) typedef struct monitor_s *monitor_t; struct monitor_s { int inuse; /* True if this object has valid data. */ pipeimpl_t pipeimpl; }; static monitor_t monitor_table; static size_t monitor_table_size; /* A criticial section object used to protect the OPNCTX_TABLE and MONITOR_TABLE. */ static CRITICAL_SECTION opnctx_table_cs; /* A global object to control the logging. */ struct { CRITICAL_SECTION lock; /* Lock for this structure. */ HANDLE filehd; /* Handle of the log output file. */ int references; /* Number of objects references this one. */ } logcontrol; /* We don't need a device context for the pipe thus we use the address of the critical section object for it. */ #define PIPECTX_VALUE ((DWORD)(&opnctx_table_cs)) /* The device context for the log device is the address of our control structure. */ #define LOGCTX_VALUE ((DWORD)(&logcontrol)) /* True if we have enabled debugging. */ static int enable_debug; /* True if logging has been enabled. */ static int enable_logging; static void log_debug (const char *fmt, ...) { if (enable_debug) { va_list arg_ptr; FILE *fp; - + fp = fopen (DBGFILENAME, "a+"); if (!fp) return; va_start (arg_ptr, fmt); vfprintf (fp, fmt, arg_ptr); va_end (arg_ptr); fclose (fp); } } /* Return a new rendezvous id. We will never return an RVID of 0. */ static LONG create_rendezvous_id (void) { static LONG rendezvous_id; LONG rvid; while (!(rvid = InterlockedIncrement (&rendezvous_id))) ; return rvid; } /* Return a new log id. These log ids are used to identify log lines send to the same device; ie. for each CreateFile("GPG2:") a new log id is assigned. We will ever return a log id of 0. */ static LONG create_log_id (void) { static LONG log_id; LONG lid; while (!(lid = InterlockedIncrement (&log_id))) ; return lid; } pipeimpl_t pipeimpl_new (void) { pipeimpl_t pimpl; pimpl = malloc (sizeof (*pimpl)); if (!pimpl) return NULL; InitializeCriticalSection (&pimpl->critsect); pimpl->refcnt = 1; pimpl->buffer_size = 512; pimpl->buffer = malloc (pimpl->buffer_size); if (!pimpl->buffer) { DeleteCriticalSection (&pimpl->critsect); free (pimpl); return NULL; } pimpl->buffer_len = 0; pimpl->buffer_pos = 0; pimpl->flags = 0; pimpl->space_available = CreateEvent (NULL, FALSE, FALSE, NULL); if (!pimpl->space_available) { free (pimpl->buffer); DeleteCriticalSection (&pimpl->critsect); free (pimpl); return NULL; } pimpl->data_available = CreateEvent (NULL, FALSE, FALSE, NULL); if (!pimpl->data_available) { CloseHandle (pimpl->space_available); free (pimpl->buffer); DeleteCriticalSection (&pimpl->critsect); free (pimpl); return NULL; } pimpl->monitor_proc = INVALID_HANDLE_VALUE; pimpl->monitor_access = 0; pimpl->monitor_rvid = 0; return pimpl; } /* PIMPL must be locked. It is unlocked at exit. */ void pipeimpl_unref (pipeimpl_t pimpl) { int release = 0; if (!pimpl) return; log_debug ("pipeimpl_unref (%p): dereference\n", pimpl); if (--pimpl->refcnt == 0) release = 1; LeaveCriticalSection (&pimpl->critsect); if (! release) return; log_debug ("pipeimpl_unref (%p): release\n", pimpl); DeleteCriticalSection (&pimpl->critsect); if (pimpl->buffer) { free (pimpl->buffer); pimpl->buffer = NULL; pimpl->buffer_size = 0; } if (pimpl->space_available != INVALID_HANDLE_VALUE) { CloseHandle (pimpl->space_available); pimpl->space_available = INVALID_HANDLE_VALUE; } if (pimpl->data_available != INVALID_HANDLE_VALUE) { CloseHandle (pimpl->data_available); pimpl->data_available = INVALID_HANDLE_VALUE; } } /* Allocate a new log structure. */ logimpl_t logimpl_new (void) { logimpl_t limpl; limpl = calloc (1, sizeof *limpl); if (!limpl) return NULL; limpl->logid = create_log_id (); limpl->linesize = 256; limpl->line = malloc (limpl->linesize); if (!limpl->line) { free (limpl); return NULL; } return limpl; } /* There is no need to lock LIMPL, thus is a dummy function. */ void logimpl_unref (logimpl_t limpl) { (void)limpl; } /* Flush a pending log line. */ static void logimpl_flush (logimpl_t limpl) { if (!limpl->linelen || !enable_logging) return; EnterCriticalSection (&logcontrol.lock); if (logcontrol.filehd == INVALID_HANDLE_VALUE) logcontrol.filehd = CreateFile (LOGFILENAME, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (!logcontrol.filehd) log_debug ("can't open log file: rc=%d\n", (int)GetLastError ()); else { char buf[50]; DWORD nwritten; - snprintf (buf, sizeof buf, + snprintf (buf, sizeof buf, "%06lu/%lu//", limpl->dsec % 1000000, limpl->logid); if (!WriteFile (logcontrol.filehd, buf, strlen (buf), &nwritten, NULL)) log_debug ("error writing log file: rc=%d\n", (int)GetLastError ()); else if (!WriteFile (logcontrol.filehd, limpl->line, limpl->linelen, &nwritten, NULL)) log_debug ("error writing log file: rc=%d\n", (int)GetLastError ()); snprintf (buf, sizeof buf, "%s\n", limpl->truncated? "[...]":""); if (!WriteFile (logcontrol.filehd, buf, strlen (buf), &nwritten, NULL)) log_debug ("error writing log file: rc=%d\n", (int)GetLastError ()); } LeaveCriticalSection (&logcontrol.lock); limpl->linelen = 0; limpl->truncated = 0; } /* Return a new opnctx handle and mark it as used. Returns NULL and sets LastError on memory failure etc. opnctx_table_cs must be locked on entry and is locked on exit. Note that the returned pointer is only valid as long as opnctx_table_cs stays locked, as it is not stable under table reallocation. */ static opnctx_t allocate_opnctx (int is_log) { opnctx_t opnctx = NULL; int idx; for (idx = 0; idx < opnctx_table_size; idx++) if (! opnctx_table[idx].inuse) break; if (idx == opnctx_table_size) { /* We need to increase the size of the table. The approach we take is straightforward to minimize the risk of bugs. */ opnctx_t newtbl; size_t newsize = opnctx_table_size + 64; newtbl = calloc (newsize, sizeof *newtbl); if (!newtbl) goto leave; memcpy (newtbl, opnctx_table, opnctx_table_size * sizeof (*newtbl)); free (opnctx_table); opnctx_table = newtbl; idx = opnctx_table_size; opnctx_table_size = newsize; } opnctx = &opnctx_table[idx]; opnctx->inuse = 1; opnctx->is_log = is_log; opnctx->rvid = 0; opnctx->access_code = 0; opnctx->share_mode = 0; opnctx->pipeimpl = 0; opnctx->logimpl = 0; leave: return opnctx; } /* Verify context CTX, returns NULL if not valid and the pointer to the context if valid. opnctx_table_cs must be locked on entry and is locked on exit. Note that the returned pointer is only valid as long as opnctx_table_cs remains locked. */ opnctx_t verify_opnctx (DWORD ctx_arg) { opnctx_t ctx; if (! OPNCTX_VALID_IDX_P (ctx_arg)) { SetLastError (ERROR_INVALID_HANDLE); return NULL; } ctx = OPNCTX_FROM_IDX (ctx_arg); if (! ctx->inuse) { SetLastError (ERROR_INVALID_HANDLE); return NULL; } return ctx; } /* Return a new monitor handle and mark it as used. Returns NULL and sets LastError on memory failure etc. opnctx_table_cs must be locked on entry and is locked on exit. Note that the returned pointer is only valid as long as opnctx_table_cs stays locked, as it is not stable under table reallocation. */ static monitor_t allocate_monitor (void) { monitor_t monitor = NULL; int idx; for (idx = 0; idx < monitor_table_size; idx++) if (! monitor_table[idx].inuse) break; if (idx == monitor_table_size) { /* We need to increase the size of the table. The approach we take is straightforward to minimize the risk of bugs. */ monitor_t newtbl; size_t newsize = monitor_table_size + 16; newtbl = calloc (newsize, sizeof *newtbl); if (!newtbl) goto leave; memcpy (newtbl, monitor_table, monitor_table_size * sizeof (*newtbl)); free (monitor_table); monitor_table = newtbl; idx = monitor_table_size; monitor_table_size = newsize; } monitor = &monitor_table[idx]; monitor->inuse = 1; monitor->pipeimpl = 0; leave: return monitor; } static pipeimpl_t assert_pipeimpl (opnctx_t ctx) { DWORD ctx_arg = OPNCTX_TO_IDX (ctx); if (ctx->is_log) { log_debug (" assert_pipeimpl (ctx=%i): " "error: not valid for a log device\n", ctx_arg); return NULL; } if (! ctx->pipeimpl) { ctx->pipeimpl = pipeimpl_new (); if (! ctx->pipeimpl) { log_debug (" assert_pipeimpl (ctx=%i): error: can't create pipe\n", ctx_arg); return NULL; } log_debug (" assert_pipeimpl (ctx=%i): created pipe 0x%p\n", ctx_arg, ctx->pipeimpl); } return ctx->pipeimpl; } static logimpl_t assert_logimpl (opnctx_t ctx) { DWORD ctx_arg = OPNCTX_TO_IDX (ctx); if (!ctx->is_log) { log_debug (" assert_logimpl (ctx=%i): " "error: not valid for a pipe device\n", ctx_arg); return NULL; } if (!ctx->logimpl) { ctx->logimpl = logimpl_new (); if (!ctx->logimpl) { log_debug (" assert_logimpl (ctx=%i): error: can't create log\n", ctx_arg); return NULL; } log_debug (" assert_logimpl (ctx=%i): created log 0x%p\n", ctx_arg, ctx->logimpl); } return ctx->logimpl; } /* Verify access CODE for context CTX_ARG, returning a reference to the locked pipe or the locked log implementation. opnctx_table_cs must be unlocked on entry and is unlocked on exit. */ int access_opnctx (DWORD ctx_arg, DWORD code, pipeimpl_t *r_pipe, logimpl_t *r_log) { opnctx_t ctx; *r_pipe = NULL; *r_log = NULL; EnterCriticalSection (&opnctx_table_cs); ctx = verify_opnctx (ctx_arg); if (! ctx) { /* Error is set by verify_opnctx. */ LeaveCriticalSection (&opnctx_table_cs); return -1; } if (! (ctx->access_code & code)) { SetLastError (ERROR_INVALID_ACCESS); LeaveCriticalSection (&opnctx_table_cs); return -1; } if (ctx->is_log) { logimpl_t limpl; limpl = assert_logimpl (ctx); if (!limpl) { LeaveCriticalSection (&opnctx_table_cs); return -1; } *r_log = limpl; } else { pipeimpl_t pimpl; pimpl = assert_pipeimpl (ctx); if (! pimpl) { LeaveCriticalSection (&opnctx_table_cs); return -1; } EnterCriticalSection (&pimpl->critsect); pimpl->refcnt++; *r_pipe = pimpl; } LeaveCriticalSection (&opnctx_table_cs); return 0; } static char * wchar_to_utf8 (const wchar_t *string) { int n; size_t length = wcslen (string); char *result; n = WideCharToMultiByte (CP_UTF8, 0, string, length, NULL, 0, NULL, NULL); if (n < 0 || (n+1) <= 0) abort (); result = malloc (n+1); if (!result) abort (); n = WideCharToMultiByte (CP_ACP, 0, string, length, result, n, NULL, NULL); if (n < 0) abort (); - + result[n] = 0; return result; } /* Initialize the device and return a device specific context. */ DWORD GPG_Init (LPCTSTR active_key, DWORD bus_context) { static int firsttimedone; HKEY handle; wchar_t buffer[25]; DWORD buflen; DWORD result; (void)bus_context; EnterCriticalSection (&logcontrol.lock); if (!firsttimedone) { firsttimedone++; if (!RegOpenKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME, 0, KEY_READ, &handle)) { buflen = sizeof buffer; if (!RegQueryValueEx (handle, L"debugDriver", 0, NULL, (PBYTE)buffer, &buflen) && wcstol (buffer, NULL, 10) > 0) enable_debug = 1; RegCloseKey (handle); } if (!RegOpenKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME2, 0, KEY_READ, &handle)) { buflen = sizeof buffer; if (!RegQueryValueEx (handle, L"enableLog", 0, NULL, (PBYTE)buffer, &buflen) && wcstol (buffer, NULL, 10) > 0) enable_logging = 1; RegCloseKey (handle); } } LeaveCriticalSection (&logcontrol.lock); if (enable_debug) { char *tmpbuf; tmpbuf = wchar_to_utf8 (active_key); log_debug ("GPG_Init (%s)\n", tmpbuf); free (tmpbuf); } if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, active_key, 0, KEY_READ, &handle)) { - log_debug ("GPG_Init: error reading registry: rc=%d\n", + log_debug ("GPG_Init: error reading registry: rc=%d\n", (int)GetLastError ()); return 0; } buflen = sizeof buffer; if (RegQueryValueEx (handle, L"Name", 0, NULL, (PBYTE)buffer, &buflen)) { - log_debug ("GPG_Init: error reading registry value 'Name': rc=%d\n", + log_debug ("GPG_Init: error reading registry value 'Name': rc=%d\n", (int)GetLastError ()); result = 0; } else if (!wcscmp (buffer, L"GPG1:")) { /* This is the pipe device: We don't need any global data. However, we need to return something. */ log_debug ("GPG_Init: created pipe device (devctx=%p)\n", PIPECTX_VALUE); result = PIPECTX_VALUE; } else if (!wcscmp (buffer, L"GPG2:")) { /* This is the log device. Clear the object and return something. */ logcontrol.filehd = INVALID_HANDLE_VALUE; log_debug ("GPG_Init: created log device (devctx=%p)\n", 0); result = LOGCTX_VALUE; } else { if (enable_debug) { char *tmpbuf; tmpbuf = wchar_to_utf8 (buffer); log_debug ("GPG_Init: device '%s' is not supported\n", tmpbuf); free (tmpbuf); } SetLastError (ERROR_DEV_NOT_EXIST); result = 0; } RegCloseKey (handle); return result; } /* Deinitialize this device driver. */ BOOL GPG_Deinit (DWORD devctx) { log_debug ("GPG_Deinit (devctx=0x%p)\n", (void*)devctx); if (devctx == PIPECTX_VALUE) { /* No need to release resources. */ } else if (devctx == LOGCTX_VALUE) { EnterCriticalSection (&logcontrol.lock); if (logcontrol.filehd != INVALID_HANDLE_VALUE) { CloseHandle (logcontrol.filehd); logcontrol.filehd = INVALID_HANDLE_VALUE; } LeaveCriticalSection (&logcontrol.lock); } else { SetLastError (ERROR_INVALID_PARAMETER); return FALSE; /* Error. */ } - + return TRUE; /* Success. */ } /* Create a new open context. This function is called due to a CreateFile from the application. */ DWORD GPG_Open (DWORD devctx, DWORD access_code, DWORD share_mode) { opnctx_t opnctx; DWORD ctx_arg = 0; int is_log; log_debug ("GPG_Open (devctx=%p)\n", (void*)devctx); if (devctx == PIPECTX_VALUE) is_log = 0; else if (devctx == LOGCTX_VALUE) is_log = 1; else { log_debug ("GPG_Open (devctx=%p): error: wrong devctx (expected 0x%p)\n", (void*) devctx); SetLastError (ERROR_INVALID_PARAMETER); return 0; /* Error. */ } EnterCriticalSection (&opnctx_table_cs); opnctx = allocate_opnctx (is_log); if (!opnctx) { log_debug ("GPG_Open (devctx=%p): error: could not allocate context\n", (void*) devctx); goto leave; } opnctx->access_code = access_code; opnctx->share_mode = share_mode; ctx_arg = OPNCTX_TO_IDX (opnctx); log_debug ("GPG_Open (devctx=%p, is_log=%d): success: created context %i\n", (void*) devctx, is_log, ctx_arg); if (is_log) { EnterCriticalSection (&logcontrol.lock); logcontrol.references++; LeaveCriticalSection (&logcontrol.lock); } leave: LeaveCriticalSection (&opnctx_table_cs); return ctx_arg; } BOOL GPG_Close (DWORD opnctx_arg) { opnctx_t opnctx; BOOL result = FALSE; log_debug ("GPG_Close (ctx=%i)\n", opnctx_arg); EnterCriticalSection (&opnctx_table_cs); opnctx = verify_opnctx (opnctx_arg); if (!opnctx) { log_debug ("GPG_Close (ctx=%i): could not find context\n", opnctx_arg); goto leave; } if (opnctx->pipeimpl) { pipeimpl_t pimpl = opnctx->pipeimpl; EnterCriticalSection (&pimpl->critsect); /* This needs to be adjusted if there can be multiple reader/writers. */ if (opnctx->access_code & GENERIC_READ) { pimpl->flags |= PIPE_FLAG_NO_READER; SetEvent (pimpl->space_available); } else if (opnctx->access_code & GENERIC_WRITE) { pimpl->flags |= PIPE_FLAG_NO_WRITER; SetEvent (pimpl->data_available); } pipeimpl_unref (pimpl); opnctx->pipeimpl = 0; } if (opnctx->logimpl) { logimpl_t limpl = opnctx->logimpl; logimpl_flush (limpl); logimpl_unref (limpl); free (limpl->line); free (limpl); opnctx->logimpl = 0; EnterCriticalSection (&logcontrol.lock); logcontrol.references--; if (!logcontrol.references && logcontrol.filehd) { CloseHandle (logcontrol.filehd); logcontrol.filehd = INVALID_HANDLE_VALUE; } LeaveCriticalSection (&logcontrol.lock); } opnctx->access_code = 0; opnctx->share_mode = 0; opnctx->rvid = 0; opnctx->inuse = 0; result = TRUE; log_debug ("GPG_Close (ctx=%i): success\n", opnctx_arg); leave: LeaveCriticalSection (&opnctx_table_cs); return result; } DWORD GPG_Read (DWORD opnctx_arg, void *buffer, DWORD count) { pipeimpl_t pimpl; logimpl_t limpl; const char *src; char *dst; int result = -1; log_debug ("GPG_Read (ctx=%i, buffer=0x%p, count=%d)\n", opnctx_arg, buffer, count); if (access_opnctx (opnctx_arg, GENERIC_READ, &pimpl, &limpl)) { log_debug ("GPG_Read (ctx=%i): error: could not access context\n", opnctx_arg); return -1; } if (limpl) { /* Reading from a log stream does not make sense. Return EOF. */ result = 0; goto leave; } retry: if (pimpl->buffer_pos == pimpl->buffer_len) { HANDLE data_available = pimpl->data_available; /* Check for end of file. */ if (pimpl->flags & PIPE_FLAG_NO_WRITER) { log_debug ("GPG_Read (ctx=%i): success: EOF\n", opnctx_arg); result = 0; goto leave; } /* Check for request to unblock once. */ if (pimpl->flags & PIPE_FLAG_UNBLOCK_READER) { log_debug ("GPG_Read (ctx=%i): success: EBUSY (due to unblock)\n", opnctx_arg); pimpl->flags &= ~PIPE_FLAG_UNBLOCK_READER; SetLastError (ERROR_BUSY); result = -1; goto leave; } LeaveCriticalSection (&pimpl->critsect); log_debug ("GPG_Read (ctx=%i): waiting: data_available\n", opnctx_arg); WaitForSingleObject (data_available, INFINITE); log_debug ("GPG_Read (ctx=%i): resuming: data_available\n", opnctx_arg); EnterCriticalSection (&pimpl->critsect); goto retry; } - + dst = buffer; src = pimpl->buffer + pimpl->buffer_pos; while (count > 0 && pimpl->buffer_pos < pimpl->buffer_len) { *dst++ = *src++; count--; pimpl->buffer_pos++; } result = (dst - (char*)buffer); if (pimpl->buffer_pos == pimpl->buffer_len) pimpl->buffer_pos = pimpl->buffer_len = 0; - + /* Now there should be some space available. Signal the write end. Even if COUNT was passed as NULL and no space is available, signaling must be done. */ if (!SetEvent (pimpl->space_available)) log_debug ("GPG_Read (ctx=%i): warning: SetEvent(space_available) " "failed: rc=%d\n", opnctx_arg, (int)GetLastError ()); log_debug ("GPG_Read (ctx=%i): success: result=%d\n", opnctx_arg, result); leave: pipeimpl_unref (pimpl); logimpl_unref (limpl); return result; } DWORD GPG_Write (DWORD opnctx_arg, const void *buffer, DWORD count) { pipeimpl_t pimpl; logimpl_t limpl; int result = -1; const char *src; char *dst; size_t nwritten = 0; log_debug ("GPG_Write (ctx=%i, buffer=0x%p, count=%d)\n", opnctx_arg, buffer, count); if (access_opnctx (opnctx_arg, GENERIC_WRITE, &pimpl, &limpl)) { log_debug ("GPG_Write (ctx=%i): error: could not access context\n", opnctx_arg); return -1; } if (!count) { log_debug ("GPG_Write (ctx=%i): success\n", opnctx_arg); result = 0; goto leave; } retry: if (limpl) { /* Store it in our buffer up to a LF and print complete lines. */ result = count; if (!limpl->linelen) limpl->dsec = GetTickCount () / 100; dst = limpl->line + limpl->linelen; for (src = buffer; count; count--, src++) { if (*src == '\n') { logimpl_flush (limpl); dst = limpl->line + limpl->linelen; } else if (limpl->linelen >= limpl->linesize) limpl->truncated = 1; else { *dst++ = *src; limpl->linelen++; } } } else /* pimpl */ { /* Check for broken pipe. */ if (pimpl->flags & PIPE_FLAG_NO_READER) { log_debug ("GPG_Write (ctx=%i): error: broken pipe\n", opnctx_arg); SetLastError (ERROR_BROKEN_PIPE); goto leave; } - + /* Check for request to unblock once. */ if (pimpl->flags & PIPE_FLAG_UNBLOCK_WRITER) { log_debug ("GPG_Write (ctx=%i): success: EBUSY (due to unblock)\n", opnctx_arg); pimpl->flags &= ~PIPE_FLAG_UNBLOCK_WRITER; SetLastError (ERROR_BUSY); result = -1; goto leave; } - + /* Write to our buffer. */ if (pimpl->buffer_len == pimpl->buffer_size) { /* Buffer is full. */ HANDLE space_available = pimpl->space_available; LeaveCriticalSection (&pimpl->critsect); log_debug ("GPG_Write (ctx=%i): waiting: space_available\n", opnctx_arg); WaitForSingleObject (space_available, INFINITE); log_debug ("GPG_Write (ctx=%i): resuming: space_available\n", opnctx_arg); EnterCriticalSection (&pimpl->critsect); goto retry; } - + src = buffer; dst = pimpl->buffer + pimpl->buffer_len; while (count > 0 && pimpl->buffer_len < pimpl->buffer_size) { *dst++ = *src++; count--; pimpl->buffer_len++; nwritten++; } result = nwritten; - + if (!SetEvent (pimpl->data_available)) log_debug ("GPG_Write (ctx=%i): warning: SetEvent(data_available) " "failed: rc=%d\n", opnctx_arg, (int)GetLastError ()); } - + log_debug ("GPG_Write (ctx=%i): success: result=%d\n", opnctx_arg, result); leave: pipeimpl_unref (pimpl); logimpl_unref (limpl); return result; } DWORD GPG_Seek (DWORD opnctx_arg, long amount, WORD type) { SetLastError (ERROR_SEEK_ON_DEVICE); return -1; /* Error. */ } /* opnctx_table_s is locked on entering and on exit. */ static BOOL make_pipe (opnctx_t ctx, LONG rvid) { DWORD ctx_arg = OPNCTX_TO_IDX (ctx); opnctx_t peerctx = NULL; int idx; pipeimpl_t pimpl; log_debug (" make_pipe (ctx=%i, rvid=%08lx)\n", ctx_arg, rvid); if (ctx->pipeimpl) { log_debug (" make_pipe (ctx=%i): error: already assigned\n", ctx_arg); SetLastError (ERROR_ALREADY_ASSIGNED); return FALSE; } /* GnuPG and other programs don't use the safe ASSIGN_RVID call because they guarantee that the context exists during the whole time the child process runs. GPGME is more asynchronous and relies on ASSIGN_RVID monitors. So, first check for open contexts, then check for monitors. */ for (idx = 0; idx < opnctx_table_size; idx++) if (opnctx_table[idx].inuse && opnctx_table[idx].rvid == rvid) { peerctx = &opnctx_table[idx]; break; } if (peerctx) { /* This is the GnuPG etc case, where the context is still open. It may also cover the GPGME case if GPGME is still using its own end of the pipe at the time of this call. */ if (peerctx == ctx) { log_debug (" make_pipe (ctx=%i): error: target and source identical\n", ctx_arg); SetLastError (ERROR_INVALID_TARGET_HANDLE); return FALSE; } if ((ctx->access_code & GENERIC_READ)) { /* Check that the peer is a write end. */ if (!(peerctx->access_code & GENERIC_WRITE)) { log_debug (" make_pipe (ctx=%i): error: peer is not writer\n", ctx_arg); SetLastError (ERROR_INVALID_ACCESS); return FALSE; } } else if ((ctx->access_code & GENERIC_WRITE)) { /* Check that the peer is a read end. */ if (!(peerctx->access_code & GENERIC_READ)) { log_debug (" make_pipe (ctx=%i): error: peer is not reader\n", ctx_arg); SetLastError (ERROR_INVALID_ACCESS); return FALSE; } } else { log_debug (" make_pipe (ctx=%i): error: invalid access\n", ctx_arg); SetLastError (ERROR_INVALID_ACCESS); return FALSE; } - + /* Make sure the peer has a pipe implementation to be shared. If not yet, create one. */ pimpl = assert_pipeimpl (peerctx); if (! pimpl) return FALSE; } else { /* This is the case where ASSIGN_RVID was called to create a monitor, and the pipe is already closed at the parent side. For example GPGME verify detached plain text, where GPG calls MAKE_PIPE very late. */ - + for (idx = 0; idx < monitor_table_size; idx++) if (monitor_table[idx].inuse && monitor_table[idx].pipeimpl->monitor_rvid == rvid) { pimpl = monitor_table[idx].pipeimpl; break; } if (idx == monitor_table_size) { log_debug (" make_pipe (ctx=%i): error: not found\n", ctx_arg); SetLastError (ERROR_NOT_FOUND); return FALSE; } if (ctx->access_code & GENERIC_READ) { /* Check that the peer is a write end. */ if (!(pimpl->monitor_access & GENERIC_READ)) { log_debug (" make_pipe (ctx=%i): error: monitor is not reader\n", ctx_arg); SetLastError (ERROR_INVALID_ACCESS); return FALSE; } } else if ((ctx->access_code & GENERIC_WRITE)) { /* Check that the peer is a read end. */ if (!(pimpl->monitor_access & GENERIC_WRITE)) { log_debug (" make_pipe (ctx=%i): error: monitor is not writer\n", ctx_arg); SetLastError (ERROR_INVALID_ACCESS); return FALSE; } } else { log_debug (" make_pipe (ctx=%i): error: invalid access\n", ctx_arg); SetLastError (ERROR_INVALID_ACCESS); return FALSE; } } EnterCriticalSection (&pimpl->critsect); pimpl->refcnt++; if (pimpl->monitor_proc != INVALID_HANDLE_VALUE) { /* If there is a monitor to the pipe, then it's now about time to ask it to go away. */ log_debug (" make_pipe (ctx=%i): waking up monitor for pipe 0x%p\n", ctx_arg, pimpl); pimpl->flags |= PIPE_FLAG_HALT_MONITOR; if (pimpl->monitor_access & GENERIC_READ) SetEvent (pimpl->data_available); else SetEvent (pimpl->space_available); } LeaveCriticalSection (&pimpl->critsect); ctx->pipeimpl = pimpl; if (peerctx) { log_debug (" make_pipe (ctx=%i): success: combined with peer ctx=%i " "(pipe 0x%p)\n", ctx_arg, OPNCTX_TO_IDX (peerctx), pimpl); } else { log_debug (" make_pipe (ctx=%i): success: combined with " "pipe 0x%p\n", ctx_arg, OPNCTX_TO_IDX (peerctx), pimpl); } return TRUE; } /* opnctx_table_s is locked on entering and on exit. */ static BOOL unblock_call (opnctx_t ctx) { /* If there is no pipe object, no thread can be blocked. */ if (!ctx->pipeimpl) return TRUE; EnterCriticalSection (&ctx->pipeimpl->critsect); if (ctx->access_code & GENERIC_READ) { ctx->pipeimpl->flags |= PIPE_FLAG_UNBLOCK_READER; SetEvent (ctx->pipeimpl->data_available); } else if (ctx->access_code & GENERIC_WRITE) { ctx->pipeimpl->flags |= PIPE_FLAG_UNBLOCK_WRITER; SetEvent (ctx->pipeimpl->space_available); } LeaveCriticalSection (&ctx->pipeimpl->critsect); return TRUE; } -static DWORD CALLBACK +static DWORD CALLBACK monitor_main (void *arg) { pipeimpl_t pimpl = (pipeimpl_t) arg; HANDLE handles[2]; int idx; log_debug ("starting monitor (pimpl=0x%p)\n", pimpl); - + EnterCriticalSection (&pimpl->critsect); /* Putting proc first in the array is convenient, as this is a hard break-out condition (and thus takes precedence in WFMO). The reader/writer event is a soft condition, which also requires a flag to be set. */ handles[0] = pimpl->monitor_proc; if (pimpl->monitor_access & GENERIC_READ) handles[1] = pimpl->data_available; else handles[1] = pimpl->space_available; retry: /* First check if the peer has not gone away. If it has, we are done. */ if (pimpl->flags & PIPE_FLAG_HALT_MONITOR) { log_debug ("monitor (pimpl=0x%p): done: monitored process taking over\n", pimpl); - } + } else { DWORD res; LeaveCriticalSection (&pimpl->critsect); log_debug ("monitor (pimpl=0x%p): waiting\n", pimpl); res = WaitForMultipleObjects (2, handles, FALSE, INFINITE); log_debug ("monitor (pimpl=0x%p): resuming\n", pimpl); EnterCriticalSection (&pimpl->critsect); if (res == WAIT_FAILED) { log_debug ("monitor (pimpl=0x%p): WFMO failed: %i\n", pimpl, GetLastError ()); } else if (res == WAIT_OBJECT_0) { log_debug ("monitor (pimpl=0x%p): done: monitored process died\n", pimpl); - } + } else if (res == WAIT_OBJECT_0 + 1) goto retry; else { log_debug ("monitor (pimpl=0x%p): unexpected result of WFMO: %i\n", pimpl, res); } } log_debug ("ending monitor (pimpl=0x%p)\n", pimpl); /* Remove the monitor from the monitor table. */ LeaveCriticalSection (&pimpl->critsect); EnterCriticalSection (&opnctx_table_cs); for (idx = 0; idx < monitor_table_size; idx++) if (monitor_table[idx].inuse && monitor_table[idx].pipeimpl == pimpl) { monitor_table[idx].pipeimpl = NULL; monitor_table[idx].inuse = 0; break; } if (idx == monitor_table_size) log_debug ("can not find monitor in table (pimpl=0x%p)\n", pimpl); LeaveCriticalSection (&opnctx_table_cs); EnterCriticalSection (&pimpl->critsect); /* Now do the rest of the cleanup. */ CloseHandle (pimpl->monitor_proc); pimpl->monitor_proc = INVALID_HANDLE_VALUE; pimpl->monitor_access = 0; pimpl->monitor_rvid = 0; pimpl->flags &= ~PIPE_FLAG_HALT_MONITOR; pipeimpl_unref (pimpl); return 0; } /* opnctx_table_s is locked on entering and on exit. */ static BOOL assign_rvid (opnctx_t ctx, DWORD rvid, DWORD pid) { DWORD ctx_arg = OPNCTX_TO_IDX (ctx); int idx; opnctx_t peerctx; HANDLE monitor_hnd; HANDLE proc; pipeimpl_t pimpl; monitor_t monitor; log_debug (" assign_rvid (ctx=%i, rvid=%08lx, pid=%08lx)\n", ctx_arg, rvid, pid); for (idx = 0; idx < opnctx_table_size; idx++) if (opnctx_table[idx].inuse && opnctx_table[idx].rvid == rvid) { peerctx = &opnctx_table[idx]; break; } if (! peerctx) { log_debug (" assign_rvid (ctx=%i): error: not found\n", ctx_arg); SetLastError (ERROR_NOT_FOUND); return FALSE; } - + if (peerctx->pipeimpl && peerctx->pipeimpl->monitor_proc != INVALID_HANDLE_VALUE) { log_debug (" assign_rvid (ctx=%i): error: rvid already assigned\n", ctx_arg); SetLastError (ERROR_ALREADY_ASSIGNED); return FALSE; } proc = OpenProcess (0, FALSE, pid); if (proc == NULL) { log_debug (" assign_rvid (ctx=%i): error: process not found\n", ctx_arg); return FALSE; } /* Acquire a reference to the pipe. We don't want accesss to be checked. */ pimpl = assert_pipeimpl (peerctx); if (! pimpl) { CloseHandle (proc); return FALSE; } monitor = allocate_monitor (); if (!monitor) { log_debug (" assign_rvid (ctx=%i): error: could not allocate monitor\n", ctx_arg); CloseHandle (proc); return FALSE; } monitor->pipeimpl = pimpl; EnterCriticalSection (&pimpl->critsect); pimpl->refcnt++; /* The monitor shadows the peer, so it takes its access. Our access is the opposite of that of the peer. */ pimpl->monitor_proc = proc; if (peerctx->access_code == GENERIC_READ) pimpl->monitor_access = GENERIC_WRITE; else pimpl->monitor_access = GENERIC_READ; pimpl->monitor_rvid = rvid; monitor_hnd = CreateThread (NULL, 0, monitor_main, pimpl, 0, NULL); if (monitor_hnd == INVALID_HANDLE_VALUE) { pimpl->monitor_access = 0; pimpl->monitor_proc = INVALID_HANDLE_VALUE; LeaveCriticalSection (&pimpl->critsect); monitor->pipeimpl = NULL; monitor->inuse = 0; CloseHandle (proc); log_debug (" assign_rvid (ctx=%i): error: can not create monitor\n", ctx_arg); return FALSE; } CloseHandle (monitor_hnd); /* Consume the pimpl reference. */ LeaveCriticalSection (&pimpl->critsect); return TRUE; } BOOL GPG_IOControl (DWORD opnctx_arg, DWORD code, void *inbuf, DWORD inbuflen, void *outbuf, DWORD outbuflen, DWORD *actualoutlen) { opnctx_t opnctx; BOOL result = FALSE; LONG rvid; LONG pid; log_debug ("GPG_IOControl (ctx=%i, %08x)\n", opnctx_arg, code); EnterCriticalSection (&opnctx_table_cs); opnctx = verify_opnctx (opnctx_arg); if (!opnctx) { - log_debug ("GPG_IOControl (ctx=%i): error: could not access context\n", + log_debug ("GPG_IOControl (ctx=%i): error: could not access context\n", opnctx_arg); goto leave; } if (opnctx->is_log) { log_debug ("GPG_IOControl (ctx=%i): error: invalid code %u" " for log device\n", opnctx_arg, (unsigned int)code); SetLastError (ERROR_INVALID_PARAMETER); goto leave; } switch (code) { case GPGCEDEV_IOCTL_GET_RVID: log_debug ("GPG_IOControl (ctx=%i): code: GET_RVID\n", opnctx_arg); if (inbuf || inbuflen || !outbuf || outbuflen < sizeof (LONG)) { - log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", + log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", opnctx_arg); SetLastError (ERROR_INVALID_PARAMETER); goto leave; } - if (! opnctx->rvid) + if (! opnctx->rvid) opnctx->rvid = create_rendezvous_id (); log_debug ("GPG_IOControl (ctx=%i): returning rvid: %08lx\n", opnctx_arg, opnctx->rvid); memcpy (outbuf, &opnctx->rvid, sizeof (LONG)); if (actualoutlen) *actualoutlen = sizeof (LONG); result = TRUE; break; case GPGCEDEV_IOCTL_MAKE_PIPE: log_debug ("GPG_IOControl (ctx=%i): code: MAKE_PIPE\n", opnctx_arg); - if (!inbuf || inbuflen < sizeof (LONG) + if (!inbuf || inbuflen < sizeof (LONG) || outbuf || outbuflen || actualoutlen) { - log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", + log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", opnctx_arg); SetLastError (ERROR_INVALID_PARAMETER); goto leave; } memcpy (&rvid, inbuf, sizeof (LONG)); - log_debug ("GPG_IOControl (ctx=%i): make pipe for rvid: %08lx\n", + log_debug ("GPG_IOControl (ctx=%i): make pipe for rvid: %08lx\n", opnctx_arg, rvid); if (make_pipe (opnctx, rvid)) result = TRUE; break; case GPGCEDEV_IOCTL_UNBLOCK: log_debug ("GPG_IOControl (ctx=%i): code: UNBLOCK\n", opnctx_arg); if (inbuf || inbuflen || outbuf || outbuflen || actualoutlen) { - log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", + log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", opnctx_arg); SetLastError (ERROR_INVALID_PARAMETER); goto leave; } - + if (unblock_call (opnctx)) result = TRUE; break; case GPGCEDEV_IOCTL_ASSIGN_RVID: log_debug ("GPG_IOControl (ctx=%i): code: ASSIGN_RVID\n", opnctx_arg); if (!inbuf || inbuflen < 2 * sizeof (DWORD) || outbuf || outbuflen || actualoutlen) { - log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", + log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", opnctx_arg); SetLastError (ERROR_INVALID_PARAMETER); goto leave; } memcpy (&rvid, inbuf, sizeof (DWORD)); memcpy (&pid, ((char *) inbuf) + sizeof (DWORD), sizeof (DWORD)); - log_debug ("GPG_IOControl (ctx=%i): assign rvid %08lx to pid %08lx\n", + log_debug ("GPG_IOControl (ctx=%i): assign rvid %08lx to pid %08lx\n", opnctx_arg, rvid, pid); if (assign_rvid (opnctx, rvid, pid)) result = TRUE; break; case IOCTL_PSL_NOTIFY: /* This notification is received if the application's main thread exits and the application has other threads running and the application has open handles for this device. What we do is to unblock them all simialr to an explicit unblock call. */ log_debug ("GPG_IOControl (ctx=%i): code: NOTIFY\n", opnctx_arg); if (unblock_call (opnctx)) result = TRUE; break; default: log_debug ("GPG_IOControl (ctx=%i): code: (unknown)\n", opnctx_arg); SetLastError (ERROR_INVALID_PARAMETER); break; } log_debug ("GPG_IOControl (ctx=%i): success: result=%d\n", opnctx_arg, result); leave: LeaveCriticalSection (&opnctx_table_cs); return result; } void GPG_PowerUp (DWORD devctx) { log_debug ("GPG_PowerUp (devctx=%i)\n", devctx); } void GPG_PowerDown (DWORD devctx) { log_debug ("GPG_PowerDown (devctx=%i)\n", devctx); } /* Entry point called by the DLL loader. */ int WINAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved) { (void)reserved; switch (reason) { case DLL_PROCESS_ATTACH: InitializeCriticalSection (&opnctx_table_cs); InitializeCriticalSection (&logcontrol.lock); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: DeleteCriticalSection (&opnctx_table_cs); break; default: break; } - + return TRUE; } diff --git a/src/gpgcedev.def b/src/gpgcedev.def index 387e3a6..bc52d3a 100644 --- a/src/gpgcedev.def +++ b/src/gpgcedev.def @@ -1,33 +1,34 @@ ; gpgcedev.def - List of symbols to export for gpgcedev. ; Copyright (C) 2010 Free Software Foundation, Inc. ; ; This file is part of Assuan. ; ; Assuan 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 3 of ; the License, or (at your option) any later version. ; ; Assuan 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-3.0+ EXPORTS GPG_Init GPG_Deinit GPG_Open GPG_Close GPG_Read GPG_Write GPG_Seek GPG_IOControl GPG_PowerUp GPG_PowerDown ; END diff --git a/src/gpgcemgr.c b/src/gpgcemgr.c index 9dafbc0..5b4f56e 100644 --- a/src/gpgcemgr.c +++ b/src/gpgcemgr.c @@ -1,607 +1,608 @@ /* gpgcempr.c - Manager for GPG CE devices - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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 3 of - the License, or (at your option) any later version. - - Assuan 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 . + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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 3 of + * the License, or (at your option) any later version. + * + * Assuan 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-3.0+ */ #define _WIN32_WCE 0x0500 #include #include #define PGM "gpgcemgr" #define GPGCEDEV_KEY_NAME L"Drivers\\GnuPG_Device" #define GPGCEDEV_KEY_NAME2 L"Drivers\\GnuPG_Log" #define GPGCEDEV_DLL_NAME L"gpgcedev.dll" #define GPGCEDEV_PREFIX L"GPG" static char * wchar_to_utf8 (const wchar_t *string) { int n; size_t length = wcslen (string); char *result; n = WideCharToMultiByte (CP_UTF8, 0, string, length, NULL, 0, NULL, NULL); if (n < 0 || (n+1) <= 0) abort (); result = malloc (n+1); if (!result) abort (); n = WideCharToMultiByte (CP_ACP, 0, string, length, result, n, NULL, NULL); if (n < 0) abort (); - + result[n] = 0; return result; } static wchar_t * utf8_to_wchar (const char *string) { int n; size_t nbytes; wchar_t *result; if (!string) abort (); n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0); if (n < 0) abort (); nbytes = (size_t)(n+1) * sizeof(*result); - if (nbytes / sizeof(*result) != (n+1)) + if (nbytes / sizeof(*result) != (n+1)) abort (); result = malloc (nbytes); if (!result) abort (); n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n); if (n < 0) abort (); return result; } static int install (void) { HKEY handle; DWORD disp, dw; int rc; - + if ((rc=RegCreateKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME, 0, NULL, 0, KEY_WRITE, NULL, &handle, &disp))) { fprintf (stderr, PGM": error creating registry key 1: rc=%d\n", rc); return 1; } - RegSetValueEx (handle, L"dll", 0, REG_SZ, + RegSetValueEx (handle, L"dll", 0, REG_SZ, (void*)GPGCEDEV_DLL_NAME, sizeof (GPGCEDEV_DLL_NAME)); RegSetValueEx (handle, L"prefix", 0, REG_SZ, (void*)GPGCEDEV_PREFIX, sizeof (GPGCEDEV_PREFIX)); dw = 1; RegSetValueEx (handle, L"Index", 0, REG_DWORD, (void*)&dw, sizeof dw); - + RegCloseKey (handle); fprintf (stderr, PGM": registry key 1 created\n"); if ((rc=RegCreateKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME2, 0, NULL, 0, KEY_WRITE, NULL, &handle, &disp))) { fprintf (stderr, PGM": error creating registry key 2: rc=%d\n", rc); return 1; } - RegSetValueEx (handle, L"dll", 0, REG_SZ, + RegSetValueEx (handle, L"dll", 0, REG_SZ, (void*)GPGCEDEV_DLL_NAME, sizeof (GPGCEDEV_DLL_NAME)); RegSetValueEx (handle, L"prefix", 0, REG_SZ, (void*)GPGCEDEV_PREFIX, sizeof (GPGCEDEV_PREFIX)); dw = 2; RegSetValueEx (handle, L"Index", 0, REG_DWORD, (void*)&dw, sizeof dw); - + RegCloseKey (handle); fprintf (stderr, PGM": registry key 2 created\n"); return 0; } static int deinstall (wchar_t *name) { int result = 0; HANDLE shd; DEVMGR_DEVICE_INFORMATION dinfo; memset (&dinfo, 0, sizeof dinfo); dinfo.dwSize = sizeof dinfo; shd = FindFirstDevice (DeviceSearchByLegacyName, name, &dinfo); if (shd == INVALID_HANDLE_VALUE) { if (GetLastError () == 18) fprintf (stderr, PGM": device not found\n"); else { - fprintf (stderr, PGM": FindFirstDevice failed: rc=%d\n", + fprintf (stderr, PGM": FindFirstDevice failed: rc=%d\n", (int)GetLastError ()); result = 1; } } else { fprintf (stderr, PGM": ActivateDevice handle is %p\n", dinfo.hDevice); if (dinfo.hDevice && dinfo.hDevice != INVALID_HANDLE_VALUE) { if (!DeactivateDevice (dinfo.hDevice)) { fprintf (stderr, PGM": DeactivateDevice failed: rc=%d\n", (int)GetLastError ()); result = 1; } else fprintf (stderr, PGM": DeactivateDevice succeeded\n"); } FindClose (shd); } return result; } static int enable_debug (int yes) { HKEY handle; DWORD disp; int rc; - + if ((rc=RegCreateKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME, 0, NULL, 0, KEY_WRITE, NULL, &handle, &disp))) { fprintf (stderr, PGM": error creating debug registry key: rc=%d\n", rc); return 1; } - - RegSetValueEx (handle, L"debugDriver", 0, REG_SZ, + + RegSetValueEx (handle, L"debugDriver", 0, REG_SZ, (void*)(yes? L"1":L"0"), sizeof L"0"); RegCloseKey (handle); return 0; } static int enable_log (int yes) { HKEY handle; DWORD disp; int rc; - + if ((rc=RegCreateKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME2, 0, NULL, 0, KEY_WRITE, NULL, &handle, &disp))) { fprintf (stderr, PGM": error creating debug registry key: rc=%d\n", rc); return 1; } - - RegSetValueEx (handle, L"enableLog", 0, REG_SZ, + + RegSetValueEx (handle, L"enableLog", 0, REG_SZ, (void*)(yes? L"1":L"0"), sizeof L"0"); RegCloseKey (handle); return 0; } /* Kudos to Scott Seligman for his work on the reverse engineering. */ struct htc_sensor_s { SHORT tilt_x; // From -1000 to 1000 (about), 0 is flat SHORT tilt_y; // From -1000 to 1000 (about), 0 is flat SHORT tilt_z; // From -1000 to 1000 (about) DWORD angle_x; // 0 .. 359 DWORD angle_y; // From 0 to 359 DWORD orientation; // 0.. 5? DWORD unknown; // Handle? }; typedef struct htc_sensor_s *htc_sensor_t; static HANDLE (WINAPI *HTCSensorOpen) (DWORD); static void (WINAPI *HTCSensorClose) (HANDLE); static DWORD (WINAPI *HTCSensorGetDataOutput) (HANDLE, htc_sensor_t); static int load_sensor_api (void) { static HMODULE dll_hd; if (dll_hd) return 0; dll_hd = LoadLibrary (L"HTCSensorSDK.dll"); if (!dll_hd) { fprintf (stderr, PGM": error loading sensor DLL: rc=%d\n", (int)GetLastError ()); return 1; } HTCSensorOpen = (void*)GetProcAddress (dll_hd, L"HTCSensorOpen"); if (HTCSensorOpen) HTCSensorClose = (void*)GetProcAddress (dll_hd, L"HTCSensorClose"); if (HTCSensorClose) HTCSensorGetDataOutput = (void*) GetProcAddress (dll_hd, L"HTCSensorGetDataOutput"); if (!HTCSensorGetDataOutput) { fprintf (stderr, PGM": error loading function from sensor DLL: rc=%d\n", (int)GetLastError ()); CloseHandle (dll_hd); return 1; } return 0; } -static int +static int gravity (void) { int rc; HANDLE sensor; struct htc_sensor_s lastdata; struct htc_sensor_s data; rc = load_sensor_api (); if (rc) return rc; sensor = HTCSensorOpen (1 /* tilt sensor */); if (!sensor || sensor == INVALID_HANDLE_VALUE) { fprintf (stderr, PGM": error opening gravity sensor: rc=%d\n", (int)GetLastError ()); HTCSensorClose (sensor); return 1; } memset (&lastdata, 0, sizeof lastdata); while (HTCSensorGetDataOutput (sensor, &data)) { if (lastdata.tilt_x/10 != data.tilt_x/10 || lastdata.tilt_y/10 != data.tilt_y/10 || lastdata.tilt_z/10 != data.tilt_z/10 || lastdata.angle_x/5 != data.angle_x/5 || lastdata.angle_y/5 != data.angle_y/5 || lastdata.orientation != data.orientation) { lastdata = data; printf ("tilt: x=%-5d y=%-5d z=%-5d " "angle: x=%-3d y=%-3d " "ori: %d\n", (int)data.tilt_x, (int)data.tilt_y, (int)data.tilt_z, (int)data.angle_x, (int)data.angle_y, (int)data.orientation); } Sleep (200); } fprintf (stderr, PGM": reading sensor data failed: rc=%d\n", (int)GetLastError ()); HTCSensorClose (sensor); return 0; } /* No GPD1 device on the HTC Touch Pro 2. */ # if 0 static int gps_raw (void) { HANDLE hd; char buffer[1000]; unsigned long nread; int count; hd = CreateFile (L"GPD1:", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hd == INVALID_HANDLE_VALUE) { fprintf (stderr, PGM": can't open `GPD1': rc=%d\n", (int)GetLastError ()); return 1; } fprintf (stderr, PGM": GPS device successfully opened\n"); for (count=0; count < 100; count++) { if (!ReadFile (hd, buffer, sizeof buffer-1, &nread, NULL)) { fprintf (stderr, PGM": error reading `GPD1': rc=%d\n", (int)GetLastError ()); CloseHandle (hd); return 1; } buffer[nread-1] = 0; fputs (buffer, stdout); } CloseHandle (hd); return 0; } #endif /* Untested samples for CE6. */ #if 0 static int gps (void) { HANDLE hd; GPS_POSITION pos; hd = GPSOpenDevice (NULL, NULL, NULL, 0); if (hd == INVALID_HANDLE_VALUE) { fprintf (stderr, PGM": GPSOpenDevice failed: rc=%d\n", (int)GetLastError ()); return 1; } fprintf (stderr, PGM": GPS device successfully opened\n"); if (GPSGetPosition (hd, &pos, 2000, 0)) { fprintf (stderr, PGM": GPSGetPosition failed: rc=%d\n", (int)GetLastError ()); GPSCloseDevice (hd); return 1; } GPSCloseDevice (hd); return 0; } #endif static void set_show_registry (const wchar_t *key, const wchar_t *name, const char *value) { HKEY handle; DWORD disp, nbytes, n1, type; int rc; - + if ((rc=RegCreateKeyEx (HKEY_LOCAL_MACHINE, key, 0, NULL, 0, KEY_WRITE, NULL, &handle, &disp))) { fprintf (stderr, PGM": error creating registry key: rc=%d\n", rc); return; } if (value && !stricmp (value, "none")) { if ((rc=RegDeleteValue (handle, name))) fprintf (stderr, PGM": error deleting registry value: rc=%d\n", rc); } else if (value) { wchar_t *tmp = utf8_to_wchar (value); - if ((rc=RegSetValueEx (handle, name, 0, REG_SZ, + if ((rc=RegSetValueEx (handle, name, 0, REG_SZ, (void*)tmp, wcslen (tmp)*sizeof(wchar_t)))) fprintf (stderr, PGM": error setting registry value: rc=%d\n", rc); free (tmp); } else { nbytes = 2; if ((rc=RegQueryValueEx (handle, name, 0, NULL, NULL, &nbytes))) { if (rc == ERROR_FILE_NOT_FOUND) - fprintf (stderr, PGM": registry value not set\n"); + fprintf (stderr, PGM": registry value not set\n"); else - fprintf (stderr, PGM": error reading registry value: rc=%d\n", rc); + fprintf (stderr, PGM": error reading registry value: rc=%d\n", rc); } else { char *result = malloc ((n1=nbytes+2)); if (!result) - fprintf (stderr, PGM": malloc failed: rc=%d\n", + fprintf (stderr, PGM": malloc failed: rc=%d\n", (int)GetLastError ()); else if ((rc=RegQueryValueEx (handle, name, 0, &type, (void*)result, &n1))) { fprintf (stderr, PGM": error reading registry value (2): " "rc=%d\n", rc); free (result); } else { result[nbytes] = 0; /* Make sure it is a string. */ - result[nbytes+1] = 0; + result[nbytes+1] = 0; if (type == REG_SZ) { wchar_t *tmp = (void*)result; result = wchar_to_utf8 (tmp); free (tmp); printf ("%s\n", result); } else fprintf (stderr, PGM": registry value is not a string\n"); free (result); } } } - + RegCloseKey (handle); } int main (int argc, char **argv) { int result = 0; if (argc > 1 && !strcmp (argv[1], "--register")) result = install (); else if (argc > 1 && !strcmp (argv[1], "--deactivate")) { if (deinstall (L"GPG1:")) result = 1; if (deinstall (L"GPG2:")) result = 1; } else if (argc > 1 && !strcmp (argv[1], "--activate")) { HANDLE hd; /* This is mainly for testing. The activation is usually done right before the device is opened. */ if (ActivateDevice (GPGCEDEV_KEY_NAME, 0) == INVALID_HANDLE_VALUE) { fprintf (stderr, PGM": ActivateDevice 1 failed: rc=%d\n", (int)GetLastError ()); result = 1; } else if (ActivateDevice (GPGCEDEV_KEY_NAME2, 0) == INVALID_HANDLE_VALUE) { fprintf (stderr, PGM": ActivateDevice 2 failed: rc=%d\n", (int)GetLastError ()); result = 1; } else { fprintf (stderr, PGM": devices activated\n"); hd = CreateFile (L"GPG1:", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hd == INVALID_HANDLE_VALUE) { fprintf (stderr, PGM": opening `GPG1:' failed: rc=%d\n", (int)GetLastError ()); result = 1; } else { fprintf (stderr, PGM": device `GPG1:' seems to work\n"); CloseHandle (hd); } hd = CreateFile (L"GPG2:", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hd == INVALID_HANDLE_VALUE) { fprintf (stderr, PGM": opening `GPG2:' failed: rc=%d\n", (int)GetLastError ()); result = 1; } else { fprintf (stderr, PGM": device `GPG2:' seems to work\n"); CloseHandle (hd); } - + } } else if (argc > 1 && !strcmp (argv[1], "--gravity")) result = gravity (); /* else if (argc > 1 && !strcmp (argv[1], "--gps")) */ /* result = gps (); */ else if (argc > 1 && !strcmp (argv[1], "--log")) { HANDLE hd; hd = CreateFile (L"GPG2:", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hd == INVALID_HANDLE_VALUE) { fprintf (stderr, PGM": opening `GPG2:' failed: rc=%d\n", (int)GetLastError ()); result = 1; } else { char marktwain[] = "I have never let my schooling interfere" " with my education.\n"; DWORD nwritten; int i; - for (i=0; i < 3; i++) + for (i=0; i < 3; i++) { if (!WriteFile (hd, marktwain, strlen (marktwain), &nwritten, NULL)) { fprintf (stderr, PGM": writing `GPG2:' failed: rc=%d\n", (int)GetLastError ()); result = 1; } Sleep (200); } CloseHandle (hd); } } else if (argc > 1 && !strcmp (argv[1], "--enable-debug")) result = enable_debug (1); else if (argc > 1 && !strcmp (argv[1], "--disable-debug")) result = enable_debug (0); else if (argc > 1 && !strcmp (argv[1], "--enable-log")) result = enable_log (1); else if (argc > 1 && !strcmp (argv[1], "--disable-log")) result = enable_log (0); else if (argc > 1 && !strcmp (argv[1], "--gpgme-log")) - set_show_registry (L"Software\\GNU\\gpgme", L"debug", - argc > 2? argv[2] : NULL); + set_show_registry (L"Software\\GNU\\gpgme", L"debug", + argc > 2? argv[2] : NULL); else if (argc > 1 && !strcmp (argv[1], "--gnupg-log")) - set_show_registry (L"Software\\GNU\\GnuPG", L"DefaultLogFile", + set_show_registry (L"Software\\GNU\\GnuPG", L"DefaultLogFile", argc > 2? argv[2] : NULL); else { fprintf (stderr, "usage: " PGM " COMMAND\n" "Commands are:\n" " --register Register the GPGCEDEV device\n" " --deactivate Deactivate the GPGCEDEV device\n" " --activate Activate the GPGCEDEV devive\n" " --enable-debug Enable debugging of GPGCEDEV device\n" " --disable-debug Disable debugging of GPGCEDEV device\n" " --gravity Show output of the gravity sensor\n" " --enable-log Enable logging via \"GPG2:\"\n" " --disable-log Disable logging via \"GPG2:\"\n" " --log Write a test string to \"GPG2:\"\n" " --gpgme-log [ARG] Show or set GPGME log output\n" " --gnupg-log [ARG] Show or set GnuPG default log file\n" " (No ARG shows, \"none\" disables)\n" ); result = 1; } fflush (stdout); fflush (stderr); Sleep (1000); return result; } diff --git a/src/isascii.c b/src/isascii.c index 77ed970..3160390 100644 --- a/src/isascii.c +++ b/src/isascii.c @@ -1,28 +1,29 @@ /* isascii.c - Replacement for isascii. * Copyright (C) 2002, 2005 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan 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. * * Assuan 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 int isascii (int c) { return (((c) & ~0x7f) == 0); } diff --git a/src/libassuan-config.in b/src/libassuan-config.in index dd25a4c..04177f0 100644 --- a/src/libassuan-config.in +++ b/src/libassuan-config.in @@ -1,133 +1,134 @@ #!/bin/sh # Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc. # # This file is free software; as a special exception the author gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# SPDX-License-Identifier: FSFULLR # Configure libgpg-error. gpg_error_cflags="@GPG_ERROR_CFLAGS@" gpg_error_libs="@GPG_ERROR_LIBS@" PGM=libassuan-config lib="@LIBASSUAN_CONFIG_LIB@" extralibs="@LIBASSUAN_CONFIG_EXTRA_LIBS@ $gpg_error_libs" cflags="@LIBASSUAN_CONFIG_CFLAGS@ $gpg_error_cflags" api_version="@LIBASSUAN_CONFIG_API_VERSION@" my_host="@LIBASSUAN_CONFIG_HOST@" prefix=@prefix@ exec_prefix=@exec_prefix@ includes="" libdirs="" exec_prefix_set=no echo_libs=no echo_cflags=no echo_prefix=no echo_exec_prefix=no echo_host=no usage() { cat <&2 fi while test $# -gt 0; do case "$1" in -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) optarg= ;; esac case $1 in --prefix=*) # Dummy ;; --prefix) echo_prefix=yes ;; --exec-prefix=*) # Dummy ;; --exec-prefix) echo_exec_prefix=yes ;; --version) echo "@VERSION@" exit 0 ;; --api-version) echo_api_version=yes ;; --cflags) echo_cflags=yes ;; --libs) echo_libs=yes ;; --host) echo_host=yes ;; *) usage 1 1>&2 ;; esac shift done if test "$echo_prefix" = "yes"; then echo $prefix fi if test "$echo_exec_prefix" = "yes"; then echo $exec_prefix fi if test "$echo_api_version" = "yes"; then echo $api_version fi if test "$echo_host" = "yes"; then echo "$my_host" fi if test "$echo_cflags" = "yes"; then if test "@includedir@" != "/usr/include" ; then includes="-I@includedir@" for i in $cflags ; do if test "$i" = "-I@includedir@" ; then includes="" fi done fi echo $includes $cflags fi if test "$echo_libs" = "yes"; then if test "@libdir@" != "/usr/lib" ; then libdirs="-L@libdir@" for i in $lib $extralibs ; do if test "$i" = "-L@libdir@" ; then libdirs="" fi done fi echo $libdirs $lib $extralibs fi diff --git a/src/libassuan.def b/src/libassuan.def index c320151..7e32a3d 100644 --- a/src/libassuan.def +++ b/src/libassuan.def @@ -1,120 +1,120 @@ ; assuan.def - List of symbols to export. ; Copyright (C) 2005, 2009 g10 Code GmbH ; ; This file is part of ASSUAN. ; ; ASSUAN 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. ; ; ASSUAN 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 +; License along with this program; if not, see . +; SPDX-License-Identifier: LGPL-2.1+ EXPORTS assuan_accept @1 assuan_begin_confidential @2 assuan_close_input_fd @3 assuan_close_output_fd @4 assuan_command_parse_fd @5 assuan_ctx_set_system_hooks @6 assuan_end_confidential @7 assuan_get_active_fds @8 assuan_get_assuan_log_prefix @9 assuan_get_command_name @10 assuan_get_data_fp @11 assuan_get_flag @12 assuan_get_gpg_err_source @13 assuan_get_input_fd @14 assuan_get_log_cb @15 assuan_get_malloc_hooks @16 assuan_get_output_fd @17 assuan_get_peercred @18 assuan_get_pid @19 assuan_get_pointer @20 assuan_init_pipe_server @21 assuan_init_socket_server @22 assuan_inquire @23 assuan_inquire_ext @24 assuan_new @25 assuan_new_ext @26 assuan_pending_line @27 assuan_pipe_connect @28 assuan_process @29 assuan_process_done @30 assuan_process_next @31 assuan_read_line @32 assuan_receivefd @33 assuan_register_bye_notify @34 assuan_register_cancel_notify @35 assuan_register_command @36 assuan_register_input_notify @37 assuan_register_option_handler @38 assuan_register_output_notify @39 assuan_register_post_cmd_notify @40 assuan_register_reset_notify @41 assuan_release @42 assuan_send_data @43 assuan_sendfd @44 assuan_set_assuan_log_prefix @45 assuan_set_assuan_log_stream @46 assuan_set_error @47 assuan_set_flag @48 assuan_set_gpg_err_source @49 assuan_set_hello_line @50 assuan_set_io_monitor @51 assuan_set_log_cb @52 assuan_set_log_stream @53 assuan_set_malloc_hooks @54 assuan_set_okay_line @55 assuan_set_pointer @56 assuan_set_system_hooks @57 assuan_sock_bind @58 assuan_sock_check_nonce @59 assuan_sock_close @60 assuan_sock_connect @61 assuan_sock_deinit @62 assuan_sock_get_nonce @63 assuan_sock_init @64 assuan_sock_new @65 assuan_socket_connect @66 assuan_transact @67 assuan_write_line @68 assuan_write_status @69 __assuan_close @70 __assuan_pipe @71 __assuan_socketpair @72 __assuan_spawn @73 __assuan_usleep @74 assuan_fdopen @75 assuan_client_read_response @76 assuan_client_parse_response @77 assuan_set_sock_nonce @78 _assuan_w32ce_create_pipe @79 assuan_free @80 _assuan_w32ce_prepare_pipe @81 _assuan_w32ce_finish_pipe @82 __assuan_socket @83 __assuan_connect @84 assuan_register_pre_cmd_notify @85 assuan_socket_connect_fd @86 __assuan_read @87 __assuan_write @88 __assuan_recvmsg @89 __assuan_sendmsg @90 __assuan_waitpid @91 assuan_check_version @92 assuan_sock_set_sockaddr_un @93 assuan_sock_set_flag @94 assuan_sock_get_flag @95 assuan_sock_connect_byname @96 ; END diff --git a/src/libassuan.m4 b/src/libassuan.m4 index 4b196a5..fe45b06 100644 --- a/src/libassuan.m4 +++ b/src/libassuan.m4 @@ -1,150 +1,151 @@ dnl Autoconf macros for libassuan dnl Copyright (C) 2002, 2003, 2011 Free Software Foundation, Inc. dnl dnl This file is free software; as a special exception the author gives dnl unlimited permission to copy and/or distribute it, with or without dnl modifications, as long as this notice is preserved. dnl dnl This file is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY, to the extent permitted by law; without even the dnl implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +dnl SPDX-License-Identifier: FSFULLR dnl dnl Common code used for libassuan detection [internal] dnl Returns ok set to yes or no. dnl AC_DEFUN([_AM_PATH_LIBASSUAN_COMMON], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_ARG_WITH(libassuan-prefix, AC_HELP_STRING([--with-libassuan-prefix=PFX], [prefix where LIBASSUAN is installed (optional)]), libassuan_config_prefix="$withval", libassuan_config_prefix="") if test x$libassuan_config_prefix != x ; then libassuan_config_args="$libassuan_config_args --prefix=$libassuan_config_prefix" if test x${LIBASSUAN_CONFIG+set} != xset ; then LIBASSUAN_CONFIG=$libassuan_config_prefix/bin/libassuan-config fi fi AC_PATH_TOOL(LIBASSUAN_CONFIG, libassuan-config, no) tmp=ifelse([$1], ,1:0.9.2,$1) if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then req_libassuan_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` min_libassuan_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` else req_libassuan_api=0 min_libassuan_version="$tmp" fi AC_MSG_CHECKING(for LIBASSUAN - version >= $min_libassuan_version) ok=no if test "$LIBASSUAN_CONFIG" != "no" \ && test -f "$LIBASSUAN_CONFIG" ; then req_major=`echo $min_libassuan_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` req_minor=`echo $min_libassuan_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` req_micro=`echo $min_libassuan_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` libassuan_config_version=`$LIBASSUAN_CONFIG --version` major=`echo $libassuan_config_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` minor=`echo $libassuan_config_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` micro=`echo $libassuan_config_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'` if test "$major" -gt "$req_major"; then ok=yes - else + else if test "$major" -eq "$req_major"; then if test "$minor" -gt "$req_minor"; then ok=yes else if test "$minor" -eq "$req_minor"; then if test "$micro" -ge "$req_micro"; then ok=yes fi fi fi fi fi fi if test $ok = yes; then AC_MSG_RESULT([yes ($libassuan_config_version)]) else AC_MSG_RESULT(no) fi if test $ok = yes; then if test "$req_libassuan_api" -gt 0 ; then tmp=`$LIBASSUAN_CONFIG --api-version 2>/dev/null || echo 0` if test "$tmp" -gt 0 ; then AC_MSG_CHECKING([LIBASSUAN API version]) if test "$req_libassuan_api" -eq "$tmp" ; then AC_MSG_RESULT(okay) else ok=no AC_MSG_RESULT([does not match. want=$req_libassuan_api got=$tmp.]) fi fi fi fi if test $ok = yes; then if test x"$host" != x ; then libassuan_config_host=`$LIBASSUAN_CONFIG --host 2>/dev/null || echo none` if test x"$libassuan_config_host" != xnone ; then if test x"$libassuan_config_host" != x"$host" ; then AC_MSG_WARN([[ *** *** The config script $LIBASSUAN_CONFIG was *** built for $libassuan_config_host and thus may not match the *** used host $host. *** You may want to use the configure option --with-libassuan-prefix *** to specify a matching config script. ***]]) fi fi fi fi ]) dnl AM_CHECK_LIBASSUAN([MINIMUM-VERSION, dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) dnl Test whether libassuan has at least MINIMUM-VERSION. This is dnl used to test for features only available in newer versions. dnl AC_DEFUN([AM_CHECK_LIBASSUAN], [ _AM_PATH_LIBASSUAN_COMMON($1) if test $ok = yes; then ifelse([$2], , :, [$2]) else ifelse([$3], , :, [$3]) fi ]) dnl AM_PATH_LIBASSUAN([MINIMUM-VERSION, dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) dnl Test for libassuan and define LIBASSUAN_CFLAGS and LIBASSUAN_LIBS dnl AC_DEFUN([AM_PATH_LIBASSUAN], [ _AM_PATH_LIBASSUAN_COMMON($1) if test $ok = yes; then LIBASSUAN_CFLAGS=`$LIBASSUAN_CONFIG $libassuan_config_args --cflags` LIBASSUAN_LIBS=`$LIBASSUAN_CONFIG $libassuan_config_args --libs` ifelse([$2], , :, [$2]) else LIBASSUAN_CFLAGS="" LIBASSUAN_LIBS="" ifelse([$3], , :, [$3]) fi AC_SUBST(LIBASSUAN_CFLAGS) AC_SUBST(LIBASSUAN_LIBS) ]) diff --git a/src/libassuan.vers b/src/libassuan.vers index 37c0131..a178127 100644 --- a/src/libassuan.vers +++ b/src/libassuan.vers @@ -1,125 +1,126 @@ # libassuan.vers - List of symbols to export. # Copyright (C) 2009 g10 Code GmbH # # This file is part of LIBASSUAN. # # LIBASSUAN 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. # # LIBASSUAN 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+ #----------------------------------------------------------- # Please remember to add new functions also to libassuan.def #----------------------------------------------------------- LIBASSUAN_1.0 { global: assuan_accept; assuan_begin_confidential; assuan_client_read_response; assuan_client_parse_response; assuan_close_input_fd; assuan_close_output_fd; assuan_command_parse_fd; assuan_ctx_set_system_hooks; assuan_end_confidential; assuan_fdopen; assuan_get_active_fds; assuan_get_assuan_log_prefix; assuan_get_command_name; assuan_get_data_fp; assuan_get_flag; assuan_get_gpg_err_source; assuan_get_input_fd; assuan_get_log_cb; assuan_get_malloc_hooks; assuan_get_output_fd; assuan_get_peercred; assuan_get_pid; assuan_get_pointer; assuan_init_pipe_server; assuan_init_socket_server; assuan_inquire; assuan_inquire_ext; assuan_new; assuan_new; assuan_new_ext; assuan_pending_line; assuan_pipe_connect; assuan_process; assuan_process_done; assuan_process_next; assuan_read_line; assuan_receivefd; assuan_register_bye_notify; assuan_register_cancel_notify; assuan_register_command; assuan_register_input_notify; assuan_register_option_handler; assuan_register_output_notify; assuan_register_pre_cmd_notify; assuan_register_post_cmd_notify; assuan_register_reset_notify; assuan_release; assuan_release; assuan_send_data; assuan_sendfd; assuan_set_assuan_log_prefix; assuan_set_assuan_log_stream; assuan_set_error; assuan_set_flag; assuan_set_gpg_err_source; assuan_set_hello_line; assuan_set_io_monitor; assuan_set_log_cb; assuan_set_log_stream; assuan_set_malloc_hooks; assuan_set_okay_line; assuan_set_pointer; assuan_set_sock_nonce; assuan_set_system_hooks; assuan_sock_bind; assuan_sock_check_nonce; assuan_sock_close; assuan_sock_connect; assuan_sock_deinit; assuan_sock_get_nonce; assuan_sock_init; assuan_sock_new; assuan_socket_connect; assuan_transact; assuan_write_line; assuan_write_status; assuan_free; assuan_socket_connect_fd; assuan_check_version; assuan_sock_set_sockaddr_un; assuan_sock_set_flag; assuan_sock_get_flag; assuan_sock_connect_byname; __assuan_close; __assuan_pipe; __assuan_socketpair; __assuan_spawn; __assuan_usleep; __assuan_socket; __assuan_connect; __assuan_read; __assuan_write; __assuan_recvmsg; __assuan_sendmsg; __assuan_waitpid; local: *; }; diff --git a/src/memrchr.c b/src/memrchr.c index 258216c..adb68d7 100644 --- a/src/memrchr.c +++ b/src/memrchr.c @@ -1,39 +1,40 @@ /* memrchr.c - Replacement for memrchr. * Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan 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. * * Assuan 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 void * memrchr (const void *block, int c, size_t size) { const unsigned char *p = block; if (size) { for (p += size - 1; size; p--, size--) if (*p == c) return (void *)p; } return NULL; } diff --git a/src/mkheader.c b/src/mkheader.c index 44eb78e..0ee0944 100644 --- a/src/mkheader.c +++ b/src/mkheader.c @@ -1,269 +1,270 @@ /* mkheader.c - Create a header file for libassuan. * Copyright (C) 2010 Free Software Foundation, Inc. * * This file is free software; as a special exception the author gives * unlimited permission to copy and/or distribute it, with or without * modifications, as long as this notice is preserved. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * SPDX-License-Identifier: FSFULLR */ #include #include #include #include #define PGM "mkheader" #define LINESIZE 1024 static const char *host_os; static char *srcdir; static const char *hdr_version; static const char *hdr_version_number; /* Include the file NAME form the source directory. The included file is not further expanded. It may have comments indicated by a double hash mark at the begin of a line. */ static void include_file (const char *fname, int lnr, const char *name) { FILE *fp; char *incfname; char line[LINESIZE]; incfname = malloc (strlen (srcdir) + strlen (name) + 1); if (!incfname) { fputs (PGM ": out of core\n", stderr); exit (1); } strcpy (incfname, srcdir); strcat (incfname, name); fp = fopen (incfname, "r"); if (!fp) { fprintf (stderr, "%s:%d: error including `%s': %s\n", fname, lnr, incfname, strerror (errno)); exit (1); } while (fgets (line, LINESIZE, fp)) { if (line[0] == '#' && line[1] == '#') { if (!strncmp (line+2, "EOF##", 5)) break; /* Forced EOF. */ } else fputs (line, stdout); } if (ferror (fp)) { fprintf (stderr, "%s:%d: error reading `%s': %s\n", fname, lnr, incfname, strerror (errno)); exit (1); } fclose (fp); free (incfname); } static int write_special (const char *fname, int lnr, const char *tag) { if (!strcmp (tag, "include:includes")) { if (strstr (host_os, "mingw32")) include_file (fname, lnr, "w32-includes.inc.h"); else include_file (fname, lnr, "posix-includes.inc.h"); } else if (!strcmp (tag, "include:sys/types.h")) { if (!strcmp (host_os, "mingw32ce")) fputs ("#ifdef __MINGW32CE__\n" "# include \n" "#endif\n", stdout); else fputs ("#include \n", stdout); } else if (!strcmp (tag, "include:unistd.h")) { if (!strcmp (host_os, "mingw32ce")) fputs ("#ifdef __MINGW32CE__\n" "# include \n" "#endif\n", stdout); else fputs ("#include \n", stdout); } else if (!strcmp (tag, "include:types")) { if (strstr (host_os, "mingw32")) include_file (fname, lnr, "w32-types.inc.h"); else include_file (fname, lnr, "posix-types.inc.h"); } else if (!strcmp (tag, "include:fd-t")) { if (!strcmp (host_os, "mingw32ce")) include_file (fname, lnr, "w32ce-fd-t.inc.h"); else if (strstr (host_os, "mingw32")) include_file (fname, lnr, "w32-fd-t.inc.h"); else include_file (fname, lnr, "posix-fd-t.inc.h"); } else if (!strcmp (tag, "include:sock-nonce")) { if (strstr (host_os, "mingw32")) include_file (fname, lnr, "w32-sock-nonce.inc.h"); else include_file (fname, lnr, "posix-sock-nonce.inc.h"); } else if (!strcmp (tag, "include:sys-pth-impl")) { if (strstr (host_os, "mingw32")) include_file (fname, lnr, "w32-sys-pth-impl.h"); else include_file (fname, lnr, "posix-sys-pth-impl.h"); } else if (!strcmp (tag, "include:w32ce-add")) { if (!strcmp (host_os, "mingw32ce")) include_file (fname, lnr, "w32ce-add.h"); } else if (!strcmp (tag, "version")) { putchar ('\"'); fputs (hdr_version, stdout); putchar ('\"'); putchar ('\n'); } else if (!strcmp (tag, "version-number")) { fputs (hdr_version_number, stdout); putchar ('\n'); } else return 0; /* Unknown tag. */ return 1; /* Tag processed. */ } int main (int argc, char **argv) { FILE *fp; char line[LINESIZE]; int lnr = 0; const char *fname, *s; char *p1, *p2; if (argc) { argc--; argv++; } if (argc != 4) { fputs ("usage: " PGM " host_os template.h version version_number\n", stderr); return 1; } host_os = argv[0]; fname = argv[1]; hdr_version = argv[2]; hdr_version_number = argv[3]; srcdir = malloc (strlen (fname) + 2 + 1); if (!srcdir) { fputs (PGM ": out of core\n", stderr); return 1; } strcpy (srcdir, fname); p1 = strrchr (srcdir, '/'); if (p1) p1[1] = 0; else strcpy (srcdir, "./"); fp = fopen (fname, "r"); if (!fp) { fprintf (stderr, "%s:%d: can't open file: %s", fname, lnr, strerror (errno)); return 1; } while (fgets (line, LINESIZE, fp)) { size_t n = strlen (line); lnr++; if (!n || line[n-1] != '\n') { fprintf (stderr, "%s:%d: trailing linefeed missing, line too long or " "embedded Nul character", fname, lnr); break; } line[--n] = 0; p1 = strchr (line, '@'); p2 = p1? strchr (p1+1, '@') : NULL; if (!p1 || !p2 || p2-p1 == 1) { puts (line); continue; } *p1++ = 0; *p2++ = 0; fputs (line, stdout); if (!strcmp (p1, "configure_input")) { s = strrchr (fname, '/'); printf ("Do not edit. Generated from %s by %s for %s.", s? s+1 : fname, PGM, host_os); fputs (p2, stdout); putchar ('\n'); } else if (!write_special (fname, lnr, p1)) { putchar ('@'); fputs (p1, stdout); putchar ('@'); fputs (p2, stdout); putchar ('\n'); } } if (ferror (fp)) { fprintf (stderr, "%s:%d: error reading file: %s\n", fname, lnr, strerror (errno)); return 1; } fputs ("/*\n" "Loc" "al Variables:\n" "buffer-read-only: t\n" "End:\n" "*/\n", stdout); if (ferror (stdout)) { fprintf (stderr, PGM ": error writing stdout: %s\n", strerror (errno)); return 1; } fclose (fp); return 0; } diff --git a/src/posix-fd-t.inc.h b/src/posix-fd-t.inc.h index 80aa5f0..832c4fa 100644 --- a/src/posix-fd-t.inc.h +++ b/src/posix-fd-t.inc.h @@ -1,32 +1,33 @@ ## posix-fd-t.inc.h - Include fragment to build assuan.h. ## Copyright (C) 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. typedef int assuan_fd_t; #define ASSUAN_INVALID_FD (-1) #define ASSUAN_INVALID_PID ((pid_t) -1) static GPG_ERR_INLINE assuan_fd_t assuan_fd_from_posix_fd (int fd) { return fd; } ##EOF## diff --git a/src/posix-includes.inc.h b/src/posix-includes.inc.h index c641f86..d95a403 100644 --- a/src/posix-includes.inc.h +++ b/src/posix-includes.inc.h @@ -1,23 +1,24 @@ ## posix-includes.inc.h - Include fragment to build assuan.h. ## Copyright (C) 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. #include ##EOF## diff --git a/src/posix-sock-nonce.inc.h b/src/posix-sock-nonce.inc.h index a62216d..edd5a57 100644 --- a/src/posix-sock-nonce.inc.h +++ b/src/posix-sock-nonce.inc.h @@ -1,32 +1,33 @@ ## posix-sock-nonce.inc.h - Include fragment to build assuan.h. ## Copyright (C) 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. /* Under Windows Assuan features an emulation of Unix domain sockets based on a local TCP connections. To implement access permissions based on file permissions a nonce is used which is expected by the server as the first bytes received. On POSIX systems this is a - dummy structure. */ + dummy structure. */ struct assuan_sock_nonce_s { size_t length; }; typedef struct assuan_sock_nonce_s assuan_sock_nonce_t; ##EOF## diff --git a/src/posix-sys-pth-impl.h b/src/posix-sys-pth-impl.h index 77d38f0..6943359 100644 --- a/src/posix-sys-pth-impl.h +++ b/src/posix-sys-pth-impl.h @@ -1,88 +1,87 @@ ## posix-sys-pth-impl.h - Include fragment to build assuan.h. ## Copyright (C) 2009, 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. ## ## Warning: This is a fragment of a macro - no empty lines please. static int _assuan_pth_recvmsg (assuan_context_t ctx, assuan_fd_t fd, \ assuan_msghdr_t msg, int flags) \ { \ /* Pth does not provide a recvmsg function. We implement it. */ \ int ret; \ int fdmode; \ \ (void) ctx; \ fdmode = pth_fdmode (fd, PTH_FDMODE_POLL); \ if (fdmode == PTH_FDMODE_ERROR) \ { \ errno = EBADF; \ return -1; \ } \ if (fdmode == PTH_FDMODE_BLOCK) \ { \ fd_set fds; \ \ FD_ZERO (&fds); \ FD_SET (fd, &fds); \ while ((ret = pth_select (fd + 1, &fds, NULL, NULL, NULL)) < 0 \ && errno == EINTR) \ ; \ if (ret < 0) \ return -1; \ } \ \ while ((ret = recvmsg (fd, msg, flags)) == -1 && errno == EINTR) \ ; \ return ret; \ } \ static int _assuan_pth_sendmsg (assuan_context_t ctx, assuan_fd_t fd, \ const assuan_msghdr_t msg, int flags) \ { \ /* Pth does not provide a sendmsg function. We implement it. */ \ int ret; \ int fdmode; \ \ (void) ctx; \ fdmode = pth_fdmode (fd, PTH_FDMODE_POLL); \ if (fdmode == PTH_FDMODE_ERROR) \ { \ errno = EBADF; \ return -1; \ } \ if (fdmode == PTH_FDMODE_BLOCK) \ { \ fd_set fds; \ \ FD_ZERO (&fds); \ FD_SET (fd, &fds); \ while ((ret = pth_select (fd + 1, NULL, &fds, NULL, NULL)) < 0 \ && errno == EINTR) \ ; \ if (ret < 0) \ return -1; \ } \ \ while ((ret = sendmsg (fd, msg, flags)) == -1 && errno == EINTR) \ ; \ return ret; \ } \ ##EOF## Force end-of file. - - diff --git a/src/posix-types.inc.h b/src/posix-types.inc.h index c242b48..e3810b3 100644 --- a/src/posix-types.inc.h +++ b/src/posix-types.inc.h @@ -1,23 +1,24 @@ ## posix-types.inc.h - Include fragment to build assuan.h. ## Copyright (C) 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. typedef struct msghdr *assuan_msghdr_t; ##EOF## diff --git a/src/putc_unlocked.c b/src/putc_unlocked.c index 80b3aa1..6adcaec 100644 --- a/src/putc_unlocked.c +++ b/src/putc_unlocked.c @@ -1,30 +1,31 @@ /* putc_unlocked.c - Replacement for putc_unlocked. * Copyright (C) 2002, 2005 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan 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. * * Assuan 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 int putc_unlocked (int c, FILE *stream) { return putc (c, stream); } diff --git a/src/server.c b/src/server.c index 6cc75ae..7c82f02 100644 --- a/src/server.c +++ b/src/server.c @@ -1,69 +1,70 @@ /* server.c - Interfaces for all assuan servers. - Copyright (C) 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 "assuan-defs.h" #include "debug.h" /* Disconnect and release the context CTX. */ void _assuan_server_finish (assuan_context_t ctx) { if (ctx->inbound.fd != ASSUAN_INVALID_FD) { _assuan_close (ctx, ctx->inbound.fd); if (ctx->inbound.fd == ctx->outbound.fd) ctx->outbound.fd = ASSUAN_INVALID_FD; ctx->inbound.fd = ASSUAN_INVALID_FD; } if (ctx->outbound.fd != ASSUAN_INVALID_FD) { _assuan_close (ctx, ctx->outbound.fd); ctx->outbound.fd = ASSUAN_INVALID_FD; } if (ctx->pid != ASSUAN_INVALID_PID && ctx->pid) { _assuan_waitpid (ctx, ctx->pid, ctx->flags.no_waitpid, NULL, 0); ctx->pid = ASSUAN_INVALID_PID; } _assuan_uds_deinit (ctx); _assuan_inquire_release (ctx); } void _assuan_server_release (assuan_context_t ctx) { _assuan_server_finish (ctx); _assuan_free (ctx, ctx->hello_line); ctx->hello_line = NULL; _assuan_free (ctx, ctx->okay_line); ctx->okay_line = NULL; _assuan_free (ctx, ctx->cmdtbl); ctx->cmdtbl = NULL; } diff --git a/src/setenv.c b/src/setenv.c index 9f91124..3410b30 100644 --- a/src/setenv.c +++ b/src/setenv.c @@ -1,357 +1,358 @@ /* Copyright (C) 1992,1995-2001,2004 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - 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. - - The GNU C Library 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 the GNU C Library; if not, - see . + * This file is part of the GNU C Library. + * + * 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. + * + * The GNU C Library 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 the GNU C Library; if not, + * see . + * SPDX-License-Identifier: LGPL-2.1+ */ #if HAVE_CONFIG_H # include #endif #ifndef HAVE_W32CE_SYSTEM #define setenv _assuan_setenv #define unsetenv _assuan_unsetenv #define clearenv _assuan_clearenv #define __builtin_expect(cond,val) (cond) #include #if !_LIBC # if !defined errno && !defined HAVE_ERRNO_DECL extern int errno; # endif # define __set_errno(ev) ((errno) = (ev)) #endif #if _LIBC || HAVE_STDLIB_H # include #endif #if _LIBC || HAVE_STRING_H # include #endif #if _LIBC || HAVE_UNISTD_H # include #endif #if !_LIBC # define __environ environ # ifndef HAVE_ENVIRON_DECL extern char **environ; # endif #endif #if _LIBC /* This lock protects against simultaneous modifications of `environ'. */ # include __libc_lock_define_initialized (static, envlock) # define LOCK __libc_lock_lock (envlock) # define UNLOCK __libc_lock_unlock (envlock) #else # define LOCK # define UNLOCK #endif /* In the GNU C library we must keep the namespace clean. */ #ifdef _LIBC # define setenv __setenv # define unsetenv __unsetenv # define clearenv __clearenv # define tfind __tfind # define tsearch __tsearch #endif /* In the GNU C library implementation we try to be more clever and allow arbitrarily many changes of the environment given that the used values are from a small set. Outside glibc this will eat up all memory after a while. */ #if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \ && defined __GNUC__) # define USE_TSEARCH 1 # include /* This is a pointer to the root of the search tree with the known values. */ static void *known_values; # define KNOWN_VALUE(Str) \ ({ \ void *value = tfind (Str, &known_values, (__compar_fn_t) strcmp); \ value != NULL ? *(char **) value : NULL; \ }) # define STORE_VALUE(Str) \ tsearch (Str, &known_values, (__compar_fn_t) strcmp) #else # undef USE_TSEARCH # define KNOWN_VALUE(Str) NULL # define STORE_VALUE(Str) do { } while (0) #endif /* If this variable is not a null pointer we allocated the current environment. */ static char **last_environ; /* This function is used by `setenv' and `putenv'. The difference between the two functions is that for the former must create a new string which is then placed in the environment, while the argument of `putenv' must be used directly. This is all complicated by the fact that we try to reuse values once generated for a `setenv' call since we can never free the strings. */ static int __add_to_environ (const char *name, const char *value, const char *combined, int replace) { register char **ep; register size_t size; const size_t namelen = strlen (name); const size_t vallen = value != NULL ? strlen (value) + 1 : 0; LOCK; /* We have to get the pointer now that we have the lock and not earlier since another thread might have created a new environment. */ ep = __environ; size = 0; if (ep != NULL) { for (; *ep != NULL; ++ep) if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=') break; else ++size; } if (ep == NULL || __builtin_expect (*ep == NULL, 1)) { char **new_environ; /* We allocated this space; we can extend it. */ new_environ = (char **) realloc (last_environ, (size + 2) * sizeof (char *)); if (new_environ == NULL) { UNLOCK; return -1; } /* If the whole entry is given add it. */ if (combined != NULL) /* We must not add the string to the search tree since it belongs to the user. */ new_environ[size] = (char *) combined; else { /* See whether the value is already known. */ #ifdef USE_TSEARCH # ifdef __GNUC__ char new_value[namelen + 1 + vallen]; # else char *new_value = (char *) alloca (namelen + 1 + vallen); # endif # ifdef _LIBC __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1), value, vallen); # else memcpy (new_value, name, namelen); new_value[namelen] = '='; memcpy (&new_value[namelen + 1], value, vallen); # endif new_environ[size] = KNOWN_VALUE (new_value); if (__builtin_expect (new_environ[size] == NULL, 1)) #endif { new_environ[size] = (char *) malloc (namelen + 1 + vallen); if (__builtin_expect (new_environ[size] == NULL, 0)) { __set_errno (ENOMEM); UNLOCK; return -1; } #ifdef USE_TSEARCH memcpy (new_environ[size], new_value, namelen + 1 + vallen); #else memcpy (new_environ[size], name, namelen); new_environ[size][namelen] = '='; memcpy (&new_environ[size][namelen + 1], value, vallen); #endif /* And save the value now. We cannot do this when we remove the string since then we cannot decide whether it is a user string or not. */ STORE_VALUE (new_environ[size]); } } if (__environ != last_environ) memcpy ((char *) new_environ, (char *) __environ, size * sizeof (char *)); new_environ[size + 1] = NULL; last_environ = __environ = new_environ; } else if (replace) { char *np; /* Use the user string if given. */ if (combined != NULL) np = (char *) combined; else { #ifdef USE_TSEARCH # ifdef __GNUC__ char new_value[namelen + 1 + vallen]; # else char *new_value = (char *) alloca (namelen + 1 + vallen); # endif # ifdef _LIBC __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1), value, vallen); # else memcpy (new_value, name, namelen); new_value[namelen] = '='; memcpy (&new_value[namelen + 1], value, vallen); # endif np = KNOWN_VALUE (new_value); if (__builtin_expect (np == NULL, 1)) #endif { np = malloc (namelen + 1 + vallen); if (__builtin_expect (np == NULL, 0)) { UNLOCK; return -1; } #ifdef USE_TSEARCH memcpy (np, new_value, namelen + 1 + vallen); #else memcpy (np, name, namelen); np[namelen] = '='; memcpy (&np[namelen + 1], value, vallen); #endif /* And remember the value. */ STORE_VALUE (np); } } *ep = np; } UNLOCK; return 0; } int setenv (const char *name, const char *value, int replace) { if (name == NULL || *name == '\0' || strchr (name, '=') != NULL) { __set_errno (EINVAL); return -1; } return __add_to_environ (name, value, NULL, replace); } int unsetenv (const char *name) { size_t len; char **ep; if (name == NULL || *name == '\0' || strchr (name, '=') != NULL) { __set_errno (EINVAL); return -1; } len = strlen (name); LOCK; ep = __environ; while (*ep != NULL) if (!strncmp (*ep, name, len) && (*ep)[len] == '=') { /* Found it. Remove this pointer by moving later ones back. */ char **dp = ep; do dp[0] = dp[1]; while (*dp++); /* Continue the loop in case NAME appears again. */ } else ++ep; UNLOCK; return 0; } /* The `clearenv' was planned to be added to POSIX.1 but probably never made it. Nevertheless the POSIX.9 standard (POSIX bindings for Fortran 77) requires this function. */ int clearenv (void) { LOCK; if (__environ == last_environ && __environ != NULL) { /* We allocated this environment so we can free it. */ free (__environ); last_environ = NULL; } /* Clear the environment pointer removes the whole environment. */ __environ = NULL; UNLOCK; return 0; } #ifdef _LIBC libc_freeres_fn (free_mem) { /* Remove all traces. */ clearenv (); /* Now remove the search tree. */ __tdestroy (known_values, free); known_values = NULL; } # undef setenv # undef unsetenv # undef clearenv weak_alias (__setenv, setenv) weak_alias (__unsetenv, unsetenv) weak_alias (__clearenv, clearenv) #endif #endif /*!HAVE_W32CE_SYSTEM*/ diff --git a/src/stpcpy.c b/src/stpcpy.c index 8c489b8..4820c7a 100644 --- a/src/stpcpy.c +++ b/src/stpcpy.c @@ -1,54 +1,56 @@ /* Copyright (C) 1992, 1995, 1997, 2002, 2004 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - 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. - - The GNU C Library 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 the GNU C Library; if not, - see . */ + * This file is part of the GNU C Library. + * + * 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. + * + * The GNU C Library 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 the GNU C Library; if not, + * see . + * SPDX-License-Identifier: LGPL-2.1+ + */ #ifdef HAVE_CONFIG_H # include #endif #include #undef __stpcpy #undef stpcpy #ifndef weak_alias # define __stpcpy stpcpy #endif /* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */ char * __stpcpy (dest, src) char *dest; const char *src; { register char *d = dest; register const char *s = src; do *d++ = *s; while (*s++ != '\0'); return d - 1; } #ifdef libc_hidden_def libc_hidden_def (__stpcpy) #endif #ifdef weak_alias weak_alias (__stpcpy, stpcpy) #endif #ifdef libc_hidden_builtin_def libc_hidden_builtin_def (stpcpy) #endif diff --git a/src/system-posix.c b/src/system-posix.c index d274994..b7da9e3 100644 --- a/src/system-posix.c +++ b/src/system-posix.c @@ -1,450 +1,451 @@ /* system-posix.c - System support functions. - Copyright (C) 2009, 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #ifdef HAVE_STDINT_H # include #endif /* Solaris 8 needs sys/types.h before time.h. */ #include #include #include #include #ifdef HAVE_GETRLIMIT # include # include #endif /*HAVE_GETRLIMIT*/ #if __linux__ # include #endif /*__linux__ */ #include "assuan-defs.h" #include "debug.h" assuan_fd_t assuan_fdopen (int fd) { return dup (fd); } /* Sleep for the given number of microseconds. Default implementation. */ void __assuan_usleep (assuan_context_t ctx, unsigned int usec) { if (! usec) return; #ifdef HAVE_NANOSLEEP { struct timespec req; struct timespec rem; req.tv_sec = usec / 1000000; req.tv_nsec = (usec % 1000000) * 1000; while (nanosleep (&req, &rem) < 0 && errno == EINTR) req = rem; } #else { struct timeval tv; tv.tv_sec = usec / 1000000; tv.tv_usec = usec % 1000000; select (0, NULL, NULL, NULL, &tv); } #endif } /* Create a pipe with one inheritable end. Easy for Posix. */ int __assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) { return pipe (fd); } /* Close the given file descriptor, created with _assuan_pipe or one of the socket functions. Easy for Posix. */ int __assuan_close (assuan_context_t ctx, assuan_fd_t fd) { return close (fd); } ssize_t __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) { return read (fd, buffer, size); } ssize_t __assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size) { return write (fd, buffer, size); } int __assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { int ret; do ret = recvmsg (fd, msg, flags); while (ret == -1 && errno == EINTR); return ret; } int __assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { int ret; do ret = sendmsg (fd, msg, flags); while (ret == -1 && errno == EINTR); return ret; } static int writen (int fd, const char *buffer, size_t length) { while (length) { int nwritten = write (fd, buffer, length); if (nwritten < 0) { if (errno == EINTR) continue; return -1; /* write error */ } length -= nwritten; buffer += nwritten; } return 0; /* okay */ } /* Return the maximum number of currently allowed open file * descriptors. */ static int get_max_fds (void) { int max_fds = -1; #ifdef HAVE_GETRLIMIT struct rlimit rl; /* Under Linux we can figure out the highest used file descriptor by * reading /proc/PID/fd. This is in the common cases much faster * than for example doing 4096 close calls where almost all of them * will fail. We use the same code in GnuPG and measured this: On a * system with a limit of 4096 files and only 8 files open with the * highest number being 10, we speedup close_all_fds from 125ms to * 0.4ms including the readdir. * * Another option would be to close the file descriptors as returned * from reading that directory - however then we need to snapshot * that list before starting to close them. */ #ifdef __linux__ { DIR *dir = NULL; struct dirent *dir_entry; const char *s; int x; dir = opendir ("/proc/self/fd"); if (dir) { while ((dir_entry = readdir (dir))) { s = dir_entry->d_name; if ( *s < '0' || *s > '9') continue; x = atoi (s); if (x > max_fds) max_fds = x; } closedir (dir); } if (max_fds != -1) return max_fds + 1; } #endif /* __linux__ */ # ifdef RLIMIT_NOFILE if (!getrlimit (RLIMIT_NOFILE, &rl)) max_fds = rl.rlim_max; # endif # ifdef RLIMIT_OFILE if (max_fds == -1 && !getrlimit (RLIMIT_OFILE, &rl)) max_fds = rl.rlim_max; # endif #endif /*HAVE_GETRLIMIT*/ #ifdef _SC_OPEN_MAX if (max_fds == -1) { long int scres = sysconf (_SC_OPEN_MAX); if (scres >= 0) max_fds = scres; } #endif #ifdef _POSIX_OPEN_MAX if (max_fds == -1) max_fds = _POSIX_OPEN_MAX; #endif #ifdef OPEN_MAX if (max_fds == -1) max_fds = OPEN_MAX; #endif if (max_fds == -1) max_fds = 256; /* Arbitrary limit. */ /* 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 (max_fds == INT32_MAX) max_fds = 256; #endif return max_fds; } int __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { int pid; pid = fork (); if (pid < 0) return -1; if (pid == 0) { /* Child process (server side). */ int i; int n; char errbuf[512]; int *fdp; int fdnul; if (atfork) atfork (atforkvalue, 0); fdnul = open ("/dev/null", O_WRONLY); if (fdnul == -1) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "can't open `/dev/null': %s", strerror (errno)); _exit (4); } /* Dup handles to stdin/stdout. */ if (fd_out != STDOUT_FILENO) { if (dup2 (fd_out == ASSUAN_INVALID_FD ? fdnul : fd_out, STDOUT_FILENO) == -1) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "dup2 failed in child: %s", strerror (errno)); _exit (4); } } if (fd_in != STDIN_FILENO) { if (dup2 (fd_in == ASSUAN_INVALID_FD ? fdnul : fd_in, STDIN_FILENO) == -1) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "dup2 failed in child: %s", strerror (errno)); _exit (4); } } /* Dup stderr to /dev/null unless it is in the list of FDs to be passed to the child. */ fdp = fd_child_list; if (fdp) { for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++) ; } if (!fdp || *fdp == -1) { if (dup2 (fdnul, STDERR_FILENO) == -1) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_unix", ctx, "dup2(dev/null, 2) failed: %s", strerror (errno)); _exit (4); } } close (fdnul); /* Close all files which will not be duped and are not in the fd_child_list. */ n = get_max_fds (); for (i = 0; i < n; i++) { if (i == STDIN_FILENO || i == STDOUT_FILENO || i == STDERR_FILENO) continue; fdp = fd_child_list; if (fdp) { while (*fdp != -1 && *fdp != i) fdp++; } if (!(fdp && *fdp != -1)) close (i); } gpg_err_set_errno (0); if (! name) { /* No name and no args given, thus we don't do an exec but continue the forked process. */ *argv = "server"; /* FIXME: Cleanup. */ return 0; } execv (name, (char *const *) argv); /* oops - use the pipe to tell the parent about it */ snprintf (errbuf, sizeof(errbuf)-1, "ERR %d can't exec `%s': %.50s\n", _assuan_error (ctx, GPG_ERR_ASS_SERVER_START), name, strerror (errno)); errbuf[sizeof(errbuf)-1] = 0; writen (1, errbuf, strlen (errbuf)); _exit (4); } if (! name) *argv = "client"; *r_pid = pid; return 0; } /* FIXME: Add some sort of waitpid function that covers GPGME and gpg-agent's use of assuan. */ pid_t __assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, int *status, int options) { /* We can't just release the PID, a waitpid is mandatory. But NOWAIT in POSIX systems just means the caller already did the waitpid for this child. */ if (! nowait) return waitpid (pid, NULL, 0); return 0; } int __assuan_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]) { return socketpair (namespace, style, protocol, filedes); } int __assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol) { return socket (namespace, style, protocol); } int __assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr, socklen_t length) { return connect (sock, addr, length); } /* The default system hooks for assuan contexts. */ struct assuan_system_hooks _assuan_system_hooks = { ASSUAN_SYSTEM_HOOKS_VERSION, __assuan_usleep, __assuan_pipe, __assuan_close, __assuan_read, __assuan_write, __assuan_recvmsg, __assuan_sendmsg, __assuan_spawn, __assuan_waitpid, __assuan_socketpair, __assuan_socket, __assuan_connect }; diff --git a/src/system-w32.c b/src/system-w32.c index 7b95d5c..9547ce0 100644 --- a/src/system-w32.c +++ b/src/system-w32.c @@ -1,595 +1,596 @@ /* system-w32.c - System support functions for Windows. - Copyright (C) 2009, 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 "assuan-defs.h" #include "debug.h" assuan_fd_t assuan_fdopen (int fd) { assuan_fd_t ifd = (assuan_fd_t) _get_osfhandle (fd); assuan_fd_t ofd; if (! DuplicateHandle(GetCurrentProcess(), ifd, GetCurrentProcess(), &ofd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { gpg_err_set_errno (EIO); return ASSUAN_INVALID_FD; } return ofd; } /* Sleep for the given number of microseconds. Default implementation. */ void __assuan_usleep (assuan_context_t ctx, unsigned int usec) { if (!usec) return; Sleep (usec / 1000); } /* Three simple wrappers, only used because thes function are named in the def file. */ HANDLE _assuan_w32ce_prepare_pipe (int *r_rvid, int write_end) { (void)r_rvid; (void)write_end; return INVALID_HANDLE_VALUE; } HANDLE _assuan_w32ce_finish_pipe (int rvid, int write_end) { (void)rvid; (void)write_end; return INVALID_HANDLE_VALUE; } DWORD _assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd, LPSECURITY_ATTRIBUTES sec_attr, DWORD size) { return CreatePipe (read_hd, write_hd, sec_attr, size); } /* Create a pipe with one inheritable end. Default implementation. */ int __assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) { HANDLE rh; HANDLE wh; HANDLE th; SECURITY_ATTRIBUTES sec_attr; memset (&sec_attr, 0, sizeof (sec_attr)); sec_attr.nLength = sizeof (sec_attr); sec_attr.bInheritHandle = FALSE; if (!CreatePipe (&rh, &wh, &sec_attr, 0)) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, "CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1)); gpg_err_set_errno (EIO); return -1; } if (! DuplicateHandle (GetCurrentProcess(), (inherit_idx == 0) ? rh : wh, GetCurrentProcess(), &th, 0, TRUE, DUPLICATE_SAME_ACCESS )) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, "DuplicateHandle failed: %s", _assuan_w32_strerror (ctx, -1)); CloseHandle (rh); CloseHandle (wh); gpg_err_set_errno (EIO); return -1; } if (inherit_idx == 0) { CloseHandle (rh); rh = th; } else { CloseHandle (wh); wh = th; } fd[0] = rh; fd[1] = wh; return 0; } /* Close the given file descriptor, created with _assuan_pipe or one of the socket functions. Default implementation. */ int __assuan_close (assuan_context_t ctx, assuan_fd_t fd) { int rc = closesocket (HANDLE2SOCKET(fd)); if (rc) gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ()) ); if (rc && WSAGetLastError () == WSAENOTSOCK) { rc = CloseHandle (fd); if (rc) /* FIXME. */ gpg_err_set_errno (EIO); } return rc; } /* Return true if HD refers to a socket. */ static int is_socket (HANDLE hd) { /* We need to figure out whether we are working on a socket or on a handle. A trivial way would be to check for the return code of recv and see if it is WSAENOTSOCK. However the recv may block after the server process died and thus the destroy_reader will hang. Another option is to use getsockopt to test whether it is a socket. The bug here is that once a socket with a certain values has been opened, closed and later a CreatePipe returned the same value (i.e. handle), getsockopt still believes it is a socket. What we do now is to use a combination of GetFileType and GetNamedPipeInfo. The specs say that the latter may be used on anonymous pipes as well. Note that there are claims that since winsocket version 2 ReadFile may be used on a socket but only if it is supported by the service provider. Tests on a stock XP using a local TCP socket show that it does not work. */ DWORD dummyflags, dummyoutsize, dummyinsize, dummyinst; if (GetFileType (hd) == FILE_TYPE_PIPE && !GetNamedPipeInfo (hd, &dummyflags, &dummyoutsize, &dummyinsize, &dummyinst)) return 1; /* Function failed; thus we assume it is a socket. */ else return 0; /* Success; this is not a socket. */ } ssize_t __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) { int res; int ec = 0; if (is_socket (fd)) { int tries = 3; again: ec = 0; res = recv (HANDLE2SOCKET (fd), buffer, size, 0); if (res == -1) ec = WSAGetLastError (); if (ec == WSAEWOULDBLOCK && tries--) { /* EAGAIN: Use select to wait for resources and try again. We do this 3 times and then give up. The higher level layer then needs to take care of EAGAIN. No need to specify a timeout - the socket is not expected to be in blocking mode. */ fd_set fds; FD_ZERO (&fds); FD_SET (HANDLE2SOCKET (fd), &fds); select (0, &fds, NULL, NULL, NULL); goto again; } } else { DWORD nread = 0; if (!ReadFile (fd, buffer, size, &nread, NULL)) { res = -1; ec = GetLastError (); } else res = nread; } if (res == -1) { switch (ec) { case WSAENOTSOCK: gpg_err_set_errno (EBADF); break; case WSAEWOULDBLOCK: gpg_err_set_errno (EAGAIN); break; case WSAECONNRESET: /* Due to the use of recv. */ case ERROR_BROKEN_PIPE: gpg_err_set_errno (EPIPE); break; default: gpg_err_set_errno (EIO); break; } } return res; } ssize_t __assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size) { int res; int ec = 0; if (is_socket (fd)) { int tries = 3; again: ec = 0; res = send (HANDLE2SOCKET (fd), buffer, size, 0); if (res == -1) ec = WSAGetLastError (); if (ec == WSAEWOULDBLOCK && tries--) { /* EAGAIN: Use select to wait for resources and try again. We do this 3 times and then give up. The higher level layer then needs to take care of EAGAIN. No need to specify a timeout - the socket is not expected to be in blocking mode. */ fd_set fds; FD_ZERO (&fds); FD_SET (HANDLE2SOCKET (fd), &fds); select (0, NULL, &fds, NULL, NULL); goto again; } } else { DWORD nwrite; if (!WriteFile (fd, buffer, size, &nwrite, NULL)) { res = -1; ec = GetLastError (); } else res = (int)nwrite; } if (res == -1) { switch (ec) { case WSAENOTSOCK: gpg_err_set_errno (EBADF); break; case WSAEWOULDBLOCK: gpg_err_set_errno (EAGAIN); break; case ERROR_BROKEN_PIPE: case ERROR_NO_DATA: gpg_err_set_errno (EPIPE); break; default: gpg_err_set_errno (EIO); break; } } return res; } int __assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { gpg_err_set_errno (ENOSYS); return -1; } int __assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { gpg_err_set_errno (ENOSYS); return -1; } /* Build a command line for use with W32's CreateProcess. On success CMDLINE gets the address of a newly allocated string. */ static int build_w32_commandline (assuan_context_t ctx, const char * const *argv, char **cmdline) { int i, n; const char *s; char *buf, *p; *cmdline = NULL; n = 0; for (i=0; (s = argv[i]); i++) { n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ for (; *s; s++) if (*s == '\"') n++; /* Need to double inner quotes. */ } n++; buf = p = _assuan_malloc (ctx, n); if (! buf) return -1; for (i = 0; argv[i]; i++) { if (i) p = stpcpy (p, " "); if (! *argv[i]) /* Empty string. */ p = stpcpy (p, "\"\""); else if (strpbrk (argv[i], " \t\n\v\f\"")) { p = stpcpy (p, "\""); for (s = argv[i]; *s; s++) { *p++ = *s; if (*s == '\"') *p++ = *s; } *p++ = '\"'; *p = 0; } else p = stpcpy (p, argv[i]); } *cmdline= buf; return 0; } int __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, /* Returns process handle. */ 0, /* Returns primary thread handle. */ 0, /* Returns pid. */ 0 /* Returns tid. */ }; STARTUPINFO si; assuan_fd_t fd; assuan_fd_t *fdp; char *cmdline; HANDLE nullfd = INVALID_HANDLE_VALUE; /* fixme: Actually we should set the "_assuan_pipe_connect_pid" env variable. However this requires us to write a full environment handler, because the strings are expected in sorted order. The suggestion given in the MS Reference Library, to save the old value, change it, create process and restore it, is not thread safe. */ /* Build the command line. */ if (build_w32_commandline (ctx, argv, &cmdline)) return -1; /* Start the process. */ memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES; /* FIXME: Dup to nul if ASSUAN_INVALID_FD. */ si.hStdInput = fd_in; si.hStdOutput = fd_out; /* Dup stderr to /dev/null unless it is in the list of FDs to be passed to the child. */ fd = assuan_fd_from_posix_fd (fileno (stderr)); fdp = fd_child_list; if (fdp) { for (; *fdp != ASSUAN_INVALID_FD && *fdp != fd; fdp++) ; } if (!fdp || *fdp == ASSUAN_INVALID_FD) { nullfd = CreateFileW (L"nul", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (nullfd == INVALID_HANDLE_VALUE) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "can't open `nul': %s", _assuan_w32_strerror (ctx, -1)); _assuan_free (ctx, cmdline); gpg_err_set_errno (EIO); return -1; } si.hStdError = nullfd; } else si.hStdError = fd; /* Note: We inherit all handles flagged as inheritable. This seems to be a security flaw but there seems to be no way of selecting handles to inherit. A fix for this would be to use a helper process like we have in gpgme. */ /* _assuan_log_printf ("CreateProcess, path=`%s' cmdline=`%s'\n", */ /* name, cmdline); */ if (!CreateProcess (name, /* Program to start. */ cmdline, /* Command line arguments. */ &sec_attr, /* Process security attributes. */ &sec_attr, /* Thread security attributes. */ TRUE, /* Inherit handles. */ (CREATE_DEFAULT_ERROR_MODE | ((flags & 128)? DETACHED_PROCESS : 0) | GetPriorityClass (GetCurrentProcess ()) | CREATE_SUSPENDED), /* Creation flags. */ NULL, /* Environment. */ NULL, /* Use current drive/directory. */ &si, /* Startup information. */ &pi /* Returns process information. */ )) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_w32", ctx, "CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1)); _assuan_free (ctx, cmdline); if (nullfd != INVALID_HANDLE_VALUE) CloseHandle (nullfd); gpg_err_set_errno (EIO); return -1; } _assuan_free (ctx, cmdline); if (nullfd != INVALID_HANDLE_VALUE) CloseHandle (nullfd); ResumeThread (pi.hThread); CloseHandle (pi.hThread); /* _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */ /* " dwProcessID=%d dwThreadId=%d\n", */ /* pi.hProcess, pi.hThread, */ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ *r_pid = (pid_t) pi.hProcess; /* No need to modify peer process, as we don't change the handle names. However this also means we are not safe, as we inherit too many handles. Should use approach similar to gpgme and glib using a helper process. */ return 0; } /* FIXME: Add some sort of waitpid function that covers GPGME and gpg-agent's use of assuan. */ pid_t __assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, int *status, int options) { CloseHandle ((HANDLE) pid); return 0; } int __assuan_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]) { gpg_err_set_errno (ENOSYS); return -1; } int __assuan_socket (assuan_context_t ctx, int domain, int type, int proto) { int res; res = socket (domain, type, proto); if (res == -1) gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ())); return res; } int __assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr, socklen_t length) { int res; res = connect (sock, addr, length); if (res < 0) gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ())); return res; } /* The default system hooks for assuan contexts. */ struct assuan_system_hooks _assuan_system_hooks = { ASSUAN_SYSTEM_HOOKS_VERSION, __assuan_usleep, __assuan_pipe, __assuan_close, __assuan_read, __assuan_write, __assuan_recvmsg, __assuan_sendmsg, __assuan_spawn, __assuan_waitpid, __assuan_socketpair, __assuan_socket, __assuan_connect }; diff --git a/src/system-w32ce.c b/src/system-w32ce.c index d3e3357..da5dcf2 100644 --- a/src/system-w32ce.c +++ b/src/system-w32ce.c @@ -1,705 +1,706 @@ /* system-w32ce.c - System support functions for WindowsCE. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 # ifdef HAVE_WINSOCK2_H # include # endif #include #include #include #include "assuan-defs.h" #include "debug.h" #define GPGCEDEV_IOCTL_GET_RVID \ CTL_CODE (FILE_DEVICE_STREAMS, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS) #define GPGCEDEV_IOCTL_MAKE_PIPE \ CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS) static wchar_t * utf8_to_wchar (const char *string) { int n; size_t nbytes; wchar_t *result; if (!string) return NULL; n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0); if (n < 0) { gpg_err_set_errno (EINVAL); return NULL; } nbytes = (size_t)(n+1) * sizeof(*result); if (nbytes / sizeof(*result) != (n+1)) { gpg_err_set_errno (ENOMEM); return NULL; } result = malloc (nbytes); if (!result) return NULL; n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n); if (n < 0) { free (result); gpg_err_set_errno (EINVAL); result = NULL; } return result; } /* Convenience function. */ static void free_wchar (wchar_t *string) { if (string) free (string); } assuan_fd_t assuan_fdopen (int fd) { return (assuan_fd_t)fd; } /* Sleep for the given number of microseconds. Default implementation. */ void __assuan_usleep (assuan_context_t ctx, unsigned int usec) { if (usec) Sleep (usec / 1000); } /* Prepare a pipe. Returns a handle which is, depending on WRITE_END, will either act the read or as the write end of the pipe. The other value returned is a rendezvous id used to complete the pipe creation with _assuan_w32ce_finish_pipe. The rendezvous id may be passed to another process and that process may finish the pipe creation. This creates the interprocess pipe. The rendezvous id is not a handle but a plain number; there is no release function and care should be taken not to pass it to a function expecting a handle. */ HANDLE _assuan_w32ce_prepare_pipe (int *r_rvid, int write_end) { HANDLE hd; LONG rvid; ActivateDevice (L"Drivers\\GnuPG_Device", 0); /* Note: Using "\\$device\\GPG1" should be identical to "GPG1:". However this returns an invalid parameter error without having called GPG_Init in the driver. The docs mention something about RegisterAFXEx but that API is not documented. */ hd = CreateFile (L"GPG1:", write_end? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hd != INVALID_HANDLE_VALUE) { if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_GET_RVID, NULL, 0, &rvid, sizeof rvid, NULL, NULL)) { DWORD lastrc = GetLastError (); CloseHandle (hd); hd = INVALID_HANDLE_VALUE; SetLastError (lastrc); } else *r_rvid = rvid; } return hd; } /* Create a pipe. WRITE_END shall have the opposite value of the one pssed to _assuan_w32ce_prepare_pipe; see there for more details. */ HANDLE _assuan_w32ce_finish_pipe (int rvid, int write_end) { HANDLE hd; if (!rvid) { SetLastError (ERROR_INVALID_HANDLE); return INVALID_HANDLE_VALUE; } hd = CreateFile (L"GPG1:", write_end? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL); if (hd != INVALID_HANDLE_VALUE) { if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_MAKE_PIPE, &rvid, sizeof rvid, NULL, 0, NULL, NULL)) { DWORD lastrc = GetLastError (); CloseHandle (hd); hd = INVALID_HANDLE_VALUE; SetLastError (lastrc); } } return hd; } /* WindowsCE does not provide a pipe feature. However we need something like a pipe to convey data between processes and in some cases within a process. This replacement is not only used by libassuan but exported and thus usable by gnupg and gpgme as well. */ DWORD _assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd, LPSECURITY_ATTRIBUTES sec_attr, DWORD size) { HANDLE hd[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; int rvid; int rc = 0; hd[0] = _assuan_w32ce_prepare_pipe (&rvid, 0); if (hd[0] != INVALID_HANDLE_VALUE) { hd[1] = _assuan_w32ce_finish_pipe (rvid, 1); if (hd[1] != INVALID_HANDLE_VALUE) rc = 1; else { DWORD lastrc = GetLastError (); CloseHandle (hd[0]); hd[0] = INVALID_HANDLE_VALUE; SetLastError (lastrc); } } *read_hd = hd[0]; *write_hd = hd[1]; return rc; } /* Create a pipe with one inheritable end. Default implementation. If INHERIT_IDX is 0, the read end of the pipe is made inheritable; with INHERIT_IDX is 1 the write end will be inheritable. The question now is how we create an inheritable pipe end under windows CE were handles are process local objects? The trick we employ is to defer the actual creation to the other end: We create an incomplete pipe and pass a rendezvous id to the other end (process). The other end now uses the rendezvous id to lookup the pipe in our device driver, creates a new handle and uses that one to finally establish the pipe. */ int __assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) { HANDLE hd; int rvid; hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx); if (hd == INVALID_HANDLE_VALUE) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, "CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1)); gpg_err_set_errno (EIO); return -1; } if (inherit_idx) { fd[0] = hd; fd[1] = (void*)rvid; } else { fd[0] = (void*)rvid; fd[1] = hd; } return 0; } /* Close the given file descriptor, created with _assuan_pipe or one of the socket functions. Default implementation. */ int __assuan_close (assuan_context_t ctx, assuan_fd_t fd) { int rc = closesocket (HANDLE2SOCKET(fd)); int err = WSAGetLastError (); /* Note that gpg_err_set_errno on Windows CE overwrites WSAGetLastError() (via SetLastError()). */ if (rc) gpg_err_set_errno (_assuan_sock_wsa2errno (err)); if (rc && err == WSAENOTSOCK) { rc = CloseHandle (fd); if (rc) /* FIXME. */ gpg_err_set_errno (EIO); } return rc; } ssize_t __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) { /* Due to the peculiarities of the W32 API we can't use read for a network socket and thus we try to use recv first and fallback to read if recv detects that it is not a network socket. */ int res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_read", ctx, "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); #ifdef HAVE_W32CE_SYSTEM /* This is a bit of a hack to support stdin over ssh. Note that fread buffers fully while getchar is line buffered. Weird, but that's the way it is. ASSUAN_STDIN and ASSUAN_STDOUT are special handle values that shouldn't occur in the wild. */ if (fd == ASSUAN_STDIN) { int i = 0; int chr; while (i < size) { chr = getchar(); if (chr == EOF) break; ((char*)buffer)[i++] = (char) chr; if (chr == '\n') break; } return TRACE_SYSRES (i); } #endif res = recv (HANDLE2SOCKET (fd), buffer, size, 0); if (res == -1) { TRACE_LOG1 ("recv failed: rc=%d", (int)WSAGetLastError ()); switch (WSAGetLastError ()) { case WSAENOTSOCK: { DWORD nread = 0; res = ReadFile (fd, buffer, size, &nread, NULL); if (! res) { TRACE_LOG1 ("ReadFile failed: rc=%d", (int)GetLastError ()); switch (GetLastError ()) { case ERROR_BROKEN_PIPE: gpg_err_set_errno (EPIPE); break; case ERROR_PIPE_NOT_CONNECTED: case ERROR_BUSY: gpg_err_set_errno (EAGAIN); break; default: gpg_err_set_errno (EIO); } res = -1; } else res = (int) nread; } break; case WSAEWOULDBLOCK: gpg_err_set_errno (EAGAIN); break; case ERROR_BROKEN_PIPE: gpg_err_set_errno (EPIPE); break; default: gpg_err_set_errno (EIO); break; } } return TRACE_SYSRES (res); } ssize_t __assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size) { /* Due to the peculiarities of the W32 API we can't use write for a network socket and thus we try to use send first and fallback to write if send detects that it is not a network socket. */ int res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_write", ctx, "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); #ifdef HAVE_W32CE_SYSTEM /* This is a bit of a hack to support stdout over ssh. Note that fread buffers fully while getchar is line buffered. Weird, but that's the way it is. ASSUAN_STDIN and ASSUAN_STDOUT are special handle values that shouldn't occur in the wild. */ if (fd == ASSUAN_STDOUT) { res = fwrite (buffer, 1, size, stdout); return TRACE_SYSRES (res); } #endif res = send ((int)fd, buffer, size, 0); if (res == -1 && WSAGetLastError () == WSAENOTSOCK) { DWORD nwrite; TRACE_LOG ("send call failed - trying WriteFile"); res = WriteFile (fd, buffer, size, &nwrite, NULL); if (! res) { TRACE_LOG1 ("WriteFile failed: rc=%d", (int)GetLastError ()); switch (GetLastError ()) { case ERROR_BROKEN_PIPE: case ERROR_NO_DATA: gpg_err_set_errno (EPIPE); break; case ERROR_PIPE_NOT_CONNECTED: case ERROR_BUSY: gpg_err_set_errno (EAGAIN); break; default: gpg_err_set_errno (EIO); break; } res = -1; } else res = (int) nwrite; } else if (res == -1) TRACE_LOG1 ("send call failed: rc=%d", (int)GetLastError ()); return TRACE_SYSRES (res); } int __assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { gpg_err_set_errno (ENOSYS); return -1; } int __assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { gpg_err_set_errno (ENOSYS); return -1; } /* Build a command line for use with W32's CreateProcess. On success CMDLINE gets the address of a newly allocated string. */ static int build_w32_commandline (assuan_context_t ctx, const char * const *argv, assuan_fd_t fd0, assuan_fd_t fd1, assuan_fd_t fd2, int fd2_isnull, char **cmdline) { int i, n; const char *s; char *buf, *p; char fdbuf[3*30]; p = fdbuf; *p = 0; if (fd0 != ASSUAN_INVALID_FD) { snprintf (p, 25, "-&S0=%d ", (int)fd0); p += strlen (p); } if (fd1 != ASSUAN_INVALID_FD) { snprintf (p, 25, "-&S1=%d ", (int)fd1); p += strlen (p); } if (fd2 != ASSUAN_INVALID_FD) { if (fd2_isnull) strcpy (p, "-&S2=null "); else snprintf (p, 25, "-&S2=%d ", (int)fd2); p += strlen (p); } *cmdline = NULL; n = strlen (fdbuf); for (i=0; (s = argv[i]); i++) { if (!i) continue; /* Ignore argv[0]. */ n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */ for (; *s; s++) if (*s == '\"') n++; /* Need to double inner quotes. */ } n++; buf = p = _assuan_malloc (ctx, n); if (! buf) return -1; p = stpcpy (p, fdbuf); for (i = 0; argv[i]; i++) { if (!i) continue; /* Ignore argv[0]. */ if (i > 1) p = stpcpy (p, " "); if (! *argv[i]) /* Empty string. */ p = stpcpy (p, "\"\""); else if (strpbrk (argv[i], " \t\n\v\f\"")) { p = stpcpy (p, "\""); for (s = argv[i]; *s; s++) { *p++ = *s; if (*s == '\"') *p++ = *s; } *p++ = '\"'; *p = 0; } else p = stpcpy (p, argv[i]); } *cmdline = buf; return 0; } int __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { PROCESS_INFORMATION pi = { NULL, /* Returns process handle. */ 0, /* Returns primary thread handle. */ 0, /* Returns pid. */ 0 /* Returns tid. */ }; assuan_fd_t fd; assuan_fd_t *fdp; assuan_fd_t fd_err; int fd_err_isnull = 0; char *cmdline; /* Dup stderr to /dev/null unless it is in the list of FDs to be passed to the child. Well we don't actually open nul because that is not available on Windows, but use our hack for it. Because an RVID of 0 is an invalid value and HANDLES will never have this value either, we test for this as well. */ /* FIXME: As long as we can't decide whether a handle is a real handler or an rendezvous id we can't do anything with the FD_CHILD_LIST. We can't do much with stderr either, thus we better don't pass stderr to the child at all. If we would do so and it is not a rendezvous id the client would run into problems. */ fd = assuan_fd_from_posix_fd (fileno (stderr)); fdp = fd_child_list; if (fdp) { for (; *fdp != ASSUAN_INVALID_FD && *fdp != 0 && *fdp != fd; fdp++) ; } if (!fdp || *fdp == ASSUAN_INVALID_FD) fd_err_isnull = 1; fd_err = ASSUAN_INVALID_FD; if (build_w32_commandline (ctx, argv, fd_in, fd_out, fd_err, fd_err_isnull, &cmdline)) { return -1; } TRACE2 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "path=`%s' cmdline=`%s'", name, cmdline); { wchar_t *wcmdline, *wname; wcmdline = utf8_to_wchar (cmdline); _assuan_free (ctx, cmdline); if (!wcmdline) return -1; wname = utf8_to_wchar (name); if (!wname) { free_wchar (wcmdline); return -1; } if (!CreateProcess (wname, /* Program to start. */ wcmdline, /* Command line arguments. */ NULL, /* (not supported) */ NULL, /* (not supported) */ FALSE, /* (not supported) */ (CREATE_SUSPENDED), /* Creation flags. */ NULL, /* (not supported) */ NULL, /* (not supported) */ NULL, /* (not supported) */ &pi /* Returns process information.*/ )) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1)); free_wchar (wname); free_wchar (wcmdline); gpg_err_set_errno (EIO); return -1; } free_wchar (wname); free_wchar (wcmdline); } ResumeThread (pi.hThread); TRACE4 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "CreateProcess ready: hProcess=%p hThread=%p" " dwProcessID=%d dwThreadId=%d\n", pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId); CloseHandle (pi.hThread); *r_pid = (pid_t) pi.hProcess; return 0; } pid_t __assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, int *status, int options) { CloseHandle ((HANDLE) pid); return 0; } int __assuan_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]) { gpg_err_set_errno (ENOSYS); return -1; } int __assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol) { int res; res = socket (namespace, style, protocol); if (res == -1) gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ())); return res; } int __assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr, socklen_t length) { int res; res = connect (sock, addr, length); if (res < 0) gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ())); return res; } /* The default system hooks for assuan contexts. */ struct assuan_system_hooks _assuan_system_hooks = { ASSUAN_SYSTEM_HOOKS_VERSION, __assuan_usleep, __assuan_pipe, __assuan_close, __assuan_read, __assuan_write, __assuan_recvmsg, __assuan_sendmsg, __assuan_spawn, __assuan_waitpid, __assuan_socketpair, __assuan_socket, __assuan_connect }; diff --git a/src/system.c b/src/system.c index ddb99fb..00b76cb 100644 --- a/src/system.c +++ b/src/system.c @@ -1,414 +1,415 @@ /* system.c - System support functions. - Copyright (C) 2009 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #ifdef HAVE_SYS_TYPES_H /* Solaris 8 needs sys/types.h before time.h. */ # include #endif #include #ifdef HAVE_FCNTL_H #include #endif #include "assuan-defs.h" #include "debug.h" #define DEBUG_SYSIO 0 /* Manage memory specific to a context. */ void * _assuan_malloc (assuan_context_t ctx, size_t cnt) { return ctx->malloc_hooks.malloc (cnt); } void * _assuan_realloc (assuan_context_t ctx, void *ptr, size_t cnt) { return ctx->malloc_hooks.realloc (ptr, cnt); } void * _assuan_calloc (assuan_context_t ctx, size_t cnt, size_t elsize) { void *ptr; size_t nbytes; nbytes = cnt * elsize; /* Check for overflow. */ if (elsize && nbytes / elsize != cnt) { gpg_err_set_errno (ENOMEM); return NULL; } ptr = ctx->malloc_hooks.malloc (nbytes); if (ptr) memset (ptr, 0, nbytes); return ptr; } void _assuan_free (assuan_context_t ctx, void *ptr) { if (ptr) ctx->malloc_hooks.free (ptr); } /* Release the memory at PTR using the allocation handler of the context CTX. This is a convenience function. */ void assuan_free (assuan_context_t ctx, void *ptr) { _assuan_free (ctx, ptr); } /* Copy the system hooks struct, paying attention to version differences. SRC is usually from the user, DST MUST be from the library. */ void _assuan_system_hooks_copy (assuan_system_hooks_t dst, assuan_system_hooks_t src) { /* Reset the defaults. */ if (dst != &_assuan_system_hooks) memcpy (dst, &_assuan_system_hooks, sizeof (*dst)); dst->version = ASSUAN_SYSTEM_HOOKS_VERSION; if (src->version >= 1) { dst->usleep = src->usleep; dst->pipe = src->pipe; dst->close = src->close; dst->read = src->read; dst->write = src->write; dst->sendmsg = src->sendmsg; dst->recvmsg = src->recvmsg; dst->spawn = src->spawn; dst->waitpid = src->waitpid; dst->socketpair = src->socketpair; } if (src->version >= 2) { dst->socket = src->socket; dst->connect = src->connect; } if (src->version > 2) /* FIXME. Application uses newer version of the library. What to do? */ ; } /* Sleep for the given number of microseconds. */ void _assuan_usleep (assuan_context_t ctx, unsigned int usec) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "_assuan_usleep", ctx, "usec=%u", usec); (ctx->system.usleep) (ctx, usec); } /* Create a pipe with one inheritable end. */ int _assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) { int err; TRACE_BEG2 (ctx, ASSUAN_LOG_SYSIO, "_assuan_pipe", ctx, "inherit_idx=%i (Assuan uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); err = (ctx->system.pipe) (ctx, fd, inherit_idx); if (err) return TRACE_SYSRES (err); return TRACE_SUC2 ("read=0x%x, write=0x%x", fd[0], fd[1]); } /* Close the given file descriptor, created with _assuan_pipe or one of the socket functions. */ int _assuan_close (assuan_context_t ctx, assuan_fd_t fd) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "_assuan_close", ctx, "fd=0x%x", fd); return (ctx->system.close) (ctx, fd); } /* Same as assuan_close but used for the inheritable end of a pipe. */ int _assuan_close_inheritable (assuan_context_t ctx, assuan_fd_t fd) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "_assuan_close_inheritable", ctx, "fd=0x%x", fd); #ifdef HAVE_W32CE_SYSTEM return 0; /* Nothing to do because it is a rendezvous id. */ #else return (ctx->system.close) (ctx, fd); #endif } ssize_t _assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) { #if DEBUG_SYSIO ssize_t res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_read", ctx, "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); res = (ctx->system.read) (ctx, fd, buffer, size); return TRACE_SYSRES (res); #else return (ctx->system.read) (ctx, fd, buffer, size); #endif } ssize_t _assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size) { #if DEBUG_SYSIO ssize_t res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_write", ctx, "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); res = (ctx->system.write) (ctx, fd, buffer, size); return TRACE_SYSRES (res); #else return (ctx->system.write) (ctx, fd, buffer, size); #endif } int _assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { #if DEBUG_SYSIO ssize_t res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_recvmsg", ctx, "fd=0x%x, msg=%p, flags=0x%x", fd, msg, flags); res = (ctx->system.recvmsg) (ctx, fd, msg, flags); if (res > 0) { struct cmsghdr *cmptr; TRACE_LOG2 ("msg->msg_iov[0] = { iov_base=%p, iov_len=%i }", msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len); TRACE_LOGBUF (msg->msg_iov[0].iov_base, res); cmptr = CMSG_FIRSTHDR (msg); if (cmptr) { void *data = CMSG_DATA (cmptr); TRACE_LOG5 ("cmsg_len=0x%x (0x%x data), cmsg_level=0x%x, " "cmsg_type=0x%x, first data int=0x%x", cmptr->cmsg_len, cmptr->cmsg_len - (((char *)data) - ((char *)cmptr)), cmptr->cmsg_level, cmptr->cmsg_type, *(int *)data); } } return TRACE_SYSRES (res); #else return (ctx->system.recvmsg) (ctx, fd, msg, flags); #endif } int _assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { #if DEBUG_SYSIO ssize_t res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_sendmsg", ctx, "fd=0x%x, msg=%p, flags=0x%x", fd, msg, flags); { struct cmsghdr *cmptr; TRACE_LOG2 ("msg->iov[0] = { iov_base=%p, iov_len=%i }", msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len); TRACE_LOGBUF (msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len); cmptr = CMSG_FIRSTHDR (msg); if (cmptr) { void *data = CMSG_DATA (cmptr); TRACE_LOG5 ("cmsg_len=0x%x (0x%x data), cmsg_level=0x%x, " "cmsg_type=0x%x, first data int=0x%x", cmptr->cmsg_len, cmptr->cmsg_len - (((char *)data) - ((char *)cmptr)), cmptr->cmsg_level, cmptr->cmsg_type, *(int *)data); } } res = (ctx->system.sendmsg) (ctx, fd, msg, flags); return TRACE_SYSRES (res); #else return (ctx->system.sendmsg) (ctx, fd, msg, flags); #endif } /* Create a new process from NAME and ARGV. Provide FD_IN and FD_OUT as stdin and stdout. Inherit the ASSUAN_INVALID_FD-terminated FD_CHILD_LIST as given (no remapping), which must be inheritable. On Unix, call ATFORK with ATFORKVALUE after fork and before exec. */ int _assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { int res; int i; TRACE_BEG6 (ctx, ASSUAN_LOG_CTX, "_assuan_spawn", ctx, "name=%s,fd_in=0x%x,fd_out=0x%x," "atfork=%p,atforkvalue=%p,flags=%i", name ? name : "(null)", fd_in, fd_out, atfork, atforkvalue, flags); if (name) { i = 0; while (argv[i]) { TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]); i++; } } i = 0; if (fd_child_list) { while (fd_child_list[i] != ASSUAN_INVALID_FD) { TRACE_LOG2 ("fd_child_list[%2i] = 0x%x", i, fd_child_list[i]); i++; } } res = (ctx->system.spawn) (ctx, r_pid, name, argv, fd_in, fd_out, fd_child_list, atfork, atforkvalue, flags); if (name) { TRACE_LOG1 ("pid = 0x%x", *r_pid); } else { TRACE_LOG2 ("pid = 0x%x (%s)", *r_pid, *argv); } return TRACE_SYSERR (res); } /* FIXME: Add some sort of waitpid function that covers GPGME and gpg-agent's use of assuan. */ pid_t _assuan_waitpid (assuan_context_t ctx, pid_t pid, int action, int *status, int options) { #if DEBUG_SYSIO ssize_t res; TRACE_BEG4 (ctx, ASSUAN_LOG_SYSIO, "_assuan_waitpid", ctx, "pid=%i, action=%i, status=%p, options=%i", pid, action, status, options); res = (ctx->system.waitpid) (ctx, pid, action, status, options); return TRACE_SYSRES (res); #else return (ctx->system.waitpid) (ctx, pid, action, status, options); #endif } int _assuan_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]) { int res; TRACE_BEG4 (ctx, ASSUAN_LOG_SYSIO, "_assuan_socketpair", ctx, "namespace=%i,style=%i,protocol=%i,filedes=%p", namespace, style, protocol, filedes); res = (ctx->system.socketpair) (ctx, namespace, style, protocol, filedes); if (res == 0) TRACE_LOG2 ("filedes = { 0x%x, 0x%x }", filedes[0], filedes[1]); return TRACE_SYSERR (res); } int _assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol) { int res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_socket", ctx, "namespace=%i,style=%i,protocol=%i", namespace, style, protocol); res = (ctx->system.socket) (ctx, namespace, style, protocol); return TRACE_SYSRES (res); } int _assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr, socklen_t length) { int res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_connect", ctx, "socket=%i,addr=%p,length=%i", sock, addr, length); res = (ctx->system.connect) (ctx, sock, addr, length); return TRACE_SYSRES (res); } diff --git a/src/sysutils.c b/src/sysutils.c index 0eb5d1c..74031cd 100644 --- a/src/sysutils.c +++ b/src/sysutils.c @@ -1,138 +1,140 @@ /* sysutils.c - System utilities - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan 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. - - Assuan 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 . + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * This file is part of Assuan. + * + * Assuan 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. + * + * Assuan 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 #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include # ifdef HAVE_W32CE_SYSTEM # include # endif /*HAVE_W32CE_SYSTEM*/ #endif /*HAVE_W32_SYSTEM*/ #include "assuan-defs.h" /* This is actually a dummy function to make sure that is module is not empty. Some compilers barf on empty modules. */ const char * _assuan_sysutils_blurb (void) { static const char blurb[] = "\n\n" "This is Libassuan " PACKAGE_VERSION " - The GnuPG IPC Library\n" "Copyright 2001-2013 Free Software Foundation, Inc.\n" "Copyright 2001-2014 g10 Code GmbH\n" + "SPDX-License-Identifier: LGPL-2.1+\n" "\n" "(" BUILD_REVISION " " BUILD_TIMESTAMP ")\n" "\n\n"; return blurb; } /* Return a string from the Win32 Registry or NULL in case of error. The returned string is allocated using a plain malloc and thus the caller needs to call the standard free(). The string is looked up under HKEY_LOCAL_MACHINE. */ #ifdef HAVE_W32CE_SYSTEM static char * w32_read_registry (const wchar_t *dir, const wchar_t *name) { HKEY handle; DWORD n, nbytes; wchar_t *buffer = NULL; char *result = NULL; if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &handle)) return NULL; /* No need for a RegClose, so return immediately. */ nbytes = 1; if (RegQueryValueEx (handle, name, 0, NULL, NULL, &nbytes)) goto out; buffer = malloc ((n=nbytes+2)); if (!buffer) goto out; if (RegQueryValueEx (handle, name, 0, NULL, (PBYTE)buffer, &n)) { free (buffer); buffer = NULL; goto out; } n = WideCharToMultiByte (CP_UTF8, 0, buffer, nbytes, NULL, 0, NULL, NULL); if (n < 0 || (n+1) <= 0) goto out; result = malloc (n+1); if (!result) goto out; n = WideCharToMultiByte (CP_UTF8, 0, buffer, nbytes, result, n, NULL, NULL); if (n < 0) { free (result); result = NULL; goto out; } result[n] = 0; out: free (buffer); RegCloseKey (handle); return result; } #endif /*HAVE_W32CE_SYSTEM*/ #ifdef HAVE_W32CE_SYSTEM /* Replacement for getenv which takes care of the our use of getenv. The code is not thread safe but we expect it to work in all cases because it is called for the first time early enough. */ char * _assuan_getenv (const char *name) { static int initialized; static char *val_debug; static char *val_full_logging; if (!initialized) { val_debug = w32_read_registry (L"\\Software\\GNU\\libassuan", L"debug"); val_full_logging = w32_read_registry (L"\\Software\\GNU\\libassuan", L"full_logging"); initialized = 1; } if (!strcmp (name, "ASSUAN_DEBUG")) return val_debug; else if (!strcmp (name, "ASSUAN_FULL_LOGGING")) return val_full_logging; else return NULL; } #endif /*HAVE_W32CE_SYSTEM*/ diff --git a/src/versioninfo.rc.in b/src/versioninfo.rc.in index 23db15e..9f0237f 100644 --- a/src/versioninfo.rc.in +++ b/src/versioninfo.rc.in @@ -1,52 +1,52 @@ /* versioninfo.rc.in - for assuan * Copyright (C) 2005 g10 Code GmbH * * This file is free software; as a special exception the author gives * unlimited permission to copy and/or distribute it, with or without * modifications, as long as this notice is preserved. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * SPDX-License-Identifier: FSFULLR */ /* This file is processed by configure to create versioninfo.rc */ #line __LINE__ "versioninfo.rc.in" #include VS_VERSION_INFO VERSIONINFO FILEVERSION @BUILD_FILEVERSION@ PRODUCTVERSION @BUILD_FILEVERSION@ FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x21L #else FILEFLAGS 0x20L #endif FILEOS 0x40004L FILETYPE 0x1L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments", "Provided under the terms of the GNU Lesser General Public License.\0" VALUE "CompanyName", "g10 Code GmbH\0" VALUE "FileDescription", "Assuan - GnuPG IPC\0" VALUE "FileVersion", "@LIBASSUAN_LT_CURRENT@.@LIBASSUAN_LT_AGE@.@LIBASSUAN_LT_REVISION@.@BUILD_REVISION@\0" VALUE "InternalName", "libassuan\0" VALUE "LegalCopyright", "Copyright © 2001-2015 g10 Code GmbH\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "libassuan.dll\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "Assuan\0" VALUE "ProductVersion", "@VERSION@\0" VALUE "SpecialBuild", "@BUILD_TIMESTAMP@\0" END END END - diff --git a/src/w32-fd-t.inc.h b/src/w32-fd-t.inc.h index 37f20f4..ca63671 100644 --- a/src/w32-fd-t.inc.h +++ b/src/w32-fd-t.inc.h @@ -1,39 +1,40 @@ ## w32-fd-t.inc.h - Include fragment to build assuan.h. ## Copyright (C) 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. /* Because we use system handles and not libc low level file descriptors on W32, we need to declare them as HANDLE (which actually is a plain pointer). This is required to eventually support 64 bit Windows systems. */ typedef void *assuan_fd_t; #define ASSUAN_INVALID_FD ((void*)(-1)) #define ASSUAN_INVALID_PID ((pid_t) -1) static GPG_ERR_INLINE assuan_fd_t assuan_fd_from_posix_fd (int fd) { if (fd < 0) return ASSUAN_INVALID_FD; else return (assuan_fd_t) _get_osfhandle (fd); } ##EOF## diff --git a/src/w32-includes.inc.h b/src/w32-includes.inc.h index 1945692..5f4ab9f 100644 --- a/src/w32-includes.inc.h +++ b/src/w32-includes.inc.h @@ -1,24 +1,25 @@ ## w32-includes.inc.h - Include fragment to build assuan.h. ## Copyright (C) 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. #include -#include +#include ##EOF## diff --git a/src/w32-sock-nonce.inc.h b/src/w32-sock-nonce.inc.h index 4b099ff..b620b54 100644 --- a/src/w32-sock-nonce.inc.h +++ b/src/w32-sock-nonce.inc.h @@ -1,52 +1,53 @@ ## w32-sock-nonce.inc.h - Include fragment to build assuan.h. ## Copyright (C) 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. /* Assuan features an emulation of Unix domain sockets based on local TCP connections. To implement access permissions based on file permissions a nonce is used which is expected by the server as the first bytes received. This structure is used by the server to save the nonce created initially by bind. */ struct assuan_sock_nonce_s { size_t length; char nonce[16]; }; typedef struct assuan_sock_nonce_s assuan_sock_nonce_t; /* Define the Unix domain socket structure for Windows. */ #ifndef _ASSUAN_NO_SOCKET_WRAPPER # ifndef AF_LOCAL # define AF_LOCAL AF_UNIX # endif # ifndef EADDRINUSE # define EADDRINUSE WSAEADDRINUSE # endif struct sockaddr_un { short sun_family; unsigned short sun_port; struct in_addr sun_addr; char sun_path[108-2-4]; }; #endif ##EOF## diff --git a/src/w32-sys-pth-impl.h b/src/w32-sys-pth-impl.h index ebd6ec6..0150e6e 100644 --- a/src/w32-sys-pth-impl.h +++ b/src/w32-sys-pth-impl.h @@ -1,39 +1,40 @@ ## w32-sys-pth-impl.h - Include fragment to build assuan.h. ## Copyright (C) 2009, 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. ## ## Warning: This is a fragment of a macro - no empty lines please. static int _assuan_pth_recvmsg (assuan_context_t ctx, assuan_fd_t fd, \ assuan_msghdr_t msg, int flags) \ { \ (void) ctx; \ gpg_err_set_errno (ENOSYS); \ return -1; \ } \ static int _assuan_pth_sendmsg (assuan_context_t ctx, assuan_fd_t fd, \ const assuan_msghdr_t msg, int flags) \ { \ (void) ctx; \ gpg_err_set_errno (ENOSYS); \ return -1; \ } \ ##EOF## Force end-of file. diff --git a/src/w32-types.inc.h b/src/w32-types.inc.h index 35aaec9..6cf9ad2 100644 --- a/src/w32-types.inc.h +++ b/src/w32-types.inc.h @@ -1,29 +1,30 @@ ## w32-types.inc.h - Include fragment to build assuan.h. ## Copyright (C) 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. typedef void *assuan_msghdr_t; #ifdef _MSC_VER typedef long ssize_t; typedef int pid_t; #endif ##EOF## diff --git a/src/w32ce-add.h b/src/w32ce-add.h index 5306f9a..1f7d7db 100644 --- a/src/w32ce-add.h +++ b/src/w32ce-add.h @@ -1,33 +1,34 @@ ## w32ce-add.h - Include fragment to build assuan.h. ## Copyright (C) 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. HANDLE _assuan_w32ce_prepare_pipe (int *r_rvid, int write_end); HANDLE _assuan_w32ce_finish_pipe (int rvid, int write_end); DWORD _assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd, LPSECURITY_ATTRIBUTES sec_attr, DWORD size); #define CreatePipe(a,b,c,d) _assuan_w32ce_create_pipe ((a),(b),(c),(d)) /* Magic handle values. Let's hope those never occur legitimately as handles or sockets. (Sockets are numbered sequentially from 0, while handles seem aligned to wordsize. */ #define ASSUAN_STDIN (void*)0x7ffffffd #define ASSUAN_STDOUT (void*)0x7fffffff diff --git a/src/w32ce-fd-t.inc.h b/src/w32ce-fd-t.inc.h index 2de72e6..70c7997 100644 --- a/src/w32ce-fd-t.inc.h +++ b/src/w32ce-fd-t.inc.h @@ -1,32 +1,33 @@ ## w32ce-fd-t.inc.h - Include fragment to build assuan.h. ## Copyright (C) 2010 Free Software Foundation, Inc. ## ## This file is part of Assuan. ## ## Assuan 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. ## ## Assuan 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+ ## ## ## This file is included by the mkheader tool. Lines starting with ## a double hash mark are not copied to the destination file. typedef void *assuan_fd_t; #define ASSUAN_INVALID_FD ((void*)(-1)) #define ASSUAN_INVALID_PID ((pid_t) -1) static GPG_ERR_INLINE assuan_fd_t assuan_fd_from_posix_fd (int fd) { return (assuan_fd_t)(fd); } ##EOF##