summaryrefslogtreecommitdiffstats
path: root/g13
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 09:59:15 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 09:59:15 +0000
commit8de1ee1b2b676b0d07586f0752750dd6b0fb7511 (patch)
treedd46fd7dc3863045696cd0e48032d8a36fa0daf5 /g13
parentInitial commit. (diff)
downloadgnupg2-8de1ee1b2b676b0d07586f0752750dd6b0fb7511.tar.xz
gnupg2-8de1ee1b2b676b0d07586f0752750dd6b0fb7511.zip
Adding upstream version 2.2.27.upstream/2.2.27upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'g13')
-rw-r--r--g13/ChangeLog-201114
-rw-r--r--g13/Makefile.am83
-rw-r--r--g13/Makefile.in1069
-rw-r--r--g13/all-tests.scm35
-rw-r--r--g13/backend.c296
-rw-r--r--g13/backend.h49
-rw-r--r--g13/be-dmcrypt.c116
-rw-r--r--g13/be-dmcrypt.h36
-rw-r--r--g13/be-encfs.c471
-rw-r--r--g13/be-encfs.h41
-rw-r--r--g13/be-truecrypt.c37
-rw-r--r--g13/be-truecrypt.h28
-rw-r--r--g13/call-syshelp.c631
-rw-r--r--g13/call-syshelp.h42
-rw-r--r--g13/create.c303
-rw-r--r--g13/create.h29
-rw-r--r--g13/g13-common.c86
-rw-r--r--g13/g13-common.h96
-rw-r--r--g13/g13-syshelp.c740
-rw-r--r--g13/g13-syshelp.h96
-rw-r--r--g13/g13.c1050
-rw-r--r--g13/g13.h60
-rw-r--r--g13/g13tuple.c340
-rw-r--r--g13/g13tuple.h52
-rw-r--r--g13/keyblob.c207
-rw-r--r--g13/keyblob.h162
-rw-r--r--g13/mount.c265
-rw-r--r--g13/mount.h31
-rw-r--r--g13/mountinfo.c198
-rw-r--r--g13/mountinfo.h40
-rw-r--r--g13/runner.c539
-rw-r--r--g13/runner.h76
-rw-r--r--g13/server.c783
-rw-r--r--g13/server.h32
-rw-r--r--g13/sh-blockdev.c152
-rw-r--r--g13/sh-cmd.c917
-rw-r--r--g13/sh-dmcrypt.c1043
-rw-r--r--g13/suspend.c145
-rw-r--r--g13/suspend.h26
-rw-r--r--g13/t-g13tuple.c223
40 files changed, 10639 insertions, 0 deletions
diff --git a/g13/ChangeLog-2011 b/g13/ChangeLog-2011
new file mode 100644
index 0000000..5d372c2
--- /dev/null
+++ b/g13/ChangeLog-2011
@@ -0,0 +1,14 @@
+2011-12-01 Werner Koch <wk@g10code.com>
+
+ NB: ChangeLog files are no longer manually maintained. Starting
+ on December 1st, 2011 we put change information only in the GIT
+ commit log, and generate a top-level ChangeLog file from logs at
+ "make dist". See doc/HACKING for details.
+
+2009-11-04 Werner Koch <wk@g10code.com>
+
+ Under initial development - no need for a ChangeLog.
+
+Local Variables:
+buffer-read-only: t
+End:
diff --git a/g13/Makefile.am b/g13/Makefile.am
new file mode 100644
index 0000000..ef5671f
--- /dev/null
+++ b/g13/Makefile.am
@@ -0,0 +1,83 @@
+# g13/Makefile.am
+# Copyright (C) 2009 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+
+## Process this file with automake to produce Makefile.in
+
+EXTRA_DIST = ChangeLog-2011 all-tests.scm
+
+bin_PROGRAMS = g13
+sbin_PROGRAMS = g13-syshelp
+
+noinst_PROGRAMS = $(module_tests)
+if DISABLE_TESTS
+TESTS =
+else
+TESTS = $(module_tests)
+endif
+
+AM_CPPFLAGS =
+
+include $(top_srcdir)/am/cmacros.am
+
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS)
+
+g13_SOURCES = \
+ g13.c g13.h \
+ g13-common.c g13-common.h \
+ keyblob.c keyblob.h \
+ g13tuple.c g13tuple.h \
+ server.c server.h \
+ create.c create.h \
+ mount.c mount.h \
+ suspend.c suspend.h \
+ mountinfo.c mountinfo.h \
+ call-syshelp.c call-syshelp.h \
+ runner.c runner.h \
+ backend.c backend.h \
+ be-encfs.c be-encfs.h \
+ be-truecrypt.c be-truecrypt.h \
+ be-dmcrypt.c be-dmcrypt.h
+
+g13_LDADD = $(libcommonpth) \
+ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
+ $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV)
+
+
+g13_syshelp_SOURCES = \
+ g13-syshelp.c g13-syshelp.h \
+ g13-common.c g13-common.h \
+ keyblob.c keyblob.h \
+ g13tuple.c g13tuple.h \
+ sh-cmd.c \
+ sh-blockdev.c \
+ sh-dmcrypt.c
+
+g13_syshelp_LDADD = $(libcommon) \
+ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) \
+ $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV)
+
+
+module_tests = t-g13tuple
+t_common_ldadd = $(libcommon) $(LIBGCRYPT_LIBS) \
+ $(LIBASSUAN_LIBS) $(LIBICONV)
+
+t_g13tuple_SOURCES = t-g13tuple.c g13tuple.c
+t_g13tuple_LDADD = $(t_common_ldadd)
+
+
+$(PROGRAMS) : $(libcommon) $(libcommonpth)
diff --git a/g13/Makefile.in b/g13/Makefile.in
new file mode 100644
index 0000000..26489ec
--- /dev/null
+++ b/g13/Makefile.in
@@ -0,0 +1,1069 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# 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.
+
+@SET_MAKE@
+
+# g13/Makefile.am
+# Copyright (C) 2009 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+
+# cmacros.am - C macro definitions
+# Copyright (C) 2004 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = g13$(EXEEXT)
+sbin_PROGRAMS = g13-syshelp$(EXEEXT)
+noinst_PROGRAMS = $(am__EXEEXT_1)
+@DISABLE_TESTS_FALSE@TESTS = $(am__EXEEXT_1)
+@HAVE_DOSISH_SYSTEM_FALSE@am__append_1 = -DGNUPG_BINDIR="\"$(bindir)\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LOCALSTATEDIR="\"$(localstatedir)\""
+
+
+# If a specific protect tool program has been defined, pass its name
+# to cc. Note that these macros should not be used directly but via
+# the gnupg_module_name function.
+@GNUPG_AGENT_PGM_TRUE@am__append_2 = -DGNUPG_DEFAULT_AGENT="\"@GNUPG_AGENT_PGM@\""
+@GNUPG_PINENTRY_PGM_TRUE@am__append_3 = -DGNUPG_DEFAULT_PINENTRY="\"@GNUPG_PINENTRY_PGM@\""
+@GNUPG_SCDAEMON_PGM_TRUE@am__append_4 = -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\""
+@GNUPG_DIRMNGR_PGM_TRUE@am__append_5 = -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\""
+@GNUPG_PROTECT_TOOL_PGM_TRUE@am__append_6 = -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\""
+@GNUPG_DIRMNGR_LDAP_PGM_TRUE@am__append_7 = -DGNUPG_DEFAULT_DIRMNGR_LDAP="\"@GNUPG_DIRMNGR_LDAP_PGM@\""
+subdir = g13
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \
+ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \
+ $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \
+ $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \
+ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \
+ $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \
+ $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)"
+am__EXEEXT_1 = t-g13tuple$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) $(sbin_PROGRAMS)
+am_g13_OBJECTS = g13.$(OBJEXT) g13-common.$(OBJEXT) keyblob.$(OBJEXT) \
+ g13tuple.$(OBJEXT) server.$(OBJEXT) create.$(OBJEXT) \
+ mount.$(OBJEXT) suspend.$(OBJEXT) mountinfo.$(OBJEXT) \
+ call-syshelp.$(OBJEXT) runner.$(OBJEXT) backend.$(OBJEXT) \
+ be-encfs.$(OBJEXT) be-truecrypt.$(OBJEXT) be-dmcrypt.$(OBJEXT)
+g13_OBJECTS = $(am_g13_OBJECTS)
+am__DEPENDENCIES_1 =
+g13_DEPENDENCIES = $(libcommonpth) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am_g13_syshelp_OBJECTS = g13-syshelp.$(OBJEXT) g13-common.$(OBJEXT) \
+ keyblob.$(OBJEXT) g13tuple.$(OBJEXT) sh-cmd.$(OBJEXT) \
+ sh-blockdev.$(OBJEXT) sh-dmcrypt.$(OBJEXT)
+g13_syshelp_OBJECTS = $(am_g13_syshelp_OBJECTS)
+g13_syshelp_DEPENDENCIES = $(libcommon) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_t_g13tuple_OBJECTS = t-g13tuple.$(OBJEXT) g13tuple.$(OBJEXT)
+t_g13tuple_OBJECTS = $(am_t_g13tuple_OBJECTS)
+am__DEPENDENCIES_2 = $(libcommon) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+t_g13tuple_DEPENDENCIES = $(am__DEPENDENCIES_2)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/backend.Po ./$(DEPDIR)/be-dmcrypt.Po \
+ ./$(DEPDIR)/be-encfs.Po ./$(DEPDIR)/be-truecrypt.Po \
+ ./$(DEPDIR)/call-syshelp.Po ./$(DEPDIR)/create.Po \
+ ./$(DEPDIR)/g13-common.Po ./$(DEPDIR)/g13-syshelp.Po \
+ ./$(DEPDIR)/g13.Po ./$(DEPDIR)/g13tuple.Po \
+ ./$(DEPDIR)/keyblob.Po ./$(DEPDIR)/mount.Po \
+ ./$(DEPDIR)/mountinfo.Po ./$(DEPDIR)/runner.Po \
+ ./$(DEPDIR)/server.Po ./$(DEPDIR)/sh-blockdev.Po \
+ ./$(DEPDIR)/sh-cmd.Po ./$(DEPDIR)/sh-dmcrypt.Po \
+ ./$(DEPDIR)/suspend.Po ./$(DEPDIR)/t-g13tuple.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(g13_SOURCES) $(g13_syshelp_SOURCES) $(t_g13tuple_SOURCES)
+DIST_SOURCES = $(g13_SOURCES) $(g13_syshelp_SOURCES) \
+ $(t_g13tuple_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/am/cmacros.am \
+ $(top_srcdir)/build-aux/depcomp \
+ $(top_srcdir)/build-aux/mkinstalldirs
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+AWK_HEX_NUMBER_OPTION = @AWK_HEX_NUMBER_OPTION@
+BUILD_FILEVERSION = @BUILD_FILEVERSION@
+BUILD_HOSTNAME = @BUILD_HOSTNAME@
+BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
+BUILD_REVISION = @BUILD_REVISION@
+BUILD_TIMESTAMP = @BUILD_TIMESTAMP@
+BUILD_VERSION = @BUILD_VERSION@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DL_LIBS = @DL_LIBS@
+DNSLIBS = @DNSLIBS@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENCFS = @ENCFS@
+EXEEXT = @EXEEXT@
+FUSERMOUNT = @FUSERMOUNT@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUPG_AGENT_PGM = @GNUPG_AGENT_PGM@
+GNUPG_DIRMNGR_LDAP_PGM = @GNUPG_DIRMNGR_LDAP_PGM@
+GNUPG_DIRMNGR_PGM = @GNUPG_DIRMNGR_PGM@
+GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@
+GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@
+GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@
+GPGKEYS_LDAP = @GPGKEYS_LDAP@
+GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@
+GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@
+GPG_ERROR_LIBS = @GPG_ERROR_LIBS@
+GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@
+GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+KSBA_CFLAGS = @KSBA_CFLAGS@
+KSBA_CONFIG = @KSBA_CONFIG@
+KSBA_LIBS = @KSBA_LIBS@
+LBER_LIBS = @LBER_LIBS@
+LDAPLIBS = @LDAPLIBS@
+LDAP_CPPFLAGS = @LDAP_CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@
+LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@
+LIBASSUAN_LIBS = @LIBASSUAN_LIBS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
+LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBREADLINE = @LIBREADLINE@
+LIBS = @LIBS@
+LIBUSB_CPPFLAGS = @LIBUSB_CPPFLAGS@
+LIBUSB_LIBS = @LIBUSB_LIBS@
+LIBUTIL_LIBS = @LIBUTIL_LIBS@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NETLIBS = @NETLIBS@
+NPTH_CFLAGS = @NPTH_CFLAGS@
+NPTH_CONFIG = @NPTH_CONFIG@
+NPTH_LIBS = @NPTH_LIBS@
+NTBTLS_CFLAGS = @NTBTLS_CFLAGS@
+NTBTLS_CONFIG = @NTBTLS_CONFIG@
+NTBTLS_LIBS = @NTBTLS_LIBS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_GT = @PACKAGE_GT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SHRED = @SHRED@
+SQLITE3_CFLAGS = @SQLITE3_CFLAGS@
+SQLITE3_LIBS = @SQLITE3_LIBS@
+STRIP = @STRIP@
+SYSROOT = @SYSROOT@
+SYS_SOCKET_H = @SYS_SOCKET_H@
+TAR = @TAR@
+USE_C99_CFLAGS = @USE_C99_CFLAGS@
+USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+W32SOCKLIBS = @W32SOCKLIBS@
+WINDRES = @WINDRES@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+YAT2M = @YAT2M@
+ZLIBS = @ZLIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = $(datadir)/locale
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = ChangeLog-2011 all-tests.scm
+
+# NB: AM_CFLAGS may also be used by tools running on the build
+# platform to create source files.
+AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" $(am__append_1) \
+ $(am__append_2) $(am__append_3) $(am__append_4) \
+ $(am__append_5) $(am__append_6) $(am__append_7)
+@HAVE_W32CE_SYSTEM_FALSE@extra_sys_libs =
+
+# Under Windows we use LockFileEx. WindowsCE provides this only on
+# the WindowsMobile 6 platform and thus we need to use the coredll6
+# import library. We also want to use a stacksize of 256k instead of
+# the 2MB which is the default with cegcc. 256k is the largest stack
+# we use with pth.
+@HAVE_W32CE_SYSTEM_TRUE@extra_sys_libs = -lcoredll6
+@HAVE_W32CE_SYSTEM_FALSE@extra_bin_ldflags =
+@HAVE_W32CE_SYSTEM_TRUE@extra_bin_ldflags = -Wl,--stack=0x40000
+resource_objs =
+
+# Convenience macros
+libcommon = ../common/libcommon.a
+libcommonpth = ../common/libcommonpth.a
+libcommontls = ../common/libcommontls.a
+libcommontlsnpth = ../common/libcommontlsnpth.a
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS)
+g13_SOURCES = \
+ g13.c g13.h \
+ g13-common.c g13-common.h \
+ keyblob.c keyblob.h \
+ g13tuple.c g13tuple.h \
+ server.c server.h \
+ create.c create.h \
+ mount.c mount.h \
+ suspend.c suspend.h \
+ mountinfo.c mountinfo.h \
+ call-syshelp.c call-syshelp.h \
+ runner.c runner.h \
+ backend.c backend.h \
+ be-encfs.c be-encfs.h \
+ be-truecrypt.c be-truecrypt.h \
+ be-dmcrypt.c be-dmcrypt.h
+
+g13_LDADD = $(libcommonpth) \
+ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
+ $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV)
+
+g13_syshelp_SOURCES = \
+ g13-syshelp.c g13-syshelp.h \
+ g13-common.c g13-common.h \
+ keyblob.c keyblob.h \
+ g13tuple.c g13tuple.h \
+ sh-cmd.c \
+ sh-blockdev.c \
+ sh-dmcrypt.c
+
+g13_syshelp_LDADD = $(libcommon) \
+ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) \
+ $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV)
+
+module_tests = t-g13tuple
+t_common_ldadd = $(libcommon) $(LIBGCRYPT_LIBS) \
+ $(LIBASSUAN_LIBS) $(LIBICONV)
+
+t_g13tuple_SOURCES = t-g13tuple.c g13tuple.c
+t_g13tuple_LDADD = $(t_common_ldadd)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj .rc
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/am/cmacros.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu g13/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu g13/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/am/cmacros.am $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+clean-noinstPROGRAMS:
+ -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+
+g13$(EXEEXT): $(g13_OBJECTS) $(g13_DEPENDENCIES) $(EXTRA_g13_DEPENDENCIES)
+ @rm -f g13$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(g13_OBJECTS) $(g13_LDADD) $(LIBS)
+
+g13-syshelp$(EXEEXT): $(g13_syshelp_OBJECTS) $(g13_syshelp_DEPENDENCIES) $(EXTRA_g13_syshelp_DEPENDENCIES)
+ @rm -f g13-syshelp$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(g13_syshelp_OBJECTS) $(g13_syshelp_LDADD) $(LIBS)
+
+t-g13tuple$(EXEEXT): $(t_g13tuple_OBJECTS) $(t_g13tuple_DEPENDENCIES) $(EXTRA_t_g13tuple_DEPENDENCIES)
+ @rm -f t-g13tuple$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(t_g13tuple_OBJECTS) $(t_g13tuple_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be-dmcrypt.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be-encfs.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be-truecrypt.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-syshelp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/create.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/g13-common.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/g13-syshelp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/g13.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/g13tuple.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyblob.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountinfo.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/runner.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sh-blockdev.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sh-cmd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sh-dmcrypt.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/suspend.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-g13tuple.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ fi; \
+ echo "$${col}$$dashes$${std}"; \
+ echo "$${col}$$banner$${std}"; \
+ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+ test -z "$$report" || echo "$${col}$$report$${std}"; \
+ echo "$${col}$$dashes$${std}"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \
+ clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/backend.Po
+ -rm -f ./$(DEPDIR)/be-dmcrypt.Po
+ -rm -f ./$(DEPDIR)/be-encfs.Po
+ -rm -f ./$(DEPDIR)/be-truecrypt.Po
+ -rm -f ./$(DEPDIR)/call-syshelp.Po
+ -rm -f ./$(DEPDIR)/create.Po
+ -rm -f ./$(DEPDIR)/g13-common.Po
+ -rm -f ./$(DEPDIR)/g13-syshelp.Po
+ -rm -f ./$(DEPDIR)/g13.Po
+ -rm -f ./$(DEPDIR)/g13tuple.Po
+ -rm -f ./$(DEPDIR)/keyblob.Po
+ -rm -f ./$(DEPDIR)/mount.Po
+ -rm -f ./$(DEPDIR)/mountinfo.Po
+ -rm -f ./$(DEPDIR)/runner.Po
+ -rm -f ./$(DEPDIR)/server.Po
+ -rm -f ./$(DEPDIR)/sh-blockdev.Po
+ -rm -f ./$(DEPDIR)/sh-cmd.Po
+ -rm -f ./$(DEPDIR)/sh-dmcrypt.Po
+ -rm -f ./$(DEPDIR)/suspend.Po
+ -rm -f ./$(DEPDIR)/t-g13tuple.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/backend.Po
+ -rm -f ./$(DEPDIR)/be-dmcrypt.Po
+ -rm -f ./$(DEPDIR)/be-encfs.Po
+ -rm -f ./$(DEPDIR)/be-truecrypt.Po
+ -rm -f ./$(DEPDIR)/call-syshelp.Po
+ -rm -f ./$(DEPDIR)/create.Po
+ -rm -f ./$(DEPDIR)/g13-common.Po
+ -rm -f ./$(DEPDIR)/g13-syshelp.Po
+ -rm -f ./$(DEPDIR)/g13.Po
+ -rm -f ./$(DEPDIR)/g13tuple.Po
+ -rm -f ./$(DEPDIR)/keyblob.Po
+ -rm -f ./$(DEPDIR)/mount.Po
+ -rm -f ./$(DEPDIR)/mountinfo.Po
+ -rm -f ./$(DEPDIR)/runner.Po
+ -rm -f ./$(DEPDIR)/server.Po
+ -rm -f ./$(DEPDIR)/sh-blockdev.Po
+ -rm -f ./$(DEPDIR)/sh-cmd.Po
+ -rm -f ./$(DEPDIR)/sh-dmcrypt.Po
+ -rm -f ./$(DEPDIR)/suspend.Po
+ -rm -f ./$(DEPDIR)/t-g13tuple.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-sbinPROGRAMS
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \
+ check-am clean clean-binPROGRAMS clean-generic \
+ clean-noinstPROGRAMS clean-sbinPROGRAMS cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-binPROGRAMS install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-sbinPROGRAMS install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-binPROGRAMS uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+@HAVE_W32_SYSTEM_TRUE@.rc.o:
+@HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@"
+
+$(PROGRAMS) : $(libcommon) $(libcommonpth)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/g13/all-tests.scm b/g13/all-tests.scm
new file mode 100644
index 0000000..69b1f24
--- /dev/null
+++ b/g13/all-tests.scm
@@ -0,0 +1,35 @@
+;; Copyright (C) 2017 g10 Code GmbH
+;;
+;; This file is part of GnuPG.
+;;
+;; GnuPG is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; GnuPG is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+(export all-tests
+ ;; Parse the Makefile.am to find all tests.
+
+ (load (with-path "makefile.scm"))
+
+ (define (expander filename port key)
+ (parse-makefile port key))
+
+ (define (parse filename key)
+ (parse-makefile-expand filename expander key))
+
+ (map (lambda (name)
+ (test::binary #f
+ (path-join "g13" name)
+ (path-join (getenv "objdir") "g13" name)))
+ (parse-makefile-expand (in-srcdir "g13" "Makefile.am")
+ (lambda (filename port key) (parse-makefile port key))
+ "module_tests")))
diff --git a/g13/backend.c b/g13/backend.c
new file mode 100644
index 0000000..a0a2675
--- /dev/null
+++ b/g13/backend.c
@@ -0,0 +1,296 @@
+/* backend.c - Dispatcher to the various backends.
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "g13.h"
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+#include "keyblob.h"
+#include "backend.h"
+#include "be-encfs.h"
+#include "be-truecrypt.h"
+#include "be-dmcrypt.h"
+#include "call-syshelp.h"
+
+#define no_such_backend(a) _no_such_backend ((a), __func__)
+static gpg_error_t
+_no_such_backend (int conttype, const char *func)
+{
+ log_error ("invalid backend %d given in %s - this is most likely a bug\n",
+ conttype, func);
+ return gpg_error (GPG_ERR_INTERNAL);
+}
+
+
+/* Parse NAME and return the corresponding content type. If the name
+ is not known, a error message is printed and zero returned. If
+ NAME is NULL the supported backend types are listed and 0 is
+ returned. */
+int
+be_parse_conttype_name (const char *name)
+{
+ static struct { const char *name; int conttype; } names[] = {
+ { "encfs", CONTTYPE_ENCFS },
+ { "dm-crypt", CONTTYPE_DM_CRYPT }
+ };
+ int i;
+
+ if (!name)
+ {
+ log_info ("Known backend types:\n");
+ for (i=0; i < DIM (names); i++)
+ log_info (" %s\n", names[i].name);
+ return 0;
+ }
+
+ for (i=0; i < DIM (names); i++)
+ {
+ if (!strcmp (names[i].name, name))
+ return names[i].conttype;
+ }
+
+ log_error ("invalid backend type '%s' given\n", name);
+ return 0;
+}
+
+
+/* Return true if CONTTYPE is supported by us. */
+int
+be_is_supported_conttype (int conttype)
+{
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ case CONTTYPE_DM_CRYPT:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+/* Create a lock file for the container FNAME and store the lock at
+ * R_LOCK and return 0. On error return an error code and store NULL
+ * at R_LOCK. */
+gpg_error_t
+be_take_lock_for_create (ctrl_t ctrl, const char *fname, dotlock_t *r_lock)
+{
+ gpg_error_t err;
+ dotlock_t lock = NULL;
+ struct stat sb;
+
+ *r_lock = NULL;
+
+ /* A DM-crypt container requires special treatment by using the
+ syshelper functions. */
+ if (ctrl->conttype == CONTTYPE_DM_CRYPT)
+ {
+ /* */
+ err = call_syshelp_set_device (ctrl, fname);
+ goto leave;
+ }
+
+
+ /* A quick check to see that no container with that name already
+ exists. */
+ if (!gnupg_access (fname, F_OK))
+ {
+ err = gpg_error (GPG_ERR_EEXIST);
+ goto leave;
+ }
+
+ /* Take a lock and proceed with the creation. If there is a lock we
+ immediately return an error because for creation it does not make
+ sense to wait. */
+ lock = dotlock_create (fname, 0);
+ if (!lock)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (dotlock_take (lock, 0))
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* Check again that the file does not exist. */
+ err = gnupg_stat (fname, &sb)? 0 : gpg_error (GPG_ERR_EEXIST);
+
+ leave:
+ if (!err)
+ {
+ *r_lock = lock;
+ lock = NULL;
+ }
+ dotlock_destroy (lock);
+ return err;
+}
+
+
+/* If the backend requires a separate file or directory for the
+ container, return its name by computing it from FNAME which gives
+ the g13 filename. The new file name is allocated and stored at
+ R_NAME, if this is expected to be a directory true is stored at
+ R_ISDIR. If no detached name is expected or an error occurs NULL
+ is stored at R_NAME. The function returns 0 on success or an error
+ code. */
+gpg_error_t
+be_get_detached_name (int conttype, const char *fname,
+ char **r_name, int *r_isdir)
+{
+ *r_name = NULL;
+ *r_isdir = 0;
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return be_encfs_get_detached_name (fname, r_name, r_isdir);
+
+ case CONTTYPE_DM_CRYPT:
+ return 0;
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
+
+
+gpg_error_t
+be_create_new_keys (int conttype, membuf_t *mb)
+{
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return be_encfs_create_new_keys (mb);
+
+ case CONTTYPE_TRUECRYPT:
+ return be_truecrypt_create_new_keys (mb);
+
+ case CONTTYPE_DM_CRYPT:
+ return 0;
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
+
+
+/* Dispatcher to the backend's create function. */
+gpg_error_t
+be_create_container (ctrl_t ctrl, int conttype,
+ const char *fname, int fd, tupledesc_t tuples,
+ unsigned int *r_id)
+{
+ (void)fd; /* Not yet used. */
+
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return be_encfs_create_container (ctrl, fname, tuples, r_id);
+
+ case CONTTYPE_DM_CRYPT:
+ return be_dmcrypt_create_container (ctrl);
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
+
+
+/* Dispatcher to the backend's mount function. */
+gpg_error_t
+be_mount_container (ctrl_t ctrl, int conttype,
+ const char *fname, const char *mountpoint,
+ tupledesc_t tuples, unsigned int *r_id)
+{
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return be_encfs_mount_container (ctrl, fname, mountpoint, tuples, r_id);
+
+ case CONTTYPE_DM_CRYPT:
+ return be_dmcrypt_mount_container (ctrl, fname, mountpoint, tuples);
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
+
+
+/* Dispatcher to the backend's umount function. */
+gpg_error_t
+be_umount_container (ctrl_t ctrl, int conttype, const char *fname)
+{
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ case CONTTYPE_DM_CRYPT:
+ return be_dmcrypt_umount_container (ctrl, fname);
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
+
+
+/* Dispatcher to the backend's suspend function. */
+gpg_error_t
+be_suspend_container (ctrl_t ctrl, int conttype, const char *fname)
+{
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ case CONTTYPE_DM_CRYPT:
+ return be_dmcrypt_suspend_container (ctrl, fname);
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
+
+
+/* Dispatcher to the backend's resume function. */
+gpg_error_t
+be_resume_container (ctrl_t ctrl, int conttype, const char *fname,
+ tupledesc_t tuples)
+{
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ case CONTTYPE_DM_CRYPT:
+ return be_dmcrypt_resume_container (ctrl, fname, tuples);
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
diff --git a/g13/backend.h b/g13/backend.h
new file mode 100644
index 0000000..2805d99
--- /dev/null
+++ b/g13/backend.h
@@ -0,0 +1,49 @@
+/* backend.h - Defs for the dispatcher to the various backends.
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_BACKEND_H
+#define G13_BACKEND_H
+
+#include "../common/membuf.h"
+#include "g13tuple.h"
+
+int be_parse_conttype_name (const char *name);
+int be_is_supported_conttype (int conttype);
+gpg_error_t be_take_lock_for_create (ctrl_t ctrl, const char *fname,
+ dotlock_t *r_lock);
+gpg_error_t be_get_detached_name (int conttype, const char *fname,
+ char **r_name, int *r_isdir);
+gpg_error_t be_create_new_keys (int conttype, membuf_t *mb);
+
+gpg_error_t be_create_container (ctrl_t ctrl, int conttype,
+ const char *fname, int fd,
+ tupledesc_t tuples,
+ unsigned int *r_id);
+gpg_error_t be_mount_container (ctrl_t ctrl, int conttype,
+ const char *fname, const char *mountpoint,
+ tupledesc_t tuples,
+ unsigned int *r_id);
+gpg_error_t be_umount_container (ctrl_t ctrl, int conttype, const char *fname);
+gpg_error_t be_suspend_container (ctrl_t ctrl, int conttype,
+ const char *fname);
+gpg_error_t be_resume_container (ctrl_t ctrl, int conttype,
+ const char *fname, tupledesc_t tuples);
+
+
+#endif /*G13_BACKEND_H*/
diff --git a/g13/be-dmcrypt.c b/g13/be-dmcrypt.c
new file mode 100644
index 0000000..59b586d
--- /dev/null
+++ b/g13/be-dmcrypt.c
@@ -0,0 +1,116 @@
+/* be-dmcrypt.c - The DM-Crypt based backend
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "g13.h"
+#include "../common/i18n.h"
+#include "keyblob.h"
+#include "call-syshelp.h"
+#include "be-dmcrypt.h"
+
+
+/* Create the container using the current device.
+ * information in TUPLES. */
+gpg_error_t
+be_dmcrypt_create_container (ctrl_t ctrl)
+{
+ gpg_error_t err;
+
+ err = call_syshelp_run_create (ctrl, CONTTYPE_DM_CRYPT);
+
+ return err;
+}
+
+
+/* Mount the container described by the filename FNAME and the keyblob
+ * information in TUPLES. On success the runner id is stored at R_ID. */
+gpg_error_t
+be_dmcrypt_mount_container (ctrl_t ctrl,
+ const char *fname, const char *mountpoint,
+ tupledesc_t tuples)
+{
+ gpg_error_t err;
+
+ err = call_syshelp_set_device (ctrl, fname);
+ if (err)
+ goto leave;
+
+ err = call_syshelp_run_mount (ctrl, CONTTYPE_DM_CRYPT, mountpoint, tuples);
+
+ leave:
+ return err;
+}
+
+
+/* Unmount the container described by the filename FNAME. */
+gpg_error_t
+be_dmcrypt_umount_container (ctrl_t ctrl, const char *fname)
+{
+ gpg_error_t err;
+
+ err = call_syshelp_set_device (ctrl, fname);
+ if (err)
+ goto leave;
+
+ err = call_syshelp_run_umount (ctrl, CONTTYPE_DM_CRYPT);
+
+ leave:
+ return err;
+}
+
+
+/* Suspend the container described by the filename FNAME. */
+gpg_error_t
+be_dmcrypt_suspend_container (ctrl_t ctrl, const char *fname)
+{
+ gpg_error_t err;
+
+ err = call_syshelp_set_device (ctrl, fname);
+ if (err)
+ goto leave;
+
+ err = call_syshelp_run_suspend (ctrl, CONTTYPE_DM_CRYPT);
+
+ leave:
+ return err;
+}
+
+
+/* Resume the container described by the filename FNAME and the keyblob
+ * information in TUPLES. */
+gpg_error_t
+be_dmcrypt_resume_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples)
+{
+ gpg_error_t err;
+
+ err = call_syshelp_set_device (ctrl, fname);
+ if (err)
+ goto leave;
+
+ err = call_syshelp_run_resume (ctrl, CONTTYPE_DM_CRYPT, tuples);
+
+ leave:
+ return err;
+}
diff --git a/g13/be-dmcrypt.h b/g13/be-dmcrypt.h
new file mode 100644
index 0000000..cc0fce5
--- /dev/null
+++ b/g13/be-dmcrypt.h
@@ -0,0 +1,36 @@
+/* be-dmcrypt.h - Public defs for the DM-Crypt based backend
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_BE_DMCRYPT_H
+#define G13_BE_DMCRYPT_H
+
+#include "backend.h"
+
+gpg_error_t be_dmcrypt_create_container (ctrl_t ctrl);
+gpg_error_t be_dmcrypt_mount_container (ctrl_t ctrl,
+ const char *fname,
+ const char *mountpoint,
+ tupledesc_t tuples);
+gpg_error_t be_dmcrypt_umount_container (ctrl_t ctrl, const char *fname);
+gpg_error_t be_dmcrypt_suspend_container (ctrl_t ctrl, const char *fname);
+gpg_error_t be_dmcrypt_resume_container (ctrl_t ctrl, const char *fname,
+ tupledesc_t tuples);
+
+
+#endif /*G13_BE_DMCRYPT_H*/
diff --git a/g13/be-encfs.c b/g13/be-encfs.c
new file mode 100644
index 0000000..0e2c68b
--- /dev/null
+++ b/g13/be-encfs.c
@@ -0,0 +1,471 @@
+/* be-encfs.c - The EncFS based backend
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "g13.h"
+#include "../common/i18n.h"
+#include "keyblob.h"
+#include "be-encfs.h"
+#include "runner.h"
+#include "../common/sysutils.h"
+#include "../common/exechelp.h"
+
+
+/* Command values used to run the encfs tool. */
+enum encfs_cmds
+ {
+ ENCFS_CMD_CREATE,
+ ENCFS_CMD_MOUNT,
+ ENCFS_CMD_UMOUNT
+ };
+
+
+/* An object to keep the private state of the encfs tool. It is
+ released by encfs_handler_cleanup. */
+struct encfs_parm_s
+{
+ enum encfs_cmds cmd; /* The current command. */
+ tupledesc_t tuples; /* NULL or the tuples object. */
+ char *mountpoint; /* The mountpoint. */
+};
+typedef struct encfs_parm_s *encfs_parm_t;
+
+
+static gpg_error_t
+send_cmd_bin (runner_t runner, const void *data, size_t datalen)
+{
+ return runner_send_line (runner, data, datalen);
+}
+
+
+static gpg_error_t
+send_cmd (runner_t runner, const char *string)
+{
+ log_debug ("sending command -->%s<--\n", string);
+ return send_cmd_bin (runner, string, strlen (string));
+}
+
+
+
+static void
+run_umount_helper (const char *mountpoint)
+{
+ gpg_error_t err;
+ const char pgmname[] = FUSERMOUNT;
+ const char *args[3];
+
+ args[0] = "-u";
+ args[1] = mountpoint;
+ args[2] = NULL;
+
+ err = gnupg_spawn_process_detached (pgmname, args, NULL);
+ if (err)
+ log_error ("failed to run '%s': %s\n",
+ pgmname, gpg_strerror (err));
+}
+
+
+/* Handle one line of the encfs tool's output. This function is
+ allowed to modify the content of BUFFER. */
+static gpg_error_t
+handle_status_line (runner_t runner, const char *line,
+ enum encfs_cmds cmd, tupledesc_t tuples)
+{
+ gpg_error_t err;
+
+ /* Check that encfs understands our new options. */
+ if (!strncmp (line, "$STATUS$", 8))
+ {
+ for (line +=8; *line && spacep (line); line++)
+ ;
+ log_info ("got status '%s'\n", line);
+ if (!strcmp (line, "fuse_main_start"))
+ {
+ /* Send a special error code back to let the caller know
+ that everything has been setup by encfs. */
+ err = gpg_error (GPG_ERR_UNFINISHED);
+ }
+ else
+ err = 0;
+ }
+ else if (!strncmp (line, "$PROMPT$", 8))
+ {
+ for (line +=8; *line && spacep (line); line++)
+ ;
+ log_info ("got prompt '%s'\n", line);
+ if (!strcmp (line, "create_root_dir"))
+ err = send_cmd (runner, cmd == ENCFS_CMD_CREATE? "y":"n");
+ else if (!strcmp (line, "create_mount_point"))
+ err = send_cmd (runner, "y");
+ else if (!strcmp (line, "passwd")
+ || !strcmp (line, "new_passwd"))
+ {
+ if (tuples)
+ {
+ size_t n;
+ const void *value;
+
+ value = find_tuple (tuples, KEYBLOB_TAG_ENCKEY, &n);
+ if (!value)
+ err = gpg_error (GPG_ERR_INV_SESSION_KEY);
+ else if ((err = send_cmd_bin (runner, value, n)))
+ {
+ if (gpg_err_code (err) == GPG_ERR_BUG
+ && gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
+ err = gpg_error (GPG_ERR_INV_SESSION_KEY);
+ }
+ }
+ else
+ err = gpg_error (GPG_ERR_NO_DATA);
+ }
+ else
+ err = send_cmd (runner, ""); /* Default to send an empty line. */
+ }
+ else if (strstr (line, "encfs: unrecognized option '"))
+ err = gpg_error (GPG_ERR_INV_ENGINE);
+ else
+ err = 0;
+
+ return err;
+}
+
+
+/* The main processing function as used by the runner. */
+static gpg_error_t
+encfs_handler (void *opaque, runner_t runner, const char *status_line)
+{
+ encfs_parm_t parm = opaque;
+ gpg_error_t err;
+
+ if (!parm || !runner)
+ return gpg_error (GPG_ERR_BUG);
+ if (!status_line)
+ {
+ /* Runner requested internal flushing - nothing to do here. */
+ return 0;
+ }
+
+ err = handle_status_line (runner, status_line, parm->cmd, parm->tuples);
+ if (gpg_err_code (err) == GPG_ERR_UNFINISHED
+ && gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
+ {
+ err = 0;
+ /* No more need for the tuples. */
+ destroy_tupledesc (parm->tuples);
+ parm->tuples = NULL;
+
+ if (parm->cmd == ENCFS_CMD_CREATE)
+ {
+ /* The encfs tool keeps on running after creation of the
+ container. We don't want that and thus need to stop the
+ encfs process. */
+ run_umount_helper (parm->mountpoint);
+ /* In case the umount helper does not work we try to kill
+ the engine. FIXME: We should figure out how to make
+ fusermount work. */
+ runner_cancel (runner);
+ }
+ }
+
+ return err;
+}
+
+
+/* Called by the runner to cleanup the private data. */
+static void
+encfs_handler_cleanup (void *opaque)
+{
+ encfs_parm_t parm = opaque;
+
+ if (!parm)
+ return;
+
+ destroy_tupledesc (parm->tuples);
+ xfree (parm->mountpoint);
+ xfree (parm);
+}
+
+
+/* Run the encfs tool. */
+static gpg_error_t
+run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd,
+ const char *rawdir, const char *mountpoint, tupledesc_t tuples,
+ unsigned int *r_id)
+{
+ gpg_error_t err;
+ encfs_parm_t parm;
+ runner_t runner = NULL;
+ int outbound[2] = { -1, -1 };
+ int inbound[2] = { -1, -1 };
+ const char *pgmname;
+ const char *argv[10];
+ pid_t pid = (pid_t)(-1);
+ int idx;
+
+ (void)ctrl;
+
+ parm = xtrycalloc (1, sizeof *parm);
+ if (!parm)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ parm->cmd = cmd;
+ parm->tuples = ref_tupledesc (tuples);
+ parm->mountpoint = xtrystrdup (mountpoint);
+ if (!parm->mountpoint)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ err = runner_new (&runner, "encfs");
+ if (err)
+ goto leave;
+
+ err = gnupg_create_inbound_pipe (inbound, NULL, 0);
+ if (!err)
+ err = gnupg_create_outbound_pipe (outbound, NULL, 0);
+ if (err)
+ {
+ log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ pgmname = ENCFS;
+ idx = 0;
+ argv[idx++] = "-f";
+ if (opt.verbose)
+ argv[idx++] = "-v";
+ argv[idx++] = "--stdinpass";
+ argv[idx++] = "--annotate";
+ argv[idx++] = rawdir;
+ argv[idx++] = mountpoint;
+ argv[idx++] = NULL;
+ assert (idx <= DIM (argv));
+
+ err = gnupg_spawn_process_fd (pgmname, argv,
+ outbound[0], -1, inbound[1], &pid);
+ if (err)
+ {
+ log_error ("error spawning '%s': %s\n", pgmname, gpg_strerror (err));
+ goto leave;
+ }
+ close (outbound[0]); outbound[0] = -1;
+ close ( inbound[1]); inbound[1] = -1;
+
+ runner_set_fds (runner, inbound[0], outbound[1]);
+ inbound[0] = -1; /* Now owned by RUNNER. */
+ outbound[1] = -1; /* Now owned by RUNNER. */
+
+ runner_set_handler (runner, encfs_handler, encfs_handler_cleanup, parm);
+ parm = NULL; /* Now owned by RUNNER. */
+
+ runner_set_pid (runner, pid);
+ pid = (pid_t)(-1); /* The process is now owned by RUNNER. */
+
+ err = runner_spawn (runner);
+ if (err)
+ goto leave;
+
+ *r_id = runner_get_rid (runner);
+ log_info ("running '%s' in the background\n", pgmname);
+
+ leave:
+ if (inbound[0] != -1)
+ close (inbound[0]);
+ if (inbound[1] != -1)
+ close (inbound[1]);
+ if (outbound[0] != -1)
+ close (outbound[0]);
+ if (outbound[1] != -1)
+ close (outbound[1]);
+ if (pid != (pid_t)(-1))
+ {
+ gnupg_wait_process (pgmname, pid, 1, NULL);
+ gnupg_release_process (pid);
+ }
+ runner_release (runner);
+ encfs_handler_cleanup (parm);
+ return err;
+}
+
+
+
+
+
+/* See be_get_detached_name for a description. Note that the
+ dispatcher code makes sure that NULL is stored at R_NAME before
+ calling us. */
+gpg_error_t
+be_encfs_get_detached_name (const char *fname, char **r_name, int *r_isdir)
+{
+ char *result;
+
+ if (!fname || !*fname)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ result = strconcat (fname, ".d", NULL);
+ if (!result)
+ return gpg_error_from_syserror ();
+ *r_name = result;
+ *r_isdir = 1;
+ return 0;
+}
+
+
+/* Create a new session key and append it as a tuple to the memory
+ buffer MB.
+
+ The EncFS daemon takes a passphrase from stdin and internally
+ mangles it by means of some KDF from OpenSSL. We want to store a
+ binary key but we need to make sure that certain characters are not
+ used because the EncFS utility reads it from stdin and obviously
+ acts on some of the characters. This we replace CR (in case of an
+ MSDOS version of EncFS), LF (the delimiter used by EncFS) and Nul
+ (because it is unlikely to work). We use 32 bytes (256 bit)
+ because that is sufficient for the largest cipher (AES-256) and in
+ addition gives enough margin for a possible entropy degradation by
+ the KDF. */
+gpg_error_t
+be_encfs_create_new_keys (membuf_t *mb)
+{
+ char *buffer;
+ int i, j;
+
+ /* Allocate a buffer of 32 bytes plus 8 spare bytes we may need to
+ replace the unwanted values. */
+ buffer = xtrymalloc_secure (32+8);
+ if (!buffer)
+ return gpg_error_from_syserror ();
+
+ /* Randomize the buffer. STRONG random should be enough as it is a
+ good compromise between security and performance. The
+ anticipated usage of this tool is the quite often creation of new
+ containers and thus this should not deplete the system's entropy
+ tool too much. */
+ gcry_randomize (buffer, 32+8, GCRY_STRONG_RANDOM);
+ for (i=j=0; i < 32; i++)
+ {
+ if (buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == 0 )
+ {
+ /* Replace. */
+ if (j == 8)
+ {
+ /* Need to get more random. */
+ gcry_randomize (buffer+32, 8, GCRY_STRONG_RANDOM);
+ j = 0;
+ }
+ buffer[i] = buffer[32+j];
+ j++;
+ }
+ }
+
+ /* Store the key. */
+ append_tuple (mb, KEYBLOB_TAG_ENCKEY, buffer, 32);
+
+ /* Free the temporary buffer. */
+ wipememory (buffer, 32+8); /* A failsafe extra wiping. */
+ xfree (buffer);
+
+ return 0;
+}
+
+
+/* Create the container described by the filename FNAME and the keyblob
+ information in TUPLES. */
+gpg_error_t
+be_encfs_create_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples,
+ unsigned int *r_id)
+{
+ gpg_error_t err;
+ int dummy;
+ char *containername = NULL;
+ char *mountpoint = NULL;
+
+ err = be_encfs_get_detached_name (fname, &containername, &dummy);
+ if (err)
+ goto leave;
+
+ mountpoint = xtrystrdup ("/tmp/.#g13_XXXXXX");
+ if (!mountpoint)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (!gnupg_mkdtemp (mountpoint))
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("can't create directory '%s': %s\n"),
+ "/tmp/.#g13_XXXXXX", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = run_encfs_tool (ctrl, ENCFS_CMD_CREATE, containername, mountpoint,
+ tuples, r_id);
+
+ /* In any case remove the temporary mount point. */
+ if (rmdir (mountpoint))
+ log_error ("error removing temporary mount point '%s': %s\n",
+ mountpoint, gpg_strerror (gpg_error_from_syserror ()));
+
+
+ leave:
+ xfree (containername);
+ xfree (mountpoint);
+ return err;
+}
+
+
+/* Mount the container described by the filename FNAME and the keyblob
+ information in TUPLES. On success the runner id is stored at R_ID. */
+gpg_error_t
+be_encfs_mount_container (ctrl_t ctrl,
+ const char *fname, const char *mountpoint,
+ tupledesc_t tuples, unsigned int *r_id)
+{
+ gpg_error_t err;
+ int dummy;
+ char *containername = NULL;
+
+ if (!mountpoint)
+ {
+ log_error ("the encfs backend requires an explicit mountpoint\n");
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ goto leave;
+ }
+
+ err = be_encfs_get_detached_name (fname, &containername, &dummy);
+ if (err)
+ goto leave;
+
+ err = run_encfs_tool (ctrl, ENCFS_CMD_MOUNT, containername, mountpoint,
+ tuples, r_id);
+
+ leave:
+ xfree (containername);
+ return err;
+}
diff --git a/g13/be-encfs.h b/g13/be-encfs.h
new file mode 100644
index 0000000..1f1b8b3
--- /dev/null
+++ b/g13/be-encfs.h
@@ -0,0 +1,41 @@
+/* be-encfs.h - Public defs for the EncFS based backend
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_BE_ENCFS_H
+#define G13_BE_ENCFS_H
+
+#include "backend.h"
+
+gpg_error_t be_encfs_get_detached_name (const char *fname,
+ char **r_name, int *r_isdir);
+gpg_error_t be_encfs_create_new_keys (membuf_t *mb);
+
+gpg_error_t be_encfs_create_container (ctrl_t ctrl,
+ const char *fname,
+ tupledesc_t tuples,
+ unsigned int *r_id);
+
+gpg_error_t be_encfs_mount_container (ctrl_t ctrl,
+ const char *fname,
+ const char *mountpoint,
+ tupledesc_t tuples,
+ unsigned int *r_id);
+
+
+#endif /*G13_BE_ENCFS_H*/
diff --git a/g13/be-truecrypt.c b/g13/be-truecrypt.c
new file mode 100644
index 0000000..1ce992f
--- /dev/null
+++ b/g13/be-truecrypt.c
@@ -0,0 +1,37 @@
+/* be-truecrypt.c - The Truecrypt based backend
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "g13.h"
+#include "../common/i18n.h"
+#include "be-truecrypt.h"
+
+
+gpg_error_t
+be_truecrypt_create_new_keys (membuf_t *mb)
+{
+ (void)mb;
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+}
diff --git a/g13/be-truecrypt.h b/g13/be-truecrypt.h
new file mode 100644
index 0000000..d6d1e84
--- /dev/null
+++ b/g13/be-truecrypt.h
@@ -0,0 +1,28 @@
+/* be-truecrypt.h - Public defs for the Truecrypt based backend
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_BE_TRUECRYPT_H
+#define G13_BE_TRUECRYPT_H
+
+#include "backend.h"
+
+gpg_error_t be_truecrypt_create_new_keys (membuf_t *mb);
+
+
+#endif /*G13_BE_TRUECRYPT_H*/
diff --git a/g13/call-syshelp.c b/g13/call-syshelp.c
new file mode 100644
index 0000000..b160ba3
--- /dev/null
+++ b/g13/call-syshelp.c
@@ -0,0 +1,631 @@
+/* call-syshelp.c - Communication with g13-syshelp
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include <npth.h>
+
+#include "g13.h"
+#include <assuan.h>
+#include "../common/i18n.h"
+#include "g13tuple.h"
+#include "keyblob.h"
+#include "../common/membuf.h"
+#include "create.h"
+#include "call-syshelp.h"
+
+
+/* Local data for this module. A pointer to this is stored in the
+ CTRL object of each connection. */
+struct call_syshelp_s
+{
+ assuan_context_t assctx; /* The Assuan context for the current
+ g13-syshep connection. */
+};
+
+
+/* Parameter used with the CREATE command. */
+struct create_parm_s
+{
+ assuan_context_t ctx;
+ ctrl_t ctrl;
+ membuf_t plaintext;
+ unsigned int expect_plaintext:1;
+ unsigned int got_plaintext:1;
+};
+
+
+/* Parameter used with the MOUNT command. */
+struct mount_parm_s
+{
+ assuan_context_t ctx;
+ ctrl_t ctrl;
+ const void *keyblob;
+ size_t keybloblen;
+};
+
+
+
+
+
+/* Fork off the syshelp tool if this has not already been done. On
+ success stores the current Assuan context for the syshelp tool at
+ R_CTX. */
+static gpg_error_t
+start_syshelp (ctrl_t ctrl, assuan_context_t *r_ctx)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ assuan_fd_t no_close_list[3];
+ int i;
+
+ *r_ctx = NULL;
+
+ if (ctrl->syshelp_local && (*r_ctx = ctrl->syshelp_local->assctx))
+ return 0; /* Already set. */
+
+ if (opt.verbose)
+ log_info ("starting a new syshelp\n");
+
+ if (!ctrl->syshelp_local)
+ {
+ ctrl->syshelp_local = xtrycalloc (1, sizeof *ctrl->syshelp_local);
+ if (!ctrl->syshelp_local)
+ return gpg_error_from_syserror ();
+ }
+
+ if (es_fflush (NULL))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error flushing pending output: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ i = 0;
+ if (log_get_fd () != -1)
+ no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
+ no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr));
+ no_close_list[i] = ASSUAN_INVALID_FD;
+
+ err = assuan_new (&ctx);
+ if (err)
+ {
+ log_error ("can't allocate assuan context: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ /* Call userv to start g13-syshelp. This userv script needs to be
+ * installed under the name "gnupg-g13-syshelp":
+ *
+ * if ( glob service-user root
+ * )
+ * reset
+ * suppress-args
+ * execute /home/wk/b/gnupg/g13/g13-syshelp -v
+ * else
+ * error Nothing to do for this service-user
+ * fi
+ * quit
+ */
+ {
+ const char *argv[4];
+
+ argv[0] = "userv";
+ argv[1] = "root";
+ argv[2] = "gnupg-g13-syshelp";
+ argv[3] = NULL;
+
+ err = assuan_pipe_connect (ctx, "/usr/bin/userv", argv,
+ no_close_list, NULL, NULL, 0);
+ }
+ if (err)
+ {
+ log_error ("can't connect to '%s': %s %s\n",
+ "g13-syshelp", gpg_strerror (err), gpg_strsource (err));
+ log_info ("(is userv and its gnupg-g13-syshelp script installed?)\n");
+ assuan_release (ctx);
+ return err;
+ }
+
+ *r_ctx = ctrl->syshelp_local->assctx = ctx;
+
+ if (DBG_IPC)
+ log_debug ("connection to g13-syshelp established\n");
+
+ return 0;
+}
+
+
+/* Release local resources associated with CTRL. */
+void
+call_syshelp_release (ctrl_t ctrl)
+{
+ if (!ctrl)
+ return;
+ if (ctrl->syshelp_local)
+ {
+ assuan_release (ctrl->syshelp_local->assctx);
+ ctrl->syshelp_local->assctx = NULL;
+ xfree (ctrl->syshelp_local);
+ ctrl->syshelp_local = NULL;
+ }
+}
+
+
+
+/* Staus callback for call_syshelp_find_device. */
+static gpg_error_t
+finddevice_status_cb (void *opaque, const char *line)
+{
+ char **r_blockdev = opaque;
+ char *p;
+
+ if ((p = has_leading_keyword (line, "BLOCKDEV")) && *p && !*r_blockdev)
+ {
+ *r_blockdev = xtrystrdup (p);
+ if (!*r_blockdev)
+ return gpg_error_from_syserror ();
+ }
+
+ return 0;
+}
+
+
+/* Send the FINDDEVICE command to the syshelper. On success the name
+ * of the block device is stored at R_BLOCKDEV. */
+gpg_error_t
+call_syshelp_find_device (ctrl_t ctrl, const char *name, char **r_blockdev)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ char *line = NULL;
+ char *blockdev = NULL; /* The result. */
+
+ *r_blockdev = NULL;
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ line = xtryasprintf ("FINDDEVICE %s", name);
+ if (!line)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL,
+ finddevice_status_cb, &blockdev);
+ if (err)
+ goto leave;
+ if (!blockdev)
+ {
+ log_error ("status line for successful FINDDEVICE missing\n");
+ err = gpg_error (GPG_ERR_UNEXPECTED);
+ goto leave;
+ }
+ *r_blockdev = blockdev;
+ blockdev = NULL;
+
+ leave:
+ xfree (blockdev);
+ xfree (line);
+ return err;
+}
+
+
+
+static gpg_error_t
+getkeyblob_data_cb (void *opaque, const void *data, size_t datalen)
+{
+ membuf_t *mb = opaque;
+
+ if (data)
+ put_membuf (mb, data, datalen);
+
+ return 0;
+}
+
+
+/* Send the GTEKEYBLOB command to the syshelper. On success the
+ * encrypted keyblpob is stored at (R_ENCKEYBLOB,R_ENCKEYBLOBLEN). */
+gpg_error_t
+call_syshelp_get_keyblob (ctrl_t ctrl,
+ void **r_enckeyblob, size_t *r_enckeybloblen)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ membuf_t mb;
+
+ *r_enckeyblob = NULL;
+ *r_enckeybloblen = 0;
+ init_membuf (&mb, 512);
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ err = assuan_transact (ctx, "GETKEYBLOB",
+ getkeyblob_data_cb, &mb,
+ NULL, NULL, NULL, NULL);
+ if (err)
+ goto leave;
+ *r_enckeyblob = get_membuf (&mb, r_enckeybloblen);
+ if (!*r_enckeyblob)
+ err = gpg_error_from_syserror ();
+
+ leave:
+ xfree (get_membuf (&mb, NULL));
+ return err;
+}
+
+
+
+/* Send the DEVICE command to the syshelper. FNAME is the name of the
+ device. */
+gpg_error_t
+call_syshelp_set_device (ctrl_t ctrl, const char *fname)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ char *line = NULL;
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ line = xtryasprintf ("DEVICE %s", fname);
+ if (!line)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ leave:
+ xfree (line);
+ return err;
+}
+
+
+
+static gpg_error_t
+create_status_cb (void *opaque, const char *line)
+{
+ struct create_parm_s *parm = opaque;
+
+ if (has_leading_keyword (line, "PLAINTEXT_FOLLOWS"))
+ parm->expect_plaintext = 1;
+
+ return 0;
+}
+
+
+static gpg_error_t
+create_data_cb (void *opaque, const void *data, size_t datalen)
+{
+ struct create_parm_s *parm = opaque;
+ gpg_error_t err = 0;
+
+ if (!parm->expect_plaintext)
+ {
+ log_error ("status line for data missing\n");
+ err = gpg_error (GPG_ERR_UNEXPECTED);
+ }
+ else if (data)
+ {
+ put_membuf (&parm->plaintext, data, datalen);
+ }
+ else
+ {
+ parm->expect_plaintext = 0;
+ parm->got_plaintext = 1;
+ }
+
+ return err;
+}
+
+
+static gpg_error_t
+create_inq_cb (void *opaque, const char *line)
+{
+ struct create_parm_s *parm = opaque;
+ gpg_error_t err;
+
+ if (has_leading_keyword (line, "ENCKEYBLOB"))
+ {
+ void *plaintext;
+ size_t plaintextlen;
+
+ if (!parm->got_plaintext)
+ err = gpg_error (GPG_ERR_UNEXPECTED);
+ else if (!(plaintext = get_membuf (&parm->plaintext, &plaintextlen)))
+ err = gpg_error_from_syserror ();
+ else
+ {
+ void *ciphertext;
+ size_t ciphertextlen;
+
+ log_printhex (plaintext, plaintextlen, "plain");
+ err = g13_encrypt_keyblob (parm->ctrl,
+ plaintext, plaintextlen,
+ &ciphertext, &ciphertextlen);
+ wipememory (plaintext, plaintextlen);
+ xfree (plaintext);
+ if (err)
+ log_error ("error encrypting keyblob: %s\n", gpg_strerror (err));
+ else
+ {
+ err = assuan_send_data (parm->ctx, ciphertext, ciphertextlen);
+ xfree (ciphertext);
+ if (err)
+ log_error ("sending ciphertext to g13-syshelp failed: %s\n",
+ gpg_strerror (err));
+ }
+ }
+ }
+ else
+ err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+
+ return err;
+}
+
+
+/* Run the CREATE command on the current device. CONTTYPES gives the
+ requested content type for the new container. */
+gpg_error_t
+call_syshelp_run_create (ctrl_t ctrl, int conttype)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ struct create_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ /* tty_get ("waiting for debugger"); */
+ /* tty_kill_prompt (); */
+
+ parm.ctx = ctx;
+ parm.ctrl = ctrl;
+ init_membuf (&parm.plaintext, 512);
+ if (conttype == CONTTYPE_DM_CRYPT)
+ {
+ err = assuan_transact (ctx, "CREATE dm-crypt",
+ create_data_cb, &parm,
+ create_inq_cb, &parm,
+ create_status_cb, &parm);
+ }
+ else
+ {
+ log_error ("invalid backend type %d given\n", conttype);
+ err = GPG_ERR_INTERNAL;
+ goto leave;
+ }
+
+ leave:
+ xfree (get_membuf (&parm.plaintext, NULL));
+ return err;
+}
+
+
+
+static gpg_error_t
+mount_status_cb (void *opaque, const char *line)
+{
+ struct mount_parm_s *parm = opaque;
+
+ /* Nothing right now. */
+ (void)parm;
+ (void)line;
+
+ return 0;
+}
+
+
+/* Inquire callback for MOUNT and RESUME. */
+static gpg_error_t
+mount_inq_cb (void *opaque, const char *line)
+{
+ struct mount_parm_s *parm = opaque;
+ gpg_error_t err;
+
+ if (has_leading_keyword (line, "KEYBLOB"))
+ {
+ int setconfidential = !assuan_get_flag (parm->ctx, ASSUAN_CONFIDENTIAL);
+
+ if (setconfidential)
+ assuan_begin_confidential (parm->ctx);
+ err = assuan_send_data (parm->ctx, parm->keyblob, parm->keybloblen);
+ if (setconfidential)
+ assuan_end_confidential (parm->ctx);
+ if (err)
+ log_error ("sending keyblob to g13-syshelp failed: %s\n",
+ gpg_strerror (err));
+ }
+ else
+ err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+
+ return err;
+}
+
+
+/*
+ * Run the MOUNT command on the current device. CONTTYPES gives the
+ * requested content type for the new container. MOUNTPOINT the
+ * desired mount point or NULL for default.
+ */
+gpg_error_t
+call_syshelp_run_mount (ctrl_t ctrl, int conttype, const char *mountpoint,
+ tupledesc_t tuples)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ struct mount_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ /* tty_get ("waiting for debugger"); */
+ /* tty_kill_prompt (); */
+
+ parm.ctx = ctx;
+ parm.ctrl = ctrl;
+ if (conttype == CONTTYPE_DM_CRYPT)
+ {
+ ref_tupledesc (tuples);
+ parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
+ err = assuan_transact (ctx, "MOUNT dm-crypt",
+ NULL, NULL,
+ mount_inq_cb, &parm,
+ mount_status_cb, &parm);
+ unref_tupledesc (tuples);
+ }
+ else
+ {
+ (void)mountpoint; /* Not used. */
+ log_error ("invalid backend type %d given\n", conttype);
+ err = GPG_ERR_INTERNAL;
+ goto leave;
+ }
+
+ leave:
+ return err;
+}
+
+
+
+/*
+ * Run the UMOUNT command on the current device. CONTTYPES gives the
+ * content type of the container (fixme: Do we really need this?).
+ */
+gpg_error_t
+call_syshelp_run_umount (ctrl_t ctrl, int conttype)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ if (conttype == CONTTYPE_DM_CRYPT)
+ {
+ err = assuan_transact (ctx, "UMOUNT dm-crypt",
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
+ }
+ else
+ {
+ log_error ("invalid backend type %d given\n", conttype);
+ err = GPG_ERR_INTERNAL;
+ goto leave;
+ }
+
+ leave:
+ return err;
+}
+
+
+
+/*
+ * Run the SUSPEND command on the current device. CONTTYPES gives the
+ * requested content type for the new container.
+ */
+gpg_error_t
+call_syshelp_run_suspend (ctrl_t ctrl, int conttype)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ if (conttype == CONTTYPE_DM_CRYPT)
+ {
+ err = assuan_transact (ctx, "SUSPEND dm-crypt",
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
+ }
+ else
+ {
+ log_error ("invalid backend type %d given\n", conttype);
+ err = GPG_ERR_INTERNAL;
+ goto leave;
+ }
+
+ leave:
+ return err;
+}
+
+
+
+/* Run the RESUME command on the current device. CONTTYPES gives the
+ requested content type for the container. */
+gpg_error_t
+call_syshelp_run_resume (ctrl_t ctrl, int conttype, tupledesc_t tuples)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ struct mount_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ /* tty_get ("waiting for debugger"); */
+ /* tty_kill_prompt (); */
+
+ parm.ctx = ctx;
+ parm.ctrl = ctrl;
+ if (conttype == CONTTYPE_DM_CRYPT)
+ {
+ ref_tupledesc (tuples);
+ parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
+ err = assuan_transact (ctx, "RESUME dm-crypt",
+ NULL, NULL,
+ mount_inq_cb, &parm,
+ NULL, NULL);
+ unref_tupledesc (tuples);
+ }
+ else
+ {
+ log_error ("invalid backend type %d given\n", conttype);
+ err = GPG_ERR_INTERNAL;
+ goto leave;
+ }
+
+ leave:
+ return err;
+}
diff --git a/g13/call-syshelp.h b/g13/call-syshelp.h
new file mode 100644
index 0000000..3e83829
--- /dev/null
+++ b/g13/call-syshelp.h
@@ -0,0 +1,42 @@
+/* call-syshelp.h - Communication with g13-syshelp
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GNUPG_G13_CALL_SYSHELP_H
+#define GNUPG_G13_CALL_SYSHELP_H
+
+#include "g13tuple.h"
+
+void call_syshelp_release (ctrl_t ctrl);
+gpg_error_t call_syshelp_find_device (ctrl_t ctrl,
+ const char *name, char **r_blockdev);
+gpg_error_t call_syshelp_get_keyblob (ctrl_t ctrl,
+ void **r_enckeyblob,
+ size_t *r_enckeybloblen);
+gpg_error_t call_syshelp_set_device (ctrl_t ctrl, const char *fname);
+gpg_error_t call_syshelp_run_create (ctrl_t ctrl, int conttype);
+gpg_error_t call_syshelp_run_mount (ctrl_t ctrl, int conttype,
+ const char *mountpoint,
+ tupledesc_t tuples);
+gpg_error_t call_syshelp_run_umount (ctrl_t ctrl, int conttype);
+gpg_error_t call_syshelp_run_suspend (ctrl_t ctrl, int conttype);
+gpg_error_t call_syshelp_run_resume (ctrl_t ctrl, int conttype,
+ tupledesc_t tuples);
+
+
+#endif /*GNUPG_G13_CALL_SYSHELP_H*/
diff --git a/g13/create.c b/g13/create.c
new file mode 100644
index 0000000..ac4d130
--- /dev/null
+++ b/g13/create.c
@@ -0,0 +1,303 @@
+/* create.c - Create a new crypto container
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "g13.h"
+#include "../common/i18n.h"
+#include "create.h"
+
+#include "keyblob.h"
+#include "backend.h"
+#include "g13tuple.h"
+#include "../common/sysutils.h"
+#include "../common/call-gpg.h"
+
+/* Create a new blob with all the session keys and other meta
+ information which are to be stored encrypted in the crypto
+ container header. On success the malloced blob is stored at R_BLOB
+ and its length at R_BLOBLEN. On error an error code is returned
+ and (R_BLOB,R_BLOBLEN) are set to (NULL,0).
+
+ The format of this blob is a sequence of tag-length-value tuples.
+ All tuples have this format:
+
+ 2 byte TAG Big endian unsigned integer (0..65535)
+ described by the KEYBLOB_TAG_ constants.
+ 2 byte LENGTH Big endian unsigned integer (0..65535)
+ giving the length of the value.
+ length bytes VALUE The value described by the tag.
+
+ The first tag in a keyblob must be a BLOBVERSION. The other tags
+ depend on the type of the container as described by the CONTTYPE
+ tag. See keyblob.h for details. */
+static gpg_error_t
+create_new_keyblob (ctrl_t ctrl, int is_detached,
+ void **r_blob, size_t *r_bloblen)
+{
+ gpg_error_t err;
+ unsigned char twobyte[2];
+ membuf_t mb;
+
+ *r_blob = NULL;
+ *r_bloblen = 0;
+
+ init_membuf_secure (&mb, 512);
+
+ append_tuple (&mb, KEYBLOB_TAG_BLOBVERSION, "\x01", 1);
+
+ twobyte[0] = (ctrl->conttype >> 8);
+ twobyte[1] = (ctrl->conttype);
+ append_tuple (&mb, KEYBLOB_TAG_CONTTYPE, twobyte, 2);
+ if (is_detached)
+ append_tuple (&mb, KEYBLOB_TAG_DETACHED, NULL, 0);
+
+ err = be_create_new_keys (ctrl->conttype, &mb);
+ if (err)
+ goto leave;
+
+ /* Just for testing. */
+ append_tuple (&mb, KEYBLOB_TAG_FILLER, "filler", 6);
+
+ *r_blob = get_membuf (&mb, r_bloblen);
+ if (!*r_blob)
+ {
+ err = gpg_error_from_syserror ();
+ *r_bloblen = 0;
+ }
+ else
+ log_debug ("used keyblob size is %zu\n", *r_bloblen);
+
+ leave:
+ xfree (get_membuf (&mb, NULL));
+ return err;
+}
+
+
+
+/* Encrypt the keyblob (KEYBLOB,KEYBLOBLEN) and store the result at
+ (R_ENCBLOB, R_ENCBLOBLEN). Returns 0 on success or an error code.
+ On error R_EKYBLOB is set to NULL. Depending on the keys set in
+ CTRL the result is a single OpenPGP binary message, a single
+ special OpenPGP packet encapsulating a CMS message or a
+ concatenation of both with the CMS packet being the last. */
+gpg_error_t
+g13_encrypt_keyblob (ctrl_t ctrl, void *keyblob, size_t keybloblen,
+ void **r_encblob, size_t *r_encbloblen)
+{
+ gpg_error_t err;
+
+ /* FIXME: For now we only implement OpenPGP. */
+ err = gpg_encrypt_blob (ctrl, opt.gpg_program, opt.gpg_arguments,
+ keyblob, keybloblen,
+ ctrl->recipients,
+ r_encblob, r_encbloblen);
+
+ return err;
+}
+
+
+/* Write a new file under the name FILENAME with the keyblob and an
+ appropriate header. This function is called with a lock file in
+ place and after checking that the filename does not exists. */
+static gpg_error_t
+write_keyblob (const char *filename,
+ const void *keyblob, size_t keybloblen)
+{
+ gpg_error_t err;
+ estream_t fp;
+ unsigned char packet[32];
+ size_t headerlen, paddinglen;
+
+ fp = es_fopen (filename, "wbx");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error creating new container '%s': %s\n",
+ filename, gpg_strerror (err));
+ return err;
+ }
+
+ /* Allow for an least 8 times larger keyblob to accommodate for
+ future key changes. Round it up to 4096 byte. */
+ headerlen = ((32 + 8 * keybloblen + 16) + 4095) / 4096 * 4096;
+ paddinglen = headerlen - 32 - keybloblen;
+ assert (paddinglen >= 16);
+
+ packet[0] = (0xc0|61); /* CTB for the private packet type 0x61. */
+ packet[1] = 0xff; /* 5 byte length packet, value 20. */
+ packet[2] = 0;
+ packet[3] = 0;
+ packet[4] = 0;
+ packet[5] = 26;
+ memcpy (packet+6, "GnuPG/G13", 10); /* Packet subtype. */
+ packet[16] = 1; /* G13 packet format version. */
+ packet[17] = 0; /* Reserved. */
+ packet[18] = 0; /* Reserved. */
+ packet[19] = 0; /* OS Flag. */
+ packet[20] = (headerlen >> 24); /* Total length of header. */
+ packet[21] = (headerlen >> 16);
+ packet[22] = (headerlen >> 8);
+ packet[23] = (headerlen);
+ packet[24] = 1; /* Number of header copies. */
+ packet[25] = 0; /* Number of header copies at the end. */
+ packet[26] = 0; /* Reserved. */
+ packet[27] = 0; /* Reserved. */
+ packet[28] = 0; /* Reserved. */
+ packet[29] = 0; /* Reserved. */
+ packet[30] = 0; /* Reserved. */
+ packet[31] = 0; /* Reserved. */
+
+ if (es_fwrite (packet, 32, 1, fp) != 1)
+ goto writeerr;
+
+ if (es_fwrite (keyblob, keybloblen, 1, fp) != 1)
+ goto writeerr;
+
+ /* Write the padding. */
+ packet[0] = (0xc0|61); /* CTB for Private packet type 0x61. */
+ packet[1] = 0xff; /* 5 byte length packet, value 20. */
+ packet[2] = (paddinglen-6) >> 24;
+ packet[3] = (paddinglen-6) >> 16;
+ packet[4] = (paddinglen-6) >> 8;
+ packet[5] = (paddinglen-6);
+ memcpy (packet+6, "GnuPG/PAD", 10); /* Packet subtype. */
+ if (es_fwrite (packet, 16, 1, fp) != 1)
+ goto writeerr;
+ memset (packet, 0, 32);
+ for (paddinglen-=16; paddinglen >= 32; paddinglen -= 32)
+ if (es_fwrite (packet, 32, 1, fp) != 1)
+ goto writeerr;
+ if (paddinglen)
+ if (es_fwrite (packet, paddinglen, 1, fp) != 1)
+ goto writeerr;
+
+ if (es_fclose (fp))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error closing '%s': %s\n",
+ filename, gpg_strerror (err));
+ remove (filename);
+ return err;
+ }
+
+ return 0;
+
+
+ writeerr:
+ err = gpg_error_from_syserror ();
+ log_error ("error writing header to '%s': %s\n",
+ filename, gpg_strerror (err));
+ es_fclose (fp);
+ remove (filename);
+ return err;
+}
+
+
+
+/* Create a new container under the name FILENAME and initialize it
+ using the current settings. If the file already exists an error is
+ returned. */
+gpg_error_t
+g13_create_container (ctrl_t ctrl, const char *filename)
+{
+ gpg_error_t err;
+ dotlock_t lock;
+ void *keyblob = NULL;
+ size_t keybloblen;
+ void *enckeyblob = NULL;
+ size_t enckeybloblen;
+ char *detachedname = NULL;
+ int detachedisdir;
+ tupledesc_t tuples = NULL;
+ unsigned int dummy_rid;
+
+ if (!ctrl->recipients)
+ return gpg_error (GPG_ERR_NO_PUBKEY);
+
+ err = be_take_lock_for_create (ctrl, filename, &lock);
+ if (err)
+ goto leave;
+
+ /* And a possible detached file or directory may not exist either. */
+ err = be_get_detached_name (ctrl->conttype, filename,
+ &detachedname, &detachedisdir);
+ if (err)
+ goto leave;
+ if (detachedname)
+ {
+ struct stat sb;
+
+ if (!gnupg_stat (detachedname, &sb))
+ {
+ err = gpg_error (GPG_ERR_EEXIST);
+ goto leave;
+ }
+ }
+
+ if (ctrl->conttype != CONTTYPE_DM_CRYPT)
+ {
+ /* Create a new keyblob. */
+ err = create_new_keyblob (ctrl, !!detachedname, &keyblob, &keybloblen);
+ if (err)
+ goto leave;
+
+ /* Encrypt that keyblob. */
+ err = g13_encrypt_keyblob (ctrl, keyblob, keybloblen,
+ &enckeyblob, &enckeybloblen);
+ if (err)
+ goto leave;
+
+ /* Put a copy of the keyblob into a tuple structure. */
+ err = create_tupledesc (&tuples, keyblob, keybloblen);
+ if (err)
+ goto leave;
+ keyblob = NULL;
+ /* if (opt.verbose) */
+ /* dump_keyblob (tuples); */
+
+ /* Write out the header, the encrypted keyblob and some padding. */
+ err = write_keyblob (filename, enckeyblob, enckeybloblen);
+ if (err)
+ goto leave;
+ }
+
+ /* Create and append the container. FIXME: We should pass the
+ estream object in addition to the filename, so that the backend
+ can append the container to the g13 file. */
+ err = be_create_container (ctrl, ctrl->conttype, filename, -1, tuples,
+ &dummy_rid);
+
+
+ leave:
+ destroy_tupledesc (tuples);
+ xfree (detachedname);
+ xfree (enckeyblob);
+ xfree (keyblob);
+ dotlock_destroy (lock);
+
+ return err;
+}
diff --git a/g13/create.h b/g13/create.h
new file mode 100644
index 0000000..ccb954a
--- /dev/null
+++ b/g13/create.h
@@ -0,0 +1,29 @@
+/* create.h - Defs to create a new crypto container
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_CREATE_H
+#define G13_CREATE_H
+
+gpg_error_t g13_encrypt_keyblob (ctrl_t ctrl,
+ void *keyblob, size_t keybloblen,
+ void **r_encblob, size_t *r_encbloblen);
+gpg_error_t g13_create_container (ctrl_t ctrl, const char *filename);
+
+
+#endif /*G13_CREATE_H*/
diff --git a/g13/g13-common.c b/g13/g13-common.c
new file mode 100644
index 0000000..35cb131
--- /dev/null
+++ b/g13/g13-common.c
@@ -0,0 +1,86 @@
+/* g13-common.c - Common code for G13 modules
+ * Copyright (C) 2009, 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "g13-common.h"
+#include <gcrypt.h>
+#include <assuan.h>
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+
+
+
+/* Global variable to keep an error count. */
+int g13_errors_seen = 0;
+
+
+
+/* Note: This function is used by signal handlers!. */
+static void
+emergency_cleanup (void)
+{
+ gcry_control (GCRYCTL_TERM_SECMEM);
+}
+
+
+/* Wrapper around gnupg_init_signals. */
+void
+g13_init_signals (void)
+{
+ gnupg_init_signals (0, emergency_cleanup);
+}
+
+
+/* Install a regular exit handler to make real sure that the secure
+ memory gets wiped out. */
+void
+g13_install_emergency_cleanup (void)
+{
+ if (atexit (emergency_cleanup))
+ {
+ log_error ("atexit failed\n");
+ g13_exit (2);
+ }
+}
+
+
+/* Use this function instead of exit() in all g13 modules. */
+void
+g13_exit (int rc)
+{
+ gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE);
+ if (opt.debug & DBG_MEMSTAT_VALUE)
+ {
+ gcry_control( GCRYCTL_DUMP_MEMORY_STATS );
+ gcry_control( GCRYCTL_DUMP_RANDOM_STATS );
+ }
+ if (opt.debug)
+ gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
+ emergency_cleanup ();
+ rc = rc? rc : log_get_errorcount(0)? 2 : g13_errors_seen? 1 : 0;
+ exit (rc);
+}
diff --git a/g13/g13-common.h b/g13/g13-common.h
new file mode 100644
index 0000000..42b8dee
--- /dev/null
+++ b/g13/g13-common.h
@@ -0,0 +1,96 @@
+/* g13.h - Global definitions for G13.
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2009, 2015 Werner Koch.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_COMMON_H
+#define G13_COMMON_H
+
+#ifdef GPG_ERR_SOURCE_DEFAULT
+#error GPG_ERR_SOURCE_DEFAULT already defined
+#endif
+#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_G13
+#include <gpg-error.h>
+
+#include "../common/util.h"
+#include "../common/status.h"
+#include "../common/session-env.h"
+#include "../common/strlist.h"
+
+/* Debug values and macros. */
+#define DBG_MOUNT_VALUE 1 /* Debug mount or device stuff. */
+#define DBG_CRYPTO_VALUE 4 /* Debug low level crypto. */
+#define DBG_MEMORY_VALUE 32 /* Debug memory allocation stuff. */
+#define DBG_MEMSTAT_VALUE 128 /* Show memory statistics. */
+#define DBG_IPC_VALUE 1024 /* Debug assuan communication. */
+
+#define DBG_MOUNT (opt.debug & DBG_MOUNT_VALUE)
+#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
+#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
+#define DBG_IPC (opt.debug & DBG_IPC_VALUE)
+
+/* A large struct named "opt" to keep global flags. Note that this
+ struct is used by g13 and g13-syshelp and thus some fields may only
+ make sense for one of them. */
+EXTERN_UNLESS_MAIN_MODULE
+struct
+{
+ unsigned int debug; /* Debug flags (DBG_foo_VALUE). */
+ int verbose; /* Verbosity level. */
+ int quiet; /* Be as quiet as possible. */
+ int dry_run; /* Don't change any persistent data. */
+
+ const char *config_filename; /* Name of the used config file. */
+
+ /* Filename of the AGENT program. */
+ const char *agent_program;
+
+ /* Filename of the GPG program. Unless set via an program option it
+ is initialized at the first engine startup to the standard gpg
+ filename. */
+ const char *gpg_program;
+
+ /* GPG arguments. XXX: Currently it is not possible to set them. */
+ strlist_t gpg_arguments;
+
+ /* Environment variables passed along to the engine. */
+ char *display;
+ char *ttyname;
+ char *ttytype;
+ char *lc_ctype;
+ char *lc_messages;
+ char *xauthority;
+ char *pinentry_user_data;
+ session_env_t session_env;
+
+ /* Name of the output file - FIXME: what is this? */
+ const char *outfile;
+
+} opt;
+
+
+/*-- g13-common.c --*/
+void g13_init_signals (void);
+void g13_install_emergency_cleanup (void);
+void g13_exit (int rc);
+
+/*-- server.c and g13-sh-cmd.c --*/
+gpg_error_t g13_status (ctrl_t ctrl, int no, ...) GPGRT_ATTR_SENTINEL(0);
+
+
+#endif /*G13_COMMON_H*/
diff --git a/g13/g13-syshelp.c b/g13/g13-syshelp.c
new file mode 100644
index 0000000..65d5c25
--- /dev/null
+++ b/g13/g13-syshelp.c
@@ -0,0 +1,740 @@
+/* g13-syshelp.c - Helper for disk key management with GnuPG
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif
+#include <unistd.h>
+
+#define INCLUDED_BY_MAIN_MODULE 1
+#include "g13-syshelp.h"
+
+#include <gcrypt.h>
+#include <assuan.h>
+
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+#include "../common/asshelp.h"
+#include "../common/init.h"
+#include "keyblob.h"
+
+
+enum cmd_and_opt_values {
+ aNull = 0,
+ oQuiet = 'q',
+ oVerbose = 'v',
+ oRecipient = 'r',
+
+ aGPGConfList = 500,
+
+ oDebug,
+ oDebugLevel,
+ oDebugAll,
+ oDebugNone,
+ oDebugWait,
+ oDebugAllowCoreDump,
+ oLogFile,
+ oNoLogFile,
+ oAuditLog,
+
+ oOutput,
+
+ oAgentProgram,
+ oGpgProgram,
+ oType,
+
+ oDisplay,
+ oTTYname,
+ oTTYtype,
+ oLCctype,
+ oLCmessages,
+ oXauthority,
+
+ oStatusFD,
+ oLoggerFD,
+
+ oNoVerbose,
+ oNoSecmemWarn,
+ oHomedir,
+ oDryRun,
+ oNoDetach,
+
+ oNoRandomSeedFile,
+ oFakedSystemTime
+ };
+
+
+static ARGPARSE_OPTS opts[] = {
+
+ ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
+
+ ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
+ ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
+
+ ARGPARSE_s_s (oDebug, "debug", "@"),
+ ARGPARSE_s_s (oDebugLevel, "debug-level",
+ N_("|LEVEL|set the debugging level to LEVEL")),
+ ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
+ ARGPARSE_s_n (oDebugNone, "debug-none", "@"),
+ ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
+ ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"),
+
+ ARGPARSE_end ()
+};
+
+
+/* The list of supported debug flags. */
+static struct debug_flags_s debug_flags [] =
+ {
+ { DBG_MOUNT_VALUE , "mount" },
+ { DBG_CRYPTO_VALUE , "crypto" },
+ { DBG_MEMORY_VALUE , "memory" },
+ { DBG_MEMSTAT_VALUE, "memstat" },
+ { DBG_IPC_VALUE , "ipc" },
+ { 0, NULL }
+ };
+
+
+/* The timer tick interval used by the idle task. */
+#define TIMERTICK_INTERVAL_SEC (1)
+
+/* It is possible that we are currently running under setuid permissions. */
+static int maybe_setuid = 1;
+
+/* Helper to implement --debug-level and --debug. */
+static const char *debug_level;
+static unsigned int debug_value;
+
+
+/* Local prototypes. */
+static void g13_syshelp_deinit_default_ctrl (ctrl_t ctrl);
+static void release_tab_items (tab_item_t tab);
+static tab_item_t parse_g13tab (const char *username);
+
+
+
+static const char *
+my_strusage( int level )
+{
+ const char *p;
+
+ switch (level)
+ {
+ case 9: p = "GPL-3.0-or-later"; break;
+ case 11: p = "@G13@-syshelp (@GNUPG@)";
+ break;
+ case 13: p = VERSION; break;
+ case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
+ break;
+ case 1:
+ case 40: p = _("Usage: @G13@-syshelp [options] [files] (-h for help)");
+ break;
+ case 41:
+ p = _("Syntax: @G13@-syshelp [options] [files]\n"
+ "Helper to perform root-only tasks for g13\n");
+ break;
+
+ case 31: p = "\nHome: "; break;
+ case 32: p = gnupg_homedir (); break;
+
+ default: p = NULL; break;
+ }
+ return p;
+}
+
+
+/* Setup the debugging. With a DEBUG_LEVEL of NULL only the active
+ debug flags are propagated to the subsystems. With DEBUG_LEVEL
+ set, a specific set of debug flags is set; and individual debugging
+ flags will be added on top. */
+static void
+set_debug (void)
+{
+ int numok = (debug_level && digitp (debug_level));
+ int numlvl = numok? atoi (debug_level) : 0;
+
+ if (!debug_level)
+ ;
+ else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
+ opt.debug = 0;
+ else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
+ opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE;
+ else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
+ opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE;
+ else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
+ opt.debug = (DBG_IPC_VALUE|DBG_MOUNT_VALUE|DBG_CRYPTO_VALUE);
+ else if (!strcmp (debug_level, "guru") || numok)
+ {
+ opt.debug = ~0;
+ /* if (numok) */
+ /* opt.debug &= ~(DBG_HASHING_VALUE); */
+ }
+ else
+ {
+ log_error (_("invalid debug-level '%s' given\n"), debug_level);
+ g13_exit(2);
+ }
+
+ opt.debug |= debug_value;
+
+ if (opt.debug && !opt.verbose)
+ opt.verbose = 1;
+ if (opt.debug)
+ opt.quiet = 0;
+
+ if (opt.debug & DBG_CRYPTO_VALUE )
+ gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+
+ if (opt.debug)
+ parse_debug_flag (NULL, &opt.debug, debug_flags);
+}
+
+
+int
+main ( int argc, char **argv)
+{
+ ARGPARSE_ARGS pargs;
+ int orig_argc;
+ char **orig_argv;
+ gpg_error_t err = 0;
+ /* const char *fname; */
+ int may_coredump;
+ char *last_configname = NULL;
+ const char *configname = NULL;
+ int debug_argparser = 0;
+ int no_more_options = 0;
+ char *logfile = NULL;
+ /* int debug_wait = 0; */
+ int use_random_seed = 1;
+ /* int nodetach = 0; */
+ /* int nokeysetup = 0; */
+ struct server_control_s ctrl;
+
+ /*mtrace();*/
+
+ early_system_init ();
+ gnupg_reopen_std (G13_NAME "-syshelp");
+ set_strusage (my_strusage);
+ gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+
+ log_set_prefix (G13_NAME "-syshelp", GPGRT_LOG_WITH_PREFIX);
+
+ /* Make sure that our subsystems are ready. */
+ i18n_init ();
+ init_common_subsystems (&argc, &argv);
+
+ /* Take extra care of the random pool. */
+ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
+
+ may_coredump = disable_core_dumps ();
+
+ g13_init_signals ();
+
+ dotlock_create (NULL, 0); /* Register locking cleanup. */
+
+ opt.session_env = session_env_new ();
+ if (!opt.session_env)
+ log_fatal ("error allocating session environment block: %s\n",
+ strerror (errno));
+
+ /* First check whether we have a debug option on the commandline. */
+ orig_argc = argc;
+ orig_argv = argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
+ while (gnupg_argparse (NULL, &pargs, opts))
+ {
+ switch (pargs.r_opt)
+ {
+ case oDebug:
+ case oDebugAll:
+ debug_argparser++;
+ break;
+ }
+ }
+ /* Reset the flags. */
+ pargs.flags &= ~(ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
+
+ /* Initialize the secure memory. */
+ gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+ maybe_setuid = 0;
+
+ /*
+ * Now we are now working under our real uid
+ */
+
+ /* Setup malloc hooks. */
+ {
+ struct assuan_malloc_hooks malloc_hooks;
+
+ malloc_hooks.malloc = gcry_malloc;
+ malloc_hooks.realloc = gcry_realloc;
+ malloc_hooks.free = gcry_free;
+ assuan_set_malloc_hooks (&malloc_hooks);
+ }
+
+ /* Prepare libassuan. */
+ assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
+ /*assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);*/
+ setup_libassuan_logging (&opt.debug, NULL);
+
+ /* Setup a default control structure for command line mode. */
+ memset (&ctrl, 0, sizeof ctrl);
+ g13_syshelp_init_default_ctrl (&ctrl);
+ ctrl.no_server = 1;
+ ctrl.status_fd = -1; /* No status output. */
+
+ /* The configuraton directories for use by gpgrt_argparser. */
+ gnupg_set_confdir (GNUPG_CONFDIR_SYS, gnupg_sysconfdir ());
+ gnupg_set_confdir (GNUPG_CONFDIR_USER, gnupg_homedir ());
+
+ argc = orig_argc;
+ argv = orig_argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags |= (ARGPARSE_FLAG_RESET
+ | ARGPARSE_FLAG_KEEP
+ | ARGPARSE_FLAG_SYS
+ | ARGPARSE_FLAG_USER);
+
+ while (!no_more_options
+ && gnupg_argparser (&pargs, opts, G13_NAME"-syshelp" EXTSEP_S "conf"))
+ {
+ switch (pargs.r_opt)
+ {
+ case ARGPARSE_CONFFILE:
+ {
+ if (debug_argparser)
+ log_info (_("reading options from '%s'\n"),
+ pargs.r_type? pargs.r.ret_str: "[cmdline]");
+ if (pargs.r_type)
+ {
+ xfree (last_configname);
+ last_configname = xstrdup (pargs.r.ret_str);
+ configname = last_configname;
+ }
+ else
+ configname = NULL;
+ }
+ break;
+
+ case oQuiet: opt.quiet = 1; break;
+
+ case oDryRun: opt.dry_run = 1; break;
+
+ case oVerbose:
+ opt.verbose++;
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+ break;
+ case oNoVerbose:
+ opt.verbose = 0;
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+ break;
+
+ case oLogFile: logfile = pargs.r.ret_str; break;
+ case oNoLogFile: logfile = NULL; break;
+
+ case oNoDetach: /*nodetach = 1; */break;
+
+ case oDebug:
+ if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
+ {
+ pargs.r_opt = ARGPARSE_INVALID_ARG;
+ pargs.err = ARGPARSE_PRINT_ERROR;
+ }
+ break;
+ case oDebugAll: debug_value = ~0; break;
+ case oDebugNone: debug_value = 0; break;
+ case oDebugLevel: debug_level = pargs.r.ret_str; break;
+ case oDebugWait: /*debug_wait = pargs.r.ret_int; */break;
+ case oDebugAllowCoreDump:
+ may_coredump = enable_core_dumps ();
+ break;
+
+ case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break;
+ case oLoggerFD: log_set_fd (pargs.r.ret_int ); break;
+
+ case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
+
+ case oFakedSystemTime:
+ {
+ time_t faked_time = isotime2epoch (pargs.r.ret_str);
+ if (faked_time == (time_t)(-1))
+ faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
+ gnupg_set_time (faked_time, 0);
+ }
+ break;
+
+ case oNoSecmemWarn: gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); break;
+
+ case oNoRandomSeedFile: use_random_seed = 0; break;
+
+ default:
+ pargs.err = configname? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
+ break;
+ }
+ }
+ gnupg_argparse (NULL, &pargs, NULL); /* Release internal state. */
+
+ if (!last_configname)
+ opt.config_filename = make_filename (gnupg_homedir (),
+ G13_NAME"-syshelp" EXTSEP_S "conf",
+ NULL);
+ else
+ {
+ opt.config_filename = last_configname;
+ last_configname = NULL;
+ }
+
+ if (log_get_errorcount(0))
+ g13_exit(2);
+
+ /* Now that we have the options parsed we need to update the default
+ control structure. */
+ g13_syshelp_init_default_ctrl (&ctrl);
+
+ if (may_coredump && !opt.quiet)
+ log_info (_("WARNING: program may create a core file!\n"));
+
+ if (logfile)
+ {
+ log_set_file (logfile);
+ log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID);
+ }
+
+ if (gnupg_faked_time_p ())
+ {
+ gnupg_isotime_t tbuf;
+
+ log_info (_("WARNING: running with faked system time: "));
+ gnupg_get_isotime (tbuf);
+ dump_isotime (tbuf);
+ log_printf ("\n");
+ }
+
+ /* Print any pending secure memory warnings. */
+ gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
+
+ /* Setup the debug flags for all subsystems. */
+ set_debug ();
+
+ /* Install a regular exit handler to make real sure that the secure
+ memory gets wiped out. */
+ g13_install_emergency_cleanup ();
+
+ /* Terminate if we found any error until now. */
+ if (log_get_errorcount(0))
+ g13_exit (2);
+
+ /* Set the standard GnuPG random seed file. */
+ if (use_random_seed)
+ {
+ char *p = make_filename (gnupg_homedir (), "random_seed", NULL);
+ gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p);
+ xfree(p);
+ }
+
+ /* Get the UID of the caller. */
+#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
+ {
+ const char *uidstr;
+ struct passwd *pwd = NULL;
+
+ uidstr = getenv ("USERV_UID");
+
+ /* Print a quick note if we are not started via userv. */
+ if (!uidstr)
+ {
+ if (getuid ())
+ {
+ log_info ("WARNING: Not started via userv\n");
+ ctrl.fail_all_cmds = 1;
+ }
+ ctrl.client.uid = getuid ();
+ }
+ else
+ {
+ unsigned long myuid;
+
+ errno = 0;
+ myuid = strtoul (uidstr, NULL, 10);
+ if (myuid == ULONG_MAX && errno)
+ {
+ log_info ("WARNING: Started via broken userv: %s\n",
+ strerror (errno));
+ ctrl.fail_all_cmds = 1;
+ ctrl.client.uid = getuid ();
+ }
+ else
+ ctrl.client.uid = (uid_t)myuid;
+ }
+
+ pwd = getpwuid (ctrl.client.uid);
+ if (!pwd || !*pwd->pw_name)
+ {
+ log_info ("WARNING: Name for UID not found: %s\n", strerror (errno));
+ ctrl.fail_all_cmds = 1;
+ ctrl.client.uname = xstrdup ("?");
+ }
+ else
+ ctrl.client.uname = xstrdup (pwd->pw_name);
+
+ /* Check that the user name does not contain a directory
+ separator. */
+ if (strchr (ctrl.client.uname, '/'))
+ {
+ log_info ("WARNING: Invalid user name passed\n");
+ ctrl.fail_all_cmds = 1;
+ }
+ }
+#else /*!HAVE_PWD_H || !HAVE_GETPWUID*/
+ log_info ("WARNING: System does not support required syscalls\n");
+ ctrl.fail_all_cmds = 1;
+ ctrl.client.uid = getuid ();
+ ctrl.client.uname = xstrdup ("?");
+#endif /*!HAVE_PWD_H || !HAVE_GETPWUID*/
+
+ /* Read the table entries for this user. */
+ if (!ctrl.fail_all_cmds
+ && !(ctrl.client.tab = parse_g13tab (ctrl.client.uname)))
+ ctrl.fail_all_cmds = 1;
+
+ /* Start the server. */
+ err = syshelp_server (&ctrl);
+ if (err)
+ log_error ("server exited with error: %s <%s>\n",
+ gpg_strerror (err), gpg_strsource (err));
+
+ /* Cleanup. */
+ g13_syshelp_deinit_default_ctrl (&ctrl);
+ g13_exit (0);
+ return 8; /*NOTREACHED*/
+}
+
+
+/* Store defaults into the per-connection CTRL object. */
+void
+g13_syshelp_init_default_ctrl (ctrl_t ctrl)
+{
+ ctrl->conttype = CONTTYPE_DM_CRYPT;
+}
+
+/* Release all resources allocated by default in the CTRl object. */
+static void
+g13_syshelp_deinit_default_ctrl (ctrl_t ctrl)
+{
+ xfree (ctrl->client.uname);
+ release_tab_items (ctrl->client.tab);
+}
+
+
+/* Release the list of g13tab itejms at TAB. */
+static void
+release_tab_items (tab_item_t tab)
+{
+ while (tab)
+ {
+ tab_item_t next = tab->next;
+ xfree (tab->mountpoint);
+ xfree (tab);
+ tab = next;
+ }
+}
+
+
+void
+g13_syshelp_i_know_what_i_am_doing (void)
+{
+ const char * const yesfile = "Yes-g13-I-know-what-I-am-doing";
+ char *fname;
+
+ fname = make_filename (gnupg_sysconfdir (), yesfile, NULL);
+ if (gnupg_access (fname, F_OK))
+ {
+ log_info ("*******************************************************\n");
+ log_info ("* The G13 support for DM-Crypt is new and not matured.\n");
+ log_info ("* Bugs or improper use may delete all your disks!\n");
+ log_info ("* To confirm that you are ware of this risk, create\n");
+ log_info ("* the file '%s'.\n", fname);
+ log_info ("*******************************************************\n");
+ exit (1);
+ }
+ xfree (fname);
+}
+
+
+/* Parse the /etc/gnupg/g13tab for user USERNAME. Return a table for
+ the user on success. Return NULL on error and print
+ diagnostics. */
+static tab_item_t
+parse_g13tab (const char *username)
+{
+ gpg_error_t err;
+ int c, n;
+ char line[512];
+ char *p;
+ char *fname;
+ estream_t fp;
+ int lnr;
+ char **words = NULL;
+ tab_item_t table = NULL;
+ tab_item_t *tabletail, ti;
+
+ fname = make_filename (gnupg_sysconfdir (), G13_NAME"tab", NULL);
+ fp = es_fopen (fname, "r");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("error opening '%s': %s\n"), fname, gpg_strerror (err));
+ goto leave;
+ }
+
+ tabletail = &table;
+ err = 0;
+ lnr = 0;
+ while (es_fgets (line, DIM(line)-1, fp))
+ {
+ lnr++;
+ n = strlen (line);
+ if (!n || line[n-1] != '\n')
+ {
+ /* Eat until end of line. */
+ while ((c=es_getc (fp)) != EOF && c != '\n')
+ ;
+ err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
+ : GPG_ERR_INCOMPLETE_LINE);
+ log_error (_("file '%s', line %d: %s\n"),
+ fname, lnr, gpg_strerror (err));
+ continue;
+ }
+ line[--n] = 0; /* Chop the LF. */
+ if (n && line[n-1] == '\r')
+ line[--n] = 0; /* Chop an optional CR. */
+
+ /* Allow for empty lines and spaces */
+ for (p=line; spacep (p); p++)
+ ;
+ if (!*p || *p == '#')
+ continue;
+
+ /* Parse the line. The format is
+ * <username> <blockdev> [<label>|"-" [<mountpoint>]]
+ */
+ xfree (words);
+ words = strtokenize (p, " \t");
+ if (!words)
+ {
+ err = gpg_error_from_syserror ();
+ break;
+ }
+ if (!words[0] || !words[1])
+ {
+ log_error (_("file '%s', line %d: %s\n"),
+ fname, lnr, gpg_strerror (GPG_ERR_SYNTAX));
+ continue;
+ }
+ if (!(*words[1] == '/'
+ || !strncmp (words[1], "PARTUUID=", 9)
+ || !strncmp (words[1], "partuuid=", 9)))
+ {
+ log_error (_("file '%s', line %d: %s\n"),
+ fname, lnr, "Invalid block device syntax");
+ continue;
+ }
+ if (words[2])
+ {
+ if (strlen (words[2]) > 16 || strchr (words[2], '/'))
+ {
+ log_error (_("file '%s', line %d: %s\n"),
+ fname, lnr, "Label too long or invalid syntax");
+ continue;
+ }
+
+ if (words[3] && *words[3] != '/')
+ {
+ log_error (_("file '%s', line %d: %s\n"),
+ fname, lnr, "Invalid mountpoint syntax");
+ continue;
+ }
+ }
+ if (strcmp (words[0], username))
+ continue; /* Skip entries for other usernames! */
+
+ ti = xtrymalloc (sizeof *ti + strlen (words[1]));
+ if (!ti)
+ {
+ err = gpg_error_from_syserror ();
+ break;
+ }
+ ti->next = NULL;
+ ti->label = NULL;
+ ti->mountpoint = NULL;
+ strcpy (ti->blockdev, *words[1]=='/'? words[1] : words[1]+9);
+ if (words[2])
+ {
+ if (strcmp (words[2], "-")
+ && !(ti->label = xtrystrdup (words[2])))
+ {
+ err = gpg_error_from_syserror ();
+ xfree (ti);
+ break;
+ }
+ if (words[3] && !(ti->mountpoint = xtrystrdup (words[3])))
+ {
+ err = gpg_error_from_syserror ();
+ xfree (ti->label);
+ xfree (ti);
+ break;
+ }
+ }
+ *tabletail = ti;
+ tabletail = &ti->next;
+ }
+
+ if (!err && !es_feof (fp))
+ err = gpg_error_from_syserror ();
+ if (err)
+ log_error (_("error reading '%s', line %d: %s\n"),
+ fname, lnr, gpg_strerror (err));
+
+ leave:
+ xfree (words);
+ es_fclose (fp);
+ xfree (fname);
+ if (err)
+ {
+ release_tab_items (table);
+ return NULL;
+ }
+ return table;
+}
diff --git a/g13/g13-syshelp.h b/g13/g13-syshelp.h
new file mode 100644
index 0000000..0243166
--- /dev/null
+++ b/g13/g13-syshelp.h
@@ -0,0 +1,96 @@
+/* g130syshelp.h - Global definitions for G13-SYSHELP.
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_SYSHELP_H
+#define G13_SYSHELP_H
+
+#include "g13-common.h"
+#include "g13tuple.h"
+
+struct tab_item_s;
+typedef struct tab_item_s *tab_item_t;
+
+struct tab_item_s
+{
+ tab_item_t next;
+ char *label; /* Optional malloced label for that entry. */
+ char *mountpoint; /* NULL or a malloced mountpoint. */
+ char blockdev[1]; /* String with the name of the block device. If
+ it starts with a slash it is a regular device
+ name, otherwise it is a PARTUUID. */
+};
+
+
+
+/* Forward declaration for an object defined in g13-sh-cmd.c. */
+struct server_local_s;
+
+/* Session control object. This object is passed down to most
+ functions. The default values for it are set by
+ g13_syshelp_init_default_ctrl(). */
+struct server_control_s
+{
+ int no_server; /* We are not running under server control */
+ int status_fd; /* Only for non-server mode */
+ struct server_local_s *server_local;
+
+ struct {
+ uid_t uid; /* UID of the client calling use. */
+ char *uname;
+ tab_item_t tab;/* Linked list with the g13tab items for this user. */
+ } client;
+
+ /* Flag indicating that we should fail all commands. */
+ int fail_all_cmds;
+
+ /* Type of the current container. See the CONTTYPE_ constants. */
+ int conttype;
+
+ /* A pointer into client.tab with the selected tab line or NULL. */
+ tab_item_t devti;
+};
+
+
+/*-- g13-syshelp.c --*/
+void g13_syshelp_init_default_ctrl (struct server_control_s *ctrl);
+void g13_syshelp_i_know_what_i_am_doing (void);
+
+/*-- sh-cmd.c --*/
+gpg_error_t syshelp_server (ctrl_t ctrl);
+gpg_error_t sh_encrypt_keyblob (ctrl_t ctrl,
+ const void *keyblob, size_t keybloblen,
+ char **r_enckeyblob, size_t *r_enckeybloblen);
+
+/*-- sh-blockdev.c --*/
+gpg_error_t sh_blockdev_getsz (const char *name, unsigned long long *r_nblocks);
+gpg_error_t sh_is_empty_partition (const char *name);
+
+/*-- sh-dmcrypt.c --*/
+gpg_error_t sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname,
+ estream_t devfp);
+gpg_error_t sh_dmcrypt_mount_container (ctrl_t ctrl, const char *devname,
+ tupledesc_t keyblob);
+gpg_error_t sh_dmcrypt_umount_container (ctrl_t ctrl, const char *devname);
+gpg_error_t sh_dmcrypt_suspend_container (ctrl_t ctrl, const char *devname);
+gpg_error_t sh_dmcrypt_resume_container (ctrl_t ctrl, const char *devname,
+ tupledesc_t keyblob);
+
+
+
+#endif /*G13_SYSHELP_H*/
diff --git a/g13/g13.c b/g13/g13.c
new file mode 100644
index 0000000..6500916
--- /dev/null
+++ b/g13/g13.c
@@ -0,0 +1,1050 @@
+/* g13.c - Disk Key management with GnuPG
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <npth.h>
+
+#define INCLUDED_BY_MAIN_MODULE 1
+#include "g13.h"
+
+#include <gcrypt.h>
+#include <assuan.h>
+
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+#include "../common/gc-opt-flags.h"
+#include "../common/asshelp.h"
+#include "../common/init.h"
+#include "keyblob.h"
+#include "server.h"
+#include "runner.h"
+#include "create.h"
+#include "mount.h"
+#include "suspend.h"
+#include "mountinfo.h"
+#include "backend.h"
+#include "call-syshelp.h"
+
+
+enum cmd_and_opt_values {
+ aNull = 0,
+ oQuiet = 'q',
+ oVerbose = 'v',
+ oRecipient = 'r',
+
+ aGPGConfList = 500,
+ aGPGConfTest,
+ aCreate,
+ aMount,
+ aUmount,
+ aSuspend,
+ aResume,
+ aServer,
+ aFindDevice,
+
+ oOptions,
+ oDebug,
+ oDebugLevel,
+ oDebugAll,
+ oDebugNone,
+ oDebugWait,
+ oDebugAllowCoreDump,
+ oLogFile,
+ oNoLogFile,
+ oAuditLog,
+
+ oOutput,
+
+ oAgentProgram,
+ oGpgProgram,
+ oType,
+
+ oDisplay,
+ oTTYname,
+ oTTYtype,
+ oLCctype,
+ oLCmessages,
+ oXauthority,
+
+ oStatusFD,
+ oLoggerFD,
+
+ oNoVerbose,
+ oNoSecmemWarn,
+ oNoGreeting,
+ oNoTTY,
+ oNoOptions,
+ oHomedir,
+ oWithColons,
+ oDryRun,
+ oNoDetach,
+
+ oNoRandomSeedFile,
+ oFakedSystemTime
+ };
+
+
+static ARGPARSE_OPTS opts[] = {
+
+ ARGPARSE_group (300, N_("@Commands:\n ")),
+
+ ARGPARSE_c (aCreate, "create", N_("Create a new file system container")),
+ ARGPARSE_c (aMount, "mount", N_("Mount a file system container") ),
+ ARGPARSE_c (aUmount, "umount", N_("Unmount a file system container") ),
+ ARGPARSE_c (aSuspend, "suspend", N_("Suspend a file system container") ),
+ ARGPARSE_c (aResume, "resume", N_("Resume a file system container") ),
+ ARGPARSE_c (aServer, "server", N_("Run in server mode")),
+ ARGPARSE_c (aFindDevice, "find-device", "@"),
+
+ ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
+ ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
+
+ ARGPARSE_group (301, N_("@\nOptions:\n ")),
+
+ ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
+ ARGPARSE_s_s (oType, "type", N_("|NAME|use container format NAME")),
+
+ ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
+ ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
+ ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
+ ARGPARSE_s_n (oNoTTY, "no-tty", N_("don't use the terminal at all")),
+ ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
+ ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write log output to FILE")),
+ ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"),
+ ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"),
+
+ ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
+
+ ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")),
+
+ ARGPARSE_s_s (oDebug, "debug", "@"),
+ ARGPARSE_s_s (oDebugLevel, "debug-level",
+ N_("|LEVEL|set the debugging level to LEVEL")),
+ ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
+ ARGPARSE_s_n (oDebugNone, "debug-none", "@"),
+ ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
+ ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"),
+
+ ARGPARSE_s_i (oStatusFD, "status-fd",
+ N_("|FD|write status info to this FD")),
+
+ ARGPARSE_group (302, N_(
+ "@\n(See the man page for a complete listing of all commands and options)\n"
+ )),
+
+ ARGPARSE_group (303, N_("@\nExamples:\n\n"
+ " blurb\n"
+ " blurb\n")),
+
+ /* Hidden options. */
+ ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"),
+ ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"),
+ ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"),
+ ARGPARSE_noconffile (oNoOptions, "no-options", "@"),
+ ARGPARSE_s_s (oHomedir, "homedir", "@"),
+ ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
+ ARGPARSE_s_s (oGpgProgram, "gpg-program", "@"),
+ ARGPARSE_s_s (oDisplay, "display", "@"),
+ ARGPARSE_s_s (oTTYname, "ttyname", "@"),
+ ARGPARSE_s_s (oTTYtype, "ttytype", "@"),
+ ARGPARSE_s_s (oLCctype, "lc-ctype", "@"),
+ ARGPARSE_s_s (oLCmessages, "lc-messages", "@"),
+ ARGPARSE_s_s (oXauthority, "xauthority", "@"),
+ ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"),
+ ARGPARSE_s_n (oWithColons, "with-colons", "@"),
+ ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"),
+
+ /* Command aliases. */
+
+ ARGPARSE_end ()
+};
+
+
+/* The list of supported debug flags. */
+static struct debug_flags_s debug_flags [] =
+ {
+ { DBG_MOUNT_VALUE , "mount" },
+ { DBG_CRYPTO_VALUE , "crypto" },
+ { DBG_MEMORY_VALUE , "memory" },
+ { DBG_MEMSTAT_VALUE, "memstat" },
+ { DBG_IPC_VALUE , "ipc" },
+ { 0, NULL }
+ };
+
+
+/* The timer tick interval used by the idle task. */
+#define TIMERTICK_INTERVAL_SEC (1)
+
+/* It is possible that we are currently running under setuid permissions. */
+static int maybe_setuid = 1;
+
+/* Helper to implement --debug-level and --debug. */
+static const char *debug_level;
+static unsigned int debug_value;
+
+/* Flag to indicate that a shutdown was requested. */
+static int shutdown_pending;
+
+/* The thread id of the idle task. */
+static npth_t idle_task_thread;
+
+
+/* The container type as specified on the command line. */
+static int cmdline_conttype;
+
+
+
+static void set_cmd (enum cmd_and_opt_values *ret_cmd,
+ enum cmd_and_opt_values new_cmd );
+
+static void start_idle_task (void);
+static void join_idle_task (void);
+
+
+/* Begin NPth wrapper functions. */
+ASSUAN_SYSTEM_NPTH_IMPL;
+
+
+static const char *
+my_strusage( int level )
+{
+ const char *p;
+
+ switch (level)
+ {
+ case 9: p = "GPL-3.0-or-later"; break;
+ case 11: p = "@G13@ (@GNUPG@)";
+ break;
+ case 13: p = VERSION; break;
+ case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
+ break;
+ case 1:
+ case 40: p = _("Usage: @G13@ [options] [files] (-h for help)");
+ break;
+ case 41:
+ p = _("Syntax: @G13@ [options] [files]\n"
+ "Create, mount or unmount an encrypted file system container\n");
+ break;
+
+ case 31: p = "\nHome: "; break;
+ case 32: p = gnupg_homedir (); break;
+
+ default: p = NULL; break;
+ }
+ return p;
+}
+
+
+static void
+wrong_args (const char *text)
+{
+ fprintf (stderr, _("usage: %s [options] "), G13_NAME);
+ fputs (text, stderr);
+ putc ('\n', stderr);
+ g13_exit (2);
+}
+
+
+/* Setup the debugging. With a DEBUG_LEVEL of NULL only the active
+ debug flags are propagated to the subsystems. With DEBUG_LEVEL
+ set, a specific set of debug flags is set; and individual debugging
+ flags will be added on top. */
+static void
+set_debug (void)
+{
+ int numok = (debug_level && digitp (debug_level));
+ int numlvl = numok? atoi (debug_level) : 0;
+
+ if (!debug_level)
+ ;
+ else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
+ opt.debug = 0;
+ else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
+ opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE;
+ else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
+ opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE;
+ else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
+ opt.debug = (DBG_IPC_VALUE|DBG_MOUNT_VALUE|DBG_CRYPTO_VALUE);
+ else if (!strcmp (debug_level, "guru") || numok)
+ {
+ opt.debug = ~0;
+ /* if (numok) */
+ /* opt.debug &= ~(DBG_HASHING_VALUE); */
+ }
+ else
+ {
+ log_error (_("invalid debug-level '%s' given\n"), debug_level);
+ g13_exit(2);
+ }
+
+ opt.debug |= debug_value;
+
+ if (opt.debug && !opt.verbose)
+ opt.verbose = 1;
+ if (opt.debug)
+ opt.quiet = 0;
+
+ if (opt.debug & DBG_CRYPTO_VALUE )
+ gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+
+ if (opt.debug)
+ parse_debug_flag (NULL, &opt.debug, debug_flags);
+}
+
+
+
+static void
+set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
+{
+ enum cmd_and_opt_values cmd = *ret_cmd;
+
+ if (!cmd || cmd == new_cmd)
+ cmd = new_cmd;
+ else
+ {
+ log_error (_("conflicting commands\n"));
+ g13_exit (2);
+ }
+
+ *ret_cmd = cmd;
+}
+
+
+int
+main ( int argc, char **argv)
+{
+ ARGPARSE_ARGS pargs;
+ int orig_argc;
+ char **orig_argv;
+ gpg_error_t err = 0;
+ /* const char *fname; */
+ int may_coredump;
+ char *last_configname = NULL;
+ const char *configname = NULL;
+ int debug_argparser = 0;
+ int no_more_options = 0;
+ char *logfile = NULL;
+ int greeting = 0;
+ int nogreeting = 0;
+ /* int debug_wait = 0; */
+ int use_random_seed = 1;
+ /* int nodetach = 0; */
+ /* int nokeysetup = 0; */
+ enum cmd_and_opt_values cmd = 0;
+ struct server_control_s ctrl;
+ strlist_t recipients = NULL;
+
+ early_system_init ();
+ gnupg_reopen_std (G13_NAME);
+ set_strusage (my_strusage);
+ gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+
+ log_set_prefix (G13_NAME, GPGRT_LOG_WITH_PREFIX);
+
+ /* Make sure that our subsystems are ready. */
+ i18n_init ();
+ init_common_subsystems (&argc, &argv);
+
+ npth_init ();
+
+ /* Take extra care of the random pool. */
+ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
+
+ may_coredump = disable_core_dumps ();
+
+ g13_init_signals ();
+
+ dotlock_create (NULL, 0); /* Register locking cleanup. */
+
+ opt.session_env = session_env_new ();
+ if (!opt.session_env)
+ log_fatal ("error allocating session environment block: %s\n",
+ strerror (errno));
+
+ /* First check whether we have a config file on the commandline. */
+ orig_argc = argc;
+ orig_argv = argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1|(1<<6); /* Do not remove the args, ignore version. */
+ pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
+ while (gnupg_argparse (NULL, &pargs, opts))
+ {
+ switch (pargs.r_opt)
+ {
+ case oDebug:
+ case oDebugAll:
+ debug_argparser++;
+ break;
+
+ case oHomedir:
+ gnupg_set_homedir (pargs.r.ret_str);
+ break;
+ }
+ }
+ /* Reset the flags. */
+ pargs.flags &= ~(ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
+
+ /* Initialize the secure memory. */
+ gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+ maybe_setuid = 0;
+
+ /*
+ * Now we are now working under our real uid
+ */
+
+ /* Setup malloc hooks. */
+ {
+ struct assuan_malloc_hooks malloc_hooks;
+
+ malloc_hooks.malloc = gcry_malloc;
+ malloc_hooks.realloc = gcry_realloc;
+ malloc_hooks.free = gcry_free;
+ assuan_set_malloc_hooks (&malloc_hooks);
+ }
+
+ /* Prepare libassuan. */
+ assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
+ assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+ setup_libassuan_logging (&opt.debug, NULL);
+
+ /* Setup a default control structure for command line mode. */
+ memset (&ctrl, 0, sizeof ctrl);
+ g13_init_default_ctrl (&ctrl);
+ ctrl.no_server = 1;
+ ctrl.status_fd = -1; /* No status output. */
+
+ /* The configuraton directories for use by gpgrt_argparser. */
+ gnupg_set_confdir (GNUPG_CONFDIR_SYS, gnupg_sysconfdir ());
+ gnupg_set_confdir (GNUPG_CONFDIR_USER, gnupg_homedir ());
+
+ argc = orig_argc;
+ argv = orig_argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags |= (ARGPARSE_FLAG_RESET
+ | ARGPARSE_FLAG_KEEP
+ | ARGPARSE_FLAG_SYS
+ | ARGPARSE_FLAG_USER);
+ while (!no_more_options
+ && gnupg_argparser (&pargs, opts, G13_NAME EXTSEP_S "conf"))
+ {
+ switch (pargs.r_opt)
+ {
+ case ARGPARSE_CONFFILE:
+ {
+ if (debug_argparser)
+ log_info (_("reading options from '%s'\n"),
+ pargs.r_type? pargs.r.ret_str: "[cmdline]");
+ if (pargs.r_type)
+ {
+ xfree (last_configname);
+ last_configname = xstrdup (pargs.r.ret_str);
+ configname = last_configname;
+ }
+ else
+ configname = NULL;
+ }
+ break;
+
+ case aGPGConfList:
+ case aGPGConfTest:
+ set_cmd (&cmd, pargs.r_opt);
+ nogreeting = 1;
+ /* nokeysetup = 1; */
+ break;
+
+ case aServer:
+ case aMount:
+ case aUmount:
+ case aSuspend:
+ case aResume:
+ case aCreate:
+ case aFindDevice:
+ set_cmd (&cmd, pargs.r_opt);
+ break;
+
+ case oOutput: opt.outfile = pargs.r.ret_str; break;
+
+ case oQuiet: opt.quiet = 1; break;
+ case oNoGreeting: nogreeting = 1; break;
+ case oNoTTY: break;
+
+ case oDryRun: opt.dry_run = 1; break;
+
+ case oVerbose:
+ opt.verbose++;
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+ break;
+ case oNoVerbose:
+ opt.verbose = 0;
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+ break;
+
+ case oLogFile: logfile = pargs.r.ret_str; break;
+ case oNoLogFile: logfile = NULL; break;
+
+ case oNoDetach: /*nodetach = 1; */break;
+
+ case oDebug:
+ if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
+ {
+ pargs.r_opt = ARGPARSE_INVALID_ARG;
+ pargs.err = ARGPARSE_PRINT_ERROR;
+ }
+ break;
+ case oDebugAll: debug_value = ~0; break;
+ case oDebugNone: debug_value = 0; break;
+ case oDebugLevel: debug_level = pargs.r.ret_str; break;
+ case oDebugWait: /*debug_wait = pargs.r.ret_int; */break;
+ case oDebugAllowCoreDump:
+ may_coredump = enable_core_dumps ();
+ break;
+
+ case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break;
+ case oLoggerFD: log_set_fd (pargs.r.ret_int ); break;
+
+ case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
+
+ case oAgentProgram: opt.agent_program = pargs.r.ret_str; break;
+ case oGpgProgram: opt.gpg_program = pargs.r.ret_str; break;
+ case oDisplay: opt.display = xstrdup (pargs.r.ret_str); break;
+ case oTTYname: opt.ttyname = xstrdup (pargs.r.ret_str); break;
+ case oTTYtype: opt.ttytype = xstrdup (pargs.r.ret_str); break;
+ case oLCctype: opt.lc_ctype = xstrdup (pargs.r.ret_str); break;
+ case oLCmessages: opt.lc_messages = xstrdup (pargs.r.ret_str); break;
+ case oXauthority: opt.xauthority = xstrdup (pargs.r.ret_str); break;
+
+ case oFakedSystemTime:
+ {
+ time_t faked_time = isotime2epoch (pargs.r.ret_str);
+ if (faked_time == (time_t)(-1))
+ faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
+ gnupg_set_time (faked_time, 0);
+ }
+ break;
+
+ case oNoSecmemWarn: gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); break;
+
+ case oNoRandomSeedFile: use_random_seed = 0; break;
+
+ case oRecipient: /* Store the encryption key. */
+ add_to_strlist (&recipients, pargs.r.ret_str);
+ break;
+
+ case oType:
+ if (!strcmp (pargs.r.ret_str, "help"))
+ {
+ be_parse_conttype_name (NULL);
+ g13_exit (0);
+ }
+ cmdline_conttype = be_parse_conttype_name (pargs.r.ret_str);
+ if (!cmdline_conttype)
+ {
+ pargs.r_opt = ARGPARSE_INVALID_ARG;
+ pargs.err = ARGPARSE_PRINT_ERROR;
+ }
+ break;
+
+ default:
+ if (configname)
+ pargs.err = ARGPARSE_PRINT_WARNING;
+ else
+ pargs.err = ARGPARSE_PRINT_ERROR;
+ break;
+ }
+ }
+ gnupg_argparse (NULL, &pargs, NULL); /* Release internal state. */
+
+ /* Construct GPG arguments. */
+ {
+ strlist_t last;
+ last = append_to_strlist (&opt.gpg_arguments, "-z");
+ last = append_to_strlist (&last, "0");
+ last = append_to_strlist (&last, "--trust-model");
+ last = append_to_strlist (&last, "always");
+ (void) last;
+ }
+
+ if (!last_configname)
+ opt.config_filename = make_filename (gnupg_homedir (),
+ G13_NAME EXTSEP_S "conf",
+ NULL);
+ else
+ {
+ opt.config_filename = last_configname;
+ last_configname = NULL;
+ }
+
+ if (log_get_errorcount(0))
+ g13_exit(2);
+
+ /* Now that we have the options parsed we need to update the default
+ control structure. */
+ g13_init_default_ctrl (&ctrl);
+ ctrl.recipients = recipients;
+ recipients = NULL;
+
+ if (nogreeting)
+ greeting = 0;
+
+ if (greeting)
+ {
+ fprintf (stderr, "%s %s; %s\n",
+ strusage(11), strusage(13), strusage(14) );
+ fprintf (stderr, "%s\n", strusage(15) );
+ }
+
+ if (may_coredump && !opt.quiet)
+ log_info (_("WARNING: program may create a core file!\n"));
+
+ /* Print a warning if an argument looks like an option. */
+ if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
+ {
+ int i;
+
+ for (i=0; i < argc; i++)
+ if (argv[i][0] == '-' && argv[i][1] == '-')
+ log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
+ }
+
+
+ if (logfile)
+ {
+ log_set_file (logfile);
+ log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
+ | GPGRT_LOG_WITH_TIME
+ | GPGRT_LOG_WITH_PID ));
+ }
+
+ if (gnupg_faked_time_p ())
+ {
+ gnupg_isotime_t tbuf;
+
+ log_info (_("WARNING: running with faked system time: "));
+ gnupg_get_isotime (tbuf);
+ dump_isotime (tbuf);
+ log_printf ("\n");
+ }
+
+ /* Print any pending secure memory warnings. */
+ gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
+
+ /* Setup the debug flags for all subsystems. */
+ set_debug ();
+
+ /* Install emergency cleanup handler. */
+ g13_install_emergency_cleanup ();
+
+ /* Terminate if we found any error until now. */
+ if (log_get_errorcount(0))
+ g13_exit (2);
+
+ /* Set the standard GnuPG random seed file. */
+ if (use_random_seed)
+ {
+ char *p = make_filename (gnupg_homedir (), "random_seed", NULL);
+ gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p);
+ xfree(p);
+ }
+
+ /* Store given filename into FNAME. */
+ /* fname = argc? *argv : NULL; */
+
+ /* Parse all given encryption keys. This does a lookup of the keys
+ and stops if any of the given keys was not found. */
+#if 0 /* Currently not implemented. */
+ if (!nokeysetup)
+ {
+ strlist_t sl;
+ int failed = 0;
+
+ for (sl = ctrl->recipients; sl; sl = sl->next)
+ if (check_encryption_key ())
+ failed = 1;
+ if (failed)
+ g13_exit (1);
+ }
+#endif /*0*/
+
+ /* Dispatch command. */
+ err = 0;
+ switch (cmd)
+ {
+ case aGPGConfList:
+ { /* List options and default values in the GPG Conf format. */
+ char *config_filename_esc = percent_escape (opt.config_filename, NULL);
+
+ printf ("gpgconf-g13.conf:%lu:\"%s\n",
+ GC_OPT_FLAG_DEFAULT, config_filename_esc);
+ xfree (config_filename_esc);
+
+ printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
+ printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE);
+ }
+ break;
+ case aGPGConfTest:
+ /* This is merely a dummy command to test whether the
+ configuration file is valid. */
+ break;
+
+ case aServer:
+ {
+ start_idle_task ();
+ ctrl.no_server = 0;
+ err = g13_server (&ctrl);
+ if (err)
+ log_error ("server exited with error: %s <%s>\n",
+ gpg_strerror (err), gpg_strsource (err));
+ else
+ g13_request_shutdown ();
+ }
+ break;
+
+ case aFindDevice:
+ {
+ char *blockdev;
+
+ if (argc != 1)
+ wrong_args ("--find-device name");
+
+ err = call_syshelp_find_device (&ctrl, argv[0], &blockdev);
+ if (err)
+ log_error ("error finding device '%s': %s <%s>\n",
+ argv[0], gpg_strerror (err), gpg_strsource (err));
+ else
+ puts (blockdev);
+ }
+ break;
+
+ case aCreate: /* Create a new container. */
+ {
+ if (argc != 1)
+ wrong_args ("--create filename");
+ start_idle_task ();
+ err = g13_create_container (&ctrl, argv[0]);
+ if (err)
+ log_error ("error creating a new container: %s <%s>\n",
+ gpg_strerror (err), gpg_strsource (err));
+ else
+ g13_request_shutdown ();
+ }
+ break;
+
+ case aMount: /* Mount a container. */
+ {
+ if (argc != 1 && argc != 2 )
+ wrong_args ("--mount filename [mountpoint]");
+ start_idle_task ();
+ err = g13_mount_container (&ctrl, argv[0], argc == 2?argv[1]:NULL);
+ if (err)
+ log_error ("error mounting container '%s': %s <%s>\n",
+ *argv, gpg_strerror (err), gpg_strsource (err));
+ }
+ break;
+
+ case aUmount: /* Unmount a mounted container. */
+ {
+ if (argc != 1)
+ wrong_args ("--umount filename");
+ err = g13_umount_container (&ctrl, argv[0], NULL);
+ if (err)
+ log_error ("error unmounting container '%s': %s <%s>\n",
+ *argv, gpg_strerror (err), gpg_strsource (err));
+ }
+ break;
+
+ case aSuspend: /* Suspend a container. */
+ {
+ /* Fixme: Should we add a suspend all container option? */
+ if (argc != 1)
+ wrong_args ("--suspend filename");
+ err = g13_suspend_container (&ctrl, argv[0]);
+ if (err)
+ log_error ("error suspending container '%s': %s <%s>\n",
+ *argv, gpg_strerror (err), gpg_strsource (err));
+ }
+ break;
+
+ case aResume: /* Resume a suspended container. */
+ {
+ /* Fixme: Should we add a resume all container option? */
+ if (argc != 1)
+ wrong_args ("--resume filename");
+ err = g13_resume_container (&ctrl, argv[0]);
+ if (err)
+ log_error ("error resuming container '%s': %s <%s>\n",
+ *argv, gpg_strerror (err), gpg_strsource (err));
+ }
+ break;
+
+ default:
+ log_error (_("invalid command (there is no implicit command)\n"));
+ break;
+ }
+
+ g13_deinit_default_ctrl (&ctrl);
+
+ if (!err)
+ join_idle_task ();
+
+ /* Cleanup. */
+ g13_exit (0);
+ return 8; /*NOTREACHED*/
+}
+
+
+/* Store defaults into the per-connection CTRL object. */
+void
+g13_init_default_ctrl (ctrl_t ctrl)
+{
+ ctrl->conttype = cmdline_conttype? cmdline_conttype : CONTTYPE_ENCFS;
+}
+
+
+/* Release remaining resources allocated in the CTRL object. */
+void
+g13_deinit_default_ctrl (ctrl_t ctrl)
+{
+ call_syshelp_release (ctrl);
+ FREE_STRLIST (ctrl->recipients);
+}
+
+
+/* Request a shutdown. This can be used when the process should
+ * finish instead of running the idle task. */
+void
+g13_request_shutdown (void)
+{
+ shutdown_pending++;
+}
+
+
+/* This function is called for each signal we catch. It is run in the
+ main context or the one of a NPth thread and thus it is not
+ restricted in what it may do. */
+static void
+handle_signal (int signo)
+{
+ switch (signo)
+ {
+#ifndef HAVE_W32_SYSTEM
+ case SIGHUP:
+ log_info ("SIGHUP received - re-reading configuration\n");
+ /* Fixme: Not yet implemented. */
+ break;
+
+ case SIGUSR1:
+ log_info ("SIGUSR1 received - printing internal information:\n");
+ /* Fixme: We need to see how to integrate pth dumping into our
+ logging system. */
+ /* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */
+ mountinfo_dump_all ();
+ break;
+
+ case SIGUSR2:
+ log_info ("SIGUSR2 received - no action defined\n");
+ break;
+
+ case SIGTERM:
+ if (!shutdown_pending)
+ log_info ("SIGTERM received - shutting down ...\n");
+ else
+ log_info ("SIGTERM received - still %u runners active\n",
+ runner_get_threads ());
+ shutdown_pending++;
+ if (shutdown_pending > 2)
+ {
+ log_info ("shutdown forced\n");
+ log_info ("%s %s stopped\n", strusage(11), strusage(13) );
+ g13_exit (0);
+ }
+ break;
+
+ case SIGINT:
+ log_info ("SIGINT received - immediate shutdown\n");
+ log_info( "%s %s stopped\n", strusage(11), strusage(13));
+ g13_exit (0);
+ break;
+#endif /*!HAVE_W32_SYSTEM*/
+
+ default:
+ log_info ("signal %d received - no action defined\n", signo);
+ }
+}
+
+
+/* This ticker function is called about every TIMERTICK_INTERVAL_SEC
+ seconds. */
+static void
+handle_tick (void)
+{
+ /* log_debug ("TICK\n"); */
+}
+
+
+/* The idle task. We use a separate thread to do idle stuff and to
+ catch signals. */
+static void *
+idle_task (void *dummy_arg)
+{
+ int signo; /* The number of a raised signal is stored here. */
+ int saved_errno;
+ struct timespec abstime;
+ struct timespec curtime;
+ struct timespec timeout;
+ int ret;
+
+ (void)dummy_arg;
+
+ /* Create the event to catch the signals. */
+#ifndef HAVE_W32_SYSTEM
+ npth_sigev_init ();
+ npth_sigev_add (SIGHUP);
+ npth_sigev_add (SIGUSR1);
+ npth_sigev_add (SIGUSR2);
+ npth_sigev_add (SIGINT);
+ npth_sigev_add (SIGTERM);
+ npth_sigev_fini ();
+#endif
+
+ npth_clock_gettime (&abstime);
+ abstime.tv_sec += TIMERTICK_INTERVAL_SEC;
+
+ for (;;)
+ {
+ /* The shutdown flag allows us to terminate the idle task. */
+ if (shutdown_pending)
+ {
+ runner_cancel_all ();
+
+ if (!runner_get_threads ())
+ break; /* ready */
+ }
+
+ npth_clock_gettime (&curtime);
+ if (!(npth_timercmp (&curtime, &abstime, <)))
+ {
+ /* Timeout. */
+ handle_tick ();
+ npth_clock_gettime (&abstime);
+ abstime.tv_sec += TIMERTICK_INTERVAL_SEC;
+ }
+ npth_timersub (&abstime, &curtime, &timeout);
+
+#ifndef HAVE_W32_SYSTEM
+ ret = npth_pselect (0, NULL, NULL, NULL, &timeout, npth_sigev_sigmask());
+ saved_errno = errno;
+
+ while (npth_sigev_get_pending(&signo))
+ handle_signal (signo);
+#else
+ ret = npth_eselect (0, NULL, NULL, NULL, &timeout, NULL, NULL);
+ saved_errno = errno;
+#endif
+
+ if (ret == -1 && saved_errno != EINTR)
+ {
+ log_error (_("npth_pselect failed: %s - waiting 1s\n"),
+ strerror (saved_errno));
+ npth_sleep (1);
+ continue;
+ }
+
+ if (ret <= 0)
+ {
+ /* Interrupt or timeout. Will be handled when calculating the
+ next timeout. */
+ continue;
+ }
+
+ /* Here one would add processing of file descriptors. */
+ }
+
+ log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
+ return NULL;
+}
+
+
+/* Start the idle task. */
+static void
+start_idle_task (void)
+{
+ npth_attr_t tattr;
+ npth_t thread;
+ sigset_t sigs; /* The set of signals we want to catch. */
+ int err;
+
+#ifndef HAVE_W32_SYSTEM
+ /* These signals should always go to the idle task, so they need to
+ be blocked everywhere else. We assume start_idle_task is called
+ from the main thread before any other threads are created. */
+ sigemptyset (&sigs);
+ sigaddset (&sigs, SIGHUP);
+ sigaddset (&sigs, SIGUSR1);
+ sigaddset (&sigs, SIGUSR2);
+ sigaddset (&sigs, SIGINT);
+ sigaddset (&sigs, SIGTERM);
+ npth_sigmask (SIG_BLOCK, &sigs, NULL);
+#endif
+
+ npth_attr_init (&tattr);
+ npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE);
+
+ err = npth_create (&thread, &tattr, idle_task, NULL);
+ if (err)
+ {
+ log_fatal ("error starting idle task: %s\n", strerror (err));
+ return; /*NOTREACHED*/
+ }
+ npth_setname_np (thread, "idle-task");
+ idle_task_thread = thread;
+ npth_attr_destroy (&tattr);
+}
+
+
+/* Wait for the idle task to finish. */
+static void
+join_idle_task (void)
+{
+ int err;
+
+ /* FIXME: This assumes that a valid pthread_t is non-null. That is
+ not guaranteed. */
+ if (idle_task_thread)
+ {
+ err = npth_join (idle_task_thread, NULL);
+ if (err)
+ log_error ("waiting for idle task thread failed: %s\n",
+ strerror (err));
+ }
+}
diff --git a/g13/g13.h b/g13/g13.h
new file mode 100644
index 0000000..9c0acb5
--- /dev/null
+++ b/g13/g13.h
@@ -0,0 +1,60 @@
+/* g13.h - Global definitions for G13.
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_H
+#define G13_H
+
+#include "g13-common.h"
+
+
+/* Forward declaration for an object defined in server.c. */
+struct server_local_s;
+/* Forward declaration for an object defined in call-syshelp.c. */
+struct call_syshelp_s;
+
+
+/* Session control object. This object is passed down to most
+ functions. The default values for it are set by
+ g13_init_default_ctrl(). */
+struct server_control_s
+{
+ int no_server; /* We are not running under server control */
+ int status_fd; /* Only for non-server mode */
+ struct server_local_s *server_local;
+ struct call_syshelp_s *syshelp_local;
+
+ int agent_seen; /* Flag indicating that the gpg-agent has been
+ accessed. */
+
+ int with_colons; /* Use column delimited output format */
+
+ /* Type of the current container. See the CONTTYPE_ constants. */
+ int conttype;
+
+ strlist_t recipients; /* List of recipients. */
+
+};
+
+
+/*-- g13.c --*/
+void g13_init_default_ctrl (ctrl_t ctrl);
+void g13_deinit_default_ctrl (ctrl_t ctrl);
+void g13_request_shutdown (void);
+
+#endif /*G13_H*/
diff --git a/g13/g13tuple.c b/g13/g13tuple.c
new file mode 100644
index 0000000..6693826
--- /dev/null
+++ b/g13/g13tuple.c
@@ -0,0 +1,340 @@
+/* g13tuple.c - Tuple handling
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2009, 2015, 2016 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "g13.h"
+#include "g13tuple.h"
+#include "keyblob.h" /* Required for dump_tupledesc. */
+
+
+/* Definition of the tuple descriptor object. */
+struct tupledesc_s
+{
+ unsigned char *data; /* The tuple data. */
+ size_t datalen; /* The length of the data. */
+ size_t pos; /* The current position as used by next_tuple. */
+ int refcount; /* Number of references hold. */
+};
+
+
+
+/* Append the TAG and the VALUE to the MEMBUF. There is no error
+ checking here; this is instead done while getting the value back
+ from the membuf. */
+void
+append_tuple (membuf_t *membuf, int tag, const void *value, size_t length)
+{
+ unsigned char buf[2];
+
+ assert (tag >= 0 && tag <= 0xffff);
+ assert (length <= 0xffff);
+
+ buf[0] = tag >> 8;
+ buf[1] = tag;
+ put_membuf (membuf, buf, 2);
+ buf[0] = length >> 8;
+ buf[1] = length;
+ put_membuf (membuf, buf, 2);
+ if (length)
+ put_membuf (membuf, value, length);
+}
+
+
+/* Append the unsigned integer VALUE under TAG to MEMBUF. We make
+ * sure that the most significant bit is always cleared to explicitly
+ * flag the value as unsigned. */
+void
+append_tuple_uint (membuf_t *membuf, int tag, unsigned long long value)
+{
+ unsigned char buf[16];
+ unsigned char *p;
+ unsigned int len;
+
+ p = buf + sizeof buf;
+ len = 0;
+ do
+ {
+ if (p == buf)
+ BUG () ;
+ *--p = (value & 0xff);
+ value >>= 8;
+ len++;
+ }
+ while (value);
+
+ /* Prepend a zero byte if the first byte has its MSB set. */
+ if ((*p & 0x80))
+ {
+ if (p == buf)
+ BUG () ;
+ *--p = 0;
+ len++;
+ }
+
+ append_tuple (membuf, tag, p, len);
+}
+
+
+/* Create a tuple object by moving the ownership of (DATA,DATALEN) to
+ * a new object. Returns 0 on success and stores the new object at
+ * R_TUPLEHD. The return object must be released using
+ * destroy_tuples(). */
+gpg_error_t
+create_tupledesc (tupledesc_t *r_desc, void *data, size_t datalen)
+{
+ if (datalen < 5 || memcmp (data, "\x00\x00\x00\x01\x01", 5))
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ *r_desc = xtrymalloc (sizeof **r_desc);
+ if (!*r_desc)
+ return gpg_error_from_syserror ();
+ (*r_desc)->data = data;
+ (*r_desc)->datalen = datalen;
+ (*r_desc)->pos = 0;
+ (*r_desc)->refcount = 1;
+ return 0;
+}
+
+/* Unref a tuple descriptor and if the refcount is down to 0 release
+ its allocated storage. */
+void
+destroy_tupledesc (tupledesc_t tupledesc)
+{
+ if (!tupledesc)
+ return;
+
+ if (!--tupledesc->refcount)
+ {
+ xfree (tupledesc->data);
+ xfree (tupledesc);
+ }
+}
+
+
+tupledesc_t
+ref_tupledesc (tupledesc_t tupledesc)
+{
+ if (tupledesc)
+ tupledesc->refcount++;
+ return tupledesc;
+}
+
+
+/* Return a pointer to the memory used to store the tuples. This is
+ * the data originally provided to create_tupledesc. It is higly
+ * recommended that the callers uses ref_tupledesc before calling this
+ * function and unref_tupledesc when the return data will not anymore
+ * be used. */
+const void *
+get_tupledesc_data (tupledesc_t tupledesc, size_t *r_datalen)
+{
+ *r_datalen = tupledesc->datalen;
+ return tupledesc->data;
+}
+
+/* Find the first tuple with tag TAG. On success return a pointer to
+ its value and store the length of the value at R_LENGTH. If no
+ tuple was found return NULL. For use by next_tuple, the last
+ position is stored in the descriptor. */
+const void *
+find_tuple (tupledesc_t tupledesc, unsigned int tag, size_t *r_length)
+{
+ const unsigned char *s;
+ const unsigned char *s_end; /* Points right behind the data. */
+ unsigned int t;
+ size_t n;
+
+ s = tupledesc->data;
+ if (!s)
+ return NULL;
+ s_end = s + tupledesc->datalen;
+ while (s < s_end)
+ {
+ /* We use addresses for the overflow check to avoid undefined
+ behaviour. size_t should work with all flat memory models. */
+ if ((size_t)s+3 >= (size_t)s_end || (size_t)s + 3 < (size_t)s)
+ break;
+ t = s[0] << 8;
+ t |= s[1];
+ n = s[2] << 8;
+ n |= s[3];
+ s += 4;
+ if ((size_t)s + n > (size_t)s_end || (size_t)s + n < (size_t)s)
+ break;
+ if (t == tag)
+ {
+ tupledesc->pos = (s + n) - tupledesc->data;
+ *r_length = n;
+ return s;
+ }
+ s += n;
+ }
+ return NULL;
+}
+
+
+/* Helper for find_tuple_uint and others. */
+static gpg_error_t
+convert_uint (const unsigned char *s, size_t n, unsigned long long *r_value)
+{
+ unsigned long long value = 0;
+
+ *r_value = 0;
+
+ if (!s)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ if (!n || (*s & 0x80)) /* No bytes or negative. */
+ return gpg_error (GPG_ERR_ERANGE);
+ if (n && !*s) /* Skip a leading zero. */
+ {
+ n--;
+ s++;
+ }
+ if (n > sizeof value)
+ return gpg_error (GPG_ERR_ERANGE);
+ for (; n; n--, s++)
+ {
+ value <<= 8;
+ value |= *s;
+ }
+ *r_value = value;
+ return 0;
+}
+
+
+/* Similar to find-tuple but expects an unsigned int value and stores
+ * that at R_VALUE. If the tag was not found GPG_ERR_NOT_FOUND is
+ * returned and 0 stored at R_VALUE. If the value cannot be converted
+ * to an unsigned integer GPG_ERR_ERANGE is returned. */
+gpg_error_t
+find_tuple_uint (tupledesc_t tupledesc, unsigned int tag,
+ unsigned long long *r_value)
+{
+ const unsigned char *s;
+ size_t n;
+
+ s = find_tuple (tupledesc, tag, &n);
+ return convert_uint (s, n, r_value);
+}
+
+
+const void *
+next_tuple (tupledesc_t tupledesc, unsigned int *r_tag, size_t *r_length)
+{
+ const unsigned char *s;
+ const unsigned char *s_end; /* Points right behind the data. */
+ unsigned int t;
+ size_t n;
+
+ s = tupledesc->data;
+ if (!s)
+ return NULL;
+ s_end = s + tupledesc->datalen;
+ s += tupledesc->pos;
+ if (s < s_end
+ && !((size_t)s + 3 >= (size_t)s_end || (size_t)s + 3 < (size_t)s))
+ {
+ t = s[0] << 8;
+ t |= s[1];
+ n = s[2] << 8;
+ n |= s[3];
+ s += 4;
+ if (!((size_t)s + n > (size_t)s_end || (size_t)s + n < (size_t)s))
+ {
+ tupledesc->pos = (s + n) - tupledesc->data;
+ *r_tag = t;
+ *r_length = n;
+ return s;
+ }
+ }
+
+ return NULL;
+}
+
+
+/* Return true if BUF has only printable characters. */
+static int
+all_printable (const void *buf, size_t buflen)
+{
+ const unsigned char *s;
+
+ for (s=buf ; buflen; s++, buflen--)
+ if (*s < 32 || *s > 126)
+ return 0;
+ return 1;
+}
+
+
+/* Print information about TUPLES to the log stream. */
+void
+dump_tupledesc (tupledesc_t tuples)
+{
+ size_t n;
+ unsigned int tag;
+ const void *value;
+ unsigned long long uint;
+
+ log_info ("keyblob dump:\n");
+ tag = KEYBLOB_TAG_BLOBVERSION;
+ value = find_tuple (tuples, tag, &n);
+ while (value)
+ {
+ log_info (" tag: %-5u len: %-2u value: ", tag, (unsigned int)n);
+ if (!n)
+ log_printf ("[none]\n");
+ else
+ {
+ switch (tag)
+ {
+ case KEYBLOB_TAG_ENCKEY:
+ case KEYBLOB_TAG_MACKEY:
+ log_printf ("[confidential]\n");
+ break;
+
+ case KEYBLOB_TAG_ALGOSTR:
+ if (n < 100 && all_printable (value, n))
+ log_printf ("%.*s\n", (int)n, (const char*)value);
+ else
+ log_printhex (value, n, "");
+ break;
+
+ case KEYBLOB_TAG_CONT_NSEC:
+ case KEYBLOB_TAG_ENC_NSEC:
+ case KEYBLOB_TAG_ENC_OFF:
+ if (!convert_uint (value, n, &uint))
+ log_printf ("%llu\n", uint);
+ else
+ log_printhex (value, n, "");
+ break;
+
+ default:
+ log_printhex (value, n, "");
+ break;
+ }
+ }
+ value = next_tuple (tuples, &tag, &n);
+ }
+}
diff --git a/g13/g13tuple.h b/g13/g13tuple.h
new file mode 100644
index 0000000..77d595d
--- /dev/null
+++ b/g13/g13tuple.h
@@ -0,0 +1,52 @@
+/* g13tuple.h - Tuple handling
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_G13TUPLE_H
+#define G13_G13TUPLE_H
+
+#include "../common/membuf.h"
+
+/* Append a new tuple to a memory buffer. */
+void append_tuple (membuf_t *membuf,
+ int tag, const void *value, size_t length);
+void append_tuple_uint (membuf_t *membuf, int tag,
+ unsigned long long value);
+
+/* The tuple descriptor object. */
+struct tupledesc_s;
+typedef struct tupledesc_s *tupledesc_t;
+
+gpg_error_t create_tupledesc (tupledesc_t *r_tupledesc,
+ void *data, size_t datalen);
+void destroy_tupledesc (tupledesc_t tupledesc);
+tupledesc_t ref_tupledesc (tupledesc_t tupledesc);
+#define unref_tupledesc(a) destroy_tupledesc ((a))
+const void *get_tupledesc_data (tupledesc_t tupledesc, size_t *r_datalen);
+
+const void *find_tuple (tupledesc_t tupledesc,
+ unsigned int tag, size_t *r_length);
+gpg_error_t find_tuple_uint (tupledesc_t tupledesc, unsigned int tag,
+ unsigned long long *r_value);
+const void *next_tuple (tupledesc_t tupledesc,
+ unsigned int *r_tag, size_t *r_length);
+
+void dump_tupledesc (tupledesc_t tuples);
+
+
+#endif /*G13_G13TUPLE_H*/
diff --git a/g13/keyblob.c b/g13/keyblob.c
new file mode 100644
index 0000000..1fb9be7
--- /dev/null
+++ b/g13/keyblob.c
@@ -0,0 +1,207 @@
+/* keyblob.c - Keyblob parser and builder.
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2016 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "g13.h"
+#include "mount.h"
+
+#include "keyblob.h"
+#include "../common/sysutils.h"
+#include "../common/host2net.h"
+
+
+/* Parse the header prefix and return the length of the entire header. */
+static gpg_error_t
+parse_header (const char *filename,
+ const unsigned char *packet, size_t packetlen,
+ size_t *r_headerlen)
+{
+ unsigned int len;
+
+ if (packetlen != 32)
+ return gpg_error (GPG_ERR_BUG);
+
+ len = buf32_to_uint (packet+2);
+ if (packet[0] != (0xc0|61) || len < 26
+ || memcmp (packet+6, "GnuPG/G13", 10))
+ {
+ log_error ("file '%s' is not valid container\n", filename);
+ return gpg_error (GPG_ERR_INV_OBJ);
+ }
+ if (packet[16] != 1)
+ {
+ log_error ("unknown version %u of container '%s'\n",
+ (unsigned int)packet[16], filename);
+ return gpg_error (GPG_ERR_INV_OBJ);
+ }
+ if (packet[17] || packet[18]
+ || packet[26] || packet[27] || packet[28] || packet[29]
+ || packet[30] || packet[31])
+ log_info ("WARNING: unknown meta information in '%s'\n", filename);
+ if (packet[19])
+ log_info ("WARNING: OS flag is not supported in '%s'\n", filename);
+ if (packet[24] > 1 )
+ log_info ("Note: meta data copies in '%s' are ignored\n", filename);
+
+ len = buf32_to_uint (packet+20);
+
+ /* Do a basic sanity check on the length. */
+ if (len < 32 || len > 1024*1024)
+ {
+ log_error ("bad length given in container '%s'\n", filename);
+ return gpg_error (GPG_ERR_INV_OBJ);
+ }
+
+ *r_headerlen = len;
+ return 0;
+}
+
+
+/* Read the prefix of the keyblob and do some basic parsing. On
+ success returns an open estream file at R_FP and the length of the
+ header at R_HEADERLEN. */
+static gpg_error_t
+read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen)
+{
+ gpg_error_t err;
+ estream_t fp;
+ unsigned char packet[32];
+
+ *r_fp = NULL;
+
+ fp = es_fopen (filename, "rb");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading '%s': %s\n", filename, gpg_strerror (err));
+ return err;
+ }
+
+ /* Read the header. It is defined as 32 bytes thus we read it in one go. */
+ if (es_fread (packet, 32, 1, fp) != 1)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading the header of '%s': %s\n",
+ filename, gpg_strerror (err));
+ es_fclose (fp);
+ return err;
+ }
+
+ err = parse_header (filename, packet, 32, r_headerlen);
+ if (err)
+ es_fclose (fp);
+ else
+ *r_fp = fp;
+
+ return err;
+}
+
+
+
+/*
+ * Test whether the container with name FILENAME is a suitable G13
+ * container. This function may even be called on a mounted
+ * container.
+ */
+gpg_error_t
+g13_is_container (ctrl_t ctrl, const char *filename)
+{
+ gpg_error_t err;
+ estream_t fp = NULL;
+ size_t dummy;
+
+ (void)ctrl;
+
+ /* Read just the prefix of the header. */
+ err = read_keyblob_prefix (filename, &fp, &dummy);
+ if (!err)
+ es_fclose (fp);
+ return err;
+}
+
+
+/*
+ * Read the keyblob at FILENAME. The caller should have acquired a
+ * lockfile and checked that the file exists.
+ */
+gpg_error_t
+g13_keyblob_read (const char *filename,
+ void **r_enckeyblob, size_t *r_enckeybloblen)
+{
+ gpg_error_t err;
+ estream_t fp = NULL;
+ size_t headerlen = 0;
+ size_t msglen;
+ void *msg = NULL;
+
+ *r_enckeyblob = NULL;
+ *r_enckeybloblen = 0;
+
+ err = read_keyblob_prefix (filename, &fp, &headerlen);
+ if (err)
+ goto leave;
+
+ if (opt.verbose)
+ log_info ("header length of '%s' is %zu\n", filename, headerlen);
+
+ /* Read everything including the padding. We should eventually do a
+ regular OpenPGP parsing to detect the padding packet and pass
+ only the actual used OpenPGP data to the engine. This is in
+ particular required when supporting CMS which will be
+ encapsulated in an OpenPGP packet. */
+ assert (headerlen >= 32);
+ msglen = headerlen - 32;
+ if (!msglen)
+ {
+ err = gpg_error (GPG_ERR_NO_DATA);
+ goto leave;
+ }
+ msg = xtrymalloc (msglen);
+ if (!msglen)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (es_fread (msg, msglen, 1, fp) != 1)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading keyblob of '%s': %s\n",
+ filename, gpg_strerror (err));
+ goto leave;
+ }
+
+ *r_enckeyblob = msg;
+ msg = NULL;
+ *r_enckeybloblen = msglen;
+
+ leave:
+ xfree (msg);
+ es_fclose (fp);
+
+ return err;
+}
diff --git a/g13/keyblob.h b/g13/keyblob.h
new file mode 100644
index 0000000..90fcf60
--- /dev/null
+++ b/g13/keyblob.h
@@ -0,0 +1,162 @@
+/* keyblob.h - Defs to describe a keyblob
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_KEYBLOB_H
+#define G13_KEYBLOB_H
+
+/* The setup area (header block) is the actual core of G13. Here is
+ the format:
+
+ u8 Packet type. Value is 61 (0x3d).
+ u8 Constant value 255 (0xff).
+ u32 Length of the following structure
+ b10 Value: "GnuPG/G13\x00".
+ u8 Version. Value is 1.
+ u8 reserved
+ u8 reserved
+ u8 OS Flag: 0 = unspecified, 1 = Linux
+ u32 Length of the entire header. This includes all bytes
+ starting at the packet type and ending with the last
+ padding byte of the header.
+ u8 Number of copies of this header (1..255).
+ u8 Number of copies of this header at the end of the
+ container (usually 0).
+ b6 reserved
+ n bytes: OpenPGP encrypted and optionally signed keyblob.
+ n bytes: CMS encrypted and optionally signed keyblob. Such a CMS
+ packet will be enclosed in a private flagged OpenPGP
+ packet. Either the OpenPGP encrypted packet as described
+ above, the CMS encrypted or both packets must exist. The
+ encapsulation packet has this structure:
+ u8 Packet type. Value is 61 (0x3d).
+ u8 Constant value 255 (0xff).
+ u32 Length of the following structure
+ b10 Value: "GnuPG/CMS\x00".
+ b(n) Regular CMS structure.
+ n bytes: Padding. The structure resembles an OpenPGP packet.
+ u8 Packet type. Value is 61 (0x3d).
+ u8 Constant value 255 (0xff).
+ u32 Length of the following structure
+ b10 Value: "GnuPG/PAD\x00".
+ b(n) Padding stuff.
+ (repeat the above value
+ or if the remaining N < 10, all 0x00).
+ Given this structure the minimum padding is 16 bytes.
+
+ n bytes: File system container.
+ (optionally followed by copies on the header).
+*/
+
+
+#define KEYBLOB_TAG_BLOBVERSION 0
+/* This tag is used to describe the version of the keyblob. It must
+ be the first tag in a keyblob and may only occur once. Its value
+ is a single byte giving the blob version. The only defined version
+ is 1. */
+
+#define KEYBLOB_TAG_CONTTYPE 1
+/* This tag gives the type of the container. The value is a two byte
+ big endian integer giving the type of the container as described by
+ the CONTTYPE_ constants. */
+
+#define KEYBLOB_TAG_DETACHED 2
+/* Indicates that the actual storage is not in the same file as the
+ keyblob. If a value is given it is expected to be the GUID of the
+ partition. */
+
+#define KEYBLOB_TAG_CREATED 3
+/* This is an ISO 8601 time string with the date the container was
+ created. */
+
+#define KEYBLOB_TAG_CONT_NSEC 7
+/* Number of 512-byte sectors of the entire container including all
+ copies of the setup area. */
+
+#define KEYBLOB_TAG_ENC_NSEC 8
+#define KEYBLOB_TAG_ENC_OFF 9
+/* Number of 512-byte sectors used for the encrypted data and its
+ start offset in 512-byte sectors from the begin of the container.
+ Note that these information can also be deduced from the
+ unencrypted part of the setup area. */
+
+#define KEYBLOB_TAG_ALGOSTR 10
+/* For a dm-crypt container this is the used algorithm string. For
+ example: "aes-cbc-essiv:sha256". */
+
+#define KEYBLOB_TAG_KEYNO 16
+/* This tag indicates a new key. The value is a 4 byte big endian
+ integer giving the key number. If the container type does only
+ need one key this key number should be 0. */
+
+#define KEYBLOB_TAG_ENCALGO 17
+/* Describes the algorithm of the key. It must follow a KEYNO tag.
+ The value is a 2 byte big endian algorithm number. The algorithm
+ numbers used are those from Libgcrypt (e.g. AES 128 is described by
+ the value 7). This tag is optional. */
+
+#define KEYBLOB_TAG_ENCKEY 18
+/* This tag gives the actual encryption key. It must follow a KEYNO
+ tag. The value is the plain key. */
+
+#define KEYBLOB_TAG_MACALGO 19
+/* Describes the MAC algorithm. It must follow a KEYNO tag. The
+ value is a 2 byte big endian algorithm number describing the MAC
+ algorithm with a value of 1 indicating HMAC. It is followed by
+ data specific to the MAC algorithm. In case of HMAC this data is a
+ 2 byte big endian integer with the Libgcrypt algorithm id of the
+ hash algorithm. */
+
+#define KEYBLOB_TAG_MACKEY 20
+/* This tag gives the actual MACing key. It must follow a KEYNO tag.
+ The value is the key used for MACing. */
+
+
+#define KEYBLOB_TAG_HDRCOPY 21
+/* The value of this tag is a copy of the setup area prefix header
+ block (packet 61 with marker "GnuPG/G13\x00". We use it to allow
+ signing of that cleartext data. */
+
+#define KEYBLOB_TAG_FILLER 0xffff
+/* This tag may be used for alignment and padding purposes. The value
+ has no meaning. */
+
+
+
+#define CONTTYPE_ENCFS 1
+/* A EncFS based backend. This requires a whole directory which
+ includes the encrypted files. Metadata is not encrypted. */
+
+#define CONTTYPE_DM_CRYPT 2
+/* A DM-Crypt based backend. */
+
+
+#define CONTTYPE_TRUECRYPT 21571
+/* A Truecrypt (www.truecrypt.org) based container. Due to the design
+ of truecrypt this requires a second datafile because it is not
+ possible to prepend a truecrypt container with our keyblob. */
+
+
+
+/*-- keyblob.c --*/
+gpg_error_t g13_is_container (ctrl_t ctrl, const char *filename);
+gpg_error_t g13_keyblob_read (const char *filename,
+ void **r_enckeyblob, size_t *r_enckeybloblen);
+
+
+#endif /*G13_KEYBLOB_H*/
diff --git a/g13/mount.c b/g13/mount.c
new file mode 100644
index 0000000..45b6080
--- /dev/null
+++ b/g13/mount.c
@@ -0,0 +1,265 @@
+/* mount.c - Mount a crypto container
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "g13.h"
+#include "../common/i18n.h"
+#include "mount.h"
+
+#include "keyblob.h"
+#include "backend.h"
+#include "g13tuple.h"
+#include "mountinfo.h"
+#include "runner.h"
+#include "../common/host2net.h"
+#include "server.h" /*(g13_keyblob_decrypt)*/
+#include "../common/sysutils.h"
+#include "call-syshelp.h"
+
+
+/* Mount the container with name FILENAME at MOUNTPOINT. */
+gpg_error_t
+g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
+{
+ gpg_error_t err;
+ dotlock_t lock;
+ int needs_syshelp = 0;
+ void *enckeyblob = NULL;
+ size_t enckeybloblen;
+ void *keyblob = NULL;
+ size_t keybloblen;
+ tupledesc_t tuples = NULL;
+ size_t n;
+ const unsigned char *value;
+ int conttype;
+ unsigned int rid;
+ char *mountpoint_buffer = NULL;
+ char *blockdev_buffer = NULL;
+
+ /* Decide whether we need to use the g13-syshelp. */
+ err = call_syshelp_find_device (ctrl, filename, &blockdev_buffer);
+ if (!err)
+ {
+ needs_syshelp = 1;
+ filename = blockdev_buffer;
+ }
+ else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+ {
+ log_error ("error finding device '%s': %s <%s>\n",
+ filename, gpg_strerror (err), gpg_strsource (err));
+ return err;
+ }
+ else
+ {
+ /* A quick check to see whether we can the container exists. */
+ if (gnupg_access (filename, R_OK))
+ return gpg_error_from_syserror ();
+ }
+
+ if (!mountpoint)
+ {
+ mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX");
+ if (!mountpoint_buffer)
+ return gpg_error_from_syserror ();
+ if (!gnupg_mkdtemp (mountpoint_buffer))
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("can't create directory '%s': %s\n"),
+ "/tmp/g13-XXXXXX", gpg_strerror (err));
+ xfree (mountpoint_buffer);
+ return err;
+ }
+ mountpoint = mountpoint_buffer;
+ }
+
+ err = 0;
+ if (needs_syshelp)
+ lock = NULL;
+ else
+ {
+ /* Try to take a lock. */
+ lock = dotlock_create (filename, 0);
+ if (!lock)
+ {
+ xfree (mountpoint_buffer);
+ return gpg_error_from_syserror ();
+ }
+
+ if (dotlock_take (lock, 0))
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
+ /* Check again that the file exists. */
+ if (!needs_syshelp)
+ {
+ struct stat sb;
+
+ if (gnupg_stat (filename, &sb))
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
+ /* Read the encrypted keyblob. */
+ if (needs_syshelp)
+ {
+ err = call_syshelp_set_device (ctrl, filename);
+ if (err)
+ goto leave;
+ err = call_syshelp_get_keyblob (ctrl, &enckeyblob, &enckeybloblen);
+ }
+ else
+ err = g13_keyblob_read (filename, &enckeyblob, &enckeybloblen);
+ if (err)
+ goto leave;
+
+ /* Decrypt that keyblob and store it in a tuple descriptor. */
+ err = g13_keyblob_decrypt (ctrl, enckeyblob, enckeybloblen,
+ &keyblob, &keybloblen);
+ if (err)
+ goto leave;
+ xfree (enckeyblob);
+ enckeyblob = NULL;
+
+ err = create_tupledesc (&tuples, keyblob, keybloblen);
+ if (!err)
+ keyblob = NULL;
+ else
+ {
+ if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
+ log_error ("unknown keyblob version\n");
+ goto leave;
+ }
+ if (opt.verbose)
+ dump_tupledesc (tuples);
+
+ value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n);
+ if (!value || n != 2)
+ conttype = 0;
+ else
+ conttype = (value[0] << 8 | value[1]);
+ if (!be_is_supported_conttype (conttype))
+ {
+ log_error ("content type %d is not supported\n", conttype);
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ goto leave;
+ }
+ err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid);
+ if (err)
+ ;
+ else if (conttype == CONTTYPE_DM_CRYPT)
+ g13_request_shutdown ();
+ else
+ {
+ /* Unless this is a DM-CRYPT mount we put it into our mounttable
+ so that we can manage the mounts ourselves. For dm-crypt we
+ do not keep a process to monitor he mounts (for now). */
+ err = mountinfo_add_mount (filename, mountpoint, conttype, rid,
+ !!mountpoint_buffer);
+ /* Fixme: What shall we do if this fails? Add a provisional
+ mountinfo entry first and remove it on error? */
+ if (!err)
+ {
+ char *tmp = percent_plus_escape (mountpoint);
+ if (!tmp)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ g13_status (ctrl, STATUS_MOUNTPOINT, tmp, NULL);
+ xfree (tmp);
+ }
+ }
+ }
+
+ leave:
+ destroy_tupledesc (tuples);
+ xfree (keyblob);
+ xfree (enckeyblob);
+ dotlock_destroy (lock);
+ xfree (mountpoint_buffer);
+ xfree (blockdev_buffer);
+ return err;
+}
+
+
+/* Unmount the container with name FILENAME or the one mounted at
+ MOUNTPOINT. If both are given the FILENAME takes precedence. */
+gpg_error_t
+g13_umount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
+{
+ gpg_error_t err;
+ char *blockdev;
+
+ if (!filename && !mountpoint)
+ return gpg_error (GPG_ERR_ENOENT);
+
+ /* Decide whether we need to use the g13-syshelp. */
+ err = call_syshelp_find_device (ctrl, filename, &blockdev);
+ if (!err)
+ {
+ /* Need to employ the syshelper to umount the file system. */
+ /* FIXME: We should get the CONTTYPE from the blockdev. */
+ err = be_umount_container (ctrl, CONTTYPE_DM_CRYPT, blockdev);
+ if (!err)
+ {
+ /* if (conttype == CONTTYPE_DM_CRYPT) */
+ g13_request_shutdown ();
+ }
+ }
+ else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+ {
+ log_error ("error finding device '%s': %s <%s>\n",
+ filename, gpg_strerror (err), gpg_strsource (err));
+ }
+ else
+ {
+ /* Not in g13tab - kill the runner process for this mount. */
+ unsigned int rid;
+ runner_t runner;
+
+ err = mountinfo_find_mount (filename, mountpoint, &rid);
+ if (err)
+ return err;
+
+ runner = runner_find_by_rid (rid);
+ if (!runner)
+ {
+ log_error ("runner %u not found\n", rid);
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ }
+
+ runner_cancel (runner);
+ runner_release (runner);
+ }
+
+ xfree (blockdev);
+ return err;
+}
diff --git a/g13/mount.h b/g13/mount.h
new file mode 100644
index 0000000..fd403d5
--- /dev/null
+++ b/g13/mount.h
@@ -0,0 +1,31 @@
+/* mount.h - Defs to mount a crypto container
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_MOUNT_H
+#define G13_MOUNT_H
+
+gpg_error_t g13_mount_container (ctrl_t ctrl,
+ const char *filename,
+ const char *mountpoint);
+gpg_error_t g13_umount_container (ctrl_t ctrl,
+ const char *filename,
+ const char *mountpoint);
+
+
+#endif /*G13_MOUNT_H*/
diff --git a/g13/mountinfo.c b/g13/mountinfo.c
new file mode 100644
index 0000000..ed898b8
--- /dev/null
+++ b/g13/mountinfo.c
@@ -0,0 +1,198 @@
+/* mountinfo.c - Track infos about mounts
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "g13.h"
+#include "../common/i18n.h"
+#include "mountinfo.h"
+
+#include "keyblob.h"
+#include "g13tuple.h"
+
+
+
+/* The object to keep track of mount information. */
+struct mounttable_s
+{
+ int in_use; /* The slot is in use. */
+ char *container; /* Name of the container. */
+ char *mountpoint; /* Name of the mounttype. */
+ int conttype; /* Type of the container. */
+ unsigned int rid; /* Identifier of the runner task. */
+ struct {
+ unsigned int remove:1; /* True if the mountpoint shall be removed
+ on umount. */
+ } flags;
+};
+
+
+/* The allocated table of mounts and its size. */
+static mtab_t mounttable;
+size_t mounttable_size;
+
+
+
+/* Add CONTAINER,MOUNTPOINT,CONTTYPE,RID to the mounttable. */
+gpg_error_t
+mountinfo_add_mount (const char *container, const char *mountpoint,
+ int conttype, unsigned int rid, int remove_flag)
+{
+ size_t idx;
+ mtab_t m;
+
+ for (idx=0; idx < mounttable_size; idx++)
+ if (!mounttable[idx].in_use)
+ break;
+ if (!(idx < mounttable_size))
+ {
+ size_t nslots = mounttable_size;
+
+ mounttable_size += 10;
+ m = xtrycalloc (mounttable_size, sizeof *mounttable);
+ if (!m)
+ return gpg_error_from_syserror ();
+ if (mounttable)
+ {
+ for (idx=0; idx < nslots; idx++)
+ m[idx] = mounttable[idx];
+ xfree (mounttable);
+ }
+ mounttable = m;
+ m = mounttable + nslots;
+ assert (!m->in_use);
+ }
+ else
+ m = mounttable + idx;
+
+ m->container = xtrystrdup (container);
+ if (!m->container)
+ return gpg_error_from_syserror ();
+ m->mountpoint = xtrystrdup (mountpoint);
+ if (!m->mountpoint)
+ {
+ xfree (m->container);
+ m->container = NULL;
+ return gpg_error_from_syserror ();
+ }
+ m->conttype = conttype;
+ m->rid = rid;
+ m->flags.remove = !!remove_flag;
+ m->in_use = 1;
+
+ return 0;
+}
+
+
+/* Remove a mount info. Either the CONTAINER, the MOUNTPOINT or the
+ RID must be given. The first argument given is used. */
+gpg_error_t
+mountinfo_del_mount (const char *container, const char *mountpoint,
+ unsigned int rid)
+{
+ gpg_error_t err;
+ size_t idx;
+ mtab_t m;
+
+ /* If a container or mountpint is givem search the RID via the
+ standard find function. */
+ if (container || mountpoint)
+ {
+ err = mountinfo_find_mount (container, mountpoint, &rid);
+ if (err)
+ return err;
+ }
+
+ /* Find via RID and delete. */
+ for (idx=0, m = mounttable; idx < mounttable_size; idx++, m++)
+ if (m->in_use && m->rid == rid)
+ {
+ if (m->flags.remove && m->mountpoint)
+ {
+ /* FIXME: This does not always work because the umount may
+ not have completed yet. We should add the mountpoints
+ to an idle queue and retry a remove. */
+ if (rmdir (m->mountpoint))
+ log_error ("error removing mount point '%s': %s\n",
+ m->mountpoint,
+ gpg_strerror (gpg_error_from_syserror ()));
+ }
+ m->in_use = 0;
+ xfree (m->container);
+ m->container = NULL;
+ xfree (m->mountpoint);
+ m->mountpoint = NULL;
+ return 0;
+ }
+ return gpg_error (GPG_ERR_NOT_FOUND);
+}
+
+
+/* Find a mount and return its rid at R_RID. If CONTAINER is given,
+ the search is done by the container name, if it is not given the
+ search is done by MOUNTPOINT. */
+gpg_error_t
+mountinfo_find_mount (const char *container, const char *mountpoint,
+ unsigned int *r_rid)
+{
+ size_t idx;
+ mtab_t m;
+
+ if (container)
+ {
+ for (idx=0, m = mounttable; idx < mounttable_size; idx++, m++)
+ if (m->in_use && !strcmp (m->container, container))
+ break;
+ }
+ else if (mountpoint)
+ {
+ for (idx=0, m = mounttable; idx < mounttable_size; idx++, m++)
+ if (m->in_use && !strcmp (m->mountpoint, mountpoint))
+ break;
+ }
+ else
+ idx = mounttable_size;
+ if (!(idx < mounttable_size))
+ return gpg_error (GPG_ERR_NOT_FOUND);
+
+ *r_rid = m->rid;
+ return 0;
+}
+
+
+/* Dump all info to the log stream. */
+void
+mountinfo_dump_all (void)
+{
+ size_t idx;
+ mtab_t m;
+
+ for (idx=0, m = mounttable; idx < mounttable_size; idx++, m++)
+ if (m->in_use)
+ log_info ("mtab[%d] %s on %s type %d rid %u%s\n",
+ (int)idx, m->container, m->mountpoint, m->conttype, m->rid,
+ m->flags.remove?" [remove]":"");
+}
diff --git a/g13/mountinfo.h b/g13/mountinfo.h
new file mode 100644
index 0000000..ab346bf
--- /dev/null
+++ b/g13/mountinfo.h
@@ -0,0 +1,40 @@
+/* mountinfo.h - Track infos about mounts
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_MOUNTINFO_H
+#define G13_MOUNTINFO_H
+
+struct mounttable_s;
+typedef struct mounttable_s *mtab_t;
+
+gpg_error_t mountinfo_add_mount (const char *container,
+ const char *mountpoint,
+ int conttype, unsigned int rid,
+ int remove_flag);
+gpg_error_t mountinfo_del_mount (const char *container,
+ const char *mountpoint,
+ unsigned int rid);
+gpg_error_t mountinfo_find_mount (const char *container,
+ const char *mountpoint,
+ unsigned int *r_rid);
+
+void mountinfo_dump_all (void);
+
+
+#endif /*G13_MOUNTINFO_H*/
diff --git a/g13/runner.c b/g13/runner.c
new file mode 100644
index 0000000..138269d
--- /dev/null
+++ b/g13/runner.c
@@ -0,0 +1,539 @@
+/* runner.c - Run and watch the backend engines
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <npth.h>
+
+#include "g13.h"
+#include "../common/i18n.h"
+#include "keyblob.h"
+#include "runner.h"
+#include "../common/exechelp.h"
+#include "mountinfo.h"
+
+/* The runner object. */
+struct runner_s
+{
+ char *name; /* The name of this runner. */
+ unsigned int identifier; /* The runner identifier. */
+
+ int spawned; /* True if runner_spawn has been called. */
+ npth_t thread; /* The TID of the runner thread. */
+ runner_t next_running; /* Builds a list of all running threads. */
+ int canceled; /* Set if a cancel has already been send once. */
+
+ int cancel_flag; /* If set the thread should terminate itself. */
+
+
+ /* We use a reference counter to know when it is safe to remove the
+ object. Lacking an explicit ref function this counter will take
+ only these two values:
+
+ 1 = Thread not running or only the thread is still running.
+ 2 = Thread is running and someone is holding a reference. */
+ int refcount;
+
+ pid_t pid; /* PID of the backend's process (the engine). */
+ int in_fd; /* File descriptors to read from the engine. */
+ int out_fd; /* File descriptors to write to the engine. */
+ engine_handler_fnc_t handler; /* The handler functions. */
+ engine_handler_cleanup_fnc_t handler_cleanup;
+ void *handler_data; /* Private data of HANDLER and HANDLER_CLEANUP. */
+
+ /* Instead of IN_FD we use an estream. Note that the runner thread
+ may close the stream and set status_fp to NULL at any time. Thus
+ it won't be a good idea to use it while the runner thread is
+ running. */
+ estream_t status_fp;
+};
+
+
+/* The head of the list of all running threads. */
+static runner_t running_threads;
+
+
+
+
+/* Write NBYTES of BUF to file descriptor FD. */
+static int
+writen (int fd, const void *buf, size_t nbytes)
+{
+ size_t nleft = nbytes;
+ int nwritten;
+
+ while (nleft > 0)
+ {
+ nwritten = npth_write (fd, buf, nleft);
+ if (nwritten < 0)
+ {
+ if (errno == EINTR)
+ nwritten = 0;
+ else
+ return -1;
+ }
+ nleft -= nwritten;
+ buf = (const char*)buf + nwritten;
+ }
+
+ return 0;
+}
+
+
+static int
+check_already_spawned (runner_t runner, const char *funcname)
+{
+ if (runner->spawned)
+ {
+ log_error ("BUG: runner already spawned - ignoring call to %s\n",
+ funcname);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/* Return the number of active threads. */
+unsigned int
+runner_get_threads (void)
+{
+ unsigned int n = 0;
+ runner_t r;
+
+ for (r = running_threads; r; r = r->next_running)
+ n++;
+ return n;
+}
+
+
+/* The public release function. */
+void
+runner_release (runner_t runner)
+{
+ gpg_error_t err;
+
+ if (!runner)
+ return;
+
+ if (!--runner->refcount)
+ return;
+
+ err = mountinfo_del_mount (NULL, NULL, runner->identifier);
+ if (err)
+ log_error ("failed to remove mount with rid %u from mtab: %s\n",
+ runner->identifier, gpg_strerror (err));
+
+ es_fclose (runner->status_fp);
+ if (runner->in_fd != -1)
+ close (runner->in_fd);
+ if (runner->out_fd != -1)
+ close (runner->out_fd);
+
+ /* Fixme: close the process. */
+
+ /* Tell the engine to release its data. */
+ if (runner->handler_cleanup)
+ runner->handler_cleanup (runner->handler_data);
+
+ if (runner->pid != (pid_t)(-1))
+ {
+ /* The process has not been cleaned up - do it now. */
+ gnupg_kill_process (runner->pid);
+ /* (Actually we should use the program name and not the
+ arbitrary NAME of the runner object. However it does not
+ matter because that information is only used for
+ diagnostics.) */
+ gnupg_wait_process (runner->name, runner->pid, 1, NULL);
+ gnupg_release_process (runner->pid);
+ }
+
+ xfree (runner->name);
+ xfree (runner);
+}
+
+
+/* Create a new runner context. On success a new runner object is
+ stored at R_RUNNER. On failure NULL is stored at this address and
+ an error code returned. */
+gpg_error_t
+runner_new (runner_t *r_runner, const char *name)
+{
+ static unsigned int namecounter; /* Global name counter. */
+ char *namebuffer;
+ runner_t runner, r;
+
+ *r_runner = NULL;
+
+ runner = xtrycalloc (1, sizeof *runner);
+ if (!runner)
+ return gpg_error_from_syserror ();
+
+ /* Bump up the namecounter. In case we ever had an overflow we
+ check that this number is currently not in use. The algorithm is
+ a bit lame but should be sufficient because such an wrap is not
+ very likely: Assuming that we do a mount 10 times a second, then
+ we would overwrap on a 32 bit system after 13 years. */
+ do
+ {
+ namecounter++;
+ for (r = running_threads; r; r = r->next_running)
+ if (r->identifier == namecounter)
+ break;
+ }
+ while (r);
+
+ runner->identifier = namecounter;
+ runner->name = namebuffer = xtryasprintf ("%s-%d", name, namecounter);
+ if (!runner->name)
+ {
+ xfree (runner);
+ return gpg_error_from_syserror ();
+ }
+ runner->refcount = 1;
+ runner->pid = (pid_t)(-1);
+ runner->in_fd = -1;
+ runner->out_fd = -1;
+
+ *r_runner = runner;
+ return 0;
+}
+
+
+/* Return the identifier of RUNNER. */
+unsigned int
+runner_get_rid (runner_t runner)
+{
+ return runner->identifier;
+}
+
+
+/* Find a runner by its rid. Returns the runner object. The caller
+ must release the runner object. */
+runner_t
+runner_find_by_rid (unsigned int rid)
+{
+ runner_t r;
+
+ for (r = running_threads; r; r = r->next_running)
+ if (r->identifier == rid)
+ {
+ r->refcount++;
+ return r;
+ }
+ return NULL;
+}
+
+
+/* A runner usually maintains two file descriptors to control the
+ backend engine. This function is used to set these file
+ descriptors. The function takes ownership of these file
+ descriptors. IN_FD will be used to read from engine and OUT_FD to
+ send data to the engine. */
+void
+runner_set_fds (runner_t runner, int in_fd, int out_fd)
+{
+ if (check_already_spawned (runner, "runner_set_fds"))
+ return;
+
+ if (runner->in_fd != -1)
+ close (runner->in_fd);
+ if (runner->out_fd != -1)
+ close (runner->out_fd);
+ runner->in_fd = in_fd;
+ runner->out_fd = out_fd;
+}
+
+
+/* Set the PID of the backend engine. After this call the engine is
+ owned by the runner object. */
+void
+runner_set_pid (runner_t runner, pid_t pid)
+{
+ if (check_already_spawned (runner, "runner_set_fds"))
+ return;
+
+ runner->pid = pid;
+}
+
+
+/* Register the engine handler fucntions HANDLER and HANDLER_CLEANUP
+ and its private HANDLER_DATA with RUNNER. */
+void
+runner_set_handler (runner_t runner,
+ engine_handler_fnc_t handler,
+ engine_handler_cleanup_fnc_t handler_cleanup,
+ void *handler_data)
+{
+ if (check_already_spawned (runner, "runner_set_handler"))
+ return;
+
+ runner->handler = handler;
+ runner->handler_cleanup = handler_cleanup;
+ runner->handler_data = handler_data;
+}
+
+
+/* The thread spawned by runner_spawn. */
+static void *
+runner_thread (void *arg)
+{
+ runner_t runner = arg;
+ gpg_error_t err = 0;
+
+ log_debug ("starting runner thread\n");
+ /* If a status_fp is available, the thread's main task is to read
+ from that stream and invoke the backend's handler function. This
+ is done on a line by line base and the line length is limited to
+ a reasonable value (about 1000 characters). Other work will
+ continue either due to an EOF of the stream or by demand of the
+ engine. */
+ if (runner->status_fp)
+ {
+ int c, cont_line;
+ unsigned int pos;
+ char buffer[1024];
+ estream_t fp = runner->status_fp;
+
+ pos = 0;
+ cont_line = 0;
+ while (!err && !runner->cancel_flag && (c=es_getc (fp)) != EOF)
+ {
+ buffer[pos++] = c;
+ if (pos >= sizeof buffer - 5 || c == '\n')
+ {
+ buffer[pos - (c == '\n')] = 0;
+ if (opt.verbose)
+ log_info ("%s%s: %s\n",
+ runner->name, cont_line? "(cont)":"", buffer);
+ /* We handle only complete lines and ignore any stuff we
+ possibly had to truncate. That is - at least for the
+ encfs engine - not an issue because our changes to
+ the tool make sure that only relatively short prompt
+ lines are of interest. */
+ if (!cont_line && runner->handler)
+ err = runner->handler (runner->handler_data,
+ runner, buffer);
+ pos = 0;
+ cont_line = (c != '\n');
+ }
+ }
+ if (!err && runner->cancel_flag)
+ log_debug ("runner thread noticed cancel flag\n");
+ else
+ log_debug ("runner thread saw EOF\n");
+ if (pos)
+ {
+ buffer[pos] = 0;
+ if (opt.verbose)
+ log_info ("%s%s: %s\n",
+ runner->name, cont_line? "(cont)":"", buffer);
+ if (!cont_line && !err && runner->handler)
+ err = runner->handler (runner->handler_data,
+ runner, buffer);
+ }
+ if (!err && es_ferror (fp))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading from %s: %s\n",
+ runner->name, gpg_strerror (err));
+ }
+
+ runner->status_fp = NULL;
+ es_fclose (fp);
+ log_debug ("runner thread closed status fp\n");
+ }
+
+ /* Now wait for the process to finish. */
+ if (!err && runner->pid != (pid_t)(-1))
+ {
+ int exitcode;
+
+ log_debug ("runner thread waiting ...\n");
+ err = gnupg_wait_process (runner->name, runner->pid, 1, &exitcode);
+ gnupg_release_process (runner->pid);
+ runner->pid = (pid_t)(-1);
+ if (err)
+ log_error ("running '%s' failed (exitcode=%d): %s\n",
+ runner->name, exitcode, gpg_strerror (err));
+ log_debug ("runner thread waiting finished\n");
+ }
+
+ /* Get rid of the runner object (note: it is refcounted). */
+ log_debug ("runner thread releasing runner ...\n");
+ {
+ runner_t r, rprev;
+
+ for (r = running_threads, rprev = NULL; r; rprev = r, r = r->next_running)
+ if (r == runner)
+ {
+ if (!rprev)
+ running_threads = r->next_running;
+ else
+ rprev->next_running = r->next_running;
+ r->next_running = NULL;
+ break;
+ }
+ }
+ runner_release (runner);
+ log_debug ("runner thread runner released\n");
+
+ return NULL;
+}
+
+
+/* Spawn a new thread to let RUNNER work as a coprocess. */
+gpg_error_t
+runner_spawn (runner_t runner)
+{
+ gpg_error_t err;
+ npth_attr_t tattr;
+ npth_t thread;
+ int ret;
+
+ if (check_already_spawned (runner, "runner_spawn"))
+ return gpg_error (GPG_ERR_BUG);
+
+ /* In case we have an input fd, open it as an estream so that the
+ Pth scheduling will work. The stdio functions don't work with
+ Pth because they don't call the pth counterparts of read and
+ write unless linker tricks are used. */
+ if (runner->in_fd != -1)
+ {
+ estream_t fp;
+
+ fp = es_fdopen (runner->in_fd, "r");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("can't fdopen pipe for reading: %s\n", gpg_strerror (err));
+ return err;
+ }
+ runner->status_fp = fp;
+ runner->in_fd = -1; /* Now owned by status_fp. */
+ }
+
+ npth_attr_init (&tattr);
+ npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+
+ ret = npth_create (&thread, &tattr, runner_thread, runner);
+ if (ret)
+ {
+ err = gpg_error_from_errno (ret);
+ log_error ("error spawning runner thread: %s\n", gpg_strerror (err));
+ return err;
+ }
+ npth_setname_np (thread, runner->name);
+
+ /* The scheduler has not yet kicked in, thus we can safely set the
+ spawned flag and the tid. */
+ runner->spawned = 1;
+ runner->thread = thread;
+ runner->next_running = running_threads;
+ running_threads = runner;
+
+ npth_attr_destroy (&tattr);
+
+ /* The runner thread is now runnable. */
+
+ return 0;
+}
+
+
+/* Cancel a running thread. */
+void
+runner_cancel (runner_t runner)
+{
+ /* Warning: runner_cancel_all has knowledge of this code. */
+ if (runner->spawned)
+ {
+ runner->canceled = 1; /* Mark that we canceled this one already. */
+ /* FIXME: This does only work if the thread emits status lines. We
+ need to change the thread to wait on an event. */
+ runner->cancel_flag = 1;
+ /* For now we use the brutal way and kill the process. */
+ gnupg_kill_process (runner->pid);
+ }
+}
+
+
+/* Cancel all runner threads. */
+void
+runner_cancel_all (void)
+{
+ runner_t r;
+
+ do
+ {
+ for (r = running_threads; r; r = r->next_running)
+ if (r->spawned && !r->canceled)
+ {
+ runner_cancel (r);
+ break;
+ }
+ }
+ while (r);
+}
+
+
+/* Send a line of data down to the engine. This line may not contain
+ a binary Nul or a LF character. This function is used by the
+ engine's handler. */
+gpg_error_t
+runner_send_line (runner_t runner, const void *data, size_t datalen)
+{
+ gpg_error_t err = 0;
+
+ if (!runner->spawned)
+ {
+ log_error ("BUG: runner for %s not spawned\n", runner->name);
+ err = gpg_error (GPG_ERR_INTERNAL);
+ }
+ else if (runner->out_fd == -1)
+ {
+ log_error ("no output file descriptor for runner %s\n", runner->name);
+ err = gpg_error (GPG_ERR_EBADF);
+ }
+ else if (data && datalen)
+ {
+ if (memchr (data, '\n', datalen))
+ {
+ log_error ("LF detected in response data\n");
+ err = gpg_error (GPG_ERR_BUG);
+ }
+ else if (memchr (data, 0, datalen))
+ {
+ log_error ("Nul detected in response data\n");
+ err = gpg_error (GPG_ERR_BUG);
+ }
+ else if (writen (runner->out_fd, data, datalen))
+ err = gpg_error_from_syserror ();
+ }
+
+ if (!err)
+ if (writen (runner->out_fd, "\n", 1))
+ err = gpg_error_from_syserror ();
+
+ return err;
+}
diff --git a/g13/runner.h b/g13/runner.h
new file mode 100644
index 0000000..36181ad
--- /dev/null
+++ b/g13/runner.h
@@ -0,0 +1,76 @@
+/* runner.h - Run and watch the backend engines
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_RUNNER_H
+#define G13_RUNNER_H
+
+/* The runner object. */
+struct runner_s;
+typedef struct runner_s *runner_t;
+
+/* Prototypes for the handler functions provided by the engine. */
+typedef gpg_error_t (*engine_handler_fnc_t) (void *opaque,
+ runner_t runner,
+ const char *statusline);
+typedef void (*engine_handler_cleanup_fnc_t) (void *opaque);
+
+
+/* Return the number of active threads. */
+unsigned int runner_get_threads (void);
+
+/* Create a new runner object. */
+gpg_error_t runner_new (runner_t *r_runner, const char *name);
+
+/* Free a runner object. */
+void runner_release (runner_t runner);
+
+/* Return the identifier of RUNNER. */
+unsigned int runner_get_rid (runner_t runner);
+
+/* Find a runner by its rid. */
+runner_t runner_find_by_rid (unsigned int rid);
+
+/* Functions to set properties of the runner. */
+void runner_set_fds (runner_t runner, int in_fd, int out_fd);
+
+void runner_set_pid (runner_t runner, pid_t pid);
+
+/* Register the handler functions with a runner. */
+void runner_set_handler (runner_t runner,
+ engine_handler_fnc_t handler,
+ engine_handler_cleanup_fnc_t handler_cleanup,
+ void *handler_data);
+
+/* Start the runner. */
+gpg_error_t runner_spawn (runner_t runner);
+
+/* Cancel a runner. */
+void runner_cancel (runner_t runner);
+
+/* Cancel all runner. */
+void runner_cancel_all (void);
+
+/* Send data back to the engine. This function is used by the
+ engine's handler. */
+gpg_error_t runner_send_line (runner_t runner,
+ const void *data, size_t datalen);
+
+
+
+#endif /*G13_RUNNER_H*/
diff --git a/g13/server.c b/g13/server.c
new file mode 100644
index 0000000..7802952
--- /dev/null
+++ b/g13/server.c
@@ -0,0 +1,783 @@
+/* server.c - The G13 Assuan server
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "g13.h"
+#include <assuan.h>
+#include "../common/i18n.h"
+#include "keyblob.h"
+#include "server.h"
+#include "create.h"
+#include "mount.h"
+#include "suspend.h"
+#include "../common/server-help.h"
+#include "../common/asshelp.h"
+#include "../common/call-gpg.h"
+
+
+/* The filepointer for status message used in non-server mode */
+static FILE *statusfp;
+
+/* Local data for this server module. A pointer to this is stored in
+ the CTRL object of each connection. */
+struct server_local_s
+{
+ /* The Assuan context we are working on. */
+ assuan_context_t assuan_ctx;
+
+ char *containername; /* Malloced active containername. */
+};
+
+
+
+
+/* Local prototypes. */
+static int command_has_option (const char *cmd, const char *cmdopt);
+
+
+
+
+/*
+ Helper functions.
+ */
+
+/* Set an error and a description. */
+#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
+
+
+/* Helper to print a message while leaving a command. */
+static gpg_error_t
+leave_cmd (assuan_context_t ctx, gpg_error_t err)
+{
+ if (err)
+ {
+ const char *name = assuan_get_command_name (ctx);
+ if (!name)
+ name = "?";
+ if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
+ log_error ("command '%s' failed: %s\n", name,
+ gpg_strerror (err));
+ else
+ log_error ("command '%s' failed: %s <%s>\n", name,
+ gpg_strerror (err), gpg_strsource (err));
+ }
+ return err;
+}
+
+
+
+
+/* The handler for Assuan OPTION commands. */
+static gpg_error_t
+option_handler (assuan_context_t ctx, const char *key, const char *value)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+
+ (void)ctrl;
+
+ if (!strcmp (key, "putenv"))
+ {
+ /* Change the session's environment to be used for the
+ Pinentry. Valid values are:
+ <NAME> Delete envvar NAME
+ <KEY>= Set envvar NAME to the empty string
+ <KEY>=<VALUE> Set envvar NAME to VALUE
+ */
+ err = session_env_putenv (opt.session_env, value);
+ }
+ else if (!strcmp (key, "display"))
+ {
+ err = session_env_setenv (opt.session_env, "DISPLAY", value);
+ }
+ else if (!strcmp (key, "ttyname"))
+ {
+ err = session_env_setenv (opt.session_env, "GPG_TTY", value);
+ }
+ else if (!strcmp (key, "ttytype"))
+ {
+ err = session_env_setenv (opt.session_env, "TERM", value);
+ }
+ else if (!strcmp (key, "lc-ctype"))
+ {
+ xfree (opt.lc_ctype);
+ opt.lc_ctype = xtrystrdup (value);
+ if (!opt.lc_ctype)
+ err = gpg_error_from_syserror ();
+ }
+ else if (!strcmp (key, "lc-messages"))
+ {
+ xfree (opt.lc_messages);
+ opt.lc_messages = xtrystrdup (value);
+ if (!opt.lc_messages)
+ err = gpg_error_from_syserror ();
+ }
+ else if (!strcmp (key, "xauthority"))
+ {
+ err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
+ }
+ else if (!strcmp (key, "pinentry-user-data"))
+ {
+ err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
+ }
+ else if (!strcmp (key, "allow-pinentry-notify"))
+ {
+ ; /* We always allow it. */
+ }
+ else
+ err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
+
+ return err;
+}
+
+
+/* The handler for an Assuan RESET command. */
+static gpg_error_t
+reset_notify (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ (void)line;
+
+ xfree (ctrl->server_local->containername);
+ ctrl->server_local->containername = NULL;
+
+ FREE_STRLIST (ctrl->recipients);
+
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ return 0;
+}
+
+
+static const char hlp_open[] =
+ "OPEN [<options>] <filename>\n"
+ "\n"
+ "Open the container FILENAME. FILENAME must be percent-plus\n"
+ "escaped. A quick check to see whether this is a suitable G13\n"
+ "container file is done. However no cryptographic check or any\n"
+ "other check is done. This command is used to define the target for\n"
+ "further commands. The filename is reset with the RESET command,\n"
+ "another OPEN or the CREATE command.";
+static gpg_error_t
+cmd_open (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ char *p, *pend;
+ size_t len;
+
+ /* In any case reset the active container. */
+ xfree (ctrl->server_local->containername);
+ ctrl->server_local->containername = NULL;
+
+ /* Parse the line. */
+ line = skip_options (line);
+ for (p=line; *p && !spacep (p); p++)
+ ;
+ pend = p;
+ while (spacep(p))
+ p++;
+ if (*p || pend == line)
+ {
+ err = gpg_error (GPG_ERR_ASS_SYNTAX);
+ goto leave;
+ }
+ *pend = 0;
+
+ /* Unescape the line and check for embedded Nul bytes. */
+ len = percent_plus_unescape_inplace (line, 0);
+ line[len] = 0;
+ if (!len || memchr (line, 0, len))
+ {
+ err = gpg_error (GPG_ERR_INV_NAME);
+ goto leave;
+ }
+
+ /* Do a basic check. */
+ err = g13_is_container (ctrl, line);
+ if (err)
+ goto leave;
+
+ /* Store the filename. */
+ ctrl->server_local->containername = xtrystrdup (line);
+ if (!ctrl->server_local->containername)
+ err = gpg_error_from_syserror ();
+
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_mount[] =
+ "MOUNT [options] [<mountpoint>]\n"
+ "\n"
+ "Mount the currently open file onto MOUNTPOINT. If MOUNTPOINT is not\n"
+ "given the system picks an unused mountpoint. MOUNTPOINT must\n"
+ "be percent-plus escaped to allow for arbitrary names.";
+static gpg_error_t
+cmd_mount (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ char *p, *pend;
+ size_t len;
+
+ line = skip_options (line);
+ for (p=line; *p && !spacep (p); p++)
+ ;
+ pend = p;
+ while (spacep(p))
+ p++;
+ if (*p)
+ {
+ err = gpg_error (GPG_ERR_ASS_SYNTAX);
+ goto leave;
+ }
+ *pend = 0;
+
+ /* Unescape the line and check for embedded Nul bytes. */
+ len = percent_plus_unescape_inplace (line, 0);
+ line[len] = 0;
+ if (memchr (line, 0, len))
+ {
+ err = gpg_error (GPG_ERR_INV_NAME);
+ goto leave;
+ }
+
+ if (!ctrl->server_local->containername)
+ {
+ err = gpg_error (GPG_ERR_MISSING_ACTION);
+ goto leave;
+ }
+
+ /* Perform the mount. */
+ err = g13_mount_container (ctrl, ctrl->server_local->containername,
+ *line? line : NULL);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_umount[] =
+ "UMOUNT [options] [<mountpoint>]\n"
+ "\n"
+ "Unmount the currently open file or the one opened at MOUNTPOINT.\n"
+ "MOUNTPOINT must be percent-plus escaped. On success the mountpoint\n"
+ "is returned via a \"MOUNTPOINT\" status line.";
+static gpg_error_t
+cmd_umount (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ char *p, *pend;
+ size_t len;
+
+ line = skip_options (line);
+ for (p=line; *p && !spacep (p); p++)
+ ;
+ pend = p;
+ while (spacep(p))
+ p++;
+ if (*p)
+ {
+ err = gpg_error (GPG_ERR_ASS_SYNTAX);
+ goto leave;
+ }
+ *pend = 0;
+
+ /* Unescape the line and check for embedded Nul bytes. */
+ len = percent_plus_unescape_inplace (line, 0);
+ line[len] = 0;
+ if (memchr (line, 0, len))
+ {
+ err = gpg_error (GPG_ERR_INV_NAME);
+ goto leave;
+ }
+
+ /* Perform the unmount. */
+ err = g13_umount_container (ctrl, ctrl->server_local->containername,
+ *line? line : NULL);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_suspend[] =
+ "SUSPEND\n"
+ "\n"
+ "Suspend the currently set device.";
+static gpg_error_t
+cmd_suspend (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+
+ line = skip_options (line);
+ if (*line)
+ {
+ err = gpg_error (GPG_ERR_ASS_SYNTAX);
+ goto leave;
+ }
+
+ /* Perform the suspend operation. */
+ err = g13_suspend_container (ctrl, ctrl->server_local->containername);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_resume[] =
+ "RESUME\n"
+ "\n"
+ "Resume the currently set device.";
+static gpg_error_t
+cmd_resume (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+
+ line = skip_options (line);
+ if (*line)
+ {
+ err = gpg_error (GPG_ERR_ASS_SYNTAX);
+ goto leave;
+ }
+
+ /* Perform the suspend operation. */
+ err = g13_resume_container (ctrl, ctrl->server_local->containername);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_recipient[] =
+ "RECIPIENT <userID>\n"
+ "\n"
+ "Add USERID to the list of recipients to be used for the next CREATE\n"
+ "command. All recipient commands are cumulative until a RESET or an\n"
+ "successful create command.";
+static gpg_error_t
+cmd_recipient (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+
+ line = skip_options (line);
+
+ if (!add_to_strlist_try (&ctrl->recipients, line))
+ err = gpg_error_from_syserror ();
+
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_signer[] =
+ "SIGNER <userID>\n"
+ "\n"
+ "Not yet implemented.";
+static gpg_error_t
+cmd_signer (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+
+ (void)ctrl;
+ (void)line;
+
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_create[] =
+ "CREATE [options] <filename>\n"
+ "\n"
+ "Create a new container. On success the OPEN command is \n"
+ "implictly done for the new container.";
+static gpg_error_t
+cmd_create (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ char *p, *pend;
+ size_t len;
+
+ /* First we close the active container. */
+ xfree (ctrl->server_local->containername);
+ ctrl->server_local->containername = NULL;
+
+ /* Parse the line. */
+ line = skip_options (line);
+ for (p=line; *p && !spacep (p); p++)
+ ;
+ pend = p;
+ while (spacep(p))
+ p++;
+ if (*p || pend == line)
+ {
+ err = gpg_error (GPG_ERR_ASS_SYNTAX);
+ goto leave;
+ }
+ *pend = 0;
+
+ /* Unescape the line and check for embedded Nul bytes. */
+ len = percent_plus_unescape_inplace (line, 0);
+ line[len] = 0;
+ if (!len || memchr (line, 0, len))
+ {
+ err = gpg_error (GPG_ERR_INV_NAME);
+ goto leave;
+ }
+
+ /* Create container. */
+ err = g13_create_container (ctrl, line);
+
+ if (!err)
+ {
+ FREE_STRLIST (ctrl->recipients);
+
+ /* Store the filename. */
+ ctrl->server_local->containername = xtrystrdup (line);
+ if (!ctrl->server_local->containername)
+ err = gpg_error_from_syserror ();
+
+ }
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_getinfo[] =
+ "GETINFO <what>\n"
+ "\n"
+ "Multipurpose function to return a variety of information.\n"
+ "Supported values for WHAT are:\n"
+ "\n"
+ " version - Return the version of the program.\n"
+ " pid - Return the process id of the server.\n"
+ " cmd_has_option CMD OPT\n"
+ " - Return OK if the command CMD implements the option OPT.";
+static gpg_error_t
+cmd_getinfo (assuan_context_t ctx, char *line)
+{
+ gpg_error_t err = 0;
+
+ if (!strcmp (line, "version"))
+ {
+ const char *s = PACKAGE_VERSION;
+ err = assuan_send_data (ctx, s, strlen (s));
+ }
+ else if (!strcmp (line, "pid"))
+ {
+ char numbuf[50];
+
+ snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
+ err = assuan_send_data (ctx, numbuf, strlen (numbuf));
+ }
+ else if (!strncmp (line, "cmd_has_option", 14)
+ && (line[14] == ' ' || line[14] == '\t' || !line[14]))
+ {
+ char *cmd, *cmdopt;
+ line += 14;
+ while (*line == ' ' || *line == '\t')
+ line++;
+ if (!*line)
+ err = gpg_error (GPG_ERR_MISSING_VALUE);
+ else
+ {
+ cmd = line;
+ while (*line && (*line != ' ' && *line != '\t'))
+ line++;
+ if (!*line)
+ err = gpg_error (GPG_ERR_MISSING_VALUE);
+ else
+ {
+ *line++ = 0;
+ while (*line == ' ' || *line == '\t')
+ line++;
+ if (!*line)
+ err = gpg_error (GPG_ERR_MISSING_VALUE);
+ else
+ {
+ cmdopt = line;
+ if (!command_has_option (cmd, cmdopt))
+ err = gpg_error (GPG_ERR_FALSE);
+ }
+ }
+ }
+ }
+ else
+ err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
+
+ return leave_cmd (ctx, err);
+}
+
+
+
+/* Return true if the command CMD implements the option CMDOPT. */
+static int
+command_has_option (const char *cmd, const char *cmdopt)
+{
+ (void)cmd;
+ (void)cmdopt;
+
+ return 0;
+}
+
+
+/* Tell the Assuan library about our commands. */
+static int
+register_commands (assuan_context_t ctx)
+{
+ static struct {
+ const char *name;
+ assuan_handler_t handler;
+ const char * const help;
+ } table[] = {
+ { "OPEN", cmd_open, hlp_open },
+ { "MOUNT", cmd_mount, hlp_mount},
+ { "UMOUNT", cmd_umount, hlp_umount },
+ { "SUSPEND", cmd_suspend, hlp_suspend },
+ { "RESUME", cmd_resume, hlp_resume },
+ { "RECIPIENT", cmd_recipient, hlp_recipient },
+ { "SIGNER", cmd_signer, hlp_signer },
+ { "CREATE", cmd_create, hlp_create },
+ { "INPUT", NULL },
+ { "OUTPUT", NULL },
+ { "GETINFO", cmd_getinfo,hlp_getinfo },
+ { NULL }
+ };
+ gpg_error_t err;
+ int i;
+
+ for (i=0; table[i].name; i++)
+ {
+ err = assuan_register_command (ctx, table[i].name, table[i].handler,
+ table[i].help);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* Startup the server. DEFAULT_RECPLIST is the list of recipients as
+ set from the command line or config file. We only require those
+ marked as encrypt-to. */
+gpg_error_t
+g13_server (ctrl_t ctrl)
+{
+ gpg_error_t err;
+ assuan_fd_t filedes[2];
+ assuan_context_t ctx = NULL;
+ static const char hello[] = ("GNU Privacy Guard's G13 server "
+ PACKAGE_VERSION " ready");
+
+ /* We use a pipe based server so that we can work from scripts.
+ assuan_init_pipe_server will automagically detect when we are
+ called with a socketpair and ignore FIELDES in this case. */
+ filedes[0] = assuan_fdopen (0);
+ filedes[1] = assuan_fdopen (1);
+ err = assuan_new (&ctx);
+ if (err)
+ {
+ log_error ("failed to allocate an Assuan context: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ err = assuan_init_pipe_server (ctx, filedes);
+ if (err)
+ {
+ log_error ("failed to initialize the server: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = register_commands (ctx);
+ if (err)
+ {
+ log_error ("failed to the register commands with Assuan: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ assuan_set_pointer (ctx, ctrl);
+
+ if (opt.verbose || opt.debug)
+ {
+ char *tmp;
+
+ tmp = xtryasprintf ("Home: %s\n"
+ "Config: %s\n"
+ "%s",
+ gnupg_homedir (),
+ opt.config_filename,
+ hello);
+ if (tmp)
+ {
+ assuan_set_hello_line (ctx, tmp);
+ xfree (tmp);
+ }
+ }
+ else
+ assuan_set_hello_line (ctx, hello);
+
+ assuan_register_reset_notify (ctx, reset_notify);
+ assuan_register_option_handler (ctx, option_handler);
+
+ ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
+ if (!ctrl->server_local)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ ctrl->server_local->assuan_ctx = ctx;
+
+ while ( !(err = assuan_accept (ctx)) )
+ {
+ err = assuan_process (ctx);
+ if (err)
+ log_info ("Assuan processing failed: %s\n", gpg_strerror (err));
+ }
+ if (err == -1)
+ err = 0;
+ else
+ log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
+
+ leave:
+ reset_notify (ctx, NULL); /* Release all items hold by SERVER_LOCAL. */
+ if (ctrl->server_local)
+ {
+ xfree (ctrl->server_local);
+ ctrl->server_local = NULL;
+ }
+
+ assuan_release (ctx);
+ return err;
+}
+
+
+/* Send a status line with status ID NO. The arguments are a list of
+ strings terminated by a NULL argument. */
+gpg_error_t
+g13_status (ctrl_t ctrl, int no, ...)
+{
+ gpg_error_t err = 0;
+ va_list arg_ptr;
+ const char *text;
+
+ va_start (arg_ptr, no);
+
+ if (ctrl->no_server && ctrl->status_fd == -1)
+ ; /* No status wanted. */
+ else if (ctrl->no_server)
+ {
+ if (!statusfp)
+ {
+ if (ctrl->status_fd == 1)
+ statusfp = stdout;
+ else if (ctrl->status_fd == 2)
+ statusfp = stderr;
+ else
+ statusfp = fdopen (ctrl->status_fd, "w");
+
+ if (!statusfp)
+ {
+ log_fatal ("can't open fd %d for status output: %s\n",
+ ctrl->status_fd, strerror(errno));
+ }
+ }
+
+ fputs ("[GNUPG:] ", statusfp);
+ fputs (get_status_string (no), statusfp);
+
+ while ( (text = va_arg (arg_ptr, const char*) ))
+ {
+ putc ( ' ', statusfp );
+ for (; *text; text++)
+ {
+ if (*text == '\n')
+ fputs ( "\\n", statusfp );
+ else if (*text == '\r')
+ fputs ( "\\r", statusfp );
+ else
+ putc ( *(const byte *)text, statusfp );
+ }
+ }
+ putc ('\n', statusfp);
+ fflush (statusfp);
+ }
+ else
+ {
+ err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx,
+ get_status_string (no), arg_ptr);
+ }
+
+ va_end (arg_ptr);
+ return err;
+}
+
+
+/* Helper to notify the client about Pinentry events. Returns an gpg
+ error code. */
+gpg_error_t
+g13_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
+{
+ if (!ctrl || !ctrl->server_local)
+ return 0;
+ return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
+}
+
+
+/*
+ * Decrypt the keyblob (ENCKEYBLOB,ENCKEYBLOBLEN) and store the result
+ * at (R_KEYBLOB, R_KEYBLOBLEN). Returns 0 on success or an error
+ * code. On error R_KEYBLOB is set to NULL.
+ *
+ * This actually does not belong here but for that simple wrapper it
+ * does not make sense to add another source file. Note that we do
+ * not want to have this in keyblob.c, because that code is also used
+ * by the syshelp.
+ */
+gpg_error_t
+g13_keyblob_decrypt (ctrl_t ctrl, const void *enckeyblob, size_t enckeybloblen,
+ void **r_keyblob, size_t *r_keybloblen)
+{
+ gpg_error_t err;
+
+ /* FIXME: For now we only implement OpenPGP. */
+ err = gpg_decrypt_blob (ctrl, opt.gpg_program, opt.gpg_arguments,
+ enckeyblob, enckeybloblen,
+ r_keyblob, r_keybloblen);
+
+ return err;
+}
diff --git a/g13/server.h b/g13/server.h
new file mode 100644
index 0000000..6338f40
--- /dev/null
+++ b/g13/server.h
@@ -0,0 +1,32 @@
+/* server.h - The G13 Assuan server
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_SERVER_H
+#define G13_SERVER_H
+
+
+gpg_error_t g13_server (ctrl_t ctrl);
+
+gpg_error_t g13_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line);
+
+gpg_error_t g13_keyblob_decrypt (ctrl_t ctrl,
+ const void *enckeyblob, size_t enckeybloblen,
+ void **r_keyblob, size_t *r_keybloblen);
+
+#endif /*G13_SERVER_H*/
diff --git a/g13/sh-blockdev.c b/g13/sh-blockdev.c
new file mode 100644
index 0000000..a477a67
--- /dev/null
+++ b/g13/sh-blockdev.c
@@ -0,0 +1,152 @@
+/* sh-blockdev.c - Block device functions for g13-syshelp
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "g13-syshelp.h"
+#include <assuan.h>
+#include "../common/i18n.h"
+#include "../common/exectool.h"
+#include "keyblob.h"
+
+#ifndef HAVE_STRTOULL
+# error building this tool requires strtoull(3)
+#endif
+#ifndef ULLONG_MAX
+# error ULLONG_MAX missing
+#endif
+
+
+/* Return the size measured in the number of 512 byte sectors for the
+ block device NAME. */
+gpg_error_t
+sh_blockdev_getsz (const char *name, unsigned long long *r_nblocks)
+{
+ gpg_error_t err;
+ const char *argv[3];
+ char *result;
+
+ *r_nblocks = 0;
+ argv[0] = "--getsz";
+ argv[1] = name;
+ argv[2] = NULL;
+ err = gnupg_exec_tool ("/sbin/blockdev", argv, NULL, &result, NULL);
+ if (!err)
+ {
+ gpg_err_set_errno (0);
+ *r_nblocks = strtoull (result, NULL, 10);
+ if (*r_nblocks == ULLONG_MAX && errno)
+ {
+ err = gpg_error_from_syserror ();
+ *r_nblocks = 0;
+ }
+ xfree (result);
+ }
+ return err;
+}
+
+
+/* Return 0 if the device NAME looks like an empty partition. */
+gpg_error_t
+sh_is_empty_partition (const char *name)
+{
+ gpg_error_t err;
+ const char *argv[6];
+ char *buffer;
+ estream_t fp;
+ char *p;
+ size_t nread;
+
+ argv[0] = "-o";
+ argv[1] = "value";
+ argv[2] = "-s";
+ argv[3] = "UUID";
+ argv[4] = name;
+ argv[5] = NULL;
+ err = gnupg_exec_tool ("/sbin/blkid", argv, NULL, &buffer, NULL);
+ if (err)
+ return gpg_error (GPG_ERR_FALSE);
+ if (*buffer)
+ {
+ /* There seems to be an UUID - thus we have a file system. */
+ xfree (buffer);
+ return gpg_error (GPG_ERR_FALSE);
+ }
+ xfree (buffer);
+
+ argv[0] = "-o";
+ argv[1] = "value";
+ argv[2] = "-s";
+ argv[3] = "PARTUUID";
+ argv[4] = name;
+ argv[5] = NULL;
+ err = gnupg_exec_tool ("/sbin/blkid", argv, NULL, &buffer, NULL);
+ if (err)
+ return gpg_error (GPG_ERR_FALSE);
+ if (!*buffer)
+ {
+ /* If there is no PARTUUID we assume that name has already a
+ mapped partition. */
+ xfree (buffer);
+ return gpg_error (GPG_ERR_FALSE);
+ }
+ xfree (buffer);
+
+ /* As a safeguard we require that the first 32k of a partition are
+ all zero before we assume the partition is empty. */
+ buffer = xtrymalloc (32 * 1024);
+ if (!buffer)
+ return gpg_error_from_syserror ();
+ fp = es_fopen (name, "rb,samethread");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error opening '%s': %s\n", name, gpg_strerror (err));
+ xfree (buffer);
+ return gpg_error (GPG_ERR_FALSE);
+ }
+ if (es_read (fp, buffer, 32 * 1024, &nread))
+ err = gpg_error_from_syserror ();
+ else if (nread != 32 *1024)
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ else
+ err = 0;
+ es_fclose (fp);
+ if (err)
+ {
+ log_error ("error reading the first 32 KiB from '%s': %s\n",
+ name, gpg_strerror (err));
+ xfree (buffer);
+ return err;
+ }
+ for (p=buffer; nread && !*p; nread--, p++)
+ ;
+ xfree (buffer);
+ if (nread)
+ return gpg_error (GPG_ERR_FALSE); /* No all zeroes. */
+
+ return 0;
+}
diff --git a/g13/sh-cmd.c b/g13/sh-cmd.c
new file mode 100644
index 0000000..791e3b7
--- /dev/null
+++ b/g13/sh-cmd.c
@@ -0,0 +1,917 @@
+/* sh-cmd.c - The Assuan server for g13-syshelp
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "g13-syshelp.h"
+#include <assuan.h>
+#include "../common/i18n.h"
+#include "../common/asshelp.h"
+#include "keyblob.h"
+
+
+/* Local data for this server module. A pointer to this is stored in
+ the CTRL object of each connection. */
+struct server_local_s
+{
+ /* The Assuan context we are working on. */
+ assuan_context_t assuan_ctx;
+
+ /* The malloced name of the device. */
+ char *devicename;
+
+ /* A stream open for read of the device set by the DEVICE command or
+ NULL if no DEVICE command has been used. */
+ estream_t devicefp;
+};
+
+
+
+
+/* Local prototypes. */
+
+
+
+
+/*
+ Helper functions.
+ */
+
+/* Set an error and a description. */
+#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
+#define set_error_fail_cmd() set_error (GPG_ERR_NOT_INITIALIZED, \
+ "not called via userv or unknown user")
+
+
+/* Skip over options. Blanks after the options are also removed. */
+static char *
+skip_options (const char *line)
+{
+ while (spacep (line))
+ line++;
+ while ( *line == '-' && line[1] == '-' )
+ {
+ while (*line && !spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ }
+ return (char*)line;
+}
+
+
+/* Check whether the option NAME appears in LINE. */
+/* static int */
+/* has_option (const char *line, const char *name) */
+/* { */
+/* const char *s; */
+/* int n = strlen (name); */
+
+/* s = strstr (line, name); */
+/* if (s && s >= skip_options (line)) */
+/* return 0; */
+/* return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); */
+/* } */
+
+
+/* Helper to print a message while leaving a command. */
+static gpg_error_t
+leave_cmd (assuan_context_t ctx, gpg_error_t err)
+{
+ if (err)
+ {
+ const char *name = assuan_get_command_name (ctx);
+ if (!name)
+ name = "?";
+ if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
+ log_error ("command '%s' failed: %s\n", name,
+ gpg_strerror (err));
+ else
+ log_error ("command '%s' failed: %s <%s>\n", name,
+ gpg_strerror (err), gpg_strsource (err));
+ }
+ return err;
+}
+
+
+
+
+/* The handler for Assuan OPTION commands. */
+static gpg_error_t
+option_handler (assuan_context_t ctx, const char *key, const char *value)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+
+ (void)ctrl;
+ (void)key;
+ (void)value;
+
+ if (ctrl->fail_all_cmds)
+ err = set_error_fail_cmd ();
+ else
+ err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
+
+ return err;
+}
+
+
+/* The handler for an Assuan RESET command. */
+static gpg_error_t
+reset_notify (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ (void)line;
+
+ xfree (ctrl->server_local->devicename);
+ ctrl->server_local->devicename = NULL;
+ es_fclose (ctrl->server_local->devicefp);
+ ctrl->server_local->devicefp = NULL;
+ ctrl->devti = NULL;
+
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ return 0;
+}
+
+
+static const char hlp_finddevice[] =
+ "FINDDEVICE <name>\n"
+ "\n"
+ "Find the device matching NAME. NAME be any identifier from\n"
+ "g13tab permissible for the user. The corresponding block\n"
+ "device is returned using a status line.";
+static gpg_error_t
+cmd_finddevice (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ tab_item_t ti;
+ const char *s;
+ const char *name;
+
+ name = skip_options (line);
+
+ /* Are we allowed to use the given device? We check several names:
+ * 1. The full block device
+ * 2. The label
+ * 3. The final part of the block device if NAME does not have a slash.
+ * 4. The mountpoint
+ */
+ for (ti=ctrl->client.tab; ti; ti = ti->next)
+ if (!strcmp (name, ti->blockdev))
+ break;
+ if (!ti)
+ {
+ for (ti=ctrl->client.tab; ti; ti = ti->next)
+ if (ti->label && !strcmp (name, ti->label))
+ break;
+ }
+ if (!ti && !strchr (name, '/'))
+ {
+ for (ti=ctrl->client.tab; ti; ti = ti->next)
+ {
+ s = strrchr (ti->blockdev, '/');
+ if (s && s[1] && !strcmp (name, s+1))
+ break;
+ }
+ }
+ if (!ti)
+ {
+ for (ti=ctrl->client.tab; ti; ti = ti->next)
+ if (ti->mountpoint && !strcmp (name, ti->mountpoint))
+ break;
+ }
+
+ if (!ti)
+ {
+ err = set_error (GPG_ERR_NOT_FOUND, "device not configured for user");
+ goto leave;
+ }
+
+ /* Check whether we have permissions to open the device. */
+ {
+ estream_t fp = es_fopen (ti->blockdev, "rb");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error opening '%s': %s\n",
+ ti->blockdev, gpg_strerror (err));
+ goto leave;
+ }
+ es_fclose (fp);
+ }
+
+ err = g13_status (ctrl, STATUS_BLOCKDEV, ti->blockdev, NULL);
+ if (err)
+ return err;
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_device[] =
+ "DEVICE <name>\n"
+ "\n"
+ "Set the device used by further commands.\n"
+ "A device name or a PARTUUID string may be used.\n"
+ "Access to that device (by the g13 system) is locked\n"
+ "until a new DEVICE command or end of this process\n";
+static gpg_error_t
+cmd_device (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ tab_item_t ti;
+ estream_t fp = NULL;
+
+ line = skip_options (line);
+
+/* # warning hardwired to /dev/sdb1 ! */
+/* if (strcmp (line, "/dev/sdb1")) */
+/* { */
+/* err = gpg_error (GPG_ERR_ENOENT); */
+/* goto leave; */
+/* } */
+
+ /* Always close an open device stream of this session. */
+ xfree (ctrl->server_local->devicename);
+ ctrl->server_local->devicename = NULL;
+ es_fclose (ctrl->server_local->devicefp);
+ ctrl->server_local->devicefp = NULL;
+
+ /* Are we allowed to use the given device? */
+ for (ti=ctrl->client.tab; ti; ti = ti->next)
+ if (!strcmp (line, ti->blockdev))
+ break;
+ if (!ti)
+ {
+ err = set_error (GPG_ERR_EACCES, "device not configured for user");
+ goto leave;
+ }
+
+ ctrl->server_local->devicename = xtrystrdup (line);
+ if (!ctrl->server_local->devicename)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+
+ /* Check whether we have permissions to open the device and keep an
+ FD open. */
+ fp = es_fopen (ctrl->server_local->devicename, "rb");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error opening '%s': %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err));
+ goto leave;
+ }
+
+ es_fclose (ctrl->server_local->devicefp);
+ ctrl->server_local->devicefp = fp;
+ fp = NULL;
+ ctrl->devti = ti;
+
+ /* Fixme: Take some kind of lock. */
+
+ leave:
+ es_fclose (fp);
+ if (err)
+ {
+ xfree (ctrl->server_local->devicename);
+ ctrl->server_local->devicename = NULL;
+ ctrl->devti = NULL;
+ }
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_create[] =
+ "CREATE <type>\n"
+ "\n"
+ "Create a new encrypted partition on the current device.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_create (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ estream_t fp = NULL;
+
+ line = skip_options (line);
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (err)
+ {
+ if (gpg_err_code (err) == GPG_ERR_FALSE)
+ err = gpg_error (GPG_ERR_CONFLICT);
+ err = assuan_set_error (ctx, err, "Partition is not empty");
+ goto leave;
+ }
+
+ /* We need a writeable stream to create the container. */
+ fp = es_fopen (ctrl->server_local->devicename, "r+b");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error opening '%s': %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err));
+ goto leave;
+ }
+ if (es_setvbuf (fp, NULL, _IONBF, 0))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error setting '%s' to _IONBF: %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err));
+ goto leave;
+ }
+
+ err = sh_dmcrypt_create_container (ctrl,
+ ctrl->server_local->devicename,
+ fp);
+ if (es_fclose (fp))
+ {
+ gpg_error_t err2 = gpg_error_from_syserror ();
+ log_error ("error closing '%s': %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err2));
+ if (!err)
+ err = err2;
+ }
+ fp = NULL;
+
+ leave:
+ es_fclose (fp);
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_getkeyblob[] =
+ "GETKEYBLOB\n"
+ "\n"
+ "Return the encrypted keyblob of the current device.";
+static gpg_error_t
+cmd_getkeyblob (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ void *enckeyblob = NULL;
+ size_t enckeybloblen;
+
+ line = skip_options (line);
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (!err)
+ {
+ err = gpg_error (GPG_ERR_ENODEV);
+ assuan_set_error (ctx, err, "Partition is empty");
+ goto leave;
+ }
+ err = 0;
+
+ err = g13_keyblob_read (ctrl->server_local->devicename,
+ &enckeyblob, &enckeybloblen);
+ if (err)
+ goto leave;
+
+ err = assuan_send_data (ctx, enckeyblob, enckeybloblen);
+ if (!err)
+ err = assuan_send_data (ctx, NULL, 0); /* Flush */
+
+ leave:
+ xfree (enckeyblob);
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_mount[] =
+ "MOUNT <type>\n"
+ "\n"
+ "Mount an encrypted partition on the current device.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_mount (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ unsigned char *keyblob = NULL;
+ size_t keybloblen;
+ tupledesc_t tuples = NULL;
+
+ line = skip_options (line);
+
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (!err)
+ {
+ err = gpg_error (GPG_ERR_ENODEV);
+ assuan_set_error (ctx, err, "Partition is empty");
+ goto leave;
+ }
+ err = 0;
+
+ /* We expect that the client already decrypted the keyblob.
+ * Eventually we should move reading of the keyblob to here and ask
+ * the client to decrypt it. */
+ assuan_begin_confidential (ctx);
+ err = assuan_inquire (ctx, "KEYBLOB",
+ &keyblob, &keybloblen, 4 * 1024);
+ assuan_end_confidential (ctx);
+ if (err)
+ {
+ log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+ err = create_tupledesc (&tuples, keyblob, keybloblen);
+ if (!err)
+ keyblob = NULL;
+ else
+ {
+ if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
+ log_error ("unknown keyblob version received\n");
+ goto leave;
+ }
+
+ err = sh_dmcrypt_mount_container (ctrl,
+ ctrl->server_local->devicename,
+ tuples);
+
+ leave:
+ destroy_tupledesc (tuples);
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_umount[] =
+ "UMOUNT <type>\n"
+ "\n"
+ "Unmount an encrypted partition and wipe the key.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_umount (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+
+ line = skip_options (line);
+
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_dmcrypt_umount_container (ctrl, ctrl->server_local->devicename);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_suspend[] =
+ "SUSPEND <type>\n"
+ "\n"
+ "Suspend an encrypted partition and wipe the key.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_suspend (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+
+ line = skip_options (line);
+
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (!err)
+ {
+ err = gpg_error (GPG_ERR_ENODEV);
+ assuan_set_error (ctx, err, "Partition is empty");
+ goto leave;
+ }
+ err = 0;
+
+ err = sh_dmcrypt_suspend_container (ctrl, ctrl->server_local->devicename);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_resume[] =
+ "RESUME <type>\n"
+ "\n"
+ "Resume an encrypted partition and set the key.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_resume (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ unsigned char *keyblob = NULL;
+ size_t keybloblen;
+ tupledesc_t tuples = NULL;
+
+ line = skip_options (line);
+
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (!err)
+ {
+ err = gpg_error (GPG_ERR_ENODEV);
+ assuan_set_error (ctx, err, "Partition is empty");
+ goto leave;
+ }
+ err = 0;
+
+ /* We expect that the client already decrypted the keyblob.
+ * Eventually we should move reading of the keyblob to here and ask
+ * the client to decrypt it. */
+ assuan_begin_confidential (ctx);
+ err = assuan_inquire (ctx, "KEYBLOB",
+ &keyblob, &keybloblen, 4 * 1024);
+ assuan_end_confidential (ctx);
+ if (err)
+ {
+ log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+ err = create_tupledesc (&tuples, keyblob, keybloblen);
+ if (!err)
+ keyblob = NULL;
+ else
+ {
+ if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
+ log_error ("unknown keyblob version received\n");
+ goto leave;
+ }
+
+ err = sh_dmcrypt_resume_container (ctrl,
+ ctrl->server_local->devicename,
+ tuples);
+
+ leave:
+ destroy_tupledesc (tuples);
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_getinfo[] =
+ "GETINFO <what>\n"
+ "\n"
+ "Multipurpose function to return a variety of information.\n"
+ "Supported values for WHAT are:\n"
+ "\n"
+ " version - Return the version of the program.\n"
+ " pid - Return the process id of the server.\n"
+ " showtab - Show the table for the user.";
+static gpg_error_t
+cmd_getinfo (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ char *buf;
+
+ if (!strcmp (line, "version"))
+ {
+ const char *s = PACKAGE_VERSION;
+ err = assuan_send_data (ctx, s, strlen (s));
+ }
+ else if (!strcmp (line, "pid"))
+ {
+ char numbuf[50];
+
+ snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
+ err = assuan_send_data (ctx, numbuf, strlen (numbuf));
+ }
+ else if (!strncmp (line, "getsz", 5))
+ {
+ unsigned long long nblocks;
+ err = sh_blockdev_getsz (line+6, &nblocks);
+ if (!err)
+ log_debug ("getsz=%llu\n", nblocks);
+ }
+ else if (!strcmp (line, "showtab"))
+ {
+ tab_item_t ti;
+
+ for (ti=ctrl->client.tab; !err && ti; ti = ti->next)
+ {
+ buf = es_bsprintf ("%s %s%s %s %s%s\n",
+ ctrl->client.uname,
+ *ti->blockdev=='/'? "":"partuuid=",
+ ti->blockdev,
+ ti->label? ti->label : "-",
+ ti->mountpoint? " ":"",
+ ti->mountpoint? ti->mountpoint:"");
+ if (!buf)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ err = assuan_send_data (ctx, buf, strlen (buf));
+ if (!err)
+ err = assuan_send_data (ctx, NULL, 0); /* Flush */
+ }
+ xfree (buf);
+ }
+ }
+ else
+ err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
+
+ return leave_cmd (ctx, err);
+}
+
+
+/* This command handler is used for all commands if this process has
+ not been started as expected. */
+static gpg_error_t
+fail_command (assuan_context_t ctx, char *line)
+{
+ gpg_error_t err;
+ const char *name = assuan_get_command_name (ctx);
+
+ (void)line;
+
+ if (!name)
+ name = "?";
+
+ err = set_error_fail_cmd ();
+ log_error ("command '%s' failed: %s\n", name, gpg_strerror (err));
+ return err;
+}
+
+
+/* Tell the Assuan library about our commands. */
+static int
+register_commands (assuan_context_t ctx, int fail_all)
+{
+ static struct {
+ const char *name;
+ assuan_handler_t handler;
+ const char * const help;
+ } table[] = {
+ { "FINDDEVICE", cmd_finddevice, hlp_finddevice },
+ { "DEVICE", cmd_device, hlp_device },
+ { "CREATE", cmd_create, hlp_create },
+ { "GETKEYBLOB", cmd_getkeyblob, hlp_getkeyblob },
+ { "MOUNT", cmd_mount, hlp_mount },
+ { "UMOUNT", cmd_umount, hlp_umount },
+ { "SUSPEND", cmd_suspend,hlp_suspend},
+ { "RESUME", cmd_resume, hlp_resume },
+ { "INPUT", NULL },
+ { "OUTPUT", NULL },
+ { "GETINFO", cmd_getinfo, hlp_getinfo },
+ { NULL }
+ };
+ gpg_error_t err;
+ int i;
+
+ for (i=0; table[i].name; i++)
+ {
+ err = assuan_register_command (ctx, table[i].name,
+ fail_all ? fail_command : table[i].handler,
+ table[i].help);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* Startup the server. */
+gpg_error_t
+syshelp_server (ctrl_t ctrl)
+{
+ gpg_error_t err;
+ assuan_fd_t filedes[2];
+ assuan_context_t ctx = NULL;
+
+ /* We use a pipe based server so that we can work from scripts.
+ assuan_init_pipe_server will automagically detect when we are
+ called with a socketpair and ignore FILEDES in this case. */
+ filedes[0] = assuan_fdopen (0);
+ filedes[1] = assuan_fdopen (1);
+ err = assuan_new (&ctx);
+ if (err)
+ {
+ log_error ("failed to allocate an Assuan context: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ err = assuan_init_pipe_server (ctx, filedes);
+ if (err)
+ {
+ log_error ("failed to initialize the server: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = register_commands (ctx, 0 /*FIXME:ctrl->fail_all_cmds*/);
+ if (err)
+ {
+ log_error ("failed to the register commands with Assuan: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ assuan_set_pointer (ctx, ctrl);
+
+ {
+ char *tmp = xtryasprintf ("G13-syshelp %s ready to serve requests "
+ "from %lu(%s)",
+ PACKAGE_VERSION,
+ (unsigned long)ctrl->client.uid,
+ ctrl->client.uname);
+ if (tmp)
+ {
+ assuan_set_hello_line (ctx, tmp);
+ xfree (tmp);
+ }
+ }
+
+ assuan_register_reset_notify (ctx, reset_notify);
+ assuan_register_option_handler (ctx, option_handler);
+
+ ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
+ if (!ctrl->server_local)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ ctrl->server_local->assuan_ctx = ctx;
+
+ while ( !(err = assuan_accept (ctx)) )
+ {
+ err = assuan_process (ctx);
+ if (err)
+ log_info ("Assuan processing failed: %s\n", gpg_strerror (err));
+ }
+ if (err == -1)
+ err = 0;
+ else
+ log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
+
+ leave:
+ reset_notify (ctx, NULL); /* Release all items hold by SERVER_LOCAL. */
+ if (ctrl->server_local)
+ {
+ xfree (ctrl->server_local);
+ ctrl->server_local = NULL;
+ }
+
+ assuan_release (ctx);
+ return err;
+}
+
+
+gpg_error_t
+sh_encrypt_keyblob (ctrl_t ctrl, const void *keyblob, size_t keybloblen,
+ char **r_enckeyblob, size_t *r_enckeybloblen)
+{
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+ gpg_error_t err;
+ unsigned char *enckeyblob;
+ size_t enckeybloblen;
+
+ *r_enckeyblob = NULL;
+
+ /* Send the plaintext. */
+ err = g13_status (ctrl, STATUS_PLAINTEXT_FOLLOWS, NULL);
+ if (err)
+ return err;
+ assuan_begin_confidential (ctx);
+ err = assuan_send_data (ctx, keyblob, keybloblen);
+ if (!err)
+ err = assuan_send_data (ctx, NULL, 0);
+ assuan_end_confidential (ctx);
+ if (!err)
+ err = assuan_write_line (ctx, "END");
+ if (err)
+ {
+ log_error (_("error sending data: %s\n"), gpg_strerror (err));
+ return err;
+ }
+
+ /* Inquire the ciphertext. */
+ err = assuan_inquire (ctx, "ENCKEYBLOB",
+ &enckeyblob, &enckeybloblen, 16 * 1024);
+ if (err)
+ {
+ log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+ return err;
+ }
+
+ *r_enckeyblob = enckeyblob;
+ *r_enckeybloblen = enckeybloblen;
+ return 0;
+}
+
+
+/* Send a status line with status ID NO. The arguments are a list of
+ strings terminated by a NULL argument. */
+gpg_error_t
+g13_status (ctrl_t ctrl, int no, ...)
+{
+ gpg_error_t err;
+ va_list arg_ptr;
+
+ va_start (arg_ptr, no);
+
+ err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx,
+ get_status_string (no), arg_ptr);
+ va_end (arg_ptr);
+ return err;
+}
diff --git a/g13/sh-dmcrypt.c b/g13/sh-dmcrypt.c
new file mode 100644
index 0000000..6f7173e
--- /dev/null
+++ b/g13/sh-dmcrypt.c
@@ -0,0 +1,1043 @@
+/* sh-dmcrypt.c - The DM-Crypt part for g13-syshelp
+ * Copyright (C) 2015, 2016 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_SYS_SYSMACROS_H
+# include <sys/sysmacros.h>
+#endif
+#ifdef HAVE_STAT
+# include <sys/stat.h>
+#endif
+#include <unistd.h>
+
+#include "g13-syshelp.h"
+#include <assuan.h>
+#include "../common/i18n.h"
+#include "g13tuple.h"
+#include "../common/exectool.h"
+#include "../common/sysutils.h"
+#include "keyblob.h"
+
+/* The standard disk block size (logical). */
+#define SECTOR_SIZE 512
+
+/* The physical block size used by modern devices. */
+#define PHY_SECTOR_SIZE (SECTOR_SIZE*8) /* 4 KiB */
+
+/* The length of the crypto setup area in sectors. 16 KiB is a nice
+ multiple of a modern block size and should be sufficient for all
+ kind of extra public key encryption packets. */
+#define SETUP_AREA_SECTORS 32 /* 16 KiB */
+
+/* The number of header block copies stored at the begin and end of
+ the device. */
+#define HEADER_SETUP_AREA_COPIES 2
+#define FOOTER_SETUP_AREA_COPIES 2
+
+/* The length in blocks of the space we put at the start and at the
+ end of the device. This space is used to store N copies of the
+ setup area for the actual encrypted container in between. */
+#define HEADER_SECTORS (SETUP_AREA_SECTORS * HEADER_SETUP_AREA_COPIES)
+#define FOOTER_SECTORS (SETUP_AREA_SECTORS * FOOTER_SETUP_AREA_COPIES)
+
+/* Minimim size of the encrypted space in blocks. This is more or
+ less an arbitrary value. */
+#define MIN_ENCRYPTED_SPACE 32
+
+/* Some consistency checks for the above constants. */
+#if (PHY_SECTOR_SIZE % SECTOR_SIZE)
+# error the physical secotor size should be a multiple of 512
+#endif
+#if ((SETUP_AREA_SECTORS*SECTOR_SIZE) % PHY_SECTOR_SIZE)
+# error The setup area size should be a multiple of the phy. sector size.
+#endif
+
+
+/*
+ * Check whether the block device DEVNAME is used by device mapper.
+ * If EXPECT_BUSY is set no error message is printed if the device is
+ * busy. Returns: 0 if the device is good and not yet used by DM.
+ */
+static gpg_error_t
+check_blockdev (const char *devname, int expect_busy)
+{
+ gpg_error_t err;
+ struct stat sb;
+ unsigned int devmajor, devminor;
+ char *result = NULL;
+ char **lines = NULL;
+ char **fields = NULL;
+ int lno, count;
+
+ if (gnupg_stat (devname, &sb))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error stating '%s': %s\n", devname, gpg_strerror (err));
+ return err;
+ }
+ if (!S_ISBLK (sb.st_mode))
+ {
+ err = gpg_error (GPG_ERR_ENOTBLK);
+ log_error ("can't use '%s': %s\n", devname, gpg_strerror (err));
+ return err;
+ }
+ devmajor = major (sb.st_rdev);
+ devminor = minor (sb.st_rdev);
+
+ {
+ const char *argv[2];
+
+ argv[0] = "deps";
+ argv[1] = NULL;
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, NULL, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running '%s' to search for '%s': %s\n",
+ "dmsetup deps", devname, gpg_strerror (err));
+ goto leave;
+ }
+ lines = strsplit (result, '\n', 0, NULL);
+ if (!lines)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (lines[0] && !strcmp (lines[0], "No devices found"))
+ ;
+ else
+ {
+ for (lno=0; lines[lno]; lno++)
+ {
+ unsigned int xmajor, xminor;
+
+ if (!*lines[lno])
+ continue;
+ xfree (fields);
+ fields = strsplit (lines[lno], ':', 0, &count);
+ if (!fields)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (count < 3
+ || sscanf (fields[2], " (%u,%u)", &xmajor, &xminor) != 2)
+ {
+ log_error ("error running '%s' to search for '%s': %s\n",
+ "dmsetup deps", devname, "unexpected output");
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+
+ if (xmajor == devmajor && xminor == devminor)
+ {
+ if (!expect_busy)
+ log_error ("device '%s' (%u:%u)"
+ " already in use by device mapper\n",
+ devname, devmajor, devminor);
+ err = gpg_error (GPG_ERR_EBUSY);
+ goto leave;
+ }
+ }
+ }
+
+
+ leave:
+ xfree (fields);
+ xfree (lines);
+ xfree (result);
+ return err;
+}
+
+
+/* Return a malloced buffer with the prefix of the setup area. This
+ is the data written right before the encrypted keyblob. Return NULL
+ on error and sets ERRNO. */
+static void *
+mk_setup_area_prefix (size_t *r_length)
+{
+ unsigned char *packet;
+ size_t setuparealen;
+
+ packet = xtrymalloc (32);
+ if (!packet)
+ return NULL;
+ *r_length = 32;
+
+ setuparealen = SETUP_AREA_SECTORS * SECTOR_SIZE;
+
+ packet[0] = (0xc0|61); /* CTB for the private packet type 0x61. */
+ packet[1] = 0xff; /* 5 byte length packet, value 20. */
+ packet[2] = 0;
+ packet[3] = 0;
+ packet[4] = 0;
+ packet[5] = 26;
+ memcpy (packet+6, "GnuPG/G13", 10); /* Packet subtype. */
+ packet[16] = 1; /* G13 packet format version. */
+ packet[17] = 0; /* Reserved. */
+ packet[18] = 0; /* Reserved. */
+ packet[19] = 1; /* OS Flag = Linux */
+ packet[20] = (setuparealen >> 24); /* Total length of header. */
+ packet[21] = (setuparealen >> 16);
+ packet[22] = (setuparealen >> 8);
+ packet[23] = (setuparealen);
+ packet[24] = HEADER_SETUP_AREA_COPIES;
+ packet[25] = FOOTER_SETUP_AREA_COPIES;
+ packet[26] = 0; /* Reserved. */
+ packet[27] = 0; /* Reserved. */
+ packet[28] = 0; /* Reserved. */
+ packet[29] = 0; /* Reserved. */
+ packet[30] = 0; /* Reserved. */
+ packet[31] = 0; /* Reserved. */
+
+ return packet;
+}
+
+
+/* Create a new g13 styloe DM-Crypt container on devoce DEVNAME. */
+gpg_error_t
+sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
+{
+ gpg_error_t err;
+ char *header_space;
+ size_t header_space_size, header_space_used;
+ size_t paddinglen;
+ char *targetname = NULL;
+ size_t nread;
+ char *p;
+ char hexkey[16*2+1];
+ char *table = NULL;
+ unsigned long long nblocks;
+ char *result = NULL;
+ unsigned char twobyte[2];
+ membuf_t keyblob;
+ void *keyblob_buf = NULL;
+ size_t keyblob_len;
+ size_t n;
+ const char *s;
+ unsigned char *packet;
+ int copy;
+
+ if (!ctrl->devti)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ g13_syshelp_i_know_what_i_am_doing ();
+
+ header_space_size = SETUP_AREA_SECTORS * SECTOR_SIZE;
+ header_space = xtrymalloc (header_space_size);
+ if (!header_space)
+ return gpg_error_from_syserror ();
+
+ /* Start building the keyblob. */
+ init_membuf (&keyblob, 512);
+ append_tuple (&keyblob, KEYBLOB_TAG_BLOBVERSION, "\x01", 1);
+ n = CONTTYPE_DM_CRYPT;
+ twobyte[0] = (n >> 8);
+ twobyte[1] = n;
+ append_tuple (&keyblob, KEYBLOB_TAG_CONTTYPE, twobyte, 2);
+ {
+ gnupg_isotime_t tbuf;
+
+ gnupg_get_isotime (tbuf);
+ append_tuple (&keyblob, KEYBLOB_TAG_CREATED, tbuf, strlen (tbuf));
+ }
+
+ /* Rewind device stream. */
+ if (es_fseeko (devfp, 0, SEEK_SET))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error seeking to begin of '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ es_clearerr (devfp);
+
+ /* Extra check that the device is empty. */
+ if (es_read (devfp, header_space, header_space_size, &nread))
+ err = gpg_error_from_syserror ();
+ else if (nread != header_space_size)
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ else
+ err = 0;
+ if (err)
+ {
+ log_error ("error reading header space of '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ for (p=header_space; nread && !*p; nread--, p++)
+ ;
+ if (nread)
+ {
+ log_error ("header space of '%s' already used - use %s to override\n",
+ devname, "--force");
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ goto leave;
+ }
+
+ /* Check that the device is not used by device mapper. */
+ err = check_blockdev (devname, 0);
+ if (err)
+ goto leave;
+
+ /* Compute the number of blocks. */
+ err = sh_blockdev_getsz (devname, &nblocks);
+ if (err)
+ {
+ log_error ("error getting size of '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ if (nblocks <= HEADER_SECTORS + MIN_ENCRYPTED_SPACE + FOOTER_SECTORS)
+ {
+ log_error ("device '%s' is too small (min=%d blocks)\n",
+ devname,
+ HEADER_SECTORS + MIN_ENCRYPTED_SPACE + FOOTER_SECTORS);
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ goto leave;
+ }
+ append_tuple_uint (&keyblob, KEYBLOB_TAG_CONT_NSEC, nblocks);
+ nblocks -= HEADER_SECTORS + FOOTER_SECTORS;
+ append_tuple_uint (&keyblob, KEYBLOB_TAG_ENC_NSEC, nblocks);
+ append_tuple_uint (&keyblob, KEYBLOB_TAG_ENC_OFF, HEADER_SECTORS);
+
+ /* Device mapper needs a name for the device: Take it from the label
+ or use "0". */
+ targetname = strconcat ("g13-", ctrl->client.uname, "-",
+ ctrl->devti->label? ctrl->devti->label : "0",
+ NULL);
+ if (!targetname)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* Create the key. */
+ {
+ char key[16];
+ gcry_randomize (key, sizeof key, GCRY_STRONG_RANDOM);
+ append_tuple (&keyblob, KEYBLOB_TAG_ENCKEY, key, sizeof key);
+ bin2hex (key, 16, hexkey);
+ wipememory (key, 16);
+ /* Add a 2*(4+16) byte filler to conceal the fact that we use
+ AES-128. If we ever want to switch to 256 bit we can resize
+ that filler to keep the keyblob at the same size. */
+ append_tuple (&keyblob, KEYBLOB_TAG_FILLER, key, sizeof key);
+ append_tuple (&keyblob, KEYBLOB_TAG_FILLER, key, sizeof key);
+ }
+
+ /* Build dmcrypt table. */
+ s = "aes-cbc-essiv:sha256";
+ append_tuple (&keyblob, KEYBLOB_TAG_ALGOSTR, s, strlen (s));
+ table = es_bsprintf ("0 %llu crypt %s %s 0 %s %d",
+ nblocks, s, hexkey, devname, HEADER_SECTORS);
+ if (!table)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ wipememory (hexkey, sizeof hexkey);
+
+ /* Add a copy of the setup area prefix to the keyblob. */
+ p = mk_setup_area_prefix (&n);
+ if (!p)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ append_tuple (&keyblob, KEYBLOB_TAG_HDRCOPY, p, n);
+ assert (n < header_space_size);
+ memcpy (header_space, p, n);
+ header_space_used = n;
+
+ /* Turn the keyblob into a buffer and callback to encrypt it. */
+ keyblob_buf = get_membuf (&keyblob, &keyblob_len);
+ if (!keyblob_buf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = sh_encrypt_keyblob (ctrl, keyblob_buf, keyblob_len, &p, &n);
+ if (err)
+ {
+ log_error ("encrypting the keyblob failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ log_debug ("plain setuparea=%p %zu bytes\n", keyblob_buf, keyblob_len);
+ wipememory (keyblob_buf, keyblob_len);
+ xfree (keyblob_buf);
+ keyblob_buf = NULL;
+
+ log_debug ("encry setuparea=%p %zu bytes\n", p, n);
+ if (n >= header_space_size || (header_space_used + n) >= header_space_size)
+ {
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ log_error ("setup area would overflow: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ memcpy (header_space + header_space_used, p, n);
+ header_space_used += n;
+
+ /* Write the padding. */
+ packet = header_space + header_space_used;
+ paddinglen = header_space_size - header_space_used;
+ if (paddinglen < 16)
+ {
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ log_error ("setup area too short for padding: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ packet[0] = (0xc0|61); /* CTB for Private packet type 0x61. */
+ packet[1] = 0xff; /* 5 byte length packet, value 20. */
+ packet[2] = (paddinglen-6) >> 24;
+ packet[3] = (paddinglen-6) >> 16;
+ packet[4] = (paddinglen-6) >> 8;
+ packet[5] = (paddinglen-6);
+ packet += 6;
+ paddinglen -= 6;
+ header_space_used += 6;
+ for ( ;paddinglen >= 10;
+ paddinglen -= 10, packet += 10, header_space_used += 10)
+ memcpy (packet, "GnuPG/PAD", 10);
+ for ( ;paddinglen; paddinglen--, packet++, header_space_used++)
+ *packet = 0;
+
+ if (header_space_used != header_space_size)
+ BUG ();
+
+ /* Create the container. */
+ {
+ const char *argv[3];
+
+ argv[0] = "create";
+ argv[1] = targetname;
+ argv[2] = NULL;
+ log_debug ("now running \"dmsetup create %s\"\n", targetname);
+ log_debug (" with table='%s'\"\n", table);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, table, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running dmsetup for '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+
+ /* Write the setup area. */
+ if (es_fseeko (devfp, 0, SEEK_SET))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error seeking to begin of '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ es_clearerr (devfp);
+
+ for (copy = 0; copy < HEADER_SETUP_AREA_COPIES; copy++)
+ {
+ size_t nwritten;
+
+ if (es_write (devfp, header_space, header_space_size, &nwritten))
+ {
+ err = gpg_error_from_syserror ();
+ break;
+ }
+ else if (nwritten != header_space_size)
+ {
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ break;
+ }
+ }
+ if (err)
+ {
+ log_error ("error writing header space copy %d of '%s': %s\n",
+ copy, devname, gpg_strerror (err));
+ goto leave;
+ }
+
+ if (es_fseeko (devfp,
+ (- header_space_size * FOOTER_SETUP_AREA_COPIES), SEEK_END))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error seeking to end of '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ es_clearerr (devfp);
+
+ for (copy = 0; copy < FOOTER_SETUP_AREA_COPIES; copy++)
+ {
+ size_t nwritten;
+
+ if (es_write (devfp, header_space, header_space_size, &nwritten))
+ {
+ err = gpg_error_from_syserror ();
+ break;
+ }
+ else if (nwritten != header_space_size)
+ {
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ break;
+ }
+ }
+ if (!err && es_fflush (devfp))
+ err = gpg_error_from_syserror ();
+ if (err)
+ {
+ log_error ("error writing footer space copy %d of '%s': %s\n",
+ copy, devname, gpg_strerror (err));
+ goto leave;
+ }
+
+ leave:
+ wipememory (hexkey, sizeof hexkey);
+ if (table)
+ {
+ wipememory (table, strlen (table));
+ xfree (table);
+ }
+ if (keyblob_buf)
+ {
+ wipememory (keyblob_buf, keyblob_len);
+ xfree (keyblob_buf);
+ }
+ xfree (get_membuf (&keyblob, NULL));
+ xfree (targetname);
+ xfree (result);
+ xfree (header_space);
+ return err;
+}
+
+
+/* Mount a DM-Crypt container on device DEVNAME taking keys and other
+ * meta data from KEYBLOB. */
+gpg_error_t
+sh_dmcrypt_mount_container (ctrl_t ctrl, const char *devname,
+ tupledesc_t keyblob)
+{
+ gpg_error_t err;
+ char *targetname_abs = NULL;
+ const char *targetname;
+ char hexkey[16*2+1];
+ char *table = NULL;
+ unsigned long long nblocks, nblocks2;
+ char *result = NULL;
+ size_t n;
+ const char *s;
+ const char *algostr;
+ size_t algostrlen;
+
+ if (!ctrl->devti)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ g13_syshelp_i_know_what_i_am_doing ();
+
+ /* Check that the device is not yet used by device mapper. */
+ err = check_blockdev (devname, 0);
+ if (err)
+ goto leave;
+
+ /* Compute the number of blocks and compare them to the value
+ provided as meta data. */
+ err = sh_blockdev_getsz (devname, &nblocks);
+ if (err)
+ {
+ log_error ("error getting size of '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ err = find_tuple_uint (keyblob, KEYBLOB_TAG_CONT_NSEC, &nblocks2);
+ if (err)
+ {
+ log_error ("error getting size from keyblob: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ if (nblocks != nblocks2)
+ {
+ log_error ("inconsistent size of container: expected==%llu got=%llu\n",
+ nblocks2, nblocks);
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+ if (nblocks <= HEADER_SECTORS + MIN_ENCRYPTED_SPACE + FOOTER_SECTORS)
+ {
+ log_error ("device '%s' is too small (min=%d blocks)\n",
+ devname,
+ HEADER_SECTORS + MIN_ENCRYPTED_SPACE + FOOTER_SECTORS);
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ goto leave;
+ }
+ nblocks -= HEADER_SECTORS + FOOTER_SECTORS;
+ err = find_tuple_uint (keyblob, KEYBLOB_TAG_ENC_NSEC, &nblocks2);
+ if (err)
+ {
+ log_error ("error getting enc size from keyblob: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ if (nblocks != nblocks2)
+ {
+ log_error ("inconsistent size of enc data: expected==%llu got=%llu\n",
+ nblocks2, nblocks);
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+ /* Check that the offset is consistent. */
+ err = find_tuple_uint (keyblob, KEYBLOB_TAG_ENC_OFF, &nblocks2);
+ if (err)
+ {
+ log_error ("error getting enc offset from keyblob: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ if (nblocks2 != HEADER_SECTORS)
+ {
+ log_error ("inconsistent offset of enc data: expected==%llu got=%d\n",
+ nblocks2, HEADER_SECTORS);
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+
+ /* Device mapper needs a name for the device: Take it from the label
+ or use "0". */
+ targetname_abs = strconcat ("/dev/mapper/",
+ "g13-", ctrl->client.uname, "-",
+ ctrl->devti->label? ctrl->devti->label : "0",
+ NULL);
+ if (!targetname_abs)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ targetname = strrchr (targetname_abs, '/');
+ if (!targetname)
+ BUG ();
+ targetname++;
+
+ /* Get the algorithm string. */
+ algostr = find_tuple (keyblob, KEYBLOB_TAG_ALGOSTR, &algostrlen);
+ if (!algostr || algostrlen > 100)
+ {
+ log_error ("algo string not found in keyblob or too long\n");
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+
+ /* Get the key. */
+ s = find_tuple (keyblob, KEYBLOB_TAG_ENCKEY, &n);
+ if (!s || n != 16)
+ {
+ if (!s)
+ log_error ("no key found in keyblob\n");
+ else
+ log_error ("unexpected size of key (%zu)\n", n);
+ err = gpg_error (GPG_ERR_INV_KEYLEN);
+ goto leave;
+ }
+ bin2hex (s, 16, hexkey);
+
+ /* Build dmcrypt table. */
+ table = es_bsprintf ("0 %llu crypt %.*s %s 0 %s %d",
+ nblocks, (int)algostrlen, algostr,
+ hexkey, devname, HEADER_SECTORS);
+ wipememory (hexkey, sizeof hexkey);
+ if (!table)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* Load the table. */
+ {
+ const char *argv[3];
+
+ argv[0] = "create";
+ argv[1] = targetname;
+ argv[2] = NULL;
+ log_debug ("now running \"dmsetup create %s\"\n", targetname);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, table, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running dmsetup for '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+ xfree (result);
+ result = NULL;
+
+ /* Mount if a mountpoint has been given. */
+ if (ctrl->devti->mountpoint)
+ {
+ const char *argv[3];
+
+ argv[0] = targetname_abs;
+ argv[1] = ctrl->devti->mountpoint;
+ argv[2] = NULL;
+ log_debug ("now running \"mount %s %s\"\n",
+ targetname_abs, ctrl->devti->mountpoint);
+ err = gnupg_exec_tool ("/bin/mount", argv, NULL, &result, NULL);
+ if (err)
+ {
+ log_error ("error running mount: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result) /* (We should not see output to stdout). */
+ log_info ("WARNING: mount returned data on stdout! (%s)\n", result);
+ }
+
+
+ leave:
+ wipememory (hexkey, sizeof hexkey);
+ if (table)
+ {
+ wipememory (table, strlen (table));
+ xfree (table);
+ }
+ xfree (targetname_abs);
+ xfree (result);
+ return err;
+}
+
+
+/* Unmount a DM-Crypt container on device DEVNAME and wipe the keys. */
+gpg_error_t
+sh_dmcrypt_umount_container (ctrl_t ctrl, const char *devname)
+{
+ gpg_error_t err;
+ char *targetname_abs = NULL;
+ char *result = NULL;
+
+ if (!ctrl->devti)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ g13_syshelp_i_know_what_i_am_doing ();
+
+ /* Check that the device is used by device mapper. */
+ err = check_blockdev (devname, 1);
+ if (gpg_err_code (err) != GPG_ERR_EBUSY)
+ {
+ log_error ("device '%s' is not used by the device mapper: %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Fixme: Check that this is really a g13 partition. */
+
+ /* Device mapper needs a name for the device: Take it from the label
+ or use "0". */
+ targetname_abs = strconcat ("/dev/mapper/",
+ "g13-", ctrl->client.uname, "-",
+ ctrl->devti->label? ctrl->devti->label : "0",
+ NULL);
+ if (!targetname_abs)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* Run the regular umount command. */
+ {
+ const char *argv[2];
+
+ argv[0] = targetname_abs;
+ argv[1] = NULL;
+ log_debug ("now running \"umount %s\"\n", targetname_abs);
+ err = gnupg_exec_tool ("/bin/umount", argv, NULL, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running umount: %s\n", gpg_strerror (err));
+ if (1)
+ {
+ /* Try to show some info about processes using the partition. */
+ const char *argv[3];
+
+ argv[0] = "-mv";
+ argv[1] = targetname_abs;
+ argv[2] = NULL;
+ gnupg_exec_tool ("/bin/fuser", argv, NULL, &result, NULL);
+ }
+ goto leave;
+ }
+ if (result && *result) /* (We should not see output to stdout). */
+ log_info ("WARNING: umount returned data on stdout! (%s)\n", result);
+ xfree (result);
+ result = NULL;
+
+ /* Run the dmsetup remove command. */
+ {
+ const char *argv[3];
+
+ argv[0] = "remove";
+ argv[1] = targetname_abs;
+ argv[2] = NULL;
+ log_debug ("now running \"dmsetup remove %s\"\n", targetname_abs);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, NULL, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running \"dmsetup remove %s\": %s\n",
+ targetname_abs, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+ xfree (result);
+ result = NULL;
+
+ leave:
+ xfree (targetname_abs);
+ xfree (result);
+ return err;
+}
+
+
+/* Suspend a DM-Crypt container on device DEVNAME and wipe the keys. */
+gpg_error_t
+sh_dmcrypt_suspend_container (ctrl_t ctrl, const char *devname)
+{
+ gpg_error_t err;
+ char *targetname_abs = NULL;
+ const char *targetname;
+ char *result = NULL;
+
+ if (!ctrl->devti)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ g13_syshelp_i_know_what_i_am_doing ();
+
+ /* Check that the device is used by device mapper. */
+ err = check_blockdev (devname, 1);
+ if (gpg_err_code (err) != GPG_ERR_EBUSY)
+ {
+ log_error ("device '%s' is not used by the device mapper: %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Fixme: Check that this is really a g13 partition. */
+
+ /* Device mapper needs a name for the device: Take it from the label
+ or use "0". */
+ targetname_abs = strconcat ("/dev/mapper/",
+ "g13-", ctrl->client.uname, "-",
+ ctrl->devti->label? ctrl->devti->label : "0",
+ NULL);
+ if (!targetname_abs)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ targetname = strrchr (targetname_abs, '/');
+ if (!targetname)
+ BUG ();
+ targetname++;
+
+ /* Send the suspend command. */
+ {
+ const char *argv[3];
+
+ argv[0] = "suspend";
+ argv[1] = targetname;
+ argv[2] = NULL;
+ log_debug ("now running \"dmsetup suspend %s\"\n", targetname);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, NULL, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running \"dmsetup suspend %s\": %s\n",
+ targetname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+ xfree (result);
+ result = NULL;
+
+ /* Send the wipe key command. */
+ {
+ const char *argv[5];
+
+ argv[0] = "message";
+ argv[1] = targetname;
+ argv[2] = "0";
+ argv[3] = "key wipe";
+ argv[4] = NULL;
+ log_debug ("now running \"dmsetup message %s 0 key wipe\"\n", targetname);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, NULL, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running \"dmsetup message %s 0 key wipe\": %s\n",
+ targetname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+ xfree (result);
+ result = NULL;
+
+
+ leave:
+ xfree (targetname_abs);
+ xfree (result);
+ return err;
+}
+
+
+/* Resume a DM-Crypt container on device DEVNAME taking keys and other
+ * meta data from KEYBLOB. */
+gpg_error_t
+sh_dmcrypt_resume_container (ctrl_t ctrl, const char *devname,
+ tupledesc_t keyblob)
+{
+ gpg_error_t err;
+ char *targetname_abs = NULL;
+ const char *targetname;
+ char hexkey[8+16*2+1]; /* 8 is used to prepend "key set ". */
+ char *table = NULL;
+ char *result = NULL;
+ size_t n;
+ const char *s;
+ const char *algostr;
+ size_t algostrlen;
+
+ if (!ctrl->devti)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ g13_syshelp_i_know_what_i_am_doing ();
+
+ /* Check that the device is used by device mapper. */
+ err = check_blockdev (devname, 1);
+ if (gpg_err_code (err) != GPG_ERR_EBUSY)
+ {
+ log_error ("device '%s' is not used by the device mapper: %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Device mapper needs a name for the device: Take it from the label
+ or use "0". */
+ targetname_abs = strconcat ("/dev/mapper/",
+ "g13-", ctrl->client.uname, "-",
+ ctrl->devti->label? ctrl->devti->label : "0",
+ NULL);
+ if (!targetname_abs)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ targetname = strrchr (targetname_abs, '/');
+ if (!targetname)
+ BUG ();
+ targetname++;
+
+ /* Get the algorithm string. */
+ algostr = find_tuple (keyblob, KEYBLOB_TAG_ALGOSTR, &algostrlen);
+ if (!algostr || algostrlen > 100)
+ {
+ log_error ("algo string not found in keyblob or too long\n");
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+
+ /* Get the key. */
+ s = find_tuple (keyblob, KEYBLOB_TAG_ENCKEY, &n);
+ if (!s || n != 16)
+ {
+ if (!s)
+ log_error ("no key found in keyblob\n");
+ else
+ log_error ("unexpected size of key (%zu)\n", n);
+ err = gpg_error (GPG_ERR_INV_KEYLEN);
+ goto leave;
+ }
+ strcpy (hexkey, "key set ");
+ bin2hex (s, 16, hexkey+8);
+
+ /* Send the key */
+ {
+ const char *argv[4];
+
+ argv[0] = "message";
+ argv[1] = targetname;
+ argv[2] = "0";
+ argv[3] = NULL;
+ log_debug ("now running \"dmsetup message %s 0 [key set]\"\n", targetname);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, hexkey, &result, NULL);
+ }
+ wipememory (hexkey, sizeof hexkey);
+ if (err)
+ {
+ log_error ("error running \"dmsetup message %s 0 [key set]\": %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+ xfree (result);
+ result = NULL;
+
+ /* Send the resume command. */
+ {
+ const char *argv[3];
+
+ argv[0] = "resume";
+ argv[1] = targetname;
+ argv[2] = NULL;
+ log_debug ("now running \"dmsetup resume %s\"\n", targetname);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, NULL, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running \"dmsetup resume %s\": %s\n",
+ targetname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+ xfree (result);
+ result = NULL;
+
+ leave:
+ wipememory (hexkey, sizeof hexkey);
+ if (table)
+ {
+ wipememory (table, strlen (table));
+ xfree (table);
+ }
+ xfree (targetname_abs);
+ xfree (result);
+ return err;
+}
diff --git a/g13/suspend.c b/g13/suspend.c
new file mode 100644
index 0000000..44c52dd
--- /dev/null
+++ b/g13/suspend.c
@@ -0,0 +1,145 @@
+/* suspend.c - Suspend/Resume a crypto container
+ * Copyright (C) 2016 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "g13.h"
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+#include "suspend.h"
+
+#include "keyblob.h"
+#include "backend.h"
+#include "g13tuple.h"
+#include "server.h" /*(g13_keyblob_decrypt)*/
+
+
+
+/* Suspend the container with name FILENAME. */
+gpg_error_t
+g13_suspend_container (ctrl_t ctrl, const char *filename)
+{
+ gpg_error_t err;
+ int needs_syshelp;
+
+ /* A quick check to see whether the container exists. */
+ if (gnupg_access (filename, R_OK))
+ return gpg_error_from_syserror ();
+
+ /* Decide whether we need to use the g13-syshelp because we can't
+ use lock files for them. This is most likely the case for device
+ files; thus we test for this. FIXME: The correct solution would
+ be to call g13-syshelp to match the file against the g13tab. */
+ needs_syshelp = !strncmp (filename, "/dev/", 5);
+
+ if (!needs_syshelp)
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ else
+ err = be_suspend_container (ctrl, CONTTYPE_DM_CRYPT, filename);
+
+ return err;
+}
+
+
+/* Resume the container with name FILENAME. */
+gpg_error_t
+g13_resume_container (ctrl_t ctrl, const char *filename)
+{
+ gpg_error_t err;
+ int needs_syshelp;
+ void *enckeyblob = NULL;
+ size_t enckeybloblen;
+ void *keyblob = NULL;
+ size_t keybloblen;
+ tupledesc_t tuples = NULL;
+ size_t n;
+ const unsigned char *value;
+ int conttype;
+ char *mountpoint_buffer = NULL;
+
+ /* A quick check to see whether the container exists. */
+ if (gnupg_access (filename, R_OK))
+ return gpg_error_from_syserror ();
+
+ /* Decide whether we need to use the g13-syshelp because we can't
+ use lock files for them. This is most likely the case for device
+ files; thus we test for this. FIXME: The correct solution would
+ be to call g13-syshelp to match the file against the g13tab. */
+ needs_syshelp = !strncmp (filename, "/dev/", 5);
+
+ if (!needs_syshelp)
+ {
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ goto leave;
+ }
+
+ /* Read the encrypted keyblob. */
+ /* Fixme: Should we move this to syshelp for dm-crypt or do we
+ assume that the encrypted device is world readable? */
+ err = g13_keyblob_read (filename, &enckeyblob, &enckeybloblen);
+ if (err)
+ goto leave;
+
+ /* Decrypt that keyblob and store it in a tuple descriptor. */
+ err = g13_keyblob_decrypt (ctrl, enckeyblob, enckeybloblen,
+ &keyblob, &keybloblen);
+ if (err)
+ goto leave;
+ xfree (enckeyblob);
+ enckeyblob = NULL;
+
+ err = create_tupledesc (&tuples, keyblob, keybloblen);
+ if (!err)
+ keyblob = NULL;
+ else
+ {
+ if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
+ log_error ("unknown keyblob version\n");
+ goto leave;
+ }
+ if (opt.verbose)
+ dump_tupledesc (tuples);
+
+ value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n);
+ if (!value || n != 2)
+ conttype = 0;
+ else
+ conttype = (value[0] << 8 | value[1]);
+ if (!be_is_supported_conttype (conttype))
+ {
+ log_error ("content type %d is not supported\n", conttype);
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ goto leave;
+ }
+ err = be_resume_container (ctrl, conttype, filename, tuples);
+
+ leave:
+ destroy_tupledesc (tuples);
+ xfree (keyblob);
+ xfree (enckeyblob);
+ xfree (mountpoint_buffer);
+ return err;
+}
diff --git a/g13/suspend.h b/g13/suspend.h
new file mode 100644
index 0000000..21943e7
--- /dev/null
+++ b/g13/suspend.h
@@ -0,0 +1,26 @@
+/* suspend.h - Suspend/Resume a crypto container.
+ * Copyright (C) 2016 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_SUSPEND_H
+#define G13_SUSPEND_H
+
+gpg_error_t g13_suspend_container (ctrl_t ctrl, const char *filename);
+gpg_error_t g13_resume_container (ctrl_t ctrl, const char *filename);
+
+#endif /*G13_SUSPEND_H*/
diff --git a/g13/t-g13tuple.c b/g13/t-g13tuple.c
new file mode 100644
index 0000000..2809d23
--- /dev/null
+++ b/g13/t-g13tuple.c
@@ -0,0 +1,223 @@
+/* t-g13tuple.c - Module test for g13tuple.c
+ * Copyright (C) 2016 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+
+#include "../common/util.h"
+#include "keyblob.h"
+#include "g13tuple.h"
+
+#define PGM "t-g13tuple"
+
+static int verbose;
+static int debug;
+static int errcount;
+
+/* Test for the functions append_tuple_uint and find_tuple_unit. */
+static void
+test_tuple_uint (void)
+{
+ static struct {
+ int tag;
+ int len;
+ char *data;
+ unsigned long long val;
+ gpg_err_code_t ec;
+ } tv[] = {
+ { 1, 0, "", 0, GPG_ERR_ERANGE },
+ { 2, 1, "\x00", 0, 0},
+ { 3, 1, "\x7f", 127ull, 0},
+ { 4, 1, "\x80", 0, GPG_ERR_ERANGE },
+ { 5, 1, "\x81", 0, GPG_ERR_ERANGE },
+ { 6, 2, "\x80\x01", 0, GPG_ERR_ERANGE },
+ { 7, 2, "\x00\x80", 128ull, 0 },
+ { 8, 1, "\x01", 1, 0 },
+ { 9, 1, "\x40", 64, 0 },
+ { 10, 2, "\x40\x00", 16384, 0 },
+ { 11, 8, "\x7f\xff\xff\xff\xff\xff\xff\xff", 0x7fffffffffffffffull, 0 },
+ { 12, 9, "\x00\xff\xff\xff\xff\xff\xff\xff\xff", 0xffffffffffffffffull, 0},
+ { 13, 9, "\x01\xff\xff\xff\xff\xff\xff\xff\xff", 0, GPG_ERR_ERANGE }
+ };
+ int tidx;
+ gpg_error_t err;
+ membuf_t mb, mb2;
+ void *p;
+ const void *s;
+ size_t n;
+ tupledesc_t tuples;
+ tupledesc_t tuples2;
+ unsigned long long value;
+ int i;
+
+ init_membuf (&mb, 512);
+ init_membuf (&mb2, 512);
+ append_tuple (&mb, KEYBLOB_TAG_BLOBVERSION, "\x01", 1);
+ append_tuple (&mb2, KEYBLOB_TAG_BLOBVERSION, "\x01", 1);
+ for (tidx=0; tidx < DIM (tv); tidx++)
+ {
+ append_tuple (&mb, tv[tidx].tag, tv[tidx].data, tv[tidx].len);
+ if (!tv[tidx].ec)
+ append_tuple_uint (&mb2, tv[tidx].tag, tv[tidx].val);
+ }
+
+ p = get_membuf (&mb, &n);
+ if (!p)
+ {
+ err = gpg_error_from_syserror ();
+ fprintf (stderr, PGM ":%s: get_membuf failed: %s\n",
+ __func__, gpg_strerror (err));
+ exit (1);
+ }
+ err = create_tupledesc (&tuples, p, n);
+ if (err)
+ {
+ fprintf (stderr, PGM ":%s: create_tupledesc failed: %s\n",
+ __func__, gpg_strerror (err));
+ exit (1);
+ }
+ p = get_membuf (&mb2, &n);
+ if (!p)
+ {
+ err = gpg_error_from_syserror ();
+ fprintf (stderr, PGM ":%s: get_membuf failed: %s\n",
+ __func__, gpg_strerror (err));
+ exit (1);
+ }
+ err = create_tupledesc (&tuples2, p, n);
+ if (err)
+ {
+ fprintf (stderr, PGM ":%s: create_tupledesc failed: %s\n",
+ __func__, gpg_strerror (err));
+ exit (1);
+ }
+
+ for (tidx=0; tidx < DIM (tv); tidx++)
+ {
+ err = find_tuple_uint (tuples, tv[tidx].tag, &value);
+ if (tv[tidx].ec != gpg_err_code (err))
+ {
+ fprintf (stderr, PGM ":%s:tidx=%d: wrong error returned; "
+ "expected(%s) got(%s)\n",
+ __func__, tidx,
+ gpg_strerror (tv[tidx].ec), gpg_strerror (err));
+ errcount++;
+ }
+ else if (!err && tv[tidx].val != value)
+ {
+ fprintf (stderr, PGM ":%s:tidx=%d: wrong value returned; "
+ "expected(%llx) got(%llx)\n",
+ __func__, tidx, tv[tidx].val, value);
+ errcount++;
+ }
+
+ err = find_tuple_uint (tuples2, tv[tidx].tag, &value);
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ {
+ if (!tv[tidx].ec)
+ {
+ fprintf (stderr, PGM ":%s:tidx=%d: find_tuple failed: %s\n",
+ __func__, tidx, gpg_strerror (err));
+ errcount++;
+ }
+ }
+ else if (tv[tidx].ec != gpg_err_code (err))
+ {
+ fprintf (stderr, PGM ":%s:tidx=%d: wrong error returned (2); "
+ "expected(%s) got(%s)\n",
+ __func__, tidx,
+ gpg_strerror (tv[tidx].ec), gpg_strerror (err));
+ errcount++;
+ }
+ else if (!err && tv[tidx].val != value)
+ {
+ fprintf (stderr, PGM ":%s:tidx=%d: wrong value returned (2); "
+ "expected(%llx) got(%llx)\n",
+ __func__, tidx, tv[tidx].val, value);
+ errcount++;
+ }
+
+ s = find_tuple (tuples2, tv[tidx].tag, &n);
+ if (!s)
+ ;
+ else if (tv[tidx].len != n)
+ {
+ fprintf (stderr, PGM ":%s:tidx=%d: wrong string length returned; "
+ "expected(%d) got(%zu)\n",
+ __func__, tidx, tv[tidx].len, n);
+ errcount++;
+ }
+ else if (memcmp (tv[tidx].data, s, n))
+ {
+ fprintf (stderr, PGM ":%s:tidx=%d: wrong string returned:",
+ __func__, tidx);
+ for (i=0; i < n; i++)
+ fprintf (stderr, " %02x", ((unsigned char*)s)[i]);
+ fputc ('\n', stderr);
+ errcount++;
+ }
+ }
+
+ destroy_tupledesc (tuples);
+ destroy_tupledesc (tuples2);
+}
+
+
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+
+ gpgrt_init ();
+ if (argc)
+ { argc--; argv++; }
+ while (argc && last_argc != argc )
+ {
+ last_argc = argc;
+ if (!strcmp (*argv, "--"))
+ {
+ argc--; argv++;
+ break;
+ }
+ else if (!strcmp (*argv, "--verbose"))
+ {
+ verbose++;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--debug"))
+ {
+ verbose += 2;
+ debug++;
+ argc--; argv++;
+ }
+ else if (!strncmp (*argv, "--", 2))
+ {
+ fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
+ exit (1);
+ }
+ }
+
+ test_tuple_uint ();
+
+ return !!errcount;
+}