diff options
Diffstat (limited to 'g13')
40 files changed, 10641 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..b8cb342 --- /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) $(GPG_ERROR_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..7a0a3aa --- /dev/null +++ b/g13/Makefile.in @@ -0,0 +1,1071 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 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) \ + $(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='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + 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@ +GPGRT_CONFIG = @GPGRT_CONFIG@ +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) $(GPG_ERROR_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; +} |