diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/plugins/mail-crypt | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/plugins/mail-crypt')
20 files changed, 6191 insertions, 0 deletions
diff --git a/src/plugins/mail-crypt/Makefile.am b/src/plugins/mail-crypt/Makefile.am new file mode 100644 index 0000000..942dc87 --- /dev/null +++ b/src/plugins/mail-crypt/Makefile.am @@ -0,0 +1,116 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test \ + -I$(top_srcdir)/src/lib-settings \ + -I$(top_srcdir)/src/lib-master \ + -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-dict \ + -I$(top_srcdir)/src/lib-index \ + -I$(top_srcdir)/src/lib-storage/index \ + -I$(top_srcdir)/src/lib-storage \ + -I$(top_srcdir)/src/lib-dcrypt \ + -I$(top_srcdir)/src/lib-fs \ + -I$(top_srcdir)/src/doveadm \ + -I$(top_srcdir)/src/plugins/acl + +if SSL_VERSION_GE_102 +test_options = +else !SSL_VERSION_GE_102 +test_options = NOUNDEF=1 +endif !SSL_VERSION_GE_102 + +doveadm_moduledir = $(moduledir)/doveadm + +NOPLUGIN_LDFLAGS = + +module_LTLIBRARIES = \ + lib10_mail_crypt_plugin.la \ + lib05_mail_crypt_acl_plugin.la \ + libfs_crypt.la \ + libfs_mail_crypt.la + +doveadm_module_LTLIBRARIES = \ + libdoveadm_mail_crypt_plugin.la + +lib10_mail_crypt_plugin_la_LDFLAGS = -module -avoid-version +lib10_mail_crypt_plugin_la_LIBADD = \ + $(LIBDCRYPT_LIBS) \ + $(LIBDOVECOT) + +lib05_mail_crypt_acl_plugin_la_LDFLAGS = -module -avoid-version +if DOVECOT_PLUGIN_DEPS +lib05_mail_crypt_acl_plugin_la_LIBADD = \ + $(LIBDCRYPT_LIBS) \ + lib10_mail_crypt_plugin.la +endif + +lib10_mail_crypt_plugin_la_SOURCES = \ + mail-crypt-global-key.c \ + mail-crypt-userenv.c \ + mail-crypt-key.c \ + mail-crypt-plugin.c + +lib05_mail_crypt_acl_plugin_la_SOURCES = \ + mail-crypt-acl-plugin.c + +libfs_crypt_la_SOURCES = fs-crypt.c \ + mail-crypt-global-key.c \ + mail-crypt-pluginenv.c \ + fs-crypt-settings.c + +libfs_crypt_la_LIBADD = $(LIBDOVECOT) +libfs_crypt_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) +libfs_crypt_la_LDFLAGS = -module -avoid-version + +libfs_mail_crypt_la_SOURCES = fs-mail-crypt.c \ + mail-crypt-global-key.c \ + mail-crypt-userenv.c +libfs_mail_crypt_la_LIBADD = $(LIBDOVECOT) +libfs_mail_crypt_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) +libfs_mail_crypt_la_LDFLAGS = -module -avoid-version + +libdoveadm_mail_crypt_plugin_la_SOURCES = \ + doveadm-mail-crypt.c +libdoveadm_mail_crypt_plugin_la_LIBADD = $(LIBDOVECOT) +libdoveadm_mail_crypt_plugin_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) +libdoveadm_mail_crypt_plugin_la_LDFLAGS = -module -avoid-version + +test_programs = \ + test-mail-global-key \ + test-mail-key + +test_mail_global_key_SOURCES = \ + test-mail-global-key.c \ + fs-crypt-settings.c \ + mail-crypt-global-key.c +test_mail_global_key_LDADD = $(LIBDOVECOT) +test_mail_global_key_DEPENDENCIES = $(LIBDOVECOT_DEPS) +test_mail_global_key_LDFLAGS = $(DOVECOT_BINARY_LDFLAGS) +test_mail_global_key_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\" + +test_mail_key_SOURCES = \ + test-mail-key.c \ + mail-crypt-key.c \ + mail-crypt-global-key.c \ + mail-crypt-userenv.c + +test_mail_key_LDADD = $(LIBDOVECOT_STORAGE) $(LIBDOVECOT) +test_mail_key_DEPENDENCIES = $(LIBDOVECOT_DEPS) $(LIBDOVECOT_STORAGE_DEPS) +test_mail_key_LDFLAGS = $(DOVECOT_BINARY_LDFLAGS) +test_mail_key_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\" + +EXTRA_DIST = fs-crypt-common.c + +noinst_HEADERS = \ + mail-crypt-plugin.h \ + mail-crypt-common.h \ + mail-crypt-global-key.h \ + mail-crypt-key.h \ + fs-crypt-settings.h + +check-local: + for bin in $(test_programs); do \ + if ! env $(test_options) $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done + +noinst_PROGRAMS = $(test_programs) diff --git a/src/plugins/mail-crypt/Makefile.in b/src/plugins/mail-crypt/Makefile.in new file mode 100644 index 0000000..c815616 --- /dev/null +++ b/src/plugins/mail-crypt/Makefile.in @@ -0,0 +1,1200 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +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@ +noinst_PROGRAMS = $(am__EXEEXT_1) +subdir = src/plugins/mail-crypt +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ac_checktype2.m4 \ + $(top_srcdir)/m4/ac_typeof.m4 $(top_srcdir)/m4/arc4random.m4 \ + $(top_srcdir)/m4/blockdev.m4 $(top_srcdir)/m4/c99_vsnprintf.m4 \ + $(top_srcdir)/m4/clock_gettime.m4 $(top_srcdir)/m4/crypt.m4 \ + $(top_srcdir)/m4/crypt_xpg6.m4 $(top_srcdir)/m4/dbqlk.m4 \ + $(top_srcdir)/m4/dirent_dtype.m4 $(top_srcdir)/m4/dovecot.m4 \ + $(top_srcdir)/m4/fd_passing.m4 $(top_srcdir)/m4/fdatasync.m4 \ + $(top_srcdir)/m4/flexible_array_member.m4 \ + $(top_srcdir)/m4/glibc.m4 $(top_srcdir)/m4/gmtime_max.m4 \ + $(top_srcdir)/m4/gmtime_tm_gmtoff.m4 \ + $(top_srcdir)/m4/ioloop.m4 $(top_srcdir)/m4/iovec.m4 \ + $(top_srcdir)/m4/ipv6.m4 $(top_srcdir)/m4/libcap.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libwrap.m4 \ + $(top_srcdir)/m4/linux_mremap.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/mmap_write.m4 \ + $(top_srcdir)/m4/mntctl.m4 $(top_srcdir)/m4/modules.m4 \ + $(top_srcdir)/m4/notify.m4 $(top_srcdir)/m4/nsl.m4 \ + $(top_srcdir)/m4/off_t_max.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/pr_set_dumpable.m4 \ + $(top_srcdir)/m4/q_quotactl.m4 $(top_srcdir)/m4/quota.m4 \ + $(top_srcdir)/m4/random.m4 $(top_srcdir)/m4/rlimit.m4 \ + $(top_srcdir)/m4/sendfile.m4 $(top_srcdir)/m4/size_t_signed.m4 \ + $(top_srcdir)/m4/sockpeercred.m4 $(top_srcdir)/m4/sql.m4 \ + $(top_srcdir)/m4/ssl.m4 $(top_srcdir)/m4/st_tim.m4 \ + $(top_srcdir)/m4/static_array.m4 $(top_srcdir)/m4/test_with.m4 \ + $(top_srcdir)/m4/time_t.m4 $(top_srcdir)/m4/typeof.m4 \ + $(top_srcdir)/m4/typeof_dev_t.m4 \ + $(top_srcdir)/m4/uoff_t_max.m4 $(top_srcdir)/m4/vararg.m4 \ + $(top_srcdir)/m4/want_apparmor.m4 \ + $(top_srcdir)/m4/want_bsdauth.m4 \ + $(top_srcdir)/m4/want_bzlib.m4 \ + $(top_srcdir)/m4/want_cassandra.m4 \ + $(top_srcdir)/m4/want_cdb.m4 \ + $(top_srcdir)/m4/want_checkpassword.m4 \ + $(top_srcdir)/m4/want_clucene.m4 $(top_srcdir)/m4/want_db.m4 \ + $(top_srcdir)/m4/want_gssapi.m4 $(top_srcdir)/m4/want_icu.m4 \ + $(top_srcdir)/m4/want_ldap.m4 $(top_srcdir)/m4/want_lua.m4 \ + $(top_srcdir)/m4/want_lz4.m4 $(top_srcdir)/m4/want_lzma.m4 \ + $(top_srcdir)/m4/want_mysql.m4 $(top_srcdir)/m4/want_pam.m4 \ + $(top_srcdir)/m4/want_passwd.m4 $(top_srcdir)/m4/want_pgsql.m4 \ + $(top_srcdir)/m4/want_prefetch.m4 \ + $(top_srcdir)/m4/want_shadow.m4 \ + $(top_srcdir)/m4/want_sodium.m4 $(top_srcdir)/m4/want_solr.m4 \ + $(top_srcdir)/m4/want_sqlite.m4 \ + $(top_srcdir)/m4/want_stemmer.m4 \ + $(top_srcdir)/m4/want_systemd.m4 \ + $(top_srcdir)/m4/want_textcat.m4 \ + $(top_srcdir)/m4/want_unwind.m4 $(top_srcdir)/m4/want_zlib.m4 \ + $(top_srcdir)/m4/want_zstd.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__EXEEXT_1 = test-mail-global-key$(EXEEXT) test-mail-key$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(doveadm_moduledir)" \ + "$(DESTDIR)$(moduledir)" +LTLIBRARIES = $(doveadm_module_LTLIBRARIES) $(module_LTLIBRARIES) +@DOVECOT_PLUGIN_DEPS_TRUE@lib05_mail_crypt_acl_plugin_la_DEPENDENCIES = \ +@DOVECOT_PLUGIN_DEPS_TRUE@ lib10_mail_crypt_plugin.la +am_lib05_mail_crypt_acl_plugin_la_OBJECTS = mail-crypt-acl-plugin.lo +lib05_mail_crypt_acl_plugin_la_OBJECTS = \ + $(am_lib05_mail_crypt_acl_plugin_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +lib05_mail_crypt_acl_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(lib05_mail_crypt_acl_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ +am__DEPENDENCIES_1 = +lib10_mail_crypt_plugin_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_lib10_mail_crypt_plugin_la_OBJECTS = mail-crypt-global-key.lo \ + mail-crypt-userenv.lo mail-crypt-key.lo mail-crypt-plugin.lo +lib10_mail_crypt_plugin_la_OBJECTS = \ + $(am_lib10_mail_crypt_plugin_la_OBJECTS) +lib10_mail_crypt_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(lib10_mail_crypt_plugin_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +am_libdoveadm_mail_crypt_plugin_la_OBJECTS = doveadm-mail-crypt.lo +libdoveadm_mail_crypt_plugin_la_OBJECTS = \ + $(am_libdoveadm_mail_crypt_plugin_la_OBJECTS) +libdoveadm_mail_crypt_plugin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(libdoveadm_mail_crypt_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ +am_libfs_crypt_la_OBJECTS = fs-crypt.lo mail-crypt-global-key.lo \ + mail-crypt-pluginenv.lo fs-crypt-settings.lo +libfs_crypt_la_OBJECTS = $(am_libfs_crypt_la_OBJECTS) +libfs_crypt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libfs_crypt_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +am_libfs_mail_crypt_la_OBJECTS = fs-mail-crypt.lo \ + mail-crypt-global-key.lo mail-crypt-userenv.lo +libfs_mail_crypt_la_OBJECTS = $(am_libfs_mail_crypt_la_OBJECTS) +libfs_mail_crypt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libfs_mail_crypt_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +am_test_mail_global_key_OBJECTS = \ + test_mail_global_key-test-mail-global-key.$(OBJEXT) \ + test_mail_global_key-fs-crypt-settings.$(OBJEXT) \ + test_mail_global_key-mail-crypt-global-key.$(OBJEXT) +test_mail_global_key_OBJECTS = $(am_test_mail_global_key_OBJECTS) +test_mail_global_key_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(test_mail_global_key_CFLAGS) $(CFLAGS) \ + $(test_mail_global_key_LDFLAGS) $(LDFLAGS) -o $@ +am_test_mail_key_OBJECTS = test_mail_key-test-mail-key.$(OBJEXT) \ + test_mail_key-mail-crypt-key.$(OBJEXT) \ + test_mail_key-mail-crypt-global-key.$(OBJEXT) \ + test_mail_key-mail-crypt-userenv.$(OBJEXT) +test_mail_key_OBJECTS = $(am_test_mail_key_OBJECTS) +test_mail_key_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_mail_key_CFLAGS) \ + $(CFLAGS) $(test_mail_key_LDFLAGS) $(LDFLAGS) -o $@ +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)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/doveadm-mail-crypt.Plo \ + ./$(DEPDIR)/fs-crypt-settings.Plo ./$(DEPDIR)/fs-crypt.Plo \ + ./$(DEPDIR)/fs-mail-crypt.Plo \ + ./$(DEPDIR)/mail-crypt-acl-plugin.Plo \ + ./$(DEPDIR)/mail-crypt-global-key.Plo \ + ./$(DEPDIR)/mail-crypt-key.Plo \ + ./$(DEPDIR)/mail-crypt-plugin.Plo \ + ./$(DEPDIR)/mail-crypt-pluginenv.Plo \ + ./$(DEPDIR)/mail-crypt-userenv.Plo \ + ./$(DEPDIR)/test_mail_global_key-fs-crypt-settings.Po \ + ./$(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Po \ + ./$(DEPDIR)/test_mail_global_key-test-mail-global-key.Po \ + ./$(DEPDIR)/test_mail_key-mail-crypt-global-key.Po \ + ./$(DEPDIR)/test_mail_key-mail-crypt-key.Po \ + ./$(DEPDIR)/test_mail_key-mail-crypt-userenv.Po \ + ./$(DEPDIR)/test_mail_key-test-mail-key.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=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 = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=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 = $(lib05_mail_crypt_acl_plugin_la_SOURCES) \ + $(lib10_mail_crypt_plugin_la_SOURCES) \ + $(libdoveadm_mail_crypt_plugin_la_SOURCES) \ + $(libfs_crypt_la_SOURCES) $(libfs_mail_crypt_la_SOURCES) \ + $(test_mail_global_key_SOURCES) $(test_mail_key_SOURCES) +DIST_SOURCES = $(lib05_mail_crypt_acl_plugin_la_SOURCES) \ + $(lib10_mail_crypt_plugin_la_SOURCES) \ + $(libdoveadm_mail_crypt_plugin_la_SOURCES) \ + $(libfs_crypt_la_SOURCES) $(libfs_mail_crypt_la_SOURCES) \ + $(test_mail_global_key_SOURCES) $(test_mail_key_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +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__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_LIBS = @APPARMOR_LIBS@ +AR = @AR@ +AUTH_CFLAGS = @AUTH_CFLAGS@ +AUTH_LIBS = @AUTH_LIBS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BINARY_CFLAGS = @BINARY_CFLAGS@ +BINARY_LDFLAGS = @BINARY_LDFLAGS@ +BISON = @BISON@ +CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ +CASSANDRA_LIBS = @CASSANDRA_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CDB_LIBS = @CDB_LIBS@ +CFLAGS = @CFLAGS@ +CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ +CLUCENE_LIBS = @CLUCENE_LIBS@ +COMPRESS_LIBS = @COMPRESS_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPT_LIBS = @CRYPT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DICT_LIBS = @DICT_LIBS@ +DLLIB = @DLLIB@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FLEX = @FLEX@ +FUZZER_CPPFLAGS = @FUZZER_CPPFLAGS@ +FUZZER_LDFLAGS = @FUZZER_LDFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KRB5CONFIG = @KRB5CONFIG@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_LIBS = @KRB5_LIBS@ +LD = @LD@ +LDAP_LIBS = @LDAP_LIBS@ +LDFLAGS = @LDFLAGS@ +LD_NO_WHOLE_ARCHIVE = @LD_NO_WHOLE_ARCHIVE@ +LD_WHOLE_ARCHIVE = @LD_WHOLE_ARCHIVE@ +LIBCAP = @LIBCAP@ +LIBDOVECOT = @LIBDOVECOT@ +LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ +LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ +LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ +LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ +LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ +LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ +LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ +LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ +LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ +LIBDOVECOT_LUA = @LIBDOVECOT_LUA@ +LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@ +LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ +LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ +LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ +LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ +LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ +LIBICONV = @LIBICONV@ +LIBICU_CFLAGS = @LIBICU_CFLAGS@ +LIBICU_LIBS = @LIBICU_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSODIUM_CFLAGS = @LIBSODIUM_CFLAGS@ +LIBSODIUM_LIBS = @LIBSODIUM_LIBS@ +LIBTIRPC_CFLAGS = @LIBTIRPC_CFLAGS@ +LIBTIRPC_LIBS = @LIBTIRPC_LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@ +LIBUNWIND_LIBS = @LIBUNWIND_LIBS@ +LIBWRAP_LIBS = @LIBWRAP_LIBS@ +LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LUA_CFLAGS = @LUA_CFLAGS@ +LUA_LIBS = @LUA_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MODULE_LIBS = @MODULE_LIBS@ +MODULE_SUFFIX = @MODULE_SUFFIX@ +MYSQL_CFLAGS = @MYSQL_CFLAGS@ +MYSQL_CONFIG = @MYSQL_CONFIG@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NOPLUGIN_LDFLAGS = +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PANDOC = @PANDOC@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PGSQL_CFLAGS = @PGSQL_CFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PG_CONFIG = @PG_CONFIG@ +PIE_CFLAGS = @PIE_CFLAGS@ +PIE_LDFLAGS = @PIE_LDFLAGS@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +QUOTA_LIBS = @QUOTA_LIBS@ +RANLIB = @RANLIB@ +RELRO_LDFLAGS = @RELRO_LDFLAGS@ +RPCGEN = @RPCGEN@ +RUN_TEST = @RUN_TEST@ +SED = @SED@ +SETTING_FILES = @SETTING_FILES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SQLITE_CFLAGS = @SQLITE_CFLAGS@ +SQLITE_LIBS = @SQLITE_LIBS@ +SQL_CFLAGS = @SQL_CFLAGS@ +SQL_LIBS = @SQL_LIBS@ +SSL_CFLAGS = @SSL_CFLAGS@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +ZSTD_CFLAGS = @ZSTD_CFLAGS@ +ZSTD_LIBS = @ZSTD_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +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@ +dict_drivers = @dict_drivers@ +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 = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +moduledir = @moduledir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rundir = @rundir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sql_drivers = @sql_drivers@ +srcdir = @srcdir@ +ssldir = @ssldir@ +statedir = @statedir@ +sysconfdir = @sysconfdir@ +systemdservicetype = @systemdservicetype@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test \ + -I$(top_srcdir)/src/lib-settings \ + -I$(top_srcdir)/src/lib-master \ + -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-dict \ + -I$(top_srcdir)/src/lib-index \ + -I$(top_srcdir)/src/lib-storage/index \ + -I$(top_srcdir)/src/lib-storage \ + -I$(top_srcdir)/src/lib-dcrypt \ + -I$(top_srcdir)/src/lib-fs \ + -I$(top_srcdir)/src/doveadm \ + -I$(top_srcdir)/src/plugins/acl + +@SSL_VERSION_GE_102_FALSE@test_options = NOUNDEF=1 +@SSL_VERSION_GE_102_TRUE@test_options = +doveadm_moduledir = $(moduledir)/doveadm +module_LTLIBRARIES = \ + lib10_mail_crypt_plugin.la \ + lib05_mail_crypt_acl_plugin.la \ + libfs_crypt.la \ + libfs_mail_crypt.la + +doveadm_module_LTLIBRARIES = \ + libdoveadm_mail_crypt_plugin.la + +lib10_mail_crypt_plugin_la_LDFLAGS = -module -avoid-version +lib10_mail_crypt_plugin_la_LIBADD = \ + $(LIBDCRYPT_LIBS) \ + $(LIBDOVECOT) + +lib05_mail_crypt_acl_plugin_la_LDFLAGS = -module -avoid-version +@DOVECOT_PLUGIN_DEPS_TRUE@lib05_mail_crypt_acl_plugin_la_LIBADD = \ +@DOVECOT_PLUGIN_DEPS_TRUE@ $(LIBDCRYPT_LIBS) \ +@DOVECOT_PLUGIN_DEPS_TRUE@ lib10_mail_crypt_plugin.la + +lib10_mail_crypt_plugin_la_SOURCES = \ + mail-crypt-global-key.c \ + mail-crypt-userenv.c \ + mail-crypt-key.c \ + mail-crypt-plugin.c + +lib05_mail_crypt_acl_plugin_la_SOURCES = \ + mail-crypt-acl-plugin.c + +libfs_crypt_la_SOURCES = fs-crypt.c \ + mail-crypt-global-key.c \ + mail-crypt-pluginenv.c \ + fs-crypt-settings.c + +libfs_crypt_la_LIBADD = $(LIBDOVECOT) +libfs_crypt_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) +libfs_crypt_la_LDFLAGS = -module -avoid-version +libfs_mail_crypt_la_SOURCES = fs-mail-crypt.c \ + mail-crypt-global-key.c \ + mail-crypt-userenv.c + +libfs_mail_crypt_la_LIBADD = $(LIBDOVECOT) +libfs_mail_crypt_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) +libfs_mail_crypt_la_LDFLAGS = -module -avoid-version +libdoveadm_mail_crypt_plugin_la_SOURCES = \ + doveadm-mail-crypt.c + +libdoveadm_mail_crypt_plugin_la_LIBADD = $(LIBDOVECOT) +libdoveadm_mail_crypt_plugin_la_DEPENDENCIES = $(LIBDOVECOT_DEPS) +libdoveadm_mail_crypt_plugin_la_LDFLAGS = -module -avoid-version +test_programs = \ + test-mail-global-key \ + test-mail-key + +test_mail_global_key_SOURCES = \ + test-mail-global-key.c \ + fs-crypt-settings.c \ + mail-crypt-global-key.c + +test_mail_global_key_LDADD = $(LIBDOVECOT) +test_mail_global_key_DEPENDENCIES = $(LIBDOVECOT_DEPS) +test_mail_global_key_LDFLAGS = $(DOVECOT_BINARY_LDFLAGS) +test_mail_global_key_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\" +test_mail_key_SOURCES = \ + test-mail-key.c \ + mail-crypt-key.c \ + mail-crypt-global-key.c \ + mail-crypt-userenv.c + +test_mail_key_LDADD = $(LIBDOVECOT_STORAGE) $(LIBDOVECOT) +test_mail_key_DEPENDENCIES = $(LIBDOVECOT_DEPS) $(LIBDOVECOT_STORAGE_DEPS) +test_mail_key_LDFLAGS = $(DOVECOT_BINARY_LDFLAGS) +test_mail_key_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\" +EXTRA_DIST = fs-crypt-common.c +noinst_HEADERS = \ + mail-crypt-plugin.h \ + mail-crypt-common.h \ + mail-crypt-global-key.h \ + mail-crypt-key.h \ + fs-crypt-settings.h + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.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) --foreign src/plugins/mail-crypt/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/plugins/mail-crypt/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_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): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +install-doveadm_moduleLTLIBRARIES: $(doveadm_module_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(doveadm_moduledir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(doveadm_moduledir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(doveadm_moduledir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(doveadm_moduledir)"; \ + } + +uninstall-doveadm_moduleLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(doveadm_module_LTLIBRARIES)'; test -n "$(doveadm_moduledir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(doveadm_moduledir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(doveadm_moduledir)/$$f"; \ + done + +clean-doveadm_moduleLTLIBRARIES: + -test -z "$(doveadm_module_LTLIBRARIES)" || rm -f $(doveadm_module_LTLIBRARIES) + @list='$(doveadm_module_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +install-moduleLTLIBRARIES: $(module_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(moduledir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(moduledir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(moduledir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(moduledir)"; \ + } + +uninstall-moduleLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(module_LTLIBRARIES)'; test -n "$(moduledir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(moduledir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(moduledir)/$$f"; \ + done + +clean-moduleLTLIBRARIES: + -test -z "$(module_LTLIBRARIES)" || rm -f $(module_LTLIBRARIES) + @list='$(module_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +lib05_mail_crypt_acl_plugin.la: $(lib05_mail_crypt_acl_plugin_la_OBJECTS) $(lib05_mail_crypt_acl_plugin_la_DEPENDENCIES) $(EXTRA_lib05_mail_crypt_acl_plugin_la_DEPENDENCIES) + $(AM_V_CCLD)$(lib05_mail_crypt_acl_plugin_la_LINK) -rpath $(moduledir) $(lib05_mail_crypt_acl_plugin_la_OBJECTS) $(lib05_mail_crypt_acl_plugin_la_LIBADD) $(LIBS) + +lib10_mail_crypt_plugin.la: $(lib10_mail_crypt_plugin_la_OBJECTS) $(lib10_mail_crypt_plugin_la_DEPENDENCIES) $(EXTRA_lib10_mail_crypt_plugin_la_DEPENDENCIES) + $(AM_V_CCLD)$(lib10_mail_crypt_plugin_la_LINK) -rpath $(moduledir) $(lib10_mail_crypt_plugin_la_OBJECTS) $(lib10_mail_crypt_plugin_la_LIBADD) $(LIBS) + +libdoveadm_mail_crypt_plugin.la: $(libdoveadm_mail_crypt_plugin_la_OBJECTS) $(libdoveadm_mail_crypt_plugin_la_DEPENDENCIES) $(EXTRA_libdoveadm_mail_crypt_plugin_la_DEPENDENCIES) + $(AM_V_CCLD)$(libdoveadm_mail_crypt_plugin_la_LINK) -rpath $(doveadm_moduledir) $(libdoveadm_mail_crypt_plugin_la_OBJECTS) $(libdoveadm_mail_crypt_plugin_la_LIBADD) $(LIBS) + +libfs_crypt.la: $(libfs_crypt_la_OBJECTS) $(libfs_crypt_la_DEPENDENCIES) $(EXTRA_libfs_crypt_la_DEPENDENCIES) + $(AM_V_CCLD)$(libfs_crypt_la_LINK) -rpath $(moduledir) $(libfs_crypt_la_OBJECTS) $(libfs_crypt_la_LIBADD) $(LIBS) + +libfs_mail_crypt.la: $(libfs_mail_crypt_la_OBJECTS) $(libfs_mail_crypt_la_DEPENDENCIES) $(EXTRA_libfs_mail_crypt_la_DEPENDENCIES) + $(AM_V_CCLD)$(libfs_mail_crypt_la_LINK) -rpath $(moduledir) $(libfs_mail_crypt_la_OBJECTS) $(libfs_mail_crypt_la_LIBADD) $(LIBS) + +test-mail-global-key$(EXEEXT): $(test_mail_global_key_OBJECTS) $(test_mail_global_key_DEPENDENCIES) $(EXTRA_test_mail_global_key_DEPENDENCIES) + @rm -f test-mail-global-key$(EXEEXT) + $(AM_V_CCLD)$(test_mail_global_key_LINK) $(test_mail_global_key_OBJECTS) $(test_mail_global_key_LDADD) $(LIBS) + +test-mail-key$(EXEEXT): $(test_mail_key_OBJECTS) $(test_mail_key_DEPENDENCIES) $(EXTRA_test_mail_key_DEPENDENCIES) + @rm -f test-mail-key$(EXEEXT) + $(AM_V_CCLD)$(test_mail_key_LINK) $(test_mail_key_OBJECTS) $(test_mail_key_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doveadm-mail-crypt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-crypt-settings.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-crypt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-mail-crypt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-acl-plugin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-global-key.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-key.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-plugin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-pluginenv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail-crypt-userenv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_global_key-fs-crypt-settings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_global_key-test-mail-global-key.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_key-mail-crypt-global-key.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_key-mail-crypt-key.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_key-mail-crypt-userenv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mail_key-test-mail-key.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) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +test_mail_global_key-test-mail-global-key.o: test-mail-global-key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-test-mail-global-key.o -MD -MP -MF $(DEPDIR)/test_mail_global_key-test-mail-global-key.Tpo -c -o test_mail_global_key-test-mail-global-key.o `test -f 'test-mail-global-key.c' || echo '$(srcdir)/'`test-mail-global-key.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-test-mail-global-key.Tpo $(DEPDIR)/test_mail_global_key-test-mail-global-key.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mail-global-key.c' object='test_mail_global_key-test-mail-global-key.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-test-mail-global-key.o `test -f 'test-mail-global-key.c' || echo '$(srcdir)/'`test-mail-global-key.c + +test_mail_global_key-test-mail-global-key.obj: test-mail-global-key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-test-mail-global-key.obj -MD -MP -MF $(DEPDIR)/test_mail_global_key-test-mail-global-key.Tpo -c -o test_mail_global_key-test-mail-global-key.obj `if test -f 'test-mail-global-key.c'; then $(CYGPATH_W) 'test-mail-global-key.c'; else $(CYGPATH_W) '$(srcdir)/test-mail-global-key.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-test-mail-global-key.Tpo $(DEPDIR)/test_mail_global_key-test-mail-global-key.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mail-global-key.c' object='test_mail_global_key-test-mail-global-key.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-test-mail-global-key.obj `if test -f 'test-mail-global-key.c'; then $(CYGPATH_W) 'test-mail-global-key.c'; else $(CYGPATH_W) '$(srcdir)/test-mail-global-key.c'; fi` + +test_mail_global_key-fs-crypt-settings.o: fs-crypt-settings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-fs-crypt-settings.o -MD -MP -MF $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Tpo -c -o test_mail_global_key-fs-crypt-settings.o `test -f 'fs-crypt-settings.c' || echo '$(srcdir)/'`fs-crypt-settings.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Tpo $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fs-crypt-settings.c' object='test_mail_global_key-fs-crypt-settings.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-fs-crypt-settings.o `test -f 'fs-crypt-settings.c' || echo '$(srcdir)/'`fs-crypt-settings.c + +test_mail_global_key-fs-crypt-settings.obj: fs-crypt-settings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-fs-crypt-settings.obj -MD -MP -MF $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Tpo -c -o test_mail_global_key-fs-crypt-settings.obj `if test -f 'fs-crypt-settings.c'; then $(CYGPATH_W) 'fs-crypt-settings.c'; else $(CYGPATH_W) '$(srcdir)/fs-crypt-settings.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Tpo $(DEPDIR)/test_mail_global_key-fs-crypt-settings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fs-crypt-settings.c' object='test_mail_global_key-fs-crypt-settings.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-fs-crypt-settings.obj `if test -f 'fs-crypt-settings.c'; then $(CYGPATH_W) 'fs-crypt-settings.c'; else $(CYGPATH_W) '$(srcdir)/fs-crypt-settings.c'; fi` + +test_mail_global_key-mail-crypt-global-key.o: mail-crypt-global-key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-mail-crypt-global-key.o -MD -MP -MF $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Tpo -c -o test_mail_global_key-mail-crypt-global-key.o `test -f 'mail-crypt-global-key.c' || echo '$(srcdir)/'`mail-crypt-global-key.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Tpo $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-global-key.c' object='test_mail_global_key-mail-crypt-global-key.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-mail-crypt-global-key.o `test -f 'mail-crypt-global-key.c' || echo '$(srcdir)/'`mail-crypt-global-key.c + +test_mail_global_key-mail-crypt-global-key.obj: mail-crypt-global-key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -MT test_mail_global_key-mail-crypt-global-key.obj -MD -MP -MF $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Tpo -c -o test_mail_global_key-mail-crypt-global-key.obj `if test -f 'mail-crypt-global-key.c'; then $(CYGPATH_W) 'mail-crypt-global-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-global-key.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Tpo $(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-global-key.c' object='test_mail_global_key-mail-crypt-global-key.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_global_key_CFLAGS) $(CFLAGS) -c -o test_mail_global_key-mail-crypt-global-key.obj `if test -f 'mail-crypt-global-key.c'; then $(CYGPATH_W) 'mail-crypt-global-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-global-key.c'; fi` + +test_mail_key-test-mail-key.o: test-mail-key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-test-mail-key.o -MD -MP -MF $(DEPDIR)/test_mail_key-test-mail-key.Tpo -c -o test_mail_key-test-mail-key.o `test -f 'test-mail-key.c' || echo '$(srcdir)/'`test-mail-key.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-test-mail-key.Tpo $(DEPDIR)/test_mail_key-test-mail-key.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mail-key.c' object='test_mail_key-test-mail-key.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-test-mail-key.o `test -f 'test-mail-key.c' || echo '$(srcdir)/'`test-mail-key.c + +test_mail_key-test-mail-key.obj: test-mail-key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-test-mail-key.obj -MD -MP -MF $(DEPDIR)/test_mail_key-test-mail-key.Tpo -c -o test_mail_key-test-mail-key.obj `if test -f 'test-mail-key.c'; then $(CYGPATH_W) 'test-mail-key.c'; else $(CYGPATH_W) '$(srcdir)/test-mail-key.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-test-mail-key.Tpo $(DEPDIR)/test_mail_key-test-mail-key.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-mail-key.c' object='test_mail_key-test-mail-key.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-test-mail-key.obj `if test -f 'test-mail-key.c'; then $(CYGPATH_W) 'test-mail-key.c'; else $(CYGPATH_W) '$(srcdir)/test-mail-key.c'; fi` + +test_mail_key-mail-crypt-key.o: mail-crypt-key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-key.o -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-key.Tpo -c -o test_mail_key-mail-crypt-key.o `test -f 'mail-crypt-key.c' || echo '$(srcdir)/'`mail-crypt-key.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-key.Tpo $(DEPDIR)/test_mail_key-mail-crypt-key.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-key.c' object='test_mail_key-mail-crypt-key.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-key.o `test -f 'mail-crypt-key.c' || echo '$(srcdir)/'`mail-crypt-key.c + +test_mail_key-mail-crypt-key.obj: mail-crypt-key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-key.obj -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-key.Tpo -c -o test_mail_key-mail-crypt-key.obj `if test -f 'mail-crypt-key.c'; then $(CYGPATH_W) 'mail-crypt-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-key.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-key.Tpo $(DEPDIR)/test_mail_key-mail-crypt-key.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-key.c' object='test_mail_key-mail-crypt-key.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-key.obj `if test -f 'mail-crypt-key.c'; then $(CYGPATH_W) 'mail-crypt-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-key.c'; fi` + +test_mail_key-mail-crypt-global-key.o: mail-crypt-global-key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-global-key.o -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-global-key.Tpo -c -o test_mail_key-mail-crypt-global-key.o `test -f 'mail-crypt-global-key.c' || echo '$(srcdir)/'`mail-crypt-global-key.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-global-key.Tpo $(DEPDIR)/test_mail_key-mail-crypt-global-key.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-global-key.c' object='test_mail_key-mail-crypt-global-key.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-global-key.o `test -f 'mail-crypt-global-key.c' || echo '$(srcdir)/'`mail-crypt-global-key.c + +test_mail_key-mail-crypt-global-key.obj: mail-crypt-global-key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-global-key.obj -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-global-key.Tpo -c -o test_mail_key-mail-crypt-global-key.obj `if test -f 'mail-crypt-global-key.c'; then $(CYGPATH_W) 'mail-crypt-global-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-global-key.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-global-key.Tpo $(DEPDIR)/test_mail_key-mail-crypt-global-key.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-global-key.c' object='test_mail_key-mail-crypt-global-key.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-global-key.obj `if test -f 'mail-crypt-global-key.c'; then $(CYGPATH_W) 'mail-crypt-global-key.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-global-key.c'; fi` + +test_mail_key-mail-crypt-userenv.o: mail-crypt-userenv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-userenv.o -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-userenv.Tpo -c -o test_mail_key-mail-crypt-userenv.o `test -f 'mail-crypt-userenv.c' || echo '$(srcdir)/'`mail-crypt-userenv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-userenv.Tpo $(DEPDIR)/test_mail_key-mail-crypt-userenv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-userenv.c' object='test_mail_key-mail-crypt-userenv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-userenv.o `test -f 'mail-crypt-userenv.c' || echo '$(srcdir)/'`mail-crypt-userenv.c + +test_mail_key-mail-crypt-userenv.obj: mail-crypt-userenv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -MT test_mail_key-mail-crypt-userenv.obj -MD -MP -MF $(DEPDIR)/test_mail_key-mail-crypt-userenv.Tpo -c -o test_mail_key-mail-crypt-userenv.obj `if test -f 'mail-crypt-userenv.c'; then $(CYGPATH_W) 'mail-crypt-userenv.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-userenv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_mail_key-mail-crypt-userenv.Tpo $(DEPDIR)/test_mail_key-mail-crypt-userenv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mail-crypt-userenv.c' object='test_mail_key-mail-crypt-userenv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_mail_key_CFLAGS) $(CFLAGS) -c -o test_mail_key-mail-crypt-userenv.obj `if test -f 'mail-crypt-userenv.c'; then $(CYGPATH_W) 'mail-crypt-userenv.c'; else $(CYGPATH_W) '$(srcdir)/mail-crypt-userenv.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +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 + +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-local +check: check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(doveadm_moduledir)" "$(DESTDIR)$(moduledir)"; 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-doveadm_moduleLTLIBRARIES clean-generic clean-libtool \ + clean-moduleLTLIBRARIES clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/doveadm-mail-crypt.Plo + -rm -f ./$(DEPDIR)/fs-crypt-settings.Plo + -rm -f ./$(DEPDIR)/fs-crypt.Plo + -rm -f ./$(DEPDIR)/fs-mail-crypt.Plo + -rm -f ./$(DEPDIR)/mail-crypt-acl-plugin.Plo + -rm -f ./$(DEPDIR)/mail-crypt-global-key.Plo + -rm -f ./$(DEPDIR)/mail-crypt-key.Plo + -rm -f ./$(DEPDIR)/mail-crypt-plugin.Plo + -rm -f ./$(DEPDIR)/mail-crypt-pluginenv.Plo + -rm -f ./$(DEPDIR)/mail-crypt-userenv.Plo + -rm -f ./$(DEPDIR)/test_mail_global_key-fs-crypt-settings.Po + -rm -f ./$(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Po + -rm -f ./$(DEPDIR)/test_mail_global_key-test-mail-global-key.Po + -rm -f ./$(DEPDIR)/test_mail_key-mail-crypt-global-key.Po + -rm -f ./$(DEPDIR)/test_mail_key-mail-crypt-key.Po + -rm -f ./$(DEPDIR)/test_mail_key-mail-crypt-userenv.Po + -rm -f ./$(DEPDIR)/test_mail_key-test-mail-key.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-doveadm_moduleLTLIBRARIES \ + install-moduleLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +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)/doveadm-mail-crypt.Plo + -rm -f ./$(DEPDIR)/fs-crypt-settings.Plo + -rm -f ./$(DEPDIR)/fs-crypt.Plo + -rm -f ./$(DEPDIR)/fs-mail-crypt.Plo + -rm -f ./$(DEPDIR)/mail-crypt-acl-plugin.Plo + -rm -f ./$(DEPDIR)/mail-crypt-global-key.Plo + -rm -f ./$(DEPDIR)/mail-crypt-key.Plo + -rm -f ./$(DEPDIR)/mail-crypt-plugin.Plo + -rm -f ./$(DEPDIR)/mail-crypt-pluginenv.Plo + -rm -f ./$(DEPDIR)/mail-crypt-userenv.Plo + -rm -f ./$(DEPDIR)/test_mail_global_key-fs-crypt-settings.Po + -rm -f ./$(DEPDIR)/test_mail_global_key-mail-crypt-global-key.Po + -rm -f ./$(DEPDIR)/test_mail_global_key-test-mail-global-key.Po + -rm -f ./$(DEPDIR)/test_mail_key-mail-crypt-global-key.Po + -rm -f ./$(DEPDIR)/test_mail_key-mail-crypt-key.Po + -rm -f ./$(DEPDIR)/test_mail_key-mail-crypt-userenv.Po + -rm -f ./$(DEPDIR)/test_mail_key-test-mail-key.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-doveadm_moduleLTLIBRARIES \ + uninstall-moduleLTLIBRARIES + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am \ + check-local clean clean-doveadm_moduleLTLIBRARIES \ + clean-generic clean-libtool clean-moduleLTLIBRARIES \ + clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-doveadm_moduleLTLIBRARIES install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man \ + install-moduleLTLIBRARIES install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-doveadm_moduleLTLIBRARIES \ + uninstall-moduleLTLIBRARIES + +.PRECIOUS: Makefile + + +check-local: + for bin in $(test_programs); do \ + if ! env $(test_options) $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done + +# 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/src/plugins/mail-crypt/doveadm-mail-crypt.c b/src/plugins/mail-crypt/doveadm-mail-crypt.c new file mode 100644 index 0000000..a4322ed --- /dev/null +++ b/src/plugins/mail-crypt/doveadm-mail-crypt.c @@ -0,0 +1,1048 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "askpass.h" +#include "doveadm-mail.h" +#include "getopt.h" +#include "array.h" +#include "str.h" +#include "buffer.h" +#include "ioloop.h" +#include "ioloop-private.h" +#include "mail-namespace.h" +#include "mail-storage.h" +#include "mail-storage-settings.h" +#include "mailbox-attribute.h" +#include "mail-crypt-common.h" +#include "mail-crypt-key.h" +#include "mailbox-list-iter.h" +#include "doveadm-print.h" +#include "hex-binary.h" + +#define DOVEADM_MCP_SUCCESS "\xE2\x9C\x93" /* emits a utf-8 CHECK MARK (U+2713) */ +#define DOVEADM_MCP_FAIL "x" +#define DOVEADM_MCP_USERKEY "<userkey>" + +struct generated_key { + const char *name; + const char *id; + const char *error; + struct mailbox *box; + bool success:1; + bool active:1; +}; + +ARRAY_DEFINE_TYPE(generated_keys, struct generated_key); + +struct mcp_cmd_context { + struct doveadm_mail_cmd_context ctx; + + const char *old_password; + const char *new_password; + + unsigned int matched_keys; + + bool userkey_only:1; + bool recrypt_box_keys:1; + bool force:1; + bool ask_old_password:1; + bool ask_new_password:1; + bool clear_password:1; +}; + +struct mcp_key_iter_ctx { + pool_t pool; + ARRAY_TYPE(generated_keys) keys; +}; + +void doveadm_mail_crypt_plugin_init(struct module *mod ATTR_UNUSED); +void doveadm_mail_crypt_plugin_deinit(void); + +static int +mcp_user_create(struct mail_user *user, const char *dest_username, + struct mail_user **dest_user_r, + struct mail_storage_service_user **dest_service_user_r, + const char **error_r) +{ + const struct mail_storage_service_input *old_input; + struct mail_storage_service_input input; + struct mail_storage_service_ctx *service_ctx; + struct ioloop_context *cur_ioloop_ctx; + + int ret; + + i_assert(user->_service_user != NULL); + service_ctx = mail_storage_service_user_get_service_ctx(user->_service_user); + old_input = mail_storage_service_user_get_input(user->_service_user); + + if ((cur_ioloop_ctx = io_loop_get_current_context(current_ioloop)) != NULL) + io_loop_context_deactivate(cur_ioloop_ctx); + + i_zero(&input); + input.module = old_input->module; + input.service = old_input->service; + input.username = dest_username; + input.session_id_prefix = user->session_id; + input.flags_override_add = MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS | + MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT; + + ret = mail_storage_service_lookup_next(service_ctx, &input, + dest_service_user_r, + dest_user_r, error_r); + + if (ret == 0) + *error_r = "User not found"; + + return ret; +} + +static int +mcp_update_shared_key(struct mailbox_transaction_context *t, + struct mail_user *user, const char *target_uid, + struct dcrypt_private_key *key, const char **error_r) +{ + const char *error; + struct mail_user *dest_user; + struct mail_storage_service_user *dest_service_user; + struct ioloop_context *cur_ioloop_ctx; + struct dcrypt_public_key *pkey; + const char *dest_username; + int ret = 0; + + bool disallow_insecure = mail_crypt_acl_secure_sharing_enabled(user); + + ret = mcp_user_create(user, target_uid, &dest_user, + &dest_service_user, &error); + + /* to make sure we get correct logging context */ + if (ret > 0) + mail_storage_service_io_deactivate_user(dest_service_user); + mail_storage_service_io_activate_user(user->_service_user); + + if (ret <= 0) { + i_error("Cannot initialize destination user %s: %s", + target_uid, error); + return ret; + } else { + i_assert(dest_user != NULL); + dest_username = dest_user->username; + + /* get public key from target user */ + if ((ret = mail_crypt_user_get_public_key(dest_user, + &pkey, error_r)) <= 0) { + if (ret == 0 && disallow_insecure) { + *error_r = t_strdup_printf("User %s has no active public key", + dest_user->username); + ret = -1; + } else if (ret == 0) { + /* perform insecure sharing */ + dest_username = NULL; + pkey = NULL; + ret = 1; + } + } + + if (ret == 1) { + ARRAY_TYPE(dcrypt_private_key) keys; + t_array_init(&keys, 1); + array_push_back(&keys, &key); + ret = mail_crypt_box_share_private_keys(t, pkey, + dest_username, + &keys, error_r); + } + + } + + /* logging context swap again */ + mail_storage_service_io_deactivate_user(user->_service_user); + mail_storage_service_io_activate_user(dest_service_user); + + mail_user_deinit(&dest_user); + mail_storage_service_user_unref(&dest_service_user); + + if ((cur_ioloop_ctx = io_loop_get_current_context(current_ioloop)) != NULL) + io_loop_context_deactivate(cur_ioloop_ctx); + + mail_storage_service_io_activate_user(user->_service_user); + + return ret; +} + +static int mcp_update_shared_keys(struct doveadm_mail_cmd_context *ctx, + struct mailbox *box, struct mail_user *user, + const char *pubid, struct dcrypt_private_key *key) +{ + const char *error; + int ret; + + ARRAY_TYPE(const_string) ids; + t_array_init(&ids, 8); + + /* figure out who needs the key */ + if (mail_crypt_box_get_pvt_digests(box, pool_datastack_create(), + MAIL_ATTRIBUTE_TYPE_SHARED, + &ids, &error) < 0) { + i_error("mail_crypt_box_get_pvt_digests(%s, /shared) failed: %s", + mailbox_get_vname(box), + error); + return -1; + } + + const char *id; + bool found = FALSE; + string_t *uid = t_str_new(64); + + struct mailbox_transaction_context *t = + mailbox_transaction_begin(box, ctx->transaction_flags, __func__); + + ret = 0; + + /* then perform sharing */ + array_foreach_elem(&ids, id) { + if (strchr(id, '/') != NULL) { + str_truncate(uid, 0); + const char *hexuid = t_strcut(id, '/'); + hex_to_binary(hexuid, uid); + if (mcp_update_shared_key(t, user, str_c(uid), key, + &error) < 0) { + i_error("mcp_update_shared_key(%s, %s) failed: %s", + mailbox_get_vname(box), + str_c(uid), + error); + ret = -1; + break; + } + } else if (!found) { + found = TRUE; + if (mail_crypt_box_set_shared_key(t, pubid, key, + NULL, NULL, + &error) < 0) { + i_error("mail_crypt_box_set_shared_key(%s) failed: %s", + mailbox_get_vname(box), + error); + ret = -1; + break; + } + } + } + + if (ret < 0) { + mailbox_transaction_rollback(&t); + } else if (mailbox_transaction_commit(&t) < 0) { + i_error("mailbox_transaction_commit(%s) failed: %s", + mailbox_get_vname(box), + error); + ret = -1; + } + + return ret; +} + +static int mcp_keypair_generate(struct mcp_cmd_context *ctx, + struct dcrypt_public_key *user_key, + struct mailbox *box, struct dcrypt_keypair *pair_r, + const char **pubid_r, const char **error_r) +{ + struct dcrypt_keypair pair = {NULL, NULL}; + + int ret; + + if ((ret = mail_crypt_box_get_public_key(box, &pair.pub, error_r)) < 0) { + ret = -1; + } else if (ret == 1 && !ctx->force) { + i_info("Folder key exists. Use -f to generate a new one"); + buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); + const char *error; + if (!dcrypt_key_id_public(pair.pub, + MAIL_CRYPT_KEY_ID_ALGORITHM, + key_id, &error)) { + i_error("dcrypt_key_id_public() failed: %s", + error); + return -1; + } + *pubid_r = p_strdup(ctx->ctx.pool, binary_to_hex(key_id->data, + key_id->used)); + *pair_r = pair; + return 1; + } else if (ret == 1 && ctx->recrypt_box_keys) { + /* do nothing, because force isn't being used *OR* + we are recrypting box keys and force refers to + user keypair. + + FIXME: this could be less confusing altogether */ + ret = 0; + } else { + if (mail_crypt_box_generate_keypair(box, &pair, user_key, + pubid_r, error_r) < 0) { + ret = -1; + } else { + *pubid_r = p_strdup(ctx->ctx.pool, *pubid_r); + *pair_r = pair; + return 1; + } + } + + if (pair.pub != NULL) + dcrypt_key_unref_public(&pair.pub); + if (pair.priv != NULL) + dcrypt_key_unref_private(&pair.priv); + + return ret; +} + +static int mcp_keypair_generate_run(struct doveadm_mail_cmd_context *_ctx, + struct mail_user *user, + ARRAY_TYPE(generated_keys) *result) +{ + const char *error; + int ret; + struct dcrypt_public_key *user_key; + struct mcp_cmd_context *ctx = + (struct mcp_cmd_context *)_ctx; + const char *pubid; + bool user_key_generated = FALSE; + struct generated_key *res; + + if ((ret = mail_crypt_user_get_public_key(user, &user_key, + &error)) <= 0) { + struct dcrypt_keypair pair; + if (ret < 0) { + i_error("mail_crypt_user_get_public_key(%s) failed: %s", + user->username, + error); + } else if (mail_crypt_user_generate_keypair(user, &pair, + &pubid, &error) < 0) { + ret = -1; + i_error("mail_crypt_user_generate_keypair(%s) failed: %s", + user->username, + error); + res = array_append_space(result); + res->name = ""; + res->error = p_strdup(_ctx->pool, error); + res->success = FALSE; + } else { + res = array_append_space(result); + res->name = DOVEADM_MCP_USERKEY; + res->id = p_strdup(_ctx->pool, pubid); + res->success = TRUE; + /* don't do it again later on */ + user_key_generated = TRUE; + ret = 1; + user_key = pair.pub; + dcrypt_key_unref_private(&pair.priv); + } + if (ret < 0) return ret; + ctx->matched_keys++; + } + if (ret == 1 && ctx->userkey_only && !user_key_generated) { + if (!ctx->force) { + i_info("userkey exists. Use -f to generate a new one"); + buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); + if (!dcrypt_key_id_public(user_key, + MAIL_CRYPT_KEY_ID_ALGORITHM, + key_id, &error)) { + i_error("dcrypt_key_id_public() failed: %s", + error); + dcrypt_key_unref_public(&user_key); + return -1; + } + const char *hash = binary_to_hex(key_id->data, + key_id->used); + res = array_append_space(result); + res->name = DOVEADM_MCP_USERKEY; + res->id = p_strdup(_ctx->pool, hash); + res->success = TRUE; + ctx->matched_keys++; + dcrypt_key_unref_public(&user_key); + return 1; + } + struct dcrypt_keypair pair; + dcrypt_key_unref_public(&user_key); + /* regen user key */ + res = array_append_space(result); + res->name = DOVEADM_MCP_USERKEY; + if (mail_crypt_user_generate_keypair(user, &pair, &pubid, + &error) < 0) { + res->success = FALSE; + res->error = p_strdup(_ctx->pool, error); + return -1; + } + res->success = TRUE; + res->id = p_strdup(_ctx->pool, pubid); + user_key = pair.pub; + dcrypt_key_unref_private(&pair.priv); + ctx->matched_keys++; + } + + if (ctx->userkey_only) { + dcrypt_key_unref_public(&user_key); + return 0; + } + + const char *const *patterns = (const char *const[]){ "*", NULL }; + + /* only re-encrypt all folder keys if wanted */ + if (!ctx->recrypt_box_keys) { + patterns = ctx->ctx.args; + } + + const struct mailbox_info *info; + struct mailbox_list_iterate_context *iter = + mailbox_list_iter_init_namespaces(user->namespaces, + patterns, + MAIL_NAMESPACE_TYPE_PRIVATE, + MAILBOX_LIST_ITER_SKIP_ALIASES | + MAILBOX_LIST_ITER_NO_AUTO_BOXES | + MAILBOX_LIST_ITER_RETURN_NO_FLAGS); + while((info = mailbox_list_iter_next(iter)) != NULL) { + if ((info->flags & MAILBOX_NOSELECT) != 0 || + (info->flags & MAILBOX_NONEXISTENT) != 0) continue; + struct dcrypt_keypair pair; + + struct mailbox *box = + mailbox_alloc(info->ns->list, + info->vname, 0); + if (mailbox_open(box) < 0) { + res = array_append_space(result); + res->name = p_strdup(_ctx->pool, info->vname); + res->success = FALSE; + res->error = p_strdup(_ctx->pool, + mailbox_get_last_internal_error(box, NULL)); + } else if ((ret = mcp_keypair_generate(ctx, user_key, box, + &pair, &pubid, + &error)) < 0) { + res = array_append_space(result); + res->name = p_strdup(_ctx->pool, info->vname); + res->success = FALSE; + res->error = p_strdup(_ctx->pool, error); + } else if (ret == 0) { + /* nothing happened because key already existed and + force wasn't used, skip */ + } else if (ret > 0) { + res = array_append_space(result); + res->name = p_strdup(_ctx->pool, info->vname); + res->success = TRUE; + res->id = pubid; + T_BEGIN { + mcp_update_shared_keys(&ctx->ctx, box, user, + pubid, pair.priv); + } T_END; + if (pair.pub != NULL) + dcrypt_key_unref_public(&pair.pub); + if (pair.priv != NULL) + dcrypt_key_unref_private(&pair.priv); + ctx->matched_keys++; + } + mailbox_free(&box); + } + + (void)mailbox_list_iter_deinit(&iter); + + dcrypt_key_unref_public(&user_key); + return 0; +} + +static int cmd_mcp_keypair_generate_run(struct doveadm_mail_cmd_context *_ctx, + struct mail_user *user) +{ + struct mcp_cmd_context *ctx = + (struct mcp_cmd_context *)_ctx; + + int ret = 0; + + ARRAY_TYPE(generated_keys) result; + p_array_init(&result, _ctx->pool, 8); + + if (mcp_keypair_generate_run(_ctx, user, &result) < 0) + _ctx->exit_code = EX_DATAERR; + + doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); + doveadm_print_header("success", " ", 0); + doveadm_print_header("box", "Folder", 0); + doveadm_print_header("pubid", "Public ID", 0); + + const struct generated_key *res; + + array_foreach(&result, res) { + if (res->success) + doveadm_print(DOVEADM_MCP_SUCCESS); + else { + _ctx->exit_code = EX_DATAERR; + ret = -1; + doveadm_print(DOVEADM_MCP_FAIL); + } + doveadm_print(res->name); + if (!res->success) + doveadm_print(t_strdup_printf("ERROR: %s", res->error)); + else + doveadm_print(res->id); + } + + if (ctx->matched_keys == 0) + i_warning("mailbox cryptokey generate: Nothing was matched. " + "Use -U or specify mask?"); + return ret; +} + +static void mcp_key_list(struct mcp_cmd_context *ctx, + struct mail_user *user, + void(*callback)(const struct generated_key *, void *), + void *context) +{ + const char *error; + + /* we need to use the mailbox attribute API here, as we + are not necessarily able to decrypt any of these keys + */ + + ARRAY_TYPE(const_string) ids; + t_array_init(&ids, 8); + + if (ctx->userkey_only) { + struct mailbox_attribute_iter *iter; + struct mail_namespace *ns = + mail_namespace_find_inbox(user->namespaces); + struct mailbox *box = + mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY); + struct mail_attribute_value value; + i_zero(&value); + + if (mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_SHARED, + USER_CRYPT_PREFIX ACTIVE_KEY_NAME, + &value) < 0) { + i_error("mailbox_get_attribute(%s, %s) failed: %s", + mailbox_get_vname(box), + USER_CRYPT_PREFIX ACTIVE_KEY_NAME, + mailbox_get_last_internal_error(box, NULL)); + } + + iter = mailbox_attribute_iter_init(box, + MAIL_ATTRIBUTE_TYPE_PRIVATE, + USER_CRYPT_PREFIX + PRIVKEYS_PREFIX); + const char *key_id; + if (value.value == NULL) + value.value = "<NO ACTIVE KEY>"; + while ((key_id = mailbox_attribute_iter_next(iter)) != NULL) { + struct generated_key key; + key.id = key_id; + key.active = strcmp(value.value, key_id) == 0; + key.name = ""; + key.box = box; + callback(&key, context); + ctx->matched_keys++; + } + if (mailbox_attribute_iter_deinit(&iter) < 0) + i_error("mailbox_attribute_iter_deinit(%s) failed: %s", + mailbox_get_vname(box), + mailbox_get_last_internal_error(box, NULL)); + mailbox_free(&box); + return; + } + + const struct mailbox_info *info; + struct mailbox_list_iterate_context *iter = + mailbox_list_iter_init_namespaces(user->namespaces, + ctx->ctx.args, + MAIL_NAMESPACE_TYPE_PRIVATE, + MAILBOX_LIST_ITER_SKIP_ALIASES | + MAILBOX_LIST_ITER_NO_AUTO_BOXES | + MAILBOX_LIST_ITER_RETURN_NO_FLAGS); + + while((info = mailbox_list_iter_next(iter)) != NULL) { + if ((info->flags & MAILBOX_NOSELECT) != 0 || + (info->flags & MAILBOX_NONEXISTENT) != 0) continue; + + struct mailbox *box = + mailbox_alloc(info->ns->list, + info->vname, MAILBOX_FLAG_READONLY); + struct mail_attribute_value value; + i_zero(&value); + array_clear(&ids); + + /* get active ID */ + if (mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_SHARED, + BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, + &value) < 0) { + i_error("mailbox_get_attribute(%s, %s) failed: %s", + mailbox_get_vname(box), + BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, + mailbox_get_last_internal_error(box, NULL)); + } else if (mail_crypt_box_get_pvt_digests(box, pool_datastack_create(), + MAIL_ATTRIBUTE_TYPE_PRIVATE, + &ids, &error) < 0) { + i_error("mail_crypt_box_get_pvt_digests(%s) failed: %s", + mailbox_get_vname(box), + error); + } else { + const char *id; + const char *boxname = mailbox_get_vname(box); + if (value.value == NULL) + value.value = "<NO ACTIVE KEY>"; + array_foreach_elem(&ids, id) { + struct generated_key key; + key.name = boxname; + key.id = id; + if (value.value != NULL) + key.active = strcmp(id, value.value) == 0; + else + key.active = FALSE; + key.box = box; + callback(&key, context); + ctx->matched_keys++; + } + } + mailbox_free(&box); + } + + (void)mailbox_list_iter_deinit(&iter); +} + +static void cmd_mcp_key_list_cb(const struct generated_key *_key, void *context) +{ + struct mcp_key_iter_ctx *ctx = context; + struct generated_key *key = array_append_space(&ctx->keys); + key->name = p_strdup(ctx->pool, _key->name); + key->id = p_strdup(ctx->pool, _key->id); + key->active = _key->active; +} + +static int cmd_mcp_key_list_run(struct doveadm_mail_cmd_context *_ctx, + struct mail_user *user) +{ + struct mcp_cmd_context *ctx = + (struct mcp_cmd_context *)_ctx; + struct mcp_key_iter_ctx iter_ctx; + i_zero(&iter_ctx); + iter_ctx.pool = _ctx->pool; + p_array_init(&iter_ctx.keys, _ctx->pool, 8); + + mcp_key_list(ctx, user, cmd_mcp_key_list_cb, &iter_ctx); + + doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); + doveadm_print_header("box", "Folder", 0); + doveadm_print_header("active", "Active", 0); + doveadm_print_header("pubid", "Public ID", 0); + + const struct generated_key *key; + array_foreach(&iter_ctx.keys, key) { + doveadm_print(key->name); + doveadm_print(key->active ? "yes" : "no"); + doveadm_print(key->id); + } + + if (ctx->matched_keys == 0) + i_warning("mailbox cryptokey list: Nothing was matched. " + "Use -U or specify mask?"); + + return 0; +} + +static void cmd_mcp_key_export_cb(const struct generated_key *key, + void *context ATTR_UNUSED) +{ + struct dcrypt_private_key *pkey; + bool user_key = FALSE; + const char *error = NULL; + int ret; + + if (*key->name == '\0') + user_key = TRUE; + + doveadm_print(key->name); + doveadm_print(key->id); + + if ((ret = mail_crypt_get_private_key(key->box, key->id, user_key, FALSE, + &pkey, &error)) <= 0) { + if (ret == 0) + error = "key not found"; + doveadm_print(t_strdup_printf("ERROR: %s", error)); + doveadm_print(""); + } else { + string_t *out = t_str_new(64); + if (!dcrypt_key_store_private(pkey, DCRYPT_FORMAT_PEM, NULL, out, + NULL, NULL, &error)) { + doveadm_print(t_strdup_printf("ERROR: %s", error)); + doveadm_print(""); + } else { + /* this is to make it more compatible with openssl cli + as it expects BEGIN on it's own line */ + doveadm_print(t_strdup_printf("\n%s", str_c(out))); + } + dcrypt_key_unref_private(&pkey); + } +} + +static int cmd_mcp_key_export_run(struct doveadm_mail_cmd_context *_ctx, + struct mail_user *user) +{ + struct mcp_cmd_context *ctx = + (struct mcp_cmd_context *)_ctx; + + doveadm_print_init(DOVEADM_PRINT_TYPE_PAGER); + doveadm_print_header("box", "Folder", 0); + doveadm_print_header("name", "Public ID", 0); + doveadm_print_header("error", "Error", 0); + doveadm_print_header("key", "Key", 0); + + mcp_key_list(ctx, user, cmd_mcp_key_export_cb, NULL); + + return 0; +} + +static int cmd_mcp_key_password_run(struct doveadm_mail_cmd_context *_ctx, + struct mail_user *user) +{ + struct mcp_cmd_context *ctx = + (struct mcp_cmd_context *)_ctx; + bool cli = (_ctx->cctx->conn_type == DOVEADM_CONNECTION_TYPE_CLI); + + struct raw_key { + const char *attr; + const char *id; + const char *data; + }; + + ARRAY(struct raw_key) raw_keys; + + doveadm_print_init(DOVEADM_PRINT_TYPE_PAGER); + + doveadm_print_header_simple("result"); + + if (ctx->ask_old_password) { + if (ctx->old_password != NULL) { + doveadm_print("old password specified, cannot ask for it"); + _ctx->exit_code = EX_USAGE; + return -1; + } + if (!cli) { + doveadm_print("No cli - cannot ask for password"); + _ctx->exit_code = EX_USAGE; + return -1; + } + ctx->old_password = + p_strdup(_ctx->pool, t_askpass("Old password: ")); + } + + if (ctx->ask_new_password) { + if (ctx->new_password != NULL) { + doveadm_print("new password specified, cannot ask for it"); + _ctx->exit_code = EX_USAGE; + return -1; + } + if (!cli) { + doveadm_print("No cli - cannot ask for password"); + _ctx->exit_code = EX_USAGE; + return -1; + } + const char *passw; + passw = t_askpass("New password: "); + if (strcmp(passw, t_askpass("Confirm new password: ")) != 0) { + doveadm_print("Passwords don't match, aborting"); + _ctx->exit_code = EX_USAGE; + return -1; + } + ctx->new_password = p_strdup(_ctx->pool, passw); + } + + if (ctx->clear_password && + (ctx->new_password != NULL || + mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_PASSWORD) != NULL)) { + doveadm_print("clear password and new password specified"); + _ctx->exit_code = EX_USAGE; + return -1; + } + + struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); + struct mailbox *box = mailbox_alloc(ns->list, "INBOX", 0); + if (mailbox_open(box) < 0) { + doveadm_print(t_strdup_printf("mailbox_open(%s) failed: %s", + mailbox_get_vname(box), + mailbox_get_last_internal_error(box, NULL))); + _ctx->exit_code = EX_TEMPFAIL; + return -1; + } + + t_array_init(&raw_keys, 8); + + /* then get the current user keys, all of them */ + struct mailbox_attribute_iter *iter = + mailbox_attribute_iter_init(box, + MAIL_ATTRIBUTE_TYPE_PRIVATE, + USER_CRYPT_PREFIX + PRIVKEYS_PREFIX); + const char *error; + const char *key_id; + int ret = 1; + unsigned int count = 0; + + while ((key_id = mailbox_attribute_iter_next(iter)) != NULL) { + const char *attr = + t_strdup_printf(USER_CRYPT_PREFIX PRIVKEYS_PREFIX "%s", + key_id); + + struct mail_attribute_value value; + if ((ret = mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_PRIVATE, + attr, &value)) < 0) { + doveadm_print(t_strdup_printf("mailbox_attribute_get(%s, %s) failed: %s", + mailbox_get_vname(box), attr, + mailbox_get_last_internal_error(box, NULL))); + _ctx->exit_code = EX_TEMPFAIL; + break; + } else if (ret > 0) { + struct raw_key *raw_key = array_append_space(&raw_keys); + raw_key->attr = p_strdup(_ctx->pool, attr); + raw_key->id = p_strdup(_ctx->pool, key_id); + raw_key->data = p_strdup(_ctx->pool, value.value); + } + } + + if (ret == 1) { + struct mailbox_transaction_context *t = + mailbox_transaction_begin(box, _ctx->transaction_flags, + __func__); + struct dcrypt_private_key *key; + const struct raw_key *raw_key; + const char *algo = ctx->new_password != NULL ? + MAIL_CRYPT_PW_CIPHER : + NULL; + string_t *newkey = t_str_new(256); + + array_foreach(&raw_keys, raw_key) { + struct mail_attribute_value value; + + if (!dcrypt_key_load_private(&key, raw_key->data, + ctx->old_password, NULL, + &error)) { + doveadm_print(t_strdup_printf("dcrypt_key_load_private(%s) failed: %s", + raw_key->id, + error)); + _ctx->exit_code = EX_DATAERR; + ret = -1; + break; + } + + /* save it */ + str_truncate(newkey, 0); + + if (!dcrypt_key_store_private(key, DCRYPT_FORMAT_DOVECOT, + algo, newkey, + ctx->new_password, + NULL, &error)) { + doveadm_print(t_strdup_printf("dcrypt_key_store_private(%s) failed: %s", + raw_key->id, + error)); + _ctx->exit_code = EX_DATAERR; + ret = -1; + } + + dcrypt_key_unref_private(&key); + if (ret == -1) break; + + i_zero(&value); + value.value = str_c(newkey); + + /* and store it */ + if (mailbox_attribute_set(t, MAIL_ATTRIBUTE_TYPE_PRIVATE, + raw_key->attr, &value) < 0) { + doveadm_print(t_strdup_printf("mailbox_attribute_set(%s, %s) failed: %s", + mailbox_get_vname(box), + raw_key->attr, + mailbox_get_last_internal_error(box, NULL))); + _ctx->exit_code = EX_TEMPFAIL; + ret = -1; + break; + } + count++; + } + + if (ret < 1) { + mailbox_transaction_rollback(&t); + } else { + if (mailbox_transaction_commit(&t) < 0) { + doveadm_print(t_strdup_printf("mailbox_transaction_commit(%s) failed: %s", + mailbox_get_vname(box), + mailbox_get_last_internal_error(box, NULL))); + } else { + doveadm_print(t_strdup_printf("Changed password for %u key(s)", + count)); + } + } + } + + (void)mailbox_attribute_iter_deinit(&iter); + mailbox_free(&box); + + return ret; +} + + +static bool cmd_mcp_keypair_generate_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) +{ + struct mcp_cmd_context *ctx = + (struct mcp_cmd_context *)_ctx; + + switch (c) { + case 'U': + ctx->userkey_only = TRUE; + break; + case 'R': + ctx->recrypt_box_keys = TRUE; + break; + case 'f': + ctx->force = TRUE; + break; + default: + return FALSE; + } + return TRUE; + +} + +static bool cmd_mcp_key_password_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) +{ + struct mcp_cmd_context *ctx = + (struct mcp_cmd_context *)_ctx; + + switch (c) { + case 'N': + ctx->ask_new_password = TRUE; + break; + case 'O': + ctx->ask_old_password = TRUE; + break; + case 'C': + ctx->clear_password = TRUE; + break; + case 'o': + ctx->old_password = p_strdup(_ctx->pool, optarg); + break; + case 'n': + ctx->new_password = p_strdup(_ctx->pool, optarg); + break; + default: + return FALSE; + } + return TRUE; +} + +static bool cmd_mcp_key_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c) +{ + struct mcp_cmd_context *ctx = + (struct mcp_cmd_context *)_ctx; + + switch (c) { + case 'U': + ctx->userkey_only = TRUE; + break; + default: + return FALSE; + } + return TRUE; + +} + +static struct doveadm_mail_cmd_context *cmd_mcp_keypair_generate_alloc(void) +{ + struct mcp_cmd_context *ctx; + + ctx = doveadm_mail_cmd_alloc(struct mcp_cmd_context); + ctx->ctx.getopt_args = "URf"; + ctx->ctx.v.parse_arg = cmd_mcp_keypair_generate_parse_arg; + ctx->ctx.v.run = cmd_mcp_keypair_generate_run; + return &ctx->ctx; +} + +static struct doveadm_mail_cmd_context *cmd_mcp_key_list_alloc(void) +{ + struct mcp_cmd_context *ctx; + + ctx = doveadm_mail_cmd_alloc(struct mcp_cmd_context); + ctx->ctx.getopt_args = "U"; + ctx->ctx.v.parse_arg = cmd_mcp_key_parse_arg; + ctx->ctx.v.run = cmd_mcp_key_list_run; + return &ctx->ctx; +} + +static struct doveadm_mail_cmd_context *cmd_mcp_key_export_alloc(void) +{ + struct mcp_cmd_context *ctx; + + ctx = doveadm_mail_cmd_alloc(struct mcp_cmd_context); + ctx->ctx.getopt_args = "U"; + ctx->ctx.v.parse_arg = cmd_mcp_key_parse_arg; + ctx->ctx.v.run = cmd_mcp_key_export_run; + return &ctx->ctx; +} + +static struct doveadm_mail_cmd_context *cmd_mcp_key_password_alloc(void) +{ + struct mcp_cmd_context *ctx; + + ctx = doveadm_mail_cmd_alloc(struct mcp_cmd_context); + ctx->ctx.getopt_args = "NOCo:n:"; + ctx->ctx.v.parse_arg = cmd_mcp_key_password_parse_arg; + ctx->ctx.v.run = cmd_mcp_key_password_run; + return &ctx->ctx; +} + +struct doveadm_cmd_ver2 doveadm_cmd_mcp_keypair_generate = { + .name = "mailbox cryptokey generate", + .mail_cmd = cmd_mcp_keypair_generate_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[-URf] mailbox [ mailbox .. ]", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('U', "user-key-only", CMD_PARAM_BOOL, 0) +DOVEADM_CMD_PARAM('R', "re-encrypt-box-keys", CMD_PARAM_BOOL, 0) +DOVEADM_CMD_PARAM('f', "force", CMD_PARAM_BOOL, 0) +DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; + +struct doveadm_cmd_ver2 doveadm_cmd_mcp_key_list = { + .name = "mailbox cryptokey list", + .mail_cmd = cmd_mcp_key_list_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "-U | mailbox [ mailbox .. ]", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('U', "user-key", CMD_PARAM_BOOL, 0) +DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; + +struct doveadm_cmd_ver2 doveadm_cmd_mcp_key_export = { + .name = "mailbox cryptokey export", + .mail_cmd = cmd_mcp_key_export_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "-U | mailbox [ mailbox .. ]", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('U', "user-key", CMD_PARAM_BOOL, 0) +DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; + +struct doveadm_cmd_ver2 doveadm_cmd_mcp_key_password = { + .name = "mailbox cryptokey password", + .mail_cmd = cmd_mcp_key_password_alloc, + .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX "[-NOC] [-opassword] [-npassword]", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_MAIL_COMMON +DOVEADM_CMD_PARAM('C', "clear-password", CMD_PARAM_BOOL, 0) +DOVEADM_CMD_PARAM('N', "ask-new-password", CMD_PARAM_BOOL, 0) +DOVEADM_CMD_PARAM('n', "new-password", CMD_PARAM_STR, 0) +DOVEADM_CMD_PARAM('O', "ask-old-password", CMD_PARAM_BOOL, 0) +DOVEADM_CMD_PARAM('o', "old-password", CMD_PARAM_STR, 0) +DOVEADM_CMD_PARAMS_END +}; + +void doveadm_mail_crypt_plugin_init(struct module *mod ATTR_UNUSED) +{ + doveadm_cmd_register_ver2(&doveadm_cmd_mcp_keypair_generate); + doveadm_cmd_register_ver2(&doveadm_cmd_mcp_key_list); + doveadm_cmd_register_ver2(&doveadm_cmd_mcp_key_export); + doveadm_cmd_register_ver2(&doveadm_cmd_mcp_key_password); +} + +void doveadm_mail_crypt_plugin_deinit(void) +{ +} diff --git a/src/plugins/mail-crypt/fs-crypt-common.c b/src/plugins/mail-crypt/fs-crypt-common.c new file mode 100644 index 0000000..7432fa6 --- /dev/null +++ b/src/plugins/mail-crypt/fs-crypt-common.c @@ -0,0 +1,368 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "randgen.h" +#include "istream.h" +#include "ostream.h" +#include "istream-decrypt.h" +#include "ostream-encrypt.h" +#include "iostream-temp.h" +#include "mailbox-list.h" +#include "mail-namespace.h" +#include "mail-crypt-common.h" +#include "mail-crypt-key.h" +#include "dcrypt-iostream.h" +#include "fs-api-private.h" + +struct crypt_fs { + struct fs fs; + struct mail_crypt_global_keys keys; + bool keys_loaded; + + char *enc_algo; + char *set_prefix; + char *public_key_path; + char *private_key_path; + char *password; +}; + +struct crypt_fs_file { + struct fs_file file; + struct crypt_fs *fs; + struct fs_file *super_read; + enum fs_open_mode open_mode; + struct istream *input; + + struct ostream *super_output; + struct ostream *temp_output; +}; + +#define CRYPT_FS(ptr) container_of((ptr), struct crypt_fs, fs) +#define CRYPT_FILE(ptr) container_of((ptr), struct crypt_fs_file, file) + +/* defined outside this file */ +extern const struct fs FS_CLASS_CRYPT; + +static +int fs_crypt_load_keys(struct crypt_fs *fs, const char **error_r); + +static struct fs *fs_crypt_alloc(void) +{ + struct crypt_fs *fs; + + fs = i_new(struct crypt_fs, 1); + fs->fs = FS_CLASS_CRYPT; + + return &fs->fs; +} + +static int +fs_crypt_init(struct fs *_fs, const char *args, const struct fs_settings *set, + const char **error_r) +{ + struct crypt_fs *fs = CRYPT_FS(_fs); + const char *enc_algo, *set_prefix; + const char *p, *arg, *value, *error, *parent_name, *parent_args; + const char *public_key_path = "", *private_key_path = "", *password = ""; + + if (!dcrypt_initialize("openssl", NULL, &error)) + i_fatal("dcrypt_initialize(): %s", error); + + /* [algo=<s>:][set_prefix=<n>:][public_key_path=<s>:] + [private_key_path=<s>:[password=<s>:]]<parent fs> */ + set_prefix = "mail_crypt_global"; + enc_algo = "aes-256-gcm-sha256"; + for (;;) { + p = strchr(args, ':'); + if (p == NULL) { + *error_r = "Missing parameters"; + return -1; + } + arg = t_strdup_until(args, p); + if ((value = strchr(arg, '=')) == NULL) + break; + arg = t_strdup_until(arg, value++); + args = p+1; + + if (strcmp(arg, "algo") == 0) + enc_algo = value; + else if (strcmp(arg, "set_prefix") == 0) + set_prefix = value; + else if (strcmp(arg, "public_key_path") == 0) + public_key_path = value; + else if (strcmp(arg, "private_key_path") == 0) + private_key_path = value; + else if (strcmp(arg, "password") == 0) + password = value; + else { + *error_r = t_strdup_printf( + "Invalid parameter '%s'", arg); + return -1; + } + } + + parent_args = strchr(args, ':'); + if (parent_args == NULL) { + parent_name = args; + parent_args = ""; + } else { + parent_name = t_strdup_until(args, parent_args); + parent_args++; + } + if (fs_init(parent_name, parent_args, set, &_fs->parent, error_r) < 0) + return -1; + fs->enc_algo = i_strdup(enc_algo); + fs->set_prefix = i_strdup(set_prefix); + fs->public_key_path = i_strdup_empty(public_key_path); + fs->private_key_path = i_strdup_empty(private_key_path); + fs->password = i_strdup_empty(password); + return 0; +} + +static void fs_crypt_free(struct fs *_fs) +{ + struct crypt_fs *fs = CRYPT_FS(_fs); + + mail_crypt_global_keys_free(&fs->keys); + i_free(fs->enc_algo); + i_free(fs->set_prefix); + i_free(fs->public_key_path); + i_free(fs->private_key_path); + i_free(fs->password); + i_free(fs); +} + +static struct fs_file *fs_crypt_file_alloc(void) +{ + struct crypt_fs_file *file = i_new(struct crypt_fs_file, 1); + return &file->file; +} + +static void +fs_crypt_file_init(struct fs_file *_file, const char *path, + enum fs_open_mode mode, enum fs_open_flags flags) +{ + struct crypt_fs *fs = CRYPT_FS(_file->fs); + struct crypt_fs_file *file = CRYPT_FILE(_file); + + file->file.path = i_strdup(path); + file->fs = fs; + file->open_mode = mode; + + /* avoid unnecessarily creating two seekable streams */ + flags &= ENUM_NEGATE(FS_OPEN_FLAG_SEEKABLE); + + file->file.parent = fs_file_init_parent(_file, path, mode, flags); + if (mode == FS_OPEN_MODE_READONLY && + (flags & FS_OPEN_FLAG_ASYNC) == 0) { + /* use async stream for super, so fs_read_stream() won't create + another seekable stream needlessly */ + file->super_read = fs_file_init_parent(_file, path, + mode, flags | FS_OPEN_FLAG_ASYNC | + FS_OPEN_FLAG_ASYNC_NOQUEUE); + } else { + file->super_read = file->file.parent; + } +} + +static void fs_crypt_file_deinit(struct fs_file *_file) +{ + struct crypt_fs_file *file = CRYPT_FILE(_file); + + if (file->super_read != _file->parent) + fs_file_deinit(&file->super_read); + fs_file_free(_file); + i_free(file->file.path); + i_free(file); +} + +static void fs_crypt_file_close(struct fs_file *_file) +{ + struct crypt_fs_file *file = CRYPT_FILE(_file); + + i_stream_unref(&file->input); + fs_file_close(file->super_read); + fs_file_close(_file->parent); +} + +static int fs_crypt_read_file(const char *set_name, const char *path, + char **key_data_r, const char **error_r) +{ + struct istream *input; + int ret; + + input = i_stream_create_file(path, SIZE_MAX); + while (i_stream_read(input) > 0) ; + if (input->stream_errno != 0) { + *error_r = t_strdup_printf("%s: read(%s) failed: %s", + set_name, path, i_stream_get_error(input)); + ret = -1; + } else { + size_t size; + const unsigned char *data = i_stream_get_data(input, &size); + *key_data_r = i_strndup(data, size); + ret = 0; + } + i_stream_unref(&input); + return ret; +} + +static int +fs_crypt_load_keys_from_path(struct crypt_fs *fs, const char **error_r) +{ + char *key_data; + + mail_crypt_global_keys_init(&fs->keys); + if (fs->public_key_path != NULL) { + if (fs_crypt_read_file("crypt:public_key_path", + fs->public_key_path, + &key_data, error_r) < 0) + return -1; + if (mail_crypt_load_global_public_key("crypt:public_key_path", + key_data, &fs->keys, + error_r) < 0) { + i_free(key_data); + return -1; + } + i_free(key_data); + } + if (fs->private_key_path != NULL) { + if (fs_crypt_read_file("crypt:private_key_path", + fs->private_key_path, + &key_data, error_r) < 0) + return -1; + if (mail_crypt_load_global_private_key("crypt:private_key_path", + key_data, "crypt:password", + fs->password, &fs->keys, + error_r) < 0) { + i_free(key_data); + return -1; + } + i_free(key_data); + } + return 0; +} + +static int +fs_crypt_istream_get_key(const char *pubkey_digest, + struct dcrypt_private_key **priv_key_r, + const char **error_r, void *context) +{ + struct crypt_fs_file *file = context; + + if (fs_crypt_load_keys(file->fs, error_r) < 0) + return -1; + + *priv_key_r = mail_crypt_global_key_find(&file->fs->keys, pubkey_digest); + if (*priv_key_r == NULL) + return 0; + dcrypt_key_ref_private(*priv_key_r); + return 1; +} + +static struct istream * +fs_crypt_read_stream(struct fs_file *_file, size_t max_buffer_size) +{ + struct crypt_fs_file *file = CRYPT_FILE(_file); + struct istream *input; + + if (file->input != NULL) { + i_stream_ref(file->input); + i_stream_seek(file->input, 0); + return file->input; + } + + input = fs_read_stream(file->super_read, max_buffer_size); + + file->input = i_stream_create_decrypt_callback(input, + fs_crypt_istream_get_key, file); + i_stream_unref(&input); + i_stream_ref(file->input); + return file->input; +} + +static void fs_crypt_write_stream(struct fs_file *_file) +{ + struct crypt_fs_file *file = CRYPT_FILE(_file); + const char *error; + + i_assert(_file->output == NULL); + + if (fs_crypt_load_keys(file->fs, &error) < 0) { + _file->output = o_stream_create_error_str(EIO, + "Couldn't read settings: %s", error); + return; + } + + if (file->fs->keys.public_key == NULL) { + if (_file->fs->set.debug) + i_debug("No public key provided, " + "NOT encrypting stream %s", + fs_file_path(_file)); + file->super_output = fs_write_stream(_file->parent); + _file->output = file->super_output; + return; + } + + enum io_stream_encrypt_flags flags; + if (strstr(file->fs->enc_algo, "gcm") != NULL || + strstr(file->fs->enc_algo, "ccm") != NULL) { + flags = IO_STREAM_ENC_INTEGRITY_AEAD; + } else { + flags = IO_STREAM_ENC_INTEGRITY_HMAC; + } + + file->temp_output = + iostream_temp_create_named(_file->fs->temp_path_prefix, + IOSTREAM_TEMP_FLAG_TRY_FD_DUP, + fs_file_path(_file)); + _file->output = o_stream_create_encrypt(file->temp_output, + file->fs->enc_algo, file->fs->keys.public_key, + flags); +} + +static int fs_crypt_write_stream_finish(struct fs_file *_file, bool success) +{ + struct crypt_fs_file *file = CRYPT_FILE(_file); + struct istream *input; + int ret; + + if (_file->output != NULL) { + if (_file->output == file->super_output) + _file->output = NULL; + else + o_stream_unref(&_file->output); + } + if (!success) { + if (file->super_output != NULL) { + /* no encryption */ + i_assert(file->temp_output == NULL); + fs_write_stream_abort_error(_file->parent, &file->super_output, + "write(%s) failed: %s", + o_stream_get_name(file->super_output), + o_stream_get_error(file->super_output)); + } else { + o_stream_destroy(&file->temp_output); + } + return -1; + } + + if (file->super_output != NULL) { + /* no encrypt */ + i_assert(file->temp_output == NULL); + return fs_write_stream_finish(_file->parent, &file->super_output); + } + if (file->temp_output == NULL) { + /* finishing up */ + i_assert(file->super_output == NULL); + return fs_write_stream_finish_async(_file->parent); + } + + /* finish writing the temporary file */ + input = iostream_temp_finish(&file->temp_output, IO_BLOCK_SIZE); + file->super_output = fs_write_stream(_file->parent); + o_stream_nsend_istream(file->super_output, input); + ret = fs_write_stream_finish(_file->parent, &file->super_output); + i_stream_unref(&input); + return ret; +} diff --git a/src/plugins/mail-crypt/fs-crypt-settings.c b/src/plugins/mail-crypt/fs-crypt-settings.c new file mode 100644 index 0000000..ba70f8a --- /dev/null +++ b/src/plugins/mail-crypt/fs-crypt-settings.c @@ -0,0 +1,35 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "settings-parser.h" +#include "service-settings.h" +#include "mail-storage-settings.h" +#include "fs-crypt-settings.h" + +static const struct setting_define fs_crypt_setting_defines[] = { + { .type = SET_STRLIST, .key = "plugin", + .offset = offsetof(struct fs_crypt_settings, plugin_envs) }, + + SETTING_DEFINE_LIST_END +}; + +const struct fs_crypt_settings fs_crypt_default_settings = { + .plugin_envs = ARRAY_INIT +}; + +static const struct setting_parser_info *fs_crypt_setting_dependencies[] = { + NULL +}; + +const struct setting_parser_info fs_crypt_setting_parser_info = { + .module_name = "fs-crypt", + .defines = fs_crypt_setting_defines, + .defaults = &fs_crypt_default_settings, + + .type_offset = SIZE_MAX, + .struct_size = sizeof(struct fs_crypt_settings), + + .parent_offset = SIZE_MAX, + .dependencies = fs_crypt_setting_dependencies +}; diff --git a/src/plugins/mail-crypt/fs-crypt-settings.h b/src/plugins/mail-crypt/fs-crypt-settings.h new file mode 100644 index 0000000..df1a7b1 --- /dev/null +++ b/src/plugins/mail-crypt/fs-crypt-settings.h @@ -0,0 +1,11 @@ +#ifndef FS_CRYPT_SETTINGS_H +#define FS_CRYPT_SETTINGS_H + +struct fs_crypt_settings { + ARRAY(const char *) plugin_envs; +}; + +extern const struct setting_parser_info fs_crypt_setting_parser_info; + +#endif + diff --git a/src/plugins/mail-crypt/fs-crypt.c b/src/plugins/mail-crypt/fs-crypt.c new file mode 100644 index 0000000..0a81c69 --- /dev/null +++ b/src/plugins/mail-crypt/fs-crypt.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ +#define FS_CLASS_CRYPT fs_class_crypt +#include "fs-crypt-common.c" + +static +int fs_crypt_load_keys(struct crypt_fs *fs, const char **error_r) +{ + const char *error; + + if (fs->keys_loaded) + return 0; + if (fs->public_key_path != NULL || fs->private_key_path != NULL) { + /* overrides using settings */ + if (fs_crypt_load_keys_from_path(fs, error_r) < 0) + return -1; + fs->keys_loaded = TRUE; + return 0; + } + if (mail_crypt_global_keys_load_pluginenv(fs->set_prefix, &fs->keys, + &error) < 0) { + *error_r = t_strdup_printf("%s: %s", fs->set_prefix, error); + return -1; + } + fs->keys_loaded = TRUE; + return 0; +} + +const struct fs fs_class_crypt = { + .name = "crypt", + .v = { + fs_crypt_alloc, + fs_crypt_init, + NULL, + fs_crypt_free, + fs_wrapper_get_properties, + fs_crypt_file_alloc, + fs_crypt_file_init, + fs_crypt_file_deinit, + fs_crypt_file_close, + fs_wrapper_file_get_path, + fs_wrapper_set_async_callback, + fs_wrapper_wait_async, + fs_wrapper_set_metadata, + fs_wrapper_get_metadata, + fs_wrapper_prefetch, + fs_read_via_stream, + fs_crypt_read_stream, + fs_write_via_stream, + fs_crypt_write_stream, + fs_crypt_write_stream_finish, + fs_wrapper_lock, + fs_wrapper_unlock, + fs_wrapper_exists, + fs_wrapper_stat, + fs_wrapper_copy, + fs_wrapper_rename, + fs_wrapper_delete, + fs_wrapper_iter_alloc, + fs_wrapper_iter_init, + fs_wrapper_iter_next, + fs_wrapper_iter_deinit, + NULL, + fs_wrapper_get_nlinks, + } +}; diff --git a/src/plugins/mail-crypt/fs-mail-crypt.c b/src/plugins/mail-crypt/fs-mail-crypt.c new file mode 100644 index 0000000..b017d3f --- /dev/null +++ b/src/plugins/mail-crypt/fs-mail-crypt.c @@ -0,0 +1,72 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ +#define FS_CLASS_CRYPT fs_class_mail_crypt +#include "fs-crypt-common.c" + +static +int fs_crypt_load_keys(struct crypt_fs *fs, const char **error_r) +{ + struct mailbox_list *list = mailbox_list_fs_get_list(&fs->fs); + const char *error; + + if (fs->keys_loaded) + return 0; + if (fs->public_key_path != NULL || fs->private_key_path != NULL) { + /* overrides using settings */ + if (fs_crypt_load_keys_from_path(fs, error_r) < 0) + return -1; + fs->keys_loaded = TRUE; + return 0; + } + if (list == NULL) { + *error_r = "fs-mail-crypt can be used only via lib-storage"; + return -1; + } + + if (mail_crypt_global_keys_load(mailbox_list_get_namespace(list)->user, + fs->set_prefix, &fs->keys, FALSE, + &error) < 0) { + *error_r = t_strdup_printf("%s: %s", fs->set_prefix, error); + return -1; + } + fs->keys_loaded = TRUE; + return 0; +} + +const struct fs fs_class_mail_crypt = { + .name = "mail-crypt", + .v = { + fs_crypt_alloc, + fs_crypt_init, + NULL, + fs_crypt_free, + fs_wrapper_get_properties, + fs_crypt_file_alloc, + fs_crypt_file_init, + fs_crypt_file_deinit, + fs_crypt_file_close, + fs_wrapper_file_get_path, + fs_wrapper_set_async_callback, + fs_wrapper_wait_async, + fs_wrapper_set_metadata, + fs_wrapper_get_metadata, + fs_wrapper_prefetch, + fs_read_via_stream, + fs_crypt_read_stream, + fs_write_via_stream, + fs_crypt_write_stream, + fs_crypt_write_stream_finish, + fs_wrapper_lock, + fs_wrapper_unlock, + fs_wrapper_exists, + fs_wrapper_stat, + fs_wrapper_copy, + fs_wrapper_rename, + fs_wrapper_delete, + fs_wrapper_iter_alloc, + fs_wrapper_iter_init, + fs_wrapper_iter_next, + fs_wrapper_iter_deinit, + NULL, + fs_wrapper_get_nlinks, + } +}; diff --git a/src/plugins/mail-crypt/mail-crypt-acl-plugin.c b/src/plugins/mail-crypt/mail-crypt-acl-plugin.c new file mode 100644 index 0000000..71ab1e7 --- /dev/null +++ b/src/plugins/mail-crypt/mail-crypt-acl-plugin.c @@ -0,0 +1,431 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop-private.h" +#include "str.h" +#include "sha2.h" +#include "module-dir.h" +#include "var-expand.h" +#include "hex-binary.h" +#include "mail-namespace.h" +#include "mail-storage-hooks.h" +#include "mail-storage-service.h" +#include "acl-plugin.h" +#include "acl-api-private.h" +#include "mail-crypt-common.h" +#include "mail-crypt-key.h" +#include "mail-crypt-plugin.h" + +#define MAIL_CRYPT_ACL_LIST_CONTEXT(obj) \ + MODULE_CONTEXT_REQUIRE(obj, mail_crypt_acl_mailbox_list_module) + +struct mail_crypt_acl_mailbox_list { + union mailbox_list_module_context module_ctx; + struct acl_backend_vfuncs acl_vprev; +}; + +static MODULE_CONTEXT_DEFINE_INIT(mail_crypt_acl_mailbox_list_module, + &mailbox_list_module_register); + +void mail_crypt_acl_plugin_init(struct module *module); +void mail_crypt_acl_plugin_deinit(void); + +static int +mail_crypt_acl_has_user_read_right(struct acl_object *aclobj, + const char *username, + const char **error_r) +{ + struct acl_object_list_iter *iter; + struct acl_rights rights; + int ret = 0; + + iter = acl_object_list_init(aclobj); + while (acl_object_list_next(iter, &rights)) { + if (rights.id_type == ACL_ID_USER && + strcmp(rights.identifier, username) == 0) { + ret = str_array_find(rights.rights, MAIL_ACL_READ) ? 1 : 0; + break; + } + } + if (acl_object_list_deinit(&iter) < 0) { + *error_r = "Failed to iterate ACL objects"; + return -1; + } + + return ret; +} + +static int mail_crypt_acl_has_nonuser_read_right(struct acl_object *aclobj, + const char **error_r) +{ + struct acl_object_list_iter *iter; + struct acl_rights rights; + int ret = 0; + + iter = acl_object_list_init(aclobj); + while (acl_object_list_next(iter, &rights)) { + if (rights.id_type != ACL_ID_USER && + rights.id_type != ACL_ID_OWNER && + rights.rights != NULL && + str_array_find(rights.rights, MAIL_ACL_READ)) { + ret = 1; + break; + } + } + if (acl_object_list_deinit(&iter) < 0) { + *error_r = "Failed to iterate ACL objects"; + return -1; + } + return ret; +} + +static int +mail_crypt_acl_unset_private_keys(struct mailbox *src_box, + const char *dest_user, + enum mail_attribute_type type, + const char **error_r) +{ + ARRAY_TYPE(const_string) digests; + const char *error; + int ret = 1; + + if (mailbox_open(src_box) < 0) { + *error_r = t_strdup_printf("mail-crypt-acl-plugin: " + "mailbox_open(%s) failed: %s", + mailbox_get_vname(src_box), + mailbox_get_last_internal_error(src_box, NULL)); + return -1; + } + + t_array_init(&digests, 4); + if (mail_crypt_box_get_pvt_digests(src_box, pool_datastack_create(), + type, &digests, &error) < 0) { + *error_r = t_strdup_printf("mail-crypt-acl-plugin: " + "Failed to lookup public key digests: %s", + error); + mailbox_free(&src_box); + return -1; + } + + struct mailbox_transaction_context *t; + t = mailbox_transaction_begin(src_box, 0, __func__); + + const char *hash; + array_foreach_elem(&digests, hash) { + const char *ptr; + /* if the id contains username part, skip to key public id */ + if ((ptr = strchr(hash, '/')) != NULL) + ptr++; + else + ptr = hash; + if ((ret = mail_crypt_box_unset_shared_key(t, ptr, dest_user, + error_r)) < 0) { + ret = -1; + break; + } + } + + if (ret < 0) { + mailbox_transaction_rollback(&t); + } else if (mailbox_transaction_commit(&t) < 0) { + *error_r = t_strdup_printf("mail-crypt-acl-plugin: " + "mailbox_transaction_commit(%s) failed: %s", + mailbox_get_vname(src_box), + mailbox_get_last_internal_error(src_box, NULL)); + return -1; + } + return 0; +} + +static int +mail_crypt_acl_user_create(struct mail_user *user, const char *dest_username, + struct mail_user **dest_user_r, + struct mail_storage_service_user **dest_service_user_r, + const char **error_r) +{ + const struct mail_storage_service_input *old_input; + struct mail_storage_service_input input; + struct mail_storage_service_ctx *service_ctx; + struct ioloop_context *cur_ioloop_ctx; + + int ret; + + i_assert(user->_service_user != NULL); + service_ctx = mail_storage_service_user_get_service_ctx(user->_service_user); + old_input = mail_storage_service_user_get_input(user->_service_user); + + if ((cur_ioloop_ctx = io_loop_get_current_context(current_ioloop)) != NULL) + io_loop_context_deactivate(cur_ioloop_ctx); + + i_zero(&input); + input.module = old_input->module; + input.service = old_input->service; + input.username = dest_username; + input.session_id_prefix = user->session_id; + input.flags_override_add = MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS | + MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT; + input.flags_override_remove = MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES; + + ret = mail_storage_service_lookup_next(service_ctx, &input, + dest_service_user_r, + dest_user_r, error_r); + + return ret; +} + +static int +mail_crypt_acl_update_private_key(struct mailbox *src_box, + struct mail_user *dest_user, bool set, + bool disallow_insecure, + const char **error_r) +{ + struct dcrypt_public_key *key = NULL; + struct dcrypt_private_key *priv_key; + int ret = 0; + + if (!set) { + return mail_crypt_acl_unset_private_keys(src_box, + dest_user->username, + MAIL_ATTRIBUTE_TYPE_SHARED, + error_r); + } + + if (dest_user != NULL) { + /* get public key from target user */ + if ((ret = mail_crypt_user_get_public_key(dest_user, + &key, error_r)) <= 0) { + if (ret == 0 && disallow_insecure) { + *error_r = t_strdup_printf("User %s has no active public key", + dest_user->username); + return -1; + } else if (ret < 0) { + return -1; + } else if (ret == 0) { + /* perform insecure sharing */ + dest_user = NULL; + key = NULL; + } + } + } + + ARRAY_TYPE(dcrypt_private_key) keys; + t_array_init(&keys, 8); + + struct mailbox_transaction_context *t = + mailbox_transaction_begin(src_box, 0, __func__); + + /* get private keys from box */ + if (mail_crypt_box_get_private_keys(src_box, &keys, error_r) < 0 || + mail_crypt_box_share_private_keys(t, key, + dest_user == NULL ? NULL : + dest_user->username, + &keys, error_r) < 0) + ret = -1; + if (key != NULL) + dcrypt_key_unref_public(&key); + + if (ret >= 0) { + array_foreach_elem(&keys, priv_key) + dcrypt_key_unref_private(&priv_key); + } + + if (mailbox_transaction_commit(&t) < 0) { + *error_r = mailbox_get_last_internal_error(src_box, NULL); + ret = -1; + } + + return ret; +} + +static int mail_crypt_acl_object_update(struct acl_object *aclobj, + const struct acl_rights_update *update) +{ + const char *error; + struct mail_crypt_acl_mailbox_list *mlist = + MAIL_CRYPT_ACL_LIST_CONTEXT(aclobj->backend->list); + const char *username; + struct mail_user *dest_user; + struct mail_storage_service_user *dest_service_user; + struct ioloop_context *cur_ioloop_ctx; + bool have_rights; + int ret = 0; + + if (mlist->acl_vprev.object_update(aclobj, update) < 0) + return -1; + + bool disallow_insecure = + mail_crypt_acl_secure_sharing_enabled(aclobj->backend->list->ns->user); + + const char *box_name = mailbox_list_get_vname(aclobj->backend->list, + aclobj->name); + struct mailbox *box = mailbox_alloc(aclobj->backend->list, box_name, 0); + + switch (update->rights.id_type) { + case ACL_ID_USER: + /* setting rights for specific user: we can encrypt the + mailbox key for the user. */ + username = update->rights.identifier; + ret = mail_crypt_acl_has_user_read_right(aclobj, username, &error); + + if (ret < 0) { + i_error("mail-crypt-acl-plugin: " + "mail_crypt_acl_has_user_read_right(%s) failed: %s", + username, + error); + break; + } + + have_rights = ret > 0; + + ret = mail_crypt_acl_user_create(aclobj->backend->list->ns->user, + username, &dest_user, + &dest_service_user, &error); + + /* to make sure we get correct logging context */ + if (ret > 0) + mail_storage_service_io_deactivate_user(dest_service_user); + mail_storage_service_io_activate_user( + aclobj->backend->list->ns->user->_service_user + ); + + if (ret <= 0) { + i_error("mail-crypt-acl-plugin: " + "Cannot initialize destination user %s: %s", + username, error); + break; + } else { + i_assert(dest_user != NULL); + if ((ret = mailbox_open(box)) < 0) { + i_error("mail-crypt-acl-plugin: " + "mailbox_open(%s) failed: %s", + mailbox_get_vname(box), + mailbox_get_last_internal_error(box, NULL)); + } else if ((ret = mail_crypt_acl_update_private_key(box, dest_user, + have_rights, + disallow_insecure, + &error)) < 0) { + i_error("mail-crypt-acl-plugin: " + "acl_update_private_key(%s, %s) failed: %s", + mailbox_get_vname(box), + username, + error); + } + } + + /* logging context swap again */ + mail_storage_service_io_deactivate_user( + aclobj->backend->list->ns->user->_service_user + ); + mail_storage_service_io_activate_user(dest_service_user); + + mail_user_deinit(&dest_user); + mail_storage_service_user_unref(&dest_service_user); + + if ((cur_ioloop_ctx = io_loop_get_current_context(current_ioloop)) != NULL) + io_loop_context_deactivate(cur_ioloop_ctx); + mail_storage_service_io_activate_user( + aclobj->backend->list->ns->user->_service_user + ); + break; + case ACL_ID_OWNER: + /* we should be the one doing this? ignore */ + break; + case ACL_ID_ANYONE: + case ACL_ID_AUTHENTICATED: + case ACL_ID_GROUP: + case ACL_ID_GROUP_OVERRIDE: + if (disallow_insecure) { + i_error("mail-crypt-acl-plugin: " + "Secure key sharing is enabled -" + "Remove or set plugin { %s = no }", + MAIL_CRYPT_ACL_SECURE_SHARE_SETTING); + ret = -1; + break; + } + /* the mailbox key needs to be stored unencrypted. for groups + we could in theory use per-group encrypted keys, which the + users belonging to the group would able to decrypt with + their private key, but that becomes quite complicated. */ + if ((ret = mail_crypt_acl_has_nonuser_read_right(aclobj, &error)) < 0) { + i_error("mail-crypt-acl-plugin: %s", error); + } else if ((ret = mailbox_open(box)) < 0) { + i_error("mail-crypt-acl-plugin: " + "mailbox_open(%s) failed: %s", + mailbox_get_vname(box), + mailbox_get_last_internal_error(box, NULL)); + } else if ((ret = mail_crypt_acl_update_private_key(box, + NULL, + TRUE, + disallow_insecure, + &error)) < 0) { + i_error("mail-crypt-acl-plugin: " + "acl_update_private_key(%s, %s) failed: %s", + mailbox_get_vname(box), + "", + error); + } + break; + case ACL_ID_TYPE_COUNT: + i_unreached(); + } + + mailbox_free(&box); + return ret; +} + +static void +mail_crypt_acl_mail_namespace_storage_added(struct mail_namespace *ns) +{ + struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ns->list); + struct mail_crypt_acl_mailbox_list *mlist = + MAIL_CRYPT_ACL_LIST_CONTEXT(ns->list); + struct acl_backend *backend; + + if (alist == NULL) + return; + + /* FIXME: this method works only if there's a single plugin doing it. + if there are ever multiple plugins hooking into ACL commands the + ACL core code would need some changing to make it work correctly. */ + backend = alist->rights.backend; + mlist->acl_vprev = backend->v; + backend->v.object_update = mail_crypt_acl_object_update; +} + +static void mail_crypt_acl_mailbox_list_deinit(struct mailbox_list *list) +{ + struct mail_crypt_acl_mailbox_list *mlist = + MAIL_CRYPT_ACL_LIST_CONTEXT(list); + + mlist->module_ctx.super.deinit(list); +} + +static void mail_crypt_acl_mailbox_list_created(struct mailbox_list *list) +{ + struct mailbox_list_vfuncs *v = list->vlast; + struct mail_crypt_acl_mailbox_list *mlist; + + mlist = p_new(list->pool, struct mail_crypt_acl_mailbox_list, 1); + mlist->module_ctx.super = *v; + list->vlast = &mlist->module_ctx.super; + v->deinit = mail_crypt_acl_mailbox_list_deinit; + + MODULE_CONTEXT_SET(list, mail_crypt_acl_mailbox_list_module, mlist); +} + +static struct mail_storage_hooks mail_crypt_acl_mail_storage_hooks = { + .mailbox_list_created = mail_crypt_acl_mailbox_list_created, + .mail_namespace_storage_added = mail_crypt_acl_mail_namespace_storage_added +}; + +void mail_crypt_acl_plugin_init(struct module *module) +{ + mail_storage_hooks_add(module, &mail_crypt_acl_mail_storage_hooks); +} + +void mail_crypt_acl_plugin_deinit(void) +{ + mail_storage_hooks_remove(&mail_crypt_acl_mail_storage_hooks); +} + +const char *mail_crypt_acl_plugin_dependencies[] = { "acl", NULL }; diff --git a/src/plugins/mail-crypt/mail-crypt-common.h b/src/plugins/mail-crypt/mail-crypt-common.h new file mode 100644 index 0000000..57e5e2f --- /dev/null +++ b/src/plugins/mail-crypt/mail-crypt-common.h @@ -0,0 +1,30 @@ +#ifndef MAIL_CRYPT_COMMON_H +#define MAIL_CRYPT_COMMON_H + +#include "dcrypt.h" + +#define MAIL_CRYPT_PW_CIPHER "aes-256-ctr" +#define MAIL_CRYPT_KEY_CIPHER "ecdh-aes-256-ctr" +#define MAIL_CRYPT_ENC_ALGORITHM "aes-256-gcm-sha256" +#define MAIL_CRYPT_KEY_ID_ALGORITHM "sha256" +#define MAIL_CRYPT_KEY_ATTRIBUTE_FORMAT DCRYPT_FORMAT_DOVECOT +#define MAIL_CRYPT_ACL_SECURE_SHARE_SETTING "mail_crypt_acl_require_secure_key_sharing" +#define MAIL_CRYPT_REQUIRE_ENCRYPTED_USER_KEY "mail_crypt_require_encrypted_user_key" +#define MAIL_CRYPT_HASH_BUF_SIZE 128 +#define MAIL_CRYPT_KEY_BUF_SIZE 1024 +#define ACTIVE_KEY_NAME "active" +#define PUBKEYS_PREFIX "pubkeys/" +#define PRIVKEYS_PREFIX "privkeys/" + +#define BOX_CRYPT_PREFIX MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"crypt/" +#define USER_CRYPT_PREFIX \ + MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER \ + MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"crypt/" + +#define MAIL_CRYPT_USERENV_PASSWORD "mail_crypt_private_password" +#define MAIL_CRYPT_USERENV_KEY "mail_crypt_private_key" +#define MAIL_CRYPT_USERENV_CURVE "mail_crypt_curve" + +ARRAY_DEFINE_TYPE(dcrypt_private_key, struct dcrypt_private_key*); + +#endif diff --git a/src/plugins/mail-crypt/mail-crypt-global-key.c b/src/plugins/mail-crypt/mail-crypt-global-key.c new file mode 100644 index 0000000..01cb937 --- /dev/null +++ b/src/plugins/mail-crypt/mail-crypt-global-key.c @@ -0,0 +1,172 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "hex-binary.h" +#include "base64.h" +#include "mail-user.h" +#include "mail-crypt-common.h" +#include "mail-crypt-key.h" +#include "mail-crypt-plugin.h" + +int mail_crypt_load_global_public_key(const char *set_key, const char *key_data, + struct mail_crypt_global_keys *global_keys, + const char **error_r) +{ + const char *error; + enum dcrypt_key_format format; + enum dcrypt_key_kind kind; + if (!dcrypt_key_string_get_info(key_data, &format, NULL, + &kind, NULL, NULL, NULL, &error)) { + key_data = str_c(t_base64_decode_str(key_data)); + if (!dcrypt_key_string_get_info(key_data, &format, NULL, + &kind, NULL, NULL, NULL, &error)) { + *error_r = t_strdup_printf("%s: Couldn't parse public key: %s", + set_key, error); + return -1; + } + } + if (kind != DCRYPT_KEY_KIND_PUBLIC) { + *error_r = t_strdup_printf("%s: key is not public", set_key); + return -1; + } + if (!dcrypt_key_load_public(&global_keys->public_key, key_data, &error)) { + *error_r = t_strdup_printf("%s: Couldn't load public key: %s", + set_key, error); + return -1; + } + return 0; +} + +static int +mail_crypt_key_get_ids(struct dcrypt_private_key *key, + const char **key_id_r, const char **key_id_old_r, + const char **error_r) +{ + const char *error; + buffer_t *key_id; + + *key_id_r = NULL; + *key_id_old_r = NULL; + + /* new key ID */ + key_id = t_buffer_create(MAIL_CRYPT_HASH_BUF_SIZE); + if (!dcrypt_key_id_private(key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error)) { + *error_r = t_strdup_printf("Failed to get private key ID: %s", error); + return -1; + } + *key_id_r = binary_to_hex(key_id->data, key_id->used); + + buffer_set_used_size(key_id, 0); + + /* old key ID */ + if (dcrypt_key_type_private(key) != DCRYPT_KEY_EC) + return 0; + + if (!dcrypt_key_id_private_old(key, key_id, &error)) { + *error_r = t_strdup_printf("Failed to get private key old ID: %s", + error); + return -1; + } + *key_id_old_r = binary_to_hex(key_id->data, key_id->used); + return 0; +} + +int mail_crypt_load_global_private_key(const char *set_key, const char *key_data, + const char *set_pw, const char *key_password, + struct mail_crypt_global_keys *global_keys, + const char **error_r) +{ + enum dcrypt_key_format format; + enum dcrypt_key_kind kind; + enum dcrypt_key_encryption_type enc_type; + const char *error; + + if (!dcrypt_key_string_get_info(key_data, &format, NULL, &kind, + &enc_type, NULL, NULL, &error)) { + key_data = str_c(t_base64_decode_str(key_data)); + if (!dcrypt_key_string_get_info(key_data, &format, NULL, &kind, + &enc_type, NULL, NULL, &error)) { + *error_r = t_strdup_printf("%s: Couldn't parse private" + " key: %s", set_key, error); + return -1; + } + } + if (kind != DCRYPT_KEY_KIND_PRIVATE) { + *error_r = t_strdup_printf("%s: key is not private", set_key); + return -1; + } + + if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD) { + /* Fail here if password is not set since openssl will prompt + * for it otherwise */ + if (key_password == NULL) { + *error_r = t_strdup_printf("%s: %s unset, no password to decrypt the key", + set_key, set_pw); + return -1; + } + } + + struct dcrypt_private_key *key = NULL; + if (!dcrypt_key_load_private(&key, key_data, key_password, NULL, &error)) { + *error_r = t_strdup_printf("%s: Couldn't load private key: %s", + set_key, error); + return -1; + } + + const char *key_id, *key_id_old; + if (mail_crypt_key_get_ids(key, &key_id, &key_id_old, error_r) < 0) { + dcrypt_key_unref_private(&key); + return -1; + } + + struct mail_crypt_global_private_key *priv_key = + array_append_space(&global_keys->private_keys); + priv_key->key = key; + priv_key->key_id = i_strdup(key_id); + priv_key->key_id_old = i_strdup(key_id_old); + return 0; +} + +void mail_crypt_global_keys_init(struct mail_crypt_global_keys *global_keys_r) +{ + i_zero(global_keys_r); + i_array_init(&global_keys_r->private_keys, 4); +} + +void mail_crypt_global_keys_free(struct mail_crypt_global_keys *global_keys) +{ + struct mail_crypt_global_private_key *priv_key; + + if (global_keys->public_key != NULL) + dcrypt_key_unref_public(&global_keys->public_key); + + if (!array_is_created(&global_keys->private_keys)) + return; + array_foreach_modifiable(&global_keys->private_keys, priv_key) { + dcrypt_key_unref_private(&priv_key->key); + i_free(priv_key->key_id); + i_free(priv_key->key_id_old); + } + array_free(&global_keys->private_keys); +} + +struct dcrypt_private_key * +mail_crypt_global_key_find(struct mail_crypt_global_keys *global_keys, + const char *pubkey_digest) +{ + const struct mail_crypt_global_private_key *priv_key; + + if (!array_is_created(&global_keys->private_keys)) + return NULL; + + array_foreach(&global_keys->private_keys, priv_key) { + if (strcmp(priv_key->key_id, pubkey_digest) == 0) + return priv_key->key; + if (priv_key->key_id_old != NULL && + strcmp(priv_key->key_id_old, pubkey_digest) == 0) + return priv_key->key; + } + return NULL; +} diff --git a/src/plugins/mail-crypt/mail-crypt-global-key.h b/src/plugins/mail-crypt/mail-crypt-global-key.h new file mode 100644 index 0000000..6f4679a --- /dev/null +++ b/src/plugins/mail-crypt/mail-crypt-global-key.h @@ -0,0 +1,38 @@ +#ifndef MAIL_CRYPT_GLOBAL_KEY_H +#define MAIL_CRYPT_GLOBAL_KEY_H + +struct mail_crypt_global_private_key { + struct dcrypt_private_key *key; + char *key_id, *key_id_old; +}; + +struct mail_crypt_global_keys { + struct dcrypt_public_key *public_key; + ARRAY(struct mail_crypt_global_private_key) private_keys; +}; + +struct mail_user; + +int mail_crypt_global_keys_load(struct mail_user *user, const char *set_prefix, + struct mail_crypt_global_keys *global_keys_r, + bool ignore_privkey_errors, + const char **error_r); +int mail_crypt_global_keys_load_pluginenv(const char *set_prefix, + struct mail_crypt_global_keys *global_keys_r, + const char **error_r); +void mail_crypt_global_keys_init(struct mail_crypt_global_keys *global_keys_r); +void mail_crypt_global_keys_free(struct mail_crypt_global_keys *global_keys); + +int mail_crypt_load_global_public_key(const char *set_key, const char *key_data, + struct mail_crypt_global_keys *global_keys, + const char **error_r); +int mail_crypt_load_global_private_key(const char *set_key, const char *key_data, + const char *set_pw, const char *key_password, + struct mail_crypt_global_keys *global_keys, + const char **error_r); + +struct dcrypt_private_key * +mail_crypt_global_key_find(struct mail_crypt_global_keys *global_keys, + const char *pubkey_digest); + +#endif diff --git a/src/plugins/mail-crypt/mail-crypt-key.c b/src/plugins/mail-crypt/mail-crypt-key.c new file mode 100644 index 0000000..22c86e3 --- /dev/null +++ b/src/plugins/mail-crypt/mail-crypt-key.c @@ -0,0 +1,1242 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "dict.h" +#include "array.h" +#include "var-expand.h" +#include "mail-storage.h" +#include "mailbox-attribute.h" +#include "mail-crypt-common.h" +#include "mail-crypt-key.h" +#include "mail-crypt-plugin.h" +#include "mail-user.h" +#include "hex-binary.h" +#include "safe-memset.h" +#include "base64.h" +#include "sha2.h" + +struct mail_crypt_key_cache_entry { + struct mail_crypt_key_cache_entry *next; + + char *pubid; + /* this is lazily initialized */ + struct dcrypt_keypair pair; +}; + +static +int mail_crypt_get_key_cache(struct mail_crypt_key_cache_entry *cache, + const char *pubid, + struct dcrypt_private_key **privkey_r, + struct dcrypt_public_key **pubkey_r) +{ + for(struct mail_crypt_key_cache_entry *ent = cache; + ent != NULL; ent = ent->next) + { + if (strcmp(pubid, ent->pubid) == 0) { + if (privkey_r != NULL && ent->pair.priv != NULL) { + dcrypt_key_ref_private(ent->pair.priv); + *privkey_r = ent->pair.priv; + return 1; + } else if (pubkey_r != NULL && ent->pair.pub != NULL) { + dcrypt_key_ref_public(ent->pair.pub); + *pubkey_r = ent->pair.pub; + return 1; + } else if ((privkey_r == NULL && pubkey_r == NULL) || + (ent->pair.priv == NULL && + ent->pair.pub == NULL)) { + i_unreached(); + } + } + } + return 0; +} + +static +void mail_crypt_put_key_cache(struct mail_crypt_key_cache_entry **cache, + const char *pubid, + struct dcrypt_private_key *privkey, + struct dcrypt_public_key *pubkey) +{ + for(struct mail_crypt_key_cache_entry *ent = *cache; + ent != NULL; ent = ent->next) + { + if (strcmp(pubid, ent->pubid) == 0) { + if (privkey != NULL) { + if (ent->pair.priv == NULL) { + ent->pair.priv = privkey; + dcrypt_key_ref_private(ent->pair.priv); + } + } else if (pubkey != NULL) { + if (ent->pair.pub == NULL) { + ent->pair.pub = pubkey; + dcrypt_key_ref_public(ent->pair.pub); + } + } else + i_unreached(); + return; + } + } + + /* not found */ + struct mail_crypt_key_cache_entry *ent = + i_new(struct mail_crypt_key_cache_entry, 1); + ent->pubid = i_strdup(pubid); + ent->pair.priv = privkey; + ent->pair.pub = pubkey; + if (ent->pair.priv != NULL) + dcrypt_key_ref_private(ent->pair.priv); + if (ent->pair.pub != NULL) + dcrypt_key_ref_public(ent->pair.pub); + + if (*cache == NULL) { + *cache = ent; + } else { + ent->next = *cache; + *cache = ent; + } +} + +void mail_crypt_key_cache_destroy(struct mail_crypt_key_cache_entry **cache) +{ + struct mail_crypt_key_cache_entry *next, *cur = *cache; + + *cache = NULL; + + while(cur != NULL) { + next = cur->next; + i_free(cur->pubid); + if (cur->pair.priv != NULL) + dcrypt_key_unref_private(&cur->pair.priv); + if (cur->pair.pub != NULL) + dcrypt_key_unref_public(&cur->pair.pub); + i_free(cur); + cur = next; + } +} + +int mail_crypt_private_key_id_match(struct dcrypt_private_key *key, + const char *pubid, const char **error_r) +{ + i_assert(key != NULL); + buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); + if (!dcrypt_key_id_private(key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, + error_r)) + return -1; + const char *hash = binary_to_hex(key_id->data, key_id->used); + if (strcmp(pubid, hash) == 0) return 1; + + buffer_set_used_size(key_id, 0); + if (!dcrypt_key_id_private_old(key, key_id, error_r)) { + return -1; + } + hash = binary_to_hex(key_id->data, key_id->used); + + if (strcmp(pubid, hash) != 0) { + *error_r = t_strdup_printf("Key %s does not match given ID %s", + hash, pubid); + return 0; + } + return 1; +} + +int mail_crypt_public_key_id_match(struct dcrypt_public_key *key, + const char *pubid, const char **error_r) +{ + i_assert(key != NULL); + buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); + if (!dcrypt_key_id_public(key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, + error_r)) + return -1; + const char *hash = binary_to_hex(key_id->data, key_id->used); + if (strcmp(pubid, hash) == 0) return 1; + + buffer_set_used_size(key_id, 0); + if (!dcrypt_key_id_public_old(key, key_id, error_r)) { + return -1; + } + hash = binary_to_hex(key_id->data, key_id->used); + + if (strcmp(pubid, hash) != 0) { + *error_r = t_strdup_printf("Key %s does not match given ID %s", + hash, pubid); + return 0; + } + return 1; +} + +static +int mail_crypt_env_get_private_key(struct mail_user *user, const char *pubid, + struct dcrypt_private_key **key_r, + const char **error_r) +{ + struct mail_crypt_global_keys global_keys; + int ret = 0; + if (mail_crypt_global_keys_load(user, "mail_crypt", &global_keys, + TRUE, error_r) < 0) { + mail_crypt_global_keys_free(&global_keys); + return -1; + } + + /* see if we got a key */ + struct dcrypt_private_key *key = + mail_crypt_global_key_find(&global_keys, pubid); + + if (key != NULL) { + dcrypt_key_ref_private(key); + *key_r = key; + ret = 1; + } + + mail_crypt_global_keys_free(&global_keys); + + return ret; +} + +static +const char *mail_crypt_get_key_path(bool user_key, bool public, const char *pubid) +{ + const char *ret = t_strdup_printf("%s%s%s", + user_key ? USER_CRYPT_PREFIX : + BOX_CRYPT_PREFIX, + public ? PUBKEYS_PREFIX : + PRIVKEYS_PREFIX, + pubid); + return ret; +} + +static +int mail_crypt_decrypt_private_key(struct mailbox *box, const char *pubid, + const char *data, + struct dcrypt_private_key **key_r, + const char **error_r) +{ + enum dcrypt_key_kind key_kind; + enum dcrypt_key_encryption_type enc_type; + const char *enc_hash = NULL, *key_hash = NULL, *pw = NULL; + struct dcrypt_private_key *key = NULL, *dec_key = NULL; + struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); + int ret = 0; + + i_assert(pubid != NULL); + i_assert(data != NULL); + + /* see what the key needs for decrypting */ + if (!dcrypt_key_string_get_info(data, NULL, NULL, &key_kind, + &enc_type, &enc_hash, &key_hash, error_r)) { + return -1; + } + + if (key_kind != DCRYPT_KEY_KIND_PRIVATE) { + *error_r = t_strdup_printf("Cannot use key %s: " + "Expected private key, got public key", + pubid); + return -1; + } + + if (key_hash != NULL && strcmp(key_hash, pubid) != 0) { + *error_r = t_strdup_printf("Cannot use key %s: " + "Incorrect key hash %s stored", + pubid, + key_hash); + return -1; + } + + /* see if it needs decrypting */ + if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE) { + /* no key or password */ + } else if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD) { + pw = mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_PASSWORD); + if (pw == NULL) { + *error_r = t_strdup_printf("Cannot decrypt key %s: " + "Password not available", + pubid); + return -1; + } + } else if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY) { + if ((ret = mail_crypt_user_get_private_key(user, enc_hash, + &dec_key, error_r)) <= 0) { + /* last resort, look at environment */ + if (ret == 0 && (ret = mail_crypt_env_get_private_key(user, enc_hash, + &dec_key, error_r)) == 0) { + *error_r = t_strdup_printf("Cannot decrypt key %s: " + "Private key %s not available:", + pubid, enc_hash); + return -1; + } else if (ret < 0) { + *error_r = t_strdup_printf("Cannot decrypt key %s: %s", + pubid, *error_r); + return ret; + } + } + } + + bool res = dcrypt_key_load_private(&key, data, pw, dec_key, error_r); + + if (dec_key != NULL) + dcrypt_key_unref_private(&dec_key); + + if (!res) + return -1; + + if (mail_crypt_private_key_id_match(key, pubid, error_r) <= 0) { + if (key != NULL) + dcrypt_key_unref_private(&key); + return -1; + } + + i_assert(key != NULL); + + *key_r = key; + + return 1; +} + +int mail_crypt_get_private_key(struct mailbox *box, const char *pubid, + bool user_key, bool shared, + struct dcrypt_private_key **key_r, + const char **error_r) +{ + struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); + struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user); + + /* check cache */ + if (mail_crypt_get_key_cache(muser->key_cache, pubid, key_r, NULL) > 0) { + return 1; + } + + struct mail_attribute_value value; + struct dcrypt_private_key *key; + int ret; + const char *attr_name = mail_crypt_get_key_path(user_key, FALSE, pubid); + + if ((ret = mailbox_attribute_get(box, + shared ? MAIL_ATTRIBUTE_TYPE_SHARED : + MAIL_ATTRIBUTE_TYPE_PRIVATE, + attr_name, &value)) <= 0) { + if (ret < 0) { + *error_r = t_strdup_printf("mailbox_attribute_get(%s, %s%s) failed: %s", + mailbox_get_vname(box), + shared ? "/shared/" : + "/priv/", + attr_name, + mailbox_get_last_internal_error(box, NULL)); + } + return ret; + } + + if ((ret = mail_crypt_decrypt_private_key(box, pubid, value.value, + &key, error_r)) <= 0) + return ret; + + i_assert(key != NULL); + + mail_crypt_put_key_cache(&muser->key_cache, pubid, key, NULL); + + *key_r = key; + + return 1; +} + +int mail_crypt_user_get_private_key(struct mail_user *user, const char *pubid, + struct dcrypt_private_key **key_r, + const char **error_r) +{ + struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); + struct mailbox *box = mailbox_alloc(ns->list, "INBOX", + MAILBOX_FLAG_READONLY); + struct mail_attribute_value value; + int ret; + + /* try retrieve currently active user key */ + if (mailbox_open(box) < 0) { + *error_r = t_strdup_printf("mailbox_open(%s) failed: %s", + "INBOX", + mailbox_get_last_internal_error(box, NULL)); + return -1; + } + + if (pubid == NULL) { + if ((ret = mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_SHARED, + USER_CRYPT_PREFIX ACTIVE_KEY_NAME, + &value)) <= 0) { + if (ret < 0) { + *error_r = t_strdup_printf("mailbox_attribute_get(%s, /shared/%s) failed: %s", + mailbox_get_vname(box), + USER_CRYPT_PREFIX ACTIVE_KEY_NAME, + mailbox_get_last_internal_error(box, NULL)); + } + } else { + pubid = value.value; + ret = 1; + } + } else + ret = 1; + + /* try to open key */ + if (ret > 0) + ret = mail_crypt_get_private_key(box, pubid, TRUE, FALSE, + key_r, error_r); + mailbox_free(&box); + return ret; +} + +int mail_crypt_box_get_private_key(struct mailbox *box, + struct dcrypt_private_key **key_r, + const char **error_r) +{ + struct mail_attribute_value value; + int ret; + /* get active key */ + if ((ret = mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_SHARED, + BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, &value)) <= 0) { + if (ret < 0) { + *error_r = t_strdup_printf("mailbox_attribute_get(%s, /shared/%s) failed: %s", + mailbox_get_vname(box), + USER_CRYPT_PREFIX ACTIVE_KEY_NAME, + mailbox_get_last_internal_error(box, NULL)); + } + return ret; + } + + return mail_crypt_get_private_key(box, value.value, + FALSE, FALSE, + key_r, error_r); +} + +static +int mail_crypt_set_private_key(struct mailbox_transaction_context *t, + bool user_key, bool shared, const char *pubid, + struct dcrypt_public_key *enc_key, + struct dcrypt_private_key *key, + const char **error_r) +{ + /* folder keys must be encrypted with some other key, + unless they are shared keys */ + i_assert(user_key || shared || enc_key != NULL); + + buffer_t *data = t_str_new(MAIL_CRYPT_KEY_BUF_SIZE); + const char *pw = NULL; + const char *algo = NULL; + struct mail_user *user = mail_storage_get_user( + mailbox_get_storage( + mailbox_transaction_get_mailbox(t))); + const char *attr_name = mail_crypt_get_key_path(user_key, FALSE, pubid); + struct mail_attribute_value value; + int ret; + + if (enc_key != NULL) { + algo = MAIL_CRYPT_KEY_CIPHER; + } else if (user_key && + (pw = mail_user_plugin_getenv(user,MAIL_CRYPT_USERENV_PASSWORD)) + != NULL) { + algo = MAIL_CRYPT_PW_CIPHER; + } + + /* export key */ + if (!dcrypt_key_store_private(key, DCRYPT_FORMAT_DOVECOT, algo, data, + pw, enc_key, error_r)) { + return -1; + } + + /* store it */ + value.value_stream = NULL; + value.value = str_c(data); + value.last_change = 0; + + if ((ret = mailbox_attribute_set(t, + shared ? MAIL_ATTRIBUTE_TYPE_SHARED : + MAIL_ATTRIBUTE_TYPE_PRIVATE, + attr_name, + &value)) < 0) { + *error_r = t_strdup_printf("mailbox_attribute_set(%s, %s/%s) failed: %s", + mailbox_get_vname(mailbox_transaction_get_mailbox(t)), + shared ? "/shared" : "/priv", + attr_name, + mailbox_get_last_internal_error( + mailbox_transaction_get_mailbox(t), NULL)); + } + + safe_memset(buffer_get_modifiable_data(data, NULL), 0, data->used); + + return ret; +} + +int mail_crypt_user_set_private_key(struct mail_user *user, const char *pubid, + struct dcrypt_private_key *key, + const char **error_r) +{ + struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); + struct mailbox *box = mailbox_alloc(ns->list, "INBOX", + MAILBOX_FLAG_READONLY); + struct dcrypt_private_key *env_key = NULL; + struct dcrypt_public_key *enc_key = NULL; + struct mailbox_transaction_context *t; + int ret; + + if ((ret = mail_crypt_env_get_private_key(user, NULL, &env_key, + error_r)) < 0) { + return -1; + } else if (ret > 0) { + dcrypt_key_convert_private_to_public(env_key, &enc_key); + dcrypt_key_unref_private(&env_key); + } + + if (mail_user_plugin_getenv(user, MAIL_CRYPT_REQUIRE_ENCRYPTED_USER_KEY) != NULL && + mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_PASSWORD) == NULL && + mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_KEY) == NULL) + { + *error_r = MAIL_CRYPT_REQUIRE_ENCRYPTED_USER_KEY " set, cannot " + "generate user keypair without password or key"; + return -1; + } + + if (mailbox_open(box) < 0) { + *error_r = t_strdup_printf("mailbox_open(%s) failed: %s", + "INBOX", + mailbox_get_last_internal_error(box, NULL)); + return -1; + } + + t = mailbox_transaction_begin(box, 0, __func__); + + if ((ret = mail_crypt_set_private_key(t, TRUE, FALSE, pubid, enc_key, key, + error_r)) < 0) { + mailbox_transaction_rollback(&t); + } else if ((ret = mailbox_transaction_commit(&t)) < 0) { + *error_r = t_strdup_printf("mailbox_transaction_commit(%s) failed: %s", + mailbox_get_vname(box), + mailbox_get_last_internal_error(box, NULL)); + } + + mailbox_free(&box); + + return ret; +} + +int mail_crypt_box_set_private_key(struct mailbox *box, const char *pubid, + struct dcrypt_private_key *key, + struct dcrypt_public_key *user_key, + const char **error_r) +{ + int ret; + struct mailbox_transaction_context *t; + + t = mailbox_transaction_begin(box, 0, __func__); + if ((ret = mail_crypt_set_private_key(t, FALSE, FALSE, pubid, user_key, + key, error_r)) < 0) { + mailbox_transaction_rollback(&t); + } else if ((ret = mailbox_transaction_commit(&t)) < 0) { + *error_r = t_strdup_printf("mailbox_transaction_commit(%s) failed: %s", + mailbox_get_vname(box), + mailbox_get_last_internal_error(box, NULL)); + } + + return ret; +} + +static +int mail_crypt_get_public_key(struct mailbox *box, const char *pubid, + bool user_key, struct dcrypt_public_key **key_r, + const char **error_r) +{ + struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); + struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user); + + /* check cache */ + if (mail_crypt_get_key_cache(muser->key_cache, pubid, NULL, key_r) > 0) { + return 1; + } + + enum dcrypt_key_kind key_kind; + const char *key_hash = NULL; + struct dcrypt_public_key *key; + struct mail_attribute_value value; + int ret; + const char *attr_name = mail_crypt_get_key_path(user_key, TRUE, pubid); + + if ((ret = mailbox_attribute_get(box, + MAIL_ATTRIBUTE_TYPE_SHARED, + attr_name, &value)) <= 0) { + if (ret < 0) { + *error_r = t_strdup_printf("mailbox_attribute_get(%s, %s) failed: %s", + mailbox_get_vname(box), + attr_name, + mailbox_get_last_internal_error(box, NULL)); + } + return ret; + } + + if (!dcrypt_key_string_get_info(value.value, NULL, NULL, &key_kind, + NULL, NULL, &key_hash, error_r)) { + return -1; + } + + if (key_kind != DCRYPT_KEY_KIND_PUBLIC) { + *error_r = t_strdup_printf("Cannot use key %s: " + "Expected public key, got private key", + pubid); + return -1; + } + + if (key_hash != NULL && strcmp(key_hash, pubid) != 0) { + *error_r = t_strdup_printf("Cannot use key %s: " + "Incorrect key hash %s stored", + pubid, key_hash); + return -1; + } + + /* load the key */ + if (!dcrypt_key_load_public(&key, value.value, error_r)) { + return -1; + } + + if (pubid != NULL && + mail_crypt_public_key_id_match(key, pubid, error_r) <= 0) { + dcrypt_key_unref_public(&key); + return -1; + } + + mail_crypt_put_key_cache(&muser->key_cache, pubid, NULL, key); + + *key_r = key; + + return 1; +} + +int mail_crypt_user_get_public_key(struct mail_user *user, + struct dcrypt_public_key **key_r, + const char **error_r) +{ + struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); + struct mailbox *box = mailbox_alloc(ns->list, "INBOX", + MAILBOX_FLAG_READONLY); + struct mail_attribute_value value; + int ret; + + /* try retrieve currently active user key */ + if (mailbox_open(box) < 0) { + *error_r = t_strdup_printf("mailbox_open(%s) failed: %s", + "INBOX", + mailbox_get_last_internal_error(box, NULL)); + return -1; + } + + if ((ret = mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_SHARED, + USER_CRYPT_PREFIX ACTIVE_KEY_NAME, + &value)) <= 0) { + if (ret < 0) { + *error_r = t_strdup_printf("mailbox_attribute_get(%s, /shared/%s) failed: %s", + mailbox_get_vname(box), + USER_CRYPT_PREFIX ACTIVE_KEY_NAME, + mailbox_get_last_internal_error(box, NULL)); + } + } else { + ret = mail_crypt_get_public_key(box, value.value, TRUE, key_r, error_r); + } + + mailbox_free(&box); + return ret; +} + +int mail_crypt_box_get_public_key(struct mailbox *box, + struct dcrypt_public_key **key_r, + const char **error_r) +{ + struct mail_attribute_value value; + int ret; + + if ((ret = mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_SHARED, + BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, + &value)) <= 0) { + if (ret < 0) { + *error_r = t_strdup_printf("mailbox_attribute_get(%s, /shared/%s) failed: %s", + mailbox_get_vname(box), + BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, + mailbox_get_last_internal_error(box, NULL)); + } + return ret; + } + return mail_crypt_get_public_key(box, value.value, FALSE, key_r, error_r); +} + +static +int mail_crypt_set_public_key(struct mailbox_transaction_context *t, + bool user_key, const char *pubid, + struct dcrypt_public_key *key, + const char **error_r) +{ + buffer_t *data = t_str_new(MAIL_CRYPT_KEY_BUF_SIZE); + const char *attr_name = mail_crypt_get_key_path(user_key, TRUE, pubid); + struct mail_attribute_value value; + + /* export key */ + if (!dcrypt_key_store_public(key, DCRYPT_FORMAT_DOVECOT, data, + error_r)) { + return -1; + } + + /* store it */ + value.value_stream = NULL; + value.value = str_c(data); + value.last_change = 0; + + if (mailbox_attribute_set(t, + MAIL_ATTRIBUTE_TYPE_SHARED, + attr_name, + &value) < 0) { + *error_r = t_strdup_printf("mailbox_attribute_set(%s, %s/%s) failed: %s", + mailbox_get_vname(mailbox_transaction_get_mailbox(t)), + "/shared", + attr_name, + mailbox_get_last_internal_error( + mailbox_transaction_get_mailbox(t), NULL)); + return -1; + } + + return 0; +} + +int mail_crypt_user_set_public_key(struct mail_user *user, const char *pubid, + struct dcrypt_public_key *key, + const char **error_r) +{ + struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces); + struct mailbox *box = mailbox_alloc(ns->list, "INBOX", + MAILBOX_FLAG_READONLY); + struct mailbox_transaction_context *t; + struct mail_attribute_value value; + int ret; + + /* try retrieve currently active user key */ + if (mailbox_open(box) < 0) { + *error_r = t_strdup_printf("mailbox_open(%s) failed: %s", + "INBOX", + mailbox_get_last_internal_error(box, NULL)); + return -1; + } + + t = mailbox_transaction_begin(box, 0, __func__); + + if ((ret = mail_crypt_set_public_key(t, TRUE, pubid, key, + error_r)) == 0) { + value.value_stream = NULL; + value.value = pubid; + value.last_change = 0; + + if ((ret = mailbox_attribute_set(t, MAIL_ATTRIBUTE_TYPE_SHARED, + USER_CRYPT_PREFIX ACTIVE_KEY_NAME, + &value)) < 0) { + *error_r = t_strdup_printf("mailbox_attribute_set(%s, /shared/%s) failed: %s", + mailbox_get_vname(box), + USER_CRYPT_PREFIX ACTIVE_KEY_NAME, + mailbox_get_last_internal_error(box, NULL)); + } + } + + if (ret < 0) { + mailbox_transaction_rollback(&t); + } else if (mailbox_transaction_commit(&t) < 0) { + *error_r = t_strdup_printf("mailbox_transaction_commit(%s) failed: %s", + mailbox_get_vname(box), + mailbox_get_last_internal_error(box, NULL)); + ret = -1; + } + + mailbox_free(&box); + + return ret; +} + +int mail_crypt_box_set_public_key(struct mailbox *box, const char *pubid, + struct dcrypt_public_key *key, + const char **error_r) +{ + int ret; + struct mailbox_transaction_context *t; + struct mail_attribute_value value; + + t = mailbox_transaction_begin(box, 0, __func__); + if ((ret = mail_crypt_set_public_key(t, FALSE, pubid, key, + error_r)) == 0) { + value.value_stream = NULL; + value.value = pubid; + value.last_change = 0; + + if ((ret = mailbox_attribute_set(t, MAIL_ATTRIBUTE_TYPE_SHARED, + BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, + &value)) < 0) { + *error_r = t_strdup_printf("mailbox_attribute_set(%s, /shared/%s) failed: %s", + mailbox_get_vname(box), + BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, + mailbox_get_last_internal_error(box, NULL)); + } + } + + if (ret < 0) { + mailbox_transaction_rollback(&t); + } else if (mailbox_transaction_commit(&t) < 0) { + *error_r = t_strdup_printf("mailbox_transaction_commit(%s) failed: %s", + mailbox_get_vname(box), + mailbox_get_last_internal_error(box, NULL)); + ret = -1; + } + + return ret; + +} + +static +int mail_crypt_user_set_keys(struct mail_user *user, + const char *pubid, + struct dcrypt_private_key *privkey, + struct dcrypt_public_key *pubkey, + const char **error_r) +{ + if (mail_crypt_user_set_private_key(user, pubid, privkey, error_r) < 0) + return -1; + if (mail_crypt_user_set_public_key(user, pubid, pubkey, error_r) < 0) + return -1; + return 0; +} + +static +int mail_crypt_box_set_keys(struct mailbox *box, + const char *pubid, + struct dcrypt_private_key *privkey, + struct dcrypt_public_key *user_key, + struct dcrypt_public_key *pubkey, + const char **error_r) +{ + if (mail_crypt_box_set_private_key(box, pubid, privkey, user_key, + error_r) < 0) + return -1; + if (mail_crypt_box_set_public_key(box, pubid, pubkey, error_r) < 0) + return -1; + return 0; +} + +int mail_crypt_box_get_shared_key(struct mailbox *box, + const char *pubid, + struct dcrypt_private_key **key_r, + const char **error_r) +{ + struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); + struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user); + + struct dcrypt_private_key *key = NULL; + struct mail_attribute_value value; + int ret; + + /* check cache */ + if (mail_crypt_get_key_cache(muser->key_cache, pubid, key_r, NULL) > 0) { + return 1; + } + + const char *hexname = + binary_to_hex((const unsigned char*)user->username, + strlen(user->username)); + + const char *attr_name = t_strdup_printf(BOX_CRYPT_PREFIX + PRIVKEYS_PREFIX"%s/%s", + hexname, + pubid); + + if ((ret = mailbox_attribute_get(box, + MAIL_ATTRIBUTE_TYPE_SHARED, + attr_name, &value)) <= 0) { + if (ret < 0) { + *error_r = t_strdup_printf("mailbox_attribute_get(%s, %s) failed: %s", + mailbox_get_vname(box), + attr_name, + mailbox_get_last_internal_error(box, NULL)); + return ret; + } + return mail_crypt_get_private_key(box, pubid, FALSE, TRUE, key_r, + error_r); + } else { + if ((ret = mail_crypt_decrypt_private_key(box, pubid, value.value, + &key, error_r)) <= 0) + return ret; + } + + mail_crypt_put_key_cache(&muser->key_cache, pubid, key, NULL); + + *key_r = key; + + return 1; +} + +int mail_crypt_box_set_shared_key(struct mailbox_transaction_context *t, + const char *pubid, + struct dcrypt_private_key *privkey, + const char *target_uid, + struct dcrypt_public_key *user_key, + const char **error_r) +{ + struct mail_attribute_value value; + buffer_t *data = t_str_new(MAIL_CRYPT_KEY_BUF_SIZE); + int ret; + const char *attr_name; + const char *algo = NULL; + + i_assert(target_uid == NULL || user_key != NULL); + + if (target_uid != NULL) { + /* hash target UID */ + algo = MAIL_CRYPT_KEY_CIPHER; + const char *hexname = + binary_to_hex((const unsigned char*)target_uid, + strlen(target_uid)); + attr_name = t_strdup_printf(BOX_CRYPT_PREFIX + PRIVKEYS_PREFIX"%s/%s", + hexname, + pubid); + } else { + attr_name = t_strdup_printf(BOX_CRYPT_PREFIX + PRIVKEYS_PREFIX"%s", + pubid); + } + + if (!dcrypt_key_store_private(privkey, DCRYPT_FORMAT_DOVECOT, + algo, data, + NULL, user_key, error_r)) { + return -1; + } + + value.value_stream = NULL; + value.value = str_c(data); + value.last_change = 0; + + if ((ret = mailbox_attribute_set(t, MAIL_ATTRIBUTE_TYPE_SHARED, + attr_name, &value)) < 0) { + *error_r = t_strdup_printf("mailbox_attribute_set(%s, /shared/%s) failed: %s", + mailbox_get_vname( + mailbox_transaction_get_mailbox(t)), + attr_name, + mailbox_get_last_internal_error( + mailbox_transaction_get_mailbox(t), + NULL)); + } + + safe_memset(buffer_get_modifiable_data(data, NULL), 0, data->used); + + return ret; +} + +int mail_crypt_box_unset_shared_key(struct mailbox_transaction_context *t, + const char *pubid, + const char *target_uid, + const char **error_r) +{ + int ret; + + const char *hexname = + binary_to_hex((const unsigned char*)target_uid, + strlen(target_uid)); + + const char *attr_name = t_strdup_printf(BOX_CRYPT_PREFIX + PRIVKEYS_PREFIX"%s/%s", + hexname, + pubid); + + if ((ret = mailbox_attribute_unset(t, MAIL_ATTRIBUTE_TYPE_SHARED, + attr_name)) <= 0) { + if (ret < 0) { + *error_r = t_strdup_printf("mailbox_attribute_unset(%s, " + " /shared/%s): failed: %s", + mailbox_get_vname( + mailbox_transaction_get_mailbox(t)), + attr_name, + mailbox_get_last_internal_error( + mailbox_transaction_get_mailbox(t), + NULL)); + } + } + + return ret; +} + +static +int mail_crypt_generate_keypair(const char *curve, + struct dcrypt_keypair *pair_r, + const char **pubid_r, + const char **error_r) +{ + if (curve == NULL) { + *error_r = MAIL_CRYPT_USERENV_CURVE " not set, cannot generate EC key"; + return -1; + } + + if (!dcrypt_keypair_generate(pair_r, DCRYPT_KEY_EC, 0, curve, error_r)) { + return -1; + } + + buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); + if (!dcrypt_key_id_public(pair_r->pub, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, + error_r)) { + dcrypt_keypair_unref(pair_r); + return -1; + } + + *pubid_r = binary_to_hex(key_id->data, key_id->used); + + return 0; +} + +int mail_crypt_user_generate_keypair(struct mail_user *user, + struct dcrypt_keypair *pair, + const char **pubid_r, + const char **error_r) +{ + struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user); + const char *curve = mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_CURVE); + + if (mail_crypt_generate_keypair(curve, pair, pubid_r, error_r) < 0) { + return -1; + } + + if (mail_crypt_user_set_keys(user, *pubid_r, + pair->priv, pair->pub, error_r) < 0) { + dcrypt_keypair_unref(pair); + return -1; + } + + mail_crypt_put_key_cache(&muser->key_cache, *pubid_r, pair->priv, pair->pub); + + return 0; +} + +int mail_crypt_box_generate_keypair(struct mailbox *box, + struct dcrypt_keypair *pair, + struct dcrypt_public_key *user_key, + const char **pubid_r, + const char **error_r) +{ + int ret; + struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box)); + struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user); + const char *curve = mail_user_plugin_getenv(user, + MAIL_CRYPT_USERENV_CURVE); + + if (user_key == NULL) { + if ((ret = mail_crypt_user_get_public_key(user, + &user_key, + error_r)) <= 0) { + if (ret < 0) + return ret; + /* generate keypair */ + struct dcrypt_keypair user_pair; + const char *user_pubid; + if (mail_crypt_user_generate_keypair(user, &user_pair, + &user_pubid, + error_r) < 0) { + return -1; + } + + mail_crypt_put_key_cache(&muser->key_cache, user_pubid, + user_pair.priv, user_pair.pub); + + user_key = user_pair.pub; + dcrypt_key_unref_private(&user_pair.priv); + } + } else { + dcrypt_key_ref_public(user_key); + } + + if ((ret = mail_crypt_generate_keypair(curve, pair, pubid_r, error_r)) < 0) { + /* failed */ + } else if ((ret = mail_crypt_box_set_keys(box, *pubid_r, + pair->priv, user_key, pair->pub, + error_r)) < 0) { + dcrypt_keypair_unref(pair); + } else { + mail_crypt_put_key_cache(&muser->key_cache, *pubid_r, pair->priv, + pair->pub); + } + + dcrypt_key_unref_public(&user_key); + + return ret; +} + +int mail_crypt_box_get_pvt_digests(struct mailbox *box, pool_t pool, + enum mail_attribute_type type, + ARRAY_TYPE(const_string) *digests, + const char **error_r) +{ + struct mailbox_attribute_iter *iter; + const char *key; + int ret; + + iter = mailbox_attribute_iter_init(box, type, + BOX_CRYPT_PREFIX PRIVKEYS_PREFIX); + while ((key = mailbox_attribute_iter_next(iter)) != NULL) { + key = p_strdup(pool, key); + array_push_back(digests, &key); + } + ret = mailbox_attribute_iter_deinit(&iter); + if (ret < 0) + *error_r = mailbox_get_last_internal_error(box, NULL); + return ret; +} + +int mail_crypt_box_get_private_keys(struct mailbox *box, + ARRAY_TYPE(dcrypt_private_key) *keys_r, + const char **error_r) +{ + struct mailbox_attribute_iter *iter; + iter = mailbox_attribute_iter_init(box, MAIL_ATTRIBUTE_TYPE_PRIVATE, + BOX_CRYPT_PREFIX PRIVKEYS_PREFIX); + const char *id; + int ret; + + while ((id = mailbox_attribute_iter_next(iter)) != NULL) { + struct dcrypt_private_key *key = NULL; + if ((ret = mail_crypt_get_private_key(box, id, FALSE, FALSE, + &key, error_r)) < 0) { + (void)mailbox_attribute_iter_deinit(&iter); + return -1; + } else if (ret > 0) + array_push_back(keys_r, &key); + } + + ret = mailbox_attribute_iter_deinit(&iter); + if (ret < 0) + *error_r = mailbox_get_last_internal_error(box, NULL); + return ret; +} + +int mail_crypt_box_share_private_keys(struct mailbox_transaction_context *t, + struct dcrypt_public_key *dest_pub_key, + const char *dest_user, + const ARRAY_TYPE(dcrypt_private_key) *priv_keys, + const char **error_r) +{ + i_assert(dest_user == NULL || dest_pub_key != NULL); + + struct dcrypt_private_key *priv_key; + buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE); + int ret = 0; + + array_foreach_elem(priv_keys, priv_key) { + ret = -1; + if (!dcrypt_key_id_private(priv_key, MAIL_CRYPT_KEY_ID_ALGORITHM, + key_id, error_r) || + (ret = mail_crypt_box_set_shared_key(t, + binary_to_hex(key_id->data, + key_id->used), + priv_key, dest_user, + dest_pub_key, error_r)) < 0) + break; + } + + return ret; +} + +int +mail_crypt_user_get_or_gen_public_key(struct mail_user *user, + struct dcrypt_public_key **pub_r, + const char **error_r) +{ + i_assert(user != NULL); + i_assert(pub_r != NULL); + i_assert(error_r != NULL); + + int ret; + if ((ret = mail_crypt_user_get_public_key(user, pub_r, error_r)) == 0) { + struct dcrypt_keypair pair; + const char *pubid = NULL; + if (mail_crypt_user_generate_keypair(user, &pair, + &pubid, error_r) < 0) { + return -1; + } + *pub_r = pair.pub; + dcrypt_key_unref_private(&pair.priv); + } else + return ret; + return 0; +} + +int +mail_crypt_box_get_or_gen_public_key(struct mailbox *box, + struct dcrypt_public_key **pub_r, + const char **error_r) +{ + i_assert(box != NULL); + i_assert(pub_r != NULL); + i_assert(error_r != NULL); + + struct mail_user *user = + mail_storage_get_user(mailbox_get_storage(box)); + int ret; + if ((ret = mail_crypt_box_get_public_key(box, pub_r, error_r)) == 0) { + struct dcrypt_public_key *user_key; + if (mail_crypt_user_get_or_gen_public_key(user, &user_key, + error_r) < 0) { + return -1; + } + + struct dcrypt_keypair pair; + const char *pubid = NULL; + if (mail_crypt_box_generate_keypair(box, &pair, user_key, + &pubid, error_r) < 0) { + return -1; + } + *pub_r = pair.pub; + dcrypt_key_unref_public(&user_key); + dcrypt_key_unref_private(&pair.priv); + } else + return ret; + return 0; +} + +bool mail_crypt_acl_secure_sharing_enabled(struct mail_user *user) +{ + const char *env = + mail_user_plugin_getenv(user, MAIL_CRYPT_ACL_SECURE_SHARE_SETTING); + + /* disabled by default */ + bool ret = FALSE; + + if (env != NULL) { + /* enable unless specifically + requested not to */ + ret = TRUE; + switch (env[0]) { + case 'n': + case 'N': + case '0': + case 'f': + case 'F': + ret = FALSE; + } + } + + return ret; +} + +static const struct mailbox_attribute_internal mailbox_internal_attributes[] = { + { .type = MAIL_ATTRIBUTE_TYPE_PRIVATE, + .key = BOX_CRYPT_PREFIX, + .flags = MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN + }, + { .type = MAIL_ATTRIBUTE_TYPE_SHARED, + .key = BOX_CRYPT_PREFIX, + .flags = MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN + }, + { .type = MAIL_ATTRIBUTE_TYPE_PRIVATE, + .key = USER_CRYPT_PREFIX, + .flags = MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN + }, + { .type = MAIL_ATTRIBUTE_TYPE_SHARED, + .key = USER_CRYPT_PREFIX, + .flags = MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN + } +}; + +void mail_crypt_key_register_mailbox_internal_attributes(void) +{ + mailbox_attribute_register_internals(mailbox_internal_attributes, + N_ELEMENTS(mailbox_internal_attributes)); +} diff --git a/src/plugins/mail-crypt/mail-crypt-key.h b/src/plugins/mail-crypt/mail-crypt-key.h new file mode 100644 index 0000000..f4a724a --- /dev/null +++ b/src/plugins/mail-crypt/mail-crypt-key.h @@ -0,0 +1,119 @@ +#ifndef MAIL_CRYPT_KEY +#define MAIL_CRYPT_KEY + +#include "mail-crypt-common.h" +#include "mail-crypt-global-key.h" +#include "mail-storage.h" + +/* + For mailboxes: + + shared/<mailbox GUID>/.../crypt/active = digest for the active public key + that is used for encrypting new emails + shared/<mailbox GUID>/.../crypt/pubkeys/<digest> = <key> + private/<mailbox GUID>/.../crypt/privkeys/<digest> = <key> + + Similarly for users: + + shared/<INBOX GUID>/.../crypt/active = digest for the active public key that + is used for encrypting new folder keys + shared/<INBOX GUID>/.../crypt/pubkeys/<digest> = <key> + private/<INBOX GUID>/.../crypt/privkeys/<digest> = <key> +*/ + +struct mail_crypt_key_cache_entry; + +/** + * key cache management functions + */ +void mail_crypt_key_cache_destroy(struct mail_crypt_key_cache_entry **cache); +void mail_crypt_key_register_mailbox_internal_attributes(void); + +/* returns -1 on error, 0 not found, 1 = found */ +int mail_crypt_get_private_key(struct mailbox *box, const char *pubid, + bool user_key, bool shared, + struct dcrypt_private_key **key_r, + const char **error_r); +int mail_crypt_user_get_private_key(struct mail_user *user, const char *pubid, + struct dcrypt_private_key **key_r, + const char **error_r); +int mail_crypt_box_get_private_key(struct mailbox *box, + struct dcrypt_private_key **key_r, + const char **error_r); +int mail_crypt_box_get_private_keys(struct mailbox *box, + ARRAY_TYPE(dcrypt_private_key) *keys_r, + const char **error_r); +int mail_crypt_user_get_public_key(struct mail_user *user, + struct dcrypt_public_key **key_r, + const char **error_r); +int mail_crypt_box_get_public_key(struct mailbox *box, + struct dcrypt_public_key **key_r, + const char **error_r); +int mail_crypt_box_get_shared_key(struct mailbox *box, + const char *pubid, + struct dcrypt_private_key **key_r, + const char **error_r); +/* returns -1 on error, 0 no match , 1 = match */ +int mail_crypt_private_key_id_match(struct dcrypt_private_key *key, + const char *pubid, const char **error_r); +int mail_crypt_public_key_id_match(struct dcrypt_public_key *key, + const char *pubid, const char **error_r); +/* returns -1 on error, 0 = ok */ +int mail_crypt_user_set_private_key(struct mail_user *user, const char *pubid, + struct dcrypt_private_key *key, + const char **error_r); +int mail_crypt_box_set_private_key(struct mailbox *box, const char *pubid, + struct dcrypt_private_key *key, + struct dcrypt_public_key *user_key, + const char **error_r); +int mail_crypt_user_set_public_key(struct mail_user *user, const char *pubid, + struct dcrypt_public_key *key, + const char **error_r); +int mail_crypt_box_set_public_key(struct mailbox *box, const char *pubid, + struct dcrypt_public_key *key, + const char **error_r); +int mail_crypt_user_generate_keypair(struct mail_user *user, + struct dcrypt_keypair *pair, + const char **pubid_r, + const char **error_r); +int mail_crypt_box_generate_keypair(struct mailbox *box, + struct dcrypt_keypair *pair, + struct dcrypt_public_key *user_key, + const char **pubid_r, + const char **error_r); +/* returns -1 on error, 0 = ok */ +int mail_crypt_box_set_shared_key(struct mailbox_transaction_context *t, + const char *pubid, + struct dcrypt_private_key *privkey, + const char *target_uid, + struct dcrypt_public_key *user_key, + const char **error_r); +int mail_crypt_box_unset_shared_key(struct mailbox_transaction_context *t, + const char *pubid, + const char *target_uid, + const char **error_r); +int mail_crypt_box_share_private_keys(struct mailbox_transaction_context *t, + struct dcrypt_public_key *dest_pub_key, + const char *dest_user, + const ARRAY_TYPE(dcrypt_private_key) *priv_keys, + const char **error_r); +/* returns -1 on error, 0 = ok + these will also attempt to generate a keypair +*/ +int mail_crypt_user_get_or_gen_public_key(struct mail_user *user, + struct dcrypt_public_key **pub_key_r, + const char **error_r); +int mail_crypt_box_get_or_gen_public_key(struct mailbox *box, + struct dcrypt_public_key **pub_key_r, + const char **error_r); + +/* Lookup all private keys' digests. Returns 0 if ok, -1 on error. */ +int mail_crypt_box_get_pvt_digests(struct mailbox *box, pool_t pool, + enum mail_attribute_type type, + ARRAY_TYPE(const_string) *digests, + const char **error_r); + +/* is secure sharing enabled */ +bool mail_crypt_acl_secure_sharing_enabled(struct mail_user *user); + +#endif diff --git a/src/plugins/mail-crypt/mail-crypt-plugin.c b/src/plugins/mail-crypt/mail-crypt-plugin.c new file mode 100644 index 0000000..21da348 --- /dev/null +++ b/src/plugins/mail-crypt/mail-crypt-plugin.c @@ -0,0 +1,486 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +/* FIXME: cache handling could be useful to move to Dovecot core, so that if + we're using this plugin together with zlib plugin there would be just one + cache. */ + +#include "lib.h" +#include "ioloop.h" +#include "randgen.h" +#include "module-dir.h" +#include "str.h" +#include "safe-mkstemp.h" +#include "istream.h" +#include "istream-decrypt.h" +#include "istream-seekable.h" +#include "ostream.h" +#include "ostream-encrypt.h" +#include "mail-user.h" +#include "mail-copy.h" +#include "index-storage.h" +#include "index-mail.h" +#include "mail-crypt-common.h" +#include "mail-crypt-key.h" +#include "mail-crypt-plugin.h" +#include "sha2.h" +#include "dcrypt-iostream.h" +#include "hex-binary.h" + +struct mail_crypt_mailbox { + union mailbox_module_context module_ctx; + struct dcrypt_public_key *pub_key; +}; + +const char *mail_crypt_plugin_version = DOVECOT_ABI_VERSION; + +#define MAIL_CRYPT_MAIL_CONTEXT(obj) \ + MODULE_CONTEXT_REQUIRE(obj, mail_crypt_mail_module) +#define MAIL_CRYPT_CONTEXT(obj) \ + MODULE_CONTEXT_REQUIRE(obj, mail_crypt_storage_module) +#define MAIL_CRYPT_USER_CONTEXT(obj) \ + MODULE_CONTEXT(obj, mail_crypt_user_module) +#define MAIL_CRYPT_USER_CONTEXT_REQUIRE(obj) \ + MODULE_CONTEXT_REQUIRE(obj, mail_crypt_user_module) + +static MODULE_CONTEXT_DEFINE_INIT(mail_crypt_user_module, + &mail_user_module_register); +static MODULE_CONTEXT_DEFINE_INIT(mail_crypt_storage_module, + &mail_storage_module_register); +static MODULE_CONTEXT_DEFINE_INIT(mail_crypt_mail_module, + &mail_module_register); + +struct mail_crypt_user *mail_crypt_get_mail_crypt_user(struct mail_user *user) +{ + return MAIL_CRYPT_USER_CONTEXT(user); +} + +static bool mail_crypt_is_stream_encrypted(struct istream *input) +{ + const unsigned char *data = NULL; + size_t size; + + if (i_stream_read_data(input, &data, &size, + sizeof(IOSTREAM_CRYPT_MAGIC)) <= 0) { + return FALSE; + } + + if (memcmp(data, IOSTREAM_CRYPT_MAGIC, + sizeof(IOSTREAM_CRYPT_MAGIC)) != 0) { + return FALSE; + } + return TRUE; +} + +static void mail_crypt_cache_close(struct mail_crypt_user *muser) +{ + struct mail_crypt_cache *cache = &muser->cache; + + timeout_remove(&cache->to); + i_stream_unref(&cache->input); + i_zero(cache); +} + +static struct istream * +mail_crypt_cache_open(struct mail_crypt_user *muser, struct mail *mail, + struct istream *input) +{ + struct mail_crypt_cache *cache = &muser->cache; + struct istream *inputs[2]; + string_t *temp_prefix = t_str_new(128); + + mail_crypt_cache_close(muser); + + input->seekable = FALSE; + inputs[0] = input; + inputs[1] = NULL; + mail_user_set_get_temp_prefix(temp_prefix, mail->box->storage->user->set); + input = i_stream_create_seekable_path(inputs, + i_stream_get_max_buffer_size(inputs[0]), + str_c(temp_prefix)); + i_stream_unref(&inputs[0]); + + if (mail->uid > 0) { + cache->to = timeout_add(MAIL_CRYPT_MAIL_CACHE_EXPIRE_MSECS, + mail_crypt_cache_close, muser); + cache->box = mail->box; + cache->uid = mail->uid; + cache->input = input; + /* index-mail wants the stream to be destroyed at close, so create + a new stream instead of just increasing reference. */ + return i_stream_create_limit(cache->input, UOFF_T_MAX); + } + + return input; +} + +static int mail_crypt_istream_get_private_key(const char *pubkey_digest, + struct dcrypt_private_key **priv_key_r, + const char **error_r, + void *context) +{ + /* mailbox_crypt_search_all_private_keys requires error_r != NULL */ + i_assert(error_r != NULL); + int ret; + struct mail *_mail = context; + struct mail_crypt_user *muser = + MAIL_CRYPT_USER_CONTEXT_REQUIRE(_mail->box->storage->user); + + *priv_key_r = mail_crypt_global_key_find(&muser->global_keys, + pubkey_digest); + if (*priv_key_r != NULL) { + dcrypt_key_ref_private(*priv_key_r); + return 1; + } + + struct mail_namespace *ns = mailbox_get_namespace(_mail->box); + + if (ns->type == MAIL_NAMESPACE_TYPE_SHARED) { + ret = mail_crypt_box_get_shared_key(_mail->box, pubkey_digest, + priv_key_r, error_r); + /* priv_key_r is already referenced */ + } else if (ns->type != MAIL_NAMESPACE_TYPE_PUBLIC) { + ret = mail_crypt_get_private_key(_mail->box, pubkey_digest, + FALSE, FALSE, priv_key_r, + error_r); + /* priv_key_r is already referenced */ + } else { + *error_r = "Public emails cannot have keys"; + ret = -1; + } + + i_assert(ret <= 0 || *priv_key_r != NULL); + + return ret; +} + +static int +mail_crypt_istream_opened(struct mail *_mail, struct istream **stream) +{ + struct mail_private *mail = (struct mail_private *)_mail; + struct mail_user *user = _mail->box->storage->user; + struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT_REQUIRE(user); + struct mail_crypt_cache *cache = &muser->cache; + union mail_module_context *mmail = MAIL_CRYPT_MAIL_CONTEXT(mail); + struct istream *input; + + if (_mail->uid > 0 && cache->uid == _mail->uid && cache->box == _mail->box) { + /* use the cached stream. when doing partial reads it should + already be seeked into the wanted offset. */ + i_stream_unref(stream); + i_stream_seek(cache->input, 0); + *stream = i_stream_create_limit(cache->input, UOFF_T_MAX); + return mmail->super.istream_opened(_mail, stream); + } + + /* decryption is the outmost stream, so add it before others + (e.g. zlib) */ + if (!mail_crypt_is_stream_encrypted(*stream)) + return mmail->super.istream_opened(_mail, stream); + + input = *stream; + *stream = i_stream_create_decrypt_callback(input, + mail_crypt_istream_get_private_key, _mail); + i_stream_unref(&input); + + *stream = mail_crypt_cache_open(muser, _mail, *stream); + return mmail->super.istream_opened(_mail, stream); +} + +static void mail_crypt_close(struct mail *_mail) +{ + struct mail_private *mail = (struct mail_private *)_mail; + union mail_module_context *mmail = MAIL_CRYPT_MAIL_CONTEXT(mail); + struct mail_crypt_user *muser = + MAIL_CRYPT_USER_CONTEXT_REQUIRE(_mail->box->storage->user); + struct mail_crypt_cache *cache = &muser->cache; + uoff_t size; + + if (_mail->uid > 0 && cache->uid == _mail->uid && cache->box == _mail->box) { + /* make sure we have read the entire email into the seekable + stream (which causes the original input stream to be + unrefed). we can't safely keep the original input stream + open after the mail is closed. */ + if (i_stream_get_size(cache->input, TRUE, &size) < 0) + mail_crypt_cache_close(muser); + } + mmail->super.close(_mail); +} + +static void mail_crypt_mail_allocated(struct mail *_mail) +{ + struct mail_crypt_user *muser = + MAIL_CRYPT_USER_CONTEXT(_mail->box->storage->user); + if (muser == NULL) return; + + struct mail_private *mail = (struct mail_private *)_mail; + struct mail_vfuncs *v = mail->vlast; + union mail_module_context *mmail; + + mmail = p_new(mail->pool, union mail_module_context, 1); + mmail->super = *v; + mail->vlast = &mmail->super; + + v->istream_opened = mail_crypt_istream_opened; + v->close = mail_crypt_close; + MODULE_CONTEXT_SET_SELF(mail, mail_crypt_mail_module, mmail); +} + +static int mail_crypt_mail_save_finish(struct mail_save_context *ctx) +{ + struct mailbox *box = ctx->transaction->box; + union mailbox_module_context *zbox = MAIL_CRYPT_CONTEXT(box); + struct istream *input; + + if (zbox->super.save_finish(ctx) < 0) + return -1; + + /* we're here only if mail-crypt plugin is disabled. we want to make + sure that even though we're saving an unencrypted mail, the mail + can't be faked to look like an encrypted mail. */ + if (mail_get_stream(ctx->dest_mail, NULL, NULL, &input) < 0) + return -1; + + if (mail_crypt_is_stream_encrypted(input)) { + mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, + "Saving mails encrypted by client isn't supported"); + return -1; + } + return 0; +} + +static int +mail_crypt_mail_save_begin(struct mail_save_context *ctx, + struct istream *input) +{ + const char *pubid; + struct mailbox *box = ctx->transaction->box; + struct mail_crypt_mailbox *mbox = MAIL_CRYPT_CONTEXT(box); + struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT(box->storage->user); + + enum io_stream_encrypt_flags enc_flags = 0; + if (muser != NULL) { + if (muser->save_version == 1) { + enc_flags = IO_STREAM_ENC_VERSION_1; + } else if (muser->save_version == 2) { + enc_flags = IO_STREAM_ENC_INTEGRITY_AEAD; + } else { + i_assert(muser->save_version == 0); + } + } + + if (mbox->module_ctx.super.save_begin(ctx, input) < 0) + return -1; + + if (enc_flags == 0) + return 0; + + struct dcrypt_public_key *pub_key; + if (muser->global_keys.public_key != NULL) + pub_key = muser->global_keys.public_key; + else if (mbox->pub_key != NULL) + pub_key = mbox->pub_key; + else { + const char *error; + int ret; + + if ((ret = mail_crypt_box_get_public_key(box, &pub_key, + &error)) <= 0) + { + struct dcrypt_keypair pair; + + if (ret < 0) { + mail_storage_set_error(box->storage, + MAIL_ERROR_PARAMS, + t_strdup_printf("get_public_key(%s) failed: %s", + mailbox_get_vname(box), + error)); + return ret; + } + + if (muser->save_version < 2) { + mail_storage_set_error(box->storage, + MAIL_ERROR_PARAMS, + t_strdup_printf("generate_keypair(%s) failed: " + "unsupported save_version=%d", + mailbox_get_vname(box), + muser->save_version)); + return -1; + } + + if (mail_crypt_box_generate_keypair(box, &pair, NULL, + &pubid, &error) < 0) { + mail_storage_set_error(box->storage, + MAIL_ERROR_PARAMS, + t_strdup_printf("generate_keypair(%s) failed: %s", + mailbox_get_vname(box), + error)); + return -1; + } + pub_key = pair.pub; + dcrypt_key_unref_private(&pair.priv); + + } + mbox->pub_key = pub_key; + } + + /* encryption is the outermost layer (zlib etc. are inside) */ + struct ostream *output = o_stream_create_encrypt(ctx->data.output, + MAIL_CRYPT_ENC_ALGORITHM, pub_key, enc_flags); + + o_stream_unref(&ctx->data.output); + ctx->data.output = output; + o_stream_cork(ctx->data.output); + return 0; +} + +static int +mail_crypt_mailbox_copy(struct mail_save_context *ctx, struct mail *mail) +{ + struct mail_crypt_mailbox *mbox = MAIL_CRYPT_CONTEXT(ctx->transaction->box); + + if (ctx->transaction->box != mail->box) + return mail_storage_copy(ctx, mail); + return mbox->module_ctx.super.copy(ctx, mail); +} + +static void mail_crypt_mailbox_close(struct mailbox *box) +{ + struct mail_crypt_mailbox *mbox = MAIL_CRYPT_CONTEXT(box); + struct mail_crypt_user *muser = + MAIL_CRYPT_USER_CONTEXT(box->storage->user); + + if (mbox->pub_key != NULL) + dcrypt_key_unref_public(&mbox->pub_key); + if (muser != NULL && muser->cache.box == box) + mail_crypt_cache_close(muser); + mbox->module_ctx.super.close(box); +} + +static void mail_crypt_mailbox_allocated(struct mailbox *box) +{ + struct mailbox_vfuncs *v = box->vlast; + struct mail_crypt_user *muser = + MAIL_CRYPT_USER_CONTEXT(box->storage->user); + struct mail_crypt_mailbox *mbox; + enum mail_storage_class_flags class_flags = box->storage->class_flags; + + mbox = p_new(box->pool, struct mail_crypt_mailbox, 1); + mbox->module_ctx.super = *v; + box->vlast = &mbox->module_ctx.super; + v->close = mail_crypt_mailbox_close; + + MODULE_CONTEXT_SET(box, mail_crypt_storage_module, mbox); + + if ((class_flags & MAIL_STORAGE_CLASS_FLAG_BINARY_DATA) != 0) { + v->save_begin = mail_crypt_mail_save_begin; + + /* if global keys are used, re-encrypting on copy/move + is not necessary, so do not attempt to do it. + with per-folder keys, emails must be re-encrypted + when moving to another folder */ + if (muser == NULL || muser->save_version == 0 || + muser->global_keys.public_key == NULL) + v->copy = mail_crypt_mailbox_copy; + if (muser == NULL || muser->save_version == 0) + v->save_finish = mail_crypt_mail_save_finish; + } +} + +static void mail_crypt_mail_user_deinit(struct mail_user *user) +{ + struct mail_crypt_user *muser = MAIL_CRYPT_USER_CONTEXT_REQUIRE(user); + + mail_crypt_key_cache_destroy(&muser->key_cache); + mail_crypt_global_keys_free(&muser->global_keys); + mail_crypt_cache_close(muser); + muser->module_ctx.super.deinit(user); +} + +static void mail_crypt_mail_user_created(struct mail_user *user) +{ + struct mail_user_vfuncs *v = user->vlast; + struct mail_crypt_user *muser; + const char *error = NULL; + + muser = p_new(user->pool, struct mail_crypt_user, 1); + muser->module_ctx.super = *v; + user->vlast = &muser->module_ctx.super; + + const char *curve = mail_user_plugin_getenv(user, "mail_crypt_curve"); + buffer_t *tmp = t_str_new(64); + if (curve == NULL || *curve == '\0') { + e_debug(user->event, "mail_crypt_plugin: mail_crypt_curve setting " + "missing - generating EC keys disabled"); + } else if (!dcrypt_name2oid(curve, tmp, &error)) { + user->error = p_strdup_printf(user->pool, + "mail_crypt_plugin: " + "invalid mail_crypt_curve setting %s: %s", + curve, error); + } else { + muser->curve = p_strdup(user->pool, curve); + } + + const char *version = mail_user_plugin_getenv(user, + "mail_crypt_save_version"); + + if (version == NULL || *version == '\0') { + user->error = p_strdup_printf(user->pool, + "mail_crypt_plugin: " + "mail_crypt_save_version setting missing"); + } else if (version[0] == '0') { + muser->save_version = 0; + } else if (version[0] == '1') { + muser->save_version = 1; + } else if (version[0] == '2') { + muser->save_version = 2; + } else { + user->error = p_strdup_printf(user->pool, + "mail_crypt_plugin: Invalid " + "mail_crypt_save_version %s: use 0, 1, or 2 ", + version); + } + + if (mail_crypt_global_keys_load(user, "mail_crypt_global", + &muser->global_keys, FALSE, &error) < 0) { + user->error = p_strdup_printf(user->pool, + "mail_crypt_plugin: %s", error); + } + + v->deinit = mail_crypt_mail_user_deinit; + MODULE_CONTEXT_SET(user, mail_crypt_user_module, muser); +} + +static struct mail_storage_hooks mail_crypt_mail_storage_hooks = { + .mail_user_created = mail_crypt_mail_user_created, + .mail_allocated = mail_crypt_mail_allocated +}; + +static struct mail_storage_hooks mail_crypt_mail_storage_hooks_post = { + .mailbox_allocated = mail_crypt_mailbox_allocated +}; + +static struct module crypto_post_module = { + .path = "lib95_mail_crypt_plugin.so" +}; + +void mail_crypt_plugin_init(struct module *module) +{ + const char* error; + if (!dcrypt_initialize("openssl", NULL, &error)) + i_fatal("dcrypt_initialize(): %s", error); + mail_storage_hooks_add(module, &mail_crypt_mail_storage_hooks); + /* when this plugin is loaded, there's the potential chance for + mixed delivery between encrypted and non-encrypted recipients. + The mail_crypt_mailbox_allocated() hook ensures encrypted + content isn't copied as such into cleartext recipients + (and the other way around) */ + mail_storage_hooks_add_forced(&crypto_post_module, + &mail_crypt_mail_storage_hooks_post); + mail_crypt_key_register_mailbox_internal_attributes(); +} + +void mail_crypt_plugin_deinit(void) +{ + mail_storage_hooks_remove(&mail_crypt_mail_storage_hooks); + mail_storage_hooks_remove(&mail_crypt_mail_storage_hooks_post); +} diff --git a/src/plugins/mail-crypt/mail-crypt-plugin.h b/src/plugins/mail-crypt/mail-crypt-plugin.h new file mode 100644 index 0000000..4556a92 --- /dev/null +++ b/src/plugins/mail-crypt/mail-crypt-plugin.h @@ -0,0 +1,32 @@ +#ifndef MAIL_CRYPT_PLUGIN_H +#define MAIL_CRYPT_PLUGIN_H + +struct mailbox; +struct module; + +struct mail_crypt_cache { + struct timeout *to; + struct mailbox *box; + uint32_t uid; + + struct istream *input; +}; + +struct mail_crypt_user { + union mail_user_module_context module_ctx; + + struct mail_crypt_global_keys global_keys; + struct mail_crypt_cache cache; + struct mail_crypt_key_cache_entry *key_cache; + const char *curve; + int save_version; +}; + +void mail_crypt_plugin_init(struct module *module); +void mail_crypt_plugin_deinit(void); + +#define MAIL_CRYPT_MAIL_CACHE_EXPIRE_MSECS (60*1000) + +struct mail_crypt_user *mail_crypt_get_mail_crypt_user(struct mail_user *user); + +#endif diff --git a/src/plugins/mail-crypt/mail-crypt-pluginenv.c b/src/plugins/mail-crypt/mail-crypt-pluginenv.c new file mode 100644 index 0000000..68cf94f --- /dev/null +++ b/src/plugins/mail-crypt/mail-crypt-pluginenv.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ +#include "lib.h" +#include "str.h" +#include "array.h" +#include "settings-parser.h" +#include "master-service.h" +#include "master-service-settings.h" +#include "mail-crypt-common.h" +#include "mail-crypt-key.h" +#include "fs-crypt-settings.h" + +static const struct fs_crypt_settings * +fs_crypt_load_settings(void) +{ + static const struct setting_parser_info *set_roots[] = { + &fs_crypt_setting_parser_info, + NULL + }; + struct master_service_settings_input input; + struct master_service_settings_output output; + const char *error; + + i_zero(&input); + input.roots = set_roots; + input.module = "fs-crypt"; + input.service = "fs-crypt"; + if (master_service_settings_read(master_service, &input, + &output, &error) < 0) + i_fatal("Error reading configuration: %s", error); + + return master_service_settings_get_others(master_service)[0]; +} + +static +const char *mail_crypt_plugin_getenv(const struct fs_crypt_settings *set, + const char *name) +{ + const char *const *envs; + unsigned int i, count; + + if (set == NULL) + return NULL; + + if (!array_is_created(&set->plugin_envs)) + return NULL; + + envs = array_get(&set->plugin_envs, &count); + for (i = 0; i < count; i += 2) { + if (strcmp(envs[i], name) == 0) + return envs[i+1]; + } + return NULL; +} + +static int +mail_crypt_load_global_private_keys(const struct fs_crypt_settings *set, + const char *set_prefix, + struct mail_crypt_global_keys *global_keys, + const char **error_r) +{ + string_t *set_key = t_str_new(64); + str_append(set_key, set_prefix); + str_append(set_key, "_private_key"); + size_t prefix_len = str_len(set_key); + + unsigned int i = 1; + const char *key_data; + while ((key_data = mail_crypt_plugin_getenv(set, str_c(set_key))) != NULL) { + const char *set_pw = t_strconcat(str_c(set_key), "_password", NULL); + const char *password = mail_crypt_plugin_getenv(set, set_pw); + if (mail_crypt_load_global_private_key(str_c(set_key), key_data, + set_pw, password, + global_keys, error_r) < 0) + return -1; + str_truncate(set_key, prefix_len); + str_printfa(set_key, "%u", ++i); + } + return 0; +} + +int mail_crypt_global_keys_load_pluginenv(const char *set_prefix, + struct mail_crypt_global_keys *global_keys_r, + const char **error_r) +{ + const struct fs_crypt_settings *set = fs_crypt_load_settings(); + + const char *set_key = t_strconcat(set_prefix, "_public_key", NULL); + const char *key_data = mail_crypt_plugin_getenv(set, set_key); + int ret = 0; + + mail_crypt_global_keys_init(global_keys_r); + if (key_data != NULL) { + if (mail_crypt_load_global_public_key(set_key, key_data, + global_keys_r, error_r) < 0) + ret = -1; + } + + if (ret == 0 && + mail_crypt_load_global_private_keys(set, set_prefix, global_keys_r, + error_r) < 0) + ret = -1; + + if (ret != 0) + mail_crypt_global_keys_free(global_keys_r); + return ret; +} diff --git a/src/plugins/mail-crypt/mail-crypt-userenv.c b/src/plugins/mail-crypt/mail-crypt-userenv.c new file mode 100644 index 0000000..b152a7f --- /dev/null +++ b/src/plugins/mail-crypt/mail-crypt-userenv.c @@ -0,0 +1,66 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ +#include "lib.h" +#include "str.h" +#include "mail-user.h" +#include "mail-crypt-common.h" +#include "mail-crypt-key.h" + +static int +mail_crypt_load_global_private_keys(struct mail_user *user, + const char *set_prefix, + struct mail_crypt_global_keys *global_keys, + bool ignore_errors, + const char **error_r) +{ + string_t *set_key = t_str_new(64); + str_append(set_key, set_prefix); + str_append(set_key, "_private_key"); + size_t prefix_len = str_len(set_key); + + unsigned int i = 1; + const char *key_data; + while ((key_data = mail_user_plugin_getenv(user, str_c(set_key))) != NULL) { + const char *set_pw = t_strconcat(str_c(set_key), "_password", NULL); + const char *password = mail_user_plugin_getenv(user, set_pw); + if (mail_crypt_load_global_private_key(str_c(set_key), key_data, + set_pw, password, + global_keys, + error_r) < 0) { + /* skip this key */ + if (ignore_errors) { + e_debug(user->event, "mail-crypt-plugin: " + "mail_crypt_load_global_private_key failed: %s", + *error_r); + *error_r = NULL; + continue; + } + return -1; + } + str_truncate(set_key, prefix_len); + str_printfa(set_key, "%u", ++i); + } + return 0; +} + +int mail_crypt_global_keys_load(struct mail_user *user, const char *set_prefix, + struct mail_crypt_global_keys *global_keys_r, + bool ignore_privkey_errors, + const char **error_r) +{ + const char *set_key = t_strconcat(set_prefix, "_public_key", NULL); + const char *key_data = mail_user_plugin_getenv(user, set_key); + + mail_crypt_global_keys_init(global_keys_r); + if (key_data != NULL) { + if (mail_crypt_load_global_public_key(set_key, + key_data, + global_keys_r, + error_r) < 0) + return -1; + } + if (mail_crypt_load_global_private_keys(user, set_prefix, global_keys_r, + ignore_privkey_errors, + error_r) < 0) + return -1; + return 0; +} diff --git a/src/plugins/mail-crypt/test-mail-global-key.c b/src/plugins/mail-crypt/test-mail-global-key.c new file mode 100644 index 0000000..4c5ed38 --- /dev/null +++ b/src/plugins/mail-crypt/test-mail-global-key.c @@ -0,0 +1,130 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "test-common.h" +#include "randgen.h" +#include "array.h" +#include "dcrypt.h" +#include "hex-binary.h" + +#include "mail-crypt-common.h" +#include "mail-crypt-key.h" +#include "fs-crypt-settings.h" + +#include "mail-crypt-pluginenv.c" + +static struct fs_crypt_settings fs_set; + +static const char *settings[] = { + "mail_crypt_global_private_key", + "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ1lJdWZKWlplMlk2aUZ6NXgKa29Jb3lzYjNkWkxaV3N5ZWtqT2MvR2pzTGQyaFJBTkNBQVNuSVdnUXVoRThqcUFMY21maXVuUnlFazd2a3EveQphOXZZSzUwYjNjRmhDc0xVNHRmVlRMa0IxWS82VmxaajYzUUtNelhOdms1RzVPRDFvZkVsY3B5agotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==", + "mail_crypt_global_public_key", + "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFcHlGb0VMb1JQSTZnQzNKbjRycDBjaEpPNzVLdgo4bXZiMkN1ZEc5M0JZUXJDMU9MWDFVeTVBZFdQK2xaV1krdDBDak0xemI1T1J1VGc5YUh4SlhLY293PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==", + "mail_crypt_global_private_key2", + "LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLQpNSUhlTUVrR0NTcUdTSWIzRFFFRkRUQThNQnNHQ1NxR1NJYjNEUUVGRERBT0JBaXA2cUpja1FET3F3SUNDQUF3CkhRWUpZSVpJQVdVREJBRXFCQkFXN09oUFRlU0xSOExLcGYwZjZHa3ZCSUdRZk5rYUpodnM2VWVWS2RkN2NzdFMKMURSNXJYTWtON09FbVNjTTljRlk2UDVrMzdnY1VJUFZudTQrOTFYZUE1MTU2cnBpUEpycEdkZnprcjhPNVFqZApsMWRycmR6Z0hqZHE4T2VmbUR1MEEzMjRZd25SS3hGRExUcjlHMkxVMkhoYmV6a0xjV1FwMVJISDZsNXRRcUtwCjZid05iMnc3OXhCb01YSjN6MVZqcElOZk9wRnJ6M3lucVlqUXhseTIrQjg2Ci0tLS0tRU5EIEVOQ1JZUFRFRCBQUklWQVRFIEtFWS0tLS0tCg==", + "mail_crypt_global_private_key2_password", + "password", +}; + +int +mail_crypt_load_global_private_keys(const struct fs_crypt_settings *set, + const char *set_prefix, + struct mail_crypt_global_keys *global_keys, + const char **error_r); + +static void test_setup(void) +{ + struct dcrypt_settings set = { + .module_dir = top_builddir "/src/lib-dcrypt/.libs" + }; + if (!dcrypt_initialize(NULL, &set, NULL)) { + i_info("No functional dcrypt backend found - skipping tests"); + test_exit(0); + } + i_array_init(&fs_set.plugin_envs, 8); + array_append(&fs_set.plugin_envs, settings, N_ELEMENTS(settings)); +} + +static void test_try_load_keys(void) +{ + const char *pubid1 = "c79e262924842de291a8bcd413f4122a570abd033adeff7c1cdfdc9d05998c75"; + const char *pubid2 = "aaf927444bff8b63425e852c6b3f769e8221b952b42cf886fae7d326c5be098e"; + buffer_t *key_id = t_buffer_create(128); + + const char *error = NULL; + test_begin("try_load_keys"); + + struct mail_crypt_global_keys keys; + i_zero(&keys); + mail_crypt_global_keys_init(&keys); + + const char *set_prefix = "mail_crypt_global"; + const char *set_key = t_strconcat(set_prefix, "_public_key", NULL); + const char *key_data = mail_crypt_plugin_getenv(&fs_set, set_key); + + test_assert(key_data != NULL); + + if (key_data != NULL) { + test_assert(mail_crypt_load_global_public_key(set_key, key_data, + &keys, &error) == 0); + test_assert(mail_crypt_load_global_private_keys(&fs_set, set_prefix, + &keys, &error) == 0); + /* did we get two private keys? */ + test_assert(array_count(&keys.private_keys) == 2); + + /* public key id checks */ + + buffer_set_used_size(key_id, 0); + test_assert(dcrypt_key_id_public(keys.public_key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error) == TRUE); + test_assert(strcmp(binary_to_hex(key_id->data, key_id->used), pubid1) == 0); + + const struct mail_crypt_global_private_key *key = + array_front(&keys.private_keys); + + buffer_set_used_size(key_id, 0); + test_assert(dcrypt_key_id_private(key->key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error) == TRUE); + test_assert(strcmp(binary_to_hex(key_id->data, key_id->used), pubid1) == 0); + + key = array_idx(&keys.private_keys, 1); + buffer_set_used_size(key_id, 0); + test_assert(dcrypt_key_id_private(key->key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error) == TRUE); + test_assert(strcmp(binary_to_hex(key_id->data, key_id->used), pubid2) == 0); + + } + + mail_crypt_global_keys_free(&keys); + + test_end(); +} + +static void test_empty_keyset(void) +{ + test_begin("test_empty_keyset"); + + /* this should not crash */ + struct mail_crypt_global_keys keys; + i_zero(&keys); + test_assert(mail_crypt_global_key_find(&keys, "423423423423") == NULL); + + test_end(); +} + +static void test_teardown(void) +{ + array_free(&fs_set.plugin_envs); + dcrypt_deinitialize(); +} + +int main(void) +{ + void (*tests[])(void) = { + test_setup, + test_try_load_keys, + test_empty_keyset, + test_teardown, + NULL + }; + + int ret = test_run(tests); + return ret; +} diff --git a/src/plugins/mail-crypt/test-mail-key.c b/src/plugins/mail-crypt/test-mail-key.c new file mode 100644 index 0000000..ac89835 --- /dev/null +++ b/src/plugins/mail-crypt/test-mail-key.c @@ -0,0 +1,424 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "test-common.h" +#include "hex-binary.h" +#include "master-service.h" +#include "test-mail-storage-common.h" +#include "dcrypt.h" + +#include "mail-crypt-common.h" +#include "mail-crypt-key.h" +#include "mail-crypt-plugin.h" + +static const char *mcp_old_user_key = "1\t716\t0\t048FD04FD3612B22D32790C592CF21CEF417EFD2EA34AE5F688FA5B51BED29E05A308B68DA78E16E90B47A11E133BD9A208A2894FD01B0BEE865CE339EA3FB17AC\td0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0"; +static const char *mcp_old_user_key_id = "d0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0"; +static const char *mcp_old_box_key = "1\t716\t1\t0567e6bf9579813ae967314423b0fceb14bda24749303923de9a9bb9370e0026f995901a57e63113eeb2baf0c940e978d00686cbb52bd5014bc318563375876255\t0300E46DA2125427BE968EB3B649910CDC4C405E5FFDE18D433A97CABFEE28CEEFAE9EE356C792004FFB80981D67E741B8CC036A34235A8D2E1F98D1658CFC963D07EB\td0cfaca5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615e77a0\t7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f"; +static const char *mcp_old_box_key_id = "7c9a1039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00fa4f"; + +static struct test_mail_storage_ctx *test_ctx; +static const char *test_user_key_id; +static const char *test_box_key_id; + +static struct mail_crypt_user mail_crypt_user; + +struct mail_crypt_user *mail_crypt_get_mail_crypt_user(struct mail_user *user ATTR_UNUSED) +{ + return &mail_crypt_user; +} + +static +int test_mail_attribute_get(struct mailbox *box, bool user_key, bool shared, + const char *pubid, const char **value_r, const char **error_r) +{ + const char *attr_name; + enum mail_attribute_type attr_type; + + if (strcmp(pubid, ACTIVE_KEY_NAME) == 0) { + attr_name = user_key ? USER_CRYPT_PREFIX ACTIVE_KEY_NAME : + BOX_CRYPT_PREFIX ACTIVE_KEY_NAME; + attr_type = MAIL_ATTRIBUTE_TYPE_SHARED; + } else { + attr_name = t_strdup_printf("%s%s%s", + user_key ? USER_CRYPT_PREFIX : + BOX_CRYPT_PREFIX, + shared ? PUBKEYS_PREFIX : + PRIVKEYS_PREFIX, + pubid); + attr_type = shared ? MAIL_ATTRIBUTE_TYPE_SHARED : MAIL_ATTRIBUTE_TYPE_PRIVATE; + } + struct mail_attribute_value value; + + int ret; + + if ((ret = mailbox_attribute_get(box, attr_type, + attr_name, &value)) <= 0) { + if (ret < 0) { + *error_r = t_strdup_printf("mailbox_attribute_get(%s, %s) failed: %s", + mailbox_get_vname(box), + attr_name, + mailbox_get_last_internal_error(box, NULL)); + } + } else { + *value_r = t_strdup(value.value); + } + return ret; +} + +static int +test_mail_attribute_set(struct mailbox_transaction_context *t, + bool user_key, bool shared, const char *pubid, + const char *value, const char **error_r) +{ + const char *attr_name; + enum mail_attribute_type attr_type; + + if (strcmp(pubid, ACTIVE_KEY_NAME) == 0) { + attr_name = user_key ? USER_CRYPT_PREFIX ACTIVE_KEY_NAME : + BOX_CRYPT_PREFIX ACTIVE_KEY_NAME; + attr_type = MAIL_ATTRIBUTE_TYPE_SHARED; + } else { + attr_name = t_strdup_printf("%s%s%s", + user_key ? USER_CRYPT_PREFIX : + BOX_CRYPT_PREFIX, + shared ? PUBKEYS_PREFIX : + PRIVKEYS_PREFIX, + pubid); + attr_type = shared ? MAIL_ATTRIBUTE_TYPE_SHARED : MAIL_ATTRIBUTE_TYPE_PRIVATE; + } + + struct mail_attribute_value attr_value; + + int ret; + + i_zero(&attr_value); + attr_value.value = value; + + if ((ret = mailbox_attribute_set(t, attr_type, + attr_name, &attr_value)) <= 0) { + if (ret < 0) { + *error_r = t_strdup_printf("mailbox_attribute_set(%s, %s) failed: %s", + mailbox_get_vname(mailbox_transaction_get_mailbox(t)), + attr_name, + mailbox_get_last_internal_error(mailbox_transaction_get_mailbox(t), NULL)); + } + } + + return ret; +} + + +static void test_generate_user_key(void) +{ + struct dcrypt_keypair pair; + const char *pubid; + const char *error = NULL; + + test_begin("generate user key"); + + /* try to generate a keypair for user */ + if (mail_crypt_user_generate_keypair(test_ctx->user, &pair, + &pubid, &error) < 0) { + i_error("generate_keypair failed: %s", error); + test_exit(1); + } + + test_assert(pubid != NULL); + + test_user_key_id = p_strdup(test_ctx->pool, pubid); + + dcrypt_keypair_unref(&pair); + error = NULL; + + /* keys ought to be in cache or somewhere...*/ + if (mail_crypt_user_get_private_key(test_ctx->user, NULL, &pair.priv, &error) <= 0) + { + i_error("Cannot get user private key: %s", error); + } + + test_assert(pair.priv != NULL); + + if (pair.priv != NULL) + dcrypt_key_unref_private(&pair.priv); + + test_end(); +} + +static void test_generate_inbox_key(void) +{ + struct dcrypt_public_key *user_key; + struct dcrypt_keypair pair; + const char *error = NULL, *pubid = NULL; + + test_begin("generate inbox key"); + + if (mail_crypt_user_get_public_key(test_ctx->user, &user_key, + &error) <= 0) { + i_error("Cannot get user private key: %s", error); + } + struct mail_namespace *ns = + mail_namespace_find_inbox(test_ctx->user->namespaces); + struct mailbox *box = mailbox_alloc(ns->list, "INBOX", + MAILBOX_FLAG_READONLY); + if (mailbox_open(box) < 0) + i_fatal("mailbox_open(INBOX) failed: %s", + mailbox_get_last_internal_error(box, NULL)); + if (mail_crypt_box_generate_keypair(box, &pair, user_key, &pubid, + &error) < 0) { + i_error("generate_keypair failed: %s", error); + test_exit(1); + } + + i_assert(pubid != NULL); + + dcrypt_keypair_unref(&pair); + dcrypt_key_unref_public(&user_key); + mailbox_free(&box); + + test_box_key_id = p_strdup(test_ctx->pool, pubid); + + test_end(); +} + +static void test_cache_reset(void) +{ + struct dcrypt_keypair pair; + const char *error = NULL; + + test_begin("cache reset"); + + struct mail_crypt_user *muser = + mail_crypt_get_mail_crypt_user(test_ctx->user); + mail_crypt_key_cache_destroy(&muser->key_cache); + + test_assert(mail_crypt_user_get_private_key(test_ctx->user, NULL, + &pair.priv, &error) > 0); + if (error != NULL) + i_error("mail_crypt_user_get_private_key() failed: %s", error); + error = NULL; + test_assert(mail_crypt_user_get_public_key(test_ctx->user, + &pair.pub, &error) > 0); + if (error != NULL) + i_error("mail_crypt_user_get_public_key() failed: %s", error); + + dcrypt_keypair_unref(&pair); + + test_end(); +} + +static void test_verify_keys(void) +{ + const char *value = "", *error = NULL; + + const char *enc_id; + enum dcrypt_key_encryption_type enc_type; + + test_begin("verify keys"); + + struct dcrypt_private_key *privkey = NULL, *user_key = NULL; + struct dcrypt_public_key *pubkey = NULL; + + struct mail_namespace *ns = + mail_namespace_find_inbox(test_ctx->user->namespaces); + struct mailbox *box = mailbox_alloc(ns->list, "INBOX", + MAILBOX_FLAG_READONLY); + if (mailbox_open(box) < 0) + i_fatal("mailbox_open(INBOX) failed: %s", + mailbox_get_last_internal_error(box, NULL)); + /* verify links */ + + /* user's public key */ + test_assert(test_mail_attribute_get(box, TRUE, TRUE, ACTIVE_KEY_NAME, + &value, &error) > 0); + test_assert(strcmp(value, test_user_key_id) == 0); + + test_assert(test_mail_attribute_get(box, TRUE, TRUE, value, &value, + &error) > 0); + + /* load key */ + test_assert(dcrypt_key_load_public(&pubkey, value, &error) == TRUE); + + /* see if it matches */ + test_assert(mail_crypt_public_key_id_match(pubkey, test_user_key_id, + &error) > 0); + dcrypt_key_unref_public(&pubkey); + + /* user's private key */ + test_assert(test_mail_attribute_get(box, TRUE, FALSE, ACTIVE_KEY_NAME, + &value, &error) > 0); + test_assert(strcmp(value, test_user_key_id) == 0); + + test_assert(test_mail_attribute_get(box, TRUE, FALSE, value, &value, + &error) > 0); + + /* load key */ + test_assert(dcrypt_key_load_private(&user_key, value, NULL, NULL, + &error) == TRUE); + + /* see if it matches */ + test_assert(mail_crypt_private_key_id_match(user_key, test_user_key_id, + &error) > 0); + + + + + /* inbox's public key */ + test_assert(test_mail_attribute_get(box, FALSE, TRUE, ACTIVE_KEY_NAME, + &value, &error) > 0); + test_assert(strcmp(value, test_box_key_id) == 0); + + test_assert(test_mail_attribute_get(box, FALSE, TRUE, value, &value, + &error) > 0); + + /* load key */ + test_assert(dcrypt_key_load_public(&pubkey, value, &error) == TRUE); + + /* see if it matches */ + test_assert(mail_crypt_public_key_id_match(pubkey, test_box_key_id, + &error) > 0); + dcrypt_key_unref_public(&pubkey); + + /* user's private key */ + test_assert(test_mail_attribute_get(box, FALSE, FALSE, ACTIVE_KEY_NAME, + &value, &error) > 0); + test_assert(strcmp(value, test_box_key_id) == 0); + + test_assert(test_mail_attribute_get(box, FALSE, FALSE, value, &value, + &error) > 0); + + test_assert(dcrypt_key_string_get_info(value, NULL, NULL, NULL, + &enc_type, &enc_id, NULL, + &error) == TRUE); + + test_assert(enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY); + test_assert(strcmp(enc_id, test_user_key_id) == 0); + + /* load key */ + test_assert(dcrypt_key_load_private(&privkey, value, NULL, user_key, + &error) == TRUE); + + /* see if it matches */ + test_assert(mail_crypt_private_key_id_match(privkey, test_box_key_id, + &error) > 0); + dcrypt_key_unref_private(&privkey); + dcrypt_key_unref_private(&user_key); + + mailbox_free(&box); + + test_end(); +} + +static void test_old_key(void) +{ + test_begin("old keys"); + + const char *error = NULL; + struct dcrypt_private_key *privkey = NULL; + + struct mail_namespace *ns = + mail_namespace_find_inbox(test_ctx->user->namespaces); + struct mailbox *box = mailbox_alloc(ns->list, "INBOX", + MAILBOX_FLAG_READONLY); + if (mailbox_open(box) < 0) + i_fatal("mailbox_open(INBOX) failed: %s", + mailbox_get_last_internal_error(box, NULL)); + + struct mailbox_transaction_context *t = + mailbox_transaction_begin(box, 0, __func__); + + test_mail_attribute_set(t, TRUE, FALSE, mcp_old_user_key_id, + mcp_old_user_key, &error); + test_mail_attribute_set(t, FALSE, FALSE, mcp_old_box_key_id, + mcp_old_box_key, &error); + + (void)mailbox_transaction_commit(&t); + + error = NULL; + + /* try to load old key */ + test_assert(mail_crypt_get_private_key(box, mcp_old_box_key_id, FALSE, FALSE, + &privkey, &error) > 0); + + if (error != NULL) + i_error("mail_crypt_get_private_key(%s) failed: %s", + mcp_old_box_key_id, + error); + + test_assert(privkey != NULL); + + if (privkey != NULL) { + buffer_t *key_id = t_buffer_create(32); + test_assert(dcrypt_key_id_private_old(privkey, key_id, &error)); + test_assert(strcmp(binary_to_hex(key_id->data, key_id->used), mcp_old_box_key_id) == 0); + dcrypt_key_unref_private(&privkey); + } + + mailbox_free(&box); + + test_end(); +} + +static void test_setup(void) +{ + struct dcrypt_settings set = { + .module_dir = top_builddir "/src/lib-dcrypt/.libs" + }; + if (!dcrypt_initialize(NULL, &set, NULL)) { + i_info("No functional dcrypt backend found - skipping tests"); + test_exit(0); + } + test_ctx = test_mail_storage_init(); + const char *username = "mcp_test@example.com"; + const char *const extra_input[] = { + t_strdup_printf("mail_crypt_curve=prime256v1"), + t_strdup_printf("mail_attribute_dict=file:%s/%s/dovecot-attributes", + test_ctx->home_root, username), + NULL + }; + struct test_mail_storage_settings storage_set = { + .username = username, + .driver = "maildir", + .hierarchy_sep = "/", + .extra_input = extra_input, + }; + test_mail_storage_init_user(test_ctx, &storage_set); + + mail_crypt_key_register_mailbox_internal_attributes(); +} + +static void test_teardown(void) +{ + struct mail_crypt_user *muser = + mail_crypt_get_mail_crypt_user(test_ctx->user); + mail_crypt_key_cache_destroy(&muser->key_cache); + + test_mail_storage_deinit_user(test_ctx); + test_mail_storage_deinit(&test_ctx); + dcrypt_deinitialize(); +} + +int main(int argc, char **argv) +{ + void (*tests[])(void) = { + test_setup, + test_generate_user_key, + test_generate_inbox_key, + test_cache_reset, + test_verify_keys, + test_old_key, + test_teardown, + NULL + }; + + + master_service = master_service_init("test-mail-key", + MASTER_SERVICE_FLAG_STANDALONE | + MASTER_SERVICE_FLAG_DONT_SEND_STATS | + MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS | + MASTER_SERVICE_FLAG_NO_SSL_INIT | + MASTER_SERVICE_FLAG_NO_INIT_DATASTACK_FRAME, + &argc, &argv, ""); + int ret = test_run(tests); + master_service_deinit(&master_service); + return ret; +} |